evnty 5.0.0 → 5.0.1-rc.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.
Files changed (56) hide show
  1. package/README.md +10 -10
  2. package/build/__bench__/event.cjs +99 -0
  3. package/build/__bench__/event.cjs.map +1 -0
  4. package/build/__bench__/event.js +54 -0
  5. package/build/__bench__/event.js.map +1 -0
  6. package/build/callable.cjs +4 -4
  7. package/build/callable.cjs.map +1 -1
  8. package/build/callable.d.ts +10 -2
  9. package/build/callable.js +4 -4
  10. package/build/callable.js.map +1 -1
  11. package/build/event.cjs +81 -51
  12. package/build/event.cjs.map +1 -1
  13. package/build/event.d.ts +24 -2
  14. package/build/event.js +81 -51
  15. package/build/event.js.map +1 -1
  16. package/build/index.cjs +1 -0
  17. package/build/index.cjs.map +1 -1
  18. package/build/index.d.ts +1 -0
  19. package/build/index.js +1 -0
  20. package/build/index.js.map +1 -1
  21. package/build/iterator.cjs +67 -34
  22. package/build/iterator.cjs.map +1 -1
  23. package/build/iterator.d.ts +9 -1
  24. package/build/iterator.js +68 -35
  25. package/build/iterator.js.map +1 -1
  26. package/build/listener-registry.cjs +109 -0
  27. package/build/listener-registry.cjs.map +1 -0
  28. package/build/listener-registry.d.ts +72 -0
  29. package/build/listener-registry.js +99 -0
  30. package/build/listener-registry.js.map +1 -0
  31. package/build/ring-buffer.cjs +143 -0
  32. package/build/ring-buffer.cjs.map +1 -0
  33. package/build/ring-buffer.d.ts +56 -0
  34. package/build/ring-buffer.js +133 -0
  35. package/build/ring-buffer.js.map +1 -0
  36. package/build/sequence.cjs +13 -10
  37. package/build/sequence.cjs.map +1 -1
  38. package/build/sequence.d.ts +9 -0
  39. package/build/sequence.js +12 -9
  40. package/build/sequence.js.map +1 -1
  41. package/build/signal.cjs +26 -12
  42. package/build/signal.cjs.map +1 -1
  43. package/build/signal.d.ts +7 -0
  44. package/build/signal.js +26 -12
  45. package/build/signal.js.map +1 -1
  46. package/build/types.cjs.map +1 -1
  47. package/build/types.d.ts +87 -0
  48. package/build/types.js.map +1 -1
  49. package/build/utils.cjs +205 -24
  50. package/build/utils.cjs.map +1 -1
  51. package/build/utils.d.ts +59 -26
  52. package/build/utils.js +190 -24
  53. package/build/utils.js.map +1 -1
  54. package/package.json +21 -22
  55. package/src/__tests__/example.js +14 -17
  56. package/src/index.ts +1 -0
package/README.md CHANGED
@@ -23,7 +23,7 @@ Async-first, reactive event handling library for complex event flows with three
23
23
  - [Features](#features)
24
24
  - [Platform Support](#platform-support)
25
25
  - [Installing](#installing)
26
- - [Documentation](https://3axap4ehko.github.io/evnty/)
26
+ - [Documentation](https://3axap4ehko.github.io/evnty/) <!--API_TOC-->
27
27
  - [Examples](#examples)
28
28
  - [License](#license)
29
29
 
@@ -113,17 +113,17 @@ Traditional event handling in JavaScript/TypeScript has limitations:
113
113
  Evnty solves these problems by providing:
114
114
  - **Type-safe events** with full TypeScript inference
115
115
  - **Three specialized primitives** for different async patterns
116
- - **Rich functional operators** (map, filter, reduce, debounce, batch, etc.)
116
+ - **Async iterator transformations** via `AsyncIteratorObject` (map, filter, reduce, expand, etc.)
117
117
  - **Composable abstractions** that work together seamlessly
118
118
 
119
119
  ## Features
120
120
 
121
121
  - **Async-First Design**: Built from the ground up for asynchronous event handling with full Promise support
122
- - **Functional Programming**: Rich set of operators including map, filter, reduce, debounce, batch, and expand for event stream transformations
122
+ - **Iterator Transformations**: `AsyncIteratorObject` provides map, filter, reduce, take, drop, flatMap, and expand operators
123
123
  - **Type-Safe**: Full TypeScript support with strong typing and inference throughout the event pipeline
124
- - **Async Iteration**: Events can be consumed as async iterables using for-await-of loops
125
- - **Event Composition**: Merge, combine, and transform multiple event streams into new events
126
- - **Minimal Dependencies**: Lightweight with only essential dependencies for optimal bundle size
124
+ - **Async Iteration**: Events, Signals, and Sequences can be consumed as async iterables using for-await-of loops
125
+ - **Event Composition**: Merge multiple event streams into unified events
126
+ - **Zero Dependencies**: Lightweight with no external dependencies for optimal bundle size
127
127
  - **Universal**: Works seamlessly in both browser and Node.js environments, including service workers
128
128
 
129
129
  ## Platform Support
@@ -176,10 +176,8 @@ userEvent.on(user => saveToCache(user));
176
176
  // Emit - all listeners are called
177
177
  userEvent({ id: 1, name: 'Alice' });
178
178
 
179
- // Functional transformations
180
- const adminEvent = userEvent
181
- .filter(user => user.id < 100)
182
- .map(user => ({ ...user, role: 'admin' }));
179
+ // One-time listener
180
+ userEvent.once(user => console.log('First user only:', user));
183
181
 
184
182
  // Async iteration
185
183
  for await (const user of userEvent) {
@@ -260,6 +258,8 @@ for await (const data of processQueue) {
260
258
  }
261
259
  ```
262
260
 
261
+ <!--API_REFERENCE-->
262
+
263
263
  ## License
264
264
 
265
265
  License [The MIT License](./LICENSE)
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ require("overtake");
6
+ function _getRequireWildcardCache(nodeInterop) {
7
+ if (typeof WeakMap !== "function") return null;
8
+ var cacheBabelInterop = new WeakMap();
9
+ var cacheNodeInterop = new WeakMap();
10
+ return (_getRequireWildcardCache = function(nodeInterop) {
11
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
12
+ })(nodeInterop);
13
+ }
14
+ function _interop_require_wildcard(obj, nodeInterop) {
15
+ if (!nodeInterop && obj && obj.__esModule) {
16
+ return obj;
17
+ }
18
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
19
+ return {
20
+ default: obj
21
+ };
22
+ }
23
+ var cache = _getRequireWildcardCache(nodeInterop);
24
+ if (cache && cache.has(obj)) {
25
+ return cache.get(obj);
26
+ }
27
+ var newObj = {
28
+ __proto__: null
29
+ };
30
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
31
+ for(var key in obj){
32
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
33
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
34
+ if (desc && (desc.get || desc.set)) {
35
+ Object.defineProperty(newObj, key, desc);
36
+ } else {
37
+ newObj[key] = obj[key];
38
+ }
39
+ }
40
+ }
41
+ newObj.default = obj;
42
+ if (cache) {
43
+ cache.set(obj, newObj);
44
+ }
45
+ return newObj;
46
+ }
47
+ const suite = benchmark('evnty benchmark', ()=>({}));
48
+ const current = suite.target('current Event', async ()=>{
49
+ const { Event } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../../build/index.js")));
50
+ const event = new Event();
51
+ const events = new Event();
52
+ let i = 1000;
53
+ while(i--){
54
+ events.on(()=>{});
55
+ }
56
+ const gc = [];
57
+ return {
58
+ Event,
59
+ gc,
60
+ event,
61
+ events
62
+ };
63
+ });
64
+ current.measure('instantiation', ({ Event, gc })=>{
65
+ gc.push(new Event());
66
+ });
67
+ current.measure('invoke', ({ event, gc })=>{
68
+ gc.push(event());
69
+ });
70
+ current.measure('invokes', ({ events, gc })=>{
71
+ gc.push(events());
72
+ });
73
+ const release = suite.target('release Event', async ()=>{
74
+ const { Event } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("evnty")));
75
+ const event = new Event();
76
+ const events = new Event();
77
+ let i = 10000;
78
+ while(i--){
79
+ events.on(()=>{});
80
+ }
81
+ const gc = [];
82
+ return {
83
+ Event,
84
+ gc,
85
+ event,
86
+ events
87
+ };
88
+ });
89
+ release.measure('instantiation', ({ Event, gc })=>{
90
+ gc.push(new Event());
91
+ });
92
+ release.measure('invoke', ({ event, gc })=>{
93
+ gc.push(event());
94
+ });
95
+ release.measure('invokes', ({ events, gc })=>{
96
+ gc.push(events());
97
+ });
98
+
99
+ //# sourceMappingURL=event.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/__bench__/event.ts"],"sourcesContent":["import 'overtake';\n\nconst suite = benchmark('evnty benchmark', () => ({}));\n\nconst current = suite.target('current Event', async () => {\n const { Event } = await import('../../build/index.js');\n const event = new Event<void>();\n const events = new Event<void>();\n let i = 1000;\n while (i--) {\n events.on(() => {});\n }\n const gc: unknown[] = [];\n return { Event, gc, event, events };\n});\n\ncurrent.measure('instantiation', ({ Event, gc }) => {\n gc.push(new Event());\n});\n\ncurrent.measure('invoke', ({ event, gc }) => {\n gc.push(event());\n});\n\ncurrent.measure('invokes', ({ events, gc }) => {\n gc.push(events());\n});\n\nconst release = suite.target('release Event', async () => {\n const { Event } = await import('evnty');\n const event = new Event<void>();\n const events = new Event<void>();\n let i = 10000;\n while (i--) {\n events.on(() => {});\n }\n const gc: unknown[] = [];\n return { Event, gc, event, events };\n});\n\nrelease.measure('instantiation', ({ Event, gc }) => {\n gc.push(new Event());\n});\n\nrelease.measure('invoke', ({ event, gc }) => {\n gc.push(event());\n});\n\nrelease.measure('invokes', ({ events, gc }) => {\n gc.push(events());\n});\n\n"],"names":["suite","benchmark","current","target","Event","event","events","i","on","gc","measure","push","release"],"mappings":";;;;QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEP,MAAMA,QAAQC,UAAU,mBAAmB,IAAO,CAAA,CAAC,CAAA;AAEnD,MAAMC,UAAUF,MAAMG,MAAM,CAAC,iBAAiB;IAC5C,MAAM,EAAEC,KAAK,EAAE,GAAG,MAAM,mEAAA,QAAO;IAC/B,MAAMC,QAAQ,IAAID;IAClB,MAAME,SAAS,IAAIF;IACnB,IAAIG,IAAI;IACR,MAAOA,IAAK;QACVD,OAAOE,EAAE,CAAC,KAAO;IACnB;IACA,MAAMC,KAAgB,EAAE;IACxB,OAAO;QAAEL;QAAOK;QAAIJ;QAAOC;IAAO;AACpC;AAEAJ,QAAQQ,OAAO,CAAC,iBAAiB,CAAC,EAAEN,KAAK,EAAEK,EAAE,EAAE;IAC7CA,GAAGE,IAAI,CAAC,IAAIP;AACd;AAEAF,QAAQQ,OAAO,CAAC,UAAU,CAAC,EAAEL,KAAK,EAAEI,EAAE,EAAE;IACtCA,GAAGE,IAAI,CAACN;AACV;AAEAH,QAAQQ,OAAO,CAAC,WAAW,CAAC,EAAEJ,MAAM,EAAEG,EAAE,EAAE;IACxCA,GAAGE,IAAI,CAACL;AACV;AAEA,MAAMM,UAAUZ,MAAMG,MAAM,CAAC,iBAAiB;IAC5C,MAAM,EAAEC,KAAK,EAAE,GAAG,MAAM,mEAAA,QAAO;IAC/B,MAAMC,QAAQ,IAAID;IAClB,MAAME,SAAS,IAAIF;IACnB,IAAIG,IAAI;IACR,MAAOA,IAAK;QACVD,OAAOE,EAAE,CAAC,KAAO;IACnB;IACA,MAAMC,KAAgB,EAAE;IACxB,OAAO;QAAEL;QAAOK;QAAIJ;QAAOC;IAAO;AACpC;AAEAM,QAAQF,OAAO,CAAC,iBAAiB,CAAC,EAAEN,KAAK,EAAEK,EAAE,EAAE;IAC7CA,GAAGE,IAAI,CAAC,IAAIP;AACd;AAEAQ,QAAQF,OAAO,CAAC,UAAU,CAAC,EAAEL,KAAK,EAAEI,EAAE,EAAE;IACtCA,GAAGE,IAAI,CAACN;AACV;AAEAO,QAAQF,OAAO,CAAC,WAAW,CAAC,EAAEJ,MAAM,EAAEG,EAAE,EAAE;IACxCA,GAAGE,IAAI,CAACL;AACV"}
@@ -0,0 +1,54 @@
1
+ import 'overtake';
2
+ const suite = benchmark('evnty benchmark', ()=>({}));
3
+ const current = suite.target('current Event', async ()=>{
4
+ const { Event } = await import('../../build/index.js');
5
+ const event = new Event();
6
+ const events = new Event();
7
+ let i = 1000;
8
+ while(i--){
9
+ events.on(()=>{});
10
+ }
11
+ const gc = [];
12
+ return {
13
+ Event,
14
+ gc,
15
+ event,
16
+ events
17
+ };
18
+ });
19
+ current.measure('instantiation', ({ Event, gc })=>{
20
+ gc.push(new Event());
21
+ });
22
+ current.measure('invoke', ({ event, gc })=>{
23
+ gc.push(event());
24
+ });
25
+ current.measure('invokes', ({ events, gc })=>{
26
+ gc.push(events());
27
+ });
28
+ const release = suite.target('release Event', async ()=>{
29
+ const { Event } = await import('evnty');
30
+ const event = new Event();
31
+ const events = new Event();
32
+ let i = 10000;
33
+ while(i--){
34
+ events.on(()=>{});
35
+ }
36
+ const gc = [];
37
+ return {
38
+ Event,
39
+ gc,
40
+ event,
41
+ events
42
+ };
43
+ });
44
+ release.measure('instantiation', ({ Event, gc })=>{
45
+ gc.push(new Event());
46
+ });
47
+ release.measure('invoke', ({ event, gc })=>{
48
+ gc.push(event());
49
+ });
50
+ release.measure('invokes', ({ events, gc })=>{
51
+ gc.push(events());
52
+ });
53
+
54
+ //# sourceMappingURL=event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/__bench__/event.ts"],"sourcesContent":["import 'overtake';\n\nconst suite = benchmark('evnty benchmark', () => ({}));\n\nconst current = suite.target('current Event', async () => {\n const { Event } = await import('../../build/index.js');\n const event = new Event<void>();\n const events = new Event<void>();\n let i = 1000;\n while (i--) {\n events.on(() => {});\n }\n const gc: unknown[] = [];\n return { Event, gc, event, events };\n});\n\ncurrent.measure('instantiation', ({ Event, gc }) => {\n gc.push(new Event());\n});\n\ncurrent.measure('invoke', ({ event, gc }) => {\n gc.push(event());\n});\n\ncurrent.measure('invokes', ({ events, gc }) => {\n gc.push(events());\n});\n\nconst release = suite.target('release Event', async () => {\n const { Event } = await import('evnty');\n const event = new Event<void>();\n const events = new Event<void>();\n let i = 10000;\n while (i--) {\n events.on(() => {});\n }\n const gc: unknown[] = [];\n return { Event, gc, event, events };\n});\n\nrelease.measure('instantiation', ({ Event, gc }) => {\n gc.push(new Event());\n});\n\nrelease.measure('invoke', ({ event, gc }) => {\n gc.push(event());\n});\n\nrelease.measure('invokes', ({ events, gc }) => {\n gc.push(events());\n});\n\n"],"names":["suite","benchmark","current","target","Event","event","events","i","on","gc","measure","push","release"],"mappings":"AAAA,OAAO,WAAW;AAElB,MAAMA,QAAQC,UAAU,mBAAmB,IAAO,CAAA,CAAC,CAAA;AAEnD,MAAMC,UAAUF,MAAMG,MAAM,CAAC,iBAAiB;IAC5C,MAAM,EAAEC,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC;IAC/B,MAAMC,QAAQ,IAAID;IAClB,MAAME,SAAS,IAAIF;IACnB,IAAIG,IAAI;IACR,MAAOA,IAAK;QACVD,OAAOE,EAAE,CAAC,KAAO;IACnB;IACA,MAAMC,KAAgB,EAAE;IACxB,OAAO;QAAEL;QAAOK;QAAIJ;QAAOC;IAAO;AACpC;AAEAJ,QAAQQ,OAAO,CAAC,iBAAiB,CAAC,EAAEN,KAAK,EAAEK,EAAE,EAAE;IAC7CA,GAAGE,IAAI,CAAC,IAAIP;AACd;AAEAF,QAAQQ,OAAO,CAAC,UAAU,CAAC,EAAEL,KAAK,EAAEI,EAAE,EAAE;IACtCA,GAAGE,IAAI,CAACN;AACV;AAEAH,QAAQQ,OAAO,CAAC,WAAW,CAAC,EAAEJ,MAAM,EAAEG,EAAE,EAAE;IACxCA,GAAGE,IAAI,CAACL;AACV;AAEA,MAAMM,UAAUZ,MAAMG,MAAM,CAAC,iBAAiB;IAC5C,MAAM,EAAEC,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC;IAC/B,MAAMC,QAAQ,IAAID;IAClB,MAAME,SAAS,IAAIF;IACnB,IAAIG,IAAI;IACR,MAAOA,IAAK;QACVD,OAAOE,EAAE,CAAC,KAAO;IACnB;IACA,MAAMC,KAAgB,EAAE;IACxB,OAAO;QAAEL;QAAOK;QAAIJ;QAAOC;IAAO;AACpC;AAEAM,QAAQF,OAAO,CAAC,iBAAiB,CAAC,EAAEN,KAAK,EAAEK,EAAE,EAAE;IAC7CA,GAAGE,IAAI,CAAC,IAAIP;AACd;AAEAQ,QAAQF,OAAO,CAAC,UAAU,CAAC,EAAEL,KAAK,EAAEI,EAAE,EAAE;IACtCA,GAAGE,IAAI,CAACN;AACV;AAEAO,QAAQF,OAAO,CAAC,WAAW,CAAC,EAAEJ,MAAM,EAAEG,EAAE,EAAE;IACxCA,GAAGE,IAAI,CAACL;AACV"}
@@ -58,12 +58,12 @@ class CallableAsyncIterator extends AsyncCallable {
58
58
  };
59
59
  }
60
60
  },
61
- return: ()=>{
62
- this[Symbol.dispose]();
63
- return Promise.resolve({
61
+ return: async ()=>{
62
+ await this.return?.();
63
+ return {
64
64
  value: undefined,
65
65
  done: true
66
- });
66
+ };
67
67
  }
68
68
  };
69
69
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/callable.ts"],"sourcesContent":["import { Fn, Promiseable } from './types.js';\n/**\n * @internal\n */\nexport interface Callable<T extends unknown[], R> {\n (...args: T): R;\n}\n\n/**\n * An abstract class that extends the built-in Function class. It allows instances of the class\n * to be called as functions. When an instance of Callable is called as a function, it will\n * call the function passed to its constructor with the same arguments.\n * @internal\n */\n\nexport abstract class Callable<T, R> {\n static {\n Object.setPrototypeOf(Callable.prototype, Function.prototype);\n }\n\n constructor(func: Fn<T, R>) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return Object.setPrototypeOf(func, new.target.prototype);\n }\n}\n\n/**\n * @internal\n */\nexport abstract class AsyncCallable<T, R> extends Callable<[T], R> implements Promiseable<T>, Promise<T> {\n constructor(func: Fn<[T], R>) {\n super(func);\n }\n\n abstract [Symbol.toStringTag]: string;\n abstract next(): Promise<T>;\n\n catch<OK = never>(onrejected?: ((reason: any) => OK | PromiseLike<OK>) | null): Promise<T | OK> {\n return this.next().catch(onrejected);\n }\n\n finally(onfinally?: (() => void) | null): Promise<T> {\n return this.next().finally(onfinally);\n }\n\n then<OK = T, ERR = never>(\n onfulfilled?: ((value: T) => OK | PromiseLike<OK>) | null,\n onrejected?: ((reason: unknown) => ERR | PromiseLike<ERR>) | null,\n ): Promise<OK | ERR> {\n return this.next().then(onfulfilled, onrejected);\n }\n}\n\n/**\n * @internal\n */\nexport abstract class CallableAsyncIterator<T, R> extends AsyncCallable<T, R> implements Promiseable<T>, Promise<T>, AsyncIterable<T>, Disposable {\n [Symbol.asyncIterator](): AsyncIterator<T, void, void> {\n return {\n next: async () => {\n try {\n const value = await this.next();\n return { value, done: false };\n } catch {\n return { value: undefined, done: true };\n }\n },\n return: () => {\n this[Symbol.dispose]();\n return Promise.resolve({ value: undefined, done: true });\n },\n };\n }\n abstract [Symbol.dispose](): void;\n}\n"],"names":["AsyncCallable","Callable","CallableAsyncIterator","Object","setPrototypeOf","prototype","Function","func","catch","onrejected","next","finally","onfinally","then","onfulfilled","Symbol","asyncIterator","value","done","undefined","return","dispose","Promise","resolve"],"mappings":";;;;;;;;;;;QA6BsBA;eAAAA;;QAdAC;eAAAA;;QAyCAC;eAAAA;;;AAzCf,MAAeD;IACpB,MAAO;QACLE,OAAOC,cAAc,CAACH,SAASI,SAAS,EAAEC,SAASD,SAAS;IAC9D,CAAC;IAED,YAAYE,IAAc,CAAE;QAE1B,OAAOJ,OAAOC,cAAc,CAACG,MAAM,WAAWF,SAAS;IACzD;AACF;AAKO,MAAeL,sBAA4BC;IAChD,YAAYM,IAAgB,CAAE;QAC5B,KAAK,CAACA;IACR;IAKAC,MAAkBC,UAA2D,EAAmB;QAC9F,OAAO,IAAI,CAACC,IAAI,GAAGF,KAAK,CAACC;IAC3B;IAEAE,QAAQC,SAA+B,EAAc;QACnD,OAAO,IAAI,CAACF,IAAI,GAAGC,OAAO,CAACC;IAC7B;IAEAC,KACEC,WAAyD,EACzDL,UAAiE,EAC9C;QACnB,OAAO,IAAI,CAACC,IAAI,GAAGG,IAAI,CAACC,aAAaL;IACvC;AACF;AAKO,MAAeP,8BAAoCF;IACxD,CAACe,OAAOC,aAAa,CAAC,GAAiC;QACrD,OAAO;YACLN,MAAM;gBACJ,IAAI;oBACF,MAAMO,QAAQ,MAAM,IAAI,CAACP,IAAI;oBAC7B,OAAO;wBAAEO;wBAAOC,MAAM;oBAAM;gBAC9B,EAAE,OAAM;oBACN,OAAO;wBAAED,OAAOE;wBAAWD,MAAM;oBAAK;gBACxC;YACF;YACAE,QAAQ;gBACN,IAAI,CAACL,OAAOM,OAAO,CAAC;gBACpB,OAAOC,QAAQC,OAAO,CAAC;oBAAEN,OAAOE;oBAAWD,MAAM;gBAAK;YACxD;QACF;IACF;AAEF"}
1
+ {"version":3,"sources":["../src/callable.ts"],"sourcesContent":["import { Fn, Promiseable } from './types.js';\n\n/**\n * Makes subclasses callable like plain functions by returning the provided delegate\n * with the subclass prototype applied. Instances can be invoked directly while\n * retaining class semantics.\n * @internal\n */\nexport interface Callable<T extends unknown[], R> {\n (...args: T): R;\n}\n\n/**\n * An abstract class that extends the built-in Function class. It allows instances of the class\n * to be called as functions. When an instance of Callable is called as a function, it will\n * call the function passed to its constructor with the same arguments.\n * @internal\n */\n\nexport abstract class Callable<T, R> {\n static {\n Object.setPrototypeOf(Callable.prototype, Function.prototype);\n }\n\n constructor(func: Fn<T, R>) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return Object.setPrototypeOf(func, new.target.prototype);\n }\n}\n\n/**\n * Promise-like callable base. Subclasses implement `next()`, and the Promise methods\n * delegate to that, so instances can be awaited or chained while remaining callable.\n * @internal\n */\nexport abstract class AsyncCallable<T, R> extends Callable<[T], R> implements Promiseable<T>, Promise<T> {\n constructor(func: Fn<[T], R>) {\n super(func);\n }\n\n abstract [Symbol.toStringTag]: string;\n abstract next(): Promise<T>;\n\n catch<OK = never>(onrejected?: ((reason: any) => OK | PromiseLike<OK>) | null): Promise<T | OK> {\n return this.next().catch(onrejected);\n }\n\n finally(onfinally?: (() => void) | null): Promise<T> {\n return this.next().finally(onfinally);\n }\n\n then<OK = T, ERR = never>(\n onfulfilled?: ((value: T) => OK | PromiseLike<OK>) | null,\n onrejected?: ((reason: unknown) => ERR | PromiseLike<ERR>) | null,\n ): Promise<OK | ERR> {\n return this.next().then(onfulfilled, onrejected);\n }\n}\n\n/**\n * Callable async iterator base. Adds `for await` support and disposal on return\n * while delegating iteration to `next()`. Subclasses become callable, awaitable,\n * and async-iterable with a single `next()` implementation.\n * @internal\n */\nexport abstract class CallableAsyncIterator<T, R> extends AsyncCallable<T, R> implements Promiseable<T>, Promise<T>, AsyncIterable<T> {\n return?(): Promise<undefined>;\n\n [Symbol.asyncIterator](): AsyncIterator<T, void, void> {\n return {\n next: async () => {\n try {\n const value = await this.next();\n return { value, done: false };\n } catch {\n return { value: undefined, done: true };\n }\n },\n return: async () => {\n await this.return?.();\n return { value: undefined, done: true };\n },\n };\n }\n}\n"],"names":["AsyncCallable","Callable","CallableAsyncIterator","Object","setPrototypeOf","prototype","Function","func","catch","onrejected","next","finally","onfinally","then","onfulfilled","Symbol","asyncIterator","value","done","undefined","return"],"mappings":";;;;;;;;;;;QAmCsBA;eAAAA;;QAhBAC;eAAAA;;QA8CAC;eAAAA;;;AA9Cf,MAAeD;IACpB,MAAO;QACLE,OAAOC,cAAc,CAACH,SAASI,SAAS,EAAEC,SAASD,SAAS;IAC9D,CAAC;IAED,YAAYE,IAAc,CAAE;QAE1B,OAAOJ,OAAOC,cAAc,CAACG,MAAM,WAAWF,SAAS;IACzD;AACF;AAOO,MAAeL,sBAA4BC;IAChD,YAAYM,IAAgB,CAAE;QAC5B,KAAK,CAACA;IACR;IAKAC,MAAkBC,UAA2D,EAAmB;QAC9F,OAAO,IAAI,CAACC,IAAI,GAAGF,KAAK,CAACC;IAC3B;IAEAE,QAAQC,SAA+B,EAAc;QACnD,OAAO,IAAI,CAACF,IAAI,GAAGC,OAAO,CAACC;IAC7B;IAEAC,KACEC,WAAyD,EACzDL,UAAiE,EAC9C;QACnB,OAAO,IAAI,CAACC,IAAI,GAAGG,IAAI,CAACC,aAAaL;IACvC;AACF;AAQO,MAAeP,8BAAoCF;IAGxD,CAACe,OAAOC,aAAa,CAAC,GAAiC;QACrD,OAAO;YACLN,MAAM;gBACJ,IAAI;oBACF,MAAMO,QAAQ,MAAM,IAAI,CAACP,IAAI;oBAC7B,OAAO;wBAAEO;wBAAOC,MAAM;oBAAM;gBAC9B,EAAE,OAAM;oBACN,OAAO;wBAAED,OAAOE;wBAAWD,MAAM;oBAAK;gBACxC;YACF;YACAE,QAAQ;gBACN,MAAM,IAAI,CAACA,MAAM;gBACjB,OAAO;oBAAEH,OAAOE;oBAAWD,MAAM;gBAAK;YACxC;QACF;IACF;AACF"}
@@ -1,5 +1,8 @@
1
1
  import { Fn, Promiseable } from './types.js';
2
2
  /**
3
+ * Makes subclasses callable like plain functions by returning the provided delegate
4
+ * with the subclass prototype applied. Instances can be invoked directly while
5
+ * retaining class semantics.
3
6
  * @internal
4
7
  */
5
8
  export interface Callable<T extends unknown[], R> {
@@ -15,6 +18,8 @@ export declare abstract class Callable<T, R> {
15
18
  constructor(func: Fn<T, R>);
16
19
  }
17
20
  /**
21
+ * Promise-like callable base. Subclasses implement `next()`, and the Promise methods
22
+ * delegate to that, so instances can be awaited or chained while remaining callable.
18
23
  * @internal
19
24
  */
20
25
  export declare abstract class AsyncCallable<T, R> extends Callable<[T], R> implements Promiseable<T>, Promise<T> {
@@ -26,9 +31,12 @@ export declare abstract class AsyncCallable<T, R> extends Callable<[T], R> imple
26
31
  then<OK = T, ERR = never>(onfulfilled?: ((value: T) => OK | PromiseLike<OK>) | null, onrejected?: ((reason: unknown) => ERR | PromiseLike<ERR>) | null): Promise<OK | ERR>;
27
32
  }
28
33
  /**
34
+ * Callable async iterator base. Adds `for await` support and disposal on return
35
+ * while delegating iteration to `next()`. Subclasses become callable, awaitable,
36
+ * and async-iterable with a single `next()` implementation.
29
37
  * @internal
30
38
  */
31
- export declare abstract class CallableAsyncIterator<T, R> extends AsyncCallable<T, R> implements Promiseable<T>, Promise<T>, AsyncIterable<T>, Disposable {
39
+ export declare abstract class CallableAsyncIterator<T, R> extends AsyncCallable<T, R> implements Promiseable<T>, Promise<T>, AsyncIterable<T> {
40
+ return?(): Promise<undefined>;
32
41
  [Symbol.asyncIterator](): AsyncIterator<T, void, void>;
33
- abstract [Symbol.dispose](): void;
34
42
  }
package/build/callable.js CHANGED
@@ -37,12 +37,12 @@ export class CallableAsyncIterator extends AsyncCallable {
37
37
  };
38
38
  }
39
39
  },
40
- return: ()=>{
41
- this[Symbol.dispose]();
42
- return Promise.resolve({
40
+ return: async ()=>{
41
+ await this.return?.();
42
+ return {
43
43
  value: undefined,
44
44
  done: true
45
- });
45
+ };
46
46
  }
47
47
  };
48
48
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/callable.ts"],"sourcesContent":["import { Fn, Promiseable } from './types.js';\n/**\n * @internal\n */\nexport interface Callable<T extends unknown[], R> {\n (...args: T): R;\n}\n\n/**\n * An abstract class that extends the built-in Function class. It allows instances of the class\n * to be called as functions. When an instance of Callable is called as a function, it will\n * call the function passed to its constructor with the same arguments.\n * @internal\n */\n\nexport abstract class Callable<T, R> {\n static {\n Object.setPrototypeOf(Callable.prototype, Function.prototype);\n }\n\n constructor(func: Fn<T, R>) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return Object.setPrototypeOf(func, new.target.prototype);\n }\n}\n\n/**\n * @internal\n */\nexport abstract class AsyncCallable<T, R> extends Callable<[T], R> implements Promiseable<T>, Promise<T> {\n constructor(func: Fn<[T], R>) {\n super(func);\n }\n\n abstract [Symbol.toStringTag]: string;\n abstract next(): Promise<T>;\n\n catch<OK = never>(onrejected?: ((reason: any) => OK | PromiseLike<OK>) | null): Promise<T | OK> {\n return this.next().catch(onrejected);\n }\n\n finally(onfinally?: (() => void) | null): Promise<T> {\n return this.next().finally(onfinally);\n }\n\n then<OK = T, ERR = never>(\n onfulfilled?: ((value: T) => OK | PromiseLike<OK>) | null,\n onrejected?: ((reason: unknown) => ERR | PromiseLike<ERR>) | null,\n ): Promise<OK | ERR> {\n return this.next().then(onfulfilled, onrejected);\n }\n}\n\n/**\n * @internal\n */\nexport abstract class CallableAsyncIterator<T, R> extends AsyncCallable<T, R> implements Promiseable<T>, Promise<T>, AsyncIterable<T>, Disposable {\n [Symbol.asyncIterator](): AsyncIterator<T, void, void> {\n return {\n next: async () => {\n try {\n const value = await this.next();\n return { value, done: false };\n } catch {\n return { value: undefined, done: true };\n }\n },\n return: () => {\n this[Symbol.dispose]();\n return Promise.resolve({ value: undefined, done: true });\n },\n };\n }\n abstract [Symbol.dispose](): void;\n}\n"],"names":["Callable","Object","setPrototypeOf","prototype","Function","func","AsyncCallable","catch","onrejected","next","finally","onfinally","then","onfulfilled","CallableAsyncIterator","Symbol","asyncIterator","value","done","undefined","return","dispose","Promise","resolve"],"mappings":"AAeA,OAAO,MAAeA;IACpB,MAAO;QACLC,OAAOC,cAAc,CAACF,SAASG,SAAS,EAAEC,SAASD,SAAS;IAC9D,CAAC;IAED,YAAYE,IAAc,CAAE;QAE1B,OAAOJ,OAAOC,cAAc,CAACG,MAAM,WAAWF,SAAS;IACzD;AACF;AAKA,OAAO,MAAeG,sBAA4BN;IAChD,YAAYK,IAAgB,CAAE;QAC5B,KAAK,CAACA;IACR;IAKAE,MAAkBC,UAA2D,EAAmB;QAC9F,OAAO,IAAI,CAACC,IAAI,GAAGF,KAAK,CAACC;IAC3B;IAEAE,QAAQC,SAA+B,EAAc;QACnD,OAAO,IAAI,CAACF,IAAI,GAAGC,OAAO,CAACC;IAC7B;IAEAC,KACEC,WAAyD,EACzDL,UAAiE,EAC9C;QACnB,OAAO,IAAI,CAACC,IAAI,GAAGG,IAAI,CAACC,aAAaL;IACvC;AACF;AAKA,OAAO,MAAeM,8BAAoCR;IACxD,CAACS,OAAOC,aAAa,CAAC,GAAiC;QACrD,OAAO;YACLP,MAAM;gBACJ,IAAI;oBACF,MAAMQ,QAAQ,MAAM,IAAI,CAACR,IAAI;oBAC7B,OAAO;wBAAEQ;wBAAOC,MAAM;oBAAM;gBAC9B,EAAE,OAAM;oBACN,OAAO;wBAAED,OAAOE;wBAAWD,MAAM;oBAAK;gBACxC;YACF;YACAE,QAAQ;gBACN,IAAI,CAACL,OAAOM,OAAO,CAAC;gBACpB,OAAOC,QAAQC,OAAO,CAAC;oBAAEN,OAAOE;oBAAWD,MAAM;gBAAK;YACxD;QACF;IACF;AAEF"}
1
+ {"version":3,"sources":["../src/callable.ts"],"sourcesContent":["import { Fn, Promiseable } from './types.js';\n\n/**\n * Makes subclasses callable like plain functions by returning the provided delegate\n * with the subclass prototype applied. Instances can be invoked directly while\n * retaining class semantics.\n * @internal\n */\nexport interface Callable<T extends unknown[], R> {\n (...args: T): R;\n}\n\n/**\n * An abstract class that extends the built-in Function class. It allows instances of the class\n * to be called as functions. When an instance of Callable is called as a function, it will\n * call the function passed to its constructor with the same arguments.\n * @internal\n */\n\nexport abstract class Callable<T, R> {\n static {\n Object.setPrototypeOf(Callable.prototype, Function.prototype);\n }\n\n constructor(func: Fn<T, R>) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return Object.setPrototypeOf(func, new.target.prototype);\n }\n}\n\n/**\n * Promise-like callable base. Subclasses implement `next()`, and the Promise methods\n * delegate to that, so instances can be awaited or chained while remaining callable.\n * @internal\n */\nexport abstract class AsyncCallable<T, R> extends Callable<[T], R> implements Promiseable<T>, Promise<T> {\n constructor(func: Fn<[T], R>) {\n super(func);\n }\n\n abstract [Symbol.toStringTag]: string;\n abstract next(): Promise<T>;\n\n catch<OK = never>(onrejected?: ((reason: any) => OK | PromiseLike<OK>) | null): Promise<T | OK> {\n return this.next().catch(onrejected);\n }\n\n finally(onfinally?: (() => void) | null): Promise<T> {\n return this.next().finally(onfinally);\n }\n\n then<OK = T, ERR = never>(\n onfulfilled?: ((value: T) => OK | PromiseLike<OK>) | null,\n onrejected?: ((reason: unknown) => ERR | PromiseLike<ERR>) | null,\n ): Promise<OK | ERR> {\n return this.next().then(onfulfilled, onrejected);\n }\n}\n\n/**\n * Callable async iterator base. Adds `for await` support and disposal on return\n * while delegating iteration to `next()`. Subclasses become callable, awaitable,\n * and async-iterable with a single `next()` implementation.\n * @internal\n */\nexport abstract class CallableAsyncIterator<T, R> extends AsyncCallable<T, R> implements Promiseable<T>, Promise<T>, AsyncIterable<T> {\n return?(): Promise<undefined>;\n\n [Symbol.asyncIterator](): AsyncIterator<T, void, void> {\n return {\n next: async () => {\n try {\n const value = await this.next();\n return { value, done: false };\n } catch {\n return { value: undefined, done: true };\n }\n },\n return: async () => {\n await this.return?.();\n return { value: undefined, done: true };\n },\n };\n }\n}\n"],"names":["Callable","Object","setPrototypeOf","prototype","Function","func","AsyncCallable","catch","onrejected","next","finally","onfinally","then","onfulfilled","CallableAsyncIterator","Symbol","asyncIterator","value","done","undefined","return"],"mappings":"AAmBA,OAAO,MAAeA;IACpB,MAAO;QACLC,OAAOC,cAAc,CAACF,SAASG,SAAS,EAAEC,SAASD,SAAS;IAC9D,CAAC;IAED,YAAYE,IAAc,CAAE;QAE1B,OAAOJ,OAAOC,cAAc,CAACG,MAAM,WAAWF,SAAS;IACzD;AACF;AAOA,OAAO,MAAeG,sBAA4BN;IAChD,YAAYK,IAAgB,CAAE;QAC5B,KAAK,CAACA;IACR;IAKAE,MAAkBC,UAA2D,EAAmB;QAC9F,OAAO,IAAI,CAACC,IAAI,GAAGF,KAAK,CAACC;IAC3B;IAEAE,QAAQC,SAA+B,EAAc;QACnD,OAAO,IAAI,CAACF,IAAI,GAAGC,OAAO,CAACC;IAC7B;IAEAC,KACEC,WAAyD,EACzDL,UAAiE,EAC9C;QACnB,OAAO,IAAI,CAACC,IAAI,GAAGG,IAAI,CAACC,aAAaL;IACvC;AACF;AAQA,OAAO,MAAeM,8BAAoCR;IAGxD,CAACS,OAAOC,aAAa,CAAC,GAAiC;QACrD,OAAO;YACLP,MAAM;gBACJ,IAAI;oBACF,MAAMQ,QAAQ,MAAM,IAAI,CAACR,IAAI;oBAC7B,OAAO;wBAAEQ;wBAAOC,MAAM;oBAAM;gBAC9B,EAAE,OAAM;oBACN,OAAO;wBAAED,OAAOE;wBAAWD,MAAM;oBAAK;gBACxC;YACF;YACAE,QAAQ;gBACN,MAAM,IAAI,CAACA,MAAM;gBACjB,OAAO;oBAAEH,OAAOE;oBAAWD,MAAM;gBAAK;YACxC;QACF;IACF;AACF"}
package/build/event.cjs CHANGED
@@ -31,37 +31,44 @@ _export(exports, {
31
31
  return merge;
32
32
  }
33
33
  });
34
- const _fastds = require("fastds");
35
34
  const _typescjs = require("./types.cjs");
36
35
  const _callablecjs = require("./callable.cjs");
37
36
  const _sequencecjs = require("./sequence.cjs");
37
+ const _listenerregistrycjs = require("./listener-registry.cjs");
38
+ const isThenable = (value)=>value !== null && typeof value === 'object' && typeof value.then === 'function';
38
39
  class Unsubscribe extends _callablecjs.Callable {
39
40
  _done = false;
40
41
  constructor(callback){
41
- super(async ()=>{
42
+ super(()=>{
42
43
  this._done = true;
43
- await callback();
44
+ return callback();
44
45
  });
45
46
  }
46
47
  get done() {
47
48
  return this._done;
48
49
  }
49
50
  pre(callback) {
50
- return new Unsubscribe(async ()=>{
51
- await callback();
52
- await this();
51
+ return new Unsubscribe(()=>{
52
+ const result = callback();
53
+ if (isThenable(result)) {
54
+ return result.then(()=>this());
55
+ }
56
+ return this();
53
57
  });
54
58
  }
55
59
  post(callback) {
56
- return new Unsubscribe(async ()=>{
57
- await this();
58
- await callback();
60
+ return new Unsubscribe(()=>{
61
+ const result = this();
62
+ if (isThenable(result)) {
63
+ return result.then(()=>callback());
64
+ }
65
+ return callback();
59
66
  });
60
67
  }
61
68
  countdown(count) {
62
- return new Unsubscribe(async ()=>{
69
+ return new Unsubscribe(()=>{
63
70
  if (!--count) {
64
- await this();
71
+ return this();
65
72
  }
66
73
  });
67
74
  }
@@ -84,25 +91,34 @@ class EventResult {
84
91
  }
85
92
  class Event extends _callablecjs.CallableAsyncIterator {
86
93
  listeners;
87
- hooks = new _fastds.RingBuffer();
94
+ hooks = new _listenerregistrycjs.ListenerRegistry();
88
95
  _disposed = false;
96
+ pending;
89
97
  dispose;
90
98
  [Symbol.toStringTag] = 'Event';
91
99
  constructor(dispose){
92
- const listeners = new _fastds.RingBuffer();
100
+ const listeners = new _listenerregistrycjs.ListenerRegistry();
93
101
  super((value)=>{
94
- const results = listeners.toArray().map(async (listener)=>listener(await value));
102
+ if (this.pending) {
103
+ this.pending.resolve(value);
104
+ this.pending = undefined;
105
+ }
106
+ const results = listeners.dispatch(value);
95
107
  return new EventResult(results);
96
108
  });
97
109
  this.listeners = listeners;
98
110
  this.dispose = ()=>{
99
111
  this._disposed = true;
112
+ if (this.pending) {
113
+ this.pending.reject(new Error('Event disposed'));
114
+ this.pending = undefined;
115
+ }
100
116
  void this.clear();
101
117
  void dispose?.();
102
118
  };
103
119
  }
104
120
  get size() {
105
- return this.listeners.length;
121
+ return this.listeners.size;
106
122
  }
107
123
  get disposed() {
108
124
  return this._disposed;
@@ -114,51 +130,47 @@ class Event extends _callablecjs.CallableAsyncIterator {
114
130
  return this.listeners.has(listener);
115
131
  }
116
132
  off(listener) {
117
- if (this.listeners.compact((l)=>l !== listener) && this.hooks.length) {
118
- [
119
- ...this.hooks
120
- ].forEach((hook)=>hook(listener, _typescjs.HookType.Remove));
133
+ if (this.listeners.off(listener)) {
134
+ void this.hooks.dispatch(listener, _typescjs.HookType.Remove);
121
135
  }
122
136
  return this;
123
137
  }
124
138
  on(listener) {
125
- this.listeners.push(listener);
126
- if (this.hooks.length) {
127
- [
128
- ...this.hooks
129
- ].forEach((hook)=>hook(listener, _typescjs.HookType.Add));
139
+ if (this.listeners.on(listener)) {
140
+ void this.hooks.dispatch(listener, _typescjs.HookType.Add);
130
141
  }
131
142
  return new Unsubscribe(()=>{
132
143
  void this.off(listener);
133
144
  });
134
145
  }
135
146
  once(listener) {
136
- const oneTimeListener = (event)=>{
137
- void this.off(oneTimeListener);
138
- return listener(event);
139
- };
140
- return this.on(oneTimeListener);
147
+ if (this.listeners.once(listener)) {
148
+ void this.hooks.dispatch(listener, _typescjs.HookType.Add);
149
+ }
150
+ return new Unsubscribe(()=>{
151
+ void this.off(listener);
152
+ });
141
153
  }
142
154
  clear() {
143
155
  this.listeners.clear();
144
- if (this.hooks.length) {
145
- [
146
- ...this.hooks
147
- ].forEach((hook)=>hook(undefined, _typescjs.HookType.Remove));
148
- }
156
+ void this.hooks.dispatch(undefined, _typescjs.HookType.Remove);
149
157
  return this;
150
158
  }
151
- async next() {
152
- const { promise, resolve } = Promise.withResolvers();
153
- this.listeners.push(resolve);
154
- return promise.finally(()=>{
155
- this.listeners.removeFirst(resolve);
156
- });
157
- }
158
- async settle() {
159
- return await Promise.allSettled([
160
- this.next()
161
- ]).then(([settled])=>settled);
159
+ next() {
160
+ if (this._disposed) {
161
+ return Promise.reject(new Error('Event disposed'));
162
+ }
163
+ this.pending ??= Promise.withResolvers();
164
+ return this.pending.promise;
165
+ }
166
+ settle() {
167
+ return this.next().then((value)=>({
168
+ status: 'fulfilled',
169
+ value
170
+ })).catch((reason)=>({
171
+ status: 'rejected',
172
+ reason
173
+ }));
162
174
  }
163
175
  [Symbol.asyncIterator]() {
164
176
  const ctrl = new AbortController();
@@ -166,23 +178,41 @@ class Event extends _callablecjs.CallableAsyncIterator {
166
178
  const emitEvent = (value)=>{
167
179
  sequence(value);
168
180
  };
169
- this.listeners.push(emitEvent);
181
+ this.listeners.on(emitEvent);
170
182
  const hook = (target = emitEvent, action)=>{
171
183
  if (target === emitEvent && action === _typescjs.HookType.Remove) {
172
184
  ctrl.abort('done');
173
- this.listeners.removeFirst(emitEvent);
185
+ this.hooks.off(hook);
186
+ }
187
+ };
188
+ this.hooks.on(hook);
189
+ const iterator = sequence[Symbol.asyncIterator]();
190
+ return {
191
+ next: (...args)=>{
192
+ return iterator.next(...args);
193
+ },
194
+ return: async ()=>{
195
+ void this.off(emitEvent);
196
+ return iterator.return?.() ?? {
197
+ value: undefined,
198
+ done: true
199
+ };
174
200
  }
175
201
  };
176
- this.hooks.push(hook);
177
- return sequence[Symbol.asyncIterator]();
178
202
  }
179
203
  [Symbol.dispose]() {
180
204
  void this.dispose();
181
205
  }
182
206
  }
183
207
  const merge = (...events)=>{
184
- const mergedEvent = new Event();
185
- events.forEach((event)=>event.on(mergedEvent));
208
+ const mergedEvent = new Event(()=>{
209
+ for (const event of events){
210
+ event.off(mergedEvent);
211
+ }
212
+ });
213
+ for (const event of events){
214
+ event.on(mergedEvent);
215
+ }
186
216
  return mergedEvent;
187
217
  };
188
218
  const createInterval = (interval)=>{