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