@reckona/mreact-reactive-core 0.0.168 → 0.0.170

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.
@@ -0,0 +1,3 @@
1
+ import type { ReadonlyCell } from "./types.js";
2
+ export declare function subscribeCell<T>(cell: ReadonlyCell<T>, listener: (value: T) => void): (() => void) | undefined;
3
+ //# sourceMappingURL=cell-subscription.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cell-subscription.d.ts","sourceRoot":"","sources":["../src/cell-subscription.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAe/C,wBAAgB,aAAa,CAAC,CAAC,EAC7B,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAC3B,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAuB1B"}
@@ -0,0 +1,50 @@
1
+ import { getCellSource } from "./cell.js";
2
+ import { queueComputation } from "./scheduler.js";
3
+ import { runtimeState } from "./state.js";
4
+ import { addSourceSubscriber, removeSourceSubscriber } from "./tracking.js";
5
+ const CELL_SUBSCRIPTION_COMPUTATION_METHODS = {
6
+ markDirty: cellSubscriptionMarkDirty,
7
+ run: cellSubscriptionRun,
8
+ dispose: cellSubscriptionDispose,
9
+ };
10
+ export function subscribeCell(cell, listener) {
11
+ const source = getCellSource(cell);
12
+ if (source === undefined) {
13
+ return undefined;
14
+ }
15
+ const computation = {
16
+ cell,
17
+ dispose: CELL_SUBSCRIPTION_COMPUTATION_METHODS.dispose,
18
+ disposed: false,
19
+ id: runtimeState.nextComputationId,
20
+ listener,
21
+ markDirty: CELL_SUBSCRIPTION_COMPUTATION_METHODS.markDirty,
22
+ queued: false,
23
+ run: CELL_SUBSCRIPTION_COMPUTATION_METHODS.run,
24
+ source,
25
+ };
26
+ runtimeState.nextComputationId += 1;
27
+ addSourceSubscriber(source, computation);
28
+ return () => computation.dispose();
29
+ }
30
+ function cellSubscriptionMarkDirty() {
31
+ queueComputation(this);
32
+ }
33
+ function cellSubscriptionRun() {
34
+ const subscription = this;
35
+ if (subscription.disposed) {
36
+ return;
37
+ }
38
+ subscription.listener(subscription.cell.get());
39
+ }
40
+ function cellSubscriptionDispose() {
41
+ const subscription = this;
42
+ if (subscription.disposed) {
43
+ return;
44
+ }
45
+ subscription.disposed = true;
46
+ subscription.queued = false;
47
+ runtimeState.pendingComputed.delete(subscription);
48
+ removeSourceSubscriber(subscription.source, subscription);
49
+ }
50
+ //# sourceMappingURL=cell-subscription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cell-subscription.js","sourceRoot":"","sources":["../src/cell-subscription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAyC,MAAM,YAAY,CAAC;AAEjF,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAQ5E,MAAM,qCAAqC,GAAG;IAC5C,SAAS,EAAE,yBAAyB;IACpC,GAAG,EAAE,mBAAmB;IACxB,OAAO,EAAE,uBAAuB;CACoC,CAAC;AAEvE,MAAM,UAAU,aAAa,CAC3B,IAAqB,EACrB,QAA4B;IAE5B,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAEnC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,WAAW,GAAwB;QACvC,IAAI;QACJ,OAAO,EAAE,qCAAqC,CAAC,OAAO;QACtD,QAAQ,EAAE,KAAK;QACf,EAAE,EAAE,YAAY,CAAC,iBAAiB;QAClC,QAAQ;QACR,SAAS,EAAE,qCAAqC,CAAC,SAAS;QAC1D,MAAM,EAAE,KAAK;QACb,GAAG,EAAE,qCAAqC,CAAC,GAAG;QAC9C,MAAM;KACP,CAAC;IAEF,YAAY,CAAC,iBAAiB,IAAI,CAAC,CAAC;IACpC,mBAAmB,CAAC,MAAM,EAAE,WAA6C,CAAC,CAAC;IAE3E,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;AACrC,CAAC;AAED,SAAS,yBAAyB;IAChC,gBAAgB,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,mBAAmB;IAC1B,MAAM,YAAY,GAAG,IAA4C,CAAC;IAElE,IAAI,YAAY,CAAC,QAAQ,EAAE,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,YAAY,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,uBAAuB;IAC9B,MAAM,YAAY,GAAG,IAA4C,CAAC;IAElE,IAAI,YAAY,CAAC,QAAQ,EAAE,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,YAAY,CAAC,QAAQ,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,MAAM,GAAG,KAAK,CAAC;IAC5B,YAAY,CAAC,eAAe,CAAC,MAAM,CAAC,YAA8C,CAAC,CAAC;IACpF,sBAAsB,CACpB,YAAY,CAAC,MAAM,EACnB,YAA8C,CAC/C,CAAC;AACJ,CAAC","sourcesContent":["import { getCellSource } from \"./cell.js\";\nimport { queueComputation } from \"./scheduler.js\";\nimport { runtimeState, type ReactiveComputation, type Source } from \"./state.js\";\nimport type { ReadonlyCell } from \"./types.js\";\nimport { addSourceSubscriber, removeSourceSubscriber } from \"./tracking.js\";\n\ninterface CellSubscription<T> extends Omit<ReactiveComputation, \"deps\"> {\n cell: ReadonlyCell<T>;\n listener: (value: T) => void;\n source: Source;\n}\n\nconst CELL_SUBSCRIPTION_COMPUTATION_METHODS = {\n markDirty: cellSubscriptionMarkDirty,\n run: cellSubscriptionRun,\n dispose: cellSubscriptionDispose,\n} satisfies Pick<ReactiveComputation, \"markDirty\" | \"run\" | \"dispose\">;\n\nexport function subscribeCell<T>(\n cell: ReadonlyCell<T>,\n listener: (value: T) => void,\n): (() => void) | undefined {\n const source = getCellSource(cell);\n\n if (source === undefined) {\n return undefined;\n }\n\n const computation: CellSubscription<T> = {\n cell,\n dispose: CELL_SUBSCRIPTION_COMPUTATION_METHODS.dispose,\n disposed: false,\n id: runtimeState.nextComputationId,\n listener,\n markDirty: CELL_SUBSCRIPTION_COMPUTATION_METHODS.markDirty,\n queued: false,\n run: CELL_SUBSCRIPTION_COMPUTATION_METHODS.run,\n source,\n };\n\n runtimeState.nextComputationId += 1;\n addSourceSubscriber(source, computation as unknown as ReactiveComputation);\n\n return () => computation.dispose();\n}\n\nfunction cellSubscriptionMarkDirty(this: ReactiveComputation): void {\n queueComputation(this);\n}\n\nfunction cellSubscriptionRun(this: ReactiveComputation): void {\n const subscription = this as unknown as CellSubscription<unknown>;\n\n if (subscription.disposed) {\n return;\n }\n\n subscription.listener(subscription.cell.get());\n}\n\nfunction cellSubscriptionDispose(this: ReactiveComputation): void {\n const subscription = this as unknown as CellSubscription<unknown>;\n\n if (subscription.disposed) {\n return;\n }\n\n subscription.disposed = true;\n subscription.queued = false;\n runtimeState.pendingComputed.delete(subscription as unknown as ReactiveComputation);\n removeSourceSubscriber(\n subscription.source,\n subscription as unknown as ReactiveComputation,\n );\n}\n"]}
package/dist/cell.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import type { Cell } from "./types.js";
2
+ import type { Source } from "./state.js";
2
3
  export declare function invalidateDevtoolsWriteCache(): void;
4
+ export declare const cellSourceSymbol: unique symbol;
5
+ export declare function getCellSource(value: unknown): Source | undefined;
3
6
  export declare function cell<T>(initial: T): Cell<T>;
4
7
  //# sourceMappingURL=cell.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AA6BvC,wBAAgB,4BAA4B,IAAI,IAAI,CAInD;AA6ED,wBAAgB,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAiB3C"}
1
+ {"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AA4BzC,wBAAgB,4BAA4B,IAAI,IAAI,CAInD;AA+CD,eAAO,MAAM,gBAAgB,EAAE,OAAO,MAErC,CAAC;AAEF,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAIhE;AAmCD,wBAAgB,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAkB3C"}
package/dist/cell.js CHANGED
@@ -46,22 +46,28 @@ function emitCellSetEvent(source, previous, value) {
46
46
  value,
47
47
  });
48
48
  }
49
+ export const cellSourceSymbol = Symbol.for("@reckona/mreact-reactive-core.cellSource");
50
+ export function getCellSource(value) {
51
+ return typeof value === "object" && value !== null
52
+ ? value[cellSourceSymbol]
53
+ : undefined;
54
+ }
49
55
  // One shared write function keeps the hot store/notify sequence in a single
50
56
  // optimizable function instead of a fresh fat closure per cell.
51
- function writeCellValue(state, next) {
52
- const previous = state.value;
57
+ function writeCellValue(source, next) {
58
+ const previous = source.value;
53
59
  const resolved = typeof next === "function" ? next(previous) : next;
54
60
  if (Object.is(previous, resolved)) {
55
61
  return;
56
62
  }
57
- state.value = resolved;
63
+ source.value = resolved;
58
64
  // clientDevtoolsDisabled folds to true under the client build define, which
59
65
  // makes this branch statically dead so bundlers drop the emit path (and its
60
66
  // globalThis.__mreactDevtools references) from production client bundles.
61
67
  if (!clientDevtoolsDisabled && cachedDevtoolsHook !== null) {
62
- emitCellSetEvent(state.source, previous, resolved);
68
+ emitCellSetEvent(source, previous, resolved);
63
69
  }
64
- const subscribers = state.source.subscribers;
70
+ const subscribers = source.subscribers;
65
71
  if (subscribers !== null) {
66
72
  if (runtimeState.batchDepth > 0 && !(subscribers instanceof Set)) {
67
73
  if (!subscribers.disposed && !subscribers.queued) {
@@ -69,24 +75,24 @@ function writeCellValue(state, next) {
69
75
  }
70
76
  return;
71
77
  }
72
- notifySubscribers(state.source);
78
+ notifySubscribers(source);
73
79
  }
74
80
  }
75
81
  export function cell(initial) {
76
- const state = {
77
- source: {
78
- subscribers: null,
79
- },
82
+ const source = {
83
+ subscribers: null,
80
84
  value: initial,
81
85
  };
82
- return {
86
+ const value = {
87
+ [cellSourceSymbol]: source,
83
88
  get() {
84
- trackSource(state.source);
85
- return state.value;
89
+ trackSource(source);
90
+ return source.value;
86
91
  },
87
92
  set(next) {
88
- writeCellValue(state, next);
93
+ writeCellValue(source, next);
89
94
  },
90
95
  };
96
+ return value;
91
97
  }
92
98
  //# sourceMappingURL=cell.js.map
package/dist/cell.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cell.js","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Cell } from \"./types.js\";\nimport type { Source } from \"./state.js\";\nimport { runtimeState } from \"./state.js\";\nimport { notifySubscribers, sourceSubscriberCount, trackSource } from \"./tracking.js\";\n\ndeclare const __MREACT_CLIENT_DEVTOOLS__: boolean | undefined;\n\nconst clientDevtoolsDisabled =\n typeof __MREACT_CLIENT_DEVTOOLS__ !== \"undefined\" &&\n __MREACT_CLIENT_DEVTOOLS__ === false;\n\ninterface DevtoolsHook {\n emit?: ((event: Record<string, unknown>) => void) | undefined;\n}\n\ntype GlobalWithDevtools = typeof globalThis & {\n __mreactDevtools?: DevtoolsHook | undefined;\n};\n\n// Write-path devtools cache: `undefined` = not sampled yet, `null` = sampled\n// and absent, object = sampled and attached. The no-devtools write fast path\n// is a single module-local null comparison instead of a globalThis property\n// walk per write. A late attach is observed at the next batch or flush\n// boundary (see invalidateDevtoolsWriteCache callers); a detach or hook swap\n// is observed on the next write because the emit path revalidates identity.\nlet cachedDevtoolsHook: DevtoolsHook | null | undefined = clientDevtoolsDisabled\n ? null\n : undefined;\n\nexport function invalidateDevtoolsWriteCache(): void {\n if (!clientDevtoolsDisabled) {\n cachedDevtoolsHook = undefined;\n }\n}\n\nfunction resolveDevtoolsHook(): DevtoolsHook | null {\n const hook = (globalThis as GlobalWithDevtools).__mreactDevtools;\n const resolved = hook !== undefined && typeof hook.emit === \"function\" ? hook : null;\n cachedDevtoolsHook = resolved;\n return resolved;\n}\n\nfunction emitCellSetEvent<T>(source: Source, previous: T, value: T): void {\n // Cold path: only reached while a devtools hook is (or was) attached, or on\n // the first write after a cache invalidation. Revalidate against the live\n // global so a disposed or swapped hook never receives stale events.\n const live = (globalThis as GlobalWithDevtools).__mreactDevtools;\n const hook =\n cachedDevtoolsHook !== undefined && cachedDevtoolsHook === live\n ? cachedDevtoolsHook\n : resolveDevtoolsHook();\n\n if (hook === null) {\n return;\n }\n\n const emit = hook.emit;\n\n if (typeof emit !== \"function\") {\n return;\n }\n\n emit.call(hook, {\n package: \"@reckona/mreact-reactive-core\",\n previous,\n subscribers: sourceSubscriberCount(source),\n timestamp: Date.now(),\n type: \"reactive:cell:set\",\n value,\n });\n}\n\ninterface CellState<T> {\n value: T;\n readonly source: Source;\n}\n\n// One shared write function keeps the hot store/notify sequence in a single\n// optimizable function instead of a fresh fat closure per cell.\nfunction writeCellValue<T>(state: CellState<T>, next: T | ((prev: T) => T)): void {\n const previous = state.value;\n const resolved =\n typeof next === \"function\" ? (next as (prev: T) => T)(previous) : next;\n\n if (Object.is(previous, resolved)) {\n return;\n }\n\n state.value = resolved;\n\n // clientDevtoolsDisabled folds to true under the client build define, which\n // makes this branch statically dead so bundlers drop the emit path (and its\n // globalThis.__mreactDevtools references) from production client bundles.\n if (!clientDevtoolsDisabled && cachedDevtoolsHook !== null) {\n emitCellSetEvent(state.source, previous, resolved);\n }\n\n const subscribers = state.source.subscribers;\n if (subscribers !== null) {\n if (runtimeState.batchDepth > 0 && !(subscribers instanceof Set)) {\n if (!subscribers.disposed && !subscribers.queued) {\n subscribers.markDirty();\n }\n return;\n }\n\n notifySubscribers(state.source);\n }\n}\n\nexport function cell<T>(initial: T): Cell<T> {\n const state: CellState<T> = {\n source: {\n subscribers: null,\n },\n value: initial,\n };\n\n return {\n get(): T {\n trackSource(state.source);\n return state.value;\n },\n set(next: T | ((prev: T) => T)): void {\n writeCellValue(state, next);\n },\n };\n}\n"]}
1
+ {"version":3,"file":"cell.js","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Cell } from \"./types.js\";\nimport type { Source } from \"./state.js\";\nimport { runtimeState } from \"./state.js\";\nimport { notifySubscribers, sourceSubscriberCount, trackSource } from \"./tracking.js\";\n\ndeclare const __MREACT_CLIENT_DEVTOOLS__: boolean | undefined;\n\nconst clientDevtoolsDisabled =\n typeof __MREACT_CLIENT_DEVTOOLS__ !== \"undefined\" &&\n __MREACT_CLIENT_DEVTOOLS__ === false;\n\ninterface DevtoolsHook {\n emit?: ((event: Record<string, unknown>) => void) | undefined;\n}\n\ntype GlobalWithDevtools = typeof globalThis & {\n __mreactDevtools?: DevtoolsHook | undefined;\n};\n\n// Write-path devtools cache: `undefined` = not sampled yet, `null` = sampled\n// and absent, object = sampled and attached. The no-devtools write fast path\n// is a single module-local null comparison instead of a globalThis property\n// walk per write. A late attach is observed at the next batch or flush\n// boundary (see invalidateDevtoolsWriteCache callers); a detach or hook swap\n// is observed on the next write because the emit path revalidates identity.\nlet cachedDevtoolsHook: DevtoolsHook | null | undefined = clientDevtoolsDisabled\n ? null\n : undefined;\n\nexport function invalidateDevtoolsWriteCache(): void {\n if (!clientDevtoolsDisabled) {\n cachedDevtoolsHook = undefined;\n }\n}\n\nfunction resolveDevtoolsHook(): DevtoolsHook | null {\n const hook = (globalThis as GlobalWithDevtools).__mreactDevtools;\n const resolved = hook !== undefined && typeof hook.emit === \"function\" ? hook : null;\n cachedDevtoolsHook = resolved;\n return resolved;\n}\n\nfunction emitCellSetEvent<T>(source: Source, previous: T, value: T): void {\n // Cold path: only reached while a devtools hook is (or was) attached, or on\n // the first write after a cache invalidation. Revalidate against the live\n // global so a disposed or swapped hook never receives stale events.\n const live = (globalThis as GlobalWithDevtools).__mreactDevtools;\n const hook =\n cachedDevtoolsHook !== undefined && cachedDevtoolsHook === live\n ? cachedDevtoolsHook\n : resolveDevtoolsHook();\n\n if (hook === null) {\n return;\n }\n\n const emit = hook.emit;\n\n if (typeof emit !== \"function\") {\n return;\n }\n\n emit.call(hook, {\n package: \"@reckona/mreact-reactive-core\",\n previous,\n subscribers: sourceSubscriberCount(source),\n timestamp: Date.now(),\n type: \"reactive:cell:set\",\n value,\n });\n}\n\ninterface CellSource<T> extends Source {\n value: T;\n}\n\ntype SourceBackedCell<T> = Cell<T> & {\n readonly [cellSourceSymbol]: Source;\n};\n\nexport const cellSourceSymbol: unique symbol = Symbol.for(\n \"@reckona/mreact-reactive-core.cellSource\",\n);\n\nexport function getCellSource(value: unknown): Source | undefined {\n return typeof value === \"object\" && value !== null\n ? (value as { [cellSourceSymbol]?: Source })[cellSourceSymbol]\n : undefined;\n}\n\n// One shared write function keeps the hot store/notify sequence in a single\n// optimizable function instead of a fresh fat closure per cell.\nfunction writeCellValue<T>(source: CellSource<T>, next: T | ((prev: T) => T)): void {\n const previous = source.value;\n const resolved =\n typeof next === \"function\" ? (next as (prev: T) => T)(previous) : next;\n\n if (Object.is(previous, resolved)) {\n return;\n }\n\n source.value = resolved;\n\n // clientDevtoolsDisabled folds to true under the client build define, which\n // makes this branch statically dead so bundlers drop the emit path (and its\n // globalThis.__mreactDevtools references) from production client bundles.\n if (!clientDevtoolsDisabled && cachedDevtoolsHook !== null) {\n emitCellSetEvent(source, previous, resolved);\n }\n\n const subscribers = source.subscribers;\n if (subscribers !== null) {\n if (runtimeState.batchDepth > 0 && !(subscribers instanceof Set)) {\n if (!subscribers.disposed && !subscribers.queued) {\n subscribers.markDirty();\n }\n return;\n }\n\n notifySubscribers(source);\n }\n}\n\nexport function cell<T>(initial: T): Cell<T> {\n const source: CellSource<T> = {\n subscribers: null,\n value: initial,\n };\n\n const value: SourceBackedCell<T> = {\n [cellSourceSymbol]: source,\n get(): T {\n trackSource(source);\n return source.value;\n },\n set(next: T | ((prev: T) => T)): void {\n writeCellValue(source, next);\n },\n };\n\n return value;\n}\n"]}
package/dist/computed.js CHANGED
@@ -92,7 +92,7 @@ export function computed(fn, options) {
92
92
  const trackedCount = computation.trackingCount ?? 0;
93
93
  const addedDepsCount = addedDeps?.length ?? 0;
94
94
  const orderedMismatch = computation.trackingOrderedMismatch;
95
- if (trackedCount !== previousDepsSize || addedDepsCount > 0) {
95
+ if (previousDepsSize > 0 && (trackedCount !== previousDepsSize || addedDepsCount > 0)) {
96
96
  const orderedIndex = computation.trackingOrderedIndex;
97
97
  if (computation.trackingTouchedDeps === undefined &&
98
98
  orderedIndex !== undefined &&
@@ -1 +1 @@
1
- {"version":3,"file":"computed.js","sourceRoot":"","sources":["../src/computed.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveComputation, Source } from \"./state.js\";\nimport { schedulePendingFlush } from \"./scheduler.js\";\nimport { runtimeState } from \"./state.js\";\nimport {\n cleanupAddedDeps,\n cleanupDeps,\n cleanupUntrackedDeps,\n nextTrackingVersionFor,\n notifySubscribers,\n preserveIncrementalTracking,\n trackIncrementalSource,\n trackSource,\n} from \"./tracking.js\";\nimport type { ReadonlyCell } from \"./types.js\";\n\n/** Equality function used to decide whether a computed value changed. */\nexport type ComputedEquality<T> = (previous: T, next: T) => boolean;\n\n/** Options for creating a computed reactive value. */\nexport interface ComputedOptions<T> {\n equals?: ComputedEquality<T> | undefined;\n}\n\n/** Creates a lazily evaluated reactive value derived from other cells. */\nexport function computed<T>(\n fn: () => T,\n options?: ComputedOptions<T> | ComputedEquality<T>,\n): ReadonlyCell<T> {\n let hasValue = false;\n let value: T;\n let dirty = true;\n const equals = typeof options === \"function\" ? options : (options?.equals ?? Object.is);\n\n const source: Source = {\n subscribers: null,\n };\n\n const computation: ReactiveComputation = {\n id: runtimeState.nextComputationId,\n deps: new Set(),\n disposed: false,\n queued: false,\n markDirty() {\n if (dirty) {\n if (source.subscribers === null || computation.queued) {\n return;\n }\n }\n\n dirty = true;\n\n if (source.subscribers !== null) {\n if (runtimeState.notificationDepth > 0 || runtimeState.batchDepth > 0) {\n computation.queued = true;\n runtimeState.pendingComputed.add(computation);\n return;\n }\n\n publishIfChanged();\n }\n },\n run() {\n publishIfChanged();\n },\n trackSource(source) {\n trackIncrementalSource(source, computation);\n },\n dispose() {\n if (computation.disposed) {\n return;\n }\n\n computation.disposed = true;\n computation.queued = false;\n runtimeState.pendingComputed.delete(computation);\n cleanupDeps(computation);\n computation.orderedDeps = undefined;\n source.subscribers = null;\n },\n };\n\n runtimeState.nextComputationId += 1;\n\n function publishIfChanged(): void {\n const previousHasValue = hasValue;\n const previousValue = value;\n\n try {\n const nextValue = recompute();\n\n if (!previousHasValue || !equals(previousValue, nextValue)) {\n notifySubscribers(source);\n }\n } catch {\n runtimeState.batchDepth += 1;\n\n try {\n notifySubscribers(source);\n } finally {\n runtimeState.batchDepth -= 1;\n\n if (runtimeState.batchDepth === 0) {\n schedulePendingFlush();\n }\n }\n }\n }\n\n function recompute(): T {\n if (!dirty && hasValue) {\n return value;\n }\n\n const previousTracker = runtimeState.activeTracker;\n const previousDepsSize = computation.deps.size;\n const nextTrackingVersion = nextTrackingVersionFor(computation);\n\n computation.trackingAddedDeps = undefined;\n computation.trackingCount = 0;\n computation.trackingOrderedIndex =\n computation.orderedDeps === undefined ? undefined : 0;\n computation.trackingOrderedMismatch = false;\n computation.trackingVersion = nextTrackingVersion;\n runtimeState.activeTracker = computation;\n\n try {\n const nextValue = fn();\n\n const addedDeps = computation.trackingAddedDeps as Source[] | undefined;\n const trackedCount = computation.trackingCount ?? 0;\n const addedDepsCount = addedDeps?.length ?? 0;\n const orderedMismatch = computation.trackingOrderedMismatch as boolean | undefined;\n\n if (trackedCount !== previousDepsSize || addedDepsCount > 0) {\n const orderedIndex = computation.trackingOrderedIndex;\n\n if (\n computation.trackingTouchedDeps === undefined &&\n orderedIndex !== undefined &&\n orderedIndex > 0 &&\n computation.orderedDeps !== undefined\n ) {\n computation.trackingTouchedDeps = computation.orderedDeps.slice(0, orderedIndex);\n }\n\n cleanupUntrackedDeps(computation, nextTrackingVersion);\n }\n\n if (\n orderedMismatch !== true &&\n trackedCount === previousDepsSize &&\n addedDepsCount === 0\n ) {\n // Keep the previous stable order.\n } else if (\n previousDepsSize === 0 &&\n addedDeps !== undefined &&\n trackedCount === addedDeps.length\n ) {\n computation.orderedDeps = addedDeps;\n } else {\n computation.orderedDeps = undefined;\n }\n\n value = nextValue;\n hasValue = true;\n dirty = false;\n\n return value;\n } catch (error) {\n cleanupAddedDeps(computation);\n dirty = true;\n\n throw error;\n } finally {\n computation.trackingAddedDeps = undefined;\n computation.trackingCount = undefined;\n computation.trackingOrderedIndex = undefined;\n computation.trackingOrderedMismatch = undefined;\n computation.trackingTouchedDeps = undefined;\n runtimeState.activeTracker = previousTracker;\n }\n }\n\n return {\n get(): T {\n trackSource(source);\n\n if (dirty) {\n const activeTracker = runtimeState.activeTracker;\n\n if (activeTracker !== null && activeTracker !== computation) {\n preserveIncrementalTracking(activeTracker);\n }\n }\n\n return recompute();\n },\n };\n}\n"]}
1
+ {"version":3,"file":"computed.js","sourceRoot":"","sources":["../src/computed.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveComputation, Source } from \"./state.js\";\nimport { schedulePendingFlush } from \"./scheduler.js\";\nimport { runtimeState } from \"./state.js\";\nimport {\n cleanupAddedDeps,\n cleanupDeps,\n cleanupUntrackedDeps,\n nextTrackingVersionFor,\n notifySubscribers,\n preserveIncrementalTracking,\n trackIncrementalSource,\n trackSource,\n} from \"./tracking.js\";\nimport type { ReadonlyCell } from \"./types.js\";\n\n/** Equality function used to decide whether a computed value changed. */\nexport type ComputedEquality<T> = (previous: T, next: T) => boolean;\n\n/** Options for creating a computed reactive value. */\nexport interface ComputedOptions<T> {\n equals?: ComputedEquality<T> | undefined;\n}\n\n/** Creates a lazily evaluated reactive value derived from other cells. */\nexport function computed<T>(\n fn: () => T,\n options?: ComputedOptions<T> | ComputedEquality<T>,\n): ReadonlyCell<T> {\n let hasValue = false;\n let value: T;\n let dirty = true;\n const equals = typeof options === \"function\" ? options : (options?.equals ?? Object.is);\n\n const source: Source = {\n subscribers: null,\n };\n\n const computation: ReactiveComputation = {\n id: runtimeState.nextComputationId,\n deps: new Set(),\n disposed: false,\n queued: false,\n markDirty() {\n if (dirty) {\n if (source.subscribers === null || computation.queued) {\n return;\n }\n }\n\n dirty = true;\n\n if (source.subscribers !== null) {\n if (runtimeState.notificationDepth > 0 || runtimeState.batchDepth > 0) {\n computation.queued = true;\n runtimeState.pendingComputed.add(computation);\n return;\n }\n\n publishIfChanged();\n }\n },\n run() {\n publishIfChanged();\n },\n trackSource(source) {\n trackIncrementalSource(source, computation);\n },\n dispose() {\n if (computation.disposed) {\n return;\n }\n\n computation.disposed = true;\n computation.queued = false;\n runtimeState.pendingComputed.delete(computation);\n cleanupDeps(computation);\n computation.orderedDeps = undefined;\n source.subscribers = null;\n },\n };\n\n runtimeState.nextComputationId += 1;\n\n function publishIfChanged(): void {\n const previousHasValue = hasValue;\n const previousValue = value;\n\n try {\n const nextValue = recompute();\n\n if (!previousHasValue || !equals(previousValue, nextValue)) {\n notifySubscribers(source);\n }\n } catch {\n runtimeState.batchDepth += 1;\n\n try {\n notifySubscribers(source);\n } finally {\n runtimeState.batchDepth -= 1;\n\n if (runtimeState.batchDepth === 0) {\n schedulePendingFlush();\n }\n }\n }\n }\n\n function recompute(): T {\n if (!dirty && hasValue) {\n return value;\n }\n\n const previousTracker = runtimeState.activeTracker;\n const previousDepsSize = computation.deps.size;\n const nextTrackingVersion = nextTrackingVersionFor(computation);\n\n computation.trackingAddedDeps = undefined;\n computation.trackingCount = 0;\n computation.trackingOrderedIndex =\n computation.orderedDeps === undefined ? undefined : 0;\n computation.trackingOrderedMismatch = false;\n computation.trackingVersion = nextTrackingVersion;\n runtimeState.activeTracker = computation;\n\n try {\n const nextValue = fn();\n\n const addedDeps = computation.trackingAddedDeps as Source[] | undefined;\n const trackedCount = computation.trackingCount ?? 0;\n const addedDepsCount = addedDeps?.length ?? 0;\n const orderedMismatch = computation.trackingOrderedMismatch as boolean | undefined;\n\n if (previousDepsSize > 0 && (trackedCount !== previousDepsSize || addedDepsCount > 0)) {\n const orderedIndex = computation.trackingOrderedIndex;\n\n if (\n computation.trackingTouchedDeps === undefined &&\n orderedIndex !== undefined &&\n orderedIndex > 0 &&\n computation.orderedDeps !== undefined\n ) {\n computation.trackingTouchedDeps = computation.orderedDeps.slice(0, orderedIndex);\n }\n\n cleanupUntrackedDeps(computation, nextTrackingVersion);\n }\n\n if (\n orderedMismatch !== true &&\n trackedCount === previousDepsSize &&\n addedDepsCount === 0\n ) {\n // Keep the previous stable order.\n } else if (\n previousDepsSize === 0 &&\n addedDeps !== undefined &&\n trackedCount === addedDeps.length\n ) {\n computation.orderedDeps = addedDeps;\n } else {\n computation.orderedDeps = undefined;\n }\n\n value = nextValue;\n hasValue = true;\n dirty = false;\n\n return value;\n } catch (error) {\n cleanupAddedDeps(computation);\n dirty = true;\n\n throw error;\n } finally {\n computation.trackingAddedDeps = undefined;\n computation.trackingCount = undefined;\n computation.trackingOrderedIndex = undefined;\n computation.trackingOrderedMismatch = undefined;\n computation.trackingTouchedDeps = undefined;\n runtimeState.activeTracker = previousTracker;\n }\n }\n\n return {\n get(): T {\n trackSource(source);\n\n if (dirty) {\n const activeTracker = runtimeState.activeTracker;\n\n if (activeTracker !== null && activeTracker !== computation) {\n preserveIncrementalTracking(activeTracker);\n }\n }\n\n return recompute();\n },\n };\n}\n"]}
package/dist/effect.js CHANGED
@@ -28,9 +28,9 @@ export function effect(fn) {
28
28
  }
29
29
  const previousDepsSize = computation.deps.size;
30
30
  const nextTrackingVersion = nextTrackingVersionFor(computation);
31
- computation.trackingAddedDeps = [];
31
+ computation.trackingAddedDeps = undefined;
32
32
  computation.trackingCount = 0;
33
- computation.trackingTouchedDeps = [];
33
+ computation.trackingTouchedDeps = undefined;
34
34
  computation.trackingVersion = nextTrackingVersion;
35
35
  runtimeState.activeTracker = computation;
36
36
  if (clientDevtoolsDisabled) {
@@ -105,7 +105,8 @@ export function effect(fn) {
105
105
  function finishIncrementalTracking(computation, previousDepsSize, trackingVersion) {
106
106
  const addedDeps = computation.trackingAddedDeps;
107
107
  const trackedCount = computation.trackingCount ?? 0;
108
- if (trackedCount !== previousDepsSize || (addedDeps?.length ?? 0) > 0) {
108
+ if (previousDepsSize > 0 &&
109
+ (trackedCount !== previousDepsSize || (addedDeps?.length ?? 0) > 0)) {
109
110
  cleanupUntrackedDeps(computation, trackingVersion);
110
111
  }
111
112
  computation.trackingAddedDeps = undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"effect.js","sourceRoot":"","sources":["../src/effect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,YAAY,EAA4B,MAAM,YAAY,CAAC;AACpE,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAIvB,MAAM,sBAAsB,GAC1B,OAAO,0BAA0B,KAAK,WAAW;IACjD,0BAA0B,KAAK,KAAK,CAAC;AAEvC,0DAA0D;AAC1D,MAAM,UAAU,MAAM,CAAC,EAA6B;IAClD,IAAI,OAAiC,CAAC;IAEtC,MAAM,WAAW,GAAwB;QACvC,EAAE,EAAE,YAAY,CAAC,iBAAiB;QAClC,IAAI,EAAE,IAAI,GAAG,EAAE;QACf,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,KAAK;QACb,SAAS;YACP,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAChC,CAAC;QACD,GAAG;YACD,IAAI,WAAW,CAAC,QAAQ,EAAE,CAAC;gBACzB,OAAO;YACT,CAAC;YAED,MAAM,eAAe,GAAG,YAAY,CAAC,aAAa,CAAC;YAEnD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,cAAc,GAAG,OAAO,CAAC;gBAC/B,cAAc,EAAE,CAAC;gBACjB,OAAO,GAAG,SAAS,CAAC;YACtB,CAAC;YAED,MAAM,gBAAgB,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;YAC/C,MAAM,mBAAmB,GAAG,sBAAsB,CAAC,WAAW,CAAC,CAAC;YAEhE,WAAW,CAAC,iBAAiB,GAAG,EAAE,CAAC;YACnC,WAAW,CAAC,aAAa,GAAG,CAAC,CAAC;YAC9B,WAAW,CAAC,mBAAmB,GAAG,EAAE,CAAC;YACrC,WAAW,CAAC,eAAe,GAAG,mBAAmB,CAAC;YAClD,YAAY,CAAC,aAAa,GAAG,WAAW,CAAC;YAEzC,IAAI,sBAAsB,EAAE,CAAC;gBAC3B,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,EAAE,EAAE,CAAC;oBACpB,OAAO,GAAG,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC9D,CAAC;wBAAS,CAAC;oBACT,yBAAyB,CAAC,WAAW,EAAE,gBAAgB,EAAE,mBAAmB,CAAC,CAAC;oBAC9E,YAAY,CAAC,aAAa,GAAG,eAAe,CAAC;gBAC/C,CAAC;gBACD,OAAO;YACT,CAAC;YAED,MAAM,QAAQ,GAAG,uBAAuB,EAAE,CAAC;YAC3C,MAAM,IAAI,GAAG,QAAQ,EAAE,IAAI,CAAC;YAC5B,MAAM,SAAS,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC;YAE5D,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,EAAE,EAAE,CAAC;gBACpB,OAAO,GAAG,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;YAC9D,CAAC;oBAAS,CAAC;gBACT,yBAAyB,CAAC,WAAW,EAAE,gBAAgB,EAAE,mBAAmB,CAAC,CAAC;gBAC9E,YAAY,CAAC,aAAa,GAAG,eAAe,CAAC;gBAC7C,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;oBAC/B,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;wBAClB,UAAU,EAAE,cAAc,EAAE,GAAG,SAAS;wBACxC,EAAE,EAAE,WAAW,CAAC,EAAE;wBAClB,OAAO,EAAE,+BAA+B;wBACxC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;wBACrB,IAAI,EAAE,qBAAqB;qBAC5B,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QACD,WAAW,CAAC,MAAM;YAChB,sBAAsB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO;YACL,IAAI,WAAW,CAAC,QAAQ,EAAE,CAAC;gBACzB,OAAO;YACT,CAAC;YAED,WAAW,CAAC,QAAQ,GAAG,IAAI,CAAC;YAC5B,WAAW,CAAC,MAAM,GAAG,KAAK,CAAC;YAC3B,YAAY,CAAC,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YACjD,WAAW,CAAC,WAAW,CAAC,CAAC;YAEzB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,cAAc,GAAG,OAAO,CAAC;gBAC/B,OAAO,GAAG,SAAS,CAAC;gBACpB,cAAc,EAAE,CAAC;YACnB,CAAC;QACH,CAAC;KACF,CAAC;IAEF,YAAY,CAAC,iBAAiB,IAAI,CAAC,CAAC;IAEpC,IAAI,CAAC;QACH,WAAW,CAAC,GAAG,EAAE,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,WAAW,CAAC,QAAQ,GAAG,IAAI,CAAC;QAC5B,WAAW,CAAC,MAAM,GAAG,KAAK,CAAC;QAC3B,YAAY,CAAC,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACjD,WAAW,CAAC,WAAW,CAAC,CAAC;QAEzB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,cAAc,GAAG,OAAO,CAAC;YAC/B,OAAO,GAAG,SAAS,CAAC;YACpB,cAAc,EAAE,CAAC;QACnB,CAAC;QAED,MAAM,KAAK,CAAC;IACd,CAAC;IAED,eAAe,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC;AAED,SAAS,yBAAyB,CAChC,WAAgC,EAChC,gBAAwB,EACxB,eAAuB;IAEvB,MAAM,SAAS,GAAG,WAAW,CAAC,iBAAiB,CAAC;IAChD,MAAM,YAAY,GAAG,WAAW,CAAC,aAAa,IAAI,CAAC,CAAC;IAEpD,IAAI,YAAY,KAAK,gBAAgB,IAAI,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;QACtE,oBAAoB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IACrD,CAAC;IAED,WAAW,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAC1C,WAAW,CAAC,aAAa,GAAG,SAAS,CAAC;IACtC,WAAW,CAAC,mBAAmB,GAAG,SAAS,CAAC;AAC9C,CAAC;AAED,SAAS,cAAc;IACrB,OAAO,OAAO,WAAW,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC;AAC7E,CAAC","sourcesContent":["import { queueComputation } from \"./scheduler.js\";\nimport { currentReactiveDevtools } from \"./devtools.js\";\nimport { registerCleanup } from \"./cleanup-scope.js\";\nimport { runtimeState, type ReactiveComputation } from \"./state.js\";\nimport {\n cleanupDeps,\n cleanupUntrackedDeps,\n nextTrackingVersionFor,\n trackIncrementalSource,\n} from \"./tracking.js\";\n\ndeclare const __MREACT_CLIENT_DEVTOOLS__: boolean | undefined;\n\nconst clientDevtoolsDisabled =\n typeof __MREACT_CLIENT_DEVTOOLS__ !== \"undefined\" &&\n __MREACT_CLIENT_DEVTOOLS__ === false;\n\n/** Runs a reactive side effect and returns a disposer. */\nexport function effect(fn: () => void | (() => void)): () => void {\n let cleanup: (() => void) | undefined;\n\n const computation: ReactiveComputation = {\n id: runtimeState.nextComputationId,\n deps: new Set(),\n disposed: false,\n queued: false,\n markDirty() {\n queueComputation(computation);\n },\n run() {\n if (computation.disposed) {\n return;\n }\n\n const previousTracker = runtimeState.activeTracker;\n\n if (cleanup !== undefined) {\n const currentCleanup = cleanup;\n currentCleanup();\n cleanup = undefined;\n }\n\n const previousDepsSize = computation.deps.size;\n const nextTrackingVersion = nextTrackingVersionFor(computation);\n\n computation.trackingAddedDeps = [];\n computation.trackingCount = 0;\n computation.trackingTouchedDeps = [];\n computation.trackingVersion = nextTrackingVersion;\n runtimeState.activeTracker = computation;\n\n if (clientDevtoolsDisabled) {\n try {\n const result = fn();\n cleanup = typeof result === \"function\" ? result : undefined;\n } finally {\n finishIncrementalTracking(computation, previousDepsSize, nextTrackingVersion);\n runtimeState.activeTracker = previousTracker;\n }\n return;\n }\n\n const devtools = currentReactiveDevtools();\n const emit = devtools?.emit;\n const startedAt = emit === undefined ? 0 : performanceNow();\n\n try {\n const result = fn();\n cleanup = typeof result === \"function\" ? result : undefined;\n } finally {\n finishIncrementalTracking(computation, previousDepsSize, nextTrackingVersion);\n runtimeState.activeTracker = previousTracker;\n if (typeof emit === \"function\") {\n emit.call(devtools, {\n durationMs: performanceNow() - startedAt,\n id: computation.id,\n package: \"@reckona/mreact-reactive-core\",\n timestamp: Date.now(),\n type: \"reactive:effect:run\",\n });\n }\n }\n },\n trackSource(source) {\n trackIncrementalSource(source, computation);\n },\n dispose() {\n if (computation.disposed) {\n return;\n }\n\n computation.disposed = true;\n computation.queued = false;\n runtimeState.pendingComputed.delete(computation);\n cleanupDeps(computation);\n\n if (cleanup !== undefined) {\n const currentCleanup = cleanup;\n cleanup = undefined;\n currentCleanup();\n }\n },\n };\n\n runtimeState.nextComputationId += 1;\n\n try {\n computation.run();\n } catch (error) {\n computation.disposed = true;\n computation.queued = false;\n runtimeState.pendingComputed.delete(computation);\n cleanupDeps(computation);\n\n if (cleanup !== undefined) {\n const currentCleanup = cleanup;\n cleanup = undefined;\n currentCleanup();\n }\n\n throw error;\n }\n\n registerCleanup(computation.dispose);\n return computation.dispose;\n}\n\nfunction finishIncrementalTracking(\n computation: ReactiveComputation,\n previousDepsSize: number,\n trackingVersion: number,\n): void {\n const addedDeps = computation.trackingAddedDeps;\n const trackedCount = computation.trackingCount ?? 0;\n\n if (trackedCount !== previousDepsSize || (addedDeps?.length ?? 0) > 0) {\n cleanupUntrackedDeps(computation, trackingVersion);\n }\n\n computation.trackingAddedDeps = undefined;\n computation.trackingCount = undefined;\n computation.trackingTouchedDeps = undefined;\n}\n\nfunction performanceNow(): number {\n return typeof performance === \"undefined\" ? Date.now() : performance.now();\n}\n"]}
1
+ {"version":3,"file":"effect.js","sourceRoot":"","sources":["../src/effect.ts"],"names":[],"mappings":"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","sourcesContent":["import { queueComputation } from \"./scheduler.js\";\nimport { currentReactiveDevtools } from \"./devtools.js\";\nimport { registerCleanup } from \"./cleanup-scope.js\";\nimport { runtimeState, type ReactiveComputation } from \"./state.js\";\nimport {\n cleanupDeps,\n cleanupUntrackedDeps,\n nextTrackingVersionFor,\n trackIncrementalSource,\n} from \"./tracking.js\";\n\ndeclare const __MREACT_CLIENT_DEVTOOLS__: boolean | undefined;\n\nconst clientDevtoolsDisabled =\n typeof __MREACT_CLIENT_DEVTOOLS__ !== \"undefined\" &&\n __MREACT_CLIENT_DEVTOOLS__ === false;\n\n/** Runs a reactive side effect and returns a disposer. */\nexport function effect(fn: () => void | (() => void)): () => void {\n let cleanup: (() => void) | undefined;\n\n const computation: ReactiveComputation = {\n id: runtimeState.nextComputationId,\n deps: new Set(),\n disposed: false,\n queued: false,\n markDirty() {\n queueComputation(computation);\n },\n run() {\n if (computation.disposed) {\n return;\n }\n\n const previousTracker = runtimeState.activeTracker;\n\n if (cleanup !== undefined) {\n const currentCleanup = cleanup;\n currentCleanup();\n cleanup = undefined;\n }\n\n const previousDepsSize = computation.deps.size;\n const nextTrackingVersion = nextTrackingVersionFor(computation);\n\n computation.trackingAddedDeps = undefined;\n computation.trackingCount = 0;\n computation.trackingTouchedDeps = undefined;\n computation.trackingVersion = nextTrackingVersion;\n runtimeState.activeTracker = computation;\n\n if (clientDevtoolsDisabled) {\n try {\n const result = fn();\n cleanup = typeof result === \"function\" ? result : undefined;\n } finally {\n finishIncrementalTracking(computation, previousDepsSize, nextTrackingVersion);\n runtimeState.activeTracker = previousTracker;\n }\n return;\n }\n\n const devtools = currentReactiveDevtools();\n const emit = devtools?.emit;\n const startedAt = emit === undefined ? 0 : performanceNow();\n\n try {\n const result = fn();\n cleanup = typeof result === \"function\" ? result : undefined;\n } finally {\n finishIncrementalTracking(computation, previousDepsSize, nextTrackingVersion);\n runtimeState.activeTracker = previousTracker;\n if (typeof emit === \"function\") {\n emit.call(devtools, {\n durationMs: performanceNow() - startedAt,\n id: computation.id,\n package: \"@reckona/mreact-reactive-core\",\n timestamp: Date.now(),\n type: \"reactive:effect:run\",\n });\n }\n }\n },\n trackSource(source) {\n trackIncrementalSource(source, computation);\n },\n dispose() {\n if (computation.disposed) {\n return;\n }\n\n computation.disposed = true;\n computation.queued = false;\n runtimeState.pendingComputed.delete(computation);\n cleanupDeps(computation);\n\n if (cleanup !== undefined) {\n const currentCleanup = cleanup;\n cleanup = undefined;\n currentCleanup();\n }\n },\n };\n\n runtimeState.nextComputationId += 1;\n\n try {\n computation.run();\n } catch (error) {\n computation.disposed = true;\n computation.queued = false;\n runtimeState.pendingComputed.delete(computation);\n cleanupDeps(computation);\n\n if (cleanup !== undefined) {\n const currentCleanup = cleanup;\n cleanup = undefined;\n currentCleanup();\n }\n\n throw error;\n }\n\n registerCleanup(computation.dispose);\n return computation.dispose;\n}\n\nfunction finishIncrementalTracking(\n computation: ReactiveComputation,\n previousDepsSize: number,\n trackingVersion: number,\n): void {\n const addedDeps = computation.trackingAddedDeps;\n const trackedCount = computation.trackingCount ?? 0;\n\n if (\n previousDepsSize > 0 &&\n (trackedCount !== previousDepsSize || (addedDeps?.length ?? 0) > 0)\n ) {\n cleanupUntrackedDeps(computation, trackingVersion);\n }\n\n computation.trackingAddedDeps = undefined;\n computation.trackingCount = undefined;\n computation.trackingTouchedDeps = undefined;\n}\n\nfunction performanceNow(): number {\n return typeof performance === \"undefined\" ? Date.now() : performance.now();\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -4,5 +4,7 @@ export { batch, batchAsync } from "./batch.js";
4
4
  export { cell } from "./cell.js";
5
5
  export { computed } from "./computed.js";
6
6
  export { effect } from "./effect.js";
7
+ export { selector } from "./selector.js";
8
+ export type { Selector, SelectorEquality } from "./selector.js";
7
9
  export { untrack } from "./untrack.js";
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACrD,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACrD,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -2,5 +2,6 @@ export { batch, batchAsync } from "./batch.js";
2
2
  export { cell } from "./cell.js";
3
3
  export { computed } from "./computed.js";
4
4
  export { effect } from "./effect.js";
5
+ export { selector } from "./selector.js";
5
6
  export { untrack } from "./untrack.js";
6
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC","sourcesContent":["export type { Cell, ReadonlyCell } from \"./types.js\";\nexport type { ComputedEquality, ComputedOptions } from \"./computed.js\";\nexport { batch, batchAsync } from \"./batch.js\";\nexport { cell } from \"./cell.js\";\nexport { computed } from \"./computed.js\";\nexport { effect } from \"./effect.js\";\nexport { untrack } from \"./untrack.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC","sourcesContent":["export type { Cell, ReadonlyCell } from \"./types.js\";\nexport type { ComputedEquality, ComputedOptions } from \"./computed.js\";\nexport { batch, batchAsync } from \"./batch.js\";\nexport { cell } from \"./cell.js\";\nexport { computed } from \"./computed.js\";\nexport { effect } from \"./effect.js\";\nexport { selector } from \"./selector.js\";\nexport type { Selector, SelectorEquality } from \"./selector.js\";\nexport { untrack } from \"./untrack.js\";\n"]}
@@ -8,6 +8,8 @@ export { flushQueuedComputations, schedulePendingFlush, setScheduler } from "./s
8
8
  export { flushPendingComputed } from "./tracking.js";
9
9
  /** Low-level source helpers used by reactive DOM keyed item proxies. */
10
10
  export { notifySubscribers, trackSource } from "./tracking.js";
11
+ /** Low-level cell subscription used by reactive DOM single-cell bindings. */
12
+ export { subscribeCell } from "./cell-subscription.js";
11
13
  /** Shared global runtime state helper for singleton package state. */
12
14
  export { getGlobalRuntimeState } from "./runtime-state.js";
13
15
  export { runtimeState } from "./state.js";
@@ -1 +1 @@
1
- {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,4DAA4D;AAC5D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACvE,uEAAuE;AACvE,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC7F,8DAA8D;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,wEAAwE;AACxE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC/D,sEAAsE;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,4DAA4D;AAC5D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACvE,uEAAuE;AACvE,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC7F,8DAA8D;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,wEAAwE;AACxE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC/D,6EAA6E;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,sEAAsE;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
package/dist/internal.js CHANGED
@@ -6,6 +6,8 @@ export { flushQueuedComputations, schedulePendingFlush, setScheduler } from "./s
6
6
  export { flushPendingComputed } from "./tracking.js";
7
7
  /** Low-level source helpers used by reactive DOM keyed item proxies. */
8
8
  export { notifySubscribers, trackSource } from "./tracking.js";
9
+ /** Low-level cell subscription used by reactive DOM single-cell bindings. */
10
+ export { subscribeCell } from "./cell-subscription.js";
9
11
  /** Shared global runtime state helper for singleton package state. */
10
12
  export { getGlobalRuntimeState } from "./runtime-state.js";
11
13
  export { runtimeState } from "./state.js";
@@ -1 +1 @@
1
- {"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAEA,4DAA4D;AAC5D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACvE,uEAAuE;AACvE,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC7F,8DAA8D;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,wEAAwE;AACxE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC/D,sEAAsE;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC","sourcesContent":["/** Scheduler type used by reactive-core internal flush control. */\nexport type { Scheduler } from \"./scheduler.js\";\n/** Cleanup scope helpers used by reactive DOM and tests. */\nexport { registerCleanup, withCleanupScope } from \"./cleanup-scope.js\";\n/** Scheduler controls used by reactive-core tests and integrations. */\nexport { flushQueuedComputations, schedulePendingFlush, setScheduler } from \"./scheduler.js\";\n/** Computed flush helper used by batched reactive updates. */\nexport { flushPendingComputed } from \"./tracking.js\";\n/** Low-level source helpers used by reactive DOM keyed item proxies. */\nexport { notifySubscribers, trackSource } from \"./tracking.js\";\n/** Shared global runtime state helper for singleton package state. */\nexport { getGlobalRuntimeState } from \"./runtime-state.js\";\nexport { runtimeState } from \"./state.js\";\nexport type { Source } from \"./state.js\";\n"]}
1
+ {"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAEA,4DAA4D;AAC5D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACvE,uEAAuE;AACvE,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC7F,8DAA8D;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,wEAAwE;AACxE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC/D,6EAA6E;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,sEAAsE;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC","sourcesContent":["/** Scheduler type used by reactive-core internal flush control. */\nexport type { Scheduler } from \"./scheduler.js\";\n/** Cleanup scope helpers used by reactive DOM and tests. */\nexport { registerCleanup, withCleanupScope } from \"./cleanup-scope.js\";\n/** Scheduler controls used by reactive-core tests and integrations. */\nexport { flushQueuedComputations, schedulePendingFlush, setScheduler } from \"./scheduler.js\";\n/** Computed flush helper used by batched reactive updates. */\nexport { flushPendingComputed } from \"./tracking.js\";\n/** Low-level source helpers used by reactive DOM keyed item proxies. */\nexport { notifySubscribers, trackSource } from \"./tracking.js\";\n/** Low-level cell subscription used by reactive DOM single-cell bindings. */\nexport { subscribeCell } from \"./cell-subscription.js\";\n/** Shared global runtime state helper for singleton package state. */\nexport { getGlobalRuntimeState } from \"./runtime-state.js\";\nexport { runtimeState } from \"./state.js\";\nexport type { Source } from \"./state.js\";\n"]}
@@ -0,0 +1,14 @@
1
+ import type { ReadonlyCell } from "./types.js";
2
+ /** Equality function used by selector keys. */
3
+ export type SelectorEquality<TValue, TKey> = (value: TValue, key: TKey) => boolean;
4
+ /** A keyed boolean selector with an explicit disposer for the source subscription. */
5
+ export interface Selector<TValue, TKey = TValue> {
6
+ (key: TKey): boolean;
7
+ subscribe(key: TKey, listener: (selected: boolean) => void): () => void;
8
+ dispose(): void;
9
+ }
10
+ /** Creates keyed boolean cells that update only for the previous and next selected keys. */
11
+ export declare function selector<TValue, TKey = TValue>(source: ReadonlyCell<TValue>, options?: {
12
+ equals?: SelectorEquality<TValue, TKey> | undefined;
13
+ }): Selector<TValue, TKey>;
14
+ //# sourceMappingURL=selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.d.ts","sourceRoot":"","sources":["../src/selector.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,+CAA+C;AAC/C,MAAM,MAAM,gBAAgB,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC;AAInF,sFAAsF;AACtF,MAAM,WAAW,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM;IAC7C,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC;IACrB,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACxE,OAAO,IAAI,IAAI,CAAC;CACjB;AASD,4FAA4F;AAC5F,wBAAgB,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM,EAC5C,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,EAC5B,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,SAAS,CAAA;CAAE,GAChE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CA2FxB"}
@@ -0,0 +1,105 @@
1
+ import { effect } from "./effect.js";
2
+ import { notifySubscribers, trackSource } from "./tracking.js";
3
+ import { untrack } from "./untrack.js";
4
+ /** Creates keyed boolean cells that update only for the previous and next selected keys. */
5
+ export function selector(source, options) {
6
+ const usesDefaultEquality = options?.equals === undefined;
7
+ const equals = options?.equals ?? (defaultSelectorEquality);
8
+ const sources = new Map();
9
+ let current = untrack(() => source.get());
10
+ const disposeSourceEffect = effect(() => {
11
+ const next = source.get();
12
+ if (Object.is(current, next)) {
13
+ return;
14
+ }
15
+ const previous = current;
16
+ current = next;
17
+ if (usesDefaultEquality) {
18
+ updateSelectorSource(sources.get(previous), false);
19
+ updateSelectorSource(sources.get(next), true);
20
+ return;
21
+ }
22
+ for (const selectorSource of sources.values()) {
23
+ const nextSelected = equals(next, selectorSource.key) === true;
24
+ updateSelectorSource(selectorSource, nextSelected);
25
+ }
26
+ });
27
+ function getOrCreateSelectorSource(key) {
28
+ let selectorSource = sources.get(key);
29
+ if (selectorSource === undefined) {
30
+ selectorSource = {
31
+ key,
32
+ onNoSubscribers: cleanupSelectorSource,
33
+ owner: sources,
34
+ selected: equals(current, key) === true,
35
+ subscribers: null,
36
+ };
37
+ sources.set(key, selectorSource);
38
+ }
39
+ return selectorSource;
40
+ }
41
+ const select = ((key) => {
42
+ const selectorSource = getOrCreateSelectorSource(key);
43
+ trackSource(selectorSource);
44
+ return selectorSource.selected;
45
+ });
46
+ select.subscribe = (key, listener) => {
47
+ const selectorSource = getOrCreateSelectorSource(key);
48
+ const callbacks = selectorSource.callbacks;
49
+ if (callbacks === undefined) {
50
+ selectorSource.callbacks = listener;
51
+ }
52
+ else if (typeof callbacks === "function") {
53
+ if (callbacks !== listener) {
54
+ selectorSource.callbacks = new Set([callbacks, listener]);
55
+ }
56
+ }
57
+ else {
58
+ callbacks.add(listener);
59
+ }
60
+ return () => {
61
+ const currentCallbacks = selectorSource.callbacks;
62
+ if (currentCallbacks === listener) {
63
+ selectorSource.callbacks = undefined;
64
+ }
65
+ else if (currentCallbacks instanceof Set) {
66
+ currentCallbacks.delete(listener);
67
+ if (currentCallbacks.size === 0) {
68
+ selectorSource.callbacks = undefined;
69
+ }
70
+ }
71
+ cleanupSelectorSource.call(selectorSource);
72
+ };
73
+ };
74
+ select.dispose = () => {
75
+ disposeSourceEffect();
76
+ sources.clear();
77
+ };
78
+ return select;
79
+ }
80
+ function defaultSelectorEquality(value, key) {
81
+ return Object.is(value, key);
82
+ }
83
+ function cleanupSelectorSource() {
84
+ if (this.subscribers === null && this.callbacks === undefined) {
85
+ this.owner.delete(this.key);
86
+ }
87
+ }
88
+ function updateSelectorSource(selectorSource, selected) {
89
+ if (selectorSource === undefined ||
90
+ selectorSource.selected === selected) {
91
+ return;
92
+ }
93
+ selectorSource.selected = selected;
94
+ const callbacks = selectorSource.callbacks;
95
+ if (typeof callbacks === "function") {
96
+ callbacks(selected);
97
+ }
98
+ else if (callbacks !== undefined) {
99
+ for (const callback of callbacks) {
100
+ callback(selected);
101
+ }
102
+ }
103
+ notifySubscribers(selectorSource);
104
+ }
105
+ //# sourceMappingURL=selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.js","sourceRoot":"","sources":["../src/selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAuBvC,4FAA4F;AAC5F,MAAM,UAAU,QAAQ,CACtB,MAA4B,EAC5B,OAAiE;IAEjE,MAAM,mBAAmB,GAAG,OAAO,EAAE,MAAM,KAAK,SAAS,CAAC;IAC1D,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,KAAI,uBAAqC,CAAA,CAAC;IACxE,MAAM,OAAO,GAAG,IAAI,GAAG,EAA8B,CAAC;IACtD,IAAI,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;IAE1C,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,EAAE;QACtC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC;QAE1B,IAAI,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;YAC7B,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC;QACzB,OAAO,GAAG,IAAI,CAAC;QAEf,IAAI,mBAAmB,EAAE,CAAC;YACxB,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,QAA2B,CAAC,EAAE,KAAK,CAAC,CAAC;YACtE,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,IAAuB,CAAC,EAAE,IAAI,CAAC,CAAC;YACjE,OAAO;QACT,CAAC;QAED,KAAK,MAAM,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC9C,MAAM,YAAY,GAChB,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;YAE5C,oBAAoB,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;QACrD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,SAAS,yBAAyB,CAAC,GAAS;QAC1C,IAAI,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEtC,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;YACjC,cAAc,GAAG;gBACf,GAAG;gBACH,eAAe,EAAE,qBAAqB;gBACtC,KAAK,EAAE,OAAO;gBACd,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,IAAI;gBACvC,WAAW,EAAE,IAAI;aAClB,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QACnC,CAAC;QAED,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,CAAC,GAAS,EAAW,EAAE;QACrC,MAAM,cAAc,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAEtD,WAAW,CAAC,cAAc,CAAC,CAAC;QAC5B,OAAO,cAAc,CAAC,QAAQ,CAAC;IACjC,CAAC,CAA2B,CAAC;IAE7B,MAAM,CAAC,SAAS,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;QACnC,MAAM,cAAc,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAC;QACtD,MAAM,SAAS,GAAG,cAAc,CAAC,SAAS,CAAC;QAE3C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,cAAc,CAAC,SAAS,GAAG,QAAQ,CAAC;QACtC,CAAC;aAAM,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;YAC3C,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;gBAC3B,cAAc,CAAC,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;QAED,OAAO,GAAG,EAAE;YACV,MAAM,gBAAgB,GAAG,cAAc,CAAC,SAAS,CAAC;YAElD,IAAI,gBAAgB,KAAK,QAAQ,EAAE,CAAC;gBAClC,cAAc,CAAC,SAAS,GAAG,SAAS,CAAC;YACvC,CAAC;iBAAM,IAAI,gBAAgB,YAAY,GAAG,EAAE,CAAC;gBAC3C,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAElC,IAAI,gBAAgB,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;oBAChC,cAAc,CAAC,SAAS,GAAG,SAAS,CAAC;gBACvC,CAAC;YACH,CAAC;YAED,qBAAqB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC7C,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE;QACpB,mBAAmB,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,uBAAuB,CAAe,KAAa,EAAE,GAAS;IACrE,OAAO,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,qBAAqB;IAC5B,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAC9D,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAC3B,cAAgD,EAChD,QAAiB;IAEjB,IACE,cAAc,KAAK,SAAS;QAC5B,cAAc,CAAC,QAAQ,KAAK,QAAQ,EACpC,CAAC;QACD,OAAO;IACT,CAAC;IAED,cAAc,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACnC,MAAM,SAAS,GAAG,cAAc,CAAC,SAAS,CAAC;IAE3C,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;QACpC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACtB,CAAC;SAAM,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QACnC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,iBAAiB,CAAC,cAAc,CAAC,CAAC;AACpC,CAAC","sourcesContent":["import { effect } from \"./effect.js\";\nimport { notifySubscribers, trackSource } from \"./tracking.js\";\nimport { untrack } from \"./untrack.js\";\nimport type { Source } from \"./state.js\";\nimport type { ReadonlyCell } from \"./types.js\";\n\n/** Equality function used by selector keys. */\nexport type SelectorEquality<TValue, TKey> = (value: TValue, key: TKey) => boolean;\ntype SelectorCallback = (selected: boolean) => void;\ntype SelectorCallbacks = SelectorCallback | Set<SelectorCallback>;\n\n/** A keyed boolean selector with an explicit disposer for the source subscription. */\nexport interface Selector<TValue, TKey = TValue> {\n (key: TKey): boolean;\n subscribe(key: TKey, listener: (selected: boolean) => void): () => void;\n dispose(): void;\n}\n\ninterface SelectorSource<TKey> extends Source {\n callbacks?: SelectorCallbacks | undefined;\n key: TKey;\n owner: Map<TKey, SelectorSource<TKey>>;\n selected: boolean;\n}\n\n/** Creates keyed boolean cells that update only for the previous and next selected keys. */\nexport function selector<TValue, TKey = TValue>(\n source: ReadonlyCell<TValue>,\n options?: { equals?: SelectorEquality<TValue, TKey> | undefined },\n): Selector<TValue, TKey> {\n const usesDefaultEquality = options?.equals === undefined;\n const equals = options?.equals ?? defaultSelectorEquality<TValue, TKey>;\n const sources = new Map<TKey, SelectorSource<TKey>>();\n let current = untrack(() => source.get());\n\n const disposeSourceEffect = effect(() => {\n const next = source.get();\n\n if (Object.is(current, next)) {\n return;\n }\n\n const previous = current;\n current = next;\n\n if (usesDefaultEquality) {\n updateSelectorSource(sources.get(previous as unknown as TKey), false);\n updateSelectorSource(sources.get(next as unknown as TKey), true);\n return;\n }\n\n for (const selectorSource of sources.values()) {\n const nextSelected =\n equals(next, selectorSource.key) === true;\n\n updateSelectorSource(selectorSource, nextSelected);\n }\n });\n\n function getOrCreateSelectorSource(key: TKey): SelectorSource<TKey> {\n let selectorSource = sources.get(key);\n\n if (selectorSource === undefined) {\n selectorSource = {\n key,\n onNoSubscribers: cleanupSelectorSource,\n owner: sources,\n selected: equals(current, key) === true,\n subscribers: null,\n };\n sources.set(key, selectorSource);\n }\n\n return selectorSource;\n }\n\n const select = ((key: TKey): boolean => {\n const selectorSource = getOrCreateSelectorSource(key);\n\n trackSource(selectorSource);\n return selectorSource.selected;\n }) as Selector<TValue, TKey>;\n\n select.subscribe = (key, listener) => {\n const selectorSource = getOrCreateSelectorSource(key);\n const callbacks = selectorSource.callbacks;\n\n if (callbacks === undefined) {\n selectorSource.callbacks = listener;\n } else if (typeof callbacks === \"function\") {\n if (callbacks !== listener) {\n selectorSource.callbacks = new Set([callbacks, listener]);\n }\n } else {\n callbacks.add(listener);\n }\n\n return () => {\n const currentCallbacks = selectorSource.callbacks;\n\n if (currentCallbacks === listener) {\n selectorSource.callbacks = undefined;\n } else if (currentCallbacks instanceof Set) {\n currentCallbacks.delete(listener);\n\n if (currentCallbacks.size === 0) {\n selectorSource.callbacks = undefined;\n }\n }\n\n cleanupSelectorSource.call(selectorSource);\n };\n };\n\n select.dispose = () => {\n disposeSourceEffect();\n sources.clear();\n };\n\n return select;\n}\n\nfunction defaultSelectorEquality<TValue, TKey>(value: TValue, key: TKey): boolean {\n return Object.is(value, key);\n}\n\nfunction cleanupSelectorSource<TKey>(this: SelectorSource<TKey>): void {\n if (this.subscribers === null && this.callbacks === undefined) {\n this.owner.delete(this.key);\n }\n}\n\nfunction updateSelectorSource<TKey>(\n selectorSource: SelectorSource<TKey> | undefined,\n selected: boolean,\n): void {\n if (\n selectorSource === undefined ||\n selectorSource.selected === selected\n ) {\n return;\n }\n\n selectorSource.selected = selected;\n const callbacks = selectorSource.callbacks;\n\n if (typeof callbacks === \"function\") {\n callbacks(selected);\n } else if (callbacks !== undefined) {\n for (const callback of callbacks) {\n callback(selected);\n }\n }\n\n notifySubscribers(selectorSource);\n}\n"]}
package/dist/state.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export interface Source {
2
2
  subscribers: ReactiveComputation | Set<ReactiveComputation> | null;
3
+ onNoSubscribers?: (() => void) | undefined;
3
4
  trackedBy?: ReactiveComputation | undefined;
4
5
  trackedVersion?: number | undefined;
5
6
  }
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,MAAM;IAKrB,WAAW,EAAE,mBAAmB,GAAG,GAAG,CAAC,mBAAmB,CAAC,GAAG,IAAI,CAAC;IACnE,SAAS,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC5C,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACnC,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACzC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,uBAAuB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9C,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC3C,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,IAAI,IAAI,CAAC;IAClB,GAAG,IAAI,IAAI,CAAC;IACZ,OAAO,IAAI,IAAI,CAAC;IAChB,WAAW,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,MAAM,OAAO,GAAG,mBAAmB,GAAG,IAAI,CAAC;AAEjD,eAAO,MAAM,YAAY,EAAE;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1D,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC;CAS3C,CAAC"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,MAAM;IAKrB,WAAW,EAAE,mBAAmB,GAAG,GAAG,CAAC,mBAAmB,CAAC,GAAG,IAAI,CAAC;IACnE,eAAe,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3C,SAAS,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC5C,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACnC,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACzC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,uBAAuB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9C,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC3C,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,IAAI,IAAI,CAAC;IAClB,GAAG,IAAI,IAAI,CAAC;IACZ,OAAO,IAAI,IAAI,CAAC;IAChB,WAAW,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,MAAM,OAAO,GAAG,mBAAmB,GAAG,IAAI,CAAC;AAEjD,eAAO,MAAM,YAAY,EAAE;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1D,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC;CAS3C,CAAC"}
package/dist/state.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,sBAAsB,CAAC;AAEvE,wEAAwE;AACxE,6EAA6E;AAC7E,+BAA+B;AAC/B,+BAA+B,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAgCjD,MAAM,CAAC,MAAM,YAAY,GAQrB;IACF,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE,CAAC;IACb,YAAY,EAAE,SAAS;IACvB,gBAAgB,EAAE,KAAK;IACvB,iBAAiB,EAAE,CAAC;IACpB,iBAAiB,EAAE,CAAC;IACpB,eAAe,EAAE,IAAI,GAAG,EAAE;CAC3B,CAAC","sourcesContent":["import { warnOnDuplicateReactiveCoreCopy } from \"./duplicate-guard.js\";\n\n// This module holds the per-copy reactive runtime identity, so a second\n// evaluation in the same browser page is exactly the duplication that breaks\n// cross-package cell tracking.\nwarnOnDuplicateReactiveCoreCopy(import.meta.url);\n\nexport interface Source {\n // null while nothing subscribes, the computation itself while exactly one\n // does, and a Set from the second subscriber on (kept as a Set until it\n // empties back to null). Most sources never allocate a Set at all, and hot\n // write sites can gate on a null check instead of a Set.size accessor.\n subscribers: ReactiveComputation | Set<ReactiveComputation> | null;\n trackedBy?: ReactiveComputation | undefined;\n trackedVersion?: number | undefined;\n}\n\nexport interface ReactiveComputation {\n readonly id: number;\n deps: Set<Source>;\n orderedDeps?: Source[] | undefined;\n trackingAddedDeps?: Source[] | undefined;\n trackingCount?: number | undefined;\n trackingOrderedIndex?: number | undefined;\n trackingOrderedMismatch?: boolean | undefined;\n trackingTouchedDeps?: Source[] | undefined;\n trackingVersion?: number | undefined;\n disposed: boolean;\n queued: boolean;\n markDirty(): void;\n run(): void;\n dispose(): void;\n trackSource?(source: Source): void;\n}\n\nexport type Tracker = ReactiveComputation | null;\n\nexport const runtimeState: {\n activeTracker: Tracker;\n batchDepth: number;\n cleanupOwner: ((dispose: () => void) => void) | undefined;\n flushingComputed: boolean;\n nextComputationId: number;\n notificationDepth: number;\n pendingComputed: Set<ReactiveComputation>;\n} = {\n activeTracker: null,\n batchDepth: 0,\n cleanupOwner: undefined,\n flushingComputed: false,\n nextComputationId: 0,\n notificationDepth: 0,\n pendingComputed: new Set(),\n};\n"]}
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,sBAAsB,CAAC;AAEvE,wEAAwE;AACxE,6EAA6E;AAC7E,+BAA+B;AAC/B,+BAA+B,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAiCjD,MAAM,CAAC,MAAM,YAAY,GAQrB;IACF,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE,CAAC;IACb,YAAY,EAAE,SAAS;IACvB,gBAAgB,EAAE,KAAK;IACvB,iBAAiB,EAAE,CAAC;IACpB,iBAAiB,EAAE,CAAC;IACpB,eAAe,EAAE,IAAI,GAAG,EAAE;CAC3B,CAAC","sourcesContent":["import { warnOnDuplicateReactiveCoreCopy } from \"./duplicate-guard.js\";\n\n// This module holds the per-copy reactive runtime identity, so a second\n// evaluation in the same browser page is exactly the duplication that breaks\n// cross-package cell tracking.\nwarnOnDuplicateReactiveCoreCopy(import.meta.url);\n\nexport interface Source {\n // null while nothing subscribes, the computation itself while exactly one\n // does, and a Set from the second subscriber on (kept as a Set until it\n // empties back to null). Most sources never allocate a Set at all, and hot\n // write sites can gate on a null check instead of a Set.size accessor.\n subscribers: ReactiveComputation | Set<ReactiveComputation> | null;\n onNoSubscribers?: (() => void) | undefined;\n trackedBy?: ReactiveComputation | undefined;\n trackedVersion?: number | undefined;\n}\n\nexport interface ReactiveComputation {\n readonly id: number;\n deps: Set<Source>;\n orderedDeps?: Source[] | undefined;\n trackingAddedDeps?: Source[] | undefined;\n trackingCount?: number | undefined;\n trackingOrderedIndex?: number | undefined;\n trackingOrderedMismatch?: boolean | undefined;\n trackingTouchedDeps?: Source[] | undefined;\n trackingVersion?: number | undefined;\n disposed: boolean;\n queued: boolean;\n markDirty(): void;\n run(): void;\n dispose(): void;\n trackSource?(source: Source): void;\n}\n\nexport type Tracker = ReactiveComputation | null;\n\nexport const runtimeState: {\n activeTracker: Tracker;\n batchDepth: number;\n cleanupOwner: ((dispose: () => void) => void) | undefined;\n flushingComputed: boolean;\n nextComputationId: number;\n notificationDepth: number;\n pendingComputed: Set<ReactiveComputation>;\n} = {\n activeTracker: null,\n batchDepth: 0,\n cleanupOwner: undefined,\n flushingComputed: false,\n nextComputationId: 0,\n notificationDepth: 0,\n pendingComputed: new Set(),\n};\n"]}
@@ -1,5 +1,7 @@
1
1
  import { type ReactiveComputation, type Source } from "./state.js";
2
2
  export declare function trackSource(source: Source): void;
3
+ export declare function addSourceSubscriber(source: Source, computation: ReactiveComputation): void;
4
+ export declare function removeSourceSubscriber(source: Source, computation: ReactiveComputation): boolean;
3
5
  export declare function sourceSubscriberCount(source: Source): number;
4
6
  export declare function cleanupDeps(computation: ReactiveComputation): void;
5
7
  export declare function nextTrackingVersionFor(computation: ReactiveComputation): number;
@@ -1 +1 @@
1
- {"version":3,"file":"tracking.d.ts","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAIjF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAchD;AAgCD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAI5D;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,mBAAmB,GAAG,IAAI,CAclE;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,mBAAmB,GAAG,MAAM,CAc/E;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,GAC/B,IAAI,CA+CN;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,mBAAmB,GAAG,IAAI,CAgBlF;AAED,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,mBAAmB,EAChC,eAAe,EAAE,MAAM,GACtB,IAAI,CAyBN;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,mBAAmB,GAAG,IAAI,CAmBvE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAuDtD;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,IAAI,IAAI,CAmC3C"}
1
+ {"version":3,"file":"tracking.d.ts","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAIjF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAchD;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,GAC/B,IAAI,CAUN;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,GAC/B,OAAO,CAkBT;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAI5D;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,mBAAmB,GAAG,IAAI,CAclE;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,mBAAmB,GAAG,MAAM,CAc/E;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,GAC/B,IAAI,CAkDN;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,mBAAmB,GAAG,IAAI,CAgBlF;AAED,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,mBAAmB,EAChC,eAAe,EAAE,MAAM,GACtB,IAAI,CAyBN;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,mBAAmB,GAAG,IAAI,CAmBvE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAuDtD;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,IAAI,IAAI,CAmC3C"}
package/dist/tracking.js CHANGED
@@ -12,7 +12,7 @@ export function trackSource(source) {
12
12
  addSourceSubscriber(source, tracker);
13
13
  tracker.deps.add(source);
14
14
  }
15
- function addSourceSubscriber(source, computation) {
15
+ export function addSourceSubscriber(source, computation) {
16
16
  const subscribers = source.subscribers;
17
17
  if (subscribers === null) {
18
18
  source.subscribers = computation;
@@ -24,15 +24,17 @@ function addSourceSubscriber(source, computation) {
24
24
  source.subscribers = new Set([subscribers, computation]);
25
25
  }
26
26
  }
27
- function removeSourceSubscriber(source, computation) {
27
+ export function removeSourceSubscriber(source, computation) {
28
28
  const subscribers = source.subscribers;
29
29
  if (subscribers === computation) {
30
30
  source.subscribers = null;
31
+ source.onNoSubscribers?.();
31
32
  return true;
32
33
  }
33
34
  if (subscribers instanceof Set && subscribers.delete(computation)) {
34
35
  if (subscribers.size === 0) {
35
36
  source.subscribers = null;
37
+ source.onNoSubscribers?.();
36
38
  }
37
39
  return true;
38
40
  }
@@ -96,7 +98,8 @@ export function trackIncrementalSource(source, computation) {
96
98
  source.trackedVersion = trackingVersion;
97
99
  computation.trackingCount = (computation.trackingCount ?? 0) + 1;
98
100
  computation.trackingTouchedDeps?.push(source);
99
- if (alreadyTrackedByComputation || computation.deps.has(source)) {
101
+ if (alreadyTrackedByComputation ||
102
+ (computation.deps.size > 0 && computation.deps.has(source))) {
100
103
  return;
101
104
  }
102
105
  addSourceSubscriber(source, computation);
@@ -1 +1 @@
1
- {"version":3,"file":"tracking.js","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"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","sourcesContent":["import { runtimeState, type ReactiveComputation, type Source } from \"./state.js\";\n\nconst maxPendingComputedFlushIterations = 100;\n\nexport function trackSource(source: Source): void {\n const tracker = runtimeState.activeTracker;\n\n if (tracker === null || tracker.disposed) {\n return;\n }\n\n if (tracker.trackSource !== undefined) {\n tracker.trackSource(source);\n return;\n }\n\n addSourceSubscriber(source, tracker);\n tracker.deps.add(source);\n}\n\nfunction addSourceSubscriber(source: Source, computation: ReactiveComputation): void {\n const subscribers = source.subscribers;\n\n if (subscribers === null) {\n source.subscribers = computation;\n } else if (subscribers instanceof Set) {\n subscribers.add(computation);\n } else if (subscribers !== computation) {\n source.subscribers = new Set([subscribers, computation]);\n }\n}\n\nfunction removeSourceSubscriber(source: Source, computation: ReactiveComputation): boolean {\n const subscribers = source.subscribers;\n\n if (subscribers === computation) {\n source.subscribers = null;\n return true;\n }\n\n if (subscribers instanceof Set && subscribers.delete(computation)) {\n if (subscribers.size === 0) {\n source.subscribers = null;\n }\n return true;\n }\n\n return false;\n}\n\nexport function sourceSubscriberCount(source: Source): number {\n const subscribers = source.subscribers;\n\n return subscribers === null ? 0 : subscribers instanceof Set ? subscribers.size : 1;\n}\n\nexport function cleanupDeps(computation: ReactiveComputation): void {\n for (const dep of computation.deps) {\n if (!removeSourceSubscriber(dep, computation)) {\n continue;\n }\n\n if (dep.trackedBy === computation) {\n dep.trackedBy = undefined;\n dep.trackedVersion = undefined;\n }\n }\n\n computation.deps.clear();\n computation.orderedDeps = undefined;\n}\n\nexport function nextTrackingVersionFor(computation: ReactiveComputation): number {\n const nextTrackingVersion = (computation.trackingVersion ?? 0) + 1;\n\n if (Number.isSafeInteger(nextTrackingVersion)) {\n return nextTrackingVersion;\n }\n\n for (const dep of computation.deps) {\n if (dep.trackedBy === computation) {\n dep.trackedVersion = undefined;\n }\n }\n\n return 1;\n}\n\nexport function trackIncrementalSource(\n source: Source,\n computation: ReactiveComputation,\n): void {\n const trackingVersion = computation.trackingVersion;\n\n if (trackingVersion === undefined) {\n trackSource(source);\n return;\n }\n\n if (source.trackedBy === computation && source.trackedVersion === trackingVersion) {\n return;\n }\n\n const orderedIndex = computation.trackingOrderedIndex;\n const orderedDeps = computation.orderedDeps;\n\n if (\n orderedIndex !== undefined &&\n computation.trackingOrderedMismatch !== true &&\n orderedDeps !== undefined\n ) {\n if (orderedDeps[orderedIndex] === source) {\n computation.trackingOrderedIndex = orderedIndex + 1;\n computation.trackingCount = (computation.trackingCount ?? 0) + 1;\n return;\n }\n\n computation.trackingOrderedMismatch = true;\n\n if (orderedIndex > 0) {\n computation.trackingTouchedDeps = orderedDeps.slice(0, orderedIndex);\n }\n }\n\n const alreadyTrackedByComputation = source.trackedBy === computation;\n\n source.trackedBy = computation;\n source.trackedVersion = trackingVersion;\n computation.trackingCount = (computation.trackingCount ?? 0) + 1;\n computation.trackingTouchedDeps?.push(source);\n\n if (alreadyTrackedByComputation || computation.deps.has(source)) {\n return;\n }\n\n addSourceSubscriber(source, computation);\n computation.deps.add(source);\n (computation.trackingAddedDeps ??= []).push(source);\n}\n\nexport function preserveIncrementalTracking(computation: ReactiveComputation): void {\n const trackingVersion = computation.trackingVersion;\n\n if (trackingVersion === undefined || computation.trackingTouchedDeps !== undefined) {\n return;\n }\n\n const touchedDeps: Source[] = [];\n\n for (const dep of computation.deps) {\n if (dep.trackedBy === computation && dep.trackedVersion === trackingVersion) {\n touchedDeps.push(dep);\n }\n }\n\n computation.trackingTouchedDeps = touchedDeps;\n}\n\nexport function cleanupUntrackedDeps(\n computation: ReactiveComputation,\n trackingVersion: number,\n): void {\n const touchedDeps =\n computation.trackingTouchedDeps === undefined\n ? undefined\n : new Set(computation.trackingTouchedDeps);\n\n for (const dep of computation.deps) {\n if (\n touchedDeps?.has(dep) === true ||\n (dep.trackedBy === computation && dep.trackedVersion === trackingVersion)\n ) {\n continue;\n }\n\n if (!removeSourceSubscriber(dep, computation)) {\n continue;\n }\n\n if (dep.trackedBy === computation) {\n dep.trackedBy = undefined;\n dep.trackedVersion = undefined;\n }\n\n computation.deps.delete(dep);\n }\n}\n\nexport function cleanupAddedDeps(computation: ReactiveComputation): void {\n const addedDeps = computation.trackingAddedDeps;\n\n if (addedDeps === undefined) {\n return;\n }\n\n for (const dep of addedDeps) {\n if (!removeSourceSubscriber(dep, computation)) {\n continue;\n }\n\n if (dep.trackedBy === computation) {\n dep.trackedBy = undefined;\n dep.trackedVersion = undefined;\n }\n\n computation.deps.delete(dep);\n }\n}\n\nexport function notifySubscribers(source: Source): void {\n const subscribers = source.subscribers;\n\n if (subscribers === null) {\n return;\n }\n\n if (!(subscribers instanceof Set)) {\n if (runtimeState.batchDepth > 0) {\n if (!subscribers.disposed && !subscribers.queued) {\n subscribers.markDirty();\n }\n return;\n }\n\n runtimeState.notificationDepth += 1;\n\n try {\n if (!subscribers.disposed && !subscribers.queued) {\n subscribers.markDirty();\n }\n } finally {\n runtimeState.notificationDepth -= 1;\n\n if (runtimeState.notificationDepth === 0 && runtimeState.batchDepth === 0) {\n flushPendingComputed();\n }\n }\n return;\n }\n\n runtimeState.notificationDepth += 1;\n\n try {\n const singleSubscriber =\n subscribers.size === 1 ? subscribers.values().next().value : undefined;\n\n if (singleSubscriber !== undefined) {\n if (!singleSubscriber.disposed && !singleSubscriber.queued) {\n singleSubscriber.markDirty();\n }\n } else {\n for (const subscriber of orderedComputations(subscribers)) {\n if (!subscriber.disposed && !subscriber.queued) {\n subscriber.markDirty();\n }\n }\n }\n } finally {\n runtimeState.notificationDepth -= 1;\n\n if (runtimeState.notificationDepth === 0 && runtimeState.batchDepth === 0) {\n flushPendingComputed();\n }\n }\n}\n\n/** Flushes computed values that were dirtied during batched notifications. */\nexport function flushPendingComputed(): void {\n if (runtimeState.flushingComputed) {\n return;\n }\n\n runtimeState.flushingComputed = true;\n\n try {\n for (\n let iteration = 0;\n runtimeState.pendingComputed.size > 0;\n iteration += 1\n ) {\n if (iteration >= maxPendingComputedFlushIterations) {\n runtimeState.pendingComputed.clear();\n throw new Error(\"Reactive computed flush limit exceeded\");\n }\n\n const computations =\n runtimeState.pendingComputed.size === 1\n ? [runtimeState.pendingComputed.values().next().value as ReactiveComputation]\n : orderedComputations(runtimeState.pendingComputed);\n runtimeState.pendingComputed.clear();\n\n for (const computation of computations) {\n computation.queued = false;\n\n if (!computation.disposed) {\n computation.run();\n }\n }\n }\n } finally {\n runtimeState.flushingComputed = false;\n }\n}\n\nfunction orderedComputations(\n computations: ReadonlySet<ReactiveComputation>,\n): ReactiveComputation[] {\n const ordered: ReactiveComputation[] = [];\n let previousId = -1;\n let monotonic = true;\n\n for (const computation of computations) {\n ordered.push(computation);\n\n if (computation.id < previousId) {\n monotonic = false;\n }\n\n previousId = computation.id;\n }\n\n return monotonic || ordered.length < 2\n ? ordered\n : ordered.sort((a, b) => a.id - b.id);\n}\n"]}
1
+ {"version":3,"file":"tracking.js","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"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","sourcesContent":["import { runtimeState, type ReactiveComputation, type Source } from \"./state.js\";\n\nconst maxPendingComputedFlushIterations = 100;\n\nexport function trackSource(source: Source): void {\n const tracker = runtimeState.activeTracker;\n\n if (tracker === null || tracker.disposed) {\n return;\n }\n\n if (tracker.trackSource !== undefined) {\n tracker.trackSource(source);\n return;\n }\n\n addSourceSubscriber(source, tracker);\n tracker.deps.add(source);\n}\n\nexport function addSourceSubscriber(\n source: Source,\n computation: ReactiveComputation,\n): void {\n const subscribers = source.subscribers;\n\n if (subscribers === null) {\n source.subscribers = computation;\n } else if (subscribers instanceof Set) {\n subscribers.add(computation);\n } else if (subscribers !== computation) {\n source.subscribers = new Set([subscribers, computation]);\n }\n}\n\nexport function removeSourceSubscriber(\n source: Source,\n computation: ReactiveComputation,\n): boolean {\n const subscribers = source.subscribers;\n\n if (subscribers === computation) {\n source.subscribers = null;\n source.onNoSubscribers?.();\n return true;\n }\n\n if (subscribers instanceof Set && subscribers.delete(computation)) {\n if (subscribers.size === 0) {\n source.subscribers = null;\n source.onNoSubscribers?.();\n }\n return true;\n }\n\n return false;\n}\n\nexport function sourceSubscriberCount(source: Source): number {\n const subscribers = source.subscribers;\n\n return subscribers === null ? 0 : subscribers instanceof Set ? subscribers.size : 1;\n}\n\nexport function cleanupDeps(computation: ReactiveComputation): void {\n for (const dep of computation.deps) {\n if (!removeSourceSubscriber(dep, computation)) {\n continue;\n }\n\n if (dep.trackedBy === computation) {\n dep.trackedBy = undefined;\n dep.trackedVersion = undefined;\n }\n }\n\n computation.deps.clear();\n computation.orderedDeps = undefined;\n}\n\nexport function nextTrackingVersionFor(computation: ReactiveComputation): number {\n const nextTrackingVersion = (computation.trackingVersion ?? 0) + 1;\n\n if (Number.isSafeInteger(nextTrackingVersion)) {\n return nextTrackingVersion;\n }\n\n for (const dep of computation.deps) {\n if (dep.trackedBy === computation) {\n dep.trackedVersion = undefined;\n }\n }\n\n return 1;\n}\n\nexport function trackIncrementalSource(\n source: Source,\n computation: ReactiveComputation,\n): void {\n const trackingVersion = computation.trackingVersion;\n\n if (trackingVersion === undefined) {\n trackSource(source);\n return;\n }\n\n if (source.trackedBy === computation && source.trackedVersion === trackingVersion) {\n return;\n }\n\n const orderedIndex = computation.trackingOrderedIndex;\n const orderedDeps = computation.orderedDeps;\n\n if (\n orderedIndex !== undefined &&\n computation.trackingOrderedMismatch !== true &&\n orderedDeps !== undefined\n ) {\n if (orderedDeps[orderedIndex] === source) {\n computation.trackingOrderedIndex = orderedIndex + 1;\n computation.trackingCount = (computation.trackingCount ?? 0) + 1;\n return;\n }\n\n computation.trackingOrderedMismatch = true;\n\n if (orderedIndex > 0) {\n computation.trackingTouchedDeps = orderedDeps.slice(0, orderedIndex);\n }\n }\n\n const alreadyTrackedByComputation = source.trackedBy === computation;\n\n source.trackedBy = computation;\n source.trackedVersion = trackingVersion;\n computation.trackingCount = (computation.trackingCount ?? 0) + 1;\n computation.trackingTouchedDeps?.push(source);\n\n if (\n alreadyTrackedByComputation ||\n (computation.deps.size > 0 && computation.deps.has(source))\n ) {\n return;\n }\n\n addSourceSubscriber(source, computation);\n computation.deps.add(source);\n (computation.trackingAddedDeps ??= []).push(source);\n}\n\nexport function preserveIncrementalTracking(computation: ReactiveComputation): void {\n const trackingVersion = computation.trackingVersion;\n\n if (trackingVersion === undefined || computation.trackingTouchedDeps !== undefined) {\n return;\n }\n\n const touchedDeps: Source[] = [];\n\n for (const dep of computation.deps) {\n if (dep.trackedBy === computation && dep.trackedVersion === trackingVersion) {\n touchedDeps.push(dep);\n }\n }\n\n computation.trackingTouchedDeps = touchedDeps;\n}\n\nexport function cleanupUntrackedDeps(\n computation: ReactiveComputation,\n trackingVersion: number,\n): void {\n const touchedDeps =\n computation.trackingTouchedDeps === undefined\n ? undefined\n : new Set(computation.trackingTouchedDeps);\n\n for (const dep of computation.deps) {\n if (\n touchedDeps?.has(dep) === true ||\n (dep.trackedBy === computation && dep.trackedVersion === trackingVersion)\n ) {\n continue;\n }\n\n if (!removeSourceSubscriber(dep, computation)) {\n continue;\n }\n\n if (dep.trackedBy === computation) {\n dep.trackedBy = undefined;\n dep.trackedVersion = undefined;\n }\n\n computation.deps.delete(dep);\n }\n}\n\nexport function cleanupAddedDeps(computation: ReactiveComputation): void {\n const addedDeps = computation.trackingAddedDeps;\n\n if (addedDeps === undefined) {\n return;\n }\n\n for (const dep of addedDeps) {\n if (!removeSourceSubscriber(dep, computation)) {\n continue;\n }\n\n if (dep.trackedBy === computation) {\n dep.trackedBy = undefined;\n dep.trackedVersion = undefined;\n }\n\n computation.deps.delete(dep);\n }\n}\n\nexport function notifySubscribers(source: Source): void {\n const subscribers = source.subscribers;\n\n if (subscribers === null) {\n return;\n }\n\n if (!(subscribers instanceof Set)) {\n if (runtimeState.batchDepth > 0) {\n if (!subscribers.disposed && !subscribers.queued) {\n subscribers.markDirty();\n }\n return;\n }\n\n runtimeState.notificationDepth += 1;\n\n try {\n if (!subscribers.disposed && !subscribers.queued) {\n subscribers.markDirty();\n }\n } finally {\n runtimeState.notificationDepth -= 1;\n\n if (runtimeState.notificationDepth === 0 && runtimeState.batchDepth === 0) {\n flushPendingComputed();\n }\n }\n return;\n }\n\n runtimeState.notificationDepth += 1;\n\n try {\n const singleSubscriber =\n subscribers.size === 1 ? subscribers.values().next().value : undefined;\n\n if (singleSubscriber !== undefined) {\n if (!singleSubscriber.disposed && !singleSubscriber.queued) {\n singleSubscriber.markDirty();\n }\n } else {\n for (const subscriber of orderedComputations(subscribers)) {\n if (!subscriber.disposed && !subscriber.queued) {\n subscriber.markDirty();\n }\n }\n }\n } finally {\n runtimeState.notificationDepth -= 1;\n\n if (runtimeState.notificationDepth === 0 && runtimeState.batchDepth === 0) {\n flushPendingComputed();\n }\n }\n}\n\n/** Flushes computed values that were dirtied during batched notifications. */\nexport function flushPendingComputed(): void {\n if (runtimeState.flushingComputed) {\n return;\n }\n\n runtimeState.flushingComputed = true;\n\n try {\n for (\n let iteration = 0;\n runtimeState.pendingComputed.size > 0;\n iteration += 1\n ) {\n if (iteration >= maxPendingComputedFlushIterations) {\n runtimeState.pendingComputed.clear();\n throw new Error(\"Reactive computed flush limit exceeded\");\n }\n\n const computations =\n runtimeState.pendingComputed.size === 1\n ? [runtimeState.pendingComputed.values().next().value as ReactiveComputation]\n : orderedComputations(runtimeState.pendingComputed);\n runtimeState.pendingComputed.clear();\n\n for (const computation of computations) {\n computation.queued = false;\n\n if (!computation.disposed) {\n computation.run();\n }\n }\n }\n } finally {\n runtimeState.flushingComputed = false;\n }\n}\n\nfunction orderedComputations(\n computations: ReadonlySet<ReactiveComputation>,\n): ReactiveComputation[] {\n const ordered: ReactiveComputation[] = [];\n let previousId = -1;\n let monotonic = true;\n\n for (const computation of computations) {\n ordered.push(computation);\n\n if (computation.id < previousId) {\n monotonic = false;\n }\n\n previousId = computation.id;\n }\n\n return monotonic || ordered.length < 2\n ? ordered\n : ordered.sort((a, b) => a.id - b.id);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reckona/mreact-reactive-core",
3
- "version": "0.0.168",
3
+ "version": "0.0.170",
4
4
  "description": "Fine-grained reactive primitives used across mreact.",
5
5
  "keywords": [
6
6
  "jsx",
@@ -0,0 +1,75 @@
1
+ import { getCellSource } from "./cell.js";
2
+ import { queueComputation } from "./scheduler.js";
3
+ import { runtimeState, type ReactiveComputation, type Source } from "./state.js";
4
+ import type { ReadonlyCell } from "./types.js";
5
+ import { addSourceSubscriber, removeSourceSubscriber } from "./tracking.js";
6
+
7
+ interface CellSubscription<T> extends Omit<ReactiveComputation, "deps"> {
8
+ cell: ReadonlyCell<T>;
9
+ listener: (value: T) => void;
10
+ source: Source;
11
+ }
12
+
13
+ const CELL_SUBSCRIPTION_COMPUTATION_METHODS = {
14
+ markDirty: cellSubscriptionMarkDirty,
15
+ run: cellSubscriptionRun,
16
+ dispose: cellSubscriptionDispose,
17
+ } satisfies Pick<ReactiveComputation, "markDirty" | "run" | "dispose">;
18
+
19
+ export function subscribeCell<T>(
20
+ cell: ReadonlyCell<T>,
21
+ listener: (value: T) => void,
22
+ ): (() => void) | undefined {
23
+ const source = getCellSource(cell);
24
+
25
+ if (source === undefined) {
26
+ return undefined;
27
+ }
28
+
29
+ const computation: CellSubscription<T> = {
30
+ cell,
31
+ dispose: CELL_SUBSCRIPTION_COMPUTATION_METHODS.dispose,
32
+ disposed: false,
33
+ id: runtimeState.nextComputationId,
34
+ listener,
35
+ markDirty: CELL_SUBSCRIPTION_COMPUTATION_METHODS.markDirty,
36
+ queued: false,
37
+ run: CELL_SUBSCRIPTION_COMPUTATION_METHODS.run,
38
+ source,
39
+ };
40
+
41
+ runtimeState.nextComputationId += 1;
42
+ addSourceSubscriber(source, computation as unknown as ReactiveComputation);
43
+
44
+ return () => computation.dispose();
45
+ }
46
+
47
+ function cellSubscriptionMarkDirty(this: ReactiveComputation): void {
48
+ queueComputation(this);
49
+ }
50
+
51
+ function cellSubscriptionRun(this: ReactiveComputation): void {
52
+ const subscription = this as unknown as CellSubscription<unknown>;
53
+
54
+ if (subscription.disposed) {
55
+ return;
56
+ }
57
+
58
+ subscription.listener(subscription.cell.get());
59
+ }
60
+
61
+ function cellSubscriptionDispose(this: ReactiveComputation): void {
62
+ const subscription = this as unknown as CellSubscription<unknown>;
63
+
64
+ if (subscription.disposed) {
65
+ return;
66
+ }
67
+
68
+ subscription.disposed = true;
69
+ subscription.queued = false;
70
+ runtimeState.pendingComputed.delete(subscription as unknown as ReactiveComputation);
71
+ removeSourceSubscriber(
72
+ subscription.source,
73
+ subscription as unknown as ReactiveComputation,
74
+ );
75
+ }
package/src/cell.ts CHANGED
@@ -70,15 +70,28 @@ function emitCellSetEvent<T>(source: Source, previous: T, value: T): void {
70
70
  });
71
71
  }
72
72
 
73
- interface CellState<T> {
73
+ interface CellSource<T> extends Source {
74
74
  value: T;
75
- readonly source: Source;
75
+ }
76
+
77
+ type SourceBackedCell<T> = Cell<T> & {
78
+ readonly [cellSourceSymbol]: Source;
79
+ };
80
+
81
+ export const cellSourceSymbol: unique symbol = Symbol.for(
82
+ "@reckona/mreact-reactive-core.cellSource",
83
+ );
84
+
85
+ export function getCellSource(value: unknown): Source | undefined {
86
+ return typeof value === "object" && value !== null
87
+ ? (value as { [cellSourceSymbol]?: Source })[cellSourceSymbol]
88
+ : undefined;
76
89
  }
77
90
 
78
91
  // One shared write function keeps the hot store/notify sequence in a single
79
92
  // optimizable function instead of a fresh fat closure per cell.
80
- function writeCellValue<T>(state: CellState<T>, next: T | ((prev: T) => T)): void {
81
- const previous = state.value;
93
+ function writeCellValue<T>(source: CellSource<T>, next: T | ((prev: T) => T)): void {
94
+ const previous = source.value;
82
95
  const resolved =
83
96
  typeof next === "function" ? (next as (prev: T) => T)(previous) : next;
84
97
 
@@ -86,16 +99,16 @@ function writeCellValue<T>(state: CellState<T>, next: T | ((prev: T) => T)): voi
86
99
  return;
87
100
  }
88
101
 
89
- state.value = resolved;
102
+ source.value = resolved;
90
103
 
91
104
  // clientDevtoolsDisabled folds to true under the client build define, which
92
105
  // makes this branch statically dead so bundlers drop the emit path (and its
93
106
  // globalThis.__mreactDevtools references) from production client bundles.
94
107
  if (!clientDevtoolsDisabled && cachedDevtoolsHook !== null) {
95
- emitCellSetEvent(state.source, previous, resolved);
108
+ emitCellSetEvent(source, previous, resolved);
96
109
  }
97
110
 
98
- const subscribers = state.source.subscribers;
111
+ const subscribers = source.subscribers;
99
112
  if (subscribers !== null) {
100
113
  if (runtimeState.batchDepth > 0 && !(subscribers instanceof Set)) {
101
114
  if (!subscribers.disposed && !subscribers.queued) {
@@ -104,25 +117,26 @@ function writeCellValue<T>(state: CellState<T>, next: T | ((prev: T) => T)): voi
104
117
  return;
105
118
  }
106
119
 
107
- notifySubscribers(state.source);
120
+ notifySubscribers(source);
108
121
  }
109
122
  }
110
123
 
111
124
  export function cell<T>(initial: T): Cell<T> {
112
- const state: CellState<T> = {
113
- source: {
114
- subscribers: null,
115
- },
125
+ const source: CellSource<T> = {
126
+ subscribers: null,
116
127
  value: initial,
117
128
  };
118
129
 
119
- return {
130
+ const value: SourceBackedCell<T> = {
131
+ [cellSourceSymbol]: source,
120
132
  get(): T {
121
- trackSource(state.source);
122
- return state.value;
133
+ trackSource(source);
134
+ return source.value;
123
135
  },
124
136
  set(next: T | ((prev: T) => T)): void {
125
- writeCellValue(state, next);
137
+ writeCellValue(source, next);
126
138
  },
127
139
  };
140
+
141
+ return value;
128
142
  }
package/src/computed.ts CHANGED
@@ -131,7 +131,7 @@ export function computed<T>(
131
131
  const addedDepsCount = addedDeps?.length ?? 0;
132
132
  const orderedMismatch = computation.trackingOrderedMismatch as boolean | undefined;
133
133
 
134
- if (trackedCount !== previousDepsSize || addedDepsCount > 0) {
134
+ if (previousDepsSize > 0 && (trackedCount !== previousDepsSize || addedDepsCount > 0)) {
135
135
  const orderedIndex = computation.trackingOrderedIndex;
136
136
 
137
137
  if (
package/src/effect.ts CHANGED
@@ -43,9 +43,9 @@ export function effect(fn: () => void | (() => void)): () => void {
43
43
  const previousDepsSize = computation.deps.size;
44
44
  const nextTrackingVersion = nextTrackingVersionFor(computation);
45
45
 
46
- computation.trackingAddedDeps = [];
46
+ computation.trackingAddedDeps = undefined;
47
47
  computation.trackingCount = 0;
48
- computation.trackingTouchedDeps = [];
48
+ computation.trackingTouchedDeps = undefined;
49
49
  computation.trackingVersion = nextTrackingVersion;
50
50
  runtimeState.activeTracker = computation;
51
51
 
@@ -133,7 +133,10 @@ function finishIncrementalTracking(
133
133
  const addedDeps = computation.trackingAddedDeps;
134
134
  const trackedCount = computation.trackingCount ?? 0;
135
135
 
136
- if (trackedCount !== previousDepsSize || (addedDeps?.length ?? 0) > 0) {
136
+ if (
137
+ previousDepsSize > 0 &&
138
+ (trackedCount !== previousDepsSize || (addedDeps?.length ?? 0) > 0)
139
+ ) {
137
140
  cleanupUntrackedDeps(computation, trackingVersion);
138
141
  }
139
142
 
package/src/index.ts CHANGED
@@ -4,4 +4,6 @@ export { batch, batchAsync } from "./batch.js";
4
4
  export { cell } from "./cell.js";
5
5
  export { computed } from "./computed.js";
6
6
  export { effect } from "./effect.js";
7
+ export { selector } from "./selector.js";
8
+ export type { Selector, SelectorEquality } from "./selector.js";
7
9
  export { untrack } from "./untrack.js";
package/src/internal.ts CHANGED
@@ -8,6 +8,8 @@ export { flushQueuedComputations, schedulePendingFlush, setScheduler } from "./s
8
8
  export { flushPendingComputed } from "./tracking.js";
9
9
  /** Low-level source helpers used by reactive DOM keyed item proxies. */
10
10
  export { notifySubscribers, trackSource } from "./tracking.js";
11
+ /** Low-level cell subscription used by reactive DOM single-cell bindings. */
12
+ export { subscribeCell } from "./cell-subscription.js";
11
13
  /** Shared global runtime state helper for singleton package state. */
12
14
  export { getGlobalRuntimeState } from "./runtime-state.js";
13
15
  export { runtimeState } from "./state.js";
@@ -0,0 +1,156 @@
1
+ import { effect } from "./effect.js";
2
+ import { notifySubscribers, trackSource } from "./tracking.js";
3
+ import { untrack } from "./untrack.js";
4
+ import type { Source } from "./state.js";
5
+ import type { ReadonlyCell } from "./types.js";
6
+
7
+ /** Equality function used by selector keys. */
8
+ export type SelectorEquality<TValue, TKey> = (value: TValue, key: TKey) => boolean;
9
+ type SelectorCallback = (selected: boolean) => void;
10
+ type SelectorCallbacks = SelectorCallback | Set<SelectorCallback>;
11
+
12
+ /** A keyed boolean selector with an explicit disposer for the source subscription. */
13
+ export interface Selector<TValue, TKey = TValue> {
14
+ (key: TKey): boolean;
15
+ subscribe(key: TKey, listener: (selected: boolean) => void): () => void;
16
+ dispose(): void;
17
+ }
18
+
19
+ interface SelectorSource<TKey> extends Source {
20
+ callbacks?: SelectorCallbacks | undefined;
21
+ key: TKey;
22
+ owner: Map<TKey, SelectorSource<TKey>>;
23
+ selected: boolean;
24
+ }
25
+
26
+ /** Creates keyed boolean cells that update only for the previous and next selected keys. */
27
+ export function selector<TValue, TKey = TValue>(
28
+ source: ReadonlyCell<TValue>,
29
+ options?: { equals?: SelectorEquality<TValue, TKey> | undefined },
30
+ ): Selector<TValue, TKey> {
31
+ const usesDefaultEquality = options?.equals === undefined;
32
+ const equals = options?.equals ?? defaultSelectorEquality<TValue, TKey>;
33
+ const sources = new Map<TKey, SelectorSource<TKey>>();
34
+ let current = untrack(() => source.get());
35
+
36
+ const disposeSourceEffect = effect(() => {
37
+ const next = source.get();
38
+
39
+ if (Object.is(current, next)) {
40
+ return;
41
+ }
42
+
43
+ const previous = current;
44
+ current = next;
45
+
46
+ if (usesDefaultEquality) {
47
+ updateSelectorSource(sources.get(previous as unknown as TKey), false);
48
+ updateSelectorSource(sources.get(next as unknown as TKey), true);
49
+ return;
50
+ }
51
+
52
+ for (const selectorSource of sources.values()) {
53
+ const nextSelected =
54
+ equals(next, selectorSource.key) === true;
55
+
56
+ updateSelectorSource(selectorSource, nextSelected);
57
+ }
58
+ });
59
+
60
+ function getOrCreateSelectorSource(key: TKey): SelectorSource<TKey> {
61
+ let selectorSource = sources.get(key);
62
+
63
+ if (selectorSource === undefined) {
64
+ selectorSource = {
65
+ key,
66
+ onNoSubscribers: cleanupSelectorSource,
67
+ owner: sources,
68
+ selected: equals(current, key) === true,
69
+ subscribers: null,
70
+ };
71
+ sources.set(key, selectorSource);
72
+ }
73
+
74
+ return selectorSource;
75
+ }
76
+
77
+ const select = ((key: TKey): boolean => {
78
+ const selectorSource = getOrCreateSelectorSource(key);
79
+
80
+ trackSource(selectorSource);
81
+ return selectorSource.selected;
82
+ }) as Selector<TValue, TKey>;
83
+
84
+ select.subscribe = (key, listener) => {
85
+ const selectorSource = getOrCreateSelectorSource(key);
86
+ const callbacks = selectorSource.callbacks;
87
+
88
+ if (callbacks === undefined) {
89
+ selectorSource.callbacks = listener;
90
+ } else if (typeof callbacks === "function") {
91
+ if (callbacks !== listener) {
92
+ selectorSource.callbacks = new Set([callbacks, listener]);
93
+ }
94
+ } else {
95
+ callbacks.add(listener);
96
+ }
97
+
98
+ return () => {
99
+ const currentCallbacks = selectorSource.callbacks;
100
+
101
+ if (currentCallbacks === listener) {
102
+ selectorSource.callbacks = undefined;
103
+ } else if (currentCallbacks instanceof Set) {
104
+ currentCallbacks.delete(listener);
105
+
106
+ if (currentCallbacks.size === 0) {
107
+ selectorSource.callbacks = undefined;
108
+ }
109
+ }
110
+
111
+ cleanupSelectorSource.call(selectorSource);
112
+ };
113
+ };
114
+
115
+ select.dispose = () => {
116
+ disposeSourceEffect();
117
+ sources.clear();
118
+ };
119
+
120
+ return select;
121
+ }
122
+
123
+ function defaultSelectorEquality<TValue, TKey>(value: TValue, key: TKey): boolean {
124
+ return Object.is(value, key);
125
+ }
126
+
127
+ function cleanupSelectorSource<TKey>(this: SelectorSource<TKey>): void {
128
+ if (this.subscribers === null && this.callbacks === undefined) {
129
+ this.owner.delete(this.key);
130
+ }
131
+ }
132
+
133
+ function updateSelectorSource<TKey>(
134
+ selectorSource: SelectorSource<TKey> | undefined,
135
+ selected: boolean,
136
+ ): void {
137
+ if (
138
+ selectorSource === undefined ||
139
+ selectorSource.selected === selected
140
+ ) {
141
+ return;
142
+ }
143
+
144
+ selectorSource.selected = selected;
145
+ const callbacks = selectorSource.callbacks;
146
+
147
+ if (typeof callbacks === "function") {
148
+ callbacks(selected);
149
+ } else if (callbacks !== undefined) {
150
+ for (const callback of callbacks) {
151
+ callback(selected);
152
+ }
153
+ }
154
+
155
+ notifySubscribers(selectorSource);
156
+ }
package/src/state.ts CHANGED
@@ -11,6 +11,7 @@ export interface Source {
11
11
  // empties back to null). Most sources never allocate a Set at all, and hot
12
12
  // write sites can gate on a null check instead of a Set.size accessor.
13
13
  subscribers: ReactiveComputation | Set<ReactiveComputation> | null;
14
+ onNoSubscribers?: (() => void) | undefined;
14
15
  trackedBy?: ReactiveComputation | undefined;
15
16
  trackedVersion?: number | undefined;
16
17
  }
package/src/tracking.ts CHANGED
@@ -18,7 +18,10 @@ export function trackSource(source: Source): void {
18
18
  tracker.deps.add(source);
19
19
  }
20
20
 
21
- function addSourceSubscriber(source: Source, computation: ReactiveComputation): void {
21
+ export function addSourceSubscriber(
22
+ source: Source,
23
+ computation: ReactiveComputation,
24
+ ): void {
22
25
  const subscribers = source.subscribers;
23
26
 
24
27
  if (subscribers === null) {
@@ -30,17 +33,22 @@ function addSourceSubscriber(source: Source, computation: ReactiveComputation):
30
33
  }
31
34
  }
32
35
 
33
- function removeSourceSubscriber(source: Source, computation: ReactiveComputation): boolean {
36
+ export function removeSourceSubscriber(
37
+ source: Source,
38
+ computation: ReactiveComputation,
39
+ ): boolean {
34
40
  const subscribers = source.subscribers;
35
41
 
36
42
  if (subscribers === computation) {
37
43
  source.subscribers = null;
44
+ source.onNoSubscribers?.();
38
45
  return true;
39
46
  }
40
47
 
41
48
  if (subscribers instanceof Set && subscribers.delete(computation)) {
42
49
  if (subscribers.size === 0) {
43
50
  source.subscribers = null;
51
+ source.onNoSubscribers?.();
44
52
  }
45
53
  return true;
46
54
  }
@@ -129,7 +137,10 @@ export function trackIncrementalSource(
129
137
  computation.trackingCount = (computation.trackingCount ?? 0) + 1;
130
138
  computation.trackingTouchedDeps?.push(source);
131
139
 
132
- if (alreadyTrackedByComputation || computation.deps.has(source)) {
140
+ if (
141
+ alreadyTrackedByComputation ||
142
+ (computation.deps.size > 0 && computation.deps.has(source))
143
+ ) {
133
144
  return;
134
145
  }
135
146