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/src/index.ts ADDED
@@ -0,0 +1,647 @@
1
+ /**
2
+ * jotai-state-tree
3
+ * MobX-State-Tree API compatible library powered by Jotai
4
+ */
5
+
6
+ // ============================================================================
7
+ // Type Exports
8
+ // ============================================================================
9
+
10
+ export type {
11
+ // Core types
12
+ IType,
13
+ ISimpleType,
14
+ IModelType,
15
+ IArrayType,
16
+ IMapType,
17
+ IOptionalType,
18
+ IMaybeType,
19
+ IMaybeNullType,
20
+ IReferenceType,
21
+ ISafeReferenceType,
22
+ IUnionType,
23
+ ILiteralType,
24
+ IEnumerationType,
25
+ IFrozenType,
26
+ ILateType,
27
+ IRefinementType,
28
+ IIdentifierType,
29
+ IIdentifierNumberType,
30
+
31
+ // Instance types
32
+ IStateTreeNode,
33
+ IMSTArray,
34
+ IMSTMap,
35
+ ModelInstance,
36
+ ModelProperties,
37
+
38
+ // Utility types
39
+ SnapshotIn,
40
+ SnapshotOut,
41
+ Instance,
42
+ IAnyType,
43
+ IAnyModelType,
44
+ IAnyComplexType,
45
+
46
+ // Patch types
47
+ IJsonPatch,
48
+ IReversibleJsonPatch,
49
+
50
+ // Validation types
51
+ IValidationContext,
52
+ IValidationResult,
53
+ IValidationError,
54
+
55
+ // Options types
56
+ ReferenceOptions,
57
+ UnionOptions,
58
+ CustomTypeOptions,
59
+
60
+ // Disposer
61
+ IDisposer,
62
+ } from "./types";
63
+
64
+ // ============================================================================
65
+ // Primitive Types
66
+ // ============================================================================
67
+
68
+ import {
69
+ string,
70
+ number,
71
+ integer,
72
+ boolean,
73
+ DatePrimitive,
74
+ nullType,
75
+ undefinedType,
76
+ identifier,
77
+ identifierNumber,
78
+ literal,
79
+ enumeration,
80
+ frozen,
81
+ custom,
82
+ finite,
83
+ float,
84
+ } from "./primitives";
85
+
86
+ // ============================================================================
87
+ // Model Type
88
+ // ============================================================================
89
+
90
+ import { model, compose } from "./model";
91
+
92
+ // ============================================================================
93
+ // Collection Types
94
+ // ============================================================================
95
+
96
+ import { array } from "./array";
97
+ import { map } from "./map";
98
+
99
+ // ============================================================================
100
+ // Utility Types
101
+ // ============================================================================
102
+
103
+ import {
104
+ optional,
105
+ maybe,
106
+ maybeNull,
107
+ union,
108
+ late,
109
+ refinement,
110
+ reference,
111
+ safeReference,
112
+ snapshotProcessor,
113
+ } from "./utilities";
114
+
115
+ // ============================================================================
116
+ // Model Registry (Dynamic Model Registration)
117
+ // ============================================================================
118
+
119
+ import {
120
+ registerModel,
121
+ unregisterModel,
122
+ isModelRegistered,
123
+ resolveModel,
124
+ tryResolveModel,
125
+ resolveModelAsync,
126
+ getModelMetadata,
127
+ getRegisteredModelNames,
128
+ onModelRegistered,
129
+ clearModelRegistry,
130
+ lateModel,
131
+ dynamicReference,
132
+ safeDynamicReference,
133
+ type DynamicReferenceOptions,
134
+ } from "./registry";
135
+
136
+ // ============================================================================
137
+ // Types Namespace (MST Compatible)
138
+ // ============================================================================
139
+
140
+ /**
141
+ * The `types` namespace contains all type constructors.
142
+ * This matches the MobX-State-Tree API.
143
+ */
144
+ export const types = {
145
+ // Primitives
146
+ string,
147
+ number,
148
+ integer,
149
+ boolean,
150
+ Date: DatePrimitive,
151
+ null: nullType,
152
+ undefined: undefinedType,
153
+ finite,
154
+ float,
155
+
156
+ // Identifiers
157
+ identifier,
158
+ identifierNumber,
159
+
160
+ // Literals & Enums
161
+ literal,
162
+ enumeration,
163
+
164
+ // Frozen
165
+ frozen,
166
+
167
+ // Custom
168
+ custom,
169
+
170
+ // Model
171
+ model,
172
+ compose,
173
+
174
+ // Collections
175
+ array,
176
+ map,
177
+
178
+ // Optionality
179
+ optional,
180
+ maybe,
181
+ maybeNull,
182
+
183
+ // Union & Late
184
+ union,
185
+ late,
186
+
187
+ // References
188
+ reference,
189
+ safeReference,
190
+
191
+ // Refinement
192
+ refinement,
193
+
194
+ // Snapshot processing
195
+ snapshotProcessor,
196
+
197
+ // Registry-based types (for dynamic model registration)
198
+ lateModel,
199
+ dynamicReference,
200
+ safeDynamicReference,
201
+ };
202
+
203
+ // Also export types directly for destructuring
204
+ export {
205
+ // Primitives
206
+ string,
207
+ number,
208
+ integer,
209
+ boolean,
210
+ DatePrimitive as Date,
211
+ nullType,
212
+ undefinedType,
213
+ finite,
214
+ float,
215
+
216
+ // Identifiers
217
+ identifier,
218
+ identifierNumber,
219
+
220
+ // Literals & Enums
221
+ literal,
222
+ enumeration,
223
+
224
+ // Frozen
225
+ frozen,
226
+
227
+ // Custom
228
+ custom,
229
+
230
+ // Model
231
+ model,
232
+ compose,
233
+
234
+ // Collections
235
+ array,
236
+ map,
237
+
238
+ // Optionality
239
+ optional,
240
+ maybe,
241
+ maybeNull,
242
+
243
+ // Union & Late
244
+ union,
245
+ late,
246
+
247
+ // References
248
+ reference,
249
+ safeReference,
250
+
251
+ // Refinement
252
+ refinement,
253
+
254
+ // Snapshot processing
255
+ snapshotProcessor,
256
+
257
+ // Registry-based types (for dynamic model registration)
258
+ lateModel,
259
+ dynamicReference,
260
+ safeDynamicReference,
261
+ };
262
+
263
+ // Also export registry functions directly
264
+ export {
265
+ // Model registry
266
+ registerModel,
267
+ unregisterModel,
268
+ isModelRegistered,
269
+ resolveModel,
270
+ tryResolveModel,
271
+ resolveModelAsync,
272
+ getModelMetadata,
273
+ getRegisteredModelNames,
274
+ onModelRegistered,
275
+ clearModelRegistry,
276
+ } from "./registry";
277
+
278
+ export type { DynamicReferenceOptions } from "./registry";
279
+
280
+ // ============================================================================
281
+ // Tree Utilities
282
+ // ============================================================================
283
+
284
+ export {
285
+ // Core utilities
286
+ getSnapshot,
287
+ applySnapshot,
288
+ onSnapshot,
289
+ onPatch,
290
+ applyPatch,
291
+ recordPatches,
292
+ onAction,
293
+
294
+ // Tree navigation
295
+ getRoot,
296
+ getParent,
297
+ tryGetParent,
298
+ hasParent,
299
+ getParentOfType,
300
+ getPath,
301
+ getPathParts,
302
+ getEnv,
303
+ getType,
304
+ getIdentifier,
305
+
306
+ // Node state
307
+ isAlive,
308
+ isRoot,
309
+ isStateTreeNode,
310
+
311
+ // Tree manipulation
312
+ destroy,
313
+ detach,
314
+ clone,
315
+ walk,
316
+
317
+ // Resolution
318
+ resolvePath,
319
+ tryResolve,
320
+ resolveIdentifier,
321
+
322
+ // Members
323
+ getMembers,
324
+
325
+ // Store management
326
+ getGlobalStore,
327
+ setGlobalStore,
328
+ resetGlobalStore,
329
+
330
+ // Advanced tree utilities
331
+ getRelativePath,
332
+ isAncestor,
333
+ haveSameRoot,
334
+ findAll,
335
+ findFirst,
336
+ isValidReference,
337
+ getTreeStats,
338
+ cloneDeep,
339
+ getOrCreatePath,
340
+ freeze,
341
+ isFrozen,
342
+ unfreeze,
343
+
344
+ // Registry utilities (for testing and debugging)
345
+ getRegistryStats,
346
+ cleanupStaleEntries,
347
+ clearAllRegistries,
348
+
349
+ // Lifecycle subscriptions
350
+ onLifecycleChange,
351
+ } from "./tree";
352
+
353
+ // ============================================================================
354
+ // Lifecycle & Middleware
355
+ // ============================================================================
356
+
357
+ export {
358
+ // Middleware
359
+ addMiddleware,
360
+
361
+ // Action recording/replay
362
+ recordActions,
363
+ applyAction,
364
+
365
+ // Protection
366
+ protect,
367
+ unprotect,
368
+ isProtected,
369
+
370
+ // Path utilities
371
+ escapeJsonPath,
372
+ unescapeJsonPath,
373
+ splitJsonPath,
374
+ joinJsonPath,
375
+ } from "./lifecycle";
376
+
377
+ export type {
378
+ IMiddlewareEvent,
379
+ IMiddlewareHandler,
380
+ ISerializedActionCall,
381
+ } from "./lifecycle";
382
+
383
+ // ============================================================================
384
+ // Compatibility Utilities
385
+ // ============================================================================
386
+
387
+ export {
388
+ // Type checking
389
+ isType,
390
+ isPrimitiveType,
391
+ getTypeName,
392
+
393
+ // Snapshot utilities
394
+ isValidSnapshot,
395
+ getValidationError,
396
+
397
+ // Instance utilities
398
+ isInstanceOf,
399
+ getOrCreate,
400
+
401
+ // Debugging
402
+ getDebugInfo,
403
+ printTree,
404
+
405
+ // Identifier utilities
406
+ hasIdentifier,
407
+ getIdentifierAttribute,
408
+
409
+ // Type composition
410
+ nullable,
411
+
412
+ // Safe creation
413
+ safeCreate,
414
+ createWithDefaults,
415
+
416
+ // Frozen clone
417
+ cloneFrozen,
418
+ } from "./compat";
419
+
420
+ // ============================================================================
421
+ // Flow (Async Actions)
422
+ // ============================================================================
423
+
424
+ /**
425
+ * Creates an async action (generator function) that can be yielded.
426
+ * Compatible with MST's flow().
427
+ */
428
+ export function flow<Args extends unknown[], R>(
429
+ generator: (...args: Args) => Generator<Promise<unknown>, R, unknown>,
430
+ ): (...args: Args) => Promise<R> {
431
+ return function flowAction(...args: Args): Promise<R> {
432
+ const gen = generator(...args);
433
+
434
+ function step(
435
+ nextFn: () => IteratorResult<Promise<unknown>, R>,
436
+ ): Promise<R> {
437
+ let result: IteratorResult<Promise<unknown>, R>;
438
+ try {
439
+ result = nextFn();
440
+ } catch (e) {
441
+ return Promise.reject(e);
442
+ }
443
+
444
+ if (result.done) {
445
+ return Promise.resolve(result.value);
446
+ }
447
+
448
+ return Promise.resolve(result.value).then(
449
+ (value) => step(() => gen.next(value)),
450
+ (error) => step(() => gen.throw(error)),
451
+ );
452
+ }
453
+
454
+ return step(() => gen.next(undefined));
455
+ };
456
+ }
457
+
458
+ /**
459
+ * Cast a value to a different type.
460
+ * Useful for working around TypeScript limitations.
461
+ */
462
+ export function cast<T>(value: unknown): T {
463
+ return value as T;
464
+ }
465
+
466
+ /**
467
+ * Cast a value to a snapshot type.
468
+ */
469
+ export function castToSnapshot<T>(value: T): T {
470
+ return value;
471
+ }
472
+
473
+ /**
474
+ * Cast a value to a reference snapshot (identifier).
475
+ */
476
+ export function castToReferenceSnapshot<T>(value: T): string | number {
477
+ const { getIdentifier } = require("./tree");
478
+ return getIdentifier(value) ?? (value as unknown as string | number);
479
+ }
480
+
481
+ // ============================================================================
482
+ // Utility Functions
483
+ // ============================================================================
484
+
485
+ /**
486
+ * Check if a value is a valid identifier.
487
+ */
488
+ export function isIdentifierType(
489
+ type: unknown,
490
+ ): type is typeof identifier | typeof identifierNumber {
491
+ return (
492
+ type !== null &&
493
+ typeof type === "object" &&
494
+ "_kind" in type &&
495
+ ((type as { _kind: string })._kind === "identifier" ||
496
+ (type as { _kind: string })._kind === "identifierNumber")
497
+ );
498
+ }
499
+
500
+ /**
501
+ * Check if a type is a model type.
502
+ */
503
+ export function isModelType(type: unknown): boolean {
504
+ return (
505
+ type !== null &&
506
+ typeof type === "object" &&
507
+ "_kind" in type &&
508
+ (type as { _kind: string })._kind === "model"
509
+ );
510
+ }
511
+
512
+ /**
513
+ * Check if a type is an array type.
514
+ */
515
+ export function isArrayType(type: unknown): boolean {
516
+ return (
517
+ type !== null &&
518
+ typeof type === "object" &&
519
+ "_kind" in type &&
520
+ (type as { _kind: string })._kind === "array"
521
+ );
522
+ }
523
+
524
+ /**
525
+ * Check if a type is a map type.
526
+ */
527
+ export function isMapType(type: unknown): boolean {
528
+ return (
529
+ type !== null &&
530
+ typeof type === "object" &&
531
+ "_kind" in type &&
532
+ (type as { _kind: string })._kind === "map"
533
+ );
534
+ }
535
+
536
+ /**
537
+ * Check if a type is a reference type.
538
+ */
539
+ export function isReferenceType(type: unknown): boolean {
540
+ return (
541
+ type !== null &&
542
+ typeof type === "object" &&
543
+ "_kind" in type &&
544
+ ((type as { _kind: string })._kind === "reference" ||
545
+ (type as { _kind: string })._kind === "safeReference")
546
+ );
547
+ }
548
+
549
+ /**
550
+ * Check if a type is a union type.
551
+ */
552
+ export function isUnionType(type: unknown): boolean {
553
+ return (
554
+ type !== null &&
555
+ typeof type === "object" &&
556
+ "_kind" in type &&
557
+ (type as { _kind: string })._kind === "union"
558
+ );
559
+ }
560
+
561
+ /**
562
+ * Check if a type is an optional type.
563
+ */
564
+ export function isOptionalType(type: unknown): boolean {
565
+ return (
566
+ type !== null &&
567
+ typeof type === "object" &&
568
+ "_kind" in type &&
569
+ ((type as { _kind: string })._kind === "optional" ||
570
+ (type as { _kind: string })._kind === "maybe" ||
571
+ (type as { _kind: string })._kind === "maybeNull")
572
+ );
573
+ }
574
+
575
+ /**
576
+ * Check if a type is a late type.
577
+ */
578
+ export function isLateType(type: unknown): boolean {
579
+ return (
580
+ type !== null &&
581
+ typeof type === "object" &&
582
+ "_kind" in type &&
583
+ (type as { _kind: string })._kind === "late"
584
+ );
585
+ }
586
+
587
+ /**
588
+ * Check if a type is a frozen type.
589
+ */
590
+ export function isFrozenType(type: unknown): boolean {
591
+ return (
592
+ type !== null &&
593
+ typeof type === "object" &&
594
+ "_kind" in type &&
595
+ (type as { _kind: string })._kind === "frozen"
596
+ );
597
+ }
598
+
599
+ /**
600
+ * Check if a type is a literal type.
601
+ */
602
+ export function isLiteralType(type: unknown): boolean {
603
+ return (
604
+ type !== null &&
605
+ typeof type === "object" &&
606
+ "_kind" in type &&
607
+ (type as { _kind: string })._kind === "literal"
608
+ );
609
+ }
610
+
611
+ /**
612
+ * Get the type of a value, or undefined if not a state tree node.
613
+ */
614
+ export function typecheck<T>(
615
+ type: { is(v: unknown): v is T },
616
+ value: unknown,
617
+ ): void {
618
+ if (!type.is(value)) {
619
+ throw new Error(`[jotai-state-tree] Value does not match type`);
620
+ }
621
+ }
622
+
623
+ // ============================================================================
624
+ // Undo/Redo & Time Travel
625
+ // ============================================================================
626
+
627
+ export {
628
+ createUndoManager,
629
+ createTimeTravelManager,
630
+ createActionRecorder,
631
+ } from "./undo";
632
+
633
+ export type {
634
+ IUndoManager,
635
+ IUndoManagerOptions,
636
+ IHistoryEntry,
637
+ ITimeTravelManager,
638
+ IActionRecorder,
639
+ IActionRecording,
640
+ } from "./undo";
641
+
642
+ // ============================================================================
643
+ // Re-export for convenience
644
+ // ============================================================================
645
+
646
+ // Default export for convenient import
647
+ export default types;