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

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 (45) hide show
  1. package/lib/JSRunner.js +23 -1
  2. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +3 -3
  3. package/lib/data-source/index.d.ts +7 -27
  4. package/lib/data-source/index.js +67 -46
  5. package/lib/flowContext.d.ts +62 -0
  6. package/lib/flowContext.js +92 -3
  7. package/lib/flowEngine.js +18 -8
  8. package/lib/index.d.ts +4 -1
  9. package/lib/index.js +5 -0
  10. package/lib/resources/sqlResource.d.ts +3 -3
  11. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +12 -2
  12. package/lib/runjs-context/contexts/JSBlockRunJSContext.js +2 -2
  13. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.d.ts +16 -0
  14. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +125 -0
  15. package/lib/runjs-context/contexts/JSItemRunJSContext.js +12 -2
  16. package/lib/runjs-context/contexts/base.js +691 -23
  17. package/lib/runjs-context/contributions.d.ts +33 -0
  18. package/lib/runjs-context/contributions.js +88 -0
  19. package/lib/runjs-context/setup.js +6 -0
  20. package/lib/runjs-context/snippets/index.d.ts +11 -1
  21. package/lib/runjs-context/snippets/index.js +61 -40
  22. package/lib/utils/safeGlobals.js +2 -0
  23. package/package.json +4 -4
  24. package/src/JSRunner.ts +29 -1
  25. package/src/__tests__/JSRunner.test.ts +64 -0
  26. package/src/__tests__/flowContext.test.ts +90 -0
  27. package/src/__tests__/flowModel.openView.navigation.test.ts +28 -0
  28. package/src/__tests__/runjsContext.test.ts +4 -1
  29. package/src/__tests__/runjsLocales.test.ts +4 -1
  30. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +3 -3
  31. package/src/data-source/index.ts +71 -105
  32. package/src/flowContext.ts +160 -2
  33. package/src/flowEngine.ts +18 -8
  34. package/src/index.ts +4 -1
  35. package/src/resources/sqlResource.ts +3 -3
  36. package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +10 -0
  37. package/src/runjs-context/contexts/JSBlockRunJSContext.ts +6 -2
  38. package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +106 -0
  39. package/src/runjs-context/contexts/JSItemRunJSContext.ts +10 -0
  40. package/src/runjs-context/contexts/base.ts +698 -30
  41. package/src/runjs-context/contributions.ts +88 -0
  42. package/src/runjs-context/setup.ts +6 -0
  43. package/src/runjs-context/snippets/index.ts +75 -41
  44. package/src/utils/__tests__/safeGlobals.test.ts +8 -0
  45. package/src/utils/safeGlobals.ts +3 -1
package/lib/JSRunner.js CHANGED
@@ -35,6 +35,7 @@ const _JSRunner = class _JSRunner {
35
35
  globals;
36
36
  timeoutMs;
37
37
  constructor(options = {}) {
38
+ var _a, _b;
38
39
  const bindWindowFn = /* @__PURE__ */ __name((key) => {
39
40
  if (typeof window !== "undefined" && typeof window[key] === "function") {
40
41
  return window[key].bind(window);
@@ -42,13 +43,34 @@ const _JSRunner = class _JSRunner {
42
43
  const fn = globalThis[key];
43
44
  return typeof fn === "function" ? fn.bind(globalThis) : fn;
44
45
  }, "bindWindowFn");
46
+ const providedGlobals = options.globals || {};
47
+ const liftedGlobals = {};
48
+ if (!Object.prototype.hasOwnProperty.call(providedGlobals, "Blob")) {
49
+ try {
50
+ const blobCtor = (_a = providedGlobals.window) == null ? void 0 : _a.Blob;
51
+ if (typeof blobCtor !== "undefined") {
52
+ liftedGlobals.Blob = blobCtor;
53
+ }
54
+ } catch {
55
+ }
56
+ }
57
+ if (!Object.prototype.hasOwnProperty.call(providedGlobals, "URL")) {
58
+ try {
59
+ const urlCtor = (_b = providedGlobals.window) == null ? void 0 : _b.URL;
60
+ if (typeof urlCtor !== "undefined") {
61
+ liftedGlobals.URL = urlCtor;
62
+ }
63
+ } catch {
64
+ }
65
+ }
45
66
  this.globals = {
46
67
  console,
47
68
  setTimeout: bindWindowFn("setTimeout"),
48
69
  clearTimeout: bindWindowFn("clearTimeout"),
49
70
  setInterval: bindWindowFn("setInterval"),
50
71
  clearInterval: bindWindowFn("clearInterval"),
51
- ...options.globals || {}
72
+ ...liftedGlobals,
73
+ ...providedGlobals
52
74
  };
53
75
  this.timeoutMs = options.timeoutMs ?? 5e3;
54
76
  }
@@ -521,7 +521,7 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
521
521
  };
522
522
  items.push({
523
523
  key: uniqueKey,
524
- label: /* @__PURE__ */ import_react.default.createElement(MenuLabelItem, { title: t(stepInfo.title), uiMode, itemProps })
524
+ label: /* @__PURE__ */ import_react.default.createElement(MenuLabelItem, { title: stepInfo.title, uiMode, itemProps })
525
525
  });
526
526
  });
527
527
  if (flow.options.divider === "bottom") {
@@ -555,7 +555,7 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
555
555
  const uniqueKey = generateUniqueKey(`${flow.key}:${stepInfo.stepKey}`);
556
556
  items.push({
557
557
  key: uniqueKey,
558
- label: t(stepInfo.title)
558
+ label: stepInfo.title
559
559
  });
560
560
  });
561
561
  });
@@ -567,7 +567,7 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
567
567
  const uniqueKey = generateUniqueKey(`${modelKey}:${flow.key}:${stepInfo.stepKey}`);
568
568
  subMenuChildren.push({
569
569
  key: uniqueKey,
570
- label: t(stepInfo.title)
570
+ label: stepInfo.title
571
571
  });
572
572
  });
573
573
  });
@@ -66,6 +66,11 @@ export interface CollectionOptions {
66
66
  export declare class CollectionManager {
67
67
  dataSource: DataSource;
68
68
  collections: Map<string, Collection>;
69
+ allCollectionsInheritChain: string[];
70
+ protected childrenCollectionsName: {
71
+ supportView?: string[];
72
+ notSupportView?: string[];
73
+ };
69
74
  constructor(dataSource: DataSource);
70
75
  get flowEngine(): FlowEngine;
71
76
  addCollection(collection: Collection | CollectionOptions): void;
@@ -83,33 +88,8 @@ export declare class CollectionManager {
83
88
  clearCollections(): void;
84
89
  getAssociation(associationName: string): CollectionField | undefined;
85
90
  getChildrenCollections(name: any): any[];
86
- getCollectionFieldsOptions(collectionName: string, type?: string | string[], interfaces?: string | string[], opts?: {
87
- dataSource?: string;
88
- cached?: Record<string, any>;
89
- collectionNames?: string[];
90
- /**
91
- * 为 true 时允许查询所有关联字段
92
- * 为 Array<string> 时仅允许查询指定的关联字段
93
- */
94
- association?: boolean | string[];
95
- /**
96
- * Max depth of recursion
97
- */
98
- maxDepth?: number;
99
- allowAllTypes?: boolean;
100
- /**
101
- * 排除这些接口的字段
102
- */
103
- exceptInterfaces?: string[];
104
- /**
105
- * field value 的前缀,用 . 连接,比如 a.b.c
106
- */
107
- prefixFieldValue?: string;
108
- /**
109
- * 是否使用 prefixFieldValue 作为 field value
110
- */
111
- usePrefix?: boolean;
112
- }): any;
91
+ getChildrenCollectionsName(name: any, isSupportView?: boolean): string[];
92
+ getAllCollectionsInheritChain(name: any): string[];
113
93
  }
114
94
  export declare class Collection {
115
95
  fields: Map<string, CollectionField>;
@@ -188,6 +188,8 @@ const _CollectionManager = class _CollectionManager {
188
188
  this.collections = import_reactive.observable.shallow(/* @__PURE__ */ new Map());
189
189
  }
190
190
  collections;
191
+ allCollectionsInheritChain;
192
+ childrenCollectionsName = {};
191
193
  get flowEngine() {
192
194
  return this.dataSource.flowEngine;
193
195
  }
@@ -317,56 +319,75 @@ const _CollectionManager = class _CollectionManager {
317
319
  }, "getChildrens");
318
320
  return getChildrens(name);
319
321
  }
320
- getCollectionFieldsOptions(collectionName, type, interfaces, opts) {
321
- var _a;
322
- const {
323
- association = false,
324
- cached = {},
325
- collectionNames = [collectionName],
326
- maxDepth = 1,
327
- allowAllTypes = false,
328
- exceptInterfaces = [],
329
- prefixFieldValue = "",
330
- usePrefix = false,
331
- dataSource: customDataSourceNameValue
332
- } = opts || {};
333
- if (collectionNames.length - 1 > maxDepth) {
334
- return;
335
- }
336
- if (cached[collectionName]) {
337
- return import_lodash.default.cloneDeep(cached[collectionName]);
322
+ getChildrenCollectionsName(name, isSupportView = false) {
323
+ const cacheKey = isSupportView ? "supportView" : "notSupportView";
324
+ if (this.childrenCollectionsName[cacheKey]) {
325
+ return this.childrenCollectionsName[cacheKey].slice();
338
326
  }
339
- const collection = this.getCollection(collectionName);
340
- if (!collection) {
341
- throw new Error(`Collection ${collectionName} not found`);
342
- }
343
- const fields = collection.getFields();
344
- const options = (_a = fields == null ? void 0 : fields.filter(
345
- (field) => field.interface && !exceptInterfaces.includes(field.interface) && (allowAllTypes || type && type.includes(field.type) || interfaces && interfaces.includes(field.interface) || (association && field.target && field.target !== collectionName && Array.isArray(association) ? association.includes(field.interface) : false))
346
- )) == null ? void 0 : _a.map((field) => {
347
- var _a2, _b;
348
- const result = {
349
- value: usePrefix && prefixFieldValue ? `${prefixFieldValue}.${field.name}` : field.name,
350
- label: ((_a2 = field == null ? void 0 : field.uiSchema) == null ? void 0 : _a2.title) || field.name,
351
- ...field
352
- };
353
- if (association && field.target) {
354
- result.children = collectionNames.includes(field.target) ? [] : this.getCollectionFieldsOptions(field.target, type, interfaces, {
355
- ...opts,
356
- cached,
357
- dataSource: customDataSourceNameValue,
358
- collectionNames: [...collectionNames, field.target],
359
- prefixFieldValue: usePrefix ? prefixFieldValue ? `${prefixFieldValue}.${field.name}` : field.name : "",
360
- usePrefix
327
+ const children = [];
328
+ const collections = [...this.getCollections()];
329
+ const getChildrenCollectionsInner = /* @__PURE__ */ __name((collectionName) => {
330
+ const inheritCollections = collections.filter((v) => {
331
+ var _a;
332
+ return (_a = [...v.inherits]) == null ? void 0 : _a.includes(collectionName);
333
+ });
334
+ inheritCollections.forEach((v) => {
335
+ const collectionKey = v.name;
336
+ children.push(collectionKey);
337
+ return getChildrenCollectionsInner(collectionKey);
338
+ });
339
+ if (isSupportView) {
340
+ const sourceCollections = collections.filter((v) => {
341
+ var _a;
342
+ return ((_a = [...v.sources]) == null ? void 0 : _a.length) === 1 && (v == null ? void 0 : v.sources[0]) === collectionName;
361
343
  });
362
- if (!((_b = result.children) == null ? void 0 : _b.length)) {
363
- return null;
344
+ sourceCollections.forEach((v) => {
345
+ const collectionKey = v.name;
346
+ children.push(v.name);
347
+ return getChildrenCollectionsInner(collectionKey);
348
+ });
349
+ }
350
+ return import_lodash.default.uniq(children);
351
+ }, "getChildrenCollectionsInner");
352
+ this.childrenCollectionsName[cacheKey] = getChildrenCollectionsInner(name);
353
+ return this.childrenCollectionsName[cacheKey];
354
+ }
355
+ getAllCollectionsInheritChain(name) {
356
+ if (this.allCollectionsInheritChain) {
357
+ return this.allCollectionsInheritChain.slice();
358
+ }
359
+ const collectionsInheritChain = [name];
360
+ const getInheritChain = /* @__PURE__ */ __name((name2) => {
361
+ const collection = this.getCollection(name2);
362
+ if (collection) {
363
+ const { inherits } = collection;
364
+ const children = this.getChildrenCollectionsName(name2);
365
+ if (inherits) {
366
+ for (let index = 0; index < inherits.length; index++) {
367
+ const collectionKey = inherits[index];
368
+ if (collectionsInheritChain.includes(collectionKey)) {
369
+ continue;
370
+ }
371
+ collectionsInheritChain.push(collectionKey);
372
+ getInheritChain(collectionKey);
373
+ }
374
+ }
375
+ if (children) {
376
+ for (let index = 0; index < children.length; index++) {
377
+ const collection2 = this.getCollection(children[index]);
378
+ const collectionKey = collection2.name;
379
+ if (collectionsInheritChain.includes(collectionKey)) {
380
+ continue;
381
+ }
382
+ collectionsInheritChain.push(collectionKey);
383
+ getInheritChain(collectionKey);
384
+ }
364
385
  }
365
386
  }
366
- return result;
367
- }).filter(Boolean);
368
- cached[collectionName] = options;
369
- return options;
387
+ return collectionsInheritChain;
388
+ }, "getInheritChain");
389
+ this.allCollectionsInheritChain = getInheritChain(name);
390
+ return this.allCollectionsInheritChain || [];
370
391
  }
371
392
  };
372
393
  __name(_CollectionManager, "CollectionManager");
@@ -143,6 +143,29 @@ export declare class FlowContext {
143
143
  * @returns 属性定义选项,或 undefined(未定义)
144
144
  */
145
145
  getPropertyOptions(key: string): PropertyOptions | undefined;
146
+ /**
147
+ * 获取当前上下文可用的顶层 API 信息(主要用于编辑器补全/工具)。
148
+ *
149
+ * 注意:
150
+ * - 目前返回值以“尽量可用”为目标,允许不完整。
151
+ * - 该方法不会展开变量 meta(变量结构应由 `getPropertyMetaTree()`/VariableInput 等机制负责)。
152
+ */
153
+ getApiInfos(options?: {
154
+ version?: string;
155
+ }): Promise<Record<string, any>>;
156
+ /**
157
+ * 变量结构信息(保留给编辑器/工具使用)。
158
+ * 当前实现为保底空对象,避免 RunJS doc 中补全到该方法时调用报错。
159
+ */
160
+ getVarInfos(_options?: {
161
+ path?: string | string[];
162
+ maxDepth?: number;
163
+ }): Promise<Record<string, any>>;
164
+ /**
165
+ * 运行时环境信息(保留给编辑器/工具使用)。
166
+ * 当前实现为保底空对象,避免 RunJS doc 中补全到该方法时调用报错。
167
+ */
168
+ getEnvInfos(): Promise<Record<string, any>>;
146
169
  }
147
170
  declare class BaseFlowEngineContext extends FlowContext {
148
171
  router: Router;
@@ -158,6 +181,7 @@ declare class BaseFlowEngineContext extends FlowContext {
158
181
  */
159
182
  renderJson: (template: JSONValue) => Promise<any>;
160
183
  resolveJsonTemplate: (template: JSONValue) => Promise<any>;
184
+ getVar: (path: string) => Promise<any>;
161
185
  runjs: (code: string, variables?: Record<string, any>, options?: JSRunnerOptions) => Promise<any>;
162
186
  getAction: <TModel extends FlowModel = FlowModel, TCtx extends FlowContext = FlowContext>(name: string) => ActionDefinition<TModel, TCtx> | undefined;
163
187
  getActions: <TModel extends FlowModel = FlowModel, TCtx extends FlowContext = FlowContext>() => Map<string, ActionDefinition<TModel, TCtx>>;
@@ -219,22 +243,60 @@ export declare class FlowRuntimeContext<TModel extends FlowModel = FlowModel, TM
219
243
  get mode(): TMode;
220
244
  }
221
245
  export type FlowSettingsContext<TModel extends FlowModel = FlowModel> = FlowRuntimeContext<TModel, 'settings'>;
246
+ export type FlowContextDocRef = string | {
247
+ url: string;
248
+ title?: string;
249
+ };
250
+ export type FlowDeprecationDoc = boolean | {
251
+ message?: string;
252
+ replacedBy?: string | string[];
253
+ since?: string;
254
+ removedIn?: string;
255
+ ref?: FlowContextDocRef;
256
+ };
257
+ export type FlowContextDocParam = {
258
+ name: string;
259
+ description?: string;
260
+ type?: string;
261
+ optional?: boolean;
262
+ default?: JSONValue;
263
+ };
264
+ export type FlowContextDocReturn = {
265
+ description?: string;
266
+ type?: string;
267
+ };
222
268
  export type RunJSDocCompletionDoc = {
223
269
  insertText?: string;
224
270
  };
271
+ export type RunJSDocHiddenDoc = boolean | ((ctx: any) => boolean | Promise<boolean>);
272
+ export type RunJSDocHiddenOrPathsDoc = boolean | string[] | ((ctx: any) => boolean | string[] | Promise<boolean | string[]>);
225
273
  export type RunJSDocPropertyDoc = string | {
226
274
  description?: string;
227
275
  detail?: string;
228
276
  type?: string;
229
277
  examples?: string[];
230
278
  completion?: RunJSDocCompletionDoc;
279
+ ref?: FlowContextDocRef;
280
+ deprecated?: FlowDeprecationDoc;
281
+ params?: FlowContextDocParam[];
282
+ returns?: FlowContextDocReturn;
231
283
  properties?: Record<string, RunJSDocPropertyDoc>;
284
+ hidden?: RunJSDocHiddenOrPathsDoc;
285
+ disabled?: boolean | ((ctx: any) => boolean | Promise<boolean>);
286
+ disabledReason?: string | ((ctx: any) => string | undefined | Promise<string | undefined>);
232
287
  };
233
288
  export type RunJSDocMethodDoc = string | {
234
289
  description?: string;
235
290
  detail?: string;
236
291
  examples?: string[];
237
292
  completion?: RunJSDocCompletionDoc;
293
+ ref?: FlowContextDocRef;
294
+ deprecated?: FlowDeprecationDoc;
295
+ params?: FlowContextDocParam[];
296
+ returns?: FlowContextDocReturn;
297
+ hidden?: RunJSDocHiddenDoc;
298
+ disabled?: boolean | ((ctx: any) => boolean | Promise<boolean>);
299
+ disabledReason?: string | ((ctx: any) => string | undefined | Promise<string | undefined>);
238
300
  };
239
301
  export type RunJSDocMeta = {
240
302
  label?: string;
@@ -469,6 +469,73 @@ const _FlowContext = class _FlowContext {
469
469
  }
470
470
  return this._findPropertyInDelegates(this._delegates, key);
471
471
  }
472
+ /**
473
+ * 获取当前上下文可用的顶层 API 信息(主要用于编辑器补全/工具)。
474
+ *
475
+ * 注意:
476
+ * - 目前返回值以“尽量可用”为目标,允许不完整。
477
+ * - 该方法不会展开变量 meta(变量结构应由 `getPropertyMetaTree()`/VariableInput 等机制负责)。
478
+ */
479
+ async getApiInfos(options = {}) {
480
+ var _a, _b, _c, _d, _e;
481
+ const version = (options == null ? void 0 : options.version) || "v1";
482
+ const modelClass = (0, import_registry.getModelClassName)(this);
483
+ const Ctor = import_registry.RunJSContextRegistry.resolve(version, modelClass) || import_registry.RunJSContextRegistry.resolve(version, "*");
484
+ const locale = ((_b = (_a = this == null ? void 0 : this.api) == null ? void 0 : _a.auth) == null ? void 0 : _b.locale) || ((_c = this == null ? void 0 : this.i18n) == null ? void 0 : _c.language) || (this == null ? void 0 : this.locale);
485
+ let doc = {};
486
+ try {
487
+ if ((_d = Ctor == null ? void 0 : Ctor.getDoc) == null ? void 0 : _d.length) doc = Ctor.getDoc(locale) || {};
488
+ else doc = ((_e = Ctor == null ? void 0 : Ctor.getDoc) == null ? void 0 : _e.call(Ctor)) || {};
489
+ } catch (_2) {
490
+ doc = {};
491
+ }
492
+ const isPrivateKey = /* @__PURE__ */ __name((key) => typeof key === "string" && key.startsWith("_"), "isPrivateKey");
493
+ const out = {};
494
+ const visited = /* @__PURE__ */ new WeakSet();
495
+ const walk = /* @__PURE__ */ __name((ctx) => {
496
+ if (!ctx || visited.has(ctx)) return;
497
+ visited.add(ctx);
498
+ try {
499
+ for (const key of Object.keys(ctx._props || {})) {
500
+ if (isPrivateKey(key)) continue;
501
+ if (typeof out[key] === "undefined") out[key] = { type: "property" };
502
+ }
503
+ } catch (_2) {
504
+ }
505
+ try {
506
+ for (const key of Object.keys(ctx._methods || {})) {
507
+ if (isPrivateKey(key)) continue;
508
+ if (typeof out[key] === "undefined") out[key] = { type: "function" };
509
+ }
510
+ } catch (_2) {
511
+ }
512
+ try {
513
+ const delegates = Array.isArray(ctx._delegates) ? ctx._delegates : [];
514
+ for (const d of delegates) walk(d);
515
+ } catch (_2) {
516
+ }
517
+ }, "walk");
518
+ walk(this);
519
+ return {
520
+ ...out,
521
+ ...(doc == null ? void 0 : doc.properties) || {},
522
+ ...(doc == null ? void 0 : doc.methods) || {}
523
+ };
524
+ }
525
+ /**
526
+ * 变量结构信息(保留给编辑器/工具使用)。
527
+ * 当前实现为保底空对象,避免 RunJS doc 中补全到该方法时调用报错。
528
+ */
529
+ async getVarInfos(_options = {}) {
530
+ return {};
531
+ }
532
+ /**
533
+ * 运行时环境信息(保留给编辑器/工具使用)。
534
+ * 当前实现为保底空对象,避免 RunJS doc 中补全到该方法时调用报错。
535
+ */
536
+ async getEnvInfos() {
537
+ return {};
538
+ }
472
539
  };
473
540
  _proxy = new WeakMap();
474
541
  _FlowContext_instances = new WeakSet();
@@ -763,7 +830,8 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
763
830
  value: this.engine
764
831
  });
765
832
  this.defineProperty("sql", {
766
- get: /* @__PURE__ */ __name(() => new import_resources.FlowSQLRepository(this), "get")
833
+ get: /* @__PURE__ */ __name((ctx) => new import_resources.FlowSQLRepository(ctx), "get"),
834
+ cache: false
767
835
  });
768
836
  this.defineProperty("dataSourceManager", {
769
837
  value: dataSourceManager
@@ -915,6 +983,19 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
915
983
  }
916
984
  return (0, import_utils.resolveExpressions)(serverResolved, this);
917
985
  });
986
+ this.defineMethod(
987
+ "getVar",
988
+ async function(varPath) {
989
+ const raw = typeof varPath === "string" ? varPath : String(varPath ?? "");
990
+ const s = raw.trim();
991
+ if (!s) return void 0;
992
+ if (s !== "ctx" && !s.startsWith("ctx.")) {
993
+ throw new Error(`ctx.getVar(path) expects an expression starting with "ctx.", got: "${s}"`);
994
+ }
995
+ return this.resolveJsonTemplate(`{{ ${s} }}`);
996
+ },
997
+ 'Resolve a ctx expression value by path (expression starts with "ctx.").'
998
+ );
918
999
  this.defineProperty("requirejs", {
919
1000
  get: /* @__PURE__ */ __name(() => {
920
1001
  var _a, _b;
@@ -1127,7 +1208,7 @@ const _FlowModelContext = class _FlowModelContext extends BaseFlowModelContext {
1127
1208
  }, "get")
1128
1209
  });
1129
1210
  this.defineMethod("openView", async function(uid, options) {
1130
- var _a, _b, _c, _d;
1211
+ var _a, _b, _c, _d, _e;
1131
1212
  const opts = { ...options };
1132
1213
  if (opts.defineProperties || opts.defineMethods) {
1133
1214
  opts.navigation = false;
@@ -1177,8 +1258,16 @@ const _FlowModelContext = class _FlowModelContext extends BaseFlowModelContext {
1177
1258
  engineCtx: this.engine.context
1178
1259
  };
1179
1260
  model2.context.defineProperty("view", { value: pendingView });
1261
+ const popupFlow = (_e = model2.getFlow) == null ? void 0 : _e.call(model2, "popupSettings");
1262
+ const on = popupFlow == null ? void 0 : popupFlow.on;
1263
+ let openEventName = "click";
1264
+ if (typeof on === "string" && on) {
1265
+ openEventName = on;
1266
+ } else if (on && typeof on === "object" && typeof on.eventName === "string" && on.eventName) {
1267
+ openEventName = on.eventName;
1268
+ }
1180
1269
  await model2.dispatchEvent(
1181
- "click",
1270
+ openEventName,
1182
1271
  {
1183
1272
  // navigation: false, // TODO: 路由模式有bug,不支持多层同样viewId的弹窗,因此这里默认先用false
1184
1273
  // ...this.model?.['getInputArgs']?.(), // 避免部分关系字段信息丢失, 仿照 ClickableCollectionField 做法
package/lib/flowEngine.js CHANGED
@@ -762,16 +762,26 @@ const _FlowEngine = class _FlowEngine {
762
762
  */
763
763
  async loadModel(options) {
764
764
  if (!this.ensureModelRepository()) return;
765
- const model = this.findModelByParentId(options.parentId, options.subKey);
766
- if (model) {
767
- return model;
768
- }
769
- const hydrated = this.hydrateModelFromPreviousEngines(options);
770
- if (hydrated) {
771
- return hydrated;
765
+ const refresh = !!(options == null ? void 0 : options.refresh);
766
+ if (!refresh) {
767
+ const model = this.findModelByParentId(options.parentId, options.subKey);
768
+ if (model) {
769
+ return model;
770
+ }
771
+ const hydrated = this.hydrateModelFromPreviousEngines(options);
772
+ if (hydrated) {
773
+ return hydrated;
774
+ }
772
775
  }
773
776
  const data = await this._modelRepository.findOne(options);
774
- return (data == null ? void 0 : data.uid) ? this.createModel(data) : null;
777
+ if (!(data == null ? void 0 : data.uid)) return null;
778
+ if (refresh) {
779
+ const existing = this.getModel(data.uid);
780
+ if (existing) {
781
+ this.removeModelWithSubModels(existing.uid);
782
+ }
783
+ }
784
+ return this.createModel(data);
775
785
  }
776
786
  /**
777
787
  * Find a sub-model by parent model ID and subKey.
package/lib/index.d.ts CHANGED
@@ -27,7 +27,10 @@ export * from './JSRunner';
27
27
  export { getRunJSDocFor, createJSRunnerWithVersion, getRunJSScenesForModel, getRunJSScenesForContext, } from './runjs-context/helpers';
28
28
  export { RunJSContextRegistry, getModelClassName } from './runjs-context/registry';
29
29
  export { setupRunJSContexts } from './runjs-context/setup';
30
- export { getSnippetBody, listSnippetsForContext } from './runjs-context/snippets';
30
+ export type { RunJSContextContribution, RunJSContextContributionApi } from './runjs-context/contributions';
31
+ export { registerRunJSContextContribution } from './runjs-context/contributions';
32
+ export type { RunJSSnippetLoader } from './runjs-context/snippets';
33
+ export { getSnippetBody, listSnippetsForContext, registerRunJSSnippet } from './runjs-context/snippets';
31
34
  export * from './views';
32
35
  export { DATA_SOURCE_DIRTY_EVENT, ENGINE_SCOPE_KEY, getEmitterViewActivatedVersion, VIEW_ACTIVATED_EVENT, VIEW_ACTIVATED_VERSION, VIEW_ENGINE_SCOPE, } from './views/viewEvents';
33
36
  export * from './FlowDefinition';
package/lib/index.js CHANGED
@@ -44,7 +44,9 @@ __export(src_exports, {
44
44
  getRunJSScenesForModel: () => import_helpers.getRunJSScenesForModel,
45
45
  getSnippetBody: () => import_snippets.getSnippetBody,
46
46
  listSnippetsForContext: () => import_snippets.listSnippetsForContext,
47
+ registerRunJSContextContribution: () => import_contributions.registerRunJSContextContribution,
47
48
  registerRunJSLib: () => import_runjsLibs.registerRunJSLib,
49
+ registerRunJSSnippet: () => import_snippets.registerRunJSSnippet,
48
50
  setupRunJSContexts: () => import_setup.setupRunJSContexts
49
51
  });
50
52
  module.exports = __toCommonJS(src_exports);
@@ -68,6 +70,7 @@ __reExport(src_exports, require("./JSRunner"), module.exports);
68
70
  var import_helpers = require("./runjs-context/helpers");
69
71
  var import_registry = require("./runjs-context/registry");
70
72
  var import_setup = require("./runjs-context/setup");
73
+ var import_contributions = require("./runjs-context/contributions");
71
74
  var import_snippets = require("./runjs-context/snippets");
72
75
  __reExport(src_exports, require("./views"), module.exports);
73
76
  var import_viewEvents = require("./views/viewEvents");
@@ -93,7 +96,9 @@ var import_BlockScopedFlowEngine = require("./BlockScopedFlowEngine");
93
96
  getRunJSScenesForModel,
94
97
  getSnippetBody,
95
98
  listSnippetsForContext,
99
+ registerRunJSContextContribution,
96
100
  registerRunJSLib,
101
+ registerRunJSSnippet,
97
102
  setupRunJSContexts,
98
103
  ...require("./types"),
99
104
  ...require("./utils"),
@@ -6,7 +6,7 @@
6
6
  * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
- import { FlowEngineContext } from '../flowContext';
9
+ import { FlowContext, FlowEngineContext } from '../flowContext';
10
10
  import { BaseRecordResource } from './baseRecordResource';
11
11
  type SQLRunOptions = {
12
12
  bind?: Record<string, any>;
@@ -20,8 +20,8 @@ type SQLSaveOptions = {
20
20
  dataSourceKey?: string;
21
21
  };
22
22
  export declare class FlowSQLRepository {
23
- protected ctx: FlowEngineContext;
24
- constructor(ctx: FlowEngineContext);
23
+ protected ctx: FlowContext;
24
+ constructor(ctx: FlowContext);
25
25
  run(sql: string, options?: SQLRunOptions): Promise<any>;
26
26
  save(data: SQLSaveOptions): Promise<void>;
27
27
  runById(uid: string, options?: SQLRunOptions): Promise<any>;
@@ -42,7 +42,12 @@ FormJSFieldItemRunJSContext.define({
42
42
  Supports innerHTML, append, and other DOM manipulation methods.`,
43
43
  value: `Current field value (read-only in display mode; in controlled scenarios, use setProps to modify).`,
44
44
  record: `Current record data object (read-only).
45
- Contains all field values of the parent record.`
45
+ Contains all field values of the parent record.`,
46
+ formValues: {
47
+ description: "Snapshot of current form values (object). Available in form contexts (CreateForm/EditForm).",
48
+ detail: "Record<string, any>",
49
+ examples: ["const { name, status } = ctx.formValues || {};"]
50
+ }
46
51
  },
47
52
  methods: {
48
53
  onRefReady: `Wait for form field container DOM element to be ready before executing callback.
@@ -58,7 +63,12 @@ FormJSFieldItemRunJSContext.define(
58
63
  properties: {
59
64
  element: "ElementProxy\uFF0C\u8868\u5355\u5B57\u6BB5\u5BB9\u5668",
60
65
  value: "\u5B57\u6BB5\u503C\uFF08\u5C55\u793A\u6A21\u5F0F\u4E3A\u53EA\u8BFB\uFF1B\u53D7\u63A7\u573A\u666F\u7528 setProps \u4FEE\u6539\uFF09",
61
- record: "\u5F53\u524D\u8BB0\u5F55\uFF08\u53EA\u8BFB\uFF09"
66
+ record: "\u5F53\u524D\u8BB0\u5F55\uFF08\u53EA\u8BFB\uFF09",
67
+ formValues: {
68
+ description: "\u5F53\u524D\u8868\u5355\u503C\u5FEB\u7167\uFF08\u5BF9\u8C61\uFF09\u3002\u4EC5\u8868\u5355\u76F8\u5173\u4E0A\u4E0B\u6587\u53EF\u7528\uFF08Create/Edit Form\uFF09\u3002",
69
+ detail: "Record<string, any>",
70
+ examples: ["const { name, status } = ctx.formValues || {};"]
71
+ }
62
72
  },
63
73
  methods: {
64
74
  onRefReady: "\u5BB9\u5668\u5C31\u7EEA\u56DE\u8C03",
@@ -58,7 +58,7 @@ JSBlockRunJSContext.define({
58
58
  Parameters: (ref: React.RefObject, callback: (element: HTMLElement) => void, timeout?: number) => void
59
59
  Example: ctx.onRefReady(ctx.ref, (el) => { el.innerHTML = "Ready!" })`,
60
60
  requireAsync: "Load external library: `const lib = await ctx.requireAsync(url)`",
61
- importAsync: "Dynamically import ESM module: `const mod = await ctx.importAsync(url)`"
61
+ importAsync: "Dynamically import an ESM module by URL: `const mod = await ctx.importAsync(url)`.\nNote: if the module has only a default export, ctx.importAsync returns that default value directly (no `.default`)."
62
62
  }
63
63
  });
64
64
  JSBlockRunJSContext.define(
@@ -80,7 +80,7 @@ JSBlockRunJSContext.define(
80
80
  methods: {
81
81
  onRefReady: "\u5BB9\u5668 ref \u5C31\u7EEA\u56DE\u8C03\uFF1A\n```js\nctx.onRefReady(ctx.ref, el => { /* ... */ })\n```",
82
82
  requireAsync: "\u52A0\u8F7D\u5916\u90E8\u5E93\uFF1A`const lib = await ctx.requireAsync(url)`",
83
- importAsync: "\u6309 URL \u52A8\u6001\u5BFC\u5165 ESM \u6A21\u5757\uFF1A`const mod = await ctx.importAsync(url)`"
83
+ importAsync: "\u6309 URL \u52A8\u6001\u5BFC\u5165 ESM \u6A21\u5757\uFF1A`const mod = await ctx.importAsync(url)`\u3002\n\u6CE8\u610F\uFF1A\u5F53\u6A21\u5757\u53EA\u6709 default \u4E00\u4E2A\u5BFC\u51FA\u65F6\uFF0Cctx.importAsync \u4F1A\u76F4\u63A5\u8FD4\u56DE default \u503C\uFF08\u65E0\u9700\u518D\u5199 `.default`\uFF09\u3002"
84
84
  }
85
85
  },
86
86
  { locale: "zh-CN" }
@@ -0,0 +1,16 @@
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
+ import { FlowRunJSContext } from '../../flowContext';
10
+ /**
11
+ * RunJS context for JSEditableFieldModel (form editable custom field).
12
+ * NOTE: Some APIs (e.g., getValue/setValue/element) are provided by the model's runtime handler.
13
+ * This doc is used for editor autocomplete and AI coding assistance.
14
+ */
15
+ export declare class JSEditableFieldRunJSContext extends FlowRunJSContext {
16
+ }