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