@fluixi/reactive 1.0.0-alpha.83 → 1.0.0-alpha.84
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/cdn/reactive.cjs +2673 -1
- package/dist/cdn/reactive.global.js +2 -1
- package/dist/cdn/reactive.mjs +2657 -1
- package/dist/cdn/signal.cjs +299 -1
- package/dist/cdn/signal.d.ts +1 -1
- package/dist/cdn/signal.global.js +2 -1
- package/dist/cdn/signal.js +1 -1
- package/dist/cdn/signal.mjs +276 -1
- package/dist/index.cjs +2673 -1
- package/dist/index.mjs +2657 -1
- package/dist/lib/index.cjs +2674 -1
- package/dist/lib/index.js +1 -1
- package/dist/lib/index.mjs +2659 -1
- package/dist/lib/observable/index.cjs +386 -1
- package/dist/lib/observable/index.mjs +364 -1
- package/dist/lib/observable/observable.cjs +384 -1
- package/dist/lib/observable/observable.d.ts +0 -137
- package/dist/lib/observable/observable.d.ts.map +1 -1
- package/dist/lib/observable/observable.js +0 -71
- package/dist/lib/observable/observable.mjs +361 -1
- package/dist/lib/observable/subscription.cjs +180 -1
- package/dist/lib/observable/subscription.d.ts +0 -12
- package/dist/lib/observable/subscription.d.ts.map +1 -1
- package/dist/lib/observable/subscription.js +0 -12
- package/dist/lib/observable/subscription.mjs +157 -1
- package/dist/lib/observable/types.cjs +18 -1
- package/dist/lib/observable/types.d.ts.map +1 -1
- package/dist/lib/predicates.cjs +44 -1
- package/dist/lib/predicates.d.ts +2 -2
- package/dist/lib/predicates.js +2 -2
- package/dist/lib/predicates.mjs +23 -1
- package/dist/lib/signal/api.cjs +571 -1
- package/dist/lib/signal/api.d.ts +211 -1
- package/dist/lib/signal/api.d.ts.map +1 -1
- package/dist/lib/signal/api.js +207 -12
- package/dist/lib/signal/api.mjs +548 -1
- package/dist/lib/signal/cached-resource.cjs +672 -1
- package/dist/lib/signal/cached-resource.d.ts +28 -16
- package/dist/lib/signal/cached-resource.d.ts.map +1 -1
- package/dist/lib/signal/cached-resource.js +35 -29
- package/dist/lib/signal/cached-resource.mjs +650 -1
- package/dist/lib/signal/graph/children.cjs +325 -1
- package/dist/lib/signal/graph/children.js +1 -1
- package/dist/lib/signal/graph/children.mjs +302 -1
- package/dist/lib/signal/graph/compat.cjs +578 -1
- package/dist/lib/signal/graph/compat.d.ts +13 -13
- package/dist/lib/signal/graph/compat.d.ts.map +1 -1
- package/dist/lib/signal/graph/compat.js +33 -31
- package/dist/lib/signal/graph/compat.mjs +556 -1
- package/dist/lib/signal/graph/core.cjs +295 -1
- package/dist/lib/signal/graph/core.d.ts +28 -24
- package/dist/lib/signal/graph/core.d.ts.map +1 -1
- package/dist/lib/signal/graph/core.js +38 -31
- package/dist/lib/signal/graph/core.mjs +273 -1
- package/dist/lib/signal/graph/debounced.cjs +668 -1
- package/dist/lib/signal/graph/debounced.d.ts +5 -5
- package/dist/lib/signal/graph/debounced.d.ts.map +1 -1
- package/dist/lib/signal/graph/debounced.js +5 -5
- package/dist/lib/signal/graph/debounced.mjs +645 -1
- package/dist/lib/signal/graph/derived.cjs +496 -1
- package/dist/lib/signal/graph/derived.d.ts +1 -1
- package/dist/lib/signal/graph/derived.js +1 -1
- package/dist/lib/signal/graph/derived.mjs +473 -1
- package/dist/lib/signal/graph/detect.cjs +66 -1
- package/dist/lib/signal/graph/detect.mjs +45 -1
- package/dist/lib/signal/graph/error-boundary.cjs +301 -1
- package/dist/lib/signal/graph/error-boundary.js +3 -3
- package/dist/lib/signal/graph/error-boundary.mjs +278 -1
- package/dist/lib/signal/graph/index.cjs +305 -1
- package/dist/lib/signal/graph/index.d.ts +7 -6
- package/dist/lib/signal/graph/index.d.ts.map +1 -1
- package/dist/lib/signal/graph/index.js +7 -6
- package/dist/lib/signal/graph/index.mjs +283 -1
- package/dist/lib/signal/graph/observe.cjs +163 -1
- package/dist/lib/signal/graph/observe.d.ts +8 -8
- package/dist/lib/signal/graph/observe.js +5 -5
- package/dist/lib/signal/graph/observe.mjs +140 -1
- package/dist/lib/signal/graph/resource.cjs +620 -1
- package/dist/lib/signal/graph/resource.d.ts +1 -1
- package/dist/lib/signal/graph/resource.js +7 -8
- package/dist/lib/signal/graph/resource.mjs +597 -1
- package/dist/lib/signal/graph/runtime.cjs +501 -1
- package/dist/lib/signal/graph/runtime.d.ts +7 -7
- package/dist/lib/signal/graph/runtime.d.ts.map +1 -1
- package/dist/lib/signal/graph/runtime.js +34 -30
- package/dist/lib/signal/graph/runtime.mjs +478 -1
- package/dist/lib/signal/graph/signal-next.cjs +1208 -1
- package/dist/lib/signal/graph/signal-next.d.ts +5 -5
- package/dist/lib/signal/graph/signal-next.d.ts.map +1 -1
- package/dist/lib/signal/graph/signal-next.js +14 -12
- package/dist/lib/signal/graph/signal-next.mjs +1186 -1
- package/dist/lib/signal/graph/ssr-hooks.cjs +60 -1
- package/dist/lib/signal/graph/ssr-hooks.d.ts +6 -7
- package/dist/lib/signal/graph/ssr-hooks.d.ts.map +1 -1
- package/dist/lib/signal/graph/ssr-hooks.js +6 -7
- package/dist/lib/signal/graph/ssr-hooks.mjs +39 -1
- package/dist/lib/signal/graph/state.cjs +72 -1
- package/dist/lib/signal/graph/state.d.ts +41 -10
- package/dist/lib/signal/graph/state.d.ts.map +1 -1
- package/dist/lib/signal/graph/state.js +83 -22
- package/dist/lib/signal/graph/state.mjs +49 -1
- package/dist/lib/signal/graph/suspense.cjs +301 -1
- package/dist/lib/signal/graph/suspense.js +4 -4
- package/dist/lib/signal/graph/suspense.mjs +278 -1
- package/dist/lib/signal/index-legacy.cjs +2540 -1
- package/dist/lib/signal/index-legacy.d.ts +1 -1
- package/dist/lib/signal/index-legacy.js +1 -1
- package/dist/lib/signal/index-legacy.mjs +2518 -1
- package/dist/lib/signal/index.cjs +1275 -1
- package/dist/lib/signal/index.d.ts +3 -2
- package/dist/lib/signal/index.d.ts.map +1 -1
- package/dist/lib/signal/index.js +7 -4
- package/dist/lib/signal/index.mjs +1253 -1
- package/dist/lib/signal/list.cjs +549 -1
- package/dist/lib/signal/list.d.ts +5 -5
- package/dist/lib/signal/list.js +5 -5
- package/dist/lib/signal/list.mjs +526 -1
- package/dist/lib/signal/resource-api.cjs +626 -1
- package/dist/lib/signal/resource-api.d.ts +5 -5
- package/dist/lib/signal/resource-api.js +5 -5
- package/dist/lib/signal/resource-api.mjs +603 -1
- package/dist/lib/signal/signal.cjs +2023 -1
- package/dist/lib/signal/signal.d.ts +4 -4
- package/dist/lib/signal/signal.d.ts.map +1 -1
- package/dist/lib/signal/signal.js +22 -23
- package/dist/lib/signal/signal.mjs +2000 -1
- package/dist/lib/signal/types.cjs +38 -1
- package/dist/lib/signal/types.d.ts.map +1 -1
- package/dist/lib/signal/types.js +2 -2
- package/dist/lib/signal/types.mjs +17 -1
- package/dist/lib/signal/utilities.cjs +1012 -1
- package/dist/lib/signal/utilities.d.ts +0 -108
- package/dist/lib/signal/utilities.d.ts.map +1 -1
- package/dist/lib/signal/utilities.js +0 -158
- package/dist/lib/signal/utilities.mjs +990 -1
- package/dist/lib/store/api.cjs +612 -1
- package/dist/lib/store/api.d.ts +33 -0
- package/dist/lib/store/api.d.ts.map +1 -1
- package/dist/lib/store/api.js +41 -8
- package/dist/lib/store/api.mjs +596 -1
- package/dist/lib/store/index.cjs +691 -1
- package/dist/lib/store/index.js +1 -1
- package/dist/lib/store/index.mjs +676 -1
- package/dist/lib/store/setter.types.cjs +18 -1
- package/dist/lib/store/setter.types.d.ts +43 -3
- package/dist/lib/store/setter.types.d.ts.map +1 -1
- package/dist/lib/store/store.cjs +658 -1
- package/dist/lib/store/store.d.ts +72 -0
- package/dist/lib/store/store.d.ts.map +1 -1
- package/dist/lib/store/store.js +148 -5
- package/dist/lib/store/store.mjs +644 -1
- package/dist/lib/store/types.cjs +18 -1
- package/dist/lib/store/types.d.ts +1 -1
- package/dist/lib/store/types.d.ts.map +1 -1
- package/dist/lib/store/utils.cjs +43 -1
- package/dist/lib/store/utils.d.ts +1 -1
- package/dist/lib/store/utils.js +1 -1
- package/dist/lib/store/utils.mjs +22 -1
- package/dist/lib/subject.cjs +450 -1
- package/dist/lib/subject.js +3 -91
- package/dist/lib/subject.mjs +427 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/version.generated.cjs +27 -1
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +2 -2
- package/dist/version.generated.mjs +7 -1
- package/package.json +5 -4
|
@@ -1 +1,295 @@
|
|
|
1
|
-
|
|
1
|
+
/*! @fluixi/reactive v1.0.0-alpha.84 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/lib/signal/graph/core.ts
|
|
22
|
+
var core_exports = {};
|
|
23
|
+
__export(core_exports, {
|
|
24
|
+
Computed: () => Computed,
|
|
25
|
+
State: () => State,
|
|
26
|
+
Watcher: () => Watcher,
|
|
27
|
+
currentComputed: () => currentComputed,
|
|
28
|
+
hasSinks: () => hasSinks,
|
|
29
|
+
untrack: () => untrack
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(core_exports);
|
|
32
|
+
|
|
33
|
+
// src/version.generated.ts
|
|
34
|
+
var VERSION = "1.0.0-alpha.84";
|
|
35
|
+
|
|
36
|
+
// src/lib/signal/graph/state.ts
|
|
37
|
+
var MAJOR = VERSION.split(".")[0];
|
|
38
|
+
var KEY = `__fluixi_signal_state_v${MAJOR}__`;
|
|
39
|
+
var VERSIONS_KEY = `__fluixi_signal_builds_v${MAJOR}__`;
|
|
40
|
+
var defaultSchedule = (run) => {
|
|
41
|
+
if (typeof queueMicrotask === "function") queueMicrotask(run);
|
|
42
|
+
else void Promise.resolve().then(run);
|
|
43
|
+
};
|
|
44
|
+
function defaults() {
|
|
45
|
+
return {
|
|
46
|
+
consumer: null,
|
|
47
|
+
untracked: false,
|
|
48
|
+
owner: null,
|
|
49
|
+
queue: [],
|
|
50
|
+
scheduled: false,
|
|
51
|
+
flushing: false,
|
|
52
|
+
batchDepth: 0,
|
|
53
|
+
hostSchedule: defaultSchedule,
|
|
54
|
+
onSuspend: null,
|
|
55
|
+
onError: null,
|
|
56
|
+
observer: null
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
var realm = globalThis;
|
|
60
|
+
function backfillState(existing) {
|
|
61
|
+
const fresh = defaults();
|
|
62
|
+
const target = existing;
|
|
63
|
+
for (const field of Object.keys(fresh)) {
|
|
64
|
+
if (!(field in existing)) target[field] = fresh[field];
|
|
65
|
+
}
|
|
66
|
+
return existing;
|
|
67
|
+
}
|
|
68
|
+
function claim() {
|
|
69
|
+
const existing = realm[KEY];
|
|
70
|
+
return existing ? backfillState(existing) : realm[KEY] = defaults();
|
|
71
|
+
}
|
|
72
|
+
var G = claim();
|
|
73
|
+
(realm[VERSIONS_KEY] ??= []).push(VERSION);
|
|
74
|
+
|
|
75
|
+
// src/lib/signal/graph/observe.ts
|
|
76
|
+
function observeReactiveNodes(next) {
|
|
77
|
+
G.observer = next;
|
|
78
|
+
return () => {
|
|
79
|
+
if (G.observer === next) G.observer = null;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
var running = [];
|
|
83
|
+
function enterReactiveRun(node) {
|
|
84
|
+
if (!G.observer) return false;
|
|
85
|
+
running.push(node);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
function exitReactiveRun() {
|
|
89
|
+
running.pop();
|
|
90
|
+
}
|
|
91
|
+
function reportReactiveRun(node) {
|
|
92
|
+
G.observer?.ran?.(node);
|
|
93
|
+
}
|
|
94
|
+
function reportReactiveWrite(node, by) {
|
|
95
|
+
G.observer?.wrote?.(node, by);
|
|
96
|
+
}
|
|
97
|
+
var HOOK = "__FLUIXI_DEVTOOLS_HOOK__";
|
|
98
|
+
var hook = globalThis[HOOK];
|
|
99
|
+
if (typeof hook?.inject === "function") {
|
|
100
|
+
try {
|
|
101
|
+
hook.inject({ observeReactiveNodes, version: VERSION });
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/lib/signal/graph/core.ts
|
|
107
|
+
var CLEAN = 0;
|
|
108
|
+
var CHECK = 1;
|
|
109
|
+
var DIRTY = 2;
|
|
110
|
+
var Source = class {
|
|
111
|
+
constructor() {
|
|
112
|
+
/** Computeds, and watched leaves, that read this node. */
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
114
|
+
this.observers = null;
|
|
115
|
+
/** Watchers eagerly observing this node, which is how `runtime.ts` schedules effects. */
|
|
116
|
+
this.watchers = null;
|
|
117
|
+
/** Bumped whenever this node's value actually changes (equality-gated). */
|
|
118
|
+
this.version = 0;
|
|
119
|
+
}
|
|
120
|
+
/** Settle this node so a reader sees a current value/version. Overridden by Computed. */
|
|
121
|
+
updateIfNecessary() {
|
|
122
|
+
}
|
|
123
|
+
/** Link the current consumer (if any) as an observer of this node. */
|
|
124
|
+
track() {
|
|
125
|
+
if (G.untracked) return;
|
|
126
|
+
const consumer = G.consumer;
|
|
127
|
+
if (!consumer) return;
|
|
128
|
+
(this.observers ??= /* @__PURE__ */ new Set()).add(consumer);
|
|
129
|
+
(consumer.sources ??= /* @__PURE__ */ new Set()).add(this);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
var State = class extends Source {
|
|
133
|
+
constructor(value, options) {
|
|
134
|
+
super();
|
|
135
|
+
this._value = value;
|
|
136
|
+
this._equals = options?.equals === false ? () => false : options?.equals ?? Object.is;
|
|
137
|
+
}
|
|
138
|
+
get() {
|
|
139
|
+
this.track();
|
|
140
|
+
return this._value;
|
|
141
|
+
}
|
|
142
|
+
set(next) {
|
|
143
|
+
if (this._equals(this._value, next)) return;
|
|
144
|
+
this._value = next;
|
|
145
|
+
this.version++;
|
|
146
|
+
reportReactiveWrite(this, G.consumer);
|
|
147
|
+
if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
|
|
148
|
+
notifyWatchers(this);
|
|
149
|
+
}
|
|
150
|
+
/** Read without subscribing (peek). */
|
|
151
|
+
peek() {
|
|
152
|
+
return this._value;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var Computed = class extends Source {
|
|
156
|
+
constructor(fn, options) {
|
|
157
|
+
super();
|
|
158
|
+
this._threw = false;
|
|
159
|
+
this._initialized = false;
|
|
160
|
+
this.state = DIRTY;
|
|
161
|
+
/** Nodes this computation read on its last run. Rebuilt on every run. */
|
|
162
|
+
this.sources = null;
|
|
163
|
+
this._fn = fn;
|
|
164
|
+
this._equals = options?.equals === false ? () => false : options?.equals ?? Object.is;
|
|
165
|
+
}
|
|
166
|
+
get() {
|
|
167
|
+
if (G.consumer === this) {
|
|
168
|
+
throw new Error("Signal.Computed cannot read itself");
|
|
169
|
+
}
|
|
170
|
+
this.track();
|
|
171
|
+
this.updateIfNecessary();
|
|
172
|
+
if (this._threw) throw this._error;
|
|
173
|
+
return this._value;
|
|
174
|
+
}
|
|
175
|
+
/** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
|
|
176
|
+
updateIfNecessary() {
|
|
177
|
+
if (this.state === CHECK) {
|
|
178
|
+
if (this.sources) {
|
|
179
|
+
for (const s of this.sources) {
|
|
180
|
+
s.updateIfNecessary();
|
|
181
|
+
if (this.state === DIRTY) break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (this.state === DIRTY || !this._initialized) this.recompute();
|
|
186
|
+
this.state = CLEAN;
|
|
187
|
+
}
|
|
188
|
+
recompute() {
|
|
189
|
+
if (this.sources) {
|
|
190
|
+
for (const s of this.sources) s.observers?.delete(this);
|
|
191
|
+
this.sources.clear();
|
|
192
|
+
}
|
|
193
|
+
const prevConsumer = G.consumer;
|
|
194
|
+
const prevUntracked = G.untracked;
|
|
195
|
+
const watched = enterReactiveRun(this);
|
|
196
|
+
G.consumer = this;
|
|
197
|
+
G.untracked = false;
|
|
198
|
+
let next;
|
|
199
|
+
let threw = false;
|
|
200
|
+
let err;
|
|
201
|
+
try {
|
|
202
|
+
next = this._fn();
|
|
203
|
+
} catch (e) {
|
|
204
|
+
threw = true;
|
|
205
|
+
err = e;
|
|
206
|
+
next = this._value;
|
|
207
|
+
} finally {
|
|
208
|
+
G.consumer = prevConsumer;
|
|
209
|
+
G.untracked = prevUntracked;
|
|
210
|
+
if (watched) exitReactiveRun();
|
|
211
|
+
}
|
|
212
|
+
const changed = !this._initialized || this._threw !== threw || (threw ? err !== this._error : !this._equals(this._value, next));
|
|
213
|
+
this._initialized = true;
|
|
214
|
+
this._threw = threw;
|
|
215
|
+
this._error = err;
|
|
216
|
+
this._value = next;
|
|
217
|
+
reportReactiveRun(this);
|
|
218
|
+
if (changed) {
|
|
219
|
+
this.version++;
|
|
220
|
+
if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
|
|
221
|
+
notifyWatchers(this);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Push a maybe-dirty mark. A direct observer of a changed node gets DIRTY; its
|
|
226
|
+
* own observers get CHECK (recurse), stopping where already marked, so the
|
|
227
|
+
* push is O(newly-dirtied) and never recomputes.
|
|
228
|
+
*/
|
|
229
|
+
markDirty(level) {
|
|
230
|
+
if (this.state >= level) return;
|
|
231
|
+
const wasClean = this.state === CLEAN;
|
|
232
|
+
this.state = level;
|
|
233
|
+
if (wasClean) {
|
|
234
|
+
if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
|
|
235
|
+
notifyWatchers(this);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
peek() {
|
|
239
|
+
this.updateIfNecessary();
|
|
240
|
+
if (this._threw) throw this._error;
|
|
241
|
+
return this._value;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
function notifyWatchers(node) {
|
|
245
|
+
if (!node.watchers) return;
|
|
246
|
+
for (const w of [...node.watchers]) w._notify(node);
|
|
247
|
+
}
|
|
248
|
+
var Watcher = class {
|
|
249
|
+
constructor(notify) {
|
|
250
|
+
this._watched = /* @__PURE__ */ new Set();
|
|
251
|
+
this._pending = /* @__PURE__ */ new Set();
|
|
252
|
+
this._notifyFn = notify;
|
|
253
|
+
}
|
|
254
|
+
watch(...nodes) {
|
|
255
|
+
for (const n of nodes) {
|
|
256
|
+
this._watched.add(n);
|
|
257
|
+
(n.watchers ??= /* @__PURE__ */ new Set()).add(this);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
unwatch(...nodes) {
|
|
261
|
+
for (const n of nodes) {
|
|
262
|
+
this._watched.delete(n);
|
|
263
|
+
n.watchers?.delete(this);
|
|
264
|
+
this._pending.delete(n);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/** Watched nodes that may have changed since the last getPending(). */
|
|
268
|
+
getPending() {
|
|
269
|
+
const out = [...this._pending];
|
|
270
|
+
this._pending.clear();
|
|
271
|
+
return out;
|
|
272
|
+
}
|
|
273
|
+
/** @internal, a watched node became non-clean. Edge-triggered: notify once. */
|
|
274
|
+
_notify(node) {
|
|
275
|
+
if (!this._watched.has(node) || this._pending.has(node)) return;
|
|
276
|
+
const wasEmpty = this._pending.size === 0;
|
|
277
|
+
this._pending.add(node);
|
|
278
|
+
if (wasEmpty) this._notifyFn();
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
function untrack(fn) {
|
|
282
|
+
if (G.untracked) return fn();
|
|
283
|
+
G.untracked = true;
|
|
284
|
+
try {
|
|
285
|
+
return fn();
|
|
286
|
+
} finally {
|
|
287
|
+
G.untracked = false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function currentComputed() {
|
|
291
|
+
return G.consumer;
|
|
292
|
+
}
|
|
293
|
+
function hasSinks(node) {
|
|
294
|
+
return !!(node.observers && node.observers.size) || !!(node.watchers && node.watchers.size);
|
|
295
|
+
}
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The TC39-shaped reactive graph core.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* algorithm the existing graph already runs (CLEAN/CHECK/DIRTY + version + the
|
|
10
|
-
* `Listener`/observers edges), so behaviour is preserved; it's just relocated
|
|
11
|
-
* onto classes whose shape matches the TC39 Signals proposal.
|
|
4
|
+
* `State`, `Computed` and `Watcher` own the dependency graph and nothing else:
|
|
5
|
+
* values, versions, source and observer edges, the lazy state machine, and
|
|
6
|
+
* glitch-free pull evaluation. Ownership, scheduling, effects, suspense and
|
|
7
|
+
* resources all live in `runtime.ts`, with the `create*` surface over them in
|
|
8
|
+
* `compat.ts`.
|
|
12
9
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
10
|
+
* `../signal.ts` also implements a graph and is not the one that runs. It is
|
|
11
|
+
* archive, reachable only through the `@fluixi/reactive/signal-legacy` entry, and
|
|
12
|
+
* editing it changes nothing here. Its `Computation` and `signalObservers` are the
|
|
13
|
+
* earlier spelling of what `Computed` and `Source.observers` do below, which is
|
|
14
|
+
* the only reason to open it.
|
|
15
|
+
*
|
|
16
|
+
* The scheme is push the dirt, pull the value, as in Reactively, SolidJS and the
|
|
17
|
+
* TC39 polyfill. A write pushes a maybe-dirty mark down to observers without
|
|
18
|
+
* recomputing anything. A read pulls: upstream settles lazily and only what
|
|
19
|
+
* actually changed recomputes, gated on equality. A read outside a tracking scope
|
|
20
|
+
* subscribes to nothing.
|
|
17
21
|
*/
|
|
18
22
|
declare const CLEAN = 0;
|
|
19
23
|
declare const CHECK = 1;
|
|
@@ -21,9 +25,9 @@ declare const DIRTY = 2;
|
|
|
21
25
|
type NodeState = typeof CLEAN | typeof CHECK | typeof DIRTY;
|
|
22
26
|
/** A thing that can be depended upon: holds observers + a monotonic version. */
|
|
23
27
|
declare abstract class Source {
|
|
24
|
-
/** Computeds
|
|
28
|
+
/** Computeds, and watched leaves, that read this node. */
|
|
25
29
|
observers: Set<Computed<any>> | null;
|
|
26
|
-
/** Watchers eagerly observing this node,
|
|
30
|
+
/** Watchers eagerly observing this node, which is how `runtime.ts` schedules effects. */
|
|
27
31
|
watchers: Set<Watcher> | null;
|
|
28
32
|
/** Bumped whenever this node's value actually changes (equality-gated). */
|
|
29
33
|
version: number;
|
|
@@ -49,7 +53,7 @@ export declare class Computed<T> extends Source {
|
|
|
49
53
|
private _threw;
|
|
50
54
|
private _initialized;
|
|
51
55
|
state: NodeState;
|
|
52
|
-
/** Nodes this computation read on its last run.
|
|
56
|
+
/** Nodes this computation read on its last run. Rebuilt on every run. */
|
|
53
57
|
sources: Set<Source> | null;
|
|
54
58
|
private readonly _fn;
|
|
55
59
|
private readonly _equals;
|
|
@@ -57,21 +61,21 @@ export declare class Computed<T> extends Source {
|
|
|
57
61
|
equals?: ((a: T, b: T) => boolean) | false;
|
|
58
62
|
});
|
|
59
63
|
get(): T;
|
|
60
|
-
/** Settle
|
|
64
|
+
/** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
|
|
61
65
|
updateIfNecessary(): void;
|
|
62
66
|
private recompute;
|
|
63
67
|
/**
|
|
64
68
|
* Push a maybe-dirty mark. A direct observer of a changed node gets DIRTY; its
|
|
65
|
-
* own observers get CHECK (recurse)
|
|
69
|
+
* own observers get CHECK (recurse), stopping where already marked, so the
|
|
66
70
|
* push is O(newly-dirtied) and never recomputes.
|
|
67
71
|
*/
|
|
68
72
|
markDirty(level: NodeState): void;
|
|
69
73
|
peek(): T;
|
|
70
74
|
}
|
|
71
75
|
/**
|
|
72
|
-
* Signal.subtle.Watcher
|
|
73
|
-
* when a watched signal MAY have changed; the callback
|
|
74
|
-
* signals. Effects
|
|
76
|
+
* Signal.subtle.Watcher, the only eager observation primitive. Its callback fires
|
|
77
|
+
* when a watched signal MAY have changed; the callback must not read or write
|
|
78
|
+
* signals. Effects are built on this and a scheduler, in `runtime.ts`.
|
|
75
79
|
*/
|
|
76
80
|
export declare class Watcher {
|
|
77
81
|
private readonly _notifyFn;
|
|
@@ -82,12 +86,12 @@ export declare class Watcher {
|
|
|
82
86
|
unwatch(...nodes: Source[]): void;
|
|
83
87
|
/** Watched nodes that may have changed since the last getPending(). */
|
|
84
88
|
getPending(): Source[];
|
|
85
|
-
/** @internal
|
|
89
|
+
/** @internal, a watched node became non-clean. Edge-triggered: notify once. */
|
|
86
90
|
_notify(node: Source): void;
|
|
87
91
|
}
|
|
88
|
-
/** Signal.subtle.untrack
|
|
92
|
+
/** Signal.subtle.untrack: run `fn` without subscribing the current consumer. */
|
|
89
93
|
export declare function untrack<T>(fn: () => T): T;
|
|
90
|
-
/** Signal.subtle.currentComputed
|
|
94
|
+
/** Signal.subtle.currentComputed, the computed currently evaluating, or null. */
|
|
91
95
|
export declare function currentComputed(): Computed<unknown> | null;
|
|
92
96
|
/** Whether a node currently has observers/watchers (Signal.subtle.hasSinks). */
|
|
93
97
|
export declare function hasSinks(node: Source): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../../src/lib/signal/graph/core.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../../src/lib/signal/graph/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The TC39-shaped reactive graph core.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* algorithm the existing graph already runs (CLEAN/CHECK/DIRTY + version + the
|
|
10
|
-
* `Listener`/observers edges), so behaviour is preserved; it's just relocated
|
|
11
|
-
* onto classes whose shape matches the TC39 Signals proposal.
|
|
4
|
+
* `State`, `Computed` and `Watcher` own the dependency graph and nothing else:
|
|
5
|
+
* values, versions, source and observer edges, the lazy state machine, and
|
|
6
|
+
* glitch-free pull evaluation. Ownership, scheduling, effects, suspense and
|
|
7
|
+
* resources all live in `runtime.ts`, with the `create*` surface over them in
|
|
8
|
+
* `compat.ts`.
|
|
12
9
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
10
|
+
* `../signal.ts` also implements a graph and is not the one that runs. It is
|
|
11
|
+
* archive, reachable only through the `@fluixi/reactive/signal-legacy` entry, and
|
|
12
|
+
* editing it changes nothing here. Its `Computation` and `signalObservers` are the
|
|
13
|
+
* earlier spelling of what `Computed` and `Source.observers` do below, which is
|
|
14
|
+
* the only reason to open it.
|
|
15
|
+
*
|
|
16
|
+
* The scheme is push the dirt, pull the value, as in Reactively, SolidJS and the
|
|
17
|
+
* TC39 polyfill. A write pushes a maybe-dirty mark down to observers without
|
|
18
|
+
* recomputing anything. A read pulls: upstream settles lazily and only what
|
|
19
|
+
* actually changed recomputes, gated on equality. A read outside a tracking scope
|
|
20
|
+
* subscribes to nothing.
|
|
17
21
|
*/
|
|
18
22
|
import { G } from './state.js';
|
|
19
23
|
import { enterReactiveRun, exitReactiveRun, reportReactiveRun, reportReactiveWrite } from './observe.js';
|
|
20
|
-
//
|
|
21
|
-
// DIRTY
|
|
24
|
+
// CLEAN, the cached value is current. CHECK, a source may have changed, so verify on
|
|
25
|
+
// read. DIRTY, recompute without checking.
|
|
26
|
+
// CHECK is what keeps a diamond from recomputing twice: a write marks direct observers
|
|
27
|
+
// DIRTY and everything downstream of them CHECK, so a transitive reader asks its
|
|
28
|
+
// sources whether their versions moved before it runs its own function.
|
|
22
29
|
const CLEAN = 0;
|
|
23
30
|
const CHECK = 1;
|
|
24
31
|
const DIRTY = 2;
|
|
@@ -26,10 +33,10 @@ const DIRTY = 2;
|
|
|
26
33
|
// so duplicate module copies share one graph. `consumer`/`untracked` proxy them.
|
|
27
34
|
/** A thing that can be depended upon: holds observers + a monotonic version. */
|
|
28
35
|
class Source {
|
|
29
|
-
/** Computeds
|
|
36
|
+
/** Computeds, and watched leaves, that read this node. */
|
|
30
37
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
38
|
observers = null;
|
|
32
|
-
/** Watchers eagerly observing this node,
|
|
39
|
+
/** Watchers eagerly observing this node, which is how `runtime.ts` schedules effects. */
|
|
33
40
|
watchers = null;
|
|
34
41
|
/** Bumped whenever this node's value actually changes (equality-gated). */
|
|
35
42
|
version = 0;
|
|
@@ -65,10 +72,10 @@ export class State extends Source {
|
|
|
65
72
|
this.version++;
|
|
66
73
|
// Who wrote it: once a write has propagated, nothing else records what caused it.
|
|
67
74
|
reportReactiveWrite(this, G.consumer);
|
|
68
|
-
// Push: direct observers
|
|
69
|
-
// Snapshot first
|
|
70
|
-
//
|
|
71
|
-
//
|
|
75
|
+
// Push: direct observers go DIRTY, their transitive observers CHECK.
|
|
76
|
+
// Snapshot first. The scheduler is synchronous, so an effect can re-run part way
|
|
77
|
+
// through this loop, and re-running re-subscribes it, which mutates `observers`.
|
|
78
|
+
// Iterating the live Set would then reach the re-added node again, and again.
|
|
72
79
|
if (this.observers)
|
|
73
80
|
for (const o of [...this.observers])
|
|
74
81
|
o.markDirty(DIRTY);
|
|
@@ -85,7 +92,7 @@ export class Computed extends Source {
|
|
|
85
92
|
_threw = false;
|
|
86
93
|
_initialized = false;
|
|
87
94
|
state = DIRTY;
|
|
88
|
-
/** Nodes this computation read on its last run.
|
|
95
|
+
/** Nodes this computation read on its last run. Rebuilt on every run. */
|
|
89
96
|
sources = null;
|
|
90
97
|
_fn;
|
|
91
98
|
_equals;
|
|
@@ -105,7 +112,7 @@ export class Computed extends Source {
|
|
|
105
112
|
throw this._error;
|
|
106
113
|
return this._value;
|
|
107
114
|
}
|
|
108
|
-
/** Settle
|
|
115
|
+
/** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
|
|
109
116
|
updateIfNecessary() {
|
|
110
117
|
if (this.state === CHECK) {
|
|
111
118
|
// Pull each source; if one recomputes to a new value it will have flipped
|
|
@@ -161,7 +168,7 @@ export class Computed extends Source {
|
|
|
161
168
|
reportReactiveRun(this);
|
|
162
169
|
if (changed) {
|
|
163
170
|
this.version++;
|
|
164
|
-
// Our value moved
|
|
171
|
+
// Our value moved -> observers that were merely CHECK must now recompute.
|
|
165
172
|
if (this.observers)
|
|
166
173
|
for (const o of [...this.observers])
|
|
167
174
|
o.markDirty(DIRTY);
|
|
@@ -170,7 +177,7 @@ export class Computed extends Source {
|
|
|
170
177
|
}
|
|
171
178
|
/**
|
|
172
179
|
* Push a maybe-dirty mark. A direct observer of a changed node gets DIRTY; its
|
|
173
|
-
* own observers get CHECK (recurse)
|
|
180
|
+
* own observers get CHECK (recurse), stopping where already marked, so the
|
|
174
181
|
* push is O(newly-dirtied) and never recomputes.
|
|
175
182
|
*/
|
|
176
183
|
markDirty(level) {
|
|
@@ -199,9 +206,9 @@ function notifyWatchers(node) {
|
|
|
199
206
|
w._notify(node);
|
|
200
207
|
}
|
|
201
208
|
/**
|
|
202
|
-
* Signal.subtle.Watcher
|
|
203
|
-
* when a watched signal MAY have changed; the callback
|
|
204
|
-
* signals. Effects
|
|
209
|
+
* Signal.subtle.Watcher, the only eager observation primitive. Its callback fires
|
|
210
|
+
* when a watched signal MAY have changed; the callback must not read or write
|
|
211
|
+
* signals. Effects are built on this and a scheduler, in `runtime.ts`.
|
|
205
212
|
*/
|
|
206
213
|
export class Watcher {
|
|
207
214
|
_notifyFn;
|
|
@@ -229,7 +236,7 @@ export class Watcher {
|
|
|
229
236
|
this._pending.clear();
|
|
230
237
|
return out;
|
|
231
238
|
}
|
|
232
|
-
/** @internal
|
|
239
|
+
/** @internal, a watched node became non-clean. Edge-triggered: notify once. */
|
|
233
240
|
_notify(node) {
|
|
234
241
|
if (!this._watched.has(node) || this._pending.has(node))
|
|
235
242
|
return;
|
|
@@ -239,7 +246,7 @@ export class Watcher {
|
|
|
239
246
|
this._notifyFn();
|
|
240
247
|
}
|
|
241
248
|
}
|
|
242
|
-
/** Signal.subtle.untrack
|
|
249
|
+
/** Signal.subtle.untrack: run `fn` without subscribing the current consumer. */
|
|
243
250
|
export function untrack(fn) {
|
|
244
251
|
if (G.untracked)
|
|
245
252
|
return fn();
|
|
@@ -251,7 +258,7 @@ export function untrack(fn) {
|
|
|
251
258
|
G.untracked = false;
|
|
252
259
|
}
|
|
253
260
|
}
|
|
254
|
-
/** Signal.subtle.currentComputed
|
|
261
|
+
/** Signal.subtle.currentComputed, the computed currently evaluating, or null. */
|
|
255
262
|
export function currentComputed() {
|
|
256
263
|
return G.consumer;
|
|
257
264
|
}
|