uneventful 0.0.12 → 0.0.13

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.
@@ -1,4 +1,4 @@
1
- import { e as batch, d as defer, i as isFunction, G as GeneratorBase, c as apply } from './utils-cyEhnyp7.mjs';
1
+ import { h as batch, d as defer, i as isFunction, G as GeneratorBase, c as apply } from './utils-BgqyDPjA.mjs';
2
2
 
3
3
  function resolve(request, val) {
4
4
  request("next", val);
@@ -186,7 +186,7 @@ function getJob() {
186
186
  }
187
187
  function recalcJob(job) {
188
188
  return (cb) => {
189
- currentJob.must(job.release(cb));
189
+ getJob().must(job.release(cb));
190
190
  };
191
191
  }
192
192
  function runChain(res, cbs) {
@@ -195,14 +195,14 @@ function runChain(res, cbs) {
195
195
  try {
196
196
  cb(res);
197
197
  } catch (e) {
198
- _detached.asyncThrow(e);
198
+ root.asyncThrow(e);
199
199
  }
200
200
  cbs && recycle(cbs);
201
201
  return void 0;
202
202
  }
203
203
  var inProcess = /* @__PURE__ */ new Set();
204
204
  class _Job {
205
- constructor() {
205
+ constructor(parent, stop) {
206
206
  this.end = () => {
207
207
  const res = this._done ||= CancelResult, cbs = this._cbs;
208
208
  if (!cbs && !isUnhandled(res))
@@ -212,7 +212,7 @@ class _Job {
212
212
  if (!ct)
213
213
  inProcess.add(null);
214
214
  if (cbs && cbs.u)
215
- cbs.u = runChain(res, cbs.u);
215
+ cbs.u = (runChain(res, cbs.u), void 0);
216
216
  inProcess.add(this);
217
217
  if (ct) {
218
218
  popCtx();
@@ -221,7 +221,7 @@ class _Job {
221
221
  inProcess.delete(null);
222
222
  for (const item of inProcess) {
223
223
  if (item._cbs)
224
- item._cbs = runChain(item._done, item._cbs);
224
+ item._cbs = (runChain(item._done, item._cbs), void 0);
225
225
  inProcess.delete(item);
226
226
  if (isUnhandled(item._done))
227
227
  item.throw(markHandled(item._done));
@@ -231,15 +231,10 @@ class _Job {
231
231
  this._done = void 0;
232
232
  // Chain whose .u stores a second chain for `release()` callbacks
233
233
  this._cbs = void 0;
234
- }
235
- /** @internal */
236
- static create(parent, stop) {
237
- const job = new _Job();
238
234
  if (parent || stop) {
239
- job.must((parent ||= getJob()).release(stop || job.end));
240
- owners.set(job, parent);
235
+ this.must((parent ||= getJob()).release(stop || this.end));
236
+ parent === root || owners.set(this, parent);
241
237
  }
242
- return job;
243
238
  }
244
239
  do(cleanup) {
245
240
  unshift(this._chain(), cleanup);
@@ -285,14 +280,17 @@ class _Job {
285
280
  _end(res) {
286
281
  if (this._done)
287
282
  throw new Error("Job already ended");
288
- if (this !== _detached)
289
- this._done = res;
283
+ this._done = res;
290
284
  this.end();
291
285
  return this;
292
286
  }
293
287
  throw(err) {
294
288
  if (this._done) {
295
- (owners.get(this) || _detached).asyncThrow(err);
289
+ const parent = owners.get(this) || root;
290
+ if (parent && parent !== this)
291
+ parent.asyncThrow(err);
292
+ else
293
+ defaultCatch(err);
296
294
  return this;
297
295
  }
298
296
  return this._end(ErrorResult(err));
@@ -319,7 +317,7 @@ class _Job {
319
317
  }
320
318
  start(fnOrCtx, fn) {
321
319
  if (!fnOrCtx)
322
- return makeJob(this);
320
+ return new _Job(this);
323
321
  let init, result;
324
322
  if (isFunction(fn)) {
325
323
  init = fn.bind(fnOrCtx);
@@ -330,7 +328,7 @@ class _Job {
330
328
  } else {
331
329
  result = fnOrCtx;
332
330
  }
333
- const job = makeJob(this);
331
+ const job = new _Job(this);
334
332
  try {
335
333
  if (init)
336
334
  result = job.run(init, job);
@@ -400,8 +398,6 @@ class _Job {
400
398
  return this;
401
399
  }
402
400
  release(cleanup) {
403
- if (this === _detached)
404
- return noop;
405
401
  let cbs = this._chain();
406
402
  if (!this._done || cbs.u)
407
403
  cbs = cbs.u ||= chain();
@@ -411,7 +407,7 @@ class _Job {
411
407
  try {
412
408
  (catchers.get(this) || this.throw).call(this, err);
413
409
  } catch (e) {
414
- if (this === _detached)
410
+ if (this === root)
415
411
  catchers.set(this, defaultCatch);
416
412
  else
417
413
  catchers.delete(this);
@@ -429,8 +425,6 @@ class _Job {
429
425
  return this;
430
426
  }
431
427
  _chain() {
432
- if (this === _detached)
433
- this.end();
434
428
  if (this._done && isEmpty(this._cbs))
435
429
  defer(this.end);
436
430
  return this._cbs ||= chain();
@@ -441,26 +435,23 @@ function nativePromise(job) {
441
435
  if (!promises.has(job)) {
442
436
  promises.set(job, new Promise((res, rej) => {
443
437
  const toPromise = fulfillPromise.bind(null, res, rej);
444
- if (job.result())
445
- toPromise(job.result());
438
+ const result = job.result();
439
+ if (result)
440
+ toPromise(result);
446
441
  else
447
442
  job.do(toPromise);
448
443
  }));
449
444
  }
450
445
  return promises.get(job);
451
446
  }
452
- const makeJob = _Job.create;
453
- const _detached = makeJob();
454
- const detached = _detached;
455
- _detached.end = () => {
456
- throw new Error("Can't do that with the detached job");
457
- };
458
- _detached.asyncCatch(defaultCatch);
447
+ function makeJob(parent, stop) {
448
+ return new _Job(parent, stop);
449
+ }
459
450
  let root;
460
451
  newRoot();
461
452
  function newRoot() {
462
453
  root?.end();
463
- const job = root = makeJob().asyncCatch((e) => _detached.asyncThrow(e));
454
+ const job = root = new _Job().asyncCatch(defaultCatch);
464
455
  job.release(() => root === job && (root = null));
465
456
  return root;
466
457
  }
@@ -527,6 +518,95 @@ function runGen(g, job) {
527
518
  }
528
519
  }
529
520
 
521
+ function backpressure(inlet = defaultInlet) {
522
+ const job = getJob();
523
+ return (cb) => {
524
+ if (!job.result() && inlet.isOpen()) {
525
+ if (cb)
526
+ inlet.onReady(cb, job);
527
+ return inlet.isReady();
528
+ }
529
+ return false;
530
+ };
531
+ }
532
+ const IsStream = "uneventful/is-stream";
533
+ function connect(src, sink, inlet) {
534
+ return getJob().connect(src, sink, inlet);
535
+ }
536
+ function throttle(job = currentJob) {
537
+ return new _Throttle(job);
538
+ }
539
+ class _Throttle {
540
+ /** @internal */
541
+ constructor(_job) {
542
+ this._job = _job;
543
+ /** @internal */
544
+ this._callbacks = void 0;
545
+ this._isReady = true;
546
+ this._isPulling = false;
547
+ }
548
+ isOpen() {
549
+ return !this._job?.result();
550
+ }
551
+ /** Is the connection ready to receive data? */
552
+ isReady() {
553
+ return this.isOpen() && this._isReady;
554
+ }
555
+ onReady(cb, job) {
556
+ if (!this.isOpen())
557
+ return this;
558
+ const _callbacks = this._callbacks ||= /* @__PURE__ */ new Map();
559
+ const unlink = job.release(() => _callbacks.delete(cb));
560
+ if (this.isReady() && this && !_callbacks.size) {
561
+ pulls.add(this);
562
+ }
563
+ _callbacks.set(cb, unlink);
564
+ return this;
565
+ }
566
+ pause() {
567
+ this._isReady = false;
568
+ return this;
569
+ }
570
+ doPull() {
571
+ if (this._isPulling)
572
+ return;
573
+ const { _callbacks } = this;
574
+ if (!_callbacks?.size)
575
+ return;
576
+ this._isPulling = true;
577
+ try {
578
+ for (let [cb, unlink] of _callbacks) {
579
+ if (!this.isReady())
580
+ break;
581
+ unlink();
582
+ _callbacks.delete(cb);
583
+ cb();
584
+ }
585
+ } finally {
586
+ this._isPulling = false;
587
+ }
588
+ }
589
+ resume() {
590
+ if (this.isOpen()) {
591
+ this._isReady = true;
592
+ this.doPull();
593
+ }
594
+ }
595
+ }
596
+ const defaultInlet = throttle();
597
+ function pipe() {
598
+ var v = arguments[0];
599
+ for (var i = 1; i < arguments.length; i++)
600
+ v = arguments[i](v);
601
+ return v;
602
+ }
603
+ function compose(...fns) {
604
+ return (val) => pipe(val, ...fns);
605
+ }
606
+ function into(...args) {
607
+ return (src) => src(...args);
608
+ }
609
+
530
610
  function must(cleanup) {
531
611
  getJob().must(cleanup);
532
612
  }
@@ -534,7 +614,7 @@ function start(init, fn) {
534
614
  return getJob().start(init, fn);
535
615
  }
536
616
  function isJobActive() {
537
- return !!currentJob;
617
+ return !!(currentJob || currentCell?.isObserved());
538
618
  }
539
619
  const timers = /* @__PURE__ */ new WeakMap();
540
620
  function timeout(ms = 0, job = getJob()) {
@@ -573,7 +653,7 @@ function abortSignal(job = getJob()) {
573
653
  return signal;
574
654
  }
575
655
  function restarting(task2) {
576
- const outer = getJob(), inner = makeJob(outer), { end } = inner;
656
+ const outer = getJob(), inner = outer.start(), { end } = inner;
577
657
  task2 ||= (f) => {
578
658
  inner.must(f());
579
659
  };
@@ -599,4 +679,4 @@ function task(fn, _ctx, desc) {
599
679
  };
600
680
  }
601
681
 
602
- export { timeout as A, abortSignal as B, CancelResult as C, task as D, ErrorResult as E, pushCtx as F, popCtx as G, currentJob as H, pulls as I, ValueResult as V, rejecter as a, resolve as b, root as c, isUnhandled as d, markHandled as e, isError as f, getJob as g, fulfillPromise as h, isCancel as i, restarting as j, currentCell as k, isValue as l, must as m, noop as n, reject as o, isHandled as p, getResult as q, resolver as r, start as s, propagateResult as t, CancelError as u, nativePromise as v, makeJob as w, detached as x, newRoot as y, isJobActive as z };
682
+ export { CancelError as A, pipe as B, CancelResult as C, compose as D, ErrorResult as E, into as F, isJobActive as G, timeout as H, IsStream as I, abortSignal as J, task as K, currentJob as L, pushCtx as M, popCtx as N, ValueResult as V, _Job as _, rejecter as a, resolve as b, backpressure as c, root as d, isUnhandled as e, markHandled as f, getJob as g, isError as h, isCancel as i, connect as j, fulfillPromise as k, restarting as l, must as m, noop as n, currentCell as o, isValue as p, nativePromise as q, resolver as r, start as s, throttle as t, makeJob as u, newRoot as v, reject as w, isHandled as x, getResult as y, propagateResult as z };
package/dist/mod.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { J as Job, C as CleanupFn, Y as Yielding, S as Source, D as DisposeFn, a as Stream, B as Backpressure, T as Transformer, b as Sink, O as OptionalCleanup, c as StartFn, d as StartObj, A as AnyFunction } from './types-N2ua11te.js';
2
- export { e as AsyncStart, z as CancelError, m as CancelResult, K as Connection, E as ErrorResult, H as HandledError, I as Inlet, M as IsStream, g as JobIterator, o as JobResult, N as Nothing, P as PlainFunction, i as RecalcSource, R as Request, L as SignalSource, h as Suspend, f as SyncStart, G as Throttle, U as UnhandledError, V as ValueResult, F as backpressure, Z as compose, Q as connect, x as fulfillPromise, w as getResult, _ as into, p as isCancel, s as isError, u as isHandled, t as isUnhandled, q as isValue, v as markHandled, n as noop, X as pipe, y as propagateResult, j as reject, l as rejecter, r as resolve, k as resolver, W as throttle } from './types-N2ua11te.js';
3
- export { a as Each, E as EachResult, N as NextMethod, U as UntilMethod, e as each, f as forEach, i as isObserved, n as next, r as recalcWhen } from './sinks-5TuxCRtX.js';
1
+ import { J as Job, C as CleanupFn, Y as Yielding, S as Source, D as DisposeFn, a as Stream, B as Backpressure, T as Transformer, b as Sink, O as OptionalCleanup, c as StartFn, d as StartObj, A as AnyFunction } from './types-pElgImr7.js';
2
+ export { e as AsyncStart, z as CancelError, m as CancelResult, K as Connection, E as ErrorResult, H as HandledError, I as Inlet, M as IsStream, g as JobIterator, o as JobResult, N as Nothing, P as PlainFunction, i as RecalcSource, R as Request, L as SignalSource, h as Suspend, f as SyncStart, G as Throttle, U as UnhandledError, V as ValueResult, F as backpressure, Z as compose, Q as connect, x as fulfillPromise, w as getResult, _ as into, p as isCancel, s as isError, u as isHandled, t as isUnhandled, q as isValue, v as markHandled, n as noop, X as pipe, y as propagateResult, j as reject, l as rejecter, r as resolve, k as resolver, W as throttle } from './types-pElgImr7.js';
3
+ export { a as Each, E as EachResult, N as NextMethod, U as UntilMethod, e as each, f as forEach, i as isObserved, n as next, r as recalcWhen } from './sinks-B_LfTZgF.js';
4
4
 
5
5
  /**
6
6
  * Invoke a no-argument function as a microtask, using queueMicrotask or Promise.resolve().then()
@@ -35,61 +35,23 @@ declare function getJob<T = unknown>(): Job<T>;
35
35
  */
36
36
  declare function nativePromise<T>(job: Job<T>): Promise<T>;
37
37
  /**
38
- * Return a new {@link Job}. If *either* a parent parameter or stop function
39
- * are given, the new job is linked to the parent.
38
+ * This function is deprecated. Please move to using `.start()` instead, as
39
+ * shown:
40
40
  *
41
- * @remarks You should generally use `start()`, `parent.start()` or
42
- * `root.start()` instead of this, unless you're creating a special kind of job
43
- * that needs a custom stop function.
41
+ * | Old | New |
42
+ * | --- | --- |
43
+ * | `makeJob()`<br>`makeJob(null/undefined)` | `root.start()` |
44
+ * | `makeJob(parent)` | `parent.start()` |
44
45
  *
45
- * @param parent The parent job to which the new job should be attached.
46
- * Defaults to the currently-active job if none given (assuming a stop parameter
47
- * is provided).
48
- *
49
- * @param stop The function to call to destroy the nested job. Defaults to the
50
- * {@link Job.end} method of the new job if none is given (assuming a parent
51
- * parameter is provided).
52
- *
53
- * @returns A new job. The job is a child job if any arguments are given, or
54
- * a detached (parentless) job otherwise.
55
- *
56
- * @category Jobs
57
- * @function
58
- */
59
- declare const makeJob: <T>(parent?: Job, stop?: CleanupFn) => Job<T>;
60
- /**
61
- * @deprecated Use {@link root} instead. (Including uses of
62
- * `.asyncCatch()` to set the default async error handling policy.)
63
- *
64
- * ---
65
- * A special {@link Job} with no parents, that can be used to create standalone
66
- * jobs. detached.start() returns a new detached job, detached.run() can be
67
- * used to run code that expects to create a child job, and detached.bind() can
68
- * wrap a function to work without a parent job.
69
- *
70
- * (Note that such `detached` child jobs *must* exit themselves or be stopped
71
- * explicitly from outside, or else they may "run" forever, never running their
72
- * cleanup callbacks. Unlike other jobs, they don't end when their parent does
73
- * because the `detached` job never "ends".)
74
- *
75
- * The detached job has a few special features and limitations:
76
- *
77
- * - It can't be ended, thrown, return()ed, etc. -- you'll get an error
78
- *
79
- * - It can't have any cleanup functions added: no do, must, onError, etc., and
80
- * thus also can't have any native promise, abort signal, etc. used. You can
81
- * call its release() method, but nothing will actually be registered and the
82
- * returned callback is a no-op.
83
- *
84
- * - Unhandled errors from jobs without parents (and errors from *any* job's
85
- * cleanup functions) are sent to the detached job for handling. This means
86
- * whatever you set as the detached job's .{@link Job.asyncCatch asyncCatch}()
87
- * handler will receive them. (Its default is Promise.reject, causing an
88
- * unhandled promise rejection.)
46
+ * If for some reason you are currently using a custom `stop` function and need
47
+ * to keep it for backward compatibility, you can use `.restart()` on the new
48
+ * job to remove the default stop function from its parent, then replace it with
49
+ * `.must(parent.release(stop))`.
89
50
  *
51
+ * @deprecated
90
52
  * @category Jobs
91
53
  */
92
- declare const detached: Job<unknown>;
54
+ declare function makeJob<T>(parent?: Job, stop?: CleanupFn): Job<T>;
93
55
  /**
94
56
  * The "main" job of the program or bundle, which all other jobs should be a
95
57
  * child of. This provides a single point of configuration and cleanup, as one
@@ -713,4 +675,4 @@ declare function task<T, A extends any[], C, D extends {
713
675
  value?: (this: C, ...args: A) => StartObj<T>;
714
676
  }>(clsOrProto: any, name: string | symbol, desc: D): D;
715
677
 
716
- export { AnyFunction, Backpressure, CleanupFn, DisposeFn, type Emitter, Job, type MockSource, OptionalCleanup, Sink, Source, StartFn, StartObj, Stream, Transformer, Yielding, abortSignal, concat, concatAll, concatMap, defer, detached, emitter, empty, filter, fromAsyncIterable, fromDomEvent, fromIterable, fromPromise, fromSubscribe, fromValue, getJob, interval, isJobActive, lazy, makeJob, map, merge, mergeAll, mergeMap, mockSource, must, nativePromise, never, newRoot, restarting, root, share, skip, skipUntil, skipWhile, slack, sleep, start, switchAll, switchMap, take, takeUntil, takeWhile, task, timeout, to };
678
+ export { AnyFunction, Backpressure, CleanupFn, DisposeFn, type Emitter, Job, type MockSource, OptionalCleanup, Sink, Source, StartFn, StartObj, Stream, Transformer, Yielding, abortSignal, concat, concatAll, concatMap, defer, emitter, empty, filter, fromAsyncIterable, fromDomEvent, fromIterable, fromPromise, fromSubscribe, fromValue, getJob, interval, isJobActive, lazy, makeJob, map, merge, mergeAll, mergeMap, mockSource, must, nativePromise, never, newRoot, restarting, root, share, skip, skipUntil, skipWhile, slack, sleep, start, switchAll, switchMap, take, takeUntil, takeWhile, task, timeout, to };
package/dist/mod.mjs CHANGED
@@ -1,8 +1,7 @@
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';
1
+ import { d as defer, i as isFunction } from './utils-BgqyDPjA.mjs';
2
+ import { r as resolver, a as rejecter, b as resolve, I as IsStream, c as backpressure, m as must, s as start, g as getJob, n as noop, d as root, i as isCancel, e as isUnhandled, f as markHandled, h as isError, t as throttle, j as connect, k as fulfillPromise, l as restarting, o as currentCell, p as isValue } from './jobutils-O9tOTNKC.mjs';
3
+ export { A as CancelError, C as CancelResult, E as ErrorResult, V as ValueResult, J as abortSignal, D as compose, y as getResult, F as into, x as isHandled, G as isJobActive, u as makeJob, q as nativePromise, v as newRoot, B as pipe, z as propagateResult, w as reject, K as task, H as timeout } from './jobutils-O9tOTNKC.mjs';
4
+ import { c as callOrWait, m as mustBeSourceOrSignal } from './call-or-wait-CvOF0pao.mjs';
6
5
 
7
6
  function* to(p) {
8
7
  return yield (res) => Promise.resolve(p).then(resolver(res), rejecter(res));
@@ -138,7 +137,7 @@ function mockSource() {
138
137
  };
139
138
  emit.end = () => outlet?.return();
140
139
  emit.throw = (e) => outlet?.throw(e);
141
- emit.ready = (cb) => ready(cb);
140
+ emit.ready = (cb) => ready?.(cb) || false;
142
141
  return emit;
143
142
  }
144
143
  function never() {
@@ -250,10 +249,11 @@ function* each(src) {
250
249
  if (waiter)
251
250
  throw new Error("Multiple `yield next` in loop");
252
251
  yielded = true;
253
- if (conn.result()) {
252
+ const res = conn.result();
253
+ if (res) {
254
254
  result.value = void 0;
255
255
  result.done = true;
256
- fulfillPromise(resolver(r), rejecter(r), conn.result());
256
+ fulfillPromise(resolver(r), rejecter(r), res);
257
257
  } else {
258
258
  waiter = r;
259
259
  t.resume();
package/dist/shared.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- import { Y as Yielding } from './types-N2ua11te.js';
1
+ import { Y as Yielding } from './types-pElgImr7.js';
2
+
3
+ /** @inline */
4
+ type CallSite = TemplateStringsArray;
2
5
 
3
6
  /**
4
7
  * Tools for sharing tasks, values, services, etc., especially across job
@@ -37,6 +40,8 @@ import { Y as Yielding } from './types-N2ua11te.js';
37
40
  * saved service value throw a TypeError after the root job ends. (Note that
38
41
  * such a thing should not be necessary in your production builds, however,
39
42
  * since at runtime you will normally only ever have one root job.)
43
+ *
44
+ * @category Resources
40
45
  */
41
46
  declare function service<T>(factory: () => T): () => T;
42
47
  /**
@@ -46,56 +51,10 @@ declare function service<T>(factory: () => T): () => T;
46
51
  * than to check its `typeof`) will result in a TypeError.
47
52
  *
48
53
  * Note: your runtime environment must support `Proxy.revocable()`.
54
+ *
55
+ * @category Resources
49
56
  */
50
57
  declare function expiring<T extends object>(obj: T): T;
51
- /**
52
- * Wrap a generator, generator function, or generator method to run in parallel
53
- * and have a result that can be waited on in parallel as well.
54
- *
55
- * Normally, when you `yield *` to a generator in a job function, you're
56
- * *pausing* the current function until that generator is finished. And
57
- * normally, this is what you *want*, because you're not trying to do things in
58
- * parallel. But if you *do* want to do things in parallel, you need `fork`.
59
- *
60
- * Generators also can't normally be *waited on* in parallel either: if multiple
61
- * jobs try to wait on an unfinished generator, the most likely result is an
62
- * error or data corruption. (Because the extra `yield *` operations will make
63
- * the generator think it's received data it was waiting for, causing all kinds
64
- * of havoc.)
65
- *
66
- * So if you want a generator to either *run* in parallel or be *waited on* in
67
- * parallel (or both), you need to `fork` it: either on the consuming side by
68
- * wrapping a generator with `fork()`, or on the producing side by wrapping a
69
- * generator function (or decorating a generator method).
70
- *
71
- * When called with a generator, `fork` returns a wrapped generator; when called
72
- * with a function, it returns a wrapped version of the function that will fork
73
- * its results. And when used as a decorator (`@fork`, compatible with both
74
- * TC39 and legacy decorator protocols), it wraps a method to fork its result as
75
- * well.
76
- *
77
- * It is safe to call `fork()` more than once on the same generator, or to
78
- * `fork()` an already-forked generator: the result will always be the same as
79
- * the original fork.
80
- *
81
- * @remarks Note that while you can *also* make a generator run or be waitable
82
- * in parallel using e.g. `start()`, the critical difference is in when resource
83
- * cleanup happens. If you `start()` the generator (or wrap the generator
84
- * function with `task`), its resources will be cleaned up when the generator
85
- * function exits.
86
- *
87
- * With `yield*`, however (with or without `fork`), the resources are cleaned up
88
- * when the original *calling* job ends. And this is what you want when the
89
- * generator's return value is some kind of resource using other active
90
- * resources (such as event listeners rules, etc.) that need to *remain* active
91
- * for the caller.
92
- *
93
- * (If you're familiar with the Effection framework, you may recognize this as
94
- * the difference between "actions" and "resources": in Uneventful we use
95
- * `start()` or `task()` for generators that return the result of an action, and
96
- * `fork` for generators that return a resource that will be owned by the
97
- * calling job.)
98
- */
99
58
  declare function fork<T>(gen: Yielding<T>): Yielding<T>;
100
59
  declare function fork<T, F extends (...args: any[]) => Yielding<T>>(genFunc: F): F;
101
60
  /** @hidden TC39 decorator */
@@ -106,5 +65,83 @@ declare function fork<T, F extends (...args: any[]) => Yielding<T>>(genFunc: F,
106
65
  declare function fork<T, F extends (...args: any[]) => Yielding<T>, D extends {
107
66
  value?: F;
108
67
  }>(clsOrProto: any, name: string | symbol, desc: D): D;
68
+ /** @inline */
69
+ type Factory<T> = (() => T) | (new () => T);
70
+ /**
71
+ * Return a singleton instance for the given factory
72
+ *
73
+ * Every call to `$()` with a given factory will return the same result. (Unless
74
+ * overridden using {@link $cache.set}, {@link $cache.unset} or
75
+ * {@link $cache.replace}.) On first use, the factory is called (or
76
+ * constructed, if it's a class) and the result (if not an error) is cached for
77
+ * future calls.
78
+ *
79
+ */
80
+ declare function $<T>(factory: Factory<T>): T;
81
+ /**
82
+ * Create a per-signal lazy constant, via ```$``()```
83
+ *
84
+ * When you call ```$``(factory)``` inside a given signal function for the first
85
+ * time, `factory()` will be called (or constructed, if it's a class) and
86
+ * returned, and the result cached for future calls *at the same location in
87
+ * that specific signal*. An error results if called outside a signal function.
88
+ *
89
+ * The primary difference between this and the singleton operator (plain `$()`),
90
+ * is that lazy constants are singletons *per call-site*, *per signal*. A
91
+ * specific invocation of ```$``()``` in a specific signal will always return
92
+ * the same value.
93
+ *
94
+ * @remarks
95
+ * Lazy constants are somewhat similar in concept to a React `useMemo()`, but
96
+ * also *very* different. React requires hooks to always be invoked in the same
97
+ * order to match them up with their targets, but lazy constants do not need
98
+ * this: they're tied to the line of code where they're called, and you can
99
+ * branch, loop, skip, or call them out of order with no consequence. (They'll
100
+ * just always return the same value each time if running in the same signal -
101
+ * even in a loop or called in another function with different arguments, and
102
+ * they don't support dependencies because you can use signals instead.)
103
+ *
104
+ * (Also unlike React hooks, they can also be used in nested functions, as long
105
+ * as those functions are only invoked from within a relevant signal.
106
+ * Conversely, because they're keyed to a specific code location, you can't just
107
+ * call a wrapping function more than once in a signal, and expect to get
108
+ * different results: a lazy constant is a per-signal *constant*, not a React
109
+ * hook!)
110
+ */
111
+ declare function $(callSite: CallSite): <T>(factory: Factory<T>) => T;
112
+ /**
113
+ * Utilities for manipulating the singleton cache (e.g. for testing)
114
+ *
115
+ * @category Singletons & Lazy Constants
116
+ * @namespace
117
+ * @experimental
118
+ */
119
+ declare const $cache: {
120
+ /**
121
+ * Set the cached singleton instance for a given factory. (e.g. for
122
+ * testing)
123
+ *
124
+ * All subsequent calls to `$(factory)` will return the given result, until
125
+ * manually set again, or reset via {@link $cache.unset}.
126
+ */
127
+ set<T>(factory: Factory<T>, result: T): void;
128
+ /**
129
+ * Unset the cached singleton for a given factory, such that the next call
130
+ * to `$(factory)` will create a new instance.
131
+ */
132
+ unset<T>(factory: Factory<T>): void;
133
+ /**
134
+ * Replace the implementation for a given factory, such that future calls to
135
+ * `$(factory)` will call or construct the replacement instead.
136
+ *
137
+ * If the replacement is omitted, null, or undefined, future calls will
138
+ * invoke the original factory again.
139
+ *
140
+ * (Note: in all cases the replacement will not take effect if there's
141
+ * already a cached singleton, so you may wish to call
142
+ * {@link $cache.unset}() to ensure a future call is actually executed.)
143
+ */
144
+ replace<T>(factory: Factory<T>, replacement?: Factory<T>): void;
145
+ };
109
146
 
110
- export { expiring, fork, service };
147
+ export { $, $cache, expiring, fork, service };
package/dist/shared.mjs CHANGED
@@ -1,5 +1,6 @@
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';
1
+ import { p as perSignal } from './hooks-BnMYHRlM.mjs';
2
+ import { d as root, m as must, s as start, n as noop } from './jobutils-O9tOTNKC.mjs';
3
+ import { a as isGeneratorFunction, b as decorateMethod, i as isFunction, c as apply, s as setMap, e as isClass } from './utils-BgqyDPjA.mjs';
3
4
 
4
5
  function service(factory) {
5
6
  let known = false, value = void 0;
@@ -48,5 +49,52 @@ function fork(genOrFunc, ...args) {
48
49
  }
49
50
  }
50
51
  const forks = /* @__PURE__ */ new WeakMap();
52
+ const constants = /* @__PURE__ */ new WeakMap(), factories = /* @__PURE__ */ new WeakMap();
53
+ function $(key) {
54
+ if (isFunction(key)) {
55
+ return constants.has(key) ? constants.get(key) : setMap(
56
+ constants,
57
+ key,
58
+ callOrConstruct(factories.has(key) ? factories.get(key) : key)
59
+ );
60
+ }
61
+ return perSignal(callOrConstruct, key, "$``() ");
62
+ }
63
+ function callOrConstruct(f) {
64
+ return isClass(f) ? new f() : f();
65
+ }
66
+ const $cache = {
67
+ /**
68
+ * Set the cached singleton instance for a given factory. (e.g. for
69
+ * testing)
70
+ *
71
+ * All subsequent calls to `$(factory)` will return the given result, until
72
+ * manually set again, or reset via {@link $cache.unset}.
73
+ */
74
+ set(factory, result) {
75
+ constants.set(factory, result);
76
+ },
77
+ /**
78
+ * Unset the cached singleton for a given factory, such that the next call
79
+ * to `$(factory)` will create a new instance.
80
+ */
81
+ unset(factory) {
82
+ constants.delete(factory);
83
+ },
84
+ /**
85
+ * Replace the implementation for a given factory, such that future calls to
86
+ * `$(factory)` will call or construct the replacement instead.
87
+ *
88
+ * If the replacement is omitted, null, or undefined, future calls will
89
+ * invoke the original factory again.
90
+ *
91
+ * (Note: in all cases the replacement will not take effect if there's
92
+ * already a cached singleton, so you may wish to call
93
+ * {@link $cache.unset}() to ensure a future call is actually executed.)
94
+ */
95
+ replace(factory, replacement) {
96
+ replacement != null && replacement !== factory ? factories.set(factory, replacement) : factories.delete(factory);
97
+ }
98
+ };
51
99
 
52
- export { expiring, fork, service };
100
+ export { $, $cache, expiring, fork, service };