@effectionx/stream-helpers 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -144,6 +144,54 @@ function* example() {
144
144
  }
145
145
  ```
146
146
 
147
+ ### ForEach
148
+
149
+ The `forEach` helper invokes a function for each item passing through a stream.
150
+ This is useful when you need to perform side effects or operations on each item
151
+ without transforming the stream itself. Unlike other stream helpers that return
152
+ transformed streams, `forEach` consumes the entire stream and returns the
153
+ stream's close value.
154
+
155
+ ```typescript
156
+ import { forEach } from "@effectionx/stream-helpers";
157
+ import { createSignal, spawn } from "effection";
158
+
159
+ function* example() {
160
+ const stream = createSignal<number, void>();
161
+
162
+ // Process each item in the stream
163
+ yield* spawn(() =>
164
+ forEach(function* (item) {
165
+ console.log(`Processing: ${item}`);
166
+ // Perform any side effects here
167
+ }, stream)
168
+ );
169
+
170
+ stream.send(1);
171
+ stream.send(2);
172
+ stream.send(3);
173
+ stream.close();
174
+ }
175
+
176
+ // Example: Handling stream close value
177
+ function* exampleWithCloseValue() {
178
+ const stream = createSignal<string, number>();
179
+
180
+ const result = yield* spawn(() =>
181
+ forEach(function* (item) {
182
+ console.log(`Item: ${item}`);
183
+ }, stream)
184
+ );
185
+
186
+ stream.send("hello");
187
+ stream.send("world");
188
+ stream.close(42); // Close with value 42
189
+
190
+ const closeValue = yield* result;
191
+ console.log(`Stream closed with: ${closeValue}`); // 42
192
+ }
193
+ ```
194
+
147
195
  ### Passthrough Tracker
148
196
 
149
197
  Passthrough Tracker stream helper provides a way to know if all items that
@@ -183,8 +231,7 @@ You can use a simple `pipe()` to compose a series of stream helpers together. In
183
231
  this example, we use one from [remeda](https://remedajs.com/docs/#pipe),
184
232
 
185
233
  ```typescript
186
- import { batch, filter, map, valve } from "@effectionx/stream-helpers";
187
- import { each } from "effection";
234
+ import { batch, filter, forEach, map, valve } from "@effectionx/stream-helpers";
188
235
  // any standard pipe function should work
189
236
  import { pipe } from "remeda";
190
237
 
@@ -202,10 +249,10 @@ function* example(source: Stream<number, unknown>) {
202
249
  batch({ maxSize: 50 }),
203
250
  );
204
251
 
205
- for (const value of yield* each(stream)) {
252
+ // Use forEach to process each value in the composed stream
253
+ yield* forEach(function* (value) {
206
254
  console.log(value);
207
- yield* each.next();
208
- }
255
+ }, stream);
209
256
  }
210
257
  ```
211
258
 
@@ -1 +1 @@
1
- {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAGlE,KAAK,iBAAiB,CAAC,CAAC,EAAE,IAAI,SAAS,MAAM,CAAC,GAAG,MAAM,CAAC,IACpD,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,GAC/B;KACC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;CACzE,CAAC,IAAI,CAAC,CAAC;AAEV,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,iBAAiB,CAAC,YAAY,CAAC,GACvC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CA4C/D"}
1
+ {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,MAAM,EAAa,MAAM,WAAW,CAAC;AAG1D,KAAK,iBAAiB,CAAC,CAAC,EAAE,IAAI,SAAS,MAAM,CAAC,GAAG,MAAM,CAAC,IACpD,IAAI,CACJ,CAAC,EACD,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CACvB,GACC;KACC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;CACzE,CAAC,IAAI,CAAC,CAAC;AAEV,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,iBAAiB,CAAC,YAAY,CAAC,GACvC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CA8E/D"}
package/esm/batch.js CHANGED
@@ -1,5 +1,5 @@
1
- import { each, race, sleep, spawn } from "effection";
2
- import { createArraySignal, is } from "@effectionx/signals";
1
+ import { spawn } from "effection";
2
+ import { timebox } from "@effectionx/timebox";
3
3
  /**
4
4
  * Creates batches of items from the source stream. The batches can be created either by
5
5
  * specifying a maximum time or a maximum size. If both are specified, the batch will be
@@ -14,38 +14,73 @@ export function batch(options) {
14
14
  return function (stream) {
15
15
  return {
16
16
  *[Symbol.iterator]() {
17
- let batch = yield* createArraySignal([]);
18
- yield* spawn(function* () {
19
- for (let item of yield* each(stream)) {
20
- batch.push(item);
21
- if (options.maxSize && batch.length >= options.maxSize) {
22
- // wait until it's drained
23
- yield* is(batch, (batch) => batch.length === 0);
24
- }
25
- yield* each.next();
26
- }
27
- });
28
- function drain() {
29
- let value = batch.valueOf();
30
- batch.set([]);
31
- return value;
32
- }
17
+ const subscription = yield* stream;
18
+ let lastPull;
33
19
  return {
34
20
  *next() {
35
- yield* is(batch, (batch) => batch.length >= 1);
36
- if (options.maxTime && options.maxSize) {
37
- yield* race([
38
- is(batch, (batch) => batch.length === options.maxSize),
39
- sleep(options.maxTime),
40
- ]);
21
+ let start = performance.now();
22
+ const batch = [];
23
+ let next = {
24
+ done: true,
25
+ value: undefined,
26
+ };
27
+ if (lastPull && options.maxTime) {
28
+ const timeout = yield* timebox(options.maxTime, () => lastPull);
29
+ if (timeout.timeout) {
30
+ yield* lastPull.halt();
31
+ lastPull = undefined;
32
+ }
33
+ else {
34
+ next = timeout.value;
35
+ lastPull = undefined;
36
+ }
37
+ }
38
+ else {
39
+ next = yield* subscription.next();
41
40
  }
42
- else if (options.maxTime) {
43
- yield* sleep(options.maxTime);
41
+ // push the next value into the batch
42
+ while (!next.done) {
43
+ batch.push(next.value);
44
+ const now = performance.now();
45
+ if (options.maxSize && batch.length >= options.maxSize) {
46
+ return {
47
+ done: false,
48
+ value: batch,
49
+ };
50
+ }
51
+ else if (options.maxTime && start + options.maxTime <= now) {
52
+ return {
53
+ done: false,
54
+ value: batch,
55
+ };
56
+ }
57
+ else if (options.maxTime) {
58
+ const task = yield* spawn(() => subscription.next());
59
+ const timeout = yield* timebox(start + options.maxTime - performance.now(), () => task);
60
+ if (timeout.timeout) {
61
+ // produce the batch that we have, save task for next batch
62
+ lastPull = task;
63
+ return {
64
+ done: false,
65
+ value: batch,
66
+ };
67
+ }
68
+ else {
69
+ next = timeout.value;
70
+ }
71
+ }
72
+ else {
73
+ next = yield* subscription.next();
74
+ }
44
75
  }
45
- else if (options.maxSize) {
46
- yield* is(batch, (batch) => batch.length === options.maxSize);
76
+ // Stream is done, return any remaining batch
77
+ if (batch.length > 0) {
78
+ return {
79
+ done: false,
80
+ value: batch,
81
+ };
47
82
  }
48
- return { done: false, value: drain() };
83
+ return next;
49
84
  },
50
85
  };
51
86
  },
@@ -0,0 +1,27 @@
1
+ import type { Operation, Stream } from "effection";
2
+ /**
3
+ * Invoke a function for each item passing through the stream.
4
+ *
5
+ * @template T - The type of items in the stream
6
+ * @template TClose - The type of the close value returned when the stream ends
7
+ * @param fn - A function that processes each item from the stream.
8
+ * @param stream: A stream to process
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { forEach } from "./for-each.ts";
13
+ * import { createSignal } from "effection";
14
+ *
15
+ * // Process items from a stream
16
+ * const stream = createSignal<number, void>();
17
+ *
18
+ * yield* spawn(() => forEach(function*(item) {
19
+ * console.log(`Processing: ${item}`);
20
+ * }, stream));
21
+ *
22
+ * yield* stream.send(1);
23
+ * yield* stream.send(2);
24
+ * ```
25
+ */
26
+ export declare function forEach<T, TClose>(fn: (item: T) => Operation<void>, stream: Stream<T, TClose>): Operation<TClose>;
27
+ //# sourceMappingURL=for-each.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"for-each.d.ts","sourceRoot":"","sources":["../src/for-each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAiB,OAAO,CAAC,CAAC,EAAE,MAAM,EAChC,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,EAChC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GACxB,SAAS,CAAC,MAAM,CAAC,CAQnB"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Invoke a function for each item passing through the stream.
3
+ *
4
+ * @template T - The type of items in the stream
5
+ * @template TClose - The type of the close value returned when the stream ends
6
+ * @param fn - A function that processes each item from the stream.
7
+ * @param stream: A stream to process
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { forEach } from "./for-each.ts";
12
+ * import { createSignal } from "effection";
13
+ *
14
+ * // Process items from a stream
15
+ * const stream = createSignal<number, void>();
16
+ *
17
+ * yield* spawn(() => forEach(function*(item) {
18
+ * console.log(`Processing: ${item}`);
19
+ * }, stream));
20
+ *
21
+ * yield* stream.send(1);
22
+ * yield* stream.send(2);
23
+ * ```
24
+ */
25
+ export function* forEach(fn, stream) {
26
+ let subscription = yield* stream;
27
+ let next = yield* subscription.next();
28
+ while (!next.done) {
29
+ yield* fn(next.value);
30
+ next = yield* subscription.next();
31
+ }
32
+ return next.value;
33
+ }
package/esm/map.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../src/map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,CAAC,EACtB,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,GAC7B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAsB1D"}
1
+ {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../src/map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,CAAC,EACtB,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,GAC7B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAwB1D"}
package/esm/map.js CHANGED
@@ -15,9 +15,10 @@ export function map(fn) {
15
15
  if (next.done) {
16
16
  return next;
17
17
  }
18
+ const value = yield* fn(next.value);
18
19
  return {
19
20
  done: false,
20
- value: yield* fn(next.value),
21
+ value,
21
22
  };
22
23
  },
23
24
  };
package/esm/mod.d.ts CHANGED
@@ -3,4 +3,5 @@ export * from "./valve.js";
3
3
  export * from "./map.js";
4
4
  export * from "./filter.js";
5
5
  export * from "./tracker.js";
6
+ export * from "./for-each.js";
6
7
  //# sourceMappingURL=mod.d.ts.map
package/esm/mod.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC"}
package/esm/mod.js CHANGED
@@ -3,3 +3,4 @@ export * from "./valve.js";
3
3
  export * from "./map.js";
4
4
  export * from "./filter.js";
5
5
  export * from "./tracker.js";
6
+ export * from "./for-each.js";
@@ -1 +1 @@
1
- {"version":3,"file":"faucet.d.ts","sourceRoot":"","sources":["../../src/test-helpers/faucet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAGtE;;GAEG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC,CAAE,SAAQ,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC;IACjD;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC;;;OAGG;IACH,IAAI,CACF,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,GAC1D,SAAS,CAAC,IAAI,CAAC,CAAC;IACnB;;OAEG;IACH,IAAI,IAAI,IAAI,CAAC;IACb;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAiB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CA0B1E"}
1
+ {"version":3,"file":"faucet.d.ts","sourceRoot":"","sources":["../../src/test-helpers/faucet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAGvE;;GAEG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC,CAAE,SAAQ,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC;IACjD;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC;;;OAGG;IACH,IAAI,CACF,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,GAC1D,SAAS,CAAC,IAAI,CAAC,CAAC;IACnB;;OAEG;IACH,IAAI,IAAI,IAAI,CAAC;IACb;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAiB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CA0B1E"}
@@ -1,4 +1,4 @@
1
- import { createSignal } from "effection";
1
+ import { createChannel } from "effection";
2
2
  import { createBooleanSignal, is } from "@effectionx/signals";
3
3
  /**
4
4
  * Creates a stream that can be used to test the behavior of streams that use backpressure.
@@ -44,7 +44,7 @@ import { createBooleanSignal, is } from "@effectionx/signals";
44
44
  * @returns stream of items coming from the faucet
45
45
  */
46
46
  export function* useFaucet(options) {
47
- let signal = createSignal();
47
+ let signal = createChannel();
48
48
  let open = yield* createBooleanSignal(options.open);
49
49
  return {
50
50
  [Symbol.iterator]: signal[Symbol.iterator],
@@ -52,13 +52,13 @@ export function* useFaucet(options) {
52
52
  if (Array.isArray(items)) {
53
53
  for (let i of items) {
54
54
  yield* is(open, (open) => open);
55
- signal.send(i);
55
+ yield* signal.send(i);
56
56
  }
57
57
  }
58
58
  else {
59
59
  yield* items(function* (item) {
60
60
  yield* is(open, (open) => open);
61
- signal.send(item);
61
+ yield* signal.send(item);
62
62
  });
63
63
  }
64
64
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effectionx/stream-helpers",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "author": "engineering@frontside.com",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,8 +28,9 @@
28
28
  },
29
29
  "sideEffects": false,
30
30
  "dependencies": {
31
- "@effectionx/signals": "^0.1.0",
32
- "effection": "^3"
31
+ "@effectionx/signals": "0.3.0",
32
+ "@effectionx/timebox": "0.2.0",
33
+ "effection": "^3 || ^4.0.0-0"
33
34
  },
34
35
  "_generatedBy": "dnt@dev"
35
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAGlE,KAAK,iBAAiB,CAAC,CAAC,EAAE,IAAI,SAAS,MAAM,CAAC,GAAG,MAAM,CAAC,IACpD,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,GAC/B;KACC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;CACzE,CAAC,IAAI,CAAC,CAAC;AAEV,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,iBAAiB,CAAC,YAAY,CAAC,GACvC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CA4C/D"}
1
+ {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,MAAM,EAAa,MAAM,WAAW,CAAC;AAG1D,KAAK,iBAAiB,CAAC,CAAC,EAAE,IAAI,SAAS,MAAM,CAAC,GAAG,MAAM,CAAC,IACpD,IAAI,CACJ,CAAC,EACD,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CACvB,GACC;KACC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;CACzE,CAAC,IAAI,CAAC,CAAC;AAEV,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,iBAAiB,CAAC,YAAY,CAAC,GACvC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CA8E/D"}
package/script/batch.js CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.batch = batch;
4
4
  const effection_1 = require("effection");
5
- const signals_1 = require("@effectionx/signals");
5
+ const timebox_1 = require("@effectionx/timebox");
6
6
  /**
7
7
  * Creates batches of items from the source stream. The batches can be created either by
8
8
  * specifying a maximum time or a maximum size. If both are specified, the batch will be
@@ -17,38 +17,73 @@ function batch(options) {
17
17
  return function (stream) {
18
18
  return {
19
19
  *[Symbol.iterator]() {
20
- let batch = yield* (0, signals_1.createArraySignal)([]);
21
- yield* (0, effection_1.spawn)(function* () {
22
- for (let item of yield* (0, effection_1.each)(stream)) {
23
- batch.push(item);
24
- if (options.maxSize && batch.length >= options.maxSize) {
25
- // wait until it's drained
26
- yield* (0, signals_1.is)(batch, (batch) => batch.length === 0);
27
- }
28
- yield* effection_1.each.next();
29
- }
30
- });
31
- function drain() {
32
- let value = batch.valueOf();
33
- batch.set([]);
34
- return value;
35
- }
20
+ const subscription = yield* stream;
21
+ let lastPull;
36
22
  return {
37
23
  *next() {
38
- yield* (0, signals_1.is)(batch, (batch) => batch.length >= 1);
39
- if (options.maxTime && options.maxSize) {
40
- yield* (0, effection_1.race)([
41
- (0, signals_1.is)(batch, (batch) => batch.length === options.maxSize),
42
- (0, effection_1.sleep)(options.maxTime),
43
- ]);
24
+ let start = performance.now();
25
+ const batch = [];
26
+ let next = {
27
+ done: true,
28
+ value: undefined,
29
+ };
30
+ if (lastPull && options.maxTime) {
31
+ const timeout = yield* (0, timebox_1.timebox)(options.maxTime, () => lastPull);
32
+ if (timeout.timeout) {
33
+ yield* lastPull.halt();
34
+ lastPull = undefined;
35
+ }
36
+ else {
37
+ next = timeout.value;
38
+ lastPull = undefined;
39
+ }
40
+ }
41
+ else {
42
+ next = yield* subscription.next();
44
43
  }
45
- else if (options.maxTime) {
46
- yield* (0, effection_1.sleep)(options.maxTime);
44
+ // push the next value into the batch
45
+ while (!next.done) {
46
+ batch.push(next.value);
47
+ const now = performance.now();
48
+ if (options.maxSize && batch.length >= options.maxSize) {
49
+ return {
50
+ done: false,
51
+ value: batch,
52
+ };
53
+ }
54
+ else if (options.maxTime && start + options.maxTime <= now) {
55
+ return {
56
+ done: false,
57
+ value: batch,
58
+ };
59
+ }
60
+ else if (options.maxTime) {
61
+ const task = yield* (0, effection_1.spawn)(() => subscription.next());
62
+ const timeout = yield* (0, timebox_1.timebox)(start + options.maxTime - performance.now(), () => task);
63
+ if (timeout.timeout) {
64
+ // produce the batch that we have, save task for next batch
65
+ lastPull = task;
66
+ return {
67
+ done: false,
68
+ value: batch,
69
+ };
70
+ }
71
+ else {
72
+ next = timeout.value;
73
+ }
74
+ }
75
+ else {
76
+ next = yield* subscription.next();
77
+ }
47
78
  }
48
- else if (options.maxSize) {
49
- yield* (0, signals_1.is)(batch, (batch) => batch.length === options.maxSize);
79
+ // Stream is done, return any remaining batch
80
+ if (batch.length > 0) {
81
+ return {
82
+ done: false,
83
+ value: batch,
84
+ };
50
85
  }
51
- return { done: false, value: drain() };
86
+ return next;
52
87
  },
53
88
  };
54
89
  },
@@ -0,0 +1,27 @@
1
+ import type { Operation, Stream } from "effection";
2
+ /**
3
+ * Invoke a function for each item passing through the stream.
4
+ *
5
+ * @template T - The type of items in the stream
6
+ * @template TClose - The type of the close value returned when the stream ends
7
+ * @param fn - A function that processes each item from the stream.
8
+ * @param stream: A stream to process
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { forEach } from "./for-each.ts";
13
+ * import { createSignal } from "effection";
14
+ *
15
+ * // Process items from a stream
16
+ * const stream = createSignal<number, void>();
17
+ *
18
+ * yield* spawn(() => forEach(function*(item) {
19
+ * console.log(`Processing: ${item}`);
20
+ * }, stream));
21
+ *
22
+ * yield* stream.send(1);
23
+ * yield* stream.send(2);
24
+ * ```
25
+ */
26
+ export declare function forEach<T, TClose>(fn: (item: T) => Operation<void>, stream: Stream<T, TClose>): Operation<TClose>;
27
+ //# sourceMappingURL=for-each.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"for-each.d.ts","sourceRoot":"","sources":["../src/for-each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAiB,OAAO,CAAC,CAAC,EAAE,MAAM,EAChC,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,EAChC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GACxB,SAAS,CAAC,MAAM,CAAC,CAQnB"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.forEach = forEach;
4
+ /**
5
+ * Invoke a function for each item passing through the stream.
6
+ *
7
+ * @template T - The type of items in the stream
8
+ * @template TClose - The type of the close value returned when the stream ends
9
+ * @param fn - A function that processes each item from the stream.
10
+ * @param stream: A stream to process
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { forEach } from "./for-each.ts";
15
+ * import { createSignal } from "effection";
16
+ *
17
+ * // Process items from a stream
18
+ * const stream = createSignal<number, void>();
19
+ *
20
+ * yield* spawn(() => forEach(function*(item) {
21
+ * console.log(`Processing: ${item}`);
22
+ * }, stream));
23
+ *
24
+ * yield* stream.send(1);
25
+ * yield* stream.send(2);
26
+ * ```
27
+ */
28
+ function* forEach(fn, stream) {
29
+ let subscription = yield* stream;
30
+ let next = yield* subscription.next();
31
+ while (!next.done) {
32
+ yield* fn(next.value);
33
+ next = yield* subscription.next();
34
+ }
35
+ return next.value;
36
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../src/map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,CAAC,EACtB,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,GAC7B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAsB1D"}
1
+ {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../src/map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,CAAC,EACtB,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,GAC7B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAwB1D"}
package/script/map.js CHANGED
@@ -18,9 +18,10 @@ function map(fn) {
18
18
  if (next.done) {
19
19
  return next;
20
20
  }
21
+ const value = yield* fn(next.value);
21
22
  return {
22
23
  done: false,
23
- value: yield* fn(next.value),
24
+ value,
24
25
  };
25
26
  },
26
27
  };
package/script/mod.d.ts CHANGED
@@ -3,4 +3,5 @@ export * from "./valve.js";
3
3
  export * from "./map.js";
4
4
  export * from "./filter.js";
5
5
  export * from "./tracker.js";
6
+ export * from "./for-each.js";
6
7
  //# sourceMappingURL=mod.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC"}
package/script/mod.js CHANGED
@@ -19,3 +19,4 @@ __exportStar(require("./valve.js"), exports);
19
19
  __exportStar(require("./map.js"), exports);
20
20
  __exportStar(require("./filter.js"), exports);
21
21
  __exportStar(require("./tracker.js"), exports);
22
+ __exportStar(require("./for-each.js"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"faucet.d.ts","sourceRoot":"","sources":["../../src/test-helpers/faucet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAGtE;;GAEG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC,CAAE,SAAQ,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC;IACjD;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC;;;OAGG;IACH,IAAI,CACF,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,GAC1D,SAAS,CAAC,IAAI,CAAC,CAAC;IACnB;;OAEG;IACH,IAAI,IAAI,IAAI,CAAC;IACb;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAiB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CA0B1E"}
1
+ {"version":3,"file":"faucet.d.ts","sourceRoot":"","sources":["../../src/test-helpers/faucet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAGvE;;GAEG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC,CAAE,SAAQ,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC;IACjD;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC;;;OAGG;IACH,IAAI,CACF,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,GAC1D,SAAS,CAAC,IAAI,CAAC,CAAC;IACnB;;OAEG;IACH,IAAI,IAAI,IAAI,CAAC;IACb;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAiB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CA0B1E"}
@@ -47,7 +47,7 @@ const signals_1 = require("@effectionx/signals");
47
47
  * @returns stream of items coming from the faucet
48
48
  */
49
49
  function* useFaucet(options) {
50
- let signal = (0, effection_1.createSignal)();
50
+ let signal = (0, effection_1.createChannel)();
51
51
  let open = yield* (0, signals_1.createBooleanSignal)(options.open);
52
52
  return {
53
53
  [Symbol.iterator]: signal[Symbol.iterator],
@@ -55,13 +55,13 @@ function* useFaucet(options) {
55
55
  if (Array.isArray(items)) {
56
56
  for (let i of items) {
57
57
  yield* (0, signals_1.is)(open, (open) => open);
58
- signal.send(i);
58
+ yield* signal.send(i);
59
59
  }
60
60
  }
61
61
  else {
62
62
  yield* items(function* (item) {
63
63
  yield* (0, signals_1.is)(open, (open) => open);
64
- signal.send(item);
64
+ yield* signal.send(item);
65
65
  });
66
66
  }
67
67
  },