@reely/signals 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,11 +32,13 @@ Pick the style you like; the two mix freely. A `ReactiveValue<T>` is anything re
32
32
 
33
33
  Effects run synchronously, and a computed recomputes only when it is read after a change. A write of an equal value (`Object.is`, or the `equals` you pass) changes nothing. A signal holds any value, a function included: `set` and `.value =` store it; `update` calls only the function you pass it.
34
34
 
35
+ A batch that puts a signal back where it was reruns nothing that read it before the batch. A run of an effect or computed does not follow a signal it writes itself, so a run that writes what it reads settles instead of looping; effects that keep writing each other's signals stop with an error after 100 waves, and a computed that reads itself throws one.
36
+
35
37
  ```ts
36
38
  import { signal } from '@reely/signals';
37
39
 
38
40
  const garage = { cars: ['Volvo'] };
39
- const state = signal(garage, { equals: () => false }); // changed in place: every write notifies
41
+ const state = signal(garage, { equals: false }); // changed in place: every write notifies
40
42
  garage.cars.push('Saab');
41
43
  state.value = garage;
42
44
 
package/dist/index.d.ts CHANGED
@@ -20,6 +20,10 @@ export declare const computed: <T>(fn: () => T) => Computed<T>;
20
20
  */
21
21
  export declare const effect: (fn: () => void) => (() => void);
22
22
 
23
+ declare type Equality<T> = {
24
+ bivarianceHack(previous: T, next: T): boolean;
25
+ }['bivarianceHack'];
26
+
23
27
  /** For code that runs later, such as a callback creating effects, to pass to `withOwner`. */
24
28
  export declare const getOwner: () => Nullable<Owner>;
25
29
 
@@ -57,9 +61,9 @@ export declare const signal: <T>(init: T, options?: SignalOptions<T>) => Signal<
57
61
  export declare interface SignalOptions<T> {
58
62
  /**
59
63
  * Whether a write leaves the value unchanged, so nothing is notified; `Object.is` by default.
60
- * `() => false` notifies every write, for an object changed in place.
64
+ * `false` (or `() => false`) notifies every write, for an object changed in place.
61
65
  */
62
- equals?(previous: T, next: T): boolean;
66
+ equals?: Equality<T> | false;
63
67
  }
64
68
 
65
69
  declare interface Subscribable<T> {
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as subscribe, c as write, i as signal$1, l as noop, n as computed$1, r as effect$1, s as untracked, t as batch } from "./signals-D9zxjm2u.js";
1
+ import { a as subscribe, c as write, i as signal$1, l as noop, n as computed$1, r as effect$1, s as untracked, t as batch } from "./signals-MA_mML__.js";
2
2
  import { hasSome } from "@reely/basics";
3
3
  //#region ../utils/dist/lib/objects/setPrototype.js
4
4
  function setPrototype(proto, object) {
@@ -1,4 +1,9 @@
1
1
  import { hasSome } from "@reely/basics";
2
+ //#region ../utils/dist/lib/objects/isNil.js
3
+ function isNil(value) {
4
+ return value === null || value === void 0;
5
+ }
6
+ //#endregion
2
7
  //#region ../utils/dist/lib/objects/isInstanceOf.js
3
8
  function isInstanceOf(instanceType, instance) {
4
9
  return instance instanceof instanceType;
@@ -13,6 +18,7 @@ var epoch = 0;
13
18
  var tracker = null;
14
19
  var running = null;
15
20
  var batchDepth = 0;
21
+ var batchWrites = [];
16
22
  var queue = [];
17
23
  var flushing = false;
18
24
  var setTracker = (observer) => {
@@ -31,6 +37,7 @@ var Signal = class {
31
37
  version = 0;
32
38
  observers = /* @__PURE__ */ new Set();
33
39
  refresh = noop;
40
+ beforeBatch = null;
34
41
  constructor(value, options) {
35
42
  this.value = value;
36
43
  this.options = options;
@@ -42,14 +49,27 @@ var Signal = class {
42
49
  observe(observer) {
43
50
  this.observers.add(observer);
44
51
  }
52
+ forgetBatch() {
53
+ this.beforeBatch = null;
54
+ }
45
55
  unobserve(observer) {
46
56
  this.observers.delete(observer);
47
57
  }
48
58
  write(value) {
49
- if (this.options.equals?.(this.value, value) ?? Object.is(this.value, value)) return;
50
- this.value = value;
51
- this.version++;
59
+ const { equals = Object.is } = this.options;
60
+ if (equals !== false && equals(this.value, value)) return;
61
+ if (batchDepth > 0 && isNil(this.beforeBatch)) {
62
+ this.beforeBatch = {
63
+ value: this.value,
64
+ version: this.version
65
+ };
66
+ batchWrites.push(this);
67
+ }
68
+ const { beforeBatch } = this;
69
+ const reverted = hasSome(beforeBatch) && equals !== false && equals(beforeBatch.value, value);
52
70
  epoch++;
71
+ this.value = value;
72
+ this.version = reverted ? beforeBatch.version : epoch;
53
73
  running?.onOwnWrite(this);
54
74
  const idle = queue.length === 0;
55
75
  for (const observer of this.observers) observer.mark(this);
@@ -66,6 +86,7 @@ var Computation = class {
66
86
  checkedAt = -1;
67
87
  markedAt = -1;
68
88
  deafTo = null;
89
+ refreshing = false;
69
90
  constructor(fn) {
70
91
  this.fn = fn;
71
92
  }
@@ -96,8 +117,23 @@ var Computation = class {
96
117
  if (this.linked) source.observe(this);
97
118
  }
98
119
  refresh() {
120
+ if (this.refreshing) throw new Error("reelx: a cycle of computeds, one reads itself through what it reads");
99
121
  const current = this.outcome;
100
122
  if (hasSome(current) && !this.needsCheck && (this.linked || this.checkedAt === epoch)) return current;
123
+ this.refreshing = true;
124
+ try {
125
+ return this.update(current);
126
+ } catch (error) {
127
+ this.outcome = null;
128
+ throw error;
129
+ } finally {
130
+ this.refreshing = false;
131
+ }
132
+ }
133
+ detach() {
134
+ for (const source of this.sources.keys()) source.unobserve(this);
135
+ }
136
+ update(current) {
101
137
  this.needsCheck = false;
102
138
  this.checkedAt = epoch;
103
139
  if (hasSome(current) && !this.sourceChanged()) return current;
@@ -125,9 +161,6 @@ var Computation = class {
125
161
  this.outcome = next;
126
162
  return next;
127
163
  }
128
- detach() {
129
- for (const source of this.sources.keys()) source.unobserve(this);
130
- }
131
164
  unlink(source) {
132
165
  this.sources.delete(source);
133
166
  source.unobserve(this);
@@ -273,7 +306,12 @@ var batch = (fn) => {
273
306
  try {
274
307
  return fn();
275
308
  } finally {
276
- if (--batchDepth === 0) flushSync();
309
+ if (--batchDepth === 0) {
310
+ const written = batchWrites;
311
+ batchWrites = [];
312
+ written.forEach((each) => each.forgetBatch());
313
+ flushSync();
314
+ }
277
315
  }
278
316
  };
279
317
  /** Runs `fn` without subscribing the running effect or computed to what it reads. */
@@ -290,4 +328,4 @@ var subscriberCount = (read) => nodes.get(read)?.observers.size ?? 0;
290
328
  //#endregion
291
329
  export { subscribe as a, write as c, signal as i, noop as l, computed as n, subscriberCount as o, effect as r, untracked as s, batch as t };
292
330
 
293
- //# sourceMappingURL=signals-D9zxjm2u.js.map
331
+ //# sourceMappingURL=signals-MA_mML__.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signals-MA_mML__.js","names":[],"sources":["../../utils/dist/lib/objects/isNil.js","../../utils/dist/lib/objects/isInstanceOf.js","../../utils/dist/lib/fp/noop.js","../src/lib/reelx.core.ts"],"sourcesContent":["export default function isNil(value) {\n return value === null || value === undefined;\n}\n","export default function isInstanceOf(instanceType, instance) {\n return instance instanceof instanceType;\n}\n","export default function noop() {\n return undefined;\n}\n","// A push-pull signal graph: a write marks what may have changed, a read brings a node up to date.\nimport { hasSome } from '@reely/basics';\nimport type { Nullable } from '@reely/utils';\nimport { isInstanceOf, isNil, noop } from '@reely/utils';\n\n/** A value read reactively: a signal, a computed or any getter of them; an effect or computed that reads it follows it. */\nexport type ReactiveValue<T> = () => T;\n\n// a method type: its parameters stay bivariant, so a `Signal<T>` is a `Signal<unknown>`\ntype Equality<T> = { bivarianceHack(previous: T, next: T): boolean }['bivarianceHack'];\n\n/** How a signal tells a write that changes nothing from one that notifies. */\nexport interface SignalOptions<T> {\n /**\n * Whether a write leaves the value unchanged, so nothing is notified; `Object.is` by default.\n * `false` (or `() => false`) notifies every write, for an object changed in place.\n */\n equals?: Equality<T> | false;\n}\n\ninterface Source {\n // grows with every change of the value, so an observer can tell whether what it read is still current\n readonly version: number;\n readonly observers: ReadonlySet<Computation<unknown>>;\n refresh(): void;\n observe(observer: Computation<unknown>): void;\n unobserve(observer: Computation<unknown>): void;\n}\n\ninterface Link {\n version: number;\n readIn: number;\n}\n\n// the value, or what the computation threw\ntype Outcome<T> = { readonly threw: false; readonly value: T } | { readonly threw: true; readonly value: unknown };\n\nconst MAX_FLUSH_WAVES = 100;\n\n// grows with every write: an unlinked computed hears no writes, but one checked at this epoch is current;\n// a signal's version is the epoch of its last change, so a version is never handed out twice\nlet epoch = 0;\nlet tracker: Nullable<Computation<unknown>> = null;\n// the computation whose `fn` runs now, tracked or not: its own writes never run it again\nlet running: Nullable<Computation<unknown>> = null;\nlet batchDepth = 0;\n// the signals written in the batch running now, which remember what they held before it\nlet batchWrites: Signal<unknown>[] = [];\nlet queue: Effect[] = [];\nlet flushing = false;\n\nconst setTracker = (observer: Nullable<Computation<unknown>>): Nullable<Computation<unknown>> => {\n const outer = tracker;\n tracker = observer;\n return outer;\n};\n\nconst setRunning = (computation: Nullable<Computation<unknown>>): Nullable<Computation<unknown>> => {\n const outer = running;\n running = computation;\n return outer;\n};\n\nclass Signal<T> implements Source {\n public version = 0;\n public readonly observers = new Set<Computation<unknown>>();\n public readonly refresh = noop;\n // the value and version before the first write of the batch running now\n private beforeBatch: Nullable<{ readonly value: T; readonly version: number }> = null;\n\n constructor(private value: T, private readonly options: SignalOptions<T>) {}\n\n public readonly read = (): T => {\n tracker?.onRead(this);\n return this.value;\n };\n\n public observe(observer: Computation<unknown>): void {\n this.observers.add(observer);\n }\n\n public forgetBatch(): void {\n this.beforeBatch = null;\n }\n\n public unobserve(observer: Computation<unknown>): void {\n this.observers.delete(observer);\n }\n\n public write(value: T): void {\n const { equals = Object.is } = this.options;\n if (equals !== false && equals(this.value, value)) {\n return;\n }\n if (batchDepth > 0 && isNil(this.beforeBatch)) {\n this.beforeBatch = { value: this.value, version: this.version };\n batchWrites.push(this);\n }\n const { beforeBatch } = this;\n // a batch that puts the value back changes nothing for what read it before the batch\n const reverted = hasSome(beforeBatch) && equals !== false && equals(beforeBatch.value, value);\n epoch++;\n this.value = value;\n this.version = reverted ? beforeBatch.version : epoch;\n running?.onOwnWrite(this);\n const idle = queue.length === 0;\n for (const observer of this.observers) {\n observer.mark(this);\n }\n if (idle && queue.length > 0 && batchDepth === 0 && !flushing) {\n flushSync();\n }\n }\n}\n\n// runs `fn` recording what it reads, and reruns it on demand only when one of those sources changed\nabstract class Computation<T> {\n public version = 0;\n // a source may have changed; the versions of the sources tell whether one did\n protected needsCheck = false;\n // in the order first read\n protected readonly sources = new Map<Source, Link>();\n protected outcome: Nullable<Outcome<T>> = null;\n private runCount = 0;\n private checkedAt = -1;\n private markedAt = -1;\n private deafTo: Nullable<Set<Source>> = null;\n // checking its sources or computing: a read of it now is a cycle\n private refreshing = false;\n\n constructor(private readonly fn: () => T) {}\n\n // whether its sources hold it, so that their writes reach it\n protected abstract get linked(): boolean;\n\n // `written` is the signal whose write this mark carries\n public mark(written: Source): void {\n // once per write: a node already marked by an earlier write still passes a new one on\n if (this.markedAt === epoch) {\n return;\n }\n this.markedAt = epoch;\n // A run does not depend on a signal it writes, read directly or through a computed: its own\n // write coming back makes it deaf to that signal until it reads it again or runs again.\n if (this === running) {\n this.deafTo ??= new Set();\n this.deafTo.add(written);\n } else if (!this.deafTo?.has(written)) {\n this.onMarked(written);\n }\n }\n\n // what a run writes itself is not a change it missed\n public onOwnWrite(source: Source): void {\n const link = this.sources.get(source);\n if (hasSome(link)) {\n link.version = source.version;\n }\n }\n\n public onRead(source: Source): void {\n this.deafTo?.delete(source);\n const link = this.sources.get(source);\n if (hasSome(link)) {\n link.version = source.version;\n link.readIn = this.runCount;\n return;\n }\n this.sources.set(source, { version: source.version, readIn: this.runCount });\n if (this.linked) {\n source.observe(this);\n }\n }\n\n public refresh(): Outcome<T> {\n if (this.refreshing) {\n throw new Error('reelx: a cycle of computeds, one reads itself through what it reads');\n }\n const current = this.outcome;\n if (hasSome(current) && !this.needsCheck && (this.linked || this.checkedAt === epoch)) {\n return current;\n }\n this.refreshing = true;\n try {\n return this.update(current);\n } catch (error) {\n // a cycle among its sources broke the check off: the next read computes it afresh\n this.outcome = null;\n throw error;\n } finally {\n this.refreshing = false;\n }\n }\n\n protected detach(): void {\n for (const source of this.sources.keys()) {\n source.unobserve(this);\n }\n }\n\n private update(current: Nullable<Outcome<T>>): Outcome<T> {\n this.needsCheck = false;\n this.checkedAt = epoch;\n if (hasSome(current) && !this.sourceChanged()) {\n return current;\n }\n\n const { fn } = this;\n this.runCount++;\n this.deafTo = null;\n const outer = setTracker(this);\n const outerRunning = setRunning(this);\n let next: Outcome<T>;\n try {\n next = { threw: false, value: fn() };\n } catch (error) {\n next = { threw: true, value: error };\n }\n setTracker(outer);\n setRunning(outerRunning);\n this.dropUnread();\n if (!hasSome(current) || current.threw !== next.threw || !Object.is(current.value, next.value)) {\n this.version++;\n }\n this.outcome = next;\n return next;\n }\n\n private unlink(source: Source): void {\n this.sources.delete(source);\n source.unobserve(this);\n }\n\n private sourceChanged(): boolean {\n for (const [source, { version }] of this.sources) {\n source.refresh();\n if (source.version !== version) {\n return true;\n }\n }\n return false;\n }\n\n private dropUnread(): void {\n for (const [source, link] of this.sources) {\n if (link.readIn !== this.runCount) {\n this.unlink(source);\n }\n }\n }\n\n protected abstract onMarked(written: Source): void;\n}\n\nclass Computed<T> extends Computation<T> implements Source {\n public readonly observers = new Set<Computation<unknown>>();\n\n protected get linked(): boolean {\n return this.observers.size > 0;\n }\n\n public readonly read = (): T => {\n const outcome = this.refresh();\n tracker?.onRead(this);\n if (outcome.threw) {\n throw outcome.value;\n }\n return outcome.value;\n };\n\n public observe(observer: Computation<unknown>): void {\n if (this.observers.size === 0) {\n for (const source of this.sources.keys()) {\n source.observe(this);\n }\n }\n this.observers.add(observer);\n }\n\n public unobserve(observer: Computation<unknown>): void {\n if (this.observers.delete(observer) && this.observers.size === 0) {\n this.detach();\n }\n }\n\n protected onMarked(written: Source): void {\n this.needsCheck = true;\n for (const observer of this.observers) {\n observer.mark(written);\n }\n }\n}\n\n// stays linked until disposed; a mark queues it to run\nclass Effect extends Computation<void> {\n private disposed = false;\n\n protected get linked(): boolean {\n return !this.disposed;\n }\n\n public readonly dispose = (): void => {\n this.disposed = true;\n this.detach();\n this.sources.clear();\n };\n\n // runs it if what it read has changed, and throws what that run threw\n public run(): void {\n if (this.disposed) {\n return;\n }\n const before = this.outcome;\n const after = this.refresh();\n if (after !== before && after.threw) {\n throw after.value;\n }\n }\n\n // taken out of the queue without running: the next write queues it again\n public dequeue(): void {\n this.needsCheck = false;\n }\n\n protected onMarked(): void {\n if (!this.needsCheck) {\n this.needsCheck = true;\n queue.push(this);\n }\n }\n}\n\n/** Runs the queued effects now; what they write joins as the next wave. Every effect runs, the first error is rethrown. */\nconst flushSync = (): void => {\n if (flushing) {\n return;\n }\n flushing = true;\n let failure: Nullable<{ readonly error: unknown }> = null;\n try {\n let wave = 0;\n while (queue.length > 0) {\n wave++;\n if (wave > MAX_FLUSH_WAVES) {\n const dropped = queue;\n queue = [];\n dropped.forEach((effect) => effect.dequeue());\n throw new Error(\n `reelx: a cycle of effects, each writes a signal another one reads (${MAX_FLUSH_WAVES} waves)`,\n { cause: failure?.error }\n );\n }\n const effects = queue;\n queue = [];\n for (const effect of effects) {\n try {\n effect.run();\n } catch (error) {\n failure ??= { error };\n }\n }\n }\n } finally {\n flushing = false;\n }\n if (hasSome(failure)) {\n throw failure.error;\n }\n};\n\n// the node behind each reader, for writes and for `subscriberCount`\nconst nodes = new WeakMap<ReactiveValue<unknown>, Signal<unknown> | Computed<unknown>>();\n\nconst toReactiveValue = <T>(node: Signal<T> | Computed<T>): ReactiveValue<T> => {\n nodes.set(node.read, node);\n return node.read;\n};\n\n/** A value that effects and computeds re-read when it is written; a function is held as a value too. */\nexport const signal = <T>(initial: T, options: SignalOptions<T> = {}): ReactiveValue<T> =>\n toReactiveValue(new Signal(initial, options));\n\n/** A value derived from what `fn` reads, recomputed on read after one of them changes; what `fn` throws is rethrown until then. */\nexport const computed = <T>(fn: () => T): ReactiveValue<T> => toReactiveValue(new Computed(fn));\n\n/** Writes `value` to the signal `read` reads (a computed's reader is ignored); a value its `equals` finds unchanged changes nothing. */\nexport const write = <T>(read: ReactiveValue<T>, value: T): void => {\n const node = nodes.get(read);\n if (isInstanceOf(Signal, node)) {\n node.write(value);\n }\n};\n\n/** Runs `fn` now and again after every change of what it read. Returns `dispose`; creation that throws leaves nothing subscribed. */\nexport const effect = (fn: () => void): (() => void) => {\n const node = new Effect(fn);\n try {\n batch(() => {\n try {\n node.run();\n } catch (error) {\n // before the flush that ends the batch, so it never runs an effect whose creation threw\n node.dispose();\n throw error;\n }\n });\n } catch (error) {\n node.dispose();\n throw error;\n }\n return node.dispose;\n};\n\n/**\n * Calls `cb` with the value of `read` and the one before it: now, unless it is `undefined`, then\n * after every change `read` notifies. Returns `unsubscribe`.\n */\nexport const subscribe = <T>(read: ReactiveValue<T>, cb: (value: T, prevValue?: T) => void): (() => void) => {\n let last: T | undefined;\n let started = false;\n return effect(() => {\n const value = read();\n const previous = last;\n last = value;\n if (started || !Object.is(value, undefined)) {\n untracked(() => cb(value, previous));\n }\n started = true;\n });\n};\n\n/** Groups writes: effects run once, when the outermost `batch` ends, even if `fn` throws. */\nexport const batch = <T>(fn: () => T): T => {\n batchDepth++;\n try {\n return fn();\n } finally {\n if (--batchDepth === 0) {\n const written = batchWrites;\n batchWrites = [];\n written.forEach((each) => each.forgetBatch());\n flushSync();\n }\n }\n};\n\n/** Runs `fn` without subscribing the running effect or computed to what it reads. */\nexport const untracked = <T>(fn: () => T): T => {\n const outer = setTracker(null);\n try {\n return fn();\n } finally {\n setTracker(outer);\n }\n};\n\n/** How many effects and computeds read `read` now: tests count them to prove that `dispose` released them. */\nexport const subscriberCount = (read: ReactiveValue<unknown>): number => nodes.get(read)?.observers.size ?? 0;\n"],"mappings":"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"}
package/dist/testing.js CHANGED
@@ -1,2 +1,2 @@
1
- import { o as subscriberCount } from "./signals-D9zxjm2u.js";
1
+ import { o as subscriberCount } from "./signals-MA_mML__.js";
2
2
  export { subscriberCount };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reely/signals",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Signals, computed values and effects: a push-pull graph with owners that release what a piece of work subscribed. No DOM.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"file":"signals-D9zxjm2u.js","names":[],"sources":["../../utils/dist/lib/objects/isInstanceOf.js","../../utils/dist/lib/fp/noop.js","../src/lib/reelx.core.ts"],"sourcesContent":["export default function isInstanceOf(instanceType, instance) {\n return instance instanceof instanceType;\n}\n","export default function noop() {\n return undefined;\n}\n","// A push-pull signal graph: a write marks what may have changed, a read brings a node up to date.\nimport { hasSome } from '@reely/basics';\nimport type { Nullable } from '@reely/utils';\nimport { isInstanceOf, noop } from '@reely/utils';\n\n/** A value read reactively: a signal, a computed or any getter of them; an effect or computed that reads it follows it. */\nexport type ReactiveValue<T> = () => T;\n\n/** How a signal tells a write that changes nothing from one that notifies. */\nexport interface SignalOptions<T> {\n /**\n * Whether a write leaves the value unchanged, so nothing is notified; `Object.is` by default.\n * `() => false` notifies every write, for an object changed in place.\n */\n // a method, not a property: its parameters stay bivariant, so a `Signal<T>` is a `Signal<unknown>`\n equals?(previous: T, next: T): boolean;\n}\n\ninterface Source {\n // grows with every change of the value, so an observer can tell whether what it read is still current\n readonly version: number;\n readonly observers: ReadonlySet<Computation<unknown>>;\n refresh(): void;\n observe(observer: Computation<unknown>): void;\n unobserve(observer: Computation<unknown>): void;\n}\n\ninterface Link {\n version: number;\n readIn: number;\n}\n\n// the value, or what the computation threw\ntype Outcome<T> = { readonly threw: false; readonly value: T } | { readonly threw: true; readonly value: unknown };\n\nconst MAX_FLUSH_WAVES = 100;\n\n// grows with every write: an unlinked computed hears no writes, but one checked at this epoch is current\nlet epoch = 0;\nlet tracker: Nullable<Computation<unknown>> = null;\n// the computation whose `fn` runs now, tracked or not: its own writes never run it again\nlet running: Nullable<Computation<unknown>> = null;\nlet batchDepth = 0;\nlet queue: Effect[] = [];\nlet flushing = false;\n\nconst setTracker = (observer: Nullable<Computation<unknown>>): Nullable<Computation<unknown>> => {\n const outer = tracker;\n tracker = observer;\n return outer;\n};\n\nconst setRunning = (computation: Nullable<Computation<unknown>>): Nullable<Computation<unknown>> => {\n const outer = running;\n running = computation;\n return outer;\n};\n\nclass Signal<T> implements Source {\n public version = 0;\n public readonly observers = new Set<Computation<unknown>>();\n public readonly refresh = noop;\n\n constructor(private value: T, private readonly options: SignalOptions<T>) {}\n\n public readonly read = (): T => {\n tracker?.onRead(this);\n return this.value;\n };\n\n public observe(observer: Computation<unknown>): void {\n this.observers.add(observer);\n }\n\n public unobserve(observer: Computation<unknown>): void {\n this.observers.delete(observer);\n }\n\n public write(value: T): void {\n if (this.options.equals?.(this.value, value) ?? Object.is(this.value, value)) {\n return;\n }\n this.value = value;\n this.version++;\n epoch++;\n running?.onOwnWrite(this);\n const idle = queue.length === 0;\n for (const observer of this.observers) {\n observer.mark(this);\n }\n if (idle && queue.length > 0 && batchDepth === 0 && !flushing) {\n flushSync();\n }\n }\n}\n\n// runs `fn` recording what it reads, and reruns it on demand only when one of those sources changed\nabstract class Computation<T> {\n public version = 0;\n // a source may have changed; the versions of the sources tell whether one did\n protected needsCheck = false;\n // in the order first read\n protected readonly sources = new Map<Source, Link>();\n protected outcome: Nullable<Outcome<T>> = null;\n private runCount = 0;\n private checkedAt = -1;\n private markedAt = -1;\n private deafTo: Nullable<Set<Source>> = null;\n\n constructor(private readonly fn: () => T) {}\n\n // whether its sources hold it, so that their writes reach it\n protected abstract get linked(): boolean;\n\n // `written` is the signal whose write this mark carries\n public mark(written: Source): void {\n // once per write: a node already marked by an earlier write still passes a new one on\n if (this.markedAt === epoch) {\n return;\n }\n this.markedAt = epoch;\n // A run does not depend on a signal it writes, read directly or through a computed: its own\n // write coming back makes it deaf to that signal until it reads it again or runs again.\n if (this === running) {\n this.deafTo ??= new Set();\n this.deafTo.add(written);\n } else if (!this.deafTo?.has(written)) {\n this.onMarked(written);\n }\n }\n\n // what a run writes itself is not a change it missed\n public onOwnWrite(source: Source): void {\n const link = this.sources.get(source);\n if (hasSome(link)) {\n link.version = source.version;\n }\n }\n\n public onRead(source: Source): void {\n this.deafTo?.delete(source);\n const link = this.sources.get(source);\n if (hasSome(link)) {\n link.version = source.version;\n link.readIn = this.runCount;\n return;\n }\n this.sources.set(source, { version: source.version, readIn: this.runCount });\n if (this.linked) {\n source.observe(this);\n }\n }\n\n public refresh(): Outcome<T> {\n const current = this.outcome;\n if (hasSome(current) && !this.needsCheck && (this.linked || this.checkedAt === epoch)) {\n return current;\n }\n this.needsCheck = false;\n this.checkedAt = epoch;\n if (hasSome(current) && !this.sourceChanged()) {\n return current;\n }\n\n const { fn } = this;\n this.runCount++;\n this.deafTo = null;\n const outer = setTracker(this);\n const outerRunning = setRunning(this);\n let next: Outcome<T>;\n try {\n next = { threw: false, value: fn() };\n } catch (error) {\n next = { threw: true, value: error };\n }\n setTracker(outer);\n setRunning(outerRunning);\n this.dropUnread();\n if (!hasSome(current) || current.threw !== next.threw || !Object.is(current.value, next.value)) {\n this.version++;\n }\n this.outcome = next;\n return next;\n }\n\n protected detach(): void {\n for (const source of this.sources.keys()) {\n source.unobserve(this);\n }\n }\n\n private unlink(source: Source): void {\n this.sources.delete(source);\n source.unobserve(this);\n }\n\n private sourceChanged(): boolean {\n for (const [source, { version }] of this.sources) {\n source.refresh();\n if (source.version !== version) {\n return true;\n }\n }\n return false;\n }\n\n private dropUnread(): void {\n for (const [source, link] of this.sources) {\n if (link.readIn !== this.runCount) {\n this.unlink(source);\n }\n }\n }\n\n protected abstract onMarked(written: Source): void;\n}\n\nclass Computed<T> extends Computation<T> implements Source {\n public readonly observers = new Set<Computation<unknown>>();\n\n protected get linked(): boolean {\n return this.observers.size > 0;\n }\n\n public readonly read = (): T => {\n const outcome = this.refresh();\n tracker?.onRead(this);\n if (outcome.threw) {\n throw outcome.value;\n }\n return outcome.value;\n };\n\n public observe(observer: Computation<unknown>): void {\n if (this.observers.size === 0) {\n for (const source of this.sources.keys()) {\n source.observe(this);\n }\n }\n this.observers.add(observer);\n }\n\n public unobserve(observer: Computation<unknown>): void {\n if (this.observers.delete(observer) && this.observers.size === 0) {\n this.detach();\n }\n }\n\n protected onMarked(written: Source): void {\n this.needsCheck = true;\n for (const observer of this.observers) {\n observer.mark(written);\n }\n }\n}\n\n// stays linked until disposed; a mark queues it to run\nclass Effect extends Computation<void> {\n private disposed = false;\n\n protected get linked(): boolean {\n return !this.disposed;\n }\n\n public readonly dispose = (): void => {\n this.disposed = true;\n this.detach();\n this.sources.clear();\n };\n\n // runs it if what it read has changed, and throws what that run threw\n public run(): void {\n if (this.disposed) {\n return;\n }\n const before = this.outcome;\n const after = this.refresh();\n if (after !== before && after.threw) {\n throw after.value;\n }\n }\n\n // taken out of the queue without running: the next write queues it again\n public dequeue(): void {\n this.needsCheck = false;\n }\n\n protected onMarked(): void {\n if (!this.needsCheck) {\n this.needsCheck = true;\n queue.push(this);\n }\n }\n}\n\n/** Runs the queued effects now; what they write joins as the next wave. Every effect runs, the first error is rethrown. */\nconst flushSync = (): void => {\n if (flushing) {\n return;\n }\n flushing = true;\n let failure: Nullable<{ readonly error: unknown }> = null;\n try {\n let wave = 0;\n while (queue.length > 0) {\n wave++;\n if (wave > MAX_FLUSH_WAVES) {\n const dropped = queue;\n queue = [];\n dropped.forEach((effect) => effect.dequeue());\n throw new Error(\n `reelx: a cycle of effects, each writes a signal another one reads (${MAX_FLUSH_WAVES} waves)`,\n { cause: failure?.error }\n );\n }\n const effects = queue;\n queue = [];\n for (const effect of effects) {\n try {\n effect.run();\n } catch (error) {\n failure ??= { error };\n }\n }\n }\n } finally {\n flushing = false;\n }\n if (hasSome(failure)) {\n throw failure.error;\n }\n};\n\n// the node behind each reader, for writes and for `subscriberCount`\nconst nodes = new WeakMap<ReactiveValue<unknown>, Signal<unknown> | Computed<unknown>>();\n\nconst toReactiveValue = <T>(node: Signal<T> | Computed<T>): ReactiveValue<T> => {\n nodes.set(node.read, node);\n return node.read;\n};\n\n/** A value that effects and computeds re-read when it is written; a function is held as a value too. */\nexport const signal = <T>(initial: T, options: SignalOptions<T> = {}): ReactiveValue<T> =>\n toReactiveValue(new Signal(initial, options));\n\n/** A value derived from what `fn` reads, recomputed on read after one of them changes; what `fn` throws is rethrown until then. */\nexport const computed = <T>(fn: () => T): ReactiveValue<T> => toReactiveValue(new Computed(fn));\n\n/** Writes `value` to the signal `read` reads (a computed's reader is ignored); a value its `equals` finds unchanged changes nothing. */\nexport const write = <T>(read: ReactiveValue<T>, value: T): void => {\n const node = nodes.get(read);\n if (isInstanceOf(Signal, node)) {\n node.write(value);\n }\n};\n\n/** Runs `fn` now and again after every change of what it read. Returns `dispose`; creation that throws leaves nothing subscribed. */\nexport const effect = (fn: () => void): (() => void) => {\n const node = new Effect(fn);\n try {\n batch(() => {\n try {\n node.run();\n } catch (error) {\n // before the flush that ends the batch, so it never runs an effect whose creation threw\n node.dispose();\n throw error;\n }\n });\n } catch (error) {\n node.dispose();\n throw error;\n }\n return node.dispose;\n};\n\n/**\n * Calls `cb` with the value of `read` and the one before it: now, unless it is `undefined`, then\n * after every change `read` notifies. Returns `unsubscribe`.\n */\nexport const subscribe = <T>(read: ReactiveValue<T>, cb: (value: T, prevValue?: T) => void): (() => void) => {\n let last: T | undefined;\n let started = false;\n return effect(() => {\n const value = read();\n const previous = last;\n last = value;\n if (started || !Object.is(value, undefined)) {\n untracked(() => cb(value, previous));\n }\n started = true;\n });\n};\n\n/** Groups writes: effects run once, when the outermost `batch` ends, even if `fn` throws. */\nexport const batch = <T>(fn: () => T): T => {\n batchDepth++;\n try {\n return fn();\n } finally {\n if (--batchDepth === 0) {\n flushSync();\n }\n }\n};\n\n/** Runs `fn` without subscribing the running effect or computed to what it reads. */\nexport const untracked = <T>(fn: () => T): T => {\n const outer = setTracker(null);\n try {\n return fn();\n } finally {\n setTracker(outer);\n }\n};\n\n/** How many effects and computeds read `read` now: tests count them to prove that `dispose` released them. */\nexport const subscriberCount = (read: ReactiveValue<unknown>): number => nodes.get(read)?.observers.size ?? 0;\n"],"mappings":"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"}