diff --git a/packages/stores/package.json b/packages/stores/package.json index ba0c318..faa3b2b 100644 --- a/packages/stores/package.json +++ b/packages/stores/package.json @@ -1,6 +1,6 @@ { "name": "@holochain-open-dev/stores", - "version": "0.7.3", + "version": "0.7.4", "description": "Re-export of svelte/store, with additional utilities to build reusable holochain-open-dev modules", "author": "guillem.cordoba@gmail.com", "main": "dist/index.js", diff --git a/packages/stores/src/pipe.ts b/packages/stores/src/pipe.ts index 123c3e9..56be69d 100644 --- a/packages/stores/src/pipe.ts +++ b/packages/stores/src/pipe.ts @@ -8,13 +8,21 @@ export type PipeStep = AsyncReadable | Readable | Promise | T; function pipeStep( store: AsyncReadable, - stepFn: (arg: T) => PipeStep + stepFn: (arg: T, ...args: any[]) => PipeStep, + previousStores: Array> ): AsyncReadable { - return derived(store, (value, set) => { + return derived([store, ...previousStores], (values, set) => { + const value = values[0]; if (value.status === "error") set(value); else if (value.status === "pending") set(value); else { - const v = stepFn(value.value); + const v = stepFn( + value.value, + ...values + .slice(1) + .map((v) => (v as any).value) + .reverse() + ); if ((v as Readable).subscribe) { return (v as Readable).subscribe((value) => { @@ -48,7 +56,9 @@ function pipeStep( /** * Takes an AsyncReadable store and derives it with the given functions - * Each step may return an `AsyncReadable`, `Readable`, `Promise` or just a raw value + * - Each step may return an `AsyncReadable`, `Readable`, `Promise` or just a raw value + * - Each step receives the results of all the previous steps, normally you'll only need + * the result for the latest one * * ```js * const asyncReadableStore = lazyLoad(async () => { @@ -58,16 +68,16 @@ function pipeStep( * const pipeStore = pipe( * asyncReadableStore, * (n1) => - * lazyLoad(async () => { // Step with `AsyncReadable` + * lazyLoad(async () => { // Step with `AsyncReadable` * await sleep(1); * return n1 + 1; * }), - * (n2) => readable(n2 + 1), // Step with `Readable` - * async (n3) => { // Step with `Promise` + * (n2) => readable(n2 + 1), // Step with `Readable` + * async (n3, n2, n1) => { // Step with `Promise` * await sleep(1); * return n3 + 1; * }, - * (n4) => n4 + 1 // Step with raw value + * (n4, n3, n2, n1) => n4 + 1 // Step with raw value * ); * pipeStore.subscribe(value => console.log(value)); // Use like any other store, will print "5" after 3 milliseconds * ``` @@ -79,64 +89,94 @@ export function pipe( export function pipe( store: AsyncReadable, fn1: (arg: T) => PipeStep, - fn2: (arg: U) => PipeStep + fn2: (arg: U, prevArg0: T) => PipeStep ): AsyncReadable; export function pipe( store: AsyncReadable, fn1: (arg: T) => PipeStep, - fn2: (arg: U) => PipeStep, - fn3: (arg: V) => PipeStep + fn2: (arg: U, prevArg: T) => PipeStep, + fn3: (arg: V, prevArg0: U, prevArg1: T) => PipeStep ): AsyncReadable; export function pipe( store: AsyncReadable, fn1: (arg: T) => PipeStep, - fn2: (arg: U) => PipeStep, - fn3: (arg: V) => PipeStep, - fn4: (arg: W) => PipeStep + fn2: (arg: U, prevArg: T) => PipeStep, + fn3: (arg: V, prevArg0: U, prevArg1: T) => PipeStep, + fn4: (arg: W, prevArg0: V, prevArg1: U, prevArg2: T) => PipeStep ): AsyncReadable; export function pipe( store: AsyncReadable, fn1: (arg: T) => PipeStep, - fn2: (arg: U) => PipeStep, - fn3: (arg: V) => PipeStep, - fn4: (arg: W) => PipeStep, - fn5: (arg: X) => PipeStep + fn2: (arg: U, prevArg: T) => PipeStep, + fn3: (arg: V, prevArg0: U, prevArg1: T) => PipeStep, + fn4: (arg: W, prevArg0: V, prevArg1: U, prevArg2: T) => PipeStep, + fn5: ( + arg: X, + prevArg0: W, + prevArg1: V, + prevArg2: U, + prevArg3: T + ) => PipeStep ): AsyncReadable; export function pipe( store: AsyncReadable, fn1: (arg: T) => PipeStep, - fn2: (arg: U) => PipeStep, - fn3: (arg: V) => PipeStep, - fn4: (arg: W) => PipeStep, - fn5: (arg: X) => PipeStep, - fn6: (arg: Y) => PipeStep + fn2: (arg: U, prevArg: T) => PipeStep, + fn3: (arg: V, prevArg0: U, prevArg1: T) => PipeStep, + fn4: (arg: W, prevArg0: V, prevArg1: U, prevArg2: T) => PipeStep, + fn5: ( + arg: X, + prevArg0: W, + prevArg1: V, + prevArg2: U, + prevArg3: T + ) => PipeStep, + fn6: ( + arg: Y, + prevArg0: X, + prevArg1: W, + prevArg2: V, + prevArg3: U, + prevArg4: T + ) => PipeStep ): AsyncReadable; export function pipe( store: AsyncReadable, fn1: (arg: T) => PipeStep, - fn2?: (arg: U) => PipeStep, - fn3?: (arg: V) => PipeStep, - fn4?: (arg: W) => PipeStep, - fn5?: (arg: X) => PipeStep, - fn6?: (arg: Y) => PipeStep + fn2?: (arg: U, prevArg: T) => PipeStep, + fn3?: (arg: V, prevArg0: U, prevArg1: T) => PipeStep, + fn4?: (arg: W, prevArg0: V, prevArg1: U, prevArg2: T) => PipeStep, + fn5?: ( + arg: X, + prevArg0: W, + prevArg1: V, + prevArg2: U, + prevArg3: T + ) => PipeStep, + fn6?: ( + arg: Y, + prevArg0: X, + prevArg1: W, + prevArg2: V, + prevArg3: U, + prevArg4: T + ) => PipeStep ): AsyncReadable { - let s: AsyncReadable = pipeStep(store, fn1); + const s1: AsyncReadable = pipeStep(store, fn1, []); - if (fn2) { - s = pipeStep(s, fn2); - } - if (fn3) { - s = pipeStep(s, fn3); - } - if (fn4) { - s = pipeStep(s, fn4); - } - if (fn5) { - s = pipeStep(s, fn5); - } - if (fn6) { - s = pipeStep(s, fn6); - } + if (!fn2) return s1; + const s2 = pipeStep(s1, fn2, [store]); - return s; + if (!fn3) return s2 as any; + const s3 = pipeStep(s2, fn3, [store, s1]); + + if (!fn4) return s3 as any; + const s4 = pipeStep(s3, fn4, [store, s1, s2]); + + if (!fn5) return s4 as any; + const s5 = pipeStep(s4, fn5, [store, s1, s2, s3]); + if (!fn6) return s5 as any; + const s6 = pipeStep(s5, fn6, [store, s1, s2, s3, s4]); + + return s6; } diff --git a/packages/stores/tests/pipe.test.js b/packages/stores/tests/pipe.test.js index 2db5d08..7732adf 100644 --- a/packages/stores/tests/pipe.test.js +++ b/packages/stores/tests/pipe.test.js @@ -33,7 +33,7 @@ it("pipe with promise", async () => { const subscriber = pipeStore.subscribe(() => {}); expect(get(pipeStore)).to.deep.equal({ status: "pending" }); - await sleep(20); + await sleep(30); expect(get(pipeStore)).to.deep.equal({ status: "complete", @@ -109,3 +109,58 @@ it("pipe with all types", async () => { value: "hihihihihi", }); }); + +it("pipe yield the results for every step", async () => { + const asyncReadableStore = lazyLoad(async () => { + await sleep(10); + return 1; + }); + const pipeStore = pipe( + asyncReadableStore, + (s1) => s1 + 1, + (s2, s1) => { + expect(s1).to.equal(1); + expect(s2).to.equal(2); + return s1 + s2; + }, + (s3, s2, s1) => { + expect(s1).to.equal(1); + expect(s2).to.equal(2); + expect(s3).to.equal(3); + return s1 + s2 + s3; + }, + (s4, s3, s2, s1) => { + expect(s1).to.equal(1); + expect(s2).to.equal(2); + expect(s3).to.equal(3); + expect(s4).to.equal(6); + return s1 + s2 + s3 + s4; + }, + (s5, s4, s3, s2, s1) => { + expect(s1).to.equal(1); + expect(s2).to.equal(2); + expect(s3).to.equal(3); + expect(s4).to.equal(6); + expect(s5).to.equal(12); + return s1 + s2 + s3 + s4 + s5; + }, + (s6, s5, s4, s3, s2, s1) => { + expect(s1).to.equal(1); + expect(s2).to.equal(2); + expect(s3).to.equal(3); + expect(s4).to.equal(6); + expect(s5).to.equal(12); + expect(s6).to.equal(24); + return s1 + s2 + s3 + s4 + s5 + s6; + } + ); + const subscriber = pipeStore.subscribe(() => {}); + + expect(get(pipeStore)).to.deep.equal({ status: "pending" }); + await sleep(30); + + expect(get(pipeStore)).to.deep.equal({ + status: "complete", + value: 48, + }); +});