j-templates 8.0.9 → 8.0.11

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.
@@ -63,7 +63,6 @@ class Store {
63
63
  this.UpdateRootObject(rootGroup[0].path[0], rootGroup);
64
64
  }
65
65
  }
66
- // protected SpliceRootMap(spliceResult: DiffSpliceResult) {
67
66
  SpliceRootObject(rootPath, start, deleteCount, items) {
68
67
  const rootObject = this.rootMap.get(rootPath);
69
68
  observableNode_1.ObservableNode.ApplySplice(rootObject[rootPath], start, deleteCount, items, this.cloneData);
@@ -125,12 +125,13 @@ class StoreAsync extends store_1.Store {
125
125
  * @returns The array of deleted elements.
126
126
  */
127
127
  async Splice(key, start, deleteCount, ...items) {
128
+ items = (0, json_1.JsonDeepClone)(items);
128
129
  return await this.queue.Next(async () => {
129
130
  if (!this.Has(key))
130
131
  throw "Key not found in store";
131
132
  const spliceResult = await this.diff.SplicePath(key, start, deleteCount, items);
132
133
  this.UpdateRootMap(spliceResult.diffResult);
133
- this.SpliceRootObject(spliceResult.path[0], spliceResult.start, spliceResult.deleteCount, (0, json_1.JsonDeepClone)(items));
134
+ this.SpliceRootObject(spliceResult.path[0], spliceResult.start, spliceResult.deleteCount, items);
134
135
  return spliceResult.spliceResult;
135
136
  });
136
137
  }
@@ -39,6 +39,7 @@ export declare namespace ObservableNode {
39
39
  * @returns A proxied version of the value that emits change events.
40
40
  */
41
41
  function Create<T>(value: T): T;
42
+ function Snapshot<T>(proxy: T): T | undefined;
42
43
  /**
43
44
  * Marks an observable node or its property as changed, triggering reactive updates.
44
45
  * Used internally to notify dependencies that a value has been modified.
@@ -6,6 +6,7 @@ const json_2 = require("../../Utils/json");
6
6
  const observableScope_1 = require("./observableScope");
7
7
  const NODE_VALUE = Symbol("NODE_VALUE");
8
8
  const NODE_PROXY = Symbol("NODE_PROXY");
9
+ const NODE_SNAPSHOT = Symbol("NODE_SNAPSHOT");
9
10
  const toJSON = "toJSON";
10
11
  const IS_NODE = Symbol("IS_NODE");
11
12
  const OBJECT_SCOPE = Symbol("OBJECT_SCOPE");
@@ -17,6 +18,24 @@ function Property(value, prop) {
17
18
  return value[prop];
18
19
  }
19
20
  const wrapperCache = new WeakMap();
21
+ const valueSnapshotCache = new WeakMap();
22
+ const valueSnapshotHierarchy = new WeakMap();
23
+ function CreateSnapshotLink(child, parent) {
24
+ let parents = valueSnapshotHierarchy.get(child);
25
+ if (!parents)
26
+ valueSnapshotHierarchy.set(child, (parents = new Set()));
27
+ parents.add(parent);
28
+ }
29
+ function InvalidateSnapshotForValue(value) {
30
+ valueSnapshotCache.delete(value);
31
+ const parents = valueSnapshotHierarchy.get(value);
32
+ if (parents === undefined)
33
+ return;
34
+ valueSnapshotHierarchy.delete(value);
35
+ for (const parent of parents)
36
+ if (valueSnapshotCache.has(parent))
37
+ InvalidateSnapshotForValue(parent);
38
+ }
20
39
  function getOwnPropertyDescriptor(target, prop) {
21
40
  const descriptor = Object.getOwnPropertyDescriptor(target[NODE_VALUE], prop);
22
41
  return {
@@ -93,45 +112,67 @@ function CloneProxy(value, type = (0, json_2.JsonType)(value)) {
93
112
  function GetPropertyScope(object, prop) {
94
113
  return object[prop] ??= observableScope_1.ObservableScope.Basic(Property.bind(null, object[NODE_VALUE], prop));
95
114
  }
115
+ function SetObjectValue(object, prop, value) {
116
+ const propExists = Object.hasOwn(object[NODE_VALUE], prop);
117
+ object[NODE_VALUE][prop] = value;
118
+ InvalidateSnapshotForValue(object[NODE_VALUE]);
119
+ observableScope_1.ObservableScope.Update(object[prop]);
120
+ !propExists && observableScope_1.ObservableScope.Update(object[ITERATOR_SCOPE]);
121
+ }
122
+ function SetArrayValue(object, prop, value) {
123
+ object[NODE_VALUE][prop] = value;
124
+ InvalidateSnapshotForValue(object[NODE_VALUE]);
125
+ observableScope_1.ObservableScope.Update(object[OBJECT_SCOPE]);
126
+ }
96
127
  function CreateProxyFactory(alias) {
97
- function ToJsonCopy(value) {
98
- const type = (0, json_2.JsonType)(value);
99
- switch (type) {
100
- case "array": {
101
- const typedValue = value;
102
- const arrayValue = alias(typedValue, false) ?? typedValue;
103
- return arrayValue.map(ToJsonCopy);
104
- }
105
- case "object": {
106
- const typedValue = alias(value) ?? value;
107
- const objectValue = alias(typedValue, false) ?? typedValue;
108
- const keys = Object.keys(objectValue);
109
- const copy = {};
110
- for (let x = 0; x < keys.length; x++)
111
- copy[keys[x]] = ToJsonCopy(typedValue[keys[x]]);
112
- return copy;
113
- }
114
- default:
115
- return value;
128
+ /**
129
+ * An immutable snapshot of a raw node value (arrays and plain objects; other values are
130
+ * returned as they are). A snapshot is cached per raw value in valueSnapshotCache and
131
+ * returned as-is until a write invalidates it: every write path calls Invalidate, which
132
+ * clears the written value and every cached ancestor, found through the parent links
133
+ * recorded here. A rebuild makes new containers but reuses the cached snapshots of
134
+ * unchanged children, so a snapshot handed out never changes and unchanged parts are
135
+ * shared between snapshots. Keyed children are read through their root.
136
+ */
137
+ function CreateSnapshot(value) {
138
+ if ((0, json_2.JsonType)(value) === "value")
139
+ return value;
140
+ const cached = valueSnapshotCache.get(value);
141
+ if (cached !== undefined)
142
+ return cached;
143
+ let snapshot;
144
+ if (Array.isArray(value)) {
145
+ snapshot = new Array(value.length);
146
+ for (let x = 0; x < value.length; x++)
147
+ snapshot[x] = ChildSnapshot(value, value[x]);
116
148
  }
149
+ else {
150
+ snapshot = {};
151
+ const keys = Object.keys(value);
152
+ for (let x = 0; x < keys.length; x++)
153
+ snapshot[keys[x]] = ChildSnapshot(value, value[keys[x]]);
154
+ }
155
+ valueSnapshotCache.set(value, snapshot);
156
+ return snapshot;
117
157
  }
118
- function ToJsonDefault(value) {
119
- return (0, json_1.JsonDeepClone)(value);
158
+ /**
159
+ * A child's snapshot, linking the child to its parent so a write to the child
160
+ * invalidates the parent. A keyed child's snapshot comes from its root, so the root is
161
+ * linked too; the parent's own embedded copy stays linked because a positional write
162
+ * (one that changes which entity a slot holds) mutates that copy in place.
163
+ */
164
+ function ChildSnapshot(parent, child) {
165
+ if ((0, json_2.JsonType)(child) === "value")
166
+ return child;
167
+ CreateSnapshotLink(child, parent);
168
+ const resolved = alias?.(child, false);
169
+ if (resolved !== undefined && resolved !== child) {
170
+ CreateSnapshotLink(resolved, parent);
171
+ return CreateSnapshot(resolved);
172
+ }
173
+ return CreateSnapshot(child);
120
174
  }
121
- const ToJson = alias !== undefined ? ToJsonCopy : ToJsonDefault;
122
175
  const readOnly = alias !== undefined;
123
- function SetObjectValue(object, prop, value) {
124
- object[NODE_VALUE][prop] = value;
125
- observableScope_1.ObservableScope.Update(object[prop]);
126
- }
127
- function SetArrayValue(object, prop, value) {
128
- object[NODE_VALUE][prop] = value;
129
- observableScope_1.ObservableScope.Update(object[OBJECT_SCOPE]);
130
- }
131
- function CreateProxy(value) {
132
- value = UnwrapProxy(value);
133
- return CreateProxyFromValue(value);
134
- }
135
176
  function CreateArrayProxy(value) {
136
177
  const wrapper = Object.assign([], {
137
178
  [NODE_VALUE]: value,
@@ -172,10 +213,12 @@ function CreateProxyFactory(alias) {
172
213
  return true;
173
214
  case toJSON:
174
215
  return function () {
175
- return ToJson(object[NODE_VALUE]);
216
+ return CreateSnapshot(object[NODE_VALUE]);
176
217
  };
177
218
  case NODE_VALUE:
178
219
  return object[NODE_VALUE];
220
+ case NODE_SNAPSHOT:
221
+ return CreateSnapshot(object[NODE_VALUE]);
179
222
  default: {
180
223
  const scope = object[OBJECT_SCOPE];
181
224
  const array = observableScope_1.ObservableScope.Value(scope);
@@ -199,6 +242,7 @@ function CreateProxyFactory(alias) {
199
242
  array.length = proxyArray.length;
200
243
  for (let x = 0; x < proxyArray.length; x++)
201
244
  array[x] = UnwrapProxy(proxyArray[x]);
245
+ InvalidateSnapshotForValue(object[NODE_VALUE]);
202
246
  observableScope_1.ObservableScope.Update(scope);
203
247
  break;
204
248
  }
@@ -237,9 +281,11 @@ function CreateProxyFactory(alias) {
237
281
  switch (prop) {
238
282
  case IS_NODE:
239
283
  return true;
284
+ case NODE_SNAPSHOT:
285
+ return CreateSnapshot(object[NODE_VALUE]);
240
286
  case toJSON:
241
287
  return function () {
242
- return ToJson(object[NODE_VALUE]);
288
+ return CreateSnapshot(object[NODE_VALUE]);
243
289
  };
244
290
  case NODE_VALUE:
245
291
  return object[NODE_VALUE];
@@ -334,6 +380,12 @@ var ObservableNode;
334
380
  return DefaultCreateProxy(value);
335
381
  }
336
382
  ObservableNode.Create = Create;
383
+ function Snapshot(proxy) {
384
+ if (proxy[IS_NODE])
385
+ return proxy[NODE_SNAPSHOT];
386
+ return undefined;
387
+ }
388
+ ObservableNode.Snapshot = Snapshot;
337
389
  /**
338
390
  * Marks an observable node or its property as changed, triggering reactive updates.
339
391
  * Used internally to notify dependencies that a value has been modified.
@@ -349,23 +401,18 @@ var ObservableNode;
349
401
  }
350
402
  ObservableNode.Update = Update;
351
403
  function Assign(value, prop, propValue) {
352
- const propExists = Object.hasOwn(value, prop);
353
- value[prop] = propValue;
404
+ // const propExists = Object.hasOwn(value as Object, prop);
405
+ // value[prop] = propValue;
354
406
  const wrapper = wrapperCache.get(value);
355
407
  if (wrapper) {
356
408
  if (Array.isArray(wrapper))
357
- observableScope_1.ObservableScope.Update(wrapper[OBJECT_SCOPE]);
358
- else {
359
- const scope = wrapper[prop];
360
- if (scope) {
361
- // A prop was just added to the object, fire the iterator scope
362
- if (!propExists)
363
- observableScope_1.ObservableScope.Update(wrapper[ITERATOR_SCOPE]);
364
- observableScope_1.ObservableScope.Update(scope);
365
- }
366
- else
367
- observableScope_1.ObservableScope.Update(wrapper[ITERATOR_SCOPE]);
368
- }
409
+ SetArrayValue(wrapper, prop, propValue);
410
+ else
411
+ SetObjectValue(wrapper, prop, propValue);
412
+ }
413
+ else {
414
+ InvalidateSnapshotForValue(value);
415
+ value[prop] = propValue;
369
416
  }
370
417
  }
371
418
  ObservableNode.Assign = Assign;
@@ -411,6 +458,7 @@ var ObservableNode;
411
458
  ObservableNode.Apply = Apply;
412
459
  function ApplySplice(rootNode, start, deleteCount, items, cloneData = true) {
413
460
  const root = rootNode[NODE_VALUE];
461
+ InvalidateSnapshotForValue(rootNode[NODE_VALUE]);
414
462
  const addItems = cloneData ? (0, json_1.JsonDeepClone)(items) : items;
415
463
  root.splice(start, deleteCount, ...addItems);
416
464
  ObservableNode.Update(root);
@@ -426,6 +474,7 @@ var ObservableNode;
426
474
  const root = rootNode[NODE_VALUE];
427
475
  if (diffResult.length === 1 && diffResult[0].path.length === 0) {
428
476
  // Replacing rootNode
477
+ InvalidateSnapshotForValue(root);
429
478
  const rootPatch = cloneData ? (0, json_1.JsonDeepClone)(diffResult[0].value) : diffResult[0].value;
430
479
  const rootType = (0, json_2.JsonType)(root);
431
480
  const rootPatchType = (0, json_2.JsonType)(rootPatch);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "j-templates",
3
- "version": "8.0.9",
3
+ "version": "8.0.11",
4
4
  "description": "j-templates",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/TypesInCode/jTemplates",