@nocobase/flow-engine 2.0.0-beta.22 → 2.0.0-beta.23

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 (44) hide show
  1. package/lib/FlowDefinition.d.ts +2 -0
  2. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +66 -13
  3. package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +24 -4
  4. package/lib/components/variables/VariableInput.js +1 -2
  5. package/lib/components/variables/VariableTag.js +46 -39
  6. package/lib/data-source/index.js +11 -5
  7. package/lib/flowContext.js +3 -0
  8. package/lib/flowI18n.js +6 -4
  9. package/lib/locale/en-US.json +2 -1
  10. package/lib/locale/index.d.ts +2 -0
  11. package/lib/locale/zh-CN.json +1 -0
  12. package/lib/types.d.ts +12 -0
  13. package/lib/utils/associationObjectVariable.d.ts +2 -2
  14. package/lib/utils/index.d.ts +4 -2
  15. package/lib/utils/index.js +16 -0
  16. package/lib/utils/resolveRunJSObjectValues.d.ts +16 -0
  17. package/lib/utils/resolveRunJSObjectValues.js +61 -0
  18. package/lib/utils/runjsValue.d.ts +29 -0
  19. package/lib/utils/runjsValue.js +275 -0
  20. package/lib/utils/safeGlobals.d.ts +14 -0
  21. package/lib/utils/safeGlobals.js +35 -2
  22. package/lib/utils/schema-utils.d.ts +10 -0
  23. package/lib/utils/schema-utils.js +61 -0
  24. package/package.json +4 -4
  25. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +98 -13
  26. package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +41 -7
  27. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +94 -1
  28. package/src/components/variables/VariableInput.tsx +4 -2
  29. package/src/components/variables/VariableTag.tsx +54 -45
  30. package/src/components/variables/__tests__/VariableTag.test.tsx +50 -0
  31. package/src/data-source/index.ts +11 -5
  32. package/src/flowContext.ts +4 -1
  33. package/src/flowI18n.ts +7 -5
  34. package/src/locale/en-US.json +2 -1
  35. package/src/locale/zh-CN.json +1 -0
  36. package/src/types.ts +12 -0
  37. package/src/utils/__tests__/runjsValue.test.ts +44 -0
  38. package/src/utils/__tests__/utils.test.ts +95 -0
  39. package/src/utils/associationObjectVariable.ts +2 -2
  40. package/src/utils/index.ts +20 -2
  41. package/src/utils/resolveRunJSObjectValues.ts +46 -0
  42. package/src/utils/runjsValue.ts +287 -0
  43. package/src/utils/safeGlobals.ts +52 -0
  44. package/src/utils/schema-utils.ts +79 -0
@@ -352,3 +352,55 @@ export function createSafeNavigator(extra?: Record<string, any>) {
352
352
  },
353
353
  );
354
354
  }
355
+
356
+ /**
357
+ * Create a safe globals object for RunJS execution.
358
+ *
359
+ * - Always tries to provide `navigator`
360
+ * - Best-effort provides `window` and `document` in browser environments
361
+ * - Never throws (so callers can decide how to handle missing globals)
362
+ */
363
+ export function createSafeRunJSGlobals(extraGlobals?: Record<string, any>): Record<string, any> {
364
+ const globals: Record<string, any> = {};
365
+
366
+ try {
367
+ const navigator = createSafeNavigator();
368
+ globals.navigator = navigator;
369
+ try {
370
+ globals.window = createSafeWindow({ navigator });
371
+ } catch {
372
+ // ignore when window is not available (e.g. SSR/tests)
373
+ }
374
+ } catch {
375
+ // ignore
376
+ }
377
+
378
+ try {
379
+ globals.document = createSafeDocument();
380
+ } catch {
381
+ // ignore when document is not available (e.g. SSR/tests)
382
+ }
383
+
384
+ return extraGlobals ? { ...globals, ...extraGlobals } : globals;
385
+ }
386
+
387
+ /**
388
+ * Execute RunJS with safe globals (window/document/navigator).
389
+ *
390
+ * Keeps `this` binding by calling `ctx.runjs(...)` instead of passing bare function references.
391
+ */
392
+ export async function runjsWithSafeGlobals(
393
+ ctx: unknown,
394
+ code: string,
395
+ options?: any,
396
+ extraGlobals?: Record<string, any>,
397
+ ): Promise<any> {
398
+ if (!ctx || (typeof ctx !== 'object' && typeof ctx !== 'function')) return undefined;
399
+ const runjs = (ctx as { runjs?: unknown }).runjs;
400
+ if (typeof runjs !== 'function') return undefined;
401
+ return (ctx as { runjs: (code: string, variables?: Record<string, any>, options?: any) => Promise<any> }).runjs(
402
+ code,
403
+ createSafeRunJSGlobals(extraGlobals),
404
+ options,
405
+ );
406
+ }
@@ -277,3 +277,82 @@ export async function shouldHideStepInSettings<TModel extends FlowModel = FlowMo
277
277
 
278
278
  return !!hideInSettings;
279
279
  }
280
+
281
+ /**
282
+ * 解析步骤在设置菜单中的禁用状态与提示文案。
283
+ * - 支持 StepDefinition.disabledInSettings 与 ActionDefinition.disabledInSettings(step 优先)。
284
+ * - 支持 StepDefinition.disabledReasonInSettings 与 ActionDefinition.disabledReasonInSettings(step 优先)。
285
+ * - 以上属性均支持静态值与函数(接收 FlowRuntimeContext)。
286
+ */
287
+ export async function resolveStepDisabledInSettings<TModel extends FlowModel = FlowModel>(
288
+ model: TModel,
289
+ flow: any,
290
+ step: StepDefinition,
291
+ ): Promise<{ disabled: boolean; reason?: string }> {
292
+ if (!step) return { disabled: false };
293
+
294
+ let disabledInSettings = step.disabledInSettings;
295
+ let disabledReasonInSettings = step.disabledReasonInSettings;
296
+
297
+ if ((typeof disabledInSettings === 'undefined' || typeof disabledReasonInSettings === 'undefined') && step.use) {
298
+ try {
299
+ const action = model.getAction?.(step.use);
300
+ if (typeof disabledInSettings === 'undefined') {
301
+ disabledInSettings = action?.disabledInSettings;
302
+ }
303
+ if (typeof disabledReasonInSettings === 'undefined') {
304
+ disabledReasonInSettings = action?.disabledReasonInSettings;
305
+ }
306
+ } catch (error) {
307
+ console.warn(`Failed to get action ${step.use}:`, error);
308
+ }
309
+ }
310
+
311
+ let ctx: FlowRuntimeContext<TModel> | null = null;
312
+ const getContext = () => {
313
+ if (ctx) return ctx;
314
+ ctx = new FlowRuntimeContext(model, flow.key, 'settings');
315
+ setupRuntimeContextSteps(ctx, flow.steps, model, flow.key);
316
+ ctx.defineProperty('currentStep', { value: step });
317
+ return ctx;
318
+ };
319
+
320
+ let disabled = false;
321
+ if (typeof disabledInSettings === 'function') {
322
+ try {
323
+ disabled = !!(await disabledInSettings(getContext() as any));
324
+ } catch (error) {
325
+ console.warn(`Error evaluating disabledInSettings for step '${step.key || ''}' in flow '${flow.key}':`, error);
326
+ return { disabled: false };
327
+ }
328
+ } else {
329
+ disabled = !!disabledInSettings;
330
+ }
331
+
332
+ if (!disabled) {
333
+ return { disabled: false };
334
+ }
335
+
336
+ let reason: string | undefined;
337
+ if (typeof disabledReasonInSettings === 'function') {
338
+ try {
339
+ const resolved = await disabledReasonInSettings(getContext() as any);
340
+ if (typeof resolved !== 'undefined' && resolved !== null && resolved !== '') {
341
+ reason = String(resolved);
342
+ }
343
+ } catch (error) {
344
+ console.warn(
345
+ `Error evaluating disabledReasonInSettings for step '${step.key || ''}' in flow '${flow.key}':`,
346
+ error,
347
+ );
348
+ }
349
+ } else if (
350
+ typeof disabledReasonInSettings !== 'undefined' &&
351
+ disabledReasonInSettings !== null &&
352
+ disabledReasonInSettings !== ''
353
+ ) {
354
+ reason = String(disabledReasonInSettings);
355
+ }
356
+
357
+ return { disabled: true, reason };
358
+ }