j-templates 7.0.104 → 8.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.
- package/Node/vNode.js +13 -24
- package/Utils/array.d.ts +18 -0
- package/Utils/array.js +34 -0
- package/Utils/decorators.d.ts +13 -9
- package/Utils/decorators.js +41 -13
- package/_not_used/array.d.ts +0 -18
- package/_not_used/array.js +0 -31
- package/package.json +1 -1
package/Node/vNode.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.vNode = void 0;
|
|
4
4
|
const Store_1 = require("../Store");
|
|
5
5
|
const observableScope_1 = require("../Store/Tree/observableScope");
|
|
6
|
+
const array_1 = require("../Utils/array");
|
|
6
7
|
const emitter_1 = require("../Utils/emitter");
|
|
7
8
|
const functions_1 = require("../Utils/functions");
|
|
8
9
|
const injector_1 = require("../Utils/injector");
|
|
@@ -152,8 +153,10 @@ function Children(vnode, children, data) {
|
|
|
152
153
|
Store_1.ObservableScope.Watch(childrenScope, CreateScheduledCallback(function (scope) {
|
|
153
154
|
if (vnode.destroyed)
|
|
154
155
|
return;
|
|
156
|
+
const oldChildrenLength = vnode.children.length;
|
|
155
157
|
vnode.children = Store_1.ObservableScope.Value(scope);
|
|
156
|
-
|
|
158
|
+
if (oldChildrenLength !== 0 || vnode.children.length !== 0)
|
|
159
|
+
UpdateChildren(vnode);
|
|
157
160
|
}));
|
|
158
161
|
vnode.children = Store_1.ObservableScope.Value(childrenScope);
|
|
159
162
|
}
|
|
@@ -204,32 +207,18 @@ function ToArray(result) {
|
|
|
204
207
|
return result;
|
|
205
208
|
return [result];
|
|
206
209
|
}
|
|
207
|
-
function
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
function FlattenFragmentsRecursive(children, startIndex, result) {
|
|
214
|
-
for (let x = startIndex; x < children.length; x++) {
|
|
215
|
-
const vnode = children[x];
|
|
216
|
-
switch (vnode.type) {
|
|
217
|
-
case vNode_types_1.FRAGMENT_NODE:
|
|
218
|
-
FlattenFragmentsRecursive(vnode.children, 0, result);
|
|
219
|
-
default:
|
|
220
|
-
if (vnode.node !== null)
|
|
221
|
-
result.push(vnode.node);
|
|
222
|
-
}
|
|
210
|
+
function MapNode(vnode) {
|
|
211
|
+
switch (vnode.type) {
|
|
212
|
+
case vNode_types_1.FRAGMENT_NODE:
|
|
213
|
+
return vnode.children.flatMap(MapNode);
|
|
214
|
+
default:
|
|
215
|
+
return vnode.node;
|
|
223
216
|
}
|
|
224
|
-
return result;
|
|
225
217
|
}
|
|
226
218
|
function CleanupChildrenArray(children) {
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const result = children.slice(0, index).map(GetNode);
|
|
231
|
-
FlattenFragmentsRecursive(children, index, result);
|
|
232
|
-
return result;
|
|
219
|
+
const nodes = children.flatMap(MapNode);
|
|
220
|
+
(0, array_1.RemoveNulls)(nodes);
|
|
221
|
+
return nodes;
|
|
233
222
|
}
|
|
234
223
|
function UpdateChildren(vnode, init = false, skipInit = false) {
|
|
235
224
|
if (!vnode.children)
|
package/Utils/array.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes all null values from an array starting from a specified index.
|
|
3
|
+
* This function modifies the array in-place by shifting non-null elements
|
|
4
|
+
* to fill the gaps left by removed null values, then truncating the array.
|
|
5
|
+
*
|
|
6
|
+
* @param array - The array from which to remove null values. Can contain mixed types including null.
|
|
7
|
+
* @param startIndex - The index to start removing null values from (default: 0).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const arr = [1, null, 2, null, 3, null, 4];
|
|
12
|
+
* RemoveNulls(arr); // Removes all null values, result: [1, 2, 3, 4]
|
|
13
|
+
*
|
|
14
|
+
* const arr2 = [null, null, 1, null, 2];
|
|
15
|
+
* RemoveNulls(arr2, 2); // Starts from index 2, result: [null, null, 1, 2]
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function RemoveNulls(array: (unknown | null)[], startIndex?: number): void;
|
package/Utils/array.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RemoveNulls = RemoveNulls;
|
|
4
|
+
/**
|
|
5
|
+
* Removes all null values from an array starting from a specified index.
|
|
6
|
+
* This function modifies the array in-place by shifting non-null elements
|
|
7
|
+
* to fill the gaps left by removed null values, then truncating the array.
|
|
8
|
+
*
|
|
9
|
+
* @param array - The array from which to remove null values. Can contain mixed types including null.
|
|
10
|
+
* @param startIndex - The index to start removing null values from (default: 0).
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const arr = [1, null, 2, null, 3, null, 4];
|
|
15
|
+
* RemoveNulls(arr); // Removes all null values, result: [1, 2, 3, 4]
|
|
16
|
+
*
|
|
17
|
+
* const arr2 = [null, null, 1, null, 2];
|
|
18
|
+
* RemoveNulls(arr2, 2); // Starts from index 2, result: [null, null, 1, 2]
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
function RemoveNulls(array, startIndex = 0) {
|
|
22
|
+
let nullIndex = startIndex;
|
|
23
|
+
for (; nullIndex < array.length && array[nullIndex] !== null; nullIndex++) { }
|
|
24
|
+
let notNullIndex = nullIndex + 1;
|
|
25
|
+
for (; notNullIndex < array.length && array[notNullIndex] === null; notNullIndex++) { }
|
|
26
|
+
while (notNullIndex < array.length) {
|
|
27
|
+
array[nullIndex] = array[notNullIndex];
|
|
28
|
+
nullIndex++;
|
|
29
|
+
notNullIndex++;
|
|
30
|
+
for (; notNullIndex < array.length && array[notNullIndex] === null; notNullIndex++) { }
|
|
31
|
+
}
|
|
32
|
+
if (nullIndex < array.length)
|
|
33
|
+
array.splice(nullIndex);
|
|
34
|
+
}
|
package/Utils/decorators.d.ts
CHANGED
|
@@ -88,7 +88,7 @@ import { Injector } from "./injector";
|
|
|
88
88
|
* @Value()
|
|
89
89
|
* lastName: string = "Doe";
|
|
90
90
|
*
|
|
91
|
-
* @Computed() // Overhead:
|
|
91
|
+
* @Computed() // Overhead: observable node, watch cycle, diff computation
|
|
92
92
|
* get fullName(): string {
|
|
93
93
|
* return this.firstName + " " + this.lastName; // Cheap string concat
|
|
94
94
|
* }
|
|
@@ -99,15 +99,18 @@ import { Injector } from "./injector";
|
|
|
99
99
|
* }
|
|
100
100
|
* ```
|
|
101
101
|
*
|
|
102
|
-
* @param defaultValue The default value to be used if the computed property is not defined.
|
|
103
102
|
* @returns A property decorator that can be applied to a getter method.
|
|
104
103
|
* @throws Will throw an error if the property is not a getter or if it has a setter.
|
|
105
104
|
* @remarks
|
|
106
|
-
* The @Computed decorator uses a
|
|
107
|
-
* 1. Getter scope:
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
105
|
+
* The @Computed decorator uses a Gated getter scope driving a persistent observable node:
|
|
106
|
+
* 1. Getter scope (Gated): Re-evaluates the getter when dependencies change, batched via
|
|
107
|
+
* the microtask queue. If the result deep-equals the previous value, no update is emitted.
|
|
108
|
+
* 2. ObservableNode.Apply: Diffs the fresh value against the node's current root and applies
|
|
109
|
+
* only the changed paths in-place, preserving the node's object identity.
|
|
110
|
+
* 3. Downstream notification: Per-property scopes are fired for each changed path, so scopes
|
|
111
|
+
* reading sub-properties update directly. If the diff collapses to a full root replacement
|
|
112
|
+
* (type change or removed keys), the root value gets a new identity and the Gated scope
|
|
113
|
+
* emits, notifying top-level subscribers.
|
|
111
114
|
*
|
|
112
115
|
* **Initialization**: @Computed uses lazy initialization - the scopes are created on first access:
|
|
113
116
|
* ```typescript
|
|
@@ -152,8 +155,9 @@ import { Injector } from "./injector";
|
|
|
152
155
|
* - The diff computation overhead is justified when you need object reuse
|
|
153
156
|
* @see {@link Scope} for simple getter-based reactive properties (caches but new reference)
|
|
154
157
|
* @see {@link ComputedAsync} for async computed properties
|
|
155
|
-
* @see {@link ObservableNode.
|
|
156
|
-
* @see {@link
|
|
158
|
+
* @see {@link ObservableNode.Apply} for how diffs are applied in-place to maintain object identity
|
|
159
|
+
* @see {@link ObservableScope} for the Gated scope mechanism (batched, ===-gated updates)
|
|
160
|
+
* @see {@link StoreSync} for the store-backed variant used by @ComputedAsync
|
|
157
161
|
*/
|
|
158
162
|
export declare function Computed<T extends WeakKey, K extends keyof T, D extends T[K]>(): (target: T, propertyKey: K, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
159
163
|
/**
|
package/Utils/decorators.js
CHANGED
|
@@ -133,7 +133,7 @@ function GetDestroyArrayForPrototype(prototype, create = true) {
|
|
|
133
133
|
}
|
|
134
134
|
return array;
|
|
135
135
|
}
|
|
136
|
-
function
|
|
136
|
+
function CreateStoreScope(getter, store, defaultValue) {
|
|
137
137
|
const getterScope = observableScope_1.ObservableScope.Gated(function () {
|
|
138
138
|
const value = getter();
|
|
139
139
|
return observableNode_1.ObservableNode.Clone(value);
|
|
@@ -154,6 +154,31 @@ function CreateComputedScope(getter, store, defaultValue) {
|
|
|
154
154
|
});
|
|
155
155
|
return propertyScope;
|
|
156
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Creates a Gated observable scope that exposes a getter's value through a persistent
|
|
159
|
+
* observable node, preserving the value's object identity across updates.
|
|
160
|
+
*
|
|
161
|
+
* On each re-evaluation the getter's value is cloned and merged into the node in-place
|
|
162
|
+
* via ObservableNode.Apply:
|
|
163
|
+
* - Content changes (same structure, changed properties) fire only the affected
|
|
164
|
+
* per-property scopes - the top-level scope does not emit.
|
|
165
|
+
* - Structural changes (type change or removed keys) replace the root value with a new
|
|
166
|
+
* identity, which the Gated scope detects and emits for.
|
|
167
|
+
* - Deep-equal results are suppressed entirely by the Gated ===-gating.
|
|
168
|
+
*
|
|
169
|
+
* @param getter Function computing the value. May read reactive dependencies.
|
|
170
|
+
* @returns The Gated scope whose value is the observable root node.
|
|
171
|
+
*/
|
|
172
|
+
function CreateNodeScope(getter) {
|
|
173
|
+
const observableNode = observableNode_1.ObservableNode.Create({ root: null });
|
|
174
|
+
const getterScope = observableScope_1.ObservableScope.Gated(function () {
|
|
175
|
+
const value = getter();
|
|
176
|
+
const clonedValue = observableNode_1.ObservableNode.Clone(value);
|
|
177
|
+
observableNode_1.ObservableNode.Apply(observableNode, { root: clonedValue });
|
|
178
|
+
return observableNode.root;
|
|
179
|
+
});
|
|
180
|
+
return getterScope;
|
|
181
|
+
}
|
|
157
182
|
/**
|
|
158
183
|
* Computed decorator factory for creating synchronous computed properties with caching and object reuse.
|
|
159
184
|
* A computed property is derived from other properties and automatically updates when its dependencies change.
|
|
@@ -184,7 +209,7 @@ function CreateComputedScope(getter, store, defaultValue) {
|
|
|
184
209
|
* @Value()
|
|
185
210
|
* lastName: string = "Doe";
|
|
186
211
|
*
|
|
187
|
-
* @Computed() // Overhead:
|
|
212
|
+
* @Computed() // Overhead: observable node, watch cycle, diff computation
|
|
188
213
|
* get fullName(): string {
|
|
189
214
|
* return this.firstName + " " + this.lastName; // Cheap string concat
|
|
190
215
|
* }
|
|
@@ -195,15 +220,18 @@ function CreateComputedScope(getter, store, defaultValue) {
|
|
|
195
220
|
* }
|
|
196
221
|
* ```
|
|
197
222
|
*
|
|
198
|
-
* @param defaultValue The default value to be used if the computed property is not defined.
|
|
199
223
|
* @returns A property decorator that can be applied to a getter method.
|
|
200
224
|
* @throws Will throw an error if the property is not a getter or if it has a setter.
|
|
201
225
|
* @remarks
|
|
202
|
-
* The @Computed decorator uses a
|
|
203
|
-
* 1. Getter scope:
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
226
|
+
* The @Computed decorator uses a Gated getter scope driving a persistent observable node:
|
|
227
|
+
* 1. Getter scope (Gated): Re-evaluates the getter when dependencies change, batched via
|
|
228
|
+
* the microtask queue. If the result deep-equals the previous value, no update is emitted.
|
|
229
|
+
* 2. ObservableNode.Apply: Diffs the fresh value against the node's current root and applies
|
|
230
|
+
* only the changed paths in-place, preserving the node's object identity.
|
|
231
|
+
* 3. Downstream notification: Per-property scopes are fired for each changed path, so scopes
|
|
232
|
+
* reading sub-properties update directly. If the diff collapses to a full root replacement
|
|
233
|
+
* (type change or removed keys), the root value gets a new identity and the Gated scope
|
|
234
|
+
* emits, notifying top-level subscribers.
|
|
207
235
|
*
|
|
208
236
|
* **Initialization**: @Computed uses lazy initialization - the scopes are created on first access:
|
|
209
237
|
* ```typescript
|
|
@@ -248,8 +276,9 @@ function CreateComputedScope(getter, store, defaultValue) {
|
|
|
248
276
|
* - The diff computation overhead is justified when you need object reuse
|
|
249
277
|
* @see {@link Scope} for simple getter-based reactive properties (caches but new reference)
|
|
250
278
|
* @see {@link ComputedAsync} for async computed properties
|
|
251
|
-
* @see {@link ObservableNode.
|
|
252
|
-
* @see {@link
|
|
279
|
+
* @see {@link ObservableNode.Apply} for how diffs are applied in-place to maintain object identity
|
|
280
|
+
* @see {@link ObservableScope} for the Gated scope mechanism (batched, ===-gated updates)
|
|
281
|
+
* @see {@link StoreSync} for the store-backed variant used by @ComputedAsync
|
|
253
282
|
*/
|
|
254
283
|
function Computed() {
|
|
255
284
|
return function (target, propertyKey, descriptor) {
|
|
@@ -262,7 +291,6 @@ function Computed() {
|
|
|
262
291
|
* @param target The target object.
|
|
263
292
|
* @param prop The property key.
|
|
264
293
|
* @param descriptor The property descriptor.
|
|
265
|
-
* @param defaultValue The default value to be used if the computed property is not defined.
|
|
266
294
|
* @returns A property descriptor that replaces the original descriptor with a computed implementation.
|
|
267
295
|
* @throws Will throw an error if the property is not a getter or if it has a setter.
|
|
268
296
|
*/
|
|
@@ -279,7 +307,7 @@ function ComputedDecorator(target, prop, descriptor) {
|
|
|
279
307
|
get: function () {
|
|
280
308
|
const scopeMap = GetScopeMapForInstance(this);
|
|
281
309
|
if (scopeMap[propertyKey] === undefined) {
|
|
282
|
-
const propertyScope =
|
|
310
|
+
const propertyScope = CreateNodeScope(getter.bind(this));
|
|
283
311
|
scopeMap[propertyKey] = [propertyScope, undefined];
|
|
284
312
|
}
|
|
285
313
|
return observableScope_1.ObservableScope.Value(scopeMap[propertyKey][0]);
|
|
@@ -405,7 +433,7 @@ function ComputedAsyncDecorator(target, prop, descriptor, defaultValue) {
|
|
|
405
433
|
get: function () {
|
|
406
434
|
const scopeMap = GetScopeMapForInstance(this);
|
|
407
435
|
if (scopeMap[propertyKey] === undefined) {
|
|
408
|
-
const propertyScope =
|
|
436
|
+
const propertyScope = CreateStoreScope(getter.bind(this), new Store_1.StoreAsync(), defaultValue);
|
|
409
437
|
scopeMap[propertyKey] = [propertyScope, undefined];
|
|
410
438
|
}
|
|
411
439
|
return observableScope_1.ObservableScope.Value(scopeMap[propertyKey][0]);
|
package/_not_used/array.d.ts
CHANGED
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Removes all null values from an array starting from a specified index.
|
|
3
|
-
* This function modifies the array in-place by shifting non-null elements
|
|
4
|
-
* to fill the gaps left by removed null values, then truncating the array.
|
|
5
|
-
*
|
|
6
|
-
* @param array - The array from which to remove null values. Can contain mixed types including null.
|
|
7
|
-
* @param startIndex - The index to start removing null values from (default: 0).
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* const arr = [1, null, 2, null, 3, null, 4];
|
|
12
|
-
* RemoveNulls(arr); // Removes all null values, result: [1, 2, 3, 4]
|
|
13
|
-
*
|
|
14
|
-
* const arr2 = [null, null, 1, null, 2];
|
|
15
|
-
* RemoveNulls(arr2, 2); // Starts from index 2, result: [null, null, 1, 2]
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
export declare function RemoveNulls(array: (unknown | null)[], startIndex?: number): void;
|
|
19
1
|
export declare function ArrayDiff(source: any[], target: any[]): boolean;
|
|
20
2
|
/**
|
|
21
3
|
* Reconciles two sorted arrays by applying add and remove operations to transition
|
package/_not_used/array.js
CHANGED
|
@@ -1,40 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RemoveNulls = RemoveNulls;
|
|
4
3
|
exports.ArrayDiff = ArrayDiff;
|
|
5
4
|
exports.ReconcileSortedEmitters = ReconcileSortedEmitters;
|
|
6
5
|
exports.InsertionSortTuples = InsertionSortTuples;
|
|
7
6
|
exports.ReconcileSortedArrays = ReconcileSortedArrays;
|
|
8
|
-
/**
|
|
9
|
-
* Removes all null values from an array starting from a specified index.
|
|
10
|
-
* This function modifies the array in-place by shifting non-null elements
|
|
11
|
-
* to fill the gaps left by removed null values, then truncating the array.
|
|
12
|
-
*
|
|
13
|
-
* @param array - The array from which to remove null values. Can contain mixed types including null.
|
|
14
|
-
* @param startIndex - The index to start removing null values from (default: 0).
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```typescript
|
|
18
|
-
* const arr = [1, null, 2, null, 3, null, 4];
|
|
19
|
-
* RemoveNulls(arr); // Removes all null values, result: [1, 2, 3, 4]
|
|
20
|
-
*
|
|
21
|
-
* const arr2 = [null, null, 1, null, 2];
|
|
22
|
-
* RemoveNulls(arr2, 2); // Starts from index 2, result: [null, null, 1, 2]
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
function RemoveNulls(array, startIndex = 0) {
|
|
26
|
-
let nullIndex = startIndex;
|
|
27
|
-
for (; nullIndex < array.length && array[nullIndex] !== null; nullIndex++) { }
|
|
28
|
-
let notNullIndex = nullIndex + 1;
|
|
29
|
-
for (; notNullIndex < array.length && array[notNullIndex] === null; notNullIndex++) { }
|
|
30
|
-
while (notNullIndex < array.length) {
|
|
31
|
-
array[nullIndex] = array[notNullIndex];
|
|
32
|
-
nullIndex++;
|
|
33
|
-
notNullIndex++;
|
|
34
|
-
for (; notNullIndex < array.length && array[notNullIndex] === null; notNullIndex++) { }
|
|
35
|
-
}
|
|
36
|
-
array.splice(nullIndex);
|
|
37
|
-
}
|
|
38
7
|
function ArrayDiff(source, target) {
|
|
39
8
|
if (source === target)
|
|
40
9
|
return false;
|