@nocobase/flow-engine 2.0.0-beta.2 → 2.0.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.
Files changed (124) hide show
  1. package/lib/BlockScopedFlowEngine.js +0 -1
  2. package/lib/JSRunner.d.ts +6 -0
  3. package/lib/JSRunner.js +2 -1
  4. package/lib/ViewScopedFlowEngine.js +3 -0
  5. package/lib/acl/Acl.js +13 -3
  6. package/lib/components/dnd/gridDragPlanner.d.ts +1 -0
  7. package/lib/components/dnd/gridDragPlanner.js +53 -1
  8. package/lib/components/settings/wrappers/component/SwitchWithTitle.js +2 -1
  9. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +11 -3
  10. package/lib/components/variables/VariableInput.js +8 -2
  11. package/lib/data-source/index.js +6 -0
  12. package/lib/executor/FlowExecutor.d.ts +2 -1
  13. package/lib/executor/FlowExecutor.js +156 -22
  14. package/lib/flowContext.d.ts +4 -1
  15. package/lib/flowContext.js +176 -107
  16. package/lib/flowEngine.d.ts +21 -0
  17. package/lib/flowEngine.js +38 -0
  18. package/lib/flowSettings.js +12 -10
  19. package/lib/index.d.ts +3 -0
  20. package/lib/index.js +16 -0
  21. package/lib/models/CollectionFieldModel.d.ts +1 -0
  22. package/lib/models/CollectionFieldModel.js +3 -2
  23. package/lib/models/flowModel.d.ts +7 -0
  24. package/lib/models/flowModel.js +66 -1
  25. package/lib/provider.js +7 -6
  26. package/lib/resources/baseRecordResource.d.ts +5 -0
  27. package/lib/resources/baseRecordResource.js +24 -0
  28. package/lib/resources/multiRecordResource.d.ts +1 -0
  29. package/lib/resources/multiRecordResource.js +11 -4
  30. package/lib/resources/singleRecordResource.js +2 -0
  31. package/lib/resources/sqlResource.d.ts +1 -0
  32. package/lib/resources/sqlResource.js +8 -3
  33. package/lib/runjs-context/contexts/base.js +10 -4
  34. package/lib/runjsLibs.d.ts +28 -0
  35. package/lib/runjsLibs.js +532 -0
  36. package/lib/scheduler/ModelOperationScheduler.d.ts +2 -0
  37. package/lib/scheduler/ModelOperationScheduler.js +21 -21
  38. package/lib/types.d.ts +15 -0
  39. package/lib/utils/createCollectionContextMeta.js +1 -0
  40. package/lib/utils/index.d.ts +2 -0
  41. package/lib/utils/index.js +10 -0
  42. package/lib/utils/params-resolvers.js +16 -9
  43. package/lib/utils/resolveModuleUrl.d.ts +58 -0
  44. package/lib/utils/resolveModuleUrl.js +65 -0
  45. package/lib/utils/runjsModuleLoader.d.ts +58 -0
  46. package/lib/utils/runjsModuleLoader.js +422 -0
  47. package/lib/utils/runjsTemplateCompat.d.ts +35 -0
  48. package/lib/utils/runjsTemplateCompat.js +743 -0
  49. package/lib/utils/safeGlobals.d.ts +5 -9
  50. package/lib/utils/safeGlobals.js +129 -17
  51. package/lib/views/createViewMeta.d.ts +0 -7
  52. package/lib/views/createViewMeta.js +19 -70
  53. package/lib/views/index.d.ts +1 -2
  54. package/lib/views/index.js +4 -3
  55. package/lib/views/useDialog.js +8 -3
  56. package/lib/views/useDrawer.js +7 -2
  57. package/lib/views/usePage.d.ts +4 -0
  58. package/lib/views/usePage.js +43 -6
  59. package/lib/views/usePopover.js +4 -1
  60. package/lib/views/viewEvents.d.ts +17 -0
  61. package/lib/views/viewEvents.js +90 -0
  62. package/package.json +4 -4
  63. package/src/BlockScopedFlowEngine.ts +2 -5
  64. package/src/JSRunner.ts +8 -1
  65. package/src/ViewScopedFlowEngine.ts +4 -0
  66. package/src/__tests__/createViewMeta.popup.test.ts +62 -1
  67. package/src/__tests__/flowEngine.dataSourceDirty.test.ts +63 -0
  68. package/src/__tests__/flowSettings.open.test.tsx +69 -15
  69. package/src/__tests__/provider.test.tsx +0 -5
  70. package/src/__tests__/runjsExternalLibs.test.ts +242 -0
  71. package/src/__tests__/runjsLibsLazyLoading.test.ts +44 -0
  72. package/src/__tests__/runjsPreprocessDefault.test.ts +49 -0
  73. package/src/acl/Acl.tsx +3 -3
  74. package/src/components/__tests__/gridDragPlanner.test.ts +141 -1
  75. package/src/components/dnd/gridDragPlanner.ts +60 -0
  76. package/src/components/settings/wrappers/component/SwitchWithTitle.tsx +2 -1
  77. package/src/components/settings/wrappers/component/__tests__/InlineControls.test.tsx +74 -0
  78. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +11 -3
  79. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +63 -4
  80. package/src/components/variables/VariableInput.tsx +8 -2
  81. package/src/data-source/index.ts +6 -0
  82. package/src/executor/FlowExecutor.ts +193 -23
  83. package/src/executor/__tests__/flowExecutor.test.ts +66 -0
  84. package/src/flowContext.ts +234 -118
  85. package/src/flowEngine.ts +41 -0
  86. package/src/flowSettings.ts +12 -11
  87. package/src/index.ts +10 -0
  88. package/src/models/CollectionFieldModel.tsx +3 -1
  89. package/src/models/__tests__/dispatchEvent.when.test.ts +356 -0
  90. package/src/models/__tests__/flowModel.clone.test.ts +416 -0
  91. package/src/models/__tests__/flowModel.test.ts +16 -0
  92. package/src/models/flowModel.tsx +94 -1
  93. package/src/provider.tsx +9 -7
  94. package/src/resources/__tests__/multiRecordResource.test.ts +44 -0
  95. package/src/resources/__tests__/sqlResource.test.ts +60 -0
  96. package/src/resources/baseRecordResource.ts +31 -0
  97. package/src/resources/multiRecordResource.ts +11 -4
  98. package/src/resources/singleRecordResource.ts +3 -0
  99. package/src/resources/sqlResource.ts +8 -3
  100. package/src/runjs-context/contexts/base.ts +9 -2
  101. package/src/runjsLibs.ts +622 -0
  102. package/src/scheduler/ModelOperationScheduler.ts +23 -21
  103. package/src/types.ts +26 -1
  104. package/src/utils/__tests__/params-resolvers.test.ts +40 -0
  105. package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +38 -0
  106. package/src/utils/__tests__/runjsTemplateCompat.test.ts +159 -0
  107. package/src/utils/__tests__/safeGlobals.test.ts +49 -2
  108. package/src/utils/createCollectionContextMeta.ts +1 -0
  109. package/src/utils/index.ts +6 -0
  110. package/src/utils/params-resolvers.ts +23 -9
  111. package/src/utils/resolveModuleUrl.ts +91 -0
  112. package/src/utils/runjsModuleLoader.ts +553 -0
  113. package/src/utils/runjsTemplateCompat.ts +828 -0
  114. package/src/utils/safeGlobals.ts +133 -16
  115. package/src/views/__tests__/FlowView.usePage.test.tsx +54 -1
  116. package/src/views/__tests__/useDialog.closeDestroy.test.tsx +35 -8
  117. package/src/views/__tests__/viewEvents.resolveOpenerEngine.test.ts +28 -0
  118. package/src/views/createViewMeta.ts +22 -75
  119. package/src/views/index.tsx +1 -2
  120. package/src/views/useDialog.tsx +9 -2
  121. package/src/views/useDrawer.tsx +8 -1
  122. package/src/views/usePage.tsx +51 -5
  123. package/src/views/usePopover.tsx +4 -1
  124. package/src/views/viewEvents.ts +55 -0
@@ -12,6 +12,7 @@ import _ from 'lodash';
12
12
  import { APIResource } from './apiResource';
13
13
  import { FilterItem } from './filterItem';
14
14
  import { ResourceError } from './flowResource';
15
+ import { DATA_SOURCE_DIRTY_EVENT } from '../views/viewEvents';
15
16
 
16
17
  export abstract class BaseRecordResource<TData = any> extends APIResource<TData> {
17
18
  protected resourceName: string;
@@ -136,6 +137,36 @@ export abstract class BaseRecordResource<TData = any> extends APIResource<TData>
136
137
  return this.resourceName;
137
138
  }
138
139
 
140
+ /**
141
+ * Mark current resource as dirty on the root FlowEngine.
142
+ * Used to coordinate "refresh on active" across view stacks.
143
+ */
144
+ protected markDataSourceDirty(resourceName?: string) {
145
+ const engine = this.context.engine;
146
+ if (!engine) return;
147
+
148
+ const dataSourceKey = this.getDataSourceKey() || 'main';
149
+ const resName = resourceName || this.getResourceName();
150
+ if (!resName) return;
151
+
152
+ const affectedResourceNames = new Set<string>([String(resName)]);
153
+ // Optional safety: association resources like "users.profile" may impact parent collection views.
154
+ if (typeof resName === 'string' && resName.includes('.')) {
155
+ affectedResourceNames.add(resName.split('.')[0]);
156
+ }
157
+
158
+ for (const name of affectedResourceNames) {
159
+ engine.markDataSourceDirty(dataSourceKey, name);
160
+ }
161
+
162
+ // Signal current view to re-evaluate dirty blocks (e.g., same-view sibling refresh).
163
+ // This is emitted on the *current* engine emitter (view-scoped) so it won't affect other views.
164
+ engine.emitter?.emit?.(DATA_SOURCE_DIRTY_EVENT, {
165
+ dataSourceKey,
166
+ resourceNames: Array.from(affectedResourceNames),
167
+ });
168
+ }
169
+
139
170
  setSourceId(sourceId: string | number) {
140
171
  this.sourceId = sourceId;
141
172
  return this;
@@ -16,6 +16,7 @@ export class MultiRecordResource<TDataItem = any> extends BaseRecordResource<TDa
16
16
  protected _data = observable.ref<TDataItem[]>([]);
17
17
  protected _meta = observable.ref<Record<string, any>>({});
18
18
  private refreshTimer: NodeJS.Timeout | null = null;
19
+ private refreshWaiters: Array<{ resolve: () => void; reject: (error: any) => void }> = [];
19
20
  protected createActionOptions = {};
20
21
  protected updateActionOptions = {};
21
22
  protected _refreshActionName = 'list';
@@ -113,6 +114,7 @@ export class MultiRecordResource<TDataItem = any> extends BaseRecordResource<TDa
113
114
  async create(data: TDataItem, options?: AxiosRequestConfig & { refresh?: boolean }): Promise<void> {
114
115
  const config = this.mergeRequestConfig({ data }, this.createActionOptions, options);
115
116
  const res = await this.runAction('create', config);
117
+ this.markDataSourceDirty();
116
118
  this.emit('saved', data);
117
119
  if (options?.refresh !== false) {
118
120
  await this.refresh();
@@ -145,6 +147,7 @@ export class MultiRecordResource<TDataItem = any> extends BaseRecordResource<TDa
145
147
  options,
146
148
  );
147
149
  await this.runAction('update', config);
150
+ this.markDataSourceDirty();
148
151
  this.emit('saved', data);
149
152
  await this.refresh();
150
153
  }
@@ -170,6 +173,7 @@ export class MultiRecordResource<TDataItem = any> extends BaseRecordResource<TDa
170
173
  options,
171
174
  );
172
175
  await this.runAction('destroy', config);
176
+ this.markDataSourceDirty();
173
177
  const currentPage = this.getPage();
174
178
  const lastPage = Math.ceil((this.getCount() - _.castArray(filterByTk).length) / this.getPageSize());
175
179
  if (currentPage > lastPage) {
@@ -197,7 +201,11 @@ export class MultiRecordResource<TDataItem = any> extends BaseRecordResource<TDa
197
201
 
198
202
  // 设置新的定时器,在下一个事件循环执行
199
203
  return new Promise<void>((resolve, reject) => {
204
+ this.refreshWaiters.push({ resolve, reject });
200
205
  this.refreshTimer = setTimeout(async () => {
206
+ const waiters = this.refreshWaiters;
207
+ this.refreshWaiters = [];
208
+ this.refreshTimer = null;
201
209
  try {
202
210
  this.clearError();
203
211
  this.loading = true;
@@ -213,13 +221,12 @@ export class MultiRecordResource<TDataItem = any> extends BaseRecordResource<TDa
213
221
  this.setPageSize(meta.pageSize);
214
222
  }
215
223
  this.emit('refresh');
216
- this.loading = false;
217
- resolve();
224
+ waiters.forEach((w) => w.resolve());
218
225
  } catch (error) {
219
226
  this.setError(error);
220
- reject(error instanceof Error ? error : new Error(String(error)));
227
+ const err = error instanceof Error ? error : new Error(String(error));
228
+ waiters.forEach((w) => w.reject(err));
221
229
  } finally {
222
- this.refreshTimer = null;
223
230
  this.loading = false;
224
231
  }
225
232
  });
@@ -44,6 +44,8 @@ export class SingleRecordResource<TData = any> extends BaseRecordResource<TData>
44
44
  ...config,
45
45
  data: result,
46
46
  });
47
+ // Mark as dirty before emitting/refreshing so other views can refresh when activated.
48
+ this.markDataSourceDirty();
47
49
  this.emit('saved', data);
48
50
  if (options?.refresh !== false) {
49
51
  await this.refresh();
@@ -61,6 +63,7 @@ export class SingleRecordResource<TData = any> extends BaseRecordResource<TData>
61
63
  options,
62
64
  );
63
65
  await this.runAction('destroy', config);
66
+ this.markDataSourceDirty();
64
67
  this.setData(null);
65
68
  }
66
69
 
@@ -110,6 +110,7 @@ export class SQLResource<TData = any> extends BaseRecordResource<TData> {
110
110
  protected _data = observable.ref<TData>(null);
111
111
  protected _meta = observable.ref<Record<string, any>>({});
112
112
  private refreshTimer: NodeJS.Timeout | null = null;
113
+ private refreshWaiters: Array<{ resolve: () => void; reject: (error: any) => void }> = [];
113
114
  private _debugEnabled = false;
114
115
  private _sql: string;
115
116
 
@@ -272,7 +273,11 @@ export class SQLResource<TData = any> extends BaseRecordResource<TData> {
272
273
 
273
274
  // 设置新的定时器,在下一个事件循环执行
274
275
  return new Promise<void>((resolve, reject) => {
276
+ this.refreshWaiters.push({ resolve, reject });
275
277
  this.refreshTimer = setTimeout(async () => {
278
+ const waiters = this.refreshWaiters;
279
+ this.refreshWaiters = [];
280
+ this.refreshTimer = null;
276
281
  try {
277
282
  this.clearError();
278
283
  this.loading = true;
@@ -281,12 +286,12 @@ export class SQLResource<TData = any> extends BaseRecordResource<TData> {
281
286
  this.setData(data).setMeta(meta);
282
287
  this.loading = false;
283
288
  this.emit('refresh');
284
- resolve();
289
+ waiters.forEach((w) => w.resolve());
285
290
  } catch (error) {
286
291
  this.setError(error);
287
- reject(error instanceof Error ? error : new Error(String(error)));
292
+ const err = error instanceof Error ? error : new Error(String(error));
293
+ waiters.forEach((w) => w.reject(err));
288
294
  } finally {
289
- this.refreshTimer = null;
290
295
  this.loading = false;
291
296
  }
292
297
  });
@@ -50,7 +50,8 @@ export function defineBaseContextMeta() {
50
50
  'ReactDOM client API including createRoot for rendering React components. Also available via `ctx.libs.ReactDOM`.',
51
51
  antd: 'Ant Design component library. Recommended access path: `ctx.libs.antd`.',
52
52
  libs: {
53
- description: 'Namespace for third-party and shared libraries. Includes React, ReactDOM, Ant Design, and dayjs.',
53
+ description:
54
+ 'Namespace for third-party and shared libraries. Includes React, ReactDOM, Ant Design, dayjs, lodash, math.js, and formula.js.',
54
55
  detail: 'Libraries namespace',
55
56
  properties: {
56
57
  React: 'React namespace (same as ctx.React).',
@@ -58,6 +59,9 @@ export function defineBaseContextMeta() {
58
59
  antd: 'Ant Design component library (same as ctx.antd).',
59
60
  dayjs: 'dayjs date-time utility library.',
60
61
  antdIcons: 'Ant Design icons library. Example: `ctx.libs.antdIcons.PlusOutlined`.',
62
+ lodash: 'Lodash utility library. Example: `ctx.libs.lodash.get(obj, "a.b.c")`.',
63
+ math: 'Math.js library for mathematical operations. Example: `ctx.libs.math.evaluate("2 + 3 * 4")`.',
64
+ formula: 'Formula.js library for spreadsheet-like formulas. Example: `ctx.libs.formula.SUM([1, 2, 3])`.',
61
65
  },
62
66
  },
63
67
  },
@@ -147,7 +151,7 @@ export function defineBaseContextMeta() {
147
151
  antd: 'Ant Design 组件库(RunJS 环境中可用)。推荐使用 `ctx.libs.antd` 访问。',
148
152
  libs: {
149
153
  description:
150
- '第三方/通用库的统一命名空间,包含 React、ReactDOM、Ant Design、dayjs 等。后续新增库会优先挂在此处。',
154
+ '第三方/通用库的统一命名空间,包含 React、ReactDOM、Ant Design、dayjs、lodash、math.js、formula.js 等。后续新增库会优先挂在此处。',
151
155
  detail: '通用库命名空间',
152
156
  properties: {
153
157
  React: 'React 命名空间(等价于 ctx.React)。',
@@ -155,6 +159,9 @@ export function defineBaseContextMeta() {
155
159
  antd: 'Ant Design 组件库(等价于 ctx.antd)。',
156
160
  dayjs: 'dayjs 日期时间工具库。',
157
161
  antdIcons: 'Ant Design 图标库。 例如:`ctx.libs.antdIcons.PlusOutlined`。',
162
+ lodash: 'Lodash 工具库。例如:`ctx.libs.lodash.get(obj, "a.b.c")`。',
163
+ math: 'Math.js 数学运算库。例如:`ctx.libs.math.evaluate("2 + 3 * 4")`。',
164
+ formula: 'Formula.js 电子表格公式库。例如:`ctx.libs.formula.SUM([1, 2, 3])`。',
158
165
  },
159
166
  },
160
167
  },