@nocobase/flow-engine 2.2.0-alpha.7 → 2.2.0-alpha.8

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.
@@ -132,16 +132,18 @@ const VariableInputComponent = /* @__PURE__ */ __name(({
132
132
  },
133
133
  [onChange]
134
134
  );
135
+ const resolvedPath = (0, import_react.useMemo)(() => {
136
+ return resolvePathFromValue == null ? void 0 : resolvePathFromValue(innerValue);
137
+ }, [innerValue, resolvePathFromValue]);
135
138
  const resolvedMetaTreeNode = (0, import_react.useMemo)(() => {
136
139
  if (currentMetaTreeNode) return currentMetaTreeNode;
137
140
  if (Array.isArray(resolvedMetaTree)) {
138
- const path = resolvePathFromValue == null ? void 0 : resolvePathFromValue(innerValue);
139
- if (path) {
140
- return findMetaTreeNodeByPath(resolvedMetaTree, path);
141
+ if (resolvedPath) {
142
+ return findMetaTreeNodeByPath(resolvedMetaTree, resolvedPath);
141
143
  }
142
144
  }
143
145
  return null;
144
- }, [currentMetaTreeNode, innerValue, resolvedMetaTree, resolvePathFromValue]);
146
+ }, [currentMetaTreeNode, resolvedMetaTree, resolvedPath]);
145
147
  (0, import_react.useEffect)(() => {
146
148
  const restoreFromValue = /* @__PURE__ */ __name(async () => {
147
149
  if (!Array.isArray(resolvedMetaTree) || value == null) return;
@@ -192,9 +194,13 @@ const VariableInputComponent = /* @__PURE__ */ __name(({
192
194
  const ValueComponent = (0, import_react.useMemo)(() => {
193
195
  const Component = renderInputComponent == null ? void 0 : renderInputComponent(resolvedMetaTreeNode);
194
196
  const CustomComponent = resolvedMetaTreeNode == null ? void 0 : resolvedMetaTreeNode.render;
195
- const finalComponent = (0, import_utils.isVariableValue)(innerValue) ? import_VariableTag.VariableTag : Component || CustomComponent || import_antd.Input;
197
+ const shouldRenderVariableTag = (0, import_utils.isVariableValue)(innerValue) || Boolean(resolvedMetaTreeNode) && Array.isArray(resolvedPath) && resolvedPath.length > 0 && !Component && !CustomComponent;
198
+ const finalComponent = shouldRenderVariableTag ? import_VariableTag.VariableTag : Component || CustomComponent || import_antd.Input;
196
199
  return finalComponent;
197
- }, [renderInputComponent, resolvedMetaTreeNode, innerValue]);
200
+ }, [renderInputComponent, resolvedMetaTreeNode, innerValue, resolvedPath]);
201
+ const isVariableActive = (0, import_react.useMemo)(() => {
202
+ return (0, import_utils.isVariableValue)(innerValue) || Boolean(resolvedMetaTreeNode) && Array.isArray(resolvedPath) && resolvedPath.length > 0 && !(renderInputComponent == null ? void 0 : renderInputComponent(resolvedMetaTreeNode)) && !(resolvedMetaTreeNode == null ? void 0 : resolvedMetaTreeNode.render);
203
+ }, [innerValue, renderInputComponent, resolvedMetaTreeNode, resolvedPath]);
198
204
  (0, import_react.useEffect)(() => {
199
205
  if (!resolvedMetaTreeNode) return;
200
206
  if (!Array.isArray(resolvedMetaTree) || innerValue == null) return;
@@ -278,6 +284,8 @@ const VariableInputComponent = /* @__PURE__ */ __name(({
278
284
  return {
279
285
  ...baseProps,
280
286
  onClear: handleClear,
287
+ disabled,
288
+ allowCustomTagInput: false,
281
289
  metaTreeNode: resolvedMetaTreeNode,
282
290
  metaTree,
283
291
  style: stableProps.style
@@ -325,7 +333,7 @@ const VariableInputComponent = /* @__PURE__ */ __name(({
325
333
  {
326
334
  metaTree: resolvedMetaTree,
327
335
  value: innerValue,
328
- active: (0, import_utils.isVariableValue)(innerValue),
336
+ active: isVariableActive,
329
337
  disabled,
330
338
  onChange: handleVariableSelect,
331
339
  parseValueToPath: resolvePathFromValue,
@@ -47,9 +47,12 @@ var import_utils = require("./utils");
47
47
  var import_useResolvedMetaTree = require("./useResolvedMetaTree");
48
48
  var import_ahooks = require("ahooks");
49
49
  var import_FlowContextProvider = require("../../FlowContextProvider");
50
+ const VARIABLE_PARSING_FAILED_TEXT = "Variable parsing failed";
50
51
  const VariableTagComponent = /* @__PURE__ */ __name(({
51
52
  value,
52
53
  onClear,
54
+ disabled = false,
55
+ allowCustomTagInput = true,
53
56
  className,
54
57
  style,
55
58
  metaTreeNode,
@@ -57,25 +60,23 @@ const VariableTagComponent = /* @__PURE__ */ __name(({
57
60
  }) => {
58
61
  const { resolvedMetaTree } = (0, import_useResolvedMetaTree.useResolvedMetaTree)(metaTree);
59
62
  const ctx = (0, import_FlowContextProvider.useFlowContext)();
60
- const { data: displayedValue } = (0, import_ahooks.useRequest)(
63
+ const { data: displayState } = (0, import_ahooks.useRequest)(
61
64
  async () => {
62
65
  const resolveLabelFromPath = /* @__PURE__ */ __name(async (rawPath2) => {
63
- if (!rawPath2) return null;
64
- if (!Array.isArray(rawPath2)) return null;
65
- if (!Array.isArray(resolvedMetaTree)) return null;
66
+ if (!rawPath2) return { label: null, resolved: false, attempted: false };
67
+ if (!Array.isArray(rawPath2)) return { label: null, resolved: false, attempted: false };
68
+ if (!Array.isArray(resolvedMetaTree)) return { label: null, resolved: false, attempted: false };
66
69
  const topNames = new Set((resolvedMetaTree || []).map((n) => String(n == null ? void 0 : n.name)));
67
70
  const path = !topNames.has(String(rawPath2[0])) ? rawPath2.slice(1) : rawPath2;
68
- if (!path.length) return "";
71
+ if (!path.length) return { label: "", resolved: true, attempted: true };
69
72
  let nodes = resolvedMetaTree;
70
73
  const titleChain = [];
71
- let matchedCount = 0;
72
74
  for (let i = 0; i < path.length; i++) {
73
- if (!nodes) break;
75
+ if (!nodes) return { label: null, resolved: false, attempted: true };
74
76
  const seg = String(path[i]);
75
77
  const node = nodes.find((n) => String(n == null ? void 0 : n.name) === seg);
76
- if (!node) break;
78
+ if (!node) return { label: null, resolved: false, attempted: true };
77
79
  titleChain.push(String(node.title ?? node.name ?? seg));
78
- matchedCount = i + 1;
79
80
  if (i < path.length - 1) {
80
81
  if (Array.isArray(node.children)) {
81
82
  nodes = node.children;
@@ -92,27 +93,35 @@ const VariableTagComponent = /* @__PURE__ */ __name(({
92
93
  }
93
94
  }
94
95
  }
95
- if (matchedCount === 0) return null;
96
- let label2 = titleChain.map(ctx.t).join("/");
97
- if (matchedCount < path.length) {
98
- const tail = path.slice(matchedCount).join("/");
99
- label2 = tail ? `${label2}/${tail}` : label2;
100
- }
101
- return label2;
96
+ return { label: titleChain.map(ctx.t).join("/"), resolved: true, attempted: true };
102
97
  }, "resolveLabelFromPath");
103
98
  if (metaTreeNode == null ? void 0 : metaTreeNode.parentTitles) {
104
- return [...metaTreeNode.parentTitles, metaTreeNode.title].map(ctx.t).join("/");
99
+ return {
100
+ text: [...metaTreeNode.parentTitles, metaTreeNode.title].map(ctx.t).join("/"),
101
+ invalid: false
102
+ };
105
103
  }
106
104
  if (metaTreeNode) {
107
105
  const rawPath2 = (0, import_utils.parseValueToPath)(value) || metaTreeNode.paths;
108
- const label2 = await resolveLabelFromPath(rawPath2);
109
- return label2 ?? ctx.t(metaTreeNode.title) ?? "";
106
+ const result2 = await resolveLabelFromPath(rawPath2);
107
+ if (result2.resolved && result2.label != null) {
108
+ return { text: result2.label, invalid: false };
109
+ }
110
+ if (result2.attempted) {
111
+ return { text: VARIABLE_PARSING_FAILED_TEXT, invalid: true, rawValue: String(value ?? "") };
112
+ }
113
+ return { text: ctx.t(metaTreeNode.title) ?? "", invalid: false };
110
114
  }
111
- if (!value) return String(value);
115
+ if (!value) return { text: String(value), invalid: false };
112
116
  const rawPath = (0, import_utils.parseValueToPath)(value);
113
- const label = await resolveLabelFromPath(rawPath);
114
- if (label != null) return label;
115
- return Array.isArray(rawPath) ? rawPath.join("/") : String(value);
117
+ const result = await resolveLabelFromPath(rawPath);
118
+ if (result.resolved && result.label != null) {
119
+ return { text: result.label, invalid: false };
120
+ }
121
+ if (result.attempted) {
122
+ return { text: VARIABLE_PARSING_FAILED_TEXT, invalid: true, rawValue: String(value ?? "") };
123
+ }
124
+ return { text: Array.isArray(rawPath) ? rawPath.join("/") : String(value), invalid: false };
116
125
  },
117
126
  { refreshDeps: [resolvedMetaTree, value, metaTreeNode] }
118
127
  );
@@ -129,12 +138,12 @@ const VariableTagComponent = /* @__PURE__ */ __name(({
129
138
  }
130
139
  `;
131
140
  const customTagRender = /* @__PURE__ */ __name((props) => {
132
- const { label } = props;
133
- const fullText = typeof label === "string" ? label : String(label);
134
- return /* @__PURE__ */ import_react.default.createElement(import_antd.Tooltip, { title: fullText, placement: "top", getPopupContainer: () => document.body }, /* @__PURE__ */ import_react.default.createElement(
141
+ const fullText = (displayState == null ? void 0 : displayState.text) || (typeof props.label === "string" ? props.label : String(props.label));
142
+ const tooltipText = (displayState == null ? void 0 : displayState.invalid) ? displayState.rawValue || fullText : fullText;
143
+ return /* @__PURE__ */ import_react.default.createElement(import_antd.Tooltip, { title: tooltipText, placement: "top", getPopupContainer: () => document.body }, /* @__PURE__ */ import_react.default.createElement(
135
144
  import_antd.Tag,
136
145
  {
137
- color: "blue",
146
+ color: (displayState == null ? void 0 : displayState.invalid) ? "error" : "blue",
138
147
  style: {
139
148
  margin: `0 ${token.marginXXS || token.marginXS}px`,
140
149
  borderRadius: token.borderRadiusSM,
@@ -175,12 +184,14 @@ const VariableTagComponent = /* @__PURE__ */ __name(({
175
184
  flex: "1 1 auto",
176
185
  ...style
177
186
  },
178
- value: displayedValue ? [displayedValue] : [],
179
- mode: "tags",
187
+ value: (displayState == null ? void 0 : displayState.text) ? [displayState.text] : [],
188
+ mode: allowCustomTagInput ? "tags" : "multiple",
180
189
  open: false,
181
- allowClear: !!onClear,
182
- onClear,
183
- disabled: !onClear,
190
+ showSearch: allowCustomTagInput,
191
+ searchValue: allowCustomTagInput ? void 0 : "",
192
+ allowClear: !disabled && !!onClear,
193
+ onClear: disabled ? void 0 : onClear,
194
+ disabled: disabled || !onClear,
184
195
  variant: "outlined",
185
196
  suffixIcon: null,
186
197
  tagRender: customTagRender,
@@ -87,6 +87,8 @@ export interface VariableInputProps {
87
87
  export interface VariableTagProps {
88
88
  value?: string;
89
89
  onClear?: () => void;
90
+ disabled?: boolean;
91
+ allowCustomTagInput?: boolean;
90
92
  className?: string;
91
93
  style?: React.CSSProperties;
92
94
  metaTreeNode?: MetaTreeNode | null;
@@ -31,8 +31,10 @@ __export(dirtyAwareApiClient_exports, {
31
31
  getDirtyAwareApiClient: () => getDirtyAwareApiClient
32
32
  });
33
33
  module.exports = __toCommonJS(dirtyAwareApiClient_exports);
34
+ var import_sdk = require("@nocobase/sdk");
34
35
  var import_dataSourceDirty = require("./dataSourceDirty");
35
36
  const SKIP_DATA_SOURCE_DIRTY = "__nocobaseSkipDataSourceDirty";
37
+ const DIRTY_DISPATCH_TOKEN = Symbol("nocobaseDirtyDispatchToken");
36
38
  const dirtyAwareApiClientCache = /* @__PURE__ */ new WeakMap();
37
39
  const dirtyAwareApiClientProxies = /* @__PURE__ */ new WeakSet();
38
40
  const MUTATING_RESOURCE_ACTIONS = [
@@ -288,6 +290,43 @@ function resolveDirtyResourceAction(options, context) {
288
290
  return parseDirtyResourceActionFromUrl(options == null ? void 0 : options.url, context);
289
291
  }
290
292
  __name(resolveDirtyResourceAction, "resolveDirtyResourceAction");
293
+ function getDirtyResourceActionDispatchKey(dirtyResourceAction, headers) {
294
+ if (!dirtyResourceAction) {
295
+ return void 0;
296
+ }
297
+ return JSON.stringify([
298
+ dirtyResourceAction.dataSourceKey || (0, import_dataSourceDirty.getDataSourceKeyFromHeaders)(headers),
299
+ dirtyResourceAction.resourceName,
300
+ dirtyResourceAction.actionName
301
+ ]);
302
+ }
303
+ __name(getDirtyResourceActionDispatchKey, "getDirtyResourceActionDispatchKey");
304
+ function getRequestDispatchKey(options, context) {
305
+ return getDirtyResourceActionDispatchKey(resolveDirtyResourceAction(options, context), options.headers);
306
+ }
307
+ __name(getRequestDispatchKey, "getRequestDispatchKey");
308
+ function getResourceDispatchKey(name, of, headers) {
309
+ const resourceName = String(name ?? "").trim();
310
+ if (!resourceName) {
311
+ return void 0;
312
+ }
313
+ return JSON.stringify([resourceName, String(of ?? ""), (0, import_dataSourceDirty.getDataSourceKeyFromHeaders)(headers)]);
314
+ }
315
+ __name(getResourceDispatchKey, "getResourceDispatchKey");
316
+ function getMatchingRequestToken(token, requestKey) {
317
+ if (!token || !requestKey || token.requestKey && token.requestKey !== requestKey) {
318
+ return void 0;
319
+ }
320
+ return token;
321
+ }
322
+ __name(getMatchingRequestToken, "getMatchingRequestToken");
323
+ function getMatchingResourceToken(token, resourceKey) {
324
+ if (!token || !resourceKey || token.resourceKey && token.resourceKey !== resourceKey) {
325
+ return void 0;
326
+ }
327
+ return token;
328
+ }
329
+ __name(getMatchingResourceToken, "getMatchingResourceToken");
291
330
  function markResourceActionDataSourceDirty(context, dirtyResourceAction, headers) {
292
331
  (0, import_dataSourceDirty.markDataSourceDirty)({
293
332
  engine: context.engine,
@@ -297,7 +336,19 @@ function markResourceActionDataSourceDirty(context, dirtyResourceAction, headers
297
336
  });
298
337
  }
299
338
  __name(markResourceActionDataSourceDirty, "markResourceActionDataSourceDirty");
300
- function createDirtyAwareResource(context, resource, resourceName, resourceOf, headers) {
339
+ function markResourceActionDataSourceDirtyOnce(token, context, dirtyResourceAction, headers) {
340
+ if (token.skip || token.marked || !dirtyResourceAction || !isMutatingResourceAction(dirtyResourceAction.actionName)) {
341
+ return;
342
+ }
343
+ token.marked = true;
344
+ markResourceActionDataSourceDirty(context, dirtyResourceAction, headers);
345
+ }
346
+ __name(markResourceActionDataSourceDirtyOnce, "markResourceActionDataSourceDirtyOnce");
347
+ function isObjectRecord(value) {
348
+ return !!value && typeof value === "object";
349
+ }
350
+ __name(isObjectRecord, "isObjectRecord");
351
+ function createDirtyAwareResource(context, resource, resourceName, resourceOf, headers, requestTokenStack, parentToken) {
301
352
  return new Proxy(resource, {
302
353
  get(target, prop, receiver) {
303
354
  const original = Reflect.get(target, prop, receiver);
@@ -306,10 +357,38 @@ function createDirtyAwareResource(context, resource, resourceName, resourceOf, h
306
357
  }
307
358
  const action = original;
308
359
  return async (...args) => {
309
- const result = await action(...args);
360
+ const actionOptions = isObjectRecord(args[1]) ? args[1] : void 0;
310
361
  const dirtyResourceAction = resolveDirtyResourceActionFromResource(resourceName, resourceOf, prop, context);
311
- if (dirtyResourceAction) {
312
- markResourceActionDataSourceDirty(context, dirtyResourceAction, headers);
362
+ const requestKey = getDirtyResourceActionDispatchKey(dirtyResourceAction, headers);
363
+ const resourceKey = getResourceDispatchKey(resourceName, resourceOf, headers);
364
+ const inheritedToken = getMatchingRequestToken(
365
+ actionOptions == null ? void 0 : actionOptions[DIRTY_DISPATCH_TOKEN],
366
+ requestKey
367
+ ) || getMatchingRequestToken(parentToken, requestKey);
368
+ const token = inheritedToken || { marked: false, skip: false };
369
+ const ownsToken = !inheritedToken;
370
+ token.requestKey ||= requestKey;
371
+ token.resourceKey ||= resourceKey;
372
+ if (actionOptions == null ? void 0 : actionOptions[SKIP_DATA_SOURCE_DIRTY]) {
373
+ token.skip = true;
374
+ }
375
+ const forwardedArgs = actionOptions || args[1] == null ? [
376
+ args[0],
377
+ {
378
+ ...actionOptions,
379
+ [DIRTY_DISPATCH_TOKEN]: token
380
+ }
381
+ ] : args;
382
+ let actionResult;
383
+ requestTokenStack.push({ key: requestKey, token });
384
+ try {
385
+ actionResult = Reflect.apply(action, receiver, forwardedArgs);
386
+ } finally {
387
+ requestTokenStack.pop();
388
+ }
389
+ const result = await actionResult;
390
+ if (ownsToken) {
391
+ markResourceActionDataSourceDirtyOnce(token, context, dirtyResourceAction, headers);
313
392
  }
314
393
  return result;
315
394
  };
@@ -318,31 +397,206 @@ function createDirtyAwareResource(context, resource, resourceName, resourceOf, h
318
397
  }
319
398
  __name(createDirtyAwareResource, "createDirtyAwareResource");
320
399
  function createDirtyAwareApiClient(api, context) {
400
+ const baseResource = api.resource;
401
+ const baseRequest = api.request;
402
+ const shouldUseResourceDispatchReceiver = baseResource === import_sdk.APIClient.prototype.resource;
403
+ const shouldUseRequestDispatchReceiver = baseRequest === import_sdk.APIClient.prototype.request;
404
+ const resourceTokenStack = [];
405
+ const requestTokenStack = [];
406
+ let hasResourceOverride = false;
407
+ let resourceOverride;
408
+ let hasRequestOverride = false;
409
+ let requestOverride;
410
+ const getCurrentResource = /* @__PURE__ */ __name(() => hasResourceOverride ? resourceOverride : resource, "getCurrentResource");
411
+ const getCurrentRequest = /* @__PURE__ */ __name(() => hasRequestOverride ? requestOverride : request, "getCurrentRequest");
412
+ const dispatchResource = /* @__PURE__ */ __name((token, args) => {
413
+ const resourceKey = getResourceDispatchKey(args[0], args[1], args[2]);
414
+ const activeToken = getMatchingResourceToken(token, resourceKey);
415
+ const shouldWrapActions = !!activeToken && hasResourceOverride;
416
+ if (activeToken) {
417
+ activeToken.resourceKey ||= resourceKey;
418
+ resourceTokenStack.push({ key: resourceKey, token: activeToken });
419
+ }
420
+ try {
421
+ const resourceInstance = Reflect.apply(getCurrentResource(), proxy, args);
422
+ if (!shouldWrapActions || !activeToken) {
423
+ return resourceInstance;
424
+ }
425
+ return new Proxy(resourceInstance, {
426
+ get(target, prop, receiver) {
427
+ const original = Reflect.get(target, prop, receiver);
428
+ if (typeof prop !== "string" || typeof original !== "function" || !isMutatingResourceAction(prop)) {
429
+ return original;
430
+ }
431
+ const action = original;
432
+ return (...actionArgs) => {
433
+ const actionOptions = isObjectRecord(actionArgs[1]) ? actionArgs[1] : void 0;
434
+ const forwardedArgs = actionOptions || actionArgs[1] == null ? [
435
+ actionArgs[0],
436
+ {
437
+ ...actionOptions,
438
+ [DIRTY_DISPATCH_TOKEN]: activeToken
439
+ }
440
+ ] : actionArgs;
441
+ resourceTokenStack.push({ key: resourceKey, token: activeToken });
442
+ requestTokenStack.push({ key: activeToken.requestKey, token: activeToken });
443
+ try {
444
+ return Reflect.apply(action, receiver, forwardedArgs);
445
+ } finally {
446
+ requestTokenStack.pop();
447
+ resourceTokenStack.pop();
448
+ }
449
+ };
450
+ }
451
+ });
452
+ } finally {
453
+ if (activeToken) {
454
+ resourceTokenStack.pop();
455
+ }
456
+ }
457
+ }, "dispatchResource");
458
+ const createDispatchReceiver = /* @__PURE__ */ __name((token) => {
459
+ const receiver = Object.create(api);
460
+ Object.defineProperties(receiver, {
461
+ request: {
462
+ configurable: true,
463
+ value: /* @__PURE__ */ __name((config) => {
464
+ const options = config;
465
+ const requestKey = getRequestDispatchKey(options, context);
466
+ const stackFrame = requestTokenStack.at(-1);
467
+ const activeToken = getMatchingRequestToken(options == null ? void 0 : options[DIRTY_DISPATCH_TOKEN], requestKey) || (requestKey && (stackFrame == null ? void 0 : stackFrame.key) === requestKey ? stackFrame.token : void 0) || getMatchingRequestToken(token, requestKey);
468
+ const { [DIRTY_DISPATCH_TOKEN]: _dirtyDispatchToken, ...cleanOptions } = options;
469
+ const configWithToken = activeToken ? { ...cleanOptions, [DIRTY_DISPATCH_TOKEN]: activeToken } : cleanOptions;
470
+ if (activeToken) {
471
+ activeToken.requestKey ||= requestKey;
472
+ requestTokenStack.push({ key: requestKey, token: activeToken });
473
+ }
474
+ try {
475
+ return Reflect.apply(getCurrentRequest(), proxy, [configWithToken]);
476
+ } finally {
477
+ if (activeToken) {
478
+ requestTokenStack.pop();
479
+ }
480
+ }
481
+ }, "value")
482
+ },
483
+ resource: {
484
+ configurable: true,
485
+ value: /* @__PURE__ */ __name((...args) => {
486
+ const resourceKey = getResourceDispatchKey(args[0], args[1], args[2]);
487
+ const stackFrame = resourceTokenStack.at(-1);
488
+ const activeToken = getMatchingResourceToken(token, resourceKey) || (resourceKey && (stackFrame == null ? void 0 : stackFrame.key) === resourceKey ? stackFrame.token : void 0);
489
+ return dispatchResource(activeToken, args);
490
+ }, "value")
491
+ }
492
+ });
493
+ return receiver;
494
+ }, "createDispatchReceiver");
321
495
  const resource = /* @__PURE__ */ __name((name, of, headers, cancel) => {
322
- const targetResource = api.resource(name, of, headers, cancel);
323
- return createDirtyAwareResource(context, targetResource, name, of, headers);
496
+ const resourceKey = getResourceDispatchKey(name, of, headers);
497
+ const stackFrame = resourceTokenStack.at(-1);
498
+ const parentToken = resourceKey && (stackFrame == null ? void 0 : stackFrame.key) === resourceKey ? stackFrame.token : void 0;
499
+ const receiver = createDispatchReceiver(parentToken);
500
+ const resourceInstance = Reflect.apply(baseResource, shouldUseResourceDispatchReceiver ? receiver : api, [
501
+ name,
502
+ of,
503
+ headers,
504
+ cancel
505
+ ]);
506
+ return createDirtyAwareResource(context, resourceInstance, name, of, headers, requestTokenStack, parentToken);
324
507
  }, "resource");
325
508
  const request = /* @__PURE__ */ __name((config) => {
326
509
  const options = config;
510
+ const requestKey = getRequestDispatchKey(options, context);
511
+ const stackFrame = requestTokenStack.at(-1);
512
+ const inheritedToken = getMatchingRequestToken(options == null ? void 0 : options[DIRTY_DISPATCH_TOKEN], requestKey) || (requestKey && (stackFrame == null ? void 0 : stackFrame.key) === requestKey ? stackFrame.token : void 0);
513
+ const token = inheritedToken || { marked: false, skip: false };
514
+ const ownsToken = !inheritedToken;
515
+ token.requestKey ||= requestKey;
516
+ if (typeof options.resource === "string") {
517
+ token.resourceKey ||= getResourceDispatchKey(options.resource, options.resourceOf, options.headers);
518
+ }
327
519
  const skipDataSourceDirty = options == null ? void 0 : options[SKIP_DATA_SOURCE_DIRTY];
328
- const dirtyResourceAction = skipDataSourceDirty ? void 0 : resolveDirtyResourceAction(options, context);
329
- const { [SKIP_DATA_SOURCE_DIRTY]: _skipDataSourceDirty, ...cleanConfig } = options;
330
- return api.request(cleanConfig).then((result) => {
331
- if (dirtyResourceAction && isMutatingResourceAction(dirtyResourceAction.actionName)) {
332
- markResourceActionDataSourceDirty(context, dirtyResourceAction, options.headers);
520
+ if (skipDataSourceDirty) {
521
+ token.skip = true;
522
+ }
523
+ const dirtyResourceAction = resolveDirtyResourceAction(options, context);
524
+ const {
525
+ [DIRTY_DISPATCH_TOKEN]: _dirtyDispatchToken,
526
+ [SKIP_DATA_SOURCE_DIRTY]: _skipDataSourceDirty,
527
+ ...cleanConfig
528
+ } = options;
529
+ const receiver = createDispatchReceiver(token);
530
+ return Reflect.apply(baseRequest, shouldUseRequestDispatchReceiver ? receiver : api, [cleanConfig]).then((result) => {
531
+ if (ownsToken) {
532
+ markResourceActionDataSourceDirtyOnce(token, context, dirtyResourceAction, options.headers);
333
533
  }
334
534
  return result;
335
535
  });
336
536
  }, "request");
537
+ const isLockedOwnProperty = /* @__PURE__ */ __name((target, prop) => {
538
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
539
+ return !!descriptor && !descriptor.configurable;
540
+ }, "isLockedOwnProperty");
337
541
  const proxy = new Proxy(api, {
338
542
  get(target, prop, receiver) {
339
543
  if (prop === "resource") {
340
- return resource;
544
+ if (isLockedOwnProperty(target, prop)) {
545
+ return Reflect.get(target, prop, receiver);
546
+ }
547
+ return hasResourceOverride ? resourceOverride : resource;
341
548
  }
342
549
  if (prop === "request") {
343
- return request;
550
+ if (isLockedOwnProperty(target, prop)) {
551
+ return Reflect.get(target, prop, receiver);
552
+ }
553
+ return hasRequestOverride ? requestOverride : request;
344
554
  }
345
555
  return Reflect.get(target, prop, receiver);
556
+ },
557
+ set(target, prop, value, receiver) {
558
+ if (prop === "resource") {
559
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
560
+ if (descriptor && (!descriptor.configurable || !Reflect.isExtensible(target))) {
561
+ return false;
562
+ }
563
+ hasResourceOverride = true;
564
+ resourceOverride = value;
565
+ return true;
566
+ }
567
+ if (prop === "request") {
568
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
569
+ if (descriptor && (!descriptor.configurable || !Reflect.isExtensible(target))) {
570
+ return false;
571
+ }
572
+ hasRequestOverride = true;
573
+ requestOverride = value;
574
+ return true;
575
+ }
576
+ return Reflect.set(target, prop, value, receiver);
577
+ },
578
+ deleteProperty(target, prop) {
579
+ if (prop !== "resource" && prop !== "request") {
580
+ return Reflect.deleteProperty(target, prop);
581
+ }
582
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
583
+ if (descriptor && (!descriptor.configurable || !Reflect.isExtensible(target))) {
584
+ return false;
585
+ }
586
+ if (prop === "resource") {
587
+ hasResourceOverride = false;
588
+ resourceOverride = void 0;
589
+ } else {
590
+ hasRequestOverride = false;
591
+ requestOverride = void 0;
592
+ }
593
+ return true;
594
+ },
595
+ defineProperty(target, prop, descriptor) {
596
+ if (prop === "resource" || prop === "request") {
597
+ return false;
598
+ }
599
+ return Reflect.defineProperty(target, prop, descriptor);
346
600
  }
347
601
  });
348
602
  dirtyAwareApiClientProxies.add(proxy);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/flow-engine",
3
- "version": "2.2.0-alpha.7",
3
+ "version": "2.2.0-alpha.8",
4
4
  "private": false,
5
5
  "description": "A standalone flow engine for NocoBase, managing workflows, models, and actions.",
6
6
  "main": "lib/index.js",
@@ -8,8 +8,8 @@
8
8
  "dependencies": {
9
9
  "@formily/antd-v5": "1.x",
10
10
  "@formily/reactive": "2.x",
11
- "@nocobase/sdk": "2.2.0-alpha.7",
12
- "@nocobase/shared": "2.2.0-alpha.7",
11
+ "@nocobase/sdk": "2.2.0-alpha.8",
12
+ "@nocobase/shared": "2.2.0-alpha.8",
13
13
  "ahooks": "^3.7.2",
14
14
  "axios": "^1.7.0",
15
15
  "dayjs": "^1.11.9",
@@ -37,5 +37,5 @@
37
37
  ],
38
38
  "author": "NocoBase Team",
39
39
  "license": "Apache-2.0",
40
- "gitHead": "34ba02960fca9ab0b6881d3db7040a8a64067bba"
40
+ "gitHead": "810c81e5963966bda7ec03b6453a3a80fe60e027"
41
41
  }
@@ -125,16 +125,19 @@ const VariableInputComponent: React.FC<VariableInputProps> = ({
125
125
  [onChange],
126
126
  );
127
127
 
128
+ const resolvedPath = useMemo(() => {
129
+ return resolvePathFromValue?.(innerValue);
130
+ }, [innerValue, resolvePathFromValue]);
131
+
128
132
  const resolvedMetaTreeNode = useMemo(() => {
129
133
  if (currentMetaTreeNode) return currentMetaTreeNode;
130
134
  if (Array.isArray(resolvedMetaTree)) {
131
- const path = resolvePathFromValue?.(innerValue);
132
- if (path) {
133
- return findMetaTreeNodeByPath(resolvedMetaTree, path);
135
+ if (resolvedPath) {
136
+ return findMetaTreeNodeByPath(resolvedMetaTree, resolvedPath);
134
137
  }
135
138
  }
136
139
  return null;
137
- }, [currentMetaTreeNode, innerValue, resolvedMetaTree, resolvePathFromValue]);
140
+ }, [currentMetaTreeNode, resolvedMetaTree, resolvedPath]);
138
141
 
139
142
  // 当 value 存在但 currentMetaTreeNode 还未恢复,尝试按路径逐级加载(支持 children 为函数的场景)
140
143
  useEffect(() => {
@@ -197,9 +200,31 @@ const VariableInputComponent: React.FC<VariableInputProps> = ({
197
200
  const ValueComponent = useMemo(() => {
198
201
  const Component = renderInputComponent?.(resolvedMetaTreeNode);
199
202
  const CustomComponent = resolvedMetaTreeNode?.render;
200
- const finalComponent = isVariableValue(innerValue) ? VariableTag : Component || CustomComponent || Input;
203
+ // Some domains (workflow) persist variables as `{{$context...}}` rather than the core `{{ ctx... }}` form.
204
+ // Those values are not recognized by `isVariableValue`, but if the active converters can resolve them back to a
205
+ // real meta-tree path and that path maps to a plain variable node (not Constant / Null / RunJS), they should still
206
+ // render as the labelled pill instead of falling back to the raw input text.
207
+ const shouldRenderVariableTag =
208
+ isVariableValue(innerValue) ||
209
+ (Boolean(resolvedMetaTreeNode) &&
210
+ Array.isArray(resolvedPath) &&
211
+ resolvedPath.length > 0 &&
212
+ !Component &&
213
+ !CustomComponent);
214
+ const finalComponent = shouldRenderVariableTag ? VariableTag : Component || CustomComponent || Input;
201
215
  return finalComponent;
202
- }, [renderInputComponent, resolvedMetaTreeNode, innerValue]);
216
+ }, [renderInputComponent, resolvedMetaTreeNode, innerValue, resolvedPath]);
217
+
218
+ const isVariableActive = useMemo(() => {
219
+ return (
220
+ isVariableValue(innerValue) ||
221
+ (Boolean(resolvedMetaTreeNode) &&
222
+ Array.isArray(resolvedPath) &&
223
+ resolvedPath.length > 0 &&
224
+ !renderInputComponent?.(resolvedMetaTreeNode) &&
225
+ !resolvedMetaTreeNode?.render)
226
+ );
227
+ }, [innerValue, renderInputComponent, resolvedMetaTreeNode, resolvedPath]);
203
228
 
204
229
  useEffect(() => {
205
230
  if (!resolvedMetaTreeNode) return;
@@ -303,6 +328,8 @@ const VariableInputComponent: React.FC<VariableInputProps> = ({
303
328
  return {
304
329
  ...baseProps,
305
330
  onClear: handleClear,
331
+ disabled,
332
+ allowCustomTagInput: false,
306
333
  metaTreeNode: resolvedMetaTreeNode,
307
334
  metaTree,
308
335
  style: stableProps.style,
@@ -356,7 +383,7 @@ const VariableInputComponent: React.FC<VariableInputProps> = ({
356
383
  <FlowContextSelector
357
384
  metaTree={resolvedMetaTree}
358
385
  value={innerValue}
359
- active={isVariableValue(innerValue)}
386
+ active={isVariableActive}
360
387
  disabled={disabled}
361
388
  onChange={handleVariableSelect}
362
389
  parseValueToPath={resolvePathFromValue}