jotai-state-tree 0.1.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/LICENSE +21 -0
- package/README.md +168 -0
- package/dist/chunk-XXZK62DD.mjs +931 -0
- package/dist/index.d.mts +1109 -0
- package/dist/index.d.ts +1109 -0
- package/dist/index.js +3579 -0
- package/dist/index.mjs +2625 -0
- package/dist/react.d.mts +144 -0
- package/dist/react.d.ts +144 -0
- package/dist/react.js +1259 -0
- package/dist/react.mjs +372 -0
- package/package.json +77 -0
- package/src/__tests__/index.test.ts +1371 -0
- package/src/__tests__/memory.test.ts +681 -0
- package/src/__tests__/performance.test.ts +667 -0
- package/src/__tests__/react.react.test.tsx +811 -0
- package/src/__tests__/registry.test.ts +589 -0
- package/src/array.ts +335 -0
- package/src/compat.ts +294 -0
- package/src/index.ts +647 -0
- package/src/lifecycle.ts +580 -0
- package/src/map.ts +276 -0
- package/src/model.ts +832 -0
- package/src/primitives.ts +400 -0
- package/src/react.ts +626 -0
- package/src/registry.ts +741 -0
- package/src/tree.ts +1275 -0
- package/src/types.ts +520 -0
- package/src/undo.ts +566 -0
- package/src/utilities.ts +616 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3579 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
+
|
|
23
|
+
// src/tree.ts
|
|
24
|
+
var tree_exports = {};
|
|
25
|
+
__export(tree_exports, {
|
|
26
|
+
$treenode: () => $treenode,
|
|
27
|
+
StateTreeNode: () => StateTreeNode,
|
|
28
|
+
applyPatch: () => applyPatch,
|
|
29
|
+
applySnapshot: () => applySnapshot,
|
|
30
|
+
applySnapshotToNode: () => applySnapshotToNode,
|
|
31
|
+
cleanupStaleEntries: () => cleanupStaleEntries,
|
|
32
|
+
clearAllRegistries: () => clearAllRegistries,
|
|
33
|
+
clone: () => clone,
|
|
34
|
+
cloneDeep: () => cloneDeep,
|
|
35
|
+
destroy: () => destroy,
|
|
36
|
+
detach: () => detach,
|
|
37
|
+
findAll: () => findAll,
|
|
38
|
+
findFirst: () => findFirst,
|
|
39
|
+
freeze: () => freeze,
|
|
40
|
+
getEnv: () => getEnv,
|
|
41
|
+
getGlobalStore: () => getGlobalStore,
|
|
42
|
+
getIdentifier: () => getIdentifier,
|
|
43
|
+
getMembers: () => getMembers,
|
|
44
|
+
getNodesOfType: () => getNodesOfType,
|
|
45
|
+
getOrCreatePath: () => getOrCreatePath,
|
|
46
|
+
getParent: () => getParent,
|
|
47
|
+
getParentOfType: () => getParentOfType,
|
|
48
|
+
getPath: () => getPath,
|
|
49
|
+
getPathParts: () => getPathParts,
|
|
50
|
+
getRegistryStats: () => getRegistryStats,
|
|
51
|
+
getRelativePath: () => getRelativePath,
|
|
52
|
+
getRoot: () => getRoot,
|
|
53
|
+
getSnapshot: () => getSnapshot,
|
|
54
|
+
getSnapshotFromNode: () => getSnapshotFromNode,
|
|
55
|
+
getStateTreeNode: () => getStateTreeNode,
|
|
56
|
+
getTreeStats: () => getTreeStats,
|
|
57
|
+
getType: () => getType,
|
|
58
|
+
hasParent: () => hasParent,
|
|
59
|
+
hasStateTreeNode: () => hasStateTreeNode,
|
|
60
|
+
haveSameRoot: () => haveSameRoot,
|
|
61
|
+
isAlive: () => isAlive,
|
|
62
|
+
isAncestor: () => isAncestor,
|
|
63
|
+
isFrozen: () => isFrozen,
|
|
64
|
+
isRoot: () => isRoot,
|
|
65
|
+
isStateTreeNode: () => isStateTreeNode,
|
|
66
|
+
isValidReference: () => isValidReference,
|
|
67
|
+
onAction: () => onAction,
|
|
68
|
+
onLifecycleChange: () => onLifecycleChange,
|
|
69
|
+
onPatch: () => onPatch,
|
|
70
|
+
onSnapshot: () => onSnapshot,
|
|
71
|
+
recordPatches: () => recordPatches,
|
|
72
|
+
registerActionRecorderHook: () => registerActionRecorderHook,
|
|
73
|
+
resetGlobalStore: () => resetGlobalStore,
|
|
74
|
+
resolveIdentifier: () => resolveIdentifier,
|
|
75
|
+
resolvePath: () => resolvePath,
|
|
76
|
+
setGlobalStore: () => setGlobalStore,
|
|
77
|
+
trackAction: () => trackAction,
|
|
78
|
+
tryGetParent: () => tryGetParent,
|
|
79
|
+
tryResolve: () => tryResolve,
|
|
80
|
+
unfreeze: () => unfreeze,
|
|
81
|
+
walk: () => walk
|
|
82
|
+
});
|
|
83
|
+
function getGlobalStore() {
|
|
84
|
+
return globalStore;
|
|
85
|
+
}
|
|
86
|
+
function setGlobalStore(store) {
|
|
87
|
+
globalStore = store;
|
|
88
|
+
}
|
|
89
|
+
function resetGlobalStore() {
|
|
90
|
+
globalStore = (0, import_jotai.createStore)();
|
|
91
|
+
}
|
|
92
|
+
function generateNodeId() {
|
|
93
|
+
return `node_${++nodeIdCounter}_${Date.now().toString(36)}`;
|
|
94
|
+
}
|
|
95
|
+
function onLifecycleChange(node, listener) {
|
|
96
|
+
let listeners = lifecycleListeners.get(node);
|
|
97
|
+
if (!listeners) {
|
|
98
|
+
listeners = /* @__PURE__ */ new Set();
|
|
99
|
+
lifecycleListeners.set(node, listeners);
|
|
100
|
+
}
|
|
101
|
+
listeners.add(listener);
|
|
102
|
+
return () => {
|
|
103
|
+
listeners?.delete(listener);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function notifyLifecycleChange(node, isAlive2) {
|
|
107
|
+
const listeners = lifecycleListeners.get(node);
|
|
108
|
+
if (listeners) {
|
|
109
|
+
listeners.forEach((listener) => listener(isAlive2));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function getStateTreeNode(instance) {
|
|
113
|
+
if (instance && typeof instance === "object" && $treenode in instance) {
|
|
114
|
+
return instance[$treenode];
|
|
115
|
+
}
|
|
116
|
+
throw new Error("[jotai-state-tree] Value is not a state tree node");
|
|
117
|
+
}
|
|
118
|
+
function hasStateTreeNode(instance) {
|
|
119
|
+
return instance !== null && typeof instance === "object" && $treenode in instance;
|
|
120
|
+
}
|
|
121
|
+
function getSnapshotFromNode(node) {
|
|
122
|
+
const type = node.$type;
|
|
123
|
+
const value = node.getValue();
|
|
124
|
+
if (type._kind === "model") {
|
|
125
|
+
const snapshot = {};
|
|
126
|
+
const children = node.getChildren();
|
|
127
|
+
for (const [key, childNode] of children) {
|
|
128
|
+
snapshot[key] = getSnapshotFromNode(childNode);
|
|
129
|
+
}
|
|
130
|
+
if (node.postProcessor) {
|
|
131
|
+
return node.postProcessor(snapshot);
|
|
132
|
+
}
|
|
133
|
+
return snapshot;
|
|
134
|
+
}
|
|
135
|
+
if (type._kind === "array") {
|
|
136
|
+
const arr = value;
|
|
137
|
+
return arr.map((_, index) => {
|
|
138
|
+
const childNode = node.getChild(String(index));
|
|
139
|
+
return childNode ? getSnapshotFromNode(childNode) : arr[index];
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (type._kind === "map") {
|
|
143
|
+
const snapshot = {};
|
|
144
|
+
const children = node.getChildren();
|
|
145
|
+
for (const [key, childNode] of children) {
|
|
146
|
+
snapshot[key] = getSnapshotFromNode(childNode);
|
|
147
|
+
}
|
|
148
|
+
return snapshot;
|
|
149
|
+
}
|
|
150
|
+
if (type._kind === "reference") {
|
|
151
|
+
return node.identifierValue ?? value;
|
|
152
|
+
}
|
|
153
|
+
return value;
|
|
154
|
+
}
|
|
155
|
+
function applySnapshotToNode(node, snapshot) {
|
|
156
|
+
if (!node.$isAlive) {
|
|
157
|
+
throw new Error("[jotai-state-tree] Cannot apply snapshot to a dead node");
|
|
158
|
+
}
|
|
159
|
+
const type = node.$type;
|
|
160
|
+
if (node.preProcessor) {
|
|
161
|
+
snapshot = node.preProcessor(snapshot);
|
|
162
|
+
}
|
|
163
|
+
if (type._kind === "model" && typeof snapshot === "object" && snapshot !== null) {
|
|
164
|
+
const snapshotObj = snapshot;
|
|
165
|
+
const children = node.getChildren();
|
|
166
|
+
for (const [key, childNode] of children) {
|
|
167
|
+
if (key in snapshotObj) {
|
|
168
|
+
applySnapshotToNode(childNode, snapshotObj[key]);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} else if (type._kind === "array" && Array.isArray(snapshot)) {
|
|
172
|
+
node.setValue(snapshot);
|
|
173
|
+
} else if (type._kind === "map" && typeof snapshot === "object" && snapshot !== null) {
|
|
174
|
+
node.setValue(snapshot);
|
|
175
|
+
} else {
|
|
176
|
+
node.setValue(snapshot);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function resolveIdentifier(typeName, identifier2) {
|
|
180
|
+
const weakRef = identifierRegistry.get(typeName)?.get(identifier2);
|
|
181
|
+
return weakRef?.deref();
|
|
182
|
+
}
|
|
183
|
+
function getNodesOfType(typeName) {
|
|
184
|
+
const typeMap = identifierRegistry.get(typeName);
|
|
185
|
+
if (!typeMap) return [];
|
|
186
|
+
const nodes = [];
|
|
187
|
+
for (const weakRef of typeMap.values()) {
|
|
188
|
+
const node = weakRef.deref();
|
|
189
|
+
if (node) {
|
|
190
|
+
nodes.push(node);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return nodes;
|
|
194
|
+
}
|
|
195
|
+
function getRegistryStats() {
|
|
196
|
+
let liveNodeCount = 0;
|
|
197
|
+
let staleNodeCount = 0;
|
|
198
|
+
for (const entry of nodeRegistry.values()) {
|
|
199
|
+
const node = entry.node.deref();
|
|
200
|
+
if (node && node.$isAlive) {
|
|
201
|
+
liveNodeCount++;
|
|
202
|
+
} else {
|
|
203
|
+
staleNodeCount++;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
let identifierCount = 0;
|
|
207
|
+
for (const typeMap of identifierRegistry.values()) {
|
|
208
|
+
for (const weakRef of typeMap.values()) {
|
|
209
|
+
const node = weakRef.deref();
|
|
210
|
+
if (node && node.$isAlive) {
|
|
211
|
+
identifierCount++;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
nodeRegistrySize: nodeRegistry.size,
|
|
217
|
+
identifierRegistrySize: identifierCount,
|
|
218
|
+
identifierTypeCount: identifierRegistry.size,
|
|
219
|
+
liveNodeCount,
|
|
220
|
+
staleNodeCount
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function cleanupStaleEntries() {
|
|
224
|
+
let cleaned = 0;
|
|
225
|
+
for (const [id, entry] of nodeRegistry.entries()) {
|
|
226
|
+
if (!entry.node.deref()) {
|
|
227
|
+
nodeRegistry.delete(id);
|
|
228
|
+
cleaned++;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
for (const [typeName, typeMap] of identifierRegistry.entries()) {
|
|
232
|
+
for (const [identifier2, weakRef] of typeMap.entries()) {
|
|
233
|
+
if (!weakRef.deref()) {
|
|
234
|
+
typeMap.delete(identifier2);
|
|
235
|
+
cleaned++;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (typeMap.size === 0) {
|
|
239
|
+
identifierRegistry.delete(typeName);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return cleaned;
|
|
243
|
+
}
|
|
244
|
+
function clearAllRegistries() {
|
|
245
|
+
for (const entry of nodeRegistry.values()) {
|
|
246
|
+
const node = entry.node.deref();
|
|
247
|
+
if (node) {
|
|
248
|
+
node.$isAlive = false;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
nodeRegistry.clear();
|
|
252
|
+
identifierRegistry.clear();
|
|
253
|
+
nodeIdCounter = 0;
|
|
254
|
+
}
|
|
255
|
+
function getRoot(target) {
|
|
256
|
+
const node = getStateTreeNode(target);
|
|
257
|
+
const rootNode = node.getRoot();
|
|
258
|
+
return rootNode.getInstance();
|
|
259
|
+
}
|
|
260
|
+
function getParent(target, depth = 1) {
|
|
261
|
+
let node = getStateTreeNode(target);
|
|
262
|
+
for (let i = 0; i < depth; i++) {
|
|
263
|
+
if (!node.$parent) {
|
|
264
|
+
throw new Error("[jotai-state-tree] Cannot get parent of root node");
|
|
265
|
+
}
|
|
266
|
+
node = node.$parent;
|
|
267
|
+
}
|
|
268
|
+
return node.getInstance();
|
|
269
|
+
}
|
|
270
|
+
function tryGetParent(target, depth = 1) {
|
|
271
|
+
try {
|
|
272
|
+
return getParent(target, depth);
|
|
273
|
+
} catch {
|
|
274
|
+
return void 0;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function hasParent(target, depth = 1) {
|
|
278
|
+
let node = getStateTreeNode(target);
|
|
279
|
+
for (let i = 0; i < depth; i++) {
|
|
280
|
+
if (!node.$parent) return false;
|
|
281
|
+
node = node.$parent;
|
|
282
|
+
}
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
function getParentOfType(target, type) {
|
|
286
|
+
let node = getStateTreeNode(target).$parent;
|
|
287
|
+
while (node) {
|
|
288
|
+
if (node.$type === type || node.$type.name === type.name) {
|
|
289
|
+
return node.getInstance();
|
|
290
|
+
}
|
|
291
|
+
node = node.$parent;
|
|
292
|
+
}
|
|
293
|
+
throw new Error(`[jotai-state-tree] No parent of type '${type.name}' found`);
|
|
294
|
+
}
|
|
295
|
+
function getPath(target) {
|
|
296
|
+
return getStateTreeNode(target).$path;
|
|
297
|
+
}
|
|
298
|
+
function getPathParts(target) {
|
|
299
|
+
const path = getPath(target);
|
|
300
|
+
return path ? path.split("/").filter(Boolean) : [];
|
|
301
|
+
}
|
|
302
|
+
function getEnv(target) {
|
|
303
|
+
return getStateTreeNode(target).$env;
|
|
304
|
+
}
|
|
305
|
+
function isAlive(target) {
|
|
306
|
+
try {
|
|
307
|
+
return getStateTreeNode(target).$isAlive;
|
|
308
|
+
} catch {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function isRoot(target) {
|
|
313
|
+
return getStateTreeNode(target).$parent === null;
|
|
314
|
+
}
|
|
315
|
+
function getType(target) {
|
|
316
|
+
return getStateTreeNode(target).$type;
|
|
317
|
+
}
|
|
318
|
+
function isStateTreeNode(value) {
|
|
319
|
+
return hasStateTreeNode(value);
|
|
320
|
+
}
|
|
321
|
+
function getIdentifier(target) {
|
|
322
|
+
const node = getStateTreeNode(target);
|
|
323
|
+
return node.identifierValue ?? null;
|
|
324
|
+
}
|
|
325
|
+
function destroy(target) {
|
|
326
|
+
const node = getStateTreeNode(target);
|
|
327
|
+
node.destroy();
|
|
328
|
+
}
|
|
329
|
+
function detach(target) {
|
|
330
|
+
const node = getStateTreeNode(target);
|
|
331
|
+
node.detach();
|
|
332
|
+
return target;
|
|
333
|
+
}
|
|
334
|
+
function clone(target, keepEnvironment = true) {
|
|
335
|
+
const node = getStateTreeNode(target);
|
|
336
|
+
const snapshot = getSnapshotFromNode(node);
|
|
337
|
+
const type = node.$type;
|
|
338
|
+
return type.create(snapshot, keepEnvironment ? node.$env : void 0);
|
|
339
|
+
}
|
|
340
|
+
function getSnapshot(target) {
|
|
341
|
+
const node = getStateTreeNode(target);
|
|
342
|
+
return getSnapshotFromNode(node);
|
|
343
|
+
}
|
|
344
|
+
function applySnapshot(target, snapshot) {
|
|
345
|
+
const node = getStateTreeNode(target);
|
|
346
|
+
applySnapshotToNode(node, snapshot);
|
|
347
|
+
}
|
|
348
|
+
function onSnapshot(target, listener) {
|
|
349
|
+
const node = getStateTreeNode(target);
|
|
350
|
+
return node.onSnapshot(listener);
|
|
351
|
+
}
|
|
352
|
+
function onPatch(target, listener) {
|
|
353
|
+
const node = getStateTreeNode(target);
|
|
354
|
+
return node.onPatch(listener);
|
|
355
|
+
}
|
|
356
|
+
function applyPatch(target, patch) {
|
|
357
|
+
const patches = Array.isArray(patch) ? patch : [patch];
|
|
358
|
+
const rootNode = getStateTreeNode(target).getRoot();
|
|
359
|
+
for (const p of patches) {
|
|
360
|
+
applyPatchToNode(rootNode, p);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function applyPatchToNode(rootNode, patch) {
|
|
364
|
+
const pathParts = patch.path.split("/").filter(Boolean);
|
|
365
|
+
let node = rootNode;
|
|
366
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
367
|
+
const childNode = node.getChild(pathParts[i]);
|
|
368
|
+
if (!childNode) {
|
|
369
|
+
throw new Error(`[jotai-state-tree] Invalid patch path: ${patch.path}`);
|
|
370
|
+
}
|
|
371
|
+
node = childNode;
|
|
372
|
+
}
|
|
373
|
+
const key = pathParts[pathParts.length - 1];
|
|
374
|
+
switch (patch.op) {
|
|
375
|
+
case "replace": {
|
|
376
|
+
const childNode = node.getChild(key);
|
|
377
|
+
if (childNode) {
|
|
378
|
+
applySnapshotToNode(childNode, patch.value);
|
|
379
|
+
} else {
|
|
380
|
+
const currentValue = node.getValue();
|
|
381
|
+
currentValue[key] = patch.value;
|
|
382
|
+
node.setValue(currentValue);
|
|
383
|
+
}
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
case "add": {
|
|
387
|
+
const currentValue = node.getValue();
|
|
388
|
+
if (Array.isArray(currentValue)) {
|
|
389
|
+
const index = key === "-" ? currentValue.length : parseInt(key, 10);
|
|
390
|
+
currentValue.splice(index, 0, patch.value);
|
|
391
|
+
node.setValue([...currentValue]);
|
|
392
|
+
} else if (typeof currentValue === "object" && currentValue !== null) {
|
|
393
|
+
currentValue[key] = patch.value;
|
|
394
|
+
node.setValue({ ...currentValue });
|
|
395
|
+
}
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
case "remove": {
|
|
399
|
+
const currentValue = node.getValue();
|
|
400
|
+
if (Array.isArray(currentValue)) {
|
|
401
|
+
const index = parseInt(key, 10);
|
|
402
|
+
currentValue.splice(index, 1);
|
|
403
|
+
node.setValue([...currentValue]);
|
|
404
|
+
} else if (typeof currentValue === "object" && currentValue !== null) {
|
|
405
|
+
delete currentValue[key];
|
|
406
|
+
node.setValue({ ...currentValue });
|
|
407
|
+
}
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function recordPatches(target) {
|
|
413
|
+
const patches = [];
|
|
414
|
+
const inversePatches = [];
|
|
415
|
+
let recording = true;
|
|
416
|
+
const disposer = onPatch(target, (patch, reversePatch) => {
|
|
417
|
+
if (recording) {
|
|
418
|
+
patches.push(patch);
|
|
419
|
+
inversePatches.push(reversePatch);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
return {
|
|
423
|
+
patches,
|
|
424
|
+
inversePatches,
|
|
425
|
+
stop: () => {
|
|
426
|
+
recording = false;
|
|
427
|
+
disposer();
|
|
428
|
+
},
|
|
429
|
+
resume: () => {
|
|
430
|
+
recording = true;
|
|
431
|
+
},
|
|
432
|
+
replay: (t) => {
|
|
433
|
+
applyPatch(t, patches);
|
|
434
|
+
},
|
|
435
|
+
undo: (t) => {
|
|
436
|
+
applyPatch(t, inversePatches.slice().reverse());
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function registerActionRecorderHook(hook) {
|
|
441
|
+
actionRecorderHooks.push(hook);
|
|
442
|
+
return () => {
|
|
443
|
+
const index = actionRecorderHooks.indexOf(hook);
|
|
444
|
+
if (index >= 0) {
|
|
445
|
+
actionRecorderHooks.splice(index, 1);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function trackAction(node, name, args, fn) {
|
|
450
|
+
const previousAction = currentAction;
|
|
451
|
+
currentAction = { name, args, tree: node };
|
|
452
|
+
try {
|
|
453
|
+
const result = fn();
|
|
454
|
+
const call = {
|
|
455
|
+
name,
|
|
456
|
+
path: node.$path,
|
|
457
|
+
args
|
|
458
|
+
};
|
|
459
|
+
actionListeners.forEach((listener) => listener(call));
|
|
460
|
+
actionRecorderHooks.forEach((hook) => hook(node, call));
|
|
461
|
+
return result;
|
|
462
|
+
} finally {
|
|
463
|
+
currentAction = previousAction;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
function onAction(target, listener) {
|
|
467
|
+
actionListeners.add(listener);
|
|
468
|
+
return () => {
|
|
469
|
+
actionListeners.delete(listener);
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
function walk(target, visitor) {
|
|
473
|
+
const treeNode = getStateTreeNode(target);
|
|
474
|
+
function visitNode(node) {
|
|
475
|
+
const instance = node.getInstance();
|
|
476
|
+
if (instance) {
|
|
477
|
+
visitor(instance);
|
|
478
|
+
}
|
|
479
|
+
node.getChildren().forEach(visitNode);
|
|
480
|
+
}
|
|
481
|
+
visitNode(treeNode);
|
|
482
|
+
}
|
|
483
|
+
function getMembers(target) {
|
|
484
|
+
const result = [];
|
|
485
|
+
const node = getStateTreeNode(target);
|
|
486
|
+
const instance = target;
|
|
487
|
+
for (const [key] of node.getChildren()) {
|
|
488
|
+
result.push({
|
|
489
|
+
name: key,
|
|
490
|
+
type: "property",
|
|
491
|
+
value: instance[key]
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
for (const [key, value] of Object.entries(node.volatileState)) {
|
|
495
|
+
result.push({
|
|
496
|
+
name: key,
|
|
497
|
+
type: "volatile",
|
|
498
|
+
value
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
return result;
|
|
502
|
+
}
|
|
503
|
+
function resolvePath(target, path) {
|
|
504
|
+
const parts = path.split("/").filter(Boolean);
|
|
505
|
+
let node = getStateTreeNode(target);
|
|
506
|
+
for (const part of parts) {
|
|
507
|
+
const child = node.getChild(part);
|
|
508
|
+
if (!child) {
|
|
509
|
+
throw new Error(`[jotai-state-tree] Invalid path: ${path}`);
|
|
510
|
+
}
|
|
511
|
+
node = child;
|
|
512
|
+
}
|
|
513
|
+
return node.getInstance();
|
|
514
|
+
}
|
|
515
|
+
function tryResolve(target, path) {
|
|
516
|
+
try {
|
|
517
|
+
return resolvePath(target, path);
|
|
518
|
+
} catch {
|
|
519
|
+
return void 0;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function getRelativePath(from, to) {
|
|
523
|
+
const fromNode = getStateTreeNode(from);
|
|
524
|
+
const toNode = getStateTreeNode(to);
|
|
525
|
+
const fromParts = fromNode.$path.split("/").filter(Boolean);
|
|
526
|
+
const toParts = toNode.$path.split("/").filter(Boolean);
|
|
527
|
+
let commonLength = 0;
|
|
528
|
+
for (let i = 0; i < Math.min(fromParts.length, toParts.length); i++) {
|
|
529
|
+
if (fromParts[i] === toParts[i]) {
|
|
530
|
+
commonLength++;
|
|
531
|
+
} else {
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
const upCount = fromParts.length - commonLength;
|
|
536
|
+
const downParts = toParts.slice(commonLength);
|
|
537
|
+
const parts = [];
|
|
538
|
+
for (let i = 0; i < upCount; i++) {
|
|
539
|
+
parts.push("..");
|
|
540
|
+
}
|
|
541
|
+
parts.push(...downParts);
|
|
542
|
+
return parts.join("/") || ".";
|
|
543
|
+
}
|
|
544
|
+
function isAncestor(ancestor, descendant) {
|
|
545
|
+
const ancestorNode = getStateTreeNode(ancestor);
|
|
546
|
+
let currentNode = getStateTreeNode(descendant);
|
|
547
|
+
while (currentNode) {
|
|
548
|
+
if (currentNode === ancestorNode) {
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
currentNode = currentNode.$parent;
|
|
552
|
+
}
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
function haveSameRoot(a, b) {
|
|
556
|
+
return getRoot(a) === getRoot(b);
|
|
557
|
+
}
|
|
558
|
+
function findAll(target, predicate) {
|
|
559
|
+
const results = [];
|
|
560
|
+
walk(target, (node) => {
|
|
561
|
+
if (predicate(node)) {
|
|
562
|
+
results.push(node);
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
return results;
|
|
566
|
+
}
|
|
567
|
+
function findFirst(target, predicate) {
|
|
568
|
+
let result;
|
|
569
|
+
walk(target, (node) => {
|
|
570
|
+
if (!result && predicate(node)) {
|
|
571
|
+
result = node;
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
return result;
|
|
575
|
+
}
|
|
576
|
+
function isValidReference(target, identifier2) {
|
|
577
|
+
if (!hasStateTreeNode(target)) return false;
|
|
578
|
+
const node = getStateTreeNode(target);
|
|
579
|
+
const typeName = node.$type.name;
|
|
580
|
+
try {
|
|
581
|
+
const resolved = resolveIdentifier(typeName, identifier2);
|
|
582
|
+
return resolved !== void 0;
|
|
583
|
+
} catch {
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function getTreeStats(target) {
|
|
588
|
+
let nodeCount = 0;
|
|
589
|
+
let maxDepth = 0;
|
|
590
|
+
const types2 = {};
|
|
591
|
+
walk(target, (node) => {
|
|
592
|
+
if (!hasStateTreeNode(node)) return;
|
|
593
|
+
const stateNode = getStateTreeNode(node);
|
|
594
|
+
nodeCount++;
|
|
595
|
+
const depth = stateNode.$path.split("/").filter(Boolean).length;
|
|
596
|
+
maxDepth = Math.max(maxDepth, depth);
|
|
597
|
+
const typeName = stateNode.$type.name;
|
|
598
|
+
types2[typeName] = (types2[typeName] || 0) + 1;
|
|
599
|
+
});
|
|
600
|
+
return {
|
|
601
|
+
nodeCount,
|
|
602
|
+
depth: maxDepth,
|
|
603
|
+
types: types2
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function cloneDeep(target) {
|
|
607
|
+
const snapshot = getSnapshot(target);
|
|
608
|
+
const node = getStateTreeNode(target);
|
|
609
|
+
return node.$type.create(snapshot, node.$env);
|
|
610
|
+
}
|
|
611
|
+
function getOrCreatePath(target, path, creator) {
|
|
612
|
+
const parts = path.split("/").filter(Boolean);
|
|
613
|
+
let node = getStateTreeNode(target);
|
|
614
|
+
for (let i = 0; i < parts.length; i++) {
|
|
615
|
+
const part = parts[i];
|
|
616
|
+
let child = node.getChild(part);
|
|
617
|
+
if (!child && i === parts.length - 1) {
|
|
618
|
+
const instance = creator();
|
|
619
|
+
if (hasStateTreeNode(instance)) {
|
|
620
|
+
child = getStateTreeNode(instance);
|
|
621
|
+
node.addChild(part, child);
|
|
622
|
+
} else {
|
|
623
|
+
throw new Error(
|
|
624
|
+
"[jotai-state-tree] Creator must return a state tree node"
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (!child) {
|
|
629
|
+
throw new Error(`[jotai-state-tree] Invalid path: ${path}`);
|
|
630
|
+
}
|
|
631
|
+
node = child;
|
|
632
|
+
}
|
|
633
|
+
return node.getInstance();
|
|
634
|
+
}
|
|
635
|
+
function freeze(target) {
|
|
636
|
+
const node = getStateTreeNode(target);
|
|
637
|
+
node.volatileState.$frozen = true;
|
|
638
|
+
for (const [, child] of node.getChildren()) {
|
|
639
|
+
const instance = child.getInstance();
|
|
640
|
+
if (instance && hasStateTreeNode(instance)) {
|
|
641
|
+
freeze(instance);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
function isFrozen(target) {
|
|
646
|
+
const node = getStateTreeNode(target);
|
|
647
|
+
return node.volatileState.$frozen === true;
|
|
648
|
+
}
|
|
649
|
+
function unfreeze(target) {
|
|
650
|
+
const node = getStateTreeNode(target);
|
|
651
|
+
delete node.volatileState.$frozen;
|
|
652
|
+
for (const [, child] of node.getChildren()) {
|
|
653
|
+
const instance = child.getInstance();
|
|
654
|
+
if (instance && hasStateTreeNode(instance)) {
|
|
655
|
+
unfreeze(instance);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
var import_jotai, globalStore, nodeRegistry, nodeFinalizationRegistry, identifierRegistry, identifierFinalizationRegistry, nodeIdCounter, lifecycleListeners, StateTreeNode, $treenode, currentAction, actionListeners, actionRecorderHooks;
|
|
660
|
+
var init_tree = __esm({
|
|
661
|
+
"src/tree.ts"() {
|
|
662
|
+
"use strict";
|
|
663
|
+
import_jotai = require("jotai");
|
|
664
|
+
globalStore = (0, import_jotai.createStore)();
|
|
665
|
+
nodeRegistry = /* @__PURE__ */ new Map();
|
|
666
|
+
nodeFinalizationRegistry = new FinalizationRegistry((nodeId) => {
|
|
667
|
+
nodeRegistry.delete(nodeId);
|
|
668
|
+
});
|
|
669
|
+
identifierRegistry = /* @__PURE__ */ new Map();
|
|
670
|
+
identifierFinalizationRegistry = new FinalizationRegistry(
|
|
671
|
+
(info) => {
|
|
672
|
+
const typeMap = identifierRegistry.get(info.typeName);
|
|
673
|
+
if (typeMap) {
|
|
674
|
+
typeMap.delete(info.identifier);
|
|
675
|
+
if (typeMap.size === 0) {
|
|
676
|
+
identifierRegistry.delete(info.typeName);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
);
|
|
681
|
+
nodeIdCounter = 0;
|
|
682
|
+
lifecycleListeners = /* @__PURE__ */ new WeakMap();
|
|
683
|
+
StateTreeNode = class {
|
|
684
|
+
constructor(type, initialValue, env, parent, pathSegment) {
|
|
685
|
+
this.$parent = null;
|
|
686
|
+
this.$path = "";
|
|
687
|
+
this.$isAlive = true;
|
|
688
|
+
/** Child nodes - uses Map but children are explicitly destroyed */
|
|
689
|
+
this.children = /* @__PURE__ */ new Map();
|
|
690
|
+
/** Snapshot listeners */
|
|
691
|
+
this.snapshotListeners = /* @__PURE__ */ new Set();
|
|
692
|
+
/** Patch listeners */
|
|
693
|
+
this.patchListeners = /* @__PURE__ */ new Set();
|
|
694
|
+
/** Volatile state (non-serialized) */
|
|
695
|
+
this.volatileState = {};
|
|
696
|
+
this.$id = generateNodeId();
|
|
697
|
+
this.$type = type;
|
|
698
|
+
this.$env = env ?? parent?.$env;
|
|
699
|
+
this.$parent = parent ?? null;
|
|
700
|
+
this.$path = parent ? `${parent.$path}/${pathSegment}` : "";
|
|
701
|
+
this.valueAtom = (0, import_jotai.atom)(initialValue);
|
|
702
|
+
nodeRegistry.set(this.$id, { node: new WeakRef(this), instance: null });
|
|
703
|
+
nodeFinalizationRegistry.register(this, this.$id, this);
|
|
704
|
+
}
|
|
705
|
+
/** Set the instance reference */
|
|
706
|
+
setInstance(instance) {
|
|
707
|
+
const entry = nodeRegistry.get(this.$id);
|
|
708
|
+
if (entry && instance && typeof instance === "object") {
|
|
709
|
+
entry.instance = new WeakRef(instance);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
/** Get the instance */
|
|
713
|
+
getInstance() {
|
|
714
|
+
const entry = nodeRegistry.get(this.$id);
|
|
715
|
+
return entry?.instance?.deref() ?? null;
|
|
716
|
+
}
|
|
717
|
+
/** Get current value from atom */
|
|
718
|
+
getValue() {
|
|
719
|
+
return globalStore.get(this.valueAtom);
|
|
720
|
+
}
|
|
721
|
+
/** Set value on atom */
|
|
722
|
+
setValue(value) {
|
|
723
|
+
if (!this.$isAlive) {
|
|
724
|
+
throw new Error(
|
|
725
|
+
`[jotai-state-tree] Cannot modify a node that is no longer part of the state tree. (Node type: '${this.$type.name}', Path: '${this.$path}')`
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
const oldValue = this.getValue();
|
|
729
|
+
globalStore.set(this.valueAtom, value);
|
|
730
|
+
this.notifyPatch(
|
|
731
|
+
{ op: "replace", path: this.$path, value },
|
|
732
|
+
{ op: "replace", path: this.$path, value: oldValue, oldValue }
|
|
733
|
+
);
|
|
734
|
+
this.notifySnapshotChange();
|
|
735
|
+
}
|
|
736
|
+
/** Add a child node */
|
|
737
|
+
addChild(key, child) {
|
|
738
|
+
child.$parent = this;
|
|
739
|
+
const newPath = `${this.$path}/${key}`;
|
|
740
|
+
this.updatePathRecursively(child, newPath);
|
|
741
|
+
child.$env = child.$env ?? this.$env;
|
|
742
|
+
this.children.set(key, child);
|
|
743
|
+
}
|
|
744
|
+
/** Recursively update the path of a node and all its children */
|
|
745
|
+
updatePathRecursively(node, newPath) {
|
|
746
|
+
node.$path = newPath;
|
|
747
|
+
for (const [childKey, childNode] of node.children) {
|
|
748
|
+
const childNewPath = `${newPath}/${childKey}`;
|
|
749
|
+
this.updatePathRecursively(childNode, childNewPath);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
/** Remove a child node */
|
|
753
|
+
removeChild(key) {
|
|
754
|
+
const child = this.children.get(key);
|
|
755
|
+
if (child) {
|
|
756
|
+
child.destroy();
|
|
757
|
+
this.children.delete(key);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
/** Get a child node */
|
|
761
|
+
getChild(key) {
|
|
762
|
+
return this.children.get(key);
|
|
763
|
+
}
|
|
764
|
+
/** Get all children */
|
|
765
|
+
getChildren() {
|
|
766
|
+
return this.children;
|
|
767
|
+
}
|
|
768
|
+
/** Register identifier */
|
|
769
|
+
registerIdentifier(typeName, identifier2) {
|
|
770
|
+
this.identifierTypeName = typeName;
|
|
771
|
+
this.identifierValue = identifier2;
|
|
772
|
+
let typeMap = identifierRegistry.get(typeName);
|
|
773
|
+
if (!typeMap) {
|
|
774
|
+
typeMap = /* @__PURE__ */ new Map();
|
|
775
|
+
identifierRegistry.set(typeName, typeMap);
|
|
776
|
+
}
|
|
777
|
+
typeMap.set(identifier2, new WeakRef(this));
|
|
778
|
+
identifierFinalizationRegistry.register(
|
|
779
|
+
this,
|
|
780
|
+
{ typeName, identifier: identifier2 },
|
|
781
|
+
this
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
/** Unregister identifier */
|
|
785
|
+
unregisterIdentifier() {
|
|
786
|
+
if (this.identifierTypeName !== void 0 && this.identifierValue !== void 0) {
|
|
787
|
+
const typeMap = identifierRegistry.get(this.identifierTypeName);
|
|
788
|
+
if (typeMap) {
|
|
789
|
+
typeMap.delete(this.identifierValue);
|
|
790
|
+
if (typeMap.size === 0) {
|
|
791
|
+
identifierRegistry.delete(this.identifierTypeName);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
identifierFinalizationRegistry.unregister(this);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
/** Subscribe to snapshot changes */
|
|
798
|
+
onSnapshot(listener) {
|
|
799
|
+
this.snapshotListeners.add(listener);
|
|
800
|
+
return () => {
|
|
801
|
+
this.snapshotListeners.delete(listener);
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
/** Subscribe to patches */
|
|
805
|
+
onPatch(listener) {
|
|
806
|
+
this.patchListeners.add(listener);
|
|
807
|
+
return () => {
|
|
808
|
+
this.patchListeners.delete(listener);
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
/** Notify patch listeners */
|
|
812
|
+
notifyPatch(patch, reversePatch) {
|
|
813
|
+
this.patchListeners.forEach((listener) => listener(patch, reversePatch));
|
|
814
|
+
if (this.$parent) {
|
|
815
|
+
this.$parent.notifyPatch(patch, reversePatch);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
/** Notify snapshot listeners */
|
|
819
|
+
notifySnapshotChange() {
|
|
820
|
+
const root = this.getRoot();
|
|
821
|
+
const snapshot = getSnapshotFromNode(root);
|
|
822
|
+
root.snapshotListeners.forEach((listener) => listener(snapshot));
|
|
823
|
+
}
|
|
824
|
+
/** Notify about a property change (for use by model proxy) */
|
|
825
|
+
notifyPropertyChange(propName, newValue, oldValue) {
|
|
826
|
+
const path = this.$path ? `${this.$path}/${propName}` : `/${propName}`;
|
|
827
|
+
this.notifyPatch(
|
|
828
|
+
{ op: "replace", path, value: newValue },
|
|
829
|
+
{ op: "replace", path, value: oldValue, oldValue }
|
|
830
|
+
);
|
|
831
|
+
this.notifySnapshotChange();
|
|
832
|
+
}
|
|
833
|
+
/** Get root node */
|
|
834
|
+
getRoot() {
|
|
835
|
+
let node = this;
|
|
836
|
+
while (node.$parent) {
|
|
837
|
+
node = node.$parent;
|
|
838
|
+
}
|
|
839
|
+
return node;
|
|
840
|
+
}
|
|
841
|
+
/** Destroy this node and all children */
|
|
842
|
+
destroy() {
|
|
843
|
+
if (!this.$isAlive) return;
|
|
844
|
+
this.children.forEach((child) => child.destroy());
|
|
845
|
+
this.children.clear();
|
|
846
|
+
this.unregisterIdentifier();
|
|
847
|
+
this.$isAlive = false;
|
|
848
|
+
notifyLifecycleChange(this, false);
|
|
849
|
+
nodeRegistry.delete(this.$id);
|
|
850
|
+
nodeFinalizationRegistry.unregister(this);
|
|
851
|
+
this.snapshotListeners.clear();
|
|
852
|
+
this.patchListeners.clear();
|
|
853
|
+
}
|
|
854
|
+
/** Detach from parent */
|
|
855
|
+
detach() {
|
|
856
|
+
if (this.$parent) {
|
|
857
|
+
for (const [key, child] of this.$parent.children) {
|
|
858
|
+
if (child === this) {
|
|
859
|
+
this.$parent.children.delete(key);
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
this.$parent = null;
|
|
864
|
+
this.$path = "";
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
$treenode = /* @__PURE__ */ Symbol.for("jotai-state-tree-node");
|
|
869
|
+
currentAction = null;
|
|
870
|
+
actionListeners = /* @__PURE__ */ new Set();
|
|
871
|
+
actionRecorderHooks = [];
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
// src/index.ts
|
|
876
|
+
var index_exports = {};
|
|
877
|
+
__export(index_exports, {
|
|
878
|
+
Date: () => DatePrimitive,
|
|
879
|
+
addMiddleware: () => addMiddleware,
|
|
880
|
+
applyAction: () => applyAction,
|
|
881
|
+
applyPatch: () => applyPatch,
|
|
882
|
+
applySnapshot: () => applySnapshot,
|
|
883
|
+
array: () => array,
|
|
884
|
+
boolean: () => boolean,
|
|
885
|
+
cast: () => cast,
|
|
886
|
+
castToReferenceSnapshot: () => castToReferenceSnapshot,
|
|
887
|
+
castToSnapshot: () => castToSnapshot,
|
|
888
|
+
cleanupStaleEntries: () => cleanupStaleEntries,
|
|
889
|
+
clearAllRegistries: () => clearAllRegistries,
|
|
890
|
+
clearModelRegistry: () => clearModelRegistry,
|
|
891
|
+
clone: () => clone,
|
|
892
|
+
cloneDeep: () => cloneDeep,
|
|
893
|
+
cloneFrozen: () => cloneFrozen,
|
|
894
|
+
compose: () => compose,
|
|
895
|
+
createActionRecorder: () => createActionRecorder,
|
|
896
|
+
createTimeTravelManager: () => createTimeTravelManager,
|
|
897
|
+
createUndoManager: () => createUndoManager,
|
|
898
|
+
createWithDefaults: () => createWithDefaults,
|
|
899
|
+
custom: () => custom,
|
|
900
|
+
default: () => index_default,
|
|
901
|
+
destroy: () => destroy,
|
|
902
|
+
detach: () => detach,
|
|
903
|
+
dynamicReference: () => dynamicReference,
|
|
904
|
+
enumeration: () => enumeration,
|
|
905
|
+
escapeJsonPath: () => escapeJsonPath,
|
|
906
|
+
findAll: () => findAll,
|
|
907
|
+
findFirst: () => findFirst,
|
|
908
|
+
finite: () => finite,
|
|
909
|
+
float: () => float,
|
|
910
|
+
flow: () => flow,
|
|
911
|
+
freeze: () => freeze,
|
|
912
|
+
frozen: () => frozen,
|
|
913
|
+
getDebugInfo: () => getDebugInfo,
|
|
914
|
+
getEnv: () => getEnv,
|
|
915
|
+
getGlobalStore: () => getGlobalStore,
|
|
916
|
+
getIdentifier: () => getIdentifier,
|
|
917
|
+
getIdentifierAttribute: () => getIdentifierAttribute,
|
|
918
|
+
getMembers: () => getMembers,
|
|
919
|
+
getModelMetadata: () => getModelMetadata,
|
|
920
|
+
getOrCreate: () => getOrCreate,
|
|
921
|
+
getOrCreatePath: () => getOrCreatePath,
|
|
922
|
+
getParent: () => getParent,
|
|
923
|
+
getParentOfType: () => getParentOfType,
|
|
924
|
+
getPath: () => getPath,
|
|
925
|
+
getPathParts: () => getPathParts,
|
|
926
|
+
getRegisteredModelNames: () => getRegisteredModelNames,
|
|
927
|
+
getRegistryStats: () => getRegistryStats,
|
|
928
|
+
getRelativePath: () => getRelativePath,
|
|
929
|
+
getRoot: () => getRoot,
|
|
930
|
+
getSnapshot: () => getSnapshot,
|
|
931
|
+
getTreeStats: () => getTreeStats,
|
|
932
|
+
getType: () => getType,
|
|
933
|
+
getTypeName: () => getTypeName,
|
|
934
|
+
getValidationError: () => getValidationError,
|
|
935
|
+
hasIdentifier: () => hasIdentifier,
|
|
936
|
+
hasParent: () => hasParent,
|
|
937
|
+
haveSameRoot: () => haveSameRoot,
|
|
938
|
+
identifier: () => identifier,
|
|
939
|
+
identifierNumber: () => identifierNumber,
|
|
940
|
+
integer: () => integer,
|
|
941
|
+
isAlive: () => isAlive,
|
|
942
|
+
isAncestor: () => isAncestor,
|
|
943
|
+
isArrayType: () => isArrayType,
|
|
944
|
+
isFrozen: () => isFrozen,
|
|
945
|
+
isFrozenType: () => isFrozenType,
|
|
946
|
+
isIdentifierType: () => isIdentifierType,
|
|
947
|
+
isInstanceOf: () => isInstanceOf,
|
|
948
|
+
isLateType: () => isLateType,
|
|
949
|
+
isLiteralType: () => isLiteralType,
|
|
950
|
+
isMapType: () => isMapType,
|
|
951
|
+
isModelRegistered: () => isModelRegistered,
|
|
952
|
+
isModelType: () => isModelType,
|
|
953
|
+
isOptionalType: () => isOptionalType,
|
|
954
|
+
isPrimitiveType: () => isPrimitiveType,
|
|
955
|
+
isProtected: () => isProtected,
|
|
956
|
+
isReferenceType: () => isReferenceType,
|
|
957
|
+
isRoot: () => isRoot,
|
|
958
|
+
isStateTreeNode: () => isStateTreeNode,
|
|
959
|
+
isType: () => isType,
|
|
960
|
+
isUnionType: () => isUnionType,
|
|
961
|
+
isValidReference: () => isValidReference,
|
|
962
|
+
isValidSnapshot: () => isValidSnapshot,
|
|
963
|
+
joinJsonPath: () => joinJsonPath,
|
|
964
|
+
late: () => late,
|
|
965
|
+
lateModel: () => lateModel,
|
|
966
|
+
literal: () => literal,
|
|
967
|
+
map: () => map,
|
|
968
|
+
maybe: () => maybe,
|
|
969
|
+
maybeNull: () => maybeNull,
|
|
970
|
+
model: () => model,
|
|
971
|
+
nullType: () => nullType,
|
|
972
|
+
nullable: () => nullable,
|
|
973
|
+
number: () => number,
|
|
974
|
+
onAction: () => onAction,
|
|
975
|
+
onLifecycleChange: () => onLifecycleChange,
|
|
976
|
+
onModelRegistered: () => onModelRegistered,
|
|
977
|
+
onPatch: () => onPatch,
|
|
978
|
+
onSnapshot: () => onSnapshot,
|
|
979
|
+
optional: () => optional,
|
|
980
|
+
printTree: () => printTree,
|
|
981
|
+
protect: () => protect,
|
|
982
|
+
recordActions: () => recordActions,
|
|
983
|
+
recordPatches: () => recordPatches,
|
|
984
|
+
reference: () => reference,
|
|
985
|
+
refinement: () => refinement,
|
|
986
|
+
registerModel: () => registerModel,
|
|
987
|
+
resetGlobalStore: () => resetGlobalStore,
|
|
988
|
+
resolveIdentifier: () => resolveIdentifier,
|
|
989
|
+
resolveModel: () => resolveModel,
|
|
990
|
+
resolveModelAsync: () => resolveModelAsync,
|
|
991
|
+
resolvePath: () => resolvePath,
|
|
992
|
+
safeCreate: () => safeCreate,
|
|
993
|
+
safeDynamicReference: () => safeDynamicReference,
|
|
994
|
+
safeReference: () => safeReference,
|
|
995
|
+
setGlobalStore: () => setGlobalStore,
|
|
996
|
+
snapshotProcessor: () => snapshotProcessor,
|
|
997
|
+
splitJsonPath: () => splitJsonPath,
|
|
998
|
+
string: () => string,
|
|
999
|
+
tryGetParent: () => tryGetParent,
|
|
1000
|
+
tryResolve: () => tryResolve,
|
|
1001
|
+
tryResolveModel: () => tryResolveModel,
|
|
1002
|
+
typecheck: () => typecheck,
|
|
1003
|
+
types: () => types,
|
|
1004
|
+
undefinedType: () => undefinedType,
|
|
1005
|
+
unescapeJsonPath: () => unescapeJsonPath,
|
|
1006
|
+
unfreeze: () => unfreeze,
|
|
1007
|
+
union: () => union,
|
|
1008
|
+
unprotect: () => unprotect,
|
|
1009
|
+
unregisterModel: () => unregisterModel,
|
|
1010
|
+
walk: () => walk
|
|
1011
|
+
});
|
|
1012
|
+
module.exports = __toCommonJS(index_exports);
|
|
1013
|
+
|
|
1014
|
+
// src/primitives.ts
|
|
1015
|
+
function createSimpleType(name, validator, defaultValue) {
|
|
1016
|
+
return {
|
|
1017
|
+
name,
|
|
1018
|
+
_kind: "simple",
|
|
1019
|
+
_C: void 0,
|
|
1020
|
+
_S: void 0,
|
|
1021
|
+
_T: void 0,
|
|
1022
|
+
create(snapshot) {
|
|
1023
|
+
if (snapshot === void 0) {
|
|
1024
|
+
if (defaultValue !== void 0) {
|
|
1025
|
+
return defaultValue;
|
|
1026
|
+
}
|
|
1027
|
+
throw new Error(`[jotai-state-tree] A value of type '${name}' is required`);
|
|
1028
|
+
}
|
|
1029
|
+
if (!validator(snapshot)) {
|
|
1030
|
+
throw new Error(
|
|
1031
|
+
`[jotai-state-tree] Value '${String(snapshot)}' is not a valid '${name}'`
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
return snapshot;
|
|
1035
|
+
},
|
|
1036
|
+
is(value) {
|
|
1037
|
+
return validator(value);
|
|
1038
|
+
},
|
|
1039
|
+
validate(value, context) {
|
|
1040
|
+
if (validator(value)) {
|
|
1041
|
+
return { valid: true, errors: [] };
|
|
1042
|
+
}
|
|
1043
|
+
return {
|
|
1044
|
+
valid: false,
|
|
1045
|
+
errors: [
|
|
1046
|
+
{
|
|
1047
|
+
context,
|
|
1048
|
+
value,
|
|
1049
|
+
message: `Value '${String(value)}' is not a valid '${name}'`
|
|
1050
|
+
}
|
|
1051
|
+
]
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
var string = createSimpleType(
|
|
1057
|
+
"string",
|
|
1058
|
+
(value) => typeof value === "string"
|
|
1059
|
+
);
|
|
1060
|
+
var number = createSimpleType(
|
|
1061
|
+
"number",
|
|
1062
|
+
(value) => typeof value === "number" && !isNaN(value)
|
|
1063
|
+
);
|
|
1064
|
+
var integer = createSimpleType(
|
|
1065
|
+
"integer",
|
|
1066
|
+
(value) => typeof value === "number" && !isNaN(value) && Number.isInteger(value)
|
|
1067
|
+
);
|
|
1068
|
+
var boolean = createSimpleType(
|
|
1069
|
+
"boolean",
|
|
1070
|
+
(value) => typeof value === "boolean"
|
|
1071
|
+
);
|
|
1072
|
+
var DatePrimitive = {
|
|
1073
|
+
name: "Date",
|
|
1074
|
+
_kind: "simple",
|
|
1075
|
+
_C: void 0,
|
|
1076
|
+
_S: void 0,
|
|
1077
|
+
_T: void 0,
|
|
1078
|
+
create(snapshot) {
|
|
1079
|
+
if (snapshot === void 0) {
|
|
1080
|
+
return /* @__PURE__ */ new Date();
|
|
1081
|
+
}
|
|
1082
|
+
if (snapshot instanceof Date) {
|
|
1083
|
+
return snapshot;
|
|
1084
|
+
}
|
|
1085
|
+
if (typeof snapshot === "number") {
|
|
1086
|
+
return new Date(snapshot);
|
|
1087
|
+
}
|
|
1088
|
+
throw new Error(`[jotai-state-tree] Value is not a valid Date`);
|
|
1089
|
+
},
|
|
1090
|
+
is(value) {
|
|
1091
|
+
return value instanceof Date;
|
|
1092
|
+
},
|
|
1093
|
+
validate(value, context) {
|
|
1094
|
+
if (value instanceof Date || typeof value === "number") {
|
|
1095
|
+
return { valid: true, errors: [] };
|
|
1096
|
+
}
|
|
1097
|
+
return {
|
|
1098
|
+
valid: false,
|
|
1099
|
+
errors: [
|
|
1100
|
+
{
|
|
1101
|
+
context,
|
|
1102
|
+
value,
|
|
1103
|
+
message: "Value is not a valid Date"
|
|
1104
|
+
}
|
|
1105
|
+
]
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
var nullType = createSimpleType(
|
|
1110
|
+
"null",
|
|
1111
|
+
(value) => value === null
|
|
1112
|
+
);
|
|
1113
|
+
var undefinedType = createSimpleType(
|
|
1114
|
+
"undefined",
|
|
1115
|
+
(value) => value === void 0
|
|
1116
|
+
);
|
|
1117
|
+
var identifier = {
|
|
1118
|
+
...createSimpleType(
|
|
1119
|
+
"identifier",
|
|
1120
|
+
(value) => typeof value === "string"
|
|
1121
|
+
),
|
|
1122
|
+
_kind: "identifier",
|
|
1123
|
+
identifierAttribute: "id"
|
|
1124
|
+
};
|
|
1125
|
+
var identifierNumber = {
|
|
1126
|
+
...createSimpleType(
|
|
1127
|
+
"identifierNumber",
|
|
1128
|
+
(value) => typeof value === "number" && !isNaN(value)
|
|
1129
|
+
),
|
|
1130
|
+
_kind: "identifierNumber",
|
|
1131
|
+
identifierAttribute: "id"
|
|
1132
|
+
};
|
|
1133
|
+
function literal(value) {
|
|
1134
|
+
return {
|
|
1135
|
+
name: `literal(${JSON.stringify(value)})`,
|
|
1136
|
+
_kind: "literal",
|
|
1137
|
+
_value: value,
|
|
1138
|
+
_C: void 0,
|
|
1139
|
+
_S: void 0,
|
|
1140
|
+
_T: void 0,
|
|
1141
|
+
create(snapshot) {
|
|
1142
|
+
if (snapshot === void 0) {
|
|
1143
|
+
return value;
|
|
1144
|
+
}
|
|
1145
|
+
if (snapshot !== value) {
|
|
1146
|
+
throw new Error(
|
|
1147
|
+
`[jotai-state-tree] Value '${String(snapshot)}' is not the literal '${String(value)}'`
|
|
1148
|
+
);
|
|
1149
|
+
}
|
|
1150
|
+
return snapshot;
|
|
1151
|
+
},
|
|
1152
|
+
is(v) {
|
|
1153
|
+
return v === value;
|
|
1154
|
+
},
|
|
1155
|
+
validate(v, context) {
|
|
1156
|
+
if (v === value) {
|
|
1157
|
+
return { valid: true, errors: [] };
|
|
1158
|
+
}
|
|
1159
|
+
return {
|
|
1160
|
+
valid: false,
|
|
1161
|
+
errors: [
|
|
1162
|
+
{
|
|
1163
|
+
context,
|
|
1164
|
+
value: v,
|
|
1165
|
+
message: `Value '${String(v)}' is not the literal '${String(value)}'`
|
|
1166
|
+
}
|
|
1167
|
+
]
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
function enumeration(nameOrOptions, maybeOptions) {
|
|
1173
|
+
const name = typeof nameOrOptions === "string" ? nameOrOptions : "enumeration";
|
|
1174
|
+
const options = typeof nameOrOptions === "string" ? maybeOptions : nameOrOptions;
|
|
1175
|
+
const optionSet = new Set(options);
|
|
1176
|
+
return {
|
|
1177
|
+
name,
|
|
1178
|
+
_kind: "enumeration",
|
|
1179
|
+
_options: options,
|
|
1180
|
+
_C: void 0,
|
|
1181
|
+
_S: void 0,
|
|
1182
|
+
_T: void 0,
|
|
1183
|
+
create(snapshot) {
|
|
1184
|
+
if (snapshot === void 0) {
|
|
1185
|
+
throw new Error(`[jotai-state-tree] A value for enumeration '${name}' is required`);
|
|
1186
|
+
}
|
|
1187
|
+
if (!optionSet.has(snapshot)) {
|
|
1188
|
+
throw new Error(
|
|
1189
|
+
`[jotai-state-tree] Value '${snapshot}' is not a valid option for enumeration '${name}'. Expected one of: ${options.join(", ")}`
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
return snapshot;
|
|
1193
|
+
},
|
|
1194
|
+
is(value) {
|
|
1195
|
+
return typeof value === "string" && optionSet.has(value);
|
|
1196
|
+
},
|
|
1197
|
+
validate(value, context) {
|
|
1198
|
+
if (typeof value === "string" && optionSet.has(value)) {
|
|
1199
|
+
return { valid: true, errors: [] };
|
|
1200
|
+
}
|
|
1201
|
+
return {
|
|
1202
|
+
valid: false,
|
|
1203
|
+
errors: [
|
|
1204
|
+
{
|
|
1205
|
+
context,
|
|
1206
|
+
value,
|
|
1207
|
+
message: `Value '${String(value)}' is not a valid option. Expected one of: ${options.join(", ")}`
|
|
1208
|
+
}
|
|
1209
|
+
]
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
function frozen(defaultValue) {
|
|
1215
|
+
return {
|
|
1216
|
+
name: "frozen",
|
|
1217
|
+
_kind: "frozen",
|
|
1218
|
+
_C: void 0,
|
|
1219
|
+
_S: void 0,
|
|
1220
|
+
_T: void 0,
|
|
1221
|
+
create(snapshot) {
|
|
1222
|
+
if (snapshot === void 0) {
|
|
1223
|
+
if (defaultValue !== void 0) {
|
|
1224
|
+
return deepFreeze(structuredClone(defaultValue));
|
|
1225
|
+
}
|
|
1226
|
+
return void 0;
|
|
1227
|
+
}
|
|
1228
|
+
return deepFreeze(structuredClone(snapshot));
|
|
1229
|
+
},
|
|
1230
|
+
is(value) {
|
|
1231
|
+
return true;
|
|
1232
|
+
},
|
|
1233
|
+
validate() {
|
|
1234
|
+
return { valid: true, errors: [] };
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
function deepFreeze(obj) {
|
|
1239
|
+
if (obj === null || typeof obj !== "object") {
|
|
1240
|
+
return obj;
|
|
1241
|
+
}
|
|
1242
|
+
Object.freeze(obj);
|
|
1243
|
+
if (Array.isArray(obj)) {
|
|
1244
|
+
obj.forEach(deepFreeze);
|
|
1245
|
+
} else {
|
|
1246
|
+
Object.values(obj).forEach(deepFreeze);
|
|
1247
|
+
}
|
|
1248
|
+
return obj;
|
|
1249
|
+
}
|
|
1250
|
+
function custom(options) {
|
|
1251
|
+
return {
|
|
1252
|
+
name: options.name,
|
|
1253
|
+
_kind: "simple",
|
|
1254
|
+
_C: void 0,
|
|
1255
|
+
_S: void 0,
|
|
1256
|
+
_T: void 0,
|
|
1257
|
+
create(snapshot) {
|
|
1258
|
+
if (snapshot === void 0) {
|
|
1259
|
+
throw new Error(`[jotai-state-tree] A value for custom type '${options.name}' is required`);
|
|
1260
|
+
}
|
|
1261
|
+
return options.fromSnapshot(snapshot);
|
|
1262
|
+
},
|
|
1263
|
+
is(value) {
|
|
1264
|
+
return options.isTargetType(value);
|
|
1265
|
+
},
|
|
1266
|
+
validate(value, context) {
|
|
1267
|
+
if (options.isTargetType(value)) {
|
|
1268
|
+
return { valid: true, errors: [] };
|
|
1269
|
+
}
|
|
1270
|
+
return {
|
|
1271
|
+
valid: false,
|
|
1272
|
+
errors: [
|
|
1273
|
+
{
|
|
1274
|
+
context,
|
|
1275
|
+
value,
|
|
1276
|
+
message: options.getValidationMessage(value)
|
|
1277
|
+
}
|
|
1278
|
+
]
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
var finite = createSimpleType(
|
|
1284
|
+
"finite",
|
|
1285
|
+
(value) => typeof value === "number" && isFinite(value)
|
|
1286
|
+
);
|
|
1287
|
+
var float = number;
|
|
1288
|
+
|
|
1289
|
+
// src/model.ts
|
|
1290
|
+
var import_jotai2 = require("jotai");
|
|
1291
|
+
init_tree();
|
|
1292
|
+
var MAX_CACHE_SIZE = 100;
|
|
1293
|
+
var LRUCache = class {
|
|
1294
|
+
constructor(maxSize = MAX_CACHE_SIZE) {
|
|
1295
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
1296
|
+
this.maxSize = maxSize;
|
|
1297
|
+
}
|
|
1298
|
+
get(key) {
|
|
1299
|
+
const value = this.cache.get(key);
|
|
1300
|
+
if (value !== void 0) {
|
|
1301
|
+
this.cache.delete(key);
|
|
1302
|
+
this.cache.set(key, value);
|
|
1303
|
+
}
|
|
1304
|
+
return value;
|
|
1305
|
+
}
|
|
1306
|
+
set(key, value) {
|
|
1307
|
+
if (this.cache.has(key)) {
|
|
1308
|
+
this.cache.delete(key);
|
|
1309
|
+
} else if (this.cache.size >= this.maxSize) {
|
|
1310
|
+
const firstKey = this.cache.keys().next().value;
|
|
1311
|
+
if (firstKey !== void 0) {
|
|
1312
|
+
this.cache.delete(firstKey);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
this.cache.set(key, value);
|
|
1316
|
+
}
|
|
1317
|
+
has(key) {
|
|
1318
|
+
return this.cache.has(key);
|
|
1319
|
+
}
|
|
1320
|
+
clear() {
|
|
1321
|
+
this.cache.clear();
|
|
1322
|
+
}
|
|
1323
|
+
get size() {
|
|
1324
|
+
return this.cache.size;
|
|
1325
|
+
}
|
|
1326
|
+
};
|
|
1327
|
+
var ModelType = class _ModelType {
|
|
1328
|
+
constructor(config) {
|
|
1329
|
+
this._kind = "model";
|
|
1330
|
+
this.config = config;
|
|
1331
|
+
this.name = config.name;
|
|
1332
|
+
this.properties = config.properties;
|
|
1333
|
+
for (const [key, type] of Object.entries(config.properties)) {
|
|
1334
|
+
if (type._kind === "identifier" || type._kind === "identifierNumber") {
|
|
1335
|
+
this.identifierAttribute = key;
|
|
1336
|
+
break;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
create(snapshot, env) {
|
|
1341
|
+
let processedSnapshot = snapshot ?? {};
|
|
1342
|
+
if (this.config.preProcessor) {
|
|
1343
|
+
processedSnapshot = this.config.preProcessor(
|
|
1344
|
+
processedSnapshot
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
const node = new StateTreeNode(this, processedSnapshot, env);
|
|
1348
|
+
node.preProcessor = this.config.preProcessor;
|
|
1349
|
+
node.postProcessor = this.config.postProcessor;
|
|
1350
|
+
const propertyAtoms = /* @__PURE__ */ new Map();
|
|
1351
|
+
const store = getGlobalStore();
|
|
1352
|
+
for (const [key, propType] of Object.entries(this.properties)) {
|
|
1353
|
+
const type = propType;
|
|
1354
|
+
const initialValue = processedSnapshot?.[key];
|
|
1355
|
+
const isComplexType = type._kind === "model" || type._kind === "array" || type._kind === "map";
|
|
1356
|
+
if (isComplexType) {
|
|
1357
|
+
const childInstance = type.create(initialValue, env);
|
|
1358
|
+
const childNode = getStateTreeNode(childInstance);
|
|
1359
|
+
node.addChild(key, childNode);
|
|
1360
|
+
propertyAtoms.set(key, childNode.valueAtom);
|
|
1361
|
+
} else {
|
|
1362
|
+
const value = type.create(initialValue, env);
|
|
1363
|
+
if (value && typeof value === "object" && $treenode in value) {
|
|
1364
|
+
const childNode = getStateTreeNode(value);
|
|
1365
|
+
node.addChild(key, childNode);
|
|
1366
|
+
propertyAtoms.set(key, childNode.valueAtom);
|
|
1367
|
+
} else {
|
|
1368
|
+
const propAtom = (0, import_jotai2.atom)(value);
|
|
1369
|
+
propertyAtoms.set(key, propAtom);
|
|
1370
|
+
const childNode = new StateTreeNode(type, value, env, node, key);
|
|
1371
|
+
childNode.valueAtom = propAtom;
|
|
1372
|
+
node.addChild(key, childNode);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
const instance = this.createInstanceProxy(node, propertyAtoms, store);
|
|
1377
|
+
if (this.identifierAttribute) {
|
|
1378
|
+
const idValue = processedSnapshot?.[this.identifierAttribute];
|
|
1379
|
+
if (idValue !== void 0) {
|
|
1380
|
+
node.registerIdentifier(this.name, idValue);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
node.setInstance(instance);
|
|
1384
|
+
for (const initializer of this.config.initializers) {
|
|
1385
|
+
initializer(instance);
|
|
1386
|
+
}
|
|
1387
|
+
if (this.config.hooks.afterCreate) {
|
|
1388
|
+
this.config.hooks.afterCreate(instance);
|
|
1389
|
+
}
|
|
1390
|
+
return instance;
|
|
1391
|
+
}
|
|
1392
|
+
createInstanceProxy(node, propertyAtoms, store) {
|
|
1393
|
+
const self = this;
|
|
1394
|
+
const viewCache = new LRUCache(MAX_CACHE_SIZE);
|
|
1395
|
+
const actionCache = new LRUCache(MAX_CACHE_SIZE);
|
|
1396
|
+
const allViews = {};
|
|
1397
|
+
const allActions = {};
|
|
1398
|
+
const volatileState = {};
|
|
1399
|
+
const base = {
|
|
1400
|
+
[$treenode]: node
|
|
1401
|
+
};
|
|
1402
|
+
const proxy = new Proxy(base, {
|
|
1403
|
+
get(target, prop) {
|
|
1404
|
+
if (prop === $treenode) {
|
|
1405
|
+
return node;
|
|
1406
|
+
}
|
|
1407
|
+
if (prop === "$treenode") {
|
|
1408
|
+
return node;
|
|
1409
|
+
}
|
|
1410
|
+
const propStr = String(prop);
|
|
1411
|
+
if (propertyAtoms.has(propStr)) {
|
|
1412
|
+
const childNode = node.getChild(propStr);
|
|
1413
|
+
if (childNode) {
|
|
1414
|
+
const instance = childNode.getInstance();
|
|
1415
|
+
if (instance !== void 0) {
|
|
1416
|
+
if (instance && typeof instance === "object" && $treenode in instance) {
|
|
1417
|
+
return instance;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return store.get(propertyAtoms.get(propStr));
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
if (propStr in node.volatileState) {
|
|
1424
|
+
return node.volatileState[propStr];
|
|
1425
|
+
}
|
|
1426
|
+
if (propStr in allViews) {
|
|
1427
|
+
const descriptor = allViews[propStr];
|
|
1428
|
+
if (descriptor.get) {
|
|
1429
|
+
return descriptor.get.call(proxy);
|
|
1430
|
+
}
|
|
1431
|
+
if (typeof descriptor.value === "function") {
|
|
1432
|
+
return descriptor.value.bind(proxy);
|
|
1433
|
+
}
|
|
1434
|
+
return descriptor.value;
|
|
1435
|
+
}
|
|
1436
|
+
if (propStr in allActions) {
|
|
1437
|
+
return allActions[propStr];
|
|
1438
|
+
}
|
|
1439
|
+
return void 0;
|
|
1440
|
+
},
|
|
1441
|
+
set(target, prop, value) {
|
|
1442
|
+
const propStr = String(prop);
|
|
1443
|
+
if (propertyAtoms.has(propStr)) {
|
|
1444
|
+
const propType = self.properties[propStr];
|
|
1445
|
+
const existingChildNode = node.getChild(propStr);
|
|
1446
|
+
if (propType._kind === "model") {
|
|
1447
|
+
if (existingChildNode) {
|
|
1448
|
+
const { applySnapshotToNode: applySnapshotToNode2 } = (init_tree(), __toCommonJS(tree_exports));
|
|
1449
|
+
applySnapshotToNode2(existingChildNode, value);
|
|
1450
|
+
}
|
|
1451
|
+
return true;
|
|
1452
|
+
}
|
|
1453
|
+
if (propType._kind === "array" || propType._kind === "map") {
|
|
1454
|
+
if (existingChildNode) {
|
|
1455
|
+
existingChildNode.setValue(value);
|
|
1456
|
+
}
|
|
1457
|
+
return true;
|
|
1458
|
+
}
|
|
1459
|
+
const oldValue = existingChildNode?.getValue();
|
|
1460
|
+
if (existingChildNode) {
|
|
1461
|
+
existingChildNode.destroy();
|
|
1462
|
+
node.getChildren().delete(propStr);
|
|
1463
|
+
}
|
|
1464
|
+
const newValue = propType.create(value, node.$env);
|
|
1465
|
+
if (newValue && typeof newValue === "object" && $treenode in newValue) {
|
|
1466
|
+
const newChildNode = getStateTreeNode(newValue);
|
|
1467
|
+
node.addChild(propStr, newChildNode);
|
|
1468
|
+
propertyAtoms.set(propStr, newChildNode.valueAtom);
|
|
1469
|
+
} else {
|
|
1470
|
+
const newChildNode = new StateTreeNode(
|
|
1471
|
+
propType,
|
|
1472
|
+
newValue,
|
|
1473
|
+
node.$env,
|
|
1474
|
+
node,
|
|
1475
|
+
propStr
|
|
1476
|
+
);
|
|
1477
|
+
const propAtom = (0, import_jotai2.atom)(newValue);
|
|
1478
|
+
newChildNode.valueAtom = propAtom;
|
|
1479
|
+
node.addChild(propStr, newChildNode);
|
|
1480
|
+
propertyAtoms.set(propStr, propAtom);
|
|
1481
|
+
store.set(propAtom, newValue);
|
|
1482
|
+
}
|
|
1483
|
+
node.notifyPropertyChange(propStr, newValue, oldValue);
|
|
1484
|
+
return true;
|
|
1485
|
+
}
|
|
1486
|
+
if (propStr in node.volatileState) {
|
|
1487
|
+
node.volatileState[propStr] = value;
|
|
1488
|
+
return true;
|
|
1489
|
+
}
|
|
1490
|
+
return false;
|
|
1491
|
+
},
|
|
1492
|
+
has(target, prop) {
|
|
1493
|
+
const propStr = String(prop);
|
|
1494
|
+
return prop === $treenode || propertyAtoms.has(propStr) || propStr in allViews || propStr in allActions || propStr in node.volatileState;
|
|
1495
|
+
},
|
|
1496
|
+
ownKeys() {
|
|
1497
|
+
return [
|
|
1498
|
+
...propertyAtoms.keys(),
|
|
1499
|
+
...Object.keys(allViews),
|
|
1500
|
+
...Object.keys(allActions),
|
|
1501
|
+
...Object.keys(node.volatileState)
|
|
1502
|
+
];
|
|
1503
|
+
},
|
|
1504
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
1505
|
+
const propStr = String(prop);
|
|
1506
|
+
if (propertyAtoms.has(propStr) || propStr in allViews || propStr in allActions || propStr in node.volatileState) {
|
|
1507
|
+
return {
|
|
1508
|
+
configurable: true,
|
|
1509
|
+
enumerable: true,
|
|
1510
|
+
writable: true
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
return void 0;
|
|
1514
|
+
}
|
|
1515
|
+
});
|
|
1516
|
+
for (const viewFn of this.config.views) {
|
|
1517
|
+
const views = viewFn(proxy);
|
|
1518
|
+
for (const [key, value] of Object.entries(
|
|
1519
|
+
Object.getOwnPropertyDescriptors(views)
|
|
1520
|
+
)) {
|
|
1521
|
+
allViews[key] = value;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
for (const actionFn of this.config.actions) {
|
|
1525
|
+
const actions = actionFn(proxy);
|
|
1526
|
+
for (const [key, value] of Object.entries(actions)) {
|
|
1527
|
+
if (typeof value === "function") {
|
|
1528
|
+
allActions[key] = (...args) => {
|
|
1529
|
+
return trackAction(node, key, args, () => {
|
|
1530
|
+
return value.apply(proxy, args);
|
|
1531
|
+
});
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
for (const volatileFn of this.config.volatiles) {
|
|
1537
|
+
const volatile = volatileFn(proxy);
|
|
1538
|
+
Object.assign(node.volatileState, volatile);
|
|
1539
|
+
}
|
|
1540
|
+
return proxy;
|
|
1541
|
+
}
|
|
1542
|
+
is(value) {
|
|
1543
|
+
if (!value || typeof value !== "object") return false;
|
|
1544
|
+
if (!($treenode in value)) return false;
|
|
1545
|
+
const node = value[$treenode];
|
|
1546
|
+
return node.$type === this || node.$type.name === this.name;
|
|
1547
|
+
}
|
|
1548
|
+
validate(value, context) {
|
|
1549
|
+
const errors = [];
|
|
1550
|
+
if (!value || typeof value !== "object") {
|
|
1551
|
+
return {
|
|
1552
|
+
valid: false,
|
|
1553
|
+
errors: [
|
|
1554
|
+
{
|
|
1555
|
+
context,
|
|
1556
|
+
value,
|
|
1557
|
+
message: `Value is not an object`
|
|
1558
|
+
}
|
|
1559
|
+
]
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
for (const [key, propType] of Object.entries(this.properties)) {
|
|
1563
|
+
const propValue = value[key];
|
|
1564
|
+
const propContext = {
|
|
1565
|
+
path: context.length > 0 ? `${context[0].path}/${key}` : `/${key}`,
|
|
1566
|
+
type: propType,
|
|
1567
|
+
parent: value
|
|
1568
|
+
};
|
|
1569
|
+
const result = propType.validate(propValue, [
|
|
1570
|
+
...context,
|
|
1571
|
+
propContext
|
|
1572
|
+
]);
|
|
1573
|
+
if (!result.valid) {
|
|
1574
|
+
errors.push(...result.errors);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
return {
|
|
1578
|
+
valid: errors.length === 0,
|
|
1579
|
+
errors
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
// ============================================================================
|
|
1583
|
+
// Model Modifiers
|
|
1584
|
+
// ============================================================================
|
|
1585
|
+
named(name) {
|
|
1586
|
+
return new _ModelType({
|
|
1587
|
+
...this.config,
|
|
1588
|
+
name
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
props(properties) {
|
|
1592
|
+
return new _ModelType({
|
|
1593
|
+
...this.config,
|
|
1594
|
+
properties: { ...this.config.properties, ...properties }
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
views(fn) {
|
|
1598
|
+
return new _ModelType({
|
|
1599
|
+
...this.config,
|
|
1600
|
+
views: [
|
|
1601
|
+
...this.config.views,
|
|
1602
|
+
fn
|
|
1603
|
+
]
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
actions(fn) {
|
|
1607
|
+
return new _ModelType({
|
|
1608
|
+
...this.config,
|
|
1609
|
+
actions: [
|
|
1610
|
+
...this.config.actions,
|
|
1611
|
+
fn
|
|
1612
|
+
]
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
volatile(fn) {
|
|
1616
|
+
return new _ModelType({
|
|
1617
|
+
...this.config,
|
|
1618
|
+
volatiles: [
|
|
1619
|
+
...this.config.volatiles,
|
|
1620
|
+
fn
|
|
1621
|
+
]
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
preProcessSnapshot(fn) {
|
|
1625
|
+
return new _ModelType({
|
|
1626
|
+
...this.config,
|
|
1627
|
+
preProcessor: fn
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
postProcessSnapshot(fn) {
|
|
1631
|
+
return new _ModelType({
|
|
1632
|
+
...this.config,
|
|
1633
|
+
postProcessor: fn
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
extend(fn) {
|
|
1637
|
+
const viewsFn = (self) => {
|
|
1638
|
+
const result = fn(self);
|
|
1639
|
+
return result.views ?? {};
|
|
1640
|
+
};
|
|
1641
|
+
const actionsFn = (self) => {
|
|
1642
|
+
const result = fn(self);
|
|
1643
|
+
return result.actions ?? {};
|
|
1644
|
+
};
|
|
1645
|
+
const volatileFn = (self) => {
|
|
1646
|
+
const result = fn(self);
|
|
1647
|
+
return result.state ?? {};
|
|
1648
|
+
};
|
|
1649
|
+
return new _ModelType({
|
|
1650
|
+
...this.config,
|
|
1651
|
+
views: [
|
|
1652
|
+
...this.config.views,
|
|
1653
|
+
viewsFn
|
|
1654
|
+
],
|
|
1655
|
+
actions: [
|
|
1656
|
+
...this.config.actions,
|
|
1657
|
+
actionsFn
|
|
1658
|
+
],
|
|
1659
|
+
volatiles: [
|
|
1660
|
+
...this.config.volatiles,
|
|
1661
|
+
volatileFn
|
|
1662
|
+
]
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Add afterCreate lifecycle hook
|
|
1667
|
+
*/
|
|
1668
|
+
afterCreate(fn) {
|
|
1669
|
+
return new _ModelType({
|
|
1670
|
+
...this.config,
|
|
1671
|
+
hooks: {
|
|
1672
|
+
...this.config.hooks,
|
|
1673
|
+
afterCreate: fn
|
|
1674
|
+
}
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
/**
|
|
1678
|
+
* Add afterAttach lifecycle hook
|
|
1679
|
+
*/
|
|
1680
|
+
afterAttach(fn) {
|
|
1681
|
+
return new _ModelType({
|
|
1682
|
+
...this.config,
|
|
1683
|
+
hooks: {
|
|
1684
|
+
...this.config.hooks,
|
|
1685
|
+
afterAttach: fn
|
|
1686
|
+
}
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Add beforeDetach lifecycle hook
|
|
1691
|
+
*/
|
|
1692
|
+
beforeDetach(fn) {
|
|
1693
|
+
return new _ModelType({
|
|
1694
|
+
...this.config,
|
|
1695
|
+
hooks: {
|
|
1696
|
+
...this.config.hooks,
|
|
1697
|
+
beforeDetach: fn
|
|
1698
|
+
}
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
/**
|
|
1702
|
+
* Add beforeDestroy lifecycle hook
|
|
1703
|
+
*/
|
|
1704
|
+
beforeDestroy(fn) {
|
|
1705
|
+
return new _ModelType({
|
|
1706
|
+
...this.config,
|
|
1707
|
+
hooks: {
|
|
1708
|
+
...this.config.hooks,
|
|
1709
|
+
beforeDestroy: fn
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
function model(nameOrProperties, maybeProperties) {
|
|
1715
|
+
const name = typeof nameOrProperties === "string" ? nameOrProperties : "AnonymousModel";
|
|
1716
|
+
const properties = typeof nameOrProperties === "string" ? maybeProperties : nameOrProperties;
|
|
1717
|
+
return new ModelType({
|
|
1718
|
+
name,
|
|
1719
|
+
properties,
|
|
1720
|
+
views: [],
|
|
1721
|
+
actions: [],
|
|
1722
|
+
volatiles: [],
|
|
1723
|
+
initializers: [],
|
|
1724
|
+
hooks: {}
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
function compose(...args) {
|
|
1728
|
+
const name = typeof args[0] === "string" ? args[0] : "ComposedModel";
|
|
1729
|
+
const types2 = typeof args[0] === "string" ? args.slice(1) : args;
|
|
1730
|
+
const mergedProperties = {};
|
|
1731
|
+
for (const type of types2) {
|
|
1732
|
+
Object.assign(mergedProperties, type.properties);
|
|
1733
|
+
}
|
|
1734
|
+
return model(name, mergedProperties);
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
// src/array.ts
|
|
1738
|
+
init_tree();
|
|
1739
|
+
var MSTArray = class _MSTArray extends Array {
|
|
1740
|
+
constructor(node, itemType, items = []) {
|
|
1741
|
+
super(...items);
|
|
1742
|
+
this.node = node;
|
|
1743
|
+
this.itemType = itemType;
|
|
1744
|
+
Object.setPrototypeOf(this, _MSTArray.prototype);
|
|
1745
|
+
}
|
|
1746
|
+
replace(items) {
|
|
1747
|
+
this.length = 0;
|
|
1748
|
+
this.push(...items);
|
|
1749
|
+
this.syncToNode();
|
|
1750
|
+
}
|
|
1751
|
+
clear() {
|
|
1752
|
+
this.length = 0;
|
|
1753
|
+
this.syncToNode();
|
|
1754
|
+
}
|
|
1755
|
+
remove(item) {
|
|
1756
|
+
const index = this.indexOf(item);
|
|
1757
|
+
if (index >= 0) {
|
|
1758
|
+
this.splice(index, 1);
|
|
1759
|
+
this.syncToNode();
|
|
1760
|
+
return true;
|
|
1761
|
+
}
|
|
1762
|
+
return false;
|
|
1763
|
+
}
|
|
1764
|
+
spliceWithArray(index, deleteCount, newItems) {
|
|
1765
|
+
const result = deleteCount !== void 0 ? newItems ? this.splice(index, deleteCount, ...newItems) : this.splice(index, deleteCount) : this.splice(index);
|
|
1766
|
+
this.syncToNode();
|
|
1767
|
+
return result;
|
|
1768
|
+
}
|
|
1769
|
+
// Override mutating methods to sync
|
|
1770
|
+
push(...items) {
|
|
1771
|
+
const result = super.push(...items);
|
|
1772
|
+
this.syncToNode();
|
|
1773
|
+
return result;
|
|
1774
|
+
}
|
|
1775
|
+
pop() {
|
|
1776
|
+
const result = super.pop();
|
|
1777
|
+
this.syncToNode();
|
|
1778
|
+
return result;
|
|
1779
|
+
}
|
|
1780
|
+
shift() {
|
|
1781
|
+
const result = super.shift();
|
|
1782
|
+
this.syncToNode();
|
|
1783
|
+
return result;
|
|
1784
|
+
}
|
|
1785
|
+
unshift(...items) {
|
|
1786
|
+
const result = super.unshift(...items);
|
|
1787
|
+
this.syncToNode();
|
|
1788
|
+
return result;
|
|
1789
|
+
}
|
|
1790
|
+
splice(start, deleteCount, ...items) {
|
|
1791
|
+
const result = deleteCount !== void 0 ? super.splice(start, deleteCount, ...items) : super.splice(start);
|
|
1792
|
+
this.syncToNode();
|
|
1793
|
+
return result;
|
|
1794
|
+
}
|
|
1795
|
+
sort(compareFn) {
|
|
1796
|
+
super.sort(compareFn);
|
|
1797
|
+
this.syncToNode();
|
|
1798
|
+
return this;
|
|
1799
|
+
}
|
|
1800
|
+
reverse() {
|
|
1801
|
+
super.reverse();
|
|
1802
|
+
this.syncToNode();
|
|
1803
|
+
return this;
|
|
1804
|
+
}
|
|
1805
|
+
fill(value, start, end) {
|
|
1806
|
+
super.fill(value, start, end);
|
|
1807
|
+
this.syncToNode();
|
|
1808
|
+
return this;
|
|
1809
|
+
}
|
|
1810
|
+
copyWithin(target, start, end) {
|
|
1811
|
+
super.copyWithin(target, start, end);
|
|
1812
|
+
this.syncToNode();
|
|
1813
|
+
return this;
|
|
1814
|
+
}
|
|
1815
|
+
toJSON() {
|
|
1816
|
+
return [...this];
|
|
1817
|
+
}
|
|
1818
|
+
syncToNode() {
|
|
1819
|
+
const existingChildNodes = /* @__PURE__ */ new Set();
|
|
1820
|
+
for (const [, child] of this.node.getChildren()) {
|
|
1821
|
+
existingChildNodes.add(child);
|
|
1822
|
+
}
|
|
1823
|
+
const newChildren = /* @__PURE__ */ new Map();
|
|
1824
|
+
const keptNodes = /* @__PURE__ */ new Set();
|
|
1825
|
+
this.forEach((item, index) => {
|
|
1826
|
+
const key = String(index);
|
|
1827
|
+
if (item && typeof item === "object" && $treenode in item) {
|
|
1828
|
+
const childNode = getStateTreeNode(item);
|
|
1829
|
+
newChildren.set(key, childNode);
|
|
1830
|
+
keptNodes.add(childNode);
|
|
1831
|
+
} else {
|
|
1832
|
+
const instance = this.itemType.create(item);
|
|
1833
|
+
if (instance && typeof instance === "object" && $treenode in instance) {
|
|
1834
|
+
const childNode = getStateTreeNode(instance);
|
|
1835
|
+
newChildren.set(key, childNode);
|
|
1836
|
+
keptNodes.add(childNode);
|
|
1837
|
+
this[index] = instance;
|
|
1838
|
+
} else {
|
|
1839
|
+
let reusedNode = null;
|
|
1840
|
+
for (const existingNode of existingChildNodes) {
|
|
1841
|
+
if (!keptNodes.has(existingNode) && existingNode.getValue() === item) {
|
|
1842
|
+
reusedNode = existingNode;
|
|
1843
|
+
break;
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
if (reusedNode) {
|
|
1847
|
+
newChildren.set(key, reusedNode);
|
|
1848
|
+
keptNodes.add(reusedNode);
|
|
1849
|
+
} else {
|
|
1850
|
+
const childNode = new StateTreeNode(
|
|
1851
|
+
this.itemType,
|
|
1852
|
+
item,
|
|
1853
|
+
this.node.$env
|
|
1854
|
+
);
|
|
1855
|
+
newChildren.set(key, childNode);
|
|
1856
|
+
keptNodes.add(childNode);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
});
|
|
1861
|
+
for (const existingNode of existingChildNodes) {
|
|
1862
|
+
if (!keptNodes.has(existingNode)) {
|
|
1863
|
+
existingNode.destroy();
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
this.node.getChildren().clear();
|
|
1867
|
+
for (const [key, childNode] of newChildren) {
|
|
1868
|
+
this.node.addChild(key, childNode);
|
|
1869
|
+
}
|
|
1870
|
+
this.node.setValue([...this]);
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
var ArrayType = class {
|
|
1874
|
+
constructor(itemType) {
|
|
1875
|
+
this._kind = "array";
|
|
1876
|
+
this._subType = itemType;
|
|
1877
|
+
this.name = `array<${itemType.name}>`;
|
|
1878
|
+
}
|
|
1879
|
+
create(snapshot, env) {
|
|
1880
|
+
const items = snapshot ?? [];
|
|
1881
|
+
const node = new StateTreeNode(this, items, env);
|
|
1882
|
+
const instances = items.map((item, index) => {
|
|
1883
|
+
const instance = this._subType.create(item, env);
|
|
1884
|
+
if (instance && typeof instance === "object" && $treenode in instance) {
|
|
1885
|
+
const childNode = getStateTreeNode(instance);
|
|
1886
|
+
node.addChild(String(index), childNode);
|
|
1887
|
+
} else {
|
|
1888
|
+
const childNode = new StateTreeNode(
|
|
1889
|
+
this._subType,
|
|
1890
|
+
instance,
|
|
1891
|
+
env,
|
|
1892
|
+
node,
|
|
1893
|
+
String(index)
|
|
1894
|
+
);
|
|
1895
|
+
node.addChild(String(index), childNode);
|
|
1896
|
+
}
|
|
1897
|
+
return instance;
|
|
1898
|
+
});
|
|
1899
|
+
const mstArray = new MSTArray(node, this._subType, instances);
|
|
1900
|
+
Object.defineProperty(mstArray, $treenode, {
|
|
1901
|
+
value: node,
|
|
1902
|
+
writable: false,
|
|
1903
|
+
enumerable: false
|
|
1904
|
+
});
|
|
1905
|
+
node.setInstance(mstArray);
|
|
1906
|
+
node.setValue(instances);
|
|
1907
|
+
return mstArray;
|
|
1908
|
+
}
|
|
1909
|
+
is(value) {
|
|
1910
|
+
if (!Array.isArray(value)) return false;
|
|
1911
|
+
return $treenode in value;
|
|
1912
|
+
}
|
|
1913
|
+
validate(value, context) {
|
|
1914
|
+
const errors = [];
|
|
1915
|
+
if (!Array.isArray(value)) {
|
|
1916
|
+
return {
|
|
1917
|
+
valid: false,
|
|
1918
|
+
errors: [
|
|
1919
|
+
{
|
|
1920
|
+
context,
|
|
1921
|
+
value,
|
|
1922
|
+
message: "Value is not an array"
|
|
1923
|
+
}
|
|
1924
|
+
]
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
value.forEach((item, index) => {
|
|
1928
|
+
const itemContext = {
|
|
1929
|
+
path: context.length > 0 ? `${context[0].path}/${index}` : `/${index}`,
|
|
1930
|
+
type: this._subType,
|
|
1931
|
+
parent: value
|
|
1932
|
+
};
|
|
1933
|
+
const result = this._subType.validate(item, [...context, itemContext]);
|
|
1934
|
+
if (!result.valid) {
|
|
1935
|
+
errors.push(...result.errors);
|
|
1936
|
+
}
|
|
1937
|
+
});
|
|
1938
|
+
return {
|
|
1939
|
+
valid: errors.length === 0,
|
|
1940
|
+
errors
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
};
|
|
1944
|
+
function array(itemType) {
|
|
1945
|
+
return new ArrayType(itemType);
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
// src/map.ts
|
|
1949
|
+
init_tree();
|
|
1950
|
+
var MSTMap = class extends Map {
|
|
1951
|
+
constructor(node, valueType, entries) {
|
|
1952
|
+
super();
|
|
1953
|
+
this.initialized = false;
|
|
1954
|
+
this.node = node;
|
|
1955
|
+
this.valueType = valueType;
|
|
1956
|
+
this.initialized = true;
|
|
1957
|
+
if (entries) {
|
|
1958
|
+
for (const [key, value] of entries) {
|
|
1959
|
+
super.set(key, value);
|
|
1960
|
+
}
|
|
1961
|
+
this.syncToNode();
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
put(value) {
|
|
1965
|
+
let key;
|
|
1966
|
+
if (value && typeof value === "object" && $treenode in value) {
|
|
1967
|
+
const valueNode = getStateTreeNode(value);
|
|
1968
|
+
if (valueNode.identifierValue !== void 0) {
|
|
1969
|
+
key = String(valueNode.identifierValue);
|
|
1970
|
+
} else {
|
|
1971
|
+
throw new Error("[jotai-state-tree] Cannot put a value without an identifier into a map");
|
|
1972
|
+
}
|
|
1973
|
+
} else {
|
|
1974
|
+
throw new Error("[jotai-state-tree] Cannot put a non-model value using put()");
|
|
1975
|
+
}
|
|
1976
|
+
this.set(key, value);
|
|
1977
|
+
return value;
|
|
1978
|
+
}
|
|
1979
|
+
merge(values) {
|
|
1980
|
+
const entries = values instanceof Map ? values.entries() : Object.entries(values);
|
|
1981
|
+
for (const [key, value] of entries) {
|
|
1982
|
+
this.set(key, value);
|
|
1983
|
+
}
|
|
1984
|
+
return this;
|
|
1985
|
+
}
|
|
1986
|
+
replace(values) {
|
|
1987
|
+
this.clear();
|
|
1988
|
+
return this.merge(values);
|
|
1989
|
+
}
|
|
1990
|
+
// Override mutating methods to sync
|
|
1991
|
+
set(key, value) {
|
|
1992
|
+
super.set(key, value);
|
|
1993
|
+
if (this.initialized) {
|
|
1994
|
+
this.syncToNode();
|
|
1995
|
+
}
|
|
1996
|
+
return this;
|
|
1997
|
+
}
|
|
1998
|
+
// Override get to return the instance from child node for complex types
|
|
1999
|
+
get(key) {
|
|
2000
|
+
if (this.valueType._kind === "model" || this.valueType._kind === "array" || this.valueType._kind === "map") {
|
|
2001
|
+
const childNode = this.node.getChild(key);
|
|
2002
|
+
if (childNode) {
|
|
2003
|
+
return childNode.getInstance();
|
|
2004
|
+
}
|
|
2005
|
+
return void 0;
|
|
2006
|
+
}
|
|
2007
|
+
return super.get(key);
|
|
2008
|
+
}
|
|
2009
|
+
delete(key) {
|
|
2010
|
+
const result = super.delete(key);
|
|
2011
|
+
if (result && this.initialized) {
|
|
2012
|
+
this.syncToNode();
|
|
2013
|
+
}
|
|
2014
|
+
return result;
|
|
2015
|
+
}
|
|
2016
|
+
clear() {
|
|
2017
|
+
super.clear();
|
|
2018
|
+
if (this.initialized) {
|
|
2019
|
+
this.syncToNode();
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
toJSON() {
|
|
2023
|
+
const result = {};
|
|
2024
|
+
this.forEach((value, key) => {
|
|
2025
|
+
result[key] = value;
|
|
2026
|
+
});
|
|
2027
|
+
return result;
|
|
2028
|
+
}
|
|
2029
|
+
syncToNode() {
|
|
2030
|
+
const existingChildren = new Map(this.node.getChildren());
|
|
2031
|
+
const newChildren = /* @__PURE__ */ new Map();
|
|
2032
|
+
this.forEach((value, key) => {
|
|
2033
|
+
if (this.valueType._kind === "model" || this.valueType._kind === "array" || this.valueType._kind === "map") {
|
|
2034
|
+
if (value && typeof value === "object" && $treenode in value) {
|
|
2035
|
+
const childNode = getStateTreeNode(value);
|
|
2036
|
+
newChildren.set(key, childNode);
|
|
2037
|
+
} else {
|
|
2038
|
+
const childInstance = this.valueType.create(value);
|
|
2039
|
+
const childNode = getStateTreeNode(childInstance);
|
|
2040
|
+
newChildren.set(key, childNode);
|
|
2041
|
+
super.set(key, childInstance);
|
|
2042
|
+
}
|
|
2043
|
+
} else {
|
|
2044
|
+
const existingChild = existingChildren.get(key);
|
|
2045
|
+
if (existingChild && existingChild.getValue() === value) {
|
|
2046
|
+
newChildren.set(key, existingChild);
|
|
2047
|
+
} else {
|
|
2048
|
+
const childNode = new StateTreeNode(this.valueType, value, this.node.$env);
|
|
2049
|
+
newChildren.set(key, childNode);
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
});
|
|
2053
|
+
for (const [key, child] of existingChildren) {
|
|
2054
|
+
if (!newChildren.has(key)) {
|
|
2055
|
+
child.destroy();
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
this.node.getChildren().clear();
|
|
2059
|
+
for (const [key, childNode] of newChildren) {
|
|
2060
|
+
this.node.addChild(key, childNode);
|
|
2061
|
+
}
|
|
2062
|
+
this.node.setValue(this.toJSON());
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
var MapType = class {
|
|
2066
|
+
constructor(valueType) {
|
|
2067
|
+
this._kind = "map";
|
|
2068
|
+
this._subType = valueType;
|
|
2069
|
+
this.name = `map<${valueType.name}>`;
|
|
2070
|
+
}
|
|
2071
|
+
create(snapshot, env) {
|
|
2072
|
+
const entries = snapshot ?? {};
|
|
2073
|
+
const node = new StateTreeNode(this, entries, env);
|
|
2074
|
+
const instanceEntries = Object.entries(entries).map(([key, value]) => {
|
|
2075
|
+
const instance = this._subType.create(value, env);
|
|
2076
|
+
if (this._subType._kind === "model" || this._subType._kind === "array" || this._subType._kind === "map") {
|
|
2077
|
+
const childNode = getStateTreeNode(instance);
|
|
2078
|
+
node.addChild(key, childNode);
|
|
2079
|
+
} else {
|
|
2080
|
+
const childNode = new StateTreeNode(this._subType, instance, env, node, key);
|
|
2081
|
+
node.addChild(key, childNode);
|
|
2082
|
+
}
|
|
2083
|
+
return [key, instance];
|
|
2084
|
+
});
|
|
2085
|
+
const mstMap = new MSTMap(
|
|
2086
|
+
node,
|
|
2087
|
+
this._subType,
|
|
2088
|
+
instanceEntries
|
|
2089
|
+
);
|
|
2090
|
+
Object.defineProperty(mstMap, $treenode, {
|
|
2091
|
+
value: node,
|
|
2092
|
+
writable: false,
|
|
2093
|
+
enumerable: false
|
|
2094
|
+
});
|
|
2095
|
+
node.setInstance(mstMap);
|
|
2096
|
+
return mstMap;
|
|
2097
|
+
}
|
|
2098
|
+
is(value) {
|
|
2099
|
+
if (!(value instanceof Map)) return false;
|
|
2100
|
+
return $treenode in value;
|
|
2101
|
+
}
|
|
2102
|
+
validate(value, context) {
|
|
2103
|
+
const errors = [];
|
|
2104
|
+
if (typeof value !== "object" || value === null) {
|
|
2105
|
+
return {
|
|
2106
|
+
valid: false,
|
|
2107
|
+
errors: [
|
|
2108
|
+
{
|
|
2109
|
+
context,
|
|
2110
|
+
value,
|
|
2111
|
+
message: "Value is not an object or Map"
|
|
2112
|
+
}
|
|
2113
|
+
]
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2116
|
+
const entries = value instanceof Map ? Array.from(value.entries()) : Object.entries(value);
|
|
2117
|
+
for (const [key, itemValue] of entries) {
|
|
2118
|
+
const itemContext = {
|
|
2119
|
+
path: context.length > 0 ? `${context[0].path}/${key}` : `/${key}`,
|
|
2120
|
+
type: this._subType,
|
|
2121
|
+
parent: value
|
|
2122
|
+
};
|
|
2123
|
+
const result = this._subType.validate(itemValue, [...context, itemContext]);
|
|
2124
|
+
if (!result.valid) {
|
|
2125
|
+
errors.push(...result.errors);
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
return {
|
|
2129
|
+
valid: errors.length === 0,
|
|
2130
|
+
errors
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
};
|
|
2134
|
+
function map(valueType) {
|
|
2135
|
+
return new MapType(valueType);
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
// src/utilities.ts
|
|
2139
|
+
init_tree();
|
|
2140
|
+
var OptionalType = class {
|
|
2141
|
+
constructor(subType, defaultValue) {
|
|
2142
|
+
this._kind = "optional";
|
|
2143
|
+
this._subType = subType;
|
|
2144
|
+
this._defaultValue = defaultValue;
|
|
2145
|
+
this.name = `optional<${subType.name}>`;
|
|
2146
|
+
}
|
|
2147
|
+
create(snapshot, env) {
|
|
2148
|
+
if (snapshot === void 0) {
|
|
2149
|
+
const defaultVal = typeof this._defaultValue === "function" ? this._defaultValue() : this._defaultValue;
|
|
2150
|
+
return this._subType.create(defaultVal, env);
|
|
2151
|
+
}
|
|
2152
|
+
return this._subType.create(snapshot, env);
|
|
2153
|
+
}
|
|
2154
|
+
is(value) {
|
|
2155
|
+
return value === void 0 || this._subType.is(value);
|
|
2156
|
+
}
|
|
2157
|
+
validate(value, context) {
|
|
2158
|
+
if (value === void 0) {
|
|
2159
|
+
return { valid: true, errors: [] };
|
|
2160
|
+
}
|
|
2161
|
+
return this._subType.validate(value, context);
|
|
2162
|
+
}
|
|
2163
|
+
};
|
|
2164
|
+
function optional(type, defaultValue) {
|
|
2165
|
+
return new OptionalType(type, defaultValue);
|
|
2166
|
+
}
|
|
2167
|
+
var MaybeType = class {
|
|
2168
|
+
constructor(subType) {
|
|
2169
|
+
this._kind = "maybe";
|
|
2170
|
+
this._subType = subType;
|
|
2171
|
+
this.name = `maybe<${subType.name}>`;
|
|
2172
|
+
}
|
|
2173
|
+
create(snapshot, env) {
|
|
2174
|
+
if (snapshot === void 0) {
|
|
2175
|
+
return void 0;
|
|
2176
|
+
}
|
|
2177
|
+
return this._subType.create(snapshot, env);
|
|
2178
|
+
}
|
|
2179
|
+
is(value) {
|
|
2180
|
+
return value === void 0 || this._subType.is(value);
|
|
2181
|
+
}
|
|
2182
|
+
validate(value, context) {
|
|
2183
|
+
if (value === void 0) {
|
|
2184
|
+
return { valid: true, errors: [] };
|
|
2185
|
+
}
|
|
2186
|
+
return this._subType.validate(value, context);
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
function maybe(type) {
|
|
2190
|
+
return new MaybeType(type);
|
|
2191
|
+
}
|
|
2192
|
+
var MaybeNullType = class {
|
|
2193
|
+
constructor(subType) {
|
|
2194
|
+
this._kind = "maybeNull";
|
|
2195
|
+
this._subType = subType;
|
|
2196
|
+
this.name = `maybeNull<${subType.name}>`;
|
|
2197
|
+
}
|
|
2198
|
+
create(snapshot, env) {
|
|
2199
|
+
if (snapshot === null || snapshot === void 0) {
|
|
2200
|
+
return null;
|
|
2201
|
+
}
|
|
2202
|
+
return this._subType.create(snapshot, env);
|
|
2203
|
+
}
|
|
2204
|
+
is(value) {
|
|
2205
|
+
return value === null || this._subType.is(value);
|
|
2206
|
+
}
|
|
2207
|
+
validate(value, context) {
|
|
2208
|
+
if (value === null) {
|
|
2209
|
+
return { valid: true, errors: [] };
|
|
2210
|
+
}
|
|
2211
|
+
return this._subType.validate(value, context);
|
|
2212
|
+
}
|
|
2213
|
+
};
|
|
2214
|
+
function maybeNull(type) {
|
|
2215
|
+
return new MaybeNullType(type);
|
|
2216
|
+
}
|
|
2217
|
+
var UnionType = class {
|
|
2218
|
+
constructor(types2, options) {
|
|
2219
|
+
this._kind = "union";
|
|
2220
|
+
this._types = types2;
|
|
2221
|
+
this.dispatcher = options?.dispatcher;
|
|
2222
|
+
this.eager = options?.eager ?? true;
|
|
2223
|
+
this.name = `union(${types2.map((t) => t.name).join(" | ")})`;
|
|
2224
|
+
}
|
|
2225
|
+
create(snapshot, env) {
|
|
2226
|
+
if (this.dispatcher && snapshot !== void 0) {
|
|
2227
|
+
const type = this.dispatcher(snapshot);
|
|
2228
|
+
return type.create(snapshot, env);
|
|
2229
|
+
}
|
|
2230
|
+
for (const type of this._types) {
|
|
2231
|
+
try {
|
|
2232
|
+
const result = type.validate(snapshot, []);
|
|
2233
|
+
if (result.valid) {
|
|
2234
|
+
return type.create(snapshot, env);
|
|
2235
|
+
}
|
|
2236
|
+
} catch {
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
throw new Error(
|
|
2240
|
+
`[jotai-state-tree] No type in union matched the value: ${JSON.stringify(snapshot)}`
|
|
2241
|
+
);
|
|
2242
|
+
}
|
|
2243
|
+
is(value) {
|
|
2244
|
+
return this._types.some((type) => type.is(value));
|
|
2245
|
+
}
|
|
2246
|
+
validate(value, context) {
|
|
2247
|
+
for (const type of this._types) {
|
|
2248
|
+
const result = type.validate(value, context);
|
|
2249
|
+
if (result.valid) {
|
|
2250
|
+
return result;
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
return {
|
|
2254
|
+
valid: false,
|
|
2255
|
+
errors: [
|
|
2256
|
+
{
|
|
2257
|
+
context,
|
|
2258
|
+
value,
|
|
2259
|
+
message: `Value does not match any type in union`
|
|
2260
|
+
}
|
|
2261
|
+
]
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
2264
|
+
};
|
|
2265
|
+
function union(optionsOrType, ...rest) {
|
|
2266
|
+
if (optionsOrType && typeof optionsOrType === "object" && "dispatcher" in optionsOrType) {
|
|
2267
|
+
return new UnionType(rest, optionsOrType);
|
|
2268
|
+
}
|
|
2269
|
+
return new UnionType([optionsOrType, ...rest]);
|
|
2270
|
+
}
|
|
2271
|
+
var LateType = class {
|
|
2272
|
+
constructor(definition, name) {
|
|
2273
|
+
this._kind = "late";
|
|
2274
|
+
this._definition = definition;
|
|
2275
|
+
this.name = name ?? "late(...)";
|
|
2276
|
+
}
|
|
2277
|
+
getType() {
|
|
2278
|
+
if (!this.resolvedType) {
|
|
2279
|
+
this.resolvedType = this._definition();
|
|
2280
|
+
}
|
|
2281
|
+
return this.resolvedType;
|
|
2282
|
+
}
|
|
2283
|
+
create(snapshot, env) {
|
|
2284
|
+
return this.getType().create(snapshot, env);
|
|
2285
|
+
}
|
|
2286
|
+
is(value) {
|
|
2287
|
+
return this.getType().is(value);
|
|
2288
|
+
}
|
|
2289
|
+
validate(value, context) {
|
|
2290
|
+
return this.getType().validate(value, context);
|
|
2291
|
+
}
|
|
2292
|
+
};
|
|
2293
|
+
function late(nameOrDefinition, maybeDefinition) {
|
|
2294
|
+
if (typeof nameOrDefinition === "string") {
|
|
2295
|
+
return new LateType(maybeDefinition, nameOrDefinition);
|
|
2296
|
+
}
|
|
2297
|
+
return new LateType(nameOrDefinition);
|
|
2298
|
+
}
|
|
2299
|
+
var RefinementType = class {
|
|
2300
|
+
constructor(subType, predicate, message) {
|
|
2301
|
+
this._kind = "refinement";
|
|
2302
|
+
this._subType = subType;
|
|
2303
|
+
this._predicate = predicate;
|
|
2304
|
+
this.message = message ?? "Value failed refinement predicate";
|
|
2305
|
+
this.name = `refinement<${subType.name}>`;
|
|
2306
|
+
}
|
|
2307
|
+
create(snapshot, env) {
|
|
2308
|
+
const instance = this._subType.create(snapshot, env);
|
|
2309
|
+
if (!this._predicate(instance)) {
|
|
2310
|
+
const msg = typeof this.message === "function" ? this.message(instance) : this.message;
|
|
2311
|
+
throw new Error(`[jotai-state-tree] ${msg}`);
|
|
2312
|
+
}
|
|
2313
|
+
return instance;
|
|
2314
|
+
}
|
|
2315
|
+
is(value) {
|
|
2316
|
+
return this._subType.is(value) && this._predicate(value);
|
|
2317
|
+
}
|
|
2318
|
+
validate(value, context) {
|
|
2319
|
+
const baseResult = this._subType.validate(value, context);
|
|
2320
|
+
if (!baseResult.valid) {
|
|
2321
|
+
return baseResult;
|
|
2322
|
+
}
|
|
2323
|
+
if (!this._predicate(value)) {
|
|
2324
|
+
const msg = typeof this.message === "function" ? this.message(value) : this.message;
|
|
2325
|
+
return {
|
|
2326
|
+
valid: false,
|
|
2327
|
+
errors: [
|
|
2328
|
+
{
|
|
2329
|
+
context,
|
|
2330
|
+
value,
|
|
2331
|
+
message: msg
|
|
2332
|
+
}
|
|
2333
|
+
]
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
return { valid: true, errors: [] };
|
|
2337
|
+
}
|
|
2338
|
+
};
|
|
2339
|
+
function refinement(type, predicate, message) {
|
|
2340
|
+
return new RefinementType(type, predicate, message);
|
|
2341
|
+
}
|
|
2342
|
+
var ReferenceType = class {
|
|
2343
|
+
constructor(targetType, options) {
|
|
2344
|
+
this._kind = "reference";
|
|
2345
|
+
this._targetType = targetType;
|
|
2346
|
+
this.options = options;
|
|
2347
|
+
this.name = `reference<${targetType.name}>`;
|
|
2348
|
+
}
|
|
2349
|
+
create(snapshot, env) {
|
|
2350
|
+
if (snapshot === void 0) {
|
|
2351
|
+
throw new Error("[jotai-state-tree] Reference requires an identifier");
|
|
2352
|
+
}
|
|
2353
|
+
const self = this;
|
|
2354
|
+
let resolved = null;
|
|
2355
|
+
if (this.options?.get) {
|
|
2356
|
+
const result = this.options.get(snapshot, null);
|
|
2357
|
+
if (result) return result;
|
|
2358
|
+
}
|
|
2359
|
+
const node = new StateTreeNode(this, snapshot, env);
|
|
2360
|
+
node.identifierValue = snapshot;
|
|
2361
|
+
const proxy = new Proxy({}, {
|
|
2362
|
+
get(target, prop) {
|
|
2363
|
+
if (!resolved) {
|
|
2364
|
+
const targetNode = resolveIdentifier(self._targetType.name, snapshot);
|
|
2365
|
+
if (!targetNode) {
|
|
2366
|
+
throw new Error(
|
|
2367
|
+
`[jotai-state-tree] Failed to resolve reference '${snapshot}' to type '${self._targetType.name}'`
|
|
2368
|
+
);
|
|
2369
|
+
}
|
|
2370
|
+
resolved = targetNode.getInstance();
|
|
2371
|
+
}
|
|
2372
|
+
if (prop === $treenode) {
|
|
2373
|
+
return node;
|
|
2374
|
+
}
|
|
2375
|
+
return resolved[prop];
|
|
2376
|
+
},
|
|
2377
|
+
set(target, prop, value) {
|
|
2378
|
+
if (!resolved) {
|
|
2379
|
+
const targetNode = resolveIdentifier(self._targetType.name, snapshot);
|
|
2380
|
+
if (!targetNode) {
|
|
2381
|
+
throw new Error(
|
|
2382
|
+
`[jotai-state-tree] Failed to resolve reference '${snapshot}' to type '${self._targetType.name}'`
|
|
2383
|
+
);
|
|
2384
|
+
}
|
|
2385
|
+
resolved = targetNode.getInstance();
|
|
2386
|
+
}
|
|
2387
|
+
resolved[prop] = value;
|
|
2388
|
+
return true;
|
|
2389
|
+
},
|
|
2390
|
+
has(target, prop) {
|
|
2391
|
+
if (!resolved) {
|
|
2392
|
+
const targetNode = resolveIdentifier(self._targetType.name, snapshot);
|
|
2393
|
+
if (targetNode) {
|
|
2394
|
+
resolved = targetNode.getInstance();
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
return resolved ? prop in resolved : false;
|
|
2398
|
+
}
|
|
2399
|
+
});
|
|
2400
|
+
node.setInstance(proxy);
|
|
2401
|
+
return proxy;
|
|
2402
|
+
}
|
|
2403
|
+
is(value) {
|
|
2404
|
+
return this._targetType.is(value);
|
|
2405
|
+
}
|
|
2406
|
+
validate(value, context) {
|
|
2407
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
2408
|
+
return { valid: true, errors: [] };
|
|
2409
|
+
}
|
|
2410
|
+
return {
|
|
2411
|
+
valid: false,
|
|
2412
|
+
errors: [
|
|
2413
|
+
{
|
|
2414
|
+
context,
|
|
2415
|
+
value,
|
|
2416
|
+
message: "Reference must be a string or number identifier"
|
|
2417
|
+
}
|
|
2418
|
+
]
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2421
|
+
};
|
|
2422
|
+
function reference(targetType, options) {
|
|
2423
|
+
return new ReferenceType(targetType, options);
|
|
2424
|
+
}
|
|
2425
|
+
var SafeReferenceType = class {
|
|
2426
|
+
constructor(targetType, options) {
|
|
2427
|
+
this._kind = "safeReference";
|
|
2428
|
+
this._targetType = targetType;
|
|
2429
|
+
this.options = options;
|
|
2430
|
+
this.name = `safeReference<${targetType.name}>`;
|
|
2431
|
+
}
|
|
2432
|
+
create(snapshot, env) {
|
|
2433
|
+
if (snapshot === void 0) {
|
|
2434
|
+
return void 0;
|
|
2435
|
+
}
|
|
2436
|
+
const targetNode = resolveIdentifier(this._targetType.name, snapshot);
|
|
2437
|
+
if (!targetNode) {
|
|
2438
|
+
if (this.options?.onInvalidated) {
|
|
2439
|
+
return void 0;
|
|
2440
|
+
}
|
|
2441
|
+
return void 0;
|
|
2442
|
+
}
|
|
2443
|
+
return targetNode.getInstance();
|
|
2444
|
+
}
|
|
2445
|
+
is(value) {
|
|
2446
|
+
return value === void 0 || this._targetType.is(value);
|
|
2447
|
+
}
|
|
2448
|
+
validate(value, context) {
|
|
2449
|
+
if (value === void 0) {
|
|
2450
|
+
return { valid: true, errors: [] };
|
|
2451
|
+
}
|
|
2452
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
2453
|
+
return { valid: true, errors: [] };
|
|
2454
|
+
}
|
|
2455
|
+
return {
|
|
2456
|
+
valid: false,
|
|
2457
|
+
errors: [
|
|
2458
|
+
{
|
|
2459
|
+
context,
|
|
2460
|
+
value,
|
|
2461
|
+
message: "Safe reference must be a string, number, or undefined"
|
|
2462
|
+
}
|
|
2463
|
+
]
|
|
2464
|
+
};
|
|
2465
|
+
}
|
|
2466
|
+
};
|
|
2467
|
+
function safeReference(targetType, options) {
|
|
2468
|
+
return new SafeReferenceType(targetType, options);
|
|
2469
|
+
}
|
|
2470
|
+
function snapshotProcessor(type, processors) {
|
|
2471
|
+
return {
|
|
2472
|
+
name: `snapshotProcessor<${type.name}>`,
|
|
2473
|
+
_kind: "simple",
|
|
2474
|
+
_C: void 0,
|
|
2475
|
+
_S: void 0,
|
|
2476
|
+
_T: void 0,
|
|
2477
|
+
create(snapshot, env) {
|
|
2478
|
+
const processed = processors.preProcessor ? processors.preProcessor(snapshot) : snapshot;
|
|
2479
|
+
return type.create(processed, env);
|
|
2480
|
+
},
|
|
2481
|
+
is(value) {
|
|
2482
|
+
return type.is(value);
|
|
2483
|
+
},
|
|
2484
|
+
validate(value, context) {
|
|
2485
|
+
return type.validate(value, context);
|
|
2486
|
+
}
|
|
2487
|
+
};
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
// src/registry.ts
|
|
2491
|
+
init_tree();
|
|
2492
|
+
var modelRegistry = /* @__PURE__ */ new Map();
|
|
2493
|
+
var pendingResolutions = /* @__PURE__ */ new Map();
|
|
2494
|
+
var registrationListeners = /* @__PURE__ */ new Set();
|
|
2495
|
+
function registerModel(name, type, metadata) {
|
|
2496
|
+
if (modelRegistry.has(name)) {
|
|
2497
|
+
throw new Error(
|
|
2498
|
+
`[jotai-state-tree] Model "${name}" is already registered. Use unregisterModel() first if you want to replace it.`
|
|
2499
|
+
);
|
|
2500
|
+
}
|
|
2501
|
+
modelRegistry.set(name, { type, metadata });
|
|
2502
|
+
const pending = pendingResolutions.get(name);
|
|
2503
|
+
if (pending) {
|
|
2504
|
+
pending.forEach(({ resolve }) => resolve(type));
|
|
2505
|
+
pendingResolutions.delete(name);
|
|
2506
|
+
}
|
|
2507
|
+
registrationListeners.forEach((listener) => listener(name, type));
|
|
2508
|
+
}
|
|
2509
|
+
function unregisterModel(name) {
|
|
2510
|
+
return modelRegistry.delete(name);
|
|
2511
|
+
}
|
|
2512
|
+
function isModelRegistered(name) {
|
|
2513
|
+
return modelRegistry.has(name);
|
|
2514
|
+
}
|
|
2515
|
+
function resolveModel(name) {
|
|
2516
|
+
const entry = modelRegistry.get(name);
|
|
2517
|
+
if (!entry) {
|
|
2518
|
+
throw new Error(
|
|
2519
|
+
`[jotai-state-tree] Model "${name}" is not registered. Make sure to call registerModel("${name}", YourModelType) before resolving.`
|
|
2520
|
+
);
|
|
2521
|
+
}
|
|
2522
|
+
return entry.type;
|
|
2523
|
+
}
|
|
2524
|
+
function tryResolveModel(name) {
|
|
2525
|
+
return modelRegistry.get(name)?.type;
|
|
2526
|
+
}
|
|
2527
|
+
function resolveModelAsync(name, timeout = 3e4) {
|
|
2528
|
+
const entry = modelRegistry.get(name);
|
|
2529
|
+
if (entry) {
|
|
2530
|
+
return Promise.resolve(entry.type);
|
|
2531
|
+
}
|
|
2532
|
+
return new Promise((resolve, reject) => {
|
|
2533
|
+
const timeoutId = setTimeout(() => {
|
|
2534
|
+
const pending = pendingResolutions.get(name);
|
|
2535
|
+
if (pending) {
|
|
2536
|
+
const index = pending.findIndex((p) => p.resolve === resolve);
|
|
2537
|
+
if (index >= 0) {
|
|
2538
|
+
pending.splice(index, 1);
|
|
2539
|
+
if (pending.length === 0) {
|
|
2540
|
+
pendingResolutions.delete(name);
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
reject(
|
|
2545
|
+
new Error(
|
|
2546
|
+
`[jotai-state-tree] Timeout waiting for model "${name}" to be registered`
|
|
2547
|
+
)
|
|
2548
|
+
);
|
|
2549
|
+
}, timeout);
|
|
2550
|
+
const wrappedResolve = (type) => {
|
|
2551
|
+
clearTimeout(timeoutId);
|
|
2552
|
+
resolve(type);
|
|
2553
|
+
};
|
|
2554
|
+
const wrappedReject = (error) => {
|
|
2555
|
+
clearTimeout(timeoutId);
|
|
2556
|
+
reject(error);
|
|
2557
|
+
};
|
|
2558
|
+
if (!pendingResolutions.has(name)) {
|
|
2559
|
+
pendingResolutions.set(name, []);
|
|
2560
|
+
}
|
|
2561
|
+
pendingResolutions.get(name).push({
|
|
2562
|
+
resolve: wrappedResolve,
|
|
2563
|
+
reject: wrappedReject
|
|
2564
|
+
});
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
function getModelMetadata(name) {
|
|
2568
|
+
return modelRegistry.get(name)?.metadata;
|
|
2569
|
+
}
|
|
2570
|
+
function getRegisteredModelNames() {
|
|
2571
|
+
return Array.from(modelRegistry.keys());
|
|
2572
|
+
}
|
|
2573
|
+
function onModelRegistered(listener) {
|
|
2574
|
+
registrationListeners.add(listener);
|
|
2575
|
+
return () => {
|
|
2576
|
+
registrationListeners.delete(listener);
|
|
2577
|
+
};
|
|
2578
|
+
}
|
|
2579
|
+
function clearModelRegistry() {
|
|
2580
|
+
modelRegistry.clear();
|
|
2581
|
+
pendingResolutions.forEach((pending, name) => {
|
|
2582
|
+
pending.forEach(({ reject }) => {
|
|
2583
|
+
reject(
|
|
2584
|
+
new Error(
|
|
2585
|
+
`[jotai-state-tree] Model registry was cleared while waiting for "${name}"`
|
|
2586
|
+
)
|
|
2587
|
+
);
|
|
2588
|
+
});
|
|
2589
|
+
});
|
|
2590
|
+
pendingResolutions.clear();
|
|
2591
|
+
}
|
|
2592
|
+
var LateModelType = class {
|
|
2593
|
+
constructor(modelName) {
|
|
2594
|
+
this._kind = "lateModel";
|
|
2595
|
+
this._modelName = modelName;
|
|
2596
|
+
this.name = `lateModel("${modelName}")`;
|
|
2597
|
+
}
|
|
2598
|
+
getType() {
|
|
2599
|
+
if (!this._resolvedType) {
|
|
2600
|
+
this._resolvedType = resolveModel(this._modelName);
|
|
2601
|
+
}
|
|
2602
|
+
return this._resolvedType;
|
|
2603
|
+
}
|
|
2604
|
+
create(snapshot, env) {
|
|
2605
|
+
return this.getType().create(snapshot, env);
|
|
2606
|
+
}
|
|
2607
|
+
is(value) {
|
|
2608
|
+
return this.getType().is(value);
|
|
2609
|
+
}
|
|
2610
|
+
validate(value, context) {
|
|
2611
|
+
return this.getType().validate(value, context);
|
|
2612
|
+
}
|
|
2613
|
+
};
|
|
2614
|
+
function lateModel(modelName) {
|
|
2615
|
+
return new LateModelType(modelName);
|
|
2616
|
+
}
|
|
2617
|
+
var DynamicReferenceType = class {
|
|
2618
|
+
constructor(modelName, options = {}) {
|
|
2619
|
+
this._kind = "dynamicReference";
|
|
2620
|
+
this._modelName = modelName;
|
|
2621
|
+
this._options = options;
|
|
2622
|
+
this.name = `dynamicReference("${modelName}")`;
|
|
2623
|
+
}
|
|
2624
|
+
create(snapshot, env) {
|
|
2625
|
+
if (snapshot === void 0 || snapshot === null) {
|
|
2626
|
+
throw new Error(
|
|
2627
|
+
`[jotai-state-tree] Cannot create dynamicReference with undefined/null identifier`
|
|
2628
|
+
);
|
|
2629
|
+
}
|
|
2630
|
+
const self = this;
|
|
2631
|
+
const referenceProxy = new Proxy({}, {
|
|
2632
|
+
get(_target, prop) {
|
|
2633
|
+
const resolved = self.resolveReference(snapshot, null);
|
|
2634
|
+
if (!resolved) {
|
|
2635
|
+
if (self._options.onInvalidated) {
|
|
2636
|
+
const fallback = self._options.onInvalidated(snapshot, null);
|
|
2637
|
+
if (fallback) {
|
|
2638
|
+
return fallback[prop];
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
throw new Error(
|
|
2642
|
+
`[jotai-state-tree] Failed to resolve dynamicReference("${self._modelName}") with identifier "${snapshot}"`
|
|
2643
|
+
);
|
|
2644
|
+
}
|
|
2645
|
+
return resolved[prop];
|
|
2646
|
+
},
|
|
2647
|
+
has(_target, prop) {
|
|
2648
|
+
const resolved = self.resolveReference(snapshot, null);
|
|
2649
|
+
if (!resolved) return false;
|
|
2650
|
+
return prop in resolved;
|
|
2651
|
+
},
|
|
2652
|
+
ownKeys(_target) {
|
|
2653
|
+
const resolved = self.resolveReference(snapshot, null);
|
|
2654
|
+
if (!resolved) return [];
|
|
2655
|
+
return Reflect.ownKeys(resolved);
|
|
2656
|
+
},
|
|
2657
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
2658
|
+
const resolved = self.resolveReference(snapshot, null);
|
|
2659
|
+
if (!resolved) return void 0;
|
|
2660
|
+
return Object.getOwnPropertyDescriptor(resolved, prop);
|
|
2661
|
+
}
|
|
2662
|
+
});
|
|
2663
|
+
return referenceProxy;
|
|
2664
|
+
}
|
|
2665
|
+
resolveReference(identifier2, parent) {
|
|
2666
|
+
if (this._options.get) {
|
|
2667
|
+
return this._options.get(identifier2, parent);
|
|
2668
|
+
}
|
|
2669
|
+
try {
|
|
2670
|
+
const targetType = tryResolveModel(this._modelName);
|
|
2671
|
+
if (!targetType) {
|
|
2672
|
+
return void 0;
|
|
2673
|
+
}
|
|
2674
|
+
return resolveIdentifier(this._modelName, identifier2);
|
|
2675
|
+
} catch {
|
|
2676
|
+
return void 0;
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
is(value) {
|
|
2680
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
2681
|
+
return true;
|
|
2682
|
+
}
|
|
2683
|
+
const targetType = tryResolveModel(this._modelName);
|
|
2684
|
+
if (targetType) {
|
|
2685
|
+
return targetType.is(value);
|
|
2686
|
+
}
|
|
2687
|
+
return false;
|
|
2688
|
+
}
|
|
2689
|
+
validate(value, context) {
|
|
2690
|
+
if (value === void 0 || value === null) {
|
|
2691
|
+
return {
|
|
2692
|
+
valid: false,
|
|
2693
|
+
errors: [
|
|
2694
|
+
{
|
|
2695
|
+
context,
|
|
2696
|
+
value,
|
|
2697
|
+
message: "Reference identifier cannot be undefined or null"
|
|
2698
|
+
}
|
|
2699
|
+
]
|
|
2700
|
+
};
|
|
2701
|
+
}
|
|
2702
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
2703
|
+
return {
|
|
2704
|
+
valid: false,
|
|
2705
|
+
errors: [
|
|
2706
|
+
{
|
|
2707
|
+
context,
|
|
2708
|
+
value,
|
|
2709
|
+
message: "Reference identifier must be a string or number"
|
|
2710
|
+
}
|
|
2711
|
+
]
|
|
2712
|
+
};
|
|
2713
|
+
}
|
|
2714
|
+
return { valid: true, errors: [] };
|
|
2715
|
+
}
|
|
2716
|
+
};
|
|
2717
|
+
function dynamicReference(modelName, options = {}) {
|
|
2718
|
+
return new DynamicReferenceType(modelName, options);
|
|
2719
|
+
}
|
|
2720
|
+
var SafeDynamicReferenceType = class {
|
|
2721
|
+
constructor(modelName, options = {}) {
|
|
2722
|
+
this._kind = "safeDynamicReference";
|
|
2723
|
+
this._modelName = modelName;
|
|
2724
|
+
this._options = options;
|
|
2725
|
+
this.name = `safeDynamicReference("${modelName}")`;
|
|
2726
|
+
}
|
|
2727
|
+
create(snapshot, env) {
|
|
2728
|
+
if (snapshot === void 0 || snapshot === null) {
|
|
2729
|
+
return void 0;
|
|
2730
|
+
}
|
|
2731
|
+
try {
|
|
2732
|
+
if (this._options.get) {
|
|
2733
|
+
return this._options.get(snapshot, null);
|
|
2734
|
+
}
|
|
2735
|
+
const targetType = tryResolveModel(this._modelName);
|
|
2736
|
+
if (!targetType) {
|
|
2737
|
+
return void 0;
|
|
2738
|
+
}
|
|
2739
|
+
return resolveIdentifier(this._modelName, snapshot);
|
|
2740
|
+
} catch {
|
|
2741
|
+
if (this._options.onInvalidated) {
|
|
2742
|
+
return this._options.onInvalidated(snapshot, null);
|
|
2743
|
+
}
|
|
2744
|
+
return void 0;
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
is(value) {
|
|
2748
|
+
if (value === void 0) return true;
|
|
2749
|
+
if (typeof value === "string" || typeof value === "number") return true;
|
|
2750
|
+
const targetType = tryResolveModel(this._modelName);
|
|
2751
|
+
if (targetType) {
|
|
2752
|
+
return targetType.is(value);
|
|
2753
|
+
}
|
|
2754
|
+
return false;
|
|
2755
|
+
}
|
|
2756
|
+
validate(value, context) {
|
|
2757
|
+
if (value === void 0 || value === null) {
|
|
2758
|
+
return { valid: true, errors: [] };
|
|
2759
|
+
}
|
|
2760
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
2761
|
+
return {
|
|
2762
|
+
valid: false,
|
|
2763
|
+
errors: [
|
|
2764
|
+
{
|
|
2765
|
+
context,
|
|
2766
|
+
value,
|
|
2767
|
+
message: "Reference identifier must be a string, number, or undefined"
|
|
2768
|
+
}
|
|
2769
|
+
]
|
|
2770
|
+
};
|
|
2771
|
+
}
|
|
2772
|
+
return { valid: true, errors: [] };
|
|
2773
|
+
}
|
|
2774
|
+
};
|
|
2775
|
+
function safeDynamicReference(modelName, options = {}) {
|
|
2776
|
+
return new SafeDynamicReferenceType(modelName, options);
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2779
|
+
// src/index.ts
|
|
2780
|
+
init_tree();
|
|
2781
|
+
|
|
2782
|
+
// src/lifecycle.ts
|
|
2783
|
+
init_tree();
|
|
2784
|
+
var middlewareStack = [];
|
|
2785
|
+
function addMiddleware(target, handler, includeHooks = true) {
|
|
2786
|
+
middlewareStack.push(handler);
|
|
2787
|
+
return () => {
|
|
2788
|
+
const index = middlewareStack.indexOf(handler);
|
|
2789
|
+
if (index >= 0) {
|
|
2790
|
+
middlewareStack.splice(index, 1);
|
|
2791
|
+
}
|
|
2792
|
+
};
|
|
2793
|
+
}
|
|
2794
|
+
var actionRecorders = /* @__PURE__ */ new WeakMap();
|
|
2795
|
+
function recordActions(target) {
|
|
2796
|
+
const node = getStateTreeNode(target);
|
|
2797
|
+
const actions = [];
|
|
2798
|
+
const recorder = (action) => {
|
|
2799
|
+
if (node.$isAlive) {
|
|
2800
|
+
actions.push(action);
|
|
2801
|
+
}
|
|
2802
|
+
};
|
|
2803
|
+
let recorders = actionRecorders.get(node);
|
|
2804
|
+
if (!recorders) {
|
|
2805
|
+
recorders = /* @__PURE__ */ new Set();
|
|
2806
|
+
actionRecorders.set(node, recorders);
|
|
2807
|
+
}
|
|
2808
|
+
recorders.add(recorder);
|
|
2809
|
+
return {
|
|
2810
|
+
actions,
|
|
2811
|
+
stop: () => {
|
|
2812
|
+
const currentRecorders = actionRecorders.get(node);
|
|
2813
|
+
if (currentRecorders) {
|
|
2814
|
+
currentRecorders.delete(recorder);
|
|
2815
|
+
if (currentRecorders.size === 0) {
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
},
|
|
2819
|
+
replay: (replayTarget) => {
|
|
2820
|
+
const replayNode = getStateTreeNode(replayTarget);
|
|
2821
|
+
for (const action of actions) {
|
|
2822
|
+
const instance = replayNode.getInstance();
|
|
2823
|
+
if (typeof instance[action.name] === "function") {
|
|
2824
|
+
instance[action.name](...action.args);
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
};
|
|
2829
|
+
}
|
|
2830
|
+
function notifyActionRecorders(node, action) {
|
|
2831
|
+
let current = node;
|
|
2832
|
+
while (current) {
|
|
2833
|
+
const recorders = actionRecorders.get(current);
|
|
2834
|
+
if (recorders) {
|
|
2835
|
+
recorders.forEach((recorder) => recorder(action));
|
|
2836
|
+
}
|
|
2837
|
+
current = current.$parent;
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
registerActionRecorderHook((node, call) => {
|
|
2841
|
+
const action = {
|
|
2842
|
+
name: call.name,
|
|
2843
|
+
path: call.path,
|
|
2844
|
+
args: call.args
|
|
2845
|
+
};
|
|
2846
|
+
notifyActionRecorders(node, action);
|
|
2847
|
+
});
|
|
2848
|
+
var protectedNodes = /* @__PURE__ */ new WeakSet();
|
|
2849
|
+
function protect(target) {
|
|
2850
|
+
const node = getStateTreeNode(target);
|
|
2851
|
+
protectedNodes.add(node);
|
|
2852
|
+
}
|
|
2853
|
+
function unprotect(target) {
|
|
2854
|
+
const node = getStateTreeNode(target);
|
|
2855
|
+
protectedNodes.delete(node);
|
|
2856
|
+
}
|
|
2857
|
+
function isProtected(target) {
|
|
2858
|
+
const node = getStateTreeNode(target);
|
|
2859
|
+
return protectedNodes.has(node);
|
|
2860
|
+
}
|
|
2861
|
+
function applyAction(target, action) {
|
|
2862
|
+
const node = getStateTreeNode(target);
|
|
2863
|
+
let currentNode = node;
|
|
2864
|
+
if (action.path) {
|
|
2865
|
+
const parts = action.path.split("/").filter(Boolean);
|
|
2866
|
+
for (const part of parts) {
|
|
2867
|
+
const child = currentNode.getChild(part);
|
|
2868
|
+
if (!child) {
|
|
2869
|
+
throw new Error(
|
|
2870
|
+
`[jotai-state-tree] Invalid action path: ${action.path}`
|
|
2871
|
+
);
|
|
2872
|
+
}
|
|
2873
|
+
currentNode = child;
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
const instance = currentNode.getInstance();
|
|
2877
|
+
if (typeof instance[action.name] !== "function") {
|
|
2878
|
+
throw new Error(`[jotai-state-tree] Action '${action.name}' not found`);
|
|
2879
|
+
}
|
|
2880
|
+
return instance[action.name](...action.args);
|
|
2881
|
+
}
|
|
2882
|
+
function escapeJsonPath(path) {
|
|
2883
|
+
return path.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
2884
|
+
}
|
|
2885
|
+
function unescapeJsonPath(path) {
|
|
2886
|
+
return path.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
2887
|
+
}
|
|
2888
|
+
function splitJsonPath(path) {
|
|
2889
|
+
return path.split("/").filter(Boolean).map(unescapeJsonPath);
|
|
2890
|
+
}
|
|
2891
|
+
function joinJsonPath(parts) {
|
|
2892
|
+
return parts.map(escapeJsonPath).join("/");
|
|
2893
|
+
}
|
|
2894
|
+
|
|
2895
|
+
// src/compat.ts
|
|
2896
|
+
init_tree();
|
|
2897
|
+
function isType(value) {
|
|
2898
|
+
return value !== null && typeof value === "object" && "_kind" in value && "create" in value && "is" in value;
|
|
2899
|
+
}
|
|
2900
|
+
function isPrimitiveType(type) {
|
|
2901
|
+
return isType(type) && (type._kind === "simple" || type._kind === "literal" || type._kind === "enumeration" || type._kind === "identifier" || type._kind === "identifierNumber");
|
|
2902
|
+
}
|
|
2903
|
+
function getTypeName(type) {
|
|
2904
|
+
return type.name;
|
|
2905
|
+
}
|
|
2906
|
+
function isValidSnapshot(type, value) {
|
|
2907
|
+
try {
|
|
2908
|
+
const result = type.validate(value, []);
|
|
2909
|
+
return result.valid;
|
|
2910
|
+
} catch {
|
|
2911
|
+
return false;
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
function getValidationError(type, value) {
|
|
2915
|
+
const result = type.validate(value, [{ path: "", type, parent: null }]);
|
|
2916
|
+
if (result.valid) return null;
|
|
2917
|
+
return result.errors.map((e) => e.message).join("; ");
|
|
2918
|
+
}
|
|
2919
|
+
function isInstanceOf(value, type) {
|
|
2920
|
+
if (!hasStateTreeNode(value)) return false;
|
|
2921
|
+
const node = getStateTreeNode(value);
|
|
2922
|
+
return node.$type === type || node.$type.name === type.name;
|
|
2923
|
+
}
|
|
2924
|
+
function getOrCreate(type, snapshotOrInstance, env) {
|
|
2925
|
+
if (hasStateTreeNode(snapshotOrInstance)) {
|
|
2926
|
+
return snapshotOrInstance;
|
|
2927
|
+
}
|
|
2928
|
+
return type.create(snapshotOrInstance, env);
|
|
2929
|
+
}
|
|
2930
|
+
function getDebugInfo(target) {
|
|
2931
|
+
const node = getStateTreeNode(target);
|
|
2932
|
+
return {
|
|
2933
|
+
typeName: node.$type.name,
|
|
2934
|
+
path: node.$path,
|
|
2935
|
+
identifier: node.identifierValue ?? null,
|
|
2936
|
+
isAlive: node.$isAlive,
|
|
2937
|
+
snapshot: getSnapshot(target)
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
function printTree(target, indent = 0) {
|
|
2941
|
+
const node = getStateTreeNode(target);
|
|
2942
|
+
const prefix = " ".repeat(indent);
|
|
2943
|
+
let output = `${prefix}${node.$type.name}`;
|
|
2944
|
+
if (node.identifierValue !== void 0) {
|
|
2945
|
+
output += ` (${node.identifierValue})`;
|
|
2946
|
+
}
|
|
2947
|
+
output += "\n";
|
|
2948
|
+
for (const [key, child] of node.getChildren()) {
|
|
2949
|
+
const childInstance = child.getInstance();
|
|
2950
|
+
if (childInstance && hasStateTreeNode(childInstance)) {
|
|
2951
|
+
output += `${prefix} ${key}: ${printTree(childInstance, indent + 2)}`;
|
|
2952
|
+
} else {
|
|
2953
|
+
output += `${prefix} ${key}: ${JSON.stringify(child.getValue())}
|
|
2954
|
+
`;
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
return output;
|
|
2958
|
+
}
|
|
2959
|
+
function hasIdentifier(type) {
|
|
2960
|
+
return type.identifierAttribute !== void 0;
|
|
2961
|
+
}
|
|
2962
|
+
function getIdentifierAttribute(type) {
|
|
2963
|
+
return type.identifierAttribute;
|
|
2964
|
+
}
|
|
2965
|
+
function nullable(type) {
|
|
2966
|
+
return {
|
|
2967
|
+
name: `nullable<${type.name}>`,
|
|
2968
|
+
_kind: "maybe",
|
|
2969
|
+
_C: void 0,
|
|
2970
|
+
_S: void 0,
|
|
2971
|
+
_T: void 0,
|
|
2972
|
+
create(snapshot, env) {
|
|
2973
|
+
if (snapshot === null || snapshot === void 0) {
|
|
2974
|
+
return snapshot;
|
|
2975
|
+
}
|
|
2976
|
+
return type.create(snapshot, env);
|
|
2977
|
+
},
|
|
2978
|
+
is(value) {
|
|
2979
|
+
return value === null || value === void 0 || type.is(value);
|
|
2980
|
+
},
|
|
2981
|
+
validate(value, context) {
|
|
2982
|
+
if (value === null || value === void 0) {
|
|
2983
|
+
return { valid: true, errors: [] };
|
|
2984
|
+
}
|
|
2985
|
+
return type.validate(value, context);
|
|
2986
|
+
}
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2989
|
+
function cloneFrozen(value) {
|
|
2990
|
+
if (value === null || typeof value !== "object") {
|
|
2991
|
+
return value;
|
|
2992
|
+
}
|
|
2993
|
+
if (Array.isArray(value)) {
|
|
2994
|
+
return value.map(cloneFrozen);
|
|
2995
|
+
}
|
|
2996
|
+
const result = {};
|
|
2997
|
+
for (const [key, val] of Object.entries(value)) {
|
|
2998
|
+
result[key] = cloneFrozen(val);
|
|
2999
|
+
}
|
|
3000
|
+
return result;
|
|
3001
|
+
}
|
|
3002
|
+
function safeCreate(type, snapshot, env) {
|
|
3003
|
+
try {
|
|
3004
|
+
return type.create(snapshot, env);
|
|
3005
|
+
} catch {
|
|
3006
|
+
return void 0;
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
function createWithDefaults(type, snapshot = {}, env) {
|
|
3010
|
+
return type.create(snapshot, env);
|
|
3011
|
+
}
|
|
3012
|
+
|
|
3013
|
+
// src/undo.ts
|
|
3014
|
+
init_tree();
|
|
3015
|
+
var UndoManager = class {
|
|
3016
|
+
constructor(target, options = {}) {
|
|
3017
|
+
this.historyEntries = [];
|
|
3018
|
+
this.currentIndex = -1;
|
|
3019
|
+
this.isUndoing = false;
|
|
3020
|
+
this.isRedoing = false;
|
|
3021
|
+
this.skipRecording = false;
|
|
3022
|
+
this.grouping = false;
|
|
3023
|
+
this.currentGroup = [];
|
|
3024
|
+
this.currentGroupInverse = [];
|
|
3025
|
+
this.disposer = null;
|
|
3026
|
+
this.lastChangeTime = 0;
|
|
3027
|
+
this.target = target;
|
|
3028
|
+
this.options = {
|
|
3029
|
+
maxHistoryLength: options.maxHistoryLength ?? 100,
|
|
3030
|
+
groupByTime: options.groupByTime ?? false,
|
|
3031
|
+
groupingWindow: options.groupingWindow ?? 200
|
|
3032
|
+
};
|
|
3033
|
+
this.disposer = onPatch(target, (patch, reversePatch) => {
|
|
3034
|
+
this.recordPatch(patch, reversePatch);
|
|
3035
|
+
});
|
|
3036
|
+
}
|
|
3037
|
+
get canUndo() {
|
|
3038
|
+
return this.currentIndex >= 0;
|
|
3039
|
+
}
|
|
3040
|
+
get canRedo() {
|
|
3041
|
+
return this.currentIndex < this.historyEntries.length - 1;
|
|
3042
|
+
}
|
|
3043
|
+
get undoLevels() {
|
|
3044
|
+
return this.currentIndex + 1;
|
|
3045
|
+
}
|
|
3046
|
+
get redoLevels() {
|
|
3047
|
+
return this.historyEntries.length - this.currentIndex - 1;
|
|
3048
|
+
}
|
|
3049
|
+
get history() {
|
|
3050
|
+
return [...this.historyEntries];
|
|
3051
|
+
}
|
|
3052
|
+
get historyIndex() {
|
|
3053
|
+
return this.currentIndex;
|
|
3054
|
+
}
|
|
3055
|
+
recordPatch(patch, reversePatch) {
|
|
3056
|
+
if (this.isUndoing || this.isRedoing || this.skipRecording) {
|
|
3057
|
+
return;
|
|
3058
|
+
}
|
|
3059
|
+
const now = Date.now();
|
|
3060
|
+
if (this.grouping) {
|
|
3061
|
+
this.currentGroup.push(reversePatch);
|
|
3062
|
+
this.currentGroupInverse.unshift({ ...patch });
|
|
3063
|
+
return;
|
|
3064
|
+
}
|
|
3065
|
+
if (this.options.groupByTime && this.historyEntries.length > 0 && now - this.lastChangeTime < this.options.groupingWindow && this.currentIndex === this.historyEntries.length - 1) {
|
|
3066
|
+
const lastEntry = this.historyEntries[this.currentIndex];
|
|
3067
|
+
lastEntry.patches.push(reversePatch);
|
|
3068
|
+
lastEntry.inversePatches.unshift({ ...patch });
|
|
3069
|
+
lastEntry.timestamp = now;
|
|
3070
|
+
} else {
|
|
3071
|
+
if (this.currentIndex < this.historyEntries.length - 1) {
|
|
3072
|
+
this.historyEntries.splice(this.currentIndex + 1);
|
|
3073
|
+
}
|
|
3074
|
+
this.historyEntries.push({
|
|
3075
|
+
patches: [reversePatch],
|
|
3076
|
+
inversePatches: [{ ...patch }],
|
|
3077
|
+
timestamp: now
|
|
3078
|
+
});
|
|
3079
|
+
this.currentIndex++;
|
|
3080
|
+
if (this.historyEntries.length > this.options.maxHistoryLength) {
|
|
3081
|
+
const excess = this.historyEntries.length - this.options.maxHistoryLength;
|
|
3082
|
+
this.historyEntries.splice(0, excess);
|
|
3083
|
+
this.currentIndex -= excess;
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
this.lastChangeTime = now;
|
|
3087
|
+
}
|
|
3088
|
+
undo() {
|
|
3089
|
+
if (!this.canUndo) {
|
|
3090
|
+
return;
|
|
3091
|
+
}
|
|
3092
|
+
this.isUndoing = true;
|
|
3093
|
+
try {
|
|
3094
|
+
const entry = this.historyEntries[this.currentIndex];
|
|
3095
|
+
for (let i = entry.patches.length - 1; i >= 0; i--) {
|
|
3096
|
+
applyPatch(this.target, entry.patches[i]);
|
|
3097
|
+
}
|
|
3098
|
+
this.currentIndex--;
|
|
3099
|
+
} finally {
|
|
3100
|
+
this.isUndoing = false;
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
redo() {
|
|
3104
|
+
if (!this.canRedo) {
|
|
3105
|
+
return;
|
|
3106
|
+
}
|
|
3107
|
+
this.isRedoing = true;
|
|
3108
|
+
try {
|
|
3109
|
+
this.currentIndex++;
|
|
3110
|
+
const entry = this.historyEntries[this.currentIndex];
|
|
3111
|
+
for (const patch of entry.inversePatches) {
|
|
3112
|
+
applyPatch(this.target, patch);
|
|
3113
|
+
}
|
|
3114
|
+
} finally {
|
|
3115
|
+
this.isRedoing = false;
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
clear() {
|
|
3119
|
+
this.historyEntries = [];
|
|
3120
|
+
this.currentIndex = -1;
|
|
3121
|
+
this.currentGroup = [];
|
|
3122
|
+
this.currentGroupInverse = [];
|
|
3123
|
+
this.grouping = false;
|
|
3124
|
+
}
|
|
3125
|
+
startGroup() {
|
|
3126
|
+
this.grouping = true;
|
|
3127
|
+
this.currentGroup = [];
|
|
3128
|
+
this.currentGroupInverse = [];
|
|
3129
|
+
}
|
|
3130
|
+
endGroup() {
|
|
3131
|
+
if (!this.grouping) {
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
3134
|
+
this.grouping = false;
|
|
3135
|
+
if (this.currentGroup.length > 0) {
|
|
3136
|
+
if (this.currentIndex < this.historyEntries.length - 1) {
|
|
3137
|
+
this.historyEntries.splice(this.currentIndex + 1);
|
|
3138
|
+
}
|
|
3139
|
+
this.historyEntries.push({
|
|
3140
|
+
patches: this.currentGroup,
|
|
3141
|
+
inversePatches: this.currentGroupInverse,
|
|
3142
|
+
timestamp: Date.now()
|
|
3143
|
+
});
|
|
3144
|
+
this.currentIndex++;
|
|
3145
|
+
if (this.historyEntries.length > this.options.maxHistoryLength) {
|
|
3146
|
+
const excess = this.historyEntries.length - this.options.maxHistoryLength;
|
|
3147
|
+
this.historyEntries.splice(0, excess);
|
|
3148
|
+
this.currentIndex -= excess;
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
this.currentGroup = [];
|
|
3152
|
+
this.currentGroupInverse = [];
|
|
3153
|
+
}
|
|
3154
|
+
withoutUndo(fn) {
|
|
3155
|
+
this.skipRecording = true;
|
|
3156
|
+
try {
|
|
3157
|
+
return fn();
|
|
3158
|
+
} finally {
|
|
3159
|
+
this.skipRecording = false;
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
dispose() {
|
|
3163
|
+
if (this.disposer) {
|
|
3164
|
+
this.disposer();
|
|
3165
|
+
this.disposer = null;
|
|
3166
|
+
}
|
|
3167
|
+
this.clear();
|
|
3168
|
+
}
|
|
3169
|
+
};
|
|
3170
|
+
function createUndoManager(target, options) {
|
|
3171
|
+
return new UndoManager(target, options);
|
|
3172
|
+
}
|
|
3173
|
+
var TimeTravelManager = class {
|
|
3174
|
+
constructor(target, options = {}) {
|
|
3175
|
+
this.snapshots = [];
|
|
3176
|
+
this.index = -1;
|
|
3177
|
+
this.isApplying = false;
|
|
3178
|
+
this.disposer = null;
|
|
3179
|
+
this.target = target;
|
|
3180
|
+
this.maxSnapshots = options.maxSnapshots ?? 50;
|
|
3181
|
+
this.autoRecord = options.autoRecord ?? false;
|
|
3182
|
+
this.record();
|
|
3183
|
+
if (this.autoRecord) {
|
|
3184
|
+
this.disposer = onPatch(target, () => {
|
|
3185
|
+
if (!this.isApplying) {
|
|
3186
|
+
this.record();
|
|
3187
|
+
}
|
|
3188
|
+
});
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
get currentIndex() {
|
|
3192
|
+
return this.index;
|
|
3193
|
+
}
|
|
3194
|
+
get snapshotCount() {
|
|
3195
|
+
return this.snapshots.length;
|
|
3196
|
+
}
|
|
3197
|
+
get canGoBack() {
|
|
3198
|
+
return this.index > 0;
|
|
3199
|
+
}
|
|
3200
|
+
get canGoForward() {
|
|
3201
|
+
return this.index < this.snapshots.length - 1;
|
|
3202
|
+
}
|
|
3203
|
+
record() {
|
|
3204
|
+
if (this.index < this.snapshots.length - 1) {
|
|
3205
|
+
this.snapshots.splice(this.index + 1);
|
|
3206
|
+
}
|
|
3207
|
+
this.snapshots.push(getSnapshot(this.target));
|
|
3208
|
+
this.index++;
|
|
3209
|
+
if (this.snapshots.length > this.maxSnapshots) {
|
|
3210
|
+
const excess = this.snapshots.length - this.maxSnapshots;
|
|
3211
|
+
this.snapshots.splice(0, excess);
|
|
3212
|
+
this.index -= excess;
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
goBack() {
|
|
3216
|
+
if (!this.canGoBack) return;
|
|
3217
|
+
this.goTo(this.index - 1);
|
|
3218
|
+
}
|
|
3219
|
+
goForward() {
|
|
3220
|
+
if (!this.canGoForward) return;
|
|
3221
|
+
this.goTo(this.index + 1);
|
|
3222
|
+
}
|
|
3223
|
+
goTo(index) {
|
|
3224
|
+
if (index < 0 || index >= this.snapshots.length) return;
|
|
3225
|
+
this.isApplying = true;
|
|
3226
|
+
try {
|
|
3227
|
+
this.index = index;
|
|
3228
|
+
applySnapshot(this.target, this.snapshots[index]);
|
|
3229
|
+
} finally {
|
|
3230
|
+
this.isApplying = false;
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
getSnapshot(index) {
|
|
3234
|
+
if (index < 0 || index >= this.snapshots.length) {
|
|
3235
|
+
throw new Error(`[jotai-state-tree] Invalid snapshot index: ${index}`);
|
|
3236
|
+
}
|
|
3237
|
+
return this.snapshots[index];
|
|
3238
|
+
}
|
|
3239
|
+
clear() {
|
|
3240
|
+
this.snapshots = [];
|
|
3241
|
+
this.index = -1;
|
|
3242
|
+
this.record();
|
|
3243
|
+
}
|
|
3244
|
+
dispose() {
|
|
3245
|
+
if (this.disposer) {
|
|
3246
|
+
this.disposer();
|
|
3247
|
+
this.disposer = null;
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
};
|
|
3251
|
+
function createTimeTravelManager(target, options) {
|
|
3252
|
+
return new TimeTravelManager(target, options);
|
|
3253
|
+
}
|
|
3254
|
+
var ActionRecorder = class {
|
|
3255
|
+
constructor(target) {
|
|
3256
|
+
this.recording = false;
|
|
3257
|
+
this.recordedActions = [];
|
|
3258
|
+
this.disposer = null;
|
|
3259
|
+
this.target = target;
|
|
3260
|
+
}
|
|
3261
|
+
get isRecording() {
|
|
3262
|
+
return this.recording;
|
|
3263
|
+
}
|
|
3264
|
+
get actions() {
|
|
3265
|
+
return [...this.recordedActions];
|
|
3266
|
+
}
|
|
3267
|
+
start() {
|
|
3268
|
+
if (this.recording) return;
|
|
3269
|
+
this.recording = true;
|
|
3270
|
+
const { onAction: onAction2 } = (init_tree(), __toCommonJS(tree_exports));
|
|
3271
|
+
this.disposer = onAction2(this.target, (action) => {
|
|
3272
|
+
this.recordedActions.push({
|
|
3273
|
+
...action,
|
|
3274
|
+
timestamp: Date.now()
|
|
3275
|
+
});
|
|
3276
|
+
});
|
|
3277
|
+
}
|
|
3278
|
+
stop() {
|
|
3279
|
+
this.recording = false;
|
|
3280
|
+
if (this.disposer) {
|
|
3281
|
+
this.disposer();
|
|
3282
|
+
this.disposer = null;
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
clear() {
|
|
3286
|
+
this.recordedActions = [];
|
|
3287
|
+
}
|
|
3288
|
+
replay(target) {
|
|
3289
|
+
const node = getStateTreeNode(target);
|
|
3290
|
+
for (const action of this.recordedActions) {
|
|
3291
|
+
let currentNode = node;
|
|
3292
|
+
if (action.path) {
|
|
3293
|
+
const parts = action.path.split("/").filter(Boolean);
|
|
3294
|
+
for (const part of parts) {
|
|
3295
|
+
const child = currentNode.getChild(part);
|
|
3296
|
+
if (!child) {
|
|
3297
|
+
console.warn(`[jotai-state-tree] Could not find path: ${action.path}`);
|
|
3298
|
+
continue;
|
|
3299
|
+
}
|
|
3300
|
+
currentNode = child;
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
const instance = currentNode.getInstance();
|
|
3304
|
+
if (typeof instance[action.name] === "function") {
|
|
3305
|
+
instance[action.name](...action.args);
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
export() {
|
|
3310
|
+
return JSON.stringify(this.recordedActions, null, 2);
|
|
3311
|
+
}
|
|
3312
|
+
import(json) {
|
|
3313
|
+
try {
|
|
3314
|
+
const actions = JSON.parse(json);
|
|
3315
|
+
if (Array.isArray(actions)) {
|
|
3316
|
+
this.recordedActions = actions;
|
|
3317
|
+
}
|
|
3318
|
+
} catch (e) {
|
|
3319
|
+
throw new Error(`[jotai-state-tree] Failed to import actions: ${e}`);
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
dispose() {
|
|
3323
|
+
this.stop();
|
|
3324
|
+
this.clear();
|
|
3325
|
+
}
|
|
3326
|
+
};
|
|
3327
|
+
function createActionRecorder(target) {
|
|
3328
|
+
return new ActionRecorder(target);
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
// src/index.ts
|
|
3332
|
+
var types = {
|
|
3333
|
+
// Primitives
|
|
3334
|
+
string,
|
|
3335
|
+
number,
|
|
3336
|
+
integer,
|
|
3337
|
+
boolean,
|
|
3338
|
+
Date: DatePrimitive,
|
|
3339
|
+
null: nullType,
|
|
3340
|
+
undefined: undefinedType,
|
|
3341
|
+
finite,
|
|
3342
|
+
float,
|
|
3343
|
+
// Identifiers
|
|
3344
|
+
identifier,
|
|
3345
|
+
identifierNumber,
|
|
3346
|
+
// Literals & Enums
|
|
3347
|
+
literal,
|
|
3348
|
+
enumeration,
|
|
3349
|
+
// Frozen
|
|
3350
|
+
frozen,
|
|
3351
|
+
// Custom
|
|
3352
|
+
custom,
|
|
3353
|
+
// Model
|
|
3354
|
+
model,
|
|
3355
|
+
compose,
|
|
3356
|
+
// Collections
|
|
3357
|
+
array,
|
|
3358
|
+
map,
|
|
3359
|
+
// Optionality
|
|
3360
|
+
optional,
|
|
3361
|
+
maybe,
|
|
3362
|
+
maybeNull,
|
|
3363
|
+
// Union & Late
|
|
3364
|
+
union,
|
|
3365
|
+
late,
|
|
3366
|
+
// References
|
|
3367
|
+
reference,
|
|
3368
|
+
safeReference,
|
|
3369
|
+
// Refinement
|
|
3370
|
+
refinement,
|
|
3371
|
+
// Snapshot processing
|
|
3372
|
+
snapshotProcessor,
|
|
3373
|
+
// Registry-based types (for dynamic model registration)
|
|
3374
|
+
lateModel,
|
|
3375
|
+
dynamicReference,
|
|
3376
|
+
safeDynamicReference
|
|
3377
|
+
};
|
|
3378
|
+
function flow(generator) {
|
|
3379
|
+
return function flowAction(...args) {
|
|
3380
|
+
const gen = generator(...args);
|
|
3381
|
+
function step(nextFn) {
|
|
3382
|
+
let result;
|
|
3383
|
+
try {
|
|
3384
|
+
result = nextFn();
|
|
3385
|
+
} catch (e) {
|
|
3386
|
+
return Promise.reject(e);
|
|
3387
|
+
}
|
|
3388
|
+
if (result.done) {
|
|
3389
|
+
return Promise.resolve(result.value);
|
|
3390
|
+
}
|
|
3391
|
+
return Promise.resolve(result.value).then(
|
|
3392
|
+
(value) => step(() => gen.next(value)),
|
|
3393
|
+
(error) => step(() => gen.throw(error))
|
|
3394
|
+
);
|
|
3395
|
+
}
|
|
3396
|
+
return step(() => gen.next(void 0));
|
|
3397
|
+
};
|
|
3398
|
+
}
|
|
3399
|
+
function cast(value) {
|
|
3400
|
+
return value;
|
|
3401
|
+
}
|
|
3402
|
+
function castToSnapshot(value) {
|
|
3403
|
+
return value;
|
|
3404
|
+
}
|
|
3405
|
+
function castToReferenceSnapshot(value) {
|
|
3406
|
+
const { getIdentifier: getIdentifier2 } = (init_tree(), __toCommonJS(tree_exports));
|
|
3407
|
+
return getIdentifier2(value) ?? value;
|
|
3408
|
+
}
|
|
3409
|
+
function isIdentifierType(type) {
|
|
3410
|
+
return type !== null && typeof type === "object" && "_kind" in type && (type._kind === "identifier" || type._kind === "identifierNumber");
|
|
3411
|
+
}
|
|
3412
|
+
function isModelType(type) {
|
|
3413
|
+
return type !== null && typeof type === "object" && "_kind" in type && type._kind === "model";
|
|
3414
|
+
}
|
|
3415
|
+
function isArrayType(type) {
|
|
3416
|
+
return type !== null && typeof type === "object" && "_kind" in type && type._kind === "array";
|
|
3417
|
+
}
|
|
3418
|
+
function isMapType(type) {
|
|
3419
|
+
return type !== null && typeof type === "object" && "_kind" in type && type._kind === "map";
|
|
3420
|
+
}
|
|
3421
|
+
function isReferenceType(type) {
|
|
3422
|
+
return type !== null && typeof type === "object" && "_kind" in type && (type._kind === "reference" || type._kind === "safeReference");
|
|
3423
|
+
}
|
|
3424
|
+
function isUnionType(type) {
|
|
3425
|
+
return type !== null && typeof type === "object" && "_kind" in type && type._kind === "union";
|
|
3426
|
+
}
|
|
3427
|
+
function isOptionalType(type) {
|
|
3428
|
+
return type !== null && typeof type === "object" && "_kind" in type && (type._kind === "optional" || type._kind === "maybe" || type._kind === "maybeNull");
|
|
3429
|
+
}
|
|
3430
|
+
function isLateType(type) {
|
|
3431
|
+
return type !== null && typeof type === "object" && "_kind" in type && type._kind === "late";
|
|
3432
|
+
}
|
|
3433
|
+
function isFrozenType(type) {
|
|
3434
|
+
return type !== null && typeof type === "object" && "_kind" in type && type._kind === "frozen";
|
|
3435
|
+
}
|
|
3436
|
+
function isLiteralType(type) {
|
|
3437
|
+
return type !== null && typeof type === "object" && "_kind" in type && type._kind === "literal";
|
|
3438
|
+
}
|
|
3439
|
+
function typecheck(type, value) {
|
|
3440
|
+
if (!type.is(value)) {
|
|
3441
|
+
throw new Error(`[jotai-state-tree] Value does not match type`);
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
var index_default = types;
|
|
3445
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
3446
|
+
0 && (module.exports = {
|
|
3447
|
+
Date,
|
|
3448
|
+
addMiddleware,
|
|
3449
|
+
applyAction,
|
|
3450
|
+
applyPatch,
|
|
3451
|
+
applySnapshot,
|
|
3452
|
+
array,
|
|
3453
|
+
boolean,
|
|
3454
|
+
cast,
|
|
3455
|
+
castToReferenceSnapshot,
|
|
3456
|
+
castToSnapshot,
|
|
3457
|
+
cleanupStaleEntries,
|
|
3458
|
+
clearAllRegistries,
|
|
3459
|
+
clearModelRegistry,
|
|
3460
|
+
clone,
|
|
3461
|
+
cloneDeep,
|
|
3462
|
+
cloneFrozen,
|
|
3463
|
+
compose,
|
|
3464
|
+
createActionRecorder,
|
|
3465
|
+
createTimeTravelManager,
|
|
3466
|
+
createUndoManager,
|
|
3467
|
+
createWithDefaults,
|
|
3468
|
+
custom,
|
|
3469
|
+
destroy,
|
|
3470
|
+
detach,
|
|
3471
|
+
dynamicReference,
|
|
3472
|
+
enumeration,
|
|
3473
|
+
escapeJsonPath,
|
|
3474
|
+
findAll,
|
|
3475
|
+
findFirst,
|
|
3476
|
+
finite,
|
|
3477
|
+
float,
|
|
3478
|
+
flow,
|
|
3479
|
+
freeze,
|
|
3480
|
+
frozen,
|
|
3481
|
+
getDebugInfo,
|
|
3482
|
+
getEnv,
|
|
3483
|
+
getGlobalStore,
|
|
3484
|
+
getIdentifier,
|
|
3485
|
+
getIdentifierAttribute,
|
|
3486
|
+
getMembers,
|
|
3487
|
+
getModelMetadata,
|
|
3488
|
+
getOrCreate,
|
|
3489
|
+
getOrCreatePath,
|
|
3490
|
+
getParent,
|
|
3491
|
+
getParentOfType,
|
|
3492
|
+
getPath,
|
|
3493
|
+
getPathParts,
|
|
3494
|
+
getRegisteredModelNames,
|
|
3495
|
+
getRegistryStats,
|
|
3496
|
+
getRelativePath,
|
|
3497
|
+
getRoot,
|
|
3498
|
+
getSnapshot,
|
|
3499
|
+
getTreeStats,
|
|
3500
|
+
getType,
|
|
3501
|
+
getTypeName,
|
|
3502
|
+
getValidationError,
|
|
3503
|
+
hasIdentifier,
|
|
3504
|
+
hasParent,
|
|
3505
|
+
haveSameRoot,
|
|
3506
|
+
identifier,
|
|
3507
|
+
identifierNumber,
|
|
3508
|
+
integer,
|
|
3509
|
+
isAlive,
|
|
3510
|
+
isAncestor,
|
|
3511
|
+
isArrayType,
|
|
3512
|
+
isFrozen,
|
|
3513
|
+
isFrozenType,
|
|
3514
|
+
isIdentifierType,
|
|
3515
|
+
isInstanceOf,
|
|
3516
|
+
isLateType,
|
|
3517
|
+
isLiteralType,
|
|
3518
|
+
isMapType,
|
|
3519
|
+
isModelRegistered,
|
|
3520
|
+
isModelType,
|
|
3521
|
+
isOptionalType,
|
|
3522
|
+
isPrimitiveType,
|
|
3523
|
+
isProtected,
|
|
3524
|
+
isReferenceType,
|
|
3525
|
+
isRoot,
|
|
3526
|
+
isStateTreeNode,
|
|
3527
|
+
isType,
|
|
3528
|
+
isUnionType,
|
|
3529
|
+
isValidReference,
|
|
3530
|
+
isValidSnapshot,
|
|
3531
|
+
joinJsonPath,
|
|
3532
|
+
late,
|
|
3533
|
+
lateModel,
|
|
3534
|
+
literal,
|
|
3535
|
+
map,
|
|
3536
|
+
maybe,
|
|
3537
|
+
maybeNull,
|
|
3538
|
+
model,
|
|
3539
|
+
nullType,
|
|
3540
|
+
nullable,
|
|
3541
|
+
number,
|
|
3542
|
+
onAction,
|
|
3543
|
+
onLifecycleChange,
|
|
3544
|
+
onModelRegistered,
|
|
3545
|
+
onPatch,
|
|
3546
|
+
onSnapshot,
|
|
3547
|
+
optional,
|
|
3548
|
+
printTree,
|
|
3549
|
+
protect,
|
|
3550
|
+
recordActions,
|
|
3551
|
+
recordPatches,
|
|
3552
|
+
reference,
|
|
3553
|
+
refinement,
|
|
3554
|
+
registerModel,
|
|
3555
|
+
resetGlobalStore,
|
|
3556
|
+
resolveIdentifier,
|
|
3557
|
+
resolveModel,
|
|
3558
|
+
resolveModelAsync,
|
|
3559
|
+
resolvePath,
|
|
3560
|
+
safeCreate,
|
|
3561
|
+
safeDynamicReference,
|
|
3562
|
+
safeReference,
|
|
3563
|
+
setGlobalStore,
|
|
3564
|
+
snapshotProcessor,
|
|
3565
|
+
splitJsonPath,
|
|
3566
|
+
string,
|
|
3567
|
+
tryGetParent,
|
|
3568
|
+
tryResolve,
|
|
3569
|
+
tryResolveModel,
|
|
3570
|
+
typecheck,
|
|
3571
|
+
types,
|
|
3572
|
+
undefinedType,
|
|
3573
|
+
unescapeJsonPath,
|
|
3574
|
+
unfreeze,
|
|
3575
|
+
union,
|
|
3576
|
+
unprotect,
|
|
3577
|
+
unregisterModel,
|
|
3578
|
+
walk
|
|
3579
|
+
});
|