@zeix/cause-effect 0.17.2 → 0.18.0
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/.ai-context.md +163 -226
- package/.cursorrules +41 -35
- package/.github/copilot-instructions.md +166 -116
- package/.zed/settings.json +3 -0
- package/ARCHITECTURE.md +274 -0
- package/CLAUDE.md +197 -202
- package/COLLECTION_REFACTORING.md +161 -0
- package/GUIDE.md +298 -0
- package/README.md +241 -220
- package/REQUIREMENTS.md +100 -0
- package/bench/reactivity.bench.ts +577 -0
- package/index.dev.js +1326 -1174
- package/index.js +1 -1
- package/index.ts +58 -85
- package/package.json +9 -6
- package/src/errors.ts +118 -70
- package/src/graph.ts +601 -0
- package/src/nodes/collection.ts +474 -0
- package/src/nodes/effect.ts +149 -0
- package/src/nodes/list.ts +588 -0
- package/src/nodes/memo.ts +120 -0
- package/src/nodes/sensor.ts +139 -0
- package/src/nodes/state.ts +135 -0
- package/src/nodes/store.ts +383 -0
- package/src/nodes/task.ts +146 -0
- package/src/signal.ts +112 -64
- package/src/util.ts +26 -57
- package/test/batch.test.ts +96 -69
- package/test/benchmark.test.ts +473 -485
- package/test/collection.test.ts +455 -955
- package/test/effect.test.ts +293 -696
- package/test/list.test.ts +332 -857
- package/test/memo.test.ts +380 -0
- package/test/regression.test.ts +156 -0
- package/test/scope.test.ts +191 -0
- package/test/sensor.test.ts +454 -0
- package/test/signal.test.ts +220 -213
- package/test/state.test.ts +217 -271
- package/test/store.test.ts +346 -898
- package/test/task.test.ts +395 -0
- package/test/untrack.test.ts +167 -0
- package/test/util/dependency-graph.ts +2 -2
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +5 -7
- package/types/index.d.ts +13 -15
- package/types/src/errors.d.ts +73 -19
- package/types/src/graph.d.ts +208 -0
- package/types/src/nodes/collection.d.ts +64 -0
- package/types/src/nodes/effect.d.ts +48 -0
- package/types/src/nodes/list.d.ts +65 -0
- package/types/src/nodes/memo.d.ts +57 -0
- package/types/src/nodes/sensor.d.ts +75 -0
- package/types/src/nodes/state.d.ts +78 -0
- package/types/src/nodes/store.d.ts +51 -0
- package/types/src/nodes/task.d.ts +73 -0
- package/types/src/signal.d.ts +43 -28
- package/types/src/util.d.ts +9 -16
- package/archive/benchmark.ts +0 -688
- package/archive/collection.ts +0 -310
- package/archive/computed.ts +0 -198
- package/archive/list.ts +0 -544
- package/archive/memo.ts +0 -140
- package/archive/state.ts +0 -90
- package/archive/store.ts +0 -357
- package/archive/task.ts +0 -191
- package/src/classes/collection.ts +0 -298
- package/src/classes/composite.ts +0 -171
- package/src/classes/computed.ts +0 -392
- package/src/classes/list.ts +0 -310
- package/src/classes/ref.ts +0 -96
- package/src/classes/state.ts +0 -131
- package/src/classes/store.ts +0 -227
- package/src/diff.ts +0 -138
- package/src/effect.ts +0 -96
- package/src/match.ts +0 -45
- package/src/resolve.ts +0 -49
- package/src/system.ts +0 -275
- package/test/computed.test.ts +0 -1126
- package/test/diff.test.ts +0 -955
- package/test/match.test.ts +0 -388
- package/test/ref.test.ts +0 -381
- package/test/resolve.test.ts +0 -154
- package/types/src/classes/collection.d.ts +0 -47
- package/types/src/classes/composite.d.ts +0 -15
- package/types/src/classes/computed.d.ts +0 -114
- package/types/src/classes/list.d.ts +0 -41
- package/types/src/classes/ref.d.ts +0 -48
- package/types/src/classes/state.d.ts +0 -61
- package/types/src/classes/store.d.ts +0 -51
- package/types/src/diff.d.ts +0 -28
- package/types/src/effect.d.ts +0 -15
- package/types/src/match.d.ts +0 -21
- package/types/src/resolve.d.ts +0 -29
- package/types/src/system.d.ts +0 -81
package/index.dev.js
CHANGED
|
@@ -1,162 +1,434 @@
|
|
|
1
1
|
// src/util.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
function isFunction(fn) {
|
|
3
|
+
return typeof fn === "function";
|
|
4
|
+
}
|
|
5
|
+
function isAsyncFunction(fn) {
|
|
6
|
+
return isFunction(fn) && fn.constructor.name === "AsyncFunction";
|
|
7
|
+
}
|
|
8
|
+
function isSyncFunction(fn) {
|
|
9
|
+
return isFunction(fn) && fn.constructor.name !== "AsyncFunction";
|
|
10
|
+
}
|
|
11
|
+
function isObjectOfType(value, type) {
|
|
12
|
+
return Object.prototype.toString.call(value) === `[object ${type}]`;
|
|
13
|
+
}
|
|
14
|
+
function isRecord(value) {
|
|
15
|
+
return isObjectOfType(value, "Object");
|
|
16
|
+
}
|
|
17
|
+
function isUniformArray(value, guard = (item) => item != null) {
|
|
18
|
+
return Array.isArray(value) && value.every(guard);
|
|
19
|
+
}
|
|
20
|
+
function valueString(value) {
|
|
21
|
+
return typeof value === "string" ? `"${value}"` : !!value && typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/errors.ts
|
|
25
|
+
class CircularDependencyError extends Error {
|
|
26
|
+
constructor(where) {
|
|
27
|
+
super(`[${where}] Circular dependency detected`);
|
|
28
|
+
this.name = "CircularDependencyError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class NullishSignalValueError extends TypeError {
|
|
33
|
+
constructor(where) {
|
|
34
|
+
super(`[${where}] Signal value cannot be null or undefined`);
|
|
35
|
+
this.name = "NullishSignalValueError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class UnsetSignalValueError extends Error {
|
|
40
|
+
constructor(where) {
|
|
41
|
+
super(`[${where}] Signal value is unset`);
|
|
42
|
+
this.name = "UnsetSignalValueError";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
class InvalidSignalValueError extends TypeError {
|
|
47
|
+
constructor(where, value) {
|
|
48
|
+
super(`[${where}] Signal value ${valueString(value)} is invalid`);
|
|
49
|
+
this.name = "InvalidSignalValueError";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class InvalidCallbackError extends TypeError {
|
|
54
|
+
constructor(where, value) {
|
|
55
|
+
super(`[${where}] Callback ${valueString(value)} is invalid`);
|
|
56
|
+
this.name = "InvalidCallbackError";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class RequiredOwnerError extends Error {
|
|
61
|
+
constructor(where) {
|
|
62
|
+
super(`[${where}] Active owner is required`);
|
|
63
|
+
this.name = "RequiredOwnerError";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
15
66
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
67
|
+
class DuplicateKeyError extends Error {
|
|
68
|
+
constructor(where, key, value) {
|
|
69
|
+
super(`[${where}] Could not add key "${key}"${value ? ` with value ${JSON.stringify(value)}` : ""} because it already exists`);
|
|
70
|
+
this.name = "DuplicateKeyError";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function validateSignalValue(where, value, guard) {
|
|
74
|
+
if (value == null)
|
|
75
|
+
throw new NullishSignalValueError(where);
|
|
76
|
+
if (guard && !guard(value))
|
|
77
|
+
throw new InvalidSignalValueError(where, value);
|
|
78
|
+
}
|
|
79
|
+
function validateReadValue(where, value) {
|
|
80
|
+
if (value == null)
|
|
81
|
+
throw new UnsetSignalValueError(where);
|
|
82
|
+
}
|
|
83
|
+
function validateCallback(where, value, guard = isFunction) {
|
|
84
|
+
if (!guard(value))
|
|
85
|
+
throw new InvalidCallbackError(where, value);
|
|
86
|
+
}
|
|
87
|
+
// src/graph.ts
|
|
88
|
+
var TYPE_STATE = "State";
|
|
89
|
+
var TYPE_MEMO = "Memo";
|
|
90
|
+
var TYPE_TASK = "Task";
|
|
91
|
+
var TYPE_SENSOR = "Sensor";
|
|
92
|
+
var TYPE_LIST = "List";
|
|
93
|
+
var TYPE_COLLECTION = "Collection";
|
|
94
|
+
var TYPE_STORE = "Store";
|
|
95
|
+
var FLAG_CLEAN = 0;
|
|
96
|
+
var FLAG_CHECK = 1 << 0;
|
|
97
|
+
var FLAG_DIRTY = 1 << 1;
|
|
98
|
+
var FLAG_RUNNING = 1 << 2;
|
|
99
|
+
var activeSink = null;
|
|
100
|
+
var activeOwner = null;
|
|
101
|
+
var queuedEffects = [];
|
|
20
102
|
var batchDepth = 0;
|
|
21
|
-
var
|
|
22
|
-
var
|
|
23
|
-
var
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
else
|
|
35
|
-
throw new InvalidHookError("watcher", type);
|
|
36
|
-
};
|
|
37
|
-
watcher.stop = () => {
|
|
38
|
-
try {
|
|
39
|
-
for (const cleanup of cleanups)
|
|
40
|
-
cleanup();
|
|
41
|
-
} finally {
|
|
42
|
-
cleanups.clear();
|
|
103
|
+
var flushing = false;
|
|
104
|
+
var DEFAULT_EQUALITY = (a, b) => a === b;
|
|
105
|
+
var SKIP_EQUALITY = (_a, _b) => false;
|
|
106
|
+
function isValidEdge(checkEdge, node) {
|
|
107
|
+
const sourcesTail = node.sourcesTail;
|
|
108
|
+
if (sourcesTail) {
|
|
109
|
+
let edge = node.sources;
|
|
110
|
+
while (edge) {
|
|
111
|
+
if (edge === checkEdge)
|
|
112
|
+
return true;
|
|
113
|
+
if (edge === sourcesTail)
|
|
114
|
+
break;
|
|
115
|
+
edge = edge.nextSource;
|
|
43
116
|
}
|
|
44
|
-
}
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
function link(source, sink) {
|
|
121
|
+
const prevSource = sink.sourcesTail;
|
|
122
|
+
if (prevSource?.source === source)
|
|
123
|
+
return;
|
|
124
|
+
let nextSource = null;
|
|
125
|
+
const isRecomputing = sink.flags & FLAG_RUNNING;
|
|
126
|
+
if (isRecomputing) {
|
|
127
|
+
nextSource = prevSource ? prevSource.nextSource : sink.sources;
|
|
128
|
+
if (nextSource?.source === source) {
|
|
129
|
+
sink.sourcesTail = nextSource;
|
|
130
|
+
return;
|
|
55
131
|
}
|
|
56
132
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
133
|
+
const prevSink = source.sinksTail;
|
|
134
|
+
if (prevSink?.sink === sink && (!isRecomputing || isValidEdge(prevSink, sink)))
|
|
135
|
+
return;
|
|
136
|
+
const newEdge = { source, sink, nextSource, prevSink, nextSink: null };
|
|
137
|
+
sink.sourcesTail = source.sinksTail = newEdge;
|
|
138
|
+
if (prevSource)
|
|
139
|
+
prevSource.nextSource = newEdge;
|
|
140
|
+
else
|
|
141
|
+
sink.sources = newEdge;
|
|
142
|
+
if (prevSink)
|
|
143
|
+
prevSink.nextSink = newEdge;
|
|
144
|
+
else
|
|
145
|
+
source.sinks = newEdge;
|
|
146
|
+
}
|
|
147
|
+
function unlink(edge) {
|
|
148
|
+
const { source, nextSource, nextSink, prevSink } = edge;
|
|
149
|
+
if (nextSink)
|
|
150
|
+
nextSink.prevSink = prevSink;
|
|
151
|
+
else
|
|
152
|
+
source.sinksTail = prevSink;
|
|
153
|
+
if (prevSink)
|
|
154
|
+
prevSink.nextSink = nextSink;
|
|
155
|
+
else
|
|
156
|
+
source.sinks = nextSink;
|
|
157
|
+
if (!source.sinks && source.stop) {
|
|
158
|
+
source.stop();
|
|
159
|
+
source.stop = undefined;
|
|
160
|
+
}
|
|
161
|
+
return nextSource;
|
|
162
|
+
}
|
|
163
|
+
function trimSources(node) {
|
|
164
|
+
const tail = node.sourcesTail;
|
|
165
|
+
let source = tail ? tail.nextSource : node.sources;
|
|
166
|
+
while (source)
|
|
167
|
+
source = unlink(source);
|
|
168
|
+
if (tail)
|
|
169
|
+
tail.nextSource = null;
|
|
170
|
+
else
|
|
171
|
+
node.sources = null;
|
|
172
|
+
}
|
|
173
|
+
function propagate(node, newFlag = FLAG_DIRTY) {
|
|
174
|
+
const flags = node.flags;
|
|
175
|
+
if ("sinks" in node) {
|
|
176
|
+
if ((flags & (FLAG_DIRTY | FLAG_CHECK)) >= newFlag)
|
|
177
|
+
return;
|
|
178
|
+
node.flags = flags | newFlag;
|
|
179
|
+
if ("controller" in node && node.controller) {
|
|
180
|
+
node.controller.abort();
|
|
181
|
+
node.controller = undefined;
|
|
182
|
+
}
|
|
183
|
+
for (let e = node.sinks;e; e = e.nextSink)
|
|
184
|
+
propagate(e.sink, FLAG_CHECK);
|
|
185
|
+
} else {
|
|
186
|
+
if (flags & FLAG_DIRTY)
|
|
187
|
+
return;
|
|
188
|
+
node.flags = FLAG_DIRTY;
|
|
189
|
+
queuedEffects.push(node);
|
|
75
190
|
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (
|
|
79
|
-
return
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
191
|
+
}
|
|
192
|
+
function setState(node, next) {
|
|
193
|
+
if (node.equals(node.value, next))
|
|
194
|
+
return;
|
|
195
|
+
node.value = next;
|
|
196
|
+
for (let e = node.sinks;e; e = e.nextSink)
|
|
197
|
+
propagate(e.sink);
|
|
198
|
+
if (batchDepth === 0)
|
|
199
|
+
flush();
|
|
200
|
+
}
|
|
201
|
+
function registerCleanup(owner, fn) {
|
|
202
|
+
if (!owner.cleanup)
|
|
203
|
+
owner.cleanup = fn;
|
|
204
|
+
else if (Array.isArray(owner.cleanup))
|
|
205
|
+
owner.cleanup.push(fn);
|
|
206
|
+
else
|
|
207
|
+
owner.cleanup = [owner.cleanup, fn];
|
|
208
|
+
}
|
|
209
|
+
function runCleanup(owner) {
|
|
210
|
+
if (!owner.cleanup)
|
|
211
|
+
return;
|
|
212
|
+
if (Array.isArray(owner.cleanup))
|
|
213
|
+
for (let i = 0;i < owner.cleanup.length; i++)
|
|
214
|
+
owner.cleanup[i]();
|
|
215
|
+
else
|
|
216
|
+
owner.cleanup();
|
|
217
|
+
owner.cleanup = null;
|
|
218
|
+
}
|
|
219
|
+
function recomputeMemo(node) {
|
|
220
|
+
const prevWatcher = activeSink;
|
|
221
|
+
activeSink = node;
|
|
222
|
+
node.sourcesTail = null;
|
|
223
|
+
node.flags = FLAG_RUNNING;
|
|
224
|
+
let changed = false;
|
|
225
|
+
try {
|
|
226
|
+
const next = node.fn(node.value);
|
|
227
|
+
if (node.error || !node.equals(next, node.value)) {
|
|
228
|
+
node.value = next;
|
|
229
|
+
node.error = undefined;
|
|
230
|
+
changed = true;
|
|
231
|
+
}
|
|
232
|
+
} catch (err) {
|
|
233
|
+
changed = true;
|
|
234
|
+
node.error = err instanceof Error ? err : new Error(String(err));
|
|
235
|
+
} finally {
|
|
236
|
+
activeSink = prevWatcher;
|
|
237
|
+
trimSources(node);
|
|
238
|
+
}
|
|
239
|
+
if (changed) {
|
|
240
|
+
for (let e = node.sinks;e; e = e.nextSink)
|
|
241
|
+
if (e.sink.flags & FLAG_CHECK)
|
|
242
|
+
e.sink.flags |= FLAG_DIRTY;
|
|
243
|
+
}
|
|
244
|
+
node.flags = FLAG_CLEAN;
|
|
245
|
+
}
|
|
246
|
+
function recomputeTask(node) {
|
|
247
|
+
node.controller?.abort();
|
|
248
|
+
const controller = new AbortController;
|
|
249
|
+
node.controller = controller;
|
|
250
|
+
node.error = undefined;
|
|
251
|
+
const prevWatcher = activeSink;
|
|
252
|
+
activeSink = node;
|
|
253
|
+
node.sourcesTail = null;
|
|
254
|
+
node.flags = FLAG_RUNNING;
|
|
255
|
+
let promise;
|
|
256
|
+
try {
|
|
257
|
+
promise = node.fn(node.value, controller.signal);
|
|
258
|
+
} catch (err) {
|
|
259
|
+
node.controller = undefined;
|
|
260
|
+
node.error = err instanceof Error ? err : new Error(String(err));
|
|
261
|
+
return;
|
|
262
|
+
} finally {
|
|
263
|
+
activeSink = prevWatcher;
|
|
264
|
+
trimSources(node);
|
|
265
|
+
}
|
|
266
|
+
promise.then((next) => {
|
|
267
|
+
if (controller.signal.aborted)
|
|
268
|
+
return;
|
|
269
|
+
node.controller = undefined;
|
|
270
|
+
if (node.error || !node.equals(next, node.value)) {
|
|
271
|
+
node.value = next;
|
|
272
|
+
node.error = undefined;
|
|
273
|
+
for (let e = node.sinks;e; e = e.nextSink)
|
|
274
|
+
propagate(e.sink);
|
|
275
|
+
if (batchDepth === 0)
|
|
276
|
+
flush();
|
|
277
|
+
}
|
|
278
|
+
}, (err) => {
|
|
279
|
+
if (controller.signal.aborted)
|
|
280
|
+
return;
|
|
281
|
+
node.controller = undefined;
|
|
282
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
283
|
+
if (!node.error || error.name !== node.error.name || error.message !== node.error.message) {
|
|
284
|
+
node.error = error;
|
|
285
|
+
for (let e = node.sinks;e; e = e.nextSink)
|
|
286
|
+
propagate(e.sink);
|
|
287
|
+
if (batchDepth === 0)
|
|
288
|
+
flush();
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
node.flags = FLAG_CLEAN;
|
|
292
|
+
}
|
|
293
|
+
function runEffect(node) {
|
|
294
|
+
runCleanup(node);
|
|
295
|
+
const prevContext = activeSink;
|
|
296
|
+
const prevOwner = activeOwner;
|
|
297
|
+
activeSink = activeOwner = node;
|
|
298
|
+
node.sourcesTail = null;
|
|
299
|
+
node.flags = FLAG_RUNNING;
|
|
300
|
+
try {
|
|
301
|
+
const out = node.fn();
|
|
302
|
+
if (typeof out === "function")
|
|
303
|
+
registerCleanup(node, out);
|
|
304
|
+
} finally {
|
|
305
|
+
activeSink = prevContext;
|
|
306
|
+
activeOwner = prevOwner;
|
|
307
|
+
trimSources(node);
|
|
308
|
+
}
|
|
309
|
+
node.flags = FLAG_CLEAN;
|
|
310
|
+
}
|
|
311
|
+
function refresh(node) {
|
|
312
|
+
if (node.flags & FLAG_CHECK) {
|
|
313
|
+
for (let e = node.sources;e; e = e.nextSource) {
|
|
314
|
+
if ("fn" in e.source)
|
|
315
|
+
refresh(e.source);
|
|
316
|
+
if (node.flags & FLAG_DIRTY)
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (node.flags & FLAG_RUNNING) {
|
|
321
|
+
throw new CircularDependencyError("controller" in node ? TYPE_TASK : ("value" in node) ? TYPE_MEMO : "Effect");
|
|
322
|
+
}
|
|
323
|
+
if (node.flags & FLAG_DIRTY) {
|
|
324
|
+
if ("controller" in node)
|
|
325
|
+
recomputeTask(node);
|
|
326
|
+
else if ("value" in node)
|
|
327
|
+
recomputeMemo(node);
|
|
83
328
|
else
|
|
84
|
-
|
|
329
|
+
runEffect(node);
|
|
330
|
+
} else {
|
|
331
|
+
node.flags = FLAG_CLEAN;
|
|
85
332
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
for (
|
|
93
|
-
|
|
333
|
+
}
|
|
334
|
+
function flush() {
|
|
335
|
+
if (flushing)
|
|
336
|
+
return;
|
|
337
|
+
flushing = true;
|
|
338
|
+
try {
|
|
339
|
+
for (let i = 0;i < queuedEffects.length; i++) {
|
|
340
|
+
const effect = queuedEffects[i];
|
|
341
|
+
if (effect.flags & FLAG_DIRTY)
|
|
342
|
+
refresh(effect);
|
|
343
|
+
}
|
|
344
|
+
queuedEffects.length = 0;
|
|
345
|
+
} finally {
|
|
346
|
+
flushing = false;
|
|
94
347
|
}
|
|
95
|
-
}
|
|
96
|
-
|
|
348
|
+
}
|
|
349
|
+
function batch(fn) {
|
|
97
350
|
batchDepth++;
|
|
98
351
|
try {
|
|
99
|
-
|
|
352
|
+
fn();
|
|
100
353
|
} finally {
|
|
101
|
-
flushPendingReactions();
|
|
102
354
|
batchDepth--;
|
|
355
|
+
if (batchDepth === 0)
|
|
356
|
+
flush();
|
|
103
357
|
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const prev =
|
|
107
|
-
|
|
358
|
+
}
|
|
359
|
+
function untrack(fn) {
|
|
360
|
+
const prev = activeSink;
|
|
361
|
+
activeSink = null;
|
|
108
362
|
try {
|
|
109
|
-
|
|
363
|
+
return fn();
|
|
110
364
|
} finally {
|
|
111
|
-
|
|
365
|
+
activeSink = prev;
|
|
112
366
|
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const cleanup = callback(payload);
|
|
129
|
-
if (isFunction(cleanup))
|
|
130
|
-
cleanups.push(cleanup);
|
|
131
|
-
} catch (error) {
|
|
132
|
-
errors.push(createError(error));
|
|
133
|
-
}
|
|
367
|
+
}
|
|
368
|
+
function createScope(fn) {
|
|
369
|
+
const prevOwner = activeOwner;
|
|
370
|
+
const scope = { cleanup: null };
|
|
371
|
+
activeOwner = scope;
|
|
372
|
+
try {
|
|
373
|
+
const out = fn();
|
|
374
|
+
if (typeof out === "function")
|
|
375
|
+
registerCleanup(scope, out);
|
|
376
|
+
const dispose = () => runCleanup(scope);
|
|
377
|
+
if (prevOwner)
|
|
378
|
+
registerCleanup(prevOwner, dispose);
|
|
379
|
+
return dispose;
|
|
380
|
+
} finally {
|
|
381
|
+
activeOwner = prevOwner;
|
|
134
382
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
383
|
+
}
|
|
384
|
+
// src/nodes/state.ts
|
|
385
|
+
function createState(value, options) {
|
|
386
|
+
validateSignalValue(TYPE_STATE, value, options?.guard);
|
|
387
|
+
const node = {
|
|
388
|
+
value,
|
|
389
|
+
sinks: null,
|
|
390
|
+
sinksTail: null,
|
|
391
|
+
equals: options?.equals ?? DEFAULT_EQUALITY,
|
|
392
|
+
guard: options?.guard
|
|
393
|
+
};
|
|
394
|
+
return {
|
|
395
|
+
[Symbol.toStringTag]: TYPE_STATE,
|
|
396
|
+
get() {
|
|
397
|
+
if (activeSink)
|
|
398
|
+
link(node, activeSink);
|
|
399
|
+
return node.value;
|
|
400
|
+
},
|
|
401
|
+
set(next) {
|
|
402
|
+
validateSignalValue(TYPE_STATE, next, node.guard);
|
|
403
|
+
setState(node, next);
|
|
404
|
+
},
|
|
405
|
+
update(fn) {
|
|
406
|
+
validateCallback(TYPE_STATE, fn);
|
|
407
|
+
const next = fn(node.value);
|
|
408
|
+
validateSignalValue(TYPE_STATE, next, node.guard);
|
|
409
|
+
setState(node, next);
|
|
147
410
|
}
|
|
148
|
-
throwError(true);
|
|
149
411
|
};
|
|
150
|
-
}
|
|
151
|
-
|
|
412
|
+
}
|
|
413
|
+
function isState(value) {
|
|
414
|
+
return isObjectOfType(value, TYPE_STATE);
|
|
415
|
+
}
|
|
152
416
|
|
|
153
|
-
// src/
|
|
154
|
-
|
|
417
|
+
// src/nodes/list.ts
|
|
418
|
+
function keysEqual(a, b) {
|
|
419
|
+
if (a.length !== b.length)
|
|
420
|
+
return false;
|
|
421
|
+
for (let i = 0;i < a.length; i++)
|
|
422
|
+
if (a[i] !== b[i])
|
|
423
|
+
return false;
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
function isEqual(a, b, visited) {
|
|
155
427
|
if (Object.is(a, b))
|
|
156
428
|
return true;
|
|
157
429
|
if (typeof a !== typeof b)
|
|
158
430
|
return false;
|
|
159
|
-
if (
|
|
431
|
+
if (a == null || typeof a !== "object" || b == null || typeof b !== "object")
|
|
160
432
|
return false;
|
|
161
433
|
if (!visited)
|
|
162
434
|
visited = new WeakSet;
|
|
@@ -165,17 +437,20 @@ var isEqual = (a, b, visited) => {
|
|
|
165
437
|
visited.add(a);
|
|
166
438
|
visited.add(b);
|
|
167
439
|
try {
|
|
168
|
-
|
|
169
|
-
|
|
440
|
+
const aIsArray = Array.isArray(a);
|
|
441
|
+
if (aIsArray !== Array.isArray(b))
|
|
442
|
+
return false;
|
|
443
|
+
if (aIsArray) {
|
|
444
|
+
const aa = a;
|
|
445
|
+
const ba = b;
|
|
446
|
+
if (aa.length !== ba.length)
|
|
170
447
|
return false;
|
|
171
|
-
for (let i = 0;i <
|
|
172
|
-
if (!isEqual(
|
|
448
|
+
for (let i = 0;i < aa.length; i++) {
|
|
449
|
+
if (!isEqual(aa[i], ba[i], visited))
|
|
173
450
|
return false;
|
|
174
451
|
}
|
|
175
452
|
return true;
|
|
176
453
|
}
|
|
177
|
-
if (Array.isArray(a) !== Array.isArray(b))
|
|
178
|
-
return false;
|
|
179
454
|
if (isRecord(a) && isRecord(b)) {
|
|
180
455
|
const aKeys = Object.keys(a);
|
|
181
456
|
const bKeys = Object.keys(b);
|
|
@@ -194,679 +469,913 @@ var isEqual = (a, b, visited) => {
|
|
|
194
469
|
visited.delete(a);
|
|
195
470
|
visited.delete(b);
|
|
196
471
|
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const oldValid = isRecordOrArray(oldObj);
|
|
200
|
-
const newValid = isRecordOrArray(newObj);
|
|
201
|
-
if (!oldValid || !newValid) {
|
|
202
|
-
const changed = !Object.is(oldObj, newObj);
|
|
203
|
-
return {
|
|
204
|
-
changed,
|
|
205
|
-
add: changed && newValid ? newObj : {},
|
|
206
|
-
change: {},
|
|
207
|
-
remove: changed && oldValid ? oldObj : {}
|
|
208
|
-
};
|
|
209
|
-
}
|
|
472
|
+
}
|
|
473
|
+
function diffArrays(oldArray, newArray, currentKeys, generateKey, contentBased) {
|
|
210
474
|
const visited = new WeakSet;
|
|
211
475
|
const add = {};
|
|
212
476
|
const change = {};
|
|
213
477
|
const remove = {};
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
for (
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
478
|
+
const newKeys = [];
|
|
479
|
+
let changed = false;
|
|
480
|
+
const oldByKey = new Map;
|
|
481
|
+
for (let i = 0;i < oldArray.length; i++) {
|
|
482
|
+
const key = currentKeys[i];
|
|
483
|
+
if (key && oldArray[i])
|
|
484
|
+
oldByKey.set(key, oldArray[i]);
|
|
485
|
+
}
|
|
486
|
+
const seenKeys = new Set;
|
|
487
|
+
for (let i = 0;i < newArray.length; i++) {
|
|
488
|
+
const newValue = newArray[i];
|
|
489
|
+
if (newValue === undefined)
|
|
225
490
|
continue;
|
|
491
|
+
const key = contentBased ? generateKey(newValue) : currentKeys[i] ?? generateKey(newValue);
|
|
492
|
+
if (seenKeys.has(key))
|
|
493
|
+
throw new DuplicateKeyError(TYPE_LIST, key, newValue);
|
|
494
|
+
newKeys.push(key);
|
|
495
|
+
seenKeys.add(key);
|
|
496
|
+
if (!oldByKey.has(key)) {
|
|
497
|
+
add[key] = newValue;
|
|
498
|
+
changed = true;
|
|
499
|
+
} else {
|
|
500
|
+
const oldValue = oldByKey.get(key);
|
|
501
|
+
if (!isEqual(oldValue, newValue, visited)) {
|
|
502
|
+
change[key] = newValue;
|
|
503
|
+
changed = true;
|
|
504
|
+
}
|
|
226
505
|
}
|
|
227
|
-
const oldValue = oldObj[key];
|
|
228
|
-
const newValue = newObj[key];
|
|
229
|
-
if (!isEqual(oldValue, newValue, visited))
|
|
230
|
-
change[key] = newValue;
|
|
231
|
-
}
|
|
232
|
-
return {
|
|
233
|
-
add,
|
|
234
|
-
change,
|
|
235
|
-
remove,
|
|
236
|
-
changed: !!(Object.keys(add).length || Object.keys(change).length || Object.keys(remove).length)
|
|
237
|
-
};
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
// src/classes/computed.ts
|
|
241
|
-
var TYPE_COMPUTED = "Computed";
|
|
242
|
-
|
|
243
|
-
class Memo {
|
|
244
|
-
#watchers = new Set;
|
|
245
|
-
#callback;
|
|
246
|
-
#value;
|
|
247
|
-
#error;
|
|
248
|
-
#dirty = true;
|
|
249
|
-
#computing = false;
|
|
250
|
-
#watcher;
|
|
251
|
-
#watchHookCallbacks;
|
|
252
|
-
constructor(callback, initialValue = UNSET) {
|
|
253
|
-
validateCallback(this.constructor.name, callback, isMemoCallback);
|
|
254
|
-
validateSignalValue(this.constructor.name, initialValue);
|
|
255
|
-
this.#callback = callback;
|
|
256
|
-
this.#value = initialValue;
|
|
257
|
-
}
|
|
258
|
-
#getWatcher() {
|
|
259
|
-
if (!this.#watcher) {
|
|
260
|
-
this.#watcher = createWatcher(() => {
|
|
261
|
-
this.#dirty = true;
|
|
262
|
-
if (!notifyWatchers(this.#watchers))
|
|
263
|
-
this.#watcher?.stop();
|
|
264
|
-
});
|
|
265
|
-
this.#watcher.on(HOOK_CLEANUP, () => {
|
|
266
|
-
this.#watcher = undefined;
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
return this.#watcher;
|
|
270
|
-
}
|
|
271
|
-
get [Symbol.toStringTag]() {
|
|
272
|
-
return TYPE_COMPUTED;
|
|
273
|
-
}
|
|
274
|
-
get() {
|
|
275
|
-
subscribeActiveWatcher(this.#watchers, this.#watchHookCallbacks);
|
|
276
|
-
flushPendingReactions();
|
|
277
|
-
if (this.#dirty) {
|
|
278
|
-
const watcher = this.#getWatcher();
|
|
279
|
-
trackSignalReads(watcher, () => {
|
|
280
|
-
if (this.#computing)
|
|
281
|
-
throw new CircularDependencyError("memo");
|
|
282
|
-
let result;
|
|
283
|
-
this.#computing = true;
|
|
284
|
-
try {
|
|
285
|
-
result = this.#callback(this.#value);
|
|
286
|
-
} catch (e) {
|
|
287
|
-
this.#value = UNSET;
|
|
288
|
-
this.#error = createError(e);
|
|
289
|
-
this.#computing = false;
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
if (result == null || UNSET === result) {
|
|
293
|
-
this.#value = UNSET;
|
|
294
|
-
this.#error = undefined;
|
|
295
|
-
} else {
|
|
296
|
-
this.#value = result;
|
|
297
|
-
this.#error = undefined;
|
|
298
|
-
this.#dirty = false;
|
|
299
|
-
}
|
|
300
|
-
this.#computing = false;
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
if (this.#error)
|
|
304
|
-
throw this.#error;
|
|
305
|
-
return this.#value;
|
|
306
506
|
}
|
|
307
|
-
|
|
308
|
-
if (
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
return () => {
|
|
312
|
-
this.#watchHookCallbacks?.delete(callback);
|
|
313
|
-
};
|
|
507
|
+
for (const [key] of oldByKey) {
|
|
508
|
+
if (!seenKeys.has(key)) {
|
|
509
|
+
remove[key] = null;
|
|
510
|
+
changed = true;
|
|
314
511
|
}
|
|
315
|
-
throw new InvalidHookError(this.constructor.name, type);
|
|
316
512
|
}
|
|
513
|
+
if (!changed && !keysEqual(currentKeys, newKeys))
|
|
514
|
+
changed = true;
|
|
515
|
+
return { add, change, remove, newKeys, changed };
|
|
317
516
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
return this.#watcher;
|
|
351
|
-
}
|
|
352
|
-
get [Symbol.toStringTag]() {
|
|
353
|
-
return TYPE_COMPUTED;
|
|
354
|
-
}
|
|
355
|
-
get() {
|
|
356
|
-
subscribeActiveWatcher(this.#watchers, this.#watchHookCallbacks);
|
|
357
|
-
flushPendingReactions();
|
|
358
|
-
const ok = (v) => {
|
|
359
|
-
if (!isEqual(v, this.#value)) {
|
|
360
|
-
this.#value = v;
|
|
361
|
-
this.#changed = true;
|
|
362
|
-
}
|
|
363
|
-
this.#error = undefined;
|
|
364
|
-
this.#dirty = false;
|
|
365
|
-
};
|
|
366
|
-
const nil = () => {
|
|
367
|
-
this.#changed = UNSET !== this.#value;
|
|
368
|
-
this.#value = UNSET;
|
|
369
|
-
this.#error = undefined;
|
|
370
|
-
};
|
|
371
|
-
const err = (e) => {
|
|
372
|
-
const newError = createError(e);
|
|
373
|
-
this.#changed = !this.#error || newError.name !== this.#error.name || newError.message !== this.#error.message;
|
|
374
|
-
this.#value = UNSET;
|
|
375
|
-
this.#error = newError;
|
|
376
|
-
};
|
|
377
|
-
const settle = (fn) => (arg) => {
|
|
378
|
-
this.#computing = false;
|
|
379
|
-
this.#controller = undefined;
|
|
380
|
-
fn(arg);
|
|
381
|
-
if (this.#changed && !notifyWatchers(this.#watchers))
|
|
382
|
-
this.#watcher?.stop();
|
|
383
|
-
};
|
|
384
|
-
const compute = () => trackSignalReads(this.#getWatcher(), () => {
|
|
385
|
-
if (this.#computing)
|
|
386
|
-
throw new CircularDependencyError("task");
|
|
387
|
-
this.#changed = false;
|
|
388
|
-
if (this.#controller)
|
|
389
|
-
return this.#value;
|
|
390
|
-
this.#controller = new AbortController;
|
|
391
|
-
this.#controller.signal.addEventListener("abort", () => {
|
|
392
|
-
this.#computing = false;
|
|
393
|
-
this.#controller = undefined;
|
|
394
|
-
compute();
|
|
395
|
-
}, {
|
|
396
|
-
once: true
|
|
397
|
-
});
|
|
398
|
-
let result;
|
|
399
|
-
this.#computing = true;
|
|
400
|
-
try {
|
|
401
|
-
result = this.#callback(this.#value, this.#controller.signal);
|
|
402
|
-
} catch (e) {
|
|
403
|
-
if (isAbortError(e))
|
|
404
|
-
nil();
|
|
405
|
-
else
|
|
406
|
-
err(e);
|
|
407
|
-
this.#computing = false;
|
|
408
|
-
return;
|
|
517
|
+
function createList(initialValue, options) {
|
|
518
|
+
validateSignalValue(TYPE_LIST, initialValue, Array.isArray);
|
|
519
|
+
const signals = new Map;
|
|
520
|
+
let keys = [];
|
|
521
|
+
let keyCounter = 0;
|
|
522
|
+
const keyConfig = options?.keyConfig;
|
|
523
|
+
const contentBased = isFunction(keyConfig);
|
|
524
|
+
const generateKey = typeof keyConfig === "string" ? () => `${keyConfig}${keyCounter++}` : contentBased ? (item) => keyConfig(item) : () => String(keyCounter++);
|
|
525
|
+
const buildValue = () => keys.map((key) => signals.get(key)?.get()).filter((v) => v !== undefined);
|
|
526
|
+
const node = {
|
|
527
|
+
fn: buildValue,
|
|
528
|
+
value: initialValue,
|
|
529
|
+
flags: FLAG_DIRTY,
|
|
530
|
+
sources: null,
|
|
531
|
+
sourcesTail: null,
|
|
532
|
+
sinks: null,
|
|
533
|
+
sinksTail: null,
|
|
534
|
+
equals: isEqual,
|
|
535
|
+
error: undefined
|
|
536
|
+
};
|
|
537
|
+
const toRecord = (array) => {
|
|
538
|
+
const record = {};
|
|
539
|
+
for (let i = 0;i < array.length; i++) {
|
|
540
|
+
const value = array[i];
|
|
541
|
+
if (value === undefined)
|
|
542
|
+
continue;
|
|
543
|
+
let key = keys[i];
|
|
544
|
+
if (!key) {
|
|
545
|
+
key = generateKey(value);
|
|
546
|
+
keys[i] = key;
|
|
409
547
|
}
|
|
410
|
-
|
|
411
|
-
result.then(settle(ok), settle(err));
|
|
412
|
-
else if (result == null || UNSET === result)
|
|
413
|
-
nil();
|
|
414
|
-
else
|
|
415
|
-
ok(result);
|
|
416
|
-
this.#computing = false;
|
|
417
|
-
});
|
|
418
|
-
if (this.#dirty)
|
|
419
|
-
compute();
|
|
420
|
-
if (this.#error)
|
|
421
|
-
throw this.#error;
|
|
422
|
-
return this.#value;
|
|
423
|
-
}
|
|
424
|
-
on(type, callback) {
|
|
425
|
-
if (type === HOOK_WATCH) {
|
|
426
|
-
this.#watchHookCallbacks ||= new Set;
|
|
427
|
-
this.#watchHookCallbacks.add(callback);
|
|
428
|
-
return () => {
|
|
429
|
-
this.#watchHookCallbacks?.delete(callback);
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
throw new InvalidHookError(this.constructor.name, type);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
var createComputed = (callback, initialValue = UNSET) => isAsyncFunction(callback) ? new Task(callback, initialValue) : new Memo(callback, initialValue);
|
|
436
|
-
var isComputed = (value) => isObjectOfType(value, TYPE_COMPUTED);
|
|
437
|
-
var isMemoCallback = (value) => isSyncFunction(value) && value.length < 2;
|
|
438
|
-
var isTaskCallback = (value) => isAsyncFunction(value) && value.length < 3;
|
|
439
|
-
|
|
440
|
-
// src/classes/composite.ts
|
|
441
|
-
class Composite {
|
|
442
|
-
signals = new Map;
|
|
443
|
-
#validate;
|
|
444
|
-
#create;
|
|
445
|
-
#watchers = new Map;
|
|
446
|
-
#hookCallbacks = {};
|
|
447
|
-
#batching = false;
|
|
448
|
-
constructor(values, validate, create) {
|
|
449
|
-
this.#validate = validate;
|
|
450
|
-
this.#create = create;
|
|
451
|
-
this.change({
|
|
452
|
-
add: values,
|
|
453
|
-
change: {},
|
|
454
|
-
remove: {},
|
|
455
|
-
changed: true
|
|
456
|
-
}, true);
|
|
457
|
-
}
|
|
458
|
-
#addWatcher(key) {
|
|
459
|
-
const watcher = createWatcher(() => {
|
|
460
|
-
trackSignalReads(watcher, () => {
|
|
461
|
-
this.signals.get(key)?.get();
|
|
462
|
-
if (!this.#batching)
|
|
463
|
-
triggerHook(this.#hookCallbacks.change, [key]);
|
|
464
|
-
});
|
|
465
|
-
});
|
|
466
|
-
this.#watchers.set(key, watcher);
|
|
467
|
-
watcher();
|
|
468
|
-
}
|
|
469
|
-
add(key, value) {
|
|
470
|
-
if (!this.#validate(key, value))
|
|
471
|
-
return false;
|
|
472
|
-
this.signals.set(key, this.#create(value));
|
|
473
|
-
if (this.#hookCallbacks.change?.size)
|
|
474
|
-
this.#addWatcher(key);
|
|
475
|
-
if (!this.#batching)
|
|
476
|
-
triggerHook(this.#hookCallbacks.add, [key]);
|
|
477
|
-
return true;
|
|
478
|
-
}
|
|
479
|
-
remove(key) {
|
|
480
|
-
const ok = this.signals.delete(key);
|
|
481
|
-
if (!ok)
|
|
482
|
-
return false;
|
|
483
|
-
const watcher = this.#watchers.get(key);
|
|
484
|
-
if (watcher) {
|
|
485
|
-
watcher.stop();
|
|
486
|
-
this.#watchers.delete(key);
|
|
548
|
+
record[key] = value;
|
|
487
549
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const notify = () => triggerHook(this.#hookCallbacks.add, Object.keys(changes.add));
|
|
498
|
-
if (initialRun)
|
|
499
|
-
setTimeout(notify, 0);
|
|
500
|
-
else
|
|
501
|
-
notify();
|
|
550
|
+
return record;
|
|
551
|
+
};
|
|
552
|
+
const applyChanges = (changes) => {
|
|
553
|
+
let structural = false;
|
|
554
|
+
for (const key in changes.add) {
|
|
555
|
+
const value = changes.add[key];
|
|
556
|
+
validateSignalValue(`${TYPE_LIST} item for key "${key}"`, value);
|
|
557
|
+
signals.set(key, createState(value));
|
|
558
|
+
structural = true;
|
|
502
559
|
}
|
|
503
560
|
if (Object.keys(changes.change).length) {
|
|
504
|
-
|
|
561
|
+
batch(() => {
|
|
505
562
|
for (const key in changes.change) {
|
|
506
563
|
const value = changes.change[key];
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
if (guardMutableSignal(`list item "${key}"`, value, signal))
|
|
564
|
+
validateSignalValue(`${TYPE_LIST} item for key "${key}"`, value);
|
|
565
|
+
const signal = signals.get(key);
|
|
566
|
+
if (signal)
|
|
511
567
|
signal.set(value);
|
|
512
568
|
}
|
|
513
569
|
});
|
|
514
|
-
triggerHook(this.#hookCallbacks.change, Object.keys(changes.change));
|
|
515
570
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
571
|
+
for (const key in changes.remove) {
|
|
572
|
+
signals.delete(key);
|
|
573
|
+
const index = keys.indexOf(key);
|
|
574
|
+
if (index !== -1)
|
|
575
|
+
keys.splice(index, 1);
|
|
576
|
+
structural = true;
|
|
520
577
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
clear() {
|
|
525
|
-
const keys = Array.from(this.signals.keys());
|
|
526
|
-
this.signals.clear();
|
|
527
|
-
this.#watchers.clear();
|
|
528
|
-
triggerHook(this.#hookCallbacks.remove, keys);
|
|
529
|
-
return true;
|
|
530
|
-
}
|
|
531
|
-
on(type, callback) {
|
|
532
|
-
if (!isHandledHook(type, [HOOK_ADD, HOOK_CHANGE, HOOK_REMOVE]))
|
|
533
|
-
throw new InvalidHookError("Composite", type);
|
|
534
|
-
this.#hookCallbacks[type] ||= new Set;
|
|
535
|
-
this.#hookCallbacks[type].add(callback);
|
|
536
|
-
if (type === HOOK_CHANGE && !this.#watchers.size) {
|
|
537
|
-
this.#batching = true;
|
|
538
|
-
for (const key of this.signals.keys())
|
|
539
|
-
this.#addWatcher(key);
|
|
540
|
-
this.#batching = false;
|
|
578
|
+
if (structural) {
|
|
579
|
+
node.sources = null;
|
|
580
|
+
node.sourcesTail = null;
|
|
541
581
|
}
|
|
542
|
-
return
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
582
|
+
return changes.changed;
|
|
583
|
+
};
|
|
584
|
+
const initRecord = toRecord(initialValue);
|
|
585
|
+
for (const key in initRecord) {
|
|
586
|
+
const value = initRecord[key];
|
|
587
|
+
validateSignalValue(`${TYPE_LIST} item for key "${key}"`, value);
|
|
588
|
+
signals.set(key, createState(value));
|
|
589
|
+
}
|
|
590
|
+
node.value = initialValue;
|
|
591
|
+
node.flags = 0;
|
|
592
|
+
const list = {
|
|
593
|
+
[Symbol.toStringTag]: TYPE_LIST,
|
|
594
|
+
[Symbol.isConcatSpreadable]: true,
|
|
595
|
+
*[Symbol.iterator]() {
|
|
596
|
+
for (const key of keys) {
|
|
597
|
+
const signal = signals.get(key);
|
|
598
|
+
if (signal)
|
|
599
|
+
yield signal;
|
|
550
600
|
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
class State {
|
|
559
|
-
#watchers = new Set;
|
|
560
|
-
#value;
|
|
561
|
-
#watchHookCallbacks;
|
|
562
|
-
constructor(initialValue) {
|
|
563
|
-
validateSignalValue(TYPE_STATE, initialValue);
|
|
564
|
-
this.#value = initialValue;
|
|
565
|
-
}
|
|
566
|
-
get [Symbol.toStringTag]() {
|
|
567
|
-
return TYPE_STATE;
|
|
568
|
-
}
|
|
569
|
-
get() {
|
|
570
|
-
subscribeActiveWatcher(this.#watchers, this.#watchHookCallbacks);
|
|
571
|
-
return this.#value;
|
|
572
|
-
}
|
|
573
|
-
set(newValue) {
|
|
574
|
-
validateSignalValue(TYPE_STATE, newValue);
|
|
575
|
-
if (isEqual(this.#value, newValue))
|
|
576
|
-
return;
|
|
577
|
-
this.#value = newValue;
|
|
578
|
-
if (this.#watchers.size)
|
|
579
|
-
notifyWatchers(this.#watchers);
|
|
580
|
-
if (UNSET === this.#value)
|
|
581
|
-
this.#watchers.clear();
|
|
582
|
-
}
|
|
583
|
-
update(updater) {
|
|
584
|
-
validateCallback(`${TYPE_STATE} update`, updater);
|
|
585
|
-
this.set(updater(this.#value));
|
|
586
|
-
}
|
|
587
|
-
on(type, callback) {
|
|
588
|
-
if (type === HOOK_WATCH) {
|
|
589
|
-
this.#watchHookCallbacks ||= new Set;
|
|
590
|
-
this.#watchHookCallbacks.add(callback);
|
|
591
|
-
return () => {
|
|
592
|
-
this.#watchHookCallbacks?.delete(callback);
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
throw new InvalidHookError(this.constructor.name, type);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
var isState = (value) => isObjectOfType(value, TYPE_STATE);
|
|
599
|
-
|
|
600
|
-
// src/classes/list.ts
|
|
601
|
-
var TYPE_LIST = "List";
|
|
602
|
-
|
|
603
|
-
class List {
|
|
604
|
-
#composite;
|
|
605
|
-
#watchers = new Set;
|
|
606
|
-
#hookCallbacks = {};
|
|
607
|
-
#order = [];
|
|
608
|
-
#generateKey;
|
|
609
|
-
constructor(initialValue, keyConfig) {
|
|
610
|
-
validateSignalValue(TYPE_LIST, initialValue, Array.isArray);
|
|
611
|
-
let keyCounter = 0;
|
|
612
|
-
this.#generateKey = isString(keyConfig) ? () => `${keyConfig}${keyCounter++}` : isFunction(keyConfig) ? (item) => keyConfig(item) : () => String(keyCounter++);
|
|
613
|
-
this.#composite = new Composite(this.#toRecord(initialValue), (key, value) => {
|
|
614
|
-
validateSignalValue(`${TYPE_LIST} for key "${key}"`, value);
|
|
615
|
-
return true;
|
|
616
|
-
}, (value) => new State(value));
|
|
617
|
-
}
|
|
618
|
-
#toRecord(array) {
|
|
619
|
-
const record = {};
|
|
620
|
-
for (let i = 0;i < array.length; i++) {
|
|
621
|
-
const value = array[i];
|
|
622
|
-
if (value === undefined)
|
|
623
|
-
continue;
|
|
624
|
-
let key = this.#order[i];
|
|
625
|
-
if (!key) {
|
|
626
|
-
key = this.#generateKey(value);
|
|
627
|
-
this.#order[i] = key;
|
|
601
|
+
},
|
|
602
|
+
get length() {
|
|
603
|
+
if (activeSink) {
|
|
604
|
+
if (!node.sinks && options?.watched)
|
|
605
|
+
node.stop = options.watched();
|
|
606
|
+
link(node, activeSink);
|
|
628
607
|
}
|
|
629
|
-
|
|
608
|
+
return keys.length;
|
|
609
|
+
},
|
|
610
|
+
get() {
|
|
611
|
+
if (activeSink) {
|
|
612
|
+
if (!node.sinks && options?.watched)
|
|
613
|
+
node.stop = options.watched();
|
|
614
|
+
link(node, activeSink);
|
|
615
|
+
}
|
|
616
|
+
if (node.sources) {
|
|
617
|
+
if (node.flags) {
|
|
618
|
+
node.value = untrack(buildValue);
|
|
619
|
+
node.flags = FLAG_CLEAN;
|
|
620
|
+
}
|
|
621
|
+
} else {
|
|
622
|
+
refresh(node);
|
|
623
|
+
if (node.error)
|
|
624
|
+
throw node.error;
|
|
625
|
+
}
|
|
626
|
+
return node.value;
|
|
627
|
+
},
|
|
628
|
+
set(newValue) {
|
|
629
|
+
const currentValue = node.flags & FLAG_DIRTY ? buildValue() : node.value;
|
|
630
|
+
const changes = diffArrays(currentValue, newValue, keys, generateKey, contentBased);
|
|
631
|
+
if (changes.changed) {
|
|
632
|
+
keys = changes.newKeys;
|
|
633
|
+
applyChanges(changes);
|
|
634
|
+
propagate(node);
|
|
635
|
+
node.flags |= FLAG_DIRTY;
|
|
636
|
+
if (batchDepth === 0)
|
|
637
|
+
flush();
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
update(fn) {
|
|
641
|
+
list.set(fn(list.get()));
|
|
642
|
+
},
|
|
643
|
+
at(index) {
|
|
644
|
+
return signals.get(keys[index]);
|
|
645
|
+
},
|
|
646
|
+
keys() {
|
|
647
|
+
if (activeSink) {
|
|
648
|
+
if (!node.sinks && options?.watched)
|
|
649
|
+
node.stop = options.watched();
|
|
650
|
+
link(node, activeSink);
|
|
651
|
+
}
|
|
652
|
+
return keys.values();
|
|
653
|
+
},
|
|
654
|
+
byKey(key) {
|
|
655
|
+
return signals.get(key);
|
|
656
|
+
},
|
|
657
|
+
keyAt(index) {
|
|
658
|
+
return keys[index];
|
|
659
|
+
},
|
|
660
|
+
indexOfKey(key) {
|
|
661
|
+
return keys.indexOf(key);
|
|
662
|
+
},
|
|
663
|
+
add(value) {
|
|
664
|
+
const key = generateKey(value);
|
|
665
|
+
if (signals.has(key))
|
|
666
|
+
throw new DuplicateKeyError(TYPE_LIST, key, value);
|
|
667
|
+
if (!keys.includes(key))
|
|
668
|
+
keys.push(key);
|
|
669
|
+
validateSignalValue(`${TYPE_LIST} item for key "${key}"`, value);
|
|
670
|
+
signals.set(key, createState(value));
|
|
671
|
+
node.sources = null;
|
|
672
|
+
node.sourcesTail = null;
|
|
673
|
+
propagate(node);
|
|
674
|
+
node.flags |= FLAG_DIRTY;
|
|
675
|
+
if (batchDepth === 0)
|
|
676
|
+
flush();
|
|
677
|
+
return key;
|
|
678
|
+
},
|
|
679
|
+
remove(keyOrIndex) {
|
|
680
|
+
const key = typeof keyOrIndex === "number" ? keys[keyOrIndex] : keyOrIndex;
|
|
681
|
+
const ok = signals.delete(key);
|
|
682
|
+
if (ok) {
|
|
683
|
+
const index = typeof keyOrIndex === "number" ? keyOrIndex : keys.indexOf(key);
|
|
684
|
+
if (index >= 0)
|
|
685
|
+
keys.splice(index, 1);
|
|
686
|
+
node.sources = null;
|
|
687
|
+
node.sourcesTail = null;
|
|
688
|
+
propagate(node);
|
|
689
|
+
node.flags |= FLAG_DIRTY;
|
|
690
|
+
if (batchDepth === 0)
|
|
691
|
+
flush();
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
sort(compareFn) {
|
|
695
|
+
const entries = keys.map((key) => [key, signals.get(key)?.get()]).sort(isFunction(compareFn) ? (a, b) => compareFn(a[1], b[1]) : (a, b) => String(a[1]).localeCompare(String(b[1])));
|
|
696
|
+
const newOrder = entries.map(([key]) => key);
|
|
697
|
+
if (!keysEqual(keys, newOrder)) {
|
|
698
|
+
keys = newOrder;
|
|
699
|
+
propagate(node);
|
|
700
|
+
node.flags |= FLAG_DIRTY;
|
|
701
|
+
if (batchDepth === 0)
|
|
702
|
+
flush();
|
|
703
|
+
}
|
|
704
|
+
},
|
|
705
|
+
splice(start, deleteCount, ...items) {
|
|
706
|
+
const length = keys.length;
|
|
707
|
+
const actualStart = start < 0 ? Math.max(0, length + start) : Math.min(start, length);
|
|
708
|
+
const actualDeleteCount = Math.max(0, Math.min(deleteCount ?? Math.max(0, length - Math.max(0, actualStart)), length - actualStart));
|
|
709
|
+
const add = {};
|
|
710
|
+
const remove = {};
|
|
711
|
+
for (let i = 0;i < actualDeleteCount; i++) {
|
|
712
|
+
const index = actualStart + i;
|
|
713
|
+
const key = keys[index];
|
|
714
|
+
if (key) {
|
|
715
|
+
const signal = signals.get(key);
|
|
716
|
+
if (signal)
|
|
717
|
+
remove[key] = signal.get();
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
const newOrder = keys.slice(0, actualStart);
|
|
721
|
+
for (const item of items) {
|
|
722
|
+
const key = generateKey(item);
|
|
723
|
+
if (signals.has(key) && !(key in remove))
|
|
724
|
+
throw new DuplicateKeyError(TYPE_LIST, key, item);
|
|
725
|
+
newOrder.push(key);
|
|
726
|
+
add[key] = item;
|
|
727
|
+
}
|
|
728
|
+
newOrder.push(...keys.slice(actualStart + actualDeleteCount));
|
|
729
|
+
const changed = !!(Object.keys(add).length || Object.keys(remove).length);
|
|
730
|
+
if (changed) {
|
|
731
|
+
applyChanges({
|
|
732
|
+
add,
|
|
733
|
+
change: {},
|
|
734
|
+
remove,
|
|
735
|
+
changed
|
|
736
|
+
});
|
|
737
|
+
keys = newOrder;
|
|
738
|
+
propagate(node);
|
|
739
|
+
node.flags |= FLAG_DIRTY;
|
|
740
|
+
if (batchDepth === 0)
|
|
741
|
+
flush();
|
|
742
|
+
}
|
|
743
|
+
return Object.values(remove);
|
|
744
|
+
},
|
|
745
|
+
deriveCollection(cb) {
|
|
746
|
+
return deriveCollection(list, cb);
|
|
630
747
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
748
|
+
};
|
|
749
|
+
return list;
|
|
750
|
+
}
|
|
751
|
+
function isList(value) {
|
|
752
|
+
return isObjectOfType(value, TYPE_LIST);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// src/nodes/memo.ts
|
|
756
|
+
function createMemo(fn, options) {
|
|
757
|
+
validateCallback(TYPE_MEMO, fn, isSyncFunction);
|
|
758
|
+
if (options?.value !== undefined)
|
|
759
|
+
validateSignalValue(TYPE_MEMO, options.value, options?.guard);
|
|
760
|
+
const node = {
|
|
761
|
+
fn,
|
|
762
|
+
value: options?.value,
|
|
763
|
+
flags: FLAG_DIRTY,
|
|
764
|
+
sources: null,
|
|
765
|
+
sourcesTail: null,
|
|
766
|
+
sinks: null,
|
|
767
|
+
sinksTail: null,
|
|
768
|
+
equals: options?.equals ?? DEFAULT_EQUALITY,
|
|
769
|
+
error: undefined
|
|
770
|
+
};
|
|
771
|
+
return {
|
|
772
|
+
[Symbol.toStringTag]: TYPE_MEMO,
|
|
773
|
+
get() {
|
|
774
|
+
if (activeSink)
|
|
775
|
+
link(node, activeSink);
|
|
776
|
+
refresh(node);
|
|
777
|
+
if (node.error)
|
|
778
|
+
throw node.error;
|
|
779
|
+
validateReadValue(TYPE_MEMO, node.value);
|
|
780
|
+
return node.value;
|
|
647
781
|
}
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
function isMemo(value) {
|
|
785
|
+
return isObjectOfType(value, TYPE_MEMO);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// src/nodes/task.ts
|
|
789
|
+
function createTask(fn, options) {
|
|
790
|
+
validateCallback(TYPE_TASK, fn, isAsyncFunction);
|
|
791
|
+
if (options?.value !== undefined)
|
|
792
|
+
validateSignalValue(TYPE_TASK, options.value, options?.guard);
|
|
793
|
+
const node = {
|
|
794
|
+
fn,
|
|
795
|
+
value: options?.value,
|
|
796
|
+
sources: null,
|
|
797
|
+
sourcesTail: null,
|
|
798
|
+
sinks: null,
|
|
799
|
+
sinksTail: null,
|
|
800
|
+
flags: FLAG_DIRTY,
|
|
801
|
+
equals: options?.equals ?? DEFAULT_EQUALITY,
|
|
802
|
+
controller: undefined,
|
|
803
|
+
error: undefined
|
|
804
|
+
};
|
|
805
|
+
return {
|
|
806
|
+
[Symbol.toStringTag]: TYPE_TASK,
|
|
807
|
+
get() {
|
|
808
|
+
if (activeSink)
|
|
809
|
+
link(node, activeSink);
|
|
810
|
+
refresh(node);
|
|
811
|
+
if (node.error)
|
|
812
|
+
throw node.error;
|
|
813
|
+
validateReadValue(TYPE_TASK, node.value);
|
|
814
|
+
return node.value;
|
|
815
|
+
},
|
|
816
|
+
isPending() {
|
|
817
|
+
return !!node.controller;
|
|
818
|
+
},
|
|
819
|
+
abort() {
|
|
820
|
+
node.controller?.abort();
|
|
821
|
+
node.controller = undefined;
|
|
663
822
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
function isTask(value) {
|
|
826
|
+
return isObjectOfType(value, TYPE_TASK);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// src/nodes/collection.ts
|
|
830
|
+
function deriveCollection(source, callback) {
|
|
831
|
+
validateCallback(TYPE_COLLECTION, callback);
|
|
832
|
+
if (!isCollectionSource(source))
|
|
833
|
+
throw new TypeError(`[${TYPE_COLLECTION}] Invalid collection source: expected a List or Collection`);
|
|
834
|
+
const isAsync = isAsyncFunction(callback);
|
|
835
|
+
const signals = new Map;
|
|
836
|
+
const addSignal = (key) => {
|
|
837
|
+
const signal = isAsync ? createTask(async (prev, abort) => {
|
|
838
|
+
const sourceValue = source.byKey(key)?.get();
|
|
839
|
+
if (sourceValue == null)
|
|
840
|
+
return prev;
|
|
841
|
+
return callback(sourceValue, abort);
|
|
842
|
+
}) : createMemo(() => {
|
|
843
|
+
const sourceValue = source.byKey(key)?.get();
|
|
844
|
+
if (sourceValue == null)
|
|
845
|
+
return;
|
|
846
|
+
return callback(sourceValue);
|
|
847
|
+
});
|
|
848
|
+
signals.set(key, signal);
|
|
849
|
+
};
|
|
850
|
+
function syncKeys() {
|
|
851
|
+
const newKeys = Array.from(source.keys());
|
|
852
|
+
const oldKeys = node.value;
|
|
853
|
+
if (!keysEqual(oldKeys, newKeys)) {
|
|
854
|
+
const oldKeySet = new Set(oldKeys);
|
|
855
|
+
const newKeySet = new Set(newKeys);
|
|
856
|
+
for (const key of oldKeys)
|
|
857
|
+
if (!newKeySet.has(key))
|
|
858
|
+
signals.delete(key);
|
|
859
|
+
for (const key of newKeys)
|
|
860
|
+
if (!oldKeySet.has(key))
|
|
861
|
+
addSignal(key);
|
|
862
|
+
}
|
|
863
|
+
return newKeys;
|
|
864
|
+
}
|
|
865
|
+
const node = {
|
|
866
|
+
fn: syncKeys,
|
|
867
|
+
value: [],
|
|
868
|
+
flags: FLAG_DIRTY,
|
|
869
|
+
sources: null,
|
|
870
|
+
sourcesTail: null,
|
|
871
|
+
sinks: null,
|
|
872
|
+
sinksTail: null,
|
|
873
|
+
equals: keysEqual,
|
|
874
|
+
error: undefined
|
|
875
|
+
};
|
|
876
|
+
function ensureSynced() {
|
|
877
|
+
if (node.sources) {
|
|
878
|
+
if (node.flags) {
|
|
879
|
+
node.value = untrack(syncKeys);
|
|
880
|
+
node.flags = FLAG_CLEAN;
|
|
673
881
|
}
|
|
674
|
-
|
|
675
|
-
|
|
882
|
+
} else {
|
|
883
|
+
refresh(node);
|
|
884
|
+
if (node.error)
|
|
885
|
+
throw node.error;
|
|
676
886
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
887
|
+
return node.value;
|
|
888
|
+
}
|
|
889
|
+
const initialKeys = Array.from(source.keys());
|
|
890
|
+
for (const key of initialKeys)
|
|
891
|
+
addSignal(key);
|
|
892
|
+
node.value = initialKeys;
|
|
893
|
+
const collection = {
|
|
894
|
+
[Symbol.toStringTag]: TYPE_COLLECTION,
|
|
895
|
+
[Symbol.isConcatSpreadable]: true,
|
|
896
|
+
*[Symbol.iterator]() {
|
|
897
|
+
for (const key of node.value) {
|
|
898
|
+
const signal = signals.get(key);
|
|
899
|
+
if (signal)
|
|
900
|
+
yield signal;
|
|
901
|
+
}
|
|
902
|
+
},
|
|
903
|
+
get length() {
|
|
904
|
+
if (activeSink)
|
|
905
|
+
link(node, activeSink);
|
|
906
|
+
return ensureSynced().length;
|
|
907
|
+
},
|
|
908
|
+
keys() {
|
|
909
|
+
if (activeSink)
|
|
910
|
+
link(node, activeSink);
|
|
911
|
+
return ensureSynced().values();
|
|
912
|
+
},
|
|
913
|
+
get() {
|
|
914
|
+
if (activeSink)
|
|
915
|
+
link(node, activeSink);
|
|
916
|
+
const currentKeys = ensureSynced();
|
|
917
|
+
const result = [];
|
|
918
|
+
for (const key of currentKeys) {
|
|
919
|
+
try {
|
|
920
|
+
const v = signals.get(key)?.get();
|
|
921
|
+
if (v != null)
|
|
922
|
+
result.push(v);
|
|
923
|
+
} catch (e) {
|
|
924
|
+
if (!(e instanceof UnsetSignalValueError))
|
|
925
|
+
throw e;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
return result;
|
|
929
|
+
},
|
|
930
|
+
at(index) {
|
|
931
|
+
return signals.get(node.value[index]);
|
|
932
|
+
},
|
|
933
|
+
byKey(key) {
|
|
934
|
+
return signals.get(key);
|
|
935
|
+
},
|
|
936
|
+
keyAt(index) {
|
|
937
|
+
return node.value[index];
|
|
938
|
+
},
|
|
939
|
+
indexOfKey(key) {
|
|
940
|
+
return node.value.indexOf(key);
|
|
941
|
+
},
|
|
942
|
+
deriveCollection(cb) {
|
|
943
|
+
return deriveCollection(collection, cb);
|
|
716
944
|
}
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
945
|
+
};
|
|
946
|
+
return collection;
|
|
947
|
+
}
|
|
948
|
+
function createCollection(start, options) {
|
|
949
|
+
const initialValue = options?.value ?? [];
|
|
950
|
+
if (initialValue.length)
|
|
951
|
+
validateSignalValue(TYPE_COLLECTION, initialValue, Array.isArray);
|
|
952
|
+
validateCallback(TYPE_COLLECTION, start);
|
|
953
|
+
const signals = new Map;
|
|
954
|
+
const keys = [];
|
|
955
|
+
let keyCounter = 0;
|
|
956
|
+
const keyConfig = options?.keyConfig;
|
|
957
|
+
const generateKey = typeof keyConfig === "string" ? () => `${keyConfig}${keyCounter++}` : isFunction(keyConfig) ? (item) => keyConfig(item) : () => String(keyCounter++);
|
|
958
|
+
const itemFactory = options?.createItem ?? ((_key, value) => createState(value));
|
|
959
|
+
function buildValue() {
|
|
960
|
+
const result = [];
|
|
961
|
+
for (const key of keys) {
|
|
962
|
+
try {
|
|
963
|
+
const v = signals.get(key)?.get();
|
|
964
|
+
if (v != null)
|
|
965
|
+
result.push(v);
|
|
966
|
+
} catch (e) {
|
|
967
|
+
if (!(e instanceof UnsetSignalValueError))
|
|
968
|
+
throw e;
|
|
969
|
+
}
|
|
725
970
|
}
|
|
971
|
+
return result;
|
|
972
|
+
}
|
|
973
|
+
const node = {
|
|
974
|
+
fn: buildValue,
|
|
975
|
+
value: initialValue,
|
|
976
|
+
flags: FLAG_DIRTY,
|
|
977
|
+
sources: null,
|
|
978
|
+
sourcesTail: null,
|
|
979
|
+
sinks: null,
|
|
980
|
+
sinksTail: null,
|
|
981
|
+
equals: () => false,
|
|
982
|
+
error: undefined
|
|
983
|
+
};
|
|
984
|
+
function applyChanges(changes) {
|
|
985
|
+
if (!changes.changed)
|
|
986
|
+
return;
|
|
987
|
+
let structural = false;
|
|
988
|
+
batch(() => {
|
|
989
|
+
for (const key in changes.add) {
|
|
990
|
+
const value = changes.add[key];
|
|
991
|
+
signals.set(key, itemFactory(key, value));
|
|
992
|
+
if (!keys.includes(key))
|
|
993
|
+
keys.push(key);
|
|
994
|
+
structural = true;
|
|
995
|
+
}
|
|
996
|
+
for (const key in changes.change) {
|
|
997
|
+
const signal = signals.get(key);
|
|
998
|
+
if (signal && isState(signal)) {
|
|
999
|
+
signal.set(changes.change[key]);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
for (const key in changes.remove) {
|
|
1003
|
+
signals.delete(key);
|
|
1004
|
+
const index = keys.indexOf(key);
|
|
1005
|
+
if (index !== -1)
|
|
1006
|
+
keys.splice(index, 1);
|
|
1007
|
+
structural = true;
|
|
1008
|
+
}
|
|
1009
|
+
if (structural) {
|
|
1010
|
+
node.sources = null;
|
|
1011
|
+
node.sourcesTail = null;
|
|
1012
|
+
}
|
|
1013
|
+
node.flags = FLAG_CLEAN;
|
|
1014
|
+
propagate(node);
|
|
1015
|
+
node.flags |= FLAG_DIRTY;
|
|
1016
|
+
});
|
|
726
1017
|
}
|
|
727
|
-
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
1018
|
+
for (const item of initialValue) {
|
|
1019
|
+
const key = generateKey(item);
|
|
1020
|
+
signals.set(key, itemFactory(key, item));
|
|
1021
|
+
keys.push(key);
|
|
1022
|
+
}
|
|
1023
|
+
node.value = initialValue;
|
|
1024
|
+
node.flags = FLAG_DIRTY;
|
|
1025
|
+
function startWatching() {
|
|
1026
|
+
if (!node.sinks)
|
|
1027
|
+
node.stop = start(applyChanges);
|
|
1028
|
+
}
|
|
1029
|
+
const collection = {
|
|
1030
|
+
[Symbol.toStringTag]: TYPE_COLLECTION,
|
|
1031
|
+
[Symbol.isConcatSpreadable]: true,
|
|
1032
|
+
*[Symbol.iterator]() {
|
|
1033
|
+
for (const key of keys) {
|
|
1034
|
+
const signal = signals.get(key);
|
|
738
1035
|
if (signal)
|
|
739
|
-
|
|
1036
|
+
yield signal;
|
|
1037
|
+
}
|
|
1038
|
+
},
|
|
1039
|
+
get length() {
|
|
1040
|
+
if (activeSink) {
|
|
1041
|
+
startWatching();
|
|
1042
|
+
link(node, activeSink);
|
|
1043
|
+
}
|
|
1044
|
+
return keys.length;
|
|
1045
|
+
},
|
|
1046
|
+
keys() {
|
|
1047
|
+
if (activeSink) {
|
|
1048
|
+
startWatching();
|
|
1049
|
+
link(node, activeSink);
|
|
1050
|
+
}
|
|
1051
|
+
return keys.values();
|
|
1052
|
+
},
|
|
1053
|
+
get() {
|
|
1054
|
+
if (activeSink) {
|
|
1055
|
+
startWatching();
|
|
1056
|
+
link(node, activeSink);
|
|
1057
|
+
}
|
|
1058
|
+
if (node.sources) {
|
|
1059
|
+
if (node.flags) {
|
|
1060
|
+
node.value = untrack(buildValue);
|
|
1061
|
+
node.flags = FLAG_CLEAN;
|
|
1062
|
+
}
|
|
1063
|
+
} else {
|
|
1064
|
+
refresh(node);
|
|
1065
|
+
if (node.error)
|
|
1066
|
+
throw node.error;
|
|
740
1067
|
}
|
|
1068
|
+
return node.value;
|
|
1069
|
+
},
|
|
1070
|
+
at(index) {
|
|
1071
|
+
return signals.get(keys[index]);
|
|
1072
|
+
},
|
|
1073
|
+
byKey(key) {
|
|
1074
|
+
return signals.get(key);
|
|
1075
|
+
},
|
|
1076
|
+
keyAt(index) {
|
|
1077
|
+
return keys[index];
|
|
1078
|
+
},
|
|
1079
|
+
indexOfKey(key) {
|
|
1080
|
+
return keys.indexOf(key);
|
|
1081
|
+
},
|
|
1082
|
+
deriveCollection(cb) {
|
|
1083
|
+
return deriveCollection(collection, cb);
|
|
741
1084
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
1085
|
+
};
|
|
1086
|
+
return collection;
|
|
1087
|
+
}
|
|
1088
|
+
function isCollection(value) {
|
|
1089
|
+
return isObjectOfType(value, TYPE_COLLECTION);
|
|
1090
|
+
}
|
|
1091
|
+
function isCollectionSource(value) {
|
|
1092
|
+
return isList(value) || isCollection(value);
|
|
1093
|
+
}
|
|
1094
|
+
// src/nodes/effect.ts
|
|
1095
|
+
function createEffect(fn) {
|
|
1096
|
+
validateCallback("Effect", fn);
|
|
1097
|
+
const node = {
|
|
1098
|
+
fn,
|
|
1099
|
+
flags: FLAG_DIRTY,
|
|
1100
|
+
sources: null,
|
|
1101
|
+
sourcesTail: null,
|
|
1102
|
+
cleanup: null
|
|
1103
|
+
};
|
|
1104
|
+
const dispose = () => {
|
|
1105
|
+
runCleanup(node);
|
|
1106
|
+
node.fn = undefined;
|
|
1107
|
+
node.flags = FLAG_CLEAN;
|
|
1108
|
+
node.sourcesTail = null;
|
|
1109
|
+
trimSources(node);
|
|
1110
|
+
};
|
|
1111
|
+
if (activeOwner)
|
|
1112
|
+
registerCleanup(activeOwner, dispose);
|
|
1113
|
+
runEffect(node);
|
|
1114
|
+
return dispose;
|
|
1115
|
+
}
|
|
1116
|
+
function match(signals, handlers) {
|
|
1117
|
+
if (!activeOwner)
|
|
1118
|
+
throw new RequiredOwnerError("match");
|
|
1119
|
+
const { ok, err = console.error, nil } = handlers;
|
|
1120
|
+
let errors;
|
|
1121
|
+
let pending = false;
|
|
1122
|
+
const values = new Array(signals.length);
|
|
1123
|
+
for (let i = 0;i < signals.length; i++) {
|
|
1124
|
+
try {
|
|
1125
|
+
values[i] = signals[i].get();
|
|
1126
|
+
} catch (e) {
|
|
1127
|
+
if (e instanceof UnsetSignalValueError) {
|
|
1128
|
+
pending = true;
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
if (!errors)
|
|
1132
|
+
errors = [];
|
|
1133
|
+
errors.push(e instanceof Error ? e : new Error(String(e)));
|
|
747
1134
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
1135
|
+
}
|
|
1136
|
+
let out;
|
|
1137
|
+
try {
|
|
1138
|
+
if (pending)
|
|
1139
|
+
out = nil?.();
|
|
1140
|
+
else if (errors)
|
|
1141
|
+
out = err(errors);
|
|
1142
|
+
else
|
|
1143
|
+
out = ok(values);
|
|
1144
|
+
} catch (e) {
|
|
1145
|
+
err([e instanceof Error ? e : new Error(String(e))]);
|
|
1146
|
+
}
|
|
1147
|
+
if (typeof out === "function")
|
|
1148
|
+
return out;
|
|
1149
|
+
if (out instanceof Promise) {
|
|
1150
|
+
const owner = activeOwner;
|
|
1151
|
+
const controller = new AbortController;
|
|
1152
|
+
registerCleanup(owner, () => controller.abort());
|
|
1153
|
+
out.then((cleanup) => {
|
|
1154
|
+
if (!controller.signal.aborted && typeof cleanup === "function")
|
|
1155
|
+
registerCleanup(owner, cleanup);
|
|
1156
|
+
}).catch((e) => {
|
|
1157
|
+
err([e instanceof Error ? e : new Error(String(e))]);
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
// src/nodes/sensor.ts
|
|
1162
|
+
function createSensor(start, options) {
|
|
1163
|
+
validateCallback(TYPE_SENSOR, start, isSyncFunction);
|
|
1164
|
+
if (options?.value !== undefined)
|
|
1165
|
+
validateSignalValue(TYPE_SENSOR, options.value, options?.guard);
|
|
1166
|
+
const node = {
|
|
1167
|
+
value: options?.value,
|
|
1168
|
+
sinks: null,
|
|
1169
|
+
sinksTail: null,
|
|
1170
|
+
equals: options?.equals ?? DEFAULT_EQUALITY,
|
|
1171
|
+
guard: options?.guard,
|
|
1172
|
+
stop: undefined
|
|
1173
|
+
};
|
|
1174
|
+
return {
|
|
1175
|
+
[Symbol.toStringTag]: TYPE_SENSOR,
|
|
1176
|
+
get() {
|
|
1177
|
+
if (activeSink) {
|
|
1178
|
+
if (!node.sinks)
|
|
1179
|
+
node.stop = start((next) => {
|
|
1180
|
+
validateSignalValue(TYPE_SENSOR, next, node.guard);
|
|
1181
|
+
setState(node, next);
|
|
1182
|
+
});
|
|
1183
|
+
link(node, activeSink);
|
|
1184
|
+
}
|
|
1185
|
+
validateReadValue(TYPE_SENSOR, node.value);
|
|
1186
|
+
return node.value;
|
|
759
1187
|
}
|
|
760
|
-
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
function isSensor(value) {
|
|
1191
|
+
return isObjectOfType(value, TYPE_SENSOR);
|
|
1192
|
+
}
|
|
1193
|
+
// src/nodes/store.ts
|
|
1194
|
+
function diffRecords(oldObj, newObj) {
|
|
1195
|
+
const oldValid = isRecord(oldObj) || Array.isArray(oldObj);
|
|
1196
|
+
const newValid = isRecord(newObj) || Array.isArray(newObj);
|
|
1197
|
+
if (!oldValid || !newValid) {
|
|
1198
|
+
const changed2 = !Object.is(oldObj, newObj);
|
|
1199
|
+
return {
|
|
1200
|
+
changed: changed2,
|
|
1201
|
+
add: changed2 && newValid ? newObj : {},
|
|
1202
|
+
change: {},
|
|
1203
|
+
remove: changed2 && oldValid ? oldObj : {}
|
|
1204
|
+
};
|
|
761
1205
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
1206
|
+
const visited = new WeakSet;
|
|
1207
|
+
const add = {};
|
|
1208
|
+
const change = {};
|
|
1209
|
+
const remove = {};
|
|
1210
|
+
let changed = false;
|
|
1211
|
+
const oldKeys = Object.keys(oldObj);
|
|
1212
|
+
const newKeys = Object.keys(newObj);
|
|
1213
|
+
for (const key of newKeys) {
|
|
1214
|
+
if (key in oldObj) {
|
|
1215
|
+
if (!isEqual(oldObj[key], newObj[key], visited)) {
|
|
1216
|
+
change[key] = newObj[key];
|
|
1217
|
+
changed = true;
|
|
1218
|
+
}
|
|
1219
|
+
} else {
|
|
1220
|
+
add[key] = newObj[key];
|
|
1221
|
+
changed = true;
|
|
771
1222
|
}
|
|
772
|
-
throw new InvalidHookError(TYPE_LIST, type);
|
|
773
1223
|
}
|
|
774
|
-
|
|
775
|
-
|
|
1224
|
+
for (const key of oldKeys) {
|
|
1225
|
+
if (!(key in newObj)) {
|
|
1226
|
+
remove[key] = undefined;
|
|
1227
|
+
changed = true;
|
|
1228
|
+
}
|
|
776
1229
|
}
|
|
1230
|
+
return { add, change, remove, changed };
|
|
777
1231
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
return true;
|
|
792
|
-
}, (value) => createMutableSignal(value));
|
|
793
|
-
}
|
|
794
|
-
get #value() {
|
|
1232
|
+
function createStore(initialValue, options) {
|
|
1233
|
+
validateSignalValue(TYPE_STORE, initialValue, isRecord);
|
|
1234
|
+
const signals = new Map;
|
|
1235
|
+
const addSignal = (key, value) => {
|
|
1236
|
+
validateSignalValue(`${TYPE_STORE} for key "${key}"`, value);
|
|
1237
|
+
if (Array.isArray(value))
|
|
1238
|
+
signals.set(key, createList(value));
|
|
1239
|
+
else if (isRecord(value))
|
|
1240
|
+
signals.set(key, createStore(value));
|
|
1241
|
+
else
|
|
1242
|
+
signals.set(key, createState(value));
|
|
1243
|
+
};
|
|
1244
|
+
const buildValue = () => {
|
|
795
1245
|
const record = {};
|
|
796
|
-
|
|
1246
|
+
signals.forEach((signal, key) => {
|
|
797
1247
|
record[key] = signal.get();
|
|
1248
|
+
});
|
|
798
1249
|
return record;
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
get() {
|
|
817
|
-
subscribeActiveWatcher(this.#watchers, this.#watchHookCallbacks);
|
|
818
|
-
return this.#value;
|
|
819
|
-
}
|
|
820
|
-
set(newValue) {
|
|
821
|
-
if (UNSET === newValue) {
|
|
822
|
-
this.#composite.clear();
|
|
823
|
-
notifyWatchers(this.#watchers);
|
|
824
|
-
this.#watchers.clear();
|
|
825
|
-
return;
|
|
1250
|
+
};
|
|
1251
|
+
const node = {
|
|
1252
|
+
fn: buildValue,
|
|
1253
|
+
value: initialValue,
|
|
1254
|
+
flags: FLAG_DIRTY,
|
|
1255
|
+
sources: null,
|
|
1256
|
+
sourcesTail: null,
|
|
1257
|
+
sinks: null,
|
|
1258
|
+
sinksTail: null,
|
|
1259
|
+
equals: isEqual,
|
|
1260
|
+
error: undefined
|
|
1261
|
+
};
|
|
1262
|
+
const applyChanges = (changes) => {
|
|
1263
|
+
let structural = false;
|
|
1264
|
+
for (const key in changes.add) {
|
|
1265
|
+
addSignal(key, changes.add[key]);
|
|
1266
|
+
structural = true;
|
|
826
1267
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
}
|
|
843
|
-
remove(key) {
|
|
844
|
-
const ok = this.#composite.remove(key);
|
|
845
|
-
if (ok)
|
|
846
|
-
notifyWatchers(this.#watchers);
|
|
847
|
-
}
|
|
848
|
-
on(type, callback) {
|
|
849
|
-
if (type === HOOK_WATCH) {
|
|
850
|
-
this.#watchHookCallbacks ||= new Set;
|
|
851
|
-
this.#watchHookCallbacks.add(callback);
|
|
852
|
-
return () => {
|
|
853
|
-
this.#watchHookCallbacks?.delete(callback);
|
|
854
|
-
};
|
|
855
|
-
} else if (isHandledHook(type, [HOOK_ADD, HOOK_CHANGE, HOOK_REMOVE])) {
|
|
856
|
-
return this.#composite.on(type, callback);
|
|
1268
|
+
if (Object.keys(changes.change).length) {
|
|
1269
|
+
batch(() => {
|
|
1270
|
+
for (const key in changes.change) {
|
|
1271
|
+
const value = changes.change[key];
|
|
1272
|
+
validateSignalValue(`${TYPE_STORE} for key "${key}"`, value);
|
|
1273
|
+
const signal = signals.get(key);
|
|
1274
|
+
if (signal) {
|
|
1275
|
+
if (isRecord(value) !== isStore(signal)) {
|
|
1276
|
+
addSignal(key, value);
|
|
1277
|
+
structural = true;
|
|
1278
|
+
} else
|
|
1279
|
+
signal.set(value);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
857
1283
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1284
|
+
for (const key in changes.remove) {
|
|
1285
|
+
signals.delete(key);
|
|
1286
|
+
structural = true;
|
|
1287
|
+
}
|
|
1288
|
+
if (structural) {
|
|
1289
|
+
node.sources = null;
|
|
1290
|
+
node.sourcesTail = null;
|
|
1291
|
+
}
|
|
1292
|
+
return changes.changed;
|
|
1293
|
+
};
|
|
1294
|
+
for (const key of Object.keys(initialValue))
|
|
1295
|
+
addSignal(key, initialValue[key]);
|
|
1296
|
+
const store = {
|
|
1297
|
+
[Symbol.toStringTag]: TYPE_STORE,
|
|
1298
|
+
[Symbol.isConcatSpreadable]: false,
|
|
1299
|
+
*[Symbol.iterator]() {
|
|
1300
|
+
for (const key of Array.from(signals.keys())) {
|
|
1301
|
+
const signal = signals.get(key);
|
|
1302
|
+
if (signal)
|
|
1303
|
+
yield [key, signal];
|
|
1304
|
+
}
|
|
1305
|
+
},
|
|
1306
|
+
keys() {
|
|
1307
|
+
if (activeSink) {
|
|
1308
|
+
if (!node.sinks && options?.watched)
|
|
1309
|
+
node.stop = options.watched();
|
|
1310
|
+
link(node, activeSink);
|
|
1311
|
+
}
|
|
1312
|
+
return signals.keys();
|
|
1313
|
+
},
|
|
1314
|
+
byKey(key) {
|
|
1315
|
+
return signals.get(key);
|
|
1316
|
+
},
|
|
1317
|
+
get() {
|
|
1318
|
+
if (activeSink) {
|
|
1319
|
+
if (!node.sinks && options?.watched)
|
|
1320
|
+
node.stop = options.watched();
|
|
1321
|
+
link(node, activeSink);
|
|
1322
|
+
}
|
|
1323
|
+
if (node.sources) {
|
|
1324
|
+
if (node.flags) {
|
|
1325
|
+
node.value = untrack(buildValue);
|
|
1326
|
+
node.flags = FLAG_CLEAN;
|
|
1327
|
+
}
|
|
1328
|
+
} else {
|
|
1329
|
+
refresh(node);
|
|
1330
|
+
if (node.error)
|
|
1331
|
+
throw node.error;
|
|
1332
|
+
}
|
|
1333
|
+
return node.value;
|
|
1334
|
+
},
|
|
1335
|
+
set(newValue) {
|
|
1336
|
+
const currentValue = node.flags & FLAG_DIRTY ? buildValue() : node.value;
|
|
1337
|
+
const changes = diffRecords(currentValue, newValue);
|
|
1338
|
+
if (applyChanges(changes)) {
|
|
1339
|
+
propagate(node);
|
|
1340
|
+
node.flags |= FLAG_DIRTY;
|
|
1341
|
+
if (batchDepth === 0)
|
|
1342
|
+
flush();
|
|
1343
|
+
}
|
|
1344
|
+
},
|
|
1345
|
+
update(fn) {
|
|
1346
|
+
store.set(fn(store.get()));
|
|
1347
|
+
},
|
|
1348
|
+
add(key, value) {
|
|
1349
|
+
if (signals.has(key))
|
|
1350
|
+
throw new DuplicateKeyError(TYPE_STORE, key, value);
|
|
1351
|
+
addSignal(key, value);
|
|
1352
|
+
node.sources = null;
|
|
1353
|
+
node.sourcesTail = null;
|
|
1354
|
+
propagate(node);
|
|
1355
|
+
node.flags |= FLAG_DIRTY;
|
|
1356
|
+
if (batchDepth === 0)
|
|
1357
|
+
flush();
|
|
1358
|
+
return key;
|
|
1359
|
+
},
|
|
1360
|
+
remove(key) {
|
|
1361
|
+
const ok = signals.delete(key);
|
|
1362
|
+
if (ok) {
|
|
1363
|
+
node.sources = null;
|
|
1364
|
+
node.sourcesTail = null;
|
|
1365
|
+
propagate(node);
|
|
1366
|
+
node.flags |= FLAG_DIRTY;
|
|
1367
|
+
if (batchDepth === 0)
|
|
1368
|
+
flush();
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
return new Proxy(store, {
|
|
864
1373
|
get(target, prop) {
|
|
865
1374
|
if (prop in target) {
|
|
866
1375
|
const value = Reflect.get(target, prop);
|
|
867
1376
|
return isFunction(value) ? value.bind(target) : value;
|
|
868
1377
|
}
|
|
869
|
-
if (
|
|
1378
|
+
if (typeof prop !== "symbol")
|
|
870
1379
|
return target.byKey(prop);
|
|
871
1380
|
},
|
|
872
1381
|
has(target, prop) {
|
|
@@ -880,7 +1389,7 @@ var createStore = (initialValue) => {
|
|
|
880
1389
|
getOwnPropertyDescriptor(target, prop) {
|
|
881
1390
|
if (prop in target)
|
|
882
1391
|
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
883
|
-
if (
|
|
1392
|
+
if (typeof prop === "symbol")
|
|
884
1393
|
return;
|
|
885
1394
|
const signal = target.byKey(String(prop));
|
|
886
1395
|
return signal ? {
|
|
@@ -891,453 +1400,96 @@ var createStore = (initialValue) => {
|
|
|
891
1400
|
} : undefined;
|
|
892
1401
|
}
|
|
893
1402
|
});
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
|
|
1403
|
+
}
|
|
1404
|
+
function isStore(value) {
|
|
1405
|
+
return isObjectOfType(value, TYPE_STORE);
|
|
1406
|
+
}
|
|
897
1407
|
// src/signal.ts
|
|
898
|
-
|
|
899
|
-
|
|
1408
|
+
function createComputed(callback, options) {
|
|
1409
|
+
return isAsyncFunction(callback) ? createTask(callback, options) : createMemo(callback, options);
|
|
1410
|
+
}
|
|
900
1411
|
function createSignal(value) {
|
|
901
|
-
if (
|
|
902
|
-
return
|
|
903
|
-
if (
|
|
904
|
-
|
|
1412
|
+
if (isSignal(value))
|
|
1413
|
+
return value;
|
|
1414
|
+
if (value == null)
|
|
1415
|
+
throw new InvalidSignalValueError("createSignal", value);
|
|
1416
|
+
if (isAsyncFunction(value))
|
|
1417
|
+
return createTask(value);
|
|
1418
|
+
if (isFunction(value))
|
|
1419
|
+
return createMemo(value);
|
|
905
1420
|
if (isUniformArray(value))
|
|
906
|
-
return
|
|
1421
|
+
return createList(value);
|
|
907
1422
|
if (isRecord(value))
|
|
908
1423
|
return createStore(value);
|
|
909
|
-
return
|
|
1424
|
+
return createState(value);
|
|
910
1425
|
}
|
|
911
1426
|
function createMutableSignal(value) {
|
|
1427
|
+
if (isMutableSignal(value))
|
|
1428
|
+
return value;
|
|
1429
|
+
if (value == null || isFunction(value) || isSignal(value))
|
|
1430
|
+
throw new InvalidSignalValueError("createMutableSignal", value);
|
|
912
1431
|
if (isUniformArray(value))
|
|
913
|
-
return
|
|
1432
|
+
return createList(value);
|
|
914
1433
|
if (isRecord(value))
|
|
915
1434
|
return createStore(value);
|
|
916
|
-
return
|
|
1435
|
+
return createState(value);
|
|
917
1436
|
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
class CircularDependencyError extends Error {
|
|
921
|
-
constructor(where) {
|
|
922
|
-
super(`Circular dependency detected in ${where}`);
|
|
923
|
-
this.name = "CircularDependencyError";
|
|
924
|
-
}
|
|
1437
|
+
function isComputed(value) {
|
|
1438
|
+
return isMemo(value) || isTask(value);
|
|
925
1439
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
}
|
|
1440
|
+
function isSignal(value) {
|
|
1441
|
+
const signalsTypes = [
|
|
1442
|
+
TYPE_STATE,
|
|
1443
|
+
TYPE_MEMO,
|
|
1444
|
+
TYPE_TASK,
|
|
1445
|
+
TYPE_SENSOR,
|
|
1446
|
+
TYPE_LIST,
|
|
1447
|
+
TYPE_COLLECTION,
|
|
1448
|
+
TYPE_STORE
|
|
1449
|
+
];
|
|
1450
|
+
const typeStyle = Object.prototype.toString.call(value).slice(8, -1);
|
|
1451
|
+
return signalsTypes.includes(typeStyle);
|
|
939
1452
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
constructor(where, value) {
|
|
943
|
-
super(`Invalid ${where} source ${valueString(value)}`);
|
|
944
|
-
this.name = "InvalidCollectionSourceError";
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
class InvalidHookError extends TypeError {
|
|
949
|
-
constructor(where, type) {
|
|
950
|
-
super(`Invalid hook "${type}" in ${where}`);
|
|
951
|
-
this.name = "InvalidHookError";
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
class InvalidSignalValueError extends TypeError {
|
|
956
|
-
constructor(where, value) {
|
|
957
|
-
super(`Invalid signal value ${valueString(value)} in ${where}`);
|
|
958
|
-
this.name = "InvalidSignalValueError";
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
class NullishSignalValueError extends TypeError {
|
|
963
|
-
constructor(where) {
|
|
964
|
-
super(`Nullish signal values are not allowed in ${where}`);
|
|
965
|
-
this.name = "NullishSignalValueError";
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
class ReadonlySignalError extends Error {
|
|
970
|
-
constructor(what, value) {
|
|
971
|
-
super(`Could not set ${what} to ${valueString(value)} because signal is read-only`);
|
|
972
|
-
this.name = "ReadonlySignalError";
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
var createError = (reason) => reason instanceof Error ? reason : Error(String(reason));
|
|
976
|
-
var validateCallback = (where, value, guard = isFunction) => {
|
|
977
|
-
if (!guard(value))
|
|
978
|
-
throw new InvalidCallbackError(where, value);
|
|
979
|
-
};
|
|
980
|
-
var validateSignalValue = (where, value, guard = () => !(isSymbol(value) && value !== UNSET) || isFunction(value)) => {
|
|
981
|
-
if (value == null)
|
|
982
|
-
throw new NullishSignalValueError(where);
|
|
983
|
-
if (!guard(value))
|
|
984
|
-
throw new InvalidSignalValueError(where, value);
|
|
985
|
-
};
|
|
986
|
-
var guardMutableSignal = (what, value, signal) => {
|
|
987
|
-
if (!isMutableSignal(signal))
|
|
988
|
-
throw new ReadonlySignalError(what, value);
|
|
989
|
-
return true;
|
|
990
|
-
};
|
|
991
|
-
|
|
992
|
-
// src/classes/collection.ts
|
|
993
|
-
var TYPE_COLLECTION = "Collection";
|
|
994
|
-
|
|
995
|
-
class DerivedCollection {
|
|
996
|
-
#watchers = new Set;
|
|
997
|
-
#source;
|
|
998
|
-
#callback;
|
|
999
|
-
#signals = new Map;
|
|
1000
|
-
#ownWatchers = new Map;
|
|
1001
|
-
#hookCallbacks = {};
|
|
1002
|
-
#order = [];
|
|
1003
|
-
constructor(source, callback) {
|
|
1004
|
-
validateCallback(TYPE_COLLECTION, callback);
|
|
1005
|
-
if (isFunction(source))
|
|
1006
|
-
source = source();
|
|
1007
|
-
if (!isCollectionSource(source))
|
|
1008
|
-
throw new InvalidCollectionSourceError(TYPE_COLLECTION, source);
|
|
1009
|
-
this.#source = source;
|
|
1010
|
-
this.#callback = callback;
|
|
1011
|
-
for (let i = 0;i < this.#source.length; i++) {
|
|
1012
|
-
const key = this.#source.keyAt(i);
|
|
1013
|
-
if (!key)
|
|
1014
|
-
continue;
|
|
1015
|
-
this.#add(key);
|
|
1016
|
-
}
|
|
1017
|
-
this.#source.on(HOOK_ADD, (additions) => {
|
|
1018
|
-
if (!additions)
|
|
1019
|
-
return;
|
|
1020
|
-
for (const key of additions) {
|
|
1021
|
-
if (!this.#signals.has(key)) {
|
|
1022
|
-
this.#add(key);
|
|
1023
|
-
const signal = this.#signals.get(key);
|
|
1024
|
-
if (signal && isAsyncCollectionCallback(this.#callback))
|
|
1025
|
-
signal.get();
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
notifyWatchers(this.#watchers);
|
|
1029
|
-
triggerHook(this.#hookCallbacks.add, additions);
|
|
1030
|
-
});
|
|
1031
|
-
this.#source.on(HOOK_REMOVE, (removals) => {
|
|
1032
|
-
if (!removals)
|
|
1033
|
-
return;
|
|
1034
|
-
for (const key of removals) {
|
|
1035
|
-
if (!this.#signals.has(key))
|
|
1036
|
-
continue;
|
|
1037
|
-
this.#signals.delete(key);
|
|
1038
|
-
const index = this.#order.indexOf(key);
|
|
1039
|
-
if (index >= 0)
|
|
1040
|
-
this.#order.splice(index, 1);
|
|
1041
|
-
const watcher = this.#ownWatchers.get(key);
|
|
1042
|
-
if (watcher) {
|
|
1043
|
-
watcher.stop();
|
|
1044
|
-
this.#ownWatchers.delete(key);
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
this.#order = this.#order.filter(() => true);
|
|
1048
|
-
notifyWatchers(this.#watchers);
|
|
1049
|
-
triggerHook(this.#hookCallbacks.remove, removals);
|
|
1050
|
-
});
|
|
1051
|
-
this.#source.on(HOOK_SORT, (newOrder) => {
|
|
1052
|
-
if (newOrder)
|
|
1053
|
-
this.#order = [...newOrder];
|
|
1054
|
-
notifyWatchers(this.#watchers);
|
|
1055
|
-
triggerHook(this.#hookCallbacks.sort, newOrder);
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
|
-
#add(key) {
|
|
1059
|
-
const computedCallback = isAsyncCollectionCallback(this.#callback) ? async (_, abort) => {
|
|
1060
|
-
const sourceValue = this.#source.byKey(key)?.get();
|
|
1061
|
-
if (sourceValue === UNSET)
|
|
1062
|
-
return UNSET;
|
|
1063
|
-
return this.#callback(sourceValue, abort);
|
|
1064
|
-
} : () => {
|
|
1065
|
-
const sourceValue = this.#source.byKey(key)?.get();
|
|
1066
|
-
if (sourceValue === UNSET)
|
|
1067
|
-
return UNSET;
|
|
1068
|
-
return this.#callback(sourceValue);
|
|
1069
|
-
};
|
|
1070
|
-
const signal = createComputed(computedCallback);
|
|
1071
|
-
this.#signals.set(key, signal);
|
|
1072
|
-
if (!this.#order.includes(key))
|
|
1073
|
-
this.#order.push(key);
|
|
1074
|
-
if (this.#hookCallbacks.change?.size)
|
|
1075
|
-
this.#addWatcher(key);
|
|
1076
|
-
return true;
|
|
1077
|
-
}
|
|
1078
|
-
#addWatcher(key) {
|
|
1079
|
-
const watcher = createWatcher(() => {
|
|
1080
|
-
trackSignalReads(watcher, () => {
|
|
1081
|
-
this.#signals.get(key)?.get();
|
|
1082
|
-
});
|
|
1083
|
-
});
|
|
1084
|
-
this.#ownWatchers.set(key, watcher);
|
|
1085
|
-
watcher();
|
|
1086
|
-
}
|
|
1087
|
-
get [Symbol.toStringTag]() {
|
|
1088
|
-
return TYPE_COLLECTION;
|
|
1089
|
-
}
|
|
1090
|
-
get [Symbol.isConcatSpreadable]() {
|
|
1091
|
-
return true;
|
|
1092
|
-
}
|
|
1093
|
-
*[Symbol.iterator]() {
|
|
1094
|
-
for (const key of this.#order) {
|
|
1095
|
-
const signal = this.#signals.get(key);
|
|
1096
|
-
if (signal)
|
|
1097
|
-
yield signal;
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
keys() {
|
|
1101
|
-
return this.#order.values();
|
|
1102
|
-
}
|
|
1103
|
-
get() {
|
|
1104
|
-
subscribeActiveWatcher(this.#watchers, this.#hookCallbacks[HOOK_WATCH]);
|
|
1105
|
-
return this.#order.map((key) => this.#signals.get(key)?.get()).filter((v) => v != null && v !== UNSET);
|
|
1106
|
-
}
|
|
1107
|
-
at(index) {
|
|
1108
|
-
return this.#signals.get(this.#order[index]);
|
|
1109
|
-
}
|
|
1110
|
-
byKey(key) {
|
|
1111
|
-
return this.#signals.get(key);
|
|
1112
|
-
}
|
|
1113
|
-
keyAt(index) {
|
|
1114
|
-
return this.#order[index];
|
|
1115
|
-
}
|
|
1116
|
-
indexOfKey(key) {
|
|
1117
|
-
return this.#order.indexOf(key);
|
|
1118
|
-
}
|
|
1119
|
-
on(type, callback) {
|
|
1120
|
-
if (isHandledHook(type, [
|
|
1121
|
-
HOOK_ADD,
|
|
1122
|
-
HOOK_CHANGE,
|
|
1123
|
-
HOOK_REMOVE,
|
|
1124
|
-
HOOK_SORT,
|
|
1125
|
-
HOOK_WATCH
|
|
1126
|
-
])) {
|
|
1127
|
-
this.#hookCallbacks[type] ||= new Set;
|
|
1128
|
-
this.#hookCallbacks[type].add(callback);
|
|
1129
|
-
if (type === HOOK_CHANGE && !this.#ownWatchers.size) {
|
|
1130
|
-
for (const key of this.#signals.keys())
|
|
1131
|
-
this.#addWatcher(key);
|
|
1132
|
-
}
|
|
1133
|
-
return () => {
|
|
1134
|
-
this.#hookCallbacks[type]?.delete(callback);
|
|
1135
|
-
if (type === HOOK_CHANGE && !this.#hookCallbacks.change?.size) {
|
|
1136
|
-
if (this.#ownWatchers.size) {
|
|
1137
|
-
for (const watcher of this.#ownWatchers.values())
|
|
1138
|
-
watcher.stop();
|
|
1139
|
-
this.#ownWatchers.clear();
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
};
|
|
1143
|
-
}
|
|
1144
|
-
throw new InvalidHookError(TYPE_COLLECTION, type);
|
|
1145
|
-
}
|
|
1146
|
-
deriveCollection(callback) {
|
|
1147
|
-
return new DerivedCollection(this, callback);
|
|
1148
|
-
}
|
|
1149
|
-
get length() {
|
|
1150
|
-
subscribeActiveWatcher(this.#watchers, this.#hookCallbacks[HOOK_WATCH]);
|
|
1151
|
-
return this.#order.length;
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
var isCollection = (value) => isObjectOfType(value, TYPE_COLLECTION);
|
|
1155
|
-
var isCollectionSource = (value) => isList(value) || isCollection(value);
|
|
1156
|
-
var isAsyncCollectionCallback = (callback) => isAsyncFunction(callback);
|
|
1157
|
-
// src/classes/ref.ts
|
|
1158
|
-
var TYPE_REF = "Ref";
|
|
1159
|
-
|
|
1160
|
-
class Ref {
|
|
1161
|
-
#watchers = new Set;
|
|
1162
|
-
#value;
|
|
1163
|
-
#watchHookCallbacks;
|
|
1164
|
-
constructor(value, guard) {
|
|
1165
|
-
validateSignalValue(TYPE_REF, value, guard);
|
|
1166
|
-
this.#value = value;
|
|
1167
|
-
}
|
|
1168
|
-
get [Symbol.toStringTag]() {
|
|
1169
|
-
return TYPE_REF;
|
|
1170
|
-
}
|
|
1171
|
-
get() {
|
|
1172
|
-
subscribeActiveWatcher(this.#watchers, this.#watchHookCallbacks);
|
|
1173
|
-
return this.#value;
|
|
1174
|
-
}
|
|
1175
|
-
notify() {
|
|
1176
|
-
notifyWatchers(this.#watchers);
|
|
1177
|
-
}
|
|
1178
|
-
on(type, callback) {
|
|
1179
|
-
if (type === HOOK_WATCH) {
|
|
1180
|
-
this.#watchHookCallbacks ||= new Set;
|
|
1181
|
-
this.#watchHookCallbacks.add(callback);
|
|
1182
|
-
return () => {
|
|
1183
|
-
this.#watchHookCallbacks?.delete(callback);
|
|
1184
|
-
};
|
|
1185
|
-
}
|
|
1186
|
-
throw new InvalidHookError(TYPE_REF, type);
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
var isRef = (value) => isObjectOfType(value, TYPE_REF);
|
|
1190
|
-
// src/effect.ts
|
|
1191
|
-
var createEffect = (callback) => {
|
|
1192
|
-
if (!isFunction(callback) || callback.length > 1)
|
|
1193
|
-
throw new InvalidCallbackError("effect", callback);
|
|
1194
|
-
const isAsync = isAsyncFunction(callback);
|
|
1195
|
-
let running = false;
|
|
1196
|
-
let controller;
|
|
1197
|
-
const watcher = createWatcher(() => trackSignalReads(watcher, () => {
|
|
1198
|
-
if (running)
|
|
1199
|
-
throw new CircularDependencyError("effect");
|
|
1200
|
-
running = true;
|
|
1201
|
-
controller?.abort();
|
|
1202
|
-
controller = undefined;
|
|
1203
|
-
let cleanup;
|
|
1204
|
-
try {
|
|
1205
|
-
if (isAsync) {
|
|
1206
|
-
controller = new AbortController;
|
|
1207
|
-
const currentController = controller;
|
|
1208
|
-
callback(controller.signal).then((cleanup2) => {
|
|
1209
|
-
if (isFunction(cleanup2) && controller === currentController)
|
|
1210
|
-
watcher.on(HOOK_CLEANUP, cleanup2);
|
|
1211
|
-
}).catch((error) => {
|
|
1212
|
-
if (!isAbortError(error))
|
|
1213
|
-
console.error("Error in async effect callback:", error);
|
|
1214
|
-
});
|
|
1215
|
-
} else {
|
|
1216
|
-
cleanup = callback();
|
|
1217
|
-
if (isFunction(cleanup))
|
|
1218
|
-
watcher.on(HOOK_CLEANUP, cleanup);
|
|
1219
|
-
}
|
|
1220
|
-
} catch (error) {
|
|
1221
|
-
if (!isAbortError(error))
|
|
1222
|
-
console.error("Error in effect callback:", error);
|
|
1223
|
-
}
|
|
1224
|
-
running = false;
|
|
1225
|
-
}));
|
|
1226
|
-
watcher();
|
|
1227
|
-
return () => {
|
|
1228
|
-
controller?.abort();
|
|
1229
|
-
try {
|
|
1230
|
-
watcher.stop();
|
|
1231
|
-
} catch (error) {
|
|
1232
|
-
console.error("Error in effect cleanup:", error);
|
|
1233
|
-
}
|
|
1234
|
-
};
|
|
1235
|
-
};
|
|
1236
|
-
// src/match.ts
|
|
1237
|
-
function match(result, handlers) {
|
|
1238
|
-
try {
|
|
1239
|
-
if (result.pending)
|
|
1240
|
-
handlers.nil?.();
|
|
1241
|
-
else if (result.errors)
|
|
1242
|
-
handlers.err?.(result.errors);
|
|
1243
|
-
else if (result.ok)
|
|
1244
|
-
handlers.ok(result.values);
|
|
1245
|
-
} catch (e) {
|
|
1246
|
-
const error = createError(e);
|
|
1247
|
-
if (handlers.err && (!result.errors || !result.errors.includes(error)))
|
|
1248
|
-
handlers.err(result.errors ? [...result.errors, error] : [error]);
|
|
1249
|
-
else
|
|
1250
|
-
throw error;
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
// src/resolve.ts
|
|
1254
|
-
function resolve(signals) {
|
|
1255
|
-
const errors = [];
|
|
1256
|
-
let pending = false;
|
|
1257
|
-
const values = {};
|
|
1258
|
-
for (const [key, signal] of Object.entries(signals)) {
|
|
1259
|
-
try {
|
|
1260
|
-
const value = signal.get();
|
|
1261
|
-
if (value === UNSET)
|
|
1262
|
-
pending = true;
|
|
1263
|
-
else
|
|
1264
|
-
values[key] = value;
|
|
1265
|
-
} catch (e) {
|
|
1266
|
-
errors.push(createError(e));
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
if (pending)
|
|
1270
|
-
return { ok: false, pending: true };
|
|
1271
|
-
if (errors.length > 0)
|
|
1272
|
-
return { ok: false, errors };
|
|
1273
|
-
return { ok: true, values };
|
|
1453
|
+
function isMutableSignal(value) {
|
|
1454
|
+
return isState(value) || isStore(value) || isList(value);
|
|
1274
1455
|
}
|
|
1275
1456
|
export {
|
|
1276
1457
|
valueString,
|
|
1277
|
-
|
|
1278
|
-
validateCallback,
|
|
1279
|
-
triggerHook,
|
|
1280
|
-
trackSignalReads,
|
|
1281
|
-
subscribeActiveWatcher,
|
|
1282
|
-
resolve,
|
|
1283
|
-
notifyWatchers,
|
|
1458
|
+
untrack,
|
|
1284
1459
|
match,
|
|
1285
|
-
|
|
1286
|
-
isSymbol,
|
|
1287
|
-
isString,
|
|
1460
|
+
isTask,
|
|
1288
1461
|
isStore,
|
|
1289
1462
|
isState,
|
|
1290
1463
|
isSignal,
|
|
1291
|
-
|
|
1292
|
-
isRecordOrArray,
|
|
1464
|
+
isSensor,
|
|
1293
1465
|
isRecord,
|
|
1294
1466
|
isObjectOfType,
|
|
1295
|
-
isNumber,
|
|
1296
1467
|
isMutableSignal,
|
|
1297
|
-
|
|
1468
|
+
isMemo,
|
|
1298
1469
|
isList,
|
|
1299
|
-
isHandledHook,
|
|
1300
1470
|
isFunction,
|
|
1301
1471
|
isEqual,
|
|
1302
1472
|
isComputed,
|
|
1303
1473
|
isCollection,
|
|
1304
1474
|
isAsyncFunction,
|
|
1305
|
-
|
|
1306
|
-
guardMutableSignal,
|
|
1307
|
-
flushPendingReactions,
|
|
1308
|
-
diff,
|
|
1309
|
-
createWatcher,
|
|
1475
|
+
createTask,
|
|
1310
1476
|
createStore,
|
|
1477
|
+
createState,
|
|
1311
1478
|
createSignal,
|
|
1312
|
-
|
|
1479
|
+
createSensor,
|
|
1480
|
+
createScope,
|
|
1481
|
+
createMutableSignal,
|
|
1482
|
+
createMemo,
|
|
1483
|
+
createList,
|
|
1313
1484
|
createEffect,
|
|
1314
1485
|
createComputed,
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
TYPE_REF,
|
|
1321
|
-
TYPE_LIST,
|
|
1322
|
-
TYPE_COMPUTED,
|
|
1323
|
-
TYPE_COLLECTION,
|
|
1324
|
-
State,
|
|
1325
|
-
Ref,
|
|
1326
|
-
ReadonlySignalError,
|
|
1486
|
+
createCollection,
|
|
1487
|
+
batch,
|
|
1488
|
+
UnsetSignalValueError,
|
|
1489
|
+
SKIP_EQUALITY,
|
|
1490
|
+
RequiredOwnerError,
|
|
1327
1491
|
NullishSignalValueError,
|
|
1328
|
-
Memo,
|
|
1329
|
-
List,
|
|
1330
1492
|
InvalidSignalValueError,
|
|
1331
|
-
InvalidCollectionSourceError,
|
|
1332
1493
|
InvalidCallbackError,
|
|
1333
|
-
|
|
1334
|
-
HOOK_SORT,
|
|
1335
|
-
HOOK_REMOVE,
|
|
1336
|
-
HOOK_CLEANUP,
|
|
1337
|
-
HOOK_CHANGE,
|
|
1338
|
-
HOOK_ADD,
|
|
1339
|
-
DuplicateKeyError,
|
|
1340
|
-
DerivedCollection,
|
|
1341
|
-
CircularDependencyError,
|
|
1342
|
-
BaseStore
|
|
1494
|
+
CircularDependencyError
|
|
1343
1495
|
};
|