@reely/signals 0.1.0 → 0.1.1

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
@@ -36,7 +36,7 @@ Effects run synchronously, and a computed recomputes only when it is read after
36
36
  import { signal } from '@reely/signals';
37
37
 
38
38
  const garage = { cars: ['Volvo'] };
39
- const state = signal(garage, { equals: () => false }); // changed in place: every write notifies
39
+ const state = signal(garage, { equals: false }); // changed in place: every write notifies
40
40
  garage.cars.push('Saab');
41
41
  state.value = garage;
42
42
 
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-CHdm5V62.js";
2
2
  import { hasSome } from "@reely/basics";
3
3
  //#region ../utils/dist/lib/objects/setPrototype.js
4
4
  function setPrototype(proto, object) {
@@ -46,7 +46,8 @@ var Signal = class {
46
46
  this.observers.delete(observer);
47
47
  }
48
48
  write(value) {
49
- if (this.options.equals?.(this.value, value) ?? Object.is(this.value, value)) return;
49
+ const { equals = Object.is } = this.options;
50
+ if (equals !== false && equals(this.value, value)) return;
50
51
  this.value = value;
51
52
  this.version++;
52
53
  epoch++;
@@ -290,4 +291,4 @@ var subscriberCount = (read) => nodes.get(read)?.observers.size ?? 0;
290
291
  //#endregion
291
292
  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
293
 
293
- //# sourceMappingURL=signals-D9zxjm2u.js.map
294
+ //# sourceMappingURL=signals-CHdm5V62.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signals-CHdm5V62.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// 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\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 const { equals = Object.is } = this.options;\n if (equals !== false && equals(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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"}
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-CHdm5V62.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.1",
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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"}