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.
@@ -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,8 @@
1
+ import { Queue } from "./Queue";
2
+ export declare class Publisher<T, K> {
3
+ private readonly queue;
4
+ private readonly transformer;
5
+ constructor(queue: Queue<T>, transformer: (v: K) => T);
6
+ send(value: K): void;
7
+ publish(value: K): void;
8
+ }
@@ -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.2","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"}}}}
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