@zeix/cause-effect 0.16.1 → 0.17.1
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 +85 -21
- package/.cursorrules +11 -5
- package/.github/copilot-instructions.md +64 -13
- package/CLAUDE.md +143 -163
- package/LICENSE +1 -1
- package/README.md +248 -333
- package/archive/benchmark.ts +688 -0
- package/archive/collection.ts +312 -0
- package/{src → archive}/computed.ts +21 -21
- package/archive/list.ts +551 -0
- package/archive/memo.ts +139 -0
- package/{src → archive}/state.ts +13 -11
- package/archive/store.ts +368 -0
- package/archive/task.ts +194 -0
- package/eslint.config.js +1 -0
- package/index.dev.js +938 -509
- package/index.js +1 -1
- package/index.ts +50 -23
- package/package.json +1 -1
- package/src/classes/collection.ts +282 -0
- package/src/classes/composite.ts +176 -0
- package/src/classes/computed.ts +333 -0
- package/src/classes/list.ts +305 -0
- package/src/classes/ref.ts +68 -0
- package/src/classes/state.ts +98 -0
- package/src/classes/store.ts +210 -0
- package/src/diff.ts +26 -53
- package/src/effect.ts +9 -9
- package/src/errors.ts +71 -25
- package/src/match.ts +5 -12
- package/src/resolve.ts +3 -2
- package/src/signal.ts +58 -41
- package/src/system.ts +79 -42
- package/src/util.ts +16 -34
- package/test/batch.test.ts +15 -17
- package/test/benchmark.test.ts +4 -4
- package/test/collection.test.ts +853 -0
- package/test/computed.test.ts +138 -130
- package/test/diff.test.ts +2 -2
- package/test/effect.test.ts +36 -35
- package/test/list.test.ts +754 -0
- package/test/match.test.ts +25 -25
- package/test/ref.test.ts +227 -0
- package/test/resolve.test.ts +17 -19
- package/test/signal.test.ts +70 -119
- package/test/state.test.ts +44 -44
- package/test/store.test.ts +253 -929
- package/types/index.d.ts +12 -9
- package/types/src/classes/collection.d.ts +46 -0
- package/types/src/classes/composite.d.ts +15 -0
- package/types/src/classes/computed.d.ts +97 -0
- package/types/src/classes/list.d.ts +41 -0
- package/types/src/classes/ref.d.ts +39 -0
- package/types/src/classes/state.d.ts +52 -0
- package/types/src/classes/store.d.ts +51 -0
- package/types/src/diff.d.ts +8 -12
- package/types/src/errors.d.ts +17 -11
- package/types/src/signal.d.ts +27 -14
- package/types/src/system.d.ts +41 -20
- package/types/src/util.d.ts +6 -4
- package/src/store.ts +0 -474
- package/types/src/collection.d.ts +0 -26
- package/types/src/computed.d.ts +0 -33
- package/types/src/scheduler.d.ts +0 -55
- package/types/src/state.d.ts +0 -24
- package/types/src/store.d.ts +0 -65
package/index.dev.js
CHANGED
|
@@ -1,53 +1,3 @@
|
|
|
1
|
-
// src/errors.ts
|
|
2
|
-
class CircularDependencyError extends Error {
|
|
3
|
-
constructor(where) {
|
|
4
|
-
super(`Circular dependency detected in ${where}`);
|
|
5
|
-
this.name = "CircularDependencyError";
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
class InvalidCallbackError extends TypeError {
|
|
10
|
-
constructor(where, value) {
|
|
11
|
-
super(`Invalid ${where} callback ${value}`);
|
|
12
|
-
this.name = "InvalidCallbackError";
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
class InvalidSignalValueError extends TypeError {
|
|
17
|
-
constructor(where, value) {
|
|
18
|
-
super(`Invalid signal value ${value} in ${where}`);
|
|
19
|
-
this.name = "InvalidSignalValueError";
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
class NullishSignalValueError extends TypeError {
|
|
24
|
-
constructor(where) {
|
|
25
|
-
super(`Nullish signal values are not allowed in ${where}`);
|
|
26
|
-
this.name = "NullishSignalValueError";
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
class StoreKeyExistsError extends Error {
|
|
31
|
-
constructor(key, value) {
|
|
32
|
-
super(`Could not add store key "${key}" with value ${value} because it already exists`);
|
|
33
|
-
this.name = "StoreKeyExistsError";
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
class StoreKeyRangeError extends RangeError {
|
|
38
|
-
constructor(index) {
|
|
39
|
-
super(`Could not remove store index ${String(index)} because it is out of range`);
|
|
40
|
-
this.name = "StoreKeyRangeError";
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
class StoreKeyReadonlyError extends Error {
|
|
45
|
-
constructor(key, value) {
|
|
46
|
-
super(`Could not set store key "${key}" to ${value} because it is readonly`);
|
|
47
|
-
this.name = "StoreKeyReadonlyError";
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
1
|
// src/util.ts
|
|
52
2
|
var UNSET = Symbol();
|
|
53
3
|
var isString = (value) => typeof value === "string";
|
|
@@ -55,26 +5,13 @@ var isNumber = (value) => typeof value === "number";
|
|
|
55
5
|
var isSymbol = (value) => typeof value === "symbol";
|
|
56
6
|
var isFunction = (fn) => typeof fn === "function";
|
|
57
7
|
var isAsyncFunction = (fn) => isFunction(fn) && fn.constructor.name === "AsyncFunction";
|
|
8
|
+
var isSyncFunction = (fn) => isFunction(fn) && fn.constructor.name !== "AsyncFunction";
|
|
9
|
+
var isNonNullObject = (value) => value != null && typeof value === "object";
|
|
58
10
|
var isObjectOfType = (value, type) => Object.prototype.toString.call(value) === `[object ${type}]`;
|
|
59
11
|
var isRecord = (value) => isObjectOfType(value, "Object");
|
|
60
12
|
var isRecordOrArray = (value) => isRecord(value) || Array.isArray(value);
|
|
61
|
-
var
|
|
62
|
-
if (!keys.length)
|
|
63
|
-
return null;
|
|
64
|
-
const indexes = keys.map((k) => isString(k) ? parseInt(k, 10) : isNumber(k) ? k : NaN);
|
|
65
|
-
return indexes.every((index) => Number.isFinite(index) && index >= 0) ? indexes.sort((a, b) => a - b) : null;
|
|
66
|
-
};
|
|
13
|
+
var isUniformArray = (value, guard = (item) => item != null) => Array.isArray(value) && value.every(guard);
|
|
67
14
|
var isAbortError = (error) => error instanceof DOMException && error.name === "AbortError";
|
|
68
|
-
var toError = (reason) => reason instanceof Error ? reason : Error(String(reason));
|
|
69
|
-
var recordToArray = (record) => {
|
|
70
|
-
const indexes = validArrayIndexes(Object.keys(record));
|
|
71
|
-
if (indexes === null)
|
|
72
|
-
return record;
|
|
73
|
-
const array = [];
|
|
74
|
-
for (const index of indexes)
|
|
75
|
-
array.push(record[String(index)]);
|
|
76
|
-
return array;
|
|
77
|
-
};
|
|
78
15
|
var valueString = (value) => isString(value) ? `"${value}"` : !!value && typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
79
16
|
|
|
80
17
|
// src/diff.ts
|
|
@@ -83,7 +20,7 @@ var isEqual = (a, b, visited) => {
|
|
|
83
20
|
return true;
|
|
84
21
|
if (typeof a !== typeof b)
|
|
85
22
|
return false;
|
|
86
|
-
if (
|
|
23
|
+
if (!isNonNullObject(a) || !isNonNullObject(b))
|
|
87
24
|
return false;
|
|
88
25
|
if (!visited)
|
|
89
26
|
visited = new WeakSet;
|
|
@@ -126,12 +63,12 @@ var diff = (oldObj, newObj) => {
|
|
|
126
63
|
const oldValid = isRecordOrArray(oldObj);
|
|
127
64
|
const newValid = isRecordOrArray(newObj);
|
|
128
65
|
if (!oldValid || !newValid) {
|
|
129
|
-
const
|
|
66
|
+
const changed = !Object.is(oldObj, newObj);
|
|
130
67
|
return {
|
|
131
|
-
changed
|
|
132
|
-
add:
|
|
68
|
+
changed,
|
|
69
|
+
add: changed && newValid ? newObj : {},
|
|
133
70
|
change: {},
|
|
134
|
-
remove:
|
|
71
|
+
remove: changed && oldValid ? oldObj : {}
|
|
135
72
|
};
|
|
136
73
|
}
|
|
137
74
|
const visited = new WeakSet;
|
|
@@ -156,192 +93,961 @@ var diff = (oldObj, newObj) => {
|
|
|
156
93
|
if (!isEqual(oldValue, newValue, visited))
|
|
157
94
|
change[key] = newValue;
|
|
158
95
|
}
|
|
159
|
-
const changed = Object.keys(add).length > 0 || Object.keys(change).length > 0 || Object.keys(remove).length > 0;
|
|
160
96
|
return {
|
|
161
|
-
changed,
|
|
162
97
|
add,
|
|
163
98
|
change,
|
|
164
|
-
remove
|
|
99
|
+
remove,
|
|
100
|
+
changed: !!(Object.keys(add).length || Object.keys(change).length || Object.keys(remove).length)
|
|
165
101
|
};
|
|
166
102
|
};
|
|
167
103
|
|
|
168
104
|
// src/system.ts
|
|
169
105
|
var activeWatcher;
|
|
170
|
-
var
|
|
106
|
+
var pendingReactions = new Set;
|
|
171
107
|
var batchDepth = 0;
|
|
172
|
-
var createWatcher = (
|
|
108
|
+
var createWatcher = (react) => {
|
|
173
109
|
const cleanups = new Set;
|
|
174
|
-
const
|
|
175
|
-
|
|
110
|
+
const watcher = react;
|
|
111
|
+
watcher.onCleanup = (cleanup) => {
|
|
176
112
|
cleanups.add(cleanup);
|
|
177
113
|
};
|
|
178
|
-
|
|
114
|
+
watcher.stop = () => {
|
|
179
115
|
for (const cleanup of cleanups)
|
|
180
116
|
cleanup();
|
|
181
117
|
cleanups.clear();
|
|
182
118
|
};
|
|
183
|
-
return
|
|
119
|
+
return watcher;
|
|
184
120
|
};
|
|
185
|
-
var
|
|
121
|
+
var subscribeActiveWatcher = (watchers) => {
|
|
186
122
|
if (activeWatcher && !watchers.has(activeWatcher)) {
|
|
187
123
|
const watcher = activeWatcher;
|
|
188
|
-
watcher.
|
|
189
|
-
watchers.delete(watcher);
|
|
190
|
-
});
|
|
124
|
+
watcher.onCleanup(() => watchers.delete(watcher));
|
|
191
125
|
watchers.add(watcher);
|
|
192
126
|
}
|
|
193
127
|
};
|
|
194
|
-
var
|
|
195
|
-
for (const
|
|
128
|
+
var notifyWatchers = (watchers) => {
|
|
129
|
+
for (const react of watchers) {
|
|
196
130
|
if (batchDepth)
|
|
197
|
-
|
|
131
|
+
pendingReactions.add(react);
|
|
198
132
|
else
|
|
199
|
-
|
|
133
|
+
react();
|
|
200
134
|
}
|
|
201
135
|
};
|
|
202
|
-
var
|
|
203
|
-
while (
|
|
204
|
-
const watchers = Array.from(
|
|
205
|
-
|
|
136
|
+
var flushPendingReactions = () => {
|
|
137
|
+
while (pendingReactions.size) {
|
|
138
|
+
const watchers = Array.from(pendingReactions);
|
|
139
|
+
pendingReactions.clear();
|
|
206
140
|
for (const watcher of watchers)
|
|
207
141
|
watcher();
|
|
208
142
|
}
|
|
209
143
|
};
|
|
210
|
-
var
|
|
144
|
+
var batchSignalWrites = (callback) => {
|
|
211
145
|
batchDepth++;
|
|
212
146
|
try {
|
|
213
|
-
|
|
147
|
+
callback();
|
|
214
148
|
} finally {
|
|
215
|
-
|
|
149
|
+
flushPendingReactions();
|
|
216
150
|
batchDepth--;
|
|
217
151
|
}
|
|
218
152
|
};
|
|
219
|
-
var
|
|
153
|
+
var trackSignalReads = (watcher, run) => {
|
|
220
154
|
const prev = activeWatcher;
|
|
221
|
-
activeWatcher = watcher;
|
|
155
|
+
activeWatcher = watcher || undefined;
|
|
222
156
|
try {
|
|
223
157
|
run();
|
|
224
158
|
} finally {
|
|
225
159
|
activeWatcher = prev;
|
|
226
160
|
}
|
|
227
161
|
};
|
|
162
|
+
var emitNotification = (listeners, payload) => {
|
|
163
|
+
for (const listener of listeners) {
|
|
164
|
+
if (batchDepth)
|
|
165
|
+
pendingReactions.add(() => listener(payload));
|
|
166
|
+
else
|
|
167
|
+
listener(payload);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
228
170
|
|
|
229
|
-
// src/computed.ts
|
|
171
|
+
// src/classes/computed.ts
|
|
230
172
|
var TYPE_COMPUTED = "Computed";
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
173
|
+
|
|
174
|
+
class Memo {
|
|
175
|
+
#watchers = new Set;
|
|
176
|
+
#callback;
|
|
177
|
+
#value;
|
|
178
|
+
#error;
|
|
179
|
+
#dirty = true;
|
|
180
|
+
#computing = false;
|
|
181
|
+
#watcher;
|
|
182
|
+
constructor(callback, initialValue = UNSET) {
|
|
183
|
+
validateCallback("memo", callback, isMemoCallback);
|
|
184
|
+
validateSignalValue("memo", initialValue);
|
|
185
|
+
this.#callback = callback;
|
|
186
|
+
this.#value = initialValue;
|
|
187
|
+
this.#watcher = createWatcher(() => {
|
|
188
|
+
this.#dirty = true;
|
|
189
|
+
if (this.#watchers.size)
|
|
190
|
+
notifyWatchers(this.#watchers);
|
|
191
|
+
else
|
|
192
|
+
this.#watcher.stop();
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
get [Symbol.toStringTag]() {
|
|
196
|
+
return TYPE_COMPUTED;
|
|
197
|
+
}
|
|
198
|
+
get() {
|
|
199
|
+
subscribeActiveWatcher(this.#watchers);
|
|
200
|
+
flushPendingReactions();
|
|
201
|
+
if (this.#dirty) {
|
|
202
|
+
trackSignalReads(this.#watcher, () => {
|
|
203
|
+
if (this.#computing)
|
|
204
|
+
throw new CircularDependencyError("memo");
|
|
205
|
+
let result;
|
|
206
|
+
this.#computing = true;
|
|
207
|
+
try {
|
|
208
|
+
result = this.#callback(this.#value);
|
|
209
|
+
} catch (e) {
|
|
210
|
+
this.#value = UNSET;
|
|
211
|
+
this.#error = createError(e);
|
|
212
|
+
this.#computing = false;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (result == null || UNSET === result) {
|
|
216
|
+
this.#value = UNSET;
|
|
217
|
+
this.#error = undefined;
|
|
218
|
+
} else {
|
|
219
|
+
this.#value = result;
|
|
220
|
+
this.#error = undefined;
|
|
221
|
+
this.#dirty = false;
|
|
222
|
+
}
|
|
223
|
+
this.#computing = false;
|
|
224
|
+
});
|
|
247
225
|
}
|
|
248
|
-
error
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
226
|
+
if (this.#error)
|
|
227
|
+
throw this.#error;
|
|
228
|
+
return this.#value;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
class Task {
|
|
233
|
+
#watchers = new Set;
|
|
234
|
+
#callback;
|
|
235
|
+
#value;
|
|
236
|
+
#error;
|
|
237
|
+
#dirty = true;
|
|
238
|
+
#computing = false;
|
|
239
|
+
#changed = false;
|
|
240
|
+
#watcher;
|
|
241
|
+
#controller;
|
|
242
|
+
constructor(callback, initialValue = UNSET) {
|
|
243
|
+
validateCallback("task", callback, isTaskCallback);
|
|
244
|
+
validateSignalValue("task", initialValue);
|
|
245
|
+
this.#callback = callback;
|
|
246
|
+
this.#value = initialValue;
|
|
247
|
+
this.#watcher = createWatcher(() => {
|
|
248
|
+
this.#dirty = true;
|
|
249
|
+
this.#controller?.abort();
|
|
250
|
+
if (this.#watchers.size)
|
|
251
|
+
notifyWatchers(this.#watchers);
|
|
252
|
+
else
|
|
253
|
+
this.#watcher.stop();
|
|
254
|
+
});
|
|
255
|
+
this.#watcher.onCleanup(() => {
|
|
256
|
+
this.#controller?.abort();
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
get [Symbol.toStringTag]() {
|
|
260
|
+
return TYPE_COMPUTED;
|
|
261
|
+
}
|
|
262
|
+
get() {
|
|
263
|
+
subscribeActiveWatcher(this.#watchers);
|
|
264
|
+
flushPendingReactions();
|
|
265
|
+
const ok = (v) => {
|
|
266
|
+
if (!isEqual(v, this.#value)) {
|
|
267
|
+
this.#value = v;
|
|
268
|
+
this.#changed = true;
|
|
269
|
+
}
|
|
270
|
+
this.#error = undefined;
|
|
271
|
+
this.#dirty = false;
|
|
272
|
+
};
|
|
273
|
+
const nil = () => {
|
|
274
|
+
this.#changed = UNSET !== this.#value;
|
|
275
|
+
this.#value = UNSET;
|
|
276
|
+
this.#error = undefined;
|
|
277
|
+
};
|
|
278
|
+
const err = (e) => {
|
|
279
|
+
const newError = createError(e);
|
|
280
|
+
this.#changed = !this.#error || newError.name !== this.#error.name || newError.message !== this.#error.message;
|
|
281
|
+
this.#value = UNSET;
|
|
282
|
+
this.#error = newError;
|
|
283
|
+
};
|
|
284
|
+
const settle = (fn) => (arg) => {
|
|
285
|
+
this.#computing = false;
|
|
286
|
+
this.#controller = undefined;
|
|
287
|
+
fn(arg);
|
|
288
|
+
if (this.#changed)
|
|
289
|
+
notifyWatchers(this.#watchers);
|
|
290
|
+
};
|
|
291
|
+
const compute = () => trackSignalReads(this.#watcher, () => {
|
|
292
|
+
if (this.#computing)
|
|
293
|
+
throw new CircularDependencyError("task");
|
|
294
|
+
this.#changed = false;
|
|
295
|
+
if (this.#controller)
|
|
296
|
+
return this.#value;
|
|
297
|
+
this.#controller = new AbortController;
|
|
298
|
+
this.#controller.signal.addEventListener("abort", () => {
|
|
299
|
+
this.#computing = false;
|
|
300
|
+
this.#controller = undefined;
|
|
291
301
|
compute();
|
|
292
302
|
}, {
|
|
293
303
|
once: true
|
|
294
304
|
});
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
305
|
+
let result;
|
|
306
|
+
this.#computing = true;
|
|
307
|
+
try {
|
|
308
|
+
result = this.#callback(this.#value, this.#controller.signal);
|
|
309
|
+
} catch (e) {
|
|
310
|
+
if (isAbortError(e))
|
|
311
|
+
nil();
|
|
312
|
+
else
|
|
313
|
+
err(e);
|
|
314
|
+
this.#computing = false;
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (result instanceof Promise)
|
|
318
|
+
result.then(settle(ok), settle(err));
|
|
319
|
+
else if (result == null || UNSET === result)
|
|
302
320
|
nil();
|
|
303
321
|
else
|
|
304
|
-
|
|
305
|
-
computing = false;
|
|
322
|
+
ok(result);
|
|
323
|
+
this.#computing = false;
|
|
324
|
+
});
|
|
325
|
+
if (this.#dirty)
|
|
326
|
+
compute();
|
|
327
|
+
if (this.#error)
|
|
328
|
+
throw this.#error;
|
|
329
|
+
return this.#value;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
var createComputed = (callback, initialValue = UNSET) => isAsyncFunction(callback) ? new Task(callback, initialValue) : new Memo(callback, initialValue);
|
|
333
|
+
var isComputed = (value) => isObjectOfType(value, TYPE_COMPUTED);
|
|
334
|
+
var isMemoCallback = (value) => isSyncFunction(value) && value.length < 2;
|
|
335
|
+
var isTaskCallback = (value) => isAsyncFunction(value) && value.length < 3;
|
|
336
|
+
|
|
337
|
+
// src/classes/composite.ts
|
|
338
|
+
class Composite {
|
|
339
|
+
signals = new Map;
|
|
340
|
+
#validate;
|
|
341
|
+
#create;
|
|
342
|
+
#watchers = new Map;
|
|
343
|
+
#listeners = {
|
|
344
|
+
add: new Set,
|
|
345
|
+
change: new Set,
|
|
346
|
+
remove: new Set
|
|
347
|
+
};
|
|
348
|
+
#batching = false;
|
|
349
|
+
constructor(values, validate, create) {
|
|
350
|
+
this.#validate = validate;
|
|
351
|
+
this.#create = create;
|
|
352
|
+
this.change({
|
|
353
|
+
add: values,
|
|
354
|
+
change: {},
|
|
355
|
+
remove: {},
|
|
356
|
+
changed: true
|
|
357
|
+
}, true);
|
|
358
|
+
}
|
|
359
|
+
#addWatcher(key) {
|
|
360
|
+
const watcher = createWatcher(() => {
|
|
361
|
+
trackSignalReads(watcher, () => {
|
|
362
|
+
this.signals.get(key)?.get();
|
|
363
|
+
if (!this.#batching)
|
|
364
|
+
emitNotification(this.#listeners.change, [key]);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
this.#watchers.set(key, watcher);
|
|
368
|
+
watcher();
|
|
369
|
+
}
|
|
370
|
+
add(key, value) {
|
|
371
|
+
if (!this.#validate(key, value))
|
|
372
|
+
return false;
|
|
373
|
+
this.signals.set(key, this.#create(value));
|
|
374
|
+
if (this.#listeners.change.size)
|
|
375
|
+
this.#addWatcher(key);
|
|
376
|
+
if (!this.#batching)
|
|
377
|
+
emitNotification(this.#listeners.add, [key]);
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
remove(key) {
|
|
381
|
+
const ok = this.signals.delete(key);
|
|
382
|
+
if (!ok)
|
|
383
|
+
return false;
|
|
384
|
+
const watcher = this.#watchers.get(key);
|
|
385
|
+
if (watcher) {
|
|
386
|
+
watcher.stop();
|
|
387
|
+
this.#watchers.delete(key);
|
|
388
|
+
}
|
|
389
|
+
if (!this.#batching)
|
|
390
|
+
emitNotification(this.#listeners.remove, [key]);
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
change(changes, initialRun) {
|
|
394
|
+
this.#batching = true;
|
|
395
|
+
if (Object.keys(changes.add).length) {
|
|
396
|
+
for (const key in changes.add)
|
|
397
|
+
this.add(key, changes.add[key]);
|
|
398
|
+
const notify = () => emitNotification(this.#listeners.add, Object.keys(changes.add));
|
|
399
|
+
if (initialRun)
|
|
400
|
+
setTimeout(notify, 0);
|
|
401
|
+
else
|
|
402
|
+
notify();
|
|
403
|
+
}
|
|
404
|
+
if (Object.keys(changes.change).length) {
|
|
405
|
+
batchSignalWrites(() => {
|
|
406
|
+
for (const key in changes.change) {
|
|
407
|
+
const value = changes.change[key];
|
|
408
|
+
if (!this.#validate(key, value))
|
|
409
|
+
continue;
|
|
410
|
+
const signal = this.signals.get(key);
|
|
411
|
+
if (guardMutableSignal(`list item "${key}"`, value, signal))
|
|
412
|
+
signal.set(value);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
emitNotification(this.#listeners.change, Object.keys(changes.change));
|
|
416
|
+
}
|
|
417
|
+
if (Object.keys(changes.remove).length) {
|
|
418
|
+
for (const key in changes.remove)
|
|
419
|
+
this.remove(key);
|
|
420
|
+
emitNotification(this.#listeners.remove, Object.keys(changes.remove));
|
|
421
|
+
}
|
|
422
|
+
this.#batching = false;
|
|
423
|
+
return changes.changed;
|
|
424
|
+
}
|
|
425
|
+
clear() {
|
|
426
|
+
const keys = Array.from(this.signals.keys());
|
|
427
|
+
this.signals.clear();
|
|
428
|
+
this.#watchers.clear();
|
|
429
|
+
emitNotification(this.#listeners.remove, keys);
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
on(type, listener) {
|
|
433
|
+
this.#listeners[type].add(listener);
|
|
434
|
+
if (type === "change" && !this.#watchers.size) {
|
|
435
|
+
this.#batching = true;
|
|
436
|
+
for (const key of this.signals.keys())
|
|
437
|
+
this.#addWatcher(key);
|
|
438
|
+
this.#batching = false;
|
|
439
|
+
}
|
|
440
|
+
return () => {
|
|
441
|
+
this.#listeners[type].delete(listener);
|
|
442
|
+
if (type === "change" && !this.#listeners.change.size) {
|
|
443
|
+
if (this.#watchers.size) {
|
|
444
|
+
for (const watcher of this.#watchers.values())
|
|
445
|
+
watcher.stop();
|
|
446
|
+
this.#watchers.clear();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// src/classes/state.ts
|
|
454
|
+
var TYPE_STATE = "State";
|
|
455
|
+
|
|
456
|
+
class State {
|
|
457
|
+
#watchers = new Set;
|
|
458
|
+
#value;
|
|
459
|
+
constructor(initialValue) {
|
|
460
|
+
validateSignalValue("state", initialValue);
|
|
461
|
+
this.#value = initialValue;
|
|
462
|
+
}
|
|
463
|
+
get [Symbol.toStringTag]() {
|
|
464
|
+
return TYPE_STATE;
|
|
465
|
+
}
|
|
466
|
+
get() {
|
|
467
|
+
subscribeActiveWatcher(this.#watchers);
|
|
468
|
+
return this.#value;
|
|
469
|
+
}
|
|
470
|
+
set(newValue) {
|
|
471
|
+
validateSignalValue("state", newValue);
|
|
472
|
+
if (isEqual(this.#value, newValue))
|
|
473
|
+
return;
|
|
474
|
+
this.#value = newValue;
|
|
475
|
+
notifyWatchers(this.#watchers);
|
|
476
|
+
if (UNSET === this.#value)
|
|
477
|
+
this.#watchers.clear();
|
|
478
|
+
}
|
|
479
|
+
update(updater) {
|
|
480
|
+
validateCallback("state update", updater);
|
|
481
|
+
this.set(updater(this.#value));
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
var isState = (value) => isObjectOfType(value, TYPE_STATE);
|
|
485
|
+
|
|
486
|
+
// src/classes/list.ts
|
|
487
|
+
var TYPE_LIST = "List";
|
|
488
|
+
|
|
489
|
+
class List {
|
|
490
|
+
#composite;
|
|
491
|
+
#watchers = new Set;
|
|
492
|
+
#listeners = {
|
|
493
|
+
sort: new Set
|
|
494
|
+
};
|
|
495
|
+
#order = [];
|
|
496
|
+
#generateKey;
|
|
497
|
+
constructor(initialValue, keyConfig) {
|
|
498
|
+
validateSignalValue("list", initialValue, Array.isArray);
|
|
499
|
+
let keyCounter = 0;
|
|
500
|
+
this.#generateKey = isString(keyConfig) ? () => `${keyConfig}${keyCounter++}` : isFunction(keyConfig) ? (item) => keyConfig(item) : () => String(keyCounter++);
|
|
501
|
+
this.#composite = new Composite(this.#toRecord(initialValue), (key, value) => {
|
|
502
|
+
validateSignalValue(`list for key "${key}"`, value);
|
|
503
|
+
return true;
|
|
504
|
+
}, (value) => new State(value));
|
|
505
|
+
}
|
|
506
|
+
#toRecord(array) {
|
|
507
|
+
const record = {};
|
|
508
|
+
for (let i = 0;i < array.length; i++) {
|
|
509
|
+
const value = array[i];
|
|
510
|
+
if (value === undefined)
|
|
511
|
+
continue;
|
|
512
|
+
let key = this.#order[i];
|
|
513
|
+
if (!key) {
|
|
514
|
+
key = this.#generateKey(value);
|
|
515
|
+
this.#order[i] = key;
|
|
516
|
+
}
|
|
517
|
+
record[key] = value;
|
|
518
|
+
}
|
|
519
|
+
return record;
|
|
520
|
+
}
|
|
521
|
+
get #value() {
|
|
522
|
+
return this.#order.map((key) => this.#composite.signals.get(key)?.get()).filter((v) => v !== undefined);
|
|
523
|
+
}
|
|
524
|
+
get [Symbol.toStringTag]() {
|
|
525
|
+
return TYPE_LIST;
|
|
526
|
+
}
|
|
527
|
+
get [Symbol.isConcatSpreadable]() {
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
*[Symbol.iterator]() {
|
|
531
|
+
for (const key of this.#order) {
|
|
532
|
+
const signal = this.#composite.signals.get(key);
|
|
533
|
+
if (signal)
|
|
534
|
+
yield signal;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
get length() {
|
|
538
|
+
subscribeActiveWatcher(this.#watchers);
|
|
539
|
+
return this.#order.length;
|
|
540
|
+
}
|
|
541
|
+
get() {
|
|
542
|
+
subscribeActiveWatcher(this.#watchers);
|
|
543
|
+
return this.#value;
|
|
544
|
+
}
|
|
545
|
+
set(newValue) {
|
|
546
|
+
if (UNSET === newValue) {
|
|
547
|
+
this.#composite.clear();
|
|
548
|
+
notifyWatchers(this.#watchers);
|
|
549
|
+
this.#watchers.clear();
|
|
306
550
|
return;
|
|
307
551
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
552
|
+
const oldValue = this.#value;
|
|
553
|
+
const changes = diff(this.#toRecord(oldValue), this.#toRecord(newValue));
|
|
554
|
+
const removedKeys = Object.keys(changes.remove);
|
|
555
|
+
const changed = this.#composite.change(changes);
|
|
556
|
+
if (changed) {
|
|
557
|
+
for (const key of removedKeys) {
|
|
558
|
+
const index = this.#order.indexOf(key);
|
|
559
|
+
if (index !== -1)
|
|
560
|
+
this.#order.splice(index, 1);
|
|
561
|
+
}
|
|
562
|
+
this.#order = this.#order.filter(() => true);
|
|
563
|
+
notifyWatchers(this.#watchers);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
update(fn) {
|
|
567
|
+
this.set(fn(this.get()));
|
|
568
|
+
}
|
|
569
|
+
at(index) {
|
|
570
|
+
return this.#composite.signals.get(this.#order[index]);
|
|
571
|
+
}
|
|
572
|
+
keys() {
|
|
573
|
+
return this.#order.values();
|
|
574
|
+
}
|
|
575
|
+
byKey(key) {
|
|
576
|
+
return this.#composite.signals.get(key);
|
|
577
|
+
}
|
|
578
|
+
keyAt(index) {
|
|
579
|
+
return this.#order[index];
|
|
580
|
+
}
|
|
581
|
+
indexOfKey(key) {
|
|
582
|
+
return this.#order.indexOf(key);
|
|
583
|
+
}
|
|
584
|
+
add(value) {
|
|
585
|
+
const key = this.#generateKey(value);
|
|
586
|
+
if (this.#composite.signals.has(key))
|
|
587
|
+
throw new DuplicateKeyError("store", key, value);
|
|
588
|
+
if (!this.#order.includes(key))
|
|
589
|
+
this.#order.push(key);
|
|
590
|
+
const ok = this.#composite.add(key, value);
|
|
591
|
+
if (ok)
|
|
592
|
+
notifyWatchers(this.#watchers);
|
|
593
|
+
return key;
|
|
594
|
+
}
|
|
595
|
+
remove(keyOrIndex) {
|
|
596
|
+
const key = isNumber(keyOrIndex) ? this.#order[keyOrIndex] : keyOrIndex;
|
|
597
|
+
const ok = this.#composite.remove(key);
|
|
598
|
+
if (ok) {
|
|
599
|
+
const index = isNumber(keyOrIndex) ? keyOrIndex : this.#order.indexOf(key);
|
|
600
|
+
if (index >= 0)
|
|
601
|
+
this.#order.splice(index, 1);
|
|
602
|
+
this.#order = this.#order.filter(() => true);
|
|
603
|
+
notifyWatchers(this.#watchers);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
sort(compareFn) {
|
|
607
|
+
const entries = this.#order.map((key) => [key, this.#composite.signals.get(key)?.get()]).sort(isFunction(compareFn) ? (a, b) => compareFn(a[1], b[1]) : (a, b) => String(a[1]).localeCompare(String(b[1])));
|
|
608
|
+
const newOrder = entries.map(([key]) => key);
|
|
609
|
+
if (!isEqual(this.#order, newOrder)) {
|
|
610
|
+
this.#order = newOrder;
|
|
611
|
+
notifyWatchers(this.#watchers);
|
|
612
|
+
emitNotification(this.#listeners.sort, this.#order);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
splice(start, deleteCount, ...items) {
|
|
616
|
+
const length = this.#order.length;
|
|
617
|
+
const actualStart = start < 0 ? Math.max(0, length + start) : Math.min(start, length);
|
|
618
|
+
const actualDeleteCount = Math.max(0, Math.min(deleteCount ?? Math.max(0, length - Math.max(0, actualStart)), length - actualStart));
|
|
619
|
+
const add = {};
|
|
620
|
+
const remove = {};
|
|
621
|
+
for (let i = 0;i < actualDeleteCount; i++) {
|
|
622
|
+
const index = actualStart + i;
|
|
623
|
+
const key = this.#order[index];
|
|
624
|
+
if (key) {
|
|
625
|
+
const signal = this.#composite.signals.get(key);
|
|
626
|
+
if (signal)
|
|
627
|
+
remove[key] = signal.get();
|
|
330
628
|
}
|
|
331
629
|
}
|
|
630
|
+
const newOrder = this.#order.slice(0, actualStart);
|
|
631
|
+
for (const item of items) {
|
|
632
|
+
const key = this.#generateKey(item);
|
|
633
|
+
newOrder.push(key);
|
|
634
|
+
add[key] = item;
|
|
635
|
+
}
|
|
636
|
+
newOrder.push(...this.#order.slice(actualStart + actualDeleteCount));
|
|
637
|
+
const changed = !!(Object.keys(add).length || Object.keys(remove).length);
|
|
638
|
+
if (changed) {
|
|
639
|
+
this.#composite.change({
|
|
640
|
+
add,
|
|
641
|
+
change: {},
|
|
642
|
+
remove,
|
|
643
|
+
changed
|
|
644
|
+
});
|
|
645
|
+
this.#order = newOrder.filter(() => true);
|
|
646
|
+
notifyWatchers(this.#watchers);
|
|
647
|
+
}
|
|
648
|
+
return Object.values(remove);
|
|
649
|
+
}
|
|
650
|
+
on(type, listener) {
|
|
651
|
+
if (type === "sort") {
|
|
652
|
+
this.#listeners.sort.add(listener);
|
|
653
|
+
return () => {
|
|
654
|
+
this.#listeners.sort.delete(listener);
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
return this.#composite.on(type, listener);
|
|
658
|
+
}
|
|
659
|
+
deriveCollection(callback) {
|
|
660
|
+
return new DerivedCollection(this, callback);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
var isList = (value) => isObjectOfType(value, TYPE_LIST);
|
|
664
|
+
|
|
665
|
+
// src/classes/store.ts
|
|
666
|
+
var TYPE_STORE = "Store";
|
|
667
|
+
|
|
668
|
+
class BaseStore {
|
|
669
|
+
#composite;
|
|
670
|
+
#watchers = new Set;
|
|
671
|
+
constructor(initialValue) {
|
|
672
|
+
validateSignalValue("store", initialValue, isRecord);
|
|
673
|
+
this.#composite = new Composite(initialValue, (key, value) => {
|
|
674
|
+
validateSignalValue(`store for key "${key}"`, value);
|
|
675
|
+
return true;
|
|
676
|
+
}, (value) => createMutableSignal(value));
|
|
677
|
+
}
|
|
678
|
+
get #value() {
|
|
679
|
+
const record = {};
|
|
680
|
+
for (const [key, signal] of this.#composite.signals.entries())
|
|
681
|
+
record[key] = signal.get();
|
|
682
|
+
return record;
|
|
683
|
+
}
|
|
684
|
+
get [Symbol.toStringTag]() {
|
|
685
|
+
return TYPE_STORE;
|
|
686
|
+
}
|
|
687
|
+
get [Symbol.isConcatSpreadable]() {
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
*[Symbol.iterator]() {
|
|
691
|
+
for (const [key, signal] of this.#composite.signals.entries())
|
|
692
|
+
yield [key, signal];
|
|
693
|
+
}
|
|
694
|
+
get() {
|
|
695
|
+
subscribeActiveWatcher(this.#watchers);
|
|
696
|
+
return this.#value;
|
|
697
|
+
}
|
|
698
|
+
set(newValue) {
|
|
699
|
+
if (UNSET === newValue) {
|
|
700
|
+
this.#composite.clear();
|
|
701
|
+
notifyWatchers(this.#watchers);
|
|
702
|
+
this.#watchers.clear();
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
const oldValue = this.#value;
|
|
706
|
+
const changed = this.#composite.change(diff(oldValue, newValue));
|
|
707
|
+
if (changed)
|
|
708
|
+
notifyWatchers(this.#watchers);
|
|
709
|
+
}
|
|
710
|
+
keys() {
|
|
711
|
+
return this.#composite.signals.keys();
|
|
712
|
+
}
|
|
713
|
+
byKey(key) {
|
|
714
|
+
return this.#composite.signals.get(key);
|
|
715
|
+
}
|
|
716
|
+
update(fn) {
|
|
717
|
+
this.set(fn(this.get()));
|
|
718
|
+
}
|
|
719
|
+
add(key, value) {
|
|
720
|
+
if (this.#composite.signals.has(key))
|
|
721
|
+
throw new DuplicateKeyError("store", key, value);
|
|
722
|
+
const ok = this.#composite.add(key, value);
|
|
723
|
+
if (ok)
|
|
724
|
+
notifyWatchers(this.#watchers);
|
|
725
|
+
return key;
|
|
726
|
+
}
|
|
727
|
+
remove(key) {
|
|
728
|
+
const ok = this.#composite.remove(key);
|
|
729
|
+
if (ok)
|
|
730
|
+
notifyWatchers(this.#watchers);
|
|
731
|
+
}
|
|
732
|
+
on(type, listener) {
|
|
733
|
+
return this.#composite.on(type, listener);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
var createStore = (initialValue) => {
|
|
737
|
+
const instance = new BaseStore(initialValue);
|
|
738
|
+
return new Proxy(instance, {
|
|
739
|
+
get(target, prop) {
|
|
740
|
+
if (prop in target) {
|
|
741
|
+
const value = Reflect.get(target, prop);
|
|
742
|
+
return isFunction(value) ? value.bind(target) : value;
|
|
743
|
+
}
|
|
744
|
+
if (!isSymbol(prop))
|
|
745
|
+
return target.byKey(prop);
|
|
746
|
+
},
|
|
747
|
+
has(target, prop) {
|
|
748
|
+
if (prop in target)
|
|
749
|
+
return true;
|
|
750
|
+
return target.byKey(String(prop)) !== undefined;
|
|
751
|
+
},
|
|
752
|
+
ownKeys(target) {
|
|
753
|
+
return Array.from(target.keys());
|
|
754
|
+
},
|
|
755
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
756
|
+
if (prop in target)
|
|
757
|
+
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
758
|
+
if (isSymbol(prop))
|
|
759
|
+
return;
|
|
760
|
+
const signal = target.byKey(String(prop));
|
|
761
|
+
return signal ? {
|
|
762
|
+
enumerable: true,
|
|
763
|
+
configurable: true,
|
|
764
|
+
writable: true,
|
|
765
|
+
value: signal
|
|
766
|
+
} : undefined;
|
|
767
|
+
}
|
|
332
768
|
});
|
|
333
|
-
return computed;
|
|
334
769
|
};
|
|
335
|
-
var
|
|
336
|
-
|
|
770
|
+
var isStore = (value) => isObjectOfType(value, TYPE_STORE);
|
|
771
|
+
|
|
772
|
+
// src/signal.ts
|
|
773
|
+
var isSignal = (value) => isState(value) || isComputed(value) || isStore(value);
|
|
774
|
+
var isMutableSignal = (value) => isState(value) || isStore(value) || isList(value);
|
|
775
|
+
function createSignal(value) {
|
|
776
|
+
if (isMemoCallback(value))
|
|
777
|
+
return new Memo(value);
|
|
778
|
+
if (isTaskCallback(value))
|
|
779
|
+
return new Task(value);
|
|
780
|
+
if (isUniformArray(value))
|
|
781
|
+
return new List(value);
|
|
782
|
+
if (isRecord(value))
|
|
783
|
+
return createStore(value);
|
|
784
|
+
return new State(value);
|
|
785
|
+
}
|
|
786
|
+
function createMutableSignal(value) {
|
|
787
|
+
if (isUniformArray(value))
|
|
788
|
+
return new List(value);
|
|
789
|
+
if (isRecord(value))
|
|
790
|
+
return createStore(value);
|
|
791
|
+
return new State(value);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// src/errors.ts
|
|
795
|
+
class CircularDependencyError extends Error {
|
|
796
|
+
constructor(where) {
|
|
797
|
+
super(`Circular dependency detected in ${where}`);
|
|
798
|
+
this.name = "CircularDependencyError";
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
class DuplicateKeyError extends Error {
|
|
803
|
+
constructor(where, key, value) {
|
|
804
|
+
super(`Could not add ${where} key "${key}"${value ? ` with value ${valueString(value)}` : ""} because it already exists`);
|
|
805
|
+
this.name = "DuplicateKeyError";
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
class InvalidCallbackError extends TypeError {
|
|
810
|
+
constructor(where, value) {
|
|
811
|
+
super(`Invalid ${where} callback ${valueString(value)}`);
|
|
812
|
+
this.name = "InvalidCallbackError";
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
class InvalidCollectionSourceError extends TypeError {
|
|
817
|
+
constructor(where, value) {
|
|
818
|
+
super(`Invalid ${where} source ${valueString(value)}`);
|
|
819
|
+
this.name = "InvalidCollectionSourceError";
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
class InvalidSignalValueError extends TypeError {
|
|
824
|
+
constructor(where, value) {
|
|
825
|
+
super(`Invalid signal value ${valueString(value)} in ${where}`);
|
|
826
|
+
this.name = "InvalidSignalValueError";
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
class NullishSignalValueError extends TypeError {
|
|
831
|
+
constructor(where) {
|
|
832
|
+
super(`Nullish signal values are not allowed in ${where}`);
|
|
833
|
+
this.name = "NullishSignalValueError";
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
class ReadonlySignalError extends Error {
|
|
838
|
+
constructor(what, value) {
|
|
839
|
+
super(`Could not set ${what} to ${valueString(value)} because signal is read-only`);
|
|
840
|
+
this.name = "ReadonlySignalError";
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
var createError = (reason) => reason instanceof Error ? reason : Error(String(reason));
|
|
844
|
+
var validateCallback = (where, value, guard = isFunction) => {
|
|
845
|
+
if (!guard(value))
|
|
846
|
+
throw new InvalidCallbackError(where, value);
|
|
847
|
+
};
|
|
848
|
+
var validateSignalValue = (where, value, guard = () => !(isSymbol(value) && value !== UNSET) || isFunction(value)) => {
|
|
849
|
+
if (value == null)
|
|
850
|
+
throw new NullishSignalValueError(where);
|
|
851
|
+
if (!guard(value))
|
|
852
|
+
throw new InvalidSignalValueError(where, value);
|
|
853
|
+
};
|
|
854
|
+
var guardMutableSignal = (what, value, signal) => {
|
|
855
|
+
if (!isMutableSignal(signal))
|
|
856
|
+
throw new ReadonlySignalError(what, value);
|
|
857
|
+
return true;
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
// src/classes/collection.ts
|
|
861
|
+
var TYPE_COLLECTION = "Collection";
|
|
862
|
+
|
|
863
|
+
class DerivedCollection {
|
|
864
|
+
#watchers = new Set;
|
|
865
|
+
#source;
|
|
866
|
+
#callback;
|
|
867
|
+
#signals = new Map;
|
|
868
|
+
#ownWatchers = new Map;
|
|
869
|
+
#listeners = {
|
|
870
|
+
add: new Set,
|
|
871
|
+
change: new Set,
|
|
872
|
+
remove: new Set,
|
|
873
|
+
sort: new Set
|
|
874
|
+
};
|
|
875
|
+
#order = [];
|
|
876
|
+
constructor(source, callback) {
|
|
877
|
+
validateCallback("collection", callback);
|
|
878
|
+
if (isFunction(source))
|
|
879
|
+
source = source();
|
|
880
|
+
if (!isCollectionSource(source))
|
|
881
|
+
throw new InvalidCollectionSourceError("derived collection", source);
|
|
882
|
+
this.#source = source;
|
|
883
|
+
this.#callback = callback;
|
|
884
|
+
for (let i = 0;i < this.#source.length; i++) {
|
|
885
|
+
const key = this.#source.keyAt(i);
|
|
886
|
+
if (!key)
|
|
887
|
+
continue;
|
|
888
|
+
this.#add(key);
|
|
889
|
+
}
|
|
890
|
+
this.#source.on("add", (additions) => {
|
|
891
|
+
for (const key of additions) {
|
|
892
|
+
if (!this.#signals.has(key)) {
|
|
893
|
+
this.#add(key);
|
|
894
|
+
const signal = this.#signals.get(key);
|
|
895
|
+
if (signal && isAsyncCollectionCallback(this.#callback))
|
|
896
|
+
signal.get();
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
notifyWatchers(this.#watchers);
|
|
900
|
+
emitNotification(this.#listeners.add, additions);
|
|
901
|
+
});
|
|
902
|
+
this.#source.on("remove", (removals) => {
|
|
903
|
+
for (const key of removals) {
|
|
904
|
+
if (!this.#signals.has(key))
|
|
905
|
+
continue;
|
|
906
|
+
this.#signals.delete(key);
|
|
907
|
+
const index = this.#order.indexOf(key);
|
|
908
|
+
if (index >= 0)
|
|
909
|
+
this.#order.splice(index, 1);
|
|
910
|
+
const watcher = this.#ownWatchers.get(key);
|
|
911
|
+
if (watcher) {
|
|
912
|
+
watcher.stop();
|
|
913
|
+
this.#ownWatchers.delete(key);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
this.#order = this.#order.filter(() => true);
|
|
917
|
+
notifyWatchers(this.#watchers);
|
|
918
|
+
emitNotification(this.#listeners.remove, removals);
|
|
919
|
+
});
|
|
920
|
+
this.#source.on("sort", (newOrder) => {
|
|
921
|
+
this.#order = [...newOrder];
|
|
922
|
+
notifyWatchers(this.#watchers);
|
|
923
|
+
emitNotification(this.#listeners.sort, newOrder);
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
#add(key) {
|
|
927
|
+
const computedCallback = isAsyncCollectionCallback(this.#callback) ? async (_, abort) => {
|
|
928
|
+
const sourceSignal = this.#source.byKey(key);
|
|
929
|
+
if (!sourceSignal)
|
|
930
|
+
return UNSET;
|
|
931
|
+
const sourceValue = sourceSignal.get();
|
|
932
|
+
if (sourceValue === UNSET)
|
|
933
|
+
return UNSET;
|
|
934
|
+
return this.#callback(sourceValue, abort);
|
|
935
|
+
} : () => {
|
|
936
|
+
const sourceSignal = this.#source.byKey(key);
|
|
937
|
+
if (!sourceSignal)
|
|
938
|
+
return UNSET;
|
|
939
|
+
const sourceValue = sourceSignal.get();
|
|
940
|
+
if (sourceValue === UNSET)
|
|
941
|
+
return UNSET;
|
|
942
|
+
return this.#callback(sourceValue);
|
|
943
|
+
};
|
|
944
|
+
const signal = createComputed(computedCallback);
|
|
945
|
+
this.#signals.set(key, signal);
|
|
946
|
+
if (!this.#order.includes(key))
|
|
947
|
+
this.#order.push(key);
|
|
948
|
+
if (this.#listeners.change.size)
|
|
949
|
+
this.#addWatcher(key);
|
|
950
|
+
return true;
|
|
951
|
+
}
|
|
952
|
+
#addWatcher(key) {
|
|
953
|
+
const watcher = createWatcher(() => {
|
|
954
|
+
trackSignalReads(watcher, () => {
|
|
955
|
+
this.#signals.get(key)?.get();
|
|
956
|
+
});
|
|
957
|
+
});
|
|
958
|
+
this.#ownWatchers.set(key, watcher);
|
|
959
|
+
watcher();
|
|
960
|
+
}
|
|
961
|
+
get [Symbol.toStringTag]() {
|
|
962
|
+
return TYPE_COLLECTION;
|
|
963
|
+
}
|
|
964
|
+
get [Symbol.isConcatSpreadable]() {
|
|
965
|
+
return true;
|
|
966
|
+
}
|
|
967
|
+
*[Symbol.iterator]() {
|
|
968
|
+
for (const key of this.#order) {
|
|
969
|
+
const signal = this.#signals.get(key);
|
|
970
|
+
if (signal)
|
|
971
|
+
yield signal;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
get length() {
|
|
975
|
+
subscribeActiveWatcher(this.#watchers);
|
|
976
|
+
return this.#order.length;
|
|
977
|
+
}
|
|
978
|
+
get() {
|
|
979
|
+
subscribeActiveWatcher(this.#watchers);
|
|
980
|
+
return this.#order.map((key) => this.#signals.get(key)?.get()).filter((v) => v != null && v !== UNSET);
|
|
981
|
+
}
|
|
982
|
+
at(index) {
|
|
983
|
+
return this.#signals.get(this.#order[index]);
|
|
984
|
+
}
|
|
985
|
+
keys() {
|
|
986
|
+
return this.#order.values();
|
|
987
|
+
}
|
|
988
|
+
byKey(key) {
|
|
989
|
+
return this.#signals.get(key);
|
|
990
|
+
}
|
|
991
|
+
keyAt(index) {
|
|
992
|
+
return this.#order[index];
|
|
993
|
+
}
|
|
994
|
+
indexOfKey(key) {
|
|
995
|
+
return this.#order.indexOf(key);
|
|
996
|
+
}
|
|
997
|
+
on(type, listener) {
|
|
998
|
+
this.#listeners[type].add(listener);
|
|
999
|
+
if (type === "change" && !this.#ownWatchers.size) {
|
|
1000
|
+
for (const key of this.#signals.keys())
|
|
1001
|
+
this.#addWatcher(key);
|
|
1002
|
+
}
|
|
1003
|
+
return () => {
|
|
1004
|
+
this.#listeners[type].delete(listener);
|
|
1005
|
+
if (type === "change" && !this.#listeners.change.size) {
|
|
1006
|
+
if (this.#ownWatchers.size) {
|
|
1007
|
+
for (const watcher of this.#ownWatchers.values())
|
|
1008
|
+
watcher.stop();
|
|
1009
|
+
this.#ownWatchers.clear();
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
deriveCollection(callback) {
|
|
1015
|
+
return new DerivedCollection(this, callback);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
var isCollection = (value) => isObjectOfType(value, TYPE_COLLECTION);
|
|
1019
|
+
var isCollectionSource = (value) => isList(value) || isCollection(value);
|
|
1020
|
+
var isAsyncCollectionCallback = (callback) => isAsyncFunction(callback);
|
|
1021
|
+
// src/classes/ref.ts
|
|
1022
|
+
var TYPE_REF = "Ref";
|
|
1023
|
+
|
|
1024
|
+
class Ref {
|
|
1025
|
+
#watchers = new Set;
|
|
1026
|
+
#value;
|
|
1027
|
+
constructor(value, guard) {
|
|
1028
|
+
validateSignalValue("ref", value, guard);
|
|
1029
|
+
this.#value = value;
|
|
1030
|
+
}
|
|
1031
|
+
get [Symbol.toStringTag]() {
|
|
1032
|
+
return TYPE_REF;
|
|
1033
|
+
}
|
|
1034
|
+
get() {
|
|
1035
|
+
subscribeActiveWatcher(this.#watchers);
|
|
1036
|
+
return this.#value;
|
|
1037
|
+
}
|
|
1038
|
+
notify() {
|
|
1039
|
+
notifyWatchers(this.#watchers);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
var isRef = (value) => isObjectOfType(value, TYPE_REF);
|
|
337
1043
|
// src/effect.ts
|
|
338
1044
|
var createEffect = (callback) => {
|
|
339
1045
|
if (!isFunction(callback) || callback.length > 1)
|
|
340
|
-
throw new InvalidCallbackError("effect",
|
|
1046
|
+
throw new InvalidCallbackError("effect", callback);
|
|
341
1047
|
const isAsync = isAsyncFunction(callback);
|
|
342
1048
|
let running = false;
|
|
343
1049
|
let controller;
|
|
344
|
-
const watcher = createWatcher(() =>
|
|
1050
|
+
const watcher = createWatcher(() => trackSignalReads(watcher, () => {
|
|
345
1051
|
if (running)
|
|
346
1052
|
throw new CircularDependencyError("effect");
|
|
347
1053
|
running = true;
|
|
@@ -354,7 +1060,7 @@ var createEffect = (callback) => {
|
|
|
354
1060
|
const currentController = controller;
|
|
355
1061
|
callback(controller.signal).then((cleanup2) => {
|
|
356
1062
|
if (isFunction(cleanup2) && controller === currentController)
|
|
357
|
-
watcher.
|
|
1063
|
+
watcher.onCleanup(cleanup2);
|
|
358
1064
|
}).catch((error) => {
|
|
359
1065
|
if (!isAbortError(error))
|
|
360
1066
|
console.error("Async effect error:", error);
|
|
@@ -362,18 +1068,18 @@ var createEffect = (callback) => {
|
|
|
362
1068
|
} else {
|
|
363
1069
|
cleanup = callback();
|
|
364
1070
|
if (isFunction(cleanup))
|
|
365
|
-
watcher.
|
|
1071
|
+
watcher.onCleanup(cleanup);
|
|
366
1072
|
}
|
|
367
1073
|
} catch (error) {
|
|
368
1074
|
if (!isAbortError(error))
|
|
369
1075
|
console.error("Effect callback error:", error);
|
|
370
1076
|
}
|
|
371
1077
|
running = false;
|
|
372
|
-
}
|
|
1078
|
+
}));
|
|
373
1079
|
watcher();
|
|
374
1080
|
return () => {
|
|
375
1081
|
controller?.abort();
|
|
376
|
-
watcher.
|
|
1082
|
+
watcher.stop();
|
|
377
1083
|
};
|
|
378
1084
|
};
|
|
379
1085
|
// src/match.ts
|
|
@@ -385,9 +1091,10 @@ function match(result, handlers) {
|
|
|
385
1091
|
handlers.err?.(result.errors);
|
|
386
1092
|
else if (result.ok)
|
|
387
1093
|
handlers.ok(result.values);
|
|
388
|
-
} catch (
|
|
389
|
-
|
|
390
|
-
|
|
1094
|
+
} catch (e) {
|
|
1095
|
+
const error = createError(e);
|
|
1096
|
+
if (handlers.err && (!result.errors || !result.errors.includes(error)))
|
|
1097
|
+
handlers.err(result.errors ? [...result.errors, error] : [error]);
|
|
391
1098
|
else
|
|
392
1099
|
throw error;
|
|
393
1100
|
}
|
|
@@ -405,7 +1112,7 @@ function resolve(signals) {
|
|
|
405
1112
|
else
|
|
406
1113
|
values[key] = value;
|
|
407
1114
|
} catch (e) {
|
|
408
|
-
errors.push(
|
|
1115
|
+
errors.push(createError(e));
|
|
409
1116
|
}
|
|
410
1117
|
}
|
|
411
1118
|
if (pending)
|
|
@@ -414,343 +1121,65 @@ function resolve(signals) {
|
|
|
414
1121
|
return { ok: false, errors };
|
|
415
1122
|
return { ok: true, values };
|
|
416
1123
|
}
|
|
417
|
-
// src/state.ts
|
|
418
|
-
var TYPE_STATE = "State";
|
|
419
|
-
var createState = (initialValue) => {
|
|
420
|
-
if (initialValue == null)
|
|
421
|
-
throw new NullishSignalValueError("state");
|
|
422
|
-
const watchers = new Set;
|
|
423
|
-
let value = initialValue;
|
|
424
|
-
const setValue = (newValue) => {
|
|
425
|
-
if (newValue == null)
|
|
426
|
-
throw new NullishSignalValueError("state");
|
|
427
|
-
if (isEqual(value, newValue))
|
|
428
|
-
return;
|
|
429
|
-
value = newValue;
|
|
430
|
-
notify(watchers);
|
|
431
|
-
if (UNSET === value)
|
|
432
|
-
watchers.clear();
|
|
433
|
-
};
|
|
434
|
-
const state = {};
|
|
435
|
-
Object.defineProperties(state, {
|
|
436
|
-
[Symbol.toStringTag]: {
|
|
437
|
-
value: TYPE_STATE
|
|
438
|
-
},
|
|
439
|
-
get: {
|
|
440
|
-
value: () => {
|
|
441
|
-
subscribe(watchers);
|
|
442
|
-
return value;
|
|
443
|
-
}
|
|
444
|
-
},
|
|
445
|
-
set: {
|
|
446
|
-
value: (newValue) => {
|
|
447
|
-
setValue(newValue);
|
|
448
|
-
}
|
|
449
|
-
},
|
|
450
|
-
update: {
|
|
451
|
-
value: (updater) => {
|
|
452
|
-
if (!isFunction(updater))
|
|
453
|
-
throw new InvalidCallbackError("state update", valueString(updater));
|
|
454
|
-
setValue(updater(value));
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
return state;
|
|
459
|
-
};
|
|
460
|
-
var isState = (value) => isObjectOfType(value, TYPE_STATE);
|
|
461
|
-
|
|
462
|
-
// src/store.ts
|
|
463
|
-
var TYPE_STORE = "Store";
|
|
464
|
-
var createStore = (initialValue) => {
|
|
465
|
-
if (initialValue == null)
|
|
466
|
-
throw new NullishSignalValueError("store");
|
|
467
|
-
const watchers = new Set;
|
|
468
|
-
const listeners = {
|
|
469
|
-
add: new Set,
|
|
470
|
-
change: new Set,
|
|
471
|
-
remove: new Set,
|
|
472
|
-
sort: new Set
|
|
473
|
-
};
|
|
474
|
-
const signals = new Map;
|
|
475
|
-
const signalWatchers = new Map;
|
|
476
|
-
const isArrayLike = Array.isArray(initialValue);
|
|
477
|
-
const current = () => {
|
|
478
|
-
const record = {};
|
|
479
|
-
for (const [key, signal] of signals)
|
|
480
|
-
record[key] = signal.get();
|
|
481
|
-
return record;
|
|
482
|
-
};
|
|
483
|
-
const emit = (key, changes) => {
|
|
484
|
-
Object.freeze(changes);
|
|
485
|
-
for (const listener of listeners[key])
|
|
486
|
-
listener(changes);
|
|
487
|
-
};
|
|
488
|
-
const getSortedIndexes = () => Array.from(signals.keys()).map((k) => Number(k)).filter((n) => Number.isInteger(n)).sort((a, b) => a - b);
|
|
489
|
-
const isValidValue = (key, value) => {
|
|
490
|
-
if (value == null)
|
|
491
|
-
throw new NullishSignalValueError(`store for key "${key}"`);
|
|
492
|
-
if (value === UNSET)
|
|
493
|
-
return true;
|
|
494
|
-
if (isSymbol(value) || isFunction(value) || isComputed(value))
|
|
495
|
-
throw new InvalidSignalValueError(`store for key "${key}"`, valueString(value));
|
|
496
|
-
return true;
|
|
497
|
-
};
|
|
498
|
-
const addProperty = (key, value, single = false) => {
|
|
499
|
-
if (!isValidValue(key, value))
|
|
500
|
-
return false;
|
|
501
|
-
const signal = isState(value) || isStore(value) ? value : isRecord(value) || Array.isArray(value) ? createStore(value) : createState(value);
|
|
502
|
-
signals.set(key, signal);
|
|
503
|
-
const watcher = createWatcher(() => observe(() => {
|
|
504
|
-
emit("change", { [key]: signal.get() });
|
|
505
|
-
}, watcher));
|
|
506
|
-
watcher();
|
|
507
|
-
signalWatchers.set(key, watcher);
|
|
508
|
-
if (single) {
|
|
509
|
-
notify(watchers);
|
|
510
|
-
emit("add", { [key]: value });
|
|
511
|
-
}
|
|
512
|
-
return true;
|
|
513
|
-
};
|
|
514
|
-
const removeProperty = (key, single = false) => {
|
|
515
|
-
const ok = signals.delete(key);
|
|
516
|
-
if (ok) {
|
|
517
|
-
const watcher = signalWatchers.get(key);
|
|
518
|
-
if (watcher)
|
|
519
|
-
watcher.cleanup();
|
|
520
|
-
signalWatchers.delete(key);
|
|
521
|
-
}
|
|
522
|
-
if (single) {
|
|
523
|
-
notify(watchers);
|
|
524
|
-
emit("remove", { [key]: UNSET });
|
|
525
|
-
}
|
|
526
|
-
return ok;
|
|
527
|
-
};
|
|
528
|
-
const reconcile = (oldValue, newValue, initialRun) => {
|
|
529
|
-
const changes = diff(oldValue, newValue);
|
|
530
|
-
batch(() => {
|
|
531
|
-
if (Object.keys(changes.add).length) {
|
|
532
|
-
for (const key in changes.add)
|
|
533
|
-
addProperty(key, changes.add[key] ?? UNSET);
|
|
534
|
-
if (initialRun) {
|
|
535
|
-
setTimeout(() => {
|
|
536
|
-
emit("add", changes.add);
|
|
537
|
-
}, 0);
|
|
538
|
-
} else {
|
|
539
|
-
emit("add", changes.add);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
if (Object.keys(changes.change).length) {
|
|
543
|
-
for (const key in changes.change) {
|
|
544
|
-
const value = changes.change[key];
|
|
545
|
-
if (!isValidValue(key, value))
|
|
546
|
-
continue;
|
|
547
|
-
const signal = signals.get(key);
|
|
548
|
-
if (isMutableSignal(signal))
|
|
549
|
-
signal.set(value);
|
|
550
|
-
else
|
|
551
|
-
throw new StoreKeyReadonlyError(key, valueString(value));
|
|
552
|
-
}
|
|
553
|
-
emit("change", changes.change);
|
|
554
|
-
}
|
|
555
|
-
if (Object.keys(changes.remove).length) {
|
|
556
|
-
for (const key in changes.remove)
|
|
557
|
-
removeProperty(key);
|
|
558
|
-
emit("remove", changes.remove);
|
|
559
|
-
}
|
|
560
|
-
});
|
|
561
|
-
return changes.changed;
|
|
562
|
-
};
|
|
563
|
-
reconcile({}, initialValue, true);
|
|
564
|
-
const store = {};
|
|
565
|
-
Object.defineProperties(store, {
|
|
566
|
-
[Symbol.toStringTag]: {
|
|
567
|
-
value: TYPE_STORE
|
|
568
|
-
},
|
|
569
|
-
[Symbol.isConcatSpreadable]: {
|
|
570
|
-
value: isArrayLike
|
|
571
|
-
},
|
|
572
|
-
[Symbol.iterator]: {
|
|
573
|
-
value: isArrayLike ? function* () {
|
|
574
|
-
const indexes = getSortedIndexes();
|
|
575
|
-
for (const index of indexes) {
|
|
576
|
-
const signal = signals.get(String(index));
|
|
577
|
-
if (signal)
|
|
578
|
-
yield signal;
|
|
579
|
-
}
|
|
580
|
-
} : function* () {
|
|
581
|
-
for (const [key, signal] of signals)
|
|
582
|
-
yield [key, signal];
|
|
583
|
-
}
|
|
584
|
-
},
|
|
585
|
-
add: {
|
|
586
|
-
value: isArrayLike ? (v) => {
|
|
587
|
-
addProperty(String(signals.size), v, true);
|
|
588
|
-
} : (k, v) => {
|
|
589
|
-
if (!signals.has(k))
|
|
590
|
-
addProperty(k, v, true);
|
|
591
|
-
else
|
|
592
|
-
throw new StoreKeyExistsError(k, valueString(v));
|
|
593
|
-
}
|
|
594
|
-
},
|
|
595
|
-
get: {
|
|
596
|
-
value: () => {
|
|
597
|
-
subscribe(watchers);
|
|
598
|
-
return recordToArray(current());
|
|
599
|
-
}
|
|
600
|
-
},
|
|
601
|
-
remove: {
|
|
602
|
-
value: isArrayLike ? (index) => {
|
|
603
|
-
const currentArray = recordToArray(current());
|
|
604
|
-
const currentLength = signals.size;
|
|
605
|
-
if (!Array.isArray(currentArray) || index <= -currentLength || index >= currentLength)
|
|
606
|
-
throw new StoreKeyRangeError(index);
|
|
607
|
-
const newArray = [...currentArray];
|
|
608
|
-
newArray.splice(index, 1);
|
|
609
|
-
if (reconcile(currentArray, newArray))
|
|
610
|
-
notify(watchers);
|
|
611
|
-
} : (k) => {
|
|
612
|
-
if (signals.has(k))
|
|
613
|
-
removeProperty(k, true);
|
|
614
|
-
}
|
|
615
|
-
},
|
|
616
|
-
set: {
|
|
617
|
-
value: (v) => {
|
|
618
|
-
if (reconcile(current(), v)) {
|
|
619
|
-
notify(watchers);
|
|
620
|
-
if (UNSET === v)
|
|
621
|
-
watchers.clear();
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
},
|
|
625
|
-
update: {
|
|
626
|
-
value: (fn) => {
|
|
627
|
-
const oldValue = current();
|
|
628
|
-
const newValue = fn(recordToArray(oldValue));
|
|
629
|
-
if (reconcile(oldValue, newValue)) {
|
|
630
|
-
notify(watchers);
|
|
631
|
-
if (UNSET === newValue)
|
|
632
|
-
watchers.clear();
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
},
|
|
636
|
-
sort: {
|
|
637
|
-
value: (compareFn) => {
|
|
638
|
-
const entries = Array.from(signals.entries()).map(([key, signal]) => [key, signal.get()]).sort(compareFn ? (a, b) => compareFn(a[1], b[1]) : (a, b) => String(a[1]).localeCompare(String(b[1])));
|
|
639
|
-
const newOrder = entries.map(([key]) => String(key));
|
|
640
|
-
const newSignals = new Map;
|
|
641
|
-
entries.forEach(([key], newIndex) => {
|
|
642
|
-
const oldKey = String(key);
|
|
643
|
-
const newKey = isArrayLike ? String(newIndex) : String(key);
|
|
644
|
-
const signal = signals.get(oldKey);
|
|
645
|
-
if (signal)
|
|
646
|
-
newSignals.set(newKey, signal);
|
|
647
|
-
});
|
|
648
|
-
signals.clear();
|
|
649
|
-
newSignals.forEach((signal, key) => signals.set(key, signal));
|
|
650
|
-
notify(watchers);
|
|
651
|
-
emit("sort", newOrder);
|
|
652
|
-
}
|
|
653
|
-
},
|
|
654
|
-
on: {
|
|
655
|
-
value: (type, listener) => {
|
|
656
|
-
listeners[type].add(listener);
|
|
657
|
-
return () => listeners[type].delete(listener);
|
|
658
|
-
}
|
|
659
|
-
},
|
|
660
|
-
length: {
|
|
661
|
-
get() {
|
|
662
|
-
subscribe(watchers);
|
|
663
|
-
return signals.size;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
return new Proxy(store, {
|
|
668
|
-
get(target, prop) {
|
|
669
|
-
if (prop in target)
|
|
670
|
-
return Reflect.get(target, prop);
|
|
671
|
-
if (isSymbol(prop))
|
|
672
|
-
return;
|
|
673
|
-
return signals.get(prop);
|
|
674
|
-
},
|
|
675
|
-
has(target, prop) {
|
|
676
|
-
if (prop in target)
|
|
677
|
-
return true;
|
|
678
|
-
return signals.has(String(prop));
|
|
679
|
-
},
|
|
680
|
-
ownKeys(target) {
|
|
681
|
-
const staticKeys = Reflect.ownKeys(target);
|
|
682
|
-
const signalKeys = isArrayLike ? getSortedIndexes().map((key) => String(key)) : Array.from(signals.keys());
|
|
683
|
-
return [...new Set([...signalKeys, ...staticKeys])];
|
|
684
|
-
},
|
|
685
|
-
getOwnPropertyDescriptor(target, prop) {
|
|
686
|
-
if (prop in target)
|
|
687
|
-
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
688
|
-
const signal = signals.get(String(prop));
|
|
689
|
-
return signal ? {
|
|
690
|
-
enumerable: true,
|
|
691
|
-
configurable: true,
|
|
692
|
-
writable: true,
|
|
693
|
-
value: signal
|
|
694
|
-
} : undefined;
|
|
695
|
-
}
|
|
696
|
-
});
|
|
697
|
-
};
|
|
698
|
-
var isStore = (value) => isObjectOfType(value, TYPE_STORE);
|
|
699
|
-
|
|
700
|
-
// src/signal.ts
|
|
701
|
-
var isSignal = (value) => isState(value) || isComputed(value) || isStore(value);
|
|
702
|
-
var isMutableSignal = (value) => isState(value) || isStore(value);
|
|
703
|
-
function toSignal(value) {
|
|
704
|
-
if (isSignal(value))
|
|
705
|
-
return value;
|
|
706
|
-
if (isComputedCallback(value))
|
|
707
|
-
return createComputed(value);
|
|
708
|
-
if (Array.isArray(value) || isRecord(value))
|
|
709
|
-
return createStore(value);
|
|
710
|
-
return createState(value);
|
|
711
|
-
}
|
|
712
1124
|
export {
|
|
713
1125
|
valueString,
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
1126
|
+
validateSignalValue,
|
|
1127
|
+
validateCallback,
|
|
1128
|
+
trackSignalReads,
|
|
1129
|
+
subscribeActiveWatcher,
|
|
717
1130
|
resolve,
|
|
718
|
-
|
|
719
|
-
notify,
|
|
1131
|
+
notifyWatchers,
|
|
720
1132
|
match,
|
|
1133
|
+
isTaskCallback,
|
|
721
1134
|
isSymbol,
|
|
722
1135
|
isString,
|
|
723
1136
|
isStore,
|
|
724
1137
|
isState,
|
|
725
1138
|
isSignal,
|
|
1139
|
+
isRef,
|
|
726
1140
|
isRecordOrArray,
|
|
727
1141
|
isRecord,
|
|
728
1142
|
isObjectOfType,
|
|
729
1143
|
isNumber,
|
|
730
1144
|
isMutableSignal,
|
|
1145
|
+
isMemoCallback,
|
|
1146
|
+
isList,
|
|
731
1147
|
isFunction,
|
|
732
1148
|
isEqual,
|
|
733
|
-
isComputedCallback,
|
|
734
1149
|
isComputed,
|
|
1150
|
+
isCollection,
|
|
735
1151
|
isAsyncFunction,
|
|
736
1152
|
isAbortError,
|
|
737
|
-
|
|
1153
|
+
guardMutableSignal,
|
|
1154
|
+
flushPendingReactions,
|
|
1155
|
+
emitNotification,
|
|
738
1156
|
diff,
|
|
739
1157
|
createWatcher,
|
|
740
1158
|
createStore,
|
|
741
|
-
|
|
1159
|
+
createSignal,
|
|
1160
|
+
createError,
|
|
742
1161
|
createEffect,
|
|
743
1162
|
createComputed,
|
|
744
|
-
|
|
1163
|
+
batchSignalWrites,
|
|
745
1164
|
UNSET,
|
|
1165
|
+
Task,
|
|
746
1166
|
TYPE_STORE,
|
|
747
1167
|
TYPE_STATE,
|
|
1168
|
+
TYPE_REF,
|
|
1169
|
+
TYPE_LIST,
|
|
748
1170
|
TYPE_COMPUTED,
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
1171
|
+
TYPE_COLLECTION,
|
|
1172
|
+
State,
|
|
1173
|
+
Ref,
|
|
1174
|
+
ReadonlySignalError,
|
|
752
1175
|
NullishSignalValueError,
|
|
1176
|
+
Memo,
|
|
1177
|
+
List,
|
|
753
1178
|
InvalidSignalValueError,
|
|
1179
|
+
InvalidCollectionSourceError,
|
|
754
1180
|
InvalidCallbackError,
|
|
755
|
-
|
|
1181
|
+
DuplicateKeyError,
|
|
1182
|
+
DerivedCollection,
|
|
1183
|
+
CircularDependencyError,
|
|
1184
|
+
BaseStore
|
|
756
1185
|
};
|