uneventful 0.0.10 → 0.0.12

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.d.ts CHANGED
@@ -5,6 +5,7 @@ import { Y as Yielding } from './types-N2ua11te.js';
5
5
  * boundaries.
6
6
  *
7
7
  * @module uneventful/shared
8
+ * @disableGroups
8
9
  */
9
10
 
10
11
  /**
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
@@ -194,7 +194,8 @@ interface RuleFactory {
194
194
  *
195
195
  * @returns A function that can be called to terminate the rule.
196
196
  *
197
- * @category Reactive Behaviors
197
+ * @category none
198
+ * @function
198
199
  */
199
200
  declare const rule: ((fn: () => OptionalCleanup) => DisposeFn) & RuleFactory;
200
201
  /**
@@ -213,7 +214,7 @@ declare const rule: ((fn: () => OptionalCleanup) => DisposeFn) & RuleFactory;
213
214
  * factory you wish to run pending rules for. If not given, the default
214
215
  * {@link rule}() factory is targeted.
215
216
  *
216
- * @category Reactive Behaviors
217
+ * @category Scheduling
217
218
  */
218
219
  declare function runRules(scheduleFn?: SchedulerFn): void;
219
220
  /**
@@ -245,34 +246,6 @@ declare class WriteConflict extends Error {
245
246
  */
246
247
  declare class CircularDependency extends Error {
247
248
  }
248
- /**
249
- * Keep an expression's old value unless there's a semantic change
250
- *
251
- * By default, reactive values (i.e. {@link cached}(), or {@link value}() with a
252
- * {@link Configurable.setf setf}()) are considered to have "changed" (and thus
253
- * trigger recalculation of their dependents) when they are different according
254
- * to `===` comparison.
255
- *
256
- * This works well for primitive values, but for arrays and objects it's not
257
- * always ideal, because two arrays can have the exact same elements and still
258
- * be different according to `===`. So this function lets you substitute a
259
- * different comparison function (like a deep-equal or shallow-equal) instead.
260
- * (The default is {@link arrayEq}() if no compare function is supplied.)
261
- *
262
- * Specifically, if your reactive expression returns `unchangedIf(newVal,
263
- * compare)`, then the expression's previous value will be kept if the compare
264
- * function returns true when called with the old and new values. Otherwise, the
265
- * new value will be used.
266
- *
267
- * @remarks
268
- * - If the reactive expression's last "value" was an error, the new value is
269
- * returned
270
- * - An error will be thrown if this function is called outside a reactive
271
- * expression or from within a {@link peek}() call or {@link action} wrapper.
272
- *
273
- * @category Reactive Values
274
- */
275
- declare function unchangedIf<T>(newVal: T, equals?: (v1: T, v2: T) => boolean): T;
276
249
 
277
250
  /**
278
251
  * The Signals API for uneventful.
@@ -376,7 +349,7 @@ interface Configurable<T> extends Writable<T> {
376
349
  /**
377
350
  * Create a {@link Configurable} signal with the given inital value
378
351
  *
379
- * @category Reactive Values
352
+ * @category none
380
353
  */
381
354
  declare function value<T>(val?: T): Configurable<T>;
382
355
  /**
@@ -388,7 +361,7 @@ declare function value<T>(val?: T): Configurable<T>;
388
361
  * calling signature below will apply, even if TypeScript doesn't see it that
389
362
  * way!)
390
363
  *
391
- * @category Reactive Values
364
+ * @category none
392
365
  */
393
366
  declare function cached<T>(compute: () => T): Signal<T>;
394
367
  /**
@@ -429,7 +402,7 @@ declare function cached<T extends Signal<any>>(signal: T): T;
429
402
  *
430
403
  * @returns The result of calling `fn(..args)`
431
404
  *
432
- * @category Reactive Values
405
+ * @category Dependency Tracking
433
406
  */
434
407
  declare function peek<F extends PlainFunction>(fn: F, ...args: Parameters<F>): ReturnType<F>;
435
408
  /**
@@ -471,7 +444,7 @@ declare function peek<F extends PlainFunction>(fn: F, ...args: Parameters<F>): R
471
444
  * to the original function, while running with dependency tracking suppressed
472
445
  * (as with {@link peek}()).
473
446
  *
474
- * @category Reactive Behaviors
447
+ * @category Dependency Tracking
475
448
  */
476
449
  declare function action<F extends AnyFunction>(fn: F): F;
477
450
  /** @hidden TC39 Decorator protocol */
@@ -482,6 +455,78 @@ declare function action<F extends AnyFunction>(fn: F, ctx: {
482
455
  declare function action<F extends AnyFunction, D extends {
483
456
  value?: F;
484
457
  }>(clsOrProto: any, name: string | symbol, desc: D): D;
458
+ /**
459
+ * Keep an expression's old value unless there's a semantic change
460
+ *
461
+ * By default, reactive values (i.e. {@link cached}(), or {@link value}() with a
462
+ * {@link Configurable.setf setf}()) are considered to have "changed" (and thus
463
+ * trigger recalculation of their dependents) when they are different according
464
+ * to `===` comparison.
465
+ *
466
+ * This works well for primitive values, but for arrays and objects it's not
467
+ * always ideal, because two arrays can have the exact same elements and still
468
+ * be different according to `===`. So this function lets you substitute a
469
+ * different comparison function (like a deep-equal or shallow-equal) instead.
470
+ *
471
+ * Specifically, if your reactive expression returns `stable(compare, newVal)`,
472
+ * then the expression's previous value will be kept if the `compare` function
473
+ * returns true when called with the old and new values. Otherwise, the new
474
+ * value will be used.
475
+ *
476
+ * @remarks
477
+ * - If the reactive expression's last "value" was an error, the new value is
478
+ * returned.
479
+ * - An error will be thrown if this function is called outside a reactive
480
+ * expression or from within a {@link peek}() call or {@link action} wrapper.
481
+ * - You can use the {@link stabilizer}() function to create custom functions
482
+ * similar to {@link stableJSON}. or {@link stableArray}().
483
+ *
484
+ * @category Dependency Tracking
485
+ */
486
+ declare function stable<T>(equals: (v1: T, v2: T) => boolean, newVal: T): T;
487
+ /**
488
+ * @deprecated This is {@link stable}() but with swapped arguments. Please switch
489
+ * to using stable(), stableArray(), or a stabilizer(), as this function will be removed
490
+ * in a future release.
491
+ *
492
+ * @hidden
493
+ */
494
+ declare function unchangedIf<T>(newVal: T, equals?: (v1: T, v2: T) => boolean): T;
495
+ /**
496
+ * Create a shorthand version of {@link stable}(), pre-bound to a specific comparison
497
+ * function.
498
+ *
499
+ * @param equals The comparison function to use
500
+ * @template T The type of value the comparison function works on
501
+ *
502
+ * @category Dependency Tracking
503
+ */
504
+ declare function stabilizer<T>(equals: <T>(v1: T, v2: T) => boolean): <R extends T>(newVal: R) => R;
505
+ /**
506
+ * Keep an expression's old value if the new value is an array with the same
507
+ * contents.
508
+ *
509
+ * (Shorthand for {@link stable}({@link arrayEq}, ...).)
510
+ *
511
+ * @function
512
+ * @category Dependency Tracking
513
+ */
514
+ declare const stableArray: <R extends unknown[]>(newVal: R) => R;
515
+ /**
516
+ * Keep an expression's old value if its JSON string is the same as the new one.
517
+ *
518
+ * (Shorthand for {@link stable}() with a JSON-string comparison function.)
519
+ *
520
+ * Note that JSON string comparison is slow and memory intensive for more
521
+ * complex data structures, is order-sensitive for object keys, and doesn't
522
+ * support maps or sets. If you need any of those things, you should probably
523
+ * use a {@link stabilizer}() based on
524
+ * [fast-equals](https://github.com/planttheidea/fast-equals) instead.
525
+ *
526
+ * @function
527
+ * @category Dependency Tracking
528
+ */
529
+ declare const stableJSON: <R extends any>(newVal: R) => R;
485
530
  /**
486
531
  * Wait for and return the next truthy value (or error) from a data source (when
487
532
  * processed with `yield *` within a {@link Job}).
@@ -513,4 +558,4 @@ declare function action<F extends AnyFunction, D extends {
513
558
  */
514
559
  declare function until<T>(source: UntilMethod<T> | Stream<T> | (() => T)): Yielding<T>;
515
560
 
516
- export { CircularDependency, type Configurable, type GenericMethodDecorator, type RuleFactory, type SchedulerFn, type Signal, type Writable, WriteConflict, action, cached, peek, rule, runRules, unchangedIf, until, value };
561
+ export { CircularDependency, type Configurable, type GenericMethodDecorator, type RuleFactory, type SchedulerFn, type Signal, type Writable, WriteConflict, action, cached, peek, rule, runRules, stabilizer, stable, stableArray, stableJSON, unchangedIf, until, value };
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, d as defer, c as apply, s as setMap, C as CallableObject, f as arrayEq } 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 */)
345
+ return;
346
+ if (this.flags & 64 /* Stateful */) {
347
+ mksub(dummySource ||= Cell.mkValue(null), this);
332
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,42 @@ 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
+ }
481
+ unchangedIf(newVal, equals) {
482
+ return this.flags & 16 /* Error */ || !equals(this.value, newVal) ? newVal : this.value;
483
+ }
454
484
  recalcWhen(fnOrKey, fn) {
485
+ if (this.flags & 8 /* Peeking */)
486
+ return;
455
487
  let trackers = fn ? obtrackers.get(fn) || setMap(obtrackers, fn, /* @__PURE__ */ new WeakMap()) : fntrackers;
456
488
  let signal = trackers.get(fnOrKey);
457
489
  if (!signal) {
@@ -465,35 +497,32 @@ class Cell {
465
497
  static mkCached(compute) {
466
498
  const cell = new Cell();
467
499
  cell.compute = compute;
468
- cell.ctx = makeCtx(null, cell);
469
500
  cell.flags = 4 /* Compute */;
470
501
  return cell;
471
502
  }
472
503
  static mkRule(fn, q) {
473
504
  const outer = getJob(), cell = Cell.mkCached(() => {
474
505
  try {
475
- cell.getJob();
476
506
  const cleanup = fn();
477
507
  if (cleanup)
478
- (cell.ctx.job || cell.getJob()).must(cleanup);
479
- cell.lastChanged = timestamp;
508
+ (cell.job || cell.getJob()).must(cleanup);
480
509
  } catch (e) {
481
510
  cell.stop();
482
511
  throw e;
483
512
  }
484
513
  }), stop = cell.stop.bind(cell);
485
514
  outer === detached || ruleStops.set(cell, outer.release(stop));
515
+ cell.flags |= 64 /* Stateful */;
486
516
  cell.setQ(q);
487
517
  return stop;
488
518
  }
489
519
  }
490
- function unchangedIf(newVal, equals = arrayEq) {
491
- const { cell } = current;
492
- if (cell) {
493
- return cell.flags & 16 /* Error */ || !equals(cell.value, newVal) ? newVal : cell.value;
494
- } else {
495
- throw new Error("unchangedIf() must be called from a reactive expression");
496
- }
520
+ function getCell(f = "") {
521
+ if (!currentCell || currentCell.flags & 8 /* Peeking */)
522
+ throw new Error(
523
+ `${f}must be called from a reactive expression`
524
+ );
525
+ return currentCell;
497
526
  }
498
527
 
499
528
  class RF extends CallableObject {
@@ -649,29 +678,26 @@ function cached(compute, initVal) {
649
678
  );
650
679
  }
651
680
  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
- }
681
+ return currentCell ? currentCell.peek(fn, null, args) : fn(...args);
660
682
  }
661
683
  function action(fn, _ctx, desc) {
662
684
  if (desc)
663
685
  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
- }
686
+ return function(...args) {
687
+ return currentCell ? currentCell.peek(fn, this, args) : apply(fn, this, args);
673
688
  };
674
689
  }
690
+ function stable(equals, newVal) {
691
+ return getCell("stabilizers ").unchangedIf(newVal, equals);
692
+ }
693
+ function unchangedIf(newVal, equals = arrayEq) {
694
+ return stable(equals, newVal);
695
+ }
696
+ function stabilizer(equals) {
697
+ return stable.bind(null, equals);
698
+ }
699
+ const stableArray = /* @__PURE__ */ stabilizer(arrayEq);
700
+ const stableJSON = /* @__PURE__ */ stabilizer((a, b) => JSON.stringify(a) === JSON.stringify(b));
675
701
  function until(source) {
676
702
  return callOrWait(source, "uneventful.until", waitTruthy, recache);
677
703
  }
@@ -682,5 +708,4 @@ function waitTruthy(job, v) {
682
708
  v && job.return(v);
683
709
  }
684
710
 
685
- export { CircularDependency, ConfigurableImpl, SignalImpl, WritableImpl, WriteConflict, action, cached, peek, rule, runRules, unchangedIf, until, value };
686
- //# sourceMappingURL=signals.mjs.map
711
+ export { CircularDependency, ConfigurableImpl, SignalImpl, WritableImpl, WriteConflict, action, cached, peek, rule, runRules, stabilizer, stable, stableArray, stableJSON, unchangedIf, until, value };
@@ -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';