@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.
- package/lib/BlockScopedFlowEngine.js +0 -1
- package/lib/JSRunner.d.ts +6 -0
- package/lib/JSRunner.js +2 -1
- package/lib/ViewScopedFlowEngine.js +3 -0
- package/lib/acl/Acl.js +13 -3
- package/lib/components/dnd/gridDragPlanner.d.ts +1 -0
- package/lib/components/dnd/gridDragPlanner.js +53 -1
- package/lib/components/settings/wrappers/component/SwitchWithTitle.js +2 -1
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +11 -3
- package/lib/components/variables/VariableInput.js +8 -2
- package/lib/data-source/index.js +6 -0
- package/lib/executor/FlowExecutor.d.ts +2 -1
- package/lib/executor/FlowExecutor.js +156 -22
- package/lib/flowContext.d.ts +4 -1
- package/lib/flowContext.js +176 -107
- package/lib/flowEngine.d.ts +21 -0
- package/lib/flowEngine.js +38 -0
- package/lib/flowSettings.js +12 -10
- package/lib/index.d.ts +3 -0
- package/lib/index.js +16 -0
- package/lib/models/CollectionFieldModel.d.ts +1 -0
- package/lib/models/CollectionFieldModel.js +3 -2
- package/lib/models/flowModel.d.ts +7 -0
- package/lib/models/flowModel.js +66 -1
- package/lib/provider.js +7 -6
- package/lib/resources/baseRecordResource.d.ts +5 -0
- package/lib/resources/baseRecordResource.js +24 -0
- package/lib/resources/multiRecordResource.d.ts +1 -0
- package/lib/resources/multiRecordResource.js +11 -4
- package/lib/resources/singleRecordResource.js +2 -0
- package/lib/resources/sqlResource.d.ts +1 -0
- package/lib/resources/sqlResource.js +8 -3
- package/lib/runjs-context/contexts/base.js +10 -4
- package/lib/runjsLibs.d.ts +28 -0
- package/lib/runjsLibs.js +532 -0
- package/lib/scheduler/ModelOperationScheduler.d.ts +2 -0
- package/lib/scheduler/ModelOperationScheduler.js +21 -21
- package/lib/types.d.ts +15 -0
- package/lib/utils/createCollectionContextMeta.js +1 -0
- package/lib/utils/index.d.ts +2 -0
- package/lib/utils/index.js +10 -0
- package/lib/utils/params-resolvers.js +16 -9
- package/lib/utils/resolveModuleUrl.d.ts +58 -0
- package/lib/utils/resolveModuleUrl.js +65 -0
- package/lib/utils/runjsModuleLoader.d.ts +58 -0
- package/lib/utils/runjsModuleLoader.js +422 -0
- package/lib/utils/runjsTemplateCompat.d.ts +35 -0
- package/lib/utils/runjsTemplateCompat.js +743 -0
- package/lib/utils/safeGlobals.d.ts +5 -9
- package/lib/utils/safeGlobals.js +129 -17
- package/lib/views/createViewMeta.d.ts +0 -7
- package/lib/views/createViewMeta.js +19 -70
- package/lib/views/index.d.ts +1 -2
- package/lib/views/index.js +4 -3
- package/lib/views/useDialog.js +8 -3
- package/lib/views/useDrawer.js +7 -2
- package/lib/views/usePage.d.ts +4 -0
- package/lib/views/usePage.js +43 -6
- package/lib/views/usePopover.js +4 -1
- package/lib/views/viewEvents.d.ts +17 -0
- package/lib/views/viewEvents.js +90 -0
- package/package.json +4 -4
- package/src/BlockScopedFlowEngine.ts +2 -5
- package/src/JSRunner.ts +8 -1
- package/src/ViewScopedFlowEngine.ts +4 -0
- package/src/__tests__/createViewMeta.popup.test.ts +62 -1
- package/src/__tests__/flowEngine.dataSourceDirty.test.ts +63 -0
- package/src/__tests__/flowSettings.open.test.tsx +69 -15
- package/src/__tests__/provider.test.tsx +0 -5
- package/src/__tests__/runjsExternalLibs.test.ts +242 -0
- package/src/__tests__/runjsLibsLazyLoading.test.ts +44 -0
- package/src/__tests__/runjsPreprocessDefault.test.ts +49 -0
- package/src/acl/Acl.tsx +3 -3
- package/src/components/__tests__/gridDragPlanner.test.ts +141 -1
- package/src/components/dnd/gridDragPlanner.ts +60 -0
- package/src/components/settings/wrappers/component/SwitchWithTitle.tsx +2 -1
- package/src/components/settings/wrappers/component/__tests__/InlineControls.test.tsx +74 -0
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +11 -3
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +63 -4
- package/src/components/variables/VariableInput.tsx +8 -2
- package/src/data-source/index.ts +6 -0
- package/src/executor/FlowExecutor.ts +193 -23
- package/src/executor/__tests__/flowExecutor.test.ts +66 -0
- package/src/flowContext.ts +234 -118
- package/src/flowEngine.ts +41 -0
- package/src/flowSettings.ts +12 -11
- package/src/index.ts +10 -0
- package/src/models/CollectionFieldModel.tsx +3 -1
- package/src/models/__tests__/dispatchEvent.when.test.ts +356 -0
- package/src/models/__tests__/flowModel.clone.test.ts +416 -0
- package/src/models/__tests__/flowModel.test.ts +16 -0
- package/src/models/flowModel.tsx +94 -1
- package/src/provider.tsx +9 -7
- package/src/resources/__tests__/multiRecordResource.test.ts +44 -0
- package/src/resources/__tests__/sqlResource.test.ts +60 -0
- package/src/resources/baseRecordResource.ts +31 -0
- package/src/resources/multiRecordResource.ts +11 -4
- package/src/resources/singleRecordResource.ts +3 -0
- package/src/resources/sqlResource.ts +8 -3
- package/src/runjs-context/contexts/base.ts +9 -2
- package/src/runjsLibs.ts +622 -0
- package/src/scheduler/ModelOperationScheduler.ts +23 -21
- package/src/types.ts +26 -1
- package/src/utils/__tests__/params-resolvers.test.ts +40 -0
- package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +38 -0
- package/src/utils/__tests__/runjsTemplateCompat.test.ts +159 -0
- package/src/utils/__tests__/safeGlobals.test.ts +49 -2
- package/src/utils/createCollectionContextMeta.ts +1 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/params-resolvers.ts +23 -9
- package/src/utils/resolveModuleUrl.ts +91 -0
- package/src/utils/runjsModuleLoader.ts +553 -0
- package/src/utils/runjsTemplateCompat.ts +828 -0
- package/src/utils/safeGlobals.ts +133 -16
- package/src/views/__tests__/FlowView.usePage.test.tsx +54 -1
- package/src/views/__tests__/useDialog.closeDestroy.test.tsx +35 -8
- package/src/views/__tests__/viewEvents.resolveOpenerEngine.test.ts +28 -0
- package/src/views/createViewMeta.ts +22 -75
- package/src/views/index.tsx +1 -2
- package/src/views/useDialog.tsx +9 -2
- package/src/views/useDrawer.tsx +8 -1
- package/src/views/usePage.tsx +51 -5
- package/src/views/usePopover.tsx +4 -1
- 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
|
-
|
|
217
|
-
resolve();
|
|
224
|
+
waiters.forEach((w) => w.resolve());
|
|
218
225
|
} catch (error) {
|
|
219
226
|
this.setError(error);
|
|
220
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
},
|