@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,414 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ObservableError = void 0;
4
+ exports.assertObservableError = assertObservableError;
5
+ exports.isObservableError = isObservableError;
6
+ // @filename: error.ts
7
+ /**
8
+ * Error primitives and guards for Observable pipelines.
9
+ *
10
+ * This entrypoint explains the error values that travel through this library
11
+ * when an operator uses pass-through error handling. It exports the
12
+ * `ObservableError` class plus helper functions for narrowing and asserting
13
+ * those wrapped failures without losing the original error object, stack, or
14
+ * operator context.
15
+ *
16
+ * Reach for this module when you want to inspect failures as data, recover from
17
+ * an upstream step without throwing away buffered values, or surface richer
18
+ * debugging information than a plain `Error` can carry on its own.
19
+ *
20
+ * @module
21
+ */
22
+ require("./_dnt.polyfills.js");
23
+ /**
24
+ * Represents an error that occurred during Observable operations,
25
+ * with the ability to aggregate multiple underlying errors.
26
+ *
27
+ * This class extends AggregateError to provide additional context about
28
+ * where and how errors occurred in an Observable pipeline. It can collect
29
+ * multiple errors that occur during a chain of operations while preserving
30
+ * the contextual information about each error.
31
+ *
32
+ * In addition, this class solves a crucial problem with error handling in ReadableStreams. When we call
33
+ * `controller.error()` on a ReadableStream, it immediately puts the stream in an errored state,
34
+ * which can cause values emitted before the error to be lost. By wrapping errors as special
35
+ * values that flow through the normal value channel, we ensure all values emitted before an
36
+ * error are properly processed.
37
+ *
38
+ * Key features:
39
+ * - Tracks which operator caused the error
40
+ * - Captures the value being processed when the error occurred
41
+ * - Aggregates multiple errors from a pipeline
42
+ * - Preserves the original error objects
43
+ * - Builds an error chain showing the full path of error propagation
44
+ */
45
+ class ObservableError extends AggregateError {
46
+ /**
47
+ * Creates a new ObservableError.
48
+ *
49
+ * @param errors - The error(s) that caused this error
50
+ * @param message - The error message
51
+ * @param options - Additional error context
52
+ */
53
+ constructor(errors, message, options) {
54
+ // Normalize errors to an array of Error objects
55
+ const errorArray = Array.isArray(errors) ? errors : [errors];
56
+ const normalizedErrors = errorArray.map((err) => err instanceof Error ? err : new Error(String(err)));
57
+ super(normalizedErrors, message, { cause: options?.cause });
58
+ /** The operator where the error occurred */
59
+ Object.defineProperty(this, "operator", {
60
+ enumerable: true,
61
+ configurable: true,
62
+ writable: true,
63
+ value: void 0
64
+ });
65
+ /** The value being processed when the error occurred */
66
+ Object.defineProperty(this, "value", {
67
+ enumerable: true,
68
+ configurable: true,
69
+ writable: true,
70
+ value: void 0
71
+ });
72
+ /** Helpful potential fixes for errors */
73
+ Object.defineProperty(this, "tip", {
74
+ enumerable: true,
75
+ configurable: true,
76
+ writable: true,
77
+ value: void 0
78
+ });
79
+ this.name = "ObservableError";
80
+ this.operator = options?.operator;
81
+ this.value = options?.value;
82
+ this.tip = options?.tip;
83
+ }
84
+ /**
85
+ * Returns a string representation of the error including the operator
86
+ * and value context if available.
87
+ */
88
+ toString() {
89
+ let result = `${this.name}: ${this.message}`;
90
+ if (this.operator) {
91
+ result += `\n in operator: ${this.operator}`;
92
+ }
93
+ if (this.value !== undefined) {
94
+ const valueStr = typeof this.value === "object"
95
+ ? JSON.stringify(this.value).slice(0, 100) // Truncate long objects
96
+ : String(this.value);
97
+ result += `\n processing value: ${valueStr}`;
98
+ }
99
+ if (this.errors.length > 0) {
100
+ result += "\n with errors:";
101
+ this.errors.forEach((err, i) => {
102
+ result += `\n ${i + 1}) ${err}`;
103
+ });
104
+ }
105
+ if (this.tip) {
106
+ result += `\n tip: ${this.tip}`;
107
+ }
108
+ return result;
109
+ }
110
+ /**
111
+ * Creates an ObservableError from any error that occurs during
112
+ * operator execution.
113
+ *
114
+ * @param error - The original error
115
+ * @param operator - The operator name
116
+ * @param value - The value being processed
117
+ * @returns An ObservableError
118
+ */
119
+ static from(error, operator, value, tip) {
120
+ if (error instanceof ObservableError) {
121
+ // If it's already an ObservableError, add context if not present
122
+ if (!error.operator && operator) {
123
+ return new ObservableError(error.errors, error.message, {
124
+ operator,
125
+ value: error.value || value,
126
+ cause: error.cause,
127
+ tip: error.tip,
128
+ });
129
+ }
130
+ return error;
131
+ }
132
+ // Create a new ObservableError
133
+ return new ObservableError(error, error instanceof Error ? error.message : String(error), { operator, value, cause: error, tip });
134
+ }
135
+ }
136
+ exports.ObservableError = ObservableError;
137
+ /**
138
+ * Asserts that a value is not an ObservableError and narrows the TypeScript type.
139
+ *
140
+ * This function acts as a TypeScript assertion function that:
141
+ * 1. **Type Narrowing**: If the function returns normally, TypeScript knows the value is definitely T (not T | ObservableError)
142
+ * 2. **Error Handling**: If the value is an ObservableError, either delegates to observer.error or throws
143
+ * 3. **Never Returns on Error**: When value is ObservableError, this function never returns normally
144
+ *
145
+ * **Key Behavior Changes**:
146
+ * - Now uses TypeScript's `asserts value is T` for proper type narrowing
147
+ * - When observer handles error, the function still doesn't return normally (assertion still fails)
148
+ * - Only returns normally when value is definitely not an ObservableError
149
+ *
150
+ * **Intent**: Provide type-safe error checking with automatic TypeScript type narrowing.
151
+ *
152
+ * **Usage Patterns**:
153
+ * ```typescript
154
+ * // Type narrowing in operator results
155
+ * const result: string | ObservableError = someOperation();
156
+ * assertObservableError(result); // Throws if error
157
+ * // TypeScript now knows result is string, not string | ObservableError
158
+ * console.log(result.toUpperCase()); // ✅ No type error
159
+ *
160
+ * // With observer error handling
161
+ * const observer = { error: err => console.error('Error:', err) };
162
+ * assertObservableError(result, observer);
163
+ * // Still throws/doesn't return normally on error, but observer is notified first
164
+ *
165
+ * // In subscribe callbacks
166
+ * observable.subscribe({
167
+ * next(value) { // value is T | ObservableError
168
+ * assertObservableError(value);
169
+ * // value is now narrowed to T
170
+ * processCleanValue(value);
171
+ * }
172
+ * });
173
+ * ```
174
+ *
175
+ * @template T - The expected type of the value when it's not an error
176
+ * @param value - The value to check, which may be either T or ObservableError
177
+ * @param obs - Optional observer that may contain an error handler function
178
+ *
179
+ * @throws {ObservableError} When value is an ObservableError (after notifying observer if provided)
180
+ *
181
+ * @example Basic type narrowing
182
+ * ```typescript
183
+ * const mixed: string | ObservableError = getValue();
184
+ * assertObservableError(mixed); // Throws if ObservableError
185
+ * console.log(mixed.length); // ✅ TypeScript knows mixed is string
186
+ * ```
187
+ *
188
+ * @example With error observer
189
+ * ```typescript
190
+ * const observer = {
191
+ * error: (err) => analytics.track('error', { message: err.message })
192
+ * };
193
+ *
194
+ * const mixed: User | ObservableError = fetchUser();
195
+ * assertObservableError(mixed, observer); // Observer notified, then throws
196
+ * console.log(mixed.name); // ✅ TypeScript knows mixed is User
197
+ * ```
198
+ *
199
+ * @example In operator pipeline
200
+ * ```typescript
201
+ * import { pipe, map, tap } from './helpers/mod.ts';
202
+ *
203
+ * pipe(
204
+ * source,
205
+ * map(x => processX(x)), // Returns T | ObservableError
206
+ * tap(result => {
207
+ * assertObservableError(result); // Type narrows from T | ObservableError to T
208
+ * sendAnalytics(result); // ✅ result is definitely T
209
+ * })
210
+ * )
211
+ * ```
212
+ */
213
+ function assertObservableError(value, obs) {
214
+ if (value instanceof ObservableError) {
215
+ // Notify observer first if available
216
+ if (typeof obs?.error === "function") {
217
+ try {
218
+ obs.error(value);
219
+ }
220
+ catch (observerError) {
221
+ // If observer throws, still throw the original error
222
+ // but log the observer error to avoid losing it
223
+ console.error("Observer error handler threw:", observerError);
224
+ }
225
+ }
226
+ // Always throw when value is ObservableError
227
+ // This ensures the function never returns normally for errors
228
+ throw value;
229
+ }
230
+ // If we reach here, value is definitely T (not T | ObservableError)
231
+ // TypeScript assertion function automatically narrows the type
232
+ }
233
+ /**
234
+ * Checks if a value is an ObservableError without throwing exceptions.
235
+ *
236
+ * When working with Observable pipelines, you often receive values that could be either successful
237
+ * results or errors. This creates a dilemma: how do you safely check what you got without risking
238
+ * crashes or poor performance? That's exactly what this function solves.
239
+ *
240
+ * **Why This Function Exists**:
241
+ *
242
+ * Imagine you're processing a stream of data where some items might be errors. Without a proper
243
+ * way to distinguish between good data and errors, you'd have to either:
244
+ * - Risk calling methods on errors (which crashes your app)
245
+ * - Write repetitive `instanceof` checks everywhere (which clutters your code)
246
+ * - Use try/catch blocks for control flow (which hurts performance)
247
+ *
248
+ * This function eliminates all those problems by giving you a clean, fast way to ask:
249
+ * "Is this thing an error?" If yes, you can handle it appropriately. If no, you can
250
+ * safely process it as valid data.
251
+ *
252
+ * **How It Fits With Other Functions**:
253
+ *
254
+ * Think of this as the gentle cousin of `assertObservableError()`. While `assertObservableError()`
255
+ * says "this better not be an error or I'm throwing an exception," this function politely asks
256
+ * "excuse me, are you an error?" and gives you a yes/no answer.
257
+ *
258
+ * This makes it perfect for situations where you want to handle both success and error cases
259
+ * gracefully, rather than just crashing when something goes wrong.
260
+ *
261
+ * **Performance Story**:
262
+ *
263
+ * Under the hood, this function uses a simple `instanceof` check. Now, you might wonder:
264
+ * "Why not just use `instanceof` directly?" The answer lies in convenience and consistency.
265
+ *
266
+ * Here's what actually happens performance-wise:
267
+ * - This function IS an `instanceof` check (no performance difference there)
268
+ * - Modern JavaScript engines are extremely good at optimizing `instanceof`
269
+ * - The real performance win comes from avoiding try/catch blocks for control flow
270
+ * - When the engine inlines this function, the overhead becomes virtually zero
271
+ *
272
+ * **Performance Reality Check**:
273
+ * - `isObservableError(value)` and `value instanceof ObservableError` perform identically
274
+ * - Both are fast enough that you'll never notice the difference in real applications
275
+ * - The real performance benefit is architectural: avoiding exceptions for normal control flow
276
+ * - Exception throwing/catching can be 10-100x slower, but that's comparing apples to oranges
277
+ *
278
+ * In practice, use this function because it's clearer and more consistent with the library's
279
+ * design patterns, not because of micro-optimizations.
280
+ *
281
+ * **Common Ways to Use This Function**:
282
+ *
283
+ * Let's walk through the most common scenarios where this function shines:
284
+ *
285
+ * ```typescript
286
+ * // Scenario 1: Branching logic in data processing
287
+ * function processItem<T>(item: T | ObservableError) {
288
+ * if (isObservableError(item)) {
289
+ * // TypeScript now knows item is an ObservableError
290
+ * console.error('Something went wrong:', item.message);
291
+ * return null; // or however you want to handle errors
292
+ * }
293
+ *
294
+ * // TypeScript now knows item is T - no more type errors!
295
+ * return transformData(item);
296
+ * }
297
+ *
298
+ * // Scenario 2: Filtering out errors from a collection
299
+ * // Note: You need proper type predicates for TypeScript to understand
300
+ * const cleanData = mixedResults.filter((item): item is T => !isObservableError(item));
301
+ * // cleanData is now properly typed as T[]
302
+ *
303
+ * // Scenario 3: Separating errors from successes
304
+ * const errors = results.filter(isObservableError);
305
+ * const successes = results.filter((item): item is T => !isObservableError(item));
306
+ * // Now you can handle each group with proper typing
307
+ *
308
+ * // Scenario 4: Early exit optimization
309
+ * function expensiveCalculation<T, U>(input: T | ObservableError): U | ObservableError {
310
+ * if (isObservableError(input)) {
311
+ * return input; // Don't waste time processing errors
312
+ * }
313
+ *
314
+ * // Only do expensive work on valid data
315
+ * return performComplexOperation(input);
316
+ * }
317
+ * ```
318
+ *
319
+ * **What Makes This Function Safe**:
320
+ *
321
+ * Unlike functions that might throw exceptions, this one is designed to never fail.
322
+ * It gracefully handles all the weird edge cases you might encounter:
323
+ * - Null or undefined values? Returns false (they're not errors)
324
+ * - Numbers, strings, or other primitives? Returns false (can't be ObservableErrors)
325
+ * - Objects that aren't ObservableErrors? Returns false (not what we're looking for)
326
+ * - Subclasses of ObservableError? Returns true (proper inheritance support)
327
+ *
328
+ * This means you can safely call it on anything without worrying about crashes.
329
+ *
330
+ * **When to Use This vs Other Options**:
331
+ *
332
+ * Choose `isObservableError()` when:
333
+ * - You want to handle both error and success cases in your code
334
+ * - You're filtering or sorting mixed arrays of results and errors
335
+ * - You're building conditional logic that branches based on error state
336
+ * - You want to avoid exceptions and prefer explicit error handling
337
+ *
338
+ * Choose `assertObservableError()` instead when:
339
+ * - You expect the value to NOT be an error, and want to crash if it is
340
+ * - You're in a context where errors should stop processing immediately
341
+ * - You want TypeScript to automatically narrow types via assertion
342
+ *
343
+ * @template T - The expected type for successful (non-error) values
344
+ * @param value - Any value that might or might not be an ObservableError
345
+ * @returns true if the value is an ObservableError, false otherwise
346
+ *
347
+ * @example Simple error checking
348
+ * ```typescript
349
+ * const result: string | ObservableError = fetchData();
350
+ *
351
+ * if (isObservableError(result)) {
352
+ * console.error('Oops, something went wrong:', result.message);
353
+ * // result is typed as ObservableError here
354
+ * return;
355
+ * }
356
+ *
357
+ * // result is typed as string here
358
+ * console.log('Success! Got:', result.toUpperCase());
359
+ * ```
360
+ *
361
+ * @example Filtering errors from a list
362
+ * ```typescript
363
+ * const mixedResults: (User | ObservableError)[] = await fetchAllUsers();
364
+ *
365
+ * // Get only the successful results (with proper type predicate)
366
+ * const validUsers = mixedResults.filter((item): item is User => !isObservableError(item));
367
+ * // validUsers is now correctly typed as User[]
368
+ *
369
+ * // Get only the errors
370
+ * const errors = mixedResults.filter(isObservableError);
371
+ * // errors is now typed as ObservableError[]
372
+ *
373
+ * // Process each group separately
374
+ * if (errors.length > 0) {
375
+ * console.error(`Found ${errors.length} errors:`, errors);
376
+ * }
377
+ * console.log(`Processing ${validUsers.length} valid users`);
378
+ * ```
379
+ *
380
+ * @example Building error-resilient operators
381
+ * ```typescript
382
+ * import { pipe } from './helpers/mod.ts';
383
+ *
384
+ * function safeMap<T, U>(transform: (value: T) => U) {
385
+ * return (source: Observable<T | ObservableError>) => {
386
+ * return pipe(
387
+ * source,
388
+ * map(value => {
389
+ * // Skip processing errors - just pass them through
390
+ * if (isObservableError(value)) return value;
391
+ *
392
+ * // Only transform valid values
393
+ * try {
394
+ * return transform(value);
395
+ * } catch (error) {
396
+ * return ObservableError.from(error, 'safeMap', value);
397
+ * }
398
+ * })
399
+ * );
400
+ * };
401
+ * }
402
+ *
403
+ * // Usage
404
+ * const result = pipe(
405
+ * mixedDataStream,
406
+ * safeMap(user => user.name.toUpperCase())
407
+ * );
408
+ * ```
409
+ */
410
+ function isObservableError(value) {
411
+ // This is just a straightforward instanceof check
412
+ // Modern JavaScript engines optimize this extremely well
413
+ return value instanceof ObservableError;
414
+ }