uneventful 0.0.10 → 0.0.11

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/dist/mod.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import { d as defer, i as isFunction } from './utils-DnMz1K-o.mjs';
2
- import { r as resolver, a as rejecter, b as resolve, m as must, s as start, g as getJob, n as noop, c as root, i as isCancel, d as isUnhandled, e as markHandled, f as isError, h as fulfillPromise, j as restarting, k as current, l as isValue } from './jobutils-CmHay7sf.mjs';
3
- export { u as CancelError, C as CancelResult, E as ErrorResult, V as ValueResult, B as abortSignal, x as detached, q as getResult, p as isHandled, z as isJobActive, w as makeJob, v as nativePromise, y as newRoot, t as propagateResult, o as reject, D as task, A as timeout } from './jobutils-CmHay7sf.mjs';
4
- import { I as IsStream, b as backpressure, t as throttle, c as connect, a as callOrWait, m as mustBeSourceOrSignal } from './call-or-wait-DORP7nkp.mjs';
5
- export { d as compose, i as into, p as pipe } from './call-or-wait-DORP7nkp.mjs';
1
+ import { d as defer, i as isFunction } from './utils-cyEhnyp7.mjs';
2
+ import { r as resolver, a as rejecter, b as resolve, m as must, s as start, g as getJob, n as noop, c as root, i as isCancel, d as isUnhandled, e as markHandled, f as isError, h as fulfillPromise, j as restarting, k as currentCell, l as isValue } from './jobutils-Dvu-e99o.mjs';
3
+ export { u as CancelError, C as CancelResult, E as ErrorResult, V as ValueResult, B as abortSignal, x as detached, q as getResult, p as isHandled, z as isJobActive, w as makeJob, v as nativePromise, y as newRoot, t as propagateResult, o as reject, D as task, A as timeout } from './jobutils-Dvu-e99o.mjs';
4
+ import { I as IsStream, b as backpressure, t as throttle, c as connect, a as callOrWait, m as mustBeSourceOrSignal } from './call-or-wait-EzXJX5Dq.mjs';
5
+ export { d as compose, i as into, p as pipe } from './call-or-wait-EzXJX5Dq.mjs';
6
6
 
7
7
  function* to(p) {
8
8
  return yield (res) => Promise.resolve(p).then(resolver(res), rejecter(res));
@@ -276,10 +276,10 @@ function forEach(src, sink, inlet) {
276
276
  return (src2) => forEach(src2, sink, inlet);
277
277
  }
278
278
  function recalcWhen(fnOrKey, fn) {
279
- current.cell?.recalcWhen(fnOrKey, fn);
279
+ currentCell?.recalcWhen(fnOrKey, fn);
280
280
  }
281
281
  function isObserved() {
282
- return current.cell?.isObserved();
282
+ return currentCell?.isObserved();
283
283
  }
284
284
 
285
285
  function concat(sources) {
@@ -371,7 +371,7 @@ function slack(size, dropped = noop) {
371
371
  conn.connect(src, (v) => {
372
372
  buffer.push(v);
373
373
  if (!draining && ready())
374
- return drain();
374
+ return void drain();
375
375
  while (buffer.length > max) {
376
376
  dropped(size < 0 ? buffer.pop() : buffer.shift());
377
377
  }
@@ -401,6 +401,7 @@ function slack(size, dropped = noop) {
401
401
  } finally {
402
402
  draining = false;
403
403
  }
404
+ return false;
404
405
  }
405
406
  return IsStream;
406
407
  };
@@ -441,4 +442,3 @@ function takeWhile(condition) {
441
442
  }
442
443
 
443
444
  export { IsStream, backpressure, concat, concatAll, concatMap, connect, defer, each, emitter, empty, filter, forEach, fromAsyncIterable, fromDomEvent, fromIterable, fromPromise, fromSubscribe, fromValue, fulfillPromise, getJob, interval, isCancel, isError, isObserved, isUnhandled, isValue, lazy, map, markHandled, merge, mergeAll, mergeMap, mockSource, must, never, next, noop, recalcWhen, rejecter, resolve, resolver, restarting, root, share, skip, skipUntil, skipWhile, slack, sleep, start, switchAll, switchMap, take, takeUntil, takeWhile, throttle, to };
444
- //# sourceMappingURL=mod.mjs.map
package/dist/shared.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { c as root, m as must, s as start, n as noop } from './jobutils-CmHay7sf.mjs';
2
- import { a as isGeneratorFunction, b as decorateMethod, i as isFunction, c as apply, s as setMap } from './utils-DnMz1K-o.mjs';
1
+ import { c as root, m as must, s as start, n as noop } from './jobutils-Dvu-e99o.mjs';
2
+ import { a as isGeneratorFunction, b as decorateMethod, i as isFunction, c as apply, s as setMap } from './utils-cyEhnyp7.mjs';
3
3
 
4
4
  function service(factory) {
5
5
  let known = false, value = void 0;
@@ -50,4 +50,3 @@ function fork(genOrFunc, ...args) {
50
50
  const forks = /* @__PURE__ */ new WeakMap();
51
51
 
52
52
  export { expiring, fork, service };
53
- //# sourceMappingURL=shared.mjs.map
package/dist/signals.d.ts CHANGED
@@ -195,6 +195,7 @@ interface RuleFactory {
195
195
  * @returns A function that can be called to terminate the rule.
196
196
  *
197
197
  * @category Reactive Behaviors
198
+ * @function
198
199
  */
199
200
  declare const rule: ((fn: () => OptionalCleanup) => DisposeFn) & RuleFactory;
200
201
  /**
package/dist/signals.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { F as swapCtx, G as nullCtx, k as current, H as makeCtx, w as makeJob, c as root, f as isError, e as markHandled, g as getJob, x as detached, o as reject, b as resolve, I as freeCtx } from './jobutils-CmHay7sf.mjs';
2
- import { b as backpressure, I as IsStream, a as callOrWait } from './call-or-wait-DORP7nkp.mjs';
3
- import { e as batch, f as arrayEq, d as defer, c as apply, s as setMap, C as CallableObject } from './utils-DnMz1K-o.mjs';
1
+ import { F as pushCtx, G as popCtx, w as makeJob, c as root, f as isError, e as markHandled, g as getJob, x as detached, k as currentCell, o as reject, b as resolve } from './jobutils-Dvu-e99o.mjs';
2
+ import { b as backpressure, I as IsStream, a as callOrWait } from './call-or-wait-EzXJX5Dq.mjs';
3
+ import { e as batch, f as arrayEq, d as defer, c as apply, s as setMap, C as CallableObject } from './utils-cyEhnyp7.mjs';
4
4
 
5
5
  const ruleQueues = /* @__PURE__ */ new WeakMap();
6
6
  function ruleQueue(scheduleFn = defer) {
@@ -32,6 +32,14 @@ var timestamp = 1;
32
32
  const fntrackers = /* @__PURE__ */ new WeakMap();
33
33
  const obtrackers = /* @__PURE__ */ new WeakMap();
34
34
  const monitors = /* @__PURE__ */ new WeakMap();
35
+ const staleStreams = batch((q) => {
36
+ for (const cell of q)
37
+ if (!cell.subscribers) {
38
+ ++timestamp;
39
+ break;
40
+ }
41
+ q.clear();
42
+ });
35
43
  const demandChanges = batch((q) => {
36
44
  for (const cell of q)
37
45
  cell.updateDemand();
@@ -129,6 +137,7 @@ function toggleDemand(c) {
129
137
  demandChanges.has(c) ? demandChanges.delete(c) : demandChanges.add(c);
130
138
  }
131
139
  }
140
+ let dummySource;
132
141
  class Cell {
133
142
  constructor() {
134
143
  this.value = void 0;
@@ -140,7 +149,7 @@ class Cell {
140
149
  this.latestSource = timestamp;
141
150
  // max lastChanged of this cell or any ancestor source
142
151
  this.flags = 0;
143
- this.ctx = void 0;
152
+ this.job = void 0;
144
153
  /** The subscription being added during the current calculation - used for uniqueness */
145
154
  this.adding = void 0;
146
155
  /** Linked list of sources */
@@ -159,11 +168,11 @@ class Cell {
159
168
  Cell.mkRule(() => {
160
169
  const val = this.getValue();
161
170
  if (val !== lastValue) {
162
- const old = swapCtx(nullCtx);
171
+ pushCtx();
163
172
  try {
164
173
  sink(lastValue = val);
165
174
  } finally {
166
- swapCtx(old);
175
+ popCtx();
167
176
  }
168
177
  }
169
178
  }, inlet ? ruleQueue(backpressure(inlet)) : defaultQ);
@@ -172,8 +181,8 @@ class Cell {
172
181
  getValue() {
173
182
  if (arguments.length)
174
183
  return apply(this.stream, this, arguments);
175
- const dep = current.cell;
176
- if (dep && this.flags & 132 /* Variable */) {
184
+ const dep = currentCell;
185
+ if (dep && this.flags & 132 /* Variable */ && (dep.flags & 8 /* Peeking */) === 0) {
177
186
  if (this.flags & 32 /* Running */)
178
187
  throw new CircularDependency("Cached function dependency cycle");
179
188
  let s = this.adding;
@@ -204,7 +213,7 @@ class Cell {
204
213
  return this.value;
205
214
  }
206
215
  shouldWrite(changed) {
207
- const cell = current.cell || currentRule;
216
+ const cell = currentCell || currentRule;
208
217
  if (cell) {
209
218
  if (!cell.queue)
210
219
  throw new WriteConflict("Side-effects not allowed outside rules");
@@ -237,7 +246,6 @@ class Cell {
237
246
  if (this.shouldWrite(compute !== this.compute)) {
238
247
  this.flags |= 4 /* Compute */;
239
248
  this.compute = compute;
240
- this.ctx ||= makeCtx(null, this);
241
249
  }
242
250
  }
243
251
  hasBeenRead() {
@@ -253,8 +261,13 @@ class Cell {
253
261
  if (validThrough === timestamp)
254
262
  return;
255
263
  this.validThrough = timestamp;
256
- if (!(this.flags & 4 /* Compute */))
264
+ if (!(this.flags & 4 /* Compute */)) {
265
+ if (this.flags & 256 /* Stream */ && !this.subscribers) {
266
+ this.lastChanged = timestamp;
267
+ staleStreams.add(this);
268
+ }
257
269
  return;
270
+ }
258
271
  if (this.sources) {
259
272
  for (let sub = this.sources; sub; sub = sub.nS) {
260
273
  const s = sub.src;
@@ -275,9 +288,9 @@ class Cell {
275
288
  return this.doRecalc();
276
289
  }
277
290
  doRecalc() {
278
- const { ctx } = this, oldCtx = swapCtx(ctx);
291
+ pushCtx(this.job, this);
279
292
  if (this.flags & 64 /* Stateful */) {
280
- ctx.job?.restart();
293
+ this.job?.restart();
281
294
  }
282
295
  for (let sub = this.sources; sub; sub = sub.nS) {
283
296
  sub.ts = -1;
@@ -307,12 +320,12 @@ class Cell {
307
320
  demandChanges.delete(this);
308
321
  if (this.flags & (16 /* Error */ | 2 /* Stopped */)) {
309
322
  this.flags &= ~2 /* Stopped */;
310
- ctx.job?.restart();
323
+ this.job?.restart();
311
324
  } else if (this.flags & 1 /* Observed */) ; else {
312
- ctx.job?.restart();
325
+ this.job?.restart();
313
326
  }
314
327
  }
315
- swapCtx(oldCtx);
328
+ popCtx();
316
329
  let head;
317
330
  for (let sub = this.sources; sub; ) {
318
331
  const pS = sub.pS;
@@ -328,8 +341,12 @@ class Cell {
328
341
  }
329
342
  }
330
343
  becomeConstant() {
331
- if (this.flags & (32 /* Running */ | 64 /* Stateful */))
344
+ if (this.flags & 32 /* Running */)
332
345
  return;
346
+ if (this.flags & 64 /* Stateful */) {
347
+ mksub(dummySource ||= Cell.mkValue(null), this);
348
+ return;
349
+ }
333
350
  this.flags &= ~4 /* Compute */;
334
351
  if (this.flags & 132 /* Variable */)
335
352
  return;
@@ -396,14 +413,15 @@ class Cell {
396
413
  return cell;
397
414
  }
398
415
  isObserved() {
416
+ if (this.flags & 8 /* Peeking */)
417
+ return false;
399
418
  return !!((this.flags |= 64 /* Stateful */) & 1 /* Observed */);
400
419
  }
401
420
  getJob() {
421
+ if (this.job)
422
+ return this.job;
402
423
  this.flags |= 64 /* Stateful */;
403
- return this.ctx.job ||= makeJob(root, () => {
404
- this.ctx.job?.end();
405
- this.ctx.job = void 0;
406
- });
424
+ return this.job = makeJob();
407
425
  }
408
426
  updateDemand() {
409
427
  demandChanges.delete(this);
@@ -412,13 +430,12 @@ class Cell {
412
430
  } else if (this.flags & 1 /* Observed */) {
413
431
  this.shouldWrite(true);
414
432
  } else {
415
- this.ctx.job?.restart();
433
+ this.job?.restart();
416
434
  }
417
435
  }
418
436
  static mkStream(src, val) {
419
437
  const cell = this.mkValue(val);
420
- cell.flags |= 64 /* Stateful */;
421
- const ctx = makeCtx();
438
+ cell.flags |= 64 /* Stateful */ | 256 /* Stream */;
422
439
  const write = (v) => {
423
440
  cell.setValue(v, false);
424
441
  };
@@ -431,27 +448,39 @@ class Cell {
431
448
  }
432
449
  if (job)
433
450
  return;
434
- job = ctx.job = makeJob(root).do((r) => {
451
+ job = makeJob(root).do((r) => {
435
452
  if (isError(r)) {
436
453
  cell.setValue(markHandled(r), true);
437
454
  } else {
438
455
  cell.setValue(val, false);
439
456
  }
440
- ctx.job = job = void 0;
457
+ job = void 0;
441
458
  });
442
- const old = swapCtx(ctx);
459
+ pushCtx(job);
443
460
  try {
444
461
  src(write, job);
445
462
  } catch (e) {
446
463
  job.end();
447
464
  root.asyncThrow(e);
448
465
  } finally {
449
- swapCtx(old);
466
+ popCtx();
450
467
  }
451
468
  });
452
469
  return cell;
453
470
  }
471
+ peek(fn, thisArg, args) {
472
+ if (this.flags & 8 /* Peeking */)
473
+ return apply(fn, thisArg, args);
474
+ this.flags |= 8 /* Peeking */;
475
+ try {
476
+ return apply(fn, thisArg, args);
477
+ } finally {
478
+ this.flags &= ~8 /* Peeking */;
479
+ }
480
+ }
454
481
  recalcWhen(fnOrKey, fn) {
482
+ if (this.flags & 8 /* Peeking */)
483
+ return;
455
484
  let trackers = fn ? obtrackers.get(fn) || setMap(obtrackers, fn, /* @__PURE__ */ new WeakMap()) : fntrackers;
456
485
  let signal = trackers.get(fnOrKey);
457
486
  if (!signal) {
@@ -465,30 +494,28 @@ class Cell {
465
494
  static mkCached(compute) {
466
495
  const cell = new Cell();
467
496
  cell.compute = compute;
468
- cell.ctx = makeCtx(null, cell);
469
497
  cell.flags = 4 /* Compute */;
470
498
  return cell;
471
499
  }
472
500
  static mkRule(fn, q) {
473
501
  const outer = getJob(), cell = Cell.mkCached(() => {
474
502
  try {
475
- cell.getJob();
476
503
  const cleanup = fn();
477
504
  if (cleanup)
478
- (cell.ctx.job || cell.getJob()).must(cleanup);
479
- cell.lastChanged = timestamp;
505
+ (cell.job || cell.getJob()).must(cleanup);
480
506
  } catch (e) {
481
507
  cell.stop();
482
508
  throw e;
483
509
  }
484
510
  }), stop = cell.stop.bind(cell);
485
511
  outer === detached || ruleStops.set(cell, outer.release(stop));
512
+ cell.flags |= 64 /* Stateful */;
486
513
  cell.setQ(q);
487
514
  return stop;
488
515
  }
489
516
  }
490
517
  function unchangedIf(newVal, equals = arrayEq) {
491
- const { cell } = current;
518
+ const cell = currentCell;
492
519
  if (cell) {
493
520
  return cell.flags & 16 /* Error */ || !equals(cell.value, newVal) ? newVal : cell.value;
494
521
  } else {
@@ -649,27 +676,13 @@ function cached(compute, initVal) {
649
676
  );
650
677
  }
651
678
  function peek(fn, ...args) {
652
- if (!current.cell)
653
- return fn(...args);
654
- const old = swapCtx(makeCtx(current.job));
655
- try {
656
- return fn(...args);
657
- } finally {
658
- freeCtx(swapCtx(old));
659
- }
679
+ return currentCell ? currentCell.peek(fn, null, args) : fn(...args);
660
680
  }
661
681
  function action(fn, _ctx, desc) {
662
682
  if (desc)
663
683
  return { ...desc, value: action(desc.value) };
664
- return function() {
665
- if (!current.cell)
666
- return apply(fn, this, arguments);
667
- const old = swapCtx(makeCtx(current.job));
668
- try {
669
- return apply(fn, this, arguments);
670
- } finally {
671
- freeCtx(swapCtx(old));
672
- }
684
+ return function(...args) {
685
+ return currentCell ? currentCell.peek(fn, this, args) : apply(fn, this, args);
673
686
  };
674
687
  }
675
688
  function until(source) {
@@ -683,4 +696,3 @@ function waitTruthy(job, v) {
683
696
  }
684
697
 
685
698
  export { CircularDependency, ConfigurableImpl, SignalImpl, WritableImpl, WriteConflict, action, cached, peek, rule, runRules, unchangedIf, until, value };
686
- //# sourceMappingURL=signals.mjs.map
@@ -79,6 +79,9 @@ const CallableObject = /* @__PURE__ */ (() => {
79
79
  return CallableObject2;
80
80
  })();
81
81
  const apply = Reflect.apply;
82
+ function call(fn, thisArg, ...args) {
83
+ return thisArg ? apply(fn, thisArg, args) : args.length ? fn(...args) : fn();
84
+ }
82
85
  const GeneratorBase = /* @__PURE__ */ (() => {
83
86
  function G() {
84
87
  }
@@ -90,5 +93,4 @@ function isGeneratorFunction(fn) {
90
93
  return isFunction(fn) && fn.prototype instanceof GeneratorBase;
91
94
  }
92
95
 
93
- export { CallableObject as C, GeneratorBase as G, isGeneratorFunction as a, decorateMethod as b, apply as c, defer as d, batch as e, arrayEq as f, isArray as g, isFunction as i, setMap as s };
94
- //# sourceMappingURL=utils-DnMz1K-o.mjs.map
96
+ export { CallableObject as C, GeneratorBase as G, isGeneratorFunction as a, decorateMethod as b, apply as c, defer as d, batch as e, arrayEq as f, isArray as g, call as h, isFunction as i, setMap as s };
package/dist/utils.d.ts CHANGED
@@ -106,6 +106,7 @@ declare function decorateMethod<F extends AnyFunction, D extends {
106
106
  * Shorthand for Array.isArray()
107
107
  *
108
108
  * @category Data Structures
109
+ * @function
109
110
  */
110
111
  declare const isArray: (arg: any) => arg is any[];
111
112
  /**
@@ -169,13 +170,25 @@ declare const CallableObject: new <T extends AnyFunction>(fn: T) => T;
169
170
  * elements of given array as the arguments. (Shorthand for Reflect.apply)
170
171
  *
171
172
  * @category Functions and Decorators
173
+ * @function
172
174
  */
173
175
  declare const apply: typeof Reflect.apply;
176
+ /**
177
+ * Like `fn.call(thisArg, ...args)`, but monomorphic, and the `thisArg`
178
+ * parameter can be omitted or null.
179
+ *
180
+ * This is mostly useful as syntax sugar for an immediately-evaluated function
181
+ * expression, replacing `(() => {...})()` with `call(() => {...})`.
182
+ *
183
+ * @category Functions and Decorators
184
+ */
185
+ declare function call<F extends AnyFunction>(fn: F, thisArg?: ThisParameterType<F>, ...args: Parameters<F>): ReturnType<F>;
174
186
  /**
175
187
  * A pseudo-constructor for the abstract ancestor type of all generators,
176
188
  * useful for testing whether something is `instanceof Generator`.
177
189
  *
178
190
  * @category Data Structures
191
+ * @class
179
192
  */
180
193
  declare const GeneratorBase: abstract new () => Generator;
181
194
  /**
@@ -185,4 +198,4 @@ declare const GeneratorBase: abstract new () => Generator;
185
198
  */
186
199
  declare function isGeneratorFunction<G extends Generator<any, any, any> = Generator>(fn: any): fn is (this: any, ...args: any[]) => G;
187
200
 
188
- export { type Batch, CallableObject, GeneratorBase, apply, arrayEq, batch, decorateMethod, isArray, isFunction, isGeneratorFunction, setMap };
201
+ export { type Batch, CallableObject, GeneratorBase, apply, arrayEq, batch, call, decorateMethod, isArray, isFunction, isGeneratorFunction, setMap };
package/dist/utils.mjs CHANGED
@@ -1,2 +1 @@
1
- export { C as CallableObject, G as GeneratorBase, c as apply, f as arrayEq, e as batch, b as decorateMethod, g as isArray, i as isFunction, a as isGeneratorFunction, s as setMap } from './utils-DnMz1K-o.mjs';
2
- //# sourceMappingURL=utils.mjs.map
1
+ export { C as CallableObject, G as GeneratorBase, c as apply, f as arrayEq, e as batch, h as call, b as decorateMethod, g as isArray, i as isFunction, a as isGeneratorFunction, s as setMap } from './utils-cyEhnyp7.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uneventful",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "Declarative, event-driven reactivity: signals, streams, structured concurrency, and easy resource cleanup",
5
5
  "keywords": [
6
6
  "signals",
@@ -26,6 +26,12 @@
26
26
  "default": "./dist/mod.mjs"
27
27
  }
28
28
  },
29
+ "./ext": {
30
+ "import": {
31
+ "types": "./dist/ext.d.ts",
32
+ "default": "./dist/ext.mjs"
33
+ }
34
+ },
29
35
  "./shared": {
30
36
  "import": {
31
37
  "types": "./dist/shared.d.ts",
@@ -47,11 +53,12 @@
47
53
  },
48
54
  "scripts": {
49
55
  "coverage": "c8 -r lcov mocha -n loader=ts-node/esm",
50
- "docs": "tsx typedoc/run.mts",
56
+ "docs": "typedoc",
51
57
  "watch": "mocha -r tsx --watch",
52
- "watch-docs": "onchange -i --kill \"*.md\" \"guides/**/*.md\" \"typedoc.config.*\" \"typedoc/*\" -- tsx typedoc/run.mts --watch",
58
+ "watch-docs": "typedoc --watch",
59
+ "watch-prep": "pkgroll --watch",
53
60
  "test": "mocha -r tsx && pkgroll",
54
- "prepare": "pkgroll --sourcemap --clean-dist"
61
+ "prepare": "pkgroll --clean-dist"
55
62
  },
56
63
  "mocha": {
57
64
  "spec": "specs/*.spec.ts",
@@ -62,28 +69,31 @@
62
69
  "README.md"
63
70
  ]
64
71
  },
72
+ "ts-node": {
73
+ "esm": true,
74
+ "transpileOnly": true,
75
+ "experimentalSpecifierResolution": "node"
76
+ },
65
77
  "packageManager": "pnpm@7.33.6",
66
78
  "devDependencies": {
67
- "@knodes/typedoc-plugin-pages": "^0.23.4",
68
- "@knodes/typedoc-pluginutils": "^0.23.4",
69
79
  "@types/chai": "^4.3.10",
70
80
  "@types/chai-as-promised": "^7.1.8",
71
81
  "@types/mocha": "^10.0.4",
72
82
  "@types/node": "^16",
73
83
  "@types/sinon": "^17.0.1",
74
84
  "@types/sinon-chai": "^3.2.12",
75
- "c8": "^8.0.1",
85
+ "c8": "^10",
76
86
  "chai": "^4.3.10",
77
87
  "chai-as-promised": "^7.1.1",
78
88
  "mocha": "^10.2.0",
79
89
  "monkey-around": "^3",
80
- "onchange": "^7.1.0",
81
90
  "pkgroll": "^2.1.1",
82
91
  "sinon": "^17.0.1",
83
92
  "sinon-chai": "^3.7.0",
93
+ "ts-expect": "^1.3.0",
84
94
  "ts-node": "^10.9.1",
85
- "tsx": "^4.1.3",
86
- "typedoc": "^0.25.13",
95
+ "tsx": "^4.6.1",
96
+ "typedoc": "0.28.1",
87
97
  "typescript": "^5.4.5"
88
98
  }
89
99
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"call-or-wait-DORP7nkp.mjs","sources":["../src/streams.ts","../src/call-or-wait.ts"],"sourcesContent":["import { pulls } from \"./internals.ts\";\nimport { DisposeFn, Job } from \"./types.ts\";\nimport { getJob } from \"./tracking.ts\";\nimport { current } from \"./ambient.ts\";\nimport type { Signal } from \"./signals.ts\"; // needed for documentation links\n\n/**\n * A backpressure controller: returns true if downstream is ready to accept\n * data.\n *\n * @param cb (optional) - a callback to run when the downstream consumer wishes\n * to resume event production (i.e., when a sink calls\n * {@link Throttle.resume}()). The callback is automatically unregistered when\n * invoked, so the producer must re-register it after each call if it wishes to\n * keep being called.\n *\n * @category Types and Interfaces\n */\nexport type Backpressure = (cb?: () => any) => boolean\n\n\n/**\n * Create a backpressure control function for the given connection\n *\n * @category Stream Producers\n */\nexport function backpressure(inlet: Inlet = defaultInlet): Backpressure {\n const job = getJob();\n return (cb?: Flush) => {\n if (!job.result() && inlet.isOpen()) {\n if (cb) inlet.onReady(cb, job);\n return inlet.isReady();\n }\n return false;\n }\n}\n\n/**\n * Control backpressure for listening streams. This interface is the API\n * internal to the implementation of {@link backpressure}(). Unless you're\n * implementing a backpressurable stream yourself, see the {@link Throttle}\n * interface instead.\n *\n * @category Types and Interfaces\n */\nexport interface Inlet {\n /** Is the main connection open? (i.e. is the creating job not closed yet?) */\n isOpen(): boolean\n\n /** Is the connection ready to receive data? */\n isReady(): boolean\n\n /**\n * Register a callback to produce more data when the inlet is resumed\n * (The callback is unregistered if the supplied job ends.)\n */\n onReady(cb: () => any, job: Job): this;\n}\n\n/**\n * Control backpressure for listening streams\n *\n * Obtain instances via {@link throttle}(), then pass them into the appropriate\n * stream-consuming API. (e.g. {@link connect}).\n *\n * @category Types and Interfaces\n */\nexport interface Throttle extends Inlet {\n /** Set inlet status to \"paused\". */\n pause(): void;\n\n /**\n * Un-pause, and iterate backpressure-able sources' onReady callbacks to\n * resume sending immediately. (i.e., synchronously!)\n */\n resume(): void;\n}\n\n/**\n * A Connection is a job that returns void when the connected stream ends\n * itself. If the stream doesn't end itself (e.g. it's an event listener), the\n * job will never return, and only end with a cancel or throw.\n *\n * @category Types and Interfaces\n */\nexport type Connection = Job<void>;\n\n/**\n * A Source is a function that can be called to arrange for data to be\n * produced and sent to a {@link Sink} function for consumption, until the\n * associated {@link Connection} is closed (either by the source or the sink,\n * e.g. if the sink doesn't want more data or the source has no more to send).\n *\n * If the source is a backpressurable stream, it can use the (optional) supplied\n * inlet (usually a {@link throttle}()) to rate-limit its output.\n *\n * A producer function *must* return the special {@link IsStream} value, so\n * TypeScript can tell what functions are usable as sources. (Otherwise any\n * void function with no arguments would appear to be usable as a source!)\n *\n * @category Types and Interfaces\n */\nexport interface Source<T> {\n /** Subscribe sink to receive values */\n (sink: Sink<T>, conn?: Connection, inlet?: Throttle | Inlet): typeof IsStream;\n}\n\n/**\n * An uneventful stream is either a {@link Source} or a {@link SignalSource}.\n * (Signals actually implement the {@link Source} interface as an overload, but\n * TypeScript gets confused about that sometimes, so we generally declare our\n * stream *inputs* as `Stream<T>` and our stream *outputs* as {@link Source}, so\n * that TypeScript knows what's what.\n *\n * @category Types and Interfaces\n */\nexport type Stream<T> = Source<T> | SignalSource<T>;\n\n/**\n * The call signatures implemented by signals. (They can be used as sources, or\n * called with no arguments to return a value.)\n *\n * This type is needed because TypeScript won't infer the overloads of\n * {@link Signal} correctly otherwise. (Specifically, it won't allow it to be\n * used as a zero-agument function.)\n *\n * @category Types and Interfaces\n*/\nexport type SignalSource<T> = Source<T> & {\n /** A signal object can be called to get its current value */\n (): T\n}\n\n/**\n * A specially-typed string used to verify that a function supports uneventful's\n * streaming protocol. Return it from a function to implement the\n * {@link Source} type.\n *\n * @category Types and Interfaces\n */\nexport const IsStream = \"uneventful/is-stream\" as const;\n\n/**\n * A `Sink` is a function that receives data from a {@link Stream}.\n *\n * @category Types and Interfaces\n */\nexport type Sink<T> = (val: T) => void;\n\n/**\n * A `Transformer` is a function that takes one stream and returns another,\n * possibly one that produces data of a different type. Most operator functions\n * return a transformer, allowing them to be combined via {@link pipe}().\n *\n * @category Types and Interfaces\n */\nexport type Transformer<T, V=T> = (input: Stream<T>) => Source<V>;\n\ntype Flush = () => any\n\n\n/**\n * Subscribe a sink to a stream, returning a nested job. (Shorthand for\n * .{@link Job.connect connect}(...) on the active job.)\n *\n * @param src An event source or signal\n * @param sink A callback that will receive the events\n * @param inlet Optional - a {@link throttle}() to control backpressure\n *\n * @returns A job that can be aborted to end the subscription, and which will\n * end naturally (with a void return or error) if the stream ends itself.\n *\n * @category Stream Consumers\n */\nexport function connect<T>(src: Stream<T>, sink: Sink<T>, inlet?: Throttle | Inlet): Connection {\n return getJob().connect(src, sink, inlet);\n}\n\n/**\n * Create a backpressure controller for a stream. Pass it to one or more\n * sources you're connecting to, and if they support backpressure they'll\n * respond when you call its .pause() and .resume() methods.\n *\n * @param job - Optional: a job that controls readiness. (The throttle will\n * pause indefinitely when the job ends.) Defaults to the currently-active job,\n * but unlike most such defaults, it won't throw if no job is active.\n *\n * @category Stream Consumers\n */\nexport function throttle(job: Job = current.job): Throttle {\n return new _Throttle(job);\n}\n\nclass _Throttle implements Throttle {\n /** @internal */\n protected _callbacks: Map<Flush, DisposeFn> = undefined;\n\n /** @internal */\n constructor(protected _job?: Job) {}\n\n isOpen(): boolean { return !this._job?.result(); }\n\n /** Is the connection ready to receive data? */\n isReady(): boolean { return this.isOpen() && this._isReady; }\n\n _isReady = true;\n _isPulling = false;\n\n onReady(cb: Flush, job: Job) {\n if (!this.isOpen()) return this;\n const _callbacks = (this._callbacks ||= new Map);\n const unlink = job.release(() => _callbacks.delete(cb));\n if (this.isReady() && this && !_callbacks.size) {\n pulls.add(this);\n }\n _callbacks.set(cb, unlink);\n return this;\n }\n\n pause() { this._isReady = false; return this; }\n\n doPull() {\n if (this._isPulling) return;\n const {_callbacks} = this;\n if (!_callbacks?.size) return;\n this._isPulling = true;\n try {\n for(let [cb, unlink] of _callbacks) {\n if (!this.isReady()) break; // we're done\n unlink()\n _callbacks.delete(cb);\n cb() // XXX error handling?\n }\n } finally {\n this._isPulling = false;\n }\n }\n\n resume() {\n if (this.isOpen()) {\n this._isReady = true;\n this.doPull();\n }\n }\n}\n\nconst defaultInlet: Inlet = throttle();\n\n/**\n * Pipe a stream (or anything else) through a series of single-argument\n * functions/operators\n *\n * e.g. the following creates a stream that outputs 4 and then 6:\n *\n * ```ts\n * pipe(fromIterable([1,2,3,4]), skip(1), take(2), map(x => x*2))\n * ```\n *\n * The first argument to pipe() can be any value, but all other arguments must\n * be functions. The value is passed to the first function, and then the result\n * is passed to the next function in turn, until all provided functions have\n * been called with the result of the previous function. The return value is\n * the last result, or the original value if no functions were given.\n *\n * The underlying implementation of pipe() works with any number of arguments,\n * but due to TypeScript limitations we only have typing defined for a max of 9\n * functions (10 arguments total). If you need more than 9 functions, you can\n * stack some of them with {@link compose}(), e.g.:\n *\n * ```typescript\n * pipe(\n * aStream,\n * compose(op1, op2, ...),\n * compose(op10, op11, ...),\n * compose(op19, ...),\n * ...\n * )\n * ```\n *\n * @category Stream Operators\n */\nexport function pipe<A,B,C,D,E,F,G,H,I,J>(input: A, ...fns: Chain9<A,J,B,C,D,E,F,G,H,I>): J\nexport function pipe<A,B,C,D,E,F,G,H,I> (input: A, ...fns: Chain8<A,I,B,C,D,E,F,G,H>): I\nexport function pipe<A,B,C,D,E,F,G,H> (input: A, ...fns: Chain7<A,H,B,C,D,E,F,G>): H\nexport function pipe<A,B,C,D,E,F,G> (input: A, ...fns: Chain6<A,G,B,C,D,E,F>): G\nexport function pipe<A,B,C,D,E,F> (input: A, ...fns: Chain5<A,F,B,C,D,E>): F\nexport function pipe<A,B,C,D,E> (input: A, ...fns: Chain4<A,E,B,C,D>): E\nexport function pipe<A,B,C,D> (input: A, ...fns: Chain3<A,D,B,C>): D\nexport function pipe<A,B,C> (input: A, ...fns: Chain2<A,C,B>): C\nexport function pipe<A,B> (input: A, ...fns: Chain1<A,B>): B\nexport function pipe<A> (input: A): A\nexport function pipe(input: any, ...fns: Array<(v: any) => any>): any;\nexport function pipe<A,X>(): X {\n var v = arguments[0];\n for (var i=1; i<arguments.length; i++) v = arguments[i](v);\n return v;\n}\n\n/**\n * Compose a series of single-argument functions/operators in application order.\n * (This is basically a deferred version of {@link pipe}().) For example:\n *\n * ```ts\n * const func = compose(skip(1), take(2), map(x => x*2));\n * const stream_4_6 = func(fromIterable([1,2,3,4])); // stream that outputs 4, 6\n * ```\n *\n * As with `pipe()`, the declared typings only support composing up to 9\n * functions at once; if you need more you'll need to nest calls to `compose()`\n * (i.e. passing the result of a `compose()` as an argument to another\n * `compose()` call.)\n *\n * @returns A function taking the same type as the first input function,\n * returning the same type as the last input function.\n *\n * @category Stream Operators\n */\nexport function compose<A,B,C,D,E,F,G,H,I,J>(...fns: Chain9<A,J,B,C,D,E,F,G,H,I>): (a: A) => J\nexport function compose<A,B,C,D,E,F,G,H,I> (...fns: Chain8<A,I,B,C,D,E,F,G,H>): (a: A) => I\nexport function compose<A,B,C,D,E,F,G,H> (...fns: Chain7<A,H,B,C,D,E,F,G>): (a: A) => H\nexport function compose<A,B,C,D,E,F,G> (...fns: Chain6<A,G,B,C,D,E,F>): (a: A) => G\nexport function compose<A,B,C,D,E,F> (...fns: Chain5<A,F,B,C,D,E>): (a: A) => F\nexport function compose<A,B,C,D,E> (...fns: Chain4<A,E,B,C,D>): (a: A) => E\nexport function compose<A,B,C,D> (...fns: Chain3<A,D,B,C>): (a: A) => D\nexport function compose<A,B,C> (...fns: Chain2<A,C,B>): (a: A) => C\nexport function compose<A,B> (...fns: Chain1<A,B>): (a: A) => B\nexport function compose<A> (): (a: A) => A\nexport function compose(...fns: ((v:any)=>any)[]) {\n return (val:any) => (pipe as any)(val, ...fns);\n}\n\ntype Chain1<A,R> = [(v: A) => R];\ntype Chain2<A,R,B> = [...Chain1<A,B>, ...Chain1<B,R>];\ntype Chain3<A,R,B,C> = [...Chain1<A,B>, ...Chain2<B,R,C>];\ntype Chain4<A,R,B,C,D> = [...Chain1<A,B>, ...Chain3<B,R,C,D>];\ntype Chain5<A,R,B,C,D,E> = [...Chain1<A,B>, ...Chain4<B,R,C,D,E>];\ntype Chain6<A,R,B,C,D,E,F> = [...Chain1<A,B>, ...Chain5<B,R,C,D,E,F>];\ntype Chain7<A,R,B,C,D,E,F,G> = [...Chain1<A,B>, ...Chain6<B,R,C,D,E,F,G>];\ntype Chain8<A,R,B,C,D,E,F,G,H> = [...Chain1<A,B>, ...Chain7<B,R,C,D,E,F,G,H>];\ntype Chain9<A,R,B,C,D,E,F,G,H,I> = [...Chain1<A,B>, ...Chain8<B,R,C,D,E,F,G,H,I>];\n\n/**\n * Pass subscriber into a stream (or any arguments into any other function).\n *\n * This utility is mainly here for uses like:\n *\n * - `pipe(src, into(sink))`,\n * - `pipe(src, into(sink, conn))`,\n * - `pipe(src, into(restarting(sink)))`, etc.\n *\n * but can also be used for argument currying generally.\n *\n * @param args The arguments to pass to the stream (or other function)\n *\n * @returns a function that takes another function and calls it with the given args.\n *\n * @category Stream Consumers\n */\nexport function into<In extends any[], Out>(...args: In): (src: (...args: In) => Out) => Out {\n return src => src(...args);\n}\n","import { Job, Yielding } from \"./types.ts\";\nimport { start } from \"./jobutils.ts\";\nimport { isValue, isError, markHandled } from \"./results.ts\";\nimport { isFunction } from \"./utils.ts\";\nimport { connect, Source } from \"./streams.ts\";\n\nexport function callOrWait<T>(\n source: any, method: string, handler: (job: Job<T>, val: T) => void, noArgs: (f?: any) => Yielding<T> | void\n) {\n if (source && isFunction(source[method])) return source[method]() as Yielding<T>;\n if (isFunction(source)) return (\n source.length === 0 ? noArgs(source) : false\n ) || start<T>(job => {\n connect(source as Source<T>, v => handler(job, v)).do(r => {\n if (isValue(r)) job.throw(new Error(\"Stream ended\"));\n else if (isError(r)) job.throw(markHandled(r));\n });\n });\n mustBeSourceOrSignal();\n}\n\nexport function mustBeSourceOrSignal() { throw new TypeError(\"not a source or signal\"); }\n"],"names":[],"mappings":"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"}