@nocobase/client-v2 2.2.0-alpha.1 → 2.2.0-alpha.3

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.
Files changed (220) hide show
  1. package/es/PluginSettingsManager.d.ts +33 -0
  2. package/es/RouteRepository.d.ts +21 -6
  3. package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
  4. package/es/components/category-tabs/index.d.ts +9 -0
  5. package/es/components/form/JsonTextArea.d.ts +2 -1
  6. package/es/components/form/TypedVariableInput.d.ts +22 -4
  7. package/es/components/form/VariableJsonTextArea.d.ts +19 -0
  8. package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
  9. package/es/components/form/filter/index.d.ts +2 -0
  10. package/es/components/form/index.d.ts +1 -0
  11. package/es/components/index.d.ts +2 -0
  12. package/es/flow/actions/afterSuccess.d.ts +8 -1
  13. package/es/flow/actions/index.d.ts +1 -1
  14. package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +2 -0
  15. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +2 -1
  16. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +2 -0
  17. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +3 -2
  18. package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +5 -0
  19. package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
  20. package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
  21. package/es/flow/components/FieldAssignValueInput.d.ts +2 -0
  22. package/es/flow/index.d.ts +1 -0
  23. package/es/flow/models/base/GridModel.d.ts +1 -1
  24. package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +5 -0
  25. package/es/flow/models/blocks/form/FormBlockModel.d.ts +4 -0
  26. package/es/flow/models/blocks/form/submitHandler.d.ts +1 -1
  27. package/es/flow/models/blocks/form/value-runtime/rules.d.ts +21 -0
  28. package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +30 -0
  29. package/es/flow/models/blocks/form/value-runtime/types.d.ts +2 -2
  30. package/es/flow/models/blocks/js-block/JSBlock.d.ts +1 -0
  31. package/es/flow/models/blocks/table/TableActionsColumnModel.d.ts +1 -0
  32. package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
  33. package/es/flow/models/blocks/table/dragSort/dragSortHooks.d.ts +1 -1
  34. package/es/flow/models/blocks/table/dragSort/dragSortSettings.d.ts +2 -1
  35. package/es/flow/models/blocks/table/dragSort/dragSortUtils.d.ts +6 -4
  36. package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +2 -0
  37. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.d.ts +6 -0
  38. package/es/flow/resolveViewParamsToViewList.d.ts +1 -1
  39. package/es/flow/utils/dataScopeRowSnapshot.d.ts +59 -0
  40. package/es/flow/utils/dateTimeDisplayProps.d.ts +49 -0
  41. package/es/flow/utils/formValueDeps.d.ts +32 -0
  42. package/es/flow-compat/FieldValidation.d.ts +2 -1
  43. package/es/index.d.ts +2 -1
  44. package/es/index.mjs +233 -137
  45. package/lib/index.js +234 -138
  46. package/package.json +7 -7
  47. package/src/PluginSettingsManager.ts +53 -0
  48. package/src/RouteRepository.ts +126 -24
  49. package/src/__tests__/PluginSettingsManager.test.ts +13 -0
  50. package/src/__tests__/RouteRepository.test.ts +216 -0
  51. package/src/__tests__/browserChecker.test.ts +61 -0
  52. package/src/__tests__/exports.test.ts +16 -0
  53. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +199 -0
  54. package/src/__tests__/plugin-manager.test.tsx +44 -2
  55. package/src/__tests__/settings-center.test.tsx +40 -1
  56. package/src/collection-manager/field-configure.ts +1 -1
  57. package/src/collection-manager/interfaces/id.ts +1 -1
  58. package/src/collection-manager/interfaces/m2m.tsx +2 -2
  59. package/src/collection-manager/interfaces/m2o.tsx +2 -2
  60. package/src/collection-manager/interfaces/nanoid.ts +1 -1
  61. package/src/collection-manager/interfaces/o2m.tsx +2 -2
  62. package/src/collection-manager/interfaces/obo.tsx +2 -2
  63. package/src/collection-manager/interfaces/oho.tsx +2 -2
  64. package/src/collection-manager/interfaces/properties/index.ts +2 -2
  65. package/src/collection-manager/interfaces/uuid.ts +1 -1
  66. package/src/components/AppComponents.tsx +19 -3
  67. package/src/components/README.md +7 -1
  68. package/src/components/README.zh-CN.md +6 -1
  69. package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
  70. package/src/components/category-tabs/index.ts +10 -0
  71. package/src/components/form/JsonTextArea.tsx +21 -11
  72. package/src/components/form/TypedVariableInput.tsx +416 -93
  73. package/src/components/form/VariableJsonTextArea.tsx +175 -0
  74. package/src/components/form/__tests__/JsonTextArea.test.tsx +43 -0
  75. package/src/components/form/__tests__/TypedVariableInput.test.tsx +268 -9
  76. package/src/components/form/__tests__/VariableJsonTextArea.test.tsx +86 -0
  77. package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
  78. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
  79. package/src/components/form/filter/index.ts +2 -0
  80. package/src/components/form/index.tsx +1 -0
  81. package/src/components/index.ts +2 -0
  82. package/src/flow/__tests__/FlowRoute.test.tsx +443 -5
  83. package/src/flow/__tests__/getKey.test.ts +7 -0
  84. package/src/flow/__tests__/resolveViewParamsToViewList.test.ts +34 -0
  85. package/src/flow/actions/__tests__/afterSuccess.test.ts +73 -0
  86. package/src/flow/actions/__tests__/dataScopeFilter.test.ts +58 -0
  87. package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +1022 -0
  88. package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +150 -1
  89. package/src/flow/actions/__tests__/linkageAssignField.legacy.test.ts +135 -0
  90. package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +19 -5
  91. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +92 -0
  92. package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +104 -0
  93. package/src/flow/actions/__tests__/subFormFieldLinkageRules.inputArgs.test.ts +2 -2
  94. package/src/flow/actions/afterSuccess.tsx +142 -3
  95. package/src/flow/actions/customVariable.tsx +1 -2
  96. package/src/flow/actions/dataScopeFilter.ts +10 -0
  97. package/src/flow/actions/dateTimeFormat.tsx +42 -28
  98. package/src/flow/actions/index.ts +1 -1
  99. package/src/flow/actions/linkageRules.tsx +123 -35
  100. package/src/flow/actions/linkageRulesFormValueRefresh.ts +22 -130
  101. package/src/flow/actions/openView.tsx +38 -4
  102. package/src/flow/actions/runjs.tsx +2 -14
  103. package/src/flow/actions/validation.tsx +62 -30
  104. package/src/flow/admin-shell/BaseLayoutModel.tsx +25 -3
  105. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +5 -2
  106. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +13 -0
  107. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +34 -10
  108. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +310 -0
  109. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +44 -8
  110. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +5 -3
  111. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +61 -9
  112. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +71 -8
  113. package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +1 -1
  114. package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +23 -9
  115. package/src/flow/admin-shell/admin-layout/HelpLite.tsx +1 -3
  116. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +188 -0
  117. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +99 -0
  118. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +82 -0
  119. package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +166 -2
  120. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +20 -0
  121. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +33 -5
  122. package/src/flow/components/DefaultValue.tsx +1 -2
  123. package/src/flow/components/DynamicFlowsIcon.tsx +447 -126
  124. package/src/flow/components/FieldAssignRulesEditor.tsx +128 -21
  125. package/src/flow/components/FieldAssignValueInput.tsx +10 -5
  126. package/src/flow/components/FlowRoute.tsx +56 -11
  127. package/src/flow/components/__tests__/DynamicFlowsIcon.test.tsx +148 -2
  128. package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +508 -4
  129. package/src/flow/components/__tests__/fieldAssignOptions.test.ts +151 -3
  130. package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +2 -17
  131. package/src/flow/components/code-editor/hooks/useCodeRunner.ts +2 -14
  132. package/src/flow/components/code-editor/runjsDiagnostics.ts +8 -45
  133. package/src/flow/components/fieldAssignOptions.ts +155 -27
  134. package/src/flow/flows/editMarkdownFlow.tsx +1 -1
  135. package/src/flow/getViewDiffAndUpdateHidden.tsx +1 -0
  136. package/src/flow/index.ts +2 -1
  137. package/src/flow/internal/utils/operatorSchemaHelper.ts +15 -1
  138. package/src/flow/models/actions/JSActionModel.tsx +2 -7
  139. package/src/flow/models/actions/JSCollectionActionModel.tsx +2 -7
  140. package/src/flow/models/actions/JSItemActionModel.tsx +2 -7
  141. package/src/flow/models/actions/JSRecordActionModel.tsx +2 -7
  142. package/src/flow/models/base/ActionModel.tsx +12 -1
  143. package/src/flow/models/base/ActionModelCore.tsx +6 -4
  144. package/src/flow/models/base/GridModel.tsx +38 -7
  145. package/src/flow/models/base/PageModel/PageModel.tsx +4 -1
  146. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +28 -4
  147. package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -0
  148. package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +37 -0
  149. package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +239 -1
  150. package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +48 -0
  151. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +31 -3
  152. package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
  153. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +77 -0
  154. package/src/flow/models/blocks/filter-form/fields/FilterFormCustomFieldModel.tsx +1 -1
  155. package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/customizeFilterRender.test.tsx +56 -1
  156. package/src/flow/models/blocks/form/EditFormModel.tsx +1 -0
  157. package/src/flow/models/blocks/form/FormActionModel.tsx +1 -1
  158. package/src/flow/models/blocks/form/FormBlockModel.tsx +7 -0
  159. package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
  160. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
  161. package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +54 -1
  162. package/src/flow/models/blocks/form/submitHandler.ts +17 -3
  163. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +940 -75
  164. package/src/flow/models/blocks/form/value-runtime/rules.ts +445 -13
  165. package/src/flow/models/blocks/form/value-runtime/runtime.ts +348 -13
  166. package/src/flow/models/blocks/form/value-runtime/types.ts +2 -2
  167. package/src/flow/models/blocks/js-block/JSBlock.tsx +225 -9
  168. package/src/flow/models/blocks/js-block/__tests__/JSBlockModel.test.tsx +150 -0
  169. package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
  170. package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +36 -13
  171. package/src/flow/models/blocks/table/TableBlockModel.tsx +38 -17
  172. package/src/flow/models/blocks/table/TableColumnModel.tsx +36 -3
  173. package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +111 -1
  174. package/src/flow/models/blocks/table/__tests__/TableBlockModel.dragSort.test.ts +127 -0
  175. package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
  176. package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +1 -0
  177. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +159 -1
  178. package/src/flow/models/blocks/table/dragSort/dragSortHooks.tsx +28 -17
  179. package/src/flow/models/blocks/table/dragSort/dragSortSettings.ts +13 -6
  180. package/src/flow/models/blocks/table/dragSort/dragSortUtils.ts +15 -2
  181. package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +1 -1
  182. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +29 -6
  183. package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +16 -4
  184. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +141 -19
  185. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +13 -1
  186. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableColumnModel.rowRecord.test.ts +197 -0
  187. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableFieldModel.reset.test.ts +180 -0
  188. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -4
  189. package/src/flow/models/fields/AssociationFieldModel/__tests__/AssociationFieldModel.updateAssociation.test.ts +72 -0
  190. package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +23 -0
  191. package/src/flow/models/fields/ClickableFieldModel.tsx +5 -0
  192. package/src/flow/models/fields/DisplayDateTimeFieldModel.tsx +29 -1
  193. package/src/flow/models/fields/InputFieldModel.tsx +40 -2
  194. package/src/flow/models/fields/JSEditableFieldModel.tsx +2 -11
  195. package/src/flow/models/fields/JSFieldModel.tsx +2 -7
  196. package/src/flow/models/fields/JSItemModel.tsx +2 -14
  197. package/src/flow/models/fields/SelectFieldModel.tsx +6 -4
  198. package/src/flow/models/fields/TextareaFieldModel.tsx +49 -3
  199. package/src/flow/models/fields/__tests__/DisplayDateTimeFieldModel.test.tsx +96 -0
  200. package/src/flow/models/fields/__tests__/InputFieldModel.test.tsx +117 -1
  201. package/src/flow/models/fields/__tests__/SelectFieldModel.test.tsx +43 -0
  202. package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +100 -0
  203. package/src/flow/models/topbar/TopbarActionModel.tsx +93 -10
  204. package/src/flow/resolveViewParamsToViewList.tsx +11 -3
  205. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +258 -0
  206. package/src/flow/utils/dataScopeFormValueClear.ts +218 -81
  207. package/src/flow/utils/dataScopeRowSnapshot.ts +616 -0
  208. package/src/flow/utils/dateTimeDisplayProps.ts +135 -0
  209. package/src/flow/utils/formValueDeps.ts +170 -0
  210. package/src/flow-compat/FieldValidation.tsx +122 -60
  211. package/src/flow-compat/Popover.tsx +43 -4
  212. package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
  213. package/src/index.ts +8 -1
  214. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
  215. package/src/nocobase-buildin-plugin/index.tsx +44 -14
  216. package/src/settings-center/AdminSettingsLayout.tsx +10 -2
  217. package/src/settings-center/SystemSettingsPage.tsx +1 -2
  218. package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
  219. package/src/settings-center/plugin-manager/index.tsx +3 -0
  220. package/src/settings-center/utils.tsx +0 -6
@@ -0,0 +1,616 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ import type { FlowContext } from '@nocobase/flow-engine';
11
+ import { cloneDeep, isEqual } from 'lodash';
12
+ import { namePathToPathKey } from '../models/blocks/form/value-runtime/path';
13
+ import { getSubTableRowIdentity } from '../models/fields/AssociationFieldModel/SubTableFieldModel/rowIdentity';
14
+ import { getChangedPathsFromPayload, isNamePathPrefix, type FieldIndexEntry, type NamePath } from './formValueDeps';
15
+
16
+ export type FilterTargetKey = string | string[] | null | undefined;
17
+
18
+ export type RowIdentity = string;
19
+
20
+ export type RowScopedFieldIndexEntry = FieldIndexEntry & {
21
+ filterTargetKey?: FilterTargetKey;
22
+ };
23
+
24
+ export type RowScope = {
25
+ entries: RowScopedFieldIndexEntry[];
26
+ tailPath: NamePath;
27
+ identityTailPaths: NamePath[];
28
+ };
29
+
30
+ export type DataScopeValuePath = {
31
+ path: NamePath;
32
+ rowScope?: RowScope;
33
+ };
34
+
35
+ export type DataScopeStructuralPath = {
36
+ path: NamePath;
37
+ kind: 'index' | 'length';
38
+ rowScope?: RowScope;
39
+ };
40
+
41
+ export type DataScopeTriggerPath = {
42
+ path: NamePath;
43
+ includeChildren: boolean;
44
+ };
45
+
46
+ export type DataScopeClearDeps = {
47
+ wildcard: boolean;
48
+ valuePaths: DataScopeValuePath[];
49
+ structuralPaths: DataScopeStructuralPath[];
50
+ triggerPaths: DataScopeTriggerPath[];
51
+ };
52
+
53
+ export type DataScopeSnapshotValue = {
54
+ value: unknown;
55
+ rowIdentities?: Array<RowIdentity | null>;
56
+ rowDetached?: boolean;
57
+ };
58
+
59
+ export type DataScopeClearSnapshot = {
60
+ complete: boolean;
61
+ rowDetached: boolean;
62
+ rowMissing: boolean;
63
+ values: Map<string, DataScopeSnapshotValue>;
64
+ structures: Map<string, DataScopeSnapshotValue>;
65
+ };
66
+
67
+ export type DataScopeClearChangeStatus = {
68
+ status: 'changed' | 'unchanged' | 'unknown';
69
+ snapshot: DataScopeClearSnapshot | null;
70
+ };
71
+
72
+ function normalizeFilterTargetKey(raw: unknown): FilterTargetKey {
73
+ if (Array.isArray(raw)) {
74
+ const keys = raw.filter((key): key is string => typeof key === 'string' && !!key);
75
+ return keys.length ? keys : 'id';
76
+ }
77
+ return typeof raw === 'string' && raw ? raw : 'id';
78
+ }
79
+
80
+ function getAssociationTargetKey(field: any): FilterTargetKey {
81
+ return normalizeFilterTargetKey(
82
+ field?.targetCollection?.filterTargetKey ?? field?.targetCollection?.filterByTk ?? field?.targetKey,
83
+ );
84
+ }
85
+
86
+ function getRootCollection(ctx: FlowContext) {
87
+ const model: any = ctx.model;
88
+ return (
89
+ model?.context?.blockModel?.context?.collection ??
90
+ model?.context?.formBlock?.context?.collection ??
91
+ model?.context?.rootCollection ??
92
+ (ctx as any)?.rootCollection ??
93
+ (ctx as any)?.collection ??
94
+ model?.context?.collectionField?.collection ??
95
+ model?.context?.collection
96
+ );
97
+ }
98
+
99
+ function getFallbackTargetKey(ctx: FlowContext): FilterTargetKey {
100
+ const model: any = ctx.model;
101
+ return normalizeFilterTargetKey(
102
+ model?.context?.collection?.filterTargetKey ??
103
+ model?.context?.collection?.filterByTk ??
104
+ model?.context?.collectionField?.targetCollection?.filterTargetKey ??
105
+ model?.context?.collectionField?.targetCollection?.filterByTk ??
106
+ model?.context?.collectionField?.targetKey,
107
+ );
108
+ }
109
+
110
+ export function applyDataScopeFieldIndexTargetKeys(
111
+ ctx: FlowContext,
112
+ entries: FieldIndexEntry[],
113
+ ): RowScopedFieldIndexEntry[] {
114
+ let collection = getRootCollection(ctx);
115
+ const fallbackTargetKey = getFallbackTargetKey(ctx);
116
+ return entries.map((entry) => {
117
+ let filterTargetKey: FilterTargetKey = fallbackTargetKey;
118
+ if (collection?.getField) {
119
+ const field = collection.getField(entry.name);
120
+ if (field?.isAssociationField?.() || field?.targetCollection || field?.targetKey) {
121
+ filterTargetKey = getAssociationTargetKey(field);
122
+ collection = field?.targetCollection;
123
+ }
124
+ }
125
+ return { ...entry, filterTargetKey };
126
+ });
127
+ }
128
+
129
+ export function createDataScopeRowScope(
130
+ entries: RowScopedFieldIndexEntry[],
131
+ parentDepth: number,
132
+ tailPath: NamePath,
133
+ ): RowScope | undefined {
134
+ const targetIndex = entries.length - 1 - parentDepth;
135
+ if (targetIndex < 0) return undefined;
136
+ const targetEntry = entries[targetIndex];
137
+ const targetKey = targetEntry?.filterTargetKey;
138
+ const identityTailPaths = Array.isArray(targetKey)
139
+ ? targetKey.map((key) => [key])
140
+ : typeof targetKey === 'string' && targetKey
141
+ ? [[targetKey]]
142
+ : [];
143
+ return {
144
+ entries: entries.slice(0, targetIndex + 1).map((entry) => ({ ...entry })),
145
+ tailPath,
146
+ identityTailPaths,
147
+ };
148
+ }
149
+
150
+ function hasOwnValue(source: unknown, key: string | number) {
151
+ if (source == null) return false;
152
+ if (Array.isArray(source) && typeof key === 'number') {
153
+ return key >= 0 && key < source.length;
154
+ }
155
+ if (typeof source !== 'object') return false;
156
+ return Object.prototype.hasOwnProperty.call(source, key);
157
+ }
158
+
159
+ function getValueAtPath(source: unknown, path: NamePath): { found: boolean; value?: unknown } {
160
+ let cursor = source;
161
+ for (const seg of path) {
162
+ if (!hasOwnValue(cursor, seg)) {
163
+ return { found: false };
164
+ }
165
+ cursor = (cursor as Record<string | number, unknown>)[seg];
166
+ }
167
+ return { found: true, value: cursor };
168
+ }
169
+
170
+ function getSnapshotSourceFromPayload(payload: unknown) {
171
+ const payloadObj = payload as { allValuesSnapshot?: unknown; allValues?: unknown } | undefined;
172
+ return payloadObj?.allValuesSnapshot ?? payloadObj?.allValues;
173
+ }
174
+
175
+ function getRowIdentity(row: unknown, filterTargetKey: FilterTargetKey): RowIdentity | null {
176
+ if (!row || typeof row !== 'object') return null;
177
+ return getSubTableRowIdentity(row as Record<string, unknown>, filterTargetKey);
178
+ }
179
+
180
+ function getRowScopePath(rowScope: RowScope): NamePath {
181
+ const path: NamePath = [];
182
+ for (const entry of rowScope.entries) {
183
+ path.push(entry.name, entry.index);
184
+ }
185
+ return path;
186
+ }
187
+
188
+ function shouldRefreshRowIdentityFromPayload(
189
+ payload: unknown,
190
+ rowScope: RowScope,
191
+ previousValue?: DataScopeSnapshotValue,
192
+ changedPaths?: NamePath[],
193
+ ) {
194
+ if (previousValue?.rowDetached) return false;
195
+ const rowPath = getRowScopePath(rowScope);
196
+ if (!rowPath.length || !rowScope.identityTailPaths.length) return false;
197
+ const identityPaths = rowScope.identityTailPaths.map((path) => [...rowPath, ...path]);
198
+ return (changedPaths ?? getChangedPathsFromPayload(payload)).some((changedPath) =>
199
+ identityPaths.some((identityPath) => isNamePathPrefix(identityPath, changedPath)),
200
+ );
201
+ }
202
+
203
+ function findRowIndexByIdentity(
204
+ rows: unknown,
205
+ identity: RowIdentity | null | undefined,
206
+ filterTargetKey: FilterTargetKey,
207
+ ) {
208
+ if (!Array.isArray(rows) || !identity) return -1;
209
+ return rows.findIndex((row) => getRowIdentity(row, filterTargetKey) === identity);
210
+ }
211
+
212
+ function getRowScopedPathFromSource(
213
+ source: unknown,
214
+ rowScope: RowScope,
215
+ previousRowIdentities?: Array<RowIdentity | null>,
216
+ allowIndexFallbackOnIdentityMiss?: boolean,
217
+ ): {
218
+ found: boolean;
219
+ rowMissing?: boolean;
220
+ path?: NamePath;
221
+ rowIndex?: number;
222
+ rowIdentities?: Array<RowIdentity | null>;
223
+ } {
224
+ let currentPath: NamePath = [];
225
+ let currentRowIndex = -1;
226
+ const rowIdentities: Array<RowIdentity | null> = [];
227
+
228
+ for (let index = 0; index < rowScope.entries.length; index++) {
229
+ const entry = rowScope.entries[index];
230
+ const listPath = [...currentPath, entry.name];
231
+ const listResult = getValueAtPath(source, listPath);
232
+ if (!listResult.found) {
233
+ return { found: false };
234
+ }
235
+ if (!Array.isArray(listResult.value)) {
236
+ return { found: false };
237
+ }
238
+
239
+ const previousIdentity = previousRowIdentities?.[index];
240
+ const matchedIndex = findRowIndexByIdentity(listResult.value, previousIdentity, entry.filterTargetKey);
241
+ const rowIndex = matchedIndex >= 0 ? matchedIndex : entry.index;
242
+ if (rowIndex < 0 || rowIndex >= listResult.value.length) {
243
+ return previousIdentity ? { found: false, rowMissing: true } : { found: false };
244
+ }
245
+
246
+ const row = listResult.value[rowIndex];
247
+ if (previousIdentity && matchedIndex < 0 && !allowIndexFallbackOnIdentityMiss) {
248
+ return { found: false, rowMissing: true };
249
+ }
250
+
251
+ rowIdentities.push(getRowIdentity(row, entry.filterTargetKey));
252
+ currentRowIndex = rowIndex;
253
+ currentPath = [...listPath, rowIndex];
254
+ }
255
+
256
+ return {
257
+ found: true,
258
+ path: [...currentPath, ...rowScope.tailPath],
259
+ rowIndex: currentRowIndex,
260
+ rowIdentities,
261
+ };
262
+ }
263
+
264
+ function getPathValue(formBlock: any, payload: unknown, path: NamePath): { found: boolean; value?: unknown } {
265
+ const payloadSource = getSnapshotSourceFromPayload(payload);
266
+ const payloadValue = getValueAtPath(payloadSource, path);
267
+ if (payloadValue.found) {
268
+ return payloadValue;
269
+ }
270
+
271
+ const form = formBlock?.context?.form;
272
+ if (typeof form?.getFieldValue === 'function') {
273
+ return { found: true, value: form.getFieldValue(path) };
274
+ }
275
+
276
+ return getValueAtPath(formBlock?.context?.formValues, path);
277
+ }
278
+
279
+ function getRowScopedPathValue(
280
+ formBlock: any,
281
+ payload: unknown,
282
+ valuePath: DataScopeValuePath,
283
+ previousValue?: DataScopeSnapshotValue,
284
+ changedPaths?: NamePath[],
285
+ ): { found: boolean; rowMissing?: boolean; value?: unknown; rowIdentities?: Array<RowIdentity | null> } {
286
+ const rowScope = valuePath.rowScope;
287
+ if (!rowScope) {
288
+ return getPathValue(formBlock, payload, valuePath.path);
289
+ }
290
+ if (previousValue?.rowDetached) {
291
+ return { found: false, rowMissing: true };
292
+ }
293
+
294
+ const allowIndexFallbackOnIdentityMiss = shouldRefreshRowIdentityFromPayload(
295
+ payload,
296
+ rowScope,
297
+ previousValue,
298
+ changedPaths,
299
+ );
300
+ const sourceCandidates = [getSnapshotSourceFromPayload(payload), formBlock?.context?.formValues];
301
+ for (const source of sourceCandidates) {
302
+ const scopedPath = getRowScopedPathFromSource(
303
+ source,
304
+ rowScope,
305
+ previousValue?.rowIdentities,
306
+ allowIndexFallbackOnIdentityMiss,
307
+ );
308
+ if (scopedPath.rowMissing) {
309
+ return { found: false, rowMissing: true };
310
+ }
311
+ if (!scopedPath.found || !scopedPath.path) {
312
+ continue;
313
+ }
314
+ const result = getValueAtPath(source, scopedPath.path);
315
+ if (result.found) {
316
+ return {
317
+ found: true,
318
+ value: result.value,
319
+ rowIdentities: scopedPath.rowIdentities,
320
+ };
321
+ }
322
+ }
323
+
324
+ const form = formBlock?.context?.form;
325
+ if (typeof form?.getFieldValue === 'function') {
326
+ return {
327
+ found: true,
328
+ value: form.getFieldValue(valuePath.path),
329
+ rowIdentities: previousValue?.rowIdentities,
330
+ };
331
+ }
332
+
333
+ return { found: false };
334
+ }
335
+
336
+ function getStructuralLengthValue(value: unknown) {
337
+ return Array.isArray(value) ? value.length : value;
338
+ }
339
+
340
+ function getStructuralIndexValueFromList(
341
+ value: unknown,
342
+ structuralPath: DataScopeStructuralPath,
343
+ previousValue?: DataScopeSnapshotValue,
344
+ ): { found: boolean; rowMissing?: boolean; value?: unknown; rowIdentities?: Array<RowIdentity | null> } {
345
+ if (!Array.isArray(value)) {
346
+ return { found: true, value };
347
+ }
348
+
349
+ const previousRowIdentity = previousValue?.rowIdentities?.[previousValue.rowIdentities.length - 1];
350
+ const entry = structuralPath.rowScope?.entries[structuralPath.rowScope.entries.length - 1];
351
+ const matchedIndex = findRowIndexByIdentity(value, previousRowIdentity, entry?.filterTargetKey);
352
+ if (matchedIndex >= 0) {
353
+ return {
354
+ found: true,
355
+ value: matchedIndex,
356
+ rowIdentities: previousValue?.rowIdentities,
357
+ };
358
+ }
359
+
360
+ if (previousRowIdentity) {
361
+ return { found: false, rowMissing: true };
362
+ }
363
+
364
+ if (!entry || entry.index < 0 || entry.index >= value.length) {
365
+ return { found: false };
366
+ }
367
+
368
+ return {
369
+ found: true,
370
+ value: entry.index,
371
+ rowIdentities: [getRowIdentity(value[entry.index], entry.filterTargetKey)],
372
+ };
373
+ }
374
+
375
+ function getStructuralPathValue(
376
+ formBlock: any,
377
+ payload: unknown,
378
+ structuralPath: DataScopeStructuralPath,
379
+ previousValue?: DataScopeSnapshotValue,
380
+ changedPaths?: NamePath[],
381
+ ): { found: boolean; rowMissing?: boolean; value?: unknown; rowIdentities?: Array<RowIdentity | null> } {
382
+ if (structuralPath.kind === 'length') {
383
+ if (previousValue?.rowDetached) {
384
+ return { found: false, rowMissing: true };
385
+ }
386
+ let rowIdentities = previousValue?.rowIdentities;
387
+ const rowScope = structuralPath.rowScope;
388
+ if (rowScope) {
389
+ const allowIndexFallbackOnIdentityMiss = shouldRefreshRowIdentityFromPayload(
390
+ payload,
391
+ rowScope,
392
+ previousValue,
393
+ changedPaths,
394
+ );
395
+ const sourceCandidates = [getSnapshotSourceFromPayload(payload), formBlock?.context?.formValues];
396
+ for (const source of sourceCandidates) {
397
+ const scopedPath = getRowScopedPathFromSource(
398
+ source,
399
+ rowScope,
400
+ previousValue?.rowIdentities,
401
+ allowIndexFallbackOnIdentityMiss,
402
+ );
403
+ if (scopedPath.rowMissing) {
404
+ return { found: false, rowMissing: true };
405
+ }
406
+ if (scopedPath.found) {
407
+ rowIdentities = scopedPath.rowIdentities;
408
+ break;
409
+ }
410
+ }
411
+ }
412
+ const result = getPathValue(formBlock, payload, structuralPath.path);
413
+ return result.found ? { found: true, value: getStructuralLengthValue(result.value), rowIdentities } : result;
414
+ }
415
+ if (previousValue?.rowDetached) {
416
+ return { found: false, rowMissing: true };
417
+ }
418
+
419
+ const rowScope = structuralPath.rowScope;
420
+ if (rowScope) {
421
+ const allowIndexFallbackOnIdentityMiss = shouldRefreshRowIdentityFromPayload(
422
+ payload,
423
+ rowScope,
424
+ previousValue,
425
+ changedPaths,
426
+ );
427
+ const sourceCandidates = [getSnapshotSourceFromPayload(payload), formBlock?.context?.formValues];
428
+ for (const source of sourceCandidates) {
429
+ const scopedPath = getRowScopedPathFromSource(
430
+ source,
431
+ rowScope,
432
+ previousValue?.rowIdentities,
433
+ allowIndexFallbackOnIdentityMiss,
434
+ );
435
+ if (scopedPath.rowMissing) {
436
+ return { found: false, rowMissing: true };
437
+ }
438
+ if (scopedPath.found && typeof scopedPath.rowIndex === 'number') {
439
+ return {
440
+ found: true,
441
+ value: scopedPath.rowIndex,
442
+ rowIdentities: scopedPath.rowIdentities,
443
+ };
444
+ }
445
+ }
446
+ }
447
+
448
+ const result = getPathValue(formBlock, payload, structuralPath.path);
449
+ if (!result.found) {
450
+ return result;
451
+ }
452
+ return getStructuralIndexValueFromList(result.value, structuralPath, previousValue);
453
+ }
454
+
455
+ export function buildDepsSnapshot(
456
+ deps: DataScopeClearDeps,
457
+ formBlock: any,
458
+ payload?: unknown,
459
+ ): DataScopeClearSnapshot | null {
460
+ if (deps.wildcard) return null;
461
+
462
+ const snapshot: DataScopeClearSnapshot = {
463
+ complete: true,
464
+ rowDetached: false,
465
+ rowMissing: false,
466
+ values: new Map(),
467
+ structures: new Map(),
468
+ };
469
+
470
+ for (const valuePath of deps.valuePaths) {
471
+ const key = namePathToPathKey(valuePath.path);
472
+ const result = getRowScopedPathValue(formBlock, payload, valuePath);
473
+ if (result.rowMissing) {
474
+ snapshot.rowMissing = true;
475
+ snapshot.complete = false;
476
+ continue;
477
+ }
478
+ if (!result.found) {
479
+ snapshot.complete = false;
480
+ continue;
481
+ }
482
+ snapshot.values.set(key, {
483
+ value: cloneDeep(result.value),
484
+ rowIdentities: result.rowIdentities,
485
+ rowDetached: false,
486
+ });
487
+ }
488
+
489
+ for (const structuralPath of deps.structuralPaths) {
490
+ const result = getStructuralPathValue(formBlock, payload, structuralPath);
491
+ if (result.rowMissing) {
492
+ snapshot.rowMissing = true;
493
+ snapshot.complete = false;
494
+ continue;
495
+ }
496
+ if (!result.found) {
497
+ snapshot.complete = false;
498
+ continue;
499
+ }
500
+ snapshot.structures.set(`${structuralPath.kind}:${namePathToPathKey(structuralPath.path)}`, {
501
+ value: cloneDeep(result.value),
502
+ rowIdentities: result.rowIdentities,
503
+ rowDetached: false,
504
+ });
505
+ }
506
+
507
+ return snapshot;
508
+ }
509
+
510
+ function rebuildDepsSnapshot(
511
+ deps: DataScopeClearDeps,
512
+ formBlock: any,
513
+ payload: unknown,
514
+ previousSnapshot: DataScopeClearSnapshot,
515
+ changedPaths: NamePath[],
516
+ ): DataScopeClearSnapshot | null {
517
+ if (deps.wildcard) return null;
518
+
519
+ const snapshot: DataScopeClearSnapshot = {
520
+ complete: true,
521
+ rowDetached: false,
522
+ rowMissing: false,
523
+ values: new Map(),
524
+ structures: new Map(),
525
+ };
526
+
527
+ for (const valuePath of deps.valuePaths) {
528
+ const key = namePathToPathKey(valuePath.path);
529
+ const previousValue = previousSnapshot.values.get(key);
530
+ const result = getRowScopedPathValue(formBlock, payload, valuePath, previousValue, changedPaths);
531
+ if (result.rowMissing) {
532
+ snapshot.rowMissing = true;
533
+ snapshot.rowDetached = true;
534
+ if (previousValue) {
535
+ snapshot.values.set(key, { ...previousValue, rowDetached: true });
536
+ } else {
537
+ snapshot.complete = false;
538
+ }
539
+ continue;
540
+ }
541
+ if (!result.found) {
542
+ snapshot.complete = false;
543
+ continue;
544
+ }
545
+ snapshot.values.set(key, {
546
+ value: cloneDeep(result.value),
547
+ rowIdentities: result.rowIdentities ?? previousValue?.rowIdentities,
548
+ rowDetached: false,
549
+ });
550
+ }
551
+
552
+ for (const structuralPath of deps.structuralPaths) {
553
+ const key = `${structuralPath.kind}:${namePathToPathKey(structuralPath.path)}`;
554
+ const previousValue = previousSnapshot.structures.get(key);
555
+ const result = getStructuralPathValue(formBlock, payload, structuralPath, previousValue, changedPaths);
556
+ if (result.rowMissing) {
557
+ snapshot.rowMissing = true;
558
+ snapshot.rowDetached = true;
559
+ if (previousValue) {
560
+ snapshot.structures.set(key, { ...previousValue, rowDetached: true });
561
+ } else {
562
+ snapshot.complete = false;
563
+ }
564
+ continue;
565
+ }
566
+ if (!result.found) {
567
+ snapshot.complete = false;
568
+ continue;
569
+ }
570
+ snapshot.structures.set(key, {
571
+ value: cloneDeep(result.value),
572
+ rowIdentities: result.rowIdentities ?? previousValue?.rowIdentities,
573
+ rowDetached: false,
574
+ });
575
+ }
576
+
577
+ return snapshot;
578
+ }
579
+
580
+ function snapshotValuesEqual(prev: Map<string, DataScopeSnapshotValue>, next: Map<string, DataScopeSnapshotValue>) {
581
+ if (prev.size !== next.size) return false;
582
+ for (const [key, value] of prev.entries()) {
583
+ if (!next.has(key) || !isEqual(value.value, next.get(key)?.value)) {
584
+ return false;
585
+ }
586
+ }
587
+ return true;
588
+ }
589
+
590
+ export function getDepsChangeStatus(
591
+ deps: DataScopeClearDeps,
592
+ formBlock: any,
593
+ payload: unknown,
594
+ snapshot: DataScopeClearSnapshot | null,
595
+ ): DataScopeClearChangeStatus {
596
+ if (deps.wildcard || !snapshot?.complete) {
597
+ return { status: 'unknown', snapshot: buildDepsSnapshot(deps, formBlock, payload) };
598
+ }
599
+
600
+ const nextSnapshot = rebuildDepsSnapshot(deps, formBlock, payload, snapshot, getChangedPathsFromPayload(payload));
601
+ if (nextSnapshot?.rowMissing) {
602
+ return { status: 'unchanged', snapshot: { ...nextSnapshot, rowMissing: false } };
603
+ }
604
+ if (!nextSnapshot?.complete) {
605
+ return { status: 'unknown', snapshot: nextSnapshot };
606
+ }
607
+
608
+ const changed =
609
+ !snapshotValuesEqual(snapshot.values, nextSnapshot.values) ||
610
+ !snapshotValuesEqual(snapshot.structures, nextSnapshot.structures);
611
+
612
+ return {
613
+ status: changed ? 'changed' : 'unchanged',
614
+ snapshot: nextSnapshot,
615
+ };
616
+ }