@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,563 @@
1
+ /**
2
+ * Operators are the building blocks of Observable pipelines.
3
+ *
4
+ * If you've ever used `Array.map` or `Array.filter`, you already know the core idea:
5
+ * an **operator** takes a sequence of values and transforms, filters, or combines them
6
+ * into a new sequence. Operators let you build data pipelines, think of them as the
7
+ * Lego bricks for working with streams of data.
8
+ *
9
+ * Think of an operator as a function that takes a stream of values and returns a new stream,
10
+ * transforming, filtering, or combining the data as it flows through.
11
+ *
12
+ * For example, to double every number in an array:
13
+ * ```ts
14
+ * [1, 2, 3].map(x => x * 2); // [2, 4, 6]
15
+ * ```
16
+ *
17
+ * With Observables, you want to do the same thing, but for values that arrive over time:
18
+ * ```ts
19
+ * // Double every number in a stream
20
+ * const double = createOperator({
21
+ * transform(chunk, controller) {
22
+ * controller.enqueue(chunk * 2);
23
+ * }
24
+ * });
25
+ *
26
+ * // Only allow even numbers through
27
+ * const evens = createOperator({
28
+ * transform(chunk, controller) {
29
+ * if (chunk % 2 === 0) controller.enqueue(chunk);
30
+ * }
31
+ * });
32
+ *
33
+ * // Use them together in a pipeline
34
+ * pipe(
35
+ * Observable.from([1, 2, 3, 4]),
36
+ * double,
37
+ * evens
38
+ * ).subscribe(console.log); // Output: 4, 8
39
+ * ```
40
+ *
41
+ * This module lets you build your own operators using the Web Streams API under the hood.
42
+ * Why streams? Because they're fast, memory-efficient, and let you process data as it arrives,
43
+ * not just after everything is loaded. This is especially useful for things like file processing,
44
+ * network requests, or any situation where you want to handle data piece-by-piece.
45
+ *
46
+ * ## Why Streams? Why Not Just Arrays?
47
+ *
48
+ * Arrays are great for data you already have. But what about data that arrives slowly,
49
+ * or is too big to fit in memory? Think files, network responses, or user events.
50
+ * That's where **streams** shine: they let you process data piece-by-piece, as it arrives,
51
+ * without waiting for everything or loading it all at once.
52
+ *
53
+ * The Web Streams API (and Node.js streams) are the standard way to do this in modern JavaScript.
54
+ * But using them directly is verbose and error-prone:
55
+ * ```ts
56
+ * // Native TransformStream: double every number
57
+ * const stream = new TransformStream({
58
+ * transform(chunk, controller) {
59
+ * controller.enqueue(chunk * 2);
60
+ * }
61
+ * });
62
+ * ```
63
+ * Your operator helpers let you write the same thing, but with less boilerplate and
64
+ * built-in error handling:
65
+ * ```ts
66
+ * const double = createOperator({
67
+ * transform(chunk, controller) {
68
+ * controller.enqueue(chunk * 2);
69
+ * }
70
+ * });
71
+ * ```
72
+ *
73
+ * By building operators on top of streams, you get:
74
+ * - **Backpressure**: Slow consumers don't overwhelm fast producers.
75
+ * - **Low memory usage**: Process data chunk-by-chunk, not all at once.
76
+ * - **Composable pipelines**: Easily chain transformations.
77
+ *
78
+ * ## Connecting Operators: Pipelines
79
+ *
80
+ * Operators are most powerful when you chain them together. This is called a pipeline.
81
+ *
82
+ * It's just like chaining `map` and `filter` on arrays, but for streams:
83
+ * ```ts
84
+ * pipe(
85
+ * Observable.from([1, 2, 3, 4]),
86
+ * createOperator({
87
+ * transform(chunk, controller) {
88
+ * controller.enqueue(chunk * 2);
89
+ * }
90
+ * }),
91
+ * createOperator({
92
+ * transform(chunk, controller) {
93
+ * if (chunk % 3 === 0) controller.enqueue(chunk);
94
+ * }
95
+ * })
96
+ * ).subscribe(console.log); // Output: 6
97
+ * ```
98
+ *
99
+ * Compare to arrays:
100
+ * ```ts
101
+ * [1, 2, 3, 4]
102
+ * .map(x => x * 2)
103
+ * .filter(x => x % 3 === 0)
104
+ * .forEach(console.log); // [2, 4, 8]
105
+ * ```
106
+ *
107
+ * Of course, no one wants to write operators from scratch every time.
108
+ * So we provide some core operations via basic familiar operators,
109
+ * plus error handling utilities to make your pipelines robust.
110
+ *
111
+ * Aka, `map`, `filter`, `reduce`, `batch`, `catchErrors`, `ignoreErrors`, and more.
112
+ * So really the example above becomes:
113
+ * ```ts
114
+ * pipe(
115
+ * Observable.from([1, 2, 3, 4]),
116
+ * map(x => x * 2),
117
+ * filter(x => x % 3 === 0)
118
+ * ).subscribe(console.log); // Output: 2, 4, 8
119
+ * ```
120
+ *
121
+ * The example is not ideal given arrays have functions for this already,
122
+ * but you get the idea. It's meant more for streams of data that arrive over time.
123
+ *
124
+ * ## Error Handling: Real-World Data is Messy
125
+ *
126
+ * Real-world data is messy. Sometimes things go wrong aka, maybe a chunk is malformed, or a network
127
+ * request fails. Our operators let you choose how to handle errors, with four modes:
128
+ *
129
+ * - `"pass-through"` (default): Errors become special values in the stream, so you can handle them downstream. Imagine almost like bubble wrap over error since they are dangerous allowing us to make sure we don't break the flow.
130
+ * - `"ignore"`: Errors are silently skipped. The stream keeps going as if nothing happened. Imagine that we're basically just remove any errors from the stream while it's flowing (pretty stressful ngl).
131
+ * - `"throw"`: The stream stops immediately on the first error. Basically start screaming bloody murder, an error has occured so everything must stop.
132
+ * - `"manual"`: You handle all errors yourself. If you don't catch them, the stream will error. This is primarily for operators who have special error handling requirements.
133
+ *
134
+ * Example: parsing JSON safely
135
+ * ```ts
136
+ * // Pass-through: errors become ObservableError values (
137
+ * // we basically package errors in bubble wrap which we call a ObservableError
138
+ * const safeParse = createOperator({
139
+ * errorMode: "pass-through",
140
+ * transform(chunk, controller) {
141
+ * controller.enqueue(JSON.parse(chunk));
142
+ * }
143
+ * });
144
+ *
145
+ * // Ignore: errors are dropped
146
+ * const ignoreParse = createOperator({
147
+ * errorMode: "ignore",
148
+ * transform(chunk, controller) {
149
+ * controller.enqueue(JSON.parse(chunk));
150
+ * }
151
+ * });
152
+ *
153
+ * // Throw: stream stops on first error
154
+ * const strictParse = createOperator({
155
+ * errorMode: "throw",
156
+ * transform(chunk, controller) {
157
+ * controller.enqueue(JSON.parse(chunk));
158
+ * }
159
+ * });
160
+ * ```
161
+ *
162
+ * Compare to native TransformStream error handling:
163
+ * ```ts
164
+ * // Native: you must handle errors yourself
165
+ * const stream = new TransformStream({
166
+ * transform(chunk, controller) {
167
+ * try {
168
+ * controller.enqueue(JSON.parse(chunk));
169
+ * } catch (err) {
170
+ * controller.error(err); // This kills the stream
171
+ * }
172
+ * }
173
+ * });
174
+ * ```
175
+ *
176
+ * ## Stateful Operators: Remembering Across Chunks
177
+ *
178
+ * Sometimes you need to keep track of things as data flows through, like running totals,
179
+ * buffers, or windows. Your `createStatefulOperator` lets you do this easily:
180
+ *
181
+ * ```ts
182
+ * // Running sum
183
+ * const runningSum = createStatefulOperator({
184
+ * createState: () => ({ sum: 0 }),
185
+ * transform(chunk, state, controller) {
186
+ * state.sum += chunk;
187
+ * controller.enqueue(state.sum);
188
+ * }
189
+ * });
190
+ *
191
+ * pipe(
192
+ * Observable.from([1, 2, 3]),
193
+ * runningSum
194
+ * ).subscribe(console.log); // Output: 1, 3, 6
195
+ * ```
196
+ *
197
+ * Native TransformStream can't do this as cleanly, you'd have to manage state outside the stream,
198
+ * which gets messy, error-prone and annoying real quick.
199
+ *
200
+ * ## Performance and Memory
201
+ *
202
+ * - **Hot path optimization**: The error handling logic is generated for each operator,
203
+ * so there are no runtime branches inside your data processing loop.
204
+ * - **Memory safety**: Only the functions and state you need are kept alive; everything else
205
+ * can be garbage collected.
206
+ * - **Streams scale**: You can process gigabytes of data with minimal RAM, and your operators
207
+ * work just as well for infinite streams as for arrays (though arrays have better performance through
208
+ * their built-in `filter`, `map`, `forEach`, etc..., methods).
209
+ *
210
+ * ## Summary
211
+ *
212
+ * - Operators are like `Array.map`/`filter`, but for async streams of data.
213
+ * - You can build pipelines that transform, filter, buffer, or combine data.
214
+ * - Error handling is flexible and explicit.
215
+ * - Streams make your code scalable and memory-efficient.
216
+ * - State is easy to manage for advanced use cases.
217
+ * - The helpers make working with streams as easy as working with arrays.
218
+ *
219
+ * @module
220
+ */
221
+ import { injectError, isTransformStreamOptions } from "./utils.js";
222
+ import { isObservableError, ObservableError } from "../error.js";
223
+ // Default case
224
+ export function createOperator(options) {
225
+ // Extract operator name from options or the function name for better error reporting
226
+ const operatorName = `operator:${options.name || "unknown"}`;
227
+ const errorMode = options?.errorMode ??
228
+ "pass-through";
229
+ // Extract only what we need to avoid retaining the full options object
230
+ const transform = options?.transform;
231
+ const start = options?.start;
232
+ const flush = options?.flush;
233
+ return (source) => {
234
+ try {
235
+ // Create a transform stream with the provided options
236
+ const transformStream = isTransformStreamOptions(options)
237
+ ? options.stream(options)
238
+ : new TransformStream({
239
+ // Transform function to process each chunk
240
+ transform: handleTransform(errorMode, transform, { operatorName }),
241
+ // Start function called when the stream is initialized
242
+ start: handleStart(errorMode, start, { operatorName }),
243
+ // Flush function called when the input is done
244
+ flush: handleFlush(errorMode, flush, { operatorName }),
245
+ }, { highWaterMark: 1 }, { highWaterMark: 0 });
246
+ // Pipe the source through the transform
247
+ return source.pipeThrough(transformStream);
248
+ }
249
+ catch (err) {
250
+ // If setup fails, return a stream that errors immediately
251
+ return source.pipeThrough(injectError(err, `${operatorName}:setup`, options));
252
+ }
253
+ };
254
+ }
255
+ /**
256
+ * Hot-path optimized error handling for transform functions
257
+ *
258
+ * Problem: Transform functions are called for EVERY chunk in a stream. Doing
259
+ * error mode checks and type checks on every call kills performance.
260
+ *
261
+ * Solution: Pre-compile the error handling logic into optimized functions.
262
+ * Each error mode gets its own specialized function with zero runtime overhead.
263
+ *
264
+ * Memory optimization: Only references the specific transform function and state,
265
+ * not the entire options object, enabling garbage collection of unused properties.
266
+ *
267
+ * @example
268
+ * ```ts
269
+ * // Instead of this slow approach:
270
+ * function slowTransform(chunk, controller) {
271
+ * if (errorMode === 'ignore' && isObservableError(chunk)) return;
272
+ * if (errorMode === 'pass-through' && isObservableError(chunk)) {
273
+ * controller.enqueue(chunk);
274
+ * return;
275
+ * }
276
+ * // ... more runtime checks
277
+ * }
278
+ *
279
+ * // handleTransform pre-compiles to this:
280
+ * function fastIgnoreTransform(chunk, controller) {
281
+ * if (isObservableError(chunk)) return; // Only one check needed
282
+ * try {
283
+ * userTransform(chunk, controller);
284
+ * } catch (_) { return; } // Pre-compiled error handling
285
+ * }
286
+ * ```
287
+ *
288
+ * @typeParam T - Input chunk type
289
+ * @typeParam O - Output chunk type
290
+ * @typeParam S - State type (for stateful operators)
291
+ * @param errorMode - How to handle errors ("pass-through", "ignore", "throw", "manual")
292
+ * @param transform - Your transform function (stateless or stateful)
293
+ * @param context - Info about the operator, including name and state
294
+ * @returns A function suitable for TransformStream's `transform` property
295
+ */
296
+ export function handleTransform(errorMode, transform, context = {}) {
297
+ const operatorName = context.operatorName || `operator:unknown`;
298
+ const isStateful = context.isStateful || false;
299
+ const state = context.state;
300
+ switch (errorMode) {
301
+ case "pass-through":
302
+ return async function (chunk, controller) {
303
+ if (isObservableError(chunk)) {
304
+ controller.enqueue(chunk);
305
+ return;
306
+ }
307
+ try {
308
+ if (isStateful) {
309
+ // If stateful, pass the state along
310
+ return await transform(chunk, state, controller);
311
+ }
312
+ await transform(chunk, controller);
313
+ }
314
+ catch (err) {
315
+ controller.enqueue(ObservableError.from(err, operatorName, chunk));
316
+ }
317
+ };
318
+ case "ignore":
319
+ return async function (chunk, controller) {
320
+ if (isObservableError(chunk))
321
+ return;
322
+ try {
323
+ if (isStateful) {
324
+ // If stateful, pass the state along
325
+ return await transform(chunk, state, controller);
326
+ }
327
+ await transform(chunk, controller);
328
+ }
329
+ catch (_) {
330
+ // Silently ignore errors
331
+ return;
332
+ }
333
+ };
334
+ case "throw":
335
+ return async function (chunk, controller) {
336
+ if (isObservableError(chunk)) {
337
+ return controller.error(ObservableError.from(chunk, operatorName, chunk));
338
+ }
339
+ try {
340
+ if (isStateful) {
341
+ // If stateful, pass the state along
342
+ return await transform(chunk, state, controller);
343
+ }
344
+ await transform(chunk, controller);
345
+ }
346
+ catch (err) {
347
+ return controller.error(ObservableError.from(err, operatorName, chunk));
348
+ }
349
+ };
350
+ case "manual":
351
+ default:
352
+ return async function (chunk, controller) {
353
+ // In manual mode, user is expected to handle ALL errors
354
+ // If they don't catch something, let it bubble up and error the stream
355
+ if (isStateful) {
356
+ // If stateful, pass the state along
357
+ return await transform(chunk, state, controller);
358
+ }
359
+ await transform(chunk, controller);
360
+ };
361
+ }
362
+ }
363
+ /**
364
+ * Lifecycle error handling for the start of the stream
365
+ *
366
+ * The start() lifecycle method runs once when a TransformStream is created.
367
+ * Unlike transform(), performance isn't critical here, but error handling
368
+ * consistency is. This wrapper ensures start() failures are handled the
369
+ * same way across all error modes.
370
+ *
371
+ * Key difference: Start errors often indicate setup failures that should
372
+ * terminate the stream immediately (unlike transform errors which might
373
+ * be recoverable).
374
+ *
375
+ * @example
376
+ * ```ts
377
+ * // Database connection setup that might fail
378
+ * createOperator({
379
+ * errorMode: 'pass-through',
380
+ * start(controller) {
381
+ * // If this throws, it becomes an ObservableError in the stream
382
+ * this.db = connectToDatabase();
383
+ * },
384
+ * transform(chunk, controller) {
385
+ * const result = this.db.process(chunk);
386
+ * controller.enqueue(result);
387
+ * }
388
+ * });
389
+ * ```
390
+ *
391
+ * @typeParam T - Input chunk type
392
+ * @typeParam O - Output chunk type
393
+ * @typeParam S - State type (for stateful operators)
394
+ * @param errorMode - How to handle errors ("pass-through", "ignore", "throw", "manual")
395
+ * @param start - Your start function (stateless or stateful, optional)
396
+ * @param context - Info about the operator, including name and state
397
+ * @returns A function suitable for TransformStream's `start` property, or undefined
398
+ */
399
+ export function handleStart(errorMode, start, context = {}) {
400
+ if (!start)
401
+ return;
402
+ const operatorName = context.operatorName || `operator:unknown`;
403
+ const isStateful = context.isStateful || false;
404
+ const state = context.state;
405
+ return async function (controller) {
406
+ try {
407
+ if (isStateful) {
408
+ // If stateful, pass the state along
409
+ return await start(state, controller);
410
+ }
411
+ return await start(controller);
412
+ }
413
+ catch (err) {
414
+ switch (errorMode) {
415
+ case "ignore":
416
+ controller.terminate();
417
+ break;
418
+ case "throw":
419
+ return controller.error(ObservableError.from(err, `${operatorName}:start`));
420
+ case "pass-through":
421
+ controller.enqueue(ObservableError.from(err, `${operatorName}:start`));
422
+ controller.terminate();
423
+ break;
424
+ case "manual":
425
+ default:
426
+ throw err;
427
+ }
428
+ }
429
+ };
430
+ }
431
+ /**
432
+ * Lifecycle error handling for stream cleanup
433
+ *
434
+ * The flush() method runs once when the input stream ends. This is your
435
+ * last chance to emit final values or clean up resources. Flush errors
436
+ * should typically terminate the stream since there's no more input to process.
437
+ *
438
+ * Common use cases: Emitting buffered data, closing file handles,
439
+ * sending final aggregated results.
440
+ *
441
+ * @example
442
+ * ```ts
443
+ * // Buffer that flushes remaining items on stream end
444
+ * createOperator({
445
+ * errorMode: 'throw', // Any flush error should fail the stream
446
+ * transform(chunk, controller) {
447
+ * this.buffer.push(chunk);
448
+ * if (this.buffer.length >= 10) {
449
+ * controller.enqueue([...this.buffer]);
450
+ * this.buffer.length = 0;
451
+ * }
452
+ * },
453
+ * flush(controller) {
454
+ * // Emit any remaining buffered items
455
+ * if (this.buffer.length > 0) {
456
+ * controller.enqueue([...this.buffer]);
457
+ * }
458
+ * // If this throws, stream fails (as intended)
459
+ * this.cleanup();
460
+ * }
461
+ * });
462
+ * ```
463
+ *
464
+ * @typeParam T - Input chunk type
465
+ * @typeParam O - Output chunk type
466
+ * @typeParam S - State type (for stateful operators)
467
+ * @param errorMode - How to handle errors ("pass-through", "ignore", "throw", "manual")
468
+ * @param flush - Your flush function (stateless or stateful, optional)
469
+ * @param context - Info about the operator, including name and state
470
+ * @returns A function suitable for TransformStream's `flush` property, or undefined
471
+ */
472
+ export function handleFlush(errorMode, flush, context = {}) {
473
+ if (!flush)
474
+ return;
475
+ const operatorName = context.operatorName || `operator:unknown`;
476
+ const isStateful = context.isStateful || false;
477
+ const state = context.state;
478
+ return async function (controller) {
479
+ try {
480
+ if (isStateful) {
481
+ // If stateful, pass the state along
482
+ return await flush(state, controller);
483
+ }
484
+ return await flush(controller);
485
+ }
486
+ catch (err) {
487
+ switch (errorMode) {
488
+ case "ignore":
489
+ controller.terminate();
490
+ break;
491
+ case "throw":
492
+ return controller.error(ObservableError.from(err, `${operatorName}:flush`));
493
+ case "pass-through":
494
+ controller.enqueue(ObservableError.from(err, `${operatorName}:flush`));
495
+ controller.terminate();
496
+ break;
497
+ case "manual":
498
+ default:
499
+ throw err;
500
+ }
501
+ }
502
+ };
503
+ }
504
+ // Default case
505
+ export function createStatefulOperator(options) {
506
+ // Extract operator name from options or the function name for better error reporting
507
+ const operatorName = `operator:stateful:${options.name || "unknown"}`;
508
+ const errorMode = options?.errorMode ??
509
+ "pass-through";
510
+ // Extract only what we need to avoid retaining the full options object
511
+ const transform = options
512
+ ?.transform;
513
+ const start = options?.start;
514
+ const flush = options?.flush;
515
+ return (source) => {
516
+ try {
517
+ // Create state only when the stream is used
518
+ let state;
519
+ try {
520
+ // Initialize the state
521
+ state = options.createState();
522
+ }
523
+ catch (err) {
524
+ switch (errorMode) {
525
+ case "ignore":
526
+ return source;
527
+ case "manual":
528
+ case "throw":
529
+ throw err;
530
+ }
531
+ // If state creation fails, return a stream that errors immediately
532
+ return source.pipeThrough(injectError(err, `${operatorName}:create:state`, options));
533
+ }
534
+ // Create a transform stream with the provided options
535
+ const transformStream = new TransformStream({
536
+ // Transform function to process each chunk
537
+ start: handleStart(errorMode, start, {
538
+ operatorName,
539
+ isStateful: true,
540
+ state,
541
+ }),
542
+ // Start function called when the stream is initialized
543
+ transform: handleTransform(errorMode, transform, {
544
+ operatorName,
545
+ isStateful: true,
546
+ state,
547
+ }),
548
+ // Flush function called when the input is done
549
+ flush: handleFlush(errorMode, flush, {
550
+ operatorName,
551
+ isStateful: true,
552
+ state,
553
+ }),
554
+ }, { highWaterMark: 1 }, { highWaterMark: 0 });
555
+ // Pipe the source through the transform
556
+ return source.pipeThrough(transformStream);
557
+ }
558
+ catch (err) {
559
+ // If setup fails, return a stream that errors immediately
560
+ return source.pipeThrough(injectError(err, `${operatorName}:setup`, options));
561
+ }
562
+ };
563
+ }
@@ -0,0 +1,118 @@
1
+ import type { ObservableError } from "../error.js";
2
+ import type { SpecObservable } from "../_spec.js";
3
+ import type { Operator } from "./_types.js";
4
+ import { Observable } from "../observable.js";
5
+ /**
6
+ * Pipe function with 19 overloads to handle up to 19 operators with proper typing.
7
+ * Takes an Observable as input and returns an Observable as output, but uses
8
+ * streams internally for efficiency.
9
+ *
10
+ * This function takes an Observable as input and applies a series of operators
11
+ * to transform it. It supports up to 19 operators with full type safety.
12
+ *
13
+ * Internally, this function converts the Observable to a ReadableStream,
14
+ * applies the stream operators, then converts back to an Observable.
15
+ *
16
+ * @returns A new Observable with all transforms applied
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // Basic pipeline with 3 operators
21
+ * const result = pipe(
22
+ * sourceObservable,
23
+ * mapValue(x => x * 2),
24
+ * filterValue(x => x > 10),
25
+ * takeValue(5)
26
+ * );
27
+ *
28
+ * // Complex pipeline with many operators
29
+ * const result = pipe(
30
+ * sourceObservable,
31
+ * mapValue(x => x * 2),
32
+ * filterValue(x => x > 10),
33
+ * takeValue(5),
34
+ * mapValue(x => x.toString()),
35
+ * filterValue(x => x.length > 1),
36
+ * mapValue(x => x.toUpperCase()),
37
+ * // ... up to 19 operators total
38
+ * );
39
+ * ```
40
+ */
41
+ export declare function pipe<T>(source: SpecObservable<T>): Observable<T>;
42
+ /**
43
+ * Pipes a source through an operator chain with one applied operator.
44
+ */
45
+ export declare function pipe<T, A>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>): Observable<A>;
46
+ /**
47
+ * Pipes a source through an operator chain with two applied operators.
48
+ */
49
+ export declare function pipe<T, A, B>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>): Observable<B>;
50
+ /**
51
+ * Pipes a source through an operator chain with three applied operators.
52
+ */
53
+ export declare function pipe<T, A, B, C>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>): Observable<C>;
54
+ /**
55
+ * Pipes a source through an operator chain with four applied operators.
56
+ */
57
+ export declare function pipe<T, A, B, C, D>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>): Observable<D>;
58
+ /**
59
+ * Pipes a source through an operator chain with five applied operators.
60
+ */
61
+ export declare function pipe<T, A, B, C, D, E>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>): Observable<E>;
62
+ /**
63
+ * Pipes a source through an operator chain with six applied operators.
64
+ */
65
+ export declare function pipe<T, A, B, C, D, E, F>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>): Observable<F>;
66
+ /**
67
+ * Pipes a source through an operator chain with seven applied operators.
68
+ */
69
+ export declare function pipe<T, A, B, C, D, E, F, G>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>): Observable<G>;
70
+ /**
71
+ * Pipes a source through an operator chain with eight applied operators.
72
+ */
73
+ export declare function pipe<T, A, B, C, D, E, F, G, H>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>): Observable<H>;
74
+ /**
75
+ * Pipes a source through an operator chain with nine applied operators.
76
+ */
77
+ export declare function pipe<T, A, B, C, D, E, F, G, H, I>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>): Observable<I>;
78
+ /**
79
+ * Pipes a source through an operator chain with ten applied operators.
80
+ */
81
+ export declare function pipe<T, A, B, C, D, E, F, G, H, I, J>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>, op10: Operator<I, J>): Observable<J>;
82
+ /**
83
+ * Pipes a source through an operator chain with eleven applied operators.
84
+ */
85
+ export declare function pipe<T, A, B, C, D, E, F, G, H, I, J, K>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>, op10: Operator<I, J>, op11: Operator<J, K>): Observable<K>;
86
+ /**
87
+ * Pipes a source through an operator chain with twelve applied operators.
88
+ */
89
+ export declare function pipe<T, A, B, C, D, E, F, G, H, I, J, K, L>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>, op10: Operator<I, J>, op11: Operator<J, K>, op12: Operator<K, L>): Observable<L>;
90
+ /**
91
+ * Pipes a source through an operator chain with thirteen applied operators.
92
+ */
93
+ export declare function pipe<T, A, B, C, D, E, F, G, H, I, J, K, L, M>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>, op10: Operator<I, J>, op11: Operator<J, K>, op12: Operator<K, L>, op13: Operator<L, M>): Observable<M>;
94
+ /**
95
+ * Pipes a source through an operator chain with fourteen applied operators.
96
+ */
97
+ export declare function pipe<T, A, B, C, D, E, F, G, H, I, J, K, L, M, N>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>, op10: Operator<I, J>, op11: Operator<J, K>, op12: Operator<K, L>, op13: Operator<L, M>, op14: Operator<M, N>): Observable<N>;
98
+ /**
99
+ * Pipes a source through an operator chain with fifteen applied operators.
100
+ */
101
+ export declare function pipe<T, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>, op10: Operator<I, J>, op11: Operator<J, K>, op12: Operator<K, L>, op13: Operator<L, M>, op14: Operator<M, N>, op15: Operator<N, O>): Observable<O>;
102
+ /**
103
+ * Pipes a source through an operator chain with sixteen applied operators.
104
+ */
105
+ export declare function pipe<T, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>, op10: Operator<I, J>, op11: Operator<J, K>, op12: Operator<K, L>, op13: Operator<L, M>, op14: Operator<M, N>, op15: Operator<N, O>, op16: Operator<O, P>): Observable<P>;
106
+ /**
107
+ * Pipes a source through an operator chain with seventeen applied operators.
108
+ */
109
+ export declare function pipe<T, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>, op10: Operator<I, J>, op11: Operator<J, K>, op12: Operator<K, L>, op13: Operator<L, M>, op14: Operator<M, N>, op15: Operator<N, O>, op16: Operator<O, P>, op17: Operator<P, Q>): Observable<Q>;
110
+ /**
111
+ * Pipes a source through an operator chain with eighteen applied operators.
112
+ */
113
+ export declare function pipe<T, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>, op10: Operator<I, J>, op11: Operator<J, K>, op12: Operator<K, L>, op13: Operator<L, M>, op14: Operator<M, N>, op15: Operator<N, O>, op16: Operator<O, P>, op17: Operator<P, Q>, op18: Operator<Q, R>): Observable<R>;
114
+ /**
115
+ * Pipes a source through an operator chain with nineteen applied operators.
116
+ */
117
+ export declare function pipe<T, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S>(source: SpecObservable<T>, op1: Operator<T | ObservableError, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>, op10: Operator<I, J>, op11: Operator<J, K>, op12: Operator<K, L>, op13: Operator<L, M>, op14: Operator<M, N>, op15: Operator<N, O>, op16: Operator<O, P>, op17: Operator<P, Q>, op18: Operator<Q, R>, op19: Operator<R, S>): Observable<S>;
118
+ //# sourceMappingURL=pipe.d.ts.map