@reckona/mreact-reactive-core 0.0.163 → 0.0.164

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AA4BvC,wBAAgB,4BAA4B,IAAI,IAAI,CAInD;AAqED,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;AA6BvC,wBAAgB,4BAA4B,IAAI,IAAI,CAInD;AA6ED,wBAAgB,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAiB3C"}
package/dist/cell.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { runtimeState } from "./state.js";
1
2
  import { notifySubscribers, sourceSubscriberCount, trackSource } from "./tracking.js";
2
3
  const clientDevtoolsDisabled = typeof __MREACT_CLIENT_DEVTOOLS__ !== "undefined" &&
3
4
  __MREACT_CLIENT_DEVTOOLS__ === false;
@@ -60,7 +61,14 @@ function writeCellValue(state, next) {
60
61
  if (!clientDevtoolsDisabled && cachedDevtoolsHook !== null) {
61
62
  emitCellSetEvent(state.source, previous, resolved);
62
63
  }
63
- if (state.source.subscribers !== null) {
64
+ const subscribers = state.source.subscribers;
65
+ if (subscribers !== null) {
66
+ if (runtimeState.batchDepth > 0 && !(subscribers instanceof Set)) {
67
+ if (!subscribers.disposed && !subscribers.queued) {
68
+ subscribers.markDirty();
69
+ }
70
+ return;
71
+ }
64
72
  notifySubscribers(state.source);
65
73
  }
66
74
  }
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 { 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 if (state.source.subscribers !== null) {\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 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 +1 @@
1
- {"version":3,"file":"computed.d.ts","sourceRoot":"","sources":["../src/computed.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;AAEpE,sDAAsD;AACtD,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CAC1C;AAED,0EAA0E;AAC1E,wBAAgB,QAAQ,CAAC,CAAC,EACxB,EAAE,EAAE,MAAM,CAAC,EACX,OAAO,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,GACjD,YAAY,CAAC,CAAC,CAAC,CAyIjB"}
1
+ {"version":3,"file":"computed.d.ts","sourceRoot":"","sources":["../src/computed.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;AAEpE,sDAAsD;AACtD,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CAC1C;AAED,0EAA0E;AAC1E,wBAAgB,QAAQ,CAAC,CAAC,EACxB,EAAE,EAAE,MAAM,CAAC,EACX,OAAO,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,GACjD,YAAY,CAAC,CAAC,CAAC,CA4KjB"}
package/dist/computed.js CHANGED
@@ -23,7 +23,7 @@ export function computed(fn, options) {
23
23
  }
24
24
  dirty = true;
25
25
  if (source.subscribers !== null) {
26
- if (runtimeState.notificationDepth > 0) {
26
+ if (runtimeState.notificationDepth > 0 || runtimeState.batchDepth > 0) {
27
27
  computation.queued = true;
28
28
  runtimeState.pendingComputed.add(computation);
29
29
  return;
@@ -45,6 +45,7 @@ export function computed(fn, options) {
45
45
  computation.queued = false;
46
46
  runtimeState.pendingComputed.delete(computation);
47
47
  cleanupDeps(computation);
48
+ computation.orderedDeps = undefined;
48
49
  source.subscribers = null;
49
50
  },
50
51
  };
@@ -78,17 +79,42 @@ export function computed(fn, options) {
78
79
  const previousTracker = runtimeState.activeTracker;
79
80
  const previousDepsSize = computation.deps.size;
80
81
  const nextTrackingVersion = nextTrackingVersionFor(computation);
81
- computation.trackingAddedDeps = [];
82
+ computation.trackingAddedDeps = undefined;
82
83
  computation.trackingCount = 0;
84
+ computation.trackingOrderedIndex =
85
+ computation.orderedDeps === undefined ? undefined : 0;
86
+ computation.trackingOrderedMismatch = false;
83
87
  computation.trackingVersion = nextTrackingVersion;
84
88
  runtimeState.activeTracker = computation;
85
89
  try {
86
90
  const nextValue = fn();
87
91
  const addedDeps = computation.trackingAddedDeps;
88
92
  const trackedCount = computation.trackingCount ?? 0;
89
- if (trackedCount !== previousDepsSize || (addedDeps?.length ?? 0) > 0) {
93
+ const addedDepsCount = addedDeps?.length ?? 0;
94
+ const orderedMismatch = computation.trackingOrderedMismatch;
95
+ if (trackedCount !== previousDepsSize || addedDepsCount > 0) {
96
+ const orderedIndex = computation.trackingOrderedIndex;
97
+ if (computation.trackingTouchedDeps === undefined &&
98
+ orderedIndex !== undefined &&
99
+ orderedIndex > 0 &&
100
+ computation.orderedDeps !== undefined) {
101
+ computation.trackingTouchedDeps = computation.orderedDeps.slice(0, orderedIndex);
102
+ }
90
103
  cleanupUntrackedDeps(computation, nextTrackingVersion);
91
104
  }
105
+ if (orderedMismatch !== true &&
106
+ trackedCount === previousDepsSize &&
107
+ addedDepsCount === 0) {
108
+ // Keep the previous stable order.
109
+ }
110
+ else if (previousDepsSize === 0 &&
111
+ addedDeps !== undefined &&
112
+ trackedCount === addedDeps.length) {
113
+ computation.orderedDeps = addedDeps;
114
+ }
115
+ else {
116
+ computation.orderedDeps = undefined;
117
+ }
92
118
  value = nextValue;
93
119
  hasValue = true;
94
120
  dirty = false;
@@ -102,6 +128,8 @@ export function computed(fn, options) {
102
128
  finally {
103
129
  computation.trackingAddedDeps = undefined;
104
130
  computation.trackingCount = undefined;
131
+ computation.trackingOrderedIndex = undefined;
132
+ computation.trackingOrderedMismatch = undefined;
105
133
  computation.trackingTouchedDeps = undefined;
106
134
  runtimeState.activeTracker = previousTracker;
107
135
  }
@@ -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) {\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 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 = [];\n computation.trackingCount = 0;\n computation.trackingVersion = nextTrackingVersion;\n runtimeState.activeTracker = computation;\n\n try {\n const nextValue = fn();\n\n const addedDeps = computation.trackingAddedDeps;\n const trackedCount = computation.trackingCount ?? 0;\n\n if (trackedCount !== previousDepsSize || (addedDeps?.length ?? 0) > 0) {\n cleanupUntrackedDeps(computation, nextTrackingVersion);\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.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 (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"]}
@@ -6,6 +6,10 @@ export { registerCleanup, withCleanupScope } from "./cleanup-scope.js";
6
6
  export { flushQueuedComputations, schedulePendingFlush, setScheduler } from "./scheduler.js";
7
7
  /** Computed flush helper used by batched reactive updates. */
8
8
  export { flushPendingComputed } from "./tracking.js";
9
+ /** Low-level source helpers used by reactive DOM keyed item proxies. */
10
+ export { notifySubscribers, trackSource } from "./tracking.js";
9
11
  /** Shared global runtime state helper for singleton package state. */
10
12
  export { getGlobalRuntimeState } from "./runtime-state.js";
13
+ export { runtimeState } from "./state.js";
14
+ export type { Source } from "./state.js";
11
15
  //# sourceMappingURL=internal.d.ts.map
@@ -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,sEAAsE;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,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,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
@@ -4,6 +4,9 @@ export { registerCleanup, withCleanupScope } from "./cleanup-scope.js";
4
4
  export { flushQueuedComputations, schedulePendingFlush, setScheduler } from "./scheduler.js";
5
5
  /** Computed flush helper used by batched reactive updates. */
6
6
  export { flushPendingComputed } from "./tracking.js";
7
+ /** Low-level source helpers used by reactive DOM keyed item proxies. */
8
+ export { notifySubscribers, trackSource } from "./tracking.js";
7
9
  /** Shared global runtime state helper for singleton package state. */
8
10
  export { getGlobalRuntimeState } from "./runtime-state.js";
11
+ export { runtimeState } from "./state.js";
9
12
  //# sourceMappingURL=internal.js.map
@@ -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,sEAAsE;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,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/** Shared global runtime state helper for singleton package state. */\nexport { getGlobalRuntimeState } from \"./runtime-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,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"]}
package/dist/state.d.ts CHANGED
@@ -6,8 +6,11 @@ export interface Source {
6
6
  export interface ReactiveComputation {
7
7
  readonly id: number;
8
8
  deps: Set<Source>;
9
+ orderedDeps?: Source[] | undefined;
9
10
  trackingAddedDeps?: Source[] | undefined;
10
11
  trackingCount?: number | undefined;
12
+ trackingOrderedIndex?: number | undefined;
13
+ trackingOrderedMismatch?: boolean | undefined;
11
14
  trackingTouchedDeps?: Source[] | undefined;
12
15
  trackingVersion?: number | undefined;
13
16
  disposed: boolean;
@@ -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,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACzC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,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,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;AA6BjD,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 trackingAddedDeps?: Source[] | undefined;\n trackingCount?: number | 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;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 +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,CAalE;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,CAwBN;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,CAgDtD;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;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"}
package/dist/tracking.js CHANGED
@@ -53,6 +53,7 @@ export function cleanupDeps(computation) {
53
53
  }
54
54
  }
55
55
  computation.deps.clear();
56
+ computation.orderedDeps = undefined;
56
57
  }
57
58
  export function nextTrackingVersionFor(computation) {
58
59
  const nextTrackingVersion = (computation.trackingVersion ?? 0) + 1;
@@ -75,16 +76,32 @@ export function trackIncrementalSource(source, computation) {
75
76
  if (source.trackedBy === computation && source.trackedVersion === trackingVersion) {
76
77
  return;
77
78
  }
79
+ const orderedIndex = computation.trackingOrderedIndex;
80
+ const orderedDeps = computation.orderedDeps;
81
+ if (orderedIndex !== undefined &&
82
+ computation.trackingOrderedMismatch !== true &&
83
+ orderedDeps !== undefined) {
84
+ if (orderedDeps[orderedIndex] === source) {
85
+ computation.trackingOrderedIndex = orderedIndex + 1;
86
+ computation.trackingCount = (computation.trackingCount ?? 0) + 1;
87
+ return;
88
+ }
89
+ computation.trackingOrderedMismatch = true;
90
+ if (orderedIndex > 0) {
91
+ computation.trackingTouchedDeps = orderedDeps.slice(0, orderedIndex);
92
+ }
93
+ }
94
+ const alreadyTrackedByComputation = source.trackedBy === computation;
78
95
  source.trackedBy = computation;
79
96
  source.trackedVersion = trackingVersion;
80
97
  computation.trackingCount = (computation.trackingCount ?? 0) + 1;
81
98
  computation.trackingTouchedDeps?.push(source);
82
- if (computation.deps.has(source)) {
99
+ if (alreadyTrackedByComputation || computation.deps.has(source)) {
83
100
  return;
84
101
  }
85
102
  addSourceSubscriber(source, computation);
86
103
  computation.deps.add(source);
87
- computation.trackingAddedDeps?.push(source);
104
+ (computation.trackingAddedDeps ??= []).push(source);
88
105
  }
89
106
  export function preserveIncrementalTracking(computation) {
90
107
  const trackingVersion = computation.trackingVersion;
@@ -140,6 +157,12 @@ export function notifySubscribers(source) {
140
157
  return;
141
158
  }
142
159
  if (!(subscribers instanceof Set)) {
160
+ if (runtimeState.batchDepth > 0) {
161
+ if (!subscribers.disposed && !subscribers.queued) {
162
+ subscribers.markDirty();
163
+ }
164
+ return;
165
+ }
143
166
  runtimeState.notificationDepth += 1;
144
167
  try {
145
168
  if (!subscribers.disposed && !subscribers.queued) {
@@ -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}\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 source.trackedBy = computation;\n source.trackedVersion = trackingVersion;\n computation.trackingCount = (computation.trackingCount ?? 0) + 1;\n computation.trackingTouchedDeps?.push(source);\n\n if (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 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\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reckona/mreact-reactive-core",
3
- "version": "0.0.163",
3
+ "version": "0.0.164",
4
4
  "description": "Fine-grained reactive primitives used across mreact.",
5
5
  "keywords": [
6
6
  "jsx",
package/src/cell.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { Cell } from "./types.js";
2
2
  import type { Source } from "./state.js";
3
+ import { runtimeState } from "./state.js";
3
4
  import { notifySubscribers, sourceSubscriberCount, trackSource } from "./tracking.js";
4
5
 
5
6
  declare const __MREACT_CLIENT_DEVTOOLS__: boolean | undefined;
@@ -94,7 +95,15 @@ function writeCellValue<T>(state: CellState<T>, next: T | ((prev: T) => T)): voi
94
95
  emitCellSetEvent(state.source, previous, resolved);
95
96
  }
96
97
 
97
- if (state.source.subscribers !== null) {
98
+ const subscribers = state.source.subscribers;
99
+ if (subscribers !== null) {
100
+ if (runtimeState.batchDepth > 0 && !(subscribers instanceof Set)) {
101
+ if (!subscribers.disposed && !subscribers.queued) {
102
+ subscribers.markDirty();
103
+ }
104
+ return;
105
+ }
106
+
98
107
  notifySubscribers(state.source);
99
108
  }
100
109
  }
package/src/computed.ts CHANGED
@@ -50,7 +50,7 @@ export function computed<T>(
50
50
  dirty = true;
51
51
 
52
52
  if (source.subscribers !== null) {
53
- if (runtimeState.notificationDepth > 0) {
53
+ if (runtimeState.notificationDepth > 0 || runtimeState.batchDepth > 0) {
54
54
  computation.queued = true;
55
55
  runtimeState.pendingComputed.add(computation);
56
56
  return;
@@ -74,6 +74,7 @@ export function computed<T>(
74
74
  computation.queued = false;
75
75
  runtimeState.pendingComputed.delete(computation);
76
76
  cleanupDeps(computation);
77
+ computation.orderedDeps = undefined;
77
78
  source.subscribers = null;
78
79
  },
79
80
  };
@@ -114,21 +115,53 @@ export function computed<T>(
114
115
  const previousDepsSize = computation.deps.size;
115
116
  const nextTrackingVersion = nextTrackingVersionFor(computation);
116
117
 
117
- computation.trackingAddedDeps = [];
118
+ computation.trackingAddedDeps = undefined;
118
119
  computation.trackingCount = 0;
120
+ computation.trackingOrderedIndex =
121
+ computation.orderedDeps === undefined ? undefined : 0;
122
+ computation.trackingOrderedMismatch = false;
119
123
  computation.trackingVersion = nextTrackingVersion;
120
124
  runtimeState.activeTracker = computation;
121
125
 
122
126
  try {
123
127
  const nextValue = fn();
124
128
 
125
- const addedDeps = computation.trackingAddedDeps;
129
+ const addedDeps = computation.trackingAddedDeps as Source[] | undefined;
126
130
  const trackedCount = computation.trackingCount ?? 0;
131
+ const addedDepsCount = addedDeps?.length ?? 0;
132
+ const orderedMismatch = computation.trackingOrderedMismatch as boolean | undefined;
133
+
134
+ if (trackedCount !== previousDepsSize || addedDepsCount > 0) {
135
+ const orderedIndex = computation.trackingOrderedIndex;
136
+
137
+ if (
138
+ computation.trackingTouchedDeps === undefined &&
139
+ orderedIndex !== undefined &&
140
+ orderedIndex > 0 &&
141
+ computation.orderedDeps !== undefined
142
+ ) {
143
+ computation.trackingTouchedDeps = computation.orderedDeps.slice(0, orderedIndex);
144
+ }
127
145
 
128
- if (trackedCount !== previousDepsSize || (addedDeps?.length ?? 0) > 0) {
129
146
  cleanupUntrackedDeps(computation, nextTrackingVersion);
130
147
  }
131
148
 
149
+ if (
150
+ orderedMismatch !== true &&
151
+ trackedCount === previousDepsSize &&
152
+ addedDepsCount === 0
153
+ ) {
154
+ // Keep the previous stable order.
155
+ } else if (
156
+ previousDepsSize === 0 &&
157
+ addedDeps !== undefined &&
158
+ trackedCount === addedDeps.length
159
+ ) {
160
+ computation.orderedDeps = addedDeps;
161
+ } else {
162
+ computation.orderedDeps = undefined;
163
+ }
164
+
132
165
  value = nextValue;
133
166
  hasValue = true;
134
167
  dirty = false;
@@ -142,6 +175,8 @@ export function computed<T>(
142
175
  } finally {
143
176
  computation.trackingAddedDeps = undefined;
144
177
  computation.trackingCount = undefined;
178
+ computation.trackingOrderedIndex = undefined;
179
+ computation.trackingOrderedMismatch = undefined;
145
180
  computation.trackingTouchedDeps = undefined;
146
181
  runtimeState.activeTracker = previousTracker;
147
182
  }
package/src/internal.ts CHANGED
@@ -6,5 +6,9 @@ export { registerCleanup, withCleanupScope } from "./cleanup-scope.js";
6
6
  export { flushQueuedComputations, schedulePendingFlush, setScheduler } from "./scheduler.js";
7
7
  /** Computed flush helper used by batched reactive updates. */
8
8
  export { flushPendingComputed } from "./tracking.js";
9
+ /** Low-level source helpers used by reactive DOM keyed item proxies. */
10
+ export { notifySubscribers, trackSource } from "./tracking.js";
9
11
  /** Shared global runtime state helper for singleton package state. */
10
12
  export { getGlobalRuntimeState } from "./runtime-state.js";
13
+ export { runtimeState } from "./state.js";
14
+ export type { Source } from "./state.js";
package/src/state.ts CHANGED
@@ -18,8 +18,11 @@ export interface Source {
18
18
  export interface ReactiveComputation {
19
19
  readonly id: number;
20
20
  deps: Set<Source>;
21
+ orderedDeps?: Source[] | undefined;
21
22
  trackingAddedDeps?: Source[] | undefined;
22
23
  trackingCount?: number | undefined;
24
+ trackingOrderedIndex?: number | undefined;
25
+ trackingOrderedMismatch?: boolean | undefined;
23
26
  trackingTouchedDeps?: Source[] | undefined;
24
27
  trackingVersion?: number | undefined;
25
28
  disposed: boolean;
package/src/tracking.ts CHANGED
@@ -67,6 +67,7 @@ export function cleanupDeps(computation: ReactiveComputation): void {
67
67
  }
68
68
 
69
69
  computation.deps.clear();
70
+ computation.orderedDeps = undefined;
70
71
  }
71
72
 
72
73
  export function nextTrackingVersionFor(computation: ReactiveComputation): number {
@@ -100,18 +101,41 @@ export function trackIncrementalSource(
100
101
  return;
101
102
  }
102
103
 
104
+ const orderedIndex = computation.trackingOrderedIndex;
105
+ const orderedDeps = computation.orderedDeps;
106
+
107
+ if (
108
+ orderedIndex !== undefined &&
109
+ computation.trackingOrderedMismatch !== true &&
110
+ orderedDeps !== undefined
111
+ ) {
112
+ if (orderedDeps[orderedIndex] === source) {
113
+ computation.trackingOrderedIndex = orderedIndex + 1;
114
+ computation.trackingCount = (computation.trackingCount ?? 0) + 1;
115
+ return;
116
+ }
117
+
118
+ computation.trackingOrderedMismatch = true;
119
+
120
+ if (orderedIndex > 0) {
121
+ computation.trackingTouchedDeps = orderedDeps.slice(0, orderedIndex);
122
+ }
123
+ }
124
+
125
+ const alreadyTrackedByComputation = source.trackedBy === computation;
126
+
103
127
  source.trackedBy = computation;
104
128
  source.trackedVersion = trackingVersion;
105
129
  computation.trackingCount = (computation.trackingCount ?? 0) + 1;
106
130
  computation.trackingTouchedDeps?.push(source);
107
131
 
108
- if (computation.deps.has(source)) {
132
+ if (alreadyTrackedByComputation || computation.deps.has(source)) {
109
133
  return;
110
134
  }
111
135
 
112
136
  addSourceSubscriber(source, computation);
113
137
  computation.deps.add(source);
114
- computation.trackingAddedDeps?.push(source);
138
+ (computation.trackingAddedDeps ??= []).push(source);
115
139
  }
116
140
 
117
141
  export function preserveIncrementalTracking(computation: ReactiveComputation): void {
@@ -191,6 +215,13 @@ export function notifySubscribers(source: Source): void {
191
215
  }
192
216
 
193
217
  if (!(subscribers instanceof Set)) {
218
+ if (runtimeState.batchDepth > 0) {
219
+ if (!subscribers.disposed && !subscribers.queued) {
220
+ subscribers.markDirty();
221
+ }
222
+ return;
223
+ }
224
+
194
225
  runtimeState.notificationDepth += 1;
195
226
 
196
227
  try {