evnty 5.0.0 → 5.1.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 +28 -28
- package/build/async.cjs +101 -0
- package/build/async.cjs.map +1 -0
- package/build/async.d.ts +37 -0
- package/build/async.js +83 -0
- package/build/async.js.map +1 -0
- package/build/broadcast.cjs +205 -0
- package/build/broadcast.cjs.map +1 -0
- package/build/broadcast.d.ts +164 -0
- package/build/broadcast.js +184 -0
- package/build/broadcast.js.map +1 -0
- package/build/dispatch-result.cjs +154 -0
- package/build/dispatch-result.cjs.map +1 -0
- package/build/dispatch-result.d.ts +49 -0
- package/build/dispatch-result.js +127 -0
- package/build/dispatch-result.js.map +1 -0
- package/build/event.cjs +92 -127
- package/build/event.cjs.map +1 -1
- package/build/event.d.ts +92 -167
- package/build/event.js +90 -122
- package/build/event.js.map +1 -1
- package/build/index.cjs +3 -1
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +3 -1
- package/build/index.js +3 -1
- package/build/index.js.map +1 -1
- package/build/iterator.cjs +578 -91
- package/build/iterator.cjs.map +1 -1
- package/build/iterator.d.ts +178 -7
- package/build/iterator.js +579 -92
- package/build/iterator.js.map +1 -1
- package/build/listener-registry.cjs +114 -0
- package/build/listener-registry.cjs.map +1 -0
- package/build/listener-registry.d.ts +54 -0
- package/build/listener-registry.js +104 -0
- package/build/listener-registry.js.map +1 -0
- package/build/ring-buffer.cjs +171 -0
- package/build/ring-buffer.cjs.map +1 -0
- package/build/ring-buffer.d.ts +80 -0
- package/build/ring-buffer.js +161 -0
- package/build/ring-buffer.js.map +1 -0
- package/build/sequence.cjs +34 -35
- package/build/sequence.cjs.map +1 -1
- package/build/sequence.d.ts +38 -24
- package/build/sequence.js +34 -35
- package/build/sequence.js.map +1 -1
- package/build/signal.cjs +26 -35
- package/build/signal.cjs.map +1 -1
- package/build/signal.d.ts +36 -39
- package/build/signal.js +26 -35
- package/build/signal.js.map +1 -1
- package/build/types.cjs +0 -11
- package/build/types.cjs.map +1 -1
- package/build/types.d.ts +86 -9
- package/build/types.js +1 -5
- package/build/types.js.map +1 -1
- package/build/utils.cjs +202 -22
- package/build/utils.cjs.map +1 -1
- package/build/utils.d.ts +85 -26
- package/build/utils.js +181 -22
- package/build/utils.js.map +1 -1
- package/package.json +27 -25
- package/src/__tests__/example.js +19 -24
- package/src/index.ts +3 -1
- package/build/callable.cjs +0 -72
- package/build/callable.cjs.map +0 -1
- package/build/callable.d.ts +0 -34
- package/build/callable.js +0 -51
- package/build/callable.js.map +0 -1
package/build/sequence.js
CHANGED
|
@@ -1,58 +1,57 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { CallableAsyncIterator } from "./callable.js";
|
|
1
|
+
import { Async } from "./async.js";
|
|
3
2
|
import { Signal } from "./signal.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
queue;
|
|
7
|
-
nextSignal;
|
|
8
|
-
sendSignal;
|
|
3
|
+
import { RingBuffer } from "./ring-buffer.js";
|
|
4
|
+
export class Sequence extends Async {
|
|
5
|
+
#queue;
|
|
6
|
+
#nextSignal;
|
|
7
|
+
#sendSignal;
|
|
9
8
|
[Symbol.toStringTag] = 'Sequence';
|
|
10
9
|
static merge(target, ...sequences) {
|
|
11
10
|
for (const source of sequences){
|
|
12
11
|
queueMicrotask(async ()=>{
|
|
13
|
-
try {
|
|
12
|
+
if (!target.disposed) try {
|
|
13
|
+
const sink = target.sink;
|
|
14
14
|
for await (const value of source){
|
|
15
|
-
|
|
15
|
+
if (!sink(value)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
16
18
|
}
|
|
17
19
|
} catch {}
|
|
18
20
|
});
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
constructor(abortSignal){
|
|
22
|
-
super(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
} else {
|
|
27
|
-
this.queue.push(value);
|
|
28
|
-
this.nextSignal(true);
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
}), this.abortSignal = abortSignal;
|
|
32
|
-
this.queue = new RingBuffer();
|
|
33
|
-
this.nextSignal = new Signal(this.abortSignal);
|
|
34
|
-
this.sendSignal = new Signal(this.abortSignal);
|
|
35
|
-
this.abortSignal?.addEventListener('abort', ()=>this.nextSignal(false), {
|
|
36
|
-
once: true
|
|
37
|
-
});
|
|
24
|
+
super(abortSignal);
|
|
25
|
+
this.#queue = new RingBuffer();
|
|
26
|
+
this.#nextSignal = new Signal(abortSignal);
|
|
27
|
+
this.#sendSignal = new Signal(abortSignal);
|
|
38
28
|
}
|
|
39
29
|
get size() {
|
|
40
|
-
return this
|
|
30
|
+
return this.#queue.length;
|
|
41
31
|
}
|
|
42
32
|
async reserve(capacity) {
|
|
43
|
-
while(this
|
|
44
|
-
await this
|
|
33
|
+
while(this.#queue.length > capacity){
|
|
34
|
+
await this.#sendSignal;
|
|
45
35
|
}
|
|
46
36
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
37
|
+
emit(value) {
|
|
38
|
+
const ok = !this.disposed;
|
|
39
|
+
if (ok) {
|
|
40
|
+
this.#queue.push(value);
|
|
50
41
|
}
|
|
51
|
-
this.
|
|
52
|
-
return
|
|
42
|
+
this.#nextSignal.emit();
|
|
43
|
+
return ok;
|
|
53
44
|
}
|
|
54
|
-
|
|
55
|
-
this.
|
|
45
|
+
async receive() {
|
|
46
|
+
while(!this.#queue.length){
|
|
47
|
+
await this.#nextSignal;
|
|
48
|
+
}
|
|
49
|
+
this.#sendSignal.emit();
|
|
50
|
+
return this.#queue.shift();
|
|
51
|
+
}
|
|
52
|
+
dispose() {
|
|
53
|
+
this.#sendSignal[Symbol.dispose]();
|
|
54
|
+
this.#nextSignal[Symbol.dispose]();
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
|
package/build/sequence.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sequence.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../src/sequence.ts"],"sourcesContent":["import { Async } from './async.js';\nimport { Signal } from './signal.js';\nimport { RingBuffer } from './ring-buffer.js';\n\n/**\n * A sequence is a FIFO (First-In-First-Out) queue for async consumption.\n * Designed for single consumer with multiple producers pattern.\n * Values are queued and consumed in order, with backpressure support.\n * Respects an optional AbortSignal: emit() returns false when aborted; waits reject.\n *\n * Key characteristics:\n * - Single consumer - values are consumed once, in order\n * - Multiple producers can push values concurrently\n * - FIFO ordering - first value in is first value out\n * - Backpressure control via reserve() method\n * - Async iteration support for continuous consumption\n *\n * @template T The type of values in the sequence.\n *\n * @example\n * ```typescript\n * // Create a sequence for processing tasks\n * const tasks = new Sequence<string>();\n *\n * // Producer: Add tasks to the queue\n * tasks.emit('task1');\n * tasks.emit('task2');\n * tasks.emit('task3');\n *\n * // Consumer: Process tasks in order\n * const task1 = await tasks.receive(); // 'task1'\n * const task2 = await tasks.receive(); // 'task2'\n * const task3 = await tasks.receive(); // 'task3'\n * ```\n */\nexport class Sequence<T> extends Async<T, boolean> {\n #queue: RingBuffer<T>;\n #nextSignal: Signal<void>;\n #sendSignal: Signal<void>;\n\n readonly [Symbol.toStringTag] = 'Sequence';\n\n /**\n * Merges multiple source sequences into a target sequence.\n * Values from all sources are forwarded to the target sequence.\n * Each source is consumed independently and concurrently.\n *\n * @param target The sequence that will receive values from all sources\n * @param sequences The source sequences to merge from\n *\n * @example\n * ```typescript\n * // Create target and source sequences\n * const target = new Sequence<number>();\n * const source1 = new Sequence<number>();\n * const source2 = new Sequence<number>();\n *\n * // Merge sources into target\n * Sequence.merge(target, source1, source2);\n *\n * // Values from both sources appear in target\n * source1.emit(1);\n * source2.emit(2);\n * source1.emit(3);\n *\n * // Consumer gets values as they arrive\n * await target.receive(); // Could be 1, 2, or 3 depending on timing\n * ```\n */\n static merge<T>(target: Sequence<T>, ...sequences: Sequence<T>[]): void {\n for (const source of sequences) {\n queueMicrotask(async () => {\n if (!target.disposed)\n try {\n const sink = target.sink;\n for await (const value of source) {\n if (!sink(value)) {\n return;\n }\n }\n } catch {\n // sequence is disposed\n }\n });\n }\n }\n\n /**\n * Creates a new Sequence instance.\n * @param abortSignal - Optional AbortSignal to cancel pending operations\n */\n constructor(abortSignal?: AbortSignal) {\n super(abortSignal);\n this.#queue = new RingBuffer();\n this.#nextSignal = new Signal(abortSignal);\n this.#sendSignal = new Signal(abortSignal);\n }\n\n /**\n * Returns the number of values currently queued.\n *\n * @returns The current queue size\n */\n get size(): number {\n return this.#queue.length;\n }\n\n /**\n * Waits until the queue size drops to or below the specified capacity.\n * Useful for implementing backpressure - producers can wait before adding more items.\n *\n * @param capacity The maximum queue size to wait for\n * @returns A promise that resolves when the queue size is at or below capacity\n *\n * @example\n * ```typescript\n * // Producer with backpressure control\n * const sequence = new Sequence<string>();\n *\n * // Wait if queue has more than 10 items\n * await sequence.reserve(10);\n * sequence.emit('new item'); // Safe to add, queue has space\n * ```\n */\n async reserve(capacity: number): Promise<void> {\n while (this.#queue.length > capacity) {\n await this.#sendSignal;\n }\n }\n\n /**\n * Pushes a value onto the queue. Wakes any pending `receive()` waiter.\n *\n * @param value - The value to enqueue.\n * @returns `true` if the sequence is still active.\n */\n emit(value: T): boolean {\n const ok = !this.disposed;\n if (ok) {\n this.#queue.push(value);\n }\n this.#nextSignal.emit();\n return ok;\n }\n\n /**\n * Consumes and returns the next value from the queue.\n * If the queue is empty, waits for a value to be added.\n * Values are consumed in FIFO order.\n * If the sequence has been aborted or disposed, this method rejects with Error('Disposed').\n *\n * @returns A promise that resolves with the next value\n *\n * @example\n * ```typescript\n * const sequence = new Sequence<number>();\n *\n * // Consumer waits for values\n * const valuePromise = sequence.receive();\n *\n * // Producer adds value\n * sequence.emit(42);\n *\n * // Consumer receives it\n * const value = await valuePromise; // 42\n * ```\n */\n async receive(): Promise<T> {\n while (!this.#queue.length) {\n await this.#nextSignal;\n }\n this.#sendSignal.emit();\n return this.#queue.shift()!;\n }\n\n /**\n * Disposes of the sequence, rejecting any pending `receive()` waiters.\n * Called by `[Symbol.dispose]()` (inherited from Async) when using the `using` declaration.\n */\n dispose(): void {\n this.#sendSignal[Symbol.dispose]();\n this.#nextSignal[Symbol.dispose]();\n }\n}\n"],"names":["Async","Signal","RingBuffer","Sequence","Symbol","toStringTag","merge","target","sequences","source","queueMicrotask","disposed","sink","value","abortSignal","size","length","reserve","capacity","emit","ok","push","receive","shift","dispose"],"mappings":"AAAA,SAASA,KAAK,QAAQ,aAAa;AACnC,SAASC,MAAM,QAAQ,cAAc;AACrC,SAASC,UAAU,QAAQ,mBAAmB;AAiC9C,OAAO,MAAMC,iBAAoBH;IAC/B,CAAA,KAAM,CAAgB;IACtB,CAAA,UAAW,CAAe;IAC1B,CAAA,UAAW,CAAe;IAEjB,CAACI,OAAOC,WAAW,CAAC,GAAG,WAAW;IA6B3C,OAAOC,MAASC,MAAmB,EAAE,GAAGC,SAAwB,EAAQ;QACtE,KAAK,MAAMC,UAAUD,UAAW;YAC9BE,eAAe;gBACb,IAAI,CAACH,OAAOI,QAAQ,EAClB,IAAI;oBACF,MAAMC,OAAOL,OAAOK,IAAI;oBACxB,WAAW,MAAMC,SAASJ,OAAQ;wBAChC,IAAI,CAACG,KAAKC,QAAQ;4BAChB;wBACF;oBACF;gBACF,EAAE,OAAM,CAER;YACJ;QACF;IACF;IAMA,YAAYC,WAAyB,CAAE;QACrC,KAAK,CAACA;QACN,IAAI,CAAC,CAAA,KAAM,GAAG,IAAIZ;QAClB,IAAI,CAAC,CAAA,UAAW,GAAG,IAAID,OAAOa;QAC9B,IAAI,CAAC,CAAA,UAAW,GAAG,IAAIb,OAAOa;IAChC;IAOA,IAAIC,OAAe;QACjB,OAAO,IAAI,CAAC,CAAA,KAAM,CAACC,MAAM;IAC3B;IAmBA,MAAMC,QAAQC,QAAgB,EAAiB;QAC7C,MAAO,IAAI,CAAC,CAAA,KAAM,CAACF,MAAM,GAAGE,SAAU;YACpC,MAAM,IAAI,CAAC,CAAA,UAAW;QACxB;IACF;IAQAC,KAAKN,KAAQ,EAAW;QACtB,MAAMO,KAAK,CAAC,IAAI,CAACT,QAAQ;QACzB,IAAIS,IAAI;YACN,IAAI,CAAC,CAAA,KAAM,CAACC,IAAI,CAACR;QACnB;QACA,IAAI,CAAC,CAAA,UAAW,CAACM,IAAI;QACrB,OAAOC;IACT;IAwBA,MAAME,UAAsB;QAC1B,MAAO,CAAC,IAAI,CAAC,CAAA,KAAM,CAACN,MAAM,CAAE;YAC1B,MAAM,IAAI,CAAC,CAAA,UAAW;QACxB;QACA,IAAI,CAAC,CAAA,UAAW,CAACG,IAAI;QACrB,OAAO,IAAI,CAAC,CAAA,KAAM,CAACI,KAAK;IAC1B;IAMAC,UAAgB;QACd,IAAI,CAAC,CAAA,UAAW,CAACpB,OAAOoB,OAAO,CAAC;QAChC,IAAI,CAAC,CAAA,UAAW,CAACpB,OAAOoB,OAAO,CAAC;IAClC;AACF"}
|
package/build/signal.cjs
CHANGED
|
@@ -8,55 +8,46 @@ Object.defineProperty(exports, "Signal", {
|
|
|
8
8
|
return Signal;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
const
|
|
12
|
-
class Signal extends
|
|
13
|
-
|
|
14
|
-
rx;
|
|
11
|
+
const _asynccjs = require("./async.cjs");
|
|
12
|
+
class Signal extends _asynccjs.Async {
|
|
13
|
+
#rx;
|
|
15
14
|
[Symbol.toStringTag] = 'Signal';
|
|
16
15
|
static merge(target, ...signals) {
|
|
16
|
+
if (target.disposed) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
17
19
|
for (const source of signals){
|
|
18
|
-
|
|
20
|
+
void (async ()=>{
|
|
19
21
|
try {
|
|
22
|
+
const sink = target.sink;
|
|
20
23
|
for await (const value of source){
|
|
21
|
-
if (target.
|
|
22
|
-
|
|
24
|
+
if (!sink(value) && target.disposed) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
23
27
|
}
|
|
24
28
|
} catch {}
|
|
25
|
-
});
|
|
29
|
+
})();
|
|
26
30
|
}
|
|
27
31
|
}
|
|
28
32
|
constructor(abortSignal){
|
|
29
|
-
super(
|
|
30
|
-
if (this.rx) {
|
|
31
|
-
this.rx.resolve(value);
|
|
32
|
-
this.rx = undefined;
|
|
33
|
-
return true;
|
|
34
|
-
} else {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
}), this.abortSignal = abortSignal;
|
|
38
|
-
this.abortSignal?.addEventListener('abort', ()=>{
|
|
39
|
-
this.rx?.reject(this.abortSignal.reason);
|
|
40
|
-
this.rx = undefined;
|
|
41
|
-
}, {
|
|
42
|
-
once: true
|
|
43
|
-
});
|
|
33
|
+
super(abortSignal);
|
|
44
34
|
}
|
|
45
|
-
|
|
46
|
-
return
|
|
35
|
+
emit(value) {
|
|
36
|
+
if (!this.#rx) return false;
|
|
37
|
+
this.#rx.resolve(value);
|
|
38
|
+
this.#rx = undefined;
|
|
39
|
+
return true;
|
|
47
40
|
}
|
|
48
|
-
|
|
49
|
-
if (this.
|
|
50
|
-
return Promise.reject(
|
|
51
|
-
}
|
|
52
|
-
if (!this.rx) {
|
|
53
|
-
this.rx = Promise.withResolvers();
|
|
41
|
+
receive() {
|
|
42
|
+
if (this.disposed) {
|
|
43
|
+
return Promise.reject(new Error('Disposed'));
|
|
54
44
|
}
|
|
55
|
-
|
|
45
|
+
this.#rx ??= Promise.withResolvers();
|
|
46
|
+
return this.#rx.promise;
|
|
56
47
|
}
|
|
57
|
-
|
|
58
|
-
this
|
|
59
|
-
this
|
|
48
|
+
dispose() {
|
|
49
|
+
this.#rx?.reject(new Error('Disposed'));
|
|
50
|
+
this.#rx = undefined;
|
|
60
51
|
}
|
|
61
52
|
}
|
|
62
53
|
|
package/build/signal.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/signal.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../src/signal.ts"],"sourcesContent":["import { Async } from './async.js';\n\n/**\n * Promise-based async coordination primitive.\n * `emit()` resolves the pending `receive()` promise (shared across callers).\n * Reusable - after each emission a new round of `receive()` calls can be made.\n * Disposable via `[Symbol.dispose]()` or an optional AbortSignal.\n *\n * @template T The type of value that this signal carries.\n *\n * @example\n * ```typescript\n * const signal = new Signal<string>();\n *\n * const promise = signal.receive();\n * signal.emit('hello');\n * await promise; // 'hello'\n * ```\n */\nexport class Signal<T> extends Async<T, boolean> {\n #rx?: PromiseWithResolvers<T>;\n\n readonly [Symbol.toStringTag] = 'Signal';\n\n /**\n * Merges multiple source signals into a target signal.\n * Values from any source signal are emitted to the target signal.\n * The merge continues until the target signal is disposed.\n *\n * Note: When the target is disposed, iteration stops after the next value\n * from each source. For immediate cleanup, dispose source signals directly.\n *\n * @param target The signal that will receive values from all sources\n * @param signals The source signals to merge from\n *\n * @example\n * ```typescript\n * // Create a target signal and source signals\n * const target = new Signal<string>();\n * const source1 = new Signal<string>();\n * const source2 = new Signal<string>();\n *\n * // Merge sources into target\n * Signal.merge(target, source1, source2);\n *\n * // Values from any source appear in target\n * const promise = target.receive();\n * source1.emit('Hello');\n * const value = await promise; // 'Hello'\n * ```\n */\n static merge<T>(target: Signal<T>, ...signals: Signal<T>[]): void {\n if (target.disposed) {\n return;\n }\n for (const source of signals) {\n void (async () => {\n try {\n const sink = target.sink;\n for await (const value of source) {\n if (!sink(value) && target.disposed) {\n return;\n }\n }\n } catch {\n // ignore disposed signal\n }\n })();\n }\n }\n\n /**\n * Creates a new Signal instance.\n *\n * @param abortSignal An optional AbortSignal that can be used to cancel the signal operation.\n *\n * @example\n * ```typescript\n * // Create a signal with abort capability\n * const controller = new AbortController();\n * const signal = new Signal<number>(controller.signal);\n *\n * // Signal can be cancelled\n * controller.abort('Operation cancelled');\n * ```\n */\n constructor(abortSignal?: AbortSignal) {\n super(abortSignal);\n }\n\n /**\n * Sends a value to the waiting receiver, if any.\n *\n * @param value - The value to send.\n * @returns `true` if the value was emitted.\n */\n emit(value: T): boolean {\n if (!this.#rx) return false;\n this.#rx.resolve(value);\n this.#rx = undefined;\n return true;\n }\n\n /**\n * Waits for the next value to be sent to this signal. If the signal has been aborted\n * or disposed, this method rejects with Error('Disposed').\n *\n * @returns A promise that resolves with the next value sent to the signal.\n *\n * @example\n * ```typescript\n * const signal = new Signal<string>();\n *\n * // Wait for a value\n * const valuePromise = signal.receive();\n *\n * // Send a value from elsewhere\n * signal.emit('Hello');\n *\n * const value = await valuePromise; // 'Hello'\n * ```\n */\n receive(): Promise<T> {\n if (this.disposed) {\n return Promise.reject(new Error('Disposed'));\n }\n this.#rx ??= Promise.withResolvers<T>();\n return this.#rx.promise;\n }\n\n dispose(): void {\n this.#rx?.reject(new Error('Disposed'));\n this.#rx = undefined;\n }\n}\n"],"names":["Signal","Async","Symbol","toStringTag","merge","target","signals","disposed","source","sink","value","abortSignal","emit","resolve","undefined","receive","Promise","reject","Error","withResolvers","promise","dispose"],"mappings":";;;;+BAmBaA;;;eAAAA;;;0BAnBS;AAmBf,MAAMA,eAAkBC,eAAK;IAClC,CAAA,EAAG,CAA2B;IAErB,CAACC,OAAOC,WAAW,CAAC,GAAG,SAAS;IA6BzC,OAAOC,MAASC,MAAiB,EAAE,GAAGC,OAAoB,EAAQ;QAChE,IAAID,OAAOE,QAAQ,EAAE;YACnB;QACF;QACA,KAAK,MAAMC,UAAUF,QAAS;YAC5B,KAAK,AAAC,CAAA;gBACJ,IAAI;oBACF,MAAMG,OAAOJ,OAAOI,IAAI;oBACxB,WAAW,MAAMC,SAASF,OAAQ;wBAChC,IAAI,CAACC,KAAKC,UAAUL,OAAOE,QAAQ,EAAE;4BACnC;wBACF;oBACF;gBACF,EAAE,OAAM,CAER;YACF,CAAA;QACF;IACF;IAiBA,YAAYI,WAAyB,CAAE;QACrC,KAAK,CAACA;IACR;IAQAC,KAAKF,KAAQ,EAAW;QACtB,IAAI,CAAC,IAAI,CAAC,CAAA,EAAG,EAAE,OAAO;QACtB,IAAI,CAAC,CAAA,EAAG,CAACG,OAAO,CAACH;QACjB,IAAI,CAAC,CAAA,EAAG,GAAGI;QACX,OAAO;IACT;IAqBAC,UAAsB;QACpB,IAAI,IAAI,CAACR,QAAQ,EAAE;YACjB,OAAOS,QAAQC,MAAM,CAAC,IAAIC,MAAM;QAClC;QACA,IAAI,CAAC,CAAA,EAAG,KAAKF,QAAQG,aAAa;QAClC,OAAO,IAAI,CAAC,CAAA,EAAG,CAACC,OAAO;IACzB;IAEAC,UAAgB;QACd,IAAI,CAAC,CAAA,EAAG,EAAEJ,OAAO,IAAIC,MAAM;QAC3B,IAAI,CAAC,CAAA,EAAG,GAAGJ;IACb;AACF"}
|
package/build/signal.d.ts
CHANGED
|
@@ -1,44 +1,36 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Async } from './async.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Key characteristics:
|
|
8
|
-
* - Multiple consumers can wait simultaneously
|
|
9
|
-
* - All waiting consumers receive the same value when sent
|
|
10
|
-
* - Reusable - can send multiple values over time
|
|
11
|
-
* - Supports async iteration for continuous value streaming
|
|
3
|
+
* Promise-based async coordination primitive.
|
|
4
|
+
* `emit()` resolves the pending `receive()` promise (shared across callers).
|
|
5
|
+
* Reusable - after each emission a new round of `receive()` calls can be made.
|
|
6
|
+
* Disposable via `[Symbol.dispose]()` or an optional AbortSignal.
|
|
12
7
|
*
|
|
13
8
|
* @template T The type of value that this signal carries.
|
|
14
9
|
*
|
|
10
|
+
* @example
|
|
15
11
|
* ```typescript
|
|
16
|
-
* // Create a signal for string values
|
|
17
12
|
* const signal = new Signal<string>();
|
|
18
13
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* // Send a value - both consumers receive it
|
|
24
|
-
* signal('Hello World');
|
|
25
|
-
*
|
|
26
|
-
* const [value1, value2] = await Promise.all([promise1, promise2]);
|
|
27
|
-
* console.log(value1 === value2); // true - both got 'Hello World'
|
|
14
|
+
* const promise = signal.receive();
|
|
15
|
+
* signal.emit('hello');
|
|
16
|
+
* await promise; // 'hello'
|
|
28
17
|
* ```
|
|
29
18
|
*/
|
|
30
|
-
export declare class Signal<T> extends
|
|
31
|
-
private
|
|
32
|
-
private rx?;
|
|
19
|
+
export declare class Signal<T> extends Async<T, boolean> {
|
|
20
|
+
#private;
|
|
33
21
|
readonly [Symbol.toStringTag] = "Signal";
|
|
34
22
|
/**
|
|
35
23
|
* Merges multiple source signals into a target signal.
|
|
36
|
-
* Values from any source signal are
|
|
37
|
-
* The merge continues until the target signal is
|
|
24
|
+
* Values from any source signal are emitted to the target signal.
|
|
25
|
+
* The merge continues until the target signal is disposed.
|
|
26
|
+
*
|
|
27
|
+
* Note: When the target is disposed, iteration stops after the next value
|
|
28
|
+
* from each source. For immediate cleanup, dispose source signals directly.
|
|
38
29
|
*
|
|
39
30
|
* @param target The signal that will receive values from all sources
|
|
40
31
|
* @param signals The source signals to merge from
|
|
41
32
|
*
|
|
33
|
+
* @example
|
|
42
34
|
* ```typescript
|
|
43
35
|
* // Create a target signal and source signals
|
|
44
36
|
* const target = new Signal<string>();
|
|
@@ -49,8 +41,9 @@ export declare class Signal<T> extends CallableAsyncIterator<T, boolean> {
|
|
|
49
41
|
* Signal.merge(target, source1, source2);
|
|
50
42
|
*
|
|
51
43
|
* // Values from any source appear in target
|
|
52
|
-
*
|
|
53
|
-
*
|
|
44
|
+
* const promise = target.receive();
|
|
45
|
+
* source1.emit('Hello');
|
|
46
|
+
* const value = await promise; // 'Hello'
|
|
54
47
|
* ```
|
|
55
48
|
*/
|
|
56
49
|
static merge<T>(target: Signal<T>, ...signals: Signal<T>[]): void;
|
|
@@ -59,6 +52,7 @@ export declare class Signal<T> extends CallableAsyncIterator<T, boolean> {
|
|
|
59
52
|
*
|
|
60
53
|
* @param abortSignal An optional AbortSignal that can be used to cancel the signal operation.
|
|
61
54
|
*
|
|
55
|
+
* @example
|
|
62
56
|
* ```typescript
|
|
63
57
|
* // Create a signal with abort capability
|
|
64
58
|
* const controller = new AbortController();
|
|
@@ -68,30 +62,33 @@ export declare class Signal<T> extends CallableAsyncIterator<T, boolean> {
|
|
|
68
62
|
* controller.abort('Operation cancelled');
|
|
69
63
|
* ```
|
|
70
64
|
*/
|
|
71
|
-
constructor(abortSignal?: AbortSignal
|
|
72
|
-
get aborted(): boolean;
|
|
65
|
+
constructor(abortSignal?: AbortSignal);
|
|
73
66
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
67
|
+
* Sends a value to the waiting receiver, if any.
|
|
68
|
+
*
|
|
69
|
+
* @param value - The value to send.
|
|
70
|
+
* @returns `true` if the value was emitted.
|
|
71
|
+
*/
|
|
72
|
+
emit(value: T): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Waits for the next value to be sent to this signal. If the signal has been aborted
|
|
75
|
+
* or disposed, this method rejects with Error('Disposed').
|
|
76
76
|
*
|
|
77
77
|
* @returns A promise that resolves with the next value sent to the signal.
|
|
78
78
|
*
|
|
79
|
+
* @example
|
|
79
80
|
* ```typescript
|
|
80
81
|
* const signal = new Signal<string>();
|
|
81
82
|
*
|
|
82
83
|
* // Wait for a value
|
|
83
|
-
* const valuePromise = signal.
|
|
84
|
+
* const valuePromise = signal.receive();
|
|
84
85
|
*
|
|
85
86
|
* // Send a value from elsewhere
|
|
86
|
-
* signal('Hello');
|
|
87
|
+
* signal.emit('Hello');
|
|
87
88
|
*
|
|
88
89
|
* const value = await valuePromise; // 'Hello'
|
|
89
90
|
* ```
|
|
90
91
|
*/
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
* Disposes of the signal, cleaning up any pending promise resolvers.
|
|
94
|
-
* This method is called automatically when the signal is used with a `using` declaration.
|
|
95
|
-
*/
|
|
96
|
-
[Symbol.dispose](): void;
|
|
92
|
+
receive(): Promise<T>;
|
|
93
|
+
dispose(): void;
|
|
97
94
|
}
|
package/build/signal.js
CHANGED
|
@@ -1,52 +1,43 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export class Signal extends
|
|
3
|
-
|
|
4
|
-
rx;
|
|
1
|
+
import { Async } from "./async.js";
|
|
2
|
+
export class Signal extends Async {
|
|
3
|
+
#rx;
|
|
5
4
|
[Symbol.toStringTag] = 'Signal';
|
|
6
5
|
static merge(target, ...signals) {
|
|
6
|
+
if (target.disposed) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
7
9
|
for (const source of signals){
|
|
8
|
-
|
|
10
|
+
void (async ()=>{
|
|
9
11
|
try {
|
|
12
|
+
const sink = target.sink;
|
|
10
13
|
for await (const value of source){
|
|
11
|
-
if (target.
|
|
12
|
-
|
|
14
|
+
if (!sink(value) && target.disposed) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
13
17
|
}
|
|
14
18
|
} catch {}
|
|
15
|
-
});
|
|
19
|
+
})();
|
|
16
20
|
}
|
|
17
21
|
}
|
|
18
22
|
constructor(abortSignal){
|
|
19
|
-
super(
|
|
20
|
-
if (this.rx) {
|
|
21
|
-
this.rx.resolve(value);
|
|
22
|
-
this.rx = undefined;
|
|
23
|
-
return true;
|
|
24
|
-
} else {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
}), this.abortSignal = abortSignal;
|
|
28
|
-
this.abortSignal?.addEventListener('abort', ()=>{
|
|
29
|
-
this.rx?.reject(this.abortSignal.reason);
|
|
30
|
-
this.rx = undefined;
|
|
31
|
-
}, {
|
|
32
|
-
once: true
|
|
33
|
-
});
|
|
23
|
+
super(abortSignal);
|
|
34
24
|
}
|
|
35
|
-
|
|
36
|
-
return
|
|
25
|
+
emit(value) {
|
|
26
|
+
if (!this.#rx) return false;
|
|
27
|
+
this.#rx.resolve(value);
|
|
28
|
+
this.#rx = undefined;
|
|
29
|
+
return true;
|
|
37
30
|
}
|
|
38
|
-
|
|
39
|
-
if (this.
|
|
40
|
-
return Promise.reject(
|
|
41
|
-
}
|
|
42
|
-
if (!this.rx) {
|
|
43
|
-
this.rx = Promise.withResolvers();
|
|
31
|
+
receive() {
|
|
32
|
+
if (this.disposed) {
|
|
33
|
+
return Promise.reject(new Error('Disposed'));
|
|
44
34
|
}
|
|
45
|
-
|
|
35
|
+
this.#rx ??= Promise.withResolvers();
|
|
36
|
+
return this.#rx.promise;
|
|
46
37
|
}
|
|
47
|
-
|
|
48
|
-
this
|
|
49
|
-
this
|
|
38
|
+
dispose() {
|
|
39
|
+
this.#rx?.reject(new Error('Disposed'));
|
|
40
|
+
this.#rx = undefined;
|
|
50
41
|
}
|
|
51
42
|
}
|
|
52
43
|
|
package/build/signal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/signal.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../src/signal.ts"],"sourcesContent":["import { Async } from './async.js';\n\n/**\n * Promise-based async coordination primitive.\n * `emit()` resolves the pending `receive()` promise (shared across callers).\n * Reusable - after each emission a new round of `receive()` calls can be made.\n * Disposable via `[Symbol.dispose]()` or an optional AbortSignal.\n *\n * @template T The type of value that this signal carries.\n *\n * @example\n * ```typescript\n * const signal = new Signal<string>();\n *\n * const promise = signal.receive();\n * signal.emit('hello');\n * await promise; // 'hello'\n * ```\n */\nexport class Signal<T> extends Async<T, boolean> {\n #rx?: PromiseWithResolvers<T>;\n\n readonly [Symbol.toStringTag] = 'Signal';\n\n /**\n * Merges multiple source signals into a target signal.\n * Values from any source signal are emitted to the target signal.\n * The merge continues until the target signal is disposed.\n *\n * Note: When the target is disposed, iteration stops after the next value\n * from each source. For immediate cleanup, dispose source signals directly.\n *\n * @param target The signal that will receive values from all sources\n * @param signals The source signals to merge from\n *\n * @example\n * ```typescript\n * // Create a target signal and source signals\n * const target = new Signal<string>();\n * const source1 = new Signal<string>();\n * const source2 = new Signal<string>();\n *\n * // Merge sources into target\n * Signal.merge(target, source1, source2);\n *\n * // Values from any source appear in target\n * const promise = target.receive();\n * source1.emit('Hello');\n * const value = await promise; // 'Hello'\n * ```\n */\n static merge<T>(target: Signal<T>, ...signals: Signal<T>[]): void {\n if (target.disposed) {\n return;\n }\n for (const source of signals) {\n void (async () => {\n try {\n const sink = target.sink;\n for await (const value of source) {\n if (!sink(value) && target.disposed) {\n return;\n }\n }\n } catch {\n // ignore disposed signal\n }\n })();\n }\n }\n\n /**\n * Creates a new Signal instance.\n *\n * @param abortSignal An optional AbortSignal that can be used to cancel the signal operation.\n *\n * @example\n * ```typescript\n * // Create a signal with abort capability\n * const controller = new AbortController();\n * const signal = new Signal<number>(controller.signal);\n *\n * // Signal can be cancelled\n * controller.abort('Operation cancelled');\n * ```\n */\n constructor(abortSignal?: AbortSignal) {\n super(abortSignal);\n }\n\n /**\n * Sends a value to the waiting receiver, if any.\n *\n * @param value - The value to send.\n * @returns `true` if the value was emitted.\n */\n emit(value: T): boolean {\n if (!this.#rx) return false;\n this.#rx.resolve(value);\n this.#rx = undefined;\n return true;\n }\n\n /**\n * Waits for the next value to be sent to this signal. If the signal has been aborted\n * or disposed, this method rejects with Error('Disposed').\n *\n * @returns A promise that resolves with the next value sent to the signal.\n *\n * @example\n * ```typescript\n * const signal = new Signal<string>();\n *\n * // Wait for a value\n * const valuePromise = signal.receive();\n *\n * // Send a value from elsewhere\n * signal.emit('Hello');\n *\n * const value = await valuePromise; // 'Hello'\n * ```\n */\n receive(): Promise<T> {\n if (this.disposed) {\n return Promise.reject(new Error('Disposed'));\n }\n this.#rx ??= Promise.withResolvers<T>();\n return this.#rx.promise;\n }\n\n dispose(): void {\n this.#rx?.reject(new Error('Disposed'));\n this.#rx = undefined;\n }\n}\n"],"names":["Async","Signal","Symbol","toStringTag","merge","target","signals","disposed","source","sink","value","abortSignal","emit","resolve","undefined","receive","Promise","reject","Error","withResolvers","promise","dispose"],"mappings":"AAAA,SAASA,KAAK,QAAQ,aAAa;AAmBnC,OAAO,MAAMC,eAAkBD;IAC7B,CAAA,EAAG,CAA2B;IAErB,CAACE,OAAOC,WAAW,CAAC,GAAG,SAAS;IA6BzC,OAAOC,MAASC,MAAiB,EAAE,GAAGC,OAAoB,EAAQ;QAChE,IAAID,OAAOE,QAAQ,EAAE;YACnB;QACF;QACA,KAAK,MAAMC,UAAUF,QAAS;YAC5B,KAAK,AAAC,CAAA;gBACJ,IAAI;oBACF,MAAMG,OAAOJ,OAAOI,IAAI;oBACxB,WAAW,MAAMC,SAASF,OAAQ;wBAChC,IAAI,CAACC,KAAKC,UAAUL,OAAOE,QAAQ,EAAE;4BACnC;wBACF;oBACF;gBACF,EAAE,OAAM,CAER;YACF,CAAA;QACF;IACF;IAiBA,YAAYI,WAAyB,CAAE;QACrC,KAAK,CAACA;IACR;IAQAC,KAAKF,KAAQ,EAAW;QACtB,IAAI,CAAC,IAAI,CAAC,CAAA,EAAG,EAAE,OAAO;QACtB,IAAI,CAAC,CAAA,EAAG,CAACG,OAAO,CAACH;QACjB,IAAI,CAAC,CAAA,EAAG,GAAGI;QACX,OAAO;IACT;IAqBAC,UAAsB;QACpB,IAAI,IAAI,CAACR,QAAQ,EAAE;YACjB,OAAOS,QAAQC,MAAM,CAAC,IAAIC,MAAM;QAClC;QACA,IAAI,CAAC,CAAA,EAAG,KAAKF,QAAQG,aAAa;QAClC,OAAO,IAAI,CAAC,CAAA,EAAG,CAACC,OAAO;IACzB;IAEAC,UAAgB;QACd,IAAI,CAAC,CAAA,EAAG,EAAEJ,OAAO,IAAIC,MAAM;QAC3B,IAAI,CAAC,CAAA,EAAG,GAAGJ;IACb;AACF"}
|
package/build/types.cjs
CHANGED
|
@@ -2,16 +2,5 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
Object.defineProperty(exports, "HookType", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return HookType;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
var HookType = /*#__PURE__*/ function(HookType) {
|
|
12
|
-
HookType[HookType["Add"] = 0] = "Add";
|
|
13
|
-
HookType[HookType["Remove"] = 1] = "Remove";
|
|
14
|
-
return HookType;
|
|
15
|
-
}({});
|
|
16
5
|
|
|
17
6
|
//# sourceMappingURL=types.cjs.map
|
package/build/types.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":""}
|