@reckona/mreact-reactive-core 0.0.169 → 0.0.171
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cell-subscription.d.ts +3 -0
- package/dist/cell-subscription.d.ts.map +1 -0
- package/dist/cell-subscription.js +50 -0
- package/dist/cell-subscription.js.map +1 -0
- package/dist/cell.d.ts +3 -0
- package/dist/cell.d.ts.map +1 -1
- package/dist/cell.js +20 -14
- package/dist/cell.js.map +1 -1
- package/dist/computed.js +1 -1
- package/dist/computed.js.map +1 -1
- package/dist/effect.js +4 -3
- package/dist/effect.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +2 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -0
- package/dist/internal.js.map +1 -1
- package/dist/selector.d.ts +14 -0
- package/dist/selector.d.ts.map +1 -0
- package/dist/selector.js +105 -0
- package/dist/selector.js.map +1 -0
- package/dist/state.d.ts +1 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js.map +1 -1
- package/dist/tracking.d.ts +2 -0
- package/dist/tracking.d.ts.map +1 -1
- package/dist/tracking.js +6 -3
- package/dist/tracking.js.map +1 -1
- package/package.json +1 -1
- package/src/cell-subscription.ts +75 -0
- package/src/cell.ts +30 -16
- package/src/computed.ts +1 -1
- package/src/effect.ts +6 -3
- package/src/index.ts +2 -0
- package/src/internal.ts +2 -0
- package/src/selector.ts +156 -0
- package/src/state.ts +1 -0
- package/src/tracking.ts +14 -3
|
@@ -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
|
package/dist/cell.d.ts.map
CHANGED
|
@@ -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;
|
|
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(
|
|
52
|
-
const previous =
|
|
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
|
-
|
|
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(
|
|
68
|
+
emitCellSetEvent(source, previous, resolved);
|
|
63
69
|
}
|
|
64
|
-
const 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(
|
|
78
|
+
notifySubscribers(source);
|
|
73
79
|
}
|
|
74
80
|
}
|
|
75
81
|
export function cell(initial) {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
subscribers: null,
|
|
79
|
-
},
|
|
82
|
+
const source = {
|
|
83
|
+
subscribers: null,
|
|
80
84
|
value: initial,
|
|
81
85
|
};
|
|
82
|
-
|
|
86
|
+
const value = {
|
|
87
|
+
[cellSourceSymbol]: source,
|
|
83
88
|
get() {
|
|
84
|
-
trackSource(
|
|
85
|
-
return
|
|
89
|
+
trackSource(source);
|
|
90
|
+
return source.value;
|
|
86
91
|
},
|
|
87
92
|
set(next) {
|
|
88
|
-
writeCellValue(
|
|
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":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAItF,MAAM,sBAAsB,GAC1B,OAAO,0BAA0B,KAAK,WAAW;IACjD,0BAA0B,KAAK,KAAK,CAAC;AAUvC,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,uEAAuE;AACvE,6EAA6E;AAC7E,4EAA4E;AAC5E,IAAI,kBAAkB,GAAoC,sBAAsB;IAC9E,CAAC,CAAC,IAAI;IACN,CAAC,CAAC,SAAS,CAAC;AAEd,MAAM,UAAU,4BAA4B;IAC1C,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC5B,kBAAkB,GAAG,SAAS,CAAC;IACjC,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB;IAC1B,MAAM,IAAI,GAAI,UAAiC,CAAC,gBAAgB,CAAC;IACjE,MAAM,QAAQ,GAAG,IAAI,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACrF,kBAAkB,GAAG,QAAQ,CAAC;IAC9B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,gBAAgB,CAAI,MAAc,EAAE,QAAW,EAAE,KAAQ;IAChE,4EAA4E;IAC5E,0EAA0E;IAC1E,oEAAoE;IACpE,MAAM,IAAI,GAAI,UAAiC,CAAC,gBAAgB,CAAC;IACjE,MAAM,IAAI,GACR,kBAAkB,KAAK,SAAS,IAAI,kBAAkB,KAAK,IAAI;QAC7D,CAAC,CAAC,kBAAkB;QACpB,CAAC,CAAC,mBAAmB,EAAE,CAAC;IAE5B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IAEvB,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;QAC/B,OAAO;IACT,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QACd,OAAO,EAAE,+BAA+B;QACxC,QAAQ;QACR,WAAW,EAAE,qBAAqB,CAAC,MAAM,CAAC;QAC1C,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,IAAI,EAAE,mBAAmB;QACzB,KAAK;KACN,CAAC,CAAC;AACL,CAAC;
|
|
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 &&
|
package/dist/computed.js.map
CHANGED
|
@@ -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 (
|
|
108
|
+
if (previousDepsSize > 0 &&
|
|
109
|
+
(trackedCount !== previousDepsSize || (addedDeps?.length ?? 0) > 0)) {
|
|
109
110
|
cleanupUntrackedDeps(computation, trackingVersion);
|
|
110
111
|
}
|
|
111
112
|
computation.trackingAddedDeps = undefined;
|
package/dist/effect.js.map
CHANGED
|
@@ -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,
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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"]}
|
package/dist/internal.d.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";
|
package/dist/internal.d.ts.map
CHANGED
|
@@ -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";
|
package/dist/internal.js.map
CHANGED
|
@@ -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"}
|
package/dist/selector.js
ADDED
|
@@ -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":"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","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
package/dist/state.d.ts.map
CHANGED
|
@@ -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;
|
|
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"]}
|
package/dist/tracking.d.ts
CHANGED
|
@@ -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;
|
package/dist/tracking.d.ts.map
CHANGED
|
@@ -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;
|
|
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 ||
|
|
101
|
+
if (alreadyTrackedByComputation ||
|
|
102
|
+
(computation.deps.size > 0 && computation.deps.has(source))) {
|
|
100
103
|
return;
|
|
101
104
|
}
|
|
102
105
|
addSourceSubscriber(source, computation);
|
package/dist/tracking.js.map
CHANGED
|
@@ -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
|
@@ -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
|
|
73
|
+
interface CellSource<T> extends Source {
|
|
74
74
|
value: T;
|
|
75
|
-
|
|
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>(
|
|
81
|
-
const previous =
|
|
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
|
-
|
|
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(
|
|
108
|
+
emitCellSetEvent(source, previous, resolved);
|
|
96
109
|
}
|
|
97
110
|
|
|
98
|
-
const 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(
|
|
120
|
+
notifySubscribers(source);
|
|
108
121
|
}
|
|
109
122
|
}
|
|
110
123
|
|
|
111
124
|
export function cell<T>(initial: T): Cell<T> {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
subscribers: null,
|
|
115
|
-
},
|
|
125
|
+
const source: CellSource<T> = {
|
|
126
|
+
subscribers: null,
|
|
116
127
|
value: initial,
|
|
117
128
|
};
|
|
118
129
|
|
|
119
|
-
|
|
130
|
+
const value: SourceBackedCell<T> = {
|
|
131
|
+
[cellSourceSymbol]: source,
|
|
120
132
|
get(): T {
|
|
121
|
-
trackSource(
|
|
122
|
-
return
|
|
133
|
+
trackSource(source);
|
|
134
|
+
return source.value;
|
|
123
135
|
},
|
|
124
136
|
set(next: T | ((prev: T) => T)): void {
|
|
125
|
-
writeCellValue(
|
|
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 (
|
|
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";
|
package/src/selector.ts
ADDED
|
@@ -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(
|
|
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(
|
|
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 (
|
|
140
|
+
if (
|
|
141
|
+
alreadyTrackedByComputation ||
|
|
142
|
+
(computation.deps.size > 0 && computation.deps.has(source))
|
|
143
|
+
) {
|
|
133
144
|
return;
|
|
134
145
|
}
|
|
135
146
|
|