@nocobase/plugin-flow-engine 2.0.0-alpha.7 → 2.0.0-alpha.71

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 (38) hide show
  1. package/dist/externalVersion.js +9 -9
  2. package/dist/locale/de-DE.json +62 -0
  3. package/dist/locale/en-US.json +57 -45
  4. package/dist/locale/es-ES.json +62 -0
  5. package/dist/locale/fr-FR.json +62 -0
  6. package/dist/locale/hu-HU.json +62 -0
  7. package/dist/locale/id-ID.json +62 -0
  8. package/dist/locale/index.d.ts +114 -90
  9. package/dist/locale/it-IT.json +62 -0
  10. package/dist/locale/ja-JP.json +62 -0
  11. package/dist/locale/ko-KR.json +62 -0
  12. package/dist/locale/nl-NL.json +62 -0
  13. package/dist/locale/pt-BR.json +62 -0
  14. package/dist/locale/ru-RU.json +62 -0
  15. package/dist/locale/tr-TR.json +62 -0
  16. package/dist/locale/uk-UA.json +62 -0
  17. package/dist/locale/vi-VN.json +62 -0
  18. package/dist/locale/zh-CN.json +58 -46
  19. package/dist/locale/zh-TW.json +62 -0
  20. package/dist/node_modules/ses/package.json +1 -1
  21. package/dist/server/collections/flowsql.js +1 -0
  22. package/dist/server/index.d.ts +1 -0
  23. package/dist/server/index.js +6 -0
  24. package/dist/server/plugin.d.ts +0 -5
  25. package/dist/server/plugin.js +17 -98
  26. package/dist/server/repository.d.ts +13 -2
  27. package/dist/server/repository.js +258 -8
  28. package/dist/server/server.js +22 -3
  29. package/dist/server/template/resolver.js +10 -6
  30. package/dist/server/variables/records.d.ts +38 -0
  31. package/dist/server/variables/records.js +120 -0
  32. package/dist/server/variables/registry.d.ts +10 -0
  33. package/dist/server/variables/registry.js +278 -64
  34. package/dist/server/variables/selects.d.ts +19 -0
  35. package/dist/server/variables/selects.js +80 -0
  36. package/dist/server/variables/utils.d.ts +17 -0
  37. package/dist/server/variables/utils.js +163 -0
  38. package/package.json +2 -2
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { Cache } from '@nocobase/cache';
10
10
  import { Repository, Transaction, Transactionable } from '@nocobase/database';
11
- import { ChildOptions, SchemaNode } from './dao/ui_schema_node_dao';
11
+ import { ChildOptions, SchemaNode, TargetPosition } from './dao/ui_schema_node_dao';
12
12
  export interface GetJsonSchemaOptions {
13
13
  includeAsyncNode?: boolean;
14
14
  readFromCache?: boolean;
@@ -18,6 +18,13 @@ export interface GetPropertiesOptions {
18
18
  readFromCache?: boolean;
19
19
  transaction?: Transaction;
20
20
  }
21
+ export type FlowModelAttachPosition = 'first' | 'last' | TargetPosition;
22
+ export interface FlowModelAttachOptions {
23
+ parentId: string;
24
+ subKey: string;
25
+ subType: 'array' | 'object';
26
+ position?: FlowModelAttachPosition;
27
+ }
21
28
  type BreakRemoveOnType = {
22
29
  [key: string]: any;
23
30
  };
@@ -51,6 +58,7 @@ export declare class FlowModelRepository extends Repository {
51
58
  getParentJsonSchema(uid: string, options?: GetJsonSchemaOptions): Promise<any>;
52
59
  getParentProperty(uid: string, options?: GetPropertiesOptions): Promise<any>;
53
60
  getJsonSchema(uid: string, options?: GetJsonSchemaOptions): Promise<any>;
61
+ static optionsToJson(options: any): any;
54
62
  nodesToSchema(nodes: any, rootUid: any): {
55
63
  uid: any;
56
64
  "x-async": boolean;
@@ -70,7 +78,9 @@ export declare class FlowModelRepository extends Repository {
70
78
  }): Promise<void>;
71
79
  remove(uid: string, options?: Transactionable & removeParentOptions): Promise<void>;
72
80
  insertAdjacent(position: 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd', target: string, schema: any, options?: InsertAdjacentOptions): Promise<any>;
73
- duplicate(uid: string, options?: Transactionable): Promise<any>;
81
+ duplicate(modelUid: string, options?: Transactionable): Promise<any>;
82
+ private dedupeNodesForDuplicate;
83
+ private replaceStepParamsModelUids;
74
84
  insert(schema: any, options?: Transactionable): Promise<any>;
75
85
  insertNewSchema(schema: any, options?: Transactionable & {
76
86
  returnNode?: boolean;
@@ -111,6 +121,7 @@ export declare class FlowModelRepository extends Repository {
111
121
  findModelByParentId(parentUid: string, options?: GetJsonSchemaOptions & {
112
122
  subKey?: string;
113
123
  }): Promise<any>;
124
+ attach(uid: string, attachOptions: FlowModelAttachOptions, options?: Transactionable): Promise<any>;
114
125
  move(options: any): Promise<any>;
115
126
  }
116
127
  export default FlowModelRepository;
@@ -204,6 +204,9 @@ const _FlowModelRepository = class _FlowModelRepository extends import_database.
204
204
  }
205
205
  return this.doGetJsonSchema(uid2, options);
206
206
  }
207
+ static optionsToJson(options) {
208
+ return import_lodash.default.isPlainObject(options) ? options : JSON.parse(options);
209
+ }
207
210
  nodesToSchema(nodes, rootUid) {
208
211
  const nodeAttributeSanitize = (node) => {
209
212
  const schema = {
@@ -426,13 +429,122 @@ const _FlowModelRepository = class _FlowModelRepository extends import_database.
426
429
  await this.clearXUidPathCache(result["uid"], transaction2);
427
430
  return result;
428
431
  }
429
- async duplicate(uid2, options) {
430
- const s = await this.getJsonSchema(uid2, { ...options, includeAsyncNode: true });
431
- if (!(s == null ? void 0 : s["uid"])) {
432
+ async duplicate(modelUid, options) {
433
+ let nodes = await this.findNodesById(modelUid, { ...options, includeAsyncNode: true });
434
+ if (!(nodes == null ? void 0 : nodes.length)) {
432
435
  return null;
433
436
  }
434
- this.regenerateUid(s);
435
- return this.insert(s, options);
437
+ nodes = this.dedupeNodesForDuplicate(nodes, modelUid);
438
+ const uidMap = {};
439
+ for (const n of nodes) {
440
+ uidMap[n["uid"]] = (0, import_utils.uid)();
441
+ }
442
+ const sorted = [...nodes].sort((a, b) => {
443
+ if (a.depth !== b.depth) return a.depth - b.depth;
444
+ const ap = a.parent || "";
445
+ const bp = b.parent || "";
446
+ if (ap !== bp) return ap < bp ? -1 : 1;
447
+ const at = a.type || "";
448
+ const bt = b.type || "";
449
+ if (at !== bt) return at < bt ? -1 : 1;
450
+ const as = a.sort ?? 0;
451
+ const bs = b.sort ?? 0;
452
+ return as - bs;
453
+ });
454
+ for (const n of sorted) {
455
+ const oldUid = n["uid"];
456
+ const newUid = uidMap[oldUid];
457
+ const oldParentUid = n["parent"];
458
+ const newParentUid = uidMap[oldParentUid] ?? null;
459
+ const optionsObj = this.replaceStepParamsModelUids(
460
+ import_lodash.default.isPlainObject(n.options) ? n.options : JSON.parse(n.options),
461
+ uidMap
462
+ );
463
+ if (newParentUid) {
464
+ optionsObj.parent = newParentUid;
465
+ optionsObj.parentId = newParentUid;
466
+ }
467
+ const schemaNode = {
468
+ uid: newUid,
469
+ ["x-async"]: !!n.async,
470
+ ...optionsObj
471
+ };
472
+ if (newParentUid) {
473
+ schemaNode.childOptions = {
474
+ parentUid: newParentUid,
475
+ type: n.type,
476
+ position: "last"
477
+ };
478
+ }
479
+ await this.insertSingleNode(schemaNode, { transaction: options == null ? void 0 : options.transaction });
480
+ }
481
+ return this.findModelById(uidMap[modelUid], { ...options });
482
+ }
483
+ dedupeNodesForDuplicate(nodes, rootUid) {
484
+ if (!Array.isArray(nodes) || nodes.length <= 1) {
485
+ return nodes;
486
+ }
487
+ const rowsByUid = import_lodash.default.groupBy(nodes, "uid");
488
+ const uniqueUids = Object.keys(rowsByUid);
489
+ if (uniqueUids.length === nodes.length) {
490
+ return nodes;
491
+ }
492
+ const uidsInSubtree = new Set(uniqueUids);
493
+ const rootDepthByUid = /* @__PURE__ */ new Map();
494
+ for (const uid2 of uniqueUids) {
495
+ const rows = rowsByUid[uid2] || [];
496
+ const depths = rows.map((row) => Number((row == null ? void 0 : row.depth) ?? 0));
497
+ rootDepthByUid.set(uid2, depths.length ? Math.min(...depths) : 0);
498
+ }
499
+ const pickRowForUid = (uid2, rows) => {
500
+ if (!(rows == null ? void 0 : rows.length)) return null;
501
+ if (rows.length === 1) return rows[0];
502
+ if (uid2 === rootUid) return rows[0];
503
+ let bestRow = rows[0];
504
+ let bestParentRootDepth = -1;
505
+ for (const row of rows) {
506
+ const parentUid = row == null ? void 0 : row.parent;
507
+ if (!parentUid || !uidsInSubtree.has(parentUid)) {
508
+ continue;
509
+ }
510
+ const parentRootDepth = rootDepthByUid.get(parentUid) ?? -1;
511
+ if (parentRootDepth > bestParentRootDepth) {
512
+ bestParentRootDepth = parentRootDepth;
513
+ bestRow = row;
514
+ }
515
+ }
516
+ return bestRow;
517
+ };
518
+ const uidsInQueryOrder = [];
519
+ const seenUidsInQueryOrder = /* @__PURE__ */ new Set();
520
+ for (const row of nodes) {
521
+ const uid2 = row == null ? void 0 : row.uid;
522
+ if (!uid2 || seenUidsInQueryOrder.has(uid2)) continue;
523
+ seenUidsInQueryOrder.add(uid2);
524
+ uidsInQueryOrder.push(uid2);
525
+ }
526
+ return uidsInQueryOrder.map((uid2) => pickRowForUid(uid2, rowsByUid[uid2])).filter(Boolean);
527
+ }
528
+ replaceStepParamsModelUids(options, uidMap) {
529
+ const opts = options && typeof options === "object" ? options : {};
530
+ const replaceUidString = (v) => typeof v === "string" && uidMap[v] ? uidMap[v] : v;
531
+ const replaceInPlace = (val) => {
532
+ if (Array.isArray(val)) {
533
+ for (let i = 0; i < val.length; i++) {
534
+ val[i] = replaceInPlace(val[i]);
535
+ }
536
+ return val;
537
+ }
538
+ if (val && typeof val === "object") {
539
+ for (const k of Object.keys(val)) {
540
+ val[k] = replaceInPlace(val[k]);
541
+ }
542
+ return val;
543
+ }
544
+ return replaceUidString(val);
545
+ };
546
+ if (opts.stepParams) opts.stepParams = replaceInPlace(opts.stepParams);
547
+ return opts;
436
548
  }
437
549
  async insert(schema, options) {
438
550
  const nodes = _FlowModelRepository.schemaToSingleNodes(schema);
@@ -1025,11 +1137,11 @@ WHERE TreeTable.depth = 1 AND TreeTable.ancestor = :ancestor and TreeTable.sort
1025
1137
  const children = nodes.filter((n) => n.parent === rootUid);
1026
1138
  const subModels = {};
1027
1139
  for (const child of children) {
1028
- const { subKey, subType } = child.options;
1140
+ const { subKey, subType } = this.optionsToJson(child.options);
1029
1141
  if (!subKey) continue;
1030
1142
  const model = _FlowModelRepository.nodesToModel(nodes, child["uid"]) || {
1031
1143
  uid: child["uid"],
1032
- ...child.options,
1144
+ ...this.optionsToJson(child.options),
1033
1145
  sortIndex: child.sort
1034
1146
  };
1035
1147
  model.sortIndex = child.sort;
@@ -1055,7 +1167,7 @@ WHERE TreeTable.depth = 1 AND TreeTable.ancestor = :ancestor and TreeTable.sort
1055
1167
  }
1056
1168
  return {
1057
1169
  uid: rootNode["uid"],
1058
- ...rootNode.options,
1170
+ ...this.optionsToJson(rootNode.options),
1059
1171
  ...Object.keys(filteredSubModels).length > 0 ? { subModels: filteredSubModels } : {}
1060
1172
  };
1061
1173
  }
@@ -1136,6 +1248,141 @@ WHERE TreeTable.depth = 1 AND TreeTable.ancestor = :ancestor and TreeTable.sort
1136
1248
  }
1137
1249
  return null;
1138
1250
  }
1251
+ async attach(uid2, attachOptions, options) {
1252
+ const { transaction: transaction2 } = options || {};
1253
+ const modelUid = String(uid2 || "").trim();
1254
+ const parentId = String((attachOptions == null ? void 0 : attachOptions.parentId) || "").trim();
1255
+ const subKey = String((attachOptions == null ? void 0 : attachOptions.subKey) || "").trim();
1256
+ const subType = attachOptions == null ? void 0 : attachOptions.subType;
1257
+ if (!modelUid || !parentId || !subKey || subType !== "array" && subType !== "object") {
1258
+ throw new Error("flowModels:attach missing required params");
1259
+ }
1260
+ if (modelUid === parentId) {
1261
+ throw new Error("flowModels:attach cannot attach model to itself");
1262
+ }
1263
+ const treeTable = this.flowModelTreePathTableName;
1264
+ const modelInstance = await this.model.findByPk(modelUid, { transaction: transaction2 });
1265
+ if (!modelInstance) {
1266
+ throw new Error(`flowModels:attach uid '${modelUid}' not found`);
1267
+ }
1268
+ const parentInstance = await this.model.findByPk(parentId, { transaction: transaction2 });
1269
+ if (!parentInstance) {
1270
+ throw new Error(`flowModels:attach parentId '${parentId}' not found`);
1271
+ }
1272
+ const cycle = await this.database.sequelize.query(
1273
+ `SELECT 1 as v
1274
+ FROM ${treeTable}
1275
+ WHERE ancestor = :ancestor AND descendant = :descendant AND depth > 0
1276
+ LIMIT 1`,
1277
+ {
1278
+ type: "SELECT",
1279
+ replacements: {
1280
+ ancestor: modelUid,
1281
+ descendant: parentId
1282
+ },
1283
+ transaction: transaction2
1284
+ }
1285
+ );
1286
+ if (cycle == null ? void 0 : cycle.length) {
1287
+ throw new Error("flowModels:attach cycle detected");
1288
+ }
1289
+ if (subType === "object") {
1290
+ const conflict = await this.database.sequelize.query(
1291
+ `SELECT TreeTable.descendant as uid
1292
+ FROM ${treeTable} as TreeTable
1293
+ LEFT JOIN ${treeTable} as NodeInfo
1294
+ ON NodeInfo.descendant = TreeTable.descendant
1295
+ AND NodeInfo.depth = 0
1296
+ WHERE TreeTable.depth = 1
1297
+ AND TreeTable.ancestor = :ancestor
1298
+ AND NodeInfo.type = :type
1299
+ AND TreeTable.descendant != :uid
1300
+ LIMIT 1`,
1301
+ {
1302
+ type: "SELECT",
1303
+ replacements: {
1304
+ ancestor: parentId,
1305
+ type: subKey,
1306
+ uid: modelUid
1307
+ },
1308
+ transaction: transaction2
1309
+ }
1310
+ );
1311
+ if (conflict == null ? void 0 : conflict.length) {
1312
+ throw new Error(`flowModels:attach subKey '${subKey}' already exists on parent '${parentId}'`);
1313
+ }
1314
+ }
1315
+ const normalizePosition = (input) => {
1316
+ if (!input) return "last";
1317
+ if (input === "first" || input === "last") return input;
1318
+ if (typeof input === "object") {
1319
+ const p = input;
1320
+ const type = p == null ? void 0 : p.type;
1321
+ const target = String((p == null ? void 0 : p.target) || "").trim();
1322
+ if ((type === "before" || type === "after") && target) {
1323
+ return { type, target };
1324
+ }
1325
+ }
1326
+ throw new Error("flowModels:attach invalid position");
1327
+ };
1328
+ const position = subType === "object" ? "last" : normalizePosition(attachOptions == null ? void 0 : attachOptions.position);
1329
+ if (typeof position === "object") {
1330
+ const target = String(position.target || "").trim();
1331
+ if (target === modelUid) {
1332
+ throw new Error("flowModels:attach position target cannot be itself");
1333
+ }
1334
+ const ok = await this.database.sequelize.query(
1335
+ `SELECT 1 as v
1336
+ FROM ${treeTable} as TreeTable
1337
+ LEFT JOIN ${treeTable} as NodeInfo
1338
+ ON NodeInfo.descendant = TreeTable.descendant
1339
+ AND NodeInfo.depth = 0
1340
+ WHERE TreeTable.depth = 1
1341
+ AND TreeTable.ancestor = :ancestor
1342
+ AND TreeTable.descendant = :descendant
1343
+ AND NodeInfo.type = :type
1344
+ LIMIT 1`,
1345
+ {
1346
+ type: "SELECT",
1347
+ replacements: {
1348
+ ancestor: parentId,
1349
+ descendant: target,
1350
+ type: subKey
1351
+ },
1352
+ transaction: transaction2
1353
+ }
1354
+ );
1355
+ if (!(ok == null ? void 0 : ok.length)) {
1356
+ throw new Error("flowModels:attach position target is not a sibling under the same parent/subKey");
1357
+ }
1358
+ }
1359
+ await this.clearXUidPathCache(modelUid, transaction2);
1360
+ await modelInstance.update(
1361
+ {
1362
+ options: {
1363
+ ...modelInstance.get("options"),
1364
+ parentId,
1365
+ parent: parentId,
1366
+ subKey,
1367
+ subType
1368
+ }
1369
+ },
1370
+ { transaction: transaction2, hooks: false }
1371
+ );
1372
+ await this.insertSingleNode(
1373
+ {
1374
+ uid: modelUid,
1375
+ name: modelUid,
1376
+ childOptions: {
1377
+ parentUid: parentId,
1378
+ type: subKey,
1379
+ position
1380
+ }
1381
+ },
1382
+ { transaction: transaction2, removeParentsIfNoChildren: false }
1383
+ );
1384
+ return await this.findModelById(modelUid, { transaction: transaction2, includeAsyncNode: true });
1385
+ }
1139
1386
  async move(options) {
1140
1387
  const { sourceId, targetId, position } = options;
1141
1388
  return await this.insertAdjacent(position === "after" ? "afterEnd" : "beforeBegin", targetId, {
@@ -1200,6 +1447,9 @@ __decorateClass([
1200
1447
  __decorateClass([
1201
1448
  transaction()
1202
1449
  ], _FlowModelRepository.prototype, "upsertModel", 1);
1450
+ __decorateClass([
1451
+ transaction()
1452
+ ], _FlowModelRepository.prototype, "attach", 1);
1203
1453
  let FlowModelRepository = _FlowModelRepository;
1204
1454
  var repository_default = FlowModelRepository;
1205
1455
  // Annotate the CommonJS export names for ESM import in node:
@@ -127,15 +127,34 @@ class PluginUISchemaStorageServer extends import_server.Plugin {
127
127
  name: "flowModels",
128
128
  actions: {
129
129
  findOne: async (ctx, next) => {
130
- const { uid: uid2, parentId, subKey } = ctx.action.params;
130
+ const { uid: uid2, parentId, subKey, includeAsyncNode = false } = ctx.action.params;
131
131
  const repository = ctx.db.getRepository("flowModels");
132
132
  if (uid2) {
133
- ctx.body = await repository.findModelById(uid2);
133
+ ctx.body = await repository.findModelById(uid2, { includeAsyncNode });
134
134
  } else if (parentId) {
135
- ctx.body = await repository.findModelByParentId(parentId, { subKey });
135
+ ctx.body = await repository.findModelByParentId(parentId, { subKey, includeAsyncNode });
136
136
  }
137
137
  await next();
138
138
  },
139
+ duplicate: async (ctx, next) => {
140
+ const { uid: uid2 } = ctx.action.params;
141
+ const repository = ctx.db.getRepository("flowModels");
142
+ const duplicated = await repository.duplicate(uid2);
143
+ ctx.body = duplicated;
144
+ await next();
145
+ },
146
+ attach: async (ctx, next) => {
147
+ const { uid: uid2, parentId, subKey, subType, position } = ctx.action.params;
148
+ const repository = ctx.db.getRepository("flowModels");
149
+ const attached = await repository.attach(String(uid2 || "").trim(), {
150
+ parentId: String(parentId || "").trim(),
151
+ subKey: String(subKey || "").trim(),
152
+ subType,
153
+ position
154
+ });
155
+ ctx.body = attached;
156
+ await next();
157
+ },
139
158
  move: async (ctx, next) => {
140
159
  const { sourceId, targetId, position } = ctx.action.params;
141
160
  const repository = ctx.db.getRepository("flowModels");
@@ -78,14 +78,18 @@ async function replacePlaceholders(input, ctx) {
78
78
  async function evaluate(expr, ctx) {
79
79
  try {
80
80
  const raw = expr.trim();
81
- const dotOnly = raw.match(/^ctx\.([a-zA-Z_$][a-zA-Z0-9_$]*(?:\.[a-zA-Z_$][a-zA-Z0-9_$]*)*)$/);
81
+ const dotOnly = raw.match(
82
+ /^ctx\.([a-zA-Z_$][a-zA-Z0-9_$]*)(?:\.([a-zA-Z_$][a-zA-Z0-9_$-]*(?:\.[a-zA-Z_$][a-zA-Z0-9_$-]*)*))?$/
83
+ );
82
84
  if (dotOnly) {
83
- const path = dotOnly[1];
84
- const segs = path.split(".");
85
- const first = segs.shift();
85
+ const first = dotOnly[1];
86
+ const rest = dotOnly[2];
86
87
  const base = await ctx[first];
87
- if (!segs.length) return base;
88
- return await asyncGetValuesByPath(base, segs.join("."));
88
+ if (!rest) return base;
89
+ const resolved = await asyncGetValuesByPath(base, rest);
90
+ if (typeof resolved !== "undefined" || !rest.includes("-")) {
91
+ return resolved;
92
+ }
89
93
  }
90
94
  const transformed = preprocessExpression(raw);
91
95
  const compartment = new Compartment({
@@ -0,0 +1,38 @@
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
+ type FilterTargetKey = string | string[] | undefined;
10
+ /**
11
+ * 为了:
12
+ * - 关联加载/缓存:fields 模式下确保包含主键
13
+ * - filterByTk 为数组时:尽量包含 filterTargetKey,以便按输入顺序对齐结果
14
+ *
15
+ * 该函数只返回“建议追加”的 key 字段(不会决定是否追加,由调用方根据是否启用 fields 决定)。
16
+ */
17
+ export declare function getExtraKeyFieldsForSelect(filterByTk: unknown, options: {
18
+ filterTargetKey?: FilterTargetKey;
19
+ pkAttr?: string;
20
+ pkIsValid?: boolean;
21
+ rawAttributes?: Record<string, unknown>;
22
+ }): string[];
23
+ export declare function mergeFieldsWithExtras(fields?: string[], extras?: string[]): string[] | undefined;
24
+ /**
25
+ * 根据 filterByTk 类型(单值/数组)查询并返回 JSON 数据:
26
+ * - 单值:返回 object | undefined
27
+ * - 数组:返回 array(空数组时返回 [],不会退化为无条件查询)
28
+ */
29
+ export declare function fetchRecordOrRecordsJson(repo: any, params: {
30
+ filterByTk: unknown;
31
+ preferFullRecord?: boolean;
32
+ fields?: string[];
33
+ appends?: string[];
34
+ filterTargetKey?: FilterTargetKey;
35
+ pkAttr?: string;
36
+ pkIsValid?: boolean;
37
+ }): Promise<unknown>;
38
+ export {};
@@ -0,0 +1,120 @@
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
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var records_exports = {};
28
+ __export(records_exports, {
29
+ fetchRecordOrRecordsJson: () => fetchRecordOrRecordsJson,
30
+ getExtraKeyFieldsForSelect: () => getExtraKeyFieldsForSelect,
31
+ mergeFieldsWithExtras: () => mergeFieldsWithExtras
32
+ });
33
+ module.exports = __toCommonJS(records_exports);
34
+ function uniqStrings(list) {
35
+ return Array.from(new Set(list));
36
+ }
37
+ function isPrimitiveTkArray(arr) {
38
+ return arr.every((v) => typeof v === "string" || typeof v === "number");
39
+ }
40
+ function getOrderKey(options) {
41
+ if (typeof options.filterTargetKey === "string") return options.filterTargetKey;
42
+ if (options.pkIsValid && options.pkAttr) return options.pkAttr;
43
+ return void 0;
44
+ }
45
+ function getExtraKeyFieldsForSelect(filterByTk, options) {
46
+ const extra = [];
47
+ if (options.pkIsValid && options.pkAttr) {
48
+ extra.push(options.pkAttr);
49
+ }
50
+ if (Array.isArray(filterByTk) && filterByTk.length > 0) {
51
+ const tkKeys = typeof options.filterTargetKey === "string" ? [options.filterTargetKey] : Array.isArray(options.filterTargetKey) ? options.filterTargetKey : [];
52
+ if (options.rawAttributes) {
53
+ for (const k of tkKeys) {
54
+ if (k && Object.prototype.hasOwnProperty.call(options.rawAttributes, k)) {
55
+ extra.push(k);
56
+ }
57
+ }
58
+ }
59
+ }
60
+ return uniqStrings(extra);
61
+ }
62
+ function mergeFieldsWithExtras(fields, extras = []) {
63
+ if (!Array.isArray(fields) || fields.length === 0 || extras.length === 0) return fields;
64
+ return uniqStrings([...fields, ...extras]);
65
+ }
66
+ function toJsonArray(rows) {
67
+ if (!Array.isArray(rows)) return [];
68
+ return rows.map((r) => (r == null ? void 0 : r.toJSON) ? r.toJSON() : r);
69
+ }
70
+ function reorderRecordsByFilterByTk(records, filterByTk, options) {
71
+ if (!Array.isArray(filterByTk) || filterByTk.length === 0) return records;
72
+ if (!isPrimitiveTkArray(filterByTk)) return records;
73
+ const orderKey = getOrderKey(options);
74
+ if (!orderKey) return records;
75
+ const map = /* @__PURE__ */ new Map();
76
+ for (const rec of records || []) {
77
+ const k = rec == null ? void 0 : rec[orderKey];
78
+ if (typeof k !== "undefined" && k !== null) {
79
+ map.set(String(k), rec);
80
+ }
81
+ }
82
+ const ordered = [];
83
+ const used = /* @__PURE__ */ new Set();
84
+ for (const tk of filterByTk) {
85
+ const k = String(tk);
86
+ const rec = map.get(k);
87
+ if (typeof rec !== "undefined") {
88
+ ordered.push(rec);
89
+ used.add(k);
90
+ }
91
+ }
92
+ for (const rec of records || []) {
93
+ const k = rec == null ? void 0 : rec[orderKey];
94
+ const ks = typeof k === "undefined" || k === null ? void 0 : String(k);
95
+ if (!ks || used.has(ks)) continue;
96
+ ordered.push(rec);
97
+ }
98
+ return ordered;
99
+ }
100
+ async function fetchRecordOrRecordsJson(repo, params) {
101
+ const { filterByTk, preferFullRecord, fields, appends } = params;
102
+ if (Array.isArray(filterByTk)) {
103
+ if (filterByTk.length === 0) return [];
104
+ const rows = await repo.find(
105
+ preferFullRecord ? { filterByTk } : { filterByTk, fields, appends }
106
+ );
107
+ const jsonArr = toJsonArray(rows);
108
+ return reorderRecordsByFilterByTk(jsonArr, filterByTk, params);
109
+ }
110
+ const rec = await repo.findOne(
111
+ preferFullRecord ? { filterByTk } : { filterByTk, fields, appends }
112
+ );
113
+ return rec ? rec.toJSON() : void 0;
114
+ }
115
+ // Annotate the CommonJS export names for ESM import in node:
116
+ 0 && (module.exports = {
117
+ fetchRecordOrRecordsJson,
118
+ getExtraKeyFieldsForSelect,
119
+ mergeFieldsWithExtras
120
+ });
@@ -39,4 +39,14 @@ declare class VariableRegistry {
39
39
  attachUsedVariables(ctx: HttpRequestContext, koaCtx: ResourcerContext, template: JSONValue, contextParams: any): Promise<void>;
40
40
  }
41
41
  export declare const variables: VariableRegistry;
42
+ /** 仅测试使用:重置变量注册表为内置默认集 */
43
+ /**
44
+ * 从使用路径推断查询所需的 fields 与 appends。
45
+ * @param paths 使用到的子路径数组
46
+ * @param params 显式参数(仅用于兼容签名)
47
+ */
48
+ export declare function inferSelectsFromUsage(paths?: string[], _params?: unknown): {
49
+ generatedAppends?: string[];
50
+ generatedFields?: string[];
51
+ };
42
52
  export {};