j-templates 8.0.7 → 8.0.9

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.
@@ -1,4 +1,4 @@
1
- import { IDiffTree } from "./diffTree";
1
+ import { DiffSpliceResult, IDiffTree } from "./diffTree";
2
2
  import { JsonDiffResult } from "../../Utils/json";
3
3
  /**
4
4
  * Async version of IDiffTree interface with all methods returning promises.
@@ -38,6 +38,7 @@ export declare class DiffAsync implements IDiffTreeAsync {
38
38
  path: string;
39
39
  value: any;
40
40
  }>): Promise<JsonDiffResult>;
41
+ SplicePath(path: string, start: number, deleteCount: number, items: any[], flatten?: boolean): Promise<DiffSpliceResult<unknown>>;
41
42
  /**
42
43
  * Destroys the DiffAsync instance and terminates the worker.
43
44
  */
@@ -45,6 +45,12 @@ class DiffAsync {
45
45
  arguments: [data],
46
46
  });
47
47
  }
48
+ async SplicePath(path, start, deleteCount, items, flatten) {
49
+ return await this.workerQueue.Push({
50
+ method: "splicepath",
51
+ arguments: [path, start, deleteCount, items, flatten]
52
+ });
53
+ }
48
54
  /**
49
55
  * Destroys the DiffAsync instance and terminates the worker.
50
56
  */
@@ -1,4 +1,4 @@
1
- import { IDiffTree } from "./diffTree";
1
+ import { DiffSpliceResult, IDiffTree } from "./diffTree";
2
2
  /**
3
3
  * Synchronous diff implementation.
4
4
  * Computes diffs immediately without queuing or workers.
@@ -31,4 +31,5 @@ export declare class DiffSync implements IDiffTree {
31
31
  path: string;
32
32
  value: any;
33
33
  }>): import("../../Utils/json").JsonDiffResult;
34
+ SplicePath(path: string, start: number, deleteCount: number | undefined, items: any[], flatten?: boolean): DiffSpliceResult;
34
35
  }
@@ -36,5 +36,8 @@ class DiffSync {
36
36
  DiffBatch(data) {
37
37
  return this.diffTree.DiffBatch(data);
38
38
  }
39
+ SplicePath(path, start, deleteCount, items, flatten) {
40
+ return this.diffTree.SplicePath(path, start, deleteCount, items, flatten);
41
+ }
39
42
  }
40
43
  exports.DiffSync = DiffSync;
@@ -4,10 +4,19 @@ import { JsonDiffFactoryResult, JsonDiffResult } from "../../Utils/json";
4
4
  */
5
5
  export interface IDiffMethod {
6
6
  /** The method to call */
7
- method: "create" | "diffpath" | "diffbatch" | "updatepath" | "getpath";
7
+ method: "create" | "diffpath" | "splicepath" | "diffbatch" | "updatepath" | "getpath";
8
8
  /** Arguments for the method call */
9
9
  arguments: Array<any>;
10
10
  }
11
+ export type DiffSpliceResult<T = unknown> = {
12
+ /** Array path segments (strings for object keys, numbers for array indices) */
13
+ path: (string | number)[];
14
+ /** Required by the splice operation **/
15
+ start: number;
16
+ deleteCount: number;
17
+ spliceResult: any[];
18
+ diffResult: JsonDiffResult;
19
+ };
11
20
  /**
12
21
  * Interface for diff tree operations.
13
22
  */
@@ -28,6 +37,7 @@ export interface IDiffTree {
28
37
  * @returns Diff results showing changes
29
38
  */
30
39
  DiffPath(path: string, value: any, flatten?: boolean): JsonDiffResult;
40
+ SplicePath(path: string, start: number, deleteCount: number | undefined, items: any[], flatten?: boolean): DiffSpliceResult;
31
41
  }
32
42
  /**
33
43
  * Constructor interface for IDiffTree.
@@ -29,6 +29,11 @@ function DiffTreeFactory(jsonDiffFactory, worker) {
29
29
  ctx.postMessage(diff);
30
30
  break;
31
31
  }
32
+ case "splicepath": {
33
+ const diff = diffTree.SplicePath(data.arguments[0], data.arguments[1], data.arguments[2], data.arguments[3], data.arguments[4]);
34
+ ctx.postMessage(diff);
35
+ break;
36
+ }
32
37
  case "diffbatch": {
33
38
  const diff = diffTree.DiffBatch(data.arguments[0]);
34
39
  ctx.postMessage(diff);
@@ -158,6 +163,34 @@ function DiffTreeFactory(jsonDiffFactory, worker) {
158
163
  }
159
164
  return diffResult;
160
165
  }
166
+ function SpliceSource(source, path, start, deleteCount, items, flatten, keyFunc) {
167
+ const sourceValue = GetPathValue(source, path);
168
+ if (!Array.isArray(sourceValue))
169
+ throw `Value found at path ${path} is not an array`;
170
+ deleteCount ??= sourceValue.length - start;
171
+ const spliceResult = {
172
+ path: path.split("."),
173
+ start,
174
+ deleteCount,
175
+ spliceResult: null,
176
+ diffResult: []
177
+ };
178
+ spliceResult.spliceResult = sourceValue.splice(start, deleteCount, ...items);
179
+ if (flatten && keyFunc) {
180
+ // Register the keyed entities the new items carry, diffed against the roots
181
+ // already held: a new entity is added whole, one re-inserted unchanged (an undo)
182
+ // produces no diff, so its readers aren't woken.
183
+ const flattened = JsonDeepClone(FlattenValue({}, items, keyFunc));
184
+ const keys = Object.keys(flattened);
185
+ for (let x = 0; x < keys.length; x++)
186
+ JsonDiff(flattened[keys[x]], source[keys[x]], keys[x], spliceResult.diffResult);
187
+ }
188
+ const filteredDiffResult = spliceResult.diffResult.filter((diff) => diff.value !== undefined);
189
+ for (let x = 0; x < filteredDiffResult.length; x++) {
190
+ SetPathValue(source, filteredDiffResult[x].path, filteredDiffResult[x].value);
191
+ }
192
+ return spliceResult;
193
+ }
161
194
  /**
162
195
  * Internal diff tree implementation.
163
196
  * Maintains root state and computes diffs for path/value updates.
@@ -192,6 +225,9 @@ function DiffTreeFactory(jsonDiffFactory, worker) {
192
225
  DiffPath(path, value, flatten = true) {
193
226
  return UpdateSource(this.rootState, path, value, flatten, this.keyFunc);
194
227
  }
228
+ SplicePath(path, start, deleteCount, items, flatten = true) {
229
+ return SpliceSource(this.rootState, path, start, deleteCount, items, flatten, this.keyFunc);
230
+ }
195
231
  /**
196
232
  * Retrieves the current value at a path.
197
233
  * @param path - Dot-separated path to the value
@@ -8,6 +8,7 @@ import { JsonDiffResult } from "../../Utils/json";
8
8
  */
9
9
  export declare abstract class Store {
10
10
  protected keyFunc?: (value: any) => string | undefined;
11
+ private cloneData;
11
12
  private rootMap;
12
13
  private createNode;
13
14
  /**
@@ -15,7 +16,7 @@ export declare abstract class Store {
15
16
  * @param keyFunc Optional function to generate a key for a given data value.
16
17
  * When provided, enables alias functionality where values can reference other root objects.
17
18
  */
18
- constructor(keyFunc?: (value: any) => string | undefined);
19
+ constructor(keyFunc?: (value: any) => string | undefined, cloneData?: boolean);
19
20
  /**
20
21
  * Retrieves a value from the store by id.
21
22
  * @template O - The type of the value to retrieve
@@ -31,6 +32,7 @@ export declare abstract class Store {
31
32
  * @returns The value or the default value
32
33
  */
33
34
  Get<O>(id: string, defaultValue: O): O;
35
+ Has(id: string): boolean;
34
36
  abstract Write(data: unknown, key?: string): Promise<void> | void;
35
37
  abstract Patch(key: string, patch: unknown): Promise<void> | void;
36
38
  abstract Push(key: string, ...data: unknown[]): Promise<void> | void;
@@ -41,6 +43,7 @@ export declare abstract class Store {
41
43
  * @protected
42
44
  */
43
45
  protected UpdateRootMap(results: JsonDiffResult): void;
46
+ protected SpliceRootObject(rootPath: string | number, start: number, deleteCount: number, items: any[]): void;
44
47
  /**
45
48
  * Updates a specific root object with diff results.
46
49
  * Creates the root object if it doesn't exist.
@@ -16,8 +16,9 @@ class Store {
16
16
  * @param keyFunc Optional function to generate a key for a given data value.
17
17
  * When provided, enables alias functionality where values can reference other root objects.
18
18
  */
19
- constructor(keyFunc) {
19
+ constructor(keyFunc, cloneData = true) {
20
20
  this.keyFunc = keyFunc;
21
+ this.cloneData = cloneData;
21
22
  this.rootMap = new Map();
22
23
  const aliasFunc = keyFunc &&
23
24
  ((value, reactive = true) => {
@@ -44,6 +45,9 @@ class Store {
44
45
  }
45
46
  return result[id];
46
47
  }
48
+ Has(id) {
49
+ return this.rootMap.has(id);
50
+ }
47
51
  /**
48
52
  * Updates the root map with diff results by grouping changes by root path.
49
53
  * @param results - Array of diff results to apply
@@ -59,6 +63,11 @@ class Store {
59
63
  this.UpdateRootObject(rootGroup[0].path[0], rootGroup);
60
64
  }
61
65
  }
66
+ // protected SpliceRootMap(spliceResult: DiffSpliceResult) {
67
+ SpliceRootObject(rootPath, start, deleteCount, items) {
68
+ const rootObject = this.rootMap.get(rootPath);
69
+ observableNode_1.ObservableNode.ApplySplice(rootObject[rootPath], start, deleteCount, items, this.cloneData);
70
+ }
62
71
  /**
63
72
  * Updates a specific root object with diff results.
64
73
  * Creates the root object if it doesn't exist.
@@ -72,7 +81,8 @@ class Store {
72
81
  if (rootObject === undefined) {
73
82
  if (results.length > 1 || results[0].path.length > 1)
74
83
  throw `Unable to initialize root path ${rootPath} with ${results.length} results and initial path ${results[0].path}`;
75
- const newRootObject = this.createNode({ [rootPath]: (0, json_1.JsonDeepClone)(results[0].value) });
84
+ const rootData = this.cloneData ? (0, json_1.JsonDeepClone)(results[0].value) : results[0].value;
85
+ const newRootObject = this.createNode({ [rootPath]: rootData });
76
86
  this.rootMap.set(rootPath, newRootObject);
77
87
  return;
78
88
  }
@@ -84,7 +84,7 @@ export declare class StoreAsync extends Store {
84
84
  * @param items Optional elements to insert into the array.
85
85
  * @returns The array of deleted elements.
86
86
  */
87
- Splice(key: string, start: number, deleteCount?: number, ...items: unknown[]): Promise<any>;
87
+ Splice(key: string, start: number, deleteCount?: number, ...items: unknown[]): Promise<any[]>;
88
88
  /**
89
89
  * Destroys the StoreAsync instance, stopping the async queue and cleaning up the diff instance.
90
90
  * This method should be called when the instance is no longer needed to free up resources.
@@ -43,7 +43,7 @@ class StoreAsync extends store_1.Store {
43
43
  * @param keyFunc Optional function to generate a key for a given data value.
44
44
  */
45
45
  constructor(keyFunc) {
46
- super(keyFunc);
46
+ super(keyFunc, false);
47
47
  /**
48
48
  * The async queue instance used to manage asynchronous operations in a non-blocking manner.
49
49
  * @private
@@ -82,6 +82,8 @@ class StoreAsync extends store_1.Store {
82
82
  */
83
83
  async Patch(key, patch) {
84
84
  await this.queue.Next(async () => {
85
+ if (!this.Has(key))
86
+ throw "Key not found in store";
85
87
  const value = this.Get(key);
86
88
  if (value === undefined)
87
89
  throw "Unable to patch undefined value";
@@ -99,6 +101,8 @@ class StoreAsync extends store_1.Store {
99
101
  */
100
102
  async Push(key, ...data) {
101
103
  await this.queue.Next(async () => {
104
+ if (!this.Has(key))
105
+ throw "Key not found in store";
102
106
  const arr = this.Get(key);
103
107
  const batch = data.map(function (d, i) {
104
108
  return {
@@ -122,12 +126,12 @@ class StoreAsync extends store_1.Store {
122
126
  */
123
127
  async Splice(key, start, deleteCount, ...items) {
124
128
  return await this.queue.Next(async () => {
125
- const arr = this.Get(key);
126
- const arrValue = arr.toJSON();
127
- const spliceResult = arrValue.splice(start, deleteCount, ...items);
128
- const diffResult = await this.diff.DiffPath(key, arrValue);
129
- this.UpdateRootMap(diffResult);
130
- return spliceResult;
129
+ if (!this.Has(key))
130
+ throw "Key not found in store";
131
+ const spliceResult = await this.diff.SplicePath(key, start, deleteCount, items);
132
+ this.UpdateRootMap(spliceResult.diffResult);
133
+ this.SpliceRootObject(spliceResult.path[0], spliceResult.start, spliceResult.deleteCount, (0, json_1.JsonDeepClone)(items));
134
+ return spliceResult.spliceResult;
131
135
  });
132
136
  }
133
137
  /**
@@ -67,5 +67,5 @@ export declare class StoreSync extends Store {
67
67
  * @param items Optional elements to insert into the array.
68
68
  * @returns The array of deleted elements.
69
69
  */
70
- Splice(key: string, start: number, deleteCount?: number, ...items: unknown[]): any;
70
+ Splice(key: string, start: number, deleteCount?: number, ...items: unknown[]): any[];
71
71
  }
@@ -63,6 +63,8 @@ class StoreSync extends store_1.Store {
63
63
  * @throws Will throw an error if the value to be patched is undefined.
64
64
  */
65
65
  Patch(key, patch) {
66
+ if (!this.Has(key))
67
+ throw "Key not found in store";
66
68
  const value = this.Get(key);
67
69
  if (value === undefined)
68
70
  throw "Unable to patch undefined value";
@@ -77,6 +79,8 @@ class StoreSync extends store_1.Store {
77
79
  * @param data The data items to be pushed into the array.
78
80
  */
79
81
  Push(key, ...data) {
82
+ if (!this.Has(key))
83
+ throw "Key not found in store";
80
84
  const arr = this.Get(key);
81
85
  const batch = data.map(function (d, i) {
82
86
  return {
@@ -97,12 +101,12 @@ class StoreSync extends store_1.Store {
97
101
  * @returns The array of deleted elements.
98
102
  */
99
103
  Splice(key, start, deleteCount, ...items) {
100
- const arr = this.Get(key);
101
- const arrValue = arr.toJSON();
102
- const spliceResult = arrValue.splice(start, deleteCount, ...items);
103
- const diffResult = this.diff.DiffPath(key, arrValue);
104
- this.UpdateRootMap(diffResult);
105
- return spliceResult;
104
+ if (!this.Has(key))
105
+ throw "Key not found in store";
106
+ const spliceResult = this.diff.SplicePath(key, start, deleteCount, items);
107
+ this.UpdateRootMap(spliceResult.diffResult);
108
+ this.SpliceRootObject(spliceResult.path[0], spliceResult.start, spliceResult.deleteCount, items);
109
+ return (0, json_1.JsonDeepClone)(spliceResult.spliceResult);
106
110
  }
107
111
  }
108
112
  exports.StoreSync = StoreSync;
@@ -68,13 +68,14 @@ export declare namespace ObservableNode {
68
68
  * @remarks No-op when `value` deep-equals the node's current value (empty diff).
69
69
  */
70
70
  function Apply(rootNode: any, value: any): void;
71
+ function ApplySplice(rootNode: any, start: number, deleteCount: number, items: any[], cloneData?: boolean): void;
71
72
  /**
72
73
  * Applies a JSON diff result to an observable node, efficiently updating only changed properties.
73
74
  * Optimizes nested object updates by computing paths incrementally and touching modified properties.
74
75
  * @param rootNode The observable node to apply the diff to.
75
76
  * @param diffResult The diff result from JsonDiff containing path-value pairs of changes.
76
77
  */
77
- function ApplyDiff(rootNode: any, diffResult: JsonDiffResult): void;
78
+ function ApplyDiff(rootNode: any, diffResult: JsonDiffResult, cloneData?: boolean): void;
78
79
  /**
79
80
  * Creates a factory function for making values observable with optional aliasing.
80
81
  * The alias function transforms values before creating the observable proxy,
@@ -409,17 +409,24 @@ var ObservableNode;
409
409
  ApplyDiff(rootNode, diff);
410
410
  }
411
411
  ObservableNode.Apply = Apply;
412
+ function ApplySplice(rootNode, start, deleteCount, items, cloneData = true) {
413
+ const root = rootNode[NODE_VALUE];
414
+ const addItems = cloneData ? (0, json_1.JsonDeepClone)(items) : items;
415
+ root.splice(start, deleteCount, ...addItems);
416
+ ObservableNode.Update(root);
417
+ }
418
+ ObservableNode.ApplySplice = ApplySplice;
412
419
  /**
413
420
  * Applies a JSON diff result to an observable node, efficiently updating only changed properties.
414
421
  * Optimizes nested object updates by computing paths incrementally and touching modified properties.
415
422
  * @param rootNode The observable node to apply the diff to.
416
423
  * @param diffResult The diff result from JsonDiff containing path-value pairs of changes.
417
424
  */
418
- function ApplyDiff(rootNode, diffResult) {
425
+ function ApplyDiff(rootNode, diffResult, cloneData = true) {
419
426
  const root = rootNode[NODE_VALUE];
420
427
  if (diffResult.length === 1 && diffResult[0].path.length === 0) {
421
428
  // Replacing rootNode
422
- const rootPatch = (0, json_1.JsonDeepClone)(diffResult[0].value);
429
+ const rootPatch = cloneData ? (0, json_1.JsonDeepClone)(diffResult[0].value) : diffResult[0].value;
423
430
  const rootType = (0, json_2.JsonType)(root);
424
431
  const rootPatchType = (0, json_2.JsonType)(rootPatch);
425
432
  if (rootType !== rootPatchType)
@@ -465,7 +472,8 @@ var ObservableNode;
465
472
  }
466
473
  }
467
474
  const assignValue = pathTuples[y][1];
468
- ObservableNode.Assign(assignValue, path[y], (0, json_1.JsonDeepClone)(value));
475
+ const assignData = cloneData ? (0, json_1.JsonDeepClone)(value) : value;
476
+ ObservableNode.Assign(assignValue, path[y], assignData);
469
477
  }
470
478
  }
471
479
  ObservableNode.ApplyDiff = ApplyDiff;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "j-templates",
3
- "version": "8.0.7",
3
+ "version": "8.0.9",
4
4
  "description": "j-templates",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/TypesInCode/jTemplates",