j-templates 7.0.70 → 7.0.71
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,12 +1,56 @@
|
|
|
1
1
|
import { JsonDiffResult } from "../../Utils/json";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Symbol to identify observable nodes.
|
|
4
|
+
* Used to check if a value is an observable node proxy.
|
|
5
|
+
*/
|
|
6
|
+
export declare const IS_OBSERVABLE_NODE: unique symbol;
|
|
7
|
+
/**
|
|
8
|
+
* Symbol to get the raw underlying value from an observable node.
|
|
9
|
+
* Returns the unwrapped, non-proxied value.
|
|
10
|
+
*/
|
|
11
|
+
export declare const GET_OBSERVABLE_VALUE: unique symbol;
|
|
12
|
+
/**
|
|
13
|
+
* Symbol for the toJSON method on observable nodes.
|
|
14
|
+
* Used to serialize observable nodes to plain JSON.
|
|
15
|
+
*/
|
|
16
|
+
export declare const GET_TO_JSON: unique symbol;
|
|
5
17
|
export declare namespace ObservableNode {
|
|
6
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Unwraps an observable node to get the raw underlying value.
|
|
20
|
+
* Recursively unwraps nested objects and arrays.
|
|
21
|
+
* @template T The type of value to unwrap.
|
|
22
|
+
* @param value The value to unwrap, which may be an observable node or plain value.
|
|
23
|
+
* @returns The unwrapped raw value without proxy wrappers.
|
|
24
|
+
*/
|
|
7
25
|
function Unwrap<T>(value: T): T;
|
|
26
|
+
/**
|
|
27
|
+
* Creates an observable node from a plain value.
|
|
28
|
+
* Wraps the value in a proxy that tracks changes and enables reactive updates.
|
|
29
|
+
* @template T The type of value to wrap.
|
|
30
|
+
* @param value The plain value (object, array, or primitive) to make observable.
|
|
31
|
+
* @returns A proxied version of the value that emits change events.
|
|
32
|
+
*/
|
|
8
33
|
function Create<T>(value: T): T;
|
|
34
|
+
/**
|
|
35
|
+
* Marks an observable node or its property as changed, triggering reactive updates.
|
|
36
|
+
* Used internally to notify dependencies that a value has been modified.
|
|
37
|
+
* @param value The observable node to touch.
|
|
38
|
+
* @param prop Optional property name or index to touch a specific nested property.
|
|
39
|
+
*/
|
|
9
40
|
function Touch(value: unknown, prop?: string | number): void;
|
|
41
|
+
/**
|
|
42
|
+
* Applies a JSON diff result to an observable node, efficiently updating only changed properties.
|
|
43
|
+
* Optimizes nested object updates by computing paths incrementally and touching modified properties.
|
|
44
|
+
* @param rootNode The observable node to apply the diff to.
|
|
45
|
+
* @param diffResult The diff result from JsonDiff containing path-value pairs of changes.
|
|
46
|
+
*/
|
|
10
47
|
function ApplyDiff(rootNode: any, diffResult: JsonDiffResult): void;
|
|
48
|
+
/**
|
|
49
|
+
* Creates a factory function for making values observable with optional aliasing.
|
|
50
|
+
* The alias function transforms values before creating the observable proxy,
|
|
51
|
+
* useful for read-only views or value transformations.
|
|
52
|
+
* @param alias Optional function to transform values before making them observable.
|
|
53
|
+
* @returns A function that creates observable nodes from plain values.
|
|
54
|
+
*/
|
|
11
55
|
function CreateFactory(alias?: (value: any) => any | undefined): <T>(value: T) => T;
|
|
12
56
|
}
|
|
@@ -4,10 +4,21 @@ exports.ObservableNode = exports.GET_TO_JSON = exports.GET_OBSERVABLE_VALUE = ex
|
|
|
4
4
|
const json_1 = require("../../Utils/json");
|
|
5
5
|
const jsonType_1 = require("../../Utils/jsonType");
|
|
6
6
|
const observableScope_1 = require("./observableScope");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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");
|
|
11
22
|
const proxyCache = new WeakMap();
|
|
12
23
|
const scopeCache = new WeakMap();
|
|
13
24
|
const leafScopeCache = new WeakMap();
|
|
@@ -40,7 +51,7 @@ function ownKeysArray(value) {
|
|
|
40
51
|
function UnwrapProxy(value, type = (0, jsonType_1.JsonType)(value)) {
|
|
41
52
|
if (type === "value")
|
|
42
53
|
return value;
|
|
43
|
-
if (value[exports.IS_OBSERVABLE_NODE]
|
|
54
|
+
if (value[exports.IS_OBSERVABLE_NODE])
|
|
44
55
|
return value[exports.GET_OBSERVABLE_VALUE];
|
|
45
56
|
switch (type) {
|
|
46
57
|
case "object": {
|
|
@@ -114,8 +125,6 @@ function CreateProxyFactory(alias) {
|
|
|
114
125
|
function ArrayProxySetter(array, prop, value) {
|
|
115
126
|
if (readOnly)
|
|
116
127
|
throw `Object is readonly`;
|
|
117
|
-
if (prop === exports.IS_OBSERVABLE_NODE)
|
|
118
|
-
throw `Cannot assign read-only property: ${exports.IS_OBSERVABLE_NODE}`;
|
|
119
128
|
value = UnwrapProxy(value);
|
|
120
129
|
array[prop] = value;
|
|
121
130
|
const scope = scopeCache.get(array);
|
|
@@ -147,8 +156,6 @@ function CreateProxyFactory(alias) {
|
|
|
147
156
|
const scope = scopeCache.get(array);
|
|
148
157
|
array = observableScope_1.ObservableScope.Value(scope);
|
|
149
158
|
const arrayValue = array[prop];
|
|
150
|
-
if (bypassProxy)
|
|
151
|
-
return arrayValue;
|
|
152
159
|
if (typeof prop === "symbol")
|
|
153
160
|
return arrayValue;
|
|
154
161
|
if (typeof arrayValue === "function")
|
|
@@ -188,8 +195,6 @@ function CreateProxyFactory(alias) {
|
|
|
188
195
|
function ObjectProxySetter(object, prop, value) {
|
|
189
196
|
if (readOnly)
|
|
190
197
|
throw `Object is readonly`;
|
|
191
|
-
if (prop === exports.IS_OBSERVABLE_NODE)
|
|
192
|
-
throw `Cannot assign read-only property: ${exports.IS_OBSERVABLE_NODE}`;
|
|
193
198
|
const jsonType = (0, jsonType_1.JsonType)(value);
|
|
194
199
|
if (jsonType === "value") {
|
|
195
200
|
value !== object[prop] && SetPropertyValue(object, prop, value);
|
|
@@ -224,8 +229,6 @@ function CreateProxyFactory(alias) {
|
|
|
224
229
|
case exports.GET_OBSERVABLE_VALUE:
|
|
225
230
|
return object;
|
|
226
231
|
default: {
|
|
227
|
-
if (bypassProxy)
|
|
228
|
-
return object[prop];
|
|
229
232
|
return GetAccessorValue(object, prop);
|
|
230
233
|
}
|
|
231
234
|
}
|
|
@@ -264,18 +267,34 @@ function CreateProxyFactory(alias) {
|
|
|
264
267
|
const DefaultCreateProxy = CreateProxyFactory();
|
|
265
268
|
var ObservableNode;
|
|
266
269
|
(function (ObservableNode) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
270
|
+
/**
|
|
271
|
+
* Unwraps an observable node to get the raw underlying value.
|
|
272
|
+
* Recursively unwraps nested objects and arrays.
|
|
273
|
+
* @template T The type of value to unwrap.
|
|
274
|
+
* @param value The value to unwrap, which may be an observable node or plain value.
|
|
275
|
+
* @returns The unwrapped raw value without proxy wrappers.
|
|
276
|
+
*/
|
|
271
277
|
function Unwrap(value) {
|
|
272
278
|
return UnwrapProxy(value);
|
|
273
279
|
}
|
|
274
280
|
ObservableNode.Unwrap = Unwrap;
|
|
281
|
+
/**
|
|
282
|
+
* Creates an observable node from a plain value.
|
|
283
|
+
* Wraps the value in a proxy that tracks changes and enables reactive updates.
|
|
284
|
+
* @template T The type of value to wrap.
|
|
285
|
+
* @param value The plain value (object, array, or primitive) to make observable.
|
|
286
|
+
* @returns A proxied version of the value that emits change events.
|
|
287
|
+
*/
|
|
275
288
|
function Create(value) {
|
|
276
289
|
return DefaultCreateProxy(value);
|
|
277
290
|
}
|
|
278
291
|
ObservableNode.Create = Create;
|
|
292
|
+
/**
|
|
293
|
+
* Marks an observable node or its property as changed, triggering reactive updates.
|
|
294
|
+
* Used internally to notify dependencies that a value has been modified.
|
|
295
|
+
* @param value The observable node to touch.
|
|
296
|
+
* @param prop Optional property name or index to touch a specific nested property.
|
|
297
|
+
*/
|
|
279
298
|
function Touch(value, prop) {
|
|
280
299
|
let scope;
|
|
281
300
|
if (prop !== undefined) {
|
|
@@ -286,6 +305,12 @@ var ObservableNode;
|
|
|
286
305
|
observableScope_1.ObservableScope.Update(scope);
|
|
287
306
|
}
|
|
288
307
|
ObservableNode.Touch = Touch;
|
|
308
|
+
/**
|
|
309
|
+
* Applies a JSON diff result to an observable node, efficiently updating only changed properties.
|
|
310
|
+
* Optimizes nested object updates by computing paths incrementally and touching modified properties.
|
|
311
|
+
* @param rootNode The observable node to apply the diff to.
|
|
312
|
+
* @param diffResult The diff result from JsonDiff containing path-value pairs of changes.
|
|
313
|
+
*/
|
|
289
314
|
function ApplyDiff(rootNode, diffResult) {
|
|
290
315
|
const root = rootNode[exports.GET_OBSERVABLE_VALUE];
|
|
291
316
|
const pathTuples = [["", root]];
|
|
@@ -312,6 +337,13 @@ var ObservableNode;
|
|
|
312
337
|
}
|
|
313
338
|
}
|
|
314
339
|
ObservableNode.ApplyDiff = ApplyDiff;
|
|
340
|
+
/**
|
|
341
|
+
* Creates a factory function for making values observable with optional aliasing.
|
|
342
|
+
* The alias function transforms values before creating the observable proxy,
|
|
343
|
+
* useful for read-only views or value transformations.
|
|
344
|
+
* @param alias Optional function to transform values before making them observable.
|
|
345
|
+
* @returns A function that creates observable nodes from plain values.
|
|
346
|
+
*/
|
|
315
347
|
function CreateFactory(alias) {
|
|
316
348
|
return CreateProxyFactory(alias);
|
|
317
349
|
}
|
|
@@ -48,6 +48,10 @@ function CreateStaticScope(initialValue) {
|
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
let scopeQueue = [];
|
|
51
|
+
/**
|
|
52
|
+
* Processes all queued scopes, recomputing dirty scopes and emitting changes.
|
|
53
|
+
* Executed as a microtask to batch updates efficiently.
|
|
54
|
+
*/
|
|
51
55
|
function ProcessScopeQueue() {
|
|
52
56
|
const queue = scopeQueue;
|
|
53
57
|
scopeQueue = [];
|
|
@@ -61,6 +65,11 @@ function ProcessScopeQueue() {
|
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Queues a scope for batched update processing.
|
|
70
|
+
* Schedules ProcessScopeQueue as a microtask if the queue was empty.
|
|
71
|
+
* @param scope The scope to queue for update.
|
|
72
|
+
*/
|
|
64
73
|
function OnSetQueued(scope) {
|
|
65
74
|
if (scopeQueue.length === 0)
|
|
66
75
|
queueMicrotask(ProcessScopeQueue);
|
|
@@ -84,6 +93,13 @@ function OnSet(scope) {
|
|
|
84
93
|
emitter_1.Emitter.Emit(scope.emitter, scope);
|
|
85
94
|
return false;
|
|
86
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Registers an emitter using SAME_STRATEGY.
|
|
98
|
+
* If the emitter is already registered at the current index, increments the index.
|
|
99
|
+
* Otherwise, switches to PUSH_STRATEGY and appends the emitter.
|
|
100
|
+
* @param state The current watch state.
|
|
101
|
+
* @param emitter The emitter to register.
|
|
102
|
+
*/
|
|
87
103
|
function RegisterSame(state, emitter) {
|
|
88
104
|
if (state.emitterIndex < state.emitters.length &&
|
|
89
105
|
state.emitters[state.emitterIndex] === emitter) {
|
|
@@ -91,10 +107,30 @@ function RegisterSame(state, emitter) {
|
|
|
91
107
|
return;
|
|
92
108
|
}
|
|
93
109
|
state.emitters = state.emitters.slice(0, state.emitterIndex);
|
|
94
|
-
state.
|
|
95
|
-
state
|
|
96
|
-
state.strategy = PUSH_STRATEGY;
|
|
110
|
+
state.strategy = SORTED_STRATEGY;
|
|
111
|
+
RegisterSorted(state, emitter);
|
|
97
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Registers an emitter while maintaining sorted order by emitter ID.
|
|
115
|
+
* If insertion would break sort order, falls back to PUSH_STRATEGY.
|
|
116
|
+
* @param state The current watch state.
|
|
117
|
+
* @param emitter The emitter to register.
|
|
118
|
+
*/
|
|
119
|
+
function RegisterSorted(state, emitter) {
|
|
120
|
+
if (state.emitters.length === 0 ||
|
|
121
|
+
state.emitters[state.emitters.length - 1][0] < emitter[0])
|
|
122
|
+
state.emitters.push(emitter);
|
|
123
|
+
else {
|
|
124
|
+
state.strategy = PUSH_STRATEGY;
|
|
125
|
+
RegisterPush(state, emitter);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Registers an emitter using PUSH_STRATEGY.
|
|
130
|
+
* Switches to DISTINCT_STRATEGY when the emitter count exceeds 50 to optimize memory.
|
|
131
|
+
* @param state The current watch state.
|
|
132
|
+
* @param emitter The emitter to register.
|
|
133
|
+
*/
|
|
98
134
|
function RegisterPush(state, emitter) {
|
|
99
135
|
state.emitters.push(emitter);
|
|
100
136
|
if (state.emitters.length > 50) {
|
|
@@ -112,12 +148,23 @@ function RegisterPush(state, emitter) {
|
|
|
112
148
|
state.strategy = DISTINCT_STRATEGY;
|
|
113
149
|
}
|
|
114
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Registers an emitter using DISTINCT_STRATEGY.
|
|
153
|
+
* Only adds emitters that haven't been registered yet, using an ID set for O(1) lookup.
|
|
154
|
+
* @param state The current watch state.
|
|
155
|
+
* @param emitter The emitter to register.
|
|
156
|
+
*/
|
|
115
157
|
function RegisterDistinct(state, emitter) {
|
|
116
158
|
if (!state.emitterIds.has(emitter[0])) {
|
|
117
159
|
state.emitters.push(emitter);
|
|
118
160
|
state.emitterIds.add(emitter[0]);
|
|
119
161
|
}
|
|
120
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Routes an emitter to the appropriate registration strategy based on current watch state.
|
|
165
|
+
* Does nothing if not within a watch context.
|
|
166
|
+
* @param emitter The emitter to register.
|
|
167
|
+
*/
|
|
121
168
|
function RegisterEmitter(emitter) {
|
|
122
169
|
if (watchState === null)
|
|
123
170
|
return;
|
|
@@ -125,6 +172,9 @@ function RegisterEmitter(emitter) {
|
|
|
125
172
|
case SAME_STRATEGY:
|
|
126
173
|
RegisterSame(watchState, emitter);
|
|
127
174
|
break;
|
|
175
|
+
case SORTED_STRATEGY:
|
|
176
|
+
RegisterSorted(watchState, emitter);
|
|
177
|
+
break;
|
|
128
178
|
case PUSH_STRATEGY:
|
|
129
179
|
RegisterPush(watchState, emitter);
|
|
130
180
|
break;
|
|
@@ -154,12 +204,22 @@ function GetScopeValue(scope) {
|
|
|
154
204
|
ExecuteScope(scope);
|
|
155
205
|
return scope.value;
|
|
156
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Strategy constants for optimizing emitter registration during watch operations.
|
|
209
|
+
* Each strategy represents a different approach to tracking and deduplicating dependencies.
|
|
210
|
+
*/
|
|
157
211
|
const SAME_STRATEGY = 1;
|
|
158
212
|
const PUSH_STRATEGY = 2;
|
|
159
|
-
const
|
|
160
|
-
const
|
|
213
|
+
const SORTED_STRATEGY = 3;
|
|
214
|
+
const DISTINCT_STRATEGY = 4;
|
|
215
|
+
const SHRINK_STRATEGY = 5;
|
|
161
216
|
let watchState = null;
|
|
162
217
|
const watchPool = list_1.List.Create();
|
|
218
|
+
/**
|
|
219
|
+
* Creates a new watch state object, reusing from pool if available.
|
|
220
|
+
* Object pooling reduces GC pressure during frequent watch operations.
|
|
221
|
+
* @returns A new or recycled watch state object.
|
|
222
|
+
*/
|
|
163
223
|
function CreateWatchState() {
|
|
164
224
|
return (list_1.List.Pop(watchPool) ?? {
|
|
165
225
|
emitterIndex: 0,
|
|
@@ -171,6 +231,11 @@ function CreateWatchState() {
|
|
|
171
231
|
strategy: PUSH_STRATEGY,
|
|
172
232
|
});
|
|
173
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Returns a watch state object to the pool for reuse.
|
|
236
|
+
* Resets all fields to their default values before pooling.
|
|
237
|
+
* @param state The watch state to return to the pool.
|
|
238
|
+
*/
|
|
174
239
|
function ReturnWatchState(state) {
|
|
175
240
|
state.emitterIndex = 0;
|
|
176
241
|
state.value = null;
|
|
@@ -178,7 +243,7 @@ function ReturnWatchState(state) {
|
|
|
178
243
|
state.emitterIds = null;
|
|
179
244
|
state.currentCalc = null;
|
|
180
245
|
state.nextCalc = null;
|
|
181
|
-
state.strategy =
|
|
246
|
+
state.strategy = SORTED_STRATEGY;
|
|
182
247
|
list_1.List.Push(watchPool, state);
|
|
183
248
|
}
|
|
184
249
|
/**
|
|
@@ -304,6 +369,7 @@ function UpdateEmitters(scope, right, strategy) {
|
|
|
304
369
|
strategy === PUSH_STRATEGY
|
|
305
370
|
? emitter_1.Emitter.Distinct(right)
|
|
306
371
|
: emitter_1.Emitter.Sort(right);
|
|
372
|
+
case SORTED_STRATEGY:
|
|
307
373
|
if (scope.emitters === null || scope.emitters.length === 0) {
|
|
308
374
|
for (let x = 0; x < right.length; x++)
|
|
309
375
|
emitter_1.Emitter.On(right[x], scope.setCallback);
|