mobx-keystone 2.3.1 → 2.4.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/mobx-keystone.esm.js +5157 -3981
  3. package/dist/mobx-keystone.esm.mjs +5157 -3981
  4. package/dist/mobx-keystone.umd.js +5170 -3980
  5. package/dist/types/action/CallKind.d.ts +29 -0
  6. package/dist/types/action/applyAction.d.ts +7 -1
  7. package/dist/types/action/index.d.ts +1 -0
  8. package/dist/types/action/resolveCallName.d.ts +1 -0
  9. package/dist/types/actionMiddlewares/actionSerialization/actionSerialization.d.ts +14 -0
  10. package/dist/types/actionMiddlewares/actionSerialization/applySerializedAction.d.ts +6 -0
  11. package/dist/types/actionMiddlewares/actionSerialization/core.d.ts +7 -0
  12. package/dist/types/index.d.ts +1 -0
  13. package/dist/types/patch/emitPatch.d.ts +6 -0
  14. package/dist/types/serialization/Call.d.ts +55 -0
  15. package/dist/types/serialization/CallError.d.ts +96 -0
  16. package/dist/types/serialization/JsonValue.d.ts +6 -0
  17. package/dist/types/serialization/Serializer.d.ts +84 -0
  18. package/dist/types/serialization/ValueSerializer.d.ts +88 -0
  19. package/dist/types/serialization/applyCall.d.ts +54 -0
  20. package/dist/types/serialization/builtinSerializers.d.ts +49 -0
  21. package/dist/types/serialization/codec.d.ts +1 -0
  22. package/dist/types/serialization/index.d.ts +8 -0
  23. package/dist/types/serialization/references.d.ts +74 -0
  24. package/dist/types/serialization/resolvedCall.d.ts +1 -0
  25. package/package.json +1 -1
  26. package/src/action/CallKind.ts +29 -0
  27. package/src/action/applyAction.ts +22 -49
  28. package/src/action/applySet.ts +21 -1
  29. package/src/action/index.ts +1 -0
  30. package/src/action/resolveCallName.ts +207 -0
  31. package/src/actionMiddlewares/actionSerialization/actionSerialization.ts +14 -0
  32. package/src/actionMiddlewares/actionSerialization/applySerializedAction.ts +6 -0
  33. package/src/actionMiddlewares/actionSerialization/core.ts +7 -0
  34. package/src/actionMiddlewares/actionSerialization/index.ts +2 -0
  35. package/src/actionMiddlewares/actionSerialization/objectPathSerializer.ts +3 -2
  36. package/src/index.ts +1 -0
  37. package/src/parent/path.ts +45 -12
  38. package/src/patch/emitPatch.ts +6 -0
  39. package/src/serialization/Call.ts +58 -0
  40. package/src/serialization/CallError.ts +153 -0
  41. package/src/serialization/JsonValue.ts +12 -0
  42. package/src/serialization/Serializer.ts +321 -0
  43. package/src/serialization/ValueSerializer.ts +98 -0
  44. package/src/serialization/applyCall.ts +190 -0
  45. package/src/serialization/builtinSerializers.ts +261 -0
  46. package/src/serialization/codec.ts +338 -0
  47. package/src/serialization/index.ts +15 -0
  48. package/src/serialization/references.ts +225 -0
  49. package/src/serialization/resolvedCall.ts +169 -0
  50. package/src/standardActions/actions.ts +11 -3
  51. package/src/utils/errorDiagnostics.ts +9 -4
@@ -0,0 +1,74 @@
1
+ import type { AnyModel } from "../model/BaseModel";
2
+ import type { PathElement } from "../parent/pathTypes";
3
+ import type { ValueSerializer } from "./ValueSerializer";
4
+ /**
5
+ * What a reference serializer's `when` option is told about a node.
6
+ */
7
+ export interface ReferenceNodeInfo {
8
+ /**
9
+ * Whether the node is a model.
10
+ */
11
+ readonly isModel: boolean;
12
+ /**
13
+ * Whether the node is a value-type model (`Model({...}, { valueType: true })`).
14
+ */
15
+ readonly isValueType: boolean;
16
+ }
17
+ /**
18
+ * Options of the built-in reference serializers.
19
+ */
20
+ export interface ReferenceSerializerOptions {
21
+ /**
22
+ * Decides, for every node the serializer could reference, whether it does. A node it declines
23
+ * falls through the list like any other value: a model ends up as its snapshot, a tree array
24
+ * or plain object is walked. The call's target ignores it: it is always a reference.
25
+ *
26
+ * Defaults to declining value-type models: `(node, { isValueType }) => !isValueType`.
27
+ */
28
+ when?(node: object, info: ReferenceNodeInfo): boolean;
29
+ }
30
+ /**
31
+ * A path reference, the payload of `{ "$mobxKeystone": "pathRef", "v": ... }`.
32
+ */
33
+ export type PathRefJson = {
34
+ /**
35
+ * Path from the root to the node, through data nodes (as in patches).
36
+ */
37
+ path: PathElement[];
38
+ /**
39
+ * Id of the model reached by each step of the path (`null` for non-models).
40
+ * Optional when decoding: without it, ids aren't checked.
41
+ */
42
+ ids?: (string | null)[];
43
+ };
44
+ /**
45
+ * An id reference, the payload of `{ "$mobxKeystone": "idRef", "v": ... }`.
46
+ */
47
+ export type IdRefJson = {
48
+ /**
49
+ * The model type (`$modelType`).
50
+ */
51
+ type: string;
52
+ /**
53
+ * The model id.
54
+ */
55
+ id: string;
56
+ };
57
+ /**
58
+ * References nodes of the tree by their path from the root, checking the ids of the models along
59
+ * it: `{ "$mobxKeystone": "pathRef", "v": { "path": ["cards", 2], "ids": [null, "c1"] } }`.
60
+ * It reaches any node, including tree arrays and plain objects.
61
+ *
62
+ * @param options Options.
63
+ * @returns The reference serializer.
64
+ */
65
+ export declare function pathRefs(options?: ReferenceSerializerOptions): ValueSerializer<object, PathRefJson>;
66
+ /**
67
+ * References models of the tree that have an id by their type and id:
68
+ * `{ "$mobxKeystone": "idRef", "v": { "type": "app/Card", "id": "c1" } }`. Ids are read and
69
+ * resolved as `getModelRefId` and `resolveId` do, and the type must match.
70
+ *
71
+ * @param options Options.
72
+ * @returns The reference serializer.
73
+ */
74
+ export declare function idRefs(options?: ReferenceSerializerOptions): ValueSerializer<AnyModel, IdRefJson>;
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobx-keystone",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "description": "A MobX powered state management solution based on data trees with first class support for TypeScript, snapshots, patches and much more",
5
5
  "keywords": [
6
6
  "mobx",
@@ -0,0 +1,29 @@
1
+ /**
2
+ * What a call's name resolved to on its target.
3
+ */
4
+ export enum CallKind {
5
+ /**
6
+ * An action declared on a class: a model's `@modelAction` / `@modelFlow` (including
7
+ * `withSetter()` setters), or a data model's action.
8
+ */
9
+ ModelAction = "modelAction",
10
+ /**
11
+ * An action the app defines with `standaloneAction` / `standaloneFlow`, under a global name.
12
+ */
13
+ StandaloneAction = "standaloneAction",
14
+ /**
15
+ * A standalone action the library ships for editing any array or plain object
16
+ * (`arrayActions`, `objectActions`).
17
+ */
18
+ StandardAction = "standardAction",
19
+ /**
20
+ * A built-in action, as library functions record when they change a node: `$$applySnapshot`,
21
+ * `$$applyPatches`, `$$detach`, `$$applySet`, `$$applyDelete`, `$$applyMethodCall` (see
22
+ * `BuiltInAction`).
23
+ */
24
+ BuiltInAction = "builtInAction",
25
+ /**
26
+ * Not an action: a method, getter or function field of a model's or data model's own classes.
27
+ */
28
+ View = "view",
29
+ }
@@ -1,20 +1,10 @@
1
- import { getDataModelAction } from "../dataModel/actions"
2
- import { detach } from "../parent/detach"
3
1
  import { resolvePathCheckingIds } from "../parent/path"
4
2
  import type { Path } from "../parent/pathTypes"
5
- import { applyPatches } from "../patch/applyPatches"
6
- import { applySnapshot } from "../snapshot/applySnapshot"
7
3
  import { getStandaloneAction } from "../standardActions/actions"
8
4
  import { assertTweakedObject } from "../tweaker/core"
9
5
  import { failure } from "../utils"
10
- import type { AnyFunction } from "../utils/AnyFunction"
11
- import { applyDelete } from "./applyDelete"
12
- import { applyMethodCall } from "./applyMethodCall"
13
- import { applySet } from "./applySet"
14
- import { BuiltInAction, isBuiltInAction } from "./builtInActions"
15
- import { isHookAction } from "./hookActions"
16
- import { isModelAction } from "./isModelAction"
17
- import { isModelFlow } from "./modelFlow"
6
+ import { CallKind } from "./CallKind"
7
+ import { resolveCallName } from "./resolveCallName"
18
8
 
19
9
  /**
20
10
  * An action call.
@@ -42,23 +32,20 @@ export interface ActionCall {
42
32
 
43
33
  /**
44
34
  * Marks this action call as non-serialized.
35
+ *
36
+ * @deprecated Not needed with `createSerializer`: a `Call` is never serialized, an
37
+ * `EncodedCall` always is.
45
38
  */
46
39
  readonly serialized?: boolean
47
40
  }
48
41
 
49
- const builtInActionToFunction = {
50
- [BuiltInAction.ApplySnapshot]: applySnapshot,
51
- [BuiltInAction.ApplyPatches]: applyPatches,
52
- [BuiltInAction.Detach]: detach,
53
- [BuiltInAction.ApplySet]: applySet,
54
- [BuiltInAction.ApplyDelete]: applyDelete,
55
- [BuiltInAction.ApplyMethodCall]: applyMethodCall,
56
- }
57
-
58
42
  /**
59
43
  * Applies (runs) an action over a target object.
60
44
  *
61
- * If you intend to apply serialized actions check one of the `applySerializedAction` methods instead.
45
+ * It runs any kind of action (built-in, standalone, model actions and flows), but not views.
46
+ * To apply calls that come from outside (e.g. as JSON), decode them with a serializer
47
+ * (`defaultSerializer.decodeCall`) and use `applyCall` / `applyCallAsync` instead, which check
48
+ * what may be called.
62
49
  *
63
50
  * @param subtreeRoot Subtree root target object to run the action over.
64
51
  * @param call The action, usually as coming from `onActionMiddleware`.
@@ -88,38 +75,24 @@ export function applyAction<TRet = any>(subtreeRoot: object, call: ActionCall):
88
75
  }
89
76
  assertTweakedObject(current, "resolved target", true)
90
77
 
91
- if (isBuiltInAction(call.actionName)) {
92
- const fnToCall = builtInActionToFunction[call.actionName] as AnyFunction | undefined
93
- if (!fnToCall) {
94
- throw failure(`assertion failed: unknown built-in action - ${call.actionName}`)
95
- }
96
-
97
- return fnToCall.apply(current, [current, ...call.args])
98
- }
99
-
100
- if (isHookAction(call.actionName)) {
78
+ const resolution = resolveCallName(current, call.actionName)
79
+ if (resolution === "hook") {
101
80
  throw failure(`calls to hooks (${call.actionName}) cannot be applied`)
102
81
  }
103
-
104
- const dataModelAction = getDataModelAction(call.actionName)
105
- if (dataModelAction) {
106
- const instance: any = new dataModelAction.modelClass(current)
107
- return instance[dataModelAction.fnName](...call.args)
82
+ if (resolution === "unknown") {
83
+ throw failure(`action '${call.actionName}' could not be resolved to a function`)
108
84
  }
109
85
 
110
- const standaloneAction = getStandaloneAction(call.actionName)
111
- if (standaloneAction) {
112
- return standaloneAction.apply(current, call.args as any)
113
- }
86
+ switch (resolution.kind) {
87
+ case CallKind.View:
88
+ throw failure(`action '${call.actionName}' is not a model action`)
114
89
 
115
- const actionFn = (current as any)[call.actionName]
116
- if (typeof actionFn !== "function") {
117
- throw failure(`action '${call.actionName}' could not be resolved to a function`)
118
- }
90
+ case CallKind.StandaloneAction:
91
+ case CallKind.StandardAction:
92
+ // the target is already the first argument
93
+ return getStandaloneAction(call.actionName)!.fn(...call.args)
119
94
 
120
- if (!isModelAction(actionFn) && !isModelFlow(actionFn)) {
121
- throw failure(`action '${call.actionName}' is not a model action`)
95
+ default:
96
+ return resolution.run(call.args) as TRet
122
97
  }
123
-
124
- return actionFn.apply(current, call.args)
125
98
  }
@@ -1,7 +1,7 @@
1
1
  import { isObservable } from "mobx"
2
2
  import { isModel } from "../model/utils"
3
3
  import { assertTweakedObject } from "../tweaker/core"
4
- import { lazy } from "../utils"
4
+ import { failure, hasOwnProp, lazy } from "../utils"
5
5
  import { setIfDifferent } from "../utils/setIfDifferent"
6
6
  import { BuiltInAction } from "./builtInActions"
7
7
  import { ActionContextActionType } from "./context"
@@ -30,10 +30,30 @@ function internalApplySet<O extends object>(this: O, fieldName: string | number,
30
30
  if (!isModel(this) && isObservable(this)) {
31
31
  setIfDifferent(this, fieldName, value)
32
32
  } else {
33
+ // assigning '__proto__' would replace the prototype of the model instead of setting a field,
34
+ // and assigning an inherited method (e.g. an action) would shadow it on this instance
35
+ if (fieldName === "__proto__" || isInheritedDataProp(this, fieldName)) {
36
+ throw failure(`applySet cannot set '${String(fieldName)}' on a model`)
37
+ }
33
38
  ;(this as any)[fieldName] = value
34
39
  }
35
40
  }
36
41
 
42
+ function isInheritedDataProp(obj: object, fieldName: string | number): boolean {
43
+ if (hasOwnProp(obj, fieldName)) {
44
+ return false
45
+ }
46
+ let proto = Object.getPrototypeOf(obj)
47
+ while (proto) {
48
+ const descriptor = Object.getOwnPropertyDescriptor(proto, fieldName)
49
+ if (descriptor) {
50
+ return !descriptor.get && !descriptor.set
51
+ }
52
+ proto = Object.getPrototypeOf(proto)
53
+ }
54
+ return false
55
+ }
56
+
37
57
  const wrappedInternalApplySet = lazy(() =>
38
58
  wrapInAction({
39
59
  nameOrNameFn: BuiltInAction.ApplySet,
@@ -3,6 +3,7 @@ export * from "./applyDelete"
3
3
  export * from "./applyMethodCall"
4
4
  export * from "./applySet"
5
5
  export * from "./builtInActions"
6
+ export * from "./CallKind"
6
7
  export * from "./context"
7
8
  export * from "./hookActions"
8
9
  export * from "./isModelAction"
@@ -0,0 +1,207 @@
1
+ import { getDataModelAction } from "../dataModel/actions"
2
+ import { BaseDataModel } from "../dataModel/BaseDataModel"
3
+ import { isDataModelClass } from "../dataModel/utils"
4
+ import { BaseModel } from "../model/BaseModel"
5
+ import { isModel } from "../model/utils"
6
+ import type { ModelClass } from "../modelShared/BaseModelShared"
7
+ import { getModelInfoForName } from "../modelShared/modelInfo"
8
+ import { detach } from "../parent/detach"
9
+ import { applyPatches } from "../patch/applyPatches"
10
+ import { applySnapshot } from "../snapshot/applySnapshot"
11
+ import { getStandaloneAction } from "../standardActions/actions"
12
+ import { failure, namespace } from "../utils"
13
+ import type { AnyFunction } from "../utils/AnyFunction"
14
+ import { applyDelete } from "./applyDelete"
15
+ import { applyMethodCall } from "./applyMethodCall"
16
+ import { applySet } from "./applySet"
17
+ import { BuiltInAction, isBuiltInAction } from "./builtInActions"
18
+ import { CallKind } from "./CallKind"
19
+ import { isHookAction } from "./hookActions"
20
+ import { isModelAction } from "./isModelAction"
21
+ import { isModelFlow } from "./modelFlow"
22
+
23
+ const builtInActionToFunction = {
24
+ [BuiltInAction.ApplySnapshot]: applySnapshot,
25
+ [BuiltInAction.ApplyPatches]: applyPatches,
26
+ [BuiltInAction.Detach]: detach,
27
+ [BuiltInAction.ApplySet]: applySet,
28
+ [BuiltInAction.ApplyDelete]: applyDelete,
29
+ [BuiltInAction.ApplyMethodCall]: applyMethodCall,
30
+ }
31
+
32
+ const standardActionPrefixes = [`${namespace}/arrayActions::`, `${namespace}/objectActions::`]
33
+
34
+ const dataModelMemberPrefix = "fn::"
35
+
36
+ // members that are never callable, even when defined below the base classes
37
+ const nonCallableMemberNames: ReadonlySet<string> = new Set([
38
+ "constructor",
39
+ "onInit",
40
+ "onLazyInit",
41
+ "onAttachedToRootStore",
42
+ "onLazyAttachedToRootStore",
43
+ ])
44
+
45
+ /**
46
+ * @internal
47
+ *
48
+ * How a view is used: a getter is read (no `args`), a method is called (with `args`).
49
+ */
50
+ export type ViewForm = "getter" | "method"
51
+
52
+ /**
53
+ * @internal
54
+ *
55
+ * What a call's name resolved to on its target.
56
+ */
57
+ export interface CallResolution {
58
+ readonly kind: CallKind
59
+ readonly isFlow: boolean
60
+ /**
61
+ * Only for views.
62
+ */
63
+ readonly viewForm: ViewForm | undefined
64
+ /**
65
+ * Runs the call on the target it was resolved for (args are ignored when reading a getter).
66
+ */
67
+ run(args: readonly unknown[]): unknown
68
+ }
69
+
70
+ /**
71
+ * @internal
72
+ *
73
+ * Why a name couldn't be resolved: a hook (never callable) or nothing callable by that name.
74
+ */
75
+ export type CallResolutionFailure = "hook" | "unknown"
76
+
77
+ type FoundMember = { readonly get: AnyFunction } | { readonly fn: AnyFunction }
78
+
79
+ /**
80
+ * Finds a callable member without running any getter: a method, getter or function field of the
81
+ * object or of its prototype chain below `stopProto`.
82
+ */
83
+ function findMember(obj: object, name: string, stopProto: object): FoundMember | undefined {
84
+ if (nonCallableMemberNames.has(name)) {
85
+ return undefined
86
+ }
87
+
88
+ // own accessors too, e.g. computeds MobX defines on the instance with legacy decorators
89
+ for (
90
+ let current: object | null = obj;
91
+ current && current !== stopProto;
92
+ current = Object.getPrototypeOf(current)
93
+ ) {
94
+ const desc = Object.getOwnPropertyDescriptor(current, name)
95
+ if (desc) {
96
+ if (desc.get) {
97
+ return { get: desc.get }
98
+ }
99
+ return typeof desc.value === "function" ? { fn: desc.value } : undefined
100
+ }
101
+ }
102
+ return undefined
103
+ }
104
+
105
+ function resolveMember(member: FoundMember, self: object): CallResolution {
106
+ if ("get" in member) {
107
+ const get = member.get
108
+ return {
109
+ kind: CallKind.View,
110
+ isFlow: false,
111
+ viewForm: "getter",
112
+ run: () => get.call(self),
113
+ }
114
+ }
115
+
116
+ const fn = member.fn
117
+ const run = (args: readonly unknown[]) => fn.apply(self, args as unknown[])
118
+ if (isModelAction(fn) || isModelFlow(fn)) {
119
+ return { kind: CallKind.ModelAction, isFlow: isModelFlow(fn), viewForm: undefined, run }
120
+ }
121
+ return { kind: CallKind.View, isFlow: false, viewForm: "method", run }
122
+ }
123
+
124
+ function getDataModelClassAndMember(
125
+ name: string
126
+ ): { modelClass: ModelClass<any>; memberName: string } | undefined {
127
+ // data model actions are registered under their name
128
+ const registered = getDataModelAction(name)
129
+ if (registered) {
130
+ return { modelClass: registered.modelClass, memberName: registered.fnName }
131
+ }
132
+
133
+ // other members are named the same way: fn::<data model name>::<member>
134
+ if (!name.startsWith(dataModelMemberPrefix)) {
135
+ return undefined
136
+ }
137
+ const separatorIndex = name.lastIndexOf("::")
138
+ if (separatorIndex < dataModelMemberPrefix.length) {
139
+ return undefined
140
+ }
141
+ const modelInfo = getModelInfoForName(name.slice(dataModelMemberPrefix.length, separatorIndex))
142
+ if (!modelInfo || !isDataModelClass(modelInfo.class)) {
143
+ return undefined
144
+ }
145
+ return { modelClass: modelInfo.class, memberName: name.slice(separatorIndex + 2) }
146
+ }
147
+
148
+ /**
149
+ * @internal
150
+ *
151
+ * Resolves what a call's name refers to on its target, in this order: built-in actions, hooks
152
+ * (refused), data model members, standalone actions, the target's own model actions and flows,
153
+ * and finally the target's own methods, getters and function fields (views).
154
+ *
155
+ * Getters are never run while resolving.
156
+ */
157
+ export function resolveCallName(
158
+ target: object,
159
+ name: string
160
+ ): CallResolution | CallResolutionFailure {
161
+ if (isBuiltInAction(name)) {
162
+ const fn = builtInActionToFunction[name] as AnyFunction | undefined
163
+ if (!fn) {
164
+ throw failure(`assertion failed: unknown built-in action - ${name}`)
165
+ }
166
+ return {
167
+ kind: CallKind.BuiltInAction,
168
+ isFlow: false,
169
+ viewForm: undefined,
170
+ run: (args) => fn.apply(target, [target, ...args]),
171
+ }
172
+ }
173
+
174
+ if (isHookAction(name)) {
175
+ return "hook"
176
+ }
177
+
178
+ const dataModelMember = getDataModelClassAndMember(name)
179
+ if (dataModelMember) {
180
+ // the instance is created (or reused, since they are cached) first, so decorators that
181
+ // initialize on first use have run; it throws when the target isn't valid data for the class
182
+ const instance: object = new dataModelMember.modelClass(target)
183
+ const member = findMember(instance, dataModelMember.memberName, BaseDataModel.prototype)
184
+ return member ? resolveMember(member, instance) : "unknown"
185
+ }
186
+
187
+ const standaloneAction = getStandaloneAction(name)
188
+ if (standaloneAction) {
189
+ return {
190
+ kind: standardActionPrefixes.some((prefix) => name.startsWith(prefix))
191
+ ? CallKind.StandardAction
192
+ : CallKind.StandaloneAction,
193
+ isFlow: standaloneAction.isFlow,
194
+ viewForm: undefined,
195
+ run: (args) => standaloneAction.fn(target, ...args),
196
+ }
197
+ }
198
+
199
+ if (isModel(target)) {
200
+ const member = findMember(target, name, BaseModel.prototype)
201
+ if (member) {
202
+ return resolveMember(member, target)
203
+ }
204
+ }
205
+
206
+ return "unknown"
207
+ }
@@ -24,6 +24,8 @@ const serializersMap = new Map<string, ActionCallArgumentSerializer<any, any>>()
24
24
  *
25
25
  * @param serializer Serializer to register.
26
26
  * @returns A disposer to unregister the serializer.
27
+ *
28
+ * @deprecated Use a `ValueSerializer` in the list passed to `createSerializer` instead.
27
29
  */
28
30
  export function registerActionCallArgumentSerializer(
29
31
  serializer: ActionCallArgumentSerializer<any, any>
@@ -54,6 +56,8 @@ export function registerActionCallArgumentSerializer(
54
56
 
55
57
  /**
56
58
  * Serialized action call argument.
59
+ *
60
+ * @deprecated Replaced by the marked value `{ "$mobxKeystone": tag, "v": payload }` of `createSerializer`.
57
61
  */
58
62
  export interface SerializedActionCallArgument {
59
63
  /**
@@ -68,6 +72,8 @@ export interface SerializedActionCallArgument {
68
72
 
69
73
  /**
70
74
  * A serialized action call.
75
+ *
76
+ * @deprecated Use `EncodedCall` instead.
71
77
  */
72
78
  export interface SerializedActionCall extends Omit<ActionCall, "serialized"> {
73
79
  /**
@@ -99,6 +105,8 @@ export interface SerializedActionCall extends Omit<ActionCall, "serialized"> {
99
105
  * @param argValue Argument value to be transformed into its serializable form.
100
106
  * @param [targetRoot] Target root node of the model where this action is being performed.
101
107
  * @returns The serializable form of the passed value.
108
+ *
109
+ * @deprecated Use `serializer.encodeValue(root, value)` instead (e.g. with `defaultSerializer`).
102
110
  */
103
111
  export function serializeActionCallArgument(
104
112
  argValue: any,
@@ -137,6 +145,8 @@ export function serializeActionCallArgument(
137
145
  * @param actionCall Action call to convert.
138
146
  * @param [targetRoot] Target root node of the model where this action is being performed.
139
147
  * @returns The serializable action call.
148
+ *
149
+ * @deprecated Use `serializer.encodeCall(root, call)` instead (e.g. with `defaultSerializer`).
140
150
  */
141
151
  export function serializeActionCall(
142
152
  actionCall: ActionCall,
@@ -165,6 +175,8 @@ export function serializeActionCall(
165
175
  * @param argValue Argument value to be transformed into its deserialized form.
166
176
  * @param [targetRoot] Target root node of the model where this action is being performed.
167
177
  * @returns The deserialized form of the passed value.
178
+ *
179
+ * @deprecated Use `serializer.decodeValue(root, json)` instead (e.g. with `defaultSerializer`).
168
180
  */
169
181
  export function deserializeActionCallArgument(
170
182
  argValue: SerializedActionCallArgument | JSONPrimitiveValue,
@@ -200,6 +212,8 @@ export function deserializeActionCallArgument(
200
212
  * @param actionCall Action call to convert.
201
213
  * @param [targetRoot] Target root node of the model where this action is being performed.
202
214
  * @returns The deserialized action call.
215
+ *
216
+ * @deprecated Use `serializer.decodeCall(root, json)` instead (e.g. with `defaultSerializer`), then `applyCall`.
203
217
  */
204
218
  export function deserializeActionCall(
205
219
  actionCall: SerializedActionCall,
@@ -17,6 +17,8 @@ import { deserializeActionCall, type SerializedActionCall } from "./actionSerial
17
17
  * Serialized action call with model ID overrides.
18
18
  * Can be generated with `applySerializedActionAndTrackNewModelIds`.
19
19
  * To be applied with `applySerializedActionAndSyncNewModelIds`.
20
+ *
21
+ * @deprecated Use `EncodedCall` instead; model id overrides aren't needed when replicas apply the patches the server broadcasts.
20
22
  */
21
23
  export interface SerializedActionCallWithModelIdOverrides extends SerializedActionCall {
22
24
  /**
@@ -38,6 +40,8 @@ export interface SerializedActionCallWithModelIdOverrides extends SerializedActi
38
40
  * @param call The serialized action, usually as coming from the server/client.
39
41
  * @returns The return value of the action, if any, plus a new serialized action
40
42
  * with model overrides.
43
+ *
44
+ * @deprecated Use `applyCall` / `applyCallAsync` on the server instead, broadcasting the patches the call produces.
41
45
  */
42
46
  export function applySerializedActionAndTrackNewModelIds<TRet = any>(
43
47
  subtreeRoot: object,
@@ -156,6 +160,8 @@ function deepScanValueForModelIdChanges(
156
160
  * @param subtreeRoot Subtree root target object to run the action over.
157
161
  * @param call The serialized action, usually as coming from the server/client.
158
162
  * @returns The return value of the action, if any.
163
+ *
164
+ * @deprecated Use `applyPatches` on the patches the server broadcasts instead.
159
165
  */
160
166
  export function applySerializedActionAndSyncNewModelIds<TRet = any>(
161
167
  subtreeRoot: object,
@@ -1,7 +1,14 @@
1
+ /**
2
+ * Returned by an `ActionCallArgumentSerializer` that can't serialize a value.
3
+ *
4
+ * @deprecated Use `ValueSerializer.is` returning false instead.
5
+ */
1
6
  export const cannotSerialize = Symbol("cannotSerialize")
2
7
 
3
8
  /**
4
9
  * Serializer of action call arguments.
10
+ *
11
+ * @deprecated Use `ValueSerializer` instead.
5
12
  */
6
13
  export interface ActionCallArgumentSerializer<TOriginal, TSerialized> {
7
14
  /**
@@ -1,3 +1,5 @@
1
+ // Deprecated: replaced by `src/serialization`; kept unchanged until the next major.
2
+
1
3
  export * from "./actionSerialization"
2
4
  export * from "./applySerializedAction"
3
5
  export * from "./core"
@@ -1,6 +1,6 @@
1
1
  import { fastGetRootPath, resolvePathCheckingIds } from "../../parent/path"
2
2
  import type { Path } from "../../parent/pathTypes"
3
- import { isTweakedObject } from "../../tweaker/core"
3
+ import { isTreeNode, isTweakedObject } from "../../tweaker/core"
4
4
  import { failure, namespace } from "../../utils"
5
5
  import { rootPathToTargetPathIds } from "../utils"
6
6
  import { type ActionCallArgumentSerializer, cannotSerialize } from "./core"
@@ -36,7 +36,8 @@ export const objectPathSerializer: ActionCallArgumentSerializer<object, ObjectPa
36
36
  // try to resolve the node back
37
37
  if (targetRoot) {
38
38
  const result = resolvePathCheckingIds(targetRoot, ref.targetPath, ref.targetPathIds)
39
- if (result.resolved) {
39
+ // only tree nodes can be serialized as paths, so anything else is not a valid target
40
+ if (result.resolved && isTreeNode(result.value)) {
40
41
  return result.value
41
42
  }
42
43
  }
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ export * from "./patch"
13
13
  export * from "./redux"
14
14
  export * from "./ref"
15
15
  export * from "./rootStore"
16
+ export * from "./serialization"
16
17
  export * from "./snapshot"
17
18
  export * from "./standardActions"
18
19
  export * from "./transforms"