evnty 5.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +28 -28
  2. package/build/async.cjs +101 -0
  3. package/build/async.cjs.map +1 -0
  4. package/build/async.d.ts +37 -0
  5. package/build/async.js +83 -0
  6. package/build/async.js.map +1 -0
  7. package/build/broadcast.cjs +205 -0
  8. package/build/broadcast.cjs.map +1 -0
  9. package/build/broadcast.d.ts +164 -0
  10. package/build/broadcast.js +184 -0
  11. package/build/broadcast.js.map +1 -0
  12. package/build/dispatch-result.cjs +154 -0
  13. package/build/dispatch-result.cjs.map +1 -0
  14. package/build/dispatch-result.d.ts +49 -0
  15. package/build/dispatch-result.js +127 -0
  16. package/build/dispatch-result.js.map +1 -0
  17. package/build/event.cjs +92 -127
  18. package/build/event.cjs.map +1 -1
  19. package/build/event.d.ts +92 -167
  20. package/build/event.js +90 -122
  21. package/build/event.js.map +1 -1
  22. package/build/index.cjs +3 -1
  23. package/build/index.cjs.map +1 -1
  24. package/build/index.d.ts +3 -1
  25. package/build/index.js +3 -1
  26. package/build/index.js.map +1 -1
  27. package/build/iterator.cjs +578 -91
  28. package/build/iterator.cjs.map +1 -1
  29. package/build/iterator.d.ts +178 -7
  30. package/build/iterator.js +579 -92
  31. package/build/iterator.js.map +1 -1
  32. package/build/listener-registry.cjs +114 -0
  33. package/build/listener-registry.cjs.map +1 -0
  34. package/build/listener-registry.d.ts +54 -0
  35. package/build/listener-registry.js +104 -0
  36. package/build/listener-registry.js.map +1 -0
  37. package/build/ring-buffer.cjs +171 -0
  38. package/build/ring-buffer.cjs.map +1 -0
  39. package/build/ring-buffer.d.ts +80 -0
  40. package/build/ring-buffer.js +161 -0
  41. package/build/ring-buffer.js.map +1 -0
  42. package/build/sequence.cjs +34 -35
  43. package/build/sequence.cjs.map +1 -1
  44. package/build/sequence.d.ts +38 -24
  45. package/build/sequence.js +34 -35
  46. package/build/sequence.js.map +1 -1
  47. package/build/signal.cjs +26 -35
  48. package/build/signal.cjs.map +1 -1
  49. package/build/signal.d.ts +36 -39
  50. package/build/signal.js +26 -35
  51. package/build/signal.js.map +1 -1
  52. package/build/types.cjs +0 -11
  53. package/build/types.cjs.map +1 -1
  54. package/build/types.d.ts +86 -9
  55. package/build/types.js +1 -5
  56. package/build/types.js.map +1 -1
  57. package/build/utils.cjs +202 -22
  58. package/build/utils.cjs.map +1 -1
  59. package/build/utils.d.ts +85 -26
  60. package/build/utils.js +181 -22
  61. package/build/utils.js.map +1 -1
  62. package/package.json +27 -25
  63. package/src/__tests__/example.js +19 -24
  64. package/src/index.ts +3 -1
  65. package/build/callable.cjs +0 -72
  66. package/build/callable.cjs.map +0 -1
  67. package/build/callable.d.ts +0 -34
  68. package/build/callable.js +0 -51
  69. package/build/callable.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ring-buffer.ts"],"sourcesContent":["const DEFAULT_CAPACITY = 8;\n\n/**\n * @internal Fixed-size circular buffer that grows in powers of two when full.\n * Provides O(1) push/pop/shift/unshift and supports wrap-around iteration.\n */\nexport class RingBuffer<T> {\n #buffer: Array<T | undefined>;\n #head = 0;\n #tail = 0;\n #mask: number;\n #length = 0;\n #shiftCount = 0;\n\n readonly [Symbol.toStringTag] = 'RingBuffer';\n\n /**\n * Creates a RingBuffer from an array of values.\n * @param values - The values to initialize the buffer with\n * @returns A new RingBuffer containing the values\n */\n static from<T>(values: T[]): RingBuffer<T> {\n const n = values.length;\n const ring = new RingBuffer<T>(n + 1);\n for (let i = 0; i < n; i++) {\n ring.#buffer[i] = values[i];\n }\n ring.#head = 0;\n ring.#tail = n;\n ring.#length = n;\n\n return ring;\n }\n\n /**\n * Creates a ring buffer with at least the requested capacity (rounded up to power of two).\n */\n constructor(capacity: number = DEFAULT_CAPACITY) {\n const size = Math.max(1 << (32 - Math.clz32(capacity - 1)), DEFAULT_CAPACITY);\n this.#buffer = new Array<T>(size);\n this.#mask = size - 1;\n }\n\n /**\n * The number of items currently in the buffer.\n */\n get length(): number {\n return this.#length;\n }\n\n /**\n * Logical position of the buffer's left edge (total items ever shifted).\n * Monotonically increasing - useful for cursor-based consumers.\n */\n get left(): number {\n return this.#shiftCount;\n }\n\n /**\n * Logical position of the buffer's right edge (left + length).\n * Represents the next logical index for push.\n */\n get right(): number {\n return this.#shiftCount + this.#length;\n }\n\n /**\n * Returns true if the buffer has wrapped around (head > tail).\n */\n isWrapped(): boolean {\n return this.#head > this.#tail;\n }\n\n /**\n * Returns true if the buffer contains no items.\n */\n isEmpty(): boolean {\n return this.#tail === this.#head;\n }\n\n /**\n * Ensures the underlying storage can hold at least `capacity` items.\n * Preserves existing order when the buffer is wrapped.\n */\n grow(capacity: number = this.#mask + 1): void {\n const buffer = this.#buffer;\n const bufferLength = buffer.length;\n if (bufferLength >= capacity + 1) {\n return;\n }\n const size = 1 << (32 - Math.clz32(capacity));\n this.#buffer.length = size;\n\n const oldTail = this.#tail;\n if (oldTail < this.#head) {\n for (let i = 0; i < oldTail; i++) {\n buffer[bufferLength + i] = buffer[i];\n buffer[i] = undefined;\n }\n this.#tail = bufferLength + oldTail;\n }\n\n this.#mask = size - 1;\n }\n\n /**\n * Appends a value at the tail, growing if full.\n */\n push(value: T) {\n const nextTail = (this.#tail + 1) & this.#mask;\n if (nextTail === this.#head) {\n this.grow(this.#mask + 2);\n this.#buffer[this.#tail] = value;\n this.#tail = (this.#tail + 1) & this.#mask;\n } else {\n this.#buffer[this.#tail] = value;\n this.#tail = nextTail;\n }\n this.#length++;\n return this;\n }\n\n /**\n * Inserts a value at the head, growing if full.\n */\n unshift(value: T): this {\n const newHead = (this.#head - 1) & this.#mask;\n if (newHead === this.#tail) {\n this.grow(this.#mask + 2);\n this.#head = (this.#head - 1) & this.#mask;\n } else {\n this.#head = newHead;\n }\n this.#buffer[this.#head] = value;\n this.#length++;\n return this;\n }\n\n /**\n * Returns the value at the given index without removing it.\n * Index 0 is the head, index length-1 is the tail.\n */\n peek(index: number): T | undefined {\n if (index < 0 || index >= this.#length) {\n return undefined;\n }\n return this.#buffer[(this.#head + index) & this.#mask];\n }\n\n /**\n * Returns the value at the given logical index without removing it.\n * Logical index is based on total push/shift history (cursor-friendly).\n */\n peekAt(logicalIndex: number): T | undefined {\n return this.peek(logicalIndex - this.#shiftCount);\n }\n\n /**\n * Removes and returns the value at the head, or undefined if empty.\n */\n shift(): T | undefined {\n if (this.#head === this.#tail) {\n return undefined;\n }\n const value = this.#buffer[this.#head];\n this.#buffer[this.#head] = undefined;\n this.#head = (this.#head + 1) & this.#mask;\n this.#length--;\n this.#shiftCount++;\n return value;\n }\n\n /**\n * Removes n values from the head.\n */\n shiftN(n: number): number {\n const count = Math.min(n, this.#length);\n for (let i = 0; i < count; i++) {\n this.#buffer[this.#head] = undefined;\n this.#head = (this.#head + 1) & this.#mask;\n }\n this.#length -= count;\n this.#shiftCount += count;\n return count;\n }\n\n /**\n * Removes and returns the value at the tail, or undefined if empty.\n */\n pop(): T | undefined {\n if (this.#head === this.#tail) {\n return undefined;\n }\n this.#tail = (this.#tail - 1) & this.#mask;\n const value = this.#buffer[this.#tail];\n this.#buffer[this.#tail] = undefined;\n this.#length--;\n return value;\n }\n\n /**\n * Clears all entries. Optionally keeps current capacity instead of resetting to default.\n */\n clear(preserveCapacity = false): this {\n const capacity = preserveCapacity ? this.#buffer.length : DEFAULT_CAPACITY;\n this.#buffer.length = 0;\n this.#buffer.length = capacity;\n this.#head = 0;\n this.#tail = 0;\n this.#length = 0;\n this.#shiftCount = 0;\n this.#mask = capacity - 1;\n return this;\n }\n\n [Symbol.iterator](): Iterator<T, void, unknown> {\n const buffer = this.#buffer;\n const mask = this.#mask;\n const length = this.#length;\n let count = 0;\n let idx = this.#head;\n return {\n next: (): IteratorResult<T> => {\n if (count >= length) {\n return { done: true, value: undefined };\n }\n const value = buffer[idx]!;\n idx = (idx + 1) & mask;\n count++;\n return { done: false, value };\n },\n };\n }\n}\n"],"names":["RingBuffer","DEFAULT_CAPACITY","Symbol","toStringTag","from","values","n","length","ring","i","capacity","size","Math","max","clz32","Array","left","right","isWrapped","isEmpty","grow","buffer","bufferLength","oldTail","undefined","push","value","nextTail","unshift","newHead","peek","index","peekAt","logicalIndex","shift","shiftN","count","min","pop","clear","preserveCapacity","iterator","mask","idx","next","done"],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @internal Fixed-size circular buffer that grows in powers of two when full.
3
+ * Provides O(1) push/pop/shift/unshift and supports wrap-around iteration.
4
+ */
5
+ export declare class RingBuffer<T> {
6
+ #private;
7
+ readonly [Symbol.toStringTag] = "RingBuffer";
8
+ /**
9
+ * Creates a RingBuffer from an array of values.
10
+ * @param values - The values to initialize the buffer with
11
+ * @returns A new RingBuffer containing the values
12
+ */
13
+ static from<T>(values: T[]): RingBuffer<T>;
14
+ /**
15
+ * Creates a ring buffer with at least the requested capacity (rounded up to power of two).
16
+ */
17
+ constructor(capacity?: number);
18
+ /**
19
+ * The number of items currently in the buffer.
20
+ */
21
+ get length(): number;
22
+ /**
23
+ * Logical position of the buffer's left edge (total items ever shifted).
24
+ * Monotonically increasing - useful for cursor-based consumers.
25
+ */
26
+ get left(): number;
27
+ /**
28
+ * Logical position of the buffer's right edge (left + length).
29
+ * Represents the next logical index for push.
30
+ */
31
+ get right(): number;
32
+ /**
33
+ * Returns true if the buffer has wrapped around (head > tail).
34
+ */
35
+ isWrapped(): boolean;
36
+ /**
37
+ * Returns true if the buffer contains no items.
38
+ */
39
+ isEmpty(): boolean;
40
+ /**
41
+ * Ensures the underlying storage can hold at least `capacity` items.
42
+ * Preserves existing order when the buffer is wrapped.
43
+ */
44
+ grow(capacity?: number): void;
45
+ /**
46
+ * Appends a value at the tail, growing if full.
47
+ */
48
+ push(value: T): this;
49
+ /**
50
+ * Inserts a value at the head, growing if full.
51
+ */
52
+ unshift(value: T): this;
53
+ /**
54
+ * Returns the value at the given index without removing it.
55
+ * Index 0 is the head, index length-1 is the tail.
56
+ */
57
+ peek(index: number): T | undefined;
58
+ /**
59
+ * Returns the value at the given logical index without removing it.
60
+ * Logical index is based on total push/shift history (cursor-friendly).
61
+ */
62
+ peekAt(logicalIndex: number): T | undefined;
63
+ /**
64
+ * Removes and returns the value at the head, or undefined if empty.
65
+ */
66
+ shift(): T | undefined;
67
+ /**
68
+ * Removes n values from the head.
69
+ */
70
+ shiftN(n: number): number;
71
+ /**
72
+ * Removes and returns the value at the tail, or undefined if empty.
73
+ */
74
+ pop(): T | undefined;
75
+ /**
76
+ * Clears all entries. Optionally keeps current capacity instead of resetting to default.
77
+ */
78
+ clear(preserveCapacity?: boolean): this;
79
+ [Symbol.iterator](): Iterator<T, void, unknown>;
80
+ }
@@ -0,0 +1,161 @@
1
+ const DEFAULT_CAPACITY = 8;
2
+ export class RingBuffer {
3
+ #buffer;
4
+ #head = 0;
5
+ #tail = 0;
6
+ #mask;
7
+ #length = 0;
8
+ #shiftCount = 0;
9
+ [Symbol.toStringTag] = 'RingBuffer';
10
+ static from(values) {
11
+ const n = values.length;
12
+ const ring = new RingBuffer(n + 1);
13
+ for(let i = 0; i < n; i++){
14
+ ring.#buffer[i] = values[i];
15
+ }
16
+ ring.#head = 0;
17
+ ring.#tail = n;
18
+ ring.#length = n;
19
+ return ring;
20
+ }
21
+ constructor(capacity = DEFAULT_CAPACITY){
22
+ const size = Math.max(1 << 32 - Math.clz32(capacity - 1), DEFAULT_CAPACITY);
23
+ this.#buffer = new Array(size);
24
+ this.#mask = size - 1;
25
+ }
26
+ get length() {
27
+ return this.#length;
28
+ }
29
+ get left() {
30
+ return this.#shiftCount;
31
+ }
32
+ get right() {
33
+ return this.#shiftCount + this.#length;
34
+ }
35
+ isWrapped() {
36
+ return this.#head > this.#tail;
37
+ }
38
+ isEmpty() {
39
+ return this.#tail === this.#head;
40
+ }
41
+ grow(capacity = this.#mask + 1) {
42
+ const buffer = this.#buffer;
43
+ const bufferLength = buffer.length;
44
+ if (bufferLength >= capacity + 1) {
45
+ return;
46
+ }
47
+ const size = 1 << 32 - Math.clz32(capacity);
48
+ this.#buffer.length = size;
49
+ const oldTail = this.#tail;
50
+ if (oldTail < this.#head) {
51
+ for(let i = 0; i < oldTail; i++){
52
+ buffer[bufferLength + i] = buffer[i];
53
+ buffer[i] = undefined;
54
+ }
55
+ this.#tail = bufferLength + oldTail;
56
+ }
57
+ this.#mask = size - 1;
58
+ }
59
+ push(value) {
60
+ const nextTail = this.#tail + 1 & this.#mask;
61
+ if (nextTail === this.#head) {
62
+ this.grow(this.#mask + 2);
63
+ this.#buffer[this.#tail] = value;
64
+ this.#tail = this.#tail + 1 & this.#mask;
65
+ } else {
66
+ this.#buffer[this.#tail] = value;
67
+ this.#tail = nextTail;
68
+ }
69
+ this.#length++;
70
+ return this;
71
+ }
72
+ unshift(value) {
73
+ const newHead = this.#head - 1 & this.#mask;
74
+ if (newHead === this.#tail) {
75
+ this.grow(this.#mask + 2);
76
+ this.#head = this.#head - 1 & this.#mask;
77
+ } else {
78
+ this.#head = newHead;
79
+ }
80
+ this.#buffer[this.#head] = value;
81
+ this.#length++;
82
+ return this;
83
+ }
84
+ peek(index) {
85
+ if (index < 0 || index >= this.#length) {
86
+ return undefined;
87
+ }
88
+ return this.#buffer[this.#head + index & this.#mask];
89
+ }
90
+ peekAt(logicalIndex) {
91
+ return this.peek(logicalIndex - this.#shiftCount);
92
+ }
93
+ shift() {
94
+ if (this.#head === this.#tail) {
95
+ return undefined;
96
+ }
97
+ const value = this.#buffer[this.#head];
98
+ this.#buffer[this.#head] = undefined;
99
+ this.#head = this.#head + 1 & this.#mask;
100
+ this.#length--;
101
+ this.#shiftCount++;
102
+ return value;
103
+ }
104
+ shiftN(n) {
105
+ const count = Math.min(n, this.#length);
106
+ for(let i = 0; i < count; i++){
107
+ this.#buffer[this.#head] = undefined;
108
+ this.#head = this.#head + 1 & this.#mask;
109
+ }
110
+ this.#length -= count;
111
+ this.#shiftCount += count;
112
+ return count;
113
+ }
114
+ pop() {
115
+ if (this.#head === this.#tail) {
116
+ return undefined;
117
+ }
118
+ this.#tail = this.#tail - 1 & this.#mask;
119
+ const value = this.#buffer[this.#tail];
120
+ this.#buffer[this.#tail] = undefined;
121
+ this.#length--;
122
+ return value;
123
+ }
124
+ clear(preserveCapacity = false) {
125
+ const capacity = preserveCapacity ? this.#buffer.length : DEFAULT_CAPACITY;
126
+ this.#buffer.length = 0;
127
+ this.#buffer.length = capacity;
128
+ this.#head = 0;
129
+ this.#tail = 0;
130
+ this.#length = 0;
131
+ this.#shiftCount = 0;
132
+ this.#mask = capacity - 1;
133
+ return this;
134
+ }
135
+ [Symbol.iterator]() {
136
+ const buffer = this.#buffer;
137
+ const mask = this.#mask;
138
+ const length = this.#length;
139
+ let count = 0;
140
+ let idx = this.#head;
141
+ return {
142
+ next: ()=>{
143
+ if (count >= length) {
144
+ return {
145
+ done: true,
146
+ value: undefined
147
+ };
148
+ }
149
+ const value = buffer[idx];
150
+ idx = idx + 1 & mask;
151
+ count++;
152
+ return {
153
+ done: false,
154
+ value
155
+ };
156
+ }
157
+ };
158
+ }
159
+ }
160
+
161
+ //# sourceMappingURL=ring-buffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ring-buffer.ts"],"sourcesContent":["const DEFAULT_CAPACITY = 8;\n\n/**\n * @internal Fixed-size circular buffer that grows in powers of two when full.\n * Provides O(1) push/pop/shift/unshift and supports wrap-around iteration.\n */\nexport class RingBuffer<T> {\n #buffer: Array<T | undefined>;\n #head = 0;\n #tail = 0;\n #mask: number;\n #length = 0;\n #shiftCount = 0;\n\n readonly [Symbol.toStringTag] = 'RingBuffer';\n\n /**\n * Creates a RingBuffer from an array of values.\n * @param values - The values to initialize the buffer with\n * @returns A new RingBuffer containing the values\n */\n static from<T>(values: T[]): RingBuffer<T> {\n const n = values.length;\n const ring = new RingBuffer<T>(n + 1);\n for (let i = 0; i < n; i++) {\n ring.#buffer[i] = values[i];\n }\n ring.#head = 0;\n ring.#tail = n;\n ring.#length = n;\n\n return ring;\n }\n\n /**\n * Creates a ring buffer with at least the requested capacity (rounded up to power of two).\n */\n constructor(capacity: number = DEFAULT_CAPACITY) {\n const size = Math.max(1 << (32 - Math.clz32(capacity - 1)), DEFAULT_CAPACITY);\n this.#buffer = new Array<T>(size);\n this.#mask = size - 1;\n }\n\n /**\n * The number of items currently in the buffer.\n */\n get length(): number {\n return this.#length;\n }\n\n /**\n * Logical position of the buffer's left edge (total items ever shifted).\n * Monotonically increasing - useful for cursor-based consumers.\n */\n get left(): number {\n return this.#shiftCount;\n }\n\n /**\n * Logical position of the buffer's right edge (left + length).\n * Represents the next logical index for push.\n */\n get right(): number {\n return this.#shiftCount + this.#length;\n }\n\n /**\n * Returns true if the buffer has wrapped around (head > tail).\n */\n isWrapped(): boolean {\n return this.#head > this.#tail;\n }\n\n /**\n * Returns true if the buffer contains no items.\n */\n isEmpty(): boolean {\n return this.#tail === this.#head;\n }\n\n /**\n * Ensures the underlying storage can hold at least `capacity` items.\n * Preserves existing order when the buffer is wrapped.\n */\n grow(capacity: number = this.#mask + 1): void {\n const buffer = this.#buffer;\n const bufferLength = buffer.length;\n if (bufferLength >= capacity + 1) {\n return;\n }\n const size = 1 << (32 - Math.clz32(capacity));\n this.#buffer.length = size;\n\n const oldTail = this.#tail;\n if (oldTail < this.#head) {\n for (let i = 0; i < oldTail; i++) {\n buffer[bufferLength + i] = buffer[i];\n buffer[i] = undefined;\n }\n this.#tail = bufferLength + oldTail;\n }\n\n this.#mask = size - 1;\n }\n\n /**\n * Appends a value at the tail, growing if full.\n */\n push(value: T) {\n const nextTail = (this.#tail + 1) & this.#mask;\n if (nextTail === this.#head) {\n this.grow(this.#mask + 2);\n this.#buffer[this.#tail] = value;\n this.#tail = (this.#tail + 1) & this.#mask;\n } else {\n this.#buffer[this.#tail] = value;\n this.#tail = nextTail;\n }\n this.#length++;\n return this;\n }\n\n /**\n * Inserts a value at the head, growing if full.\n */\n unshift(value: T): this {\n const newHead = (this.#head - 1) & this.#mask;\n if (newHead === this.#tail) {\n this.grow(this.#mask + 2);\n this.#head = (this.#head - 1) & this.#mask;\n } else {\n this.#head = newHead;\n }\n this.#buffer[this.#head] = value;\n this.#length++;\n return this;\n }\n\n /**\n * Returns the value at the given index without removing it.\n * Index 0 is the head, index length-1 is the tail.\n */\n peek(index: number): T | undefined {\n if (index < 0 || index >= this.#length) {\n return undefined;\n }\n return this.#buffer[(this.#head + index) & this.#mask];\n }\n\n /**\n * Returns the value at the given logical index without removing it.\n * Logical index is based on total push/shift history (cursor-friendly).\n */\n peekAt(logicalIndex: number): T | undefined {\n return this.peek(logicalIndex - this.#shiftCount);\n }\n\n /**\n * Removes and returns the value at the head, or undefined if empty.\n */\n shift(): T | undefined {\n if (this.#head === this.#tail) {\n return undefined;\n }\n const value = this.#buffer[this.#head];\n this.#buffer[this.#head] = undefined;\n this.#head = (this.#head + 1) & this.#mask;\n this.#length--;\n this.#shiftCount++;\n return value;\n }\n\n /**\n * Removes n values from the head.\n */\n shiftN(n: number): number {\n const count = Math.min(n, this.#length);\n for (let i = 0; i < count; i++) {\n this.#buffer[this.#head] = undefined;\n this.#head = (this.#head + 1) & this.#mask;\n }\n this.#length -= count;\n this.#shiftCount += count;\n return count;\n }\n\n /**\n * Removes and returns the value at the tail, or undefined if empty.\n */\n pop(): T | undefined {\n if (this.#head === this.#tail) {\n return undefined;\n }\n this.#tail = (this.#tail - 1) & this.#mask;\n const value = this.#buffer[this.#tail];\n this.#buffer[this.#tail] = undefined;\n this.#length--;\n return value;\n }\n\n /**\n * Clears all entries. Optionally keeps current capacity instead of resetting to default.\n */\n clear(preserveCapacity = false): this {\n const capacity = preserveCapacity ? this.#buffer.length : DEFAULT_CAPACITY;\n this.#buffer.length = 0;\n this.#buffer.length = capacity;\n this.#head = 0;\n this.#tail = 0;\n this.#length = 0;\n this.#shiftCount = 0;\n this.#mask = capacity - 1;\n return this;\n }\n\n [Symbol.iterator](): Iterator<T, void, unknown> {\n const buffer = this.#buffer;\n const mask = this.#mask;\n const length = this.#length;\n let count = 0;\n let idx = this.#head;\n return {\n next: (): IteratorResult<T> => {\n if (count >= length) {\n return { done: true, value: undefined };\n }\n const value = buffer[idx]!;\n idx = (idx + 1) & mask;\n count++;\n return { done: false, value };\n },\n };\n }\n}\n"],"names":["DEFAULT_CAPACITY","RingBuffer","Symbol","toStringTag","from","values","n","length","ring","i","capacity","size","Math","max","clz32","Array","left","right","isWrapped","isEmpty","grow","buffer","bufferLength","oldTail","undefined","push","value","nextTail","unshift","newHead","peek","index","peekAt","logicalIndex","shift","shiftN","count","min","pop","clear","preserveCapacity","iterator","mask","idx","next","done"],"mappings":"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"}
@@ -8,61 +8,60 @@ Object.defineProperty(exports, "Sequence", {
8
8
  return Sequence;
9
9
  }
10
10
  });
11
- const _fastds = require("fastds");
12
- const _callablecjs = require("./callable.cjs");
11
+ const _asynccjs = require("./async.cjs");
13
12
  const _signalcjs = require("./signal.cjs");
14
- class Sequence extends _callablecjs.CallableAsyncIterator {
15
- abortSignal;
16
- queue;
17
- nextSignal;
18
- sendSignal;
13
+ const _ringbuffercjs = require("./ring-buffer.cjs");
14
+ class Sequence extends _asynccjs.Async {
15
+ #queue;
16
+ #nextSignal;
17
+ #sendSignal;
19
18
  [Symbol.toStringTag] = 'Sequence';
20
19
  static merge(target, ...sequences) {
21
20
  for (const source of sequences){
22
21
  queueMicrotask(async ()=>{
23
- try {
22
+ if (!target.disposed) try {
23
+ const sink = target.sink;
24
24
  for await (const value of source){
25
- target(value);
25
+ if (!sink(value)) {
26
+ return;
27
+ }
26
28
  }
27
29
  } catch {}
28
30
  });
29
31
  }
30
32
  }
31
33
  constructor(abortSignal){
32
- super((value)=>{
33
- if (this.abortSignal?.aborted) {
34
- this.nextSignal(false);
35
- return false;
36
- } else {
37
- this.queue.push(value);
38
- this.nextSignal(true);
39
- return true;
40
- }
41
- }), this.abortSignal = abortSignal;
42
- this.queue = new _fastds.RingBuffer();
43
- this.nextSignal = new _signalcjs.Signal(this.abortSignal);
44
- this.sendSignal = new _signalcjs.Signal(this.abortSignal);
45
- this.abortSignal?.addEventListener('abort', ()=>this.nextSignal(false), {
46
- once: true
47
- });
34
+ super(abortSignal);
35
+ this.#queue = new _ringbuffercjs.RingBuffer();
36
+ this.#nextSignal = new _signalcjs.Signal(abortSignal);
37
+ this.#sendSignal = new _signalcjs.Signal(abortSignal);
48
38
  }
49
39
  get size() {
50
- return this.queue.length;
40
+ return this.#queue.length;
51
41
  }
52
42
  async reserve(capacity) {
53
- while(this.queue.length > capacity){
54
- await this.sendSignal;
43
+ while(this.#queue.length > capacity){
44
+ await this.#sendSignal;
55
45
  }
56
46
  }
57
- async next() {
58
- while(!this.queue.length){
59
- await this.nextSignal;
47
+ emit(value) {
48
+ const ok = !this.disposed;
49
+ if (ok) {
50
+ this.#queue.push(value);
60
51
  }
61
- this.sendSignal();
62
- return this.queue.shift();
52
+ this.#nextSignal.emit();
53
+ return ok;
63
54
  }
64
- [Symbol.dispose]() {
65
- this.nextSignal(false);
55
+ async receive() {
56
+ while(!this.#queue.length){
57
+ await this.#nextSignal;
58
+ }
59
+ this.#sendSignal.emit();
60
+ return this.#queue.shift();
61
+ }
62
+ dispose() {
63
+ this.#sendSignal[Symbol.dispose]();
64
+ this.#nextSignal[Symbol.dispose]();
66
65
  }
67
66
  }
68
67
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/sequence.ts"],"sourcesContent":["import { RingBuffer } from 'fastds';\nimport { CallableAsyncIterator } from './callable.js';\nimport { Signal } from './signal.js';\n\n/**\n * A sequence is a FIFO (First-In-First-Out) queue for async consumption.\n * Designed for single consumer with multiple producers pattern.\n * Values are queued and consumed in order, with backpressure support.\n *\n * Key characteristics:\n * - Single consumer - values are consumed once, in order\n * - Multiple producers can push values concurrently\n * - FIFO ordering - first value in is first value out\n * - Backpressure control via reserve() method\n * - Async iteration support for continuous consumption\n *\n * @template T The type of values in the sequence.\n *\n * ```typescript\n * // Create a sequence for processing tasks\n * const tasks = new Sequence<string>();\n *\n * // Producer: Add tasks to the queue\n * tasks('task1');\n * tasks('task2');\n * tasks('task3');\n *\n * // Consumer: Process tasks in order\n * const task1 = await tasks.next(); // 'task1'\n * const task2 = await tasks.next(); // 'task2'\n * const task3 = await tasks.next(); // 'task3'\n * ```\n */\nexport class Sequence<T> extends CallableAsyncIterator<T, boolean> {\n private queue: RingBuffer<T>;\n private nextSignal: Signal<boolean>;\n private sendSignal: Signal<void>;\n\n readonly [Symbol.toStringTag] = 'Sequence';\n\n /**\n * Merges multiple source sequences into a target sequence.\n * Values from all sources are forwarded to the target sequence.\n * Each source is consumed independently and concurrently.\n *\n * @param target The sequence that will receive values from all sources\n * @param sequences The source sequences to merge from\n *\n * ```typescript\n * // Create target and source sequences\n * const target = new Sequence<number>();\n * const source1 = new Sequence<number>();\n * const source2 = new Sequence<number>();\n *\n * // Merge sources into target\n * Sequence.merge(target, source1, source2);\n *\n * // Values from both sources appear in target\n * source1(1);\n * source2(2);\n * source1(3);\n *\n * // Consumer gets values as they arrive\n * await target.next(); // Could be 1, 2, or 3 depending on timing\n * ```\n */\n static merge<T>(target: Sequence<T>, ...sequences: Sequence<T>[]): void {\n for (const source of sequences) {\n queueMicrotask(async () => {\n try {\n for await (const value of source) {\n target(value);\n }\n } catch {\n // sequence is aborted\n }\n });\n }\n }\n\n constructor(private readonly abortSignal?: AbortSignal) {\n super((value: T) => {\n if (this.abortSignal?.aborted) {\n this.nextSignal(false);\n return false;\n } else {\n this.queue.push(value);\n this.nextSignal(true);\n return true;\n }\n });\n this.queue = new RingBuffer();\n this.nextSignal = new Signal(this.abortSignal);\n this.sendSignal = new Signal(this.abortSignal);\n this.abortSignal?.addEventListener('abort', () => this.nextSignal(false), { once: true });\n }\n\n /**\n * Returns the number of values currently queued.\n *\n * @returns The current queue size\n */\n get size(): number {\n return this.queue.length;\n }\n\n /**\n * Waits until the queue size drops to or below the specified capacity.\n * Useful for implementing backpressure - producers can wait before adding more items.\n *\n * @param capacity The maximum queue size to wait for\n * @returns A promise that resolves when the queue size is at or below capacity\n *\n * ```typescript\n * // Producer with backpressure control\n * const sequence = new Sequence<string>();\n *\n * // Wait if queue has more than 10 items\n * await sequence.reserve(10);\n * sequence('new item'); // Safe to add, queue has space\n * ```\n */\n async reserve(capacity: number): Promise<void> {\n while (this.queue.length > capacity) {\n await this.sendSignal;\n }\n }\n\n /**\n * Consumes and returns the next value from the queue.\n * If the queue is empty, waits for a value to be added.\n * Values are consumed in FIFO order.\n *\n * @returns A promise that resolves with the next value\n *\n * ```typescript\n * const sequence = new Sequence<number>();\n *\n * // Consumer waits for values\n * const valuePromise = sequence.next();\n *\n * // Producer adds value\n * sequence(42);\n *\n * // Consumer receives it\n * const value = await valuePromise; // 42\n * ```\n */\n async next(): Promise<T> {\n while (!this.queue.length) {\n await this.nextSignal;\n }\n this.sendSignal();\n return this.queue.shift()!;\n }\n\n /**\n * Disposes of the sequence, signaling any waiting consumers.\n * Called automatically when used with `using` declaration.\n */\n [Symbol.dispose](): void {\n this.nextSignal(false);\n }\n}\n"],"names":["Sequence","CallableAsyncIterator","queue","nextSignal","sendSignal","Symbol","toStringTag","merge","target","sequences","source","queueMicrotask","value","abortSignal","aborted","push","RingBuffer","Signal","addEventListener","once","size","length","reserve","capacity","next","shift","dispose"],"mappings":";;;;+BAiCaA;;;eAAAA;;;wBAjCc;6BACW;2BACf;AA+BhB,MAAMA,iBAAoBC,kCAAqB;;IAC5CC,MAAqB;IACrBC,WAA4B;IAC5BC,WAAyB;IAExB,CAACC,OAAOC,WAAW,CAAC,GAAG,WAAW;IA4B3C,OAAOC,MAASC,MAAmB,EAAE,GAAGC,SAAwB,EAAQ;QACtE,KAAK,MAAMC,UAAUD,UAAW;YAC9BE,eAAe;gBACb,IAAI;oBACF,WAAW,MAAMC,SAASF,OAAQ;wBAChCF,OAAOI;oBACT;gBACF,EAAE,OAAM,CAER;YACF;QACF;IACF;IAEA,YAAY,AAAiBC,WAAyB,CAAE;QACtD,KAAK,CAAC,CAACD;YACL,IAAI,IAAI,CAACC,WAAW,EAAEC,SAAS;gBAC7B,IAAI,CAACX,UAAU,CAAC;gBAChB,OAAO;YACT,OAAO;gBACL,IAAI,CAACD,KAAK,CAACa,IAAI,CAACH;gBAChB,IAAI,CAACT,UAAU,CAAC;gBAChB,OAAO;YACT;QACF,SAV2BU,cAAAA;QAW3B,IAAI,CAACX,KAAK,GAAG,IAAIc,kBAAU;QAC3B,IAAI,CAACb,UAAU,GAAG,IAAIc,iBAAM,CAAC,IAAI,CAACJ,WAAW;QAC7C,IAAI,CAACT,UAAU,GAAG,IAAIa,iBAAM,CAAC,IAAI,CAACJ,WAAW;QAC7C,IAAI,CAACA,WAAW,EAAEK,iBAAiB,SAAS,IAAM,IAAI,CAACf,UAAU,CAAC,QAAQ;YAAEgB,MAAM;QAAK;IACzF;IAOA,IAAIC,OAAe;QACjB,OAAO,IAAI,CAAClB,KAAK,CAACmB,MAAM;IAC1B;IAkBA,MAAMC,QAAQC,QAAgB,EAAiB;QAC7C,MAAO,IAAI,CAACrB,KAAK,CAACmB,MAAM,GAAGE,SAAU;YACnC,MAAM,IAAI,CAACnB,UAAU;QACvB;IACF;IAsBA,MAAMoB,OAAmB;QACvB,MAAO,CAAC,IAAI,CAACtB,KAAK,CAACmB,MAAM,CAAE;YACzB,MAAM,IAAI,CAAClB,UAAU;QACvB;QACA,IAAI,CAACC,UAAU;QACf,OAAO,IAAI,CAACF,KAAK,CAACuB,KAAK;IACzB;IAMA,CAACpB,OAAOqB,OAAO,CAAC,GAAS;QACvB,IAAI,CAACvB,UAAU,CAAC;IAClB;AACF"}
1
+ {"version":3,"sources":["../src/sequence.ts"],"sourcesContent":["import { Async } from './async.js';\nimport { Signal } from './signal.js';\nimport { RingBuffer } from './ring-buffer.js';\n\n/**\n * A sequence is a FIFO (First-In-First-Out) queue for async consumption.\n * Designed for single consumer with multiple producers pattern.\n * Values are queued and consumed in order, with backpressure support.\n * Respects an optional AbortSignal: emit() returns false when aborted; waits reject.\n *\n * Key characteristics:\n * - Single consumer - values are consumed once, in order\n * - Multiple producers can push values concurrently\n * - FIFO ordering - first value in is first value out\n * - Backpressure control via reserve() method\n * - Async iteration support for continuous consumption\n *\n * @template T The type of values in the sequence.\n *\n * @example\n * ```typescript\n * // Create a sequence for processing tasks\n * const tasks = new Sequence<string>();\n *\n * // Producer: Add tasks to the queue\n * tasks.emit('task1');\n * tasks.emit('task2');\n * tasks.emit('task3');\n *\n * // Consumer: Process tasks in order\n * const task1 = await tasks.receive(); // 'task1'\n * const task2 = await tasks.receive(); // 'task2'\n * const task3 = await tasks.receive(); // 'task3'\n * ```\n */\nexport class Sequence<T> extends Async<T, boolean> {\n #queue: RingBuffer<T>;\n #nextSignal: Signal<void>;\n #sendSignal: Signal<void>;\n\n readonly [Symbol.toStringTag] = 'Sequence';\n\n /**\n * Merges multiple source sequences into a target sequence.\n * Values from all sources are forwarded to the target sequence.\n * Each source is consumed independently and concurrently.\n *\n * @param target The sequence that will receive values from all sources\n * @param sequences The source sequences to merge from\n *\n * @example\n * ```typescript\n * // Create target and source sequences\n * const target = new Sequence<number>();\n * const source1 = new Sequence<number>();\n * const source2 = new Sequence<number>();\n *\n * // Merge sources into target\n * Sequence.merge(target, source1, source2);\n *\n * // Values from both sources appear in target\n * source1.emit(1);\n * source2.emit(2);\n * source1.emit(3);\n *\n * // Consumer gets values as they arrive\n * await target.receive(); // Could be 1, 2, or 3 depending on timing\n * ```\n */\n static merge<T>(target: Sequence<T>, ...sequences: Sequence<T>[]): void {\n for (const source of sequences) {\n queueMicrotask(async () => {\n if (!target.disposed)\n try {\n const sink = target.sink;\n for await (const value of source) {\n if (!sink(value)) {\n return;\n }\n }\n } catch {\n // sequence is disposed\n }\n });\n }\n }\n\n /**\n * Creates a new Sequence instance.\n * @param abortSignal - Optional AbortSignal to cancel pending operations\n */\n constructor(abortSignal?: AbortSignal) {\n super(abortSignal);\n this.#queue = new RingBuffer();\n this.#nextSignal = new Signal(abortSignal);\n this.#sendSignal = new Signal(abortSignal);\n }\n\n /**\n * Returns the number of values currently queued.\n *\n * @returns The current queue size\n */\n get size(): number {\n return this.#queue.length;\n }\n\n /**\n * Waits until the queue size drops to or below the specified capacity.\n * Useful for implementing backpressure - producers can wait before adding more items.\n *\n * @param capacity The maximum queue size to wait for\n * @returns A promise that resolves when the queue size is at or below capacity\n *\n * @example\n * ```typescript\n * // Producer with backpressure control\n * const sequence = new Sequence<string>();\n *\n * // Wait if queue has more than 10 items\n * await sequence.reserve(10);\n * sequence.emit('new item'); // Safe to add, queue has space\n * ```\n */\n async reserve(capacity: number): Promise<void> {\n while (this.#queue.length > capacity) {\n await this.#sendSignal;\n }\n }\n\n /**\n * Pushes a value onto the queue. Wakes any pending `receive()` waiter.\n *\n * @param value - The value to enqueue.\n * @returns `true` if the sequence is still active.\n */\n emit(value: T): boolean {\n const ok = !this.disposed;\n if (ok) {\n this.#queue.push(value);\n }\n this.#nextSignal.emit();\n return ok;\n }\n\n /**\n * Consumes and returns the next value from the queue.\n * If the queue is empty, waits for a value to be added.\n * Values are consumed in FIFO order.\n * If the sequence has been aborted or disposed, this method rejects with Error('Disposed').\n *\n * @returns A promise that resolves with the next value\n *\n * @example\n * ```typescript\n * const sequence = new Sequence<number>();\n *\n * // Consumer waits for values\n * const valuePromise = sequence.receive();\n *\n * // Producer adds value\n * sequence.emit(42);\n *\n * // Consumer receives it\n * const value = await valuePromise; // 42\n * ```\n */\n async receive(): Promise<T> {\n while (!this.#queue.length) {\n await this.#nextSignal;\n }\n this.#sendSignal.emit();\n return this.#queue.shift()!;\n }\n\n /**\n * Disposes of the sequence, rejecting any pending `receive()` waiters.\n * Called by `[Symbol.dispose]()` (inherited from Async) when using the `using` declaration.\n */\n dispose(): void {\n this.#sendSignal[Symbol.dispose]();\n this.#nextSignal[Symbol.dispose]();\n }\n}\n"],"names":["Sequence","Async","Symbol","toStringTag","merge","target","sequences","source","queueMicrotask","disposed","sink","value","abortSignal","RingBuffer","Signal","size","length","reserve","capacity","emit","ok","push","receive","shift","dispose"],"mappings":";;;;+BAmCaA;;;eAAAA;;;0BAnCS;2BACC;+BACI;AAiCpB,MAAMA,iBAAoBC,eAAK;IACpC,CAAA,KAAM,CAAgB;IACtB,CAAA,UAAW,CAAe;IAC1B,CAAA,UAAW,CAAe;IAEjB,CAACC,OAAOC,WAAW,CAAC,GAAG,WAAW;IA6B3C,OAAOC,MAASC,MAAmB,EAAE,GAAGC,SAAwB,EAAQ;QACtE,KAAK,MAAMC,UAAUD,UAAW;YAC9BE,eAAe;gBACb,IAAI,CAACH,OAAOI,QAAQ,EAClB,IAAI;oBACF,MAAMC,OAAOL,OAAOK,IAAI;oBACxB,WAAW,MAAMC,SAASJ,OAAQ;wBAChC,IAAI,CAACG,KAAKC,QAAQ;4BAChB;wBACF;oBACF;gBACF,EAAE,OAAM,CAER;YACJ;QACF;IACF;IAMA,YAAYC,WAAyB,CAAE;QACrC,KAAK,CAACA;QACN,IAAI,CAAC,CAAA,KAAM,GAAG,IAAIC,yBAAU;QAC5B,IAAI,CAAC,CAAA,UAAW,GAAG,IAAIC,iBAAM,CAACF;QAC9B,IAAI,CAAC,CAAA,UAAW,GAAG,IAAIE,iBAAM,CAACF;IAChC;IAOA,IAAIG,OAAe;QACjB,OAAO,IAAI,CAAC,CAAA,KAAM,CAACC,MAAM;IAC3B;IAmBA,MAAMC,QAAQC,QAAgB,EAAiB;QAC7C,MAAO,IAAI,CAAC,CAAA,KAAM,CAACF,MAAM,GAAGE,SAAU;YACpC,MAAM,IAAI,CAAC,CAAA,UAAW;QACxB;IACF;IAQAC,KAAKR,KAAQ,EAAW;QACtB,MAAMS,KAAK,CAAC,IAAI,CAACX,QAAQ;QACzB,IAAIW,IAAI;YACN,IAAI,CAAC,CAAA,KAAM,CAACC,IAAI,CAACV;QACnB;QACA,IAAI,CAAC,CAAA,UAAW,CAACQ,IAAI;QACrB,OAAOC;IACT;IAwBA,MAAME,UAAsB;QAC1B,MAAO,CAAC,IAAI,CAAC,CAAA,KAAM,CAACN,MAAM,CAAE;YAC1B,MAAM,IAAI,CAAC,CAAA,UAAW;QACxB;QACA,IAAI,CAAC,CAAA,UAAW,CAACG,IAAI;QACrB,OAAO,IAAI,CAAC,CAAA,KAAM,CAACI,KAAK;IAC1B;IAMAC,UAAgB;QACd,IAAI,CAAC,CAAA,UAAW,CAACtB,OAAOsB,OAAO,CAAC;QAChC,IAAI,CAAC,CAAA,UAAW,CAACtB,OAAOsB,OAAO,CAAC;IAClC;AACF"}
@@ -1,8 +1,9 @@
1
- import { CallableAsyncIterator } from './callable.js';
1
+ import { Async } from './async.js';
2
2
  /**
3
3
  * A sequence is a FIFO (First-In-First-Out) queue for async consumption.
4
4
  * Designed for single consumer with multiple producers pattern.
5
5
  * Values are queued and consumed in order, with backpressure support.
6
+ * Respects an optional AbortSignal: emit() returns false when aborted; waits reject.
6
7
  *
7
8
  * Key characteristics:
8
9
  * - Single consumer - values are consumed once, in order
@@ -13,26 +14,24 @@ import { CallableAsyncIterator } from './callable.js';
13
14
  *
14
15
  * @template T The type of values in the sequence.
15
16
  *
17
+ * @example
16
18
  * ```typescript
17
19
  * // Create a sequence for processing tasks
18
20
  * const tasks = new Sequence<string>();
19
21
  *
20
22
  * // Producer: Add tasks to the queue
21
- * tasks('task1');
22
- * tasks('task2');
23
- * tasks('task3');
23
+ * tasks.emit('task1');
24
+ * tasks.emit('task2');
25
+ * tasks.emit('task3');
24
26
  *
25
27
  * // Consumer: Process tasks in order
26
- * const task1 = await tasks.next(); // 'task1'
27
- * const task2 = await tasks.next(); // 'task2'
28
- * const task3 = await tasks.next(); // 'task3'
28
+ * const task1 = await tasks.receive(); // 'task1'
29
+ * const task2 = await tasks.receive(); // 'task2'
30
+ * const task3 = await tasks.receive(); // 'task3'
29
31
  * ```
30
32
  */
31
- export declare class Sequence<T> extends CallableAsyncIterator<T, boolean> {
32
- private readonly abortSignal?;
33
- private queue;
34
- private nextSignal;
35
- private sendSignal;
33
+ export declare class Sequence<T> extends Async<T, boolean> {
34
+ #private;
36
35
  readonly [Symbol.toStringTag] = "Sequence";
37
36
  /**
38
37
  * Merges multiple source sequences into a target sequence.
@@ -42,6 +41,7 @@ export declare class Sequence<T> extends CallableAsyncIterator<T, boolean> {
42
41
  * @param target The sequence that will receive values from all sources
43
42
  * @param sequences The source sequences to merge from
44
43
  *
44
+ * @example
45
45
  * ```typescript
46
46
  * // Create target and source sequences
47
47
  * const target = new Sequence<number>();
@@ -52,16 +52,20 @@ export declare class Sequence<T> extends CallableAsyncIterator<T, boolean> {
52
52
  * Sequence.merge(target, source1, source2);
53
53
  *
54
54
  * // Values from both sources appear in target
55
- * source1(1);
56
- * source2(2);
57
- * source1(3);
55
+ * source1.emit(1);
56
+ * source2.emit(2);
57
+ * source1.emit(3);
58
58
  *
59
59
  * // Consumer gets values as they arrive
60
- * await target.next(); // Could be 1, 2, or 3 depending on timing
60
+ * await target.receive(); // Could be 1, 2, or 3 depending on timing
61
61
  * ```
62
62
  */
63
63
  static merge<T>(target: Sequence<T>, ...sequences: Sequence<T>[]): void;
64
- constructor(abortSignal?: AbortSignal | undefined);
64
+ /**
65
+ * Creates a new Sequence instance.
66
+ * @param abortSignal - Optional AbortSignal to cancel pending operations
67
+ */
68
+ constructor(abortSignal?: AbortSignal);
65
69
  /**
66
70
  * Returns the number of values currently queued.
67
71
  *
@@ -75,40 +79,50 @@ export declare class Sequence<T> extends CallableAsyncIterator<T, boolean> {
75
79
  * @param capacity The maximum queue size to wait for
76
80
  * @returns A promise that resolves when the queue size is at or below capacity
77
81
  *
82
+ * @example
78
83
  * ```typescript
79
84
  * // Producer with backpressure control
80
85
  * const sequence = new Sequence<string>();
81
86
  *
82
87
  * // Wait if queue has more than 10 items
83
88
  * await sequence.reserve(10);
84
- * sequence('new item'); // Safe to add, queue has space
89
+ * sequence.emit('new item'); // Safe to add, queue has space
85
90
  * ```
86
91
  */
87
92
  reserve(capacity: number): Promise<void>;
93
+ /**
94
+ * Pushes a value onto the queue. Wakes any pending `receive()` waiter.
95
+ *
96
+ * @param value - The value to enqueue.
97
+ * @returns `true` if the sequence is still active.
98
+ */
99
+ emit(value: T): boolean;
88
100
  /**
89
101
  * Consumes and returns the next value from the queue.
90
102
  * If the queue is empty, waits for a value to be added.
91
103
  * Values are consumed in FIFO order.
104
+ * If the sequence has been aborted or disposed, this method rejects with Error('Disposed').
92
105
  *
93
106
  * @returns A promise that resolves with the next value
94
107
  *
108
+ * @example
95
109
  * ```typescript
96
110
  * const sequence = new Sequence<number>();
97
111
  *
98
112
  * // Consumer waits for values
99
- * const valuePromise = sequence.next();
113
+ * const valuePromise = sequence.receive();
100
114
  *
101
115
  * // Producer adds value
102
- * sequence(42);
116
+ * sequence.emit(42);
103
117
  *
104
118
  * // Consumer receives it
105
119
  * const value = await valuePromise; // 42
106
120
  * ```
107
121
  */
108
- next(): Promise<T>;
122
+ receive(): Promise<T>;
109
123
  /**
110
- * Disposes of the sequence, signaling any waiting consumers.
111
- * Called automatically when used with `using` declaration.
124
+ * Disposes of the sequence, rejecting any pending `receive()` waiters.
125
+ * Called by `[Symbol.dispose]()` (inherited from Async) when using the `using` declaration.
112
126
  */
113
- [Symbol.dispose](): void;
127
+ dispose(): void;
114
128
  }