@fluixi/reactive 1.0.0-alpha.82 → 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/README.md +4 -0
- 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 +51 -32
- 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 +45 -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 -0
- package/dist/lib/signal/graph/observe.d.ts +141 -0
- package/dist/lib/signal/graph/observe.d.ts.map +1 -0
- package/dist/lib/signal/graph/observe.js +149 -0
- package/dist/lib/signal/graph/observe.mjs +140 -0
- 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 +9 -9
- package/dist/lib/signal/graph/runtime.d.ts.map +1 -1
- package/dist/lib/signal/graph/runtime.js +56 -37
- 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 +43 -10
- package/dist/lib/signal/graph/state.d.ts.map +1 -1
- package/dist/lib/signal/graph/state.js +83 -21
- 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 +5 -2
- package/dist/lib/signal/index.d.ts.map +1 -1
- package/dist/lib/signal/index.js +8 -3
- 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 +10 -10
- 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 +294 -11
- 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,23 +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
|
-
|
|
20
|
-
//
|
|
23
|
+
import { enterReactiveRun, exitReactiveRun, reportReactiveRun, reportReactiveWrite } from './observe.js';
|
|
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.
|
|
21
29
|
const CLEAN = 0;
|
|
22
30
|
const CHECK = 1;
|
|
23
31
|
const DIRTY = 2;
|
|
@@ -25,10 +33,10 @@ const DIRTY = 2;
|
|
|
25
33
|
// so duplicate module copies share one graph. `consumer`/`untracked` proxy them.
|
|
26
34
|
/** A thing that can be depended upon: holds observers + a monotonic version. */
|
|
27
35
|
class Source {
|
|
28
|
-
/** Computeds
|
|
36
|
+
/** Computeds, and watched leaves, that read this node. */
|
|
29
37
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
38
|
observers = null;
|
|
31
|
-
/** Watchers eagerly observing this node,
|
|
39
|
+
/** Watchers eagerly observing this node, which is how `runtime.ts` schedules effects. */
|
|
32
40
|
watchers = null;
|
|
33
41
|
/** Bumped whenever this node's value actually changes (equality-gated). */
|
|
34
42
|
version = 0;
|
|
@@ -62,10 +70,12 @@ export class State extends Source {
|
|
|
62
70
|
return;
|
|
63
71
|
this._value = next;
|
|
64
72
|
this.version++;
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
//
|
|
73
|
+
// Who wrote it: once a write has propagated, nothing else records what caused it.
|
|
74
|
+
reportReactiveWrite(this, G.consumer);
|
|
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.
|
|
69
79
|
if (this.observers)
|
|
70
80
|
for (const o of [...this.observers])
|
|
71
81
|
o.markDirty(DIRTY);
|
|
@@ -82,7 +92,7 @@ export class Computed extends Source {
|
|
|
82
92
|
_threw = false;
|
|
83
93
|
_initialized = false;
|
|
84
94
|
state = DIRTY;
|
|
85
|
-
/** Nodes this computation read on its last run.
|
|
95
|
+
/** Nodes this computation read on its last run. Rebuilt on every run. */
|
|
86
96
|
sources = null;
|
|
87
97
|
_fn;
|
|
88
98
|
_equals;
|
|
@@ -102,7 +112,7 @@ export class Computed extends Source {
|
|
|
102
112
|
throw this._error;
|
|
103
113
|
return this._value;
|
|
104
114
|
}
|
|
105
|
-
/** Settle
|
|
115
|
+
/** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
|
|
106
116
|
updateIfNecessary() {
|
|
107
117
|
if (this.state === CHECK) {
|
|
108
118
|
// Pull each source; if one recomputes to a new value it will have flipped
|
|
@@ -128,6 +138,7 @@ export class Computed extends Source {
|
|
|
128
138
|
}
|
|
129
139
|
const prevConsumer = G.consumer;
|
|
130
140
|
const prevUntracked = G.untracked;
|
|
141
|
+
const watched = enterReactiveRun(this);
|
|
131
142
|
G.consumer = this;
|
|
132
143
|
G.untracked = false;
|
|
133
144
|
let next;
|
|
@@ -144,6 +155,8 @@ export class Computed extends Source {
|
|
|
144
155
|
finally {
|
|
145
156
|
G.consumer = prevConsumer;
|
|
146
157
|
G.untracked = prevUntracked;
|
|
158
|
+
if (watched)
|
|
159
|
+
exitReactiveRun();
|
|
147
160
|
}
|
|
148
161
|
const changed = !this._initialized ||
|
|
149
162
|
this._threw !== threw ||
|
|
@@ -152,9 +165,10 @@ export class Computed extends Source {
|
|
|
152
165
|
this._threw = threw;
|
|
153
166
|
this._error = err;
|
|
154
167
|
this._value = next;
|
|
168
|
+
reportReactiveRun(this);
|
|
155
169
|
if (changed) {
|
|
156
170
|
this.version++;
|
|
157
|
-
// Our value moved
|
|
171
|
+
// Our value moved -> observers that were merely CHECK must now recompute.
|
|
158
172
|
if (this.observers)
|
|
159
173
|
for (const o of [...this.observers])
|
|
160
174
|
o.markDirty(DIRTY);
|
|
@@ -163,7 +177,7 @@ export class Computed extends Source {
|
|
|
163
177
|
}
|
|
164
178
|
/**
|
|
165
179
|
* Push a maybe-dirty mark. A direct observer of a changed node gets DIRTY; its
|
|
166
|
-
* own observers get CHECK (recurse)
|
|
180
|
+
* own observers get CHECK (recurse), stopping where already marked, so the
|
|
167
181
|
* push is O(newly-dirtied) and never recomputes.
|
|
168
182
|
*/
|
|
169
183
|
markDirty(level) {
|
|
@@ -192,9 +206,9 @@ function notifyWatchers(node) {
|
|
|
192
206
|
w._notify(node);
|
|
193
207
|
}
|
|
194
208
|
/**
|
|
195
|
-
* Signal.subtle.Watcher
|
|
196
|
-
* when a watched signal MAY have changed; the callback
|
|
197
|
-
* 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`.
|
|
198
212
|
*/
|
|
199
213
|
export class Watcher {
|
|
200
214
|
_notifyFn;
|
|
@@ -222,7 +236,7 @@ export class Watcher {
|
|
|
222
236
|
this._pending.clear();
|
|
223
237
|
return out;
|
|
224
238
|
}
|
|
225
|
-
/** @internal
|
|
239
|
+
/** @internal, a watched node became non-clean. Edge-triggered: notify once. */
|
|
226
240
|
_notify(node) {
|
|
227
241
|
if (!this._watched.has(node) || this._pending.has(node))
|
|
228
242
|
return;
|
|
@@ -232,7 +246,7 @@ export class Watcher {
|
|
|
232
246
|
this._notifyFn();
|
|
233
247
|
}
|
|
234
248
|
}
|
|
235
|
-
/** Signal.subtle.untrack
|
|
249
|
+
/** Signal.subtle.untrack: run `fn` without subscribing the current consumer. */
|
|
236
250
|
export function untrack(fn) {
|
|
237
251
|
if (G.untracked)
|
|
238
252
|
return fn();
|
|
@@ -244,7 +258,7 @@ export function untrack(fn) {
|
|
|
244
258
|
G.untracked = false;
|
|
245
259
|
}
|
|
246
260
|
}
|
|
247
|
-
/** Signal.subtle.currentComputed
|
|
261
|
+
/** Signal.subtle.currentComputed, the computed currently evaluating, or null. */
|
|
248
262
|
export function currentComputed() {
|
|
249
263
|
return G.consumer;
|
|
250
264
|
}
|