j-templates 7.0.74 → 7.0.76
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/createPropertyAssignment.js +14 -7
- package/DOM/elements.d.ts +24 -24
- package/DOM/index.d.ts +2 -0
- package/DOM/index.js +2 -2
- package/Node/vNode.js +4 -0
- package/Node/vNode.types.d.ts +2 -0
- package/Store/Diff/diffAsync.d.ts +28 -0
- package/Store/Diff/diffAsync.js +25 -0
- package/Store/Diff/diffSync.d.ts +22 -0
- package/Store/Diff/diffSync.js +22 -0
- package/Store/Diff/diffTree.d.ts +33 -0
- package/Store/Diff/diffTree.js +68 -0
- package/Store/Store/store.d.ts +38 -0
- package/Store/Store/store.js +25 -0
- package/Store/Tree/observableNode.js +5 -5
- package/Store/Tree/observableScope.js +99 -105
- package/Utils/asyncQueue.d.ts +20 -0
- package/Utils/asyncQueue.js +20 -0
- package/Utils/decorators.d.ts +674 -49
- package/Utils/decorators.js +674 -49
- package/Utils/emitter.d.ts +1 -3
- package/Utils/emitter.js +5 -23
- package/Utils/functions.d.ts +5 -0
- package/Utils/functions.js +5 -0
- package/Utils/json.d.ts +10 -0
- package/Utils/json.js +63 -1
- package/package.json +1 -1
- package/Utils/bitSet.d.ts +0 -23
- package/Utils/bitSet.js +0 -139
- package/Utils/jsonType.d.ts +0 -1
- package/Utils/jsonType.js +0 -5
- package/index.debug.d.ts +0 -0
- package/index.debug.js +0 -0
package/Store/Diff/diffTree.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DiffTreeFactory = DiffTreeFactory;
|
|
4
|
+
/**
|
|
5
|
+
* Factory function that creates a DiffTree class.
|
|
6
|
+
* Can operate in worker mode for async operations.
|
|
7
|
+
* @param jsonDiffFactory - Optional factory for JSON diff utilities
|
|
8
|
+
* @param worker - If true, sets up worker message handling
|
|
9
|
+
* @returns DiffTree constructor
|
|
10
|
+
*/
|
|
4
11
|
function DiffTreeFactory(jsonDiffFactory, worker) {
|
|
5
12
|
const { JsonDiff, JsonType, JsonDeepClone } = jsonDiffFactory();
|
|
6
13
|
const ctx = this;
|
|
@@ -35,6 +42,13 @@ function DiffTreeFactory(jsonDiffFactory, worker) {
|
|
|
35
42
|
}
|
|
36
43
|
};
|
|
37
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Flattens nested objects/arrays, extracting keyed objects to root.
|
|
47
|
+
* @param root - Root object to store flattened values
|
|
48
|
+
* @param value - Value to flatten
|
|
49
|
+
* @param keyFunc - Function to extract key from objects
|
|
50
|
+
* @returns The root object with flattened values
|
|
51
|
+
*/
|
|
38
52
|
function FlattenValue(root, value, keyFunc) {
|
|
39
53
|
const type = JsonType(value);
|
|
40
54
|
switch (type) {
|
|
@@ -54,6 +68,12 @@ function DiffTreeFactory(jsonDiffFactory, worker) {
|
|
|
54
68
|
}
|
|
55
69
|
return root;
|
|
56
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Retrieves a value from a source object using a dot-separated path.
|
|
73
|
+
* @param source - The source object
|
|
74
|
+
* @param path - Dot-separated path to the value (empty string returns source)
|
|
75
|
+
* @returns The value at the specified path
|
|
76
|
+
*/
|
|
57
77
|
function GetPathValue(source, path) {
|
|
58
78
|
if (path === "")
|
|
59
79
|
return source;
|
|
@@ -63,6 +83,12 @@ function DiffTreeFactory(jsonDiffFactory, worker) {
|
|
|
63
83
|
curr = curr[parts[x]];
|
|
64
84
|
return curr;
|
|
65
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Sets a value in a source object at the specified path.
|
|
88
|
+
* @param source - The source object
|
|
89
|
+
* @param path - Array of path segments (strings for keys, numbers for indices)
|
|
90
|
+
* @param value - The value to set
|
|
91
|
+
*/
|
|
66
92
|
function SetPathValue(source, path, value) {
|
|
67
93
|
if (path.length === 0)
|
|
68
94
|
return;
|
|
@@ -72,6 +98,14 @@ function DiffTreeFactory(jsonDiffFactory, worker) {
|
|
|
72
98
|
curr = curr[path[x]];
|
|
73
99
|
curr[path[x]] = value;
|
|
74
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Resolves a path to its keyed root object if applicable.
|
|
103
|
+
* Searches up the path to find an object with a key from keyFunc.
|
|
104
|
+
* @param source - The source object
|
|
105
|
+
* @param path - Dot-separated path
|
|
106
|
+
* @param keyFunc - Function to extract key from objects
|
|
107
|
+
* @returns The resolved path (possibly shortened to root key)
|
|
108
|
+
*/
|
|
75
109
|
function ResolveKeyPath(source, path, keyFunc) {
|
|
76
110
|
const parts = path.split(".");
|
|
77
111
|
const pathValues = new Array(parts.length - 1);
|
|
@@ -90,6 +124,15 @@ function DiffTreeFactory(jsonDiffFactory, worker) {
|
|
|
90
124
|
}
|
|
91
125
|
return path;
|
|
92
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Updates the source with a new value at the specified path and computes diffs.
|
|
129
|
+
* Also updates any keyed objects that were nested in the value.
|
|
130
|
+
* @param source - The source object to update
|
|
131
|
+
* @param path - Dot-separated path to update
|
|
132
|
+
* @param value - The new value
|
|
133
|
+
* @param keyFunc - Optional function to extract keys from objects
|
|
134
|
+
* @returns Diff results showing all changes made
|
|
135
|
+
*/
|
|
93
136
|
function UpdateSource(source, path, value, keyFunc) {
|
|
94
137
|
const diffResult = [];
|
|
95
138
|
if (keyFunc) {
|
|
@@ -115,20 +158,45 @@ function DiffTreeFactory(jsonDiffFactory, worker) {
|
|
|
115
158
|
}
|
|
116
159
|
return diffResult;
|
|
117
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Internal diff tree implementation.
|
|
163
|
+
* Maintains root state and computes diffs for path/value updates.
|
|
164
|
+
* @private
|
|
165
|
+
*/
|
|
118
166
|
class DiffTree {
|
|
167
|
+
/**
|
|
168
|
+
* Creates a DiffTree instance.
|
|
169
|
+
* @param keyFunc - Optional function to extract a key from objects
|
|
170
|
+
*/
|
|
119
171
|
constructor(keyFunc) {
|
|
120
172
|
this.keyFunc = keyFunc;
|
|
121
173
|
this.rootState = {};
|
|
122
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Computes diffs for a batch of path/value pairs.
|
|
177
|
+
* @param data - Array of objects with path and value properties
|
|
178
|
+
* @returns Combined diff results
|
|
179
|
+
*/
|
|
123
180
|
DiffBatch(data) {
|
|
124
181
|
const results = data
|
|
125
182
|
.map(({ path, value }) => this.DiffPath(path, value))
|
|
126
183
|
.flat(1);
|
|
127
184
|
return results;
|
|
128
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Computes the diff between a new value and the current value at a path.
|
|
188
|
+
* @param path - Dot-separated path to the value
|
|
189
|
+
* @param value - The new value to compare
|
|
190
|
+
* @returns Diff results showing changes
|
|
191
|
+
*/
|
|
129
192
|
DiffPath(path, value) {
|
|
130
193
|
return UpdateSource(this.rootState, path, value, this.keyFunc);
|
|
131
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Retrieves the current value at a path.
|
|
197
|
+
* @param path - Dot-separated path to the value
|
|
198
|
+
* @returns The value at the specified path
|
|
199
|
+
*/
|
|
132
200
|
GetPath(path) {
|
|
133
201
|
return GetPathValue(this.rootState, path);
|
|
134
202
|
}
|
package/Store/Store/store.d.ts
CHANGED
|
@@ -1,11 +1,49 @@
|
|
|
1
1
|
import { JsonDiffResult } from "../../Utils/json";
|
|
2
|
+
/**
|
|
3
|
+
* Base class for observable data store management.
|
|
4
|
+
* Stores root objects as ObservableNode instances and manages updates through diff operations.
|
|
5
|
+
*
|
|
6
|
+
* @see StoreSync
|
|
7
|
+
* @see StoreAsync
|
|
8
|
+
*/
|
|
2
9
|
export declare class Store {
|
|
3
10
|
protected keyFunc?: (value: any) => string | undefined;
|
|
4
11
|
private rootMap;
|
|
5
12
|
private createNode;
|
|
13
|
+
/**
|
|
14
|
+
* Creates an instance of Store.
|
|
15
|
+
* @param keyFunc Optional function to generate a key for a given data value.
|
|
16
|
+
* When provided, enables alias functionality where values can reference other root objects.
|
|
17
|
+
*/
|
|
6
18
|
constructor(keyFunc?: (value: any) => string | undefined);
|
|
19
|
+
/**
|
|
20
|
+
* Retrieves a value from the store by id.
|
|
21
|
+
* @template O - The type of the value to retrieve
|
|
22
|
+
* @param id - The id/key of the value
|
|
23
|
+
* @returns The value or undefined if not found
|
|
24
|
+
*/
|
|
7
25
|
Get<O>(id: string): O | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Retrieves a value from the store by id, with a default value if not found.
|
|
28
|
+
* @template O - The type of the value to retrieve
|
|
29
|
+
* @param id - The id/key of the value
|
|
30
|
+
* @param defaultValue - The default value to return if id doesn't exist
|
|
31
|
+
* @returns The value or the default value
|
|
32
|
+
*/
|
|
8
33
|
Get<O>(id: string, defaultValue: O): O;
|
|
34
|
+
/**
|
|
35
|
+
* Updates the root map with diff results by grouping changes by root path.
|
|
36
|
+
* @param results - Array of diff results to apply
|
|
37
|
+
* @protected
|
|
38
|
+
*/
|
|
9
39
|
protected UpdateRootMap(results: JsonDiffResult): void;
|
|
40
|
+
/**
|
|
41
|
+
* Updates a specific root object with diff results.
|
|
42
|
+
* Creates the root object if it doesn't exist.
|
|
43
|
+
* @param rootPath - The path of the root object
|
|
44
|
+
* @param results - Array of diff results to apply to this root object
|
|
45
|
+
* @throws If unable to initialize root path with the given results
|
|
46
|
+
* @private
|
|
47
|
+
*/
|
|
10
48
|
private UpdateRootObject;
|
|
11
49
|
}
|
package/Store/Store/store.js
CHANGED
|
@@ -2,7 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Store = void 0;
|
|
4
4
|
const observableNode_1 = require("../Tree/observableNode");
|
|
5
|
+
/**
|
|
6
|
+
* Base class for observable data store management.
|
|
7
|
+
* Stores root objects as ObservableNode instances and manages updates through diff operations.
|
|
8
|
+
*
|
|
9
|
+
* @see StoreSync
|
|
10
|
+
* @see StoreAsync
|
|
11
|
+
*/
|
|
5
12
|
class Store {
|
|
13
|
+
/**
|
|
14
|
+
* Creates an instance of Store.
|
|
15
|
+
* @param keyFunc Optional function to generate a key for a given data value.
|
|
16
|
+
* When provided, enables alias functionality where values can reference other root objects.
|
|
17
|
+
*/
|
|
6
18
|
constructor(keyFunc) {
|
|
7
19
|
this.keyFunc = keyFunc;
|
|
8
20
|
this.rootMap = new Map();
|
|
@@ -30,6 +42,11 @@ class Store {
|
|
|
30
42
|
}
|
|
31
43
|
return result[id];
|
|
32
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Updates the root map with diff results by grouping changes by root path.
|
|
47
|
+
* @param results - Array of diff results to apply
|
|
48
|
+
* @protected
|
|
49
|
+
*/
|
|
33
50
|
UpdateRootMap(results) {
|
|
34
51
|
for (let x = 0; x < results.length;) {
|
|
35
52
|
const root = results[x].path[0];
|
|
@@ -40,6 +57,14 @@ class Store {
|
|
|
40
57
|
this.UpdateRootObject(rootGroup[0].path[0], rootGroup);
|
|
41
58
|
}
|
|
42
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Updates a specific root object with diff results.
|
|
62
|
+
* Creates the root object if it doesn't exist.
|
|
63
|
+
* @param rootPath - The path of the root object
|
|
64
|
+
* @param results - Array of diff results to apply to this root object
|
|
65
|
+
* @throws If unable to initialize root path with the given results
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
43
68
|
UpdateRootObject(rootPath, results) {
|
|
44
69
|
const rootObject = this.rootMap.get(rootPath);
|
|
45
70
|
if (rootObject === undefined) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ObservableNode = exports.GET_TO_JSON = exports.GET_OBSERVABLE_VALUE = exports.IS_OBSERVABLE_NODE = void 0;
|
|
4
4
|
const json_1 = require("../../Utils/json");
|
|
5
|
-
const
|
|
5
|
+
const json_2 = require("../../Utils/json");
|
|
6
6
|
const observableScope_1 = require("./observableScope");
|
|
7
7
|
/**
|
|
8
8
|
* Symbol to identify observable nodes.
|
|
@@ -48,7 +48,7 @@ function ownKeys(value) {
|
|
|
48
48
|
function ownKeysArray(value) {
|
|
49
49
|
return Object.keys(value);
|
|
50
50
|
}
|
|
51
|
-
function UnwrapProxy(value, type = (0,
|
|
51
|
+
function UnwrapProxy(value, type = (0, json_2.JsonType)(value)) {
|
|
52
52
|
if (type === "value")
|
|
53
53
|
return value;
|
|
54
54
|
if (value[exports.IS_OBSERVABLE_NODE])
|
|
@@ -72,7 +72,7 @@ function CreateProxyFactory(alias) {
|
|
|
72
72
|
return CreateProxyFromValue(value);
|
|
73
73
|
}
|
|
74
74
|
function ToJsonCopy(value) {
|
|
75
|
-
const type = (0,
|
|
75
|
+
const type = (0, json_2.JsonType)(value);
|
|
76
76
|
switch (type) {
|
|
77
77
|
case "array": {
|
|
78
78
|
const typedValue = value;
|
|
@@ -195,7 +195,7 @@ function CreateProxyFactory(alias) {
|
|
|
195
195
|
function ObjectProxySetter(object, prop, value) {
|
|
196
196
|
if (readOnly)
|
|
197
197
|
throw `Object is readonly`;
|
|
198
|
-
const jsonType = (0,
|
|
198
|
+
const jsonType = (0, json_2.JsonType)(value);
|
|
199
199
|
if (jsonType === "value") {
|
|
200
200
|
value !== object[prop] && SetPropertyValue(object, prop, value);
|
|
201
201
|
}
|
|
@@ -242,7 +242,7 @@ function CreateProxyFactory(alias) {
|
|
|
242
242
|
return observableScope_1.ObservableScope.Value(leafScopes[prop]);
|
|
243
243
|
}
|
|
244
244
|
function CreateProxyFromValue(value) {
|
|
245
|
-
const type = (0,
|
|
245
|
+
const type = (0, json_2.JsonType)(value);
|
|
246
246
|
switch (type) {
|
|
247
247
|
case "object": {
|
|
248
248
|
let proxy = proxyCache.get(value) ?? CreateObjectProxy(value);
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ObservableScope = void 0;
|
|
4
4
|
exports.CalcScope = CalcScope;
|
|
5
|
-
const bitSet_1 = require("../../Utils/bitSet");
|
|
6
5
|
const emitter_1 = require("../../Utils/emitter");
|
|
7
6
|
const functions_1 = require("../../Utils/functions");
|
|
8
7
|
/**
|
|
@@ -45,11 +44,6 @@ function CreateStaticScope(initialValue) {
|
|
|
45
44
|
value: initialValue,
|
|
46
45
|
};
|
|
47
46
|
}
|
|
48
|
-
/**
|
|
49
|
-
* Queue of scopes pending batched update processing.
|
|
50
|
-
* Scopes are added when their dependencies change and greedy batching is enabled.
|
|
51
|
-
* The queue is processed in a microtask to batch multiple updates efficiently.
|
|
52
|
-
*/
|
|
53
47
|
let scopeQueue = [];
|
|
54
48
|
/**
|
|
55
49
|
* Processes all queued scopes, recomputing dirty scopes and emitting changes.
|
|
@@ -94,78 +88,67 @@ function OnSet() {
|
|
|
94
88
|
emitter_1.Emitter.Emit(this.emitter, this);
|
|
95
89
|
}
|
|
96
90
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
91
|
+
* Registers an emitter using SAME_STRATEGY.
|
|
92
|
+
* If the emitter is already registered at the current index, increments the index.
|
|
93
|
+
* Otherwise, switches to PUSH_STRATEGY and appends the emitter.
|
|
94
|
+
* @param state The current watch state.
|
|
95
|
+
* @param emitter The emitter to register.
|
|
100
96
|
*/
|
|
101
|
-
|
|
97
|
+
function RegisterSame(state, emitter) {
|
|
98
|
+
if (state.emitterIndex < state.emitters.length &&
|
|
99
|
+
state.emitters[state.emitterIndex] === emitter) {
|
|
100
|
+
state.emitterIndex++;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
state.emitters = state.emitters.slice(0, state.emitterIndex);
|
|
104
|
+
state.strategy = PUSH_STRATEGY;
|
|
105
|
+
RegisterPush(state, emitter);
|
|
106
|
+
}
|
|
102
107
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
108
|
+
* Registers an emitter using PUSH_STRATEGY.
|
|
109
|
+
* Switches to DISTINCT_STRATEGY when the emitter count exceeds 50 to optimize memory.
|
|
110
|
+
* @param state The current watch state.
|
|
111
|
+
* @param emitter The emitter to register.
|
|
105
112
|
*/
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
function RegisterPush(state, emitter) {
|
|
114
|
+
if (state.emitters.length > 10) {
|
|
115
|
+
state.strategy = DISTINCT_STRATEGY;
|
|
116
|
+
state.emitterSet = new Set(state.emitters);
|
|
117
|
+
RegisterDistinct(state, emitter);
|
|
118
|
+
}
|
|
119
|
+
else if (!state.emitters.includes(emitter))
|
|
120
|
+
state.emitters.push(emitter);
|
|
112
121
|
}
|
|
113
122
|
/**
|
|
114
|
-
* Registers an emitter
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* @param emitter The emitter to register
|
|
123
|
+
* Registers an emitter using DISTINCT_STRATEGY.
|
|
124
|
+
* Only adds emitters that haven't been registered yet, using an ID set for O(1) lookup.
|
|
125
|
+
* @param state The current watch state.
|
|
126
|
+
* @param emitter The emitter to register.
|
|
127
|
+
*/
|
|
128
|
+
function RegisterDistinct(state, emitter) {
|
|
129
|
+
if (!state.emitterSet.has(emitter)) {
|
|
130
|
+
state.emitters.push(emitter);
|
|
131
|
+
state.emitterSet.add(emitter);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Routes an emitter to the appropriate registration strategy based on current watch state.
|
|
136
|
+
* Does nothing if not within a watch context.
|
|
137
|
+
* @param emitter The emitter to register.
|
|
129
138
|
*/
|
|
130
139
|
function RegisterEmitter(emitter) {
|
|
131
140
|
if (watchState === null)
|
|
132
141
|
return;
|
|
133
142
|
switch (watchState.strategy) {
|
|
134
|
-
case SAME_STRATEGY:
|
|
135
|
-
|
|
136
|
-
watchState.emitters[watchState.emitterIndex][0] === emitter[0]) {
|
|
137
|
-
watchState.emitterIndex++;
|
|
138
|
-
// BitSet.set(watchState.emitterIds, emitter[0]);
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
watchState.strategy = PUSH_STRATEGY;
|
|
142
|
-
watchState.emitters = watchState.emitters.slice(0, watchState.emitterIndex);
|
|
143
|
-
RegisterEmitter(emitter);
|
|
144
|
-
}
|
|
143
|
+
case SAME_STRATEGY:
|
|
144
|
+
RegisterSame(watchState, emitter);
|
|
145
145
|
break;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
case 1: {
|
|
154
|
-
if (watchState.emitters[0][0] !== emitter[0])
|
|
155
|
-
watchState.emitters.push(emitter);
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
default: {
|
|
159
|
-
if (watchState.emitters[watchState.emitters.length - 1][0] !== emitter[0]) {
|
|
160
|
-
watchState.emitterIds ??= GetBitSetFor(watchState.emitters);
|
|
161
|
-
if (bitSet_1.BitSet.add(watchState.emitterIds, emitter[0]))
|
|
162
|
-
watchState.emitters.push(emitter);
|
|
163
|
-
}
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
146
|
+
case PUSH_STRATEGY:
|
|
147
|
+
RegisterPush(watchState, emitter);
|
|
148
|
+
break;
|
|
149
|
+
case DISTINCT_STRATEGY:
|
|
150
|
+
RegisterDistinct(watchState, emitter);
|
|
167
151
|
break;
|
|
168
|
-
}
|
|
169
152
|
}
|
|
170
153
|
}
|
|
171
154
|
/**
|
|
@@ -190,16 +173,14 @@ function GetScopeValue(scope) {
|
|
|
190
173
|
return scope.value;
|
|
191
174
|
}
|
|
192
175
|
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* Nested watch contexts are supported via the parent reference pattern.
|
|
176
|
+
* Strategy constants for optimizing emitter registration during watch operations.
|
|
177
|
+
* Each strategy represents a different approach to tracking and deduplicating dependencies.
|
|
196
178
|
*/
|
|
179
|
+
const SAME_STRATEGY = 1;
|
|
180
|
+
const PUSH_STRATEGY = 2;
|
|
181
|
+
const DISTINCT_STRATEGY = 3;
|
|
182
|
+
const SHRINK_STRATEGY = 4;
|
|
197
183
|
let watchState = null;
|
|
198
|
-
/**
|
|
199
|
-
* Pool for recycling BitSet objects to reduce garbage collection overhead.
|
|
200
|
-
* BitSets are borrowed during watch operations and returned after use.
|
|
201
|
-
*/
|
|
202
|
-
const BIT_SET_POOL = bitSet_1.BitSetPool.Create();
|
|
203
184
|
/**
|
|
204
185
|
* Executes a callback while tracking all scope and emitter dependencies.
|
|
205
186
|
* Creates a watch context that records what was accessed during execution.
|
|
@@ -211,16 +192,21 @@ function WatchFunction(callback, currentCalc, initialEmitters) {
|
|
|
211
192
|
const parent = watchState;
|
|
212
193
|
watchState = {
|
|
213
194
|
emitterIndex: 0,
|
|
214
|
-
strategy: initialEmitters === null ? PUSH_STRATEGY : SAME_STRATEGY,
|
|
215
195
|
value: null,
|
|
216
196
|
emitters: initialEmitters ?? [],
|
|
217
|
-
|
|
197
|
+
emitterSet: null,
|
|
218
198
|
currentCalc,
|
|
219
199
|
nextCalc: null,
|
|
200
|
+
strategy: initialEmitters === null ? PUSH_STRATEGY : SAME_STRATEGY,
|
|
220
201
|
};
|
|
221
202
|
watchState.value = callback();
|
|
222
203
|
const resultState = watchState;
|
|
223
204
|
watchState = parent;
|
|
205
|
+
if (resultState.strategy === SAME_STRATEGY &&
|
|
206
|
+
resultState.emitterIndex < resultState.emitters.length) {
|
|
207
|
+
resultState.emitters = resultState.emitters.slice(0, resultState.emitterIndex);
|
|
208
|
+
resultState.strategy = SHRINK_STRATEGY;
|
|
209
|
+
}
|
|
224
210
|
return resultState;
|
|
225
211
|
}
|
|
226
212
|
/**
|
|
@@ -231,11 +217,7 @@ function WatchFunction(callback, currentCalc, initialEmitters) {
|
|
|
231
217
|
function ExecuteScope(scope) {
|
|
232
218
|
scope.dirty = false;
|
|
233
219
|
const state = WatchFunction(scope.getFunction, scope.calcScopes, scope.emitters);
|
|
234
|
-
if (state.strategy === SAME_STRATEGY &&
|
|
235
|
-
state.emitterIndex < state.emitters.length)
|
|
236
|
-
state.emitters = state.emitters.slice(0, state.emitterIndex);
|
|
237
220
|
UpdateEmitters(scope, state);
|
|
238
|
-
state.emitterIds && bitSet_1.BitSetPool.Return(BIT_SET_POOL, state.emitterIds);
|
|
239
221
|
const calcScopes = state.currentCalc;
|
|
240
222
|
scope.calcScopes = state.nextCalc;
|
|
241
223
|
for (const key in calcScopes)
|
|
@@ -263,7 +245,6 @@ function ExecuteFunction(callback, greedy, allowStatic) {
|
|
|
263
245
|
const scope = CreateDynamicScope(callback, greedy, async ? null : state.value);
|
|
264
246
|
scope.calcScopes = state.nextCalc;
|
|
265
247
|
UpdateEmitters(scope, state);
|
|
266
|
-
state.emitterIds && bitSet_1.BitSetPool.Return(BIT_SET_POOL, state.emitterIds);
|
|
267
248
|
if (async)
|
|
268
249
|
state.value.then(function (result) {
|
|
269
250
|
scope.value = result;
|
|
@@ -294,7 +275,7 @@ function CalcScope(callback, idOverride) {
|
|
|
294
275
|
return callback();
|
|
295
276
|
const nextScopes = (watchState.nextCalc ??= {});
|
|
296
277
|
const id = idOverride ?? "default";
|
|
297
|
-
if (
|
|
278
|
+
if (nextScopes[id]) {
|
|
298
279
|
RegisterScope(nextScopes[id]);
|
|
299
280
|
return GetScopeValue(nextScopes[id]);
|
|
300
281
|
}
|
|
@@ -306,31 +287,45 @@ function CalcScope(callback, idOverride) {
|
|
|
306
287
|
return GetScopeValue(nextScopes[id]);
|
|
307
288
|
}
|
|
308
289
|
/**
|
|
309
|
-
* Updates a scope's
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
* @param scope The scope to update emitter subscriptions for.
|
|
316
|
-
* @param state The watch state containing the new emitter list.
|
|
290
|
+
* Updates a scope's dependency emitters using efficient diffing.
|
|
291
|
+
* Subscribes to new emitters and unsubscribes from removed ones.
|
|
292
|
+
* @param scope The scope to update dependencies for.
|
|
293
|
+
* @param right The new list of emitters to track.
|
|
294
|
+
* @param distinct Whether emitters are already unique (sorted and deduplicated).
|
|
317
295
|
*/
|
|
318
296
|
function UpdateEmitters(scope, state) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
for (let x = sameIndex; x < currentEmitters.length; x++) {
|
|
325
|
-
if (nextEmitterIds === null || !bitSet_1.BitSet.remove(nextEmitterIds, currentEmitters[x][0]))
|
|
326
|
-
emitter_1.Emitter.Remove(currentEmitters[x], scope.setCallback);
|
|
327
|
-
}
|
|
297
|
+
if (scope.emitters === null) {
|
|
298
|
+
for (let x = 0; x < state.emitters.length; x++)
|
|
299
|
+
emitter_1.Emitter.On(state.emitters[x], scope.setCallback);
|
|
300
|
+
scope.emitters = state.emitters;
|
|
301
|
+
return;
|
|
328
302
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
303
|
+
switch (state.strategy) {
|
|
304
|
+
case SHRINK_STRATEGY: {
|
|
305
|
+
for (let x = state.emitters.length; x < scope.emitters.length; x++)
|
|
306
|
+
emitter_1.Emitter.Remove(scope.emitters[x], scope.setCallback);
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
case PUSH_STRATEGY: {
|
|
310
|
+
for (let x = state.emitterIndex; x < scope.emitters.length; x++)
|
|
311
|
+
if (!state.emitters.includes(scope.emitters[x], state.emitterIndex))
|
|
312
|
+
emitter_1.Emitter.Remove(scope.emitters[x], scope.setCallback);
|
|
313
|
+
for (let x = state.emitterIndex; x < state.emitters.length; x++)
|
|
314
|
+
if (!scope.emitters.includes(state.emitters[x], state.emitterIndex))
|
|
315
|
+
emitter_1.Emitter.On(state.emitters[x], scope.setCallback);
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
case DISTINCT_STRATEGY: {
|
|
319
|
+
for (let x = state.emitterIndex; x < scope.emitters.length; x++)
|
|
320
|
+
if (!state.emitterSet.delete(scope.emitters[x]))
|
|
321
|
+
emitter_1.Emitter.Remove(scope.emitters[x], scope.setCallback);
|
|
322
|
+
for (let x = state.emitterIndex; x < state.emitters.length; x++)
|
|
323
|
+
if (state.emitterSet.has(state.emitters[x]))
|
|
324
|
+
emitter_1.Emitter.On(state.emitters[x], scope.setCallback);
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
332
327
|
}
|
|
333
|
-
scope.emitters =
|
|
328
|
+
scope.emitters = state.emitters;
|
|
334
329
|
}
|
|
335
330
|
/**
|
|
336
331
|
* Destroys multiple scopes, cleaning up their resources.
|
|
@@ -348,7 +343,7 @@ function DestroyAllScopes(scopes) {
|
|
|
348
343
|
function DestroyScope(scope) {
|
|
349
344
|
if (!scope || scope.type === "static")
|
|
350
345
|
return;
|
|
351
|
-
|
|
346
|
+
// Emitter.Destroy(scope.emitter);
|
|
352
347
|
for (const key in scope.calcScopes)
|
|
353
348
|
DestroyScope(scope.calcScopes[key]);
|
|
354
349
|
scope.calcScopes = null;
|
|
@@ -362,7 +357,6 @@ function DestroyScope(scope) {
|
|
|
362
357
|
scope.setCallback = null;
|
|
363
358
|
scope.destroyed = true;
|
|
364
359
|
scope.onDestroyed && emitter_1.Emitter.Emit(scope.onDestroyed, scope);
|
|
365
|
-
scope.onDestroyed && emitter_1.Emitter.Destroy(scope.onDestroyed);
|
|
366
360
|
}
|
|
367
361
|
var ObservableScope;
|
|
368
362
|
(function (ObservableScope) {
|
package/Utils/asyncQueue.d.ts
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A queue that executes async callbacks sequentially, one at a time.
|
|
3
|
+
*/
|
|
1
4
|
export declare class AsyncQueue {
|
|
2
5
|
private running;
|
|
3
6
|
private queue;
|
|
7
|
+
/**
|
|
8
|
+
* Adds a callback to the queue and returns a promise that resolves when it executes.
|
|
9
|
+
* @template T - The type the callback promise resolves to
|
|
10
|
+
* @param callback - Async function to queue
|
|
11
|
+
* @returns Promise that resolves with the callback's result when executed
|
|
12
|
+
*/
|
|
4
13
|
Next<T>(callback: () => Promise<T>): Promise<T>;
|
|
14
|
+
/**
|
|
15
|
+
* Clears the queue and stops execution.
|
|
16
|
+
*/
|
|
5
17
|
Stop(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Starts queue execution if not already running.
|
|
20
|
+
* @private
|
|
21
|
+
*/
|
|
6
22
|
private Start;
|
|
23
|
+
/**
|
|
24
|
+
* Executes the next callback in the queue recursively until empty.
|
|
25
|
+
* @private
|
|
26
|
+
*/
|
|
7
27
|
private ExecuteQueue;
|
|
8
28
|
}
|
package/Utils/asyncQueue.js
CHANGED
|
@@ -2,11 +2,20 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AsyncQueue = void 0;
|
|
4
4
|
const list_1 = require("./list");
|
|
5
|
+
/**
|
|
6
|
+
* A queue that executes async callbacks sequentially, one at a time.
|
|
7
|
+
*/
|
|
5
8
|
class AsyncQueue {
|
|
6
9
|
constructor() {
|
|
7
10
|
this.running = false;
|
|
8
11
|
this.queue = list_1.List.Create();
|
|
9
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Adds a callback to the queue and returns a promise that resolves when it executes.
|
|
15
|
+
* @template T - The type the callback promise resolves to
|
|
16
|
+
* @param callback - Async function to queue
|
|
17
|
+
* @returns Promise that resolves with the callback's result when executed
|
|
18
|
+
*/
|
|
10
19
|
Next(callback) {
|
|
11
20
|
const ret = new Promise((resolve, reject) => {
|
|
12
21
|
list_1.List.Add(this.queue, async function () {
|
|
@@ -22,15 +31,26 @@ class AsyncQueue {
|
|
|
22
31
|
this.Start();
|
|
23
32
|
return ret;
|
|
24
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Clears the queue and stops execution.
|
|
36
|
+
*/
|
|
25
37
|
Stop() {
|
|
26
38
|
list_1.List.Clear(this.queue);
|
|
27
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Starts queue execution if not already running.
|
|
42
|
+
* @private
|
|
43
|
+
*/
|
|
28
44
|
Start() {
|
|
29
45
|
if (this.running)
|
|
30
46
|
return;
|
|
31
47
|
this.running = true;
|
|
32
48
|
this.ExecuteQueue();
|
|
33
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Executes the next callback in the queue recursively until empty.
|
|
52
|
+
* @private
|
|
53
|
+
*/
|
|
34
54
|
async ExecuteQueue() {
|
|
35
55
|
const callback = list_1.List.Pop(this.queue);
|
|
36
56
|
if (callback !== undefined) {
|