@zeix/cause-effect 0.14.1 → 0.15.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/README.md +256 -27
- package/biome.json +35 -0
- package/index.d.ts +32 -7
- package/index.dev.js +629 -0
- package/index.js +1 -1
- package/index.ts +41 -21
- package/package.json +6 -7
- package/src/computed.ts +30 -21
- package/src/diff.ts +136 -0
- package/src/effect.ts +59 -49
- package/src/match.ts +57 -0
- package/src/resolve.ts +58 -0
- package/src/scheduler.ts +3 -3
- package/src/signal.ts +48 -15
- package/src/state.ts +4 -3
- package/src/store.ts +325 -0
- package/src/util.ts +57 -5
- package/test/batch.test.ts +29 -25
- package/test/benchmark.test.ts +81 -45
- package/test/computed.test.ts +43 -39
- package/test/diff.test.ts +638 -0
- package/test/effect.test.ts +657 -49
- package/test/match.test.ts +378 -0
- package/test/resolve.test.ts +156 -0
- package/test/state.test.ts +33 -33
- package/test/store.test.ts +719 -0
- package/test/util/framework-types.ts +2 -2
- package/test/util/perf-tests.ts +2 -2
- package/test/util/reactive-framework.ts +1 -1
- package/tsconfig.json +9 -10
- package/types/index.d.ts +15 -0
- package/{src → types/src}/computed.d.ts +2 -2
- package/types/src/diff.d.ts +27 -0
- package/types/src/effect.d.ts +16 -0
- package/types/src/match.d.ts +21 -0
- package/types/src/resolve.d.ts +29 -0
- package/{src → types/src}/scheduler.d.ts +2 -2
- package/types/src/signal.d.ts +40 -0
- package/{src → types/src}/state.d.ts +1 -1
- package/types/src/store.d.ts +57 -0
- package/types/src/util.d.ts +15 -0
- package/types/test-new-effect.d.ts +1 -0
- package/src/effect.d.ts +0 -17
- package/src/signal.d.ts +0 -26
- package/src/util.d.ts +0 -7
package/index.dev.js
ADDED
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
// src/scheduler.ts
|
|
2
|
+
var active;
|
|
3
|
+
var pending = new Set;
|
|
4
|
+
var batchDepth = 0;
|
|
5
|
+
var updateMap = new Map;
|
|
6
|
+
var requestId;
|
|
7
|
+
var updateDOM = () => {
|
|
8
|
+
requestId = undefined;
|
|
9
|
+
const updates = Array.from(updateMap.values());
|
|
10
|
+
updateMap.clear();
|
|
11
|
+
for (const update of updates) {
|
|
12
|
+
update();
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var requestTick = () => {
|
|
16
|
+
if (requestId)
|
|
17
|
+
cancelAnimationFrame(requestId);
|
|
18
|
+
requestId = requestAnimationFrame(updateDOM);
|
|
19
|
+
};
|
|
20
|
+
queueMicrotask(updateDOM);
|
|
21
|
+
var watch = (notice) => {
|
|
22
|
+
const cleanups = new Set;
|
|
23
|
+
const w = notice;
|
|
24
|
+
w.off = (on) => {
|
|
25
|
+
cleanups.add(on);
|
|
26
|
+
};
|
|
27
|
+
w.cleanup = () => {
|
|
28
|
+
for (const cleanup of cleanups) {
|
|
29
|
+
cleanup();
|
|
30
|
+
}
|
|
31
|
+
cleanups.clear();
|
|
32
|
+
};
|
|
33
|
+
return w;
|
|
34
|
+
};
|
|
35
|
+
var subscribe = (watchers) => {
|
|
36
|
+
if (active && !watchers.has(active)) {
|
|
37
|
+
const watcher = active;
|
|
38
|
+
watchers.add(watcher);
|
|
39
|
+
active.off(() => {
|
|
40
|
+
watchers.delete(watcher);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var notify = (watchers) => {
|
|
45
|
+
for (const watcher of watchers) {
|
|
46
|
+
if (batchDepth)
|
|
47
|
+
pending.add(watcher);
|
|
48
|
+
else
|
|
49
|
+
watcher();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var flush = () => {
|
|
53
|
+
while (pending.size) {
|
|
54
|
+
const watchers = Array.from(pending);
|
|
55
|
+
pending.clear();
|
|
56
|
+
for (const watcher of watchers) {
|
|
57
|
+
watcher();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var batch = (fn) => {
|
|
62
|
+
batchDepth++;
|
|
63
|
+
try {
|
|
64
|
+
fn();
|
|
65
|
+
} finally {
|
|
66
|
+
flush();
|
|
67
|
+
batchDepth--;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var observe = (run, watcher) => {
|
|
71
|
+
const prev = active;
|
|
72
|
+
active = watcher;
|
|
73
|
+
try {
|
|
74
|
+
run();
|
|
75
|
+
} finally {
|
|
76
|
+
active = prev;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var enqueue = (fn, dedupe) => new Promise((resolve, reject) => {
|
|
80
|
+
updateMap.set(dedupe || Symbol(), () => {
|
|
81
|
+
try {
|
|
82
|
+
resolve(fn());
|
|
83
|
+
} catch (error) {
|
|
84
|
+
reject(error);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
requestTick();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// src/util.ts
|
|
91
|
+
var isFunction = (fn) => typeof fn === "function";
|
|
92
|
+
var isAsyncFunction = (fn) => isFunction(fn) && fn.constructor.name === "AsyncFunction";
|
|
93
|
+
var isObjectOfType = (value, type) => Object.prototype.toString.call(value) === `[object ${type}]`;
|
|
94
|
+
var isRecord = (value) => isObjectOfType(value, "Object");
|
|
95
|
+
var arrayToRecord = (array) => {
|
|
96
|
+
const record = {};
|
|
97
|
+
for (let i = 0;i < array.length; i++) {
|
|
98
|
+
if (i in array)
|
|
99
|
+
record[String(i)] = array[i];
|
|
100
|
+
}
|
|
101
|
+
return record;
|
|
102
|
+
};
|
|
103
|
+
var hasMethod = (obj, methodName) => (methodName in obj) && isFunction(obj[methodName]);
|
|
104
|
+
var isAbortError = (error) => error instanceof DOMException && error.name === "AbortError";
|
|
105
|
+
var toError = (reason) => reason instanceof Error ? reason : Error(String(reason));
|
|
106
|
+
|
|
107
|
+
class CircularDependencyError extends Error {
|
|
108
|
+
constructor(where) {
|
|
109
|
+
super(`Circular dependency in ${where} detected`);
|
|
110
|
+
this.name = "CircularDependencyError";
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/state.ts
|
|
115
|
+
var TYPE_STATE = "State";
|
|
116
|
+
var state = (initialValue) => {
|
|
117
|
+
const watchers = new Set;
|
|
118
|
+
let value = initialValue;
|
|
119
|
+
const s = {
|
|
120
|
+
[Symbol.toStringTag]: TYPE_STATE,
|
|
121
|
+
get: () => {
|
|
122
|
+
subscribe(watchers);
|
|
123
|
+
return value;
|
|
124
|
+
},
|
|
125
|
+
set: (v) => {
|
|
126
|
+
if (isEqual(value, v))
|
|
127
|
+
return;
|
|
128
|
+
value = v;
|
|
129
|
+
notify(watchers);
|
|
130
|
+
if (UNSET === value)
|
|
131
|
+
watchers.clear();
|
|
132
|
+
},
|
|
133
|
+
update: (fn) => {
|
|
134
|
+
s.set(fn(value));
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
return s;
|
|
138
|
+
};
|
|
139
|
+
var isState = (value) => isObjectOfType(value, TYPE_STATE);
|
|
140
|
+
|
|
141
|
+
// src/effect.ts
|
|
142
|
+
var effect = (callback) => {
|
|
143
|
+
const isAsync = isAsyncFunction(callback);
|
|
144
|
+
let running = false;
|
|
145
|
+
let controller;
|
|
146
|
+
const run = watch(() => observe(() => {
|
|
147
|
+
if (running)
|
|
148
|
+
throw new CircularDependencyError("effect");
|
|
149
|
+
running = true;
|
|
150
|
+
controller?.abort();
|
|
151
|
+
controller = undefined;
|
|
152
|
+
let cleanup;
|
|
153
|
+
try {
|
|
154
|
+
if (isAsync) {
|
|
155
|
+
controller = new AbortController;
|
|
156
|
+
const currentController = controller;
|
|
157
|
+
callback(controller.signal).then((cleanup2) => {
|
|
158
|
+
if (isFunction(cleanup2) && controller === currentController) {
|
|
159
|
+
run.off(cleanup2);
|
|
160
|
+
}
|
|
161
|
+
}).catch((error) => {
|
|
162
|
+
if (!isAbortError(error))
|
|
163
|
+
console.error("Async effect error:", error);
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
cleanup = callback();
|
|
167
|
+
if (isFunction(cleanup))
|
|
168
|
+
run.off(cleanup);
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
if (!isAbortError(error))
|
|
172
|
+
console.error("Effect callback error:", error);
|
|
173
|
+
}
|
|
174
|
+
running = false;
|
|
175
|
+
}, run));
|
|
176
|
+
run();
|
|
177
|
+
return () => {
|
|
178
|
+
controller?.abort();
|
|
179
|
+
run.cleanup();
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// src/store.ts
|
|
184
|
+
var TYPE_STORE = "Store";
|
|
185
|
+
var store = (initialValue) => {
|
|
186
|
+
const watchers = new Set;
|
|
187
|
+
const eventTarget = new EventTarget;
|
|
188
|
+
const signals = new Map;
|
|
189
|
+
const cleanups = new Map;
|
|
190
|
+
const size = state(0);
|
|
191
|
+
const current = () => {
|
|
192
|
+
const record = {};
|
|
193
|
+
for (const [key, value] of signals) {
|
|
194
|
+
record[key] = value.get();
|
|
195
|
+
}
|
|
196
|
+
return record;
|
|
197
|
+
};
|
|
198
|
+
const emit = (type, detail) => eventTarget.dispatchEvent(new CustomEvent(type, { detail }));
|
|
199
|
+
const addSignalAndEffect = (key, value) => {
|
|
200
|
+
const signal = toMutableSignal(value);
|
|
201
|
+
signals.set(key, signal);
|
|
202
|
+
const cleanup = effect(() => {
|
|
203
|
+
const value2 = signal.get();
|
|
204
|
+
if (value2 != null)
|
|
205
|
+
emit("store-change", { [key]: value2 });
|
|
206
|
+
});
|
|
207
|
+
cleanups.set(key, cleanup);
|
|
208
|
+
};
|
|
209
|
+
const removeSignalAndEffect = (key) => {
|
|
210
|
+
signals.delete(key);
|
|
211
|
+
const cleanup = cleanups.get(key);
|
|
212
|
+
if (cleanup)
|
|
213
|
+
cleanup();
|
|
214
|
+
cleanups.delete(key);
|
|
215
|
+
};
|
|
216
|
+
const reconcile = (oldValue, newValue) => {
|
|
217
|
+
const changes = diff(oldValue, newValue);
|
|
218
|
+
batch(() => {
|
|
219
|
+
if (Object.keys(changes.add).length) {
|
|
220
|
+
for (const key in changes.add) {
|
|
221
|
+
const value = changes.add[key];
|
|
222
|
+
if (value != null)
|
|
223
|
+
addSignalAndEffect(key, value);
|
|
224
|
+
}
|
|
225
|
+
emit("store-add", changes.add);
|
|
226
|
+
}
|
|
227
|
+
if (Object.keys(changes.change).length) {
|
|
228
|
+
for (const key in changes.change) {
|
|
229
|
+
const signal = signals.get(key);
|
|
230
|
+
const value = changes.change[key];
|
|
231
|
+
if (signal && value != null && hasMethod(signal, "set"))
|
|
232
|
+
signal.set(value);
|
|
233
|
+
}
|
|
234
|
+
emit("store-change", changes.change);
|
|
235
|
+
}
|
|
236
|
+
if (Object.keys(changes.remove).length) {
|
|
237
|
+
for (const key in changes.remove) {
|
|
238
|
+
removeSignalAndEffect(key);
|
|
239
|
+
}
|
|
240
|
+
emit("store-remove", changes.remove);
|
|
241
|
+
}
|
|
242
|
+
size.set(signals.size);
|
|
243
|
+
});
|
|
244
|
+
return changes.changed;
|
|
245
|
+
};
|
|
246
|
+
reconcile({}, initialValue);
|
|
247
|
+
setTimeout(() => {
|
|
248
|
+
const initialAdditionsEvent = new CustomEvent("store-add", {
|
|
249
|
+
detail: initialValue
|
|
250
|
+
});
|
|
251
|
+
eventTarget.dispatchEvent(initialAdditionsEvent);
|
|
252
|
+
}, 0);
|
|
253
|
+
const storeProps = [
|
|
254
|
+
"add",
|
|
255
|
+
"get",
|
|
256
|
+
"remove",
|
|
257
|
+
"set",
|
|
258
|
+
"update",
|
|
259
|
+
"addEventListener",
|
|
260
|
+
"removeEventListener",
|
|
261
|
+
"dispatchEvent",
|
|
262
|
+
"size"
|
|
263
|
+
];
|
|
264
|
+
return new Proxy({}, {
|
|
265
|
+
get(_target, prop) {
|
|
266
|
+
const key = String(prop);
|
|
267
|
+
switch (prop) {
|
|
268
|
+
case "add":
|
|
269
|
+
return (k, v) => {
|
|
270
|
+
if (!signals.has(k)) {
|
|
271
|
+
addSignalAndEffect(k, v);
|
|
272
|
+
notify(watchers);
|
|
273
|
+
emit("store-add", {
|
|
274
|
+
[k]: v
|
|
275
|
+
});
|
|
276
|
+
size.set(signals.size);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
case "get":
|
|
280
|
+
return () => {
|
|
281
|
+
subscribe(watchers);
|
|
282
|
+
return current();
|
|
283
|
+
};
|
|
284
|
+
case "remove":
|
|
285
|
+
return (k) => {
|
|
286
|
+
if (signals.has(k)) {
|
|
287
|
+
removeSignalAndEffect(k);
|
|
288
|
+
notify(watchers);
|
|
289
|
+
emit("store-remove", { [k]: UNSET });
|
|
290
|
+
size.set(signals.size);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
case "set":
|
|
294
|
+
return (v) => {
|
|
295
|
+
if (reconcile(current(), v)) {
|
|
296
|
+
notify(watchers);
|
|
297
|
+
if (UNSET === v)
|
|
298
|
+
watchers.clear();
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
case "update":
|
|
302
|
+
return (fn) => {
|
|
303
|
+
const oldValue = current();
|
|
304
|
+
const newValue = fn(oldValue);
|
|
305
|
+
if (reconcile(oldValue, newValue)) {
|
|
306
|
+
notify(watchers);
|
|
307
|
+
if (UNSET === newValue)
|
|
308
|
+
watchers.clear();
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
case "addEventListener":
|
|
312
|
+
return eventTarget.addEventListener.bind(eventTarget);
|
|
313
|
+
case "removeEventListener":
|
|
314
|
+
return eventTarget.removeEventListener.bind(eventTarget);
|
|
315
|
+
case "dispatchEvent":
|
|
316
|
+
return eventTarget.dispatchEvent.bind(eventTarget);
|
|
317
|
+
case "size":
|
|
318
|
+
return size;
|
|
319
|
+
}
|
|
320
|
+
if (prop === Symbol.toStringTag)
|
|
321
|
+
return TYPE_STORE;
|
|
322
|
+
if (prop === Symbol.iterator) {
|
|
323
|
+
return function* () {
|
|
324
|
+
for (const [key2, signal] of signals) {
|
|
325
|
+
yield [key2, signal];
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
return signals.get(key);
|
|
330
|
+
},
|
|
331
|
+
has(_target, prop) {
|
|
332
|
+
const key = String(prop);
|
|
333
|
+
return signals.has(key) || storeProps.includes(key) || prop === Symbol.toStringTag || prop === Symbol.iterator;
|
|
334
|
+
},
|
|
335
|
+
ownKeys() {
|
|
336
|
+
return Array.from(signals.keys());
|
|
337
|
+
},
|
|
338
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
339
|
+
const signal = signals.get(String(prop));
|
|
340
|
+
return signal ? {
|
|
341
|
+
enumerable: true,
|
|
342
|
+
configurable: true,
|
|
343
|
+
writable: true,
|
|
344
|
+
value: signal
|
|
345
|
+
} : undefined;
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
};
|
|
349
|
+
var isStore = (value) => isObjectOfType(value, TYPE_STORE);
|
|
350
|
+
|
|
351
|
+
// src/signal.ts
|
|
352
|
+
var UNSET = Symbol();
|
|
353
|
+
var isSignal = (value) => isState(value) || isComputed(value) || isStore(value);
|
|
354
|
+
function toSignal(value) {
|
|
355
|
+
if (isSignal(value))
|
|
356
|
+
return value;
|
|
357
|
+
if (isComputedCallback(value))
|
|
358
|
+
return computed(value);
|
|
359
|
+
if (Array.isArray(value))
|
|
360
|
+
return store(arrayToRecord(value));
|
|
361
|
+
if (isRecord(value))
|
|
362
|
+
return store(value);
|
|
363
|
+
return state(value);
|
|
364
|
+
}
|
|
365
|
+
function toMutableSignal(value) {
|
|
366
|
+
if (isState(value) || isStore(value))
|
|
367
|
+
return value;
|
|
368
|
+
if (Array.isArray(value))
|
|
369
|
+
return store(arrayToRecord(value));
|
|
370
|
+
if (isRecord(value))
|
|
371
|
+
return store(value);
|
|
372
|
+
return state(value);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// src/diff.ts
|
|
376
|
+
var isEqual = (a, b, visited) => {
|
|
377
|
+
if (Object.is(a, b))
|
|
378
|
+
return true;
|
|
379
|
+
if (typeof a !== typeof b)
|
|
380
|
+
return false;
|
|
381
|
+
if (typeof a !== "object" || a === null || b === null)
|
|
382
|
+
return false;
|
|
383
|
+
if (!visited)
|
|
384
|
+
visited = new WeakSet;
|
|
385
|
+
if (visited.has(a) || visited.has(b))
|
|
386
|
+
throw new CircularDependencyError("isEqual");
|
|
387
|
+
visited.add(a);
|
|
388
|
+
visited.add(b);
|
|
389
|
+
try {
|
|
390
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
391
|
+
if (a.length !== b.length)
|
|
392
|
+
return false;
|
|
393
|
+
for (let i = 0;i < a.length; i++) {
|
|
394
|
+
if (!isEqual(a[i], b[i], visited))
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
400
|
+
return false;
|
|
401
|
+
if (isRecord(a) && isRecord(b)) {
|
|
402
|
+
const aKeys = Object.keys(a);
|
|
403
|
+
const bKeys = Object.keys(b);
|
|
404
|
+
if (aKeys.length !== bKeys.length)
|
|
405
|
+
return false;
|
|
406
|
+
for (const key of aKeys) {
|
|
407
|
+
if (!(key in b))
|
|
408
|
+
return false;
|
|
409
|
+
if (!isEqual(a[key], b[key], visited))
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
return false;
|
|
415
|
+
} finally {
|
|
416
|
+
visited.delete(a);
|
|
417
|
+
visited.delete(b);
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
var diff = (oldObj, newObj) => {
|
|
421
|
+
const visited = new WeakSet;
|
|
422
|
+
const diffRecords = (oldRecord, newRecord) => {
|
|
423
|
+
const add = {};
|
|
424
|
+
const change = {};
|
|
425
|
+
const remove = {};
|
|
426
|
+
const oldKeys = Object.keys(oldRecord);
|
|
427
|
+
const newKeys = Object.keys(newRecord);
|
|
428
|
+
const allKeys = new Set([...oldKeys, ...newKeys]);
|
|
429
|
+
for (const key of allKeys) {
|
|
430
|
+
const oldHas = key in oldRecord;
|
|
431
|
+
const newHas = key in newRecord;
|
|
432
|
+
if (!oldHas && newHas) {
|
|
433
|
+
add[key] = newRecord[key];
|
|
434
|
+
continue;
|
|
435
|
+
} else if (oldHas && !newHas) {
|
|
436
|
+
remove[key] = UNSET;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
const oldValue = oldRecord[key];
|
|
440
|
+
const newValue = newRecord[key];
|
|
441
|
+
if (!isEqual(oldValue, newValue, visited))
|
|
442
|
+
change[key] = newValue;
|
|
443
|
+
}
|
|
444
|
+
const changed = Object.keys(add).length > 0 || Object.keys(change).length > 0 || Object.keys(remove).length > 0;
|
|
445
|
+
return {
|
|
446
|
+
changed,
|
|
447
|
+
add,
|
|
448
|
+
change,
|
|
449
|
+
remove
|
|
450
|
+
};
|
|
451
|
+
};
|
|
452
|
+
return diffRecords(oldObj, newObj);
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// src/computed.ts
|
|
456
|
+
var TYPE_COMPUTED = "Computed";
|
|
457
|
+
var computed = (fn) => {
|
|
458
|
+
const watchers = new Set;
|
|
459
|
+
let value = UNSET;
|
|
460
|
+
let error;
|
|
461
|
+
let controller;
|
|
462
|
+
let dirty = true;
|
|
463
|
+
let changed = false;
|
|
464
|
+
let computing = false;
|
|
465
|
+
const ok = (v) => {
|
|
466
|
+
if (!isEqual(v, value)) {
|
|
467
|
+
value = v;
|
|
468
|
+
changed = true;
|
|
469
|
+
}
|
|
470
|
+
error = undefined;
|
|
471
|
+
dirty = false;
|
|
472
|
+
};
|
|
473
|
+
const nil = () => {
|
|
474
|
+
changed = UNSET !== value;
|
|
475
|
+
value = UNSET;
|
|
476
|
+
error = undefined;
|
|
477
|
+
};
|
|
478
|
+
const err = (e) => {
|
|
479
|
+
const newError = toError(e);
|
|
480
|
+
changed = !error || newError.name !== error.name || newError.message !== error.message;
|
|
481
|
+
value = UNSET;
|
|
482
|
+
error = newError;
|
|
483
|
+
};
|
|
484
|
+
const settle = (settleFn) => (arg) => {
|
|
485
|
+
computing = false;
|
|
486
|
+
controller = undefined;
|
|
487
|
+
settleFn(arg);
|
|
488
|
+
if (changed)
|
|
489
|
+
notify(watchers);
|
|
490
|
+
};
|
|
491
|
+
const mark = watch(() => {
|
|
492
|
+
dirty = true;
|
|
493
|
+
controller?.abort();
|
|
494
|
+
if (watchers.size)
|
|
495
|
+
notify(watchers);
|
|
496
|
+
else
|
|
497
|
+
mark.cleanup();
|
|
498
|
+
});
|
|
499
|
+
mark.off(() => {
|
|
500
|
+
controller?.abort();
|
|
501
|
+
});
|
|
502
|
+
const compute = () => observe(() => {
|
|
503
|
+
if (computing)
|
|
504
|
+
throw new CircularDependencyError("computed");
|
|
505
|
+
changed = false;
|
|
506
|
+
if (isAsyncFunction(fn)) {
|
|
507
|
+
if (controller)
|
|
508
|
+
return value;
|
|
509
|
+
controller = new AbortController;
|
|
510
|
+
controller.signal.addEventListener("abort", () => {
|
|
511
|
+
computing = false;
|
|
512
|
+
controller = undefined;
|
|
513
|
+
compute();
|
|
514
|
+
}, {
|
|
515
|
+
once: true
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
let result;
|
|
519
|
+
computing = true;
|
|
520
|
+
try {
|
|
521
|
+
result = controller ? fn(controller.signal) : fn();
|
|
522
|
+
} catch (e) {
|
|
523
|
+
if (isAbortError(e))
|
|
524
|
+
nil();
|
|
525
|
+
else
|
|
526
|
+
err(e);
|
|
527
|
+
computing = false;
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
if (result instanceof Promise)
|
|
531
|
+
result.then(settle(ok), settle(err));
|
|
532
|
+
else if (result == null || UNSET === result)
|
|
533
|
+
nil();
|
|
534
|
+
else
|
|
535
|
+
ok(result);
|
|
536
|
+
computing = false;
|
|
537
|
+
}, mark);
|
|
538
|
+
const c = {
|
|
539
|
+
[Symbol.toStringTag]: TYPE_COMPUTED,
|
|
540
|
+
get: () => {
|
|
541
|
+
subscribe(watchers);
|
|
542
|
+
flush();
|
|
543
|
+
if (dirty)
|
|
544
|
+
compute();
|
|
545
|
+
if (error)
|
|
546
|
+
throw error;
|
|
547
|
+
return value;
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
return c;
|
|
551
|
+
};
|
|
552
|
+
var isComputed = (value) => isObjectOfType(value, TYPE_COMPUTED);
|
|
553
|
+
var isComputedCallback = (value) => isFunction(value) && value.length < 2;
|
|
554
|
+
// src/match.ts
|
|
555
|
+
function match(result, handlers) {
|
|
556
|
+
try {
|
|
557
|
+
if (result.pending) {
|
|
558
|
+
handlers.nil?.();
|
|
559
|
+
} else if (result.errors) {
|
|
560
|
+
handlers.err?.(result.errors);
|
|
561
|
+
} else {
|
|
562
|
+
handlers.ok?.(result.values);
|
|
563
|
+
}
|
|
564
|
+
} catch (error) {
|
|
565
|
+
if (handlers.err && (!result.errors || !result.errors.includes(toError(error)))) {
|
|
566
|
+
const allErrors = result.errors ? [...result.errors, toError(error)] : [toError(error)];
|
|
567
|
+
handlers.err(allErrors);
|
|
568
|
+
} else {
|
|
569
|
+
throw error;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
// src/resolve.ts
|
|
574
|
+
function resolve(signals) {
|
|
575
|
+
const errors = [];
|
|
576
|
+
let pending2 = false;
|
|
577
|
+
const values = {};
|
|
578
|
+
for (const [key, signal] of Object.entries(signals)) {
|
|
579
|
+
try {
|
|
580
|
+
const value = signal.get();
|
|
581
|
+
if (value === UNSET) {
|
|
582
|
+
pending2 = true;
|
|
583
|
+
} else {
|
|
584
|
+
values[key] = value;
|
|
585
|
+
}
|
|
586
|
+
} catch (e) {
|
|
587
|
+
errors.push(toError(e));
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (pending2) {
|
|
591
|
+
return { ok: false, pending: true };
|
|
592
|
+
}
|
|
593
|
+
if (errors.length > 0) {
|
|
594
|
+
return { ok: false, errors };
|
|
595
|
+
}
|
|
596
|
+
return { ok: true, values };
|
|
597
|
+
}
|
|
598
|
+
export {
|
|
599
|
+
watch,
|
|
600
|
+
toSignal,
|
|
601
|
+
toError,
|
|
602
|
+
subscribe,
|
|
603
|
+
store,
|
|
604
|
+
state,
|
|
605
|
+
resolve,
|
|
606
|
+
observe,
|
|
607
|
+
notify,
|
|
608
|
+
match,
|
|
609
|
+
isStore,
|
|
610
|
+
isState,
|
|
611
|
+
isSignal,
|
|
612
|
+
isFunction,
|
|
613
|
+
isEqual,
|
|
614
|
+
isComputedCallback,
|
|
615
|
+
isComputed,
|
|
616
|
+
isAsyncFunction,
|
|
617
|
+
isAbortError,
|
|
618
|
+
flush,
|
|
619
|
+
enqueue,
|
|
620
|
+
effect,
|
|
621
|
+
diff,
|
|
622
|
+
computed,
|
|
623
|
+
batch,
|
|
624
|
+
UNSET,
|
|
625
|
+
TYPE_STORE,
|
|
626
|
+
TYPE_STATE,
|
|
627
|
+
TYPE_COMPUTED,
|
|
628
|
+
CircularDependencyError
|
|
629
|
+
};
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var q,f=new Set,p=0,h=new Map,k,l=()=>{k=void 0;let $=Array.from(h.values());h.clear();for(let B of $)B()},B$=()=>{if(k)cancelAnimationFrame(k);k=requestAnimationFrame(l)};queueMicrotask(l);var m=($)=>{let B=new Set,W=$;return W.off=(x)=>{B.add(x)},W.cleanup=()=>{for(let x of B)x();B.clear()},W},U=($)=>{if(q&&!$.has(q)){let B=q;$.add(B),q.off(()=>{$.delete(B)})}},M=($)=>{for(let B of $)if(p)f.add(B);else B()},w=()=>{while(f.size){let $=Array.from(f);f.clear();for(let B of $)B()}},v=($)=>{p++;try{$()}finally{w(),p--}},y=($,B)=>{let W=q;q=B;try{$()}finally{q=W}},W$=($,B)=>new Promise((W,x)=>{h.set(B||Symbol(),()=>{try{W($())}catch(J){x(J)}}),B$()});var A=($)=>typeof $==="function",E=($)=>A($)&&$.constructor.name==="AsyncFunction",O=($,B)=>Object.prototype.toString.call($)===`[object ${B}]`,V=($)=>O($,"Object");var d=($)=>{let B={};for(let W=0;W<$.length;W++)if(W in $)B[String(W)]=$[W];return B},a=($,B)=>(B in $)&&A($[B]),R=($)=>$ instanceof DOMException&&$.name==="AbortError",Y=($)=>$ instanceof Error?$:Error(String($));class D extends Error{constructor($){super(`Circular dependency in ${$} detected`);this.name="CircularDependencyError"}}var o="State",S=($)=>{let B=new Set,W=$,x={[Symbol.toStringTag]:o,get:()=>{return U(B),W},set:(J)=>{if(P(W,J))return;if(W=J,M(B),C===W)B.clear()},update:(J)=>{x.set(J(W))}};return x},b=($)=>O($,o);var c=($)=>{let B=E($),W=!1,x,J=m(()=>y(()=>{if(W)throw new D("effect");W=!0,x?.abort(),x=void 0;let F;try{if(B){x=new AbortController;let H=x;$(x.signal).then((z)=>{if(A(z)&&x===H)J.off(z)}).catch((z)=>{if(!R(z))console.error("Async effect error:",z)})}else if(F=$(),A(F))J.off(F)}catch(H){if(!R(H))console.error("Effect callback error:",H)}W=!1},J));return J(),()=>{x?.abort(),J.cleanup()}};var n="Store",T=($)=>{let B=new Set,W=new EventTarget,x=new Map,J=new Map,F=S(0),H=()=>{let Z={};for(let[L,Q]of x)Z[L]=Q.get();return Z},z=(Z,L)=>W.dispatchEvent(new CustomEvent(Z,{detail:L})),K=(Z,L)=>{let Q=e(L);x.set(Z,Q);let G=c(()=>{let X=Q.get();if(X!=null)z("store-change",{[Z]:X})});J.set(Z,G)},N=(Z)=>{x.delete(Z);let L=J.get(Z);if(L)L();J.delete(Z)},j=(Z,L)=>{let Q=u(Z,L);return v(()=>{if(Object.keys(Q.add).length){for(let G in Q.add){let X=Q.add[G];if(X!=null)K(G,X)}z("store-add",Q.add)}if(Object.keys(Q.change).length){for(let G in Q.change){let X=x.get(G),I=Q.change[G];if(X&&I!=null&&a(X,"set"))X.set(I)}z("store-change",Q.change)}if(Object.keys(Q.remove).length){for(let G in Q.remove)N(G);z("store-remove",Q.remove)}F.set(x.size)}),Q.changed};j({},$),setTimeout(()=>{let Z=new CustomEvent("store-add",{detail:$});W.dispatchEvent(Z)},0);let _=["add","get","remove","set","update","addEventListener","removeEventListener","dispatchEvent","size"];return new Proxy({},{get(Z,L){let Q=String(L);switch(L){case"add":return(G,X)=>{if(!x.has(G))K(G,X),M(B),z("store-add",{[G]:X}),F.set(x.size)};case"get":return()=>{return U(B),H()};case"remove":return(G)=>{if(x.has(G))N(G),M(B),z("store-remove",{[G]:C}),F.set(x.size)};case"set":return(G)=>{if(j(H(),G)){if(M(B),C===G)B.clear()}};case"update":return(G)=>{let X=H(),I=G(X);if(j(X,I)){if(M(B),C===I)B.clear()}};case"addEventListener":return W.addEventListener.bind(W);case"removeEventListener":return W.removeEventListener.bind(W);case"dispatchEvent":return W.dispatchEvent.bind(W);case"size":return F}if(L===Symbol.toStringTag)return n;if(L===Symbol.iterator)return function*(){for(let[G,X]of x)yield[G,X]};return x.get(Q)},has(Z,L){let Q=String(L);return x.has(Q)||_.includes(Q)||L===Symbol.toStringTag||L===Symbol.iterator},ownKeys(){return Array.from(x.keys())},getOwnPropertyDescriptor(Z,L){let Q=x.get(String(L));return Q?{enumerable:!0,configurable:!0,writable:!0,value:Q}:void 0}})},g=($)=>O($,n);var C=Symbol(),$$=($)=>b($)||t($)||g($);function x$($){if($$($))return $;if(s($))return i($);if(Array.isArray($))return T(d($));if(V($))return T($);return S($)}function e($){if(b($)||g($))return $;if(Array.isArray($))return T(d($));if(V($))return T($);return S($)}var P=($,B,W)=>{if(Object.is($,B))return!0;if(typeof $!==typeof B)return!1;if(typeof $!=="object"||$===null||B===null)return!1;if(!W)W=new WeakSet;if(W.has($)||W.has(B))throw new D("isEqual");W.add($),W.add(B);try{if(Array.isArray($)&&Array.isArray(B)){if($.length!==B.length)return!1;for(let x=0;x<$.length;x++)if(!P($[x],B[x],W))return!1;return!0}if(Array.isArray($)!==Array.isArray(B))return!1;if(V($)&&V(B)){let x=Object.keys($),J=Object.keys(B);if(x.length!==J.length)return!1;for(let F of x){if(!(F in B))return!1;if(!P($[F],B[F],W))return!1}return!0}return!1}finally{W.delete($),W.delete(B)}},u=($,B)=>{let W=new WeakSet;return((J,F)=>{let H={},z={},K={},N=Object.keys(J),j=Object.keys(F),_=new Set([...N,...j]);for(let L of _){let Q=L in J,G=L in F;if(!Q&&G){H[L]=F[L];continue}else if(Q&&!G){K[L]=C;continue}let X=J[L],I=F[L];if(!P(X,I,W))z[L]=I}return{changed:Object.keys(H).length>0||Object.keys(z).length>0||Object.keys(K).length>0,add:H,change:z,remove:K}})($,B)};var r="Computed",i=($)=>{let B=new Set,W=C,x,J,F=!0,H=!1,z=!1,K=(G)=>{if(!P(G,W))W=G,H=!0;x=void 0,F=!1},N=()=>{H=C!==W,W=C,x=void 0},j=(G)=>{let X=Y(G);H=!x||X.name!==x.name||X.message!==x.message,W=C,x=X},_=(G)=>(X)=>{if(z=!1,J=void 0,G(X),H)M(B)},Z=m(()=>{if(F=!0,J?.abort(),B.size)M(B);else Z.cleanup()});Z.off(()=>{J?.abort()});let L=()=>y(()=>{if(z)throw new D("computed");if(H=!1,E($)){if(J)return W;J=new AbortController,J.signal.addEventListener("abort",()=>{z=!1,J=void 0,L()},{once:!0})}let G;z=!0;try{G=J?$(J.signal):$()}catch(X){if(R(X))N();else j(X);z=!1;return}if(G instanceof Promise)G.then(_(K),_(j));else if(G==null||C===G)N();else K(G);z=!1},Z);return{[Symbol.toStringTag]:r,get:()=>{if(U(B),w(),F)L();if(x)throw x;return W}}},t=($)=>O($,r),s=($)=>A($)&&$.length<2;function G$($,B){try{if($.pending)B.nil?.();else if($.errors)B.err?.($.errors);else B.ok?.($.values)}catch(W){if(B.err&&(!$.errors||!$.errors.includes(Y(W)))){let x=$.errors?[...$.errors,Y(W)]:[Y(W)];B.err(x)}else throw W}}function J$($){let B=[],W=!1,x={};for(let[J,F]of Object.entries($))try{let H=F.get();if(H===C)W=!0;else x[J]=H}catch(H){B.push(Y(H))}if(W)return{ok:!1,pending:!0};if(B.length>0)return{ok:!1,errors:B};return{ok:!0,values:x}}export{m as watch,x$ as toSignal,Y as toError,U as subscribe,T as store,S as state,J$ as resolve,y as observe,M as notify,G$ as match,g as isStore,b as isState,$$ as isSignal,A as isFunction,P as isEqual,s as isComputedCallback,t as isComputed,E as isAsyncFunction,R as isAbortError,w as flush,W$ as enqueue,c as effect,u as diff,i as computed,v as batch,C as UNSET,n as TYPE_STORE,o as TYPE_STATE,r as TYPE_COMPUTED,D as CircularDependencyError};
|