@flowgram-vue/variable-core 0.2.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/LICENSE +22 -0
- package/dist/index.cjs +2630 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2293 -0
- package/dist/index.js +2592 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
- package/src/ast/ast-node.ts +374 -0
- package/src/ast/ast-registers.ts +129 -0
- package/src/ast/common/data-node.ts +63 -0
- package/src/ast/common/index.ts +8 -0
- package/src/ast/common/list-node.ts +70 -0
- package/src/ast/common/map-node.ts +89 -0
- package/src/ast/declaration/base-variable-field.ts +184 -0
- package/src/ast/declaration/index.ts +13 -0
- package/src/ast/declaration/property.ts +19 -0
- package/src/ast/declaration/variable-declaration-list.ts +112 -0
- package/src/ast/declaration/variable-declaration.ts +78 -0
- package/src/ast/expression/base-expression.ts +117 -0
- package/src/ast/expression/enumerate-expression.ts +77 -0
- package/src/ast/expression/index.ts +10 -0
- package/src/ast/expression/keypath-expression.ts +157 -0
- package/src/ast/expression/legacy-keypath-expression.ts +119 -0
- package/src/ast/expression/wrap-array-expression.ts +96 -0
- package/src/ast/factory.ts +163 -0
- package/src/ast/flags.ts +50 -0
- package/src/ast/index.ts +26 -0
- package/src/ast/match.ts +146 -0
- package/src/ast/type/array.ts +109 -0
- package/src/ast/type/base-type.ts +49 -0
- package/src/ast/type/boolean.ts +26 -0
- package/src/ast/type/custom-type.ts +70 -0
- package/src/ast/type/index.ts +19 -0
- package/src/ast/type/integer.ts +29 -0
- package/src/ast/type/map.ts +96 -0
- package/src/ast/type/number.ts +26 -0
- package/src/ast/type/object.ts +185 -0
- package/src/ast/type/string.ts +55 -0
- package/src/ast/type/union.ts +13 -0
- package/src/ast/types.ts +188 -0
- package/src/ast/utils/expression.ts +61 -0
- package/src/ast/utils/helpers.ts +73 -0
- package/src/ast/utils/inversify.ts +42 -0
- package/src/ast/utils/observable.ts +5 -0
- package/src/ast/utils/variable-field.ts +25 -0
- package/src/composables/index.ts +9 -0
- package/src/composables/scope-provider.ts +78 -0
- package/src/composables/use-available-variables.ts +39 -0
- package/src/composables/use-output-variables.ts +36 -0
- package/src/composables/use-scope-available.ts +32 -0
- package/src/index.ts +14 -0
- package/src/providers.ts +22 -0
- package/src/scope/datas/index.ts +8 -0
- package/src/scope/datas/scope-available-data.ts +234 -0
- package/src/scope/datas/scope-event-data.ts +67 -0
- package/src/scope/datas/scope-output-data.ts +151 -0
- package/src/scope/index.ts +9 -0
- package/src/scope/scope-chain.ts +69 -0
- package/src/scope/scope.ts +200 -0
- package/src/scope/types.ts +102 -0
- package/src/scope/variable-table.ts +203 -0
- package/src/services/index.ts +6 -0
- package/src/services/variable-field-key-rename-service.ts +131 -0
- package/src/utils/memo.ts +38 -0
- package/src/utils/toDisposable.ts +16 -0
- package/src/variable-container-module.ts +28 -0
- package/src/variable-engine.ts +197 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2592 @@
|
|
|
1
|
+
import { inject, injectable, preDestroy, postConstruct, ContainerModule } from 'inversify';
|
|
2
|
+
import { Subject, distinctUntilChanged, switchMap, merge, skip, share, BehaviorSubject, map, tap, debounceTime, animationFrameScheduler, of, combineLatest, filter, startWith } from 'rxjs';
|
|
3
|
+
import { DisposableCollection, Emitter, Disposable } from '@flowgram-vue/utils';
|
|
4
|
+
import { omit, difference, xor, omitBy, isNil, intersection, flatten } from 'lodash-es';
|
|
5
|
+
import { nanoid } from 'nanoid';
|
|
6
|
+
import { shallowEqual } from 'fast-equals';
|
|
7
|
+
import { defineComponent, computed, provide, inject as inject$1, unref, shallowRef, onScopeDispose } from 'vue';
|
|
8
|
+
import { useService } from '@flowgram-vue/core';
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
13
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
14
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
15
|
+
if (decorator = decorators[i])
|
|
16
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
17
|
+
if (kind && result) __defProp(target, key, result);
|
|
18
|
+
return result;
|
|
19
|
+
};
|
|
20
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
21
|
+
function subsToDisposable(subscription) {
|
|
22
|
+
return Disposable.create(() => subscription.unsubscribe());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/utils/memo.ts
|
|
26
|
+
var createMemo = () => {
|
|
27
|
+
const _memoCache = /* @__PURE__ */ new Map();
|
|
28
|
+
const memo = (key, fn) => {
|
|
29
|
+
if (_memoCache.has(key)) {
|
|
30
|
+
return _memoCache.get(key);
|
|
31
|
+
}
|
|
32
|
+
const data = fn();
|
|
33
|
+
_memoCache.set(key, data);
|
|
34
|
+
return data;
|
|
35
|
+
};
|
|
36
|
+
const clear = (key) => {
|
|
37
|
+
if (key) {
|
|
38
|
+
_memoCache.delete(key);
|
|
39
|
+
} else {
|
|
40
|
+
_memoCache.clear();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
memo.clear = clear;
|
|
44
|
+
return memo;
|
|
45
|
+
};
|
|
46
|
+
var VariableTable = class {
|
|
47
|
+
constructor(parentTable) {
|
|
48
|
+
this.parentTable = parentTable;
|
|
49
|
+
this.table = /* @__PURE__ */ new Map();
|
|
50
|
+
this.toDispose = new DisposableCollection();
|
|
51
|
+
/**
|
|
52
|
+
* @deprecated
|
|
53
|
+
*/
|
|
54
|
+
this.onDataChangeEmitter = new Emitter();
|
|
55
|
+
this.variables$ = new Subject();
|
|
56
|
+
/**
|
|
57
|
+
* An observable that listens for value changes on any variable within the table.
|
|
58
|
+
*/
|
|
59
|
+
this.anyVariableChange$ = this.variables$.pipe(
|
|
60
|
+
switchMap(
|
|
61
|
+
(_variables) => merge(
|
|
62
|
+
..._variables.map(
|
|
63
|
+
(_v) => _v.value$.pipe(
|
|
64
|
+
// Skip the initial value of the BehaviorSubject
|
|
65
|
+
skip(1)
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
),
|
|
70
|
+
share()
|
|
71
|
+
);
|
|
72
|
+
/**
|
|
73
|
+
* @deprecated Use onListOrAnyVarChange instead.
|
|
74
|
+
*/
|
|
75
|
+
this.onDataChange = this.onDataChangeEmitter.event;
|
|
76
|
+
this._version = 0;
|
|
77
|
+
this.toDispose.pushAll([
|
|
78
|
+
this.onDataChangeEmitter,
|
|
79
|
+
// Activate the share() operator
|
|
80
|
+
this.onAnyVariableChange(() => {
|
|
81
|
+
this.bumpVersion();
|
|
82
|
+
})
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Subscribes to updates on any variable in the list.
|
|
87
|
+
* @param observer A function to be called when any variable's value changes.
|
|
88
|
+
* @returns A disposable object to unsubscribe from the updates.
|
|
89
|
+
*/
|
|
90
|
+
onAnyVariableChange(observer) {
|
|
91
|
+
return subsToDisposable(this.anyVariableChange$.subscribe(observer));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Subscribes to changes in the variable list (additions or removals).
|
|
95
|
+
* @param observer A function to be called when the list of variables changes.
|
|
96
|
+
* @returns A disposable object to unsubscribe from the updates.
|
|
97
|
+
*/
|
|
98
|
+
onVariableListChange(observer) {
|
|
99
|
+
return subsToDisposable(this.variables$.subscribe(observer));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Subscribes to both variable list changes and updates to any variable in the list.
|
|
103
|
+
* @param observer A function to be called when either the list or a variable in it changes.
|
|
104
|
+
* @returns A disposable collection to unsubscribe from both events.
|
|
105
|
+
*/
|
|
106
|
+
onListOrAnyVarChange(observer) {
|
|
107
|
+
const disposables = new DisposableCollection();
|
|
108
|
+
disposables.pushAll([this.onVariableListChange(observer), this.onAnyVariableChange(observer)]);
|
|
109
|
+
return disposables;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Fires change events to notify listeners that the data has been updated.
|
|
113
|
+
*/
|
|
114
|
+
fireChange() {
|
|
115
|
+
this.bumpVersion();
|
|
116
|
+
this.onDataChangeEmitter.fire();
|
|
117
|
+
this.variables$.next(this.variables);
|
|
118
|
+
this.parentTable?.fireChange();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The current version of the variable table, incremented on each change.
|
|
122
|
+
*/
|
|
123
|
+
get version() {
|
|
124
|
+
return this._version;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Increments the version number, resetting to 0 if it reaches MAX_SAFE_INTEGER.
|
|
128
|
+
*/
|
|
129
|
+
bumpVersion() {
|
|
130
|
+
this._version = this._version + 1;
|
|
131
|
+
if (this._version === Number.MAX_SAFE_INTEGER) {
|
|
132
|
+
this._version = 0;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* An array of all variables in the table.
|
|
137
|
+
*/
|
|
138
|
+
get variables() {
|
|
139
|
+
return Array.from(this.table.values());
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* An array of all variable keys in the table.
|
|
143
|
+
*/
|
|
144
|
+
get variableKeys() {
|
|
145
|
+
return Array.from(this.table.keys());
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Retrieves a variable or a nested property field by its key path.
|
|
149
|
+
* @param keyPath An array of keys representing the path to the desired field.
|
|
150
|
+
* @returns The found variable or property field, or undefined if not found.
|
|
151
|
+
*/
|
|
152
|
+
getByKeyPath(keyPath) {
|
|
153
|
+
const [variableKey, ...propertyKeys] = keyPath || [];
|
|
154
|
+
if (!variableKey) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const variable = this.getVariableByKey(variableKey);
|
|
158
|
+
return propertyKeys.length ? variable?.getByKeyPath(propertyKeys) : variable;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Retrieves a variable by its key.
|
|
162
|
+
* @param key The key of the variable to retrieve.
|
|
163
|
+
* @returns The variable declaration if found, otherwise undefined.
|
|
164
|
+
*/
|
|
165
|
+
getVariableByKey(key) {
|
|
166
|
+
return this.table.get(key);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Adds a variable to the table.
|
|
170
|
+
* If a parent table exists, the variable is also added to the parent.
|
|
171
|
+
* @param variable The variable declaration to add.
|
|
172
|
+
*/
|
|
173
|
+
addVariableToTable(variable) {
|
|
174
|
+
this.table.set(variable.key, variable);
|
|
175
|
+
if (this.parentTable) {
|
|
176
|
+
this.parentTable.addVariableToTable(variable);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Removes a variable from the table.
|
|
181
|
+
* If a parent table exists, the variable is also removed from the parent.
|
|
182
|
+
* @param key The key of the variable to remove.
|
|
183
|
+
*/
|
|
184
|
+
removeVariableFromTable(key) {
|
|
185
|
+
this.table.delete(key);
|
|
186
|
+
if (this.parentTable) {
|
|
187
|
+
this.parentTable.removeVariableFromTable(key);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Disposes of all resources used by the variable table.
|
|
192
|
+
*/
|
|
193
|
+
dispose() {
|
|
194
|
+
this.variableKeys.forEach(
|
|
195
|
+
(_key) => this.parentTable?.removeVariableFromTable(_key)
|
|
196
|
+
);
|
|
197
|
+
this.parentTable?.fireChange();
|
|
198
|
+
this.variables$.complete();
|
|
199
|
+
this.variables$.unsubscribe();
|
|
200
|
+
this.toDispose.dispose();
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// src/providers.ts
|
|
205
|
+
var VariableEngineProvider = /* @__PURE__ */ Symbol("DynamicVariableEngine");
|
|
206
|
+
var ContainerProvider = /* @__PURE__ */ Symbol("ContainerProvider");
|
|
207
|
+
|
|
208
|
+
// src/scope/scope-chain.ts
|
|
209
|
+
var ScopeChain = class {
|
|
210
|
+
constructor() {
|
|
211
|
+
this.toDispose = new DisposableCollection();
|
|
212
|
+
}
|
|
213
|
+
get variableEngine() {
|
|
214
|
+
return this.variableEngineProvider();
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Refreshes the dependency and coverage relationships for all scopes.
|
|
218
|
+
*/
|
|
219
|
+
refreshAllChange() {
|
|
220
|
+
this.variableEngine.getAllScopes().forEach((_scope) => {
|
|
221
|
+
_scope.refreshCovers();
|
|
222
|
+
_scope.refreshDeps();
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
dispose() {
|
|
226
|
+
this.toDispose.dispose();
|
|
227
|
+
}
|
|
228
|
+
get disposed() {
|
|
229
|
+
return this.toDispose.disposed;
|
|
230
|
+
}
|
|
231
|
+
get onDispose() {
|
|
232
|
+
return this.toDispose.onDispose;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
__decorateClass([
|
|
236
|
+
inject(VariableEngineProvider)
|
|
237
|
+
], ScopeChain.prototype, "variableEngineProvider", 2);
|
|
238
|
+
ScopeChain = __decorateClass([
|
|
239
|
+
injectable()
|
|
240
|
+
], ScopeChain);
|
|
241
|
+
|
|
242
|
+
// src/ast/types.ts
|
|
243
|
+
var ASTKind = /* @__PURE__ */ ((ASTKind2) => {
|
|
244
|
+
ASTKind2["String"] = "String";
|
|
245
|
+
ASTKind2["Number"] = "Number";
|
|
246
|
+
ASTKind2["Integer"] = "Integer";
|
|
247
|
+
ASTKind2["Boolean"] = "Boolean";
|
|
248
|
+
ASTKind2["Object"] = "Object";
|
|
249
|
+
ASTKind2["Array"] = "Array";
|
|
250
|
+
ASTKind2["Map"] = "Map";
|
|
251
|
+
ASTKind2["Union"] = "Union";
|
|
252
|
+
ASTKind2["Any"] = "Any";
|
|
253
|
+
ASTKind2["CustomType"] = "CustomType";
|
|
254
|
+
ASTKind2["Property"] = "Property";
|
|
255
|
+
ASTKind2["VariableDeclaration"] = "VariableDeclaration";
|
|
256
|
+
ASTKind2["VariableDeclarationList"] = "VariableDeclarationList";
|
|
257
|
+
ASTKind2["KeyPathExpression"] = "KeyPathExpression";
|
|
258
|
+
ASTKind2["EnumerateExpression"] = "EnumerateExpression";
|
|
259
|
+
ASTKind2["WrapArrayExpression"] = "WrapArrayExpression";
|
|
260
|
+
ASTKind2["ListNode"] = "ListNode";
|
|
261
|
+
ASTKind2["DataNode"] = "DataNode";
|
|
262
|
+
ASTKind2["MapNode"] = "MapNode";
|
|
263
|
+
return ASTKind2;
|
|
264
|
+
})(ASTKind || {});
|
|
265
|
+
|
|
266
|
+
// src/ast/utils/inversify.ts
|
|
267
|
+
var injectToAST = (serviceIdentifier) => function(target, propertyKey) {
|
|
268
|
+
if (!serviceIdentifier) {
|
|
269
|
+
throw new Error(
|
|
270
|
+
`ServiceIdentifier ${serviceIdentifier} in @lazyInject is Empty, it might be caused by file circular dependency, please check it.`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
const descriptor = {
|
|
274
|
+
get() {
|
|
275
|
+
const container = this.scope.variableEngine.container;
|
|
276
|
+
return container.get(serviceIdentifier);
|
|
277
|
+
},
|
|
278
|
+
set() {
|
|
279
|
+
},
|
|
280
|
+
configurable: true,
|
|
281
|
+
enumerable: true
|
|
282
|
+
};
|
|
283
|
+
return descriptor;
|
|
284
|
+
};
|
|
285
|
+
var POST_CONSTRUCT_AST_SYMBOL = /* @__PURE__ */ Symbol("post_construct_ast");
|
|
286
|
+
var postConstructAST = () => (target, propertyKey) => {
|
|
287
|
+
if (!Reflect.hasMetadata(POST_CONSTRUCT_AST_SYMBOL, target)) {
|
|
288
|
+
Reflect.defineMetadata(POST_CONSTRUCT_AST_SYMBOL, propertyKey, target);
|
|
289
|
+
} else {
|
|
290
|
+
throw Error("Duplication Post Construct AST");
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// src/ast/flags.ts
|
|
295
|
+
var ASTNodeFlags = /* @__PURE__ */ ((ASTNodeFlags2) => {
|
|
296
|
+
ASTNodeFlags2[ASTNodeFlags2["None"] = 0] = "None";
|
|
297
|
+
ASTNodeFlags2[ASTNodeFlags2["VariableField"] = 1] = "VariableField";
|
|
298
|
+
ASTNodeFlags2[ASTNodeFlags2["Expression"] = 4] = "Expression";
|
|
299
|
+
ASTNodeFlags2[ASTNodeFlags2["BasicType"] = 8] = "BasicType";
|
|
300
|
+
ASTNodeFlags2[ASTNodeFlags2["DrilldownType"] = 16] = "DrilldownType";
|
|
301
|
+
ASTNodeFlags2[ASTNodeFlags2["EnumerateType"] = 32] = "EnumerateType";
|
|
302
|
+
ASTNodeFlags2[ASTNodeFlags2["UnionType"] = 64] = "UnionType";
|
|
303
|
+
ASTNodeFlags2[ASTNodeFlags2["VariableType"] = 120] = "VariableType";
|
|
304
|
+
return ASTNodeFlags2;
|
|
305
|
+
})(ASTNodeFlags || {});
|
|
306
|
+
|
|
307
|
+
// src/ast/match.ts
|
|
308
|
+
var ASTMatch;
|
|
309
|
+
((ASTMatch2) => {
|
|
310
|
+
ASTMatch2.isString = (node) => node?.kind === "String" /* String */;
|
|
311
|
+
ASTMatch2.isNumber = (node) => node?.kind === "Number" /* Number */;
|
|
312
|
+
ASTMatch2.isBoolean = (node) => node?.kind === "Boolean" /* Boolean */;
|
|
313
|
+
ASTMatch2.isInteger = (node) => node?.kind === "Integer" /* Integer */;
|
|
314
|
+
ASTMatch2.isObject = (node) => node?.kind === "Object" /* Object */;
|
|
315
|
+
ASTMatch2.isArray = (node) => node?.kind === "Array" /* Array */;
|
|
316
|
+
ASTMatch2.isMap = (node) => node?.kind === "Map" /* Map */;
|
|
317
|
+
ASTMatch2.isCustomType = (node) => node?.kind === "CustomType" /* CustomType */;
|
|
318
|
+
ASTMatch2.isVariableDeclaration = (node) => node?.kind === "VariableDeclaration" /* VariableDeclaration */;
|
|
319
|
+
ASTMatch2.isProperty = (node) => node?.kind === "Property" /* Property */;
|
|
320
|
+
ASTMatch2.isBaseVariableField = (node) => !!(node?.flags || 0 & 1 /* VariableField */);
|
|
321
|
+
ASTMatch2.isVariableDeclarationList = (node) => node?.kind === "VariableDeclarationList" /* VariableDeclarationList */;
|
|
322
|
+
ASTMatch2.isEnumerateExpression = (node) => node?.kind === "EnumerateExpression" /* EnumerateExpression */;
|
|
323
|
+
ASTMatch2.isWrapArrayExpression = (node) => node?.kind === "WrapArrayExpression" /* WrapArrayExpression */;
|
|
324
|
+
ASTMatch2.isKeyPathExpression = (node) => node?.kind === "KeyPathExpression" /* KeyPathExpression */;
|
|
325
|
+
function is(node, targetType) {
|
|
326
|
+
return node?.kind === targetType?.kind;
|
|
327
|
+
}
|
|
328
|
+
ASTMatch2.is = is;
|
|
329
|
+
})(ASTMatch || (ASTMatch = {}));
|
|
330
|
+
|
|
331
|
+
// src/ast/utils/helpers.ts
|
|
332
|
+
function updateChildNodeHelper({
|
|
333
|
+
getChildNode,
|
|
334
|
+
updateChildNode,
|
|
335
|
+
removeChildNode,
|
|
336
|
+
nextJSON
|
|
337
|
+
}) {
|
|
338
|
+
const currNode = getChildNode();
|
|
339
|
+
const isNewKind = currNode?.kind !== nextJSON?.kind;
|
|
340
|
+
const isNewKey = nextJSON?.key && nextJSON?.key !== currNode?.key;
|
|
341
|
+
if (isNewKind || isNewKey) {
|
|
342
|
+
if (currNode) {
|
|
343
|
+
currNode.dispose();
|
|
344
|
+
removeChildNode();
|
|
345
|
+
}
|
|
346
|
+
if (nextJSON) {
|
|
347
|
+
const newNode = this.createChildNode(nextJSON);
|
|
348
|
+
updateChildNode(newNode);
|
|
349
|
+
this.fireChange();
|
|
350
|
+
return newNode;
|
|
351
|
+
} else {
|
|
352
|
+
this.fireChange();
|
|
353
|
+
}
|
|
354
|
+
} else if (nextJSON) {
|
|
355
|
+
currNode?.fromJSON(nextJSON);
|
|
356
|
+
}
|
|
357
|
+
return currNode;
|
|
358
|
+
}
|
|
359
|
+
function parseTypeJsonOrKind(typeJSONOrKind) {
|
|
360
|
+
return typeof typeJSONOrKind === "string" ? { kind: typeJSONOrKind } : typeJSONOrKind;
|
|
361
|
+
}
|
|
362
|
+
function getAllChildren(ast) {
|
|
363
|
+
return [...ast.children, ...ast.children.map((_child) => getAllChildren(_child)).flat()];
|
|
364
|
+
}
|
|
365
|
+
function isMatchAST(node, targetType) {
|
|
366
|
+
return ASTMatch.is(node, targetType);
|
|
367
|
+
}
|
|
368
|
+
var ASTNode = class _ASTNode {
|
|
369
|
+
/**
|
|
370
|
+
* Constructor.
|
|
371
|
+
* @param createParams Necessary parameters for creating an ASTNode.
|
|
372
|
+
* @param injectOptions Dependency injection for various modules.
|
|
373
|
+
*/
|
|
374
|
+
constructor({ key, parent, scope }, opts) {
|
|
375
|
+
/**
|
|
376
|
+
* Node flags, used to record some flag information.
|
|
377
|
+
*/
|
|
378
|
+
this.flags = 0 /* None */;
|
|
379
|
+
/**
|
|
380
|
+
* The version number of the ASTNode, which increments by 1 each time `fireChange` is called.
|
|
381
|
+
*/
|
|
382
|
+
this._version = 0;
|
|
383
|
+
/**
|
|
384
|
+
* Update lock.
|
|
385
|
+
* - When set to `true`, `fireChange` will not trigger any events.
|
|
386
|
+
* - This is useful when multiple updates are needed, and you want to avoid multiple triggers.
|
|
387
|
+
*/
|
|
388
|
+
this.changeLocked = false;
|
|
389
|
+
/**
|
|
390
|
+
* Parameters related to batch updates.
|
|
391
|
+
*/
|
|
392
|
+
this._batch = {
|
|
393
|
+
batching: false,
|
|
394
|
+
hasChangesInBatch: false
|
|
395
|
+
};
|
|
396
|
+
/**
|
|
397
|
+
* AST node change Observable events, implemented based on RxJS.
|
|
398
|
+
* - Emits the current ASTNode value upon subscription.
|
|
399
|
+
* - Emits a new value whenever `fireChange` is called.
|
|
400
|
+
*/
|
|
401
|
+
this.value$ = new BehaviorSubject(this);
|
|
402
|
+
/**
|
|
403
|
+
* Child ASTNodes.
|
|
404
|
+
*/
|
|
405
|
+
this._children = /* @__PURE__ */ new Set();
|
|
406
|
+
/**
|
|
407
|
+
* List of disposal handlers for the ASTNode.
|
|
408
|
+
*/
|
|
409
|
+
this.toDispose = new DisposableCollection(
|
|
410
|
+
Disposable.create(() => {
|
|
411
|
+
this.parent?.fireChange();
|
|
412
|
+
this.children.forEach((child) => child.dispose());
|
|
413
|
+
})
|
|
414
|
+
);
|
|
415
|
+
/**
|
|
416
|
+
* Callback triggered upon disposal.
|
|
417
|
+
*/
|
|
418
|
+
this.onDispose = this.toDispose.onDispose;
|
|
419
|
+
this.scope = scope;
|
|
420
|
+
this.parent = parent;
|
|
421
|
+
this.opts = opts;
|
|
422
|
+
this.key = key || nanoid();
|
|
423
|
+
this.fromJSON = this.withBatchUpdate(this.fromJSON.bind(this));
|
|
424
|
+
const rawToJSON = this.toJSON?.bind(this);
|
|
425
|
+
this.toJSON = () => omitBy(
|
|
426
|
+
{
|
|
427
|
+
// always include kind
|
|
428
|
+
kind: this.kind,
|
|
429
|
+
...rawToJSON?.() || {}
|
|
430
|
+
},
|
|
431
|
+
// remove undefined fields
|
|
432
|
+
isNil
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* The type of the ASTNode.
|
|
437
|
+
*/
|
|
438
|
+
get kind() {
|
|
439
|
+
if (!this.constructor.kind) {
|
|
440
|
+
throw new Error(`ASTNode Registry need a kind: ${this.constructor.name}`);
|
|
441
|
+
}
|
|
442
|
+
return this.constructor.kind;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Gets all child ASTNodes of the current ASTNode.
|
|
446
|
+
*/
|
|
447
|
+
get children() {
|
|
448
|
+
return Array.from(this._children);
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Creates a child ASTNode.
|
|
452
|
+
* @param json The AST JSON of the child ASTNode.
|
|
453
|
+
* @returns
|
|
454
|
+
*/
|
|
455
|
+
createChildNode(json) {
|
|
456
|
+
const astRegisters = this.scope.variableEngine.astRegisters;
|
|
457
|
+
const child = astRegisters.createAST(json, {
|
|
458
|
+
parent: this,
|
|
459
|
+
scope: this.scope
|
|
460
|
+
});
|
|
461
|
+
this._children.add(child);
|
|
462
|
+
child.toDispose.push(
|
|
463
|
+
Disposable.create(() => {
|
|
464
|
+
this._children.delete(child);
|
|
465
|
+
})
|
|
466
|
+
);
|
|
467
|
+
return child;
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Updates a child ASTNode, quickly implementing the consumption logic for child ASTNode updates.
|
|
471
|
+
* @param keyInThis The specified key on the current object.
|
|
472
|
+
*/
|
|
473
|
+
updateChildNodeByKey(keyInThis, nextJSON) {
|
|
474
|
+
this.withBatchUpdate(updateChildNodeHelper).call(this, {
|
|
475
|
+
getChildNode: () => this[keyInThis],
|
|
476
|
+
updateChildNode: (_node) => this[keyInThis] = _node,
|
|
477
|
+
removeChildNode: () => this[keyInThis] = void 0,
|
|
478
|
+
nextJSON
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Batch updates the ASTNode, merging all `fireChange` calls within the batch function into one.
|
|
483
|
+
* @param updater The batch function.
|
|
484
|
+
* @returns
|
|
485
|
+
*/
|
|
486
|
+
withBatchUpdate(updater) {
|
|
487
|
+
return (...args) => {
|
|
488
|
+
if (this._batch.batching) {
|
|
489
|
+
return updater.call(this, ...args);
|
|
490
|
+
}
|
|
491
|
+
this._batch.hasChangesInBatch = false;
|
|
492
|
+
this._batch.batching = true;
|
|
493
|
+
const res = updater.call(this, ...args);
|
|
494
|
+
this._batch.batching = false;
|
|
495
|
+
if (this._batch.hasChangesInBatch) {
|
|
496
|
+
this.fireChange();
|
|
497
|
+
}
|
|
498
|
+
this._batch.hasChangesInBatch = false;
|
|
499
|
+
return res;
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Triggers an update for the current node.
|
|
504
|
+
*/
|
|
505
|
+
fireChange() {
|
|
506
|
+
if (this.changeLocked || this.disposed) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
if (this._batch.batching) {
|
|
510
|
+
this._batch.hasChangesInBatch = true;
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
this._version++;
|
|
514
|
+
this.value$.next(this);
|
|
515
|
+
this.dispatchGlobalEvent({ type: "UpdateAST" });
|
|
516
|
+
this.parent?.fireChange();
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* The version value of the ASTNode.
|
|
520
|
+
* - You can used to check whether ASTNode are updated.
|
|
521
|
+
*/
|
|
522
|
+
get version() {
|
|
523
|
+
return this._version;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* The unique hash value of the ASTNode.
|
|
527
|
+
* - It will update when the ASTNode is updated.
|
|
528
|
+
* - You can used to check two ASTNode are equal.
|
|
529
|
+
*/
|
|
530
|
+
get hash() {
|
|
531
|
+
return `${this._version}${this.kind}${this.key}`;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Listens for changes to the ASTNode.
|
|
535
|
+
* @param observer The listener callback.
|
|
536
|
+
* @param selector Listens for specified data.
|
|
537
|
+
* @returns
|
|
538
|
+
*/
|
|
539
|
+
subscribe(observer, { selector, debounceAnimation, triggerOnInit } = {}) {
|
|
540
|
+
return subsToDisposable(
|
|
541
|
+
this.value$.pipe(
|
|
542
|
+
map(() => selector ? selector(this) : this),
|
|
543
|
+
distinctUntilChanged(
|
|
544
|
+
(a, b) => shallowEqual(a, b),
|
|
545
|
+
(value) => {
|
|
546
|
+
if (value instanceof _ASTNode) {
|
|
547
|
+
return value.hash;
|
|
548
|
+
}
|
|
549
|
+
return value;
|
|
550
|
+
}
|
|
551
|
+
),
|
|
552
|
+
// By default, skip the first trigger of BehaviorSubject.
|
|
553
|
+
triggerOnInit ? tap(() => null) : skip(1),
|
|
554
|
+
// All updates within each animationFrame are merged into one.
|
|
555
|
+
debounceAnimation ? debounceTime(0, animationFrameScheduler) : tap(() => null)
|
|
556
|
+
).subscribe(observer)
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Dispatches a global event for the current ASTNode.
|
|
561
|
+
* @param event The global event.
|
|
562
|
+
*/
|
|
563
|
+
dispatchGlobalEvent(event) {
|
|
564
|
+
this.scope.event.dispatch({
|
|
565
|
+
...event,
|
|
566
|
+
ast: this
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Disposes the ASTNode.
|
|
571
|
+
*/
|
|
572
|
+
dispose() {
|
|
573
|
+
if (this.toDispose.disposed) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
this.toDispose.dispose();
|
|
577
|
+
this.dispatchGlobalEvent({ type: "DisposeAST" });
|
|
578
|
+
this.value$.complete();
|
|
579
|
+
this.value$.unsubscribe();
|
|
580
|
+
}
|
|
581
|
+
get disposed() {
|
|
582
|
+
return this.toDispose.disposed;
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
// src/ast/type/base-type.ts
|
|
587
|
+
var BaseType = class extends ASTNode {
|
|
588
|
+
constructor() {
|
|
589
|
+
super(...arguments);
|
|
590
|
+
this.flags = 8 /* BasicType */;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Check if the current type is equal to the target type.
|
|
594
|
+
* @param targetTypeJSONOrKind The type to compare with.
|
|
595
|
+
* @returns `true` if the types are equal, `false` otherwise.
|
|
596
|
+
*/
|
|
597
|
+
isTypeEqual(targetTypeJSONOrKind) {
|
|
598
|
+
const targetTypeJSON = parseTypeJsonOrKind(targetTypeJSONOrKind);
|
|
599
|
+
if (targetTypeJSON?.kind === "Union" /* Union */) {
|
|
600
|
+
return (targetTypeJSON?.types || [])?.some(
|
|
601
|
+
(_subType) => this.isTypeEqual(_subType)
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
return this.kind === targetTypeJSON?.kind;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Get a variable field by key path.
|
|
608
|
+
*
|
|
609
|
+
* This method should be implemented by drillable types.
|
|
610
|
+
* @param keyPath The key path to search for.
|
|
611
|
+
* @returns The variable field if found, otherwise `undefined`.
|
|
612
|
+
*/
|
|
613
|
+
getByKeyPath(keyPath = []) {
|
|
614
|
+
throw new Error(`Get By Key Path is not implemented for Type: ${this.kind}`);
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
// src/ast/type/array.ts
|
|
619
|
+
var ArrayType = class extends BaseType {
|
|
620
|
+
constructor() {
|
|
621
|
+
super(...arguments);
|
|
622
|
+
this.flags = 16 /* DrilldownType */ | 32 /* EnumerateType */;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Deserializes the `ArrayJSON` to the `ArrayType`.
|
|
626
|
+
* @param json The `ArrayJSON` to deserialize.
|
|
627
|
+
*/
|
|
628
|
+
fromJSON({ items }) {
|
|
629
|
+
this.updateChildNodeByKey("items", parseTypeJsonOrKind(items));
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Whether the items type can be drilled down.
|
|
633
|
+
*/
|
|
634
|
+
get canDrilldownItems() {
|
|
635
|
+
return !!(this.items?.flags & 16 /* DrilldownType */);
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Get a variable field by key path.
|
|
639
|
+
* @param keyPath The key path to search for.
|
|
640
|
+
* @returns The variable field if found, otherwise `undefined`.
|
|
641
|
+
*/
|
|
642
|
+
getByKeyPath(keyPath) {
|
|
643
|
+
const [curr, ...rest] = keyPath || [];
|
|
644
|
+
if (curr === "0" && this.canDrilldownItems) {
|
|
645
|
+
return this.items.getByKeyPath(rest);
|
|
646
|
+
}
|
|
647
|
+
return void 0;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Check if the current type is equal to the target type.
|
|
651
|
+
* @param targetTypeJSONOrKind The type to compare with.
|
|
652
|
+
* @returns `true` if the types are equal, `false` otherwise.
|
|
653
|
+
*/
|
|
654
|
+
isTypeEqual(targetTypeJSONOrKind) {
|
|
655
|
+
const targetTypeJSON = parseTypeJsonOrKind(targetTypeJSONOrKind);
|
|
656
|
+
const isSuperEqual = super.isTypeEqual(targetTypeJSONOrKind);
|
|
657
|
+
if (targetTypeJSON?.weak || targetTypeJSON?.kind === "Union" /* Union */) {
|
|
658
|
+
return isSuperEqual;
|
|
659
|
+
}
|
|
660
|
+
return targetTypeJSON && isSuperEqual && // Weak comparison, only need to compare the Kind.
|
|
661
|
+
(targetTypeJSON?.weak || this.customStrongEqual(targetTypeJSON));
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Array strong comparison.
|
|
665
|
+
* @param targetTypeJSON The type to compare with.
|
|
666
|
+
* @returns `true` if the types are equal, `false` otherwise.
|
|
667
|
+
*/
|
|
668
|
+
customStrongEqual(targetTypeJSON) {
|
|
669
|
+
if (!this.items) {
|
|
670
|
+
return !targetTypeJSON?.items;
|
|
671
|
+
}
|
|
672
|
+
return this.items?.isTypeEqual(targetTypeJSON.items);
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Serialize the `ArrayType` to `ArrayJSON`
|
|
676
|
+
* @returns The JSON representation of `ArrayType`.
|
|
677
|
+
*/
|
|
678
|
+
toJSON() {
|
|
679
|
+
return {
|
|
680
|
+
kind: "Array" /* Array */,
|
|
681
|
+
items: this.items?.toJSON()
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
ArrayType.kind = "Array" /* Array */;
|
|
686
|
+
|
|
687
|
+
// src/ast/type/string.ts
|
|
688
|
+
var StringType = class extends BaseType {
|
|
689
|
+
constructor() {
|
|
690
|
+
super(...arguments);
|
|
691
|
+
this.flags = 8 /* BasicType */;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* see https://json-schema.org/understanding-json-schema/reference/string#format
|
|
695
|
+
*/
|
|
696
|
+
get format() {
|
|
697
|
+
return this._format;
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Deserialize the `StringJSON` to the `StringType`.
|
|
701
|
+
*
|
|
702
|
+
* @param json StringJSON representation of the `StringType`.
|
|
703
|
+
*/
|
|
704
|
+
fromJSON(json) {
|
|
705
|
+
if (json?.format !== this._format) {
|
|
706
|
+
this._format = json?.format;
|
|
707
|
+
this.fireChange();
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Serialize the `StringType` to `StringJSON`.
|
|
712
|
+
* @returns The JSON representation of `StringType`.
|
|
713
|
+
*/
|
|
714
|
+
toJSON() {
|
|
715
|
+
return {
|
|
716
|
+
format: this._format
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
StringType.kind = "String" /* String */;
|
|
721
|
+
|
|
722
|
+
// src/ast/type/integer.ts
|
|
723
|
+
var IntegerType = class extends BaseType {
|
|
724
|
+
constructor() {
|
|
725
|
+
super(...arguments);
|
|
726
|
+
this.flags = 8 /* BasicType */;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Deserializes the `IntegerJSON` to the `IntegerType`.
|
|
730
|
+
* @param json The `IntegerJSON` to deserialize.
|
|
731
|
+
*/
|
|
732
|
+
fromJSON() {
|
|
733
|
+
}
|
|
734
|
+
toJSON() {
|
|
735
|
+
return {};
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
IntegerType.kind = "Integer" /* Integer */;
|
|
739
|
+
|
|
740
|
+
// src/ast/type/boolean.ts
|
|
741
|
+
var BooleanType = class extends BaseType {
|
|
742
|
+
/**
|
|
743
|
+
* Deserializes the `BooleanJSON` to the `BooleanType`.
|
|
744
|
+
* @param json The `BooleanJSON` to deserialize.
|
|
745
|
+
*/
|
|
746
|
+
fromJSON() {
|
|
747
|
+
}
|
|
748
|
+
toJSON() {
|
|
749
|
+
return {};
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
BooleanType.kind = "Boolean" /* Boolean */;
|
|
753
|
+
|
|
754
|
+
// src/ast/type/number.ts
|
|
755
|
+
var NumberType = class extends BaseType {
|
|
756
|
+
/**
|
|
757
|
+
* Deserializes the `NumberJSON` to the `NumberType`.
|
|
758
|
+
* @param json The `NumberJSON` to deserialize.
|
|
759
|
+
*/
|
|
760
|
+
fromJSON() {
|
|
761
|
+
}
|
|
762
|
+
toJSON() {
|
|
763
|
+
return {};
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
NumberType.kind = "Number" /* Number */;
|
|
767
|
+
|
|
768
|
+
// src/ast/type/map.ts
|
|
769
|
+
var MapType = class extends BaseType {
|
|
770
|
+
/**
|
|
771
|
+
* Deserializes the `MapJSON` to the `MapType`.
|
|
772
|
+
* @param json The `MapJSON` to deserialize.
|
|
773
|
+
*/
|
|
774
|
+
fromJSON({ keyType = "String" /* String */, valueType }) {
|
|
775
|
+
this.updateChildNodeByKey("keyType", parseTypeJsonOrKind(keyType));
|
|
776
|
+
this.updateChildNodeByKey("valueType", parseTypeJsonOrKind(valueType));
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Check if the current type is equal to the target type.
|
|
780
|
+
* @param targetTypeJSONOrKind The type to compare with.
|
|
781
|
+
* @returns `true` if the types are equal, `false` otherwise.
|
|
782
|
+
*/
|
|
783
|
+
isTypeEqual(targetTypeJSONOrKind) {
|
|
784
|
+
const targetTypeJSON = parseTypeJsonOrKind(targetTypeJSONOrKind);
|
|
785
|
+
const isSuperEqual = super.isTypeEqual(targetTypeJSONOrKind);
|
|
786
|
+
if (targetTypeJSON?.weak || targetTypeJSON?.kind === "Union" /* Union */) {
|
|
787
|
+
return isSuperEqual;
|
|
788
|
+
}
|
|
789
|
+
return targetTypeJSON && isSuperEqual && // Weak comparison, only need to compare the Kind.
|
|
790
|
+
(targetTypeJSON?.weak || this.customStrongEqual(targetTypeJSON));
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Map strong comparison.
|
|
794
|
+
* @param targetTypeJSON The type to compare with.
|
|
795
|
+
* @returns `true` if the types are equal, `false` otherwise.
|
|
796
|
+
*/
|
|
797
|
+
customStrongEqual(targetTypeJSON) {
|
|
798
|
+
const { keyType = "String" /* String */, valueType } = targetTypeJSON;
|
|
799
|
+
const isValueTypeEqual = !valueType && !this.valueType || this.valueType?.isTypeEqual(valueType);
|
|
800
|
+
return isValueTypeEqual && this.keyType?.isTypeEqual(keyType);
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Serialize the node to a JSON object.
|
|
804
|
+
* @returns The JSON representation of the node.
|
|
805
|
+
*/
|
|
806
|
+
toJSON() {
|
|
807
|
+
return {
|
|
808
|
+
kind: "Map" /* Map */,
|
|
809
|
+
keyType: this.keyType?.toJSON(),
|
|
810
|
+
valueType: this.valueType?.toJSON()
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
MapType.kind = "Map" /* Map */;
|
|
815
|
+
var ObjectType = class extends BaseType {
|
|
816
|
+
constructor() {
|
|
817
|
+
super(...arguments);
|
|
818
|
+
this.flags = 16 /* DrilldownType */;
|
|
819
|
+
/**
|
|
820
|
+
* A map of property keys to `Property` instances.
|
|
821
|
+
*/
|
|
822
|
+
this.propertyTable = /* @__PURE__ */ new Map();
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Deserializes the `ObjectJSON` to the `ObjectType`.
|
|
826
|
+
* @param json The `ObjectJSON` to deserialize.
|
|
827
|
+
*/
|
|
828
|
+
fromJSON({ properties }) {
|
|
829
|
+
const removedKeys = new Set(this.propertyTable.keys());
|
|
830
|
+
const prev = [...this.properties || []];
|
|
831
|
+
this.properties = (properties || []).map((property) => {
|
|
832
|
+
const existProperty = this.propertyTable.get(property.key);
|
|
833
|
+
removedKeys.delete(property.key);
|
|
834
|
+
if (existProperty) {
|
|
835
|
+
existProperty.fromJSON(property);
|
|
836
|
+
return existProperty;
|
|
837
|
+
} else {
|
|
838
|
+
const newProperty = this.createChildNode({
|
|
839
|
+
...property,
|
|
840
|
+
kind: "Property" /* Property */
|
|
841
|
+
});
|
|
842
|
+
this.fireChange();
|
|
843
|
+
this.propertyTable.set(property.key, newProperty);
|
|
844
|
+
return newProperty;
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
removedKeys.forEach((key) => {
|
|
848
|
+
const property = this.propertyTable.get(key);
|
|
849
|
+
property?.dispose();
|
|
850
|
+
this.propertyTable.delete(key);
|
|
851
|
+
this.fireChange();
|
|
852
|
+
});
|
|
853
|
+
this.dispatchGlobalEvent({
|
|
854
|
+
type: "ObjectPropertiesChange",
|
|
855
|
+
payload: {
|
|
856
|
+
prev,
|
|
857
|
+
next: [...this.properties]
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Serialize the `ObjectType` to `ObjectJSON`.
|
|
863
|
+
* @returns The JSON representation of `ObjectType`.
|
|
864
|
+
*/
|
|
865
|
+
toJSON() {
|
|
866
|
+
return {
|
|
867
|
+
properties: this.properties.map((_property) => _property.toJSON())
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Get a variable field by key path.
|
|
872
|
+
* @param keyPath The key path to search for.
|
|
873
|
+
* @returns The variable field if found, otherwise `undefined`.
|
|
874
|
+
*/
|
|
875
|
+
getByKeyPath(keyPath) {
|
|
876
|
+
const [curr, ...restKeyPath] = keyPath;
|
|
877
|
+
const property = this.propertyTable.get(curr);
|
|
878
|
+
if (!restKeyPath.length) {
|
|
879
|
+
return property;
|
|
880
|
+
}
|
|
881
|
+
if (property?.type && property?.type?.flags & 16 /* DrilldownType */) {
|
|
882
|
+
return property.type.getByKeyPath(restKeyPath);
|
|
883
|
+
}
|
|
884
|
+
return void 0;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Check if the current type is equal to the target type.
|
|
888
|
+
* @param targetTypeJSONOrKind The type to compare with.
|
|
889
|
+
* @returns `true` if the types are equal, `false` otherwise.
|
|
890
|
+
*/
|
|
891
|
+
isTypeEqual(targetTypeJSONOrKind) {
|
|
892
|
+
const targetTypeJSON = parseTypeJsonOrKind(targetTypeJSONOrKind);
|
|
893
|
+
const isSuperEqual = super.isTypeEqual(targetTypeJSONOrKind);
|
|
894
|
+
if (targetTypeJSON?.weak || targetTypeJSON?.kind === "Union" /* Union */) {
|
|
895
|
+
return isSuperEqual;
|
|
896
|
+
}
|
|
897
|
+
return targetTypeJSON && isSuperEqual && // Weak comparison, only need to compare the Kind.
|
|
898
|
+
(targetTypeJSON?.weak || this.customStrongEqual(targetTypeJSON));
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Object type strong comparison.
|
|
902
|
+
* @param targetTypeJSON The type to compare with.
|
|
903
|
+
* @returns `true` if the types are equal, `false` otherwise.
|
|
904
|
+
*/
|
|
905
|
+
customStrongEqual(targetTypeJSON) {
|
|
906
|
+
const targetProperties = targetTypeJSON.properties || [];
|
|
907
|
+
const sourcePropertyKeys = Array.from(this.propertyTable.keys());
|
|
908
|
+
const targetPropertyKeys = targetProperties.map((_target) => _target.key);
|
|
909
|
+
const isKeyStrongEqual = !xor(sourcePropertyKeys, targetPropertyKeys).length;
|
|
910
|
+
return isKeyStrongEqual && targetProperties.every((targetProperty) => {
|
|
911
|
+
const sourceProperty = this.propertyTable.get(targetProperty.key);
|
|
912
|
+
return sourceProperty && sourceProperty.key === targetProperty.key && sourceProperty.type?.isTypeEqual(targetProperty?.type);
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
ObjectType.kind = "Object" /* Object */;
|
|
917
|
+
|
|
918
|
+
// src/ast/type/custom-type.ts
|
|
919
|
+
var CustomType = class extends BaseType {
|
|
920
|
+
/**
|
|
921
|
+
* The name of the custom type.
|
|
922
|
+
*/
|
|
923
|
+
get typeName() {
|
|
924
|
+
return this._typeName;
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* Deserializes the `CustomTypeJSON` to the `CustomType`.
|
|
928
|
+
* @param json The `CustomTypeJSON` to deserialize.
|
|
929
|
+
*/
|
|
930
|
+
fromJSON(json) {
|
|
931
|
+
if (this._typeName !== json.typeName) {
|
|
932
|
+
this._typeName = json.typeName;
|
|
933
|
+
this.fireChange();
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Check if the current type is equal to the target type.
|
|
938
|
+
* @param targetTypeJSONOrKind The type to compare with.
|
|
939
|
+
* @returns `true` if the types are equal, `false` otherwise.
|
|
940
|
+
*/
|
|
941
|
+
isTypeEqual(targetTypeJSONOrKind) {
|
|
942
|
+
const targetTypeJSON = parseTypeJsonOrKind(targetTypeJSONOrKind);
|
|
943
|
+
if (targetTypeJSON?.kind === "Union" /* Union */) {
|
|
944
|
+
return (targetTypeJSON?.types || [])?.some(
|
|
945
|
+
(_subType) => this.isTypeEqual(_subType)
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
return targetTypeJSON?.kind === this.kind && targetTypeJSON?.typeName === this.typeName;
|
|
949
|
+
}
|
|
950
|
+
toJSON() {
|
|
951
|
+
return {
|
|
952
|
+
typeName: this.typeName
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
CustomType.kind = "CustomType" /* CustomType */;
|
|
957
|
+
|
|
958
|
+
// src/ast/utils/variable-field.ts
|
|
959
|
+
function getParentFields(ast) {
|
|
960
|
+
let curr = ast.parent;
|
|
961
|
+
const res = [];
|
|
962
|
+
while (curr) {
|
|
963
|
+
if (curr.flags & 1 /* VariableField */) {
|
|
964
|
+
res.push(curr);
|
|
965
|
+
}
|
|
966
|
+
curr = curr.parent;
|
|
967
|
+
}
|
|
968
|
+
return res;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// src/ast/expression/base-expression.ts
|
|
972
|
+
var BaseExpression = class extends ASTNode {
|
|
973
|
+
constructor(params, opts) {
|
|
974
|
+
super(params, opts);
|
|
975
|
+
this.flags = 4 /* Expression */;
|
|
976
|
+
/**
|
|
977
|
+
* The variable fields referenced by the expression.
|
|
978
|
+
*/
|
|
979
|
+
this._refs = [];
|
|
980
|
+
this.refreshRefs$ = new Subject();
|
|
981
|
+
/**
|
|
982
|
+
* An observable that emits the referenced variable fields when they change.
|
|
983
|
+
*/
|
|
984
|
+
this.refs$ = this.refreshRefs$.pipe(
|
|
985
|
+
map(() => this.getRefFields()),
|
|
986
|
+
distinctUntilChanged(shallowEqual),
|
|
987
|
+
switchMap(
|
|
988
|
+
(refs) => !refs?.length ? of([]) : combineLatest(
|
|
989
|
+
refs.map(
|
|
990
|
+
(ref) => ref ? ref.value$ : of(void 0)
|
|
991
|
+
)
|
|
992
|
+
)
|
|
993
|
+
),
|
|
994
|
+
share()
|
|
995
|
+
);
|
|
996
|
+
this.toDispose.push(
|
|
997
|
+
subsToDisposable(
|
|
998
|
+
this.refs$.subscribe((_refs) => {
|
|
999
|
+
this._refs = _refs;
|
|
1000
|
+
this.fireChange();
|
|
1001
|
+
})
|
|
1002
|
+
)
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Get the global variable table, which is used to access referenced variables.
|
|
1007
|
+
*/
|
|
1008
|
+
get globalVariableTable() {
|
|
1009
|
+
return this.scope.variableEngine.globalVariableTable;
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Parent variable fields, sorted from closest to farthest.
|
|
1013
|
+
*/
|
|
1014
|
+
get parentFields() {
|
|
1015
|
+
return getParentFields(this);
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* The variable fields referenced by the expression.
|
|
1019
|
+
*/
|
|
1020
|
+
get refs() {
|
|
1021
|
+
return this._refs;
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Refresh the variable references.
|
|
1025
|
+
*/
|
|
1026
|
+
refreshRefs() {
|
|
1027
|
+
this.refreshRefs$.next();
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
|
|
1031
|
+
// src/ast/expression/enumerate-expression.ts
|
|
1032
|
+
var EnumerateExpression = class extends BaseExpression {
|
|
1033
|
+
/**
|
|
1034
|
+
* The expression to be enumerated.
|
|
1035
|
+
*/
|
|
1036
|
+
get enumerateFor() {
|
|
1037
|
+
return this._enumerateFor;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* The return type of the expression.
|
|
1041
|
+
*/
|
|
1042
|
+
get returnType() {
|
|
1043
|
+
const childReturnType = this.enumerateFor?.returnType;
|
|
1044
|
+
if (childReturnType?.kind === "Array" /* Array */) {
|
|
1045
|
+
return childReturnType.items;
|
|
1046
|
+
}
|
|
1047
|
+
return void 0;
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Get the variable fields referenced by the expression.
|
|
1051
|
+
* @returns An empty array, as this expression does not reference any variables.
|
|
1052
|
+
*/
|
|
1053
|
+
getRefFields() {
|
|
1054
|
+
return [];
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Deserializes the `EnumerateExpressionJSON` to the `EnumerateExpression`.
|
|
1058
|
+
* @param json The `EnumerateExpressionJSON` to deserialize.
|
|
1059
|
+
*/
|
|
1060
|
+
fromJSON({ enumerateFor: expression }) {
|
|
1061
|
+
this.updateChildNodeByKey("_enumerateFor", expression);
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Serialize the `EnumerateExpression` to `EnumerateExpressionJSON`.
|
|
1065
|
+
* @returns The JSON representation of `EnumerateExpression`.
|
|
1066
|
+
*/
|
|
1067
|
+
toJSON() {
|
|
1068
|
+
return {
|
|
1069
|
+
kind: "EnumerateExpression" /* EnumerateExpression */,
|
|
1070
|
+
enumerateFor: this.enumerateFor?.toJSON()
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
EnumerateExpression.kind = "EnumerateExpression" /* EnumerateExpression */;
|
|
1075
|
+
function getAllRefs(ast) {
|
|
1076
|
+
return getAllChildren(ast).filter((_child) => _child.flags & 4 /* Expression */).map((_child) => _child.refs).flat().filter(Boolean);
|
|
1077
|
+
}
|
|
1078
|
+
function checkRefCycle(curr, refNodes) {
|
|
1079
|
+
if (intersection(curr.scope.coverScopes, refNodes.map((_ref) => _ref?.scope).filter(Boolean)).length === 0) {
|
|
1080
|
+
return false;
|
|
1081
|
+
}
|
|
1082
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1083
|
+
const queue = [...refNodes];
|
|
1084
|
+
while (queue.length) {
|
|
1085
|
+
const currNode = queue.shift();
|
|
1086
|
+
visited.add(currNode);
|
|
1087
|
+
for (const ref of getAllRefs(currNode).filter((_ref) => !visited.has(_ref))) {
|
|
1088
|
+
queue.push(ref);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
return intersection(Array.from(visited), getParentFields(curr)).length > 0;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// src/ast/expression/keypath-expression.ts
|
|
1095
|
+
var KeyPathExpression = class extends BaseExpression {
|
|
1096
|
+
constructor(params, opts) {
|
|
1097
|
+
super(params, opts);
|
|
1098
|
+
this._keyPath = [];
|
|
1099
|
+
this.toDispose.pushAll([
|
|
1100
|
+
// Can be used when the variable list changes (when there are additions or deletions).
|
|
1101
|
+
this.scope.available.onVariableListChange(() => {
|
|
1102
|
+
this.refreshRefs();
|
|
1103
|
+
}),
|
|
1104
|
+
// When the referable variable pointed to by this._keyPath changes, refresh the reference data.
|
|
1105
|
+
this.scope.available.onAnyVariableChange((_v) => {
|
|
1106
|
+
if (_v.key === this._keyPath[0]) {
|
|
1107
|
+
this.refreshRefs();
|
|
1108
|
+
}
|
|
1109
|
+
}),
|
|
1110
|
+
subsToDisposable(
|
|
1111
|
+
this.refs$.pipe(
|
|
1112
|
+
distinctUntilChanged(
|
|
1113
|
+
(prev, next) => prev === next,
|
|
1114
|
+
(_refs) => _refs?.[0]?.type?.hash
|
|
1115
|
+
)
|
|
1116
|
+
).subscribe((_type) => {
|
|
1117
|
+
const [ref] = this._refs;
|
|
1118
|
+
this.updateChildNodeByKey("_returnType", this.getReturnTypeJSONByRef(ref));
|
|
1119
|
+
})
|
|
1120
|
+
)
|
|
1121
|
+
]);
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* The key path of the variable.
|
|
1125
|
+
*/
|
|
1126
|
+
get keyPath() {
|
|
1127
|
+
return this._keyPath;
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Get the variable fields referenced by the expression.
|
|
1131
|
+
* @returns An array of referenced variable fields.
|
|
1132
|
+
*/
|
|
1133
|
+
getRefFields() {
|
|
1134
|
+
const ref = this.scope.available.getByKeyPath(this._keyPath);
|
|
1135
|
+
if (checkRefCycle(this, [ref])) {
|
|
1136
|
+
console.warn(
|
|
1137
|
+
"[CustomKeyPathExpression] checkRefCycle: Reference Cycle Existed",
|
|
1138
|
+
this.parentFields.map((_field) => _field.key).reverse()
|
|
1139
|
+
);
|
|
1140
|
+
return [];
|
|
1141
|
+
}
|
|
1142
|
+
return ref ? [ref] : [];
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* The return type of the expression.
|
|
1146
|
+
*/
|
|
1147
|
+
get returnType() {
|
|
1148
|
+
return this._returnType;
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Parse the business-defined path expression into a key path.
|
|
1152
|
+
*
|
|
1153
|
+
* Businesses can quickly customize their own path expressions by modifying this method.
|
|
1154
|
+
* @param json The path expression defined by the business.
|
|
1155
|
+
* @returns The key path.
|
|
1156
|
+
*/
|
|
1157
|
+
parseToKeyPath(json) {
|
|
1158
|
+
return json.keyPath;
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Deserializes the `KeyPathExpressionJSON` to the `KeyPathExpression`.
|
|
1162
|
+
* @param json The `KeyPathExpressionJSON` to deserialize.
|
|
1163
|
+
*/
|
|
1164
|
+
fromJSON(json) {
|
|
1165
|
+
const keyPath = this.parseToKeyPath(json);
|
|
1166
|
+
if (!shallowEqual(keyPath, this._keyPath)) {
|
|
1167
|
+
this._keyPath = keyPath;
|
|
1168
|
+
this._rawPathJson = json;
|
|
1169
|
+
this.refreshRefs();
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Get the return type JSON by reference.
|
|
1174
|
+
* @param _ref The referenced variable field.
|
|
1175
|
+
* @returns The JSON representation of the return type.
|
|
1176
|
+
*/
|
|
1177
|
+
getReturnTypeJSONByRef(_ref) {
|
|
1178
|
+
return _ref?.type?.toJSON();
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Serialize the `KeyPathExpression` to `KeyPathExpressionJSON`.
|
|
1182
|
+
* @returns The JSON representation of `KeyPathExpression`.
|
|
1183
|
+
*/
|
|
1184
|
+
toJSON() {
|
|
1185
|
+
return this._rawPathJson;
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
KeyPathExpression.kind = "KeyPathExpression" /* KeyPathExpression */;
|
|
1189
|
+
var LegacyKeyPathExpression = class extends BaseExpression {
|
|
1190
|
+
constructor(params, opts) {
|
|
1191
|
+
super(params, opts);
|
|
1192
|
+
this._keyPath = [];
|
|
1193
|
+
this.toDispose.pushAll([
|
|
1194
|
+
// Can be used when the variable list changes (when there are additions or deletions).
|
|
1195
|
+
this.scope.available.onVariableListChange(() => {
|
|
1196
|
+
this.refreshRefs();
|
|
1197
|
+
}),
|
|
1198
|
+
// When the referable variable pointed to by this._keyPath changes, refresh the reference data.
|
|
1199
|
+
this.scope.available.onAnyVariableChange((_v) => {
|
|
1200
|
+
if (_v.key === this._keyPath[0]) {
|
|
1201
|
+
this.refreshRefs();
|
|
1202
|
+
}
|
|
1203
|
+
})
|
|
1204
|
+
]);
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* The key path of the variable.
|
|
1208
|
+
*/
|
|
1209
|
+
get keyPath() {
|
|
1210
|
+
return this._keyPath;
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Get the variable fields referenced by the expression.
|
|
1214
|
+
* @returns An array of referenced variable fields.
|
|
1215
|
+
*/
|
|
1216
|
+
getRefFields() {
|
|
1217
|
+
const ref = this.scope.available.getByKeyPath(this._keyPath);
|
|
1218
|
+
return ref ? [ref] : [];
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* The return type of the expression.
|
|
1222
|
+
*/
|
|
1223
|
+
get returnType() {
|
|
1224
|
+
const [refNode] = this._refs || [];
|
|
1225
|
+
if (refNode && refNode.flags & 1 /* VariableField */) {
|
|
1226
|
+
return refNode.type;
|
|
1227
|
+
}
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Parse the business-defined path expression into a key path.
|
|
1232
|
+
*
|
|
1233
|
+
* Businesses can quickly customize their own path expressions by modifying this method.
|
|
1234
|
+
* @param json The path expression defined by the business.
|
|
1235
|
+
* @returns The key path.
|
|
1236
|
+
*/
|
|
1237
|
+
parseToKeyPath(json) {
|
|
1238
|
+
return json.keyPath;
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Deserializes the `KeyPathExpressionJSON` to the `KeyPathExpression`.
|
|
1242
|
+
* @param json The `KeyPathExpressionJSON` to deserialize.
|
|
1243
|
+
*/
|
|
1244
|
+
fromJSON(json) {
|
|
1245
|
+
const keyPath = this.parseToKeyPath(json);
|
|
1246
|
+
if (!shallowEqual(keyPath, this._keyPath)) {
|
|
1247
|
+
this._keyPath = keyPath;
|
|
1248
|
+
this._rawPathJson = json;
|
|
1249
|
+
this.refreshRefs();
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Serialize the `KeyPathExpression` to `KeyPathExpressionJSON`.
|
|
1254
|
+
* @returns The JSON representation of `KeyPathExpression`.
|
|
1255
|
+
*/
|
|
1256
|
+
toJSON() {
|
|
1257
|
+
return this._rawPathJson;
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
LegacyKeyPathExpression.kind = "KeyPathExpression" /* KeyPathExpression */;
|
|
1261
|
+
|
|
1262
|
+
// src/ast/expression/wrap-array-expression.ts
|
|
1263
|
+
var WrapArrayExpression = class extends BaseExpression {
|
|
1264
|
+
/**
|
|
1265
|
+
* The expression to be wrapped.
|
|
1266
|
+
*/
|
|
1267
|
+
get wrapFor() {
|
|
1268
|
+
return this._wrapFor;
|
|
1269
|
+
}
|
|
1270
|
+
/**
|
|
1271
|
+
* The return type of the expression.
|
|
1272
|
+
*/
|
|
1273
|
+
get returnType() {
|
|
1274
|
+
return this._returnType;
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Refresh the return type of the expression.
|
|
1278
|
+
*/
|
|
1279
|
+
refreshReturnType() {
|
|
1280
|
+
const childReturnTypeJSON = this.wrapFor?.returnType?.toJSON();
|
|
1281
|
+
this.updateChildNodeByKey("_returnType", {
|
|
1282
|
+
kind: "Array" /* Array */,
|
|
1283
|
+
items: childReturnTypeJSON
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* Get the variable fields referenced by the expression.
|
|
1288
|
+
* @returns An empty array, as this expression does not reference any variables.
|
|
1289
|
+
*/
|
|
1290
|
+
getRefFields() {
|
|
1291
|
+
return [];
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Deserializes the `WrapArrayExpressionJSON` to the `WrapArrayExpression`.
|
|
1295
|
+
* @param json The `WrapArrayExpressionJSON` to deserialize.
|
|
1296
|
+
*/
|
|
1297
|
+
fromJSON({ wrapFor: expression }) {
|
|
1298
|
+
this.updateChildNodeByKey("_wrapFor", expression);
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Serialize the `WrapArrayExpression` to `WrapArrayExpressionJSON`.
|
|
1302
|
+
* @returns The JSON representation of `WrapArrayExpression`.
|
|
1303
|
+
*/
|
|
1304
|
+
toJSON() {
|
|
1305
|
+
return {
|
|
1306
|
+
kind: "WrapArrayExpression" /* WrapArrayExpression */,
|
|
1307
|
+
wrapFor: this.wrapFor?.toJSON()
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
init() {
|
|
1311
|
+
this.refreshReturnType = this.refreshReturnType.bind(this);
|
|
1312
|
+
this.toDispose.push(
|
|
1313
|
+
this.subscribe(this.refreshReturnType, {
|
|
1314
|
+
selector: (curr) => curr.wrapFor?.returnType,
|
|
1315
|
+
triggerOnInit: true
|
|
1316
|
+
})
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
};
|
|
1320
|
+
WrapArrayExpression.kind = "WrapArrayExpression" /* WrapArrayExpression */;
|
|
1321
|
+
__decorateClass([
|
|
1322
|
+
postConstructAST()
|
|
1323
|
+
], WrapArrayExpression.prototype, "init", 1);
|
|
1324
|
+
var BaseVariableField = class extends ASTNode {
|
|
1325
|
+
constructor() {
|
|
1326
|
+
super(...arguments);
|
|
1327
|
+
this.flags = 1 /* VariableField */;
|
|
1328
|
+
this._meta = {};
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Parent variable fields, sorted from closest to farthest
|
|
1332
|
+
*/
|
|
1333
|
+
get parentFields() {
|
|
1334
|
+
return getParentFields(this);
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* KeyPath of the variable field, sorted from farthest to closest
|
|
1338
|
+
*/
|
|
1339
|
+
get keyPath() {
|
|
1340
|
+
return [...this.parentFields.reverse().map((_field) => _field.key), this.key];
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Metadata of the variable field, you cans store information like `title`, `icon`, etc.
|
|
1344
|
+
*/
|
|
1345
|
+
get meta() {
|
|
1346
|
+
return this._meta;
|
|
1347
|
+
}
|
|
1348
|
+
/**
|
|
1349
|
+
* Type of the variable field, similar to js code:
|
|
1350
|
+
* `const v: string`
|
|
1351
|
+
*/
|
|
1352
|
+
get type() {
|
|
1353
|
+
return this._initializer?.returnType || this._type;
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Initializer of the variable field, similar to js code:
|
|
1357
|
+
* `const v = 'hello'`
|
|
1358
|
+
*
|
|
1359
|
+
* with initializer, the type of field will be inferred from the initializer.
|
|
1360
|
+
*/
|
|
1361
|
+
get initializer() {
|
|
1362
|
+
return this._initializer;
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* The global unique hash of the field, and will be changed when the field is updated.
|
|
1366
|
+
*/
|
|
1367
|
+
get hash() {
|
|
1368
|
+
return `[${this._version}]${this.keyPath.join(".")}`;
|
|
1369
|
+
}
|
|
1370
|
+
/**
|
|
1371
|
+
* Deserialize the `BaseVariableFieldJSON` to the `BaseVariableField`.
|
|
1372
|
+
* @param json ASTJSON representation of `BaseVariableField`
|
|
1373
|
+
*/
|
|
1374
|
+
fromJSON({ type, initializer, meta }) {
|
|
1375
|
+
this.updateType(type);
|
|
1376
|
+
this.updateInitializer(initializer);
|
|
1377
|
+
this.updateMeta(meta);
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Update the type of the variable field
|
|
1381
|
+
* @param type type ASTJSON representation of Type
|
|
1382
|
+
*/
|
|
1383
|
+
updateType(type) {
|
|
1384
|
+
const nextTypeJson = typeof type === "string" ? { kind: type } : type;
|
|
1385
|
+
this.updateChildNodeByKey("_type", nextTypeJson);
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Update the initializer of the variable field
|
|
1389
|
+
* @param nextInitializer initializer ASTJSON representation of Expression
|
|
1390
|
+
*/
|
|
1391
|
+
updateInitializer(nextInitializer) {
|
|
1392
|
+
this.updateChildNodeByKey("_initializer", nextInitializer);
|
|
1393
|
+
}
|
|
1394
|
+
/**
|
|
1395
|
+
* Update the meta data of the variable field
|
|
1396
|
+
* @param nextMeta meta data of the variable field
|
|
1397
|
+
*/
|
|
1398
|
+
updateMeta(nextMeta) {
|
|
1399
|
+
if (!shallowEqual(nextMeta, this._meta)) {
|
|
1400
|
+
this._meta = nextMeta;
|
|
1401
|
+
this.fireChange();
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Get the variable field by keyPath, similar to js code:
|
|
1406
|
+
* `v.a.b`
|
|
1407
|
+
* @param keyPath
|
|
1408
|
+
* @returns
|
|
1409
|
+
*/
|
|
1410
|
+
getByKeyPath(keyPath) {
|
|
1411
|
+
if (this.type?.flags & 16 /* DrilldownType */) {
|
|
1412
|
+
return this.type.getByKeyPath(keyPath);
|
|
1413
|
+
}
|
|
1414
|
+
return void 0;
|
|
1415
|
+
}
|
|
1416
|
+
/**
|
|
1417
|
+
* Subscribe to type change of the variable field
|
|
1418
|
+
* @param observer
|
|
1419
|
+
* @returns
|
|
1420
|
+
*/
|
|
1421
|
+
onTypeChange(observer) {
|
|
1422
|
+
return this.subscribe(observer, { selector: (curr) => curr.type });
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Serialize the variable field to JSON
|
|
1426
|
+
* @returns ASTNodeJSON representation of `BaseVariableField`
|
|
1427
|
+
*/
|
|
1428
|
+
toJSON() {
|
|
1429
|
+
return {
|
|
1430
|
+
key: this.key,
|
|
1431
|
+
type: this.type?.toJSON(),
|
|
1432
|
+
initializer: this.initializer?.toJSON(),
|
|
1433
|
+
meta: this._meta
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
1437
|
+
|
|
1438
|
+
// src/ast/declaration/variable-declaration.ts
|
|
1439
|
+
var VariableDeclaration = class extends BaseVariableField {
|
|
1440
|
+
constructor(params) {
|
|
1441
|
+
super(params);
|
|
1442
|
+
this._order = 0;
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Variable sorting order, which is used to sort variables in `scope.outputs.variables`
|
|
1446
|
+
*/
|
|
1447
|
+
get order() {
|
|
1448
|
+
return this._order;
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Deserialize the `VariableDeclarationJSON` to the `VariableDeclaration`.
|
|
1452
|
+
*/
|
|
1453
|
+
fromJSON({ order, ...rest }) {
|
|
1454
|
+
this.updateOrder(order);
|
|
1455
|
+
super.fromJSON(rest);
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Update the sorting order of the variable declaration.
|
|
1459
|
+
* @param order Variable sorting order. Default is 0.
|
|
1460
|
+
*/
|
|
1461
|
+
updateOrder(order = 0) {
|
|
1462
|
+
if (order !== this._order) {
|
|
1463
|
+
this._order = order;
|
|
1464
|
+
this.dispatchGlobalEvent({
|
|
1465
|
+
type: "ReSortVariableDeclarations"
|
|
1466
|
+
});
|
|
1467
|
+
this.fireChange();
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Serialize the `VariableDeclaration` to `VariableDeclarationJSON`.
|
|
1472
|
+
* @returns The JSON representation of `VariableDeclaration`.
|
|
1473
|
+
*/
|
|
1474
|
+
toJSON() {
|
|
1475
|
+
return {
|
|
1476
|
+
...super.toJSON(),
|
|
1477
|
+
order: this.order
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
VariableDeclaration.kind = "VariableDeclaration" /* VariableDeclaration */;
|
|
1482
|
+
|
|
1483
|
+
// src/ast/declaration/variable-declaration-list.ts
|
|
1484
|
+
var VariableDeclarationList = class extends ASTNode {
|
|
1485
|
+
constructor() {
|
|
1486
|
+
super(...arguments);
|
|
1487
|
+
/**
|
|
1488
|
+
* Map of variable declarations, keyed by variable name.
|
|
1489
|
+
*/
|
|
1490
|
+
this.declarationTable = /* @__PURE__ */ new Map();
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Deserialize the `VariableDeclarationListJSON` to the `VariableDeclarationList`.
|
|
1494
|
+
* - VariableDeclarationListChangeAction will be dispatched after deserialization.
|
|
1495
|
+
*
|
|
1496
|
+
* @param declarations Variable declarations.
|
|
1497
|
+
* @param startOrder The starting order number for variables. Default is 0.
|
|
1498
|
+
*/
|
|
1499
|
+
fromJSON({ declarations, startOrder }) {
|
|
1500
|
+
const removedKeys = new Set(this.declarationTable.keys());
|
|
1501
|
+
const prev = [...this.declarations || []];
|
|
1502
|
+
this.declarations = (declarations || []).map(
|
|
1503
|
+
(declaration, idx) => {
|
|
1504
|
+
const order = (startOrder || 0) + idx;
|
|
1505
|
+
const declarationKey = declaration.key || this.declarations?.[idx]?.key;
|
|
1506
|
+
const existDeclaration = this.declarationTable.get(declarationKey);
|
|
1507
|
+
if (declarationKey) {
|
|
1508
|
+
removedKeys.delete(declarationKey);
|
|
1509
|
+
}
|
|
1510
|
+
if (existDeclaration) {
|
|
1511
|
+
existDeclaration.fromJSON({ order, ...declaration });
|
|
1512
|
+
return existDeclaration;
|
|
1513
|
+
} else {
|
|
1514
|
+
const newDeclaration = this.createChildNode({
|
|
1515
|
+
order,
|
|
1516
|
+
...declaration,
|
|
1517
|
+
kind: "VariableDeclaration" /* VariableDeclaration */
|
|
1518
|
+
});
|
|
1519
|
+
this.fireChange();
|
|
1520
|
+
this.declarationTable.set(newDeclaration.key, newDeclaration);
|
|
1521
|
+
return newDeclaration;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
);
|
|
1525
|
+
removedKeys.forEach((key) => {
|
|
1526
|
+
const declaration = this.declarationTable.get(key);
|
|
1527
|
+
declaration?.dispose();
|
|
1528
|
+
this.declarationTable.delete(key);
|
|
1529
|
+
});
|
|
1530
|
+
this.dispatchGlobalEvent({
|
|
1531
|
+
type: "VariableListChange",
|
|
1532
|
+
payload: {
|
|
1533
|
+
prev,
|
|
1534
|
+
next: [...this.declarations]
|
|
1535
|
+
}
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Serialize the `VariableDeclarationList` to the `VariableDeclarationListJSON`.
|
|
1540
|
+
* @returns ASTJSON representation of `VariableDeclarationList`
|
|
1541
|
+
*/
|
|
1542
|
+
toJSON() {
|
|
1543
|
+
return {
|
|
1544
|
+
kind: "VariableDeclarationList" /* VariableDeclarationList */,
|
|
1545
|
+
declarations: this.declarations.map((_declaration) => _declaration.toJSON())
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
VariableDeclarationList.kind = "VariableDeclarationList" /* VariableDeclarationList */;
|
|
1550
|
+
|
|
1551
|
+
// src/ast/declaration/property.ts
|
|
1552
|
+
var Property = class extends BaseVariableField {
|
|
1553
|
+
};
|
|
1554
|
+
Property.kind = "Property" /* Property */;
|
|
1555
|
+
var DataNode = class extends ASTNode {
|
|
1556
|
+
/**
|
|
1557
|
+
* The data of the node.
|
|
1558
|
+
*/
|
|
1559
|
+
get data() {
|
|
1560
|
+
return this._data;
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Deserializes the `DataNodeJSON` to the `DataNode`.
|
|
1564
|
+
* @param json The `DataNodeJSON` to deserialize.
|
|
1565
|
+
*/
|
|
1566
|
+
fromJSON(json) {
|
|
1567
|
+
const { kind, ...restData } = json;
|
|
1568
|
+
if (!shallowEqual(restData, this._data)) {
|
|
1569
|
+
this._data = restData;
|
|
1570
|
+
this.fireChange();
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
/**
|
|
1574
|
+
* Serialize the `DataNode` to `DataNodeJSON`.
|
|
1575
|
+
* @returns The JSON representation of `DataNode`.
|
|
1576
|
+
*/
|
|
1577
|
+
toJSON() {
|
|
1578
|
+
return {
|
|
1579
|
+
kind: "DataNode" /* DataNode */,
|
|
1580
|
+
...this._data
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
/**
|
|
1584
|
+
* Partially update the data of the node.
|
|
1585
|
+
* @param nextData The data to be updated.
|
|
1586
|
+
*/
|
|
1587
|
+
partialUpdate(nextData) {
|
|
1588
|
+
if (!shallowEqual(nextData, this._data)) {
|
|
1589
|
+
this._data = {
|
|
1590
|
+
...this._data,
|
|
1591
|
+
...nextData
|
|
1592
|
+
};
|
|
1593
|
+
this.fireChange();
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
DataNode.kind = "DataNode" /* DataNode */;
|
|
1598
|
+
|
|
1599
|
+
// src/ast/common/list-node.ts
|
|
1600
|
+
var ListNode = class extends ASTNode {
|
|
1601
|
+
/**
|
|
1602
|
+
* The list of nodes.
|
|
1603
|
+
*/
|
|
1604
|
+
get list() {
|
|
1605
|
+
return this._list;
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Deserializes the `ListNodeJSON` to the `ListNode`.
|
|
1609
|
+
* @param json The `ListNodeJSON` to deserialize.
|
|
1610
|
+
*/
|
|
1611
|
+
fromJSON({ list }) {
|
|
1612
|
+
this._list.slice(list.length).forEach((_item) => {
|
|
1613
|
+
_item.dispose();
|
|
1614
|
+
this.fireChange();
|
|
1615
|
+
});
|
|
1616
|
+
this._list = list.map((_item, idx) => {
|
|
1617
|
+
const prevItem = this._list[idx];
|
|
1618
|
+
if (prevItem.kind !== _item.kind) {
|
|
1619
|
+
prevItem.dispose();
|
|
1620
|
+
this.fireChange();
|
|
1621
|
+
return this.createChildNode(_item);
|
|
1622
|
+
}
|
|
1623
|
+
prevItem.fromJSON(_item);
|
|
1624
|
+
return prevItem;
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Serialize the `ListNode` to `ListNodeJSON`.
|
|
1629
|
+
* @returns The JSON representation of `ListNode`.
|
|
1630
|
+
*/
|
|
1631
|
+
toJSON() {
|
|
1632
|
+
return {
|
|
1633
|
+
kind: "ListNode" /* ListNode */,
|
|
1634
|
+
list: this._list.map((item) => item.toJSON())
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
ListNode.kind = "ListNode" /* ListNode */;
|
|
1639
|
+
|
|
1640
|
+
// src/ast/common/map-node.ts
|
|
1641
|
+
var MapNode = class extends ASTNode {
|
|
1642
|
+
constructor() {
|
|
1643
|
+
super(...arguments);
|
|
1644
|
+
this.map = /* @__PURE__ */ new Map();
|
|
1645
|
+
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Deserializes the `MapNodeJSON` to the `MapNode`.
|
|
1648
|
+
* @param json The `MapNodeJSON` to deserialize.
|
|
1649
|
+
*/
|
|
1650
|
+
fromJSON({ map: map4 }) {
|
|
1651
|
+
const removedKeys = new Set(this.map.keys());
|
|
1652
|
+
for (const [key, item] of map4 || []) {
|
|
1653
|
+
removedKeys.delete(key);
|
|
1654
|
+
this.set(key, item);
|
|
1655
|
+
}
|
|
1656
|
+
for (const removeKey of Array.from(removedKeys)) {
|
|
1657
|
+
this.remove(removeKey);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Serialize the `MapNode` to `MapNodeJSON`.
|
|
1662
|
+
* @returns The JSON representation of `MapNode`.
|
|
1663
|
+
*/
|
|
1664
|
+
toJSON() {
|
|
1665
|
+
return {
|
|
1666
|
+
kind: "MapNode" /* MapNode */,
|
|
1667
|
+
map: Array.from(this.map.entries())
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* Set a node in the map.
|
|
1672
|
+
* @param key The key of the node.
|
|
1673
|
+
* @param nextJSON The JSON representation of the node.
|
|
1674
|
+
* @returns The node instance.
|
|
1675
|
+
*/
|
|
1676
|
+
set(key, nextJSON) {
|
|
1677
|
+
return this.withBatchUpdate(updateChildNodeHelper).call(this, {
|
|
1678
|
+
getChildNode: () => this.get(key),
|
|
1679
|
+
removeChildNode: () => this.map.delete(key),
|
|
1680
|
+
updateChildNode: (nextNode) => this.map.set(key, nextNode),
|
|
1681
|
+
nextJSON
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Remove a node from the map.
|
|
1686
|
+
* @param key The key of the node.
|
|
1687
|
+
*/
|
|
1688
|
+
remove(key) {
|
|
1689
|
+
this.get(key)?.dispose();
|
|
1690
|
+
this.map.delete(key);
|
|
1691
|
+
this.fireChange();
|
|
1692
|
+
}
|
|
1693
|
+
/**
|
|
1694
|
+
* Get a node from the map.
|
|
1695
|
+
* @param key The key of the node.
|
|
1696
|
+
* @returns The node instance if found, otherwise `undefined`.
|
|
1697
|
+
*/
|
|
1698
|
+
get(key) {
|
|
1699
|
+
return this.map.get(key);
|
|
1700
|
+
}
|
|
1701
|
+
};
|
|
1702
|
+
MapNode.kind = "MapNode" /* MapNode */;
|
|
1703
|
+
|
|
1704
|
+
// src/ast/ast-registers.ts
|
|
1705
|
+
var ASTRegisters = class {
|
|
1706
|
+
/**
|
|
1707
|
+
* Core AST node registration.
|
|
1708
|
+
*/
|
|
1709
|
+
constructor() {
|
|
1710
|
+
/**
|
|
1711
|
+
* @deprecated Please use `@injectToAst(XXXService) declare xxxService: XXXService` to achieve external dependency injection.
|
|
1712
|
+
*/
|
|
1713
|
+
this.injectors = /* @__PURE__ */ new Map();
|
|
1714
|
+
this.astMap = /* @__PURE__ */ new Map();
|
|
1715
|
+
this.registerAST(StringType);
|
|
1716
|
+
this.registerAST(NumberType);
|
|
1717
|
+
this.registerAST(BooleanType);
|
|
1718
|
+
this.registerAST(IntegerType);
|
|
1719
|
+
this.registerAST(ObjectType);
|
|
1720
|
+
this.registerAST(ArrayType);
|
|
1721
|
+
this.registerAST(MapType);
|
|
1722
|
+
this.registerAST(CustomType);
|
|
1723
|
+
this.registerAST(Property);
|
|
1724
|
+
this.registerAST(VariableDeclaration);
|
|
1725
|
+
this.registerAST(VariableDeclarationList);
|
|
1726
|
+
this.registerAST(KeyPathExpression);
|
|
1727
|
+
this.registerAST(EnumerateExpression);
|
|
1728
|
+
this.registerAST(WrapArrayExpression);
|
|
1729
|
+
this.registerAST(MapNode);
|
|
1730
|
+
this.registerAST(DataNode);
|
|
1731
|
+
}
|
|
1732
|
+
/**
|
|
1733
|
+
* Creates an AST node.
|
|
1734
|
+
* @param param Creation parameters.
|
|
1735
|
+
* @returns
|
|
1736
|
+
*/
|
|
1737
|
+
createAST(json, { parent, scope }) {
|
|
1738
|
+
const Registry = this.astMap.get(json.kind);
|
|
1739
|
+
if (!Registry) {
|
|
1740
|
+
throw Error(`ASTKind: ${String(json.kind)} can not find its ASTNode Registry`);
|
|
1741
|
+
}
|
|
1742
|
+
const injector = this.injectors.get(json.kind);
|
|
1743
|
+
const node = new Registry(
|
|
1744
|
+
{
|
|
1745
|
+
key: json.key,
|
|
1746
|
+
scope,
|
|
1747
|
+
parent
|
|
1748
|
+
},
|
|
1749
|
+
injector?.() || {}
|
|
1750
|
+
);
|
|
1751
|
+
node.changeLocked = true;
|
|
1752
|
+
node.fromJSON(omit(json, ["key", "kind"]));
|
|
1753
|
+
node.changeLocked = false;
|
|
1754
|
+
node.dispatchGlobalEvent({ type: "NewAST" });
|
|
1755
|
+
if (Reflect.hasMetadata(POST_CONSTRUCT_AST_SYMBOL, node)) {
|
|
1756
|
+
const postConstructKey = Reflect.getMetadata(POST_CONSTRUCT_AST_SYMBOL, node);
|
|
1757
|
+
node[postConstructKey]?.();
|
|
1758
|
+
}
|
|
1759
|
+
return node;
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Gets the node Registry by AST node type.
|
|
1763
|
+
* @param kind
|
|
1764
|
+
* @returns
|
|
1765
|
+
*/
|
|
1766
|
+
getASTRegistryByKind(kind) {
|
|
1767
|
+
return this.astMap.get(kind);
|
|
1768
|
+
}
|
|
1769
|
+
/**
|
|
1770
|
+
* Registers an AST node.
|
|
1771
|
+
* @param ASTNode
|
|
1772
|
+
*/
|
|
1773
|
+
registerAST(ASTNode2, injector) {
|
|
1774
|
+
this.astMap.set(ASTNode2.kind, ASTNode2);
|
|
1775
|
+
if (injector) {
|
|
1776
|
+
this.injectors.set(ASTNode2.kind, injector);
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
ASTRegisters = __decorateClass([
|
|
1781
|
+
injectable()
|
|
1782
|
+
], ASTRegisters);
|
|
1783
|
+
|
|
1784
|
+
// src/ast/factory.ts
|
|
1785
|
+
var ASTFactory;
|
|
1786
|
+
((ASTFactory2) => {
|
|
1787
|
+
ASTFactory2.createString = (json) => ({
|
|
1788
|
+
kind: "String" /* String */,
|
|
1789
|
+
...json || {}
|
|
1790
|
+
});
|
|
1791
|
+
ASTFactory2.createNumber = () => ({ kind: "Number" /* Number */ });
|
|
1792
|
+
ASTFactory2.createBoolean = () => ({ kind: "Boolean" /* Boolean */ });
|
|
1793
|
+
ASTFactory2.createInteger = () => ({ kind: "Integer" /* Integer */ });
|
|
1794
|
+
ASTFactory2.createObject = (json) => ({
|
|
1795
|
+
kind: "Object" /* Object */,
|
|
1796
|
+
...json
|
|
1797
|
+
});
|
|
1798
|
+
ASTFactory2.createArray = (json) => ({
|
|
1799
|
+
kind: "Array" /* Array */,
|
|
1800
|
+
...json
|
|
1801
|
+
});
|
|
1802
|
+
ASTFactory2.createMap = (json) => ({
|
|
1803
|
+
kind: "Map" /* Map */,
|
|
1804
|
+
...json
|
|
1805
|
+
});
|
|
1806
|
+
ASTFactory2.createUnion = (json) => ({
|
|
1807
|
+
kind: "Union" /* Union */,
|
|
1808
|
+
...json
|
|
1809
|
+
});
|
|
1810
|
+
ASTFactory2.createCustomType = (json) => ({
|
|
1811
|
+
kind: "CustomType" /* CustomType */,
|
|
1812
|
+
...json
|
|
1813
|
+
});
|
|
1814
|
+
ASTFactory2.createVariableDeclaration = (json) => ({
|
|
1815
|
+
kind: "VariableDeclaration" /* VariableDeclaration */,
|
|
1816
|
+
...json
|
|
1817
|
+
});
|
|
1818
|
+
ASTFactory2.createProperty = (json) => ({
|
|
1819
|
+
kind: "Property" /* Property */,
|
|
1820
|
+
...json
|
|
1821
|
+
});
|
|
1822
|
+
ASTFactory2.createVariableDeclarationList = (json) => ({
|
|
1823
|
+
kind: "VariableDeclarationList" /* VariableDeclarationList */,
|
|
1824
|
+
...json
|
|
1825
|
+
});
|
|
1826
|
+
ASTFactory2.createEnumerateExpression = (json) => ({
|
|
1827
|
+
kind: "EnumerateExpression" /* EnumerateExpression */,
|
|
1828
|
+
...json
|
|
1829
|
+
});
|
|
1830
|
+
ASTFactory2.createKeyPathExpression = (json) => ({
|
|
1831
|
+
kind: "KeyPathExpression" /* KeyPathExpression */,
|
|
1832
|
+
...json
|
|
1833
|
+
});
|
|
1834
|
+
ASTFactory2.createWrapArrayExpression = (json) => ({
|
|
1835
|
+
kind: "WrapArrayExpression" /* WrapArrayExpression */,
|
|
1836
|
+
...json
|
|
1837
|
+
});
|
|
1838
|
+
ASTFactory2.create = (targetType, json) => ({ kind: targetType.kind, ...json });
|
|
1839
|
+
})(ASTFactory || (ASTFactory = {}));
|
|
1840
|
+
|
|
1841
|
+
// src/scope/datas/scope-output-data.ts
|
|
1842
|
+
var ScopeOutputData = class {
|
|
1843
|
+
constructor(scope) {
|
|
1844
|
+
this.scope = scope;
|
|
1845
|
+
this.memo = createMemo();
|
|
1846
|
+
this._hasChanges = false;
|
|
1847
|
+
this.variableTable = new VariableTable(scope.variableEngine.globalVariableTable);
|
|
1848
|
+
this.scope.toDispose.pushAll([
|
|
1849
|
+
// When the root AST node is updated, check if there are any changes.
|
|
1850
|
+
this.scope.ast.subscribe(() => {
|
|
1851
|
+
if (this._hasChanges) {
|
|
1852
|
+
this.memo.clear();
|
|
1853
|
+
this.notifyCoversChange();
|
|
1854
|
+
this.variableTable.fireChange();
|
|
1855
|
+
this._hasChanges = false;
|
|
1856
|
+
}
|
|
1857
|
+
}),
|
|
1858
|
+
this.scope.event.on("DisposeAST", (_action) => {
|
|
1859
|
+
if (_action.ast?.kind === "VariableDeclaration" /* VariableDeclaration */) {
|
|
1860
|
+
this.removeVariableFromTable(_action.ast.key);
|
|
1861
|
+
}
|
|
1862
|
+
}),
|
|
1863
|
+
this.scope.event.on("NewAST", (_action) => {
|
|
1864
|
+
if (_action.ast?.kind === "VariableDeclaration" /* VariableDeclaration */) {
|
|
1865
|
+
this.addVariableToTable(_action.ast);
|
|
1866
|
+
}
|
|
1867
|
+
}),
|
|
1868
|
+
this.scope.event.on("ReSortVariableDeclarations", () => {
|
|
1869
|
+
this._hasChanges = true;
|
|
1870
|
+
}),
|
|
1871
|
+
this.variableTable
|
|
1872
|
+
]);
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* The variable engine instance.
|
|
1876
|
+
*/
|
|
1877
|
+
get variableEngine() {
|
|
1878
|
+
return this.scope.variableEngine;
|
|
1879
|
+
}
|
|
1880
|
+
/**
|
|
1881
|
+
* The global variable table from the variable engine.
|
|
1882
|
+
*/
|
|
1883
|
+
get globalVariableTable() {
|
|
1884
|
+
return this.scope.variableEngine.globalVariableTable;
|
|
1885
|
+
}
|
|
1886
|
+
/**
|
|
1887
|
+
* The current version of the output data, which increments on each change.
|
|
1888
|
+
*/
|
|
1889
|
+
get version() {
|
|
1890
|
+
return this.variableTable.version;
|
|
1891
|
+
}
|
|
1892
|
+
/**
|
|
1893
|
+
* @deprecated use onListOrAnyVarChange instead
|
|
1894
|
+
*/
|
|
1895
|
+
get onDataChange() {
|
|
1896
|
+
return this.variableTable.onDataChange.bind(this.variableTable);
|
|
1897
|
+
}
|
|
1898
|
+
/**
|
|
1899
|
+
* An event that fires when the list of output variables changes.
|
|
1900
|
+
*/
|
|
1901
|
+
get onVariableListChange() {
|
|
1902
|
+
return this.variableTable.onVariableListChange.bind(this.variableTable);
|
|
1903
|
+
}
|
|
1904
|
+
/**
|
|
1905
|
+
* An event that fires when any output variable's value changes.
|
|
1906
|
+
*/
|
|
1907
|
+
get onAnyVariableChange() {
|
|
1908
|
+
return this.variableTable.onAnyVariableChange.bind(this.variableTable);
|
|
1909
|
+
}
|
|
1910
|
+
/**
|
|
1911
|
+
* An event that fires when the output variable list changes or any variable's value is updated.
|
|
1912
|
+
*/
|
|
1913
|
+
get onListOrAnyVarChange() {
|
|
1914
|
+
return this.variableTable.onListOrAnyVarChange.bind(this.variableTable);
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* The output variable declarations of the scope, sorted by order.
|
|
1918
|
+
*/
|
|
1919
|
+
get variables() {
|
|
1920
|
+
return this.memo(
|
|
1921
|
+
"variables",
|
|
1922
|
+
() => this.variableTable.variables.sort((a, b) => a.order - b.order)
|
|
1923
|
+
);
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* The keys of the output variables.
|
|
1927
|
+
*/
|
|
1928
|
+
get variableKeys() {
|
|
1929
|
+
return this.memo("variableKeys", () => this.variableTable.variableKeys);
|
|
1930
|
+
}
|
|
1931
|
+
addVariableToTable(variable) {
|
|
1932
|
+
if (variable.scope !== this.scope) {
|
|
1933
|
+
throw Error("VariableDeclaration must be a ast node in scope");
|
|
1934
|
+
}
|
|
1935
|
+
this.variableTable.addVariableToTable(variable);
|
|
1936
|
+
this._hasChanges = true;
|
|
1937
|
+
}
|
|
1938
|
+
removeVariableFromTable(key) {
|
|
1939
|
+
this.variableTable.removeVariableFromTable(key);
|
|
1940
|
+
this._hasChanges = true;
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Retrieves a variable declaration by its key.
|
|
1944
|
+
* @param key The key of the variable.
|
|
1945
|
+
* @returns The `VariableDeclaration` or `undefined` if not found.
|
|
1946
|
+
*/
|
|
1947
|
+
getVariableByKey(key) {
|
|
1948
|
+
return this.variableTable.getVariableByKey(key);
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* Notifies the covering scopes that the available variables have changed.
|
|
1952
|
+
*/
|
|
1953
|
+
notifyCoversChange() {
|
|
1954
|
+
this.scope.coverScopes.forEach((scope) => scope.available.refresh());
|
|
1955
|
+
}
|
|
1956
|
+
};
|
|
1957
|
+
var ScopeAvailableData = class {
|
|
1958
|
+
constructor(scope) {
|
|
1959
|
+
this.scope = scope;
|
|
1960
|
+
this.memo = createMemo();
|
|
1961
|
+
this._version = 0;
|
|
1962
|
+
this.refresh$ = new Subject();
|
|
1963
|
+
this._variables = [];
|
|
1964
|
+
/**
|
|
1965
|
+
* An observable that emits when the list of available variables changes.
|
|
1966
|
+
*/
|
|
1967
|
+
this.variables$ = this.refresh$.pipe(
|
|
1968
|
+
// Map to the flattened list of variables from all dependency scopes.
|
|
1969
|
+
map(() => flatten(this.depScopes.map((scope) => scope.output.variables || []))),
|
|
1970
|
+
// Use shallow equality to check if the variable list has changed.
|
|
1971
|
+
distinctUntilChanged(shallowEqual),
|
|
1972
|
+
share()
|
|
1973
|
+
);
|
|
1974
|
+
/**
|
|
1975
|
+
* An observable that emits when any variable in the available list changes its value.
|
|
1976
|
+
*/
|
|
1977
|
+
this.anyVariableChange$ = this.variables$.pipe(
|
|
1978
|
+
switchMap(
|
|
1979
|
+
(_variables) => merge(
|
|
1980
|
+
..._variables.map(
|
|
1981
|
+
(_v) => _v.value$.pipe(
|
|
1982
|
+
// Skip the initial value of the BehaviorSubject.
|
|
1983
|
+
skip(1)
|
|
1984
|
+
)
|
|
1985
|
+
)
|
|
1986
|
+
)
|
|
1987
|
+
),
|
|
1988
|
+
share()
|
|
1989
|
+
);
|
|
1990
|
+
/**
|
|
1991
|
+
* @deprecated
|
|
1992
|
+
*/
|
|
1993
|
+
this.onDataChangeEmitter = new Emitter();
|
|
1994
|
+
this.onListOrAnyVarChangeEmitter = new Emitter();
|
|
1995
|
+
/**
|
|
1996
|
+
* @deprecated use available.onListOrAnyVarChange instead
|
|
1997
|
+
*/
|
|
1998
|
+
this.onDataChange = this.onDataChangeEmitter.event;
|
|
1999
|
+
/**
|
|
2000
|
+
* An event that fires when the variable list changes or any variable's value is updated.
|
|
2001
|
+
*/
|
|
2002
|
+
this.onListOrAnyVarChange = this.onListOrAnyVarChangeEmitter.event;
|
|
2003
|
+
this.scope.toDispose.pushAll([
|
|
2004
|
+
this.onVariableListChange((_variables) => {
|
|
2005
|
+
this._variables = _variables;
|
|
2006
|
+
this.memo.clear();
|
|
2007
|
+
this.onDataChangeEmitter.fire(this._variables);
|
|
2008
|
+
this.bumpVersion();
|
|
2009
|
+
this.onListOrAnyVarChangeEmitter.fire(this._variables);
|
|
2010
|
+
}),
|
|
2011
|
+
this.onAnyVariableChange(() => {
|
|
2012
|
+
this.onDataChangeEmitter.fire(this._variables);
|
|
2013
|
+
this.bumpVersion();
|
|
2014
|
+
this.onListOrAnyVarChangeEmitter.fire(this._variables);
|
|
2015
|
+
}),
|
|
2016
|
+
Disposable.create(() => {
|
|
2017
|
+
this.refresh$.complete();
|
|
2018
|
+
this.refresh$.unsubscribe();
|
|
2019
|
+
})
|
|
2020
|
+
]);
|
|
2021
|
+
}
|
|
2022
|
+
/**
|
|
2023
|
+
* The global variable table from the variable engine.
|
|
2024
|
+
*/
|
|
2025
|
+
get globalVariableTable() {
|
|
2026
|
+
return this.scope.variableEngine.globalVariableTable;
|
|
2027
|
+
}
|
|
2028
|
+
/**
|
|
2029
|
+
* The current version of the available data, which increments on each change.
|
|
2030
|
+
*/
|
|
2031
|
+
get version() {
|
|
2032
|
+
return this._version;
|
|
2033
|
+
}
|
|
2034
|
+
bumpVersion() {
|
|
2035
|
+
this._version = this._version + 1;
|
|
2036
|
+
if (this._version === Number.MAX_SAFE_INTEGER) {
|
|
2037
|
+
this._version = 0;
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
/**
|
|
2041
|
+
* Refreshes the list of available variables.
|
|
2042
|
+
* This should be called when the dependencies of the scope change.
|
|
2043
|
+
*/
|
|
2044
|
+
refresh() {
|
|
2045
|
+
if (this.scope.disposed) {
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
this.refresh$.next();
|
|
2049
|
+
}
|
|
2050
|
+
/**
|
|
2051
|
+
* Subscribes to changes in any variable's value in the available list.
|
|
2052
|
+
* @param observer A function to be called with the changed variable.
|
|
2053
|
+
* @returns A disposable to unsubscribe from the changes.
|
|
2054
|
+
*/
|
|
2055
|
+
onAnyVariableChange(observer) {
|
|
2056
|
+
return subsToDisposable(this.anyVariableChange$.subscribe(observer));
|
|
2057
|
+
}
|
|
2058
|
+
/**
|
|
2059
|
+
* Subscribes to changes in the list of available variables.
|
|
2060
|
+
* @param observer A function to be called with the new list of variables.
|
|
2061
|
+
* @returns A disposable to unsubscribe from the changes.
|
|
2062
|
+
*/
|
|
2063
|
+
onVariableListChange(observer) {
|
|
2064
|
+
return subsToDisposable(this.variables$.subscribe(observer));
|
|
2065
|
+
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Gets the list of available variables.
|
|
2068
|
+
*/
|
|
2069
|
+
get variables() {
|
|
2070
|
+
return this._variables;
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* Gets the keys of the available variables.
|
|
2074
|
+
*/
|
|
2075
|
+
get variableKeys() {
|
|
2076
|
+
return this.memo("availableKeys", () => this._variables.map((_v) => _v.key));
|
|
2077
|
+
}
|
|
2078
|
+
/**
|
|
2079
|
+
* Gets the dependency scopes.
|
|
2080
|
+
*/
|
|
2081
|
+
get depScopes() {
|
|
2082
|
+
return this.scope.depScopes;
|
|
2083
|
+
}
|
|
2084
|
+
/**
|
|
2085
|
+
* Retrieves a variable field by its key path from the available variables.
|
|
2086
|
+
* @param keyPath The key path to the variable field.
|
|
2087
|
+
* @returns The found `BaseVariableField` or `undefined`.
|
|
2088
|
+
*/
|
|
2089
|
+
getByKeyPath(keyPath = []) {
|
|
2090
|
+
if (!this.variableKeys.includes(keyPath[0])) {
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2093
|
+
return this.globalVariableTable.getByKeyPath(keyPath);
|
|
2094
|
+
}
|
|
2095
|
+
/**
|
|
2096
|
+
* Tracks changes to a variable field by its key path.
|
|
2097
|
+
* This includes changes to its type, value, or any nested properties.
|
|
2098
|
+
* @param keyPath The key path to the variable field to track.
|
|
2099
|
+
* @param cb The callback to execute when the variable changes.
|
|
2100
|
+
* @param opts Configuration options for the subscription.
|
|
2101
|
+
* @returns A disposable to unsubscribe from the tracking.
|
|
2102
|
+
*/
|
|
2103
|
+
trackByKeyPath(keyPath = [], cb, opts) {
|
|
2104
|
+
const { triggerOnInit = true, debounceAnimation, selector } = opts || {};
|
|
2105
|
+
return subsToDisposable(
|
|
2106
|
+
merge(this.anyVariableChange$, this.variables$).pipe(
|
|
2107
|
+
triggerOnInit ? startWith() : tap(() => null),
|
|
2108
|
+
map(() => {
|
|
2109
|
+
const v = this.getByKeyPath(keyPath);
|
|
2110
|
+
return selector ? selector(v) : v;
|
|
2111
|
+
}),
|
|
2112
|
+
distinctUntilChanged(
|
|
2113
|
+
(a, b) => shallowEqual(a, b),
|
|
2114
|
+
(value) => {
|
|
2115
|
+
if (value instanceof ASTNode) {
|
|
2116
|
+
return value.hash;
|
|
2117
|
+
}
|
|
2118
|
+
return value;
|
|
2119
|
+
}
|
|
2120
|
+
),
|
|
2121
|
+
// Debounce updates to a single emission per animation frame.
|
|
2122
|
+
debounceAnimation ? debounceTime(0, animationFrameScheduler) : tap(() => null)
|
|
2123
|
+
).subscribe(cb)
|
|
2124
|
+
);
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
2127
|
+
var ScopeEventData = class {
|
|
2128
|
+
constructor(scope) {
|
|
2129
|
+
this.scope = scope;
|
|
2130
|
+
this.event$ = new Subject();
|
|
2131
|
+
scope.toDispose.pushAll([
|
|
2132
|
+
this.subscribe((_action) => {
|
|
2133
|
+
scope.variableEngine.fireGlobalEvent(_action);
|
|
2134
|
+
})
|
|
2135
|
+
]);
|
|
2136
|
+
}
|
|
2137
|
+
/**
|
|
2138
|
+
* Dispatches a global event.
|
|
2139
|
+
* @param action The event action to dispatch.
|
|
2140
|
+
*/
|
|
2141
|
+
dispatch(action) {
|
|
2142
|
+
if (this.scope.disposed) {
|
|
2143
|
+
return;
|
|
2144
|
+
}
|
|
2145
|
+
this.event$.next(action);
|
|
2146
|
+
}
|
|
2147
|
+
/**
|
|
2148
|
+
* Subscribes to all global events.
|
|
2149
|
+
* @param observer The observer function to call with the event action.
|
|
2150
|
+
* @returns A disposable to unsubscribe from the events.
|
|
2151
|
+
*/
|
|
2152
|
+
subscribe(observer) {
|
|
2153
|
+
return subsToDisposable(this.event$.subscribe(observer));
|
|
2154
|
+
}
|
|
2155
|
+
/**
|
|
2156
|
+
* Subscribes to a specific type of global event.
|
|
2157
|
+
* @param type The type of the event to subscribe to.
|
|
2158
|
+
* @param observer The observer function to call with the event action.
|
|
2159
|
+
* @returns A disposable to unsubscribe from the event.
|
|
2160
|
+
*/
|
|
2161
|
+
on(type, observer) {
|
|
2162
|
+
return subsToDisposable(
|
|
2163
|
+
this.event$.pipe(filter((_action) => _action.type === type)).subscribe(observer)
|
|
2164
|
+
);
|
|
2165
|
+
}
|
|
2166
|
+
};
|
|
2167
|
+
|
|
2168
|
+
// src/scope/scope.ts
|
|
2169
|
+
var Scope = class {
|
|
2170
|
+
constructor(options) {
|
|
2171
|
+
/**
|
|
2172
|
+
* A memoization utility for caching computed values.
|
|
2173
|
+
*/
|
|
2174
|
+
this.memo = createMemo();
|
|
2175
|
+
this.toDispose = new DisposableCollection();
|
|
2176
|
+
this.onDispose = this.toDispose.onDispose;
|
|
2177
|
+
this.id = options.id;
|
|
2178
|
+
this.meta = options.meta || {};
|
|
2179
|
+
this.variableEngine = options.variableEngine;
|
|
2180
|
+
this.event = new ScopeEventData(this);
|
|
2181
|
+
this.ast = this.variableEngine.astRegisters.createAST(
|
|
2182
|
+
{
|
|
2183
|
+
kind: "MapNode" /* MapNode */,
|
|
2184
|
+
key: String(this.id)
|
|
2185
|
+
},
|
|
2186
|
+
{
|
|
2187
|
+
scope: this
|
|
2188
|
+
}
|
|
2189
|
+
);
|
|
2190
|
+
this.output = new ScopeOutputData(this);
|
|
2191
|
+
this.available = new ScopeAvailableData(this);
|
|
2192
|
+
}
|
|
2193
|
+
/**
|
|
2194
|
+
* Refreshes the covering scopes.
|
|
2195
|
+
*/
|
|
2196
|
+
refreshCovers() {
|
|
2197
|
+
this.memo.clear("covers");
|
|
2198
|
+
}
|
|
2199
|
+
/**
|
|
2200
|
+
* Refreshes the dependency scopes and the available variables.
|
|
2201
|
+
*/
|
|
2202
|
+
refreshDeps() {
|
|
2203
|
+
this.memo.clear("deps");
|
|
2204
|
+
this.available.refresh();
|
|
2205
|
+
}
|
|
2206
|
+
/**
|
|
2207
|
+
* Gets the scopes that this scope depends on.
|
|
2208
|
+
*/
|
|
2209
|
+
get depScopes() {
|
|
2210
|
+
return this.memo(
|
|
2211
|
+
"deps",
|
|
2212
|
+
() => this.variableEngine.chain.getDeps(this).filter((_scope) => Boolean(_scope) && !_scope?.disposed)
|
|
2213
|
+
);
|
|
2214
|
+
}
|
|
2215
|
+
/**
|
|
2216
|
+
* Gets the scopes that are covered by this scope.
|
|
2217
|
+
*/
|
|
2218
|
+
get coverScopes() {
|
|
2219
|
+
return this.memo(
|
|
2220
|
+
"covers",
|
|
2221
|
+
() => this.variableEngine.chain.getCovers(this).filter((_scope) => Boolean(_scope) && !_scope?.disposed)
|
|
2222
|
+
);
|
|
2223
|
+
}
|
|
2224
|
+
/**
|
|
2225
|
+
* Disposes of the scope and its resources.
|
|
2226
|
+
* This will also trigger updates in dependent and covering scopes.
|
|
2227
|
+
*/
|
|
2228
|
+
dispose() {
|
|
2229
|
+
this.ast.dispose();
|
|
2230
|
+
this.toDispose.dispose();
|
|
2231
|
+
this.coverScopes.forEach((_scope) => _scope.refreshDeps());
|
|
2232
|
+
this.depScopes.forEach((_scope) => _scope.refreshCovers());
|
|
2233
|
+
}
|
|
2234
|
+
get disposed() {
|
|
2235
|
+
return this.toDispose.disposed;
|
|
2236
|
+
}
|
|
2237
|
+
setVar(arg1, arg2) {
|
|
2238
|
+
if (typeof arg1 === "string" && arg2 !== void 0) {
|
|
2239
|
+
return this.ast.set(arg1, arg2);
|
|
2240
|
+
}
|
|
2241
|
+
if (typeof arg1 === "object" && arg2 === void 0) {
|
|
2242
|
+
return this.ast.set("outputs", arg1);
|
|
2243
|
+
}
|
|
2244
|
+
throw new Error("Invalid arguments");
|
|
2245
|
+
}
|
|
2246
|
+
/**
|
|
2247
|
+
* Retrieves a variable from the scope by its key.
|
|
2248
|
+
*
|
|
2249
|
+
* @param key The key of the variable to retrieve. Defaults to 'outputs'.
|
|
2250
|
+
* @returns The AST node for the variable, or `undefined` if not found.
|
|
2251
|
+
*/
|
|
2252
|
+
getVar(key = "outputs") {
|
|
2253
|
+
return this.ast.get(key);
|
|
2254
|
+
}
|
|
2255
|
+
/**
|
|
2256
|
+
* Clears a variable from the scope by its key.
|
|
2257
|
+
*
|
|
2258
|
+
* @param key The key of the variable to clear. Defaults to 'outputs'.
|
|
2259
|
+
*/
|
|
2260
|
+
clearVar(key = "outputs") {
|
|
2261
|
+
return this.ast.remove(key);
|
|
2262
|
+
}
|
|
2263
|
+
};
|
|
2264
|
+
|
|
2265
|
+
// src/variable-engine.ts
|
|
2266
|
+
var VariableEngine = class {
|
|
2267
|
+
constructor(chain, astRegisters) {
|
|
2268
|
+
this.chain = chain;
|
|
2269
|
+
this.astRegisters = astRegisters;
|
|
2270
|
+
this.toDispose = new DisposableCollection();
|
|
2271
|
+
this.memo = createMemo();
|
|
2272
|
+
this.scopeMap = /* @__PURE__ */ new Map();
|
|
2273
|
+
/**
|
|
2274
|
+
* A rxjs subject that emits global events occurring within the variable engine.
|
|
2275
|
+
*/
|
|
2276
|
+
this.globalEvent$ = new Subject();
|
|
2277
|
+
this.onScopeChangeEmitter = new Emitter();
|
|
2278
|
+
/**
|
|
2279
|
+
* A table containing all global variables.
|
|
2280
|
+
*/
|
|
2281
|
+
this.globalVariableTable = new VariableTable();
|
|
2282
|
+
/**
|
|
2283
|
+
* An event that fires whenever a scope is added, updated, or deleted.
|
|
2284
|
+
*/
|
|
2285
|
+
this.onScopeChange = this.onScopeChangeEmitter.event;
|
|
2286
|
+
this.toDispose.pushAll([
|
|
2287
|
+
chain,
|
|
2288
|
+
Disposable.create(() => {
|
|
2289
|
+
this.getAllScopes().forEach((scope) => scope.dispose());
|
|
2290
|
+
this.globalVariableTable.dispose();
|
|
2291
|
+
})
|
|
2292
|
+
]);
|
|
2293
|
+
}
|
|
2294
|
+
/**
|
|
2295
|
+
* The Inversify container instance.
|
|
2296
|
+
*/
|
|
2297
|
+
get container() {
|
|
2298
|
+
return this.containerProvider();
|
|
2299
|
+
}
|
|
2300
|
+
dispose() {
|
|
2301
|
+
this.toDispose.dispose();
|
|
2302
|
+
}
|
|
2303
|
+
/**
|
|
2304
|
+
* Retrieves a scope by its unique identifier.
|
|
2305
|
+
* @param scopeId The ID of the scope to retrieve.
|
|
2306
|
+
* @returns The scope if found, otherwise undefined.
|
|
2307
|
+
*/
|
|
2308
|
+
getScopeById(scopeId) {
|
|
2309
|
+
return this.scopeMap.get(scopeId);
|
|
2310
|
+
}
|
|
2311
|
+
/**
|
|
2312
|
+
* Removes a scope by its unique identifier and disposes of it.
|
|
2313
|
+
* @param scopeId The ID of the scope to remove.
|
|
2314
|
+
*/
|
|
2315
|
+
removeScopeById(scopeId) {
|
|
2316
|
+
this.getScopeById(scopeId)?.dispose();
|
|
2317
|
+
}
|
|
2318
|
+
/**
|
|
2319
|
+
* Creates a new scope or retrieves an existing one if the ID and type match.
|
|
2320
|
+
* @param id The unique identifier for the scope.
|
|
2321
|
+
* @param meta Optional metadata for the scope, defined by the user.
|
|
2322
|
+
* @param options Options for creating the scope.
|
|
2323
|
+
* @param options.ScopeConstructor The constructor to use for creating the scope. Defaults to `Scope`.
|
|
2324
|
+
* @returns The created or existing scope.
|
|
2325
|
+
*/
|
|
2326
|
+
createScope(id, meta, options = {}) {
|
|
2327
|
+
const { ScopeConstructor = Scope } = options;
|
|
2328
|
+
let scope = this.getScopeById(id);
|
|
2329
|
+
if (!scope) {
|
|
2330
|
+
scope = new ScopeConstructor({ variableEngine: this, meta, id });
|
|
2331
|
+
this.scopeMap.set(id, scope);
|
|
2332
|
+
this.onScopeChangeEmitter.fire({ type: "add", scope });
|
|
2333
|
+
scope.toDispose.pushAll([
|
|
2334
|
+
scope.ast.subscribe(() => {
|
|
2335
|
+
this.onScopeChangeEmitter.fire({ type: "update", scope });
|
|
2336
|
+
}),
|
|
2337
|
+
// Fires when available variables change
|
|
2338
|
+
scope.available.onDataChange(() => {
|
|
2339
|
+
this.onScopeChangeEmitter.fire({ type: "available", scope });
|
|
2340
|
+
})
|
|
2341
|
+
]);
|
|
2342
|
+
scope.onDispose(() => {
|
|
2343
|
+
this.scopeMap.delete(id);
|
|
2344
|
+
this.onScopeChangeEmitter.fire({ type: "delete", scope });
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
return scope;
|
|
2348
|
+
}
|
|
2349
|
+
/**
|
|
2350
|
+
* Retrieves all scopes currently managed by the engine.
|
|
2351
|
+
* @param options Options for retrieving the scopes.
|
|
2352
|
+
* @param options.sort Whether to sort the scopes based on their dependency chain.
|
|
2353
|
+
* @returns An array of all scopes.
|
|
2354
|
+
*/
|
|
2355
|
+
getAllScopes({
|
|
2356
|
+
sort
|
|
2357
|
+
} = {}) {
|
|
2358
|
+
const allScopes = Array.from(this.scopeMap.values());
|
|
2359
|
+
if (sort) {
|
|
2360
|
+
const sortScopes = this.chain.sortAll();
|
|
2361
|
+
const remainScopes = new Set(allScopes);
|
|
2362
|
+
sortScopes.forEach((_scope) => remainScopes.delete(_scope));
|
|
2363
|
+
return [...sortScopes, ...Array.from(remainScopes)];
|
|
2364
|
+
}
|
|
2365
|
+
return [...allScopes];
|
|
2366
|
+
}
|
|
2367
|
+
/**
|
|
2368
|
+
* Fires a global event to be broadcast to all listeners.
|
|
2369
|
+
* @param event The global event to fire.
|
|
2370
|
+
*/
|
|
2371
|
+
fireGlobalEvent(event) {
|
|
2372
|
+
this.globalEvent$.next(event);
|
|
2373
|
+
}
|
|
2374
|
+
/**
|
|
2375
|
+
* Subscribes to a specific type of global event.
|
|
2376
|
+
* @param type The type of the event to listen for.
|
|
2377
|
+
* @param observer A function to be called when the event is observed.
|
|
2378
|
+
* @returns A disposable object to unsubscribe from the event.
|
|
2379
|
+
*/
|
|
2380
|
+
onGlobalEvent(type, observer) {
|
|
2381
|
+
return subsToDisposable(
|
|
2382
|
+
this.globalEvent$.subscribe((_action) => {
|
|
2383
|
+
if (_action.type === type) {
|
|
2384
|
+
observer(_action);
|
|
2385
|
+
}
|
|
2386
|
+
})
|
|
2387
|
+
);
|
|
2388
|
+
}
|
|
2389
|
+
};
|
|
2390
|
+
__decorateClass([
|
|
2391
|
+
inject(ContainerProvider)
|
|
2392
|
+
], VariableEngine.prototype, "containerProvider", 2);
|
|
2393
|
+
__decorateClass([
|
|
2394
|
+
preDestroy()
|
|
2395
|
+
], VariableEngine.prototype, "dispose", 1);
|
|
2396
|
+
VariableEngine = __decorateClass([
|
|
2397
|
+
injectable(),
|
|
2398
|
+
__decorateParam(0, inject(ScopeChain)),
|
|
2399
|
+
__decorateParam(1, inject(ASTRegisters))
|
|
2400
|
+
], VariableEngine);
|
|
2401
|
+
var VariableFieldKeyRenameService = class {
|
|
2402
|
+
constructor() {
|
|
2403
|
+
this.toDispose = new DisposableCollection();
|
|
2404
|
+
this.renameEmitter = new Emitter();
|
|
2405
|
+
/**
|
|
2406
|
+
* Emits events for fields that are disposed of during a list change, but not renamed.
|
|
2407
|
+
* This helps distinguish between a field that was truly removed and one that was renamed.
|
|
2408
|
+
*/
|
|
2409
|
+
this.disposeInListEmitter = new Emitter();
|
|
2410
|
+
/**
|
|
2411
|
+
* An event that fires when a variable field key is successfully renamed.
|
|
2412
|
+
*/
|
|
2413
|
+
this.onRename = this.renameEmitter.event;
|
|
2414
|
+
/**
|
|
2415
|
+
* An event that fires when a field is removed from a list (and not part of a rename).
|
|
2416
|
+
*/
|
|
2417
|
+
this.onDisposeInList = this.disposeInListEmitter.event;
|
|
2418
|
+
}
|
|
2419
|
+
/**
|
|
2420
|
+
* Handles changes in a list of fields to detect rename operations.
|
|
2421
|
+
* @param ast The AST node where the change occurred.
|
|
2422
|
+
* @param prev The list of fields before the change.
|
|
2423
|
+
* @param next The list of fields after the change.
|
|
2424
|
+
*/
|
|
2425
|
+
handleFieldListChange(ast, prev, next) {
|
|
2426
|
+
if (!ast || !prev?.length || !next?.length) {
|
|
2427
|
+
this.notifyFieldsDispose(prev, next);
|
|
2428
|
+
return;
|
|
2429
|
+
}
|
|
2430
|
+
if (prev.length !== next.length) {
|
|
2431
|
+
this.notifyFieldsDispose(prev, next);
|
|
2432
|
+
return;
|
|
2433
|
+
}
|
|
2434
|
+
let renameNodeInfo = null;
|
|
2435
|
+
let existFieldChanged = false;
|
|
2436
|
+
for (const [index, prevField] of prev.entries()) {
|
|
2437
|
+
const nextField = next[index];
|
|
2438
|
+
if (prevField.key !== nextField.key) {
|
|
2439
|
+
if (existFieldChanged) {
|
|
2440
|
+
this.notifyFieldsDispose(prev, next);
|
|
2441
|
+
return;
|
|
2442
|
+
}
|
|
2443
|
+
existFieldChanged = true;
|
|
2444
|
+
if (prevField.type?.kind === nextField.type?.kind) {
|
|
2445
|
+
renameNodeInfo = { before: prevField, after: nextField };
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
if (!renameNodeInfo) {
|
|
2450
|
+
this.notifyFieldsDispose(prev, next);
|
|
2451
|
+
return;
|
|
2452
|
+
}
|
|
2453
|
+
this.renameEmitter.fire(renameNodeInfo);
|
|
2454
|
+
}
|
|
2455
|
+
/**
|
|
2456
|
+
* Notifies listeners about fields that were removed from a list.
|
|
2457
|
+
* @param prev The list of fields before the change.
|
|
2458
|
+
* @param next The list of fields after the change.
|
|
2459
|
+
*/
|
|
2460
|
+
notifyFieldsDispose(prev, next) {
|
|
2461
|
+
const removedFields = difference(prev || [], next || []);
|
|
2462
|
+
removedFields.forEach((_field) => this.disposeInListEmitter.fire(_field));
|
|
2463
|
+
}
|
|
2464
|
+
init() {
|
|
2465
|
+
this.toDispose.pushAll([
|
|
2466
|
+
this.variableEngine.onGlobalEvent(
|
|
2467
|
+
"VariableListChange",
|
|
2468
|
+
(_action) => {
|
|
2469
|
+
this.handleFieldListChange(_action.ast, _action.payload?.prev, _action.payload?.next);
|
|
2470
|
+
}
|
|
2471
|
+
),
|
|
2472
|
+
this.variableEngine.onGlobalEvent(
|
|
2473
|
+
"ObjectPropertiesChange",
|
|
2474
|
+
(_action) => {
|
|
2475
|
+
this.handleFieldListChange(_action.ast, _action.payload?.prev, _action.payload?.next);
|
|
2476
|
+
}
|
|
2477
|
+
)
|
|
2478
|
+
]);
|
|
2479
|
+
}
|
|
2480
|
+
dispose() {
|
|
2481
|
+
this.toDispose.dispose();
|
|
2482
|
+
}
|
|
2483
|
+
};
|
|
2484
|
+
__decorateClass([
|
|
2485
|
+
inject(VariableEngine)
|
|
2486
|
+
], VariableFieldKeyRenameService.prototype, "variableEngine", 2);
|
|
2487
|
+
__decorateClass([
|
|
2488
|
+
postConstruct()
|
|
2489
|
+
], VariableFieldKeyRenameService.prototype, "init", 1);
|
|
2490
|
+
__decorateClass([
|
|
2491
|
+
preDestroy()
|
|
2492
|
+
], VariableFieldKeyRenameService.prototype, "dispose", 1);
|
|
2493
|
+
VariableFieldKeyRenameService = __decorateClass([
|
|
2494
|
+
injectable()
|
|
2495
|
+
], VariableFieldKeyRenameService);
|
|
2496
|
+
|
|
2497
|
+
// src/variable-container-module.ts
|
|
2498
|
+
var VariableContainerModule = new ContainerModule((bind) => {
|
|
2499
|
+
bind(VariableEngine).toSelf().inSingletonScope();
|
|
2500
|
+
bind(ASTRegisters).toSelf().inSingletonScope();
|
|
2501
|
+
bind(VariableFieldKeyRenameService).toSelf().inSingletonScope();
|
|
2502
|
+
bind(VariableEngineProvider).toDynamicValue((ctx) => () => ctx.container.get(VariableEngine));
|
|
2503
|
+
bind(ContainerProvider).toDynamicValue((ctx) => () => ctx.container);
|
|
2504
|
+
});
|
|
2505
|
+
var ScopeKey = /* @__PURE__ */ Symbol("Scope");
|
|
2506
|
+
var ScopeProvider = defineComponent({
|
|
2507
|
+
name: "ScopeProvider",
|
|
2508
|
+
props: {
|
|
2509
|
+
/**
|
|
2510
|
+
* scope used in the context
|
|
2511
|
+
*/
|
|
2512
|
+
scope: {
|
|
2513
|
+
type: Object
|
|
2514
|
+
},
|
|
2515
|
+
/**
|
|
2516
|
+
* @deprecated use scope prop instead, this is kept for backward compatibility
|
|
2517
|
+
*/
|
|
2518
|
+
value: {
|
|
2519
|
+
type: Object
|
|
2520
|
+
}
|
|
2521
|
+
},
|
|
2522
|
+
setup(props, { slots }) {
|
|
2523
|
+
const scopeToUse = computed(() => props.scope || props.value?.scope);
|
|
2524
|
+
if (!scopeToUse.value) {
|
|
2525
|
+
throw new Error("[ScopeProvider] scope is required");
|
|
2526
|
+
}
|
|
2527
|
+
provide(ScopeKey, scopeToUse);
|
|
2528
|
+
return () => slots.default?.();
|
|
2529
|
+
}
|
|
2530
|
+
});
|
|
2531
|
+
var useCurrentScope = (params) => {
|
|
2532
|
+
const { strict = false } = params || {};
|
|
2533
|
+
const context = inject$1(ScopeKey, void 0);
|
|
2534
|
+
if (!context) {
|
|
2535
|
+
if (strict) {
|
|
2536
|
+
throw new Error("useCurrentScope must be used within a <ScopeProvider scope={scope}>");
|
|
2537
|
+
}
|
|
2538
|
+
console.warn("useCurrentScope should be used within a <ScopeProvider scope={scope}>");
|
|
2539
|
+
}
|
|
2540
|
+
return unref(context);
|
|
2541
|
+
};
|
|
2542
|
+
function useScopeAvailable(params) {
|
|
2543
|
+
const { autoRefresh = true } = params || {};
|
|
2544
|
+
const scope = useCurrentScope({ strict: true });
|
|
2545
|
+
const tick = shallowRef(0);
|
|
2546
|
+
if (autoRefresh) {
|
|
2547
|
+
const disposable = scope.available.onListOrAnyVarChange(() => {
|
|
2548
|
+
tick.value += 1;
|
|
2549
|
+
});
|
|
2550
|
+
onScopeDispose(() => disposable.dispose());
|
|
2551
|
+
}
|
|
2552
|
+
return computed(() => {
|
|
2553
|
+
tick.value;
|
|
2554
|
+
return scope.available;
|
|
2555
|
+
});
|
|
2556
|
+
}
|
|
2557
|
+
function useAvailableVariables() {
|
|
2558
|
+
const scope = useCurrentScope();
|
|
2559
|
+
const variableEngine = useService(VariableEngine);
|
|
2560
|
+
const tick = shallowRef(0);
|
|
2561
|
+
const disposable = !scope ? variableEngine.globalVariableTable.onListOrAnyVarChange(() => {
|
|
2562
|
+
tick.value += 1;
|
|
2563
|
+
}) : scope.available.onDataChange(() => {
|
|
2564
|
+
tick.value += 1;
|
|
2565
|
+
});
|
|
2566
|
+
onScopeDispose(() => disposable.dispose());
|
|
2567
|
+
return computed(() => {
|
|
2568
|
+
tick.value;
|
|
2569
|
+
return scope ? scope.available.variables : variableEngine.globalVariableTable.variables;
|
|
2570
|
+
});
|
|
2571
|
+
}
|
|
2572
|
+
function useOutputVariables() {
|
|
2573
|
+
const scope = useCurrentScope();
|
|
2574
|
+
const tick = shallowRef(0);
|
|
2575
|
+
if (!scope) {
|
|
2576
|
+
throw new Error(
|
|
2577
|
+
"[useOutputVariables]: No scope found, useOutputVariables must be used in <ScopeProvider>"
|
|
2578
|
+
);
|
|
2579
|
+
}
|
|
2580
|
+
const disposable = scope.output.onListOrAnyVarChange(() => {
|
|
2581
|
+
tick.value += 1;
|
|
2582
|
+
});
|
|
2583
|
+
onScopeDispose(() => disposable.dispose());
|
|
2584
|
+
return computed(() => {
|
|
2585
|
+
tick.value;
|
|
2586
|
+
return scope.output.variables;
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
export { ASTFactory, ASTKind, ASTMatch, ASTNode, ASTNodeFlags, ASTRegisters, ArrayType, BaseExpression, BaseType, BaseVariableField, BooleanType, CustomType, DataNode, EnumerateExpression, IntegerType, KeyPathExpression, LegacyKeyPathExpression, ListNode, MapNode, MapType, NumberType, ObjectType, Property, Scope, ScopeChain, ScopeKey, ScopeOutputData, ScopeProvider, StringType, VariableContainerModule, VariableDeclaration, VariableDeclarationList, VariableEngine, VariableEngineProvider, VariableFieldKeyRenameService, WrapArrayExpression, injectToAST, isMatchAST, postConstructAST, useAvailableVariables, useCurrentScope, useOutputVariables, useScopeAvailable };
|
|
2591
|
+
//# sourceMappingURL=index.js.map
|
|
2592
|
+
//# sourceMappingURL=index.js.map
|