UNPKG

mobx-state-tree

Version:

Opinionated, transactional, MobX powered state container

468 lines (467 loc) 17.8 kB
import { IAnyStateTreeNode, IType, IAnyModelType, IStateTreeNode, IJsonPatch, IDisposer, IAnyType, ReferenceIdentifier, TypeOfValue, IActionContext, IAnyComplexType } from "../internal"; /** @hidden */ export type TypeOrStateTreeNodeToStateTreeNode<T extends IAnyType | IAnyStateTreeNode> = T extends IType<any, any, infer TT> ? TT & IStateTreeNode<T> : T; /** * Returns the _actual_ type of the given tree node. (Or throws) * * @param object * @returns */ export declare function getType(object: IAnyStateTreeNode): IAnyComplexType; /** * Returns the _declared_ type of the given sub property of an object, array or map. * In the case of arrays and maps the property name is optional and will be ignored. * * Example: * ```ts * const Box = types.model({ x: 0, y: 0 }) * const box = Box.create() * * console.log(getChildType(box, "x").name) // 'number' * ``` * * @param object * @param propertyName * @returns */ export declare function getChildType(object: IAnyStateTreeNode, propertyName?: string): IAnyType; /** * Registers a function that will be invoked for each mutation that is applied to the provided model instance, or to any of its children. * See [patches](https://github.com/mobxjs/mobx-state-tree#patches) for more details. onPatch events are emitted immediately and will not await the end of a transaction. * Patches can be used to deeply observe a model tree. * * @param target the model instance from which to receive patches * @param callback the callback that is invoked for each patch. The reversePatch is a patch that would actually undo the emitted patch * @returns function to remove the listener */ export declare function onPatch(target: IAnyStateTreeNode, callback: (patch: IJsonPatch, reversePatch: IJsonPatch) => void): IDisposer; /** * Registers a function that is invoked whenever a new snapshot for the given model instance is available. * The listener will only be fire at the end of the current MobX (trans)action. * See [snapshots](https://github.com/mobxjs/mobx-state-tree#snapshots) for more details. * * @param target * @param callback * @returns */ export declare function onSnapshot<S>(target: IStateTreeNode<IType<any, S, any>>, callback: (snapshot: S) => void): IDisposer; /** * Applies a JSON-patch to the given model instance or bails out if the patch couldn't be applied * See [patches](https://github.com/mobxjs/mobx-state-tree#patches) for more details. * * Can apply a single past, or an array of patches. * * @param target * @param patch * @returns */ export declare function applyPatch(target: IAnyStateTreeNode, patch: IJsonPatch | ReadonlyArray<IJsonPatch>): void; export interface IPatchRecorder { patches: ReadonlyArray<IJsonPatch>; inversePatches: ReadonlyArray<IJsonPatch>; reversedInversePatches: ReadonlyArray<IJsonPatch>; readonly recording: boolean; stop(): void; resume(): void; replay(target?: IAnyStateTreeNode): void; undo(target?: IAnyStateTreeNode): void; } /** * Small abstraction around `onPatch` and `applyPatch`, attaches a patch listener to a tree and records all the patches. * Returns a recorder object with the following signature: * * Example: * ```ts * export interface IPatchRecorder { * // the recorded patches * patches: IJsonPatch[] * // the inverse of the recorded patches * inversePatches: IJsonPatch[] * // true if currently recording * recording: boolean * // stop recording patches * stop(): void * // resume recording patches * resume(): void * // apply all the recorded patches on the given target (the original subject if omitted) * replay(target?: IAnyStateTreeNode): void * // reverse apply the recorded patches on the given target (the original subject if omitted) * // stops the recorder if not already stopped * undo(): void * } * ``` * * The optional filter function allows to skip recording certain patches. * * @param subject * @param filter * @returns */ export declare function recordPatches(subject: IAnyStateTreeNode, filter?: (patch: IJsonPatch, inversePatch: IJsonPatch, actionContext: IActionContext | undefined) => boolean): IPatchRecorder; /** * The inverse of `unprotect`. * * @param target */ export declare function protect(target: IAnyStateTreeNode): void; /** * By default it is not allowed to directly modify a model. Models can only be modified through actions. * However, in some cases you don't care about the advantages (like replayability, traceability, etc) this yields. * For example because you are building a PoC or don't have any middleware attached to your tree. * * In that case you can disable this protection by calling `unprotect` on the root of your tree. * * Example: * ```ts * const Todo = types.model({ * done: false * }).actions(self => ({ * toggle() { * self.done = !self.done * } * })) * * const todo = Todo.create() * todo.done = true // throws! * todo.toggle() // OK * unprotect(todo) * todo.done = false // OK * ``` */ export declare function unprotect(target: IAnyStateTreeNode): void; /** * Returns true if the object is in protected mode, @see protect */ export declare function isProtected(target: IAnyStateTreeNode): boolean; /** * Applies a snapshot to a given model instances. Patch and snapshot listeners will be invoked as usual. * * @param target * @param snapshot * @returns */ export declare function applySnapshot<C>(target: IStateTreeNode<IType<C, any, any>>, snapshot: C): void; /** * Calculates a snapshot from the given model instance. The snapshot will always reflect the latest state but use * structural sharing where possible. Doesn't require MobX transactions to be completed. * * @param target * @param applyPostProcess If true (the default) then postProcessSnapshot gets applied. * @returns */ export declare function getSnapshot<S>(target: IStateTreeNode<IType<any, S, any>>, applyPostProcess?: boolean): S; /** * Given a model instance, returns `true` if the object has a parent, that is, is part of another object, map or array. * * @param target * @param depth How far should we look upward? 1 by default. * @returns */ export declare function hasParent(target: IAnyStateTreeNode, depth?: number): boolean; /** * Returns the immediate parent of this object, or throws. * * Note that the immediate parent can be either an object, map or array, and * doesn't necessarily refer to the parent model. * * Please note that in child nodes access to the root is only possible * once the `afterAttach` hook has fired. * * @param target * @param depth How far should we look upward? 1 by default. * @returns */ export declare function getParent<IT extends IAnyStateTreeNode | IAnyComplexType>(target: IAnyStateTreeNode, depth?: number): TypeOrStateTreeNodeToStateTreeNode<IT>; /** * Given a model instance, returns `true` if the object has a parent of given type, that is, is part of another object, map or array * * @param target * @param type * @returns */ export declare function hasParentOfType(target: IAnyStateTreeNode, type: IAnyComplexType): boolean; /** * Returns the target's parent of a given type, or throws. * * @param target * @param type * @returns */ export declare function getParentOfType<IT extends IAnyComplexType>(target: IAnyStateTreeNode, type: IT): IT["Type"]; /** * Given an object in a model tree, returns the root object of that tree. * * Please note that in child nodes access to the root is only possible * once the `afterAttach` hook has fired. * * @param target * @returns */ export declare function getRoot<IT extends IAnyComplexType | IAnyStateTreeNode>(target: IAnyStateTreeNode): TypeOrStateTreeNodeToStateTreeNode<IT>; /** * Returns the path of the given object in the model tree * * @param target * @returns */ export declare function getPath(target: IAnyStateTreeNode): string; /** * Returns the path of the given object as unescaped string array. * * @param target * @returns */ export declare function getPathParts(target: IAnyStateTreeNode): string[]; /** * Returns true if the given object is the root of a model tree. * * @param target * @returns */ export declare function isRoot(target: IAnyStateTreeNode): boolean; /** * Resolves a path relatively to a given object. * Returns undefined if no value can be found. * * @param target * @param path escaped json path * @returns */ export declare function resolvePath(target: IAnyStateTreeNode, path: string): any; /** * Resolves a model instance given a root target, the type and the identifier you are searching for. * Returns undefined if no value can be found. * * @param type * @param target * @param identifier * @returns */ export declare function resolveIdentifier<IT extends IAnyModelType>(type: IT, target: IAnyStateTreeNode, identifier: ReferenceIdentifier): IT["Type"] | undefined; /** * Returns the identifier of the target node. * This is the *string normalized* identifier, which might not match the type of the identifier attribute * * @param target * @returns */ export declare function getIdentifier(target: IAnyStateTreeNode): string | null; /** * Tests if a reference is valid (pointing to an existing node and optionally if alive) and returns such reference if the check passes, * else it returns undefined. * * @param getter Function to access the reference. * @param checkIfAlive true to also make sure the referenced node is alive (default), false to skip this check. * @returns */ export declare function tryReference<N extends IAnyStateTreeNode>(getter: () => N | null | undefined, checkIfAlive?: boolean): N | undefined; /** * Tests if a reference is valid (pointing to an existing node and optionally if alive) and returns if the check passes or not. * * @param getter Function to access the reference. * @param checkIfAlive true to also make sure the referenced node is alive (default), false to skip this check. * @returns */ export declare function isValidReference<N extends IAnyStateTreeNode>(getter: () => N | null | undefined, checkIfAlive?: boolean): boolean; /** * Try to resolve a given path relative to a given node. * * @param target * @param path * @returns */ export declare function tryResolve(target: IAnyStateTreeNode, path: string): any; /** * Given two state tree nodes that are part of the same tree, * returns the shortest jsonpath needed to navigate from the one to the other * * @param base * @param target * @returns */ export declare function getRelativePath(base: IAnyStateTreeNode, target: IAnyStateTreeNode): string; /** * Returns a deep copy of the given state tree node as new tree. * Shorthand for `snapshot(x) = getType(x).create(getSnapshot(x))` * * _Tip: clone will create a literal copy, including the same identifiers. To modify identifiers etc. during cloning, don't use clone but take a snapshot of the tree, modify it, and create new instance_ * * @param source * @param keepEnvironment indicates whether the clone should inherit the same environment (`true`, the default), or not have an environment (`false`). If an object is passed in as second argument, that will act as the environment for the cloned tree. * @returns */ export declare function clone<T extends IAnyStateTreeNode>(source: T, keepEnvironment?: boolean | any): T; /** * Removes a model element from the state tree, and let it live on as a new state tree */ export declare function detach<T extends IAnyStateTreeNode>(target: T): T; /** * Removes a model element from the state tree, and mark it as end-of-life; the element should not be used anymore */ export declare function destroy(target: IAnyStateTreeNode): void; /** * Returns true if the given state tree node is not killed yet. * This means that the node is still a part of a tree, and that `destroy` * has not been called. If a node is not alive anymore, the only thing one can do with it * is requesting it's last path and snapshot * * @param target * @returns */ export declare function isAlive(target: IAnyStateTreeNode): boolean; /** * Use this utility to register a function that should be called whenever the * targeted state tree node is destroyed. This is a useful alternative to managing * cleanup methods yourself using the `beforeDestroy` hook. * * This methods returns the same disposer that was passed as argument. * * Example: * ```ts * const Todo = types.model({ * title: types.string * }).actions(self => ({ * afterCreate() { * const autoSaveDisposer = reaction( * () => getSnapshot(self), * snapshot => sendSnapshotToServerSomehow(snapshot) * ) * // stop sending updates to server if this * // instance is destroyed * addDisposer(self, autoSaveDisposer) * } * })) * ``` * * @param target * @param disposer * @returns The same disposer that was passed as argument */ export declare function addDisposer(target: IAnyStateTreeNode, disposer: IDisposer): IDisposer; /** * Returns the environment of the current state tree, or throws. For more info on environments, * see [Dependency injection](https://github.com/mobxjs/mobx-state-tree#dependency-injection) * * Please note that in child nodes access to the root is only possible * once the `afterAttach` hook has fired * * Returns an empty environment if the tree wasn't initialized with an environment * * @param target * @returns */ export declare function getEnv<T = any>(target: IAnyStateTreeNode): T; /** * Returns whether the current state tree has environment or not. * * @export * @param {IStateTreeNode} target * @return {boolean} */ export declare function hasEnv(target: IAnyStateTreeNode): boolean; /** * Performs a depth first walk through a tree. */ export declare function walk(target: IAnyStateTreeNode, processor: (item: IAnyStateTreeNode) => void): void; export interface IModelReflectionPropertiesData { name: string; properties: { [K: string]: IAnyType; }; } /** * Returns a reflection of the model type properties and name for either a model type or model node. * * @param typeOrNode * @returns */ export declare function getPropertyMembers(typeOrNode: IAnyModelType | IAnyStateTreeNode): IModelReflectionPropertiesData; export interface IModelReflectionData extends IModelReflectionPropertiesData { actions: string[]; views: string[]; volatile: string[]; flowActions: string[]; } /** * Returns a reflection of the model node, including name, properties, views, volatile state, * and actions. `flowActions` is also provided as a separate array of names for any action that * came from a flow generator as well. * * In the case where a model has two actions: `doSomething` and `doSomethingWithFlow`, where * `doSomethingWithFlow` is a flow generator, the `actions` array will contain both actions, * i.e. ["doSomething", "doSomethingWithFlow"], and the `flowActions` array will contain only * the flow action, i.e. ["doSomethingWithFlow"]. * * @param target * @returns */ export declare function getMembers(target: IAnyStateTreeNode): IModelReflectionData; export declare function cast<O extends string | number | boolean | null | undefined = never>(snapshotOrInstance: O): O; export declare function cast<O = never>(snapshotOrInstance: TypeOfValue<O>["CreationType"] | TypeOfValue<O>["SnapshotType"] | TypeOfValue<O>["Type"]): O; /** * Casts a node instance type to a snapshot type so it can be assigned to a type snapshot (e.g. to be used inside a create call). * Note that this is just a cast for the type system, this is, it won't actually convert an instance to a snapshot, * but just fool typescript into thinking so. * * Example: * ```ts * const ModelA = types.model({ * n: types.number * }).actions(self => ({ * setN(aNumber: number) { * self.n = aNumber * } * })) * * const ModelB = types.model({ * innerModel: ModelA * }) * * const a = ModelA.create({ n: 5 }); * // this will allow the compiler to use a model as if it were a snapshot * const b = ModelB.create({ innerModel: castToSnapshot(a)}) * ``` * * @param snapshotOrInstance Snapshot or instance * @returns The same object cast as an input (creation) snapshot */ export declare function castToSnapshot<I>(snapshotOrInstance: I): Extract<I, IAnyStateTreeNode> extends never ? I : TypeOfValue<I>["CreationType"]; /** * Casts a node instance type to a reference snapshot type so it can be assigned to a reference snapshot (e.g. to be used inside a create call). * Note that this is just a cast for the type system, this is, it won't actually convert an instance to a reference snapshot, * but just fool typescript into thinking so. * * Example: * ```ts * const ModelA = types.model({ * id: types.identifier, * n: types.number * }).actions(self => ({ * setN(aNumber: number) { * self.n = aNumber * } * })) * * const ModelB = types.model({ * refA: types.reference(ModelA) * }) * * const a = ModelA.create({ id: 'someId', n: 5 }); * // this will allow the compiler to use a model as if it were a reference snapshot * const b = ModelB.create({ refA: castToReferenceSnapshot(a)}) * ``` * * @param instance Instance * @returns The same object cast as a reference snapshot (string or number) */ export declare function castToReferenceSnapshot<I>(instance: I): Extract<I, IAnyStateTreeNode> extends never ? I : ReferenceIdentifier; /** * Returns the unique node id (not to be confused with the instance identifier) for a * given instance. * This id is a number that is unique for each instance. * * @export * @param target * @returns */ export declare function getNodeId(target: IAnyStateTreeNode): number;