@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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"}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* @module
|
|
14
14
|
*/
|
|
15
15
|
import "../../_dnt.polyfills.js";
|
|
16
|
+
import { isObservableError, ObservableError } from "../../error.js";
|
|
16
17
|
import { createStatefulOperator } from "../operators.js";
|
|
17
18
|
/**
|
|
18
19
|
* Checks if every item in the stream passes a test.
|
|
@@ -181,6 +182,74 @@ export function find(predicate) {
|
|
|
181
182
|
},
|
|
182
183
|
});
|
|
183
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Finds the index of the first item in the stream that passes a test.
|
|
187
|
+
*
|
|
188
|
+
* This mirrors `Array.prototype.findIndex()`: it emits the zero-based index of
|
|
189
|
+
* the first matching value, or `-1` if the stream completes without a match.
|
|
190
|
+
*
|
|
191
|
+
* @typeParam T - Type of values from the source stream
|
|
192
|
+
* @param predicate - Function to test each value
|
|
193
|
+
* @returns An operator that emits the first matching index or `-1`
|
|
194
|
+
*/
|
|
195
|
+
export function findIndex(predicate) {
|
|
196
|
+
return createStatefulOperator({
|
|
197
|
+
name: "findIndex",
|
|
198
|
+
createState: () => ({ index: 0, finished: false }),
|
|
199
|
+
transform(chunk, state, controller) {
|
|
200
|
+
if (state.finished) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const currentIndex = state.index;
|
|
204
|
+
const result = predicate(chunk, currentIndex);
|
|
205
|
+
state.index++;
|
|
206
|
+
if (result) {
|
|
207
|
+
state.finished = true;
|
|
208
|
+
controller.enqueue(currentIndex);
|
|
209
|
+
controller.terminate();
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
flush(state, controller) {
|
|
213
|
+
if (!state.finished) {
|
|
214
|
+
controller.enqueue(-1);
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Emits the value at a specific zero-based source index.
|
|
221
|
+
*
|
|
222
|
+
* Unlike arrays, streams cannot jump to a position directly, so this operator
|
|
223
|
+
* reads and counts values until it reaches the requested slot. If the stream
|
|
224
|
+
* completes first, it emits nothing.
|
|
225
|
+
*
|
|
226
|
+
* @typeParam T - Type of values from the source stream
|
|
227
|
+
* @param targetIndex - Zero-based index to emit
|
|
228
|
+
* @returns An operator that emits at most one value from the requested index
|
|
229
|
+
*/
|
|
230
|
+
export function elementAt(targetIndex) {
|
|
231
|
+
if (!Number.isInteger(targetIndex) || targetIndex < 0) {
|
|
232
|
+
throw new RangeError("elementAt: targetIndex must be a non-negative integer");
|
|
233
|
+
}
|
|
234
|
+
return createStatefulOperator({
|
|
235
|
+
name: "elementAt",
|
|
236
|
+
createState: () => ({ index: 0 }),
|
|
237
|
+
transform(chunk, state, controller) {
|
|
238
|
+
if (state.index === targetIndex) {
|
|
239
|
+
controller.enqueue(chunk);
|
|
240
|
+
controller.terminate();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
state.index++;
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
export function first(predicate) {
|
|
248
|
+
if (!predicate) {
|
|
249
|
+
return elementAt(0);
|
|
250
|
+
}
|
|
251
|
+
return find(predicate);
|
|
252
|
+
}
|
|
184
253
|
/**
|
|
185
254
|
* Removes duplicate values from the stream, keeping only the first occurrence.
|
|
186
255
|
*
|
|
@@ -235,12 +304,27 @@ export function unique(keySelector) {
|
|
|
235
304
|
},
|
|
236
305
|
});
|
|
237
306
|
}
|
|
307
|
+
function defaultBy(value) {
|
|
308
|
+
return value;
|
|
309
|
+
}
|
|
310
|
+
function defaultEquals(previous, current) {
|
|
311
|
+
return Object.is(previous, current);
|
|
312
|
+
}
|
|
238
313
|
/**
|
|
239
314
|
* Emits an item only if it is different from the previous one.
|
|
240
315
|
*
|
|
241
316
|
* This is useful for streams where values can be emitted repeatedly, but you
|
|
242
317
|
* only care about the changes.
|
|
243
318
|
*
|
|
319
|
+
* Example 1:
|
|
320
|
+
* source: 1, 1, 2, 2, 3
|
|
321
|
+
* output: 1, 2, 3
|
|
322
|
+
*
|
|
323
|
+
* Example 2:
|
|
324
|
+
* source: { id: 1, name: "A" }, { id: 1, name: "B" }, { id: 2, name: "C" }
|
|
325
|
+
* changed({ by: value => value.id })
|
|
326
|
+
* output: first item, then the item with id 2
|
|
327
|
+
*
|
|
244
328
|
* @example
|
|
245
329
|
* ```ts
|
|
246
330
|
* import { pipe, changed, from } from "./helpers/mod.ts";
|
|
@@ -278,3 +362,58 @@ export function unique(keySelector) {
|
|
|
278
362
|
* @param compare An optional function to compare two keys for equality.
|
|
279
363
|
* @returns An operator that filters out consecutive duplicate values.
|
|
280
364
|
*/
|
|
365
|
+
export function changed(options = {}) {
|
|
366
|
+
const by = (options.by ??
|
|
367
|
+
defaultBy);
|
|
368
|
+
const equals = (options.equals ??
|
|
369
|
+
defaultEquals);
|
|
370
|
+
const emitFirst = options.emitFirst ?? true;
|
|
371
|
+
return createStatefulOperator({
|
|
372
|
+
name: "changed",
|
|
373
|
+
createState: () => ({
|
|
374
|
+
hasPrevious: false,
|
|
375
|
+
previousKey: undefined,
|
|
376
|
+
index: 0,
|
|
377
|
+
}),
|
|
378
|
+
transform(chunk, state, controller) {
|
|
379
|
+
if (isObservableError(chunk)) {
|
|
380
|
+
controller.enqueue(chunk);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const value = chunk;
|
|
384
|
+
let currentKey;
|
|
385
|
+
try {
|
|
386
|
+
currentKey = by(value, state.index++);
|
|
387
|
+
}
|
|
388
|
+
catch (err) {
|
|
389
|
+
controller.enqueue(ObservableError.from(err, "operator:stateful:changed:by", value));
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (!state.hasPrevious) {
|
|
393
|
+
state.hasPrevious = true;
|
|
394
|
+
state.previousKey = currentKey;
|
|
395
|
+
if (emitFirst) {
|
|
396
|
+
controller.enqueue(value);
|
|
397
|
+
}
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
let isSame;
|
|
401
|
+
try {
|
|
402
|
+
isSame = equals(state.previousKey, currentKey);
|
|
403
|
+
}
|
|
404
|
+
catch (err) {
|
|
405
|
+
controller.enqueue(ObservableError.from(err, "operator:stateful:changed:equals", value));
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
state.previousKey = currentKey;
|
|
409
|
+
if (!isSame) {
|
|
410
|
+
controller.enqueue(value);
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
cancel(state) {
|
|
414
|
+
state.hasPrevious = false;
|
|
415
|
+
state.previousKey = undefined;
|
|
416
|
+
state.index = 0;
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
}
|
|
@@ -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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"}
|
package/esm/helpers/operators.js
CHANGED
|
@@ -230,6 +230,7 @@ export function createOperator(options) {
|
|
|
230
230
|
const transform = options?.transform;
|
|
231
231
|
const start = options?.start;
|
|
232
232
|
const flush = options?.flush;
|
|
233
|
+
const cancel = options?.cancel;
|
|
233
234
|
return (source) => {
|
|
234
235
|
try {
|
|
235
236
|
// Create a transform stream with the provided options
|
|
@@ -243,8 +244,10 @@ export function createOperator(options) {
|
|
|
243
244
|
// Flush function called when the input is done
|
|
244
245
|
flush: handleFlush(errorMode, flush, { operatorName }),
|
|
245
246
|
}, { highWaterMark: 1 }, { highWaterMark: 0 });
|
|
246
|
-
// Pipe the source through the transform
|
|
247
|
-
return source.pipeThrough(transformStream)
|
|
247
|
+
// Pipe the source through the transform and bind downstream cancellation
|
|
248
|
+
return wrapStreamWithCancel(source.pipeThrough(transformStream), {
|
|
249
|
+
cancel: handleCancel(errorMode, cancel, { operatorName }),
|
|
250
|
+
});
|
|
248
251
|
}
|
|
249
252
|
catch (err) {
|
|
250
253
|
// If setup fails, return a stream that errors immediately
|
|
@@ -501,6 +504,131 @@ export function handleFlush(errorMode, flush, context = {}) {
|
|
|
501
504
|
}
|
|
502
505
|
};
|
|
503
506
|
}
|
|
507
|
+
/**
|
|
508
|
+
* Lifecycle handling for downstream cancellation cleanup.
|
|
509
|
+
*
|
|
510
|
+
* `flush()` runs when the source ends normally. `cancel()` runs when a consumer
|
|
511
|
+
* stops early, for example by unsubscribing, breaking out of `for await`, or
|
|
512
|
+
* otherwise cancelling the pipeline before the source completes.
|
|
513
|
+
*
|
|
514
|
+
* ```text
|
|
515
|
+
* source completes normally -> flush()
|
|
516
|
+
* consumer stops early -> cancel(reason)
|
|
517
|
+
* ```
|
|
518
|
+
*
|
|
519
|
+
* That distinction matters for operators that hold timers, inner
|
|
520
|
+
* subscriptions, or abort controllers. Those resources should be released when
|
|
521
|
+
* the consumer goes away, even if the source never reached completion.
|
|
522
|
+
*
|
|
523
|
+
* Cleanup here is intentionally not treated like transform output. If the
|
|
524
|
+
* cleanup callback fails, the cancellation promise rejects, but no
|
|
525
|
+
* `ObservableError` is emitted into the stream because the consumer has already
|
|
526
|
+
* said it is no longer interested in more values.
|
|
527
|
+
*
|
|
528
|
+
* @typeParam T - Input chunk type
|
|
529
|
+
* @typeParam O - Output chunk type
|
|
530
|
+
* @typeParam S - State type (for stateful operators)
|
|
531
|
+
* @param _errorMode - Unused here. Cancellation cleanup does not route through
|
|
532
|
+
* the operator error modes because it is teardown, not part of the data path.
|
|
533
|
+
* @param cancel - Your cancellation handler (stateless or stateful, optional)
|
|
534
|
+
* @param context - Info about the operator, including name and state
|
|
535
|
+
* @returns A function suitable for a ReadableStream `cancel` hook, or undefined
|
|
536
|
+
*/
|
|
537
|
+
export function handleCancel(_errorMode, cancel, context = {}) {
|
|
538
|
+
if (!cancel)
|
|
539
|
+
return;
|
|
540
|
+
const operatorName = context.operatorName || `operator:unknown`;
|
|
541
|
+
const isStateful = context.isStateful || false;
|
|
542
|
+
const state = context.state;
|
|
543
|
+
return async function (reason) {
|
|
544
|
+
try {
|
|
545
|
+
if (isStateful) {
|
|
546
|
+
await cancel(state, reason);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
await cancel(reason);
|
|
550
|
+
}
|
|
551
|
+
catch (err) {
|
|
552
|
+
throw ObservableError.from(err, `${operatorName}:cancel`, reason);
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Wraps a transformed stream so operator-level cleanup runs when a downstream
|
|
558
|
+
* consumer stops early.
|
|
559
|
+
*
|
|
560
|
+
* This wrapper has a deliberately narrow job: it forwards readable-side
|
|
561
|
+
* cancellation to an operator cleanup callback while preserving the chunks
|
|
562
|
+
* coming from the wrapped stream. It does not try to redefine TransformStream
|
|
563
|
+
* semantics or reinterpret cancellation as a normal data-path error.
|
|
564
|
+
*
|
|
565
|
+
* ```text
|
|
566
|
+
* source -> TransformStream -> wrapped readable -> consumer
|
|
567
|
+
* |
|
|
568
|
+
* +-> cancel(reason)
|
|
569
|
+
* ```
|
|
570
|
+
*
|
|
571
|
+
* @typeParam T - Output chunk type of the wrapped stream
|
|
572
|
+
* @param stream - The transformed stream to expose downstream
|
|
573
|
+
* @param options - Cancellation behavior for the wrapped stream
|
|
574
|
+
* @returns A readable stream that preserves output values and forwards cancel
|
|
575
|
+
*/
|
|
576
|
+
export function wrapStreamWithCancel(stream, options = {}) {
|
|
577
|
+
if (!options.cancel)
|
|
578
|
+
return stream;
|
|
579
|
+
const reader = stream.getReader();
|
|
580
|
+
let settled = false;
|
|
581
|
+
// The wrapper acquires an exclusive reader, so releasing it exactly once
|
|
582
|
+
// keeps teardown predictable even when both cancel and read completion race.
|
|
583
|
+
const release = () => {
|
|
584
|
+
if (settled)
|
|
585
|
+
return;
|
|
586
|
+
settled = true;
|
|
587
|
+
try {
|
|
588
|
+
reader.releaseLock();
|
|
589
|
+
}
|
|
590
|
+
catch {
|
|
591
|
+
// Releasing the reader is best-effort during teardown.
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
return new ReadableStream({
|
|
595
|
+
async pull(controller) {
|
|
596
|
+
try {
|
|
597
|
+
// Forward chunks one-by-one so downstream backpressure still controls
|
|
598
|
+
// how quickly we read from the wrapped stream.
|
|
599
|
+
const { done, value } = await reader.read();
|
|
600
|
+
if (done) {
|
|
601
|
+
release();
|
|
602
|
+
controller.close();
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
controller.enqueue(value);
|
|
606
|
+
}
|
|
607
|
+
catch (err) {
|
|
608
|
+
release();
|
|
609
|
+
controller.error(err);
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
async cancel(reason) {
|
|
613
|
+
try {
|
|
614
|
+
// Run operator cleanup first so resources such as timers or inner
|
|
615
|
+
// subscriptions are released before we cancel the wrapped reader.
|
|
616
|
+
await options.cancel?.(reason);
|
|
617
|
+
}
|
|
618
|
+
finally {
|
|
619
|
+
try {
|
|
620
|
+
// Cancelling the reader tells the wrapped stream that nobody wants
|
|
621
|
+
// more output. Any rejection here is surfaced through the returned
|
|
622
|
+
// cancellation promise instead of being emitted as a chunk.
|
|
623
|
+
await reader.cancel(reason);
|
|
624
|
+
}
|
|
625
|
+
finally {
|
|
626
|
+
release();
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
},
|
|
630
|
+
});
|
|
631
|
+
}
|
|
504
632
|
// Default case
|
|
505
633
|
export function createStatefulOperator(options) {
|
|
506
634
|
// Extract operator name from options or the function name for better error reporting
|
|
@@ -512,6 +640,7 @@ export function createStatefulOperator(options) {
|
|
|
512
640
|
?.transform;
|
|
513
641
|
const start = options?.start;
|
|
514
642
|
const flush = options?.flush;
|
|
643
|
+
const cancel = options?.cancel;
|
|
515
644
|
return (source) => {
|
|
516
645
|
try {
|
|
517
646
|
// Create state only when the stream is used
|
|
@@ -552,8 +681,14 @@ export function createStatefulOperator(options) {
|
|
|
552
681
|
state,
|
|
553
682
|
}),
|
|
554
683
|
}, { highWaterMark: 1 }, { highWaterMark: 0 });
|
|
555
|
-
// Pipe the source through the transform
|
|
556
|
-
return source.pipeThrough(transformStream)
|
|
684
|
+
// Pipe the source through the transform and bind downstream cancellation
|
|
685
|
+
return wrapStreamWithCancel(source.pipeThrough(transformStream), {
|
|
686
|
+
cancel: handleCancel(errorMode, cancel, {
|
|
687
|
+
operatorName,
|
|
688
|
+
isStateful: true,
|
|
689
|
+
state,
|
|
690
|
+
}),
|
|
691
|
+
});
|
|
557
692
|
}
|
|
558
693
|
catch (err) {
|
|
559
694
|
// If setup fails, return a stream that errors immediately
|
package/esm/helpers/utils.d.ts
CHANGED
|
@@ -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"}
|