gingersnap 0.23.2 → 0.23.4
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/data-structures/object/Publisher.cjs +16 -0
- package/data-structures/object/Publisher.d.ts +8 -0
- package/data-structures/object/Publisher.mjs +14 -0
- package/data-structures/object/Queue.cjs +7 -0
- package/data-structures/object/Queue.d.ts +3 -0
- package/data-structures/object/Queue.mjs +7 -0
- package/package.json +1 -1
- package/stream/index.d.ts +26 -0
- package/stream.cjs +95 -0
- package/stream.mjs +95 -0
- package/README.md +0 -25
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class Publisher {
|
|
4
|
+
constructor(queue, transformer) {
|
|
5
|
+
this.queue = queue;
|
|
6
|
+
this.transformer = transformer;
|
|
7
|
+
}
|
|
8
|
+
send(value) {
|
|
9
|
+
this.queue.enqueue(this.transformer(value));
|
|
10
|
+
}
|
|
11
|
+
publish(value) {
|
|
12
|
+
this.send(value);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
exports.Publisher = Publisher;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class Publisher {
|
|
2
|
+
constructor(queue, transformer) {
|
|
3
|
+
this.queue = queue;
|
|
4
|
+
this.transformer = transformer;
|
|
5
|
+
}
|
|
6
|
+
send(value) {
|
|
7
|
+
this.queue.enqueue(this.transformer(value));
|
|
8
|
+
}
|
|
9
|
+
publish(value) {
|
|
10
|
+
this.send(value);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { Publisher };
|
|
@@ -9,6 +9,7 @@ var synchronize = require('../../synchronize.cjs');
|
|
|
9
9
|
var stream = require('../../stream.cjs');
|
|
10
10
|
var QueueFullError = require('../../errors/QueueFullError.cjs');
|
|
11
11
|
var ValueDestroyedError = require('../../errors/ValueDestroyedError.cjs');
|
|
12
|
+
var Publisher = require('./Publisher.cjs');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Queue data structure for First In First Out operation (FIFO)
|
|
@@ -25,6 +26,9 @@ class Queue extends WatchableObject.WatchableObject {
|
|
|
25
26
|
get streamEntries() {
|
|
26
27
|
return stream.Stream.of(this.asyncIterator);
|
|
27
28
|
}
|
|
29
|
+
createPublisher(transformer) {
|
|
30
|
+
return new Publisher.Publisher(this, transformer);
|
|
31
|
+
}
|
|
28
32
|
close() {
|
|
29
33
|
this.closed = true;
|
|
30
34
|
this.closedSignal.dispatchEvent(new CustomEvent("abort"));
|
|
@@ -49,6 +53,9 @@ class Queue extends WatchableObject.WatchableObject {
|
|
|
49
53
|
this.set(this.tail, value);
|
|
50
54
|
this.tail++;
|
|
51
55
|
}
|
|
56
|
+
add(value) {
|
|
57
|
+
this.enqueue(value);
|
|
58
|
+
}
|
|
52
59
|
awaitEnqueue(value) {
|
|
53
60
|
return future.Future.of((resolve, reject, signal) => _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
54
61
|
while (this.objectMaxSize && this.size() >= this.objectMaxSize) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { WatchableObject } from "./WatchableObject";
|
|
2
2
|
import { Future, WaitPeriod } from "../../future";
|
|
3
3
|
import { Stream } from "../../stream";
|
|
4
|
+
import { Publisher } from "./Publisher";
|
|
4
5
|
/**
|
|
5
6
|
* Queue data structure for First In First Out operation (FIFO)
|
|
6
7
|
*/
|
|
@@ -12,10 +13,12 @@ export declare class Queue<T> extends WatchableObject<number, T> implements Iter
|
|
|
12
13
|
private readonly dequeueEvt;
|
|
13
14
|
constructor(objectMaxSize?: number, expiryPeriod?: WaitPeriod);
|
|
14
15
|
get streamEntries(): Stream<T>;
|
|
16
|
+
createPublisher<K>(transformer: (v: K) => T): Publisher<T, K>;
|
|
15
17
|
close(): void;
|
|
16
18
|
clone(): Queue<T>;
|
|
17
19
|
ingest(stream: Stream<T>): Future<void>;
|
|
18
20
|
enqueue(value: T): void;
|
|
21
|
+
add(value: T): void;
|
|
19
22
|
awaitEnqueue(value: T): Future<void>;
|
|
20
23
|
dequeue(): T;
|
|
21
24
|
awaitDequeue(abortSignal?: AbortSignal): Future<T>;
|
|
@@ -7,6 +7,7 @@ import { FutureEvent } from '../../synchronize.mjs';
|
|
|
7
7
|
import { Stream } from '../../stream.mjs';
|
|
8
8
|
import { QueueFullError } from '../../errors/QueueFullError.mjs';
|
|
9
9
|
import { ValueDestroyedError } from '../../errors/ValueDestroyedError.mjs';
|
|
10
|
+
import { Publisher } from './Publisher.mjs';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Queue data structure for First In First Out operation (FIFO)
|
|
@@ -23,6 +24,9 @@ class Queue extends WatchableObject {
|
|
|
23
24
|
get streamEntries() {
|
|
24
25
|
return Stream.of(this.asyncIterator);
|
|
25
26
|
}
|
|
27
|
+
createPublisher(transformer) {
|
|
28
|
+
return new Publisher(this, transformer);
|
|
29
|
+
}
|
|
26
30
|
close() {
|
|
27
31
|
this.closed = true;
|
|
28
32
|
this.closedSignal.dispatchEvent(new CustomEvent("abort"));
|
|
@@ -47,6 +51,9 @@ class Queue extends WatchableObject {
|
|
|
47
51
|
this.set(this.tail, value);
|
|
48
52
|
this.tail++;
|
|
49
53
|
}
|
|
54
|
+
add(value) {
|
|
55
|
+
this.enqueue(value);
|
|
56
|
+
}
|
|
50
57
|
awaitEnqueue(value) {
|
|
51
58
|
return Future.of((resolve, reject, signal) => __awaiter(this, void 0, void 0, function* () {
|
|
52
59
|
while (this.objectMaxSize && this.size() >= this.objectMaxSize) {
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"gingersnap","version":"0.23.
|
|
1
|
+
{"name":"gingersnap","version":"0.23.4","description":"A general purpose library built with the aim of filling the gaps of javascript shortcomings","dependencies":{"@msgpack/msgpack":"^3.0.0-beta2","browser-or-node":"^3.0.0-pre.0","modern-isomorphic-ws":"^1.0.5","object-hash":"^3.0.0","papaparse":"^5.4.1","protobufjs":"^7.2.6","ramda":"^0.30.1","reflect-metadata":"^0.2.2","tslib":"^2.6.3","uuid":"^9.0.1","ws":"^8.16.0","x2js":"^3.4.4"},"author":"CookieNerds LLC","exports":{"./synchronize":{"import":{"types":"./synchronize.d.ts","default":"./synchronize.mjs"},"require":{"types":"./synchronize.d.ts","default":"./synchronize.cjs"}},"./mocks":{"import":{"types":"./mocks.d.ts","default":"./mocks.mjs"},"require":{"types":"./mocks.d.ts","default":"./mocks.cjs"}},"./socket":{"import":{"types":"./socket.d.ts","default":"./socket.mjs"},"require":{"types":"./socket.d.ts","default":"./socket.cjs"}},"./typing":{"import":{"types":"./typing.d.ts","default":"./typing.mjs"},"require":{"types":"./typing.d.ts","default":"./typing.cjs"}},"./stream":{"import":{"types":"./stream/index.d.ts","default":"./stream.mjs"},"require":{"types":"./stream/index.d.ts","default":"./stream.cjs"}},"./reflection/injector":{"import":{"types":"./reflection/injector.d.ts","default":"./reflection/injector.mjs"},"require":{"types":"./reflection/injector.d.ts","default":"./reflection/injector.cjs"}},"./stream/call":{"import":{"types":"./stream/call.d.ts","default":"./stream/call.mjs"},"require":{"types":"./stream/call.d.ts","default":"./stream/call.cjs"}},"./stream/state":{"import":{"types":"./stream/state.d.ts","default":"./stream/state.mjs"},"require":{"types":"./stream/state.d.ts","default":"./stream/state.cjs"}},"./stream/collector":{"import":{"types":"./stream/collector.d.ts","default":"./stream/collector.mjs"},"require":{"types":"./stream/collector.d.ts","default":"./stream/collector.cjs"}},"./stream/observable":{"import":{"types":"./stream/observable.d.ts","default":"./stream/observable.mjs"},"require":{"types":"./stream/observable.d.ts","default":"./stream/observable.cjs"}},"./networking":{"import":{"types":"./networking/index.d.ts","default":"./networking.mjs"},"require":{"types":"./networking/index.d.ts","default":"./networking.cjs"}},"./managers":{"import":{"types":"./managers/index.d.ts","default":"./managers.mjs"},"require":{"types":"./managers/index.d.ts","default":"./managers.cjs"}},"./future":{"import":{"types":"./future/index.d.ts","default":"./future.mjs"},"require":{"types":"./future/index.d.ts","default":"./future.cjs"}},"./errors":{"import":{"types":"./errors/index.d.ts","default":"./errors.mjs"},"require":{"types":"./errors/index.d.ts","default":"./errors.cjs"}},"./data-structures/array":{"import":{"types":"./data-structures/array/index.d.ts","default":"./data-structures/array.mjs"},"require":{"types":"./data-structures/array/index.d.ts","default":"./data-structures/array.cjs"}},"./data-structures/object":{"import":{"types":"./data-structures/object/index.d.ts","default":"./data-structures/object.mjs"},"require":{"types":"./data-structures/object/index.d.ts","default":"./data-structures/object.cjs"}},"./data/decoders":{"import":{"types":"./data/decoders/index.d.ts","default":"./data/decoders.mjs"},"require":{"types":"./data/decoders/index.d.ts","default":"./data/decoders.cjs"}},"./data/model":{"import":{"types":"./data/model/index.d.ts","default":"./data/model.mjs"},"require":{"types":"./data/model/index.d.ts","default":"./data/model.cjs"}},"./data/bus":{"import":{"types":"./data/bus.d.ts","default":"./data/bus.mjs"},"require":{"types":"./data/bus.d.ts","default":"./data/bus.cjs"}},"./data/value":{"import":{"types":"./data/value.d.ts","default":"./data/value.mjs"},"require":{"types":"./data/value.d.ts","default":"./data/value.cjs"}},"./store":{"import":{"types":"./store/index.d.ts","default":"./store.mjs"},"require":{"types":"./store/index.d.ts","default":"./store.cjs"}},"./functools":{"import":{"types":"./functools/index.d.ts","default":"./functools.mjs"},"require":{"types":"./functools/index.d.ts","default":"./functools.cjs"}}}}
|
package/stream/index.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export declare class Stream<T> implements AsyncGenerator<T> {
|
|
|
33
33
|
protected cancelHooks: Set<() => any>;
|
|
34
34
|
protected completionHooks: Set<() => any>;
|
|
35
35
|
protected completionHookInvoked: boolean;
|
|
36
|
+
protected monitorHooks: Set<(v: T) => void>;
|
|
37
|
+
private frozen;
|
|
36
38
|
protected done: boolean;
|
|
37
39
|
protected backlog: Array<{
|
|
38
40
|
records: T[] | Stream<T>;
|
|
@@ -92,6 +94,29 @@ export declare class Stream<T> implements AsyncGenerator<T> {
|
|
|
92
94
|
*/
|
|
93
95
|
get future(): Future<T>;
|
|
94
96
|
get readableStream(): ReadableStream<T>;
|
|
97
|
+
/**
|
|
98
|
+
* Breaks up a stream into 2 other streams based on the callback result. If callback result is true, data will go to
|
|
99
|
+
* the first stream, otherwise it will go to the second stream. Note that splitting the stream will immediately invoke
|
|
100
|
+
* the parent stream if it's not already running
|
|
101
|
+
*
|
|
102
|
+
* Important Note! This will freeze the parent stream (this means you cannot add additional transformers to the parent
|
|
103
|
+
* stream)
|
|
104
|
+
* @param callback
|
|
105
|
+
*/
|
|
106
|
+
split(callback: (v: T) => boolean | Promise<boolean> | Future<boolean>): [Stream<T>, Stream<T>];
|
|
107
|
+
/**
|
|
108
|
+
* Creates a new branch from the main stream, that monitors and collects values that matches the filter specified.
|
|
109
|
+
* Note, this does not trigger the main stream to run nor does it alter the main stream. It only collects matched
|
|
110
|
+
* records from main stream once main stream is running.
|
|
111
|
+
*
|
|
112
|
+
* Important Note! This will freeze the parent stream (this means you cannot add additional transformers to the parent
|
|
113
|
+
* stream)
|
|
114
|
+
* @param filter
|
|
115
|
+
* @param objectMaxSize max records to keep in queue, provided you start reading from this stream slower than data is
|
|
116
|
+
* being collected from main stream
|
|
117
|
+
* @param expiryPeriod
|
|
118
|
+
*/
|
|
119
|
+
subStream(filter: (v: T) => boolean | Promise<boolean> | Future<boolean>, objectMaxSize?: number, expiryPeriod?: WaitPeriod): Stream<T>;
|
|
95
120
|
/**
|
|
96
121
|
* Cancel the stream on the given signal
|
|
97
122
|
* @param signal
|
|
@@ -263,5 +288,6 @@ export declare class Stream<T> implements AsyncGenerator<T> {
|
|
|
263
288
|
private yieldTrueResult;
|
|
264
289
|
private forwardExecute;
|
|
265
290
|
private collectResult;
|
|
291
|
+
private get canAlterStream();
|
|
266
292
|
}
|
|
267
293
|
export {};
|
package/stream.cjs
CHANGED
|
@@ -68,6 +68,8 @@ class Stream {
|
|
|
68
68
|
this.completionHookInvoked = false;
|
|
69
69
|
this.runLock = new synchronize.Lock();
|
|
70
70
|
this.concurrencyLimit = 0;
|
|
71
|
+
this.monitorHooks = new Set();
|
|
72
|
+
this.frozen = false;
|
|
71
73
|
}
|
|
72
74
|
/**
|
|
73
75
|
* Used to provide setup logic that should only be invoked once, when stream
|
|
@@ -245,6 +247,66 @@ class Stream {
|
|
|
245
247
|
},
|
|
246
248
|
});
|
|
247
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* Breaks up a stream into 2 other streams based on the callback result. If callback result is true, data will go to
|
|
252
|
+
* the first stream, otherwise it will go to the second stream. Note that splitting the stream will immediately invoke
|
|
253
|
+
* the parent stream if it's not already running
|
|
254
|
+
*
|
|
255
|
+
* Important Note! This will freeze the parent stream (this means you cannot add additional transformers to the parent
|
|
256
|
+
* stream)
|
|
257
|
+
* @param callback
|
|
258
|
+
*/
|
|
259
|
+
split(callback) {
|
|
260
|
+
this.frozen = true;
|
|
261
|
+
const queue1 = new Queue.Queue();
|
|
262
|
+
const queue2 = new Queue.Queue();
|
|
263
|
+
let canWrite1 = true;
|
|
264
|
+
let canWrite2 = true;
|
|
265
|
+
const future = this.forEach((v) => {
|
|
266
|
+
if (callback(v)) {
|
|
267
|
+
if (canWrite1) {
|
|
268
|
+
queue1.enqueue(v);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
if (canWrite2) {
|
|
273
|
+
queue2.enqueue(v);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (!canWrite1 && !canWrite2) {
|
|
277
|
+
future.cancel();
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
return [
|
|
281
|
+
queue1.streamEntries.onCancellation(() => {
|
|
282
|
+
canWrite1 = false;
|
|
283
|
+
}),
|
|
284
|
+
queue2.streamEntries.onCancellation(() => {
|
|
285
|
+
canWrite2 = false;
|
|
286
|
+
}),
|
|
287
|
+
];
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Creates a new branch from the main stream, that monitors and collects values that matches the filter specified.
|
|
291
|
+
* Note, this does not trigger the main stream to run nor does it alter the main stream. It only collects matched
|
|
292
|
+
* records from main stream once main stream is running.
|
|
293
|
+
*
|
|
294
|
+
* Important Note! This will freeze the parent stream (this means you cannot add additional transformers to the parent
|
|
295
|
+
* stream)
|
|
296
|
+
* @param filter
|
|
297
|
+
* @param objectMaxSize max records to keep in queue, provided you start reading from this stream slower than data is
|
|
298
|
+
* being collected from main stream
|
|
299
|
+
* @param expiryPeriod
|
|
300
|
+
*/
|
|
301
|
+
subStream(filter, objectMaxSize, expiryPeriod) {
|
|
302
|
+
this.frozen = true;
|
|
303
|
+
const queue = new Queue.Queue(objectMaxSize, expiryPeriod);
|
|
304
|
+
this.monitorHooks.add((v) => {
|
|
305
|
+
if (filter(v))
|
|
306
|
+
queue.enqueue(v);
|
|
307
|
+
});
|
|
308
|
+
return queue.streamEntries;
|
|
309
|
+
}
|
|
248
310
|
/**
|
|
249
311
|
* Cancel the stream on the given signal
|
|
250
312
|
* @param signal
|
|
@@ -304,6 +366,8 @@ class Stream {
|
|
|
304
366
|
* @param callback
|
|
305
367
|
*/
|
|
306
368
|
map(callback) {
|
|
369
|
+
if (!this.canAlterStream)
|
|
370
|
+
throw new IllegalOperationError.IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
307
371
|
this.actions.push({ type: ActionType.TRANSFORM, functor: callback });
|
|
308
372
|
return this;
|
|
309
373
|
}
|
|
@@ -312,6 +376,8 @@ class Stream {
|
|
|
312
376
|
* @param callback
|
|
313
377
|
*/
|
|
314
378
|
filter(callback) {
|
|
379
|
+
if (!this.canAlterStream)
|
|
380
|
+
throw new IllegalOperationError.IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
315
381
|
this.actions.push({
|
|
316
382
|
type: ActionType.FILTER,
|
|
317
383
|
functor: (v) => _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
@@ -328,6 +394,8 @@ class Stream {
|
|
|
328
394
|
return this;
|
|
329
395
|
}
|
|
330
396
|
reduce(initialData, functor, exhaustive = true) {
|
|
397
|
+
if (!this.canAlterStream)
|
|
398
|
+
throw new IllegalOperationError.IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
331
399
|
let previousData = initialData;
|
|
332
400
|
if (exhaustive) {
|
|
333
401
|
this.actions.push({
|
|
@@ -353,6 +421,8 @@ class Stream {
|
|
|
353
421
|
return this;
|
|
354
422
|
}
|
|
355
423
|
reduceWhile(predicate, initialData, functor, exhaustive = true) {
|
|
424
|
+
if (!this.canAlterStream)
|
|
425
|
+
throw new IllegalOperationError.IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
356
426
|
let previousData = initialData;
|
|
357
427
|
if (exhaustive) {
|
|
358
428
|
this.actions.push({
|
|
@@ -429,6 +499,8 @@ class Stream {
|
|
|
429
499
|
* for the split criteria should be added to the chunk, if false then the data will be added to the next chunk
|
|
430
500
|
*/
|
|
431
501
|
chunk(value, keepSplitCriteria = false) {
|
|
502
|
+
if (!this.canAlterStream)
|
|
503
|
+
throw new IllegalOperationError.IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
432
504
|
if (typeof value === "number" && value < 1)
|
|
433
505
|
throw new Error("Invalid chunk size");
|
|
434
506
|
let chunkedResults = [];
|
|
@@ -535,6 +607,8 @@ class Stream {
|
|
|
535
607
|
* @param count
|
|
536
608
|
*/
|
|
537
609
|
take(count) {
|
|
610
|
+
if (!this.canAlterStream)
|
|
611
|
+
throw new IllegalOperationError.IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
538
612
|
let index = 0;
|
|
539
613
|
this.actions.push({
|
|
540
614
|
type: ActionType.LIMIT,
|
|
@@ -552,6 +626,8 @@ class Stream {
|
|
|
552
626
|
* @param predicate
|
|
553
627
|
*/
|
|
554
628
|
takeWhile(predicate) {
|
|
629
|
+
if (!this.canAlterStream)
|
|
630
|
+
throw new IllegalOperationError.IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
555
631
|
this.actions.push({
|
|
556
632
|
type: ActionType.LIMIT,
|
|
557
633
|
functor: (value) => {
|
|
@@ -568,6 +644,8 @@ class Stream {
|
|
|
568
644
|
* @param predicate
|
|
569
645
|
*/
|
|
570
646
|
skipWhile(predicate) {
|
|
647
|
+
if (!this.canAlterStream)
|
|
648
|
+
throw new IllegalOperationError.IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
571
649
|
let startFound = false;
|
|
572
650
|
this.actions.push({
|
|
573
651
|
type: ActionType.FILTER,
|
|
@@ -587,6 +665,8 @@ class Stream {
|
|
|
587
665
|
* @param expiryPeriod
|
|
588
666
|
*/
|
|
589
667
|
dropRepeats(uniqKeyExtractor, expiryPeriod = undefined) {
|
|
668
|
+
if (!this.canAlterStream)
|
|
669
|
+
throw new IllegalOperationError.IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
590
670
|
const cache = new TimeableObject.TimeableObject(undefined, expiryPeriod);
|
|
591
671
|
this.actions.push({
|
|
592
672
|
type: ActionType.FILTER,
|
|
@@ -612,6 +692,8 @@ class Stream {
|
|
|
612
692
|
* @param count
|
|
613
693
|
*/
|
|
614
694
|
skip(count) {
|
|
695
|
+
if (!this.canAlterStream)
|
|
696
|
+
throw new IllegalOperationError.IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
615
697
|
let index = 1;
|
|
616
698
|
this.actions.push({
|
|
617
699
|
type: ActionType.FILTER,
|
|
@@ -629,6 +711,8 @@ class Stream {
|
|
|
629
711
|
* @param period
|
|
630
712
|
*/
|
|
631
713
|
throttleBy(period) {
|
|
714
|
+
if (!this.canAlterStream)
|
|
715
|
+
throw new IllegalOperationError.IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
632
716
|
let future$1;
|
|
633
717
|
this.actions.push({
|
|
634
718
|
type: ActionType.TRANSFORM,
|
|
@@ -648,6 +732,8 @@ class Stream {
|
|
|
648
732
|
* Flattens any nested structure from the data arriving on the stream
|
|
649
733
|
*/
|
|
650
734
|
flatten() {
|
|
735
|
+
if (!this.canAlterStream)
|
|
736
|
+
throw new IllegalOperationError.IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
651
737
|
this.actions.push({
|
|
652
738
|
type: ActionType.UNPACK,
|
|
653
739
|
functor: (value) => {
|
|
@@ -804,6 +890,9 @@ class Stream {
|
|
|
804
890
|
const { state, value } = yield (this.sourceStream && this.concurrencyLimit
|
|
805
891
|
? this.forwardExecute().registerSignal(signal)
|
|
806
892
|
: this.__execute__().registerSignal(signal));
|
|
893
|
+
if (value !== null && value !== undefined) {
|
|
894
|
+
this.monitorHooks.forEach((hook) => hook(value));
|
|
895
|
+
}
|
|
807
896
|
if (state !== exports.State.CONTINUE) {
|
|
808
897
|
this.done = true;
|
|
809
898
|
this.invokeCompletionHooks();
|
|
@@ -1023,6 +1112,9 @@ class Stream {
|
|
|
1023
1112
|
const { state, value } = yield (this.sourceStream && this.concurrencyLimit
|
|
1024
1113
|
? this.forwardExecute()
|
|
1025
1114
|
: this.__execute__());
|
|
1115
|
+
if (value !== null && value !== undefined) {
|
|
1116
|
+
this.monitorHooks.forEach((hook) => hook(value));
|
|
1117
|
+
}
|
|
1026
1118
|
if (state === exports.State.MATCHED && value !== undefined)
|
|
1027
1119
|
return { done: false, value };
|
|
1028
1120
|
if (state === exports.State.DONE) {
|
|
@@ -1180,6 +1272,9 @@ class Stream {
|
|
|
1180
1272
|
return resolve({ state: exports.State.CONTINUE });
|
|
1181
1273
|
}), signal);
|
|
1182
1274
|
}
|
|
1275
|
+
get canAlterStream() {
|
|
1276
|
+
return !this.frozen;
|
|
1277
|
+
}
|
|
1183
1278
|
}
|
|
1184
1279
|
|
|
1185
1280
|
exports.Stream = Stream;
|
package/stream.mjs
CHANGED
|
@@ -47,6 +47,8 @@ class Stream {
|
|
|
47
47
|
this.completionHookInvoked = false;
|
|
48
48
|
this.runLock = new Lock();
|
|
49
49
|
this.concurrencyLimit = 0;
|
|
50
|
+
this.monitorHooks = new Set();
|
|
51
|
+
this.frozen = false;
|
|
50
52
|
}
|
|
51
53
|
/**
|
|
52
54
|
* Used to provide setup logic that should only be invoked once, when stream
|
|
@@ -224,6 +226,66 @@ class Stream {
|
|
|
224
226
|
},
|
|
225
227
|
});
|
|
226
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Breaks up a stream into 2 other streams based on the callback result. If callback result is true, data will go to
|
|
231
|
+
* the first stream, otherwise it will go to the second stream. Note that splitting the stream will immediately invoke
|
|
232
|
+
* the parent stream if it's not already running
|
|
233
|
+
*
|
|
234
|
+
* Important Note! This will freeze the parent stream (this means you cannot add additional transformers to the parent
|
|
235
|
+
* stream)
|
|
236
|
+
* @param callback
|
|
237
|
+
*/
|
|
238
|
+
split(callback) {
|
|
239
|
+
this.frozen = true;
|
|
240
|
+
const queue1 = new Queue();
|
|
241
|
+
const queue2 = new Queue();
|
|
242
|
+
let canWrite1 = true;
|
|
243
|
+
let canWrite2 = true;
|
|
244
|
+
const future = this.forEach((v) => {
|
|
245
|
+
if (callback(v)) {
|
|
246
|
+
if (canWrite1) {
|
|
247
|
+
queue1.enqueue(v);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
if (canWrite2) {
|
|
252
|
+
queue2.enqueue(v);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (!canWrite1 && !canWrite2) {
|
|
256
|
+
future.cancel();
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
return [
|
|
260
|
+
queue1.streamEntries.onCancellation(() => {
|
|
261
|
+
canWrite1 = false;
|
|
262
|
+
}),
|
|
263
|
+
queue2.streamEntries.onCancellation(() => {
|
|
264
|
+
canWrite2 = false;
|
|
265
|
+
}),
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Creates a new branch from the main stream, that monitors and collects values that matches the filter specified.
|
|
270
|
+
* Note, this does not trigger the main stream to run nor does it alter the main stream. It only collects matched
|
|
271
|
+
* records from main stream once main stream is running.
|
|
272
|
+
*
|
|
273
|
+
* Important Note! This will freeze the parent stream (this means you cannot add additional transformers to the parent
|
|
274
|
+
* stream)
|
|
275
|
+
* @param filter
|
|
276
|
+
* @param objectMaxSize max records to keep in queue, provided you start reading from this stream slower than data is
|
|
277
|
+
* being collected from main stream
|
|
278
|
+
* @param expiryPeriod
|
|
279
|
+
*/
|
|
280
|
+
subStream(filter, objectMaxSize, expiryPeriod) {
|
|
281
|
+
this.frozen = true;
|
|
282
|
+
const queue = new Queue(objectMaxSize, expiryPeriod);
|
|
283
|
+
this.monitorHooks.add((v) => {
|
|
284
|
+
if (filter(v))
|
|
285
|
+
queue.enqueue(v);
|
|
286
|
+
});
|
|
287
|
+
return queue.streamEntries;
|
|
288
|
+
}
|
|
227
289
|
/**
|
|
228
290
|
* Cancel the stream on the given signal
|
|
229
291
|
* @param signal
|
|
@@ -283,6 +345,8 @@ class Stream {
|
|
|
283
345
|
* @param callback
|
|
284
346
|
*/
|
|
285
347
|
map(callback) {
|
|
348
|
+
if (!this.canAlterStream)
|
|
349
|
+
throw new IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
286
350
|
this.actions.push({ type: ActionType.TRANSFORM, functor: callback });
|
|
287
351
|
return this;
|
|
288
352
|
}
|
|
@@ -291,6 +355,8 @@ class Stream {
|
|
|
291
355
|
* @param callback
|
|
292
356
|
*/
|
|
293
357
|
filter(callback) {
|
|
358
|
+
if (!this.canAlterStream)
|
|
359
|
+
throw new IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
294
360
|
this.actions.push({
|
|
295
361
|
type: ActionType.FILTER,
|
|
296
362
|
functor: (v) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -307,6 +373,8 @@ class Stream {
|
|
|
307
373
|
return this;
|
|
308
374
|
}
|
|
309
375
|
reduce(initialData, functor, exhaustive = true) {
|
|
376
|
+
if (!this.canAlterStream)
|
|
377
|
+
throw new IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
310
378
|
let previousData = initialData;
|
|
311
379
|
if (exhaustive) {
|
|
312
380
|
this.actions.push({
|
|
@@ -332,6 +400,8 @@ class Stream {
|
|
|
332
400
|
return this;
|
|
333
401
|
}
|
|
334
402
|
reduceWhile(predicate, initialData, functor, exhaustive = true) {
|
|
403
|
+
if (!this.canAlterStream)
|
|
404
|
+
throw new IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
335
405
|
let previousData = initialData;
|
|
336
406
|
if (exhaustive) {
|
|
337
407
|
this.actions.push({
|
|
@@ -408,6 +478,8 @@ class Stream {
|
|
|
408
478
|
* for the split criteria should be added to the chunk, if false then the data will be added to the next chunk
|
|
409
479
|
*/
|
|
410
480
|
chunk(value, keepSplitCriteria = false) {
|
|
481
|
+
if (!this.canAlterStream)
|
|
482
|
+
throw new IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
411
483
|
if (typeof value === "number" && value < 1)
|
|
412
484
|
throw new Error("Invalid chunk size");
|
|
413
485
|
let chunkedResults = [];
|
|
@@ -514,6 +586,8 @@ class Stream {
|
|
|
514
586
|
* @param count
|
|
515
587
|
*/
|
|
516
588
|
take(count) {
|
|
589
|
+
if (!this.canAlterStream)
|
|
590
|
+
throw new IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
517
591
|
let index = 0;
|
|
518
592
|
this.actions.push({
|
|
519
593
|
type: ActionType.LIMIT,
|
|
@@ -531,6 +605,8 @@ class Stream {
|
|
|
531
605
|
* @param predicate
|
|
532
606
|
*/
|
|
533
607
|
takeWhile(predicate) {
|
|
608
|
+
if (!this.canAlterStream)
|
|
609
|
+
throw new IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
534
610
|
this.actions.push({
|
|
535
611
|
type: ActionType.LIMIT,
|
|
536
612
|
functor: (value) => {
|
|
@@ -547,6 +623,8 @@ class Stream {
|
|
|
547
623
|
* @param predicate
|
|
548
624
|
*/
|
|
549
625
|
skipWhile(predicate) {
|
|
626
|
+
if (!this.canAlterStream)
|
|
627
|
+
throw new IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
550
628
|
let startFound = false;
|
|
551
629
|
this.actions.push({
|
|
552
630
|
type: ActionType.FILTER,
|
|
@@ -566,6 +644,8 @@ class Stream {
|
|
|
566
644
|
* @param expiryPeriod
|
|
567
645
|
*/
|
|
568
646
|
dropRepeats(uniqKeyExtractor, expiryPeriod = undefined) {
|
|
647
|
+
if (!this.canAlterStream)
|
|
648
|
+
throw new IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
569
649
|
const cache = new TimeableObject(undefined, expiryPeriod);
|
|
570
650
|
this.actions.push({
|
|
571
651
|
type: ActionType.FILTER,
|
|
@@ -591,6 +671,8 @@ class Stream {
|
|
|
591
671
|
* @param count
|
|
592
672
|
*/
|
|
593
673
|
skip(count) {
|
|
674
|
+
if (!this.canAlterStream)
|
|
675
|
+
throw new IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
594
676
|
let index = 1;
|
|
595
677
|
this.actions.push({
|
|
596
678
|
type: ActionType.FILTER,
|
|
@@ -608,6 +690,8 @@ class Stream {
|
|
|
608
690
|
* @param period
|
|
609
691
|
*/
|
|
610
692
|
throttleBy(period) {
|
|
693
|
+
if (!this.canAlterStream)
|
|
694
|
+
throw new IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
611
695
|
let future;
|
|
612
696
|
this.actions.push({
|
|
613
697
|
type: ActionType.TRANSFORM,
|
|
@@ -627,6 +711,8 @@ class Stream {
|
|
|
627
711
|
* Flattens any nested structure from the data arriving on the stream
|
|
628
712
|
*/
|
|
629
713
|
flatten() {
|
|
714
|
+
if (!this.canAlterStream)
|
|
715
|
+
throw new IllegalOperationError("Cannot alter the stream as it is frozen");
|
|
630
716
|
this.actions.push({
|
|
631
717
|
type: ActionType.UNPACK,
|
|
632
718
|
functor: (value) => {
|
|
@@ -783,6 +869,9 @@ class Stream {
|
|
|
783
869
|
const { state, value } = yield (this.sourceStream && this.concurrencyLimit
|
|
784
870
|
? this.forwardExecute().registerSignal(signal)
|
|
785
871
|
: this.__execute__().registerSignal(signal));
|
|
872
|
+
if (value !== null && value !== undefined) {
|
|
873
|
+
this.monitorHooks.forEach((hook) => hook(value));
|
|
874
|
+
}
|
|
786
875
|
if (state !== State.CONTINUE) {
|
|
787
876
|
this.done = true;
|
|
788
877
|
this.invokeCompletionHooks();
|
|
@@ -1002,6 +1091,9 @@ class Stream {
|
|
|
1002
1091
|
const { state, value } = yield (this.sourceStream && this.concurrencyLimit
|
|
1003
1092
|
? this.forwardExecute()
|
|
1004
1093
|
: this.__execute__());
|
|
1094
|
+
if (value !== null && value !== undefined) {
|
|
1095
|
+
this.monitorHooks.forEach((hook) => hook(value));
|
|
1096
|
+
}
|
|
1005
1097
|
if (state === State.MATCHED && value !== undefined)
|
|
1006
1098
|
return { done: false, value };
|
|
1007
1099
|
if (state === State.DONE) {
|
|
@@ -1159,6 +1251,9 @@ class Stream {
|
|
|
1159
1251
|
return resolve({ state: State.CONTINUE });
|
|
1160
1252
|
}), signal);
|
|
1161
1253
|
}
|
|
1254
|
+
get canAlterStream() {
|
|
1255
|
+
return !this.frozen;
|
|
1256
|
+
}
|
|
1162
1257
|
}
|
|
1163
1258
|
|
|
1164
1259
|
export { State, Stream };
|
package/README.md
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
## GingerSnap
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
## Description
|
|
5
|
-
Gingersnap is a general purpose library built with the aim of filling the gaps of javascript shortcomings. First-Class support for networking, IO operations and data validation is at the core of this library.
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
8
|
-
To install the package, run `npm install gingersnap`
|
|
9
|
-
|
|
10
|
-
## Roadmap
|
|
11
|
-
- Support request timeout
|
|
12
|
-
- Opt out of using an authenticator for a method's request
|
|
13
|
-
- Support multiple authenticators, and setting Primary authenticator
|
|
14
|
-
- Allow methods to choose authenticator
|
|
15
|
-
- Support endpoint polling with subscription methods
|
|
16
|
-
- Support offline mode with resume feature once net is back
|
|
17
|
-
- Support default response when offline
|
|
18
|
-
- Support grouping requests, if one fail cancel all
|
|
19
|
-
- Support race requests, the first one that finishes/fails cancel all others and return the response
|
|
20
|
-
- Plugin support for services
|
|
21
|
-
- Support for NodeJs
|
|
22
|
-
- Push message support via subscribers
|
|
23
|
-
- Websocket support via subscribers, and methods for sending messages
|
|
24
|
-
- Websocket auth support
|
|
25
|
-
- Improved documentation
|