@flowgram.ai/variable-core 0.1.0-alpha.2 → 0.1.0-alpha.21

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