@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
@@ -36,12 +36,15 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
36
36
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
37
  var registry_exports = {};
38
38
  __export(registry_exports, {
39
+ inferSelectsFromUsage: () => inferSelectsFromUsage,
39
40
  variables: () => variables
40
41
  });
41
42
  module.exports = __toCommonJS(registry_exports);
42
43
  var import_lodash = __toESM(require("lodash"));
43
44
  var import_contexts = require("../template/contexts");
44
45
  var import_utils = require("@nocobase/utils");
46
+ var import_selects = require("./selects");
47
+ var import_records = require("./records");
45
48
  class VariableRegistry {
46
49
  vars = /* @__PURE__ */ new Map();
47
50
  register(def) {
@@ -90,40 +93,51 @@ if (!g[GLOBAL_KEY]) {
90
93
  g[GLOBAL_KEY] = new VariableRegistry();
91
94
  }
92
95
  const variables = g[GLOBAL_KEY];
93
- function inferSelectsFromUsage(paths = [], params) {
96
+ function inferSelectsFromUsage(paths = [], _params) {
94
97
  if (!Array.isArray(paths) || paths.length === 0) {
95
98
  return { generatedAppends: void 0, generatedFields: void 0 };
96
99
  }
97
100
  const appendSet = /* @__PURE__ */ new Set();
98
101
  const fieldSet = /* @__PURE__ */ new Set();
102
+ const normalizePath = (raw) => {
103
+ if (!raw) return "";
104
+ let s = String(raw);
105
+ s = s.replace(/\[(?:\d+)\]/g, "");
106
+ s = s.replace(/\[(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)')\]/g, (_m, g1, g2) => `.${g1 || g2}`);
107
+ s = s.replace(/\.\.+/g, ".");
108
+ s = s.replace(/^\./, "").replace(/\.$/, "");
109
+ return s;
110
+ };
99
111
  for (let path of paths) {
100
112
  if (!path) continue;
101
113
  while (/^\[(\d+)\](\.|$)/.test(path)) {
102
114
  path = path.replace(/^\[(\d+)\]\.?/, "");
103
115
  }
104
- if (!path) continue;
105
- let first = "";
106
- let rest = "";
107
- const mStr = path.match(/^\[(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)')\](.*)$/);
108
- if (mStr) {
109
- first = (mStr[1] ?? mStr[2]) || "";
110
- rest = mStr[3] || "";
111
- } else {
112
- const m = path.match(/^([^.[]+)([\s\S]*)$/);
113
- first = (m == null ? void 0 : m[1]) ?? "";
114
- rest = (m == null ? void 0 : m[2]) ?? "";
116
+ const norm = normalizePath(path);
117
+ if (!norm) continue;
118
+ const segments = norm.split(".").filter(Boolean);
119
+ if (segments.length === 0) continue;
120
+ if (segments.length === 1) {
121
+ fieldSet.add(segments[0]);
122
+ continue;
123
+ }
124
+ for (let i = 0; i < segments.length - 1; i++) {
125
+ appendSet.add(segments.slice(0, i + 1).join("."));
115
126
  }
116
- if (!first) continue;
117
- const hasDeep = rest.includes(".") || rest.includes("[");
118
- if (hasDeep) appendSet.add(first);
119
- else fieldSet.add(first);
127
+ fieldSet.add(segments.join("."));
120
128
  }
121
129
  const generatedAppends = appendSet.size ? Array.from(appendSet) : void 0;
122
130
  const generatedFields = fieldSet.size ? Array.from(fieldSet) : void 0;
123
131
  return { generatedAppends, generatedFields };
124
132
  }
125
- async function fetchRecordWithRequestCache(koaCtx, dataSourceKey, collection, filterByTk, fields, appends) {
133
+ async function fetchRecordWithRequestCache(koaCtx, dataSourceKey, collection, filterByTk, fields, appends, strictSelects, preferFullRecord, associationName, sourceId) {
134
+ var _a, _b, _c, _d, _e;
126
135
  try {
136
+ const log = (_b = (_a = koaCtx.app) == null ? void 0 : _a.logger) == null ? void 0 : _b.child({
137
+ module: "plugin-flow-engine",
138
+ submodule: "variables.resolve",
139
+ method: "fetchRecordWithRequestCache"
140
+ });
127
141
  const kctx = koaCtx;
128
142
  if (!kctx.state) kctx.state = {};
129
143
  if (!kctx.state["__varResolveBatchCache"]) {
@@ -133,42 +147,89 @@ async function fetchRecordWithRequestCache(koaCtx, dataSourceKey, collection, fi
133
147
  const ds = koaCtx.app.dataSourceManager.get(dataSourceKey || "main");
134
148
  const cm = ds.collectionManager;
135
149
  if (!(cm == null ? void 0 : cm.db)) return void 0;
136
- const repo = cm.db.getRepository(collection);
150
+ const repo = associationName && typeof sourceId !== "undefined" ? cm.db.getRepository(associationName, sourceId) : cm.db.getRepository(collection);
151
+ const modelInfo = (_c = repo.collection) == null ? void 0 : _c.model;
152
+ const pkAttr = modelInfo == null ? void 0 : modelInfo.primaryKeyAttribute;
153
+ const pkIsValid = pkAttr && (modelInfo == null ? void 0 : modelInfo.rawAttributes) && Object.prototype.hasOwnProperty.call(modelInfo.rawAttributes, pkAttr);
154
+ const collectionInfo = repo == null ? void 0 : repo.collection;
155
+ const filterTargetKey = collectionInfo == null ? void 0 : collectionInfo.filterTargetKey;
156
+ const extraKeys = (0, import_records.getExtraKeyFieldsForSelect)(filterByTk, {
157
+ filterTargetKey,
158
+ pkAttr,
159
+ pkIsValid,
160
+ rawAttributes: (modelInfo == null ? void 0 : modelInfo.rawAttributes) || void 0
161
+ });
162
+ const effectiveExtras = strictSelects && Array.isArray(extraKeys) && extraKeys.length ? extraKeys.filter((k) => k === pkAttr) : extraKeys;
163
+ const fieldsWithExtras = (0, import_records.mergeFieldsWithExtras)(fields, effectiveExtras);
164
+ const cacheKeyFields = preferFullRecord && pkIsValid ? void 0 : Array.isArray(fieldsWithExtras) ? [...fieldsWithExtras].sort() : void 0;
165
+ const cacheKeyAppends = preferFullRecord ? void 0 : Array.isArray(appends) ? [...appends].sort() : void 0;
137
166
  const keyObj = {
138
167
  ds: dataSourceKey || "main",
139
168
  c: collection,
140
169
  tk: filterByTk,
141
- f: Array.isArray(fields) ? [...fields].sort() : void 0,
142
- a: Array.isArray(appends) ? [...appends].sort() : void 0
170
+ f: cacheKeyFields,
171
+ a: cacheKeyAppends,
172
+ full: preferFullRecord ? true : void 0,
173
+ assoc: associationName,
174
+ sid: typeof sourceId === "undefined" ? void 0 : sourceId
143
175
  };
144
176
  const key = JSON.stringify(keyObj);
145
177
  if (cache) {
146
- if (cache.has(key)) return cache.get(key);
147
- const needFields = Array.isArray(fields) ? new Set(fields) : void 0;
148
- const needAppends = Array.isArray(appends) ? new Set(appends) : void 0;
149
- let fallbackAny = void 0;
150
- for (const [cacheKey, cacheVal] of cache.entries()) {
151
- const parsed = JSON.parse(cacheKey);
152
- if (!parsed || parsed.ds !== keyObj.ds || parsed.c !== keyObj.c || parsed.tk !== keyObj.tk) continue;
153
- const cachedFields = Array.isArray(parsed.f) ? new Set(parsed.f) : void 0;
154
- const cachedAppends = Array.isArray(parsed.a) ? new Set(parsed.a) : void 0;
155
- const fieldsOk = !needFields || cachedFields && [...needFields].every((x) => cachedFields.has(x));
156
- const appendsOk = !needAppends || cachedAppends && [...needAppends].every((x) => cachedAppends.has(x));
157
- if (fieldsOk && appendsOk) return cacheVal;
158
- if (typeof fallbackAny === "undefined") fallbackAny = cacheVal;
178
+ if (cache.has(key)) {
179
+ return cache.get(key);
180
+ }
181
+ if (!strictSelects) {
182
+ const needFields = !preferFullRecord && Array.isArray(fieldsWithExtras) ? [...new Set(fieldsWithExtras)] : void 0;
183
+ const needAppends = !preferFullRecord && Array.isArray(appends) ? new Set(appends) : void 0;
184
+ for (const [cacheKey, cacheVal] of cache.entries()) {
185
+ const parsed = JSON.parse(cacheKey);
186
+ if (!parsed || parsed.ds !== keyObj.ds || parsed.c !== keyObj.c || !import_lodash.default.isEqual(parsed.tk, keyObj.tk) || parsed.assoc !== keyObj.assoc || !import_lodash.default.isEqual(parsed.sid, keyObj.sid))
187
+ continue;
188
+ const cachedFields = new Set(parsed.f || []);
189
+ const cachedAppends = new Set(parsed.a || []);
190
+ const fieldCoveredByAppends = (fieldPath) => {
191
+ const p = String(fieldPath || "");
192
+ for (const a of cachedAppends) {
193
+ if (!a) continue;
194
+ if (p === a || p.startsWith(a + ".")) return true;
195
+ }
196
+ return false;
197
+ };
198
+ const fieldsOk = needFields ? needFields.every((f) => cachedFields.has(f) || fieldCoveredByAppends(f)) : parsed.f === void 0;
199
+ const appendsOk = !needAppends || [...needAppends].every((a) => cachedAppends.has(a));
200
+ const fullOk = preferFullRecord ? parsed.full === true : true;
201
+ if (fieldsOk && appendsOk && fullOk) {
202
+ return cacheVal;
203
+ }
204
+ }
159
205
  }
160
- if (typeof fallbackAny !== "undefined") return fallbackAny;
161
206
  }
162
- const tk = filterByTk;
163
- const rec = await repo.findOne({
164
- filterByTk: tk,
165
- fields,
166
- appends
207
+ const json = await (0, import_records.fetchRecordOrRecordsJson)(repo, {
208
+ filterByTk,
209
+ preferFullRecord,
210
+ fields: fieldsWithExtras,
211
+ appends,
212
+ filterTargetKey,
213
+ pkAttr,
214
+ pkIsValid
167
215
  });
168
- const json = rec ? rec.toJSON() : void 0;
169
216
  if (cache) cache.set(key, json);
170
217
  return json;
171
- } catch (_2) {
218
+ } catch (e) {
219
+ const log = (_e = (_d = koaCtx.app) == null ? void 0 : _d.logger) == null ? void 0 : _e.child({
220
+ module: "plugin-flow-engine",
221
+ submodule: "variables.resolve",
222
+ method: "fetchRecordWithRequestCache"
223
+ });
224
+ const errMsg = e instanceof Error ? e.message : String(e);
225
+ log == null ? void 0 : log.warn("[variables.resolve] fetchRecordWithRequestCache error", {
226
+ ds: dataSourceKey,
227
+ collection,
228
+ tk: filterByTk,
229
+ fields,
230
+ appends,
231
+ error: errMsg
232
+ });
172
233
  return void 0;
173
234
  }
174
235
  }
@@ -183,19 +244,107 @@ function attachGenericRecordVariables(flowCtx, koaCtx, usage, contextParams) {
183
244
  for (const varName of Object.keys(usage)) {
184
245
  const usedPaths = usage[varName] || [];
185
246
  const topParams = import_lodash.default.get(contextParams, varName);
247
+ const deepRecordMap = /* @__PURE__ */ new Map();
248
+ const cp = contextParams;
249
+ if (cp && typeof cp === "object") {
250
+ const cpRec = cp;
251
+ for (const key of Object.keys(cpRec)) {
252
+ if (!key || key !== varName && !key.startsWith(`${varName}.`)) continue;
253
+ if (key === varName) continue;
254
+ const val = cpRec[key];
255
+ if (!isRecordParams(val)) continue;
256
+ const relative = key.slice(varName.length + 1);
257
+ if (!relative) continue;
258
+ deepRecordMap.set(relative, val);
259
+ }
260
+ }
186
261
  if (isRecordParams(topParams)) {
187
- const { generatedAppends, generatedFields } = inferSelectsFromUsage(usedPaths, topParams);
262
+ const usedPathsForBase = deepRecordMap.size ? (usedPaths || []).filter((p) => {
263
+ if (!p) return true;
264
+ for (const relative of deepRecordMap.keys()) {
265
+ if (!relative) continue;
266
+ if (p === relative || p.startsWith(relative + ".") || p.startsWith(relative + "[")) return false;
267
+ }
268
+ return true;
269
+ }) : usedPaths || [];
270
+ const hasDirectRefTop = usedPathsForBase.some((p) => p === "");
188
271
  flowCtx.defineProperty(varName, {
189
272
  get: async () => {
190
273
  const dataSourceKey = (topParams == null ? void 0 : topParams.dataSourceKey) || "main";
191
- return await fetchRecordWithRequestCache(
274
+ const strictSelects = Array.isArray(topParams == null ? void 0 : topParams.fields) || Array.isArray(topParams == null ? void 0 : topParams.appends);
275
+ let { generatedAppends, generatedFields } = inferSelectsFromUsage(usedPathsForBase, topParams);
276
+ if (Array.isArray(topParams == null ? void 0 : topParams.fields)) generatedFields = topParams.fields;
277
+ if (Array.isArray(topParams == null ? void 0 : topParams.appends)) generatedAppends = topParams.appends;
278
+ const fixed = (0, import_selects.adjustSelectsForCollection)(
192
279
  koaCtx,
193
280
  dataSourceKey,
194
281
  topParams.collection,
195
- topParams.filterByTk,
196
282
  generatedFields,
197
283
  generatedAppends
198
284
  );
285
+ const base = await fetchRecordWithRequestCache(
286
+ koaCtx,
287
+ dataSourceKey,
288
+ topParams.collection,
289
+ topParams.filterByTk,
290
+ fixed.fields,
291
+ fixed.appends,
292
+ strictSelects,
293
+ hasDirectRefTop,
294
+ topParams.associationName,
295
+ topParams.sourceId
296
+ );
297
+ if (!deepRecordMap.size) return base;
298
+ const merged = base && typeof base === "object" && !Array.isArray(base) ? { ...base } : {};
299
+ const setClonedPath = (obj, path, value) => {
300
+ const segs = String(path || "").split(".").filter(Boolean);
301
+ if (!segs.length) return;
302
+ if (segs.length === 1) {
303
+ obj[segs[0]] = value;
304
+ return;
305
+ }
306
+ let cur = obj;
307
+ for (let i = 0; i < segs.length - 1; i++) {
308
+ const seg = segs[i];
309
+ const prev = cur[seg];
310
+ const next = prev && typeof prev === "object" && !Array.isArray(prev) ? { ...prev } : {};
311
+ cur[seg] = next;
312
+ cur = next;
313
+ }
314
+ cur[segs[segs.length - 1]] = value;
315
+ };
316
+ const buildNestedPromise = (recordParams, relative) => {
317
+ const subPaths = (usedPaths || []).map((p) => p === relative ? "" : p.startsWith(relative + ".") ? p.slice(relative.length + 1) : "").filter((x) => x !== "");
318
+ const hasDirectRef = (usedPaths || []).some((p) => p === relative);
319
+ const dataSourceKey2 = (recordParams == null ? void 0 : recordParams.dataSourceKey) || "main";
320
+ const strictSelects2 = Array.isArray(recordParams == null ? void 0 : recordParams.fields) || Array.isArray(recordParams == null ? void 0 : recordParams.appends);
321
+ let { generatedAppends: generatedAppends2, generatedFields: generatedFields2 } = inferSelectsFromUsage(subPaths, recordParams);
322
+ if (Array.isArray(recordParams == null ? void 0 : recordParams.fields)) generatedFields2 = recordParams.fields;
323
+ if (Array.isArray(recordParams == null ? void 0 : recordParams.appends)) generatedAppends2 = recordParams.appends;
324
+ const fixed2 = (0, import_selects.adjustSelectsForCollection)(
325
+ koaCtx,
326
+ dataSourceKey2,
327
+ recordParams.collection,
328
+ generatedFields2,
329
+ generatedAppends2
330
+ );
331
+ return fetchRecordWithRequestCache(
332
+ koaCtx,
333
+ dataSourceKey2,
334
+ recordParams.collection,
335
+ recordParams.filterByTk,
336
+ fixed2.fields,
337
+ fixed2.appends,
338
+ strictSelects2,
339
+ hasDirectRef,
340
+ recordParams.associationName,
341
+ recordParams.sourceId
342
+ );
343
+ };
344
+ for (const [relative, recordParams] of deepRecordMap.entries()) {
345
+ setClonedPath(merged, relative, buildNestedPromise(recordParams, relative));
346
+ }
347
+ return merged;
199
348
  },
200
349
  cache: true
201
350
  });
@@ -228,44 +377,97 @@ function attachGenericRecordVariables(flowCtx, koaCtx, usage, contextParams) {
228
377
  segmentMap.set(seg, arr);
229
378
  }
230
379
  const segEntries = Array.from(segmentMap.entries());
231
- const recordChildren = segEntries.filter(([seg]) => {
380
+ const oneLevelRecordChildren = segEntries.filter(([seg]) => {
232
381
  const idx = parseIndexSegment(seg);
233
382
  const nestedObj = import_lodash.default.get(contextParams, [varName, seg]) ?? (idx ? import_lodash.default.get(contextParams, [varName, idx]) : void 0);
234
383
  const dotted = (contextParams || {})[`${varName}.${seg}`] ?? (idx ? (contextParams || {})[`${varName}.${idx}`] : void 0);
235
384
  return isRecordParams(nestedObj) || isRecordParams(dotted);
236
385
  });
237
- if (!recordChildren.length) continue;
386
+ if (!oneLevelRecordChildren.length && deepRecordMap.size === 0) continue;
238
387
  flowCtx.defineProperty(varName, {
239
388
  get: () => {
240
- const subContext = new import_contexts.ServerBaseContext();
241
- for (const [seg, remainders] of recordChildren) {
242
- const idx = parseIndexSegment(seg);
243
- const recordParams = import_lodash.default.get(contextParams, [varName, seg]) ?? (idx ? import_lodash.default.get(contextParams, [varName, idx]) : void 0) ?? (contextParams || {})[`${varName}.${seg}`] ?? (idx ? (contextParams || {})[`${varName}.${idx}`] : void 0);
244
- let effRemainders = remainders.filter((r) => !!r);
245
- if (!effRemainders.length) {
246
- const all = usedPaths.map(
247
- (p) => p.startsWith(`${seg}.`) ? p.slice(seg.length + 1) : p.startsWith(`${seg}[`) ? p.slice(seg.length) : ""
248
- ).filter((x) => !!x);
249
- if (all.length) effRemainders = all;
250
- }
251
- const { generatedAppends, generatedFields } = inferSelectsFromUsage(effRemainders, recordParams);
252
- const definitionKey = idx ?? seg;
253
- subContext.defineProperty(definitionKey, {
389
+ const root = new import_contexts.ServerBaseContext();
390
+ const definedFirstLevel = /* @__PURE__ */ new Set();
391
+ const defineRecordGetter = (container, key, recordParams, subPaths = [], preferFull) => {
392
+ const strictSelects = Array.isArray(recordParams == null ? void 0 : recordParams.fields) || Array.isArray(recordParams == null ? void 0 : recordParams.appends);
393
+ let { generatedAppends, generatedFields } = inferSelectsFromUsage(subPaths, recordParams);
394
+ if (Array.isArray(recordParams == null ? void 0 : recordParams.fields)) generatedFields = recordParams.fields;
395
+ if (Array.isArray(recordParams == null ? void 0 : recordParams.appends)) generatedAppends = recordParams.appends;
396
+ container.defineProperty(key, {
254
397
  get: async () => {
255
398
  const dataSourceKey = (recordParams == null ? void 0 : recordParams.dataSourceKey) || "main";
256
- return await fetchRecordWithRequestCache(
399
+ const fixed = (0, import_selects.adjustSelectsForCollection)(
257
400
  koaCtx,
258
401
  dataSourceKey,
259
402
  recordParams.collection,
260
- recordParams.filterByTk,
261
403
  generatedFields,
262
404
  generatedAppends
263
405
  );
406
+ return await fetchRecordWithRequestCache(
407
+ koaCtx,
408
+ dataSourceKey,
409
+ recordParams.collection,
410
+ recordParams.filterByTk,
411
+ fixed.fields,
412
+ fixed.appends,
413
+ strictSelects,
414
+ preferFull || ((subPaths == null ? void 0 : subPaths.length) ?? 0) === 0,
415
+ recordParams.associationName,
416
+ recordParams.sourceId
417
+ );
264
418
  },
265
419
  cache: true
266
420
  });
421
+ };
422
+ const subContainers = /* @__PURE__ */ new Map();
423
+ const ensureSubContainer = (parent, key) => {
424
+ let map = subContainers.get(parent);
425
+ if (!map) {
426
+ map = /* @__PURE__ */ new Map();
427
+ subContainers.set(parent, map);
428
+ }
429
+ let child = map.get(key);
430
+ if (!child) {
431
+ const inst = new import_contexts.ServerBaseContext();
432
+ parent.defineProperty(key, { get: () => inst.createProxy(), cache: true });
433
+ map.set(key, inst);
434
+ child = inst;
435
+ }
436
+ return child;
437
+ };
438
+ for (const [seg, remainders] of oneLevelRecordChildren) {
439
+ const idx = parseIndexSegment(seg);
440
+ const recordParams = import_lodash.default.get(contextParams, [varName, seg]) ?? (idx ? import_lodash.default.get(contextParams, [varName, idx]) : void 0) ?? (contextParams || {})[`${varName}.${seg}`] ?? (idx ? (contextParams || {})[`${varName}.${idx}`] : void 0);
441
+ let effRemainders = (remainders || []).filter((r) => !!r);
442
+ if (!effRemainders.length) {
443
+ const all = usedPaths.map(
444
+ (p) => p.startsWith(`${seg}.`) ? p.slice(seg.length + 1) : p.startsWith(`${seg}[`) ? p.slice(seg.length) : ""
445
+ ).filter((x) => !!x);
446
+ if (all.length) effRemainders = all;
447
+ }
448
+ const hasDirectRefOne = (usedPaths || []).some((p) => p === seg || !!idx && p === `[${idx}]`);
449
+ defineRecordGetter(root, idx ?? seg, recordParams, effRemainders, hasDirectRefOne);
450
+ definedFirstLevel.add(idx ?? seg);
451
+ }
452
+ for (const [relative, recordParams] of deepRecordMap.entries()) {
453
+ const segs = String(relative).split(".").filter(Boolean);
454
+ if (segs.length === 0) continue;
455
+ const first = segs[0];
456
+ let container;
457
+ if (definedFirstLevel.has(first)) {
458
+ continue;
459
+ } else {
460
+ container = root;
461
+ for (let i = 0; i < segs.length - 1; i++) {
462
+ container = ensureSubContainer(container, segs[i]);
463
+ }
464
+ }
465
+ const leaf = segs[segs.length - 1];
466
+ const subPaths = (usedPaths || []).map((p) => p === relative ? "" : p.startsWith(relative + ".") ? p.slice(relative.length + 1) : "").filter((x) => x !== "");
467
+ const hasDirectRef = (usedPaths || []).some((p) => p === relative);
468
+ defineRecordGetter(container, leaf, recordParams, subPaths, hasDirectRef);
267
469
  }
268
- return subContext.createProxy();
470
+ return root.createProxy();
269
471
  },
270
472
  cache: true
271
473
  });
@@ -285,7 +487,18 @@ function registerBuiltInVariables(reg) {
285
487
  const authObj = koaCtx.auth;
286
488
  const uid = (_a = authObj == null ? void 0 : authObj.user) == null ? void 0 : _a.id;
287
489
  if (typeof uid === "undefined" || uid === null) return void 0;
288
- return await fetchRecordWithRequestCache(koaCtx, "main", "users", uid, generatedFields, generatedAppends);
490
+ return await fetchRecordWithRequestCache(
491
+ koaCtx,
492
+ "main",
493
+ "users",
494
+ uid,
495
+ generatedFields,
496
+ generatedAppends,
497
+ false,
498
+ void 0,
499
+ void 0,
500
+ void 0
501
+ );
289
502
  },
290
503
  cache: true
291
504
  });
@@ -295,5 +508,6 @@ function registerBuiltInVariables(reg) {
295
508
  registerBuiltInVariables(variables);
296
509
  // Annotate the CommonJS export names for ESM import in node:
297
510
  0 && (module.exports = {
511
+ inferSelectsFromUsage,
298
512
  variables
299
513
  });
@@ -0,0 +1,19 @@
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 { ResourcerContext } from '@nocobase/resourcer';
10
+ /**
11
+ * 针对给定集合,修正 selects:
12
+ * - 若 fields 中包含单段且为关联名(如 'roles'),则将其从 fields 移到 appends。
13
+ * - 若 fields 中包含多段且首段为关联名(如 'roles.name'),确保 appends 包含该关联名,并将首段替换为模型真实关联名。
14
+ * - 非关联字段:仅当模型存在该属性(或其 snake/camel 变体)时才保留,否则丢弃以避免数据库错误。
15
+ */
16
+ export declare function adjustSelectsForCollection(koaCtx: ResourcerContext, dataSourceKey: string, collection: string, fields?: string[], appends?: string[]): {
17
+ fields?: string[];
18
+ appends?: string[];
19
+ };
@@ -0,0 +1,80 @@
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 selects_exports = {};
28
+ __export(selects_exports, {
29
+ adjustSelectsForCollection: () => adjustSelectsForCollection
30
+ });
31
+ module.exports = __toCommonJS(selects_exports);
32
+ function adjustSelectsForCollection(koaCtx, dataSourceKey, collection, fields, appends) {
33
+ var _a, _b, _c, _d;
34
+ const ds = koaCtx.app.dataSourceManager.get(dataSourceKey || "main");
35
+ const cm = ds.collectionManager;
36
+ const coll = (_b = (_a = cm == null ? void 0 : cm.db) == null ? void 0 : _a.getCollection) == null ? void 0 : _b.call(_a, collection);
37
+ const assocKeys = Object.keys(((_c = coll == null ? void 0 : coll.model) == null ? void 0 : _c.associations) || {});
38
+ const rawAttrs = ((_d = coll == null ? void 0 : coll.model) == null ? void 0 : _d.rawAttributes) || {};
39
+ const toCamel = (s) => s.replace(/_([a-zA-Z0-9])/g, (_m, c) => String(c).toUpperCase());
40
+ const toSnake = (s) => s.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
41
+ const assocMap = /* @__PURE__ */ new Map();
42
+ for (const k of assocKeys) {
43
+ assocMap.set(k, k);
44
+ assocMap.set(toSnake(k), k);
45
+ assocMap.set(toCamel(k), k);
46
+ }
47
+ const outFields = [];
48
+ const outAppends = new Set(appends || []);
49
+ for (const f of fields || []) {
50
+ const segs = String(f).split(".").filter(Boolean);
51
+ if (!segs.length) continue;
52
+ const first = segs[0];
53
+ const assocCanonical = assocMap.get(first) || assocMap.get(toCamel(first)) || assocMap.get(toSnake(first));
54
+ if (assocCanonical) {
55
+ outAppends.add(assocCanonical);
56
+ if (segs.length === 1) {
57
+ continue;
58
+ }
59
+ outFields.push([assocCanonical, ...segs.slice(1)].join("."));
60
+ continue;
61
+ }
62
+ if (rawAttrs[first]) {
63
+ outFields.push(f);
64
+ } else if (rawAttrs[toSnake(first)]) {
65
+ outFields.push([toSnake(first), ...segs.slice(1)].join("."));
66
+ } else if (rawAttrs[toCamel(first)]) {
67
+ outFields.push([toCamel(first), ...segs.slice(1)].join("."));
68
+ } else {
69
+ continue;
70
+ }
71
+ }
72
+ return {
73
+ fields: outFields.length ? outFields : void 0,
74
+ appends: outAppends.size ? Array.from(outAppends) : void 0
75
+ };
76
+ }
77
+ // Annotate the CommonJS export names for ESM import in node:
78
+ 0 && (module.exports = {
79
+ adjustSelectsForCollection
80
+ });
@@ -0,0 +1,17 @@
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 type { ResourcerContext } from '@nocobase/resourcer';
10
+ import type { JSONValue } from '../template/resolver';
11
+ /**
12
+ * 预取:构建“同记录”的字段/关联并集,一次查询写入 ctx.state.__varResolveBatchCache,供后续解析复用
13
+ */
14
+ export declare function prefetchRecordsForResolve(koaCtx: ResourcerContext, items: Array<{
15
+ template: JSONValue;
16
+ contextParams?: Record<string, unknown>;
17
+ }>): Promise<void>;