evnty 5.1.0 → 5.2.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/LICENSE +1 -1
- package/README.md +0 -1
- package/build/dispatch-result.cjs +13 -26
- package/build/dispatch-result.cjs.map +1 -1
- package/build/dispatch-result.js +13 -26
- package/build/dispatch-result.js.map +1 -1
- package/package.json +9 -10
- package/src/async.ts +112 -0
- package/src/broadcast.ts +348 -0
- package/src/dispatch-result.ts +166 -0
- package/src/event.ts +408 -0
- package/src/iterator.ts +899 -0
- package/src/listener-registry.ts +178 -0
- package/src/ring-buffer.ts +234 -0
- package/src/sequence.ts +184 -0
- package/src/signal.ts +135 -0
- package/src/types.ts +137 -0
- package/src/utils.ts +426 -0
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2019-
|
|
3
|
+
Copyright (c) 2019-2026 Ivan Zakharchanka
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -263,7 +263,6 @@ for await (const data of processQueue) {
|
|
|
263
263
|
## License
|
|
264
264
|
|
|
265
265
|
License [The MIT License](./LICENSE)
|
|
266
|
-
Copyright (c) 2025 Ivan Zakharchanka
|
|
267
266
|
|
|
268
267
|
[npm-url]: https://www.npmjs.com/package/evnty
|
|
269
268
|
[downloads-image]: https://img.shields.io/npm/dw/evnty.svg?maxAge=43200
|
|
@@ -73,13 +73,8 @@ function resolveAll(results) {
|
|
|
73
73
|
hasError = true;
|
|
74
74
|
firstError = r.error;
|
|
75
75
|
}
|
|
76
|
-
} else
|
|
77
|
-
|
|
78
|
-
} catch (e) {
|
|
79
|
-
if (!hasError) {
|
|
80
|
-
hasError = true;
|
|
81
|
-
firstError = e;
|
|
82
|
-
}
|
|
76
|
+
} else if ((0, _utilscjs.isThenable)(r)) {
|
|
77
|
+
(asyncIndices ??= []).push(i);
|
|
83
78
|
}
|
|
84
79
|
}
|
|
85
80
|
if (hasError) {
|
|
@@ -105,27 +100,19 @@ function settleAll(results) {
|
|
|
105
100
|
status: 'rejected',
|
|
106
101
|
reason: r.error
|
|
107
102
|
};
|
|
108
|
-
} else
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
status: 'fulfilled',
|
|
112
|
-
value
|
|
113
|
-
}), (reason)=>({
|
|
114
|
-
status: 'rejected',
|
|
115
|
-
reason
|
|
116
|
-
}));
|
|
117
|
-
(asyncIndices ??= []).push(i);
|
|
118
|
-
settled[i] = wrapped;
|
|
119
|
-
} else {
|
|
120
|
-
settled[i] = {
|
|
103
|
+
} else if ((0, _utilscjs.isThenable)(r)) {
|
|
104
|
+
(asyncIndices ??= []).push(i);
|
|
105
|
+
settled[i] = r.then((value)=>({
|
|
121
106
|
status: 'fulfilled',
|
|
122
|
-
value
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
107
|
+
value
|
|
108
|
+
}), (reason)=>({
|
|
109
|
+
status: 'rejected',
|
|
110
|
+
reason
|
|
111
|
+
}));
|
|
112
|
+
} else {
|
|
126
113
|
settled[i] = {
|
|
127
|
-
status: '
|
|
128
|
-
|
|
114
|
+
status: 'fulfilled',
|
|
115
|
+
value: r
|
|
129
116
|
};
|
|
130
117
|
}
|
|
131
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dispatch-result.ts"],"sourcesContent":["import { Fn, MaybePromise } from './types.js';\nimport { isThenable, noop } from './utils.js';\n\nconst ERR_BRAND = Symbol.for('evnty.ResultError');\n\n/**\n * @internal\n */\nexport interface ResultError<E = unknown> {\n readonly error: E;\n readonly [ERR_BRAND]: true;\n}\n\nfunction ResultError<E>(this: ResultError<E>, error: E): void {\n (this as { error: E }).error = error;\n}\n(ResultError.prototype as { [ERR_BRAND]: true })[ERR_BRAND] = true;\n\n/**\n * @internal\n */\nexport function err<E>(error: E): ResultError<E> {\n return new (ResultError as unknown as new (error: E) => ResultError<E>)(error);\n}\n\n/**\n * @internal\n */\nexport function isErr(result: unknown): result is ResultError<unknown> {\n return typeof result === 'object' && result !== null && (result as Record<symbol, boolean>)[ERR_BRAND] === true;\n}\n\n/**\n * @internal\n */\nexport function isOk(result: unknown): boolean {\n return typeof result !== 'object' || result === null || !(result as Record<symbol, boolean>)[ERR_BRAND];\n}\n\n/**\n * @internal\n */\nexport type DispatchResultItem<T> = MaybePromise<T> | ResultError;\n\n/**\n * @internal\n */\nexport function unwrap<T>(results: DispatchResultItem<T>[]): MaybePromise<T>[] {\n const len = results.length;\n const unwrapped = new Array<MaybePromise<T>>(len);\n for (let i = 0; i < len; i++) {\n const r = results[i];\n unwrapped[i] = isErr(r) ? Promise.reject(r.error) : r;\n }\n return unwrapped;\n}\n\nasync function resolveMaybePromises<T>(items: MaybePromise<T>[], asyncIndices: number[]): Promise<T[]> {\n const pending = new Array<PromiseLike<T>>(asyncIndices.length);\n for (let j = 0; j < asyncIndices.length; j++) {\n pending[j] = items[asyncIndices[j]] as PromiseLike<T>;\n }\n const resolved = await Promise.all(pending);\n for (let j = 0; j < asyncIndices.length; j++) {\n items[asyncIndices[j]] = resolved[j];\n }\n return items as T[];\n}\n\nfunction resolveAll<T>(results: DispatchResultItem<T>[]): T[] | Promise<T[]> {\n const len = results.length;\n if (len === 0) return results as T[];\n\n let firstError: unknown;\n let hasError = false;\n let asyncIndices: number[] | null = null;\n\n for (let i = 0; i < len; i++) {\n const r = results[i];\n if (isErr(r)) {\n if (!hasError) {\n hasError = true;\n firstError = r.error;\n }\n } else
|
|
1
|
+
{"version":3,"sources":["../src/dispatch-result.ts"],"sourcesContent":["import { Fn, MaybePromise } from './types.js';\nimport { isThenable, noop } from './utils.js';\n\nconst ERR_BRAND = Symbol.for('evnty.ResultError');\n\n/**\n * @internal\n */\nexport interface ResultError<E = unknown> {\n readonly error: E;\n readonly [ERR_BRAND]: true;\n}\n\nfunction ResultError<E>(this: ResultError<E>, error: E): void {\n (this as { error: E }).error = error;\n}\n(ResultError.prototype as { [ERR_BRAND]: true })[ERR_BRAND] = true;\n\n/**\n * @internal\n */\nexport function err<E>(error: E): ResultError<E> {\n return new (ResultError as unknown as new (error: E) => ResultError<E>)(error);\n}\n\n/**\n * @internal\n */\nexport function isErr(result: unknown): result is ResultError<unknown> {\n return typeof result === 'object' && result !== null && (result as Record<symbol, boolean>)[ERR_BRAND] === true;\n}\n\n/**\n * @internal\n */\nexport function isOk(result: unknown): boolean {\n return typeof result !== 'object' || result === null || !(result as Record<symbol, boolean>)[ERR_BRAND];\n}\n\n/**\n * @internal\n */\nexport type DispatchResultItem<T> = MaybePromise<T> | ResultError;\n\n/**\n * @internal\n */\nexport function unwrap<T>(results: DispatchResultItem<T>[]): MaybePromise<T>[] {\n const len = results.length;\n const unwrapped = new Array<MaybePromise<T>>(len);\n for (let i = 0; i < len; i++) {\n const r = results[i];\n unwrapped[i] = isErr(r) ? Promise.reject(r.error) : r;\n }\n return unwrapped;\n}\n\nasync function resolveMaybePromises<T>(items: MaybePromise<T>[], asyncIndices: number[]): Promise<T[]> {\n const pending = new Array<PromiseLike<T>>(asyncIndices.length);\n for (let j = 0; j < asyncIndices.length; j++) {\n pending[j] = items[asyncIndices[j]] as PromiseLike<T>;\n }\n const resolved = await Promise.all(pending);\n for (let j = 0; j < asyncIndices.length; j++) {\n items[asyncIndices[j]] = resolved[j];\n }\n return items as T[];\n}\n\nfunction resolveAll<T>(results: DispatchResultItem<T>[]): T[] | Promise<T[]> {\n const len = results.length;\n if (len === 0) return results as T[];\n\n let firstError: unknown;\n let hasError = false;\n let asyncIndices: number[] | null = null;\n\n for (let i = 0; i < len; i++) {\n const r = results[i];\n if (isErr(r)) {\n if (!hasError) {\n hasError = true;\n firstError = r.error;\n }\n } else if (isThenable(r)) {\n (asyncIndices ??= []).push(i);\n }\n }\n\n if (hasError) {\n if (asyncIndices !== null) {\n for (let j = 0; j < asyncIndices.length; j++) {\n (results[asyncIndices[j]] as PromiseLike<T>).then(noop, noop);\n }\n }\n return Promise.reject(firstError);\n }\n if (asyncIndices === null) return results as T[];\n\n return resolveMaybePromises(results as MaybePromise<T>[], asyncIndices);\n}\n\nfunction settleAll<T>(results: DispatchResultItem<T>[]): PromiseSettledResult<T>[] | Promise<PromiseSettledResult<T>[]> {\n const len = results.length;\n if (len === 0) return [] as PromiseSettledResult<T>[];\n\n let asyncIndices: number[] | null = null;\n const settled = new Array<MaybePromise<PromiseSettledResult<T>>>(len);\n\n for (let i = 0; i < len; i++) {\n const r = results[i];\n if (isErr(r)) {\n settled[i] = { status: 'rejected', reason: r.error };\n } else if (isThenable(r)) {\n (asyncIndices ??= []).push(i);\n settled[i] = r.then(\n (value): PromiseFulfilledResult<T> => ({ status: 'fulfilled', value }),\n (reason: unknown): PromiseRejectedResult => ({ status: 'rejected', reason }),\n );\n } else {\n settled[i] = { status: 'fulfilled', value: r };\n }\n }\n\n if (asyncIndices === null) return settled as PromiseSettledResult<T>[];\n\n return resolveMaybePromises(settled, asyncIndices);\n}\n\n/**\n * Wraps an array of values or promises (typically listener results) and provides batch resolution.\n *\n * @template T\n */\nexport class DispatchResult<T> implements PromiseLike<T[]> {\n #results: DispatchResultItem<T>[];\n\n readonly [Symbol.toStringTag] = 'DispatchResult';\n\n constructor(results: DispatchResultItem<T>[]) {\n this.#results = results;\n }\n\n then<TResult1 = T, TResult2 = never>(\n onfulfilled?: Fn<[T[]], MaybePromise<TResult1>> | null,\n onrejected?: Fn<[any], MaybePromise<TResult2>> | null,\n ): PromiseLike<TResult1 | TResult2> {\n const resolved = this.all();\n if (resolved instanceof Promise) return resolved.then(onfulfilled, onrejected);\n return Promise.resolve(resolved).then(onfulfilled, onrejected);\n }\n\n /**\n * Resolves all listener results, rejecting if any promise rejects or any ResultError exists.\n */\n all(): T[] | Promise<T[]> {\n return resolveAll(this.#results);\n }\n\n /**\n * Waits for all listener results to settle, regardless of fulfillment or rejection.\n */\n settled(): PromiseSettledResult<T>[] | Promise<PromiseSettledResult<T>[]> {\n return settleAll(this.#results);\n }\n}\n"],"names":["DispatchResult","err","isErr","isOk","unwrap","ERR_BRAND","Symbol","for","ResultError","error","prototype","result","results","len","length","unwrapped","Array","i","r","Promise","reject","resolveMaybePromises","items","asyncIndices","pending","j","resolved","all","resolveAll","firstError","hasError","isThenable","push","then","noop","settleAll","settled","status","reason","value","toStringTag","onfulfilled","onrejected","resolve"],"mappings":"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"}
|
package/build/dispatch-result.js
CHANGED
|
@@ -46,13 +46,8 @@ function resolveAll(results) {
|
|
|
46
46
|
hasError = true;
|
|
47
47
|
firstError = r.error;
|
|
48
48
|
}
|
|
49
|
-
} else
|
|
50
|
-
|
|
51
|
-
} catch (e) {
|
|
52
|
-
if (!hasError) {
|
|
53
|
-
hasError = true;
|
|
54
|
-
firstError = e;
|
|
55
|
-
}
|
|
49
|
+
} else if (isThenable(r)) {
|
|
50
|
+
(asyncIndices ??= []).push(i);
|
|
56
51
|
}
|
|
57
52
|
}
|
|
58
53
|
if (hasError) {
|
|
@@ -78,27 +73,19 @@ function settleAll(results) {
|
|
|
78
73
|
status: 'rejected',
|
|
79
74
|
reason: r.error
|
|
80
75
|
};
|
|
81
|
-
} else
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
status: 'fulfilled',
|
|
85
|
-
value
|
|
86
|
-
}), (reason)=>({
|
|
87
|
-
status: 'rejected',
|
|
88
|
-
reason
|
|
89
|
-
}));
|
|
90
|
-
(asyncIndices ??= []).push(i);
|
|
91
|
-
settled[i] = wrapped;
|
|
92
|
-
} else {
|
|
93
|
-
settled[i] = {
|
|
76
|
+
} else if (isThenable(r)) {
|
|
77
|
+
(asyncIndices ??= []).push(i);
|
|
78
|
+
settled[i] = r.then((value)=>({
|
|
94
79
|
status: 'fulfilled',
|
|
95
|
-
value
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
80
|
+
value
|
|
81
|
+
}), (reason)=>({
|
|
82
|
+
status: 'rejected',
|
|
83
|
+
reason
|
|
84
|
+
}));
|
|
85
|
+
} else {
|
|
99
86
|
settled[i] = {
|
|
100
|
-
status: '
|
|
101
|
-
|
|
87
|
+
status: 'fulfilled',
|
|
88
|
+
value: r
|
|
102
89
|
};
|
|
103
90
|
}
|
|
104
91
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dispatch-result.ts"],"sourcesContent":["import { Fn, MaybePromise } from './types.js';\nimport { isThenable, noop } from './utils.js';\n\nconst ERR_BRAND = Symbol.for('evnty.ResultError');\n\n/**\n * @internal\n */\nexport interface ResultError<E = unknown> {\n readonly error: E;\n readonly [ERR_BRAND]: true;\n}\n\nfunction ResultError<E>(this: ResultError<E>, error: E): void {\n (this as { error: E }).error = error;\n}\n(ResultError.prototype as { [ERR_BRAND]: true })[ERR_BRAND] = true;\n\n/**\n * @internal\n */\nexport function err<E>(error: E): ResultError<E> {\n return new (ResultError as unknown as new (error: E) => ResultError<E>)(error);\n}\n\n/**\n * @internal\n */\nexport function isErr(result: unknown): result is ResultError<unknown> {\n return typeof result === 'object' && result !== null && (result as Record<symbol, boolean>)[ERR_BRAND] === true;\n}\n\n/**\n * @internal\n */\nexport function isOk(result: unknown): boolean {\n return typeof result !== 'object' || result === null || !(result as Record<symbol, boolean>)[ERR_BRAND];\n}\n\n/**\n * @internal\n */\nexport type DispatchResultItem<T> = MaybePromise<T> | ResultError;\n\n/**\n * @internal\n */\nexport function unwrap<T>(results: DispatchResultItem<T>[]): MaybePromise<T>[] {\n const len = results.length;\n const unwrapped = new Array<MaybePromise<T>>(len);\n for (let i = 0; i < len; i++) {\n const r = results[i];\n unwrapped[i] = isErr(r) ? Promise.reject(r.error) : r;\n }\n return unwrapped;\n}\n\nasync function resolveMaybePromises<T>(items: MaybePromise<T>[], asyncIndices: number[]): Promise<T[]> {\n const pending = new Array<PromiseLike<T>>(asyncIndices.length);\n for (let j = 0; j < asyncIndices.length; j++) {\n pending[j] = items[asyncIndices[j]] as PromiseLike<T>;\n }\n const resolved = await Promise.all(pending);\n for (let j = 0; j < asyncIndices.length; j++) {\n items[asyncIndices[j]] = resolved[j];\n }\n return items as T[];\n}\n\nfunction resolveAll<T>(results: DispatchResultItem<T>[]): T[] | Promise<T[]> {\n const len = results.length;\n if (len === 0) return results as T[];\n\n let firstError: unknown;\n let hasError = false;\n let asyncIndices: number[] | null = null;\n\n for (let i = 0; i < len; i++) {\n const r = results[i];\n if (isErr(r)) {\n if (!hasError) {\n hasError = true;\n firstError = r.error;\n }\n } else
|
|
1
|
+
{"version":3,"sources":["../src/dispatch-result.ts"],"sourcesContent":["import { Fn, MaybePromise } from './types.js';\nimport { isThenable, noop } from './utils.js';\n\nconst ERR_BRAND = Symbol.for('evnty.ResultError');\n\n/**\n * @internal\n */\nexport interface ResultError<E = unknown> {\n readonly error: E;\n readonly [ERR_BRAND]: true;\n}\n\nfunction ResultError<E>(this: ResultError<E>, error: E): void {\n (this as { error: E }).error = error;\n}\n(ResultError.prototype as { [ERR_BRAND]: true })[ERR_BRAND] = true;\n\n/**\n * @internal\n */\nexport function err<E>(error: E): ResultError<E> {\n return new (ResultError as unknown as new (error: E) => ResultError<E>)(error);\n}\n\n/**\n * @internal\n */\nexport function isErr(result: unknown): result is ResultError<unknown> {\n return typeof result === 'object' && result !== null && (result as Record<symbol, boolean>)[ERR_BRAND] === true;\n}\n\n/**\n * @internal\n */\nexport function isOk(result: unknown): boolean {\n return typeof result !== 'object' || result === null || !(result as Record<symbol, boolean>)[ERR_BRAND];\n}\n\n/**\n * @internal\n */\nexport type DispatchResultItem<T> = MaybePromise<T> | ResultError;\n\n/**\n * @internal\n */\nexport function unwrap<T>(results: DispatchResultItem<T>[]): MaybePromise<T>[] {\n const len = results.length;\n const unwrapped = new Array<MaybePromise<T>>(len);\n for (let i = 0; i < len; i++) {\n const r = results[i];\n unwrapped[i] = isErr(r) ? Promise.reject(r.error) : r;\n }\n return unwrapped;\n}\n\nasync function resolveMaybePromises<T>(items: MaybePromise<T>[], asyncIndices: number[]): Promise<T[]> {\n const pending = new Array<PromiseLike<T>>(asyncIndices.length);\n for (let j = 0; j < asyncIndices.length; j++) {\n pending[j] = items[asyncIndices[j]] as PromiseLike<T>;\n }\n const resolved = await Promise.all(pending);\n for (let j = 0; j < asyncIndices.length; j++) {\n items[asyncIndices[j]] = resolved[j];\n }\n return items as T[];\n}\n\nfunction resolveAll<T>(results: DispatchResultItem<T>[]): T[] | Promise<T[]> {\n const len = results.length;\n if (len === 0) return results as T[];\n\n let firstError: unknown;\n let hasError = false;\n let asyncIndices: number[] | null = null;\n\n for (let i = 0; i < len; i++) {\n const r = results[i];\n if (isErr(r)) {\n if (!hasError) {\n hasError = true;\n firstError = r.error;\n }\n } else if (isThenable(r)) {\n (asyncIndices ??= []).push(i);\n }\n }\n\n if (hasError) {\n if (asyncIndices !== null) {\n for (let j = 0; j < asyncIndices.length; j++) {\n (results[asyncIndices[j]] as PromiseLike<T>).then(noop, noop);\n }\n }\n return Promise.reject(firstError);\n }\n if (asyncIndices === null) return results as T[];\n\n return resolveMaybePromises(results as MaybePromise<T>[], asyncIndices);\n}\n\nfunction settleAll<T>(results: DispatchResultItem<T>[]): PromiseSettledResult<T>[] | Promise<PromiseSettledResult<T>[]> {\n const len = results.length;\n if (len === 0) return [] as PromiseSettledResult<T>[];\n\n let asyncIndices: number[] | null = null;\n const settled = new Array<MaybePromise<PromiseSettledResult<T>>>(len);\n\n for (let i = 0; i < len; i++) {\n const r = results[i];\n if (isErr(r)) {\n settled[i] = { status: 'rejected', reason: r.error };\n } else if (isThenable(r)) {\n (asyncIndices ??= []).push(i);\n settled[i] = r.then(\n (value): PromiseFulfilledResult<T> => ({ status: 'fulfilled', value }),\n (reason: unknown): PromiseRejectedResult => ({ status: 'rejected', reason }),\n );\n } else {\n settled[i] = { status: 'fulfilled', value: r };\n }\n }\n\n if (asyncIndices === null) return settled as PromiseSettledResult<T>[];\n\n return resolveMaybePromises(settled, asyncIndices);\n}\n\n/**\n * Wraps an array of values or promises (typically listener results) and provides batch resolution.\n *\n * @template T\n */\nexport class DispatchResult<T> implements PromiseLike<T[]> {\n #results: DispatchResultItem<T>[];\n\n readonly [Symbol.toStringTag] = 'DispatchResult';\n\n constructor(results: DispatchResultItem<T>[]) {\n this.#results = results;\n }\n\n then<TResult1 = T, TResult2 = never>(\n onfulfilled?: Fn<[T[]], MaybePromise<TResult1>> | null,\n onrejected?: Fn<[any], MaybePromise<TResult2>> | null,\n ): PromiseLike<TResult1 | TResult2> {\n const resolved = this.all();\n if (resolved instanceof Promise) return resolved.then(onfulfilled, onrejected);\n return Promise.resolve(resolved).then(onfulfilled, onrejected);\n }\n\n /**\n * Resolves all listener results, rejecting if any promise rejects or any ResultError exists.\n */\n all(): T[] | Promise<T[]> {\n return resolveAll(this.#results);\n }\n\n /**\n * Waits for all listener results to settle, regardless of fulfillment or rejection.\n */\n settled(): PromiseSettledResult<T>[] | Promise<PromiseSettledResult<T>[]> {\n return settleAll(this.#results);\n }\n}\n"],"names":["isThenable","noop","ERR_BRAND","Symbol","for","ResultError","error","prototype","err","isErr","result","isOk","unwrap","results","len","length","unwrapped","Array","i","r","Promise","reject","resolveMaybePromises","items","asyncIndices","pending","j","resolved","all","resolveAll","firstError","hasError","push","then","settleAll","settled","status","reason","value","DispatchResult","toStringTag","onfulfilled","onrejected","resolve"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "evnty",
|
|
3
3
|
"description": "Async-first, reactive event handling library for complex event flows in browser and Node.js",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
7
7
|
"main": "build/index.cjs",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"build",
|
|
15
|
-
"src
|
|
15
|
+
"src/*.ts",
|
|
16
16
|
"src/__tests__/example.js"
|
|
17
17
|
],
|
|
18
18
|
"sideEffects": false,
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@eslint/js": "^10.0.1",
|
|
53
53
|
"@fast-check/vitest": "^0.2.4",
|
|
54
|
-
"@stryker-mutator/core": "^9.
|
|
55
|
-
"@stryker-mutator/typescript-checker": "^9.
|
|
56
|
-
"@stryker-mutator/vitest-runner": "^9.
|
|
57
|
-
"@types/node": "^25.3.
|
|
54
|
+
"@stryker-mutator/core": "^9.6.0",
|
|
55
|
+
"@stryker-mutator/typescript-checker": "^9.6.0",
|
|
56
|
+
"@stryker-mutator/vitest-runner": "^9.6.0",
|
|
57
|
+
"@types/node": "^25.3.3",
|
|
58
58
|
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
|
59
59
|
"@typescript-eslint/parser": "^8.56.1",
|
|
60
60
|
"@typescript-eslint/typescript-estree": "^8.56.1",
|
|
@@ -64,10 +64,9 @@
|
|
|
64
64
|
"eslint": "^10.0.2",
|
|
65
65
|
"eslint-config-prettier": "^10.1.8",
|
|
66
66
|
"eslint-plugin-prettier": "^5.5.5",
|
|
67
|
-
"evnty": "
|
|
68
|
-
"husky": "^9.1.7",
|
|
67
|
+
"evnty-release": "npm:evnty@latest",
|
|
69
68
|
"inop": "^0.9.0",
|
|
70
|
-
"overtake": "1.
|
|
69
|
+
"overtake": "1.4.0",
|
|
71
70
|
"prettier": "^3.8.1",
|
|
72
71
|
"recast": "^0.23.11",
|
|
73
72
|
"sass-embedded": "^1.97.3",
|
|
@@ -87,6 +86,6 @@
|
|
|
87
86
|
"docs:dev": "vuepress dev docs",
|
|
88
87
|
"docs:gen": "node ./scripts/docgen.ts src/*.ts",
|
|
89
88
|
"docs:update-package": "pnpm dlx vp-update",
|
|
90
|
-
"bench": "overtake src/__bench__/event.ts"
|
|
89
|
+
"bench": "overtake src/__bench__/event.ts --progress"
|
|
91
90
|
}
|
|
92
91
|
}
|
package/src/async.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Action, Fn, Emitter, MaybePromise, Promiseable } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export class Disposer {
|
|
7
|
+
#target?: Disposable;
|
|
8
|
+
#abortSignal?: AbortSignal;
|
|
9
|
+
|
|
10
|
+
constructor(target: Disposable, abortSignal?: AbortSignal) {
|
|
11
|
+
if (abortSignal?.aborted) return;
|
|
12
|
+
this.#target = target;
|
|
13
|
+
if (abortSignal) {
|
|
14
|
+
this.#abortSignal = abortSignal;
|
|
15
|
+
abortSignal.addEventListener('abort', this);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get disposed(): boolean {
|
|
20
|
+
return !this.#target;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
[Symbol.dispose](): boolean {
|
|
24
|
+
if (!this.#target) return false;
|
|
25
|
+
this.#target = undefined;
|
|
26
|
+
// Stryker disable all: cleanup is memory optimization, no observable behavior after disposal
|
|
27
|
+
if (this.#abortSignal) {
|
|
28
|
+
this.#abortSignal.removeEventListener('abort', this);
|
|
29
|
+
this.#abortSignal = undefined;
|
|
30
|
+
}
|
|
31
|
+
// Stryker restore all
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
handleEvent(): void {
|
|
36
|
+
// Stryker disable next-line OptionalChaining: #target is always set when abort listener fires (synchronous code)
|
|
37
|
+
this.#target?.[Symbol.dispose]();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
44
|
+
export interface Async<T, R> extends Emitter<T, R>, Disposable {}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
export abstract class Async<T, R> implements Emitter<T, R>, Promiseable<T>, Promise<T>, AsyncIterator<T, void, void>, AsyncIterable<T> {
|
|
50
|
+
abstract [Symbol.toStringTag]: string;
|
|
51
|
+
abstract emit(value: T): R;
|
|
52
|
+
abstract receive(): Promise<T>;
|
|
53
|
+
|
|
54
|
+
dispose?(): void;
|
|
55
|
+
|
|
56
|
+
#sink?: Fn<[T], R>;
|
|
57
|
+
#disposer: Disposer;
|
|
58
|
+
|
|
59
|
+
constructor(abortSignal?: AbortSignal) {
|
|
60
|
+
this.#disposer = new Disposer(this, abortSignal);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get disposed(): boolean {
|
|
64
|
+
return this.#disposer.disposed;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get sink(): Fn<[T], R> {
|
|
68
|
+
return (this.#sink ??= this.emit.bind(this));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
handleEvent(event: T) {
|
|
72
|
+
this.emit(event);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
catch<OK = never>(onrejected?: Fn<[unknown], MaybePromise<OK>> | null): Promise<T | OK> {
|
|
76
|
+
return this.receive().catch(onrejected);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
finally(onfinally?: Action | null): Promise<T> {
|
|
80
|
+
return this.receive().finally(onfinally);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
then<OK = T, ERR = never>(onfulfilled?: Fn<[T], MaybePromise<OK>> | null, onrejected?: Fn<[unknown], MaybePromise<ERR>> | null): Promise<OK | ERR> {
|
|
84
|
+
return this.receive().then(onfulfilled, onrejected);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async next(): Promise<IteratorResult<T, void>> {
|
|
88
|
+
try {
|
|
89
|
+
const value = await this.receive();
|
|
90
|
+
return { value, done: false };
|
|
91
|
+
} catch {
|
|
92
|
+
return { value: undefined, done: true };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async return(): Promise<IteratorResult<T, void>> {
|
|
97
|
+
// Stryker disable next-line OptionalChaining: all subclasses define dispose()
|
|
98
|
+
this.dispose?.();
|
|
99
|
+
return { value: undefined, done: true };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
[Symbol.asyncIterator](): AsyncIterator<T, void, void> {
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
[Symbol.dispose](): void {
|
|
107
|
+
if (this.#disposer[Symbol.dispose]()) {
|
|
108
|
+
// Stryker disable next-line OptionalChaining: all subclasses define dispose()
|
|
109
|
+
this.dispose?.();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|