j-templates 8.1.2 → 9.0.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 (40) hide show
  1. package/SYNTAX_PRIMER.md +3 -4
  2. package/Store/Diff/diffTree.d.ts +39 -33
  3. package/Store/Diff/diffTree.js +147 -435
  4. package/Store/Diff/diffTree.types.d.ts +5 -79
  5. package/Store/Diff/diffTree.types.js +1 -3
  6. package/Store/Diff/diffTreeWorker.d.ts +4 -0
  7. package/Store/Diff/diffTreeWorker.js +17 -39
  8. package/Store/Diff/diffTreeWriter.d.ts +27 -38
  9. package/Store/Diff/diffTreeWriter.js +44 -95
  10. package/Store/Diff/index.d.ts +2 -0
  11. package/Store/Diff/index.js +2 -0
  12. package/Store/Diff/utils/diffTree.types.d.ts +113 -0
  13. package/Store/Diff/utils/diffTree.types.js +1 -0
  14. package/Store/Diff/utils/homes.d.ts +46 -0
  15. package/Store/Diff/utils/homes.js +213 -0
  16. package/Store/Diff/utils/paths.d.ts +42 -0
  17. package/Store/Diff/utils/paths.js +87 -0
  18. package/Store/Diff/utils/projections.d.ts +55 -0
  19. package/Store/Diff/utils/projections.js +290 -0
  20. package/Store/Diff/utils/routes.d.ts +27 -0
  21. package/Store/Diff/utils/routes.js +168 -0
  22. package/Store/Diff/utils/state.d.ts +56 -0
  23. package/Store/Diff/utils/state.js +155 -0
  24. package/Store/Store/pushStore.d.ts +1 -5
  25. package/Store/Store/pushStore.js +3 -22
  26. package/Store/Store/store.d.ts +10 -4
  27. package/Store/Store/store.js +25 -11
  28. package/Store/Store/storeAsync.d.ts +4 -5
  29. package/Store/Store/storeAsync.js +19 -36
  30. package/Store/Store/storeSync.d.ts +14 -9
  31. package/Store/Store/storeSync.js +27 -22
  32. package/Store/Tree/observableNode.d.ts +2 -23
  33. package/Store/Tree/observableNode.js +46 -183
  34. package/Store/Tree/snapshot.d.ts +68 -0
  35. package/Store/Tree/snapshot.js +147 -0
  36. package/Utils/json.d.ts +180 -10
  37. package/Utils/json.js +532 -67
  38. package/package.json +1 -1
  39. package/Store/Diff/diffSync.d.ts +0 -36
  40. package/Store/Diff/diffSync.js +0 -43
package/Utils/json.d.ts CHANGED
@@ -1,13 +1,75 @@
1
+ export type JsonWrite = {
2
+ type: "write";
3
+ path: (string | number)[];
4
+ value: unknown;
5
+ };
6
+ /** Removes the property the path ends with from the object holding it */
7
+ export type JsonDelete = {
8
+ type: "delete";
9
+ path: (string | number)[];
10
+ };
11
+ /** How an inserted splice item gets its value */
12
+ export declare enum SPLICE_ACTION {
13
+ /** The element at `from` in the array before the splice is applied */
14
+ MOVE = 1,
15
+ /** A new value */
16
+ INSERT = 2
17
+ }
18
+ export type JsonSpliceItem = {
19
+ type: SPLICE_ACTION.MOVE;
20
+ from: number;
21
+ } | {
22
+ type: SPLICE_ACTION.INSERT;
23
+ value: unknown;
24
+ };
25
+ /**
26
+ * One `Array.prototype.splice` call. Splices are applied in order, so `index` is a position in the
27
+ * array as the splices before it left it. A moved item's `from` is an index in the array before any
28
+ * splice was applied.
29
+ */
30
+ export type JsonSplice = {
31
+ index: number;
32
+ delete: number;
33
+ insert: JsonSpliceItem[];
34
+ };
35
+ /** The splices that turn the array at `path` into its new value */
36
+ export type JsonSpliceRecord = {
37
+ type: "splice";
38
+ path: (string | number)[];
39
+ splices: JsonSplice[];
40
+ };
1
41
  /**
2
42
  * Result of a JSON diff operation, containing paths and values that changed.
3
43
  * @template T - The type of the JSON value being diffed
4
44
  */
5
- export type JsonDiffResult<T = unknown> = {
6
- /** Array path segments (strings for object keys, numbers for array indices) */
7
- path: (string | number)[];
8
- /** The new value at this path */
9
- value: unknown;
10
- }[];
45
+ export type JsonDiffResult<T = unknown> = (JsonWrite | JsonDelete | JsonSpliceRecord)[];
46
+ /**
47
+ * How JsonDiff handles an object or array that changed:
48
+ * - `"diff"`: walk into it. An array is diffed by position.
49
+ * - `"splice"`: arrays only. Diff it as splices that match elements by the diff's identity (see
50
+ * JsonDiffIdentityFunc), without walking into them. Anything else is diffed as `"diff"`.
51
+ * - `"skip"`: record nothing for it.
52
+ * - `"write"`: write the new value as is.
53
+ */
54
+ export type JsonDiffStrategy = "diff" | "splice" | "skip" | "write";
55
+ /**
56
+ * Chooses how JsonDiff handles a changed object or array, given the new value, the old one (of any
57
+ * type) and the path they're at. Returning undefined means `"diff"`. It isn't called for primitives. `path` is the diff's own, so it
58
+ * must not be kept or changed.
59
+ */
60
+ export type JsonDiffStrategyFunc = (newValue: any, oldValue: any, path: readonly (string | number)[]) => JsonDiffStrategy | undefined;
61
+ /**
62
+ * What a spliced array's element is compared by: two elements whose identities are === are the same
63
+ * element, so it's matched and never diffed into. It's called for elements of any type, primitives
64
+ * included, but not to compare two elements that are already ===, so it must give the same identity
65
+ * for the same value. When two different elements share an identity, they're treated as the same
66
+ * element; avoiding that is up to the function.
67
+ */
68
+ export type JsonDiffIdentityFunc = (value: any) => unknown;
69
+ /** The default strategy: every changed object and array is diffed into, arrays by position */
70
+ export declare const JsonDiffDefaultStrategy: JsonDiffStrategyFunc;
71
+ /** The default identity: an element is its own identity, so elements are compared by === */
72
+ export declare const JsonDiffDefaultIdentity: JsonDiffIdentityFunc;
11
73
  /**
12
74
  * Determines the JSON type of a value.
13
75
  * @param value - The value to check
@@ -31,14 +93,122 @@ export declare function JsonMerge(source: unknown, patch: unknown): unknown;
31
93
  * @returns A deep clone of the value
32
94
  */
33
95
  export declare function JsonDeepClone<T>(value: T, type?: string): T;
96
+ /**
97
+ * Applies a diff result to a root value in place, one record at a time in order. A diff doesn't own
98
+ * its values (see JsonDiff), so each value written or inserted is deep-cloned first: the root never
99
+ * shares an object with the diff, the value it was computed from, or another record, unless
100
+ * `clone` says otherwise.
101
+ * - A write assigns its value at its path
102
+ * - A delete removes the property its path ends with
103
+ * - A splice record splices the array at its path in place (see JsonApplySplices)
104
+ * @param root - The value to update; must be an object or array
105
+ * @param diff - The diff result to apply
106
+ * @param assign - Assigns a written value (already cloned) to its parent
107
+ * @param remove - Removes a deleted property from its parent
108
+ * @param spliced - Called with each array a splice record changed, after it's changed
109
+ * @param clone - Copies each value before it's written or inserted. A caller that already owns the
110
+ * diff's values can pass one that keeps them, or parts of them, as they are.
111
+ * @throws For a write or delete with an empty path: the root can't be replaced or removed, only
112
+ * changed inside. JsonDiff records one only when the root's JSON type changes.
113
+ */
114
+ export declare function JsonApplyDiff(root: any, diff: JsonDiffResult, assign?: (parent: any, prop: string | number, value: any) => void, remove?: (parent: any, prop: string | number) => void, spliced?: (array: unknown[]) => void, clone?: (value: unknown) => unknown): void;
115
+ /**
116
+ * Applies splices to an array in place, in one pass however many there are: the new contents are
117
+ * built from the old ones, then written back into the same array. A moved item is the old element
118
+ * at its `from`, as is; an inserted item's value is deep-cloned.
119
+ * @param splices - In ascending, non-overlapping order, as JsonDiffArraySplice returns them: each
120
+ * one's index is at or after where the one before it ended
121
+ * @param clone - Copies each inserted value; see JsonApplyDiff
122
+ * @throws When a splice starts before the one before it ended
123
+ */
124
+ export declare function JsonApplySplices(array: unknown[], splices: readonly JsonSplice[], clone?: (value: unknown) => unknown): void;
125
+ /**
126
+ * Whether two JSON values differ, without recording how: it stops at the first difference and
127
+ * skips any subtree both values share, so it costs no more than walking what differs. An
128
+ * undefined property counts as absent, as in JSON. Arrays are compared by position.
129
+ * @param newValue - The new value
130
+ * @param oldValue - The old value to compare against
131
+ * @returns true when the values differ
132
+ */
133
+ export declare function JsonHasChanges(newValue: unknown, oldValue: unknown): boolean;
134
+ /**
135
+ * `newValue`, with every part that has no changes from the same place in `oldValue` (as
136
+ * JsonHasChanges compares them) replaced by `oldValue`'s object, so unchanged parts keep their
137
+ * identity. Neither value is changed: a container of `newValue` is copied only when one of its
138
+ * children is replaced, and kept as it is otherwise. Arrays are compared by position.
139
+ *
140
+ * old: { a: { x: 1 }, b: { y: 1 } }
141
+ * new: { a: { x: 1 }, b: { y: 2 } }
142
+ * result: { a: old.a, b: new.b }, a new object
143
+ *
144
+ * @param newValue - The new value
145
+ * @param oldValue - The value whose objects are kept where nothing changed
146
+ * @returns `oldValue` itself when nothing changed, otherwise `newValue` or a copy of it holding
147
+ * `oldValue`'s unchanged parts
148
+ */
149
+ export declare function JsonMaintainUnchanged<T>(newValue: T, oldValue: unknown): T;
150
+ /** Sets a key as an own property, even `__proto__`, which plain assignment would make the prototype */
151
+ export declare function SetOwnProperty(target: {
152
+ [key: string]: unknown;
153
+ }, key: string, value: unknown): void;
34
154
  /**
35
155
  * Computes the differences between two JSON values.
36
- * Returns an array of paths and values that changed from oldValue to newValue.
156
+ * Returns an array of paths and values that changed from oldValue to newValue. A value is only
157
+ * written whole where its JSON type changes (or a strategy says so); an object or array of the
158
+ * same type is always diffed into, however much of it changed. So the root itself is only ever
159
+ * written when its type changes, and a diff of values of the same type can always be applied
160
+ * in place. A record doesn't
161
+ * own its value: it references `newValue`'s objects, so two records can share one, and
162
+ * JsonApplyDiff clones each value as it's applied.
37
163
  * @template T - The type of the JSON values being compared
38
164
  * @param newValue - The new value
39
165
  * @param oldValue - The old value to compare against
40
- * @param rootPath - Optional dot-separated string for the base path (for internal recursion)
41
- * @param initResult - Optional initial result array (for internal recursion)
166
+ * @param rootPath - Optional dot-separated string for the base path
167
+ * @param strategy - Optional choice of how each changed object or array is diffed; without one,
168
+ * everything is diffed by walking into it, arrays by position
169
+ * @param identity - Optional identity a spliced array's elements are compared by; without one,
170
+ * elements are compared by ===. See JsonDiffIdentityFunc
42
171
  * @returns Array of change records with path and new value
43
172
  */
44
- export declare function JsonDiff<T>(newValue: T, oldValue: T, rootPath?: string, initResult?: JsonDiffResult<T>): JsonDiffResult<T>;
173
+ export declare function JsonDiff<T>(newValue: T, oldValue: T, rootPath?: string, strategy?: JsonDiffStrategyFunc, identity?: JsonDiffIdentityFunc): JsonDiffResult<T>;
174
+ /**
175
+ * Appends the differences between two JSON values to `result`, so several values can be diffed into
176
+ * one result.
177
+ * @param path - The path the values are at
178
+ * @param strategy - See JsonDiff
179
+ * @param identity - See JsonDiff
180
+ * @returns The result
181
+ */
182
+ export declare function JsonDiffInto<T>(newValue: T, oldValue: T, path: (string | number)[], result: JsonDiffResult<T>, strategy?: JsonDiffStrategyFunc, identity?: JsonDiffIdentityFunc): JsonDiffResult<T>;
183
+ /**
184
+ * The splices that turn `oldValue` into `newValue`, matching elements by identity without walking
185
+ * into them: two elements are the same when their identities are ===. With the default identity,
186
+ * that's the elements themselves. A matched element is never diffed into, even when it isn't === to
187
+ * its match. An identity that repeats is matched in order, and a repeat with no old element left to
188
+ * match is inserted.
189
+ *
190
+ * It works in four stages, followed through this example in the comments below:
191
+ *
192
+ * old: [A, B, C, D, E, F]
193
+ * new: [A, E, B, X, D, F]
194
+ *
195
+ * 1. Trim the elements identical at the start and end (A and F): they can't need a change.
196
+ * 2. Match each remaining new element to the old index holding the same element, or -1.
197
+ * 3. Choose which matched elements stay in place: the longest run of them still in their old
198
+ * order (B, D). Keeping the longest run is what keeps the number of moves as small as possible.
199
+ * 4. Walk both arrays from kept element to kept element. Between two kept elements, delete the
200
+ * old elements that aren't kept (C, E) and insert the new ones that aren't (E as a move, X as
201
+ * a new value).
202
+ *
203
+ * @returns The splices, in the order to apply them; none when the arrays hold the same elements.
204
+ * Applied in order to [A, B, C, D, E, F], with each move read from the array before any splice:
205
+ *
206
+ * splice(1, 0, E) [A, E, B, C, D, E, F]
207
+ * splice(3, 1, X) [A, E, B, X, D, E, F]
208
+ * splice(5, 1) [A, E, B, X, D, F]
209
+ *
210
+ * @param identity - What elements are compared by; see JsonDiffIdentityFunc. Elements that are
211
+ * === are the same element without calling it, and it's called at most a few times for each
212
+ * other element, so it must give the same identity for the same value.
213
+ */
214
+ export declare function JsonDiffArraySplice(newValue: readonly unknown[], oldValue: readonly unknown[], identity?: JsonDiffIdentityFunc): JsonSplice[];