mobx-keystone 2.3.0 → 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 (205) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/mobx-keystone.esm.js +3737 -2722
  3. package/dist/mobx-keystone.esm.mjs +3737 -2722
  4. package/dist/mobx-keystone.umd.js +3750 -2721
  5. package/dist/types/action/CallKind.d.ts +29 -0
  6. package/dist/types/action/applyAction.d.ts +8 -2
  7. package/dist/types/action/applyMethodCall.d.ts +1 -1
  8. package/dist/types/action/index.d.ts +13 -12
  9. package/dist/types/action/isModelAction.d.ts +1 -1
  10. package/dist/types/action/middleware.d.ts +1 -1
  11. package/dist/types/action/resolveCallName.d.ts +1 -0
  12. package/dist/types/actionMiddlewares/actionSerialization/actionSerialization.d.ts +17 -3
  13. package/dist/types/actionMiddlewares/actionSerialization/applySerializedAction.d.ts +8 -2
  14. package/dist/types/actionMiddlewares/actionSerialization/arraySerializer.d.ts +2 -2
  15. package/dist/types/actionMiddlewares/actionSerialization/core.d.ts +7 -0
  16. package/dist/types/actionMiddlewares/actionSerialization/dateSerializer.d.ts +1 -1
  17. package/dist/types/actionMiddlewares/actionSerialization/index.d.ts +3 -3
  18. package/dist/types/actionMiddlewares/actionSerialization/mapSerializer.d.ts +2 -2
  19. package/dist/types/actionMiddlewares/actionSerialization/objectPathSerializer.d.ts +2 -2
  20. package/dist/types/actionMiddlewares/actionSerialization/objectSnapshotSerializer.d.ts +1 -1
  21. package/dist/types/actionMiddlewares/actionSerialization/plainObjectSerializer.d.ts +1 -1
  22. package/dist/types/actionMiddlewares/actionSerialization/primitiveSerializer.d.ts +1 -1
  23. package/dist/types/actionMiddlewares/actionSerialization/setSerializer.d.ts +2 -2
  24. package/dist/types/actionMiddlewares/actionTrackingMiddleware.d.ts +3 -3
  25. package/dist/types/actionMiddlewares/index.d.ts +6 -6
  26. package/dist/types/actionMiddlewares/onActionMiddleware.d.ts +3 -3
  27. package/dist/types/actionMiddlewares/readonlyMiddleware.d.ts +1 -1
  28. package/dist/types/actionMiddlewares/transactionMiddleware.d.ts +2 -2
  29. package/dist/types/actionMiddlewares/undoMiddleware.d.ts +8 -8
  30. package/dist/types/computedTree/index.d.ts +1 -1
  31. package/dist/types/context/index.d.ts +1 -1
  32. package/dist/types/dataModel/BaseDataModel.d.ts +3 -3
  33. package/dist/types/dataModel/DataModel.d.ts +3 -3
  34. package/dist/types/dataModel/getDataModelMetadata.d.ts +3 -3
  35. package/dist/types/dataModel/index.d.ts +4 -4
  36. package/dist/types/dataModel/utils.d.ts +1 -1
  37. package/dist/types/deepChange/index.d.ts +1 -1
  38. package/dist/types/deepChange/onDeepChange.d.ts +1 -1
  39. package/dist/types/frozen/Frozen.d.ts +1 -1
  40. package/dist/types/frozen/index.d.ts +1 -1
  41. package/dist/types/globalConfig/index.d.ts +1 -1
  42. package/dist/types/index.d.ts +25 -24
  43. package/dist/types/model/BaseModel.d.ts +5 -5
  44. package/dist/types/model/Model.d.ts +3 -3
  45. package/dist/types/model/getModelMetadata.d.ts +3 -3
  46. package/dist/types/model/index.d.ts +6 -6
  47. package/dist/types/model/mixins.d.ts +4 -4
  48. package/dist/types/model/utils.d.ts +4 -4
  49. package/dist/types/modelShared/BaseModelShared.d.ts +5 -5
  50. package/dist/types/modelShared/index.d.ts +4 -4
  51. package/dist/types/modelShared/modelDecorator.d.ts +3 -3
  52. package/dist/types/modelShared/modelInfo.d.ts +3 -3
  53. package/dist/types/modelShared/newModel.d.ts +3 -3
  54. package/dist/types/modelShared/prop.d.ts +3 -3
  55. package/dist/types/modelShared/sharedInternalModel.d.ts +4 -4
  56. package/dist/types/parent/findParent.d.ts +1 -1
  57. package/dist/types/parent/index.d.ts +9 -9
  58. package/dist/types/parent/path.d.ts +1 -1
  59. package/dist/types/patch/Patch.d.ts +1 -1
  60. package/dist/types/patch/applyPatches.d.ts +1 -1
  61. package/dist/types/patch/emitPatch.d.ts +7 -1
  62. package/dist/types/patch/index.d.ts +5 -5
  63. package/dist/types/patch/jsonPatch.d.ts +2 -2
  64. package/dist/types/patch/patchRecorder.d.ts +2 -2
  65. package/dist/types/redux/index.d.ts +2 -2
  66. package/dist/types/redux/redux.d.ts +3 -3
  67. package/dist/types/ref/Ref.d.ts +3 -3
  68. package/dist/types/ref/core.d.ts +2 -2
  69. package/dist/types/ref/customRef.d.ts +2 -2
  70. package/dist/types/ref/index.d.ts +4 -4
  71. package/dist/types/ref/rootRef.d.ts +2 -2
  72. package/dist/types/rootStore/index.d.ts +1 -1
  73. package/dist/types/serialization/Call.d.ts +55 -0
  74. package/dist/types/serialization/CallError.d.ts +96 -0
  75. package/dist/types/serialization/JsonValue.d.ts +6 -0
  76. package/dist/types/serialization/Serializer.d.ts +84 -0
  77. package/dist/types/serialization/ValueSerializer.d.ts +88 -0
  78. package/dist/types/serialization/applyCall.d.ts +54 -0
  79. package/dist/types/serialization/builtinSerializers.d.ts +49 -0
  80. package/dist/types/serialization/codec.d.ts +1 -0
  81. package/dist/types/serialization/index.d.ts +8 -0
  82. package/dist/types/serialization/references.d.ts +74 -0
  83. package/dist/types/serialization/resolvedCall.d.ts +1 -0
  84. package/dist/types/snapshot/SnapshotOf.d.ts +5 -5
  85. package/dist/types/snapshot/SnapshotProcessingError.d.ts +2 -2
  86. package/dist/types/snapshot/applySnapshot.d.ts +1 -1
  87. package/dist/types/snapshot/cloneTreeValue.d.ts +1 -1
  88. package/dist/types/snapshot/fromSnapshot.d.ts +2 -2
  89. package/dist/types/snapshot/getSnapshot.d.ts +2 -2
  90. package/dist/types/snapshot/index.d.ts +8 -8
  91. package/dist/types/snapshot/onSnapshot.d.ts +1 -1
  92. package/dist/types/standardActions/index.d.ts +3 -3
  93. package/dist/types/standardActions/objectActions.d.ts +1 -1
  94. package/dist/types/transforms/asMap.d.ts +1 -1
  95. package/dist/types/transforms/asSet.d.ts +2 -2
  96. package/dist/types/transforms/bigint.d.ts +1 -1
  97. package/dist/types/transforms/date.d.ts +1 -1
  98. package/dist/types/transforms/index.d.ts +4 -4
  99. package/dist/types/treeUtils/draft.d.ts +1 -1
  100. package/dist/types/treeUtils/index.d.ts +3 -3
  101. package/dist/types/tweaker/index.d.ts +2 -2
  102. package/dist/types/tweaker/tweak.d.ts +1 -1
  103. package/dist/types/types/SnapshotTypeMismatchError.d.ts +2 -2
  104. package/dist/types/types/TypeCheckError.d.ts +2 -2
  105. package/dist/types/types/TypeChecker.d.ts +3 -3
  106. package/dist/types/types/arrayBased/typesArray.d.ts +2 -2
  107. package/dist/types/types/arrayBased/typesTuple.d.ts +2 -2
  108. package/dist/types/types/getTypeInfo.d.ts +2 -2
  109. package/dist/types/types/index.d.ts +7 -7
  110. package/dist/types/types/objectBased/typesArraySet.d.ts +3 -3
  111. package/dist/types/types/objectBased/typesDataModelData.d.ts +4 -4
  112. package/dist/types/types/objectBased/typesModel.d.ts +4 -4
  113. package/dist/types/types/objectBased/typesObject.d.ts +3 -3
  114. package/dist/types/types/objectBased/typesObjectMap.d.ts +3 -3
  115. package/dist/types/types/objectBased/typesRecord.d.ts +2 -2
  116. package/dist/types/types/objectBased/typesRef.d.ts +3 -3
  117. package/dist/types/types/primitiveBased/typesEnum.d.ts +1 -1
  118. package/dist/types/types/primitiveBased/typesPrimitive.d.ts +3 -3
  119. package/dist/types/types/primitiveBased/typesRefinedPrimitive.d.ts +2 -2
  120. package/dist/types/types/schemas.d.ts +5 -5
  121. package/dist/types/types/tProp.d.ts +4 -4
  122. package/dist/types/types/typeCheck.d.ts +2 -2
  123. package/dist/types/types/types.d.ts +39 -39
  124. package/dist/types/types/utility/typeToStoredData.d.ts +2 -2
  125. package/dist/types/types/utility/typesBigInt.d.ts +1 -1
  126. package/dist/types/types/utility/typesCodec.d.ts +6 -6
  127. package/dist/types/types/utility/typesCodecCore.d.ts +4 -4
  128. package/dist/types/types/utility/typesCodecSupport.d.ts +4 -4
  129. package/dist/types/types/utility/typesDate.d.ts +2 -2
  130. package/dist/types/types/utility/typesMap.d.ts +2 -2
  131. package/dist/types/types/utility/typesMaybe.d.ts +1 -1
  132. package/dist/types/types/utility/typesOr.d.ts +2 -2
  133. package/dist/types/types/utility/typesRefinement.d.ts +3 -3
  134. package/dist/types/types/utility/typesSet.d.ts +3 -3
  135. package/dist/types/types/utility/typesSkipCheck.d.ts +2 -2
  136. package/dist/types/types/utility/typesTag.d.ts +2 -2
  137. package/dist/types/types/utility/typesUnchecked.d.ts +2 -2
  138. package/dist/types/utils/ModelPool.d.ts +1 -1
  139. package/dist/types/wrappers/ArraySet.d.ts +1 -1
  140. package/dist/types/wrappers/ObjectMap.d.ts +1 -1
  141. package/dist/types/wrappers/asMap.d.ts +1 -1
  142. package/dist/types/wrappers/asSet.d.ts +1 -1
  143. package/dist/types/wrappers/index.d.ts +4 -4
  144. package/package.json +5 -18
  145. package/src/action/CallKind.ts +29 -0
  146. package/src/action/applyAction.ts +22 -49
  147. package/src/action/applySet.ts +21 -1
  148. package/src/action/index.ts +1 -0
  149. package/src/action/modelAction.ts +1 -1
  150. package/src/action/modelFlow.ts +1 -1
  151. package/src/action/resolveCallName.ts +207 -0
  152. package/src/actionMiddlewares/actionSerialization/actionSerialization.ts +14 -0
  153. package/src/actionMiddlewares/actionSerialization/applySerializedAction.ts +6 -0
  154. package/src/actionMiddlewares/actionSerialization/core.ts +7 -0
  155. package/src/actionMiddlewares/actionSerialization/index.ts +2 -0
  156. package/src/actionMiddlewares/actionSerialization/objectPathSerializer.ts +3 -2
  157. package/src/dataModel/BaseDataModel.ts +5 -5
  158. package/src/index.ts +1 -0
  159. package/src/model/BaseModel.ts +7 -6
  160. package/src/model/mixins.ts +1 -1
  161. package/src/model/newModel.ts +1 -1
  162. package/src/modelShared/modelDecorator.ts +4 -6
  163. package/src/modelShared/modelInfo.ts +2 -1
  164. package/src/modelShared/sharedInternalModel.ts +1 -1
  165. package/src/parent/path.ts +45 -12
  166. package/src/patch/emitPatch.ts +6 -0
  167. package/src/ref/Ref.ts +1 -1
  168. package/src/serialization/Call.ts +58 -0
  169. package/src/serialization/CallError.ts +153 -0
  170. package/src/serialization/JsonValue.ts +12 -0
  171. package/src/serialization/Serializer.ts +321 -0
  172. package/src/serialization/ValueSerializer.ts +98 -0
  173. package/src/serialization/applyCall.ts +190 -0
  174. package/src/serialization/builtinSerializers.ts +261 -0
  175. package/src/serialization/codec.ts +338 -0
  176. package/src/serialization/index.ts +15 -0
  177. package/src/serialization/references.ts +225 -0
  178. package/src/serialization/resolvedCall.ts +169 -0
  179. package/src/snapshot/SnapshotterAndReconcilerPriority.ts +2 -1
  180. package/src/snapshot/fromModelSnapshot.ts +1 -1
  181. package/src/snapshot/fromSnapshot.ts +1 -1
  182. package/src/standardActions/actions.ts +11 -3
  183. package/src/transforms/ImmutableDate.ts +19 -19
  184. package/src/tweaker/tweakArray.ts +1 -1
  185. package/src/tweaker/tweakFrozen.ts +1 -1
  186. package/src/tweaker/tweakModel.ts +1 -1
  187. package/src/tweaker/tweakPlainObject.ts +1 -1
  188. package/src/types/arrayBased/typesArray.ts +1 -1
  189. package/src/types/arrayBased/typesTuple.ts +1 -1
  190. package/src/types/objectBased/typesArraySet.ts +1 -1
  191. package/src/types/objectBased/typesModel.ts +2 -2
  192. package/src/types/objectBased/typesObject.ts +1 -1
  193. package/src/types/objectBased/typesObjectMap.ts +1 -1
  194. package/src/types/objectBased/typesRecord.ts +1 -1
  195. package/src/types/objectBased/typesRef.ts +2 -2
  196. package/src/types/primitiveBased/typesPrimitive.ts +1 -1
  197. package/src/types/schemas.ts +1 -1
  198. package/src/types/utility/typesCodecCore.ts +1 -1
  199. package/src/types/utility/typesCodecSupport.ts +2 -0
  200. package/src/types/utility/typesOr.ts +2 -2
  201. package/src/types/utility/typesRefinement.ts +1 -1
  202. package/src/utils/errorDiagnostics.ts +9 -4
  203. package/src/utils/index.ts +20 -13
  204. package/src/wrappers/ArraySet.ts +3 -5
  205. package/src/wrappers/ObjectMap.ts +3 -5
@@ -0,0 +1,58 @@
1
+ import type { CallKind } from "../action/CallKind"
2
+ import type { JsonValue } from "./JsonValue"
3
+
4
+ /**
5
+ * A call as JSON, as `Serializer.encodeCall` returns it and `Serializer.decodeCall` takes it.
6
+ */
7
+ export interface EncodedCall {
8
+ /**
9
+ * A reference to the node the call runs on; for a standalone action, its first argument.
10
+ */
11
+ target: JsonValue
12
+ /**
13
+ * Name of the action or view.
14
+ */
15
+ name: string
16
+ /**
17
+ * Encoded arguments; left out to read a getter.
18
+ */
19
+ args?: JsonValue[]
20
+ }
21
+
22
+ /**
23
+ * A call to an action or a view of a node.
24
+ */
25
+ export interface Call {
26
+ /**
27
+ * The node the call runs on; for a standalone action, its first argument.
28
+ */
29
+ target: object
30
+ /**
31
+ * Name of the action or view.
32
+ */
33
+ name: string
34
+ /**
35
+ * Arguments (for a standalone action, without the target); left out to read a getter.
36
+ */
37
+ args?: unknown[]
38
+ }
39
+
40
+ /**
41
+ * A call whose target and name are resolved, as `Serializer.decodeCall` returns it and `canCall`
42
+ * gets it.
43
+ */
44
+ export interface ResolvedCall extends Readonly<Call> {
45
+ /**
46
+ * What `name` resolved to on the target.
47
+ */
48
+ readonly kind: CallKind
49
+ /**
50
+ * Whether it is a `@modelFlow` or a `standaloneFlow`, which needs `applyCallAsync`.
51
+ */
52
+ readonly isFlow: boolean
53
+ /**
54
+ * The arguments as JSON, only on a call returned by `decodeCall`. Reading `args` decodes them
55
+ * (once).
56
+ */
57
+ readonly rawArgs?: JsonValue[]
58
+ }
@@ -0,0 +1,153 @@
1
+ import type { Path } from "../parent/pathTypes"
2
+ import { MobxKeystoneError } from "../utils"
3
+ import {
4
+ buildErrorMessageWithDiagnostics,
5
+ getErrorPathSnapshot,
6
+ noErrorValuePreview,
7
+ } from "../utils/errorDiagnostics"
8
+
9
+ /**
10
+ * The reason a call failed.
11
+ */
12
+ export enum CallErrorCode {
13
+ /**
14
+ * The target, or a reference in the arguments, doesn't resolve.
15
+ */
16
+ NotFound = "notFound",
17
+ /**
18
+ * Nothing by that name can be called on the target.
19
+ */
20
+ UnknownName = "unknownName",
21
+ /**
22
+ * The name exists, but can't be used as asked (e.g. a method read without `args`).
23
+ */
24
+ WrongKind = "wrongKind",
25
+ /**
26
+ * Refused by `canCall`.
27
+ */
28
+ Forbidden = "forbidden",
29
+ /**
30
+ * An argument (or any other value being decoded) failed to decode.
31
+ */
32
+ BadArgument = "badArgument",
33
+ /**
34
+ * A value (usually the result) can't be encoded.
35
+ */
36
+ CannotEncode = "cannotEncode",
37
+ }
38
+
39
+ /**
40
+ * What a `CallError` is built from, e.g. by a custom `ValueSerializer` that wants to choose the
41
+ * code (a `CallError` it throws passes through as it is; any other error is wrapped).
42
+ */
43
+ export interface CallErrorData {
44
+ /**
45
+ * The reason.
46
+ */
47
+ code: CallErrorCode
48
+ /**
49
+ * The message, without the path and value preview the error adds to it.
50
+ */
51
+ message: string
52
+ /**
53
+ * Path relative to the call or value, e.g. `["args", 1, "items", 3]`.
54
+ * Defaults to where the value being encoded or decoded is.
55
+ */
56
+ path?: Path
57
+ /**
58
+ * The value the error is about, shown in the message.
59
+ */
60
+ previewValue?: unknown
61
+ /**
62
+ * The error that caused it, if any. Its message is added to `message`, but left out of
63
+ * `toJSON()`, since it may hold the server's data.
64
+ */
65
+ cause?: unknown
66
+ }
67
+
68
+ /**
69
+ * The JSON form of a `CallError`, safe to send back to the caller.
70
+ */
71
+ export interface CallErrorJson {
72
+ code: CallErrorCode
73
+ message: string
74
+ path: Path
75
+ }
76
+
77
+ /**
78
+ * Thrown when a call or a value can't be decoded, resolved, allowed, run or encoded
79
+ * (extends `MobxKeystoneError`). Errors thrown by the action or view being called are not
80
+ * `CallError`s: they are rethrown as they are.
81
+ *
82
+ * Use `instanceof CallError` to distinguish call errors from other
83
+ * `MobxKeystoneError` instances.
84
+ */
85
+ export class CallError extends MobxKeystoneError {
86
+ /**
87
+ * The reason.
88
+ */
89
+ readonly code: CallErrorCode
90
+ /**
91
+ * Where in the call or value the error is, e.g. `["args", 1]`.
92
+ */
93
+ readonly path: Path
94
+ /**
95
+ * The error that caused it, if any.
96
+ */
97
+ readonly cause?: unknown
98
+
99
+ private readonly jsonMessage: string
100
+
101
+ constructor(data: CallErrorData) {
102
+ const path = data.path ?? getErrorPathSnapshot() ?? []
103
+ const previewValue = "previewValue" in data ? data.previewValue : noErrorValuePreview
104
+ const causeMessage =
105
+ data.cause === undefined
106
+ ? undefined
107
+ : data.cause instanceof Error
108
+ ? data.cause.message
109
+ : String(data.cause)
110
+
111
+ super(
112
+ buildErrorMessageWithDiagnostics({
113
+ message: causeMessage === undefined ? data.message : `${data.message}: ${causeMessage}`,
114
+ path,
115
+ previewValue,
116
+ })
117
+ )
118
+
119
+ this.code = data.code
120
+ this.path = path
121
+ this.cause = data.cause
122
+ // a CannotEncode value is the server's, and so may be a cause's message
123
+ this.jsonMessage =
124
+ data.code === CallErrorCode.CannotEncode
125
+ ? data.message
126
+ : causeMessage === undefined
127
+ ? this.message
128
+ : buildErrorMessageWithDiagnostics({ message: data.message, path, previewValue })
129
+ }
130
+
131
+ /**
132
+ * The error as JSON, for RPC and tool layers. It leaves out the cause's message, and the value
133
+ * preview of a `CannotEncode` error, since they may hold the server's data.
134
+ */
135
+ toJSON(): CallErrorJson {
136
+ return {
137
+ code: this.code,
138
+ message: this.jsonMessage,
139
+ path: this.path,
140
+ }
141
+ }
142
+ }
143
+
144
+ /**
145
+ * @internal
146
+ */
147
+ export function badPayloadError(tag: string, json: unknown): CallError {
148
+ return new CallError({
149
+ code: CallErrorCode.BadArgument,
150
+ message: `invalid '${tag}' value`,
151
+ previewValue: json,
152
+ })
153
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * A JSON value: what `JSON.parse` can return.
3
+ */
4
+ export type JsonValue =
5
+ | null
6
+ | boolean
7
+ | number
8
+ | string
9
+ | JsonValue[]
10
+ | {
11
+ [key: string]: JsonValue
12
+ }
@@ -0,0 +1,321 @@
1
+ import type { ActionCall } from "../action/applyAction"
2
+ import { resolvePathCheckingIds } from "../parent/path"
3
+ import { getStandaloneAction } from "../standardActions/actions"
4
+ import { failure, hasOwnProp, isArray, isObject, isPlainObject } from "../utils"
5
+ import { runWithNewErrorDiagnosticsContext, withErrorPathSegment } from "../utils/errorDiagnostics"
6
+ import { builtinSerializers } from "./builtinSerializers"
7
+ import type { Call, EncodedCall, ResolvedCall } from "./Call"
8
+ import { CallError, CallErrorCode } from "./CallError"
9
+ import {
10
+ createDecodeContext,
11
+ createEncodeContext,
12
+ escapedObjectTag,
13
+ groupSerializersByTypeof,
14
+ isUnmarkedSerializer,
15
+ markValue,
16
+ type SerializerConfig,
17
+ wrapSerializerError,
18
+ } from "./codec"
19
+ import type { JsonValue } from "./JsonValue"
20
+ import { decodePathRef, encodePathRef, pathRefs, pathRefTag } from "./references"
21
+ import {
22
+ assertIsCallShape,
23
+ assertIsTargetInTree,
24
+ assertIsTreeRoot,
25
+ createResolvedCall,
26
+ resolveCallNameOrThrow,
27
+ } from "./resolvedCall"
28
+ import type { DecodeContext, EncodeContext, ValueSerializer } from "./ValueSerializer"
29
+ import { serializationMarkerKey } from "./ValueSerializer"
30
+
31
+ /**
32
+ * Encodes and decodes values and calls against a tree, with a list of value serializers.
33
+ * Created with `createSerializer`.
34
+ *
35
+ * Every operation takes the root of the tree as its first argument; it must be a tree root.
36
+ */
37
+ export interface Serializer {
38
+ /**
39
+ * The list of value serializers it was created with, without the built-ins.
40
+ */
41
+ readonly serializers: readonly ValueSerializer[]
42
+
43
+ /**
44
+ * Encodes a value as JSON. Nodes of the tree go as references when a reference serializer in
45
+ * the list claims them.
46
+ *
47
+ * @param root Root of the tree.
48
+ * @param value Value to encode.
49
+ * @returns The encoded value.
50
+ */
51
+ encodeValue(root: object, value: unknown): JsonValue
52
+
53
+ /**
54
+ * Decodes a value encoded with `encodeValue`. References are resolved against the tree;
55
+ * snapshots become new instances.
56
+ *
57
+ * @param root Root of the tree.
58
+ * @param json Value to decode.
59
+ * @returns The decoded value.
60
+ */
61
+ decodeValue(root: object, json: JsonValue): unknown
62
+
63
+ /**
64
+ * Encodes a call (an `ActionCall`, e.g. from `onActionMiddleware`'s `onStart`, or a `Call`)
65
+ * as JSON. The target always goes as a reference; for a standalone action, it isn't repeated
66
+ * in the arguments.
67
+ *
68
+ * Encode an `ActionCall` before the action runs (e.g. in `onStart`), so its arguments are
69
+ * encoded against the tree the receiver has.
70
+ *
71
+ * @param root Root of the tree.
72
+ * @param call The call.
73
+ * @returns The encoded call.
74
+ */
75
+ encodeCall(root: object, call: ActionCall | Call): EncodedCall
76
+
77
+ /**
78
+ * Decodes a call encoded with `encodeCall`: the target is resolved and the name resolved on
79
+ * it, nothing is run. The arguments are decoded when `args` is first read.
80
+ *
81
+ * @param root Root of the tree.
82
+ * @param json The encoded call.
83
+ * @returns The resolved call, to pass to `applyCall` or `applyCallAsync`.
84
+ */
85
+ decodeCall(root: object, json: EncodedCall | JsonValue): ResolvedCall
86
+ }
87
+
88
+ /**
89
+ * Options of `createSerializer`.
90
+ */
91
+ export interface SerializerOptions {
92
+ /**
93
+ * Value serializers, tried in order before the built-ins when encoding.
94
+ */
95
+ serializers?: readonly ValueSerializer[]
96
+ /**
97
+ * Whether to append the built-in serializers (`builtinSerializers`) to the list.
98
+ * Defaults to `true`.
99
+ */
100
+ builtins?: boolean
101
+ }
102
+
103
+ function assertIsValueSerializer(serializer: unknown, index: number): void {
104
+ if (isObject(serializer) && typeof serializer.encodeValue === "function") {
105
+ throw failure(
106
+ `serializers[${index}] is a serializer; to extend one, spread its '.serializers' into the list`
107
+ )
108
+ }
109
+ if (
110
+ !isObject(serializer) ||
111
+ typeof serializer.tag !== "string" ||
112
+ serializer.tag === "" ||
113
+ typeof serializer.is !== "function" ||
114
+ typeof serializer.encode !== "function" ||
115
+ typeof serializer.decode !== "function" ||
116
+ (serializer.claimsTarget !== undefined && typeof serializer.claimsTarget !== "function")
117
+ ) {
118
+ throw failure(
119
+ `serializers[${index}] must be a value serializer ({ tag, is, encode, decode, claimsTarget? }) with a non-empty tag`
120
+ )
121
+ }
122
+ }
123
+
124
+ function isActionCall(call: ActionCall | Call): call is ActionCall {
125
+ return hasOwnProp(call, "actionName")
126
+ }
127
+
128
+ /**
129
+ * Creates a serializer from a list of value serializers. When encoding, a value goes through the
130
+ * first serializer whose `is` accepts it, then the built-ins (unless `builtins` is `false`),
131
+ * then as plain JSON; decoding picks the serializer by tag.
132
+ *
133
+ * @param options Options.
134
+ * @returns The serializer.
135
+ */
136
+ export function createSerializer(options: SerializerOptions = {}): Serializer {
137
+ const custom = options.serializers ? [...options.serializers] : []
138
+ custom.forEach(assertIsValueSerializer)
139
+
140
+ const serializers: readonly ValueSerializer[] =
141
+ options.builtins === false
142
+ ? custom
143
+ : [
144
+ ...custom,
145
+ ...Object.values<ValueSerializer>(builtinSerializers).filter((s) => !custom.includes(s)),
146
+ ]
147
+
148
+ const byTag = new Map<string, ValueSerializer>()
149
+ for (const serializer of serializers) {
150
+ if (serializer.tag === escapedObjectTag) {
151
+ throw failure(`the tag '${escapedObjectTag}' is reserved`)
152
+ }
153
+ if (byTag.has(serializer.tag)) {
154
+ throw failure(`there is more than one serializer with tag '${serializer.tag}'`)
155
+ }
156
+ byTag.set(serializer.tag, serializer)
157
+ }
158
+
159
+ const config: SerializerConfig = {
160
+ serializers,
161
+ byTypeof: groupSerializersByTypeof(serializers),
162
+ byTag,
163
+ unmarked: serializers.find(isUnmarkedSerializer),
164
+ }
165
+
166
+ const encodeTarget = (target: unknown, ctx: EncodeContext): JsonValue => {
167
+ assertIsTargetInTree(target, ctx.root)
168
+ // only reference serializers are asked, through `claimsTarget`, which ignores `when` since
169
+ // the call must run on the node itself
170
+ for (const serializer of serializers) {
171
+ let payload: JsonValue | undefined
172
+ try {
173
+ if (!serializer.claimsTarget?.(target, ctx)) {
174
+ continue
175
+ }
176
+ payload = serializer.encode(target, ctx)
177
+ } catch (e) {
178
+ throw wrapSerializerError(e, CallErrorCode.CannotEncode, serializer.tag, target)
179
+ }
180
+ return markValue(serializer.tag, payload)
181
+ }
182
+ return markValue(pathRefTag, encodePathRef(target))
183
+ }
184
+
185
+ const decodeTarget = (json: unknown, ctx: DecodeContext): object => {
186
+ const tag = isPlainObject(json) ? json[serializationMarkerKey] : undefined
187
+ const serializer = typeof tag === "string" ? byTag.get(tag) : undefined
188
+
189
+ let target: unknown
190
+ if (tag === pathRefTag && !serializer) {
191
+ // the fallback every serializer decodes in a target position
192
+ target = decodePathRef((json as Record<string, unknown>).v, ctx.root)
193
+ } else if (serializer?.claimsTarget) {
194
+ target = ctx.decode(json as JsonValue)
195
+ } else {
196
+ throw new CallError({
197
+ code: CallErrorCode.BadArgument,
198
+ message: "the call target must be a reference",
199
+ previewValue: json,
200
+ })
201
+ }
202
+
203
+ assertIsTargetInTree(target, ctx.root)
204
+ return target
205
+ }
206
+
207
+ return {
208
+ serializers: Object.freeze(custom),
209
+
210
+ encodeValue(root, value) {
211
+ assertIsTreeRoot(root)
212
+ return runWithNewErrorDiagnosticsContext(() =>
213
+ createEncodeContext(config, root).encode(value)
214
+ )
215
+ },
216
+
217
+ decodeValue(root, json) {
218
+ assertIsTreeRoot(root)
219
+ return runWithNewErrorDiagnosticsContext(() => createDecodeContext(config, root).decode(json))
220
+ },
221
+
222
+ encodeCall(root, call) {
223
+ assertIsTreeRoot(root)
224
+ if (!isObject(call)) {
225
+ throw failure("call must be an object")
226
+ }
227
+
228
+ return runWithNewErrorDiagnosticsContext(() => {
229
+ let target: unknown
230
+ let name: string
231
+ let args: readonly unknown[] | undefined
232
+
233
+ if (isActionCall(call)) {
234
+ if (call.serialized) {
235
+ throw failure("cannot encode a serialized action call; deserialize it first")
236
+ }
237
+ const { value, resolved } = resolvePathCheckingIds(
238
+ root,
239
+ call.targetPath,
240
+ call.targetPathIds
241
+ )
242
+ if (!resolved) {
243
+ throw new CallError({
244
+ code: CallErrorCode.NotFound,
245
+ message: `object at path ${JSON.stringify(call.targetPath)} with ids ${JSON.stringify(
246
+ call.targetPathIds
247
+ )} could not be resolved`,
248
+ path: ["target"],
249
+ })
250
+ }
251
+ target = value
252
+ name = call.actionName
253
+ // the target of a standalone action is its first argument, sent once as the target
254
+ args = getStandaloneAction(name) ? call.args.slice(1) : call.args
255
+ } else {
256
+ assertIsCallShape(call)
257
+ target = call.target
258
+ name = call.name
259
+ args = call.args
260
+ }
261
+
262
+ const ctx = createEncodeContext(config, root)
263
+ const encoded: EncodedCall = {
264
+ target: withErrorPathSegment("target", () => encodeTarget(target, ctx)),
265
+ name,
266
+ }
267
+ if (args !== undefined) {
268
+ encoded.args = withErrorPathSegment("args", () =>
269
+ Array.from(args, (arg, i) => ctx.encode(arg, i))
270
+ )
271
+ }
272
+ return encoded
273
+ })
274
+ },
275
+
276
+ decodeCall(root, json) {
277
+ assertIsTreeRoot(root)
278
+
279
+ return runWithNewErrorDiagnosticsContext(() => {
280
+ if (
281
+ !isPlainObject(json) ||
282
+ !hasOwnProp(json, "target") ||
283
+ typeof json.name !== "string" ||
284
+ (json.args !== undefined && !isArray(json.args))
285
+ ) {
286
+ throw new CallError({
287
+ code: CallErrorCode.BadArgument,
288
+ message: "invalid call: expected { target, name, args? }",
289
+ previewValue: json,
290
+ })
291
+ }
292
+
293
+ const name = json.name
294
+ const rawArgs = json.args as JsonValue[] | undefined
295
+ const ctx = createDecodeContext(config, root)
296
+
297
+ const target = withErrorPathSegment("target", () => decodeTarget(json.target, ctx))
298
+ const resolution = resolveCallNameOrThrow(target, name)
299
+
300
+ return createResolvedCall(
301
+ target,
302
+ name,
303
+ resolution,
304
+ rawArgs && {
305
+ rawArgs,
306
+ decode: () =>
307
+ runWithNewErrorDiagnosticsContext(() =>
308
+ withErrorPathSegment("args", () => rawArgs.map((arg, i) => ctx.decode(arg, i)))
309
+ ),
310
+ }
311
+ )
312
+ })
313
+ },
314
+ }
315
+ }
316
+
317
+ /**
318
+ * The default serializer: nodes of the tree as path references (`pathRefs()`, value-type models
319
+ * excepted), plus the built-ins.
320
+ */
321
+ export const defaultSerializer: Serializer = createSerializer({ serializers: [pathRefs()] })
@@ -0,0 +1,98 @@
1
+ import type { PathElement } from "../parent/pathTypes"
2
+ import type { JsonValue } from "./JsonValue"
3
+
4
+ /**
5
+ * The key that marks a special value in the encoded JSON:
6
+ * `{ "$mobxKeystone": <tag>, "v": <payload> }`.
7
+ */
8
+ export const serializationMarkerKey = "$mobxKeystone"
9
+
10
+ /**
11
+ * Context passed to `ValueSerializer.is` and `ValueSerializer.encode`.
12
+ */
13
+ export interface EncodeContext {
14
+ /**
15
+ * Root of the tree the value is encoded against.
16
+ */
17
+ readonly root: object
18
+
19
+ /**
20
+ * Encodes a nested value with the same serializer.
21
+ *
22
+ * @param value Value to encode.
23
+ * @param key Where the value is inside its parent, for error paths.
24
+ * @returns The encoded value.
25
+ */
26
+ encode(value: unknown, key?: PathElement): JsonValue
27
+ }
28
+
29
+ /**
30
+ * Context passed to `ValueSerializer.decode`.
31
+ */
32
+ export interface DecodeContext {
33
+ /**
34
+ * Root of the tree the value is decoded against.
35
+ */
36
+ readonly root: object
37
+
38
+ /**
39
+ * Decodes a nested value with the same serializer.
40
+ *
41
+ * @param json Value to decode.
42
+ * @param key Where the value is inside its parent, for error paths.
43
+ * @returns The decoded value.
44
+ */
45
+ decode(json: JsonValue, key?: PathElement): unknown
46
+ }
47
+
48
+ /**
49
+ * Handles one kind of value: encodes it as `{ "$mobxKeystone": tag, "v": payload }` and decodes
50
+ * it back. `createSerializer` composes a list of them into a `Serializer`.
51
+ *
52
+ * @template T Type of the values it handles.
53
+ * @template J Type of the encoded payload (`undefined` to carry none).
54
+ */
55
+ export interface ValueSerializer<T = any, J extends JsonValue | undefined = any> {
56
+ /**
57
+ * Unique, e.g. `"geom/Polygon"`; the `$mobxKeystone` of the encoded value.
58
+ */
59
+ readonly tag: string
60
+
61
+ /**
62
+ * Whether this serializer encodes `value`.
63
+ *
64
+ * @param value Value to check.
65
+ * @param ctx Encoding context; `ctx.root` is the tree the value is encoded against.
66
+ */
67
+ is(value: unknown, ctx: EncodeContext): value is T
68
+
69
+ /**
70
+ * Encodes a value `is` accepted. Use `ctx.encode(child)` for nested values.
71
+ *
72
+ * @param value Value to encode.
73
+ * @param ctx Encoding context.
74
+ * @returns The payload (`v`), or `undefined` to carry none.
75
+ */
76
+ encode(value: T, ctx: EncodeContext): J
77
+
78
+ /**
79
+ * Decodes a payload back into a value. Use `ctx.decode(child)` for nested values.
80
+ *
81
+ * @param json The payload (`v`).
82
+ * @param ctx Decoding context.
83
+ * @returns The decoded value.
84
+ */
85
+ decode(json: J, ctx: DecodeContext): T
86
+
87
+ /**
88
+ * Only for reference serializers: whether this serializer references `value`, a node of the
89
+ * tree, when it is a call's target. Unlike `is`, it ignores options such as `when`, since the
90
+ * call must run on the node itself. A call's target goes through the first serializer whose
91
+ * `claimsTarget` accepts it (else as a `pathRef`), and only serializers with `claimsTarget`
92
+ * decode it.
93
+ *
94
+ * @param value The call's target.
95
+ * @param ctx Encoding context.
96
+ */
97
+ claimsTarget?(value: object, ctx: EncodeContext): boolean
98
+ }