@tmagic/editor 1.8.0-beta.19 → 1.8.0-beta.20

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.
@@ -6593,7 +6593,7 @@
6593
6593
  * 派发「页面 / 页面片结构变更」事件(`page-structure-change`)。
6594
6594
  *
6595
6595
  * 常规 `editorService.add` / `remove` 页面节点不会写入 `page` 历史栈(见 editor.add / remove 中
6596
- * 对 isPage / isPageFragment 的分支),因此不会产生任何 historyService 事件。该方法用于在这些
6596
+ * 对 isPageOrFragment 的分支),因此不会产生任何 historyService 事件。该方法用于在这些
6597
6597
  * 场景(以及 setRoot 整体替换 DSL 增删页面)下,向外统一通知页面结构的增删变化,供业务方感知。
6598
6598
  *
6599
6599
  * 一次操作涉及多个页面时,调用方应把本次增删的页面**合并为一个 change 一次性传入**,
@@ -7322,7 +7322,7 @@
7322
7322
  };
7323
7323
  }
7324
7324
  const root = editor_default.get("root");
7325
- if (((0, _tmagic_utils.isPage)(pasteConfig) || (0, _tmagic_utils.isPageFragment)(pasteConfig)) && root) pasteConfig.name = generatePageNameByApp(root, (0, _tmagic_utils.isPage)(pasteConfig) ? _tmagic_core.NodeType.PAGE : _tmagic_core.NodeType.PAGE_FRAGMENT);
7325
+ if ((0, _tmagic_utils.isPageOrFragment)(pasteConfig) && root) pasteConfig.name = generatePageNameByApp(root, (0, _tmagic_utils.isPage)(pasteConfig) ? _tmagic_core.NodeType.PAGE : _tmagic_core.NodeType.PAGE_FRAGMENT);
7326
7326
  return pasteConfig;
7327
7327
  });
7328
7328
  };
@@ -7510,7 +7510,7 @@
7510
7510
  isOnDifferentPage(node) {
7511
7511
  const currentPageId = this.get("page")?.id;
7512
7512
  if (currentPageId === void 0 || currentPageId === null) return false;
7513
- if ((0, _tmagic_utils.isPage)(node) || (0, _tmagic_utils.isPageFragment)(node)) return `${node.id}` !== `${currentPageId}`;
7513
+ if ((0, _tmagic_utils.isPageOrFragment)(node)) return `${node.id}` !== `${currentPageId}`;
7514
7514
  const nodePage = this.getNodeInfo(node.id, false).page;
7515
7515
  if (!nodePage) return false;
7516
7516
  return `${nodePage.id}` !== `${currentPageId}`;
@@ -7603,8 +7603,8 @@
7603
7603
  const curNode = this.get("node");
7604
7604
  const stage = this.get("stage");
7605
7605
  if (!curNode) throw new Error("当前选中节点为空");
7606
- if ((parent.type === _tmagic_core.NodeType.ROOT || curNode?.type === _tmagic_core.NodeType.ROOT) && !((0, _tmagic_utils.isPage)(node) || (0, _tmagic_utils.isPageFragment)(node))) throw new Error("app下不能添加组件");
7607
- if (parent.id !== curNode.id && !((0, _tmagic_utils.isPage)(node) || (0, _tmagic_utils.isPageFragment)(node))) {
7606
+ if ((parent.type === _tmagic_core.NodeType.ROOT || curNode?.type === _tmagic_core.NodeType.ROOT) && !(0, _tmagic_utils.isPageOrFragment)(node)) throw new Error("app下不能添加组件");
7607
+ if (parent.id !== curNode.id && !(0, _tmagic_utils.isPageOrFragment)(node)) {
7608
7608
  const index = parent.items.indexOf(curNode);
7609
7609
  parent.items?.splice(index + 1, 0, node);
7610
7610
  } else parent.items?.push(node);
@@ -7649,7 +7649,7 @@
7649
7649
  } else addNodes.push(...addNode);
7650
7650
  const newNodes = await Promise.all(addNodes.map((node) => {
7651
7651
  const root = this.get("root");
7652
- if (((0, _tmagic_utils.isPage)(node) || (0, _tmagic_utils.isPageFragment)(node)) && root) return this.doAdd(node, root);
7652
+ if ((0, _tmagic_utils.isPageOrFragment)(node) && root) return this.doAdd(node, root);
7653
7653
  const parentNode = parent ?? getAddParent(node);
7654
7654
  if (!parentNode) throw new Error("未找到父元素");
7655
7655
  return this.doAdd(node, parentNode);
@@ -7669,7 +7669,7 @@
7669
7669
  else if ((0, _tmagic_utils.isPageFragment)(newNodes[0])) this.state.pageFragmentLength += 1;
7670
7670
  else if (!skipSelect) stage?.select(newNodes[0].id);
7671
7671
  }
7672
- if (!((0, _tmagic_utils.isPage)(newNodes[0]) || (0, _tmagic_utils.isPageFragment)(newNodes[0]))) {
7672
+ if (!(0, _tmagic_utils.isPageOrFragment)(newNodes[0])) {
7673
7673
  const pageForOp = this.getNodeInfo(newNodes[0].id, false).page;
7674
7674
  if (!doNotPushHistory) {
7675
7675
  const parentId = (this.getParentById(newNodes[0].id, false) ?? this.get("root")).id;
@@ -7702,7 +7702,7 @@
7702
7702
  historySource,
7703
7703
  doNotPushHistory
7704
7704
  });
7705
- const addedPages = newNodes.filter((node) => (0, _tmagic_utils.isPage)(node) || (0, _tmagic_utils.isPageFragment)(node));
7705
+ const addedPages = newNodes.filter((node) => (0, _tmagic_utils.isPageOrFragment)(node));
7706
7706
  if (addedPages.length) history_default.notifyPageStructureChange({
7707
7707
  add: addedPages,
7708
7708
  remove: []
@@ -7716,13 +7716,10 @@
7716
7716
  if (!parent || !curNode) throw new Error("找不要删除的节点");
7717
7717
  const index = getNodeIndex(curNode.id, parent);
7718
7718
  if (typeof index !== "number" || index === -1) throw new Error("找不要删除的节点");
7719
- parent.items?.splice(index, 1);
7720
7719
  const stage = this.get("stage");
7721
- stage?.remove({
7722
- id: node.id,
7723
- parentId: parent.id,
7724
- root: cloneDeep$1(root)
7725
- });
7720
+ const currentPage = this.get("page");
7721
+ const isDeletingCurrentPage = (0, _tmagic_utils.isPageOrFragment)(node) && !!currentPage && `${currentPage.id}` === `${node.id}`;
7722
+ parent.items?.splice(index, 1);
7726
7723
  const selectedNodes = this.get("nodes");
7727
7724
  const removedSelectedIndex = selectedNodes.findIndex((n) => `${n.id}` === `${node.id}`);
7728
7725
  if (removedSelectedIndex !== -1) {
@@ -7730,24 +7727,29 @@
7730
7727
  nextSelected.splice(removedSelectedIndex, 1);
7731
7728
  this.set("nodes", nextSelected);
7732
7729
  }
7733
- if ((0, _tmagic_utils.isPage)(node) || (0, _tmagic_utils.isPageFragment)(node)) {
7734
- const currentPage = this.get("page");
7735
- if (currentPage && `${currentPage.id}` === `${node.id}`) this.set("page", null);
7736
- }
7737
- const selectDefault = async (pages) => {
7738
- if (pages[0]) {
7739
- await this.select(pages[0]);
7740
- stage?.select(pages[0].id);
7741
- } else this.selectRoot();
7730
+ const removeData = {
7731
+ id: node.id,
7732
+ parentId: parent.id,
7733
+ root: cloneDeep$1(root)
7742
7734
  };
7743
7735
  const rootItems = root.items || [];
7744
- if ((0, _tmagic_utils.isPage)(node)) {
7745
- this.state.pageLength -= 1;
7746
- if (!doNotSelect && !doNotSwitchPage) await selectDefault(rootItems);
7747
- } else if ((0, _tmagic_utils.isPageFragment)(node)) {
7748
- this.state.pageFragmentLength -= 1;
7749
- if (!doNotSelect && !doNotSwitchPage) await selectDefault(rootItems);
7736
+ const shouldReselect = isDeletingCurrentPage && !doNotSelect && !doNotSwitchPage;
7737
+ const shouldSwitchPage = shouldReselect && rootItems.length > 0;
7738
+ if ((0, _tmagic_utils.isPageOrFragment)(node)) {
7739
+ if ((0, _tmagic_utils.isPage)(node)) this.state.pageLength -= 1;
7740
+ else this.state.pageFragmentLength -= 1;
7741
+ if (shouldSwitchPage) {
7742
+ await this.select(rootItems[0]);
7743
+ stage?.select(rootItems[0].id);
7744
+ await (0, vue.nextTick)();
7745
+ stage?.remove(removeData);
7746
+ } else {
7747
+ stage?.remove(removeData);
7748
+ if (shouldReselect) this.selectRoot();
7749
+ else if (isDeletingCurrentPage) this.set("page", null);
7750
+ }
7750
7751
  } else {
7752
+ stage?.remove(removeData);
7751
7753
  if (!doNotSelect) {
7752
7754
  await this.select(parent);
7753
7755
  stage?.select(parent.id);
@@ -7776,7 +7778,7 @@
7776
7778
  }));
7777
7779
  const removedItems = [];
7778
7780
  let pageForOp = null;
7779
- if (!((0, _tmagic_utils.isPage)(nodes[0]) || (0, _tmagic_utils.isPageFragment)(nodes[0]))) for (const n of nodes) {
7781
+ if (!(0, _tmagic_utils.isPageOrFragment)(nodes[0])) for (const n of nodes) {
7780
7782
  const { parent, node: curNode, page } = this.getNodeInfo(n.id, false);
7781
7783
  if (parent && curNode) {
7782
7784
  if (!pageForOp && page) pageForOp = {
@@ -7810,7 +7812,7 @@
7810
7812
  historySource,
7811
7813
  doNotPushHistory
7812
7814
  });
7813
- const removedPages = nodes.filter((node) => (0, _tmagic_utils.isPage)(node) || (0, _tmagic_utils.isPageFragment)(node));
7815
+ const removedPages = nodes.filter((node) => (0, _tmagic_utils.isPageOrFragment)(node));
7814
7816
  if (removedPages.length) history_default.notifyPageStructureChange({
7815
7817
  add: [],
7816
7818
  remove: removedPages
@@ -7848,7 +7850,7 @@
7848
7850
  selectedNodes.splice(targetIndex, 1, newConfig);
7849
7851
  this.set("nodes", [...selectedNodes]);
7850
7852
  }
7851
- if ((0, _tmagic_utils.isPage)(newConfig) || (0, _tmagic_utils.isPageFragment)(newConfig)) {
7853
+ if ((0, _tmagic_utils.isPageOrFragment)(newConfig)) {
7852
7854
  const currentPage = this.get("page");
7853
7855
  if (currentPage && `${currentPage.id}` === `${newConfig.id}`) this.set("page", newConfig);
7854
7856
  }
@@ -8102,7 +8104,7 @@
8102
8104
  */
8103
8105
  async moveToContainer(config, targetId, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
8104
8106
  const isBatch = Array.isArray(config);
8105
- const configs = (isBatch ? config : [config]).filter((item) => !((0, _tmagic_utils.isPage)(item) || (0, _tmagic_utils.isPageFragment)(item)));
8107
+ const configs = (isBatch ? config : [config]).filter((item) => !(0, _tmagic_utils.isPageOrFragment)(item));
8106
8108
  if (configs.length === 0) throw new Error("没有可移动的节点");
8107
8109
  this.captureSelectionBeforeOp();
8108
8110
  const target = this.getNodeById(targetId, false);
@@ -8576,7 +8578,7 @@
8576
8578
  getPageOfNode(id) {
8577
8579
  const { node, page } = this.getNodeInfo(id, false);
8578
8580
  if (page) return page;
8579
- if (node && ((0, _tmagic_utils.isPage)(node) || (0, _tmagic_utils.isPageFragment)(node))) return node;
8581
+ if (node && (0, _tmagic_utils.isPageOrFragment)(node)) return node;
8580
8582
  return null;
8581
8583
  }
8582
8584
  captureSelectionBeforeOp() {
@@ -17051,7 +17053,7 @@
17051
17053
  icon: _element_plus_icons_vue.Delete,
17052
17054
  display: ({ editorService }) => {
17053
17055
  const node = editorService.get("node");
17054
- return node?.type !== _tmagic_core.NodeType.ROOT && !(0, _tmagic_utils.isPage)(node) && !(0, _tmagic_utils.isPageFragment)(node);
17056
+ return node?.type !== _tmagic_core.NodeType.ROOT && !(0, _tmagic_utils.isPageOrFragment)(node);
17055
17057
  },
17056
17058
  handler: ({ editorService }) => {
17057
17059
  const nodes = editorService.get("nodes");
@@ -17166,7 +17168,7 @@
17166
17168
  type: "button",
17167
17169
  text: "全部折叠",
17168
17170
  icon: FolderMinusIcon_default,
17169
- display: () => (0, _tmagic_utils.isPage)(node.value) || (0, _tmagic_utils.isPageFragment)(node.value),
17171
+ display: () => (0, _tmagic_utils.isPageOrFragment)(node.value),
17170
17172
  handler: () => {
17171
17173
  emit("collapse-all");
17172
17174
  }
@@ -17292,7 +17294,7 @@
17292
17294
  }
17293
17295
  };
17294
17296
  const multiSelect = async (data) => {
17295
- if ((0, _tmagic_utils.isPage)(data) || (0, _tmagic_utils.isPageFragment)(data)) return;
17297
+ if ((0, _tmagic_utils.isPageOrFragment)(data)) return;
17296
17298
  const nodes = editorService.get("nodes") || [];
17297
17299
  const newNodes = [];
17298
17300
  let isCancel = false;
@@ -17301,7 +17303,7 @@
17301
17303
  isCancel = true;
17302
17304
  return;
17303
17305
  }
17304
- if ((0, _tmagic_utils.isPage)(node) || (0, _tmagic_utils.isPageFragment)(node)) return;
17306
+ if ((0, _tmagic_utils.isPageOrFragment)(node)) return;
17305
17307
  newNodes.push(node.id);
17306
17308
  });
17307
17309
  if (!isCancel || newNodes.length === 0) newNodes.push(data.id);
@@ -17710,7 +17712,7 @@
17710
17712
  }, { immediate: true });
17711
17713
  const addHandler = (newNodes) => {
17712
17714
  newNodes.forEach((node) => {
17713
- if ((0, _tmagic_utils.isPage)(node) || (0, _tmagic_utils.isPageFragment)(node)) return;
17715
+ if ((0, _tmagic_utils.isPageOrFragment)(node)) return;
17714
17716
  (0, _tmagic_utils.traverseNode)(node, (node) => {
17715
17717
  nodeStatusMap.value?.set(node.id, {
17716
17718
  visible: true,
@@ -18690,14 +18692,14 @@
18690
18692
  direction: "horizontal",
18691
18693
  display: () => {
18692
18694
  if (!node.value) return false;
18693
- return !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value);
18695
+ return !(0, _tmagic_utils.isPageOrFragment)(node.value);
18694
18696
  }
18695
18697
  },
18696
18698
  {
18697
18699
  type: "button",
18698
18700
  text: "上移一层",
18699
18701
  icon: (0, vue.markRaw)(_element_plus_icons_vue.Top),
18700
- display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect,
18702
+ display: () => !(0, _tmagic_utils.isPageOrFragment)(node.value) && !props.isMultiSelect,
18701
18703
  handler: () => {
18702
18704
  editorService.moveLayer(1, { historySource: "stage-contextmenu" });
18703
18705
  }
@@ -18706,7 +18708,7 @@
18706
18708
  type: "button",
18707
18709
  text: "下移一层",
18708
18710
  icon: (0, vue.markRaw)(_element_plus_icons_vue.Bottom),
18709
- display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect,
18711
+ display: () => !(0, _tmagic_utils.isPageOrFragment)(node.value) && !props.isMultiSelect,
18710
18712
  handler: () => {
18711
18713
  editorService.moveLayer(-1, { historySource: "stage-contextmenu" });
18712
18714
  }
@@ -18715,7 +18717,7 @@
18715
18717
  type: "button",
18716
18718
  text: "置顶",
18717
18719
  icon: (0, vue.markRaw)(_element_plus_icons_vue.Top),
18718
- display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect,
18720
+ display: () => !(0, _tmagic_utils.isPageOrFragment)(node.value) && !props.isMultiSelect,
18719
18721
  handler: () => {
18720
18722
  editorService.moveLayer(LayerOffset.TOP, { historySource: "stage-contextmenu" });
18721
18723
  }
@@ -18724,7 +18726,7 @@
18724
18726
  type: "button",
18725
18727
  text: "置底",
18726
18728
  icon: (0, vue.markRaw)(_element_plus_icons_vue.Bottom),
18727
- display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect,
18729
+ display: () => !(0, _tmagic_utils.isPageOrFragment)(node.value) && !props.isMultiSelect,
18728
18730
  handler: () => {
18729
18731
  editorService.moveLayer(LayerOffset.BOTTOM, { historySource: "stage-contextmenu" });
18730
18732
  }
@@ -18733,7 +18735,7 @@
18733
18735
  {
18734
18736
  type: "divider",
18735
18737
  direction: "horizontal",
18736
- display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect
18738
+ display: () => !(0, _tmagic_utils.isPageOrFragment)(node.value) && !props.isMultiSelect
18737
18739
  },
18738
18740
  useDeleteMenu("stage-contextmenu"),
18739
18741
  {
@@ -19232,7 +19234,7 @@
19232
19234
  var componentList_default = new ComponentList();
19233
19235
  //#endregion
19234
19236
  //#region packages/editor/src/utils/dep/worker.ts?worker&inline
19235
- var jsContent = "(function() {\n //#region packages/schema/src/index.ts\n const NODE_CONDS_KEY = \"displayConds\";\n const NODE_DISABLE_DATA_SOURCE_KEY = \"_tmagic_node_disabled_data_source\";\n const NODE_DISABLE_CODE_BLOCK_KEY = \"_tmagic_node_disabled_code_block\";\n let HookType = /* @__PURE__ */ function(HookType) {\n /** 代码块钩子标识 */\n HookType[\"CODE\"] = \"code\";\n return HookType;\n }({});\n //#endregion\n //#region packages/utils/src/const.ts\n const DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX = \"ds-field::\";\n //#endregion\n //#region packages/utils/src/index.ts\n const isObject = (obj) => Object.prototype.toString.call(obj) === \"[object Object]\";\n const getKeysArray = (keys) => `${keys}`.replace(/\\[(\\d+)\\]/g, \".$1\").split(\".\");\n const dataSourceTemplateRegExp = /\\$\\{([\\s\\S]+?)\\}/g;\n //#endregion\n //#region packages/dep/src/types.ts\n /** 依赖收集的目标类型 */\n let DepTargetType = /* @__PURE__ */ function(DepTargetType) {\n DepTargetType[\"DEFAULT\"] = \"default\";\n /** 代码块 */\n DepTargetType[\"CODE_BLOCK\"] = \"code-block\";\n /** 数据源 */\n DepTargetType[\"DATA_SOURCE\"] = \"data-source\";\n /** 数据源方法 */\n DepTargetType[\"DATA_SOURCE_METHOD\"] = \"data-source-method\";\n /** 数据源条件 */\n DepTargetType[\"DATA_SOURCE_COND\"] = \"data-source-cond\";\n return DepTargetType;\n }({});\n //#endregion\n //#region packages/dep/src/Target.ts\n /**\n * 需要收集依赖的目标\n * 例如:一个代码块可以为一个目标\n */\n var Target = class {\n /**\n * 如何识别目标\n */\n isTarget;\n /**\n * 目标id,不可重复\n * 例如目标是代码块,则为代码块id\n */\n id;\n /**\n * 目标名称,用于显示在依赖列表中\n */\n name;\n /**\n * 不同的目标可以进行分类,例如代码块,数据源可以为两个不同的type\n */\n type = DepTargetType.DEFAULT;\n /**\n * 依赖详情\n * 实例:{ 'node_id': { name: 'node_name', keys: [ created, mounted ] } }\n */\n deps = {};\n /**\n * 是否默认收集,默认为true,当值为false时需要传入type参数给collect方法才会被收集\n */\n isCollectByDefault;\n constructor(options) {\n this.isTarget = options.isTarget;\n this.id = options.id;\n this.name = options.name;\n this.isCollectByDefault = options.isCollectByDefault ?? true;\n if (options.type) this.type = options.type;\n if (options.initialDeps) this.deps = options.initialDeps;\n }\n /**\n * 更新依赖\n * @param option 节点配置\n * @param key 哪个key配置了这个目标的id\n */\n updateDep({ id, name, key, data }) {\n const dep = this.deps[id] || {\n name,\n keys: []\n };\n dep.name = name;\n dep.data = data;\n this.deps[id] = dep;\n if (!dep.keys.includes(key)) dep.keys.push(key);\n }\n /**\n * 删除依赖\n * @param node 哪个节点的依赖需要移除,如果为空,则移除所有依赖\n * @param key 节点下哪个key需要移除,如果为空,则移除改节点下的所有依赖key\n * @returns void\n */\n removeDep(id, key) {\n if (typeof id === \"undefined\") {\n Object.keys(this.deps).forEach((depKey) => {\n delete this.deps[depKey];\n });\n return;\n }\n const dep = this.deps[id];\n if (!dep) return;\n if (key) {\n const index = dep.keys.indexOf(key);\n dep.keys.splice(index, 1);\n if (dep.keys.length === 0) delete this.deps[id];\n } else delete this.deps[id];\n }\n /**\n * 判断指定节点下的指定key是否存在在依赖列表中\n * @param node 哪个节点\n * @param key 哪个key\n * @returns boolean\n */\n hasDep(id, key) {\n return this.deps[id]?.keys.includes(key) ?? false;\n }\n destroy() {\n this.deps = {};\n }\n };\n //#endregion\n //#region packages/dep/src/utils.ts\n const INTEGER_REGEXP = /^\\d+$/;\n const createCodeBlockTarget = (id, codeBlock, initialDeps = {}) => new Target({\n type: DepTargetType.CODE_BLOCK,\n id,\n initialDeps,\n name: codeBlock.name,\n isTarget: (_key, value) => {\n if (id === value) return true;\n if (value?.hookType === HookType.CODE && Array.isArray(value.hookData)) return value.hookData.some((item) => item.codeId === id);\n return false;\n }\n });\n /**\n * ['array'] ['array', '0'] ['array', '0', 'a'] 这种返回false\n * ['array', 'a'] 这种返回true\n * @param keys\n * @param fields\n * @returns boolean\n */\n const isIncludeArrayField = (keys, fields) => {\n let f = fields;\n return keys.some((key, index) => {\n const field = f.find(({ name }) => name === key);\n f = field?.fields || [];\n return field?.type === \"array\" && index < keys.length - 1 && !INTEGER_REGEXP.test(keys[index + 1]);\n });\n };\n /**\n * 判断模板(value)是不是使用数据源Id(dsId),如:`xxx${dsId.field}xxx${dsId.field}`\n * @param value any\n * @param dsId string | number\n * @param hasArray boolean true: 一定要包含有需要迭代的模板; false: 一定要包含普通模板;\n * @returns boolean\n */\n const isDataSourceTemplate = (value, ds, hasArray = false) => {\n const templates = value.match(dataSourceTemplateRegExp) || [];\n if (templates.length <= 0) return false;\n for (const tpl of templates) {\n const keys = getKeysArray(tpl.substring(2, tpl.length - 1));\n const dsId = keys.shift();\n if (!dsId || dsId !== ds.id) continue;\n if (hasArray === isIncludeArrayField(keys, ds.fields)) return true;\n }\n return false;\n };\n /**\n * 指定数据源的字符串模板,如:{ isBindDataSourceField: true, dataSourceId: 'id', template: `xxx${field}xxx`}\n * @param value any\n * @param dsId string | number\n * @returns boolean\n */\n const isSpecificDataSourceTemplate = (value, dsId) => value?.isBindDataSourceField && value.dataSourceId && value.dataSourceId === dsId && typeof value.template === \"string\";\n /**\n * 关联数据源字段,格式为 [前缀+数据源ID, 字段名]\n * 使用data-source-field-select value: 'value' 可以配置出来\n * @param value any[]\n * @param id string | number\n * @returns boolean\n */\n const isUseDataSourceField = (value, id) => {\n if (!Array.isArray(value) || typeof value[0] !== \"string\") return false;\n const [prefixId] = value;\n const prefixIndex = prefixId.indexOf(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX);\n if (prefixIndex === -1) return false;\n return prefixId.substring(prefixIndex + DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX.length) === id;\n };\n const isDataSourceTarget = (ds, key, value, hasArray = false) => {\n if (!value) return false;\n const valueType = typeof value;\n if (valueType !== \"string\" && valueType !== \"object\") return false;\n if (`${key}`.startsWith(\"displayConds\")) return false;\n if (valueType === \"string\") return isDataSourceTemplate(value, ds, hasArray);\n if (isObject(value) && value.isBindDataSource && value.dataSourceId === ds.id) return true;\n if (isSpecificDataSourceTemplate(value, ds.id)) return true;\n if (isUseDataSourceField(value, ds.id)) {\n const [, ...keys] = value;\n const includeArray = isIncludeArrayField(keys, ds.fields);\n return hasArray ? includeArray : !includeArray;\n }\n return false;\n };\n const isDataSourceCondTarget = (ds, key, value, hasArray = false) => {\n if (!Array.isArray(value) || !ds) return false;\n const [dsId, ...keys] = value;\n if (dsId !== ds.id || !`${key}`.startsWith(\"displayConds\")) return false;\n if (ds.fields?.some((field) => field.name === keys[0])) {\n const includeArray = isIncludeArrayField(keys, ds.fields);\n return hasArray ? includeArray : !includeArray;\n }\n return false;\n };\n const createDataSourceTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE,\n id: ds.id,\n initialDeps,\n isTarget: (key, value) => isDataSourceTarget(ds, key, value)\n });\n const createDataSourceCondTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE_COND,\n id: ds.id,\n initialDeps,\n isTarget: (key, value) => isDataSourceCondTarget(ds, key, value)\n });\n const createDataSourceMethodTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE_METHOD,\n id: ds.id,\n initialDeps,\n isTarget: (_key, value) => {\n if (!Array.isArray(value)) return false;\n const [dsId, methodName] = value;\n if (!methodName || dsId !== ds.id) return false;\n if (ds.methods?.some((method) => method.name === methodName)) return true;\n if (ds.fields?.some((field) => field.name === methodName)) return false;\n return true;\n }\n });\n const traverseTarget = (targetsList, cb, type) => {\n if (type) {\n const targets = targetsList[type];\n if (targets) for (const target of Object.values(targets)) cb(target);\n return;\n }\n for (const targets of Object.values(targetsList)) for (const target of Object.values(targets)) cb(target);\n };\n //#endregion\n //#region packages/dep/src/Watcher.ts\n const DATA_SOURCE_TARGET_TYPES = /* @__PURE__ */ new Set([\n DepTargetType.DATA_SOURCE,\n DepTargetType.DATA_SOURCE_COND,\n DepTargetType.DATA_SOURCE_METHOD\n ]);\n var Watcher = class {\n targetsList = {};\n childrenProp = \"items\";\n idProp = \"id\";\n nameProp = \"name\";\n constructor(options) {\n if (options?.initialTargets) this.targetsList = options.initialTargets;\n if (options?.childrenProp) this.childrenProp = options.childrenProp;\n }\n getTargetsList() {\n return this.targetsList;\n }\n /**\n * 获取指定类型中的所有target\n * @param type 分类\n * @returns Target[]\n */\n getTargets(type = DepTargetType.DEFAULT) {\n return this.targetsList[type] || {};\n }\n /**\n * 添加新的目标\n * @param target Target\n */\n addTarget(target) {\n const targets = this.getTargets(target.type) || {};\n this.targetsList[target.type] = targets;\n targets[target.id] = target;\n }\n /**\n * 获取指定id的target\n * @param id target id\n * @returns Target\n */\n getTarget(id, type = DepTargetType.DEFAULT) {\n return this.getTargets(type)[id];\n }\n /**\n * 判断是否存在指定id的target\n * @param id target id\n * @returns boolean\n */\n hasTarget(id, type = DepTargetType.DEFAULT) {\n return Boolean(this.getTarget(id, type));\n }\n /**\n * 判断是否存在指定类型的target\n * @param type target type\n * @returns boolean\n */\n hasSpecifiedTypeTarget(type = DepTargetType.DEFAULT) {\n return Object.keys(this.getTargets(type)).length > 0;\n }\n /**\n * 删除指定id的target\n * @param id target id\n */\n removeTarget(id, type = DepTargetType.DEFAULT) {\n const targets = this.getTargets(type);\n if (targets[id]) {\n targets[id].destroy();\n delete targets[id];\n }\n }\n /**\n * 删除指定分类的所有target\n * @param type 分类\n * @returns void\n */\n removeTargets(type = DepTargetType.DEFAULT) {\n const targets = this.targetsList[type];\n if (!targets) return;\n for (const target of Object.values(targets)) target.destroy();\n delete this.targetsList[type];\n }\n /**\n * 删除所有target\n */\n clearTargets() {\n for (const key of Object.keys(this.targetsList)) delete this.targetsList[key];\n }\n /**\n * 收集依赖\n * @param nodes 需要收集的节点\n * @param deep 是否需要收集子节点\n * @param type 强制收集指定类型的依赖\n */\n collect(nodes, depExtendedData = {}, deep = false, type) {\n const targets = this.getCollectableTargets(type);\n if (!targets.length) return;\n for (const node of nodes) {\n this.removeTargetsDep(targets, node);\n this.collectItems(node, targets, depExtendedData, deep);\n }\n }\n /**\n * 获取本次需要参与收集的 target(过滤规则与 collectByCallback 一致)\n *\n * 注:供 editor 的 dep service / worker 跨包批量收集时复用,因此为 public。\n * @param type 强制收集指定类型的依赖\n */\n getCollectableTargets(type) {\n const targets = [];\n traverseTarget(this.targetsList, (target) => {\n if (!type && !target.isCollectByDefault) return;\n targets.push(target);\n }, type);\n return targets;\n }\n collectByCallback(nodes, type, cb) {\n traverseTarget(this.targetsList, (target) => {\n if (!type && !target.isCollectByDefault) return;\n for (const node of nodes) cb({\n node,\n target\n });\n }, type);\n }\n /**\n * 清除所有目标的依赖\n * @param nodes 需要清除依赖的节点\n */\n clear(nodes, type) {\n let { targetsList } = this;\n if (type) targetsList = { [type]: this.getTargets(type) };\n const clearedItemsNodeIds = /* @__PURE__ */ new Set();\n traverseTarget(targetsList, (target) => {\n if (nodes) for (const node of nodes) {\n target.removeDep(node[this.idProp]);\n if (Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length && !clearedItemsNodeIds.has(node[this.idProp])) {\n clearedItemsNodeIds.add(node[this.idProp]);\n this.clear(node[this.childrenProp]);\n }\n }\n else target.removeDep();\n });\n }\n /**\n * 清除指定类型的依赖\n * @param type 类型\n * @param nodes 需要清除依赖的节点\n */\n clearByType(type, nodes) {\n this.clear(nodes, type);\n }\n /**\n * 收集单个 target 的依赖,等价于 collectItems(node, [target], ...)\n */\n collectItem(node, target, depExtendedData = {}, deep = false) {\n this.collectItems(node, [target], depExtendedData, deep);\n }\n removeTargetDep(target, node, key) {\n target.removeDep(node[this.idProp], key);\n if (typeof key === \"undefined\" && Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length) for (const item of node[this.childrenProp]) this.removeTargetDep(target, item, key);\n }\n /**\n * 与 removeTargetDep 等价,但一次子树递归同时处理多个 target,\n * 把删除阶段的结构遍历从 ×targets 降到 ×1。\n *\n * 注:供 editor 的 dep service 跨包批量删除时复用,因此为 public。\n */\n removeTargetsDep(targets, node, key) {\n const id = node[this.idProp];\n for (const target of targets) target.removeDep(id, key);\n if (typeof key === \"undefined\" && Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length) for (const item of node[this.childrenProp]) this.removeTargetsDep(targets, item, key);\n }\n /**\n * 与 collectItem 等价,但一次遍历同时处理多个 target(不含删除阶段)。\n *\n * 关键优化:原实现对每个 target 都完整遍历一遍节点树(O(targets × 树规模)),大页面 + 大量数据源时,\n * 结构遍历(Object.entries / 递归 / fullKey 字符串拼接)会被重复 targets 次。这里改为「整棵树只遍历一次,\n * 在每个属性上检查所有 target」,把结构遍历开销从 ×targets 降到 ×1,isTarget 调用次数不变,收集结果完全一致。\n *\n * 注:供 editor 的 dep service / worker 跨包批量收集时复用,因此为 public。\n */\n collectItems(node, targets, depExtendedData = {}, deep = false) {\n const activeTargets = this.filterTargetsByNode(node, targets);\n if (!activeTargets.length) return;\n this.collectTargetForTargets(node, node, \"\", activeTargets, depExtendedData, deep);\n }\n filterTargetsByNode(node, targets) {\n const disableDataSource = Boolean(node[NODE_DISABLE_DATA_SOURCE_KEY]);\n const disableCodeBlock = Boolean(node[NODE_DISABLE_CODE_BLOCK_KEY]);\n if (!disableDataSource && !disableCodeBlock) return targets;\n return targets.filter((target) => {\n if (disableDataSource && DATA_SOURCE_TARGET_TYPES.has(target.type)) return false;\n if (disableCodeBlock && target.type === DepTargetType.CODE_BLOCK) return false;\n return true;\n });\n }\n collectTargetForTargets(node, config, prop, targets, depExtendedData, deep) {\n const id = node[this.idProp];\n const name = `${node[this.nameProp] || node[this.idProp]}`;\n for (const [key, value] of Object.entries(config)) {\n if (typeof value === \"undefined\" || value === \"\") continue;\n const keyIsItems = key === this.childrenProp;\n const fullKey = prop ? `${prop}.${key}` : key;\n let notMatched = null;\n for (let i = 0, l = targets.length; i < l; i++) {\n const target = targets[i];\n if (target.isTarget(fullKey, value, config)) target.updateDep({\n id,\n name,\n data: depExtendedData,\n key: fullKey\n });\n else (notMatched || (notMatched = [])).push(target);\n }\n if (notMatched) {\n if (!keyIsItems && Array.isArray(value)) for (let i = 0, l = value.length; i < l; i++) {\n const item = value[i];\n if (isObject(item)) this.collectTargetForTargets(node, item, `${fullKey}[${i}]`, notMatched, depExtendedData, deep);\n }\n else if (isObject(value)) this.collectTargetForTargets(node, value, fullKey, notMatched, depExtendedData, deep);\n }\n if (keyIsItems && deep && Array.isArray(value)) for (const child of value) this.collectItems(child, targets, depExtendedData, deep);\n }\n }\n };\n //#endregion\n //#region packages/editor/src/utils/logger.ts\n const error = (...args) => {\n if (process.env.NODE_ENV === \"development\") console.error(\"magic editor: \", ...args);\n };\n //#endregion\n //#region packages/editor/src/utils/dep/worker.ts\n onmessage = (e) => {\n const watcher = new Watcher({ initialTargets: {} });\n const { dsl } = e.data;\n try {\n const mApp = eval(`(${dsl})`);\n if (!mApp) postMessage({});\n watcher.clearTargets();\n if (mApp.codeBlocks) for (const [id, code] of Object.entries(mApp.codeBlocks)) watcher.addTarget(createCodeBlockTarget(id, code));\n if (mApp.dataSources) for (const ds of mApp.dataSources) {\n watcher.addTarget(createDataSourceTarget(ds, {}));\n watcher.addTarget(createDataSourceMethodTarget(ds, {}));\n watcher.addTarget(createDataSourceCondTarget(ds, {}));\n }\n const targets = watcher.getCollectableTargets();\n for (const page of mApp.items) watcher.collectItems(page, targets, { pageId: page.id }, true);\n const data = {\n [DepTargetType.DATA_SOURCE]: {},\n [DepTargetType.DATA_SOURCE_METHOD]: {},\n [DepTargetType.DATA_SOURCE_COND]: {},\n [DepTargetType.CODE_BLOCK]: {}\n };\n traverseTarget(watcher.getTargetsList(), (target) => {\n data[target.type][target.id] = target.deps;\n });\n postMessage(data);\n } catch (e) {\n error(e);\n postMessage({});\n }\n };\n //#endregion\n})();\n";
19237
+ var jsContent = "(function() {\n //#region packages/schema/src/index.ts\n const NODE_CONDS_KEY = \"displayConds\";\n const NODE_DISABLE_DATA_SOURCE_KEY = \"_tmagic_node_disabled_data_source\";\n const NODE_DISABLE_CODE_BLOCK_KEY = \"_tmagic_node_disabled_code_block\";\n let HookType = /* @__PURE__ */ function(HookType) {\n /** 代码块钩子标识 */\n HookType[\"CODE\"] = \"code\";\n return HookType;\n }({});\n //#endregion\n //#region packages/utils/src/const.ts\n const DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX = \"ds-field::\";\n //#endregion\n //#region packages/utils/src/index.ts\n const isObject = (obj) => Object.prototype.toString.call(obj) === \"[object Object]\";\n const getKeysArray = (keys) => `${keys}`.replace(/\\[(\\d+)\\]/g, \".$1\").split(\".\");\n const dataSourceTemplateRegExp = /\\$\\{([\\s\\S]+?)\\}/g;\n //#endregion\n //#region packages/dep/src/types.ts\n /** 依赖收集的目标类型 */\n let DepTargetType = /* @__PURE__ */ function(DepTargetType) {\n DepTargetType[\"DEFAULT\"] = \"default\";\n /** 代码块 */\n DepTargetType[\"CODE_BLOCK\"] = \"code-block\";\n /** 数据源 */\n DepTargetType[\"DATA_SOURCE\"] = \"data-source\";\n /** 数据源方法 */\n DepTargetType[\"DATA_SOURCE_METHOD\"] = \"data-source-method\";\n /** 数据源条件 */\n DepTargetType[\"DATA_SOURCE_COND\"] = \"data-source-cond\";\n return DepTargetType;\n }({});\n //#endregion\n //#region packages/dep/src/Target.ts\n /**\n * 需要收集依赖的目标\n * 例如:一个代码块可以为一个目标\n */\n var Target = class {\n /**\n * 如何识别目标\n */\n isTarget;\n /**\n * 目标id,不可重复\n * 例如目标是代码块,则为代码块id\n */\n id;\n /**\n * 目标名称,用于显示在依赖列表中\n */\n name;\n /**\n * 不同的目标可以进行分类,例如代码块,数据源可以为两个不同的type\n */\n type = DepTargetType.DEFAULT;\n /**\n * 依赖详情\n * 实例:{ 'node_id': { name: 'node_name', keys: [ created, mounted ] } }\n */\n deps = {};\n /**\n * 是否默认收集,默认为true,当值为false时需要传入type参数给collect方法才会被收集\n */\n isCollectByDefault;\n /**\n * 可序列化描述,有该描述的 target 可以在 worker 中重建,从而把依赖收集放到子线程执行\n */\n descriptor;\n constructor(options) {\n this.isTarget = options.isTarget;\n this.id = options.id;\n this.name = options.name;\n this.isCollectByDefault = options.isCollectByDefault ?? true;\n this.descriptor = options.descriptor;\n if (options.type) this.type = options.type;\n if (options.initialDeps) this.deps = options.initialDeps;\n }\n /**\n * 更新依赖\n * @param option 节点配置\n * @param key 哪个key配置了这个目标的id\n */\n updateDep({ id, name, key, data }) {\n const dep = this.deps[id] || {\n name,\n keys: []\n };\n dep.name = name;\n dep.data = data;\n this.deps[id] = dep;\n if (!dep.keys.includes(key)) dep.keys.push(key);\n }\n /**\n * 删除依赖\n * @param node 哪个节点的依赖需要移除,如果为空,则移除所有依赖\n * @param key 节点下哪个key需要移除,如果为空,则移除改节点下的所有依赖key\n * @returns void\n */\n removeDep(id, key) {\n if (typeof id === \"undefined\") {\n Object.keys(this.deps).forEach((depKey) => {\n delete this.deps[depKey];\n });\n return;\n }\n const dep = this.deps[id];\n if (!dep) return;\n if (key) {\n const index = dep.keys.indexOf(key);\n dep.keys.splice(index, 1);\n if (dep.keys.length === 0) delete this.deps[id];\n } else delete this.deps[id];\n }\n /**\n * 判断指定节点下的指定key是否存在在依赖列表中\n * @param node 哪个节点\n * @param key 哪个key\n * @returns boolean\n */\n hasDep(id, key) {\n return this.deps[id]?.keys.includes(key) ?? false;\n }\n destroy() {\n this.deps = {};\n }\n };\n //#endregion\n //#region packages/dep/src/utils.ts\n const INTEGER_REGEXP = /^\\d+$/;\n const createCodeBlockTarget = (id, codeBlock, initialDeps = {}) => new Target({\n type: DepTargetType.CODE_BLOCK,\n id,\n initialDeps,\n name: codeBlock.name,\n descriptor: {\n type: DepTargetType.CODE_BLOCK,\n id,\n codeBlock: { name: codeBlock.name }\n },\n isTarget: (_key, value) => {\n if (id === value) return true;\n if (value?.hookType === HookType.CODE && Array.isArray(value.hookData)) return value.hookData.some((item) => item.codeId === id);\n return false;\n }\n });\n /**\n * ['array'] ['array', '0'] ['array', '0', 'a'] 这种返回false\n * ['array', 'a'] 这种返回true\n * @param keys\n * @param fields\n * @returns boolean\n */\n const isIncludeArrayField = (keys, fields) => {\n let f = fields;\n return keys.some((key, index) => {\n const field = f.find(({ name }) => name === key);\n f = field?.fields || [];\n return field?.type === \"array\" && index < keys.length - 1 && !INTEGER_REGEXP.test(keys[index + 1]);\n });\n };\n /**\n * 判断模板(value)是不是使用数据源Id(dsId),如:`xxx${dsId.field}xxx${dsId.field}`\n * @param value any\n * @param dsId string | number\n * @param hasArray boolean true: 一定要包含有需要迭代的模板; false: 一定要包含普通模板;\n * @returns boolean\n */\n const isDataSourceTemplate = (value, ds, hasArray = false) => {\n const templates = value.match(dataSourceTemplateRegExp) || [];\n if (templates.length <= 0) return false;\n for (const tpl of templates) {\n const keys = getKeysArray(tpl.substring(2, tpl.length - 1));\n const dsId = keys.shift();\n if (!dsId || dsId !== ds.id) continue;\n if (hasArray === isIncludeArrayField(keys, ds.fields)) return true;\n }\n return false;\n };\n /**\n * 指定数据源的字符串模板,如:{ isBindDataSourceField: true, dataSourceId: 'id', template: `xxx${field}xxx`}\n * @param value any\n * @param dsId string | number\n * @returns boolean\n */\n const isSpecificDataSourceTemplate = (value, dsId) => value?.isBindDataSourceField && value.dataSourceId && value.dataSourceId === dsId && typeof value.template === \"string\";\n /**\n * 关联数据源字段,格式为 [前缀+数据源ID, 字段名]\n * 使用data-source-field-select value: 'value' 可以配置出来\n * @param value any[]\n * @param id string | number\n * @returns boolean\n */\n const isUseDataSourceField = (value, id) => {\n if (!Array.isArray(value) || typeof value[0] !== \"string\") return false;\n const [prefixId] = value;\n const prefixIndex = prefixId.indexOf(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX);\n if (prefixIndex === -1) return false;\n return prefixId.substring(prefixIndex + DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX.length) === id;\n };\n const isDataSourceTarget = (ds, key, value, hasArray = false) => {\n if (!value) return false;\n const valueType = typeof value;\n if (valueType !== \"string\" && valueType !== \"object\") return false;\n if (`${key}`.startsWith(\"displayConds\")) return false;\n if (valueType === \"string\") return isDataSourceTemplate(value, ds, hasArray);\n if (isObject(value) && value.isBindDataSource && value.dataSourceId === ds.id) return true;\n if (isSpecificDataSourceTemplate(value, ds.id)) return true;\n if (isUseDataSourceField(value, ds.id)) {\n const [, ...keys] = value;\n const includeArray = isIncludeArrayField(keys, ds.fields);\n return hasArray ? includeArray : !includeArray;\n }\n return false;\n };\n const isDataSourceCondTarget = (ds, key, value, hasArray = false) => {\n if (!Array.isArray(value) || !ds) return false;\n const [dsId, ...keys] = value;\n if (dsId !== ds.id || !`${key}`.startsWith(\"displayConds\")) return false;\n if (ds.fields?.some((field) => field.name === keys[0])) {\n const includeArray = isIncludeArrayField(keys, ds.fields);\n return hasArray ? includeArray : !includeArray;\n }\n return false;\n };\n const createDataSourceTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE,\n id: ds.id,\n initialDeps,\n descriptor: {\n type: DepTargetType.DATA_SOURCE,\n ds: {\n id: ds.id,\n fields: ds.fields || []\n }\n },\n isTarget: (key, value) => isDataSourceTarget(ds, key, value)\n });\n const createDataSourceCondTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE_COND,\n id: ds.id,\n initialDeps,\n descriptor: {\n type: DepTargetType.DATA_SOURCE_COND,\n ds: {\n id: ds.id,\n fields: ds.fields || []\n }\n },\n isTarget: (key, value) => isDataSourceCondTarget(ds, key, value)\n });\n const createDataSourceMethodTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE_METHOD,\n id: ds.id,\n initialDeps,\n descriptor: {\n type: DepTargetType.DATA_SOURCE_METHOD,\n ds: {\n id: ds.id,\n methods: (ds.methods || []).map((method) => ({ name: method.name })),\n fields: (ds.fields || []).map((field) => ({ name: field.name }))\n }\n },\n isTarget: (_key, value) => {\n if (!Array.isArray(value)) return false;\n const [dsId, methodName] = value;\n if (!methodName || dsId !== ds.id) return false;\n if (ds.methods?.some((method) => method.name === methodName)) return true;\n if (ds.fields?.some((field) => field.name === methodName)) return false;\n return true;\n }\n });\n /**\n * 用可序列化描述重建 target,使依赖收集可以在 worker 等无法传递函数的环境中进行\n * @param descriptor 由工厂函数写入 target 的描述\n * @param initialDeps 初始依赖\n * @returns Target\n */\n const createTargetByDescriptor = (descriptor, initialDeps = {}) => {\n switch (descriptor.type) {\n case DepTargetType.CODE_BLOCK: return createCodeBlockTarget(descriptor.id, descriptor.codeBlock, initialDeps);\n case DepTargetType.DATA_SOURCE: return createDataSourceTarget(descriptor.ds, initialDeps);\n case DepTargetType.DATA_SOURCE_COND: return createDataSourceCondTarget(descriptor.ds, initialDeps);\n case DepTargetType.DATA_SOURCE_METHOD: return createDataSourceMethodTarget(descriptor.ds, initialDeps);\n default: throw new Error(`unknown target descriptor: ${JSON.stringify(descriptor)}`);\n }\n };\n const traverseTarget = (targetsList, cb, type) => {\n if (type) {\n const targets = targetsList[type];\n if (targets) for (const target of Object.values(targets)) cb(target);\n return;\n }\n for (const targets of Object.values(targetsList)) for (const target of Object.values(targets)) cb(target);\n };\n //#endregion\n //#region packages/dep/src/Watcher.ts\n const DATA_SOURCE_TARGET_TYPES = /* @__PURE__ */ new Set([\n DepTargetType.DATA_SOURCE,\n DepTargetType.DATA_SOURCE_COND,\n DepTargetType.DATA_SOURCE_METHOD\n ]);\n var Watcher = class {\n targetsList = {};\n childrenProp = \"items\";\n idProp = \"id\";\n nameProp = \"name\";\n constructor(options) {\n if (options?.initialTargets) this.targetsList = options.initialTargets;\n if (options?.childrenProp) this.childrenProp = options.childrenProp;\n }\n getTargetsList() {\n return this.targetsList;\n }\n /**\n * 获取指定类型中的所有target\n * @param type 分类\n * @returns Target[]\n */\n getTargets(type = DepTargetType.DEFAULT) {\n return this.targetsList[type] || {};\n }\n /**\n * 添加新的目标\n * @param target Target\n */\n addTarget(target) {\n const targets = this.getTargets(target.type) || {};\n this.targetsList[target.type] = targets;\n targets[target.id] = target;\n }\n /**\n * 获取指定id的target\n * @param id target id\n * @returns Target\n */\n getTarget(id, type = DepTargetType.DEFAULT) {\n return this.getTargets(type)[id];\n }\n /**\n * 判断是否存在指定id的target\n * @param id target id\n * @returns boolean\n */\n hasTarget(id, type = DepTargetType.DEFAULT) {\n return Boolean(this.getTarget(id, type));\n }\n /**\n * 判断是否存在指定类型的target\n * @param type target type\n * @returns boolean\n */\n hasSpecifiedTypeTarget(type = DepTargetType.DEFAULT) {\n return Object.keys(this.getTargets(type)).length > 0;\n }\n /**\n * 删除指定id的target\n * @param id target id\n */\n removeTarget(id, type = DepTargetType.DEFAULT) {\n const targets = this.getTargets(type);\n if (targets[id]) {\n targets[id].destroy();\n delete targets[id];\n }\n }\n /**\n * 删除指定分类的所有target\n * @param type 分类\n * @returns void\n */\n removeTargets(type = DepTargetType.DEFAULT) {\n const targets = this.targetsList[type];\n if (!targets) return;\n for (const target of Object.values(targets)) target.destroy();\n delete this.targetsList[type];\n }\n /**\n * 删除所有target\n */\n clearTargets() {\n for (const key of Object.keys(this.targetsList)) delete this.targetsList[key];\n }\n /**\n * 收集依赖\n * @param nodes 需要收集的节点\n * @param deep 是否需要收集子节点\n * @param type 强制收集指定类型的依赖\n */\n collect(nodes, depExtendedData = {}, deep = false, type) {\n const targets = this.getCollectableTargets(type);\n if (!targets.length) return;\n for (const node of nodes) {\n this.removeTargetsDep(targets, node);\n this.collectItems(node, targets, depExtendedData, deep);\n }\n }\n /**\n * 获取本次需要参与收集的 target(过滤规则与 collectByCallback 一致)\n *\n * 注:供 editor 的 dep service / worker 跨包批量收集时复用,因此为 public。\n * @param type 强制收集指定类型的依赖\n */\n getCollectableTargets(type) {\n const targets = [];\n traverseTarget(this.targetsList, (target) => {\n if (!type && !target.isCollectByDefault) return;\n targets.push(target);\n }, type);\n return targets;\n }\n collectByCallback(nodes, type, cb) {\n traverseTarget(this.targetsList, (target) => {\n if (!type && !target.isCollectByDefault) return;\n for (const node of nodes) cb({\n node,\n target\n });\n }, type);\n }\n /**\n * 清除所有目标的依赖\n * @param nodes 需要清除依赖的节点\n */\n clear(nodes, type) {\n let { targetsList } = this;\n if (type) targetsList = { [type]: this.getTargets(type) };\n const clearedItemsNodeIds = /* @__PURE__ */ new Set();\n traverseTarget(targetsList, (target) => {\n if (nodes) for (const node of nodes) {\n target.removeDep(node[this.idProp]);\n if (Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length && !clearedItemsNodeIds.has(node[this.idProp])) {\n clearedItemsNodeIds.add(node[this.idProp]);\n this.clear(node[this.childrenProp]);\n }\n }\n else target.removeDep();\n });\n }\n /**\n * 清除指定类型的依赖\n * @param type 类型\n * @param nodes 需要清除依赖的节点\n */\n clearByType(type, nodes) {\n this.clear(nodes, type);\n }\n /**\n * 收集单个 target 的依赖,等价于 collectItems(node, [target], ...)\n */\n collectItem(node, target, depExtendedData = {}, deep = false) {\n this.collectItems(node, [target], depExtendedData, deep);\n }\n removeTargetDep(target, node, key) {\n target.removeDep(node[this.idProp], key);\n if (typeof key === \"undefined\" && Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length) for (const item of node[this.childrenProp]) this.removeTargetDep(target, item, key);\n }\n /**\n * 与 removeTargetDep 等价,但一次子树递归同时处理多个 target,\n * 把删除阶段的结构遍历从 ×targets 降到 ×1。\n *\n * 注:供 editor 的 dep service 跨包批量删除时复用,因此为 public。\n */\n removeTargetsDep(targets, node, key) {\n const id = node[this.idProp];\n for (const target of targets) target.removeDep(id, key);\n if (typeof key === \"undefined\" && Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length) for (const item of node[this.childrenProp]) this.removeTargetsDep(targets, item, key);\n }\n /**\n * 与 collectItem 等价,但一次遍历同时处理多个 target(不含删除阶段)。\n *\n * 关键优化:原实现对每个 target 都完整遍历一遍节点树(O(targets × 树规模)),大页面 + 大量数据源时,\n * 结构遍历(Object.entries / 递归 / fullKey 字符串拼接)会被重复 targets 次。这里改为「整棵树只遍历一次,\n * 在每个属性上检查所有 target」,把结构遍历开销从 ×targets 降到 ×1,isTarget 调用次数不变,收集结果完全一致。\n *\n * 注:供 editor 的 dep service / worker 跨包批量收集时复用,因此为 public。\n */\n collectItems(node, targets, depExtendedData = {}, deep = false) {\n const activeTargets = this.filterTargetsByNode(node, targets);\n if (!activeTargets.length) return;\n this.collectTargetForTargets(node, node, \"\", activeTargets, depExtendedData, deep);\n }\n filterTargetsByNode(node, targets) {\n const disableDataSource = Boolean(node[NODE_DISABLE_DATA_SOURCE_KEY]);\n const disableCodeBlock = Boolean(node[NODE_DISABLE_CODE_BLOCK_KEY]);\n if (!disableDataSource && !disableCodeBlock) return targets;\n return targets.filter((target) => {\n if (disableDataSource && DATA_SOURCE_TARGET_TYPES.has(target.type)) return false;\n if (disableCodeBlock && target.type === DepTargetType.CODE_BLOCK) return false;\n return true;\n });\n }\n collectTargetForTargets(node, config, prop, targets, depExtendedData, deep) {\n const id = node[this.idProp];\n const name = `${node[this.nameProp] || node[this.idProp]}`;\n for (const [key, value] of Object.entries(config)) {\n if (typeof value === \"undefined\" || value === \"\") continue;\n const keyIsItems = key === this.childrenProp;\n const fullKey = prop ? `${prop}.${key}` : key;\n let notMatched = null;\n for (let i = 0, l = targets.length; i < l; i++) {\n const target = targets[i];\n if (target.isTarget(fullKey, value, config)) target.updateDep({\n id,\n name,\n data: depExtendedData,\n key: fullKey\n });\n else (notMatched || (notMatched = [])).push(target);\n }\n if (notMatched) {\n if (!keyIsItems && Array.isArray(value)) for (let i = 0, l = value.length; i < l; i++) {\n const item = value[i];\n if (isObject(item)) this.collectTargetForTargets(node, item, `${fullKey}[${i}]`, notMatched, depExtendedData, deep);\n }\n else if (isObject(value)) this.collectTargetForTargets(node, value, fullKey, notMatched, depExtendedData, deep);\n }\n if (keyIsItems && deep && Array.isArray(value)) for (const child of value) this.collectItems(child, targets, depExtendedData, deep);\n }\n }\n };\n //#endregion\n //#region packages/editor/src/utils/logger.ts\n const error = (...args) => {\n if (process.env.NODE_ENV === \"development\") console.error(\"magic editor: \", ...args);\n };\n //#endregion\n //#region packages/editor/src/utils/dep/worker.ts\n /**\n * 收集本次会覆盖到的节点 id\n * 必须与 deep 一致:deep=false 时不能带上子孙 id,否则写回阶段 removeDep 会清掉未重收的子节点依赖\n */\n const getNodeIds = (nodes, deep, ids = []) => {\n for (const node of nodes) {\n ids.push(node.id);\n if (deep && Array.isArray(node.items) && node.items.length) getNodeIds(node.items, deep, ids);\n }\n return ids;\n };\n const collectDsl = ({ id, dsl }) => {\n try {\n const mApp = eval(`(${dsl})`);\n if (!mApp) {\n postMessage({\n id,\n deps: {}\n });\n return;\n }\n const watcher = new Watcher({ initialTargets: {} });\n if (mApp.codeBlocks) for (const [id, code] of Object.entries(mApp.codeBlocks)) watcher.addTarget(createCodeBlockTarget(id, code));\n if (mApp.dataSources) for (const ds of mApp.dataSources) {\n watcher.addTarget(createDataSourceTarget(ds, {}));\n watcher.addTarget(createDataSourceMethodTarget(ds, {}));\n watcher.addTarget(createDataSourceCondTarget(ds, {}));\n }\n const targets = watcher.getCollectableTargets();\n for (const page of mApp.items) watcher.collectItems(page, targets, { pageId: page.id }, true);\n const deps = {\n [DepTargetType.DATA_SOURCE]: {},\n [DepTargetType.DATA_SOURCE_METHOD]: {},\n [DepTargetType.DATA_SOURCE_COND]: {},\n [DepTargetType.CODE_BLOCK]: {}\n };\n traverseTarget(watcher.getTargetsList(), (target) => {\n deps[target.type][target.id] = target.deps;\n });\n const response = {\n id,\n deps\n };\n postMessage(response);\n } catch (e) {\n error(e);\n postMessage({\n id,\n deps: {},\n failed: true\n });\n }\n };\n const collectNodes = ({ id, payload }) => {\n try {\n const { nodes, targets: descriptors, depExtendedData, deep } = eval(`(${payload})`);\n const watcher = new Watcher({ initialTargets: {} });\n const targets = descriptors.map((descriptor) => createTargetByDescriptor(descriptor));\n for (const node of nodes) watcher.collectItems(node, targets, depExtendedData, deep);\n const deps = {};\n for (const target of targets) {\n if (!deps[target.type]) deps[target.type] = {};\n deps[target.type][target.id] = target.deps;\n }\n const response = {\n id,\n deps,\n nodeIds: getNodeIds(nodes, deep)\n };\n postMessage(response);\n } catch (e) {\n error(e);\n postMessage({\n id,\n deps: {},\n nodeIds: [],\n failed: true\n });\n }\n };\n /**\n * 依赖收集 worker\n *\n * 依赖收集需要深度遍历整棵节点树并对每个属性做 target 匹配,节点/数据源多时会长时间占用主线程导致页面卡死,\n * 因此把遍历匹配放到 worker 中执行。\n * 全量与增量收集共用一个常驻 worker:收集逻辑只打进一份 inline worker 产物,\n * 也不会因为频繁收集而反复创建线程,每个请求用 id 与调用方对应。\n */\n onmessage = (e) => {\n if (\"dsl\" in e.data) {\n collectDsl(e.data);\n return;\n }\n collectNodes(e.data);\n };\n //#endregion\n})();\n";
19236
19238
  var blob = typeof self !== "undefined" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", jsContent], { type: "text/javascript;charset=utf-8" });
19237
19239
  function WorkerWrapper(options) {
19238
19240
  let objURL;
@@ -19249,6 +19251,116 @@
19249
19251
  }
19250
19252
  }
19251
19253
  //#endregion
19254
+ //#region packages/editor/src/utils/dep/collect-worker-client.ts
19255
+ /**
19256
+ * 依赖收集 worker 的主线程客户端
19257
+ *
19258
+ * 依赖收集需要深度遍历整棵节点树并对每个属性做匹配,节点/数据源多时会长时间占用主线程导致页面卡死,
19259
+ * 这里把遍历匹配放到 worker 中执行,主线程只负责把结果写回 target。
19260
+ *
19261
+ * 全量收集与增量收集共用一个常驻 worker:增量收集调用频繁(每次节点更新都会触发),
19262
+ * 按次创建 worker 的启动开销无法接受,也容易漏掉销毁导致线程泄漏。
19263
+ */
19264
+ var CollectWorkerClient = class {
19265
+ worker = null;
19266
+ seed = 0;
19267
+ pending = /* @__PURE__ */ new Map();
19268
+ get isSupported() {
19269
+ return typeof Worker !== "undefined";
19270
+ }
19271
+ /**
19272
+ * 在 worker 中全量收集整个 DSL 的依赖,target 由 worker 根据 DSL 重建
19273
+ * @returns 收集结果,返回 null 表示 worker 不可用或执行失败
19274
+ */
19275
+ collectDsl(dsl) {
19276
+ return this.request((id) => ({
19277
+ id,
19278
+ dsl: (0, serialize_javascript.default)(dsl)
19279
+ })).then((response) => response?.deps || null);
19280
+ }
19281
+ /**
19282
+ * 在 worker 中增量收集指定节点的依赖,target 以可序列化描述的形式传入
19283
+ * @returns 收集结果,返回 null 表示 worker 不可用或执行失败,调用方需要回退到主线程收集
19284
+ */
19285
+ collect(payload) {
19286
+ return this.request((id) => ({
19287
+ id,
19288
+ payload: (0, serialize_javascript.default)(payload)
19289
+ })).then((response) => response ? {
19290
+ deps: response.deps || {},
19291
+ nodeIds: response.nodeIds || []
19292
+ } : null);
19293
+ }
19294
+ terminate() {
19295
+ this.worker?.terminate();
19296
+ this.worker = null;
19297
+ this.settleAllPending();
19298
+ }
19299
+ /**
19300
+ * 丢弃在途请求并重建 worker
19301
+ * clearIdleTasks / reset 中断收集时调用,避免已 abort 的长任务继续占着常驻 worker 拖慢后续收集
19302
+ */
19303
+ abort() {
19304
+ this.terminate();
19305
+ }
19306
+ /**
19307
+ * worker 按收到的顺序逐个处理请求,因此请求间不会互相打断,用 id 把结果分发回各自的调用方
19308
+ */
19309
+ request(createRequest) {
19310
+ const worker = this.getWorker();
19311
+ if (!worker) return Promise.resolve(null);
19312
+ this.seed += 1;
19313
+ const id = this.seed;
19314
+ return new Promise((resolve) => {
19315
+ this.pending.set(id, resolve);
19316
+ try {
19317
+ worker.postMessage(createRequest(id));
19318
+ } catch (e) {
19319
+ error("magic editor: 依赖收集 worker 通信失败", e);
19320
+ this.pending.delete(id);
19321
+ resolve(null);
19322
+ }
19323
+ });
19324
+ }
19325
+ getWorker() {
19326
+ if (!this.isSupported) return null;
19327
+ if (this.worker) return this.worker;
19328
+ try {
19329
+ const worker = new WorkerWrapper();
19330
+ worker.onmessage = (e) => {
19331
+ this.handleResponse(e.data);
19332
+ };
19333
+ worker.onerror = () => {
19334
+ this.handleFatalError();
19335
+ };
19336
+ worker.onmessageerror = () => {
19337
+ this.handleFatalError();
19338
+ };
19339
+ this.worker = worker;
19340
+ } catch (e) {
19341
+ error("magic editor: 依赖收集 worker 创建失败", e);
19342
+ return null;
19343
+ }
19344
+ return this.worker;
19345
+ }
19346
+ handleResponse(response) {
19347
+ const resolve = this.pending.get(response?.id);
19348
+ if (!resolve) return;
19349
+ this.pending.delete(response.id);
19350
+ resolve(response.failed ? null : response);
19351
+ }
19352
+ handleFatalError() {
19353
+ this.worker?.terminate();
19354
+ this.worker = null;
19355
+ this.settleAllPending();
19356
+ }
19357
+ settleAllPending() {
19358
+ const resolvers = [...this.pending.values()];
19359
+ this.pending.clear();
19360
+ for (const resolve of resolvers) resolve(null);
19361
+ }
19362
+ };
19363
+ //#endregion
19252
19364
  //#region packages/editor/src/services/dep.ts
19253
19365
  init_lodash();
19254
19366
  var Dep = class extends BaseService {
@@ -19257,6 +19369,7 @@
19257
19369
  taskLength: 0
19258
19370
  });
19259
19371
  idleTask = new IdleTask();
19372
+ collectWorker = new CollectWorkerClient();
19260
19373
  watcher = new _tmagic_core.Watcher({ initialTargets: (0, vue.reactive)({}) });
19261
19374
  waitingWorker;
19262
19375
  resolveWaitingWorker;
@@ -19309,6 +19422,14 @@
19309
19422
  this.emit("collected", nodes, deep);
19310
19423
  this.emit("ds-collected", nodes, deep);
19311
19424
  }
19425
+ /**
19426
+ * 空闲收集依赖
19427
+ *
19428
+ * 遍历匹配是整个收集过程中最耗时的部分(要深度遍历节点树并对每个属性做 target 匹配),
19429
+ * 节点/数据源多时在主线程执行会长时间卡死页面,因此优先交给 worker 执行,主线程只负责把结果写回 target。
19430
+ * 自定义 target 的 isTarget 是无法跨线程传递的闭包函数(没有 descriptor),仍走主线程空闲队列;
19431
+ * worker 不可用(如 SSR)或执行失败时也会回退到主线程空闲队列。
19432
+ */
19312
19433
  async collectIdle(nodes, depExtendedData = {}, deep = false, type) {
19313
19434
  if (this.waitingWorker) await this.waitingWorker;
19314
19435
  const batch = {
@@ -19318,11 +19439,13 @@
19318
19439
  dsPending: 0,
19319
19440
  collectedEmitted: false,
19320
19441
  dsSettled: false,
19442
+ aborted: false,
19321
19443
  resolve: () => {}
19322
19444
  };
19323
- this.watcher.collectByCallback(nodes, type, ({ node, target }) => {
19324
- this.enqueueTask(node, target, depExtendedData, deep, batch);
19325
- });
19445
+ const workerTargets = [];
19446
+ for (const target of this.watcher.getCollectableTargets(type)) if (target.descriptor && this.collectWorker.isSupported) workerTargets.push(target);
19447
+ else this.enqueueTasks(nodes, target, depExtendedData, deep, batch);
19448
+ if (workerTargets.length && nodes.length) this.collectByCollectWorker(nodes, workerTargets, depExtendedData, deep, batch);
19326
19449
  if (batch.pending === 0) {
19327
19450
  this.emit("collected", nodes, deep);
19328
19451
  this.updateCollectingState();
@@ -19341,16 +19464,8 @@
19341
19464
  const { promise, resolve: waitingResolve } = Promise.withResolvers();
19342
19465
  this.waitingWorker = promise;
19343
19466
  this.resolveWaitingWorker = waitingResolve;
19344
- return new Promise((resolve) => {
19345
- const worker = new WorkerWrapper();
19346
- worker.postMessage({ dsl: (0, serialize_javascript.default)(dsl) });
19347
- worker.onmessage = (e) => {
19348
- resolve(e.data);
19349
- };
19350
- worker.onerror = () => {
19351
- resolve({});
19352
- };
19353
- }).then((depsData) => {
19467
+ return this.collectWorker.collectDsl(dsl).then((deps) => {
19468
+ const depsData = deps || {};
19354
19469
  if (generation !== this.workerGeneration) {
19355
19470
  waitingResolve();
19356
19471
  return depsData;
@@ -19376,7 +19491,8 @@
19376
19491
  }
19377
19492
  collectNode(node, target, depExtendedData = {}, deep = false) {
19378
19493
  if ((0, _tmagic_utils.isPage)(node)) this.removePageDep(target, depExtendedData);
19379
- else this.watcher.removeTargetDep(target, node);
19494
+ else if (deep) this.watcher.removeTargetDep(target, node);
19495
+ else target.removeDep(node.id);
19380
19496
  this.watcher.collectItem(node, target, depExtendedData, deep);
19381
19497
  }
19382
19498
  clear(nodes) {
@@ -19394,6 +19510,7 @@
19394
19510
  clearIdleTasks() {
19395
19511
  this.abortActiveBatches();
19396
19512
  this.idleTask.clearTasks();
19513
+ this.collectWorker.abort();
19397
19514
  }
19398
19515
  on(eventName, listener) {
19399
19516
  return super.on(eventName, listener);
@@ -19408,6 +19525,7 @@
19408
19525
  this.resolveWaitingWorker = void 0;
19409
19526
  this.waitingWorker = void 0;
19410
19527
  this.idleTask.clearTasks();
19528
+ this.collectWorker.abort();
19411
19529
  for (const type of Object.keys(this.watcher.getTargetsList())) this.removeTargets(type);
19412
19530
  this.set("collecting", false);
19413
19531
  }
@@ -19417,6 +19535,7 @@
19417
19535
  this.reset();
19418
19536
  this.removeAllPlugins();
19419
19537
  this.idleTask.removeAllListeners();
19538
+ this.collectWorker.terminate();
19420
19539
  }
19421
19540
  emit(eventName, ...args) {
19422
19541
  return super.emit(eventName, ...args);
@@ -19431,26 +19550,73 @@
19431
19550
  if (dep.data?.pageId && dep.data.pageId === depExtendedData.pageId) delete target.deps[depKey];
19432
19551
  }
19433
19552
  }
19553
+ /**
19554
+ * 把遍历匹配交给 worker,结果回来后按 target 分片写回主线程
19555
+ */
19556
+ collectByCollectWorker(nodes, targets, depExtendedData, deep, batch) {
19557
+ const hasDataSource = targets.some((target) => target.type === _tmagic_core.DepTargetType.DATA_SOURCE);
19558
+ this.beginBatchTask(batch, hasDataSource);
19559
+ this.collectWorker.collect({
19560
+ nodes,
19561
+ targets: targets.map((target) => target.descriptor),
19562
+ depExtendedData,
19563
+ deep
19564
+ }).then((result) => {
19565
+ if (batch.aborted) return;
19566
+ for (const target of targets) if (result) this.enqueueApplyTask(target, result.deps[target.type]?.[target.id] || {}, result.nodeIds, batch, {
19567
+ nodes,
19568
+ depExtendedData,
19569
+ deep
19570
+ });
19571
+ else this.enqueueTasks(nodes, target, depExtendedData, deep, batch);
19572
+ }).finally(() => {
19573
+ this.onBatchTaskDone(batch, hasDataSource);
19574
+ });
19575
+ }
19576
+ /**
19577
+ * 用 worker 的收集结果替换 target 上这批节点的依赖
19578
+ */
19579
+ enqueueApplyTask(target, deps, nodeIds, batch, { nodes, depExtendedData, deep }) {
19580
+ const isDataSource = target.type === _tmagic_core.DepTargetType.DATA_SOURCE;
19581
+ this.beginBatchTask(batch, isDataSource);
19582
+ this.idleTask.enqueueTask(() => {
19583
+ try {
19584
+ const current = this.watcher.getTarget(target.id, target.type);
19585
+ if (current !== target) {
19586
+ if (current) this.enqueueTasks(nodes, current, depExtendedData, deep, batch);
19587
+ return;
19588
+ }
19589
+ if (nodes.some((node) => (0, _tmagic_utils.isPage)(node))) this.removePageDep(target, depExtendedData);
19590
+ for (const id of nodeIds) target.removeDep(id);
19591
+ for (const [id, dep] of Object.entries(deps)) target.deps[id] = dep;
19592
+ } finally {
19593
+ this.onBatchTaskDone(batch, isDataSource);
19594
+ }
19595
+ }, void 0, isDataSource);
19596
+ }
19597
+ enqueueTasks(nodes, target, depExtendedData, deep, batch) {
19598
+ for (const node of nodes) this.enqueueTask(node, target, depExtendedData, deep, batch);
19599
+ }
19434
19600
  enqueueTask(node, target, depExtendedData, deep, batch) {
19435
19601
  const isDataSource = target.type === _tmagic_core.DepTargetType.DATA_SOURCE;
19436
- batch.pending += 1;
19437
- if (isDataSource) batch.dsPending += 1;
19438
- this.idleTask.enqueueTask(({ node, deep, target }) => {
19602
+ this.beginBatchTask(batch, isDataSource);
19603
+ this.idleTask.enqueueTask(() => {
19439
19604
  try {
19440
- this.collectNode(node, target, depExtendedData, deep);
19605
+ this.collectNode(node, target, depExtendedData);
19441
19606
  } finally {
19442
19607
  this.onBatchTaskDone(batch, isDataSource);
19443
19608
  }
19444
- }, {
19445
- node,
19446
- deep: false,
19447
- target
19448
- }, isDataSource);
19609
+ }, void 0, isDataSource);
19449
19610
  if (deep && Array.isArray(node.items) && node.items.length) node.items.forEach((item) => {
19450
19611
  this.enqueueTask(item, target, depExtendedData, deep, batch);
19451
19612
  });
19452
19613
  }
19614
+ beginBatchTask(batch, isDataSource) {
19615
+ batch.pending += 1;
19616
+ if (isDataSource) batch.dsPending += 1;
19617
+ }
19453
19618
  onBatchTaskDone(batch, isDataSource) {
19619
+ if (batch.aborted) return;
19454
19620
  if (isDataSource) {
19455
19621
  batch.dsPending -= 1;
19456
19622
  if (batch.dsPending === 0) this.settleBatchDs(batch);
@@ -19482,7 +19648,10 @@
19482
19648
  if (!this.activeBatches.size) return;
19483
19649
  const batches = [...this.activeBatches];
19484
19650
  this.activeBatches.clear();
19485
- for (const batch of batches) batch.resolve(false);
19651
+ for (const batch of batches) {
19652
+ batch.aborted = true;
19653
+ batch.resolve(false);
19654
+ }
19486
19655
  this.updateCollectingState();
19487
19656
  }
19488
19657
  updateCollectingState() {
@@ -19500,7 +19669,7 @@
19500
19669
  commands = {
19501
19670
  [KeyBindingCommand.DELETE_NODE]: () => {
19502
19671
  const nodes = editor_default.get("nodes");
19503
- if (!nodes || (0, _tmagic_utils.isPage)(nodes[0]) || (0, _tmagic_utils.isPageFragment)(nodes[0])) return;
19672
+ if (!nodes || (0, _tmagic_utils.isPageOrFragment)(nodes[0])) return;
19504
19673
  editor_default.remove(nodes, { historySource: "shortcut" });
19505
19674
  },
19506
19675
  [KeyBindingCommand.COPY_NODE]: () => {
@@ -19509,7 +19678,7 @@
19509
19678
  },
19510
19679
  [KeyBindingCommand.CUT_NODE]: () => {
19511
19680
  const nodes = editor_default.get("nodes");
19512
- if (!nodes || (0, _tmagic_utils.isPage)(nodes[0]) || (0, _tmagic_utils.isPageFragment)(nodes[0])) return;
19681
+ if (!nodes || (0, _tmagic_utils.isPageOrFragment)(nodes[0])) return;
19513
19682
  editor_default.copy(nodes);
19514
19683
  editor_default.remove(nodes, { historySource: "shortcut" });
19515
19684
  },