@johnhenry/iteration 0.0.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/MIT-LICENSE.txt +20 -0
- package/dist/abort.d.ts +26 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/abort.js +69 -0
- package/dist/abort.js.map +1 -0
- package/dist/assertions/eventualequal.d.ts +9 -0
- package/dist/assertions/eventualequal.d.ts.map +1 -0
- package/dist/assertions/eventualequal.js +30 -0
- package/dist/assertions/eventualequal.js.map +1 -0
- package/dist/async-channel.d.ts +101 -0
- package/dist/async-channel.d.ts.map +1 -0
- package/dist/async-channel.js +208 -0
- package/dist/async-channel.js.map +1 -0
- package/dist/channel-decorators.d.ts +23 -0
- package/dist/channel-decorators.d.ts.map +1 -0
- package/dist/channel-decorators.js +31 -0
- package/dist/channel-decorators.js.map +1 -0
- package/dist/concurrency.d.ts +43 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +145 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/consumers.d.ts +199 -0
- package/dist/consumers.d.ts.map +1 -0
- package/dist/consumers.js +349 -0
- package/dist/consumers.js.map +1 -0
- package/dist/count.d.ts +37 -0
- package/dist/count.d.ts.map +1 -0
- package/dist/count.js +70 -0
- package/dist/count.js.map +1 -0
- package/dist/empty-iterator.d.ts +15 -0
- package/dist/empty-iterator.d.ts.map +1 -0
- package/dist/empty-iterator.js +16 -0
- package/dist/empty-iterator.js.map +1 -0
- package/dist/eventualequal.d.ts +2 -0
- package/dist/eventualequal.d.ts.map +1 -0
- package/dist/eventualequal.js +54 -0
- package/dist/eventualequal.js.map +1 -0
- package/dist/exhaust.d.ts +7 -0
- package/dist/exhaust.d.ts.map +1 -0
- package/dist/exhaust.js +22 -0
- package/dist/exhaust.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/is-iterator.d.ts +5 -0
- package/dist/is-iterator.d.ts.map +1 -0
- package/dist/is-iterator.js +5 -0
- package/dist/is-iterator.js.map +1 -0
- package/dist/iterator-tools.d.ts +164 -0
- package/dist/iterator-tools.d.ts.map +1 -0
- package/dist/iterator-tools.js +231 -0
- package/dist/iterator-tools.js.map +1 -0
- package/dist/itertools.d.ts +237 -0
- package/dist/itertools.d.ts.map +1 -0
- package/dist/itertools.js +471 -0
- package/dist/itertools.js.map +1 -0
- package/dist/tee.d.ts +18 -0
- package/dist/tee.d.ts.map +1 -0
- package/dist/tee.js +118 -0
- package/dist/tee.js.map +1 -0
- package/dist/transduce.d.ts +79 -0
- package/dist/transduce.d.ts.map +1 -0
- package/dist/transduce.js +37 -0
- package/dist/transduce.js.map +1 -0
- package/dist/transducers.d.ts +123 -0
- package/dist/transducers.d.ts.map +1 -0
- package/dist/transducers.js +212 -0
- package/dist/transducers.js.map +1 -0
- package/package.json +122 -0
- package/readme.md +761 -0
- package/src/abort.ts +80 -0
- package/src/assertions/eventualequal.ts +36 -0
- package/src/async-channel.ts +228 -0
- package/src/channel-decorators.ts +49 -0
- package/src/concurrency.ts +168 -0
- package/src/consumers.ts +425 -0
- package/src/count.ts +72 -0
- package/src/empty-iterator.ts +17 -0
- package/src/eventualequal.ts +54 -0
- package/src/exhaust.ts +26 -0
- package/src/index.ts +17 -0
- package/src/is-iterator.ts +9 -0
- package/src/iterator-tools.ts +290 -0
- package/src/itertools.ts +518 -0
- package/src/tee.ts +136 -0
- package/src/transduce.ts +161 -0
- package/src/transducers.ts +256 -0
package/MIT-LICENSE.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2018-2022 John Henry
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/dist/abort.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AbortSignal plumbing shared by the async consumers (consumers.ts),
|
|
3
|
+
* transduceAsync (transduce.ts), and the concurrency helpers
|
|
4
|
+
* (concurrency.ts).
|
|
5
|
+
*/
|
|
6
|
+
/** Options bag accepted by cancellable async operations. */
|
|
7
|
+
export interface SignalOptions {
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
}
|
|
10
|
+
/** Throw the signal's abort reason if it has already aborted. */
|
|
11
|
+
export declare const throwIfAborted: (signal?: AbortSignal) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Wrap an (a)sync iterable so that iteration rejects promptly with an
|
|
14
|
+
* AbortError when `signal` aborts -- even while awaiting a slow source --
|
|
15
|
+
* and `iterator.return()` is propagated to the source on abort, error, or
|
|
16
|
+
* early consumer exit. With no signal, delegates straight through.
|
|
17
|
+
*
|
|
18
|
+
* Note: on abort the source's `return()` is invoked but deliberately not
|
|
19
|
+
* awaited -- an async generator queues `return()` behind its in-flight
|
|
20
|
+
* `next()`, so awaiting it could postpone the rejection indefinitely on a
|
|
21
|
+
* hung source (the very case aborting is for).
|
|
22
|
+
* @kind function
|
|
23
|
+
* @name abortable
|
|
24
|
+
*/
|
|
25
|
+
export declare const abortable: <T>(iterable: AsyncIterable<T> | Iterable<T>, signal?: AbortSignal) => AsyncGenerator<T>;
|
|
26
|
+
//# sourceMappingURL=abort.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../src/abort.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAWD,iEAAiE;AACjE,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,KAAG,IAIrD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,SAAS,GAAoB,CAAC,EACzC,UAAU,aAAa,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,EACxC,SAAS,WAAW,KACnB,cAAc,CAAC,CAAC,CAoClB,CAAC"}
|
package/dist/abort.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AbortSignal plumbing shared by the async consumers (consumers.ts),
|
|
3
|
+
* transduceAsync (transduce.ts), and the concurrency helpers
|
|
4
|
+
* (concurrency.ts).
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The error an aborted operation rejects with: the signal's own `reason`
|
|
8
|
+
* when set (AbortController.abort() defaults it to an "AbortError"
|
|
9
|
+
* DOMException), otherwise a fresh "AbortError" DOMException.
|
|
10
|
+
* @ignore
|
|
11
|
+
*/
|
|
12
|
+
const abortError = (signal) => signal.reason ?? new DOMException("This operation was aborted", "AbortError");
|
|
13
|
+
/** Throw the signal's abort reason if it has already aborted. */
|
|
14
|
+
export const throwIfAborted = (signal) => {
|
|
15
|
+
if (signal?.aborted) {
|
|
16
|
+
throw abortError(signal);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Wrap an (a)sync iterable so that iteration rejects promptly with an
|
|
21
|
+
* AbortError when `signal` aborts -- even while awaiting a slow source --
|
|
22
|
+
* and `iterator.return()` is propagated to the source on abort, error, or
|
|
23
|
+
* early consumer exit. With no signal, delegates straight through.
|
|
24
|
+
*
|
|
25
|
+
* Note: on abort the source's `return()` is invoked but deliberately not
|
|
26
|
+
* awaited -- an async generator queues `return()` behind its in-flight
|
|
27
|
+
* `next()`, so awaiting it could postpone the rejection indefinitely on a
|
|
28
|
+
* hung source (the very case aborting is for).
|
|
29
|
+
* @kind function
|
|
30
|
+
* @name abortable
|
|
31
|
+
*/
|
|
32
|
+
export const abortable = async function* (iterable, signal) {
|
|
33
|
+
if (!signal) {
|
|
34
|
+
yield* iterable;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
throwIfAborted(signal);
|
|
38
|
+
const it = Symbol.asyncIterator in Object(iterable)
|
|
39
|
+
? iterable[Symbol.asyncIterator]()
|
|
40
|
+
: iterable[Symbol.iterator]();
|
|
41
|
+
let onAbort = () => { };
|
|
42
|
+
const aborted = new Promise((_, reject) => {
|
|
43
|
+
onAbort = () => reject(abortError(signal));
|
|
44
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
45
|
+
});
|
|
46
|
+
// Pre-handle so an abort that never races (e.g. after completion, before
|
|
47
|
+
// listener removal) can't surface as an unhandled rejection.
|
|
48
|
+
aborted.catch(() => { });
|
|
49
|
+
let done = false;
|
|
50
|
+
try {
|
|
51
|
+
while (true) {
|
|
52
|
+
const result = await Promise.race([Promise.resolve(it.next()), aborted]);
|
|
53
|
+
if (result.done) {
|
|
54
|
+
done = true;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
yield result.value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
signal.removeEventListener("abort", onAbort);
|
|
62
|
+
if (!done && it.return) {
|
|
63
|
+
// Close the source on abort, error, or early consumer exit --
|
|
64
|
+
// fire-and-forget; see note above.
|
|
65
|
+
Promise.resolve(it.return()).catch(() => { });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=abort.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abort.js","sourceRoot":"","sources":["../src/abort.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH;;;;;GAKG;AACH,MAAM,UAAU,GAAG,CAAC,MAAmB,EAAW,EAAE,CAClD,MAAM,CAAC,MAAM,IAAI,IAAI,YAAY,CAAC,4BAA4B,EAAE,YAAY,CAAC,CAAC;AAEhF,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAoB,EAAQ,EAAE;IAC3D,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACpB,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,SAAS,CAAC,EACtC,QAAwC,EACxC,MAAoB;IAEpB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,KAAK,CAAC,CAAC,QAAQ,CAAC;QAChB,OAAO;IACT,CAAC;IACD,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,EAAE,GACN,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,QAAQ,CAAC;QACtC,CAAC,CAAE,QAA6B,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE;QACxD,CAAC,CAAE,QAAwB,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;IACnD,IAAI,OAAO,GAAe,GAAG,EAAE,GAAE,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QAC/C,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IACH,yEAAyE;IACzE,6DAA6D;IAC7D,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACxB,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;YACzE,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAChB,IAAI,GAAG,IAAI,CAAC;gBACZ,OAAO;YACT,CAAC;YACD,MAAM,MAAM,CAAC,KAAK,CAAC;QACrB,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC;YACvB,8DAA8D;YAC9D,mCAAmC;YACnC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class TestError extends Error {
|
|
2
|
+
val: Record<string, unknown>;
|
|
3
|
+
constructor(message: string, val?: Record<string, unknown>);
|
|
4
|
+
[Symbol.iterator](): IterableIterator<[string, unknown]>;
|
|
5
|
+
}
|
|
6
|
+
export declare const DefaultMessage = "should eventually be deep equal";
|
|
7
|
+
declare const _default: (actual: unknown, expected: unknown, message?: string, operator?: string) => Promise<string | TestError>;
|
|
8
|
+
export default _default;
|
|
9
|
+
//# sourceMappingURL=eventualequal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eventualequal.d.ts","sourceRoot":"","sources":["../../src/assertions/eventualequal.ts"],"names":[],"mappings":"AASA,qBAAa,SAAU,SAAQ,KAAK;IAClC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBACjB,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IAI9D,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAGzD;AAED,eAAO,MAAM,cAAc,oCAAoC,CAAC;yBAE9D,QAAQ,OAAO,EACf,UAAU,OAAO,EACjB,UAAS,MAAuB,EAChC,iBAA0B,KACzB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;AAL9B,wBAcE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Historically this module was a pop-quiz assertion and imported
|
|
2
|
+
// pop-quiz/testerror. pop-quiz is no longer a dependency (the test suite
|
|
3
|
+
// moved to node:test), so a structurally-compatible TestError is inlined
|
|
4
|
+
// here to keep the "@johnhenry/iteration/pop-quiz/asserteventualequal" subpath
|
|
5
|
+
// export working for existing consumers: it extends Error, exposes the
|
|
6
|
+
// details on `.val`, and is iterable over the [key, value] entries --
|
|
7
|
+
// exactly the shape pop-quiz@1's TestError had.
|
|
8
|
+
import { eventualequal } from "../eventualequal.js";
|
|
9
|
+
export class TestError extends Error {
|
|
10
|
+
val;
|
|
11
|
+
constructor(message, val = {}) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.val = val;
|
|
14
|
+
}
|
|
15
|
+
[Symbol.iterator]() {
|
|
16
|
+
return Object.entries(this.val).values();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export const DefaultMessage = "should eventually be deep equal";
|
|
20
|
+
export default async (actual, expected, message = DefaultMessage, operator = "eventualequal") => {
|
|
21
|
+
if (await eventualequal(actual, expected)) {
|
|
22
|
+
return message;
|
|
23
|
+
}
|
|
24
|
+
return new TestError(message, {
|
|
25
|
+
actual: actual,
|
|
26
|
+
expected: expected,
|
|
27
|
+
operator,
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=eventualequal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eventualequal.js","sourceRoot":"","sources":["../../src/assertions/eventualequal.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,yEAAyE;AACzE,yEAAyE;AACzE,+EAA+E;AAC/E,uEAAuE;AACvE,sEAAsE;AACtE,gDAAgD;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC,GAAG,CAA0B;IAC7B,YAAY,OAAe,EAAE,MAA+B,EAAE;QAC5D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IACD,CAAC,MAAM,CAAC,QAAQ,CAAC;QACf,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;IAC3C,CAAC;CACF;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,iCAAiC,CAAC;AAChE,eAAe,KAAK,EAClB,MAAe,EACf,QAAiB,EACjB,UAAkB,cAAc,EAChC,QAAQ,GAAG,eAAe,EACG,EAAE;IAC/B,IAAI,MAAM,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;QAC1C,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,SAAS,CAAC,OAAO,EAAE;QAC5B,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,QAAQ;QAClB,QAAQ;KACT,CAAC,CAAC;AACL,CAAC,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asynchronous Channel
|
|
3
|
+
* @kind namespace
|
|
4
|
+
* @name AsyncChannel
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Constant signaling channel's end
|
|
8
|
+
* @kind constant
|
|
9
|
+
* @name CHANNEL_END
|
|
10
|
+
*/
|
|
11
|
+
export declare const CHANNEL_END: unique symbol;
|
|
12
|
+
export interface AsyncChannelOptions<T> {
|
|
13
|
+
cache?: Array<T | typeof CHANNEL_END | Error>;
|
|
14
|
+
limit?: number;
|
|
15
|
+
transform?: (item: T) => T | Promise<T>;
|
|
16
|
+
debug?: (...args: unknown[]) => unknown;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Asynchronous Channel class
|
|
20
|
+
* @kind class
|
|
21
|
+
* @name AsyncChannel
|
|
22
|
+
*/
|
|
23
|
+
export declare class AsyncChannel<T = unknown> {
|
|
24
|
+
limit: number;
|
|
25
|
+
cache: Array<T | typeof CHANNEL_END | Error>;
|
|
26
|
+
transform: (item: T) => T | Promise<T>;
|
|
27
|
+
debug?: (...args: unknown[]) => unknown;
|
|
28
|
+
private promise?;
|
|
29
|
+
private resolve?;
|
|
30
|
+
private reject?;
|
|
31
|
+
/** Producers awaiting capacity, FIFO (backpressure -- see put()). */
|
|
32
|
+
private putters;
|
|
33
|
+
/**
|
|
34
|
+
* Serializes delivery of concurrent put() calls into call order. Each
|
|
35
|
+
* put() grabs the current tail synchronously (before any `await`, so
|
|
36
|
+
* position is fixed by call order rather than by how long e.g.
|
|
37
|
+
* `transform()` happens to take) and only commits its value once its
|
|
38
|
+
* predecessor has committed.
|
|
39
|
+
*/
|
|
40
|
+
private putOrder;
|
|
41
|
+
/**
|
|
42
|
+
* Asynchronous Channel constructor
|
|
43
|
+
* @kind function
|
|
44
|
+
* @name constructor
|
|
45
|
+
*/
|
|
46
|
+
constructor({ cache, limit, transform, debug }?: AsyncChannelOptions<T>);
|
|
47
|
+
/**
|
|
48
|
+
* Move waiting producers' items into the cache while there is capacity,
|
|
49
|
+
* releasing their pending put() promises (FIFO).
|
|
50
|
+
* @ignore
|
|
51
|
+
*/
|
|
52
|
+
private drainPutters;
|
|
53
|
+
/**
|
|
54
|
+
* Put item onto Asynchronous Channel. Returns a Promise<void> that
|
|
55
|
+
* resolves once the channel has accepted the item: immediately when a
|
|
56
|
+
* taker is waiting or the cache has capacity, otherwise when a later
|
|
57
|
+
* take() frees a slot (backpressure -- bounded by `limit`). Replaces the
|
|
58
|
+
* pre-2.1 behavior of throwing "cache full" at capacity.
|
|
59
|
+
* @kind function
|
|
60
|
+
* @name put
|
|
61
|
+
*/
|
|
62
|
+
put(item: T, ...debug: unknown[]): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Take item off of Asynchronous Channel
|
|
65
|
+
* @kind function
|
|
66
|
+
* @name take
|
|
67
|
+
*/
|
|
68
|
+
take(...debug: unknown[]): Promise<T | typeof CHANNEL_END>;
|
|
69
|
+
/**
|
|
70
|
+
* Pause Asynchronous Channel
|
|
71
|
+
* @kind function
|
|
72
|
+
* @name break
|
|
73
|
+
*/
|
|
74
|
+
break(...debug: unknown[]): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Stop Asynchronous Channel
|
|
77
|
+
* @kind function
|
|
78
|
+
* @name throw
|
|
79
|
+
*/
|
|
80
|
+
throw(message?: string, ...debug: unknown[]): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Return pending status of Asynchronous Channel
|
|
83
|
+
* @kind function
|
|
84
|
+
* @name pending
|
|
85
|
+
* Note: should this be a getter?
|
|
86
|
+
*/
|
|
87
|
+
pending(): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Return string representation of Asynchronous Channel
|
|
90
|
+
* @kind function
|
|
91
|
+
* @name toString
|
|
92
|
+
*/
|
|
93
|
+
toString(): string;
|
|
94
|
+
/**
|
|
95
|
+
* Return Asynchronous Channel's iterator
|
|
96
|
+
* @kind function
|
|
97
|
+
* @name [Symbol.asyncIterator]
|
|
98
|
+
*/
|
|
99
|
+
[Symbol.asyncIterator](...debug: unknown[]): AsyncGenerator<T>;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=async-channel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-channel.d.ts","sourceRoot":"","sources":["../src/async-channel.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,OAAO,MAA8B,CAAC;AAuBhE,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,OAAO,WAAW,GAAG,KAAK,CAAC,CAAC;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACxC,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CACzC;AAED;;;;GAIG;AACH,qBAAa,YAAY,CAAC,CAAC,GAAG,OAAO;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC,CAAC,GAAG,OAAO,WAAW,GAAG,KAAK,CAAC,CAAC;IAC7C,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACvC,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;IACxC,OAAO,CAAC,OAAO,CAAC,CAAkC;IAClD,OAAO,CAAC,OAAO,CAAC,CAA0C;IAC1D,OAAO,CAAC,MAAM,CAAC,CAA6B;IAC5C,qEAAqE;IACrE,OAAO,CAAC,OAAO,CAGP;IACR;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAoC;IAEpD;;;;OAIG;gBACS,EAAE,KAAU,EAAE,KAAgB,EAAE,SAAuB,EAAE,KAAK,EAAE,GAAE,mBAAmB,CAAC,CAAC,CAAM;IAMzG;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAUpB;;;;;;;;OAQG;IACG,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BtD;;;;OAIG;IACG,IAAI,CAAC,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,OAAO,WAAW,CAAC;IA2BhE;;;;OAIG;IACG,KAAK,CAAC,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAY/C;;;;OAIG;IACG,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjE;;;;;OAKG;IACH,OAAO,IAAI,OAAO;IAGlB;;;;OAIG;IACH,QAAQ,IAAI,MAAM;IAGlB;;;;OAIG;IACI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC;CAStE"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asynchronous Channel
|
|
3
|
+
* @kind namespace
|
|
4
|
+
* @name AsyncChannel
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Constant signaling channel's end
|
|
8
|
+
* @kind constant
|
|
9
|
+
* @name CHANNEL_END
|
|
10
|
+
*/
|
|
11
|
+
export const CHANNEL_END = Symbol("CHANNEL_END");
|
|
12
|
+
/**
|
|
13
|
+
* Creates a promise that can be resolved/rejected outside of initial closure
|
|
14
|
+
* @kind function
|
|
15
|
+
* @name InvertedPromise
|
|
16
|
+
* @ignore
|
|
17
|
+
*/
|
|
18
|
+
const InvertedPromise = () => {
|
|
19
|
+
const out = {};
|
|
20
|
+
out.promise = new Promise((resolve, reject) => {
|
|
21
|
+
out.resolve = resolve;
|
|
22
|
+
out.reject = reject;
|
|
23
|
+
});
|
|
24
|
+
return out;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Asynchronous Channel class
|
|
28
|
+
* @kind class
|
|
29
|
+
* @name AsyncChannel
|
|
30
|
+
*/
|
|
31
|
+
export class AsyncChannel {
|
|
32
|
+
limit;
|
|
33
|
+
cache;
|
|
34
|
+
transform;
|
|
35
|
+
debug;
|
|
36
|
+
promise;
|
|
37
|
+
resolve;
|
|
38
|
+
reject;
|
|
39
|
+
/** Producers awaiting capacity, FIFO (backpressure -- see put()). */
|
|
40
|
+
putters = [];
|
|
41
|
+
/**
|
|
42
|
+
* Serializes delivery of concurrent put() calls into call order. Each
|
|
43
|
+
* put() grabs the current tail synchronously (before any `await`, so
|
|
44
|
+
* position is fixed by call order rather than by how long e.g.
|
|
45
|
+
* `transform()` happens to take) and only commits its value once its
|
|
46
|
+
* predecessor has committed.
|
|
47
|
+
*/
|
|
48
|
+
putOrder = Promise.resolve();
|
|
49
|
+
/**
|
|
50
|
+
* Asynchronous Channel constructor
|
|
51
|
+
* @kind function
|
|
52
|
+
* @name constructor
|
|
53
|
+
*/
|
|
54
|
+
constructor({ cache = [], limit = Infinity, transform = ($) => $, debug } = {}) {
|
|
55
|
+
this.limit = limit;
|
|
56
|
+
this.cache = cache.slice(0, limit);
|
|
57
|
+
this.transform = transform;
|
|
58
|
+
this.debug = debug;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Move waiting producers' items into the cache while there is capacity,
|
|
62
|
+
* releasing their pending put() promises (FIFO).
|
|
63
|
+
* @ignore
|
|
64
|
+
*/
|
|
65
|
+
drainPutters() {
|
|
66
|
+
while (this.putters.length > 0 && this.cache.length < this.limit) {
|
|
67
|
+
const { value, release } = this.putters.shift();
|
|
68
|
+
this.cache.push(value);
|
|
69
|
+
release();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Put item onto Asynchronous Channel. Returns a Promise<void> that
|
|
74
|
+
* resolves once the channel has accepted the item: immediately when a
|
|
75
|
+
* taker is waiting or the cache has capacity, otherwise when a later
|
|
76
|
+
* take() frees a slot (backpressure -- bounded by `limit`). Replaces the
|
|
77
|
+
* pre-2.1 behavior of throwing "cache full" at capacity.
|
|
78
|
+
* @kind function
|
|
79
|
+
* @name put
|
|
80
|
+
*/
|
|
81
|
+
async put(item, ...debug) {
|
|
82
|
+
this.debug?.("put", item, ...debug);
|
|
83
|
+
// Reserve this call's delivery slot synchronously, in call order,
|
|
84
|
+
// before doing any async work (transform() may resolve at a different
|
|
85
|
+
// speed for different items -- see class doc on putOrder).
|
|
86
|
+
const myTurn = this.putOrder;
|
|
87
|
+
let advance;
|
|
88
|
+
this.putOrder = new Promise((resolve) => {
|
|
89
|
+
advance = resolve;
|
|
90
|
+
});
|
|
91
|
+
try {
|
|
92
|
+
const value = await this.transform(item);
|
|
93
|
+
await myTurn;
|
|
94
|
+
if (this.promise) {
|
|
95
|
+
this.resolve?.(value);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (this.cache.length < this.limit) {
|
|
99
|
+
this.cache.push(value);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
await new Promise((release) => {
|
|
103
|
+
this.putters.push({ value, release });
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
advance();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Take item off of Asynchronous Channel
|
|
112
|
+
* @kind function
|
|
113
|
+
* @name take
|
|
114
|
+
*/
|
|
115
|
+
async take(...debug) {
|
|
116
|
+
this.debug?.("take", ...debug);
|
|
117
|
+
if (this.cache.length) {
|
|
118
|
+
const value = this.cache.shift();
|
|
119
|
+
this.drainPutters();
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
if (this.putters.length) {
|
|
123
|
+
// limit of 0 (rendezvous) or drained cache with waiting producers:
|
|
124
|
+
// hand off directly.
|
|
125
|
+
const { value, release } = this.putters.shift();
|
|
126
|
+
release();
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
const { promise, resolve, reject } = InvertedPromise();
|
|
130
|
+
this.promise = promise;
|
|
131
|
+
this.resolve = resolve;
|
|
132
|
+
this.reject = reject;
|
|
133
|
+
const value = await this.promise;
|
|
134
|
+
delete this.promise;
|
|
135
|
+
delete this.resolve;
|
|
136
|
+
delete this.reject;
|
|
137
|
+
return value;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Pause Asynchronous Channel
|
|
141
|
+
* @kind function
|
|
142
|
+
* @name break
|
|
143
|
+
*/
|
|
144
|
+
async break(...debug) {
|
|
145
|
+
this.debug?.("break", ...debug);
|
|
146
|
+
if (this.promise) {
|
|
147
|
+
await this.resolve?.(CHANNEL_END);
|
|
148
|
+
}
|
|
149
|
+
else if (this.putters.length > 0) {
|
|
150
|
+
// Keep FIFO order: the end marker must not overtake producers
|
|
151
|
+
// already waiting for capacity.
|
|
152
|
+
this.putters.push({ value: CHANNEL_END, release: () => { } });
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
this.cache.push(CHANNEL_END);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Stop Asynchronous Channel
|
|
160
|
+
* @kind function
|
|
161
|
+
* @name throw
|
|
162
|
+
*/
|
|
163
|
+
async throw(message, ...debug) {
|
|
164
|
+
this.debug?.("throw", ...debug);
|
|
165
|
+
if (this.promise) {
|
|
166
|
+
await this.reject?.(new Error(message));
|
|
167
|
+
}
|
|
168
|
+
else if (this.putters.length > 0) {
|
|
169
|
+
// Keep FIFO order behind producers already waiting for capacity.
|
|
170
|
+
this.putters.push({ value: new Error(message), release: () => { } });
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
this.cache.push(new Error(message));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Return pending status of Asynchronous Channel
|
|
178
|
+
* @kind function
|
|
179
|
+
* @name pending
|
|
180
|
+
* Note: should this be a getter?
|
|
181
|
+
*/
|
|
182
|
+
pending() {
|
|
183
|
+
return !!this.promise;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Return string representation of Asynchronous Channel
|
|
187
|
+
* @kind function
|
|
188
|
+
* @name toString
|
|
189
|
+
*/
|
|
190
|
+
toString() {
|
|
191
|
+
return `AsyncChannel {${this.pending() ? "pending" : ""}} [${this.cache.length}/${this.limit}]`;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Return Asynchronous Channel's iterator
|
|
195
|
+
* @kind function
|
|
196
|
+
* @name [Symbol.asyncIterator]
|
|
197
|
+
*/
|
|
198
|
+
async *[Symbol.asyncIterator](...debug) {
|
|
199
|
+
while (true) {
|
|
200
|
+
const answer = await this.take(...debug);
|
|
201
|
+
if (answer === CHANNEL_END) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
yield answer;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=async-channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-channel.js","sourceRoot":"","sources":["../src/async-channel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AsyncChannel } from "./async-channel.ts";
|
|
2
|
+
interface EmitterLike {
|
|
3
|
+
addListener(event: string, listener: (...args: never[]) => unknown): unknown;
|
|
4
|
+
}
|
|
5
|
+
interface WebSocketLike {
|
|
6
|
+
onmessage: ((event: unknown) => unknown) | null;
|
|
7
|
+
onclose: ((event?: unknown) => unknown) | null;
|
|
8
|
+
onerror: ((event?: unknown) => unknown) | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Decorate Asynchronous Channel with generic emitter
|
|
12
|
+
* @kind function
|
|
13
|
+
* @name withEmitter
|
|
14
|
+
*/
|
|
15
|
+
export declare const withEmitter: <T, C extends AsyncChannel<T>>(channel: C, emitter: EmitterLike, data?: string, end?: string, error?: string) => C;
|
|
16
|
+
/**
|
|
17
|
+
* Decorate Asynchronous Channel with websocket
|
|
18
|
+
* @kind function
|
|
19
|
+
* @name withWebSocket
|
|
20
|
+
*/
|
|
21
|
+
export declare const withWebSocket: <T, C extends AsyncChannel<T>>(channel: C, websocket: WebSocketLike) => C;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=channel-decorators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-decorators.d.ts","sourceRoot":"","sources":["../src/channel-decorators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,UAAU,WAAW;IACnB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GAAG,OAAO,CAAC;CAC9E;AAED,UAAU,aAAa;IACrB,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,GAAG,IAAI,CAAC;IAChD,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/C,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,GAAG,IAAI,CAAC;CAChD;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,EACtD,SAAS,CAAC,EACV,SAAS,WAAW,EACpB,aAAa,EACb,YAAW,EACX,cAAe,KACd,CASF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,WAAW,aAAa,KAAG,CASlG,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decorate Asynchronous Channel with generic emitter
|
|
3
|
+
* @kind function
|
|
4
|
+
* @name withEmitter
|
|
5
|
+
*/
|
|
6
|
+
export const withEmitter = (channel, emitter, data = "data", end = "end", error = "error") => {
|
|
7
|
+
emitter.addListener(data, (...args) => {
|
|
8
|
+
void channel.put(...args).catch((reason) => {
|
|
9
|
+
void channel.throw(reason instanceof Error ? reason.message : String(reason));
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
emitter.addListener(end, channel.break.bind(channel));
|
|
13
|
+
emitter.addListener(error, channel.throw.bind(channel));
|
|
14
|
+
return channel;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Decorate Asynchronous Channel with websocket
|
|
18
|
+
* @kind function
|
|
19
|
+
* @name withWebSocket
|
|
20
|
+
*/
|
|
21
|
+
export const withWebSocket = (channel, websocket) => {
|
|
22
|
+
websocket.onmessage = (event) => {
|
|
23
|
+
void channel.put(event).catch((reason) => {
|
|
24
|
+
void channel.throw(reason instanceof Error ? reason.message : String(reason));
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
websocket.onclose = channel.break.bind(channel);
|
|
28
|
+
websocket.onerror = channel.throw.bind(channel);
|
|
29
|
+
return channel;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=channel-decorators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-decorators.js","sourceRoot":"","sources":["../src/channel-decorators.ts"],"names":[],"mappings":"AAYA;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,OAAU,EACV,OAAoB,EACpB,IAAI,GAAG,MAAM,EACb,GAAG,GAAG,KAAK,EACX,KAAK,GAAG,OAAO,EACZ,EAAE;IACL,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,GAAG,IAAa,EAAE,EAAE;QAC7C,KAAK,OAAO,CAAC,GAAG,CAAC,GAAI,IAAkD,CAAC,CAAC,KAAK,CAAC,CAAC,MAAe,EAAE,EAAE;YACjG,KAAK,OAAO,CAAC,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACxD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAA+B,OAAU,EAAE,SAAwB,EAAK,EAAE;IACrG,SAAS,CAAC,SAAS,GAAG,CAAC,KAAc,EAAE,EAAE;QACvC,KAAK,OAAO,CAAC,GAAG,CAAC,KAAU,CAAC,CAAC,KAAK,CAAC,CAAC,MAAe,EAAE,EAAE;YACrD,KAAK,OAAO,CAAC,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IACF,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChD,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAiC,CAAC;IAChF,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded-concurrency helpers: mapConcurrentAsync (parallel map over an
|
|
3
|
+
* (a)sync iterable with a concurrency limit) and prefetchAsync (eager
|
|
4
|
+
* read-ahead into a bounded buffer). Both propagate cancellation/early
|
|
5
|
+
* consumer exit upstream via iterator.return().
|
|
6
|
+
*/
|
|
7
|
+
import { type SignalOptions } from "./abort.ts";
|
|
8
|
+
export interface MapConcurrentOptions extends SignalOptions {
|
|
9
|
+
/** Maximum number of `fn` invocations in flight at once (required, >= 1). */
|
|
10
|
+
concurrency: number;
|
|
11
|
+
/**
|
|
12
|
+
* true (default): yield results in input order.
|
|
13
|
+
* false: yield results in completion order.
|
|
14
|
+
*/
|
|
15
|
+
ordered?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Map `fn` over `iterable` with up to `concurrency` invocations in flight
|
|
19
|
+
* at once. With `ordered: true` (the default) results are yielded in input
|
|
20
|
+
* order; with `ordered: false` they are yielded in completion order.
|
|
21
|
+
*
|
|
22
|
+
* - Backpressure: at most `concurrency` results are held; the source is
|
|
23
|
+
* only pulled while there is spare in-flight capacity.
|
|
24
|
+
* - Cancellation: an optional `signal` aborts promptly (rejecting with the
|
|
25
|
+
* signal's reason, an "AbortError" DOMException by default) even while
|
|
26
|
+
* awaiting a slow `fn` or source; the source iterator is closed via
|
|
27
|
+
* `return()`.
|
|
28
|
+
* - Early exit / error: breaking out of the loop, or an `fn` rejection,
|
|
29
|
+
* also closes the source via `return()`.
|
|
30
|
+
* @kind function
|
|
31
|
+
* @name mapConcurrentAsync
|
|
32
|
+
*/
|
|
33
|
+
export declare const mapConcurrentAsync: <In, Out>(fn: (item: In) => Out | Promise<Out>, iterable: AsyncIterable<In> | Iterable<In>, { concurrency, ordered, signal }: MapConcurrentOptions) => AsyncGenerator<Out>;
|
|
34
|
+
/**
|
|
35
|
+
* Eagerly pull up to `n` items ahead of the consumer into a bounded
|
|
36
|
+
* buffer, so a slow consumer overlaps with a slow producer. `n <= 0`
|
|
37
|
+
* degenerates to plain iteration. Early consumer exit (or an error)
|
|
38
|
+
* closes the source via `return()`.
|
|
39
|
+
* @kind function
|
|
40
|
+
* @name prefetchAsync
|
|
41
|
+
*/
|
|
42
|
+
export declare const prefetchAsync: <T>(n: number, iterable: AsyncIterable<T> | Iterable<T>) => AsyncGenerator<T>;
|
|
43
|
+
//# sourceMappingURL=concurrency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.d.ts","sourceRoot":"","sources":["../src/concurrency.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,YAAY,CAAC;AAEhE,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACzD,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAUD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,kBAAkB,GAAoB,EAAE,EAAE,GAAG,EACxD,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,EACpC,UAAU,aAAa,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,EAC1C,kCAAyC,oBAAoB,KAC5D,cAAc,CAAC,GAAG,CA4EpB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GAAoB,CAAC,EAC7C,GAAG,MAAM,EACT,UAAU,aAAa,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,KACvC,cAAc,CAAC,CAAC,CA+BlB,CAAC"}
|