@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.
- package/LICENSE +21 -0
- package/README.md +578 -0
- package/esm/_dnt.polyfills.d.ts +20 -0
- package/esm/_dnt.polyfills.d.ts.map +1 -0
- package/esm/_dnt.polyfills.js +12 -0
- package/esm/_spec.d.ts +260 -0
- package/esm/_spec.d.ts.map +1 -0
- package/esm/_spec.js +1 -0
- package/esm/_types.d.ts +141 -0
- package/esm/_types.d.ts.map +1 -0
- package/esm/_types.js +20 -0
- package/esm/error.d.ts +331 -0
- package/esm/error.d.ts.map +1 -0
- package/esm/error.js +408 -0
- package/esm/events.d.ts +320 -0
- package/esm/events.d.ts.map +1 -0
- package/esm/events.js +451 -0
- package/esm/helpers/_types.d.ts +188 -0
- package/esm/helpers/_types.d.ts.map +1 -0
- package/esm/helpers/_types.js +1 -0
- package/esm/helpers/mod.d.ts +90 -0
- package/esm/helpers/mod.d.ts.map +1 -0
- package/esm/helpers/mod.js +90 -0
- package/esm/helpers/operations/batch.d.ts +109 -0
- package/esm/helpers/operations/batch.d.ts.map +1 -0
- package/esm/helpers/operations/batch.js +140 -0
- package/esm/helpers/operations/combination.d.ts +162 -0
- package/esm/helpers/operations/combination.d.ts.map +1 -0
- package/esm/helpers/operations/combination.js +350 -0
- package/esm/helpers/operations/conditional.d.ts +211 -0
- package/esm/helpers/operations/conditional.d.ts.map +1 -0
- package/esm/helpers/operations/conditional.js +280 -0
- package/esm/helpers/operations/core.d.ts +198 -0
- package/esm/helpers/operations/core.d.ts.map +1 -0
- package/esm/helpers/operations/core.js +264 -0
- package/esm/helpers/operations/errors.d.ts +277 -0
- package/esm/helpers/operations/errors.d.ts.map +1 -0
- package/esm/helpers/operations/errors.js +378 -0
- package/esm/helpers/operations/mod.d.ts +26 -0
- package/esm/helpers/operations/mod.d.ts.map +1 -0
- package/esm/helpers/operations/mod.js +25 -0
- package/esm/helpers/operations/timing.d.ts +206 -0
- package/esm/helpers/operations/timing.d.ts.map +1 -0
- package/esm/helpers/operations/timing.js +457 -0
- package/esm/helpers/operators.d.ts +520 -0
- package/esm/helpers/operators.d.ts.map +1 -0
- package/esm/helpers/operators.js +563 -0
- package/esm/helpers/pipe.d.ts +118 -0
- package/esm/helpers/pipe.d.ts.map +1 -0
- package/esm/helpers/pipe.js +129 -0
- package/esm/helpers/utils.d.ts +142 -0
- package/esm/helpers/utils.d.ts.map +1 -0
- package/esm/helpers/utils.js +193 -0
- package/esm/mod.d.ts +863 -0
- package/esm/mod.d.ts.map +1 -0
- package/esm/mod.js +861 -0
- package/esm/observable.d.ts +1610 -0
- package/esm/observable.d.ts.map +1 -0
- package/esm/observable.js +1970 -0
- package/esm/package.json +3 -0
- package/esm/queue.d.ts +201 -0
- package/esm/queue.d.ts.map +1 -0
- package/esm/queue.js +273 -0
- package/esm/symbol.d.ts +60 -0
- package/esm/symbol.d.ts.map +1 -0
- package/esm/symbol.js +132 -0
- package/package.json +96 -0
- package/script/_dnt.polyfills.d.ts +20 -0
- package/script/_dnt.polyfills.d.ts.map +1 -0
- package/script/_dnt.polyfills.js +13 -0
- package/script/_spec.d.ts +260 -0
- package/script/_spec.d.ts.map +1 -0
- package/script/_spec.js +2 -0
- package/script/_types.d.ts +141 -0
- package/script/_types.d.ts.map +1 -0
- package/script/_types.js +22 -0
- package/script/error.d.ts +331 -0
- package/script/error.d.ts.map +1 -0
- package/script/error.js +414 -0
- package/script/events.d.ts +320 -0
- package/script/events.d.ts.map +1 -0
- package/script/events.js +458 -0
- package/script/helpers/_types.d.ts +188 -0
- package/script/helpers/_types.d.ts.map +1 -0
- package/script/helpers/_types.js +2 -0
- package/script/helpers/mod.d.ts +90 -0
- package/script/helpers/mod.d.ts.map +1 -0
- package/script/helpers/mod.js +106 -0
- package/script/helpers/operations/batch.d.ts +109 -0
- package/script/helpers/operations/batch.d.ts.map +1 -0
- package/script/helpers/operations/batch.js +144 -0
- package/script/helpers/operations/combination.d.ts +162 -0
- package/script/helpers/operations/combination.d.ts.map +1 -0
- package/script/helpers/operations/combination.js +355 -0
- package/script/helpers/operations/conditional.d.ts +211 -0
- package/script/helpers/operations/conditional.d.ts.map +1 -0
- package/script/helpers/operations/conditional.js +286 -0
- package/script/helpers/operations/core.d.ts +198 -0
- package/script/helpers/operations/core.d.ts.map +1 -0
- package/script/helpers/operations/core.js +272 -0
- package/script/helpers/operations/errors.d.ts +277 -0
- package/script/helpers/operations/errors.d.ts.map +1 -0
- package/script/helpers/operations/errors.js +387 -0
- package/script/helpers/operations/mod.d.ts +26 -0
- package/script/helpers/operations/mod.d.ts.map +1 -0
- package/script/helpers/operations/mod.js +41 -0
- package/script/helpers/operations/timing.d.ts +206 -0
- package/script/helpers/operations/timing.d.ts.map +1 -0
- package/script/helpers/operations/timing.js +464 -0
- package/script/helpers/operators.d.ts +520 -0
- package/script/helpers/operators.d.ts.map +1 -0
- package/script/helpers/operators.js +570 -0
- package/script/helpers/pipe.d.ts +118 -0
- package/script/helpers/pipe.d.ts.map +1 -0
- package/script/helpers/pipe.js +132 -0
- package/script/helpers/utils.d.ts +142 -0
- package/script/helpers/utils.d.ts.map +1 -0
- package/script/helpers/utils.js +200 -0
- package/script/mod.d.ts +863 -0
- package/script/mod.d.ts.map +1 -0
- package/script/mod.js +877 -0
- package/script/observable.d.ts +1610 -0
- package/script/observable.d.ts.map +1 -0
- package/script/observable.js +1984 -0
- package/script/package.json +3 -0
- package/script/queue.d.ts +201 -0
- package/script/queue.d.ts.map +1 -0
- package/script/queue.js +286 -0
- package/script/symbol.d.ts +60 -0
- package/script/symbol.d.ts.map +1 -0
- package/script/symbol.js +135 -0
package/script/error.js
ADDED
|
@@ -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
|
+
}
|