@effectionx/stream-helpers 0.5.1 → 0.6.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/batch.test.ts +112 -0
- package/batch.ts +107 -0
- package/dist/batch.d.ts.map +1 -0
- package/dist/batch.js +86 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.js +43 -0
- package/dist/for-each.d.ts.map +1 -0
- package/dist/lines.d.ts +29 -0
- package/dist/lines.d.ts.map +1 -0
- package/dist/lines.js +61 -0
- package/dist/map.d.ts.map +1 -0
- package/dist/map.js +26 -0
- package/dist/mod.d.ts +9 -0
- package/dist/mod.d.ts.map +1 -0
- package/{esm → dist}/mod.js +1 -0
- package/dist/subject.d.ts.map +1 -0
- package/{esm → dist}/subject.js +3 -1
- package/dist/test-helpers/faucet.d.ts.map +1 -0
- package/dist/test-helpers.d.ts +2 -0
- package/dist/test-helpers.d.ts.map +1 -0
- package/dist/tracker.d.ts.map +1 -0
- package/{esm → dist}/tracker.js +12 -14
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/valve.d.ts.map +1 -0
- package/dist/valve.js +46 -0
- package/filter.test.ts +47 -0
- package/filter.ts +48 -0
- package/for-each.test.ts +42 -0
- package/{script/for-each.d.ts → for-each.ts} +13 -2
- package/lines.ts +74 -0
- package/map.test.ts +50 -0
- package/map.ts +33 -0
- package/mod.ts +8 -0
- package/package.json +29 -23
- package/subject.test.ts +93 -0
- package/{script/subject.js → subject.ts} +31 -25
- package/test-helpers/faucet.test.ts +120 -0
- package/{script/test-helpers/faucet.d.ts → test-helpers/faucet.ts} +56 -25
- package/test-helpers.ts +1 -0
- package/tracker.test.ts +109 -0
- package/tracker.ts +57 -0
- package/tsconfig.json +20 -0
- package/valve.test.ts +52 -0
- package/valve.ts +73 -0
- package/esm/batch.d.ts.map +0 -1
- package/esm/batch.js +0 -89
- package/esm/filter.d.ts.map +0 -1
- package/esm/filter.js +0 -45
- package/esm/for-each.d.ts.map +0 -1
- package/esm/map.d.ts.map +0 -1
- package/esm/map.js +0 -28
- package/esm/mod.d.ts +0 -8
- package/esm/mod.d.ts.map +0 -1
- package/esm/package.json +0 -3
- package/esm/subject.d.ts.map +0 -1
- package/esm/test-helpers/faucet.d.ts.map +0 -1
- package/esm/test-helpers.d.ts +0 -2
- package/esm/test-helpers.d.ts.map +0 -1
- package/esm/tracker.d.ts.map +0 -1
- package/esm/valve.d.ts.map +0 -1
- package/esm/valve.js +0 -48
- package/script/batch.d.ts +0 -21
- package/script/batch.d.ts.map +0 -1
- package/script/batch.js +0 -92
- package/script/filter.d.ts +0 -23
- package/script/filter.d.ts.map +0 -1
- package/script/filter.js +0 -48
- package/script/for-each.d.ts.map +0 -1
- package/script/for-each.js +0 -36
- package/script/map.d.ts +0 -9
- package/script/map.d.ts.map +0 -1
- package/script/map.js +0 -31
- package/script/mod.d.ts +0 -8
- package/script/mod.d.ts.map +0 -1
- package/script/mod.js +0 -23
- package/script/package.json +0 -3
- package/script/subject.d.ts +0 -38
- package/script/subject.d.ts.map +0 -1
- package/script/test-helpers/faucet.d.ts.map +0 -1
- package/script/test-helpers/faucet.js +0 -75
- package/script/test-helpers.d.ts +0 -2
- package/script/test-helpers.d.ts.map +0 -1
- package/script/test-helpers.js +0 -17
- package/script/tracker.d.ts +0 -24
- package/script/tracker.d.ts.map +0 -1
- package/script/tracker.js +0 -42
- package/script/valve.d.ts +0 -37
- package/script/valve.d.ts.map +0 -1
- package/script/valve.js +0 -51
- /package/{esm → dist}/batch.d.ts +0 -0
- /package/{esm → dist}/filter.d.ts +0 -0
- /package/{esm → dist}/for-each.d.ts +0 -0
- /package/{esm → dist}/for-each.js +0 -0
- /package/{esm → dist}/map.d.ts +0 -0
- /package/{esm → dist}/subject.d.ts +0 -0
- /package/{esm → dist}/test-helpers/faucet.d.ts +0 -0
- /package/{esm → dist}/test-helpers/faucet.js +0 -0
- /package/{esm → dist}/test-helpers.js +0 -0
- /package/{esm → dist}/tracker.d.ts +0 -0
- /package/{esm → dist}/valve.d.ts +0 -0
package/batch.test.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, it } from "@effectionx/bdd";
|
|
2
|
+
import { createArraySignal, is } from "@effectionx/signals";
|
|
3
|
+
import { expect } from "expect";
|
|
4
|
+
import { createChannel, sleep, spawn } from "effection";
|
|
5
|
+
|
|
6
|
+
import { batch } from "./batch.ts";
|
|
7
|
+
import { forEach } from "./for-each.ts";
|
|
8
|
+
import { useFaucet } from "./test-helpers/faucet.ts";
|
|
9
|
+
|
|
10
|
+
describe("batch", () => {
|
|
11
|
+
it("creates a batch when maxTime expires", function* () {
|
|
12
|
+
const source = createChannel<number, never>();
|
|
13
|
+
const stream = batch({ maxTime: 50 })(source);
|
|
14
|
+
|
|
15
|
+
const subscription = yield* stream;
|
|
16
|
+
|
|
17
|
+
let next = yield* spawn(() => subscription.next());
|
|
18
|
+
|
|
19
|
+
yield* source.send(1);
|
|
20
|
+
yield* source.send(2);
|
|
21
|
+
yield* source.send(3);
|
|
22
|
+
|
|
23
|
+
expect((yield* next).value).toEqual([1, 2, 3]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("creates a batch by maxSize when maxTime is not set", function* () {
|
|
27
|
+
const faucet = yield* useFaucet<number>({ open: true });
|
|
28
|
+
const stream = batch({ maxSize: 3 })(faucet);
|
|
29
|
+
|
|
30
|
+
const subscription = yield* stream;
|
|
31
|
+
|
|
32
|
+
yield* faucet.pour([1, 2, 3, 4, 5, 6]);
|
|
33
|
+
|
|
34
|
+
let next = yield* subscription.next();
|
|
35
|
+
expect(next.value).toEqual([1, 2, 3]);
|
|
36
|
+
|
|
37
|
+
next = yield* subscription.next();
|
|
38
|
+
expect(next.value).toEqual([4, 5, 6]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("creates a batch within maxTime when maxSize is never reached", function* () {
|
|
42
|
+
const faucet = yield* useFaucet<number>({ open: true });
|
|
43
|
+
const stream = batch({ maxSize: 8, maxTime: 50 })(faucet);
|
|
44
|
+
|
|
45
|
+
const batches = yield* createArraySignal<readonly number[]>([]);
|
|
46
|
+
const windows: number[] = [];
|
|
47
|
+
|
|
48
|
+
let last = performance.now();
|
|
49
|
+
|
|
50
|
+
yield* spawn(() =>
|
|
51
|
+
forEach<readonly number[], void>(function* (batch) {
|
|
52
|
+
const now = performance.now();
|
|
53
|
+
windows.push(now - last);
|
|
54
|
+
last = now;
|
|
55
|
+
|
|
56
|
+
batches.push(batch);
|
|
57
|
+
}, stream),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
yield* sleep(1);
|
|
61
|
+
|
|
62
|
+
yield* faucet.pour(function* (send) {
|
|
63
|
+
for (let i = 1; i <= 10; i++) {
|
|
64
|
+
yield* send(i);
|
|
65
|
+
yield* sleep(20);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
yield* is(batches, (list) => list.flat().length >= 10);
|
|
70
|
+
|
|
71
|
+
expect(windows.length).toBeGreaterThanOrEqual(3);
|
|
72
|
+
|
|
73
|
+
const avg = average(windows);
|
|
74
|
+
const percentDiff = Math.abs((avg - 50) / 50) * 100;
|
|
75
|
+
expect(percentDiff).toBeLessThanOrEqual(30);
|
|
76
|
+
|
|
77
|
+
expect(batches.valueOf().flat()).toHaveLength(10);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("creates a batch within maxSize in maxTime window", function* () {
|
|
81
|
+
const faucet = yield* useFaucet<number>({ open: true });
|
|
82
|
+
const stream = batch({ maxSize: 5, maxTime: 3 })(faucet);
|
|
83
|
+
|
|
84
|
+
const batches = yield* createArraySignal<readonly number[]>([]);
|
|
85
|
+
|
|
86
|
+
yield* spawn(() =>
|
|
87
|
+
forEach<readonly number[], void>(function* (batch) {
|
|
88
|
+
batches.push(batch);
|
|
89
|
+
}, stream),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
yield* sleep(1);
|
|
93
|
+
|
|
94
|
+
yield* faucet.pour([1, 2, 3, 4, 5, 6]);
|
|
95
|
+
|
|
96
|
+
yield* is(batches, (batches) => batches.flat().length >= 6);
|
|
97
|
+
|
|
98
|
+
expect(batches.length).toBeGreaterThan(1);
|
|
99
|
+
expect(batches.valueOf().every((batch) => batch.length <= 5)).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
function average(arr: number[]) {
|
|
104
|
+
if (arr.length === 0) {
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
const sum = arr.reduce(
|
|
108
|
+
(accumulator, currentValue) => accumulator + currentValue,
|
|
109
|
+
0,
|
|
110
|
+
);
|
|
111
|
+
return sum / arr.length;
|
|
112
|
+
}
|
package/batch.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { timebox } from "@effectionx/timebox";
|
|
2
|
+
import { type Stream, type Task, spawn } from "effection";
|
|
3
|
+
|
|
4
|
+
type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<
|
|
5
|
+
T,
|
|
6
|
+
Exclude<keyof T, Keys>
|
|
7
|
+
> &
|
|
8
|
+
{
|
|
9
|
+
[K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>;
|
|
10
|
+
}[Keys];
|
|
11
|
+
|
|
12
|
+
export interface BatchOptions {
|
|
13
|
+
readonly maxTime: number;
|
|
14
|
+
readonly maxSize: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates batches of items from the source stream. The batches can be created either by
|
|
19
|
+
* specifying a maximum time or a maximum size. If both are specified, the batch will be
|
|
20
|
+
* created when either condition is met.
|
|
21
|
+
*
|
|
22
|
+
* @param options - The options for the batch.
|
|
23
|
+
* @param options.maxTime - The maximum time to wait for a batch.
|
|
24
|
+
* @param options.maxSize - The maximum size of a batch.
|
|
25
|
+
* @returns A stream of arrays of items from the source stream.
|
|
26
|
+
*/
|
|
27
|
+
export function batch(
|
|
28
|
+
options: RequireAtLeastOne<BatchOptions>,
|
|
29
|
+
): <T>(stream: Stream<T, never>) => Stream<Readonly<T[]>, never> {
|
|
30
|
+
return <T>(stream: Stream<T, never>): Stream<Readonly<T[]>, never> => ({
|
|
31
|
+
*[Symbol.iterator]() {
|
|
32
|
+
const subscription = yield* stream;
|
|
33
|
+
let lastPull: Task<IteratorResult<T, never>> | undefined;
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
*next() {
|
|
37
|
+
let start: DOMHighResTimeStamp = performance.now();
|
|
38
|
+
const batch: T[] = [];
|
|
39
|
+
let next: IteratorResult<T, never> = {
|
|
40
|
+
done: true as const,
|
|
41
|
+
value: undefined as never,
|
|
42
|
+
};
|
|
43
|
+
if (lastPull && options.maxTime) {
|
|
44
|
+
// biome-ignore lint/style/noNonNullAssertion: lastPull checked above
|
|
45
|
+
const timeout = yield* timebox(options.maxTime, () => lastPull!);
|
|
46
|
+
if (timeout.timeout) {
|
|
47
|
+
yield* lastPull.halt();
|
|
48
|
+
lastPull = undefined;
|
|
49
|
+
} else {
|
|
50
|
+
next = timeout.value;
|
|
51
|
+
lastPull = undefined;
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
next = yield* subscription.next();
|
|
55
|
+
}
|
|
56
|
+
// push the next value into the batch
|
|
57
|
+
while (!next.done) {
|
|
58
|
+
batch.push(next.value);
|
|
59
|
+
const now = performance.now();
|
|
60
|
+
if (options.maxSize && batch.length >= options.maxSize) {
|
|
61
|
+
return {
|
|
62
|
+
done: false as const,
|
|
63
|
+
value: batch,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (options.maxTime && start + options.maxTime <= now) {
|
|
67
|
+
return {
|
|
68
|
+
done: false as const,
|
|
69
|
+
value: batch,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (options.maxTime) {
|
|
73
|
+
const task = yield* spawn(() => subscription.next());
|
|
74
|
+
|
|
75
|
+
const timeout = yield* timebox(
|
|
76
|
+
start + options.maxTime - performance.now(),
|
|
77
|
+
() => task,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (timeout.timeout) {
|
|
81
|
+
// produce the batch that we have, save task for next batch
|
|
82
|
+
lastPull = task;
|
|
83
|
+
return {
|
|
84
|
+
done: false as const,
|
|
85
|
+
value: batch,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
next = timeout.value;
|
|
89
|
+
} else {
|
|
90
|
+
next = yield* subscription.next();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Stream is done, return any remaining batch
|
|
95
|
+
if (batch.length > 0) {
|
|
96
|
+
return {
|
|
97
|
+
done: false as const,
|
|
98
|
+
value: batch,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return next;
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../batch.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,WAAW,CAAC;AAE1D,KAAK,iBAAiB,CAAC,CAAC,EAAE,IAAI,SAAS,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,CAC9D,CAAC,EACD,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CACvB,GACC;KACG,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/dist/batch.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { timebox } from "@effectionx/timebox";
|
|
2
|
+
import { spawn } from "effection";
|
|
3
|
+
/**
|
|
4
|
+
* Creates batches of items from the source stream. The batches can be created either by
|
|
5
|
+
* specifying a maximum time or a maximum size. If both are specified, the batch will be
|
|
6
|
+
* created when either condition is met.
|
|
7
|
+
*
|
|
8
|
+
* @param options - The options for the batch.
|
|
9
|
+
* @param options.maxTime - The maximum time to wait for a batch.
|
|
10
|
+
* @param options.maxSize - The maximum size of a batch.
|
|
11
|
+
* @returns A stream of arrays of items from the source stream.
|
|
12
|
+
*/
|
|
13
|
+
export function batch(options) {
|
|
14
|
+
return (stream) => ({
|
|
15
|
+
*[Symbol.iterator]() {
|
|
16
|
+
const subscription = yield* stream;
|
|
17
|
+
let lastPull;
|
|
18
|
+
return {
|
|
19
|
+
*next() {
|
|
20
|
+
let start = performance.now();
|
|
21
|
+
const batch = [];
|
|
22
|
+
let next = {
|
|
23
|
+
done: true,
|
|
24
|
+
value: undefined,
|
|
25
|
+
};
|
|
26
|
+
if (lastPull && options.maxTime) {
|
|
27
|
+
// biome-ignore lint/style/noNonNullAssertion: lastPull checked above
|
|
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();
|
|
40
|
+
}
|
|
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
|
+
if (options.maxTime && start + options.maxTime <= now) {
|
|
52
|
+
return {
|
|
53
|
+
done: false,
|
|
54
|
+
value: batch,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
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
|
+
next = timeout.value;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
next = yield* subscription.next();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Stream is done, return any remaining batch
|
|
75
|
+
if (batch.length > 0) {
|
|
76
|
+
return {
|
|
77
|
+
done: false,
|
|
78
|
+
value: batch,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return next;
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,CAAC,CAAC,EACtB,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,SAAS,CAAC,OAAO,CAAC,GAC1C,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAuBvD"}
|
package/dist/filter.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filters items from the stream based on a predicate function.
|
|
3
|
+
*
|
|
4
|
+
* @param predicate - The function to test each item
|
|
5
|
+
* @returns A stream transformer that only emits items that pass the predicate
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { filter } from "@effectionx/stream-helpers";
|
|
10
|
+
* import { run, each } from "effection";
|
|
11
|
+
*
|
|
12
|
+
* await run(function* () {
|
|
13
|
+
* const stream = filter((x: number) => x > 5)(sourceStream);
|
|
14
|
+
*
|
|
15
|
+
* for (const value of yield* each(stream)) {
|
|
16
|
+
* console.log(value); // Only values > 5
|
|
17
|
+
* }
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function filter(predicate) {
|
|
22
|
+
return (stream) => ({
|
|
23
|
+
*[Symbol.iterator]() {
|
|
24
|
+
const subscription = yield* stream;
|
|
25
|
+
return {
|
|
26
|
+
*next() {
|
|
27
|
+
while (true) {
|
|
28
|
+
const next = yield* subscription.next();
|
|
29
|
+
if (next.done) {
|
|
30
|
+
return next;
|
|
31
|
+
}
|
|
32
|
+
if (yield* predicate(next.value)) {
|
|
33
|
+
return {
|
|
34
|
+
done: false,
|
|
35
|
+
value: next.value,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"for-each.d.ts","sourceRoot":"","sources":["../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"}
|
package/dist/lines.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Stream } from "effection";
|
|
2
|
+
/**
|
|
3
|
+
* Represents the close value of a lines stream, containing any
|
|
4
|
+
* remaining content that didn't end with a newline.
|
|
5
|
+
*/
|
|
6
|
+
export interface Remainder<T> {
|
|
7
|
+
remainder: string;
|
|
8
|
+
result: T;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Stream helper that transforms a stream of binary chunks into a stream of lines.
|
|
12
|
+
*
|
|
13
|
+
* Lines are split on newline characters (`\n`). The final line (content after
|
|
14
|
+
* the last newline) is returned as the `remainder` in the close value.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { lines } from "@effectionx/stream-helpers";
|
|
19
|
+
* import { pipe, each } from "effection";
|
|
20
|
+
*
|
|
21
|
+
* const lineStream = pipe(process.stdout, lines());
|
|
22
|
+
* for (const line of yield* each(lineStream)) {
|
|
23
|
+
* console.log(line);
|
|
24
|
+
* yield* each.next();
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function lines(): <T extends Uint8Array, TReturn>(stream: Stream<T, TReturn>) => Stream<string, Remainder<TReturn>>;
|
|
29
|
+
//# sourceMappingURL=lines.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lines.d.ts","sourceRoot":"","sources":["../lines.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAExC;;;GAGG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,CAAC,CAAC;CACX;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,KAAK,IAAI,CAAC,CAAC,SAAS,UAAU,EAAE,OAAO,EACrD,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,KACvB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC,CA0CtC"}
|
package/dist/lines.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream helper that transforms a stream of binary chunks into a stream of lines.
|
|
3
|
+
*
|
|
4
|
+
* Lines are split on newline characters (`\n`). The final line (content after
|
|
5
|
+
* the last newline) is returned as the `remainder` in the close value.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { lines } from "@effectionx/stream-helpers";
|
|
10
|
+
* import { pipe, each } from "effection";
|
|
11
|
+
*
|
|
12
|
+
* const lineStream = pipe(process.stdout, lines());
|
|
13
|
+
* for (const line of yield* each(lineStream)) {
|
|
14
|
+
* console.log(line);
|
|
15
|
+
* yield* each.next();
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function lines() {
|
|
20
|
+
const decoder = new TextDecoder();
|
|
21
|
+
return (stream) => ({
|
|
22
|
+
*[Symbol.iterator]() {
|
|
23
|
+
const subscription = yield* stream;
|
|
24
|
+
const buffer = [];
|
|
25
|
+
let remainder = "";
|
|
26
|
+
return {
|
|
27
|
+
*next() {
|
|
28
|
+
while (buffer.length === 0) {
|
|
29
|
+
const next = yield* subscription.next();
|
|
30
|
+
if (next.done) {
|
|
31
|
+
return {
|
|
32
|
+
done: true,
|
|
33
|
+
value: {
|
|
34
|
+
remainder,
|
|
35
|
+
result: next.value,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const current = remainder + decoder.decode(next.value);
|
|
40
|
+
const lines = current.split("\n");
|
|
41
|
+
if (lines.length > 0) {
|
|
42
|
+
buffer.push(...lines.slice(0, -1));
|
|
43
|
+
remainder = lines.at(-1) ?? "";
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
remainder = current;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const value = buffer.shift();
|
|
50
|
+
if (value === undefined) {
|
|
51
|
+
throw new Error("Unexpected empty buffer");
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
done: false,
|
|
55
|
+
value,
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../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"}
|
package/dist/map.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transforms each item in the stream using the provided function.
|
|
3
|
+
*
|
|
4
|
+
* @param fn - The function to transform each item
|
|
5
|
+
* @returns A stream transformer that applies the function to each item
|
|
6
|
+
*/
|
|
7
|
+
export function map(fn) {
|
|
8
|
+
return (stream) => ({
|
|
9
|
+
*[Symbol.iterator]() {
|
|
10
|
+
const subscription = yield* stream;
|
|
11
|
+
return {
|
|
12
|
+
*next() {
|
|
13
|
+
const next = yield* subscription.next();
|
|
14
|
+
if (next.done) {
|
|
15
|
+
return next;
|
|
16
|
+
}
|
|
17
|
+
const value = yield* fn(next.value);
|
|
18
|
+
return {
|
|
19
|
+
done: false,
|
|
20
|
+
value,
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
package/dist/mod.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./batch.ts";
|
|
2
|
+
export * from "./valve.ts";
|
|
3
|
+
export * from "./map.ts";
|
|
4
|
+
export * from "./filter.ts";
|
|
5
|
+
export * from "./tracker.ts";
|
|
6
|
+
export * from "./for-each.ts";
|
|
7
|
+
export * from "./subject.ts";
|
|
8
|
+
export * from "./lines.ts";
|
|
9
|
+
//# sourceMappingURL=mod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../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;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
|
package/{esm → dist}/mod.js
RENAMED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subject.d.ts","sourceRoot":"","sources":["../subject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAgB,MAAM,WAAW,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,aAAa,CAAC,CAAC,KAAK,CAAC,MAAM,EACzC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KACtB,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CA2BrB"}
|
package/{esm → dist}/subject.js
RENAMED
|
@@ -42,12 +42,14 @@ export function createSubject() {
|
|
|
42
42
|
? {
|
|
43
43
|
*next() {
|
|
44
44
|
iterator = upstream;
|
|
45
|
+
// biome-ignore lint/style/noNonNullAssertion: current checked in ternary condition
|
|
45
46
|
return current;
|
|
46
47
|
},
|
|
47
48
|
}
|
|
48
49
|
: {
|
|
49
50
|
*next() {
|
|
50
|
-
|
|
51
|
+
current = yield* upstream.next();
|
|
52
|
+
return current;
|
|
51
53
|
},
|
|
52
54
|
};
|
|
53
55
|
return {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faucet.d.ts","sourceRoot":"","sources":["../../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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-helpers.d.ts","sourceRoot":"","sources":["../test-helpers.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../tracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAY,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAGlE,MAAM,WAAW,OAAQ,SAAQ,SAAS,CAAC,IAAI,CAAC;IAC9C;;;;OAIG;IACH,WAAW,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACjE;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,SAAS,CAAC,OAAO,CAAC,CA+BlD"}
|
package/{esm → dist}/tracker.js
RENAMED
|
@@ -13,20 +13,18 @@ export function createTracker() {
|
|
|
13
13
|
yield* is(tracked, (set) => set.size === 0);
|
|
14
14
|
},
|
|
15
15
|
passthrough() {
|
|
16
|
-
return
|
|
17
|
-
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*next()
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
29
|
-
};
|
|
16
|
+
return (stream) => ({
|
|
17
|
+
*[Symbol.iterator]() {
|
|
18
|
+
const subscription = yield* stream;
|
|
19
|
+
return {
|
|
20
|
+
*next() {
|
|
21
|
+
const next = yield* subscription.next();
|
|
22
|
+
tracked.add(next.value);
|
|
23
|
+
return next;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
});
|
|
30
28
|
},
|
|
31
29
|
markOne(item) {
|
|
32
30
|
tracked.delete(item);
|