@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,457 @@
1
+ /**
2
+ * Time-based operators for spacing, delaying, and expiring stream values.
3
+ *
4
+ * This entrypoint groups the operators that make time part of your pipeline's
5
+ * behavior. Use it for UI patterns such as debouncing search input, throttling
6
+ * bursty events, delaying retries, or timing out work that takes too long.
7
+ *
8
+ * Timing operators do not just change values; they change when work is allowed
9
+ * to move downstream. That makes them especially important for coordinating
10
+ * async side effects without piling up stale requests or overwhelming slower
11
+ * consumers.
12
+ *
13
+ * @module
14
+ */
15
+ import "../../_dnt.polyfills.js";
16
+ import { createOperator, createStatefulOperator } from "../operators.js";
17
+ import { isObservableError, ObservableError } from "../../error.js";
18
+ /**
19
+ * Delays each item in the stream by a specified number of milliseconds.
20
+ *
21
+ * This operator shifts the entire stream of events forward in time, preserving
22
+ * the relative time between them.
23
+ *
24
+ * > Note: This does not delay error emissions. If an error occurs, it will
25
+ * > be emitted immediately, regardless of the delay.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { pipe, delay, from } from "./helpers/mod.ts";
30
+ *
31
+ * // No direct Array equivalent, as it's about timing.
32
+ *
33
+ * // Stream behavior
34
+ * const sourceStream = from([1, 2, 3]);
35
+ * const delayedStream = pipe(sourceStream, delay(1000));
36
+ * // Emits 1 (after 1s), then 2 (immediately after), then 3 (immediately after).
37
+ * ```
38
+ *
39
+ * ## Practical Use Case
40
+ *
41
+ * Use `delay` to simulate network latency in tests, or to introduce a small
42
+ * pause in a UI animation sequence to make it feel more natural.
43
+ *
44
+ * ## Key Insight
45
+ *
46
+ * `delay` is about shifting the timeline of events, not about pausing between
47
+ * them. It's a simple way to control when a stream begins to emit its values.
48
+ *
49
+ * @typeParam T - Type of values from the source stream
50
+ * @param ms - The delay duration in milliseconds
51
+ * @returns A stream operator that delays each value
52
+ */
53
+ export function delay(ms) {
54
+ return createStatefulOperator({
55
+ name: "delay",
56
+ errorMode: "pass-through", // Should be explicit
57
+ createState: () => ({
58
+ timeoutId: null,
59
+ buffer: [],
60
+ hasStarted: false,
61
+ delayOver: false,
62
+ pendingFlush: null,
63
+ }),
64
+ transform(chunk, state, controller) {
65
+ // If delay period is over, emit immediately
66
+ if (state.delayOver)
67
+ controller.enqueue(chunk);
68
+ // Buffer the item and start delay timer if not already started
69
+ else
70
+ state.buffer.push(chunk);
71
+ // Start the delay timer on the first item
72
+ if (!state.hasStarted) {
73
+ state.pendingFlush = Promise.withResolvers();
74
+ state.timeoutId = setTimeout(() => {
75
+ // Mark delay as complete
76
+ state.delayOver = true;
77
+ // Release all buffered items
78
+ for (const value of state.buffer) {
79
+ controller.enqueue(value);
80
+ }
81
+ state.buffer.length = 0; // Fast array clear
82
+ // Clean up timeout reference
83
+ clearTimeout(state.timeoutId);
84
+ state.timeoutId = null;
85
+ // If flush was waiting, complete it now
86
+ if (state.pendingFlush) {
87
+ state.pendingFlush.resolve();
88
+ state.pendingFlush = null;
89
+ }
90
+ }, ms);
91
+ state.hasStarted = true;
92
+ }
93
+ },
94
+ async flush(state, controller) {
95
+ // If delay hasn't completed yet, we need to wait
96
+ if (state.pendingFlush)
97
+ await state.pendingFlush.promise;
98
+ // Cancel timeout if stream ends during delay
99
+ if (state.timeoutId !== null) {
100
+ clearTimeout(state.timeoutId);
101
+ state.timeoutId = null;
102
+ }
103
+ // Emit any remaining buffered items
104
+ for (const item of state.buffer) {
105
+ controller.enqueue(item);
106
+ }
107
+ state.buffer.length = 0;
108
+ },
109
+ cancel(state) {
110
+ // Critical: clean up timeout to prevent memory leaks
111
+ if (state.timeoutId !== null) {
112
+ clearTimeout(state.timeoutId);
113
+ state.timeoutId = null;
114
+ }
115
+ state.buffer.length = 0;
116
+ },
117
+ });
118
+ }
119
+ /**
120
+ * Delays each individual item by a specified number of milliseconds.
121
+ *
122
+ * This operator delays every item independently, preserving the relative
123
+ * spacing between them while shifting each one forward by the same amount.
124
+ * Think of it as "adding X milliseconds to each item's timestamp."
125
+ *
126
+ * > Note: This does not delay error emissions. If an error occurs, it will
127
+ * > be emitted immediately, regardless of the delay.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * import { pipe, delayEach, from } from "./helpers/mod.ts";
132
+ *
133
+ * // Stream behavior
134
+ * const sourceStream = from([1, 2, 3]); // Items at T+0, T+100, T+200
135
+ * const delayedStream = pipe(sourceStream, delayEach(1000));
136
+ * // Emits 1 (at T+1000), 2 (at T+1100), 3 (at T+1200)
137
+ * ```
138
+ *
139
+ * ## Practical Use Case
140
+ *
141
+ * Use `delayEach` to simulate processing time for each item, or to add
142
+ * consistent lag to every operation (useful for testing race conditions
143
+ * or simulating network latency per request).
144
+ *
145
+ * ## Key Insight
146
+ *
147
+ * `delayEach` maintains the original spacing between items while shifting
148
+ * each one. For delaying the entire stream timeline, use `delay` instead.
149
+ *
150
+ * @typeParam T - Type of values from the source stream
151
+ * @param ms - The delay duration in milliseconds for each item
152
+ * @returns A stream operator that delays each value individually
153
+ */
154
+ export function delayEach(ms) {
155
+ return createStatefulOperator({
156
+ name: "delayEach",
157
+ errorMode: "pass-through",
158
+ createState: () => ({
159
+ pendingTimeouts: new Set(),
160
+ isComplete: false,
161
+ }),
162
+ transform(chunk, state, controller) {
163
+ // Schedule delayed emission for this specific item
164
+ const timeout = setTimeout((_chunk) => {
165
+ controller.enqueue(_chunk);
166
+ state.pendingTimeouts.delete(timeout);
167
+ clearTimeout(timeout);
168
+ // If stream ended and this was the last pending timeout, close stream
169
+ if (state.isComplete && state.pendingTimeouts.size === 0) {
170
+ controller.terminate();
171
+ }
172
+ }, ms, chunk);
173
+ state.pendingTimeouts.add(timeout);
174
+ },
175
+ flush(state, controller) {
176
+ state.isComplete = true;
177
+ // If no pending timeouts, stream can complete immediately
178
+ if (state.pendingTimeouts.size === 0) {
179
+ controller.terminate();
180
+ }
181
+ // Otherwise, wait for pending timeouts to finish (handled in transform)
182
+ },
183
+ cancel(state) {
184
+ // Critical: clean up all pending timeouts to prevent memory leaks
185
+ for (const timeout of state.pendingTimeouts) {
186
+ clearTimeout(timeout);
187
+ }
188
+ state.pendingTimeouts.clear();
189
+ },
190
+ });
191
+ }
192
+ /**
193
+ * Emits only the latest item after a specified period of inactivity.
194
+ *
195
+ * This is the "search bar" operator. It waits for the user to stop typing
196
+ * before firing off a search query.
197
+ *
198
+ * > Note: This operator does not delay error emissions. If an error occurs,
199
+ * > it will be emitted immediately, regardless of the debounce period.
200
+ *
201
+ * @example
202
+ * ```ts
203
+ * import { pipe, debounce, from } from "./helpers/mod.ts";
204
+ *
205
+ * // No direct Array equivalent.
206
+ *
207
+ * // Stream behavior
208
+ * const inputStream = from(["a", "ab", "abc"]); // User typing quickly
209
+ * const debouncedStream = pipe(inputStream, debounce(300));
210
+ * // After 300ms of no new input, it will emit "abc".
211
+ * ```
212
+ *
213
+ * ## Practical Use Case
214
+ *
215
+ * Use `debounce` for any event that fires rapidly, but you only care about the
216
+ * final value, such as search inputs, window resize events, or auto-saving
217
+ * form fields.
218
+ *
219
+ * ## Key Insight
220
+ *
221
+ * `debounce` filters out noise from rapid-fire events, ensuring that expensive
222
+ * operations (like API calls) are only triggered when necessary.
223
+ *
224
+ * @typeParam T - Type of values from the source stream
225
+ * @param ms - The debounce duration in milliseconds
226
+ * @returns A stream operator that debounces values
227
+ */
228
+ export function debounce(ms) {
229
+ return createStatefulOperator({
230
+ name: "debounce",
231
+ errorMode: "pass-through",
232
+ createState: () => ({
233
+ timeout: null,
234
+ lastValue: null,
235
+ hasValue: false,
236
+ }),
237
+ transform(chunk, state, controller) {
238
+ // Cancel any pending timeout
239
+ if (state.timeout !== null) {
240
+ clearTimeout(state.timeout);
241
+ state.timeout = null;
242
+ }
243
+ // Store the latest value
244
+ state.lastValue = chunk;
245
+ state.hasValue = true;
246
+ // Set up a new timeout to emit the latest value
247
+ state.timeout = setTimeout(() => {
248
+ if (state.hasValue) {
249
+ controller.enqueue(state.lastValue);
250
+ }
251
+ state.timeout = null;
252
+ state.lastValue = null;
253
+ state.hasValue = false;
254
+ }, ms);
255
+ },
256
+ flush(state, controller) {
257
+ // Clear any pending timeout
258
+ if (state.timeout !== null) {
259
+ clearTimeout(state.timeout);
260
+ state.timeout = null;
261
+ }
262
+ // Emit the last value if we have one
263
+ if (state.hasValue) {
264
+ controller.enqueue(state.lastValue);
265
+ state.lastValue = null;
266
+ state.hasValue = false;
267
+ }
268
+ },
269
+ cancel(state) {
270
+ // Clean up on cancellation
271
+ if (state.timeout !== null) {
272
+ clearTimeout(state.timeout);
273
+ state.timeout = null;
274
+ }
275
+ state.lastValue = null;
276
+ state.hasValue = false;
277
+ },
278
+ });
279
+ }
280
+ /**
281
+ * Limits the stream to emit at most one item per specified time interval.
282
+ *
283
+ * This is the "scroll event" operator. It ensures that even if an event fires
284
+ * hundreds of times per second, you only handle it at a manageable rate.
285
+ *
286
+ * @example
287
+ * ```ts
288
+ * import { pipe, throttle, from } from "./helpers/mod.ts";
289
+ *
290
+ * // No direct Array equivalent.
291
+ *
292
+ * // Stream behavior
293
+ * const scrollStream = from([10, 20, 50, 100, 150]); // Scroll events
294
+ * const throttledStream = pipe(scrollStream, throttle(100));
295
+ * // Emits 10 immediately, then waits 100ms before being able to emit again.
296
+ * // If 150 is the last value, it will be emitted after the throttle window.
297
+ * ```
298
+ *
299
+ * ## Practical Use Case
300
+ *
301
+ * Use `throttle` for high-frequency events where you need to guarantee a
302
+ * regular sampling of the data, such as scroll position tracking, mouse
303
+ * movement, or real-time data visualization.
304
+ *
305
+ * ## Key Insight
306
+ *
307
+ * `throttle` guarantees a steady flow of data, unlike `debounce` which waits
308
+ * for silence. It's about rate-limiting, not just handling the final value.
309
+ *
310
+ * @typeParam T - Type of values from the source stream
311
+ * @param ms - The throttle duration in milliseconds
312
+ * @returns A stream operator that throttles values
313
+ */
314
+ export function throttle(ms) {
315
+ return createStatefulOperator({
316
+ name: "throttle",
317
+ errorMode: "pass-through",
318
+ createState: () => ({
319
+ lastEmitTime: 0,
320
+ nextValue: null,
321
+ hasNextValue: false,
322
+ timeoutId: null,
323
+ }),
324
+ transform(chunk, state, controller) {
325
+ if (isObservableError(chunk)) {
326
+ // If the chunk is an error, we can immediately emit it
327
+ controller.enqueue(chunk);
328
+ return;
329
+ }
330
+ const now = Date.now();
331
+ const timeSinceLastEmit = now - state.lastEmitTime;
332
+ // If we haven't emitted for the throttle duration, emit immediately
333
+ if (timeSinceLastEmit >= ms) {
334
+ state.lastEmitTime = now;
335
+ controller.enqueue(chunk);
336
+ return;
337
+ }
338
+ // Otherwise, store this value to emit later
339
+ state.nextValue = chunk;
340
+ state.hasNextValue = true;
341
+ // If we don't have a timeout scheduled, schedule one
342
+ if (state.timeoutId === null) {
343
+ const remainingTime = ms - timeSinceLastEmit;
344
+ state.timeoutId = setTimeout(() => {
345
+ if (state.hasNextValue) {
346
+ state.lastEmitTime = Date.now();
347
+ controller.enqueue(state.nextValue);
348
+ state.nextValue = null;
349
+ state.hasNextValue = false;
350
+ }
351
+ state.timeoutId = null;
352
+ }, remainingTime);
353
+ }
354
+ },
355
+ flush(state, controller) {
356
+ // Clean up any scheduled timeout
357
+ if (state.timeoutId !== null) {
358
+ clearTimeout(state.timeoutId);
359
+ state.timeoutId = null;
360
+ }
361
+ // Emit the last value if we have one
362
+ if (state.hasNextValue) {
363
+ controller.enqueue(state.nextValue);
364
+ state.nextValue = null;
365
+ state.hasNextValue = false;
366
+ }
367
+ },
368
+ cancel(state) {
369
+ // Clean up on cancellation
370
+ if (state.timeoutId !== null) {
371
+ clearTimeout(state.timeoutId);
372
+ state.timeoutId = null;
373
+ }
374
+ state.nextValue = null;
375
+ state.hasNextValue = false;
376
+ },
377
+ });
378
+ }
379
+ /**
380
+ * Errors if an item takes too long to be processed.
381
+ *
382
+ * This operator passes normal synchronous values through immediately, but when
383
+ * an upstream operator emits a promise-like chunk it waits for that chunk to
384
+ * settle and races it against a timer. If the promise-like chunk does not
385
+ * resolve before the timer finishes, it emits an `ObservableError` instead.
386
+ *
387
+ * The implementation uses an async transform so Web Streams backpressure keeps
388
+ * later chunks from overtaking the timed chunk. In practice that means operator
389
+ * chains still stay ordered: each promise-like chunk either resolves to a value
390
+ * or times out before the next chunk is processed.
391
+ *
392
+ * @example
393
+ * ```ts
394
+ * import { pipe, timeout, from } from "./helpers/mod.ts";
395
+ *
396
+ * // Stream behavior
397
+ * const sourceStream = from([
398
+ * new Promise(res => setTimeout(() => res(1), 100)),
399
+ * new Promise(res => setTimeout(() => res(2), 2000))
400
+ * ]);
401
+ *
402
+ * const timedStream = pipe(sourceStream, timeout(1000));
403
+ * // Emits 1, then emits an error because the second promise took too long.
404
+ * ```
405
+ *
406
+ * ## Practical Use Case
407
+ *
408
+ * Use `timeout` to enforce Service Level Agreements (SLAs) on asynchronous
409
+ * operations, such as API calls. If a request takes too long, you can gracefully
410
+ * handle the timeout instead of letting your application hang.
411
+ *
412
+ * ## Key Insight
413
+ *
414
+ * `timeout` is a crucial tool for building resilient systems that can handle
415
+ * slow or unresponsive dependencies. It turns an indefinite wait into a
416
+ * predictable failure.
417
+ *
418
+ * @typeParam T The type of data in the stream.
419
+ * @param ms The timeout duration in milliseconds.
420
+ * @returns An operator that enforces a timeout on each item.
421
+ */
422
+ export function timeout(ms) {
423
+ return createOperator({
424
+ name: "timeout",
425
+ errorMode: "pass-through",
426
+ async transform(chunk, controller) {
427
+ // If the chunk is an error, we can immediately emit it
428
+ if (isObservableError(chunk)) {
429
+ controller.enqueue(chunk);
430
+ return;
431
+ }
432
+ if (chunk !== null &&
433
+ (typeof chunk === "object" || typeof chunk === "function") &&
434
+ "then" in chunk &&
435
+ typeof chunk.then === "function") {
436
+ const timeoutResult = Symbol("timeout");
437
+ let timeoutId;
438
+ const result = await Promise.race([
439
+ chunk,
440
+ new Promise((resolve) => {
441
+ timeoutId = setTimeout(() => resolve(timeoutResult), ms);
442
+ }),
443
+ ]);
444
+ if (timeoutId !== undefined) {
445
+ clearTimeout(timeoutId);
446
+ }
447
+ if (result === timeoutResult) {
448
+ controller.enqueue(ObservableError.from(new Error(`Operation timed out after ${ms}ms`), "operator:timeout", { timeoutMs: ms, chunk }));
449
+ return;
450
+ }
451
+ controller.enqueue(result);
452
+ return;
453
+ }
454
+ controller.enqueue(chunk);
455
+ },
456
+ });
457
+ }