gingersnap 0.23.0 → 0.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,25 @@
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
@@ -1,11 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var FutureCancelled = require('./FutureCancelled.cjs');
4
-
5
3
  /**
6
4
  * Thrown to indicate that some timed operation has exceeded the maximum duration
7
5
  */
8
- class TimeoutError extends FutureCancelled.FutureCancelled {
6
+ class TimeoutError extends Error {
9
7
  }
10
8
 
11
9
  exports.TimeoutError = TimeoutError;
@@ -1,6 +1,5 @@
1
- import { FutureCancelled } from "./FutureCancelled";
2
1
  /**
3
2
  * Thrown to indicate that some timed operation has exceeded the maximum duration
4
3
  */
5
- export declare class TimeoutError extends FutureCancelled {
4
+ export declare class TimeoutError extends Error {
6
5
  }
@@ -1,9 +1,7 @@
1
- import { FutureCancelled } from './FutureCancelled.mjs';
2
-
3
1
  /**
4
2
  * Thrown to indicate that some timed operation has exceeded the maximum duration
5
3
  */
6
- class TimeoutError extends FutureCancelled {
4
+ class TimeoutError extends Error {
7
5
  }
8
6
 
9
7
  export { TimeoutError };
package/future/future.cjs CHANGED
@@ -91,7 +91,7 @@ class Future {
91
91
  const timeableFuture = Future.sleep(timeout)
92
92
  .registerSignal(value.defaultSignal)
93
93
  .catch(() => { });
94
- return Future.of((resolve, reject) => Promise.race([timeableFuture, value.run()])
94
+ return Future.of((resolve, reject) => Promise.race([timeableFuture, value])
95
95
  .then((v) => {
96
96
  if (value.done) {
97
97
  timeableFuture.cancel();
@@ -130,19 +130,28 @@ class Future {
130
130
  * @param value
131
131
  */
132
132
  static completed(value) {
133
- return new Future((resolve) => resolve(value));
133
+ const future = new Future(() => { });
134
+ future.underLyingPromise = Promise.resolve(value);
135
+ future.completedResult = value;
136
+ future.fulfilled = true;
137
+ future.isRunning = false;
138
+ return future;
134
139
  }
135
140
  /**
136
141
  * Returns a future that fails with the given value
137
142
  * @param value
138
143
  */
139
144
  static exceptionally(value) {
140
- return new Future((_, reject) => reject(value));
145
+ const future = new Future(() => { });
146
+ future.isRunning = false;
147
+ future.failureResult = value;
148
+ future.underLyingPromise = Promise.reject(value);
149
+ return future;
141
150
  }
142
151
  /**
143
152
  * Returns the first completed or failed. If this is cancelled, then all futures provided will also be cancelled
144
153
  * @param futures list of futures
145
- * @param signal optinal abort signal
154
+ * @param signal optional abort signal
146
155
  */
147
156
  static firstCompleted(futures, signal) {
148
157
  return Future.of((resolve, reject, signal) => {
@@ -243,7 +252,7 @@ class Future {
243
252
  * Checks if the future failed
244
253
  */
245
254
  get failed() {
246
- return this.error instanceof Error;
255
+ return this.error !== undefined && this.error instanceof Error;
247
256
  }
248
257
  /**
249
258
  * Retrieves the result of the future, if there is any
@@ -327,7 +336,7 @@ class Future {
327
336
  this.signals.forEach((signal) => newFuture.registerSignal(signal));
328
337
  return newFuture;
329
338
  }
330
- else if (this.done) {
339
+ else if (this.done || this.failed) {
331
340
  const newFuture = Future.wrap(this.underLyingPromise).thenApply(callback);
332
341
  return newFuture;
333
342
  }
@@ -438,24 +447,20 @@ class Future {
438
447
  const resolver = (v) => {
439
448
  if (v instanceof Promise) {
440
449
  v.then((v) => {
441
- this.fulfilled = true;
442
450
  resolve(v);
443
451
  }).catch(reject);
444
452
  }
445
453
  else if (v instanceof Future) {
446
454
  v.run()
447
455
  .then((v) => {
448
- this.fulfilled = true;
449
456
  resolve(v);
450
457
  })
451
458
  .catch(reject);
452
459
  }
453
460
  else if (v instanceof result.FutureResult) {
454
- this.fulfilled = true;
455
461
  resolve(v.value);
456
462
  }
457
463
  else {
458
- this.fulfilled = true;
459
464
  resolve(v);
460
465
  }
461
466
  };
@@ -482,7 +487,6 @@ class Future {
482
487
  result$1
483
488
  .then(() => {
484
489
  if (!this.fulfilled) {
485
- this.fulfilled = true;
486
490
  resolve(null);
487
491
  }
488
492
  })
@@ -492,10 +496,13 @@ class Future {
492
496
  .then((v) => {
493
497
  this.completedResult = v;
494
498
  onFulfilled === null || onFulfilled === void 0 ? void 0 : onFulfilled(v);
499
+ this.isRunning = false;
500
+ this.fulfilled = true;
495
501
  return v;
496
502
  })
497
503
  .catch((e) => {
498
504
  this.failureResult = e instanceof Error ? e : new Error(String(e));
505
+ this.isRunning = false;
499
506
  throw this.failureResult;
500
507
  })
501
508
  .finally(() => {
@@ -508,7 +515,6 @@ class Future {
508
515
  })
509
516
  .finally(() => {
510
517
  this.signals = new Set();
511
- this.isRunning = false;
512
518
  });
513
519
  return this.underLyingPromise;
514
520
  }
@@ -72,11 +72,11 @@ export declare class Future<T> {
72
72
  * Returns a future that fails with the given value
73
73
  * @param value
74
74
  */
75
- static exceptionally(value: Error): Future<unknown>;
75
+ static exceptionally<K>(value: Error): Future<K>;
76
76
  /**
77
77
  * Returns the first completed or failed. If this is cancelled, then all futures provided will also be cancelled
78
78
  * @param futures list of futures
79
- * @param signal optinal abort signal
79
+ * @param signal optional abort signal
80
80
  */
81
81
  static firstCompleted<T extends Array<Future<any>>>(futures: T, signal?: AbortSignal): Future<FutureReturnType<T[number]>>;
82
82
  /**
package/future/future.mjs CHANGED
@@ -70,7 +70,7 @@ class Future {
70
70
  const timeableFuture = Future.sleep(timeout)
71
71
  .registerSignal(value.defaultSignal)
72
72
  .catch(() => { });
73
- return Future.of((resolve, reject) => Promise.race([timeableFuture, value.run()])
73
+ return Future.of((resolve, reject) => Promise.race([timeableFuture, value])
74
74
  .then((v) => {
75
75
  if (value.done) {
76
76
  timeableFuture.cancel();
@@ -109,19 +109,28 @@ class Future {
109
109
  * @param value
110
110
  */
111
111
  static completed(value) {
112
- return new Future((resolve) => resolve(value));
112
+ const future = new Future(() => { });
113
+ future.underLyingPromise = Promise.resolve(value);
114
+ future.completedResult = value;
115
+ future.fulfilled = true;
116
+ future.isRunning = false;
117
+ return future;
113
118
  }
114
119
  /**
115
120
  * Returns a future that fails with the given value
116
121
  * @param value
117
122
  */
118
123
  static exceptionally(value) {
119
- return new Future((_, reject) => reject(value));
124
+ const future = new Future(() => { });
125
+ future.isRunning = false;
126
+ future.failureResult = value;
127
+ future.underLyingPromise = Promise.reject(value);
128
+ return future;
120
129
  }
121
130
  /**
122
131
  * Returns the first completed or failed. If this is cancelled, then all futures provided will also be cancelled
123
132
  * @param futures list of futures
124
- * @param signal optinal abort signal
133
+ * @param signal optional abort signal
125
134
  */
126
135
  static firstCompleted(futures, signal) {
127
136
  return Future.of((resolve, reject, signal) => {
@@ -222,7 +231,7 @@ class Future {
222
231
  * Checks if the future failed
223
232
  */
224
233
  get failed() {
225
- return this.error instanceof Error;
234
+ return this.error !== undefined && this.error instanceof Error;
226
235
  }
227
236
  /**
228
237
  * Retrieves the result of the future, if there is any
@@ -306,7 +315,7 @@ class Future {
306
315
  this.signals.forEach((signal) => newFuture.registerSignal(signal));
307
316
  return newFuture;
308
317
  }
309
- else if (this.done) {
318
+ else if (this.done || this.failed) {
310
319
  const newFuture = Future.wrap(this.underLyingPromise).thenApply(callback);
311
320
  return newFuture;
312
321
  }
@@ -417,24 +426,20 @@ class Future {
417
426
  const resolver = (v) => {
418
427
  if (v instanceof Promise) {
419
428
  v.then((v) => {
420
- this.fulfilled = true;
421
429
  resolve(v);
422
430
  }).catch(reject);
423
431
  }
424
432
  else if (v instanceof Future) {
425
433
  v.run()
426
434
  .then((v) => {
427
- this.fulfilled = true;
428
435
  resolve(v);
429
436
  })
430
437
  .catch(reject);
431
438
  }
432
439
  else if (v instanceof FutureResult) {
433
- this.fulfilled = true;
434
440
  resolve(v.value);
435
441
  }
436
442
  else {
437
- this.fulfilled = true;
438
443
  resolve(v);
439
444
  }
440
445
  };
@@ -461,7 +466,6 @@ class Future {
461
466
  result
462
467
  .then(() => {
463
468
  if (!this.fulfilled) {
464
- this.fulfilled = true;
465
469
  resolve(null);
466
470
  }
467
471
  })
@@ -471,10 +475,13 @@ class Future {
471
475
  .then((v) => {
472
476
  this.completedResult = v;
473
477
  onFulfilled === null || onFulfilled === void 0 ? void 0 : onFulfilled(v);
478
+ this.isRunning = false;
479
+ this.fulfilled = true;
474
480
  return v;
475
481
  })
476
482
  .catch((e) => {
477
483
  this.failureResult = e instanceof Error ? e : new Error(String(e));
484
+ this.isRunning = false;
478
485
  throw this.failureResult;
479
486
  })
480
487
  .finally(() => {
@@ -487,7 +494,6 @@ class Future {
487
494
  })
488
495
  .finally(() => {
489
496
  this.signals = new Set();
490
- this.isRunning = false;
491
497
  });
492
498
  return this.underLyingPromise;
493
499
  }
@@ -131,7 +131,7 @@ class WebSocketService extends NetworkService.NetworkService {
131
131
  data = body.object();
132
132
  }
133
133
  data = R__namespace.set(lens, guid, data);
134
- yield this.socket.send(JSON.stringify(data));
134
+ yield this.socket.sendNow(JSON.stringify(data));
135
135
  return 1;
136
136
  })))
137
137
  .map(() => this.socket.streamView(R__namespace.compose(R__namespace.equals(guid), R__namespace.view(lens)), config.socketRequestReply.objectMaxSize, config.socketRequestReply.expiryPeriod))
@@ -162,13 +162,13 @@ class WebSocketService extends NetworkService.NetworkService {
162
162
  yield this.socket.open();
163
163
  }
164
164
  if (body instanceof model.Model) {
165
- yield this.socket.send(body.blob());
165
+ yield this.socket.sendNow(body.blob());
166
166
  }
167
167
  else if (body instanceof ArrayBuffer || body instanceof Blob) {
168
- yield this.socket.send(body);
168
+ yield this.socket.sendNow(body);
169
169
  }
170
170
  else {
171
- yield this.socket.send(JSON.stringify(body));
171
+ yield this.socket.sendNow(JSON.stringify(body));
172
172
  }
173
173
  const result = oldMethod();
174
174
  if (result instanceof Promise) {
@@ -110,7 +110,7 @@ class WebSocketService extends NetworkService {
110
110
  data = body.object();
111
111
  }
112
112
  data = R.set(lens, guid, data);
113
- yield this.socket.send(JSON.stringify(data));
113
+ yield this.socket.sendNow(JSON.stringify(data));
114
114
  return 1;
115
115
  })))
116
116
  .map(() => this.socket.streamView(R.compose(R.equals(guid), R.view(lens)), config.socketRequestReply.objectMaxSize, config.socketRequestReply.expiryPeriod))
@@ -141,13 +141,13 @@ class WebSocketService extends NetworkService {
141
141
  yield this.socket.open();
142
142
  }
143
143
  if (body instanceof Model) {
144
- yield this.socket.send(body.blob());
144
+ yield this.socket.sendNow(body.blob());
145
145
  }
146
146
  else if (body instanceof ArrayBuffer || body instanceof Blob) {
147
- yield this.socket.send(body);
147
+ yield this.socket.sendNow(body);
148
148
  }
149
149
  else {
150
- yield this.socket.send(JSON.stringify(body));
150
+ yield this.socket.sendNow(JSON.stringify(body));
151
151
  }
152
152
  const result = oldMethod();
153
153
  if (result instanceof Promise) {
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"gingersnap","version":"0.23.0","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.d.ts","default":"./stream.mjs"},"require":{"types":"./stream.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.d.ts","default":"./networking.mjs"},"require":{"types":"./networking.d.ts","default":"./networking.cjs"}},"./managers":{"import":{"types":"./managers.d.ts","default":"./managers.mjs"},"require":{"types":"./managers.d.ts","default":"./managers.cjs"}},"./future":{"import":{"types":"./future.d.ts","default":"./future.mjs"},"require":{"types":"./future.d.ts","default":"./future.cjs"}},"./errors":{"import":{"types":"./errors.d.ts","default":"./errors.mjs"},"require":{"types":"./errors.d.ts","default":"./errors.cjs"}},"./data-structures/array":{"import":{"types":"./data-structures/array.d.ts","default":"./data-structures/array.mjs"},"require":{"types":"./data-structures/array.d.ts","default":"./data-structures/array.cjs"}},"./data-structures/object":{"import":{"types":"./data-structures/object.d.ts","default":"./data-structures/object.mjs"},"require":{"types":"./data-structures/object.d.ts","default":"./data-structures/object.cjs"}},"./data/decoders":{"import":{"types":"./data/decoders.d.ts","default":"./data/decoders.mjs"},"require":{"types":"./data/decoders.d.ts","default":"./data/decoders.cjs"}},"./data/model":{"import":{"types":"./data/model.d.ts","default":"./data/model.mjs"},"require":{"types":"./data/model.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.d.ts","default":"./store.mjs"},"require":{"types":"./store.d.ts","default":"./store.cjs"}},"./functools":{"import":{"types":"./functools.d.ts","default":"./functools.mjs"},"require":{"types":"./functools.d.ts","default":"./functools.cjs"}}}}
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"}}}}
package/socket.cjs CHANGED
@@ -11,6 +11,7 @@ require('ramda');
11
11
  require('uuid');
12
12
  var stream = require('./stream.cjs');
13
13
  var future = require('./future/future.cjs');
14
+ var result = require('./future/result.cjs');
14
15
  var synchronize = require('./synchronize.cjs');
15
16
  var stream_state = require('./stream/state.cjs');
16
17
  var WebSocket = require('modern-isomorphic-ws');
@@ -155,15 +156,28 @@ class StreamableWebSocket {
155
156
  this.signal.dispatchEvent(new CustomEvent("abort"));
156
157
  }
157
158
  /**
158
- * Sends data via socket
159
+ * Sends data via socket as soon as possible
159
160
  * @param data
160
161
  */
161
- send(data) {
162
+ sendNow(data) {
162
163
  var _a;
163
164
  return _tslib.__awaiter(this, void 0, void 0, function* () {
164
165
  (_a = this.getSocket()) === null || _a === void 0 ? void 0 : _a.send(data instanceof Blob ? yield data.arrayBuffer() : data);
165
166
  });
166
167
  }
168
+ /**
169
+ * Sends data via socket, awaiting socket connection if currently disconnected
170
+ * @param data
171
+ */
172
+ send(data) {
173
+ return this.open().thenApply(() => _tslib.__awaiter(this, void 0, void 0, function* () { var _a; return (_a = this.getSocket()) === null || _a === void 0 ? void 0 : _a.send(data instanceof Blob ? yield data.arrayBuffer() : data); }));
174
+ }
175
+ /**
176
+ * Gets a stream of messages that match the given filter provided
177
+ * @param lens filter function to select specific messages
178
+ * @param objectMaxSize max messages to buffer if reading from this stream is slower than messages coming in
179
+ * @param expiryPeriod how long to store buffered messages if not read
180
+ */
167
181
  streamView(lens, objectMaxSize, expiryPeriod) {
168
182
  const queue = new Queue.Queue(objectMaxSize, expiryPeriod);
169
183
  const tuple = Pair.pair(queue, lens);
@@ -200,6 +214,21 @@ class StreamableWebSocket {
200
214
  this.messageQueues = this.messageQueues.filter((v) => v !== queue);
201
215
  });
202
216
  }
217
+ with(functor) {
218
+ return future.Future.of((_, __, signal) => _tslib.__awaiter(this, void 0, void 0, function* () {
219
+ const stream = this.stream().buffer(this.cacheSize);
220
+ yield this.open().registerSignal(signal);
221
+ try {
222
+ functor(new result.FutureResult(stream, signal));
223
+ }
224
+ finally {
225
+ if (!this.closed) {
226
+ this.close();
227
+ }
228
+ stream.cancel();
229
+ }
230
+ })).schedule();
231
+ }
203
232
  addEventListener(type, functor) {
204
233
  this.socketListeners.push([type, functor]);
205
234
  if (this.socket) {
package/socket.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Stream } from "./stream";
2
- import { Future, WaitPeriod } from "./future";
2
+ import { Future, FutureResult, WaitPeriod } from "./future";
3
3
  import { Decoder } from "./data/decoders";
4
+ import { ContextManager } from "./managers";
4
5
  interface WebSocketConfiguration {
5
6
  retryOnDisconnect?: boolean;
6
7
  cacheSize?: number;
@@ -11,7 +12,7 @@ interface WebSocketConfiguration {
11
12
  /**
12
13
  * Future-based web sockets
13
14
  */
14
- export declare class StreamableWebSocket<T> {
15
+ export declare class StreamableWebSocket<T> implements ContextManager<Stream<T>> {
15
16
  private readonly maxReconnectAttempt;
16
17
  private readonly retryOnDisconnect;
17
18
  private openFuture?;
@@ -49,15 +50,27 @@ export declare class StreamableWebSocket<T> {
49
50
  */
50
51
  close(): void;
51
52
  /**
52
- * Sends data via socket
53
+ * Sends data via socket as soon as possible
53
54
  * @param data
54
55
  */
55
- send(data: string | ArrayBufferView | Blob | ArrayBufferLike): Promise<void>;
56
+ sendNow(data: string | ArrayBufferView | Blob | ArrayBufferLike): Promise<void>;
57
+ /**
58
+ * Sends data via socket, awaiting socket connection if currently disconnected
59
+ * @param data
60
+ */
61
+ send(data: string | ArrayBufferView | Blob | ArrayBufferLike): Future<void>;
62
+ /**
63
+ * Gets a stream of messages that match the given filter provided
64
+ * @param lens filter function to select specific messages
65
+ * @param objectMaxSize max messages to buffer if reading from this stream is slower than messages coming in
66
+ * @param expiryPeriod how long to store buffered messages if not read
67
+ */
56
68
  streamView(lens: (v: T) => boolean, objectMaxSize?: number, expiryPeriod?: WaitPeriod): Stream<T>;
57
69
  /**
58
70
  * Gets the stream for messages received via this socket
59
71
  */
60
72
  stream(): Stream<T>;
73
+ with(functor: (value: FutureResult<Stream<T>>) => any): Future<void>;
61
74
  private addEventListener;
62
75
  private createSocket;
63
76
  private getSocket;
package/socket.mjs CHANGED
@@ -9,6 +9,7 @@ import 'ramda';
9
9
  import 'uuid';
10
10
  import { Stream } from './stream.mjs';
11
11
  import { Future } from './future/future.mjs';
12
+ import { FutureResult } from './future/result.mjs';
12
13
  import { FutureEvent, Lock } from './synchronize.mjs';
13
14
  import { ExecutorState } from './stream/state.mjs';
14
15
  import WebSocket from 'modern-isomorphic-ws';
@@ -153,15 +154,28 @@ class StreamableWebSocket {
153
154
  this.signal.dispatchEvent(new CustomEvent("abort"));
154
155
  }
155
156
  /**
156
- * Sends data via socket
157
+ * Sends data via socket as soon as possible
157
158
  * @param data
158
159
  */
159
- send(data) {
160
+ sendNow(data) {
160
161
  var _a;
161
162
  return __awaiter(this, void 0, void 0, function* () {
162
163
  (_a = this.getSocket()) === null || _a === void 0 ? void 0 : _a.send(data instanceof Blob ? yield data.arrayBuffer() : data);
163
164
  });
164
165
  }
166
+ /**
167
+ * Sends data via socket, awaiting socket connection if currently disconnected
168
+ * @param data
169
+ */
170
+ send(data) {
171
+ return this.open().thenApply(() => __awaiter(this, void 0, void 0, function* () { var _a; return (_a = this.getSocket()) === null || _a === void 0 ? void 0 : _a.send(data instanceof Blob ? yield data.arrayBuffer() : data); }));
172
+ }
173
+ /**
174
+ * Gets a stream of messages that match the given filter provided
175
+ * @param lens filter function to select specific messages
176
+ * @param objectMaxSize max messages to buffer if reading from this stream is slower than messages coming in
177
+ * @param expiryPeriod how long to store buffered messages if not read
178
+ */
165
179
  streamView(lens, objectMaxSize, expiryPeriod) {
166
180
  const queue = new Queue(objectMaxSize, expiryPeriod);
167
181
  const tuple = pair(queue, lens);
@@ -198,6 +212,21 @@ class StreamableWebSocket {
198
212
  this.messageQueues = this.messageQueues.filter((v) => v !== queue);
199
213
  });
200
214
  }
215
+ with(functor) {
216
+ return Future.of((_, __, signal) => __awaiter(this, void 0, void 0, function* () {
217
+ const stream = this.stream().buffer(this.cacheSize);
218
+ yield this.open().registerSignal(signal);
219
+ try {
220
+ functor(new FutureResult(stream, signal));
221
+ }
222
+ finally {
223
+ if (!this.closed) {
224
+ this.close();
225
+ }
226
+ stream.cancel();
227
+ }
228
+ })).schedule();
229
+ }
201
230
  addEventListener(type, functor) {
202
231
  this.socketListeners.push([type, functor]);
203
232
  if (this.socket) {
package/stream/index.d.ts CHANGED
@@ -97,6 +97,16 @@ export declare class Stream<T> implements AsyncGenerator<T> {
97
97
  * @param signal
98
98
  */
99
99
  cancelOnSignal(signal: AbortSignal): this;
100
+ /**
101
+ * Waits at most 'period' time for the data upstream to be received, otherwise will cancel the stream
102
+ * @param period
103
+ */
104
+ waitFor(period: WaitPeriod): Stream<T>;
105
+ /**
106
+ * Waits at most 'period' time for the first data upstream to be received, otherwise will cancel the stream
107
+ * @param period
108
+ */
109
+ waitFirstFor(period: WaitPeriod): Stream<T>;
100
110
  parallel(concurrentlyLimit?: number): Stream<T>;
101
111
  /**
102
112
  * Transforms each data on the stream using the callback provided
@@ -39,6 +39,30 @@ class Observable {
39
39
  this.publish(reqTopic, data);
40
40
  return future.Future.waitFor(this.subscribe(replyTopic, 2).future, timeout).thenApply(({ value }) => value.data);
41
41
  }
42
+ /**
43
+ * Provides a Request-Multiple-Reply model by sending data over the given topic, and
44
+ * stream responses from the second topic provided. If you cancel the reply stream, a cancellation message
45
+ * can be sent via the cancellationTopic
46
+ * @param reqTopic request topic
47
+ * @param replyTopic reply topic which will be used to form the reply stream
48
+ * @param data request data
49
+ * @param cancellationTopic topic used to send a message if the stream is cancelled
50
+ * @param cancellationData cancellation message
51
+ * @param bufferSize max reply stream message buffer size
52
+ * @param timeout
53
+ */
54
+ requestStream(reqTopic, replyTopic, data, cancellationTopic, cancellationData, bufferSize = 100, timeout = { seconds: 15 }) {
55
+ this.publish(reqTopic, data);
56
+ const stream = this.subscribe(replyTopic, bufferSize);
57
+ return stream
58
+ .waitFirstFor(timeout)
59
+ .map(({ data }) => data)
60
+ .onCancellation(() => {
61
+ if (cancellationTopic) {
62
+ this.publish(cancellationTopic, cancellationData);
63
+ }
64
+ });
65
+ }
42
66
  }
43
67
  /**
44
68
  * Provides Publisher-Subscriber service around an EventTarget
@@ -38,6 +38,19 @@ export declare abstract class Observable<T> {
38
38
  * @param timeout how long to wait for a response, defaults to 15 seconds
39
39
  */
40
40
  request(reqTopic: string, replyTopic: string, data: T, timeout?: WaitPeriod): Future<T>;
41
+ /**
42
+ * Provides a Request-Multiple-Reply model by sending data over the given topic, and
43
+ * stream responses from the second topic provided. If you cancel the reply stream, a cancellation message
44
+ * can be sent via the cancellationTopic
45
+ * @param reqTopic request topic
46
+ * @param replyTopic reply topic which will be used to form the reply stream
47
+ * @param data request data
48
+ * @param cancellationTopic topic used to send a message if the stream is cancelled
49
+ * @param cancellationData cancellation message
50
+ * @param bufferSize max reply stream message buffer size
51
+ * @param timeout
52
+ */
53
+ requestStream(reqTopic: string, replyTopic: string, data: T, cancellationTopic?: string, cancellationData?: T, bufferSize?: number, timeout?: WaitPeriod): Stream<T>;
41
54
  }
42
55
  /**
43
56
  * Provides Publisher-Subscriber service around an EventTarget
@@ -37,6 +37,30 @@ class Observable {
37
37
  this.publish(reqTopic, data);
38
38
  return Future.waitFor(this.subscribe(replyTopic, 2).future, timeout).thenApply(({ value }) => value.data);
39
39
  }
40
+ /**
41
+ * Provides a Request-Multiple-Reply model by sending data over the given topic, and
42
+ * stream responses from the second topic provided. If you cancel the reply stream, a cancellation message
43
+ * can be sent via the cancellationTopic
44
+ * @param reqTopic request topic
45
+ * @param replyTopic reply topic which will be used to form the reply stream
46
+ * @param data request data
47
+ * @param cancellationTopic topic used to send a message if the stream is cancelled
48
+ * @param cancellationData cancellation message
49
+ * @param bufferSize max reply stream message buffer size
50
+ * @param timeout
51
+ */
52
+ requestStream(reqTopic, replyTopic, data, cancellationTopic, cancellationData, bufferSize = 100, timeout = { seconds: 15 }) {
53
+ this.publish(reqTopic, data);
54
+ const stream = this.subscribe(replyTopic, bufferSize);
55
+ return stream
56
+ .waitFirstFor(timeout)
57
+ .map(({ data }) => data)
58
+ .onCancellation(() => {
59
+ if (cancellationTopic) {
60
+ this.publish(cancellationTopic, cancellationData);
61
+ }
62
+ });
63
+ }
40
64
  }
41
65
  /**
42
66
  * Provides Publisher-Subscriber service around an EventTarget
package/stream.cjs CHANGED
@@ -253,6 +253,38 @@ class Stream {
253
253
  signal.addEventListener("abort", () => this.cancel());
254
254
  return this;
255
255
  }
256
+ /**
257
+ * Waits at most 'period' time for the data upstream to be received, otherwise will cancel the stream
258
+ * @param period
259
+ */
260
+ waitFor(period) {
261
+ const action = () => future.Future.waitFor(this.internalNext(), period);
262
+ return new Stream((signal) => _tslib.__awaiter(this, void 0, void 0, function* () {
263
+ return action()
264
+ .registerSignal(signal)
265
+ .thenApply(({ value }) => new stream_state.ExecutorState(value.done, value.value));
266
+ }));
267
+ }
268
+ /**
269
+ * Waits at most 'period' time for the first data upstream to be received, otherwise will cancel the stream
270
+ * @param period
271
+ */
272
+ waitFirstFor(period) {
273
+ let i = 0;
274
+ const actions = [
275
+ () => future.Future.waitFor(this.internalNext(), period).thenApply(({ value }) => {
276
+ i = 1;
277
+ return value;
278
+ }),
279
+ () => this.internalNext(),
280
+ ];
281
+ return new Stream((signal) => _tslib.__awaiter(this, void 0, void 0, function* () {
282
+ const action = actions[i];
283
+ return action()
284
+ .registerSignal(signal)
285
+ .thenApply(({ value }) => new stream_state.ExecutorState(value.done, value.value));
286
+ }));
287
+ }
256
288
  parallel(concurrentlyLimit = 3) {
257
289
  if (concurrentlyLimit < 1) {
258
290
  throw new IllegalOperationError.IllegalOperationError("Cannot start parallel stream less than 2");
package/stream.mjs CHANGED
@@ -232,6 +232,38 @@ class Stream {
232
232
  signal.addEventListener("abort", () => this.cancel());
233
233
  return this;
234
234
  }
235
+ /**
236
+ * Waits at most 'period' time for the data upstream to be received, otherwise will cancel the stream
237
+ * @param period
238
+ */
239
+ waitFor(period) {
240
+ const action = () => Future.waitFor(this.internalNext(), period);
241
+ return new Stream((signal) => __awaiter(this, void 0, void 0, function* () {
242
+ return action()
243
+ .registerSignal(signal)
244
+ .thenApply(({ value }) => new ExecutorState(value.done, value.value));
245
+ }));
246
+ }
247
+ /**
248
+ * Waits at most 'period' time for the first data upstream to be received, otherwise will cancel the stream
249
+ * @param period
250
+ */
251
+ waitFirstFor(period) {
252
+ let i = 0;
253
+ const actions = [
254
+ () => Future.waitFor(this.internalNext(), period).thenApply(({ value }) => {
255
+ i = 1;
256
+ return value;
257
+ }),
258
+ () => this.internalNext(),
259
+ ];
260
+ return new Stream((signal) => __awaiter(this, void 0, void 0, function* () {
261
+ const action = actions[i];
262
+ return action()
263
+ .registerSignal(signal)
264
+ .thenApply(({ value }) => new ExecutorState(value.done, value.value));
265
+ }));
266
+ }
235
267
  parallel(concurrentlyLimit = 3) {
236
268
  if (concurrentlyLimit < 1) {
237
269
  throw new IllegalOperationError("Cannot start parallel stream less than 2");