j-templates 7.0.69 → 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.
- package/DOM/domNodeConfig.js +5 -4
- package/Node/component.d.ts +0 -8
- package/Node/component.js +1 -42
- package/Node/vNode.js +1 -41
- package/Store/Tree/observableNode.d.ts +48 -4
- package/Store/Tree/observableNode.js +49 -17
- package/Store/Tree/observableScope.js +172 -64
- package/Utils/decorators.js +0 -10
- package/Utils/emitter.js +1 -19
- package/package.json +1 -1
package/DOM/domNodeConfig.js
CHANGED
|
@@ -169,9 +169,10 @@ exports.DOMNodeConfig = {
|
|
|
169
169
|
target.appendChild(children[x]);
|
|
170
170
|
},
|
|
171
171
|
reconcileChild(target, child) {
|
|
172
|
-
if (target.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
if (target.firstChild === child)
|
|
173
|
+
return;
|
|
174
|
+
target.appendChild(child);
|
|
175
|
+
while (target.firstChild !== child)
|
|
176
|
+
target.removeChild(target.firstChild);
|
|
176
177
|
},
|
|
177
178
|
};
|
package/Node/component.d.ts
CHANGED
|
@@ -38,14 +38,6 @@ export declare class Component<D = void, T = void, E = {}> {
|
|
|
38
38
|
* Accessor for the component's template collection.
|
|
39
39
|
*/
|
|
40
40
|
protected get Templates(): T;
|
|
41
|
-
/**
|
|
42
|
-
* Creates a new Component instance. Not intended to be overriden.
|
|
43
|
-
*
|
|
44
|
-
* @param data - Initial data or a factory function returning data/promise.
|
|
45
|
-
* @param templates - Template definitions for rendering.
|
|
46
|
-
* @param vNode - The underlying virtual node instance.
|
|
47
|
-
* @param componentEvents - Optional event callbacks.
|
|
48
|
-
*/
|
|
49
41
|
constructor(vNode: vNodeType, config: vComponentConfig<D, E>, templates: T);
|
|
50
42
|
/**
|
|
51
43
|
* Returns the component's rendered vNode(s).
|
package/Node/component.js
CHANGED
|
@@ -48,32 +48,9 @@ class Component {
|
|
|
48
48
|
get Templates() {
|
|
49
49
|
return this.templates;
|
|
50
50
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Creates a new Component instance. Not intended to be overriden.
|
|
53
|
-
*
|
|
54
|
-
* @param data - Initial data or a factory function returning data/promise.
|
|
55
|
-
* @param templates - Template definitions for rendering.
|
|
56
|
-
* @param vNode - The underlying virtual node instance.
|
|
57
|
-
* @param componentEvents - Optional event callbacks.
|
|
58
|
-
*/
|
|
59
|
-
/* constructor(
|
|
60
|
-
data: D | (() => D | Promise<D>),
|
|
61
|
-
templates: T,
|
|
62
|
-
private vNode: vNodeType,
|
|
63
|
-
private componentEvents: ComponentEvents<E>,
|
|
64
|
-
) {
|
|
65
|
-
if (typeof data === "function")
|
|
66
|
-
this.scope = new ObservableScope<D>(data as () => D | Promise<D>);
|
|
67
|
-
else this.scope = new ObservableScope<D>(() => data);
|
|
68
|
-
|
|
69
|
-
this.templates = templates || ({} as T);
|
|
70
|
-
} */
|
|
71
51
|
constructor(vNode, config, templates) {
|
|
72
52
|
this.vNode = vNode;
|
|
73
53
|
const { data, on } = config;
|
|
74
|
-
/* if (typeof data === "function")
|
|
75
|
-
this.scope = new ObservableScope<D>(data as () => D | Promise<D>);
|
|
76
|
-
else this.scope = new ObservableScope<D>(() => data); */
|
|
77
54
|
if (typeof data === "function")
|
|
78
55
|
this.scope = Store_1.ObservableScope.Create(data);
|
|
79
56
|
else
|
|
@@ -119,30 +96,13 @@ class Component {
|
|
|
119
96
|
}
|
|
120
97
|
}
|
|
121
98
|
exports.Component = Component;
|
|
122
|
-
/* type ComponentConstructor<D, T, E> = {
|
|
123
|
-
new (
|
|
124
|
-
data: D | (() => D | Promise<D>),
|
|
125
|
-
templates: T,
|
|
126
|
-
vNode: vNodeType,
|
|
127
|
-
componentEvents: ComponentEvents<E>,
|
|
128
|
-
): Component<D, T, E>;
|
|
129
|
-
}; */
|
|
130
99
|
(function (Component) {
|
|
131
100
|
/**
|
|
132
101
|
* Function wraps the Component as a function that can be used to create vNode objects
|
|
133
102
|
* and generate templates.
|
|
134
103
|
*/
|
|
135
|
-
function ToFunction(type,
|
|
136
|
-
// constructor: ComponentConstructor<D, T, E>,
|
|
137
|
-
constructor, namespace) {
|
|
104
|
+
function ToFunction(type, constructor, namespace) {
|
|
138
105
|
return function (config, templates) {
|
|
139
|
-
/* const { data, on, props } = config;
|
|
140
|
-
|
|
141
|
-
class ConcreteComponent extends constructor {
|
|
142
|
-
constructor(vnode: vNodeType) {
|
|
143
|
-
super(data, templates, vnode, on);
|
|
144
|
-
}
|
|
145
|
-
} */
|
|
146
106
|
function ComponentFactory(vnode) {
|
|
147
107
|
return new constructor(vnode, config, templates);
|
|
148
108
|
}
|
|
@@ -151,7 +111,6 @@ exports.Component = Component;
|
|
|
151
111
|
namespace: namespace ?? null,
|
|
152
112
|
props: config.props,
|
|
153
113
|
componentFactory: ComponentFactory,
|
|
154
|
-
// componentConstructor: ConcreteComponent,
|
|
155
114
|
};
|
|
156
115
|
return vNode_1.vNode.Create(definition);
|
|
157
116
|
};
|
package/Node/vNode.js
CHANGED
|
@@ -14,7 +14,6 @@ var vNode;
|
|
|
14
14
|
return {
|
|
15
15
|
definition,
|
|
16
16
|
type: definition.type,
|
|
17
|
-
// injector: definition.componentConstructor
|
|
18
17
|
injector: definition.componentFactory
|
|
19
18
|
? injector_1.Injector.Scope(injector_1.Injector.Current(), function () {
|
|
20
19
|
return new injector_1.Injector();
|
|
@@ -95,9 +94,7 @@ var vNode;
|
|
|
95
94
|
vNode.Attach = Attach;
|
|
96
95
|
})(vNode || (exports.vNode = vNode = {}));
|
|
97
96
|
function InitNode(vnode) {
|
|
98
|
-
const { type, namespace, props, attrs, on, data,
|
|
99
|
-
// componentConstructor,
|
|
100
|
-
componentFactory, children, childrenArray, } = vnode.definition;
|
|
97
|
+
const { type, namespace, props, attrs, on, data, componentFactory, children, childrenArray, } = vnode.definition;
|
|
101
98
|
const node = (vnode.node =
|
|
102
99
|
vnode.definition.node ?? nodeConfig_1.NodeConfig.createNode(type, namespace));
|
|
103
100
|
vnode.definition = null;
|
|
@@ -105,11 +102,8 @@ function InitNode(vnode) {
|
|
|
105
102
|
const assignProperties = nodeConfig_1.NodeConfig.createPropertyAssignment(node);
|
|
106
103
|
if (typeof props === "function") {
|
|
107
104
|
const scope = Store_1.ObservableScope.Create(props);
|
|
108
|
-
// const [value, scope] = ObservableScope.CreateIf(props as () => any);
|
|
109
|
-
// if (scope) {
|
|
110
105
|
vnode.scopes.push(scope);
|
|
111
106
|
Store_1.ObservableScope.Watch(scope, ScheduledAssignment(assignProperties));
|
|
112
|
-
// }
|
|
113
107
|
assignProperties(Store_1.ObservableScope.Value(scope));
|
|
114
108
|
}
|
|
115
109
|
else
|
|
@@ -119,11 +113,8 @@ function InitNode(vnode) {
|
|
|
119
113
|
const assignEvents = nodeConfig_1.NodeConfig.createEventAssignment(node);
|
|
120
114
|
if (typeof on === "function") {
|
|
121
115
|
const scope = Store_1.ObservableScope.Create(on);
|
|
122
|
-
// const [value, scope] = ObservableScope.CreateIf(on);
|
|
123
|
-
// if (scope) {
|
|
124
116
|
vnode.scopes.push(scope);
|
|
125
117
|
Store_1.ObservableScope.Watch(scope, ScheduledAssignment(assignEvents));
|
|
126
|
-
// }
|
|
127
118
|
assignEvents(Store_1.ObservableScope.Value(scope));
|
|
128
119
|
}
|
|
129
120
|
else
|
|
@@ -133,19 +124,14 @@ function InitNode(vnode) {
|
|
|
133
124
|
const assignAttributes = nodeConfig_1.NodeConfig.createAttributeAssignment(node);
|
|
134
125
|
if (typeof attrs === "function") {
|
|
135
126
|
const scope = Store_1.ObservableScope.Create(attrs);
|
|
136
|
-
// const [value, scope] = ObservableScope.CreateIf(attrs);
|
|
137
|
-
// if (scope) {
|
|
138
127
|
vnode.scopes.push(scope);
|
|
139
128
|
Store_1.ObservableScope.Watch(scope, ScheduledAssignment(assignAttributes));
|
|
140
|
-
//}
|
|
141
129
|
assignAttributes(Store_1.ObservableScope.Value(scope));
|
|
142
130
|
}
|
|
143
131
|
else
|
|
144
132
|
assignAttributes(attrs);
|
|
145
133
|
}
|
|
146
|
-
// if (componentConstructor) {
|
|
147
134
|
if (componentFactory) {
|
|
148
|
-
// vnode.component = new componentConstructor(vnode);
|
|
149
135
|
vnode.component = componentFactory(vnode);
|
|
150
136
|
vnode.component.Bound();
|
|
151
137
|
function componentChildren() {
|
|
@@ -171,7 +157,6 @@ function Children(vnode, children, data) {
|
|
|
171
157
|
return;
|
|
172
158
|
const startChildren = vnode.children;
|
|
173
159
|
const newChildren = Store_1.ObservableScope.Value(scope);
|
|
174
|
-
// AssignChildren(vnode, scope);
|
|
175
160
|
if (startChildren !== newChildren) {
|
|
176
161
|
vnode.children = newChildren;
|
|
177
162
|
UpdateChildren(vnode);
|
|
@@ -179,21 +164,7 @@ function Children(vnode, children, data) {
|
|
|
179
164
|
}));
|
|
180
165
|
}
|
|
181
166
|
vnode.children = Store_1.ObservableScope.Value(childrenScope);
|
|
182
|
-
// AssignChildren(vnode, childrenScope);
|
|
183
167
|
}
|
|
184
|
-
/* function AssignChildren(
|
|
185
|
-
vnode: vNodeType,
|
|
186
|
-
childrenScope: IObservableScope<
|
|
187
|
-
[
|
|
188
|
-
any,
|
|
189
|
-
vNodeType[],
|
|
190
|
-
IObservableScope<string | vNodeType | vNodeType[]> | null,
|
|
191
|
-
][]
|
|
192
|
-
>,
|
|
193
|
-
) {
|
|
194
|
-
const children = ObservableScope.Peek(childrenScope);
|
|
195
|
-
vnode.children = children;
|
|
196
|
-
} */
|
|
197
168
|
const DEFAULT_DATA = [undefined];
|
|
198
169
|
function DefaultData() {
|
|
199
170
|
return DEFAULT_DATA;
|
|
@@ -302,7 +273,6 @@ function EvaluateNextNodesLarge(injector, getNextChildren, nextData, nodeArray)
|
|
|
302
273
|
if (currentChildIndex !== -1) {
|
|
303
274
|
const currentChild = currentChildren[currentChildIndex];
|
|
304
275
|
currentChildren[currentChildIndex] = null;
|
|
305
|
-
// if (currentChild[2]) {
|
|
306
276
|
const scope = currentChild[2];
|
|
307
277
|
const value = scope.value;
|
|
308
278
|
const updatedValue = Store_1.ObservableScope.Value(scope);
|
|
@@ -310,13 +280,7 @@ function EvaluateNextNodesLarge(injector, getNextChildren, nextData, nodeArray)
|
|
|
310
280
|
vNode.DestroyAll(currentChild[1]);
|
|
311
281
|
currentChild[1] = CreateNodeArray(updatedValue);
|
|
312
282
|
}
|
|
313
|
-
// }
|
|
314
|
-
/* if (currentChild[2]?.dirty) {
|
|
315
|
-
const nextChildren = ObservableScope.Value(currentChild[2]);
|
|
316
|
-
currentChild[1] = CreateNodeArray(nextChildren);
|
|
317
|
-
} */
|
|
318
283
|
nextNodes[x] = currentChild;
|
|
319
|
-
// currentChildren[currentChildIndex] = null;
|
|
320
284
|
if (currentChildIndex === 0)
|
|
321
285
|
dataMap.delete(data);
|
|
322
286
|
}
|
|
@@ -324,9 +288,6 @@ function EvaluateNextNodesLarge(injector, getNextChildren, nextData, nodeArray)
|
|
|
324
288
|
const scope = Store_1.ObservableScope.Create(function () {
|
|
325
289
|
return injector_1.Injector.Scope(injector, getNextChildren, data);
|
|
326
290
|
});
|
|
327
|
-
/* const [nextChildren, scope] = ObservableScope.CreateIf(function () {
|
|
328
|
-
return Injector.Scope(injector, getNextChildren, data);
|
|
329
|
-
}); */
|
|
330
291
|
nextNodes[x] = [
|
|
331
292
|
data,
|
|
332
293
|
CreateNodeArray(Store_1.ObservableScope.Value(scope)),
|
|
@@ -411,7 +372,6 @@ function ScheduledAssignment(assign) {
|
|
|
411
372
|
return;
|
|
412
373
|
scheduled = true;
|
|
413
374
|
nodeConfig_1.NodeConfig.scheduleUpdate(function () {
|
|
414
|
-
// if (scope.destroyed) return;
|
|
415
375
|
scheduled = false;
|
|
416
376
|
const value = Store_1.ObservableScope.Peek(scope);
|
|
417
377
|
assign(value);
|
|
@@ -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
|
}
|
|
@@ -5,6 +5,7 @@ exports.CalcScope = CalcScope;
|
|
|
5
5
|
const array_1 = require("../../Utils/array");
|
|
6
6
|
const emitter_1 = require("../../Utils/emitter");
|
|
7
7
|
const functions_1 = require("../../Utils/functions");
|
|
8
|
+
const list_1 = require("../../Utils/list");
|
|
8
9
|
/**
|
|
9
10
|
* Creates a dynamic (reactive) observable scope.
|
|
10
11
|
* @template T The type of value stored in the scope.
|
|
@@ -47,6 +48,10 @@ function CreateStaticScope(initialValue) {
|
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
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
|
+
*/
|
|
50
55
|
function ProcessScopeQueue() {
|
|
51
56
|
const queue = scopeQueue;
|
|
52
57
|
scopeQueue = [];
|
|
@@ -60,6 +65,11 @@ function ProcessScopeQueue() {
|
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
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
|
+
*/
|
|
63
73
|
function OnSetQueued(scope) {
|
|
64
74
|
if (scopeQueue.length === 0)
|
|
65
75
|
queueMicrotask(ProcessScopeQueue);
|
|
@@ -84,43 +94,93 @@ function OnSet(scope) {
|
|
|
84
94
|
return false;
|
|
85
95
|
}
|
|
86
96
|
/**
|
|
87
|
-
* Registers an emitter
|
|
88
|
-
*
|
|
89
|
-
*
|
|
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.
|
|
90
102
|
*/
|
|
91
|
-
function
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
watchState.emitters[watchState.emitterIndex] === emitter) {
|
|
96
|
-
watchState.emitterIndex++;
|
|
103
|
+
function RegisterSame(state, emitter) {
|
|
104
|
+
if (state.emitterIndex < state.emitters.length &&
|
|
105
|
+
state.emitters[state.emitterIndex] === emitter) {
|
|
106
|
+
state.emitterIndex++;
|
|
97
107
|
return;
|
|
98
108
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
state.emitters = state.emitters.slice(0, state.emitterIndex);
|
|
110
|
+
state.strategy = SORTED_STRATEGY;
|
|
111
|
+
RegisterSorted(state, emitter);
|
|
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);
|
|
110
126
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
+
*/
|
|
134
|
+
function RegisterPush(state, emitter) {
|
|
135
|
+
state.emitters.push(emitter);
|
|
136
|
+
if (state.emitters.length > 50) {
|
|
137
|
+
const idSet = (state.emitterIds = new Set([state.emitters[0][0]]));
|
|
114
138
|
let writePos = 0;
|
|
115
|
-
for (let x = 1; x <
|
|
116
|
-
if (!idSet.has(
|
|
117
|
-
|
|
118
|
-
idSet.add(
|
|
139
|
+
for (let x = 1; x < state.emitters.length; x++) {
|
|
140
|
+
if (!idSet.has(state.emitters[x][0])) {
|
|
141
|
+
state.emitters[++writePos] = state.emitters[x];
|
|
142
|
+
idSet.add(state.emitters[x][0]);
|
|
119
143
|
}
|
|
120
144
|
}
|
|
121
145
|
writePos++;
|
|
122
|
-
if (writePos <
|
|
123
|
-
|
|
146
|
+
if (writePos < state.emitters.length)
|
|
147
|
+
state.emitters.splice(writePos);
|
|
148
|
+
state.strategy = DISTINCT_STRATEGY;
|
|
149
|
+
}
|
|
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
|
+
*/
|
|
157
|
+
function RegisterDistinct(state, emitter) {
|
|
158
|
+
if (!state.emitterIds.has(emitter[0])) {
|
|
159
|
+
state.emitters.push(emitter);
|
|
160
|
+
state.emitterIds.add(emitter[0]);
|
|
161
|
+
}
|
|
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
|
+
*/
|
|
168
|
+
function RegisterEmitter(emitter) {
|
|
169
|
+
if (watchState === null)
|
|
170
|
+
return;
|
|
171
|
+
switch (watchState.strategy) {
|
|
172
|
+
case SAME_STRATEGY:
|
|
173
|
+
RegisterSame(watchState, emitter);
|
|
174
|
+
break;
|
|
175
|
+
case SORTED_STRATEGY:
|
|
176
|
+
RegisterSorted(watchState, emitter);
|
|
177
|
+
break;
|
|
178
|
+
case PUSH_STRATEGY:
|
|
179
|
+
RegisterPush(watchState, emitter);
|
|
180
|
+
break;
|
|
181
|
+
case DISTINCT_STRATEGY:
|
|
182
|
+
RegisterDistinct(watchState, emitter);
|
|
183
|
+
break;
|
|
124
184
|
}
|
|
125
185
|
}
|
|
126
186
|
/**
|
|
@@ -144,7 +204,48 @@ function GetScopeValue(scope) {
|
|
|
144
204
|
ExecuteScope(scope);
|
|
145
205
|
return scope.value;
|
|
146
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
|
+
*/
|
|
211
|
+
const SAME_STRATEGY = 1;
|
|
212
|
+
const PUSH_STRATEGY = 2;
|
|
213
|
+
const SORTED_STRATEGY = 3;
|
|
214
|
+
const DISTINCT_STRATEGY = 4;
|
|
215
|
+
const SHRINK_STRATEGY = 5;
|
|
147
216
|
let watchState = null;
|
|
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
|
+
*/
|
|
223
|
+
function CreateWatchState() {
|
|
224
|
+
return (list_1.List.Pop(watchPool) ?? {
|
|
225
|
+
emitterIndex: 0,
|
|
226
|
+
value: null,
|
|
227
|
+
emitters: null,
|
|
228
|
+
emitterIds: null,
|
|
229
|
+
currentCalc: null,
|
|
230
|
+
nextCalc: null,
|
|
231
|
+
strategy: PUSH_STRATEGY,
|
|
232
|
+
});
|
|
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
|
+
*/
|
|
239
|
+
function ReturnWatchState(state) {
|
|
240
|
+
state.emitterIndex = 0;
|
|
241
|
+
state.value = null;
|
|
242
|
+
state.emitters = null;
|
|
243
|
+
state.emitterIds = null;
|
|
244
|
+
state.currentCalc = null;
|
|
245
|
+
state.nextCalc = null;
|
|
246
|
+
state.strategy = SORTED_STRATEGY;
|
|
247
|
+
list_1.List.Push(watchPool, state);
|
|
248
|
+
}
|
|
148
249
|
/**
|
|
149
250
|
* Executes a callback while tracking all scope and emitter dependencies.
|
|
150
251
|
* Creates a watch context that records what was accessed during execution.
|
|
@@ -152,22 +253,21 @@ let watchState = null;
|
|
|
152
253
|
* @param currentCalc Optional map of existing calc scopes to reuse.
|
|
153
254
|
* @returns The watch state containing tracked dependencies and result.
|
|
154
255
|
*/
|
|
155
|
-
function WatchFunction(callback, currentCalc
|
|
256
|
+
function WatchFunction(callback, currentCalc, initialEmitters) {
|
|
156
257
|
const parent = watchState;
|
|
157
|
-
watchState =
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
currentCalc: currentCalc,
|
|
163
|
-
nextCalc: null,
|
|
164
|
-
};
|
|
258
|
+
watchState = CreateWatchState();
|
|
259
|
+
watchState.emitters = initialEmitters ?? [];
|
|
260
|
+
watchState.currentCalc = currentCalc;
|
|
261
|
+
if (initialEmitters !== null)
|
|
262
|
+
watchState.strategy = SAME_STRATEGY;
|
|
165
263
|
watchState.value = callback();
|
|
166
264
|
const resultState = watchState;
|
|
167
265
|
watchState = parent;
|
|
168
|
-
if (resultState.
|
|
169
|
-
resultState.emitterIndex < resultState.emitters.length)
|
|
266
|
+
if (resultState.strategy === SAME_STRATEGY &&
|
|
267
|
+
resultState.emitterIndex < resultState.emitters.length) {
|
|
170
268
|
resultState.emitters = resultState.emitters.slice(0, resultState.emitterIndex);
|
|
269
|
+
resultState.strategy = SHRINK_STRATEGY;
|
|
270
|
+
}
|
|
171
271
|
return resultState;
|
|
172
272
|
}
|
|
173
273
|
/**
|
|
@@ -178,8 +278,7 @@ function WatchFunction(callback, currentCalc = null, initialEmitters = []) {
|
|
|
178
278
|
function ExecuteScope(scope) {
|
|
179
279
|
scope.dirty = false;
|
|
180
280
|
const state = WatchFunction(scope.getFunction, scope.calcScopes, scope.emitters);
|
|
181
|
-
|
|
182
|
-
UpdateEmitters(scope, state.emitters, !!state.emitterIds);
|
|
281
|
+
UpdateEmitters(scope, state.emitters, state.strategy);
|
|
183
282
|
const calcScopes = state.currentCalc;
|
|
184
283
|
scope.calcScopes = state.nextCalc;
|
|
185
284
|
for (const key in calcScopes)
|
|
@@ -191,6 +290,7 @@ function ExecuteScope(scope) {
|
|
|
191
290
|
});
|
|
192
291
|
else
|
|
193
292
|
scope.value = state.value;
|
|
293
|
+
ReturnWatchState(state);
|
|
194
294
|
}
|
|
195
295
|
/**
|
|
196
296
|
* Creates a scope from a function, choosing between static and dynamic based on dependencies.
|
|
@@ -202,19 +302,22 @@ function ExecuteScope(scope) {
|
|
|
202
302
|
*/
|
|
203
303
|
function ExecuteFunction(callback, greedy, allowStatic) {
|
|
204
304
|
const async = (0, functions_1.IsAsync)(callback);
|
|
205
|
-
const state = WatchFunction(callback);
|
|
206
|
-
if (!allowStatic || async || state.emitters
|
|
305
|
+
const state = WatchFunction(callback, null, null);
|
|
306
|
+
if (!allowStatic || async || state.emitters !== null) {
|
|
207
307
|
const scope = CreateDynamicScope(callback, greedy, async ? null : state.value);
|
|
208
308
|
scope.calcScopes = state.nextCalc;
|
|
209
|
-
UpdateEmitters(scope, state.emitters,
|
|
309
|
+
UpdateEmitters(scope, state.emitters, state.strategy);
|
|
210
310
|
if (async)
|
|
211
311
|
state.value.then(function (result) {
|
|
212
312
|
scope.value = result;
|
|
213
313
|
emitter_1.Emitter.Emit(scope.emitter, scope);
|
|
214
314
|
});
|
|
315
|
+
ReturnWatchState(state);
|
|
215
316
|
return scope;
|
|
216
317
|
}
|
|
217
|
-
|
|
318
|
+
const value = state.value;
|
|
319
|
+
ReturnWatchState(state);
|
|
320
|
+
return CreateStaticScope(value);
|
|
218
321
|
}
|
|
219
322
|
/**
|
|
220
323
|
* Creates a computed scope that acts as a gatekeeper for parent scope emissions.
|
|
@@ -254,27 +357,32 @@ function CalcScope(callback, idOverride) {
|
|
|
254
357
|
* @param right The new list of emitters to track.
|
|
255
358
|
* @param distinct Whether emitters are already unique (sorted and deduplicated).
|
|
256
359
|
*/
|
|
257
|
-
function UpdateEmitters(scope, right,
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
emitter_1.Emitter.On(right[x], scope.setCallback);
|
|
262
|
-
scope.emitters = right;
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
if (right.length === 0) {
|
|
266
|
-
if (scope.emitters.length > 0) {
|
|
267
|
-
for (let x = 0; x < scope.emitters.length; x++)
|
|
360
|
+
function UpdateEmitters(scope, right, strategy) {
|
|
361
|
+
switch (strategy) {
|
|
362
|
+
case SHRINK_STRATEGY: {
|
|
363
|
+
for (let x = right.length; x < scope.emitters.length; x++)
|
|
268
364
|
emitter_1.Emitter.Remove(scope.emitters[x], scope.setCallback);
|
|
269
|
-
|
|
365
|
+
break;
|
|
270
366
|
}
|
|
271
|
-
|
|
367
|
+
case PUSH_STRATEGY:
|
|
368
|
+
case DISTINCT_STRATEGY:
|
|
369
|
+
strategy === PUSH_STRATEGY
|
|
370
|
+
? emitter_1.Emitter.Distinct(right)
|
|
371
|
+
: emitter_1.Emitter.Sort(right);
|
|
372
|
+
case SORTED_STRATEGY:
|
|
373
|
+
if (scope.emitters === null || scope.emitters.length === 0) {
|
|
374
|
+
for (let x = 0; x < right.length; x++)
|
|
375
|
+
emitter_1.Emitter.On(right[x], scope.setCallback);
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
(0, array_1.ReconcileSortedEmitters)(scope.emitters, right, function (emitter) {
|
|
379
|
+
emitter_1.Emitter.On(emitter, scope.setCallback);
|
|
380
|
+
}, function (emitter) {
|
|
381
|
+
emitter_1.Emitter.Remove(emitter, scope.setCallback);
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
break;
|
|
272
385
|
}
|
|
273
|
-
(0, array_1.ReconcileSortedEmitters)(scope.emitters, right, function (emitter) {
|
|
274
|
-
emitter_1.Emitter.On(emitter, scope.setCallback);
|
|
275
|
-
}, function (emitter) {
|
|
276
|
-
emitter_1.Emitter.Remove(emitter, scope.setCallback);
|
|
277
|
-
});
|
|
278
386
|
scope.emitters = right;
|
|
279
387
|
}
|
|
280
388
|
/**
|
package/Utils/decorators.js
CHANGED
|
@@ -20,9 +20,7 @@ exports.Scope = Scope;
|
|
|
20
20
|
exports.Watch = Watch;
|
|
21
21
|
exports.Inject = Inject;
|
|
22
22
|
exports.Destroy = Destroy;
|
|
23
|
-
// import { Component } from "../Node/component";
|
|
24
23
|
const observableScope_1 = require("../Store/Tree/observableScope");
|
|
25
|
-
// import { ElementNodeRefTypes } from "../Node/nodeRef.types";
|
|
26
24
|
const observableNode_1 = require("../Store/Tree/observableNode");
|
|
27
25
|
const Store_1 = require("../Store");
|
|
28
26
|
/**
|
|
@@ -365,14 +363,6 @@ function WatchDecorator(target, propertyKey, descriptor, scopeFunction) {
|
|
|
365
363
|
observableScope_1.ObservableScope.Watch(scope, function (scope) {
|
|
366
364
|
instance[propertyKey](observableScope_1.ObservableScope.Value(scope));
|
|
367
365
|
});
|
|
368
|
-
/* const [value, scope] = ObservableScope.CreateIf(scopeFunctionWrapper, true);
|
|
369
|
-
if (scope) {
|
|
370
|
-
const propertyMap = GetScopeMapForInstance(this);
|
|
371
|
-
propertyMap[propertyKey as string] = [scope, undefined];
|
|
372
|
-
ObservableScope.Watch(scope, function (scope) {
|
|
373
|
-
(instance as any)[propertyKey](ObservableScope.Value(scope));
|
|
374
|
-
});
|
|
375
|
-
} */
|
|
376
366
|
instance[propertyKey](observableScope_1.ObservableScope.Value(scope));
|
|
377
367
|
return instance;
|
|
378
368
|
}
|
package/Utils/emitter.js
CHANGED
|
@@ -42,11 +42,6 @@ var Emitter;
|
|
|
42
42
|
function Distinct(emitters) {
|
|
43
43
|
if (emitters.length < 2)
|
|
44
44
|
return;
|
|
45
|
-
// emitters.length < 51 ? DistinctSmall(emitters) : DistinctLarge(emitters);
|
|
46
|
-
DistinctSmall(emitters);
|
|
47
|
-
}
|
|
48
|
-
Emitter.Distinct = Distinct;
|
|
49
|
-
function DistinctSmall(emitters) {
|
|
50
45
|
Sort(emitters);
|
|
51
46
|
let writePos = 0;
|
|
52
47
|
for (let x = 1; x < emitters.length; x++) {
|
|
@@ -58,20 +53,7 @@ var Emitter;
|
|
|
58
53
|
if (writePos < emitters.length)
|
|
59
54
|
emitters.splice(writePos);
|
|
60
55
|
}
|
|
61
|
-
|
|
62
|
-
let writePos = 0;
|
|
63
|
-
const ids = new Set();
|
|
64
|
-
for (let x = 0; x < emitters.length; x++) {
|
|
65
|
-
const id = emitters[x][0];
|
|
66
|
-
if (!ids.has(id)) {
|
|
67
|
-
ids.add(id);
|
|
68
|
-
emitters[writePos++] = emitters[x];
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (writePos < emitters.length)
|
|
72
|
-
emitters.splice(writePos);
|
|
73
|
-
Sort(emitters);
|
|
74
|
-
}
|
|
56
|
+
Emitter.Distinct = Distinct;
|
|
75
57
|
function Sort(emitters) {
|
|
76
58
|
if (emitters.length < 11)
|
|
77
59
|
(0, array_1.InsertionSortTuples)(emitters);
|