j-templates 7.0.89 → 7.0.91

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,76 +1,68 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ObservableNode = exports.GET_TO_JSON = exports.GET_OBSERVABLE_VALUE = exports.IS_OBSERVABLE_NODE = void 0;
3
+ exports.ObservableNode = void 0;
4
4
  const json_1 = require("../../Utils/json");
5
5
  const json_2 = require("../../Utils/json");
6
6
  const observableScope_1 = require("./observableScope");
7
- /**
8
- * Symbol to identify observable nodes.
9
- * Used to check if a value is an observable node proxy.
10
- */
11
- exports.IS_OBSERVABLE_NODE = Symbol("isObservableNode");
12
- /**
13
- * Symbol to get the raw underlying value from an observable node.
14
- * Returns the unwrapped, non-proxied value.
15
- */
16
- exports.GET_OBSERVABLE_VALUE = Symbol("getObservableValue");
17
- /**
18
- * Symbol for the toJSON method on observable nodes.
19
- * Used to serialize observable nodes to plain JSON.
20
- */
21
- exports.GET_TO_JSON = Symbol("toJSON");
22
- const proxyCache = new WeakMap();
23
- const scopeCache = new WeakMap();
24
- const leafScopeCache = new WeakMap();
7
+ const NODE_VALUE = Symbol("NODE_VALUE");
8
+ const NODE_PROXY = Symbol("NODE_PROXY");
9
+ const toJSON = "toJSON";
10
+ const IS_NODE = Symbol("IS_NODE");
11
+ const ARRAY_SCOPE = "ARRAY_SCOPE";
12
+ function Identity(value) {
13
+ return value;
14
+ }
15
+ function Property(value, prop) {
16
+ return value[prop];
17
+ }
18
+ const wrapperCache = new WeakMap();
25
19
  function getOwnPropertyDescriptor(target, prop) {
26
- const descriptor = Object.getOwnPropertyDescriptor(target, prop);
20
+ const descriptor = Object.getOwnPropertyDescriptor(target[NODE_VALUE], prop);
27
21
  return {
28
22
  ...descriptor,
29
23
  configurable: true,
30
24
  };
31
25
  }
32
26
  function getOwnPropertyDescriptorArray(target, prop) {
33
- const descriptor = Object.getOwnPropertyDescriptor(target, prop);
27
+ const descriptor = Object.getOwnPropertyDescriptor(target[NODE_VALUE], prop);
34
28
  return {
35
29
  ...descriptor,
36
30
  configurable: true,
37
31
  };
38
32
  }
39
33
  function has(value, prop) {
40
- return Object.hasOwn(value, prop);
34
+ return Object.hasOwn(value[NODE_VALUE], prop);
41
35
  }
42
36
  function hasArray(value, prop) {
43
- return Object.hasOwn(value, prop);
37
+ return Object.hasOwn(value[NODE_VALUE], prop);
44
38
  }
45
39
  function ownKeys(value) {
46
- return Object.keys(value);
40
+ return Object.keys(value[NODE_VALUE]);
47
41
  }
48
42
  function ownKeysArray(value) {
49
- return Object.keys(value);
43
+ return Object.keys(value[NODE_VALUE]);
50
44
  }
51
45
  function UnwrapProxy(value, type = (0, json_2.JsonType)(value)) {
52
46
  if (type === "value")
53
47
  return value;
54
- if (value[exports.IS_OBSERVABLE_NODE])
55
- return value[exports.GET_OBSERVABLE_VALUE];
48
+ if (value[IS_NODE])
49
+ return value[NODE_VALUE];
56
50
  switch (type) {
57
51
  case "object": {
58
52
  const keys = Object.keys(value);
59
53
  for (let x = 0; x < keys.length; x++)
60
54
  value[keys[x]] = UnwrapProxy(value[keys[x]]);
55
+ break;
61
56
  }
62
57
  case "array": {
63
58
  for (let x = 0; x < value.length; x++)
64
59
  value[x] = UnwrapProxy(value[x]);
60
+ break;
65
61
  }
66
62
  }
67
63
  return value;
68
64
  }
69
65
  function CreateProxyFactory(alias) {
70
- function CreateProxy(value) {
71
- value = UnwrapProxy(value);
72
- return CreateProxyFromValue(value);
73
- }
74
66
  function ToJsonCopy(value) {
75
67
  const type = (0, json_2.JsonType)(value);
76
68
  switch (type) {
@@ -97,41 +89,42 @@ function CreateProxyFactory(alias) {
97
89
  }
98
90
  const ToJson = alias !== undefined ? ToJsonCopy : ToJsonDefault;
99
91
  const readOnly = alias !== undefined;
92
+ function SetObjectValue(object, prop, value) {
93
+ object[NODE_VALUE][prop] = value;
94
+ observableScope_1.ObservableScope.Update(object[prop]);
95
+ }
96
+ function SetArrayValue(object, prop, value) {
97
+ object[NODE_VALUE][prop] = value;
98
+ observableScope_1.ObservableScope.Update(object[ARRAY_SCOPE]);
99
+ }
100
+ function CreateProxy(value) {
101
+ value = UnwrapProxy(value);
102
+ return CreateProxyFromValue(value);
103
+ }
100
104
  function CreateArrayProxy(value) {
101
- const scope = observableScope_1.ObservableScope.Create(() => value, false, true);
102
- const proxy = new Proxy(value, {
105
+ const wrapper = {
106
+ [NODE_VALUE]: value,
107
+ [NODE_PROXY]: null,
108
+ [ARRAY_SCOPE]: observableScope_1.ObservableScope.Basic(Identity.bind(null, value))
109
+ };
110
+ const proxy = wrapper[NODE_PROXY] = new Proxy(wrapper, {
103
111
  get: ArrayProxyGetter,
104
112
  set: ArrayProxySetter,
105
113
  has: hasArray,
106
114
  ownKeys: ownKeysArray,
107
115
  getOwnPropertyDescriptor: getOwnPropertyDescriptorArray,
108
116
  });
109
- scopeCache.set(value, scope);
110
- proxyCache.set(value, proxy);
117
+ wrapperCache.set(value, wrapper);
111
118
  return proxy;
112
119
  }
113
- function CreateObjectProxy(value) {
114
- const proxy = new Proxy(value, {
115
- get: ObjectProxyGetter,
116
- set: ObjectProxySetter,
117
- has,
118
- ownKeys,
119
- getOwnPropertyDescriptor,
120
- });
121
- leafScopeCache.set(value, {});
122
- proxyCache.set(value, proxy);
123
- return proxy;
124
- }
125
- function ArrayProxySetter(array, prop, value) {
120
+ function ArrayProxySetter(object, prop, value) {
126
121
  if (readOnly)
127
122
  throw `Object is readonly`;
128
123
  value = UnwrapProxy(value);
129
- array[prop] = value;
130
- const scope = scopeCache.get(array);
131
- observableScope_1.ObservableScope.Update(scope);
124
+ SetArrayValue(object, prop, value);
132
125
  return true;
133
126
  }
134
- function ArrayProxyGetter(array, prop) {
127
+ function ArrayProxyGetter(object, prop) {
135
128
  if (readOnly)
136
129
  switch (prop) {
137
130
  case "push":
@@ -144,25 +137,23 @@ function CreateProxyFactory(alias) {
144
137
  throw "Object is readonly";
145
138
  }
146
139
  switch (prop) {
147
- case exports.IS_OBSERVABLE_NODE:
140
+ case IS_NODE:
148
141
  return true;
149
- case exports.GET_TO_JSON:
142
+ case toJSON:
150
143
  return function () {
151
- return ToJson(array);
144
+ return ToJson(object[NODE_VALUE]);
152
145
  };
153
- case exports.GET_OBSERVABLE_VALUE:
154
- return array;
146
+ case NODE_VALUE:
147
+ return object[NODE_VALUE];
155
148
  default: {
156
- const scope = scopeCache.get(array);
157
- array = observableScope_1.ObservableScope.Value(scope);
149
+ const scope = object[ARRAY_SCOPE]; // scopeCache.get(array) as IObservableScope<unknown[]>;
150
+ const array = observableScope_1.ObservableScope.Value(scope);
158
151
  const arrayValue = array[prop];
159
152
  if (typeof prop === "symbol")
160
153
  return arrayValue;
161
154
  if (typeof arrayValue === "function")
162
155
  return function ArrayFunction(...args) {
163
- const proxyArray = prop === "slice" ? array.slice(...args) : array.slice();
164
- for (let x = 0; x < proxyArray.length; x++)
165
- proxyArray[x] = CreateProxyFromValue(proxyArray[x]);
156
+ const proxyArray = prop === "slice" ? array.slice(...args).map(CreateProxyFromValue) : array.map(CreateProxyFromValue);
166
157
  let result = prop === "slice"
167
158
  ? proxyArray
168
159
  : proxyArray[prop](...args);
@@ -187,82 +178,77 @@ function CreateProxyFactory(alias) {
187
178
  }
188
179
  }
189
180
  }
190
- function SetPropertyValue(object, prop, value) {
191
- object[prop] = value;
192
- const leafScopes = leafScopeCache.get(object);
193
- observableScope_1.ObservableScope.Update((leafScopes && leafScopes[prop]) || scopeCache.get(object));
181
+ function CreateObjectProxy(value) {
182
+ const wrapper = {
183
+ [NODE_VALUE]: value,
184
+ [NODE_PROXY]: null
185
+ };
186
+ const proxy = wrapper[NODE_PROXY] = new Proxy(wrapper, {
187
+ get: ObjectProxyGetter,
188
+ set: ObjectProxySetter,
189
+ has,
190
+ ownKeys,
191
+ getOwnPropertyDescriptor,
192
+ });
193
+ wrapperCache.set(value, wrapper);
194
+ return proxy;
194
195
  }
195
196
  function ObjectProxySetter(object, prop, value) {
196
197
  if (readOnly)
197
198
  throw `Object is readonly`;
198
- const jsonType = (0, json_2.JsonType)(value);
199
- if (jsonType === "value") {
200
- value !== object[prop] && SetPropertyValue(object, prop, value);
201
- }
202
- else {
203
- value = UnwrapProxy(value, jsonType);
204
- const diff = (0, json_1.JsonDiff)(value, object[prop]);
205
- for (let x = 0; x < diff.length; x++) {
206
- if (diff[x].path.length === 0) {
207
- SetPropertyValue(object, prop, diff[x].value);
208
- }
209
- else {
210
- const path = diff[x].path;
211
- let curr = object[prop];
212
- let y = 0;
213
- for (; y < path.length - 1; y++)
214
- curr = curr[path[y]];
215
- SetPropertyValue(curr, path[y], diff[x].value);
216
- }
217
- }
218
- }
199
+ value = UnwrapProxy(value);
200
+ SetObjectValue(object, prop, value);
219
201
  return true;
220
202
  }
221
203
  function ObjectProxyGetter(object, prop) {
222
204
  switch (prop) {
223
- case exports.IS_OBSERVABLE_NODE:
205
+ case IS_NODE:
224
206
  return true;
225
- case exports.GET_TO_JSON:
207
+ case toJSON:
226
208
  return function () {
227
209
  return ToJson(object);
228
210
  };
229
- case exports.GET_OBSERVABLE_VALUE:
230
- return object;
211
+ case NODE_VALUE:
212
+ return object[NODE_VALUE];
231
213
  default: {
232
214
  return GetAccessorValue(object, prop);
233
215
  }
234
216
  }
235
217
  }
236
- function GetAccessorValue(parent, prop) {
237
- const leafScopes = leafScopeCache.get(parent);
238
- leafScopes[prop] ??= observableScope_1.ObservableScope.Create(function () {
239
- const value = parent[prop];
240
- return CreateProxyFromValue(value);
241
- }, false, true);
242
- return observableScope_1.ObservableScope.Value(leafScopes[prop]);
218
+ function GetAccessorValue(object, prop) {
219
+ const scope = object[prop] ??= observableScope_1.ObservableScope.Basic(Property.bind(null, object[NODE_VALUE], prop));
220
+ const value = observableScope_1.ObservableScope.Value(scope);
221
+ return CreateProxyFromValue(value);
243
222
  }
244
223
  function CreateProxyFromValue(value) {
245
224
  const type = (0, json_2.JsonType)(value);
246
225
  switch (type) {
247
226
  case "object": {
248
- let proxy = proxyCache.get(value) ?? CreateObjectProxy(value);
227
+ const wrapper = wrapperCache.get(value);
228
+ let proxy = wrapper?.[NODE_PROXY] ?? CreateObjectProxy(value);
249
229
  if (alias !== undefined) {
250
230
  const aliasValue = alias(proxy);
251
- if (aliasValue !== undefined)
252
- proxy = proxyCache.get(aliasValue) ?? CreateObjectProxy(aliasValue);
231
+ if (aliasValue !== undefined && aliasValue !== value) {
232
+ const wrapper = wrapperCache.get(aliasValue);
233
+ proxy = wrapper?.[NODE_PROXY] ?? CreateObjectProxy(aliasValue);
234
+ }
253
235
  }
254
236
  return proxy;
255
237
  }
256
238
  case "array": {
257
- const proxy = proxyCache.get(value) ?? CreateArrayProxy(value);
258
- observableScope_1.ObservableScope.Touch(scopeCache.get(value));
239
+ const wrapper = wrapperCache.get(value);
240
+ const proxy = wrapper?.[NODE_PROXY] ?? CreateArrayProxy(value);
241
+ observableScope_1.ObservableScope.Touch(wrapper?.[ARRAY_SCOPE]);
259
242
  return proxy;
260
243
  }
261
244
  default:
262
245
  return value;
263
246
  }
264
247
  }
265
- return CreateProxy;
248
+ return function CreateProxy(value) {
249
+ value = UnwrapProxy(value);
250
+ return CreateProxyFromValue(value);
251
+ };
266
252
  }
267
253
  const DefaultCreateProxy = CreateProxyFactory();
268
254
  var ObservableNode;
@@ -295,16 +281,20 @@ var ObservableNode;
295
281
  * @param value The observable node to touch.
296
282
  * @param prop Optional property name or index to touch a specific nested property.
297
283
  */
298
- function Touch(value, prop) {
299
- let scope;
300
- if (prop !== undefined) {
301
- const leafScopes = leafScopeCache.get(value);
302
- scope = leafScopes?.[prop];
284
+ function Touch(value, prop = ARRAY_SCOPE) {
285
+ const wrapper = wrapperCache.get(value);
286
+ if (wrapper) {
287
+ const scope = wrapper[prop] ?? wrapper[ARRAY_SCOPE];
288
+ observableScope_1.ObservableScope.Update(scope);
303
289
  }
304
- scope ??= scopeCache.get(value);
305
- observableScope_1.ObservableScope.Update(scope);
306
290
  }
307
291
  ObservableNode.Touch = Touch;
292
+ function Apply(rootNode, update) {
293
+ const root = rootNode[NODE_VALUE];
294
+ const diff = (0, json_1.JsonDiff)(update, root);
295
+ ApplyDiff(rootNode, diff);
296
+ }
297
+ ObservableNode.Apply = Apply;
308
298
  /**
309
299
  * Applies a JSON diff result to an observable node, efficiently updating only changed properties.
310
300
  * Optimizes nested object updates by computing paths incrementally and touching modified properties.
@@ -312,7 +302,36 @@ var ObservableNode;
312
302
  * @param diffResult The diff result from JsonDiff containing path-value pairs of changes.
313
303
  */
314
304
  function ApplyDiff(rootNode, diffResult) {
315
- const root = rootNode[exports.GET_OBSERVABLE_VALUE];
305
+ const root = rootNode[NODE_VALUE];
306
+ if (diffResult.length === 1 && diffResult[0].path.length === 0) {
307
+ // Replacing rootNode
308
+ const rootPatch = diffResult[0].value;
309
+ const rootType = (0, json_2.JsonType)(root);
310
+ const rootPatchType = (0, json_2.JsonType)(root);
311
+ if (rootType !== rootPatchType)
312
+ throw new Error("Unable to change type of Root ObservableNode: " + rootType);
313
+ switch (rootType) {
314
+ case "array": {
315
+ root.splice(0, root.length, ...rootPatch);
316
+ ObservableNode.Touch(root);
317
+ break;
318
+ }
319
+ case "object": {
320
+ const keys = Object.keys(root);
321
+ const patchKeys = Object.keys(rootPatch);
322
+ for (let x = 0; x < keys.length; x++)
323
+ if (!patchKeys.includes(keys[x]))
324
+ delete root[keys[x]];
325
+ Object.assign(root, rootPatch);
326
+ for (let x = 0; x < keys.length; x++)
327
+ ObservableNode.Touch(root, keys[x]);
328
+ break;
329
+ }
330
+ case "value":
331
+ throw new Error("Unable to replace value type: " + root);
332
+ }
333
+ return;
334
+ }
316
335
  const pathTuples = [["", root]];
317
336
  for (let x = 0; x < diffResult.length; x++) {
318
337
  const { path, value } = diffResult[x];
@@ -7,6 +7,14 @@ import { Emitter, EmitterCallback } from "../../Utils/emitter";
7
7
  interface IStaticObservableScope<T> {
8
8
  type: "static";
9
9
  value: T;
10
+ onDestroyed: Emitter | null;
11
+ destroyed: boolean;
12
+ }
13
+ export interface IBasicObservableScope<T> {
14
+ type: "basic";
15
+ emitter: Emitter;
16
+ getFunction: () => T;
17
+ destroyed: boolean;
10
18
  }
11
19
  /**
12
20
  * Represents a dynamic (reactive) observable scope.
@@ -17,6 +25,7 @@ interface IDynamicObservableScope<T> {
17
25
  type: "dynamic";
18
26
  /** Whether the scope's getter function is async */
19
27
  async: boolean;
28
+ pending: Promise<T> | null;
20
29
  /** Whether updates are batched (true) or immediate (false) */
21
30
  greedy: boolean;
22
31
  /** Whether the scope needs recomputation */
@@ -33,12 +42,12 @@ interface IDynamicObservableScope<T> {
33
42
  emitter: Emitter;
34
43
  /** Array of emitters this scope listens to for dependency changes */
35
44
  emitters: (Emitter | null)[];
45
+ onUpdated: Emitter | null;
36
46
  /** Emitter for notifying when the scope is destroyed */
37
47
  onDestroyed: Emitter | null;
48
+ scopes: [any, IObservableScope<any>][] | null;
38
49
  /** Map of nested scopes created during this scope's execution */
39
- scopes: {
40
- [id: string]: IObservableScope<unknown> | null;
41
- } | null;
50
+ mappedScopes: Map<any, IObservableScope<unknown>[]> | null;
42
51
  }
43
52
  /**
44
53
  * A reactive or static scope containing a value.
@@ -62,6 +71,7 @@ export type IObservableScope<T> = IStaticObservableScope<T> | IDynamicObservable
62
71
  * @throws Error if called outside a watch context.
63
72
  */
64
73
  export declare function InlineScope<T>(callback: () => T | Promise<T>, idOverride?: string): T;
74
+ export declare function MappedScope<D, T>(data: D, callback: (data: D) => T | Promise<T>, onUpdated?: (lastValue: T, scope: IObservableScope<T>) => void, onDestroyed?: (lastValue: T) => void): T;
65
75
  /**
66
76
  * Creates a gatekeeper scope that only emits when the value actually changes (===).
67
77
  * Prevents unnecessary downstream re-evaluations when the derived value stays the same.
@@ -113,7 +123,13 @@ export declare namespace ObservableScope {
113
123
  */
114
124
  function Create<T>(valueFunction: {
115
125
  (): T | Promise<T>;
116
- }, greedy?: boolean, force?: boolean): IObservableScope<T>;
126
+ }): IObservableScope<T>;
127
+ function Greedy<T>(valueFunction: {
128
+ (): T | Promise<T>;
129
+ }): IObservableScope<T>;
130
+ function Basic<T>(valueFunction: {
131
+ (): T;
132
+ }): IBasicObservableScope<T>;
117
133
  /**
118
134
  * Registers an emitter as a dependency for the current watch context.
119
135
  * @param emitter The emitter to register as a dependency.
@@ -125,51 +141,56 @@ export declare namespace ObservableScope {
125
141
  * @param scope The scope to peek at.
126
142
  * @returns The scope's current value.
127
143
  */
128
- function Peek<T>(scope: IObservableScope<T>): T;
144
+ function Peek<T>(scope: IObservableScope<T> | IBasicObservableScope<T>): T;
129
145
  /**
130
146
  * Gets a scope's current value and registers as a dependency.
131
147
  * @template T The type of value stored in the scope.
132
148
  * @param scope The scope to get the value from.
133
149
  * @returns The scope's current value.
134
150
  */
135
- function Value<T>(scope: IObservableScope<T>): T;
151
+ function Value<T>(scope: IObservableScope<T> | IBasicObservableScope<T>): T;
136
152
  /**
137
153
  * Registers a scope as a dependency without retrieving its value.
138
154
  * @template T The type of value stored in the scope.
139
155
  * @param scope The scope to register as a dependency.
140
156
  */
141
- function Touch<T>(scope: IObservableScope<T>): void;
157
+ function Touch<T>(scope: IObservableScope<T> | IBasicObservableScope<T>): void;
142
158
  /**
143
159
  * Subscribes to changes on a dynamic scope.
144
160
  * @template T The type of value stored in the scope.
145
161
  * @param scope The scope to watch for changes.
146
162
  * @param callback Function to invoke when the scope's value changes.
147
163
  */
148
- function Watch<T>(scope: IObservableScope<T>, callback: EmitterCallback<[IObservableScope<T>]>): void;
164
+ function Watch<T>(scope: IObservableScope<T>, callback: (scope: IObservableScope<T>) => void): void;
149
165
  /**
150
166
  * Unsubscribes from changes on a dynamic scope.
151
167
  * @template T The type of value stored in the scope.
152
168
  * @param scope The scope to stop watching.
153
169
  * @param callback The callback function to remove.
154
170
  */
155
- function Unwatch<T>(scope: IObservableScope<T>, callback: EmitterCallback<[IObservableScope<T>]>): void;
171
+ function Unwatch<T>(scope: IObservableScope<T>, callback: (scope: IObservableScope<T>) => void): void;
172
+ function OnUpdated<T>(scope: IObservableScope<T>, callback: {
173
+ (lastValue: T, scope: IObservableScope<T>): void;
174
+ }): void;
156
175
  /**
157
176
  * Registers a callback to be invoked when the scope is destroyed.
158
177
  * @param scope The scope to monitor for destruction.
159
178
  * @param callback Function to invoke when the scope is destroyed.
160
179
  */
161
- function OnDestroyed(scope: IObservableScope<unknown>, callback: EmitterCallback): void;
180
+ function OnDestroyed<T>(scope: IObservableScope<T>, callback: {
181
+ (lastValue: T): void;
182
+ }): void;
162
183
  /**
163
184
  * Marks a scope as dirty, triggering recomputation on next access or batch.
164
185
  * @param scope The scope to mark for update.
165
186
  */
166
- function Update(scope: IObservableScope<any>): void;
187
+ function Update(scope: IObservableScope<any> | IBasicObservableScope<any>): void;
167
188
  /**
168
189
  * Destroys a scope, cleaning up all resources and dependencies.
169
190
  * @template T The type of value stored in the scope.
170
191
  * @param scope The scope to destroy.
171
192
  */
172
- function Destroy<T>(scope: IObservableScope<T>): void;
193
+ function Destroy<T>(scope: IObservableScope<T> | IBasicObservableScope<T>): void;
173
194
  /**
174
195
  * Destroys multiple scopes, cleaning up their resources.
175
196
  * @param scopes Array of scopes to destroy.