@okikio/observables 1.0.2

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 (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +578 -0
  3. package/esm/_dnt.polyfills.d.ts +20 -0
  4. package/esm/_dnt.polyfills.d.ts.map +1 -0
  5. package/esm/_dnt.polyfills.js +12 -0
  6. package/esm/_spec.d.ts +260 -0
  7. package/esm/_spec.d.ts.map +1 -0
  8. package/esm/_spec.js +1 -0
  9. package/esm/_types.d.ts +141 -0
  10. package/esm/_types.d.ts.map +1 -0
  11. package/esm/_types.js +20 -0
  12. package/esm/error.d.ts +331 -0
  13. package/esm/error.d.ts.map +1 -0
  14. package/esm/error.js +408 -0
  15. package/esm/events.d.ts +320 -0
  16. package/esm/events.d.ts.map +1 -0
  17. package/esm/events.js +451 -0
  18. package/esm/helpers/_types.d.ts +188 -0
  19. package/esm/helpers/_types.d.ts.map +1 -0
  20. package/esm/helpers/_types.js +1 -0
  21. package/esm/helpers/mod.d.ts +90 -0
  22. package/esm/helpers/mod.d.ts.map +1 -0
  23. package/esm/helpers/mod.js +90 -0
  24. package/esm/helpers/operations/batch.d.ts +109 -0
  25. package/esm/helpers/operations/batch.d.ts.map +1 -0
  26. package/esm/helpers/operations/batch.js +140 -0
  27. package/esm/helpers/operations/combination.d.ts +162 -0
  28. package/esm/helpers/operations/combination.d.ts.map +1 -0
  29. package/esm/helpers/operations/combination.js +350 -0
  30. package/esm/helpers/operations/conditional.d.ts +211 -0
  31. package/esm/helpers/operations/conditional.d.ts.map +1 -0
  32. package/esm/helpers/operations/conditional.js +280 -0
  33. package/esm/helpers/operations/core.d.ts +198 -0
  34. package/esm/helpers/operations/core.d.ts.map +1 -0
  35. package/esm/helpers/operations/core.js +264 -0
  36. package/esm/helpers/operations/errors.d.ts +277 -0
  37. package/esm/helpers/operations/errors.d.ts.map +1 -0
  38. package/esm/helpers/operations/errors.js +378 -0
  39. package/esm/helpers/operations/mod.d.ts +26 -0
  40. package/esm/helpers/operations/mod.d.ts.map +1 -0
  41. package/esm/helpers/operations/mod.js +25 -0
  42. package/esm/helpers/operations/timing.d.ts +206 -0
  43. package/esm/helpers/operations/timing.d.ts.map +1 -0
  44. package/esm/helpers/operations/timing.js +457 -0
  45. package/esm/helpers/operators.d.ts +520 -0
  46. package/esm/helpers/operators.d.ts.map +1 -0
  47. package/esm/helpers/operators.js +563 -0
  48. package/esm/helpers/pipe.d.ts +118 -0
  49. package/esm/helpers/pipe.d.ts.map +1 -0
  50. package/esm/helpers/pipe.js +129 -0
  51. package/esm/helpers/utils.d.ts +142 -0
  52. package/esm/helpers/utils.d.ts.map +1 -0
  53. package/esm/helpers/utils.js +193 -0
  54. package/esm/mod.d.ts +863 -0
  55. package/esm/mod.d.ts.map +1 -0
  56. package/esm/mod.js +861 -0
  57. package/esm/observable.d.ts +1610 -0
  58. package/esm/observable.d.ts.map +1 -0
  59. package/esm/observable.js +1970 -0
  60. package/esm/package.json +3 -0
  61. package/esm/queue.d.ts +201 -0
  62. package/esm/queue.d.ts.map +1 -0
  63. package/esm/queue.js +273 -0
  64. package/esm/symbol.d.ts +60 -0
  65. package/esm/symbol.d.ts.map +1 -0
  66. package/esm/symbol.js +132 -0
  67. package/package.json +96 -0
  68. package/script/_dnt.polyfills.d.ts +20 -0
  69. package/script/_dnt.polyfills.d.ts.map +1 -0
  70. package/script/_dnt.polyfills.js +13 -0
  71. package/script/_spec.d.ts +260 -0
  72. package/script/_spec.d.ts.map +1 -0
  73. package/script/_spec.js +2 -0
  74. package/script/_types.d.ts +141 -0
  75. package/script/_types.d.ts.map +1 -0
  76. package/script/_types.js +22 -0
  77. package/script/error.d.ts +331 -0
  78. package/script/error.d.ts.map +1 -0
  79. package/script/error.js +414 -0
  80. package/script/events.d.ts +320 -0
  81. package/script/events.d.ts.map +1 -0
  82. package/script/events.js +458 -0
  83. package/script/helpers/_types.d.ts +188 -0
  84. package/script/helpers/_types.d.ts.map +1 -0
  85. package/script/helpers/_types.js +2 -0
  86. package/script/helpers/mod.d.ts +90 -0
  87. package/script/helpers/mod.d.ts.map +1 -0
  88. package/script/helpers/mod.js +106 -0
  89. package/script/helpers/operations/batch.d.ts +109 -0
  90. package/script/helpers/operations/batch.d.ts.map +1 -0
  91. package/script/helpers/operations/batch.js +144 -0
  92. package/script/helpers/operations/combination.d.ts +162 -0
  93. package/script/helpers/operations/combination.d.ts.map +1 -0
  94. package/script/helpers/operations/combination.js +355 -0
  95. package/script/helpers/operations/conditional.d.ts +211 -0
  96. package/script/helpers/operations/conditional.d.ts.map +1 -0
  97. package/script/helpers/operations/conditional.js +286 -0
  98. package/script/helpers/operations/core.d.ts +198 -0
  99. package/script/helpers/operations/core.d.ts.map +1 -0
  100. package/script/helpers/operations/core.js +272 -0
  101. package/script/helpers/operations/errors.d.ts +277 -0
  102. package/script/helpers/operations/errors.d.ts.map +1 -0
  103. package/script/helpers/operations/errors.js +387 -0
  104. package/script/helpers/operations/mod.d.ts +26 -0
  105. package/script/helpers/operations/mod.d.ts.map +1 -0
  106. package/script/helpers/operations/mod.js +41 -0
  107. package/script/helpers/operations/timing.d.ts +206 -0
  108. package/script/helpers/operations/timing.d.ts.map +1 -0
  109. package/script/helpers/operations/timing.js +464 -0
  110. package/script/helpers/operators.d.ts +520 -0
  111. package/script/helpers/operators.d.ts.map +1 -0
  112. package/script/helpers/operators.js +570 -0
  113. package/script/helpers/pipe.d.ts +118 -0
  114. package/script/helpers/pipe.d.ts.map +1 -0
  115. package/script/helpers/pipe.js +132 -0
  116. package/script/helpers/utils.d.ts +142 -0
  117. package/script/helpers/utils.d.ts.map +1 -0
  118. package/script/helpers/utils.js +200 -0
  119. package/script/mod.d.ts +863 -0
  120. package/script/mod.d.ts.map +1 -0
  121. package/script/mod.js +877 -0
  122. package/script/observable.d.ts +1610 -0
  123. package/script/observable.d.ts.map +1 -0
  124. package/script/observable.js +1984 -0
  125. package/script/package.json +3 -0
  126. package/script/queue.d.ts +201 -0
  127. package/script/queue.d.ts.map +1 -0
  128. package/script/queue.js +286 -0
  129. package/script/symbol.d.ts +60 -0
  130. package/script/symbol.d.ts.map +1 -0
  131. package/script/symbol.js +135 -0
@@ -0,0 +1,458 @@
1
+ "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var _EventBus_subscribers, _EventBus_closed;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.EventBus = void 0;
16
+ exports.createEventDispatcher = createEventDispatcher;
17
+ exports.waitForEvent = waitForEvent;
18
+ exports.withReplay = withReplay;
19
+ /**
20
+ * Multicast event primitives built on top of the Observable runtime.
21
+ *
22
+ * This entrypoint is for the hot side of the library: shared event streams that
23
+ * multiple consumers can listen to at the same time. It exports `EventBus` for
24
+ * one-to-many pub/sub, `createEventDispatcher` for named and type-safe events,
25
+ * and helpers such as `withReplay` and `waitForEvent` for common coordination
26
+ * patterns.
27
+ *
28
+ * Use `Observable` when each subscription should start fresh work. Use this
29
+ * module when one emission should fan out to many listeners, such as UI events,
30
+ * app-wide notifications, or workflow status updates.
31
+ *
32
+ * @module
33
+ */
34
+ require("./_dnt.polyfills.js");
35
+ const observable_js_1 = require("./observable.js");
36
+ const symbol_js_1 = require("./symbol.js");
37
+ const queue_js_1 = require("./queue.js"); // Assume path to your queue utils
38
+ /**
39
+ * A multicast event bus that extends {@link Observable<T>}, allowing
40
+ * emission of values to multiple subscribers and supporting both
41
+ * Observer-style and async-iterator consumption.
42
+ *
43
+ * @typeParam T - The type of values emitted by this bus.
44
+ *
45
+ * - Calling {@link emit} delivers the value to all active subscribers.
46
+ * - Calling {@link close} completes all subscribers and prevents further emissions.
47
+ * - Implements both {@link Symbol.dispose} and {@link Symbol.asyncDispose}
48
+ * for cleanup in synchronous and asynchronous contexts.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { EventBus } from './EventBus.ts';
53
+ *
54
+ * // Create a bus for string messages
55
+ * const bus = new EventBus<string>();
56
+ *
57
+ * // Subscribe using Observer
58
+ * bus.events.subscribe({
59
+ * next(msg) { console.log('Received:', msg); },
60
+ * complete() { console.log('Bus closed'); }
61
+ * });
62
+ *
63
+ * // Emit values
64
+ * bus.emit('hello');
65
+ * bus.emit('world');
66
+ *
67
+ * // Close the bus
68
+ * bus.close();
69
+ * ```
70
+ */
71
+ class EventBus extends observable_js_1.Observable {
72
+ /**
73
+ * Construct a new EventBus instance.
74
+ *
75
+ * The base {@link Observable} constructor is invoked with the subscriber
76
+ * registration logic, adding and removing subscribers to the internal set.
77
+ */
78
+ constructor() {
79
+ super((subscriber) => {
80
+ if (__classPrivateFieldGet(this, _EventBus_closed, "f")) {
81
+ subscriber.complete?.();
82
+ return;
83
+ }
84
+ __classPrivateFieldGet(this, _EventBus_subscribers, "f").add(subscriber);
85
+ return () => {
86
+ __classPrivateFieldGet(this, _EventBus_subscribers, "f").delete(subscriber);
87
+ };
88
+ });
89
+ /** Active subscribers receiving emitted values */
90
+ _EventBus_subscribers.set(this, new Set());
91
+ /** Tracks whether the bus has been closed */
92
+ _EventBus_closed.set(this, false);
93
+ }
94
+ /**
95
+ * Exposes the bus itself as an {@link Observable<T>} for subscription.
96
+ *
97
+ * @returns The current instance as an Observable of T.
98
+ */
99
+ get events() {
100
+ return this;
101
+ }
102
+ /**
103
+ * Emit a value to all active subscribers.
104
+ *
105
+ * @param value - The value to deliver.
106
+ */
107
+ emit(value) {
108
+ if (__classPrivateFieldGet(this, _EventBus_closed, "f"))
109
+ return;
110
+ for (const subscriber of __classPrivateFieldGet(this, _EventBus_subscribers, "f")) {
111
+ subscriber.next?.(value);
112
+ }
113
+ }
114
+ /**
115
+ * Close the bus, completing all subscribers and preventing further emits.
116
+ */
117
+ close() {
118
+ if (__classPrivateFieldGet(this, _EventBus_closed, "f"))
119
+ return;
120
+ __classPrivateFieldSet(this, _EventBus_closed, true, "f");
121
+ for (const subscriber of __classPrivateFieldGet(this, _EventBus_subscribers, "f")) {
122
+ subscriber.complete?.();
123
+ }
124
+ __classPrivateFieldGet(this, _EventBus_subscribers, "f").clear();
125
+ }
126
+ /**
127
+ * Synchronous disposal method (for `using` syntax).
128
+ *
129
+ * Alias for {@link close}.
130
+ */
131
+ [(_EventBus_subscribers = new WeakMap(), _EventBus_closed = new WeakMap(), symbol_js_1.Symbol.dispose)]() {
132
+ this.close();
133
+ }
134
+ /**
135
+ * Asynchronous disposal method.
136
+ *
137
+ * Alias for {@link close}.
138
+ */
139
+ async [symbol_js_1.Symbol.asyncDispose]() {
140
+ return await this.close();
141
+ }
142
+ }
143
+ exports.EventBus = EventBus;
144
+ /**
145
+ * Creates a strongly-typed event bus based on {@link EventBus}, ensuring
146
+ * that both `emit` and `on` methods enforce matching event names and payload types.
147
+ *
148
+ * @typeParam E - The event map type, mapping event names to payloads.
149
+ *
150
+ * @returns An object with the following methods:
151
+ * - `emit(name, payload)`: Emit an event.
152
+ * - `on(name, handler)`: Subscribe to a specific event.
153
+ * - `events`: Observable stream of all events.
154
+ * - `close()`: Close the bus and complete all subscribers.
155
+ *
156
+ * @example
157
+ * ```ts
158
+ * interface MyEvents {
159
+ * message: { text: string };
160
+ * error: { code: number; message: string };
161
+ * }
162
+ *
163
+ * const bus = createTypedEventBus<MyEvents>();
164
+ *
165
+ * // Subscribe to `message` events
166
+ * bus.on('message', payload => {
167
+ * console.log('New message:', payload.text);
168
+ * });
169
+ *
170
+ * // Emit an event
171
+ * bus.emit('message', { text: 'Hello World' });
172
+ *
173
+ * // Close the bus when done
174
+ * bus.close();
175
+ * ```
176
+ */
177
+ function createEventDispatcher() {
178
+ // Internal bus carries a union of all event types and payloads
179
+ const bus = new EventBus();
180
+ return {
181
+ /**
182
+ * Emit an event with the given name and payload.
183
+ * @param name - The event name.
184
+ * @param payload - The payload matching the event name.
185
+ */
186
+ emit(name, payload) {
187
+ bus.emit({ type: name, payload });
188
+ },
189
+ /**
190
+ * Subscribe to a specific event by name.
191
+ * Only events with a matching `type` will invoke the handler.
192
+ * @param name - The event name to listen for.
193
+ * @param handler - The callback invoked with the event payload.
194
+ * @returns A subscription object with `unsubscribe()`.
195
+ */
196
+ on(name, handler) {
197
+ return bus.events.subscribe({
198
+ next(event) {
199
+ if (event.type === name) {
200
+ handler(event.payload);
201
+ }
202
+ },
203
+ });
204
+ },
205
+ /**
206
+ * Observable stream of all emitted events, carrying `{ type, payload }` objects.
207
+ */
208
+ events: bus.events,
209
+ /**
210
+ * Synchronous disposal method (for `using` syntax).
211
+ * Closes the bus and completes all subscribers.
212
+ */
213
+ [symbol_js_1.Symbol.dispose]() {
214
+ bus[symbol_js_1.Symbol.dispose]();
215
+ },
216
+ /**
217
+ * Asynchronous disposal method.
218
+ * Closes the bus and completes all subscribers.
219
+ */
220
+ [symbol_js_1.Symbol.asyncDispose]() {
221
+ return bus[symbol_js_1.Symbol.asyncDispose]();
222
+ },
223
+ /**
224
+ * Close the bus, completing all subscribers and preventing further emits.
225
+ */
226
+ close() {
227
+ bus.close();
228
+ },
229
+ };
230
+ }
231
+ /**
232
+ * Waits for the next occurrence of a specific event on a typed event bus.
233
+ * Resolves with the event payload when the named event fires.
234
+ * Can be aborted or optionally reject if the bus closes first.
235
+ *
236
+ * @typeParam E - The event map type.
237
+ * @typeParam K - The specific event key to listen for.
238
+ *
239
+ * @param bus - An object with an `events` Observable emitting `{ type, payload }`.
240
+ * @param type - The event name to wait for.
241
+ * @param options - Optional signal to abort, and throwOnClose behavior.
242
+ * @returns A promise resolving to the payload of the event, or rejecting on error/abort/close.
243
+ *
244
+ * @example
245
+ * ```ts
246
+ * interface MyEvents {
247
+ * data: { value: number };
248
+ * done: void;
249
+ * }
250
+ *
251
+ * const bus = createTypedEventBus<MyEvents>();
252
+ *
253
+ * // somewhere else...
254
+ * waitForEvent(bus, 'data').then(payload => {
255
+ * console.log('Data arrived:', payload.value);
256
+ * });
257
+ *
258
+ * // later
259
+ * bus.emit('data', { value: 42 });
260
+ * ```
261
+ */
262
+ function waitForEvent(bus, type, { signal, throwOnClose = false } = {}) {
263
+ const { resolve, reject, promise } = Promise.withResolvers();
264
+ let settled = false;
265
+ // Immediate abort
266
+ if (signal?.aborted) {
267
+ reject(signal.reason);
268
+ return promise;
269
+ }
270
+ const subscription_ref = {};
271
+ function cleanup() {
272
+ subscription_ref.current?.unsubscribe?.();
273
+ signal?.removeEventListener?.("abort", onAbort);
274
+ }
275
+ function onAbort() {
276
+ if (settled)
277
+ return;
278
+ settled = true;
279
+ cleanup();
280
+ reject(signal.reason);
281
+ }
282
+ signal?.addEventListener?.("abort", onAbort, { once: true });
283
+ subscription_ref.current = bus.events.subscribe({
284
+ next(event) {
285
+ if (settled)
286
+ return;
287
+ if (event.type === type) {
288
+ settled = true;
289
+ cleanup();
290
+ // cast payload to the correct type
291
+ resolve(event.payload);
292
+ }
293
+ },
294
+ error(err) {
295
+ if (settled)
296
+ return;
297
+ settled = true;
298
+ cleanup();
299
+ reject(err);
300
+ },
301
+ complete() {
302
+ if (settled)
303
+ return;
304
+ settled = true;
305
+ cleanup();
306
+ if (throwOnClose) {
307
+ reject(new Error(`Stream closed before event "${String(type)}" fired`));
308
+ }
309
+ else {
310
+ resolve(undefined);
311
+ }
312
+ },
313
+ });
314
+ // We need both hooks for full race coverage:
315
+ // 1. the listener above catches aborts that happen before or during
316
+ // subscription setup, so the promise rejects immediately
317
+ // 2. this re-check catches the narrow case where that abort happened before
318
+ // `subscription_ref.current` was assigned, so we can still unsubscribe the
319
+ // newly created subscription instead of leaving it attached. For example,
320
+ // the signal can abort synchronously from inside `addEventListener()`
321
+ // before control returns to the subscription assignment above.
322
+ if (signal?.aborted) {
323
+ onAbort();
324
+ }
325
+ return promise;
326
+ }
327
+ /**
328
+ * Adds replay capability to an Observable, multicasting values to multiple subscribers
329
+ * while maintaining a buffer of recent emissions.
330
+ *
331
+ * Without replay, each new subscriber triggers a fresh execution of the source Observable:
332
+ * ```ts
333
+ * const apiCall = new Observable(subscriber => {
334
+ * console.log('Making expensive API call...');
335
+ * fetch('/api/data').then(response => subscriber.next(response));
336
+ * });
337
+ *
338
+ * apiCall.subscribe(data1 => {}); // Triggers API call #1
339
+ * apiCall.subscribe(data2 => {}); // Triggers API call #2 (duplicate!)
340
+ * ```
341
+ *
342
+ * With replay, the source executes once and shares results:
343
+ * ```ts
344
+ * const sharedApi = withReplay(apiCall, { count: 1, mode: 'lazy' });
345
+ *
346
+ * sharedApi.subscribe(data1 => {}); // Triggers API call
347
+ * sharedApi.subscribe(data2 => {}); // Gets cached result, no new call!
348
+ * ```
349
+ *
350
+ * ## Memory Considerations
351
+ *
352
+ * - Buffer size directly impacts memory usage: `count * sizeof(T)`
353
+ * - 'eager' mode holds references even with no subscribers (potential memory leak)
354
+ * - 'lazy' mode clears buffer when all subscribers disconnect (automatic cleanup)
355
+ * - Consider using finite counts for long-running streams to prevent unbounded growth
356
+ *
357
+ * > **Note**: Infinite buffers are by default capped at 1000 items to prevent memory issues.
358
+ * > The primary reason for this cap is because some runtimes such as Deno and Node.js
359
+ * > litereally crash when you try to allocate Ininity-sized arrays.
360
+ *
361
+ * ## Performance Characteristics
362
+ *
363
+ * - Enqueue/Dequeue: O(1) constant time
364
+ * - New subscriber replay: O(n) where n = buffer size
365
+ * - Memory overhead: One queue + subscriber set + source subscription
366
+ *
367
+ * ## Edge Cases & Gotchas
368
+ *
369
+ * 1. **Late subscribers in eager mode**: May receive very old values if the source
370
+ * emitted long ago and no cleanup occurred.
371
+ *
372
+ * 2. **Infinite buffers**: Without a count limit, buffers grow indefinitely.
373
+ * Always set a reasonable count for production use.
374
+ *
375
+ * 3. **Error handling**: Errors are multicast to all subscribers but don't clear
376
+ * the buffer. New subscribers still get the replay before the error.
377
+ *
378
+ * 4. **Completion**: The source completion is multicast, but the replay buffer
379
+ * remains accessible to new subscribers (they get replay + completion).
380
+ *
381
+ * @param source The source Observable to add replay behavior to
382
+ * @param options Configuration for replay behavior
383
+ * @returns A new Observable with replay capability
384
+ *
385
+ * @example
386
+ * ```ts
387
+ * // Lazy mode - only buffers when subscribers are present
388
+ * const shared = withReplay(expensive, {
389
+ * count: 5,
390
+ * mode: 'lazy' // Only run expensive when needed
391
+ * });
392
+ *
393
+ * // Eager mode - always buffering, like a flight recorder
394
+ * const eventLog = withReplay(systemEvents, {
395
+ * count: 100,
396
+ * mode: 'eager' // Capture events even if no one's listening
397
+ * });
398
+ * ```
399
+ */
400
+ function withReplay(source, { count = Infinity, mode = "lazy" } = {}) {
401
+ // Validate inputs
402
+ if (count <= 0) {
403
+ throw new Error(`Replay count must be positive, got {count}`);
404
+ }
405
+ // Shared state across all subscribers
406
+ // Using 1000 as a reasonable default for "infinite" to avoid memory issues
407
+ const buffer = (0, queue_js_1.createQueue)(count === Infinity ? 1000 : count);
408
+ const subscribers = new Set();
409
+ const observer = {
410
+ next(value) {
411
+ // Manage buffer capacity
412
+ if (count !== Infinity && (0, queue_js_1.isFull)(buffer)) {
413
+ (0, queue_js_1.dequeue)(buffer); // Remove oldest
414
+ }
415
+ (0, queue_js_1.enqueue)(buffer, value);
416
+ // Emit to all active subscribers
417
+ for (const sub of subscribers) {
418
+ sub.next(value);
419
+ }
420
+ },
421
+ error(err) {
422
+ for (const sub of subscribers) {
423
+ sub.error(err);
424
+ }
425
+ },
426
+ complete() {
427
+ for (const sub of subscribers) {
428
+ sub.complete();
429
+ }
430
+ },
431
+ };
432
+ const isEager = mode === "eager";
433
+ let shared = isEager ? source.subscribe(observer) : null;
434
+ /**
435
+ * Creates the replay Observable that new subscribers will receive.
436
+ */
437
+ return new observable_js_1.Observable((subscriber) => {
438
+ // Step 1: Replay buffered values to the new subscriber
439
+ (0, queue_js_1.forEach)(buffer, (item) => subscriber.next(item));
440
+ // Step 2: Add to active subscribers for future emissions
441
+ subscribers.add(subscriber);
442
+ // Step 3: Connect to source if needed (lazy mode, first subscriber)
443
+ if (!isEager && !shared && subscribers.size > 0) {
444
+ shared = source.subscribe(observer);
445
+ }
446
+ // Step 4: Return cleanup function
447
+ return () => {
448
+ subscribers.delete(subscriber);
449
+ // In lazy mode, disconnect and clear when last subscriber leaves
450
+ if (!isEager && subscribers.size === 0 && shared) {
451
+ shared.unsubscribe();
452
+ shared = null;
453
+ (0, queue_js_1.clear)(buffer); // Clear shared buffer when fully disconnected
454
+ }
455
+ // In eager mode, we keep the connection alive regardless
456
+ };
457
+ });
458
+ }
@@ -0,0 +1,188 @@
1
+ import type { ObservableError } from "../error.js";
2
+ import type { Observable } from "../observable.js";
3
+ /**
4
+ * Type representing a stream operator function
5
+ * Transforms a ReadableStream of type In to a ReadableStream of type Out
6
+ */
7
+ export type Operator<In, Out> = (stream: ReadableStream<In>) => ReadableStream<Out>;
8
+ /**
9
+ * Removes `ObservableError` from a type so operators can describe
10
+ * error-filtered output channels.
11
+ */
12
+ export type ExcludeError<T> = Exclude<T, ObservableError>;
13
+ /**
14
+ * Represents an operator slot in a pipeline where the previous operator may
15
+ * or may not have filtered `ObservableError` values out of the stream.
16
+ */
17
+ export type OperatorItem<T, R> = Operator<T, R> | Operator<T, ExcludeError<R>>;
18
+ /**
19
+ * Infers the item type contributed by either an Observable source or an
20
+ * operator in a pipe chain.
21
+ */
22
+ export type InferSourceType<TSource extends unknown> = TSource extends Observable<unknown> ? InferObservableType<TSource> : TSource extends OperatorItem<unknown, unknown> ? InferOperatorItemOutputType<TSource> : TSource;
23
+ /**
24
+ * Extracts the chunk type emitted by an operator.
25
+ */
26
+ export type InferOperatorItemOutputType<TSource extends OperatorItem<any, any>> = ReturnType<TSource> extends ReadableStream<infer T> ? T : never;
27
+ /**
28
+ * Extracts the value type emitted by an Observable.
29
+ */
30
+ export type InferObservableType<TSource extends Observable<unknown>> = TSource extends Observable<infer R> ? R : any;
31
+ /**
32
+ * Returns the first item in a non-empty tuple.
33
+ */
34
+ export type FirstTupleItem<TTuple extends readonly [unknown, ...unknown[]]> = TTuple[0];
35
+ /**
36
+ * Returns the last item in any tuple shape.
37
+ */
38
+ export type GenericLastTupleItem<T extends readonly any[]> = T extends [
39
+ ...infer _,
40
+ infer L
41
+ ] ? L : never;
42
+ /**
43
+ * Returns the last tuple item only when it is a valid operator item.
44
+ */
45
+ export type LastTupleItem<T extends readonly unknown[]> = GenericLastTupleItem<T> extends OperatorItem<any, any> ? GenericLastTupleItem<T> : never;
46
+ /**
47
+ * Computes the Observable type returned by a `pipe()` call from the source and
48
+ * its final operator.
49
+ */
50
+ export type ObservableWithPipe<TPipe extends readonly [Observable<unknown>, ...OperatorItem<any, unknown>[]]> = Observable<InferOperatorItemOutputType<LastTupleItem<TPipe>>>;
51
+ /**
52
+ * Type representing how to handle errors in operators
53
+ * - "ignore" => errors wrapped in ObservableError will be used as values
54
+ * and can then be transformed as the operator sees fit
55
+ * - "pass-through" => errors automatically pass through, meaning ObservableError
56
+ * will not appear as a value
57
+ * - "throw" => errors will cause the stream to throw and terminate
58
+ * - "manual" => errors are passed to the transform function to handle manually
59
+ */
60
+ export type OperatorErrorMode = "ignore" | "pass-through" | "throw" | "manual";
61
+ /**
62
+ * Base interface with properties shared across all transform options
63
+ */
64
+ export interface BaseTransformOptions {
65
+ /**
66
+ * Optional name for the operator (used in error reporting)
67
+ */
68
+ name?: string;
69
+ }
70
+ /**
71
+ * Options for using an existing TransformStream
72
+ */
73
+ export interface TransformStreamOptions<T, R> extends BaseTransformOptions {
74
+ /**
75
+ * An existing TransformStream to use for transformation
76
+ */
77
+ stream: (opts: TransformStreamOptions<T, R>) => TransformStream<T, R>;
78
+ }
79
+ /**
80
+ * Options for custom transformation logic
81
+ */
82
+ export interface TransformFunctionOptions<T, R> extends BaseTransformOptions {
83
+ /**
84
+ * How to handle errors in the stream:
85
+ * - "ignore" => errors wrapped in ObservableError will be used as values
86
+ * and can then be transformed as the operator sees fit
87
+ * - "pass-through" => errors automatically pass through, meaning ObservableError
88
+ * will not appear as a value
89
+ * - "throw" => errors will cause the stream to throw and terminate
90
+ * - "manual" => errors are passed to the transform function to handle manually
91
+ * @default "pass-through"
92
+ */
93
+ errorMode?: OperatorErrorMode;
94
+ /**
95
+ * Function to transform each chunk
96
+ * @param chunk - The input chunk
97
+ * @param controller - The TransformStreamDefaultController
98
+ * @returns The transformed chunk(s) or undefined to filter out the chunk
99
+ */
100
+ transform: (chunk: T, controller: TransformStreamDefaultController<R>) => R | undefined | void | null | Promise<R | undefined | void | null>;
101
+ /**
102
+ * Function called when the stream is about to close
103
+ * Can enqueue final chunks before closing
104
+ * @param controller - The TransformStreamDefaultController
105
+ */
106
+ flush?: (controller: TransformStreamDefaultController<R>) => void | Promise<void>;
107
+ /**
108
+ * Called when the stream starts, before processing any chunks
109
+ * @param controller - The TransformStreamDefaultController
110
+ */
111
+ start?: (controller: TransformStreamDefaultController<R>) => void | Promise<void>;
112
+ /**
113
+ * Called when the stream is cancelled before natural completion
114
+ */
115
+ cancel?: (reason?: unknown) => void | Promise<void>;
116
+ }
117
+ /**
118
+ * Union type for all createOperator options
119
+ */
120
+ export type CreateOperatorOptions<T, R> = TransformStreamOptions<T, R> | TransformFunctionOptions<T, R>;
121
+ /**
122
+ * Options for stateful transformation logic
123
+ */
124
+ export interface StatefulTransformFunctionOptions<T, R, S> extends BaseTransformOptions {
125
+ /**
126
+ * How to handle errors in the stream:
127
+ * - "ignore" => errors wrapped in ObservableError will be used as values
128
+ * and can then be transformed as the operator sees fit
129
+ * - "pass-through" => errors automatically pass through, meaning ObservableError
130
+ * will not appear as a value
131
+ * - "throw" => errors will cause the stream to throw and terminate
132
+ * - "manual" => errors are passed to the transform function to handle manually
133
+ * @default "pass-through"
134
+ */
135
+ errorMode?: OperatorErrorMode;
136
+ /**
137
+ * Function to create the initial state
138
+ * @returns The initial state
139
+ */
140
+ createState: () => S;
141
+ /**
142
+ * Function to transform each chunk, with access to the current state
143
+ * @param chunk - The input chunk
144
+ * @param state - The current state (can be modified)
145
+ * @param controller - The TransformStreamDefaultController
146
+ */
147
+ transform: (chunk: T, state: S, controller: TransformStreamDefaultController<R>) => void | Promise<void>;
148
+ /**
149
+ * Function called when the stream is about to close
150
+ * Can enqueue final chunks based on the state
151
+ * @param state - The final state
152
+ * @param controller - The TransformStreamDefaultController
153
+ */
154
+ flush?: (state: S, controller: TransformStreamDefaultController<R>) => void | Promise<void>;
155
+ /**
156
+ * Called when the stream starts, before processing any chunks
157
+ * @param state - The initial state
158
+ * @param controller - The TransformStreamDefaultController
159
+ */
160
+ start?: (state: S, controller: TransformStreamDefaultController<R>) => void | Promise<void>;
161
+ /**
162
+ * Called when the stream is cancelled before natural completion
163
+ * @param state - The current state
164
+ */
165
+ cancel?: (state: S, reason?: unknown) => void | Promise<void>;
166
+ }
167
+ /**
168
+ * Context for transform handlers
169
+ * This interface provides additional information for the transform handler,
170
+ * such as the operator name, whether it is
171
+ * stateful, and any associated state.
172
+ * @typeParam S - State type (if applicable)
173
+ */
174
+ export interface TransformHandlerContext<S extends unknown = undefined> {
175
+ /**
176
+ * Human-readable operator name used in wrapped error messages.
177
+ */
178
+ operatorName?: string;
179
+ /**
180
+ * Indicates that the lifecycle handler should pass shared state through.
181
+ */
182
+ isStateful?: boolean;
183
+ /**
184
+ * Shared operator state for stateful transforms.
185
+ */
186
+ state?: S;
187
+ }
188
+ //# sourceMappingURL=_types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_types.d.ts","sourceRoot":"","sources":["../../src/helpers/_types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });