jotai-state-tree 1.0.3 → 1.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/dist/index.d.mts CHANGED
@@ -105,6 +105,21 @@ interface IModelType<P extends ModelProperties, V extends object, A extends obje
105
105
  beforeDetach(fn: (self: ModelInstance<P, V, A, Vol> & V & A & Vol) => void): IModelType<P, V, A, Vol>;
106
106
  /** Add beforeDestroy lifecycle hook (called before node is destroyed) */
107
107
  beforeDestroy(fn: (self: ModelInstance<P, V, A, Vol> & V & A & Vol) => void): IModelType<P, V, A, Vol>;
108
+ /**
109
+ * Apply a mixin to this model.
110
+ * The mixin's requirements must be satisfied by this model's properties.
111
+ *
112
+ * @example
113
+ * const Timestamped = types.mixin({
114
+ * requires: { createdAt: types.number },
115
+ * views: (self) => ({ get formattedDate() { ... } }),
116
+ * });
117
+ *
118
+ * const MyModel = types
119
+ * .model({ name: types.string, createdAt: types.number })
120
+ * .apply(Timestamped);
121
+ */
122
+ apply<RequiredProps extends ModelProperties, MV extends object, MA extends object, MVol extends object>(mixin: IMixin<RequiredProps, MV, MA, MVol>): P extends RequiredProps ? IModelType<P, V & MV, A & MA, Vol & MVol> : never;
108
123
  }
109
124
  /**
110
125
  * MST Array interface that allows both instance types and snapshot/creation types
@@ -243,6 +258,58 @@ interface IReversibleJsonPatch extends IJsonPatch {
243
258
  type IAnyType = IType<unknown, unknown, unknown>;
244
259
  type IAnyModelType = IModelType<ModelProperties, object, object, object>;
245
260
  type IAnyComplexType = IAnyModelType | IArrayType<IAnyType> | IMapType<IAnyType>;
261
+ /**
262
+ * Extract the full "self" type from a model type.
263
+ * This includes properties, views, actions, and volatile state.
264
+ *
265
+ * @example
266
+ * const User = types.model({ name: types.string }).views(self => ({ ... }));
267
+ * type UserSelf = ModelSelf<typeof User>; // includes name + all views
268
+ */
269
+ type ModelSelf<M> = M extends IModelType<infer P, infer V, infer A, infer Vol> ? ModelInstance<P, V, A, Vol> & V & A & Vol : never;
270
+ /**
271
+ * Configuration for creating a mixin.
272
+ * @template RequiredProps - Properties the mixin requires from the base model
273
+ * @template V - Views the mixin provides
274
+ * @template A - Actions the mixin provides
275
+ * @template Vol - Volatile state the mixin provides
276
+ */
277
+ interface MixinConfig<RequiredProps extends ModelProperties, V extends object = object, A extends object = object, Vol extends object = object> {
278
+ /** Properties the mixin requires from any model it's applied to */
279
+ requires?: RequiredProps;
280
+ /** Views to add to the model */
281
+ views?: (self: ModelInstanceType<RequiredProps>) => V;
282
+ /** Actions to add to the model */
283
+ actions?: (self: ModelInstanceType<RequiredProps> & V) => A;
284
+ /** Volatile state to add to the model */
285
+ volatile?: (self: ModelInstanceType<RequiredProps> & V & A) => Vol;
286
+ }
287
+ /**
288
+ * A mixin that can be applied to models to add views, actions, and volatile state.
289
+ * Mixins declare what properties they require and what they provide.
290
+ *
291
+ * @template RequiredProps - Properties the mixin requires
292
+ * @template V - Views the mixin provides
293
+ * @template A - Actions the mixin provides
294
+ * @template Vol - Volatile state the mixin provides
295
+ */
296
+ interface IMixin<RequiredProps extends ModelProperties, V extends object = object, A extends object = object, Vol extends object = object> {
297
+ readonly _kind: "mixin";
298
+ /** Properties the mixin requires from the base model */
299
+ readonly requires: RequiredProps;
300
+ /** Function to create views */
301
+ readonly views?: (self: ModelInstanceType<RequiredProps>) => V;
302
+ /** Function to create actions */
303
+ readonly actions?: (self: ModelInstanceType<RequiredProps> & V) => A;
304
+ /** Function to create volatile state */
305
+ readonly volatile?: (self: ModelInstanceType<RequiredProps> & V & A) => Vol;
306
+ /** Phantom types for inference */
307
+ readonly _V: V;
308
+ readonly _A: A;
309
+ readonly _Vol: Vol;
310
+ }
311
+ /** Any mixin type */
312
+ type IAnyMixin = IMixin<ModelProperties, object, object, object>;
246
313
  interface IDisposer {
247
314
  (): void;
248
315
  }
@@ -302,6 +369,39 @@ declare function model<P extends ModelProperties>(name: string, properties: P):
302
369
  declare function model<P extends ModelProperties>(properties: P): IModelType<P, object, object, object>;
303
370
  declare function compose<PA extends ModelProperties, PB extends ModelProperties, VA extends object, VB extends object, AA extends object, AB extends object, VolA extends object, VolB extends object>(name: string, a: IModelType<PA, VA, AA, VolA>, b: IModelType<PB, VB, AB, VolB>): IModelType<PA & PB, VA & VB, AA & AB, VolA & VolB>;
304
371
  declare function compose<PA extends ModelProperties, PB extends ModelProperties, VA extends object, VB extends object, AA extends object, AB extends object, VolA extends object, VolB extends object>(a: IModelType<PA, VA, AA, VolA>, b: IModelType<PB, VB, AB, VolB>): IModelType<PA & PB, VA & VB, AA & AB, VolA & VolB>;
372
+ /**
373
+ * Create a type-safe mixin that can be applied to models.
374
+ *
375
+ * Mixins declare:
376
+ * - `requires`: Properties the mixin needs from any model it's applied to
377
+ * - `views`: Computed views to add
378
+ * - `actions`: Actions to add
379
+ * - `volatile`: Volatile (non-serialized) state to add
380
+ *
381
+ * @example
382
+ * ```typescript
383
+ * // Create a mixin that adds validation capabilities
384
+ * const Validatable = types.mixin({
385
+ * requires: {
386
+ * errors: types.array(types.string),
387
+ * },
388
+ * views: (self) => ({
389
+ * get isValid() { return self.errors.length === 0; },
390
+ * get hasErrors() { return self.errors.length > 0; },
391
+ * }),
392
+ * actions: (self) => ({
393
+ * clearErrors() { self.errors.clear(); },
394
+ * addError(msg: string) { self.errors.push(msg); },
395
+ * }),
396
+ * });
397
+ *
398
+ * // Apply to a model
399
+ * const FormModel = types
400
+ * .model({ name: types.string, errors: types.array(types.string) })
401
+ * .apply(Validatable);
402
+ * ```
403
+ */
404
+ declare function mixin<RequiredProps extends ModelProperties = ModelProperties, V extends object = object, A extends object = object, Vol extends object = object>(config: MixinConfig<RequiredProps, V, A, Vol>): IMixin<RequiredProps, V, A, Vol>;
305
405
 
306
406
  /**
307
407
  * Array type implementation
@@ -1025,6 +1125,7 @@ declare const types: {
1025
1125
  custom: typeof custom;
1026
1126
  model: typeof model;
1027
1127
  compose: typeof compose;
1128
+ mixin: typeof mixin;
1028
1129
  array: typeof array;
1029
1130
  map: typeof map;
1030
1131
  optional: typeof optional;
@@ -1106,4 +1207,4 @@ declare function typecheck<T>(type: {
1106
1207
  is(v: unknown): v is T;
1107
1208
  }, value: unknown): void;
1108
1209
 
1109
- export { type CustomTypeOptions$1 as CustomTypeOptions, DatePrimitive as Date, type DynamicReferenceOptions, type IActionRecorder, type IActionRecording, type IAnyComplexType, type IAnyModelType, type IAnyType, type IArrayType, type IDisposer, type IEnumerationType, type IFrozenType, type IHistoryEntry, type IIdentifierNumberType, type IIdentifierType, type IJsonPatch, type ILateType, type ILiteralType, type IMSTArray, type IMSTMap, type IMapType, type IMaybeNullType, type IMaybeType, type IMiddlewareEvent, type IMiddlewareHandler, type IModelType, type IOptionalType, type IReferenceType, type IRefinementType, type IReversibleJsonPatch, type ISafeReferenceType, type ISerializedActionCall, type ISimpleType, type IStateTreeNode, type ITimeTravelManager, type IType, type IUndoManager, type IUndoManagerOptions, type IUnionType, type IValidationContext, type IValidationError, type IValidationResult, type Instance, type ModelInstance, type ModelProperties, type ReferenceOptions, type SnapshotIn, type SnapshotOut, type UnionOptions, addMiddleware, applyAction, applyPatch, applySnapshot, array, boolean, cast, castToReferenceSnapshot, castToSnapshot, cleanupStaleEntries, clearAllRegistries, clearModelRegistry, clone, cloneDeep, cloneFrozen, compose, createActionRecorder, createTimeTravelManager, createUndoManager, createWithDefaults, custom, types as default, destroy, detach, dynamicReference, enumeration, escapeJsonPath, findAll, findFirst, finite, float, flow, freeze, frozen, getDebugInfo, getEnv, getGlobalStore, getIdentifier, getIdentifierAttribute, getMembers, getModelMetadata, getOrCreate, getOrCreatePath, getParent, getParentOfType, getPath, getPathParts, getRegisteredModelNames, getRegistryStats, getRelativePath, getRoot, getSnapshot, getTreeStats, getType, getTypeName, getValidationError, hasIdentifier, hasParent, haveSameRoot, identifier, identifierNumber, integer, isAlive, isAncestor, isArrayType, isFrozen, isFrozenType, isIdentifierType, isInstanceOf, isLateType, isLiteralType, isMapType, isModelRegistered, isModelType, isOptionalType, isPrimitiveType, isProtected, isReferenceType, isRoot, isStateTreeNode, isType, isUnionType, isValidReference, isValidSnapshot, joinJsonPath, late, lateModel, literal, map, maybe, maybeNull, model, nullType, nullable, number, onAction, onLifecycleChange, onModelRegistered, onPatch, onSnapshot, optional, printTree, protect, recordActions, recordPatches, reference, refinement, registerModel, resetGlobalStore, resolveIdentifier, resolveModel, resolveModelAsync, resolvePath, safeCreate, safeDynamicReference, safeReference, setGlobalStore, snapshotProcessor, splitJsonPath, string, tryGetParent, tryResolve, tryResolveModel, typecheck, types, undefinedType, unescapeJsonPath, unfreeze, union, unprotect, unregisterModel, walk };
1210
+ export { type CustomTypeOptions$1 as CustomTypeOptions, DatePrimitive as Date, type DynamicReferenceOptions, type IActionRecorder, type IActionRecording, type IAnyComplexType, type IAnyMixin, type IAnyModelType, type IAnyType, type IArrayType, type IDisposer, type IEnumerationType, type IFrozenType, type IHistoryEntry, type IIdentifierNumberType, type IIdentifierType, type IJsonPatch, type ILateType, type ILiteralType, type IMSTArray, type IMSTMap, type IMapType, type IMaybeNullType, type IMaybeType, type IMiddlewareEvent, type IMiddlewareHandler, type IMixin, type IModelType, type IOptionalType, type IReferenceType, type IRefinementType, type IReversibleJsonPatch, type ISafeReferenceType, type ISerializedActionCall, type ISimpleType, type IStateTreeNode, type ITimeTravelManager, type IType, type IUndoManager, type IUndoManagerOptions, type IUnionType, type IValidationContext, type IValidationError, type IValidationResult, type Instance, type MixinConfig, type ModelInstance, type ModelProperties, type ModelSelf, type ReferenceOptions, type SnapshotIn, type SnapshotOut, type UnionOptions, addMiddleware, applyAction, applyPatch, applySnapshot, array, boolean, cast, castToReferenceSnapshot, castToSnapshot, cleanupStaleEntries, clearAllRegistries, clearModelRegistry, clone, cloneDeep, cloneFrozen, compose, createActionRecorder, createTimeTravelManager, createUndoManager, createWithDefaults, custom, types as default, destroy, detach, dynamicReference, enumeration, escapeJsonPath, findAll, findFirst, finite, float, flow, freeze, frozen, getDebugInfo, getEnv, getGlobalStore, getIdentifier, getIdentifierAttribute, getMembers, getModelMetadata, getOrCreate, getOrCreatePath, getParent, getParentOfType, getPath, getPathParts, getRegisteredModelNames, getRegistryStats, getRelativePath, getRoot, getSnapshot, getTreeStats, getType, getTypeName, getValidationError, hasIdentifier, hasParent, haveSameRoot, identifier, identifierNumber, integer, isAlive, isAncestor, isArrayType, isFrozen, isFrozenType, isIdentifierType, isInstanceOf, isLateType, isLiteralType, isMapType, isModelRegistered, isModelType, isOptionalType, isPrimitiveType, isProtected, isReferenceType, isRoot, isStateTreeNode, isType, isUnionType, isValidReference, isValidSnapshot, joinJsonPath, late, lateModel, literal, map, maybe, maybeNull, mixin, model, nullType, nullable, number, onAction, onLifecycleChange, onModelRegistered, onPatch, onSnapshot, optional, printTree, protect, recordActions, recordPatches, reference, refinement, registerModel, resetGlobalStore, resolveIdentifier, resolveModel, resolveModelAsync, resolvePath, safeCreate, safeDynamicReference, safeReference, setGlobalStore, snapshotProcessor, splitJsonPath, string, tryGetParent, tryResolve, tryResolveModel, typecheck, types, undefinedType, unescapeJsonPath, unfreeze, union, unprotect, unregisterModel, walk };
package/dist/index.d.ts CHANGED
@@ -105,6 +105,21 @@ interface IModelType<P extends ModelProperties, V extends object, A extends obje
105
105
  beforeDetach(fn: (self: ModelInstance<P, V, A, Vol> & V & A & Vol) => void): IModelType<P, V, A, Vol>;
106
106
  /** Add beforeDestroy lifecycle hook (called before node is destroyed) */
107
107
  beforeDestroy(fn: (self: ModelInstance<P, V, A, Vol> & V & A & Vol) => void): IModelType<P, V, A, Vol>;
108
+ /**
109
+ * Apply a mixin to this model.
110
+ * The mixin's requirements must be satisfied by this model's properties.
111
+ *
112
+ * @example
113
+ * const Timestamped = types.mixin({
114
+ * requires: { createdAt: types.number },
115
+ * views: (self) => ({ get formattedDate() { ... } }),
116
+ * });
117
+ *
118
+ * const MyModel = types
119
+ * .model({ name: types.string, createdAt: types.number })
120
+ * .apply(Timestamped);
121
+ */
122
+ apply<RequiredProps extends ModelProperties, MV extends object, MA extends object, MVol extends object>(mixin: IMixin<RequiredProps, MV, MA, MVol>): P extends RequiredProps ? IModelType<P, V & MV, A & MA, Vol & MVol> : never;
108
123
  }
109
124
  /**
110
125
  * MST Array interface that allows both instance types and snapshot/creation types
@@ -243,6 +258,58 @@ interface IReversibleJsonPatch extends IJsonPatch {
243
258
  type IAnyType = IType<unknown, unknown, unknown>;
244
259
  type IAnyModelType = IModelType<ModelProperties, object, object, object>;
245
260
  type IAnyComplexType = IAnyModelType | IArrayType<IAnyType> | IMapType<IAnyType>;
261
+ /**
262
+ * Extract the full "self" type from a model type.
263
+ * This includes properties, views, actions, and volatile state.
264
+ *
265
+ * @example
266
+ * const User = types.model({ name: types.string }).views(self => ({ ... }));
267
+ * type UserSelf = ModelSelf<typeof User>; // includes name + all views
268
+ */
269
+ type ModelSelf<M> = M extends IModelType<infer P, infer V, infer A, infer Vol> ? ModelInstance<P, V, A, Vol> & V & A & Vol : never;
270
+ /**
271
+ * Configuration for creating a mixin.
272
+ * @template RequiredProps - Properties the mixin requires from the base model
273
+ * @template V - Views the mixin provides
274
+ * @template A - Actions the mixin provides
275
+ * @template Vol - Volatile state the mixin provides
276
+ */
277
+ interface MixinConfig<RequiredProps extends ModelProperties, V extends object = object, A extends object = object, Vol extends object = object> {
278
+ /** Properties the mixin requires from any model it's applied to */
279
+ requires?: RequiredProps;
280
+ /** Views to add to the model */
281
+ views?: (self: ModelInstanceType<RequiredProps>) => V;
282
+ /** Actions to add to the model */
283
+ actions?: (self: ModelInstanceType<RequiredProps> & V) => A;
284
+ /** Volatile state to add to the model */
285
+ volatile?: (self: ModelInstanceType<RequiredProps> & V & A) => Vol;
286
+ }
287
+ /**
288
+ * A mixin that can be applied to models to add views, actions, and volatile state.
289
+ * Mixins declare what properties they require and what they provide.
290
+ *
291
+ * @template RequiredProps - Properties the mixin requires
292
+ * @template V - Views the mixin provides
293
+ * @template A - Actions the mixin provides
294
+ * @template Vol - Volatile state the mixin provides
295
+ */
296
+ interface IMixin<RequiredProps extends ModelProperties, V extends object = object, A extends object = object, Vol extends object = object> {
297
+ readonly _kind: "mixin";
298
+ /** Properties the mixin requires from the base model */
299
+ readonly requires: RequiredProps;
300
+ /** Function to create views */
301
+ readonly views?: (self: ModelInstanceType<RequiredProps>) => V;
302
+ /** Function to create actions */
303
+ readonly actions?: (self: ModelInstanceType<RequiredProps> & V) => A;
304
+ /** Function to create volatile state */
305
+ readonly volatile?: (self: ModelInstanceType<RequiredProps> & V & A) => Vol;
306
+ /** Phantom types for inference */
307
+ readonly _V: V;
308
+ readonly _A: A;
309
+ readonly _Vol: Vol;
310
+ }
311
+ /** Any mixin type */
312
+ type IAnyMixin = IMixin<ModelProperties, object, object, object>;
246
313
  interface IDisposer {
247
314
  (): void;
248
315
  }
@@ -302,6 +369,39 @@ declare function model<P extends ModelProperties>(name: string, properties: P):
302
369
  declare function model<P extends ModelProperties>(properties: P): IModelType<P, object, object, object>;
303
370
  declare function compose<PA extends ModelProperties, PB extends ModelProperties, VA extends object, VB extends object, AA extends object, AB extends object, VolA extends object, VolB extends object>(name: string, a: IModelType<PA, VA, AA, VolA>, b: IModelType<PB, VB, AB, VolB>): IModelType<PA & PB, VA & VB, AA & AB, VolA & VolB>;
304
371
  declare function compose<PA extends ModelProperties, PB extends ModelProperties, VA extends object, VB extends object, AA extends object, AB extends object, VolA extends object, VolB extends object>(a: IModelType<PA, VA, AA, VolA>, b: IModelType<PB, VB, AB, VolB>): IModelType<PA & PB, VA & VB, AA & AB, VolA & VolB>;
372
+ /**
373
+ * Create a type-safe mixin that can be applied to models.
374
+ *
375
+ * Mixins declare:
376
+ * - `requires`: Properties the mixin needs from any model it's applied to
377
+ * - `views`: Computed views to add
378
+ * - `actions`: Actions to add
379
+ * - `volatile`: Volatile (non-serialized) state to add
380
+ *
381
+ * @example
382
+ * ```typescript
383
+ * // Create a mixin that adds validation capabilities
384
+ * const Validatable = types.mixin({
385
+ * requires: {
386
+ * errors: types.array(types.string),
387
+ * },
388
+ * views: (self) => ({
389
+ * get isValid() { return self.errors.length === 0; },
390
+ * get hasErrors() { return self.errors.length > 0; },
391
+ * }),
392
+ * actions: (self) => ({
393
+ * clearErrors() { self.errors.clear(); },
394
+ * addError(msg: string) { self.errors.push(msg); },
395
+ * }),
396
+ * });
397
+ *
398
+ * // Apply to a model
399
+ * const FormModel = types
400
+ * .model({ name: types.string, errors: types.array(types.string) })
401
+ * .apply(Validatable);
402
+ * ```
403
+ */
404
+ declare function mixin<RequiredProps extends ModelProperties = ModelProperties, V extends object = object, A extends object = object, Vol extends object = object>(config: MixinConfig<RequiredProps, V, A, Vol>): IMixin<RequiredProps, V, A, Vol>;
305
405
 
306
406
  /**
307
407
  * Array type implementation
@@ -1025,6 +1125,7 @@ declare const types: {
1025
1125
  custom: typeof custom;
1026
1126
  model: typeof model;
1027
1127
  compose: typeof compose;
1128
+ mixin: typeof mixin;
1028
1129
  array: typeof array;
1029
1130
  map: typeof map;
1030
1131
  optional: typeof optional;
@@ -1106,4 +1207,4 @@ declare function typecheck<T>(type: {
1106
1207
  is(v: unknown): v is T;
1107
1208
  }, value: unknown): void;
1108
1209
 
1109
- export { type CustomTypeOptions$1 as CustomTypeOptions, DatePrimitive as Date, type DynamicReferenceOptions, type IActionRecorder, type IActionRecording, type IAnyComplexType, type IAnyModelType, type IAnyType, type IArrayType, type IDisposer, type IEnumerationType, type IFrozenType, type IHistoryEntry, type IIdentifierNumberType, type IIdentifierType, type IJsonPatch, type ILateType, type ILiteralType, type IMSTArray, type IMSTMap, type IMapType, type IMaybeNullType, type IMaybeType, type IMiddlewareEvent, type IMiddlewareHandler, type IModelType, type IOptionalType, type IReferenceType, type IRefinementType, type IReversibleJsonPatch, type ISafeReferenceType, type ISerializedActionCall, type ISimpleType, type IStateTreeNode, type ITimeTravelManager, type IType, type IUndoManager, type IUndoManagerOptions, type IUnionType, type IValidationContext, type IValidationError, type IValidationResult, type Instance, type ModelInstance, type ModelProperties, type ReferenceOptions, type SnapshotIn, type SnapshotOut, type UnionOptions, addMiddleware, applyAction, applyPatch, applySnapshot, array, boolean, cast, castToReferenceSnapshot, castToSnapshot, cleanupStaleEntries, clearAllRegistries, clearModelRegistry, clone, cloneDeep, cloneFrozen, compose, createActionRecorder, createTimeTravelManager, createUndoManager, createWithDefaults, custom, types as default, destroy, detach, dynamicReference, enumeration, escapeJsonPath, findAll, findFirst, finite, float, flow, freeze, frozen, getDebugInfo, getEnv, getGlobalStore, getIdentifier, getIdentifierAttribute, getMembers, getModelMetadata, getOrCreate, getOrCreatePath, getParent, getParentOfType, getPath, getPathParts, getRegisteredModelNames, getRegistryStats, getRelativePath, getRoot, getSnapshot, getTreeStats, getType, getTypeName, getValidationError, hasIdentifier, hasParent, haveSameRoot, identifier, identifierNumber, integer, isAlive, isAncestor, isArrayType, isFrozen, isFrozenType, isIdentifierType, isInstanceOf, isLateType, isLiteralType, isMapType, isModelRegistered, isModelType, isOptionalType, isPrimitiveType, isProtected, isReferenceType, isRoot, isStateTreeNode, isType, isUnionType, isValidReference, isValidSnapshot, joinJsonPath, late, lateModel, literal, map, maybe, maybeNull, model, nullType, nullable, number, onAction, onLifecycleChange, onModelRegistered, onPatch, onSnapshot, optional, printTree, protect, recordActions, recordPatches, reference, refinement, registerModel, resetGlobalStore, resolveIdentifier, resolveModel, resolveModelAsync, resolvePath, safeCreate, safeDynamicReference, safeReference, setGlobalStore, snapshotProcessor, splitJsonPath, string, tryGetParent, tryResolve, tryResolveModel, typecheck, types, undefinedType, unescapeJsonPath, unfreeze, union, unprotect, unregisterModel, walk };
1210
+ export { type CustomTypeOptions$1 as CustomTypeOptions, DatePrimitive as Date, type DynamicReferenceOptions, type IActionRecorder, type IActionRecording, type IAnyComplexType, type IAnyMixin, type IAnyModelType, type IAnyType, type IArrayType, type IDisposer, type IEnumerationType, type IFrozenType, type IHistoryEntry, type IIdentifierNumberType, type IIdentifierType, type IJsonPatch, type ILateType, type ILiteralType, type IMSTArray, type IMSTMap, type IMapType, type IMaybeNullType, type IMaybeType, type IMiddlewareEvent, type IMiddlewareHandler, type IMixin, type IModelType, type IOptionalType, type IReferenceType, type IRefinementType, type IReversibleJsonPatch, type ISafeReferenceType, type ISerializedActionCall, type ISimpleType, type IStateTreeNode, type ITimeTravelManager, type IType, type IUndoManager, type IUndoManagerOptions, type IUnionType, type IValidationContext, type IValidationError, type IValidationResult, type Instance, type MixinConfig, type ModelInstance, type ModelProperties, type ModelSelf, type ReferenceOptions, type SnapshotIn, type SnapshotOut, type UnionOptions, addMiddleware, applyAction, applyPatch, applySnapshot, array, boolean, cast, castToReferenceSnapshot, castToSnapshot, cleanupStaleEntries, clearAllRegistries, clearModelRegistry, clone, cloneDeep, cloneFrozen, compose, createActionRecorder, createTimeTravelManager, createUndoManager, createWithDefaults, custom, types as default, destroy, detach, dynamicReference, enumeration, escapeJsonPath, findAll, findFirst, finite, float, flow, freeze, frozen, getDebugInfo, getEnv, getGlobalStore, getIdentifier, getIdentifierAttribute, getMembers, getModelMetadata, getOrCreate, getOrCreatePath, getParent, getParentOfType, getPath, getPathParts, getRegisteredModelNames, getRegistryStats, getRelativePath, getRoot, getSnapshot, getTreeStats, getType, getTypeName, getValidationError, hasIdentifier, hasParent, haveSameRoot, identifier, identifierNumber, integer, isAlive, isAncestor, isArrayType, isFrozen, isFrozenType, isIdentifierType, isInstanceOf, isLateType, isLiteralType, isMapType, isModelRegistered, isModelType, isOptionalType, isPrimitiveType, isProtected, isReferenceType, isRoot, isStateTreeNode, isType, isUnionType, isValidReference, isValidSnapshot, joinJsonPath, late, lateModel, literal, map, maybe, maybeNull, mixin, model, nullType, nullable, number, onAction, onLifecycleChange, onModelRegistered, onPatch, onSnapshot, optional, printTree, protect, recordActions, recordPatches, reference, refinement, registerModel, resetGlobalStore, resolveIdentifier, resolveModel, resolveModelAsync, resolvePath, safeCreate, safeDynamicReference, safeReference, setGlobalStore, snapshotProcessor, splitJsonPath, string, tryGetParent, tryResolve, tryResolveModel, typecheck, types, undefinedType, unescapeJsonPath, unfreeze, union, unprotect, unregisterModel, walk };
package/dist/index.js CHANGED
@@ -967,6 +967,7 @@ __export(index_exports, {
967
967
  map: () => map,
968
968
  maybe: () => maybe,
969
969
  maybeNull: () => maybeNull,
970
+ mixin: () => mixin,
970
971
  model: () => model,
971
972
  nullType: () => nullType,
972
973
  nullable: () => nullable,
@@ -1710,6 +1711,48 @@ var ModelType = class _ModelType {
1710
1711
  }
1711
1712
  });
1712
1713
  }
1714
+ /**
1715
+ * Apply a mixin to this model.
1716
+ * The mixin's views, actions, and volatile state are added to the model.
1717
+ */
1718
+ apply(mixinDef) {
1719
+ const newViews = [];
1720
+ const newActions = [];
1721
+ const newVolatiles = [];
1722
+ if (mixinDef.views) {
1723
+ const viewsFn = mixinDef.views;
1724
+ newViews.push(
1725
+ (self) => viewsFn(self)
1726
+ );
1727
+ }
1728
+ if (mixinDef.actions) {
1729
+ const actionsFn = mixinDef.actions;
1730
+ newActions.push(
1731
+ (self) => actionsFn(self)
1732
+ );
1733
+ }
1734
+ if (mixinDef.volatile) {
1735
+ const volatileFn = mixinDef.volatile;
1736
+ newVolatiles.push(
1737
+ (self) => volatileFn(
1738
+ self
1739
+ )
1740
+ );
1741
+ }
1742
+ return new _ModelType({
1743
+ ...this.config,
1744
+ views: [...this.config.views, ...newViews],
1745
+ actions: [...this.config.actions, ...newActions],
1746
+ volatiles: [...this.config.volatiles, ...newVolatiles]
1747
+ });
1748
+ }
1749
+ /**
1750
+ * Get the internal config for use by compose
1751
+ * @internal
1752
+ */
1753
+ getConfig() {
1754
+ return this.config;
1755
+ }
1713
1756
  };
1714
1757
  function model(nameOrProperties, maybeProperties) {
1715
1758
  const name = typeof nameOrProperties === "string" ? nameOrProperties : "AnonymousModel";
@@ -1728,10 +1771,44 @@ function compose(...args) {
1728
1771
  const name = typeof args[0] === "string" ? args[0] : "ComposedModel";
1729
1772
  const types2 = typeof args[0] === "string" ? args.slice(1) : args;
1730
1773
  const mergedProperties = {};
1774
+ const mergedViews = [];
1775
+ const mergedActions = [];
1776
+ const mergedVolatiles = [];
1777
+ const mergedInitializers = [];
1778
+ let mergedHooks = {};
1731
1779
  for (const type of types2) {
1732
1780
  Object.assign(mergedProperties, type.properties);
1781
+ if (type instanceof ModelType) {
1782
+ const config = type.getConfig();
1783
+ mergedViews.push(...config.views);
1784
+ mergedActions.push(...config.actions);
1785
+ mergedVolatiles.push(...config.volatiles);
1786
+ mergedInitializers.push(...config.initializers);
1787
+ mergedHooks = { ...mergedHooks, ...config.hooks };
1788
+ }
1733
1789
  }
1734
- return model(name, mergedProperties);
1790
+ return new ModelType({
1791
+ name,
1792
+ properties: mergedProperties,
1793
+ views: mergedViews,
1794
+ actions: mergedActions,
1795
+ volatiles: mergedVolatiles,
1796
+ initializers: mergedInitializers,
1797
+ hooks: mergedHooks
1798
+ });
1799
+ }
1800
+ function mixin(config) {
1801
+ return {
1802
+ _kind: "mixin",
1803
+ requires: config.requires ?? {},
1804
+ views: config.views,
1805
+ actions: config.actions,
1806
+ volatile: config.volatile,
1807
+ // Phantom types for inference
1808
+ _V: void 0,
1809
+ _A: void 0,
1810
+ _Vol: void 0
1811
+ };
1735
1812
  }
1736
1813
 
1737
1814
  // src/array.ts
@@ -3353,6 +3430,7 @@ var types = {
3353
3430
  // Model
3354
3431
  model,
3355
3432
  compose,
3433
+ mixin,
3356
3434
  // Collections
3357
3435
  array,
3358
3436
  map,
@@ -3535,6 +3613,7 @@ var index_default = types;
3535
3613
  map,
3536
3614
  maybe,
3537
3615
  maybeNull,
3616
+ mixin,
3538
3617
  model,
3539
3618
  nullType,
3540
3619
  nullable,
package/dist/index.mjs CHANGED
@@ -756,6 +756,48 @@ var ModelType = class _ModelType {
756
756
  }
757
757
  });
758
758
  }
759
+ /**
760
+ * Apply a mixin to this model.
761
+ * The mixin's views, actions, and volatile state are added to the model.
762
+ */
763
+ apply(mixinDef) {
764
+ const newViews = [];
765
+ const newActions = [];
766
+ const newVolatiles = [];
767
+ if (mixinDef.views) {
768
+ const viewsFn = mixinDef.views;
769
+ newViews.push(
770
+ (self) => viewsFn(self)
771
+ );
772
+ }
773
+ if (mixinDef.actions) {
774
+ const actionsFn = mixinDef.actions;
775
+ newActions.push(
776
+ (self) => actionsFn(self)
777
+ );
778
+ }
779
+ if (mixinDef.volatile) {
780
+ const volatileFn = mixinDef.volatile;
781
+ newVolatiles.push(
782
+ (self) => volatileFn(
783
+ self
784
+ )
785
+ );
786
+ }
787
+ return new _ModelType({
788
+ ...this.config,
789
+ views: [...this.config.views, ...newViews],
790
+ actions: [...this.config.actions, ...newActions],
791
+ volatiles: [...this.config.volatiles, ...newVolatiles]
792
+ });
793
+ }
794
+ /**
795
+ * Get the internal config for use by compose
796
+ * @internal
797
+ */
798
+ getConfig() {
799
+ return this.config;
800
+ }
759
801
  };
760
802
  function model(nameOrProperties, maybeProperties) {
761
803
  const name = typeof nameOrProperties === "string" ? nameOrProperties : "AnonymousModel";
@@ -774,10 +816,44 @@ function compose(...args) {
774
816
  const name = typeof args[0] === "string" ? args[0] : "ComposedModel";
775
817
  const types2 = typeof args[0] === "string" ? args.slice(1) : args;
776
818
  const mergedProperties = {};
819
+ const mergedViews = [];
820
+ const mergedActions = [];
821
+ const mergedVolatiles = [];
822
+ const mergedInitializers = [];
823
+ let mergedHooks = {};
777
824
  for (const type of types2) {
778
825
  Object.assign(mergedProperties, type.properties);
826
+ if (type instanceof ModelType) {
827
+ const config = type.getConfig();
828
+ mergedViews.push(...config.views);
829
+ mergedActions.push(...config.actions);
830
+ mergedVolatiles.push(...config.volatiles);
831
+ mergedInitializers.push(...config.initializers);
832
+ mergedHooks = { ...mergedHooks, ...config.hooks };
833
+ }
779
834
  }
780
- return model(name, mergedProperties);
835
+ return new ModelType({
836
+ name,
837
+ properties: mergedProperties,
838
+ views: mergedViews,
839
+ actions: mergedActions,
840
+ volatiles: mergedVolatiles,
841
+ initializers: mergedInitializers,
842
+ hooks: mergedHooks
843
+ });
844
+ }
845
+ function mixin(config) {
846
+ return {
847
+ _kind: "mixin",
848
+ requires: config.requires ?? {},
849
+ views: config.views,
850
+ actions: config.actions,
851
+ volatile: config.volatile,
852
+ // Phantom types for inference
853
+ _V: void 0,
854
+ _A: void 0,
855
+ _Vol: void 0
856
+ };
781
857
  }
782
858
 
783
859
  // src/array.ts
@@ -2399,6 +2475,7 @@ var types = {
2399
2475
  // Model
2400
2476
  model,
2401
2477
  compose,
2478
+ mixin,
2402
2479
  // Collections
2403
2480
  array,
2404
2481
  map,
@@ -2581,6 +2658,7 @@ export {
2581
2658
  map,
2582
2659
  maybe,
2583
2660
  maybeNull,
2661
+ mixin,
2584
2662
  model,
2585
2663
  nullType,
2586
2664
  nullable,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jotai-state-tree",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "MobX-State-Tree API compatible library powered by Jotai",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",