@okikio/observables 1.0.2 → 1.3.0
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/README.md +117 -6
- package/esm/helpers/_types.d.ts +62 -0
- package/esm/helpers/_types.d.ts.map +1 -1
- package/esm/helpers/operations/combination.d.ts +112 -3
- package/esm/helpers/operations/combination.d.ts.map +1 -1
- package/esm/helpers/operations/combination.js +682 -115
- package/esm/helpers/operations/conditional.d.ts +77 -1
- package/esm/helpers/operations/conditional.d.ts.map +1 -1
- package/esm/helpers/operations/conditional.js +139 -0
- package/esm/helpers/operators.d.ts +57 -0
- package/esm/helpers/operators.d.ts.map +1 -1
- package/esm/helpers/operators.js +139 -4
- package/esm/helpers/utils.d.ts +73 -1
- package/esm/helpers/utils.d.ts.map +1 -1
- package/esm/helpers/utils.js +168 -0
- package/package.json +1 -1
- package/script/helpers/_types.d.ts +62 -0
- package/script/helpers/_types.d.ts.map +1 -1
- package/script/helpers/operations/combination.d.ts +112 -3
- package/script/helpers/operations/combination.d.ts.map +1 -1
- package/script/helpers/operations/combination.js +686 -115
- package/script/helpers/operations/conditional.d.ts +77 -1
- package/script/helpers/operations/conditional.d.ts.map +1 -1
- package/script/helpers/operations/conditional.js +143 -0
- package/script/helpers/operators.d.ts +57 -0
- package/script/helpers/operators.d.ts.map +1 -1
- package/script/helpers/operators.js +141 -4
- package/script/helpers/utils.d.ts +73 -1
- package/script/helpers/utils.d.ts.map +1 -1
- package/script/helpers/utils.js +172 -0
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import "../../_dnt.polyfills.js";
|
|
16
16
|
import type { ExcludeError, Operator } from "../_types.js";
|
|
17
|
-
import
|
|
17
|
+
import { ObservableError } from "../../error.js";
|
|
18
18
|
/**
|
|
19
19
|
* Checks if every item in the stream passes a test.
|
|
20
20
|
*
|
|
@@ -125,6 +125,45 @@ export declare function some<T>(predicate: (value: ExcludeError<T>, index: numbe
|
|
|
125
125
|
* @returns A stream operator that finds the first matching value
|
|
126
126
|
*/
|
|
127
127
|
export declare function find<T>(predicate: (value: ExcludeError<T>, index: number) => boolean): Operator<T | ObservableError, T | ObservableError>;
|
|
128
|
+
/**
|
|
129
|
+
* Finds the index of the first item in the stream that passes a test.
|
|
130
|
+
*
|
|
131
|
+
* This mirrors `Array.prototype.findIndex()`: it emits the zero-based index of
|
|
132
|
+
* the first matching value, or `-1` if the stream completes without a match.
|
|
133
|
+
*
|
|
134
|
+
* @typeParam T - Type of values from the source stream
|
|
135
|
+
* @param predicate - Function to test each value
|
|
136
|
+
* @returns An operator that emits the first matching index or `-1`
|
|
137
|
+
*/
|
|
138
|
+
export declare function findIndex<T>(predicate: (value: ExcludeError<T>, index: number) => boolean): Operator<T | ObservableError, number | ObservableError>;
|
|
139
|
+
/**
|
|
140
|
+
* Emits the value at a specific zero-based source index.
|
|
141
|
+
*
|
|
142
|
+
* Unlike arrays, streams cannot jump to a position directly, so this operator
|
|
143
|
+
* reads and counts values until it reaches the requested slot. If the stream
|
|
144
|
+
* completes first, it emits nothing.
|
|
145
|
+
*
|
|
146
|
+
* @typeParam T - Type of values from the source stream
|
|
147
|
+
* @param targetIndex - Zero-based index to emit
|
|
148
|
+
* @returns An operator that emits at most one value from the requested index
|
|
149
|
+
*/
|
|
150
|
+
export declare function elementAt<T>(targetIndex: number): Operator<T | ObservableError, T | ObservableError>;
|
|
151
|
+
/**
|
|
152
|
+
* Emits the first value in the stream, optionally constrained by a predicate.
|
|
153
|
+
*
|
|
154
|
+
* Without a predicate this is the Observable equivalent of reading index `0`.
|
|
155
|
+
* With a predicate it behaves like `find()`, but the name is useful when the
|
|
156
|
+
* caller wants to emphasize "take the first match" rather than "search".
|
|
157
|
+
*
|
|
158
|
+
* @typeParam T - Type of values from the source stream
|
|
159
|
+
* @param predicate - Optional test that the first emitted value must satisfy
|
|
160
|
+
* @returns An operator that emits at most one matching value
|
|
161
|
+
*/
|
|
162
|
+
export declare function first<T>(): Operator<T | ObservableError, T | ObservableError>;
|
|
163
|
+
/**
|
|
164
|
+
* Emits the first value that satisfies the provided predicate.
|
|
165
|
+
*/
|
|
166
|
+
export declare function first<T>(predicate: (value: ExcludeError<T>, index: number) => boolean): Operator<T | ObservableError, T | ObservableError>;
|
|
128
167
|
/**
|
|
129
168
|
* Removes duplicate values from the stream, keeping only the first occurrence.
|
|
130
169
|
*
|
|
@@ -165,12 +204,48 @@ export declare function find<T>(predicate: (value: ExcludeError<T>, index: numbe
|
|
|
165
204
|
* @returns An operator that filters out duplicate values.
|
|
166
205
|
*/
|
|
167
206
|
export declare function unique<T, K = T>(keySelector?: (value: ExcludeError<T>) => K): Operator<T | ObservableError, ExcludeError<T> | ObservableError>;
|
|
207
|
+
/**
|
|
208
|
+
* Configuration for `changed()`.
|
|
209
|
+
*
|
|
210
|
+
* `by` lets you compare on a derived key instead of the full value.
|
|
211
|
+
* For example, you may want to emit only when `user.id` changes.
|
|
212
|
+
*/
|
|
213
|
+
export interface ChangedOptions<T, K = ExcludeError<T>> {
|
|
214
|
+
/**
|
|
215
|
+
* Pick the value that should be compared.
|
|
216
|
+
*
|
|
217
|
+
* By default, the full value is compared.
|
|
218
|
+
*/
|
|
219
|
+
by?: (value: ExcludeError<T>, index: number) => K;
|
|
220
|
+
/**
|
|
221
|
+
* Decide whether two comparison keys should be treated as the same.
|
|
222
|
+
*
|
|
223
|
+
* By default, `Object.is()` is used.
|
|
224
|
+
*/
|
|
225
|
+
equals?: (previous: K, current: K) => boolean;
|
|
226
|
+
/**
|
|
227
|
+
* Whether the first non-error value should be emitted.
|
|
228
|
+
*
|
|
229
|
+
* This defaults to `true`, which is usually what people expect from a
|
|
230
|
+
* "changed" operator.
|
|
231
|
+
*/
|
|
232
|
+
emitFirst?: boolean;
|
|
233
|
+
}
|
|
168
234
|
/**
|
|
169
235
|
* Emits an item only if it is different from the previous one.
|
|
170
236
|
*
|
|
171
237
|
* This is useful for streams where values can be emitted repeatedly, but you
|
|
172
238
|
* only care about the changes.
|
|
173
239
|
*
|
|
240
|
+
* Example 1:
|
|
241
|
+
* source: 1, 1, 2, 2, 3
|
|
242
|
+
* output: 1, 2, 3
|
|
243
|
+
*
|
|
244
|
+
* Example 2:
|
|
245
|
+
* source: { id: 1, name: "A" }, { id: 1, name: "B" }, { id: 2, name: "C" }
|
|
246
|
+
* changed({ by: value => value.id })
|
|
247
|
+
* output: first item, then the item with id 2
|
|
248
|
+
*
|
|
174
249
|
* @example
|
|
175
250
|
* ```ts
|
|
176
251
|
* import { pipe, changed, from } from "./helpers/mod.ts";
|
|
@@ -208,4 +283,5 @@ export declare function unique<T, K = T>(keySelector?: (value: ExcludeError<T>)
|
|
|
208
283
|
* @param compare An optional function to compare two keys for equality.
|
|
209
284
|
* @returns An operator that filters out consecutive duplicate values.
|
|
210
285
|
*/
|
|
286
|
+
export declare function changed<T, K = ExcludeError<T>>(options?: ChangedOptions<T, K>): Operator<T | ObservableError, T | ObservableError>;
|
|
211
287
|
//# sourceMappingURL=conditional.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conditional.d.ts","sourceRoot":"","sources":["../../../src/helpers/operations/conditional.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"conditional.d.ts","sourceRoot":"","sources":["../../../src/helpers/operations/conditional.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.every = every;
|
|
4
4
|
exports.some = some;
|
|
5
5
|
exports.find = find;
|
|
6
|
+
exports.findIndex = findIndex;
|
|
7
|
+
exports.elementAt = elementAt;
|
|
8
|
+
exports.first = first;
|
|
6
9
|
exports.unique = unique;
|
|
10
|
+
exports.changed = changed;
|
|
7
11
|
/**
|
|
8
12
|
* Predicate and decision-oriented operators for Observable streams.
|
|
9
13
|
*
|
|
@@ -19,6 +23,7 @@ exports.unique = unique;
|
|
|
19
23
|
* @module
|
|
20
24
|
*/
|
|
21
25
|
require("../../_dnt.polyfills.js");
|
|
26
|
+
const error_js_1 = require("../../error.js");
|
|
22
27
|
const operators_js_1 = require("../operators.js");
|
|
23
28
|
/**
|
|
24
29
|
* Checks if every item in the stream passes a test.
|
|
@@ -187,6 +192,74 @@ function find(predicate) {
|
|
|
187
192
|
},
|
|
188
193
|
});
|
|
189
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Finds the index of the first item in the stream that passes a test.
|
|
197
|
+
*
|
|
198
|
+
* This mirrors `Array.prototype.findIndex()`: it emits the zero-based index of
|
|
199
|
+
* the first matching value, or `-1` if the stream completes without a match.
|
|
200
|
+
*
|
|
201
|
+
* @typeParam T - Type of values from the source stream
|
|
202
|
+
* @param predicate - Function to test each value
|
|
203
|
+
* @returns An operator that emits the first matching index or `-1`
|
|
204
|
+
*/
|
|
205
|
+
function findIndex(predicate) {
|
|
206
|
+
return (0, operators_js_1.createStatefulOperator)({
|
|
207
|
+
name: "findIndex",
|
|
208
|
+
createState: () => ({ index: 0, finished: false }),
|
|
209
|
+
transform(chunk, state, controller) {
|
|
210
|
+
if (state.finished) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const currentIndex = state.index;
|
|
214
|
+
const result = predicate(chunk, currentIndex);
|
|
215
|
+
state.index++;
|
|
216
|
+
if (result) {
|
|
217
|
+
state.finished = true;
|
|
218
|
+
controller.enqueue(currentIndex);
|
|
219
|
+
controller.terminate();
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
flush(state, controller) {
|
|
223
|
+
if (!state.finished) {
|
|
224
|
+
controller.enqueue(-1);
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Emits the value at a specific zero-based source index.
|
|
231
|
+
*
|
|
232
|
+
* Unlike arrays, streams cannot jump to a position directly, so this operator
|
|
233
|
+
* reads and counts values until it reaches the requested slot. If the stream
|
|
234
|
+
* completes first, it emits nothing.
|
|
235
|
+
*
|
|
236
|
+
* @typeParam T - Type of values from the source stream
|
|
237
|
+
* @param targetIndex - Zero-based index to emit
|
|
238
|
+
* @returns An operator that emits at most one value from the requested index
|
|
239
|
+
*/
|
|
240
|
+
function elementAt(targetIndex) {
|
|
241
|
+
if (!Number.isInteger(targetIndex) || targetIndex < 0) {
|
|
242
|
+
throw new RangeError("elementAt: targetIndex must be a non-negative integer");
|
|
243
|
+
}
|
|
244
|
+
return (0, operators_js_1.createStatefulOperator)({
|
|
245
|
+
name: "elementAt",
|
|
246
|
+
createState: () => ({ index: 0 }),
|
|
247
|
+
transform(chunk, state, controller) {
|
|
248
|
+
if (state.index === targetIndex) {
|
|
249
|
+
controller.enqueue(chunk);
|
|
250
|
+
controller.terminate();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
state.index++;
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function first(predicate) {
|
|
258
|
+
if (!predicate) {
|
|
259
|
+
return elementAt(0);
|
|
260
|
+
}
|
|
261
|
+
return find(predicate);
|
|
262
|
+
}
|
|
190
263
|
/**
|
|
191
264
|
* Removes duplicate values from the stream, keeping only the first occurrence.
|
|
192
265
|
*
|
|
@@ -241,12 +314,27 @@ function unique(keySelector) {
|
|
|
241
314
|
},
|
|
242
315
|
});
|
|
243
316
|
}
|
|
317
|
+
function defaultBy(value) {
|
|
318
|
+
return value;
|
|
319
|
+
}
|
|
320
|
+
function defaultEquals(previous, current) {
|
|
321
|
+
return Object.is(previous, current);
|
|
322
|
+
}
|
|
244
323
|
/**
|
|
245
324
|
* Emits an item only if it is different from the previous one.
|
|
246
325
|
*
|
|
247
326
|
* This is useful for streams where values can be emitted repeatedly, but you
|
|
248
327
|
* only care about the changes.
|
|
249
328
|
*
|
|
329
|
+
* Example 1:
|
|
330
|
+
* source: 1, 1, 2, 2, 3
|
|
331
|
+
* output: 1, 2, 3
|
|
332
|
+
*
|
|
333
|
+
* Example 2:
|
|
334
|
+
* source: { id: 1, name: "A" }, { id: 1, name: "B" }, { id: 2, name: "C" }
|
|
335
|
+
* changed({ by: value => value.id })
|
|
336
|
+
* output: first item, then the item with id 2
|
|
337
|
+
*
|
|
250
338
|
* @example
|
|
251
339
|
* ```ts
|
|
252
340
|
* import { pipe, changed, from } from "./helpers/mod.ts";
|
|
@@ -284,3 +372,58 @@ function unique(keySelector) {
|
|
|
284
372
|
* @param compare An optional function to compare two keys for equality.
|
|
285
373
|
* @returns An operator that filters out consecutive duplicate values.
|
|
286
374
|
*/
|
|
375
|
+
function changed(options = {}) {
|
|
376
|
+
const by = (options.by ??
|
|
377
|
+
defaultBy);
|
|
378
|
+
const equals = (options.equals ??
|
|
379
|
+
defaultEquals);
|
|
380
|
+
const emitFirst = options.emitFirst ?? true;
|
|
381
|
+
return (0, operators_js_1.createStatefulOperator)({
|
|
382
|
+
name: "changed",
|
|
383
|
+
createState: () => ({
|
|
384
|
+
hasPrevious: false,
|
|
385
|
+
previousKey: undefined,
|
|
386
|
+
index: 0,
|
|
387
|
+
}),
|
|
388
|
+
transform(chunk, state, controller) {
|
|
389
|
+
if ((0, error_js_1.isObservableError)(chunk)) {
|
|
390
|
+
controller.enqueue(chunk);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const value = chunk;
|
|
394
|
+
let currentKey;
|
|
395
|
+
try {
|
|
396
|
+
currentKey = by(value, state.index++);
|
|
397
|
+
}
|
|
398
|
+
catch (err) {
|
|
399
|
+
controller.enqueue(error_js_1.ObservableError.from(err, "operator:stateful:changed:by", value));
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (!state.hasPrevious) {
|
|
403
|
+
state.hasPrevious = true;
|
|
404
|
+
state.previousKey = currentKey;
|
|
405
|
+
if (emitFirst) {
|
|
406
|
+
controller.enqueue(value);
|
|
407
|
+
}
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
let isSame;
|
|
411
|
+
try {
|
|
412
|
+
isSame = equals(state.previousKey, currentKey);
|
|
413
|
+
}
|
|
414
|
+
catch (err) {
|
|
415
|
+
controller.enqueue(error_js_1.ObservableError.from(err, "operator:stateful:changed:equals", value));
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
state.previousKey = currentKey;
|
|
419
|
+
if (!isSame) {
|
|
420
|
+
controller.enqueue(value);
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
cancel(state) {
|
|
424
|
+
state.hasPrevious = false;
|
|
425
|
+
state.previousKey = undefined;
|
|
426
|
+
state.index = 0;
|
|
427
|
+
},
|
|
428
|
+
});
|
|
429
|
+
}
|
|
@@ -436,6 +436,63 @@ export declare function handleStart<T, O, S extends unknown = undefined>(errorMo
|
|
|
436
436
|
* @returns A function suitable for TransformStream's `flush` property, or undefined
|
|
437
437
|
*/
|
|
438
438
|
export declare function handleFlush<T, O, S extends unknown = undefined>(errorMode: OperatorErrorMode, flush?: TransformFunctionOptions<T, O>["flush"] | StatefulTransformFunctionOptions<T, O, S>["flush"], context?: TransformHandlerContext<S>): Transformer<T, O>["flush"];
|
|
439
|
+
/**
|
|
440
|
+
* Lifecycle handling for downstream cancellation cleanup.
|
|
441
|
+
*
|
|
442
|
+
* `flush()` runs when the source ends normally. `cancel()` runs when a consumer
|
|
443
|
+
* stops early, for example by unsubscribing, breaking out of `for await`, or
|
|
444
|
+
* otherwise cancelling the pipeline before the source completes.
|
|
445
|
+
*
|
|
446
|
+
* ```text
|
|
447
|
+
* source completes normally -> flush()
|
|
448
|
+
* consumer stops early -> cancel(reason)
|
|
449
|
+
* ```
|
|
450
|
+
*
|
|
451
|
+
* That distinction matters for operators that hold timers, inner
|
|
452
|
+
* subscriptions, or abort controllers. Those resources should be released when
|
|
453
|
+
* the consumer goes away, even if the source never reached completion.
|
|
454
|
+
*
|
|
455
|
+
* Cleanup here is intentionally not treated like transform output. If the
|
|
456
|
+
* cleanup callback fails, the cancellation promise rejects, but no
|
|
457
|
+
* `ObservableError` is emitted into the stream because the consumer has already
|
|
458
|
+
* said it is no longer interested in more values.
|
|
459
|
+
*
|
|
460
|
+
* @typeParam T - Input chunk type
|
|
461
|
+
* @typeParam O - Output chunk type
|
|
462
|
+
* @typeParam S - State type (for stateful operators)
|
|
463
|
+
* @param _errorMode - Unused here. Cancellation cleanup does not route through
|
|
464
|
+
* the operator error modes because it is teardown, not part of the data path.
|
|
465
|
+
* @param cancel - Your cancellation handler (stateless or stateful, optional)
|
|
466
|
+
* @param context - Info about the operator, including name and state
|
|
467
|
+
* @returns A function suitable for a ReadableStream `cancel` hook, or undefined
|
|
468
|
+
*/
|
|
469
|
+
export declare function handleCancel<T, O, S extends unknown = undefined>(_errorMode: OperatorErrorMode, cancel?: TransformFunctionOptions<T, O>["cancel"] | StatefulTransformFunctionOptions<T, O, S>["cancel"], context?: TransformHandlerContext<S>): ((reason?: unknown) => Promise<void>) | undefined;
|
|
470
|
+
/**
|
|
471
|
+
* Wraps a transformed stream so operator-level cleanup runs when a downstream
|
|
472
|
+
* consumer stops early.
|
|
473
|
+
*
|
|
474
|
+
* This wrapper has a deliberately narrow job: it forwards readable-side
|
|
475
|
+
* cancellation to an operator cleanup callback while preserving the chunks
|
|
476
|
+
* coming from the wrapped stream. It does not try to redefine TransformStream
|
|
477
|
+
* semantics or reinterpret cancellation as a normal data-path error.
|
|
478
|
+
*
|
|
479
|
+
* ```text
|
|
480
|
+
* source -> TransformStream -> wrapped readable -> consumer
|
|
481
|
+
* |
|
|
482
|
+
* +-> cancel(reason)
|
|
483
|
+
* ```
|
|
484
|
+
*
|
|
485
|
+
* @typeParam T - Output chunk type of the wrapped stream
|
|
486
|
+
* @param stream - The transformed stream to expose downstream
|
|
487
|
+
* @param options - Cancellation behavior for the wrapped stream
|
|
488
|
+
* @returns A readable stream that preserves output values and forwards cancel
|
|
489
|
+
*/
|
|
490
|
+
export declare function wrapStreamWithCancel<T>(stream: ReadableStream<T>, options?: {
|
|
491
|
+
/**
|
|
492
|
+
* Cleanup to run if the consumer cancels the stream before it completes.
|
|
493
|
+
*/
|
|
494
|
+
cancel?: (reason?: unknown) => void | Promise<void>;
|
|
495
|
+
}): ReadableStream<T>;
|
|
439
496
|
/**
|
|
440
497
|
* Creates operators that maintain state across stream chunks
|
|
441
498
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operators.d.ts","sourceRoot":"","sources":["../../src/helpers/operators.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"operators.d.ts","sourceRoot":"","sources":["../../src/helpers/operators.ts"],"names":[],"mappings":"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"}
|
|
@@ -224,6 +224,8 @@ exports.createOperator = createOperator;
|
|
|
224
224
|
exports.handleTransform = handleTransform;
|
|
225
225
|
exports.handleStart = handleStart;
|
|
226
226
|
exports.handleFlush = handleFlush;
|
|
227
|
+
exports.handleCancel = handleCancel;
|
|
228
|
+
exports.wrapStreamWithCancel = wrapStreamWithCancel;
|
|
227
229
|
exports.createStatefulOperator = createStatefulOperator;
|
|
228
230
|
const utils_js_1 = require("./utils.js");
|
|
229
231
|
const error_js_1 = require("../error.js");
|
|
@@ -237,6 +239,7 @@ function createOperator(options) {
|
|
|
237
239
|
const transform = options?.transform;
|
|
238
240
|
const start = options?.start;
|
|
239
241
|
const flush = options?.flush;
|
|
242
|
+
const cancel = options?.cancel;
|
|
240
243
|
return (source) => {
|
|
241
244
|
try {
|
|
242
245
|
// Create a transform stream with the provided options
|
|
@@ -250,8 +253,10 @@ function createOperator(options) {
|
|
|
250
253
|
// Flush function called when the input is done
|
|
251
254
|
flush: handleFlush(errorMode, flush, { operatorName }),
|
|
252
255
|
}, { highWaterMark: 1 }, { highWaterMark: 0 });
|
|
253
|
-
// Pipe the source through the transform
|
|
254
|
-
return source.pipeThrough(transformStream)
|
|
256
|
+
// Pipe the source through the transform and bind downstream cancellation
|
|
257
|
+
return wrapStreamWithCancel(source.pipeThrough(transformStream), {
|
|
258
|
+
cancel: handleCancel(errorMode, cancel, { operatorName }),
|
|
259
|
+
});
|
|
255
260
|
}
|
|
256
261
|
catch (err) {
|
|
257
262
|
// If setup fails, return a stream that errors immediately
|
|
@@ -508,6 +513,131 @@ function handleFlush(errorMode, flush, context = {}) {
|
|
|
508
513
|
}
|
|
509
514
|
};
|
|
510
515
|
}
|
|
516
|
+
/**
|
|
517
|
+
* Lifecycle handling for downstream cancellation cleanup.
|
|
518
|
+
*
|
|
519
|
+
* `flush()` runs when the source ends normally. `cancel()` runs when a consumer
|
|
520
|
+
* stops early, for example by unsubscribing, breaking out of `for await`, or
|
|
521
|
+
* otherwise cancelling the pipeline before the source completes.
|
|
522
|
+
*
|
|
523
|
+
* ```text
|
|
524
|
+
* source completes normally -> flush()
|
|
525
|
+
* consumer stops early -> cancel(reason)
|
|
526
|
+
* ```
|
|
527
|
+
*
|
|
528
|
+
* That distinction matters for operators that hold timers, inner
|
|
529
|
+
* subscriptions, or abort controllers. Those resources should be released when
|
|
530
|
+
* the consumer goes away, even if the source never reached completion.
|
|
531
|
+
*
|
|
532
|
+
* Cleanup here is intentionally not treated like transform output. If the
|
|
533
|
+
* cleanup callback fails, the cancellation promise rejects, but no
|
|
534
|
+
* `ObservableError` is emitted into the stream because the consumer has already
|
|
535
|
+
* said it is no longer interested in more values.
|
|
536
|
+
*
|
|
537
|
+
* @typeParam T - Input chunk type
|
|
538
|
+
* @typeParam O - Output chunk type
|
|
539
|
+
* @typeParam S - State type (for stateful operators)
|
|
540
|
+
* @param _errorMode - Unused here. Cancellation cleanup does not route through
|
|
541
|
+
* the operator error modes because it is teardown, not part of the data path.
|
|
542
|
+
* @param cancel - Your cancellation handler (stateless or stateful, optional)
|
|
543
|
+
* @param context - Info about the operator, including name and state
|
|
544
|
+
* @returns A function suitable for a ReadableStream `cancel` hook, or undefined
|
|
545
|
+
*/
|
|
546
|
+
function handleCancel(_errorMode, cancel, context = {}) {
|
|
547
|
+
if (!cancel)
|
|
548
|
+
return;
|
|
549
|
+
const operatorName = context.operatorName || `operator:unknown`;
|
|
550
|
+
const isStateful = context.isStateful || false;
|
|
551
|
+
const state = context.state;
|
|
552
|
+
return async function (reason) {
|
|
553
|
+
try {
|
|
554
|
+
if (isStateful) {
|
|
555
|
+
await cancel(state, reason);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
await cancel(reason);
|
|
559
|
+
}
|
|
560
|
+
catch (err) {
|
|
561
|
+
throw error_js_1.ObservableError.from(err, `${operatorName}:cancel`, reason);
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Wraps a transformed stream so operator-level cleanup runs when a downstream
|
|
567
|
+
* consumer stops early.
|
|
568
|
+
*
|
|
569
|
+
* This wrapper has a deliberately narrow job: it forwards readable-side
|
|
570
|
+
* cancellation to an operator cleanup callback while preserving the chunks
|
|
571
|
+
* coming from the wrapped stream. It does not try to redefine TransformStream
|
|
572
|
+
* semantics or reinterpret cancellation as a normal data-path error.
|
|
573
|
+
*
|
|
574
|
+
* ```text
|
|
575
|
+
* source -> TransformStream -> wrapped readable -> consumer
|
|
576
|
+
* |
|
|
577
|
+
* +-> cancel(reason)
|
|
578
|
+
* ```
|
|
579
|
+
*
|
|
580
|
+
* @typeParam T - Output chunk type of the wrapped stream
|
|
581
|
+
* @param stream - The transformed stream to expose downstream
|
|
582
|
+
* @param options - Cancellation behavior for the wrapped stream
|
|
583
|
+
* @returns A readable stream that preserves output values and forwards cancel
|
|
584
|
+
*/
|
|
585
|
+
function wrapStreamWithCancel(stream, options = {}) {
|
|
586
|
+
if (!options.cancel)
|
|
587
|
+
return stream;
|
|
588
|
+
const reader = stream.getReader();
|
|
589
|
+
let settled = false;
|
|
590
|
+
// The wrapper acquires an exclusive reader, so releasing it exactly once
|
|
591
|
+
// keeps teardown predictable even when both cancel and read completion race.
|
|
592
|
+
const release = () => {
|
|
593
|
+
if (settled)
|
|
594
|
+
return;
|
|
595
|
+
settled = true;
|
|
596
|
+
try {
|
|
597
|
+
reader.releaseLock();
|
|
598
|
+
}
|
|
599
|
+
catch {
|
|
600
|
+
// Releasing the reader is best-effort during teardown.
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
return new ReadableStream({
|
|
604
|
+
async pull(controller) {
|
|
605
|
+
try {
|
|
606
|
+
// Forward chunks one-by-one so downstream backpressure still controls
|
|
607
|
+
// how quickly we read from the wrapped stream.
|
|
608
|
+
const { done, value } = await reader.read();
|
|
609
|
+
if (done) {
|
|
610
|
+
release();
|
|
611
|
+
controller.close();
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
controller.enqueue(value);
|
|
615
|
+
}
|
|
616
|
+
catch (err) {
|
|
617
|
+
release();
|
|
618
|
+
controller.error(err);
|
|
619
|
+
}
|
|
620
|
+
},
|
|
621
|
+
async cancel(reason) {
|
|
622
|
+
try {
|
|
623
|
+
// Run operator cleanup first so resources such as timers or inner
|
|
624
|
+
// subscriptions are released before we cancel the wrapped reader.
|
|
625
|
+
await options.cancel?.(reason);
|
|
626
|
+
}
|
|
627
|
+
finally {
|
|
628
|
+
try {
|
|
629
|
+
// Cancelling the reader tells the wrapped stream that nobody wants
|
|
630
|
+
// more output. Any rejection here is surfaced through the returned
|
|
631
|
+
// cancellation promise instead of being emitted as a chunk.
|
|
632
|
+
await reader.cancel(reason);
|
|
633
|
+
}
|
|
634
|
+
finally {
|
|
635
|
+
release();
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
});
|
|
640
|
+
}
|
|
511
641
|
// Default case
|
|
512
642
|
function createStatefulOperator(options) {
|
|
513
643
|
// Extract operator name from options or the function name for better error reporting
|
|
@@ -519,6 +649,7 @@ function createStatefulOperator(options) {
|
|
|
519
649
|
?.transform;
|
|
520
650
|
const start = options?.start;
|
|
521
651
|
const flush = options?.flush;
|
|
652
|
+
const cancel = options?.cancel;
|
|
522
653
|
return (source) => {
|
|
523
654
|
try {
|
|
524
655
|
// Create state only when the stream is used
|
|
@@ -559,8 +690,14 @@ function createStatefulOperator(options) {
|
|
|
559
690
|
state,
|
|
560
691
|
}),
|
|
561
692
|
}, { highWaterMark: 1 }, { highWaterMark: 0 });
|
|
562
|
-
// Pipe the source through the transform
|
|
563
|
-
return source.pipeThrough(transformStream)
|
|
693
|
+
// Pipe the source through the transform and bind downstream cancellation
|
|
694
|
+
return wrapStreamWithCancel(source.pipeThrough(transformStream), {
|
|
695
|
+
cancel: handleCancel(errorMode, cancel, {
|
|
696
|
+
operatorName,
|
|
697
|
+
isStateful: true,
|
|
698
|
+
state,
|
|
699
|
+
}),
|
|
700
|
+
});
|
|
564
701
|
}
|
|
565
702
|
catch (err) {
|
|
566
703
|
// If setup fails, return a stream that errors immediately
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { TransformFunctionOptions, TransformStreamOptions } from "./_types.js";
|
|
1
|
+
import type { ObservableInteropInputLike, ObservableOperatorInterop, ObservableOperatorInteropOptions, TransformFunctionOptions, StreamPair, TransformStreamOptions } from "./_types.js";
|
|
2
2
|
import type { CreateOperatorOptions, Operator } from "./_types.js";
|
|
3
3
|
import { ObservableError } from "../error.js";
|
|
4
|
+
import { Observable } from "../observable.js";
|
|
4
5
|
/**
|
|
5
6
|
* Type guard to check if options is a TransformStreamOptions
|
|
6
7
|
*
|
|
@@ -73,6 +74,77 @@ export declare function applyOperator(input: ReadableStream<unknown>, operator:
|
|
|
73
74
|
* ```
|
|
74
75
|
*/
|
|
75
76
|
export declare function toStream<T>(iterable: Iterable<T> | AsyncIterable<T>): ReadableStream<T | ObservableError>;
|
|
77
|
+
/**
|
|
78
|
+
* Adapts a readable/writable stream pair into an Observable operator.
|
|
79
|
+
*
|
|
80
|
+
* Some platform transforms, such as `CompressionStream`, expose a writable side
|
|
81
|
+
* and a readable side without being created through this library's operator
|
|
82
|
+
* builders. This helper turns that pair into a normal `Operator` so it can be
|
|
83
|
+
* used inside `pipe()` like any built-in operator.
|
|
84
|
+
*
|
|
85
|
+
* A fresh pair is created for each operator application. That preserves the
|
|
86
|
+
* cold semantics of the surrounding Observable pipeline and avoids reusing a
|
|
87
|
+
* consumed stream pair across subscriptions.
|
|
88
|
+
*
|
|
89
|
+
* @typeParam TIn - Chunk type written into the pair
|
|
90
|
+
* @typeParam TOut - Chunk type read from the pair
|
|
91
|
+
* @param createPair - Factory that returns a fresh readable/writable pair
|
|
92
|
+
* @returns An operator that pipes input through the created pair
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* const gzip = fromStreamPair<Uint8Array, Uint8Array>(
|
|
97
|
+
* () => new CompressionStream('gzip')
|
|
98
|
+
* );
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function fromStreamPair<TIn, TOut>(createPair: () => StreamPair<TIn, TOut>): Operator<TIn, TOut>;
|
|
102
|
+
/**
|
|
103
|
+
* Wraps a `ReadableStream` as an Observable so foreign Observable-style
|
|
104
|
+
* operators can subscribe to it directly.
|
|
105
|
+
*
|
|
106
|
+
* This avoids first converting the stream into an async-iterable Observable via
|
|
107
|
+
* `Observable.from()`, which would add an extra adaptation layer before the
|
|
108
|
+
* foreign operator even starts running.
|
|
109
|
+
*/
|
|
110
|
+
export declare function streamAsObservable<T>(stream: ReadableStream<T>): Observable<T>;
|
|
111
|
+
/**
|
|
112
|
+
* Subscribes to an Observable-like output and exposes it as a ReadableStream.
|
|
113
|
+
*
|
|
114
|
+
* Foreign operators are allowed to return either a normal `Observable.from()`
|
|
115
|
+
* input or a direct subscribable from another Observable implementation.
|
|
116
|
+
* Converting the result with a direct subscription avoids the extra
|
|
117
|
+
* async-generator and stream layers that `pull(...)+toStream()` would add.
|
|
118
|
+
*/
|
|
119
|
+
export declare function observableInputToStream<T>(input: ObservableInteropInputLike<T>, errorContext: string): ReadableStream<T | ObservableError>;
|
|
120
|
+
/**
|
|
121
|
+
* Adapts a foreign Observable-style operator into a stream operator.
|
|
122
|
+
*
|
|
123
|
+
* Libraries such as RxJS model operators as functions from one Observable-like
|
|
124
|
+
* source to another Observable-like result. This helper bridges that shape into
|
|
125
|
+
* this library's `Operator` contract by:
|
|
126
|
+
*
|
|
127
|
+
* 1. wrapping the input stream as an Observable-like source
|
|
128
|
+
* 2. calling the foreign operator
|
|
129
|
+
* 3. converting the resulting Observable-like output back into a stream
|
|
130
|
+
*
|
|
131
|
+
* The result keeps this library's buffered error behavior by converting the
|
|
132
|
+
* foreign output into a `ReadableStream` that enqueues wrapped
|
|
133
|
+
* `ObservableError` values instead of failing the readable side outright.
|
|
134
|
+
*
|
|
135
|
+
* @typeParam TIn - Value type accepted by the foreign operator
|
|
136
|
+
* @typeParam TOut - Value type produced by the foreign operator
|
|
137
|
+
* @param operator - Foreign operator function to adapt
|
|
138
|
+
* @returns An operator compatible with this library's `pipe()`
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* const foreignTakeOne = fromObservableOperator<number, number>((source) =>
|
|
143
|
+
* rxTake(1)(source)
|
|
144
|
+
* , { sourceAdapter: (source) => rxFrom(source) });
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
export declare function fromObservableOperator<TIn, TOut, TSource = Observable<TIn>>(operator: ObservableOperatorInterop<TIn, TOut, TSource>, options?: ObservableOperatorInteropOptions<TIn, TSource>): Operator<TIn, TOut | ObservableError>;
|
|
76
148
|
/**
|
|
77
149
|
* Creates a TransformStream that injects an error at the start and passes through all original data.
|
|
78
150
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/helpers/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,CAAC,EAC3C,OAAO,EAAE,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC,GACnC,OAAO,IAAI,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,CAEzC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,CAAC,EAC7C,OAAO,EAAE,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC,GACnC,OAAO,IAAI,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC,CAE3C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EAE9B,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,EAC5B,EAAE,OAAyB,EAAE;;CAAK,GACjC,cAAc,CAAC,OAAO,CAAC,CAOzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GACvC,cAAc,CAAC,CAAC,GAAG,eAAe,CAAC,CAkCrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,KAAK,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE,GAAG,OAAO,GAAG,OAAO,EAAE,EAC7C,OAAO,CAAC,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,OAAO,GACd,eAAe,CAAC,CAAC,EAAE,eAAe,CAAC,CAOrC"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/helpers/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,wBAAwB,EACxB,UAAU,EACV,sBAAsB,EACvB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,CAAC,EAC3C,OAAO,EAAE,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC,GACnC,OAAO,IAAI,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,CAEzC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,CAAC,EAC7C,OAAO,EAAE,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC,GACnC,OAAO,IAAI,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC,CAE3C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EAE9B,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,EAC5B,EAAE,OAAyB,EAAE;;CAAK,GACjC,cAAc,CAAC,OAAO,CAAC,CAOzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GACvC,cAAc,CAAC,CAAC,GAAG,eAAe,CAAC,CAkCrC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,IAAI,EACtC,UAAU,EAAE,MAAM,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GACtC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAErB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAsC9E;AAsBD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,KAAK,EAAE,0BAA0B,CAAC,CAAC,CAAC,EACpC,YAAY,EAAE,MAAM,GACnB,cAAc,CAAC,CAAC,GAAG,eAAe,CAAC,CAqCrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,EACzE,QAAQ,EAAE,yBAAyB,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,EACvD,OAAO,CAAC,EAAE,gCAAgC,CAAC,GAAG,EAAE,OAAO,CAAC,GACvD,QAAQ,CAAC,GAAG,EAAE,IAAI,GAAG,eAAe,CAAC,CAcvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,KAAK,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE,GAAG,OAAO,GAAG,OAAO,EAAE,EAC7C,OAAO,CAAC,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,OAAO,GACd,eAAe,CAAC,CAAC,EAAE,eAAe,CAAC,CAOrC"}
|