@nocobase/plugin-flow-engine 3.0.0-alpha.5 → 3.0.0-alpha.7

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 (50) hide show
  1. package/dist/externalVersion.js +9 -9
  2. package/dist/node_modules/@ant-design/icons-svg/package.json +1 -1
  3. package/dist/node_modules/acorn/package.json +1 -1
  4. package/dist/node_modules/acorn-jsx/package.json +1 -1
  5. package/dist/node_modules/acorn-walk/package.json +1 -1
  6. package/dist/node_modules/ses/package.json +1 -1
  7. package/dist/node_modules/zod/package.json +1 -1
  8. package/dist/server/flow-surfaces/builder.js +5 -0
  9. package/dist/server/flow-surfaces/catalog.js +27 -8
  10. package/dist/server/flow-surfaces/configure-options.js +17 -1
  11. package/dist/server/flow-surfaces/service-utils.d.ts +1 -0
  12. package/dist/server/flow-surfaces/service-utils.js +17 -0
  13. package/dist/server/flow-surfaces/service.js +9 -0
  14. package/dist/server/index.d.ts +2 -0
  15. package/dist/server/index.js +7 -0
  16. package/dist/server/plugin.d.ts +11 -0
  17. package/dist/server/plugin.js +85 -3
  18. package/dist/server/repository.d.ts +8 -0
  19. package/dist/server/repository.js +29 -0
  20. package/dist/server/template/context-keys.d.ts +14 -0
  21. package/dist/server/template/context-keys.js +86 -0
  22. package/dist/server/template/contexts.d.ts +5 -8
  23. package/dist/server/template/contexts.js +43 -51
  24. package/dist/server/template/resolver.d.ts +5 -13
  25. package/dist/server/template/resolver.js +188 -257
  26. package/dist/server/template/variable-expression.d.ts +72 -0
  27. package/dist/server/template/variable-expression.js +597 -0
  28. package/dist/server/variables/allow-list.d.ts +48 -0
  29. package/dist/server/variables/allow-list.js +374 -0
  30. package/dist/server/variables/form-item-record-slot-resolvers.d.ts +10 -0
  31. package/dist/server/variables/form-item-record-slot-resolvers.js +280 -0
  32. package/dist/server/variables/record-bindings.d.ts +42 -0
  33. package/dist/server/variables/record-bindings.js +196 -0
  34. package/dist/server/variables/record-projection.d.ts +10 -0
  35. package/dist/server/variables/record-projection.js +140 -0
  36. package/dist/server/variables/record-slot-policy.d.ts +23 -0
  37. package/dist/server/variables/record-slot-policy.js +100 -0
  38. package/dist/server/variables/record-slot-resolvers.d.ts +46 -0
  39. package/dist/server/variables/record-slot-resolvers.js +115 -0
  40. package/dist/server/variables/records.d.ts +47 -1
  41. package/dist/server/variables/records.js +279 -5
  42. package/dist/server/variables/registry.d.ts +33 -10
  43. package/dist/server/variables/registry.js +197 -362
  44. package/dist/server/variables/resolve.d.ts +18 -2
  45. package/dist/server/variables/resolve.js +79 -13
  46. package/dist/server/variables/runjs-variable-dependencies.d.ts +21 -0
  47. package/dist/server/variables/runjs-variable-dependencies.js +313 -0
  48. package/dist/server/variables/utils.d.ts +3 -6
  49. package/dist/server/variables/utils.js +50 -111
  50. package/package.json +2 -2
@@ -0,0 +1,23 @@
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 { Application } from '@nocobase/server';
10
+ import type { AnalyzedTemplate } from '../template/variable-expression';
11
+ import { type RecordSlotResolved, type RecordSlotResolverInput, type RecordSlotResolverRegistration } from './record-slot-resolvers';
12
+ export type RecordSlotPolicy = RecordSlotResolved;
13
+ export type RecordSlotPolicies = ReadonlyMap<string, RecordSlotPolicy>;
14
+ export type FlowModelVariableContract = Readonly<{
15
+ allowedPaths: ReadonlySet<string>;
16
+ recordSlots: RecordSlotPolicies;
17
+ }>;
18
+ export type CompileRecordSlotPoliciesOptions = Omit<RecordSlotResolverInput, 'path'> & Readonly<{
19
+ app: Application;
20
+ }>;
21
+ export declare function createBuiltInRecordSlotResolvers(): readonly RecordSlotResolverRegistration[];
22
+ export declare function compileRecordSlotPolicies(analysis: Pick<AnalyzedTemplate, 'paths'>, options: CompileRecordSlotPoliciesOptions): Promise<RecordSlotPolicies>;
23
+ export declare function createFlowModelVariableContract(analysis: AnalyzedTemplate, options: CompileRecordSlotPoliciesOptions): Promise<FlowModelVariableContract>;
@@ -0,0 +1,100 @@
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 record_slot_policy_exports = {};
28
+ __export(record_slot_policy_exports, {
29
+ compileRecordSlotPolicies: () => compileRecordSlotPolicies,
30
+ createBuiltInRecordSlotResolvers: () => createBuiltInRecordSlotResolvers,
31
+ createFlowModelVariableContract: () => createFlowModelVariableContract
32
+ });
33
+ module.exports = __toCommonJS(record_slot_policy_exports);
34
+ var import_record_slot_resolvers = require("./record-slot-resolvers");
35
+ function resolved(slot) {
36
+ return { status: "resolved", slot };
37
+ }
38
+ function createBuiltInRecordSlotResolvers() {
39
+ return [
40
+ ...["record", "responseRecord", "clickedRowRecord"].map(
41
+ (varName) => ({
42
+ owner: "@nocobase/plugin-flow-engine",
43
+ id: `direct:${varName}`,
44
+ match: (path) => path.varName === varName,
45
+ resolve: () => resolved([])
46
+ })
47
+ ),
48
+ {
49
+ owner: "@nocobase/plugin-flow-engine",
50
+ id: "view:record",
51
+ match: (path) => path.varName === "view" && path.runtimeSegments[0] === "record",
52
+ resolve: () => resolved(["record"])
53
+ },
54
+ {
55
+ owner: "@nocobase/plugin-flow-engine",
56
+ id: "popup:record",
57
+ match: (path) => {
58
+ if (path.varName !== "popup") return false;
59
+ let index = 0;
60
+ while (path.runtimeSegments[index] === "parent") index += 1;
61
+ return path.runtimeSegments[index] === "record" || path.runtimeSegments[index] === "sourceRecord";
62
+ },
63
+ resolve: ({ path }) => {
64
+ let index = 0;
65
+ while (path.runtimeSegments[index] === "parent") index += 1;
66
+ return resolved(path.runtimeSegments.slice(0, index + 1));
67
+ }
68
+ }
69
+ ];
70
+ }
71
+ function samePolicy(left, right) {
72
+ return left.slot.length === right.slot.length && left.slot.every((segment, index) => segment === right.slot[index]);
73
+ }
74
+ async function compileRecordSlotPolicies(analysis, options) {
75
+ const registry = (0, import_record_slot_resolvers.getRecordSlotResolverRegistry)(options.app);
76
+ const occurrences = /* @__PURE__ */ new Map();
77
+ for (const path of analysis.paths) {
78
+ const policies = occurrences.get(path.canonicalKey) || [];
79
+ policies.push(await registry.resolve({ ...options, path }));
80
+ occurrences.set(path.canonicalKey, policies);
81
+ }
82
+ const result = /* @__PURE__ */ new Map();
83
+ for (const [canonicalKey, policies] of occurrences) {
84
+ const first = policies[0];
85
+ if (first && policies.every((policy) => !!policy && samePolicy(policy, first))) result.set(canonicalKey, first);
86
+ }
87
+ return result;
88
+ }
89
+ async function createFlowModelVariableContract(analysis, options) {
90
+ return Object.freeze({
91
+ allowedPaths: new Set(analysis.paths.map((path) => path.canonicalKey)),
92
+ recordSlots: await compileRecordSlotPolicies(analysis, options)
93
+ });
94
+ }
95
+ // Annotate the CommonJS export names for ESM import in node:
96
+ 0 && (module.exports = {
97
+ compileRecordSlotPolicies,
98
+ createBuiltInRecordSlotResolvers,
99
+ createFlowModelVariableContract
100
+ });
@@ -0,0 +1,46 @@
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 { Application } from '@nocobase/server';
11
+ import type { PathSegment, VariablePathRef } from '../template/variable-expression';
12
+ type MaybePromise<T> = T | Promise<T>;
13
+ export type RecordSlotResolved = Readonly<{
14
+ status: 'resolved';
15
+ slot: readonly PathSegment[];
16
+ }>;
17
+ export type RecordSlotResolverResult = RecordSlotResolved | Readonly<{
18
+ status: 'deny' | 'abstain';
19
+ }>;
20
+ export type RecordSlotResolverInput = Readonly<{
21
+ path: VariablePathRef;
22
+ currentNode?: unknown;
23
+ loadAncestors?: () => Promise<readonly unknown[]>;
24
+ ctx?: ResourcerContext;
25
+ getCollection?: (dataSourceKey: string, collection: string) => unknown;
26
+ }>;
27
+ export type RecordSlotResolverRegistration = Readonly<{
28
+ owner: string;
29
+ id: string;
30
+ needsAncestors?: boolean;
31
+ match: (path: VariablePathRef) => boolean;
32
+ resolve: (input: RecordSlotResolverInput) => MaybePromise<RecordSlotResolverResult>;
33
+ }>;
34
+ export declare class RecordSlotResolverRegistry {
35
+ private readonly registrations;
36
+ has(owner: string, id: string): boolean;
37
+ register(registration: RecordSlotResolverRegistration): () => void;
38
+ resolve(input: RecordSlotResolverInput): Promise<RecordSlotResolved | undefined>;
39
+ }
40
+ export declare function getRecordSlotResolverRegistry(app: Application): RecordSlotResolverRegistry;
41
+ export declare function createNestedRecordSlotResolver(registration: Readonly<{
42
+ owner: string;
43
+ id: string;
44
+ varName: string;
45
+ }>): RecordSlotResolverRegistration;
46
+ export {};
@@ -0,0 +1,115 @@
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 record_slot_resolvers_exports = {};
28
+ __export(record_slot_resolvers_exports, {
29
+ RecordSlotResolverRegistry: () => RecordSlotResolverRegistry,
30
+ createNestedRecordSlotResolver: () => createNestedRecordSlotResolver,
31
+ getRecordSlotResolverRegistry: () => getRecordSlotResolverRegistry
32
+ });
33
+ module.exports = __toCommonJS(record_slot_resolvers_exports);
34
+ function normalizeResolved(result) {
35
+ if (result.status !== "resolved" || !Array.isArray(result.slot)) return void 0;
36
+ if (result.slot.some(
37
+ (segment) => typeof segment !== "string" && typeof segment !== "number" || typeof segment === "string" && segment.includes("*") || typeof segment === "number" && (!Number.isSafeInteger(segment) || segment < 0)
38
+ )) {
39
+ return void 0;
40
+ }
41
+ return Object.freeze({ status: "resolved", slot: Object.freeze([...result.slot]) });
42
+ }
43
+ function sameResolved(left, right) {
44
+ return left.slot.length === right.slot.length && left.slot.every((segment, index) => segment === right.slot[index]);
45
+ }
46
+ class RecordSlotResolverRegistry {
47
+ registrations = /* @__PURE__ */ new Map();
48
+ has(owner, id) {
49
+ var _a;
50
+ return ((_a = this.registrations.get(owner.trim())) == null ? void 0 : _a.has(id.trim())) === true;
51
+ }
52
+ register(registration) {
53
+ const owner = registration.owner.trim();
54
+ const id = registration.id.trim();
55
+ if (!owner || !id) throw new TypeError("Record Slot resolver owner and id are required");
56
+ const entry = Object.freeze({ ...registration, owner, id });
57
+ let owned = this.registrations.get(owner);
58
+ if (!owned) {
59
+ owned = /* @__PURE__ */ new Map();
60
+ this.registrations.set(owner, owned);
61
+ }
62
+ owned.set(id, entry);
63
+ return () => {
64
+ const current = this.registrations.get(owner);
65
+ if ((current == null ? void 0 : current.get(id)) !== entry) return;
66
+ current.delete(id);
67
+ if (!current.size) this.registrations.delete(owner);
68
+ };
69
+ }
70
+ async resolve(input) {
71
+ const resolutions = [];
72
+ for (const owned of this.registrations.values()) {
73
+ for (const registration of owned.values()) {
74
+ try {
75
+ if (!registration.match(input.path)) continue;
76
+ const result = await registration.resolve(
77
+ registration.needsAncestors ? input : { ...input, loadAncestors: void 0 }
78
+ );
79
+ if (result.status === "deny") return void 0;
80
+ if (result.status === "abstain") continue;
81
+ const normalized = normalizeResolved(result);
82
+ if (!normalized) return void 0;
83
+ resolutions.push(normalized);
84
+ } catch (_) {
85
+ return void 0;
86
+ }
87
+ }
88
+ }
89
+ const first = resolutions[0];
90
+ return first && resolutions.every((resolution) => sameResolved(resolution, first)) ? first : void 0;
91
+ }
92
+ }
93
+ const registries = /* @__PURE__ */ new WeakMap();
94
+ function getRecordSlotResolverRegistry(app) {
95
+ let registry = registries.get(app);
96
+ if (!registry) {
97
+ registry = new RecordSlotResolverRegistry();
98
+ registries.set(app, registry);
99
+ }
100
+ return registry;
101
+ }
102
+ function createNestedRecordSlotResolver(registration) {
103
+ return Object.freeze({
104
+ owner: registration.owner,
105
+ id: registration.id,
106
+ match: (path) => path.varName === registration.varName && path.runtimeSegments[0] === "record",
107
+ resolve: () => ({ status: "resolved", slot: ["record"] })
108
+ });
109
+ }
110
+ // Annotate the CommonJS export names for ESM import in node:
111
+ 0 && (module.exports = {
112
+ RecordSlotResolverRegistry,
113
+ createNestedRecordSlotResolver,
114
+ getRecordSlotResolverRegistry
115
+ });
@@ -6,7 +6,53 @@
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 type { Collection, RelationRepository, Repository } from '@nocobase/database';
10
+ import type { ResourcerContext } from '@nocobase/resourcer';
9
11
  type FilterTargetKey = string | string[] | undefined;
12
+ type RecordRepository = Repository | RelationRepository;
13
+ export type RecordParams = {
14
+ associationName?: string;
15
+ collection: string;
16
+ dataSourceKey?: string;
17
+ filterByTk: unknown;
18
+ sourceId?: unknown;
19
+ fields?: string[];
20
+ appends?: string[];
21
+ };
22
+ export type RecordTarget = {
23
+ repository: RecordRepository;
24
+ dataSourceKey: string;
25
+ collection: Collection;
26
+ collectionName: string;
27
+ filterTargetKey: FilterTargetKey;
28
+ pkAttr?: string;
29
+ pkIsValid: boolean;
30
+ rawAttributes?: Record<string, unknown>;
31
+ cacheIdentity: Readonly<{
32
+ ds: string;
33
+ c: string;
34
+ assoc?: string;
35
+ sid?: unknown;
36
+ }>;
37
+ };
38
+ export type RecordQuery = RecordTarget & {
39
+ filterByTk: unknown;
40
+ fields?: string[];
41
+ appends?: string[];
42
+ preferFullRecord: boolean;
43
+ strictSelects: boolean;
44
+ cacheKey: string;
45
+ };
46
+ export declare function isRecordParams(value: unknown): value is RecordParams;
47
+ export declare function resolveRecordTarget(koaCtx: ResourcerContext, params: RecordParams): RecordTarget | undefined;
48
+ export declare function prepareRecordQuery(koaCtx: ResourcerContext, target: RecordTarget, filterByTk: unknown, fields?: string[], appends?: string[], strictSelects?: boolean, preferFullRecord?: boolean): RecordQuery;
49
+ export declare function getRecordRequestCache(koaCtx: ResourcerContext): Map<string, unknown>;
50
+ export declare function setRecordRequestCache(cache: Map<string, unknown>, query: RecordQuery, value: unknown): Map<string, unknown>;
51
+ export declare function findRecordRequestCacheValue(cache: Map<string, unknown>, query: RecordQuery): {
52
+ hit: boolean;
53
+ value: unknown;
54
+ };
55
+ export declare function fetchRecordWithRequestCache(koaCtx: ResourcerContext, params: RecordParams, fields?: string[], appends?: string[], strictSelects?: boolean, preferFullRecord?: boolean): Promise<unknown>;
10
56
  /**
11
57
  * 为了:
12
58
  * - 关联加载/缓存:fields 模式下确保包含主键
@@ -26,7 +72,7 @@ export declare function mergeFieldsWithExtras(fields?: string[], extras?: string
26
72
  * - 单值:返回 object | undefined
27
73
  * - 数组:返回 array(空数组时返回 [],不会退化为无条件查询)
28
74
  */
29
- export declare function fetchRecordOrRecordsJson(repo: any, params: {
75
+ export declare function fetchRecordOrRecordsJson(repo: RecordRepository, params: {
30
76
  filterByTk: unknown;
31
77
  preferFullRecord?: boolean;
32
78
  fields?: string[];
@@ -7,9 +7,11 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
+ var __create = Object.create;
10
11
  var __defProp = Object.defineProperty;
11
12
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
13
  var __getOwnPropNames = Object.getOwnPropertyNames;
14
+ var __getProtoOf = Object.getPrototypeOf;
13
15
  var __hasOwnProp = Object.prototype.hasOwnProperty;
14
16
  var __export = (target, all) => {
15
17
  for (var name in all)
@@ -23,14 +25,279 @@ var __copyProps = (to, from, except, desc) => {
23
25
  }
24
26
  return to;
25
27
  };
28
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
29
+ // If the importer is in node compatibility mode or this is not an ESM
30
+ // file that has been converted to a CommonJS file using a Babel-
31
+ // compatible transform (i.e. "__esModule" has not been set), then set
32
+ // "default" to the CommonJS "module.exports" for node compatibility.
33
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
34
+ mod
35
+ ));
26
36
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
37
  var records_exports = {};
28
38
  __export(records_exports, {
29
39
  fetchRecordOrRecordsJson: () => fetchRecordOrRecordsJson,
40
+ fetchRecordWithRequestCache: () => fetchRecordWithRequestCache,
41
+ findRecordRequestCacheValue: () => findRecordRequestCacheValue,
30
42
  getExtraKeyFieldsForSelect: () => getExtraKeyFieldsForSelect,
31
- mergeFieldsWithExtras: () => mergeFieldsWithExtras
43
+ getRecordRequestCache: () => getRecordRequestCache,
44
+ isRecordParams: () => isRecordParams,
45
+ mergeFieldsWithExtras: () => mergeFieldsWithExtras,
46
+ prepareRecordQuery: () => prepareRecordQuery,
47
+ resolveRecordTarget: () => resolveRecordTarget,
48
+ setRecordRequestCache: () => setRecordRequestCache
32
49
  });
33
50
  module.exports = __toCommonJS(records_exports);
51
+ var import_lodash = __toESM(require("lodash"));
52
+ var import_selects = require("./selects");
53
+ function sortIdentityValue(value) {
54
+ if (Array.isArray(value)) return value.map(sortIdentityValue);
55
+ if (!import_lodash.default.isPlainObject(value)) return value;
56
+ return Object.fromEntries(
57
+ Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => [key, sortIdentityValue(child)])
58
+ );
59
+ }
60
+ function getBaseIdentityKey(identity) {
61
+ return JSON.stringify(sortIdentityValue(identity));
62
+ }
63
+ function createCacheMetadata(query) {
64
+ const cacheIdentity = {
65
+ ds: query.ds,
66
+ c: query.c,
67
+ assoc: query.assoc,
68
+ sid: query.sid,
69
+ tk: query.tk
70
+ };
71
+ return {
72
+ appends: query.a ? new Set(query.a) : void 0,
73
+ baseIdentityKey: getBaseIdentityKey(cacheIdentity),
74
+ cacheIdentity,
75
+ fields: query.f ? new Set(query.f) : void 0,
76
+ full: query.full === true
77
+ };
78
+ }
79
+ class RecordRequestCache extends Map {
80
+ keysByBaseIdentity = /* @__PURE__ */ new Map();
81
+ metadataByKey = /* @__PURE__ */ new Map();
82
+ constructor(entries) {
83
+ super();
84
+ entries == null ? void 0 : entries.forEach((value, key) => this.set(key, value));
85
+ }
86
+ set(cacheKey, value) {
87
+ let metadata;
88
+ try {
89
+ metadata = createCacheMetadata(JSON.parse(cacheKey));
90
+ } catch (_2) {
91
+ }
92
+ return this.setWithMetadata(cacheKey, value, metadata);
93
+ }
94
+ setQuery(query, value) {
95
+ return this.setWithMetadata(
96
+ query.cacheKey,
97
+ value,
98
+ createCacheMetadata({
99
+ ...query.cacheIdentity,
100
+ tk: query.filterByTk,
101
+ f: query.fields,
102
+ a: query.appends,
103
+ full: query.preferFullRecord
104
+ })
105
+ );
106
+ }
107
+ delete(cacheKey) {
108
+ this.removeFromIndex(cacheKey);
109
+ return super.delete(cacheKey);
110
+ }
111
+ clear() {
112
+ super.clear();
113
+ this.keysByBaseIdentity.clear();
114
+ this.metadataByKey.clear();
115
+ }
116
+ *candidates(query) {
117
+ const baseIdentityKey = getBaseIdentityKey({
118
+ ...query.cacheIdentity,
119
+ tk: query.filterByTk
120
+ });
121
+ for (const cacheKey of this.keysByBaseIdentity.get(baseIdentityKey) || []) {
122
+ const metadata = this.metadataByKey.get(cacheKey);
123
+ if (metadata && super.has(cacheKey)) yield [metadata, super.get(cacheKey)];
124
+ }
125
+ }
126
+ setWithMetadata(cacheKey, value, metadata) {
127
+ this.removeFromIndex(cacheKey);
128
+ super.set(cacheKey, value);
129
+ if (!metadata) return this;
130
+ this.metadataByKey.set(cacheKey, metadata);
131
+ let keys = this.keysByBaseIdentity.get(metadata.baseIdentityKey);
132
+ if (!keys) {
133
+ keys = /* @__PURE__ */ new Set();
134
+ this.keysByBaseIdentity.set(metadata.baseIdentityKey, keys);
135
+ }
136
+ keys.add(cacheKey);
137
+ return this;
138
+ }
139
+ removeFromIndex(cacheKey) {
140
+ const metadata = this.metadataByKey.get(cacheKey);
141
+ if (!metadata) return;
142
+ const keys = this.keysByBaseIdentity.get(metadata.baseIdentityKey);
143
+ keys == null ? void 0 : keys.delete(cacheKey);
144
+ if (!(keys == null ? void 0 : keys.size)) this.keysByBaseIdentity.delete(metadata.baseIdentityKey);
145
+ this.metadataByKey.delete(cacheKey);
146
+ }
147
+ }
148
+ function isRecordParams(value) {
149
+ return !!value && typeof value === "object" && typeof value.collection === "string" && Object.prototype.hasOwnProperty.call(value, "filterByTk");
150
+ }
151
+ function resolveRecordTarget(koaCtx, params) {
152
+ var _a, _b;
153
+ const dataSourceKey = params.dataSourceKey || "main";
154
+ const usesAssociation = !!(params.associationName && typeof params.sourceId !== "undefined");
155
+ const context = koaCtx;
156
+ if (!context.state) context.state = {};
157
+ const targets = context.state.__varResolveRecordTargets ??= /* @__PURE__ */ new Map();
158
+ const requestKey = JSON.stringify({
159
+ ds: dataSourceKey,
160
+ c: params.collection,
161
+ assoc: usesAssociation ? params.associationName : void 0,
162
+ sid: usesAssociation ? params.sourceId : void 0
163
+ });
164
+ if (targets.has(requestKey)) return targets.get(requestKey);
165
+ const ds = koaCtx.app.dataSourceManager.get(dataSourceKey);
166
+ const cm = ds.collectionManager;
167
+ if (!(cm == null ? void 0 : cm.db)) {
168
+ targets.set(requestKey, void 0);
169
+ return void 0;
170
+ }
171
+ const repository = usesAssociation ? cm.db.getRepository(params.associationName, params.sourceId) : cm.db.getRepository(params.collection);
172
+ if (!repository) {
173
+ targets.set(requestKey, void 0);
174
+ return void 0;
175
+ }
176
+ const collection = "targetCollection" in repository && repository.targetCollection || repository.collection || cm.db.getCollection(params.associationName || params.collection);
177
+ if (!collection) {
178
+ targets.set(requestKey, void 0);
179
+ return void 0;
180
+ }
181
+ if (usesAssociation && collection.name !== params.collection) {
182
+ targets.set(requestKey, void 0);
183
+ return void 0;
184
+ }
185
+ const rawAttributes = (_a = collection.model) == null ? void 0 : _a.rawAttributes;
186
+ const pkAttr = (_b = collection.model) == null ? void 0 : _b.primaryKeyAttribute;
187
+ const pkIsValid = !!(pkAttr && rawAttributes && Object.prototype.hasOwnProperty.call(rawAttributes, pkAttr));
188
+ const collectionName = collection.name || params.associationName || params.collection;
189
+ const target = {
190
+ repository,
191
+ dataSourceKey,
192
+ collection,
193
+ collectionName,
194
+ filterTargetKey: collection.filterTargetKey,
195
+ pkAttr,
196
+ pkIsValid,
197
+ rawAttributes,
198
+ cacheIdentity: {
199
+ ds: dataSourceKey,
200
+ c: collectionName,
201
+ assoc: usesAssociation ? params.associationName : void 0,
202
+ sid: usesAssociation ? params.sourceId : void 0
203
+ }
204
+ };
205
+ targets.set(requestKey, target);
206
+ return target;
207
+ }
208
+ function prepareRecordQuery(koaCtx, target, filterByTk, fields, appends, strictSelects = false, preferFullRecord = false) {
209
+ var _a;
210
+ const adjusted = (0, import_selects.adjustSelectsForCollection)(koaCtx, target.dataSourceKey, target.collectionName, fields, appends);
211
+ const extraKeys = getExtraKeyFieldsForSelect(filterByTk, target);
212
+ const selectedFields = mergeFieldsWithExtras(
213
+ strictSelects && Array.isArray(fields) ? adjusted.fields || [] : adjusted.fields,
214
+ extraKeys
215
+ );
216
+ const queryFields = preferFullRecord ? void 0 : selectedFields == null ? void 0 : selectedFields.slice().sort();
217
+ const queryAppends = preferFullRecord ? void 0 : (_a = adjusted.appends) == null ? void 0 : _a.slice().sort();
218
+ const cacheKey = JSON.stringify({
219
+ ...target.cacheIdentity,
220
+ tk: filterByTk,
221
+ f: queryFields,
222
+ a: queryAppends,
223
+ full: preferFullRecord || void 0,
224
+ strict: strictSelects || void 0
225
+ });
226
+ return {
227
+ ...target,
228
+ filterByTk,
229
+ fields: queryFields,
230
+ appends: queryAppends,
231
+ preferFullRecord,
232
+ strictSelects,
233
+ cacheKey
234
+ };
235
+ }
236
+ function getRecordRequestCache(koaCtx) {
237
+ const context = koaCtx;
238
+ if (!context.state) context.state = {};
239
+ const existing = context.state.__varResolveBatchCache;
240
+ if (existing instanceof RecordRequestCache) return existing;
241
+ const cache = new RecordRequestCache(existing instanceof Map ? existing : void 0);
242
+ context.state.__varResolveBatchCache = cache;
243
+ return cache;
244
+ }
245
+ function setRecordRequestCache(cache, query, value) {
246
+ if (cache instanceof RecordRequestCache) return cache.setQuery(query, value);
247
+ return cache.set(query.cacheKey, value);
248
+ }
249
+ function cachedQueryCovers(query, cached) {
250
+ if (cached.cacheIdentity.ds !== query.cacheIdentity.ds || cached.cacheIdentity.c !== query.cacheIdentity.c || cached.cacheIdentity.assoc !== query.cacheIdentity.assoc || !import_lodash.default.isEqual(cached.cacheIdentity.sid, query.cacheIdentity.sid) || !import_lodash.default.isEqual(cached.cacheIdentity.tk, query.filterByTk)) {
251
+ return false;
252
+ }
253
+ if (cached.full) return true;
254
+ if (query.preferFullRecord) return false;
255
+ const cachedFields = cached.fields || /* @__PURE__ */ new Set();
256
+ const cachedAppends = cached.appends || /* @__PURE__ */ new Set();
257
+ const fieldCovered = (field) => cachedFields.has(field) || [...cachedAppends].some((append) => field === append || field.startsWith(`${append}.`));
258
+ return (query.fields ? query.fields.every(fieldCovered) : cached.fields === void 0) && (!query.appends || query.appends.every((append) => cachedAppends.has(append)));
259
+ }
260
+ function findRecordRequestCacheValue(cache, query) {
261
+ if (cache.has(query.cacheKey)) return { hit: true, value: cache.get(query.cacheKey) };
262
+ if (cache instanceof RecordRequestCache) {
263
+ for (const [metadata, value] of cache.candidates(query)) {
264
+ if (cachedQueryCovers(query, metadata)) return { hit: true, value };
265
+ }
266
+ }
267
+ return { hit: false, value: void 0 };
268
+ }
269
+ async function fetchRecordWithRequestCache(koaCtx, params, fields, appends, strictSelects = false, preferFullRecord = false) {
270
+ var _a, _b, _c;
271
+ try {
272
+ const target = resolveRecordTarget(koaCtx, params);
273
+ if (!target) return void 0;
274
+ const query = prepareRecordQuery(
275
+ koaCtx,
276
+ target,
277
+ params.filterByTk,
278
+ fields,
279
+ appends,
280
+ strictSelects,
281
+ preferFullRecord
282
+ );
283
+ const cache = getRecordRequestCache(koaCtx);
284
+ const cached = findRecordRequestCacheValue(cache, query);
285
+ if (cached.hit) return cached.value;
286
+ const value = await fetchRecordOrRecordsJson(query.repository, query);
287
+ setRecordRequestCache(cache, query, value);
288
+ return value;
289
+ } catch (error) {
290
+ (_c = (_b = (_a = koaCtx.app) == null ? void 0 : _a.logger) == null ? void 0 : _b.child({ module: "plugin-flow-engine", submodule: "variables.resolve", method: "fetchRecordWithRequestCache" })) == null ? void 0 : _c.warn("[variables.resolve] fetchRecordWithRequestCache error", {
291
+ ds: params.dataSourceKey || "main",
292
+ collection: params.collection,
293
+ tk: params.filterByTk,
294
+ fields,
295
+ appends,
296
+ error: error instanceof Error ? error.message : String(error)
297
+ });
298
+ return void 0;
299
+ }
300
+ }
34
301
  function uniqStrings(list) {
35
302
  return Array.from(new Set(list));
36
303
  }
@@ -60,7 +327,7 @@ function getExtraKeyFieldsForSelect(filterByTk, options) {
60
327
  return uniqStrings(extra);
61
328
  }
62
329
  function mergeFieldsWithExtras(fields, extras = []) {
63
- if (!Array.isArray(fields) || fields.length === 0 || extras.length === 0) return fields;
330
+ if (!Array.isArray(fields) || extras.length === 0) return fields;
64
331
  return uniqStrings([...fields, ...extras]);
65
332
  }
66
333
  function toJsonRecord(record) {
@@ -79,7 +346,7 @@ function reorderRecordsByFilterByTk(records, filterByTk, options) {
79
346
  if (!orderKey) return records;
80
347
  const map = /* @__PURE__ */ new Map();
81
348
  for (const rec of records || []) {
82
- const k = rec == null ? void 0 : rec[orderKey];
349
+ const k = rec && typeof rec === "object" ? rec[orderKey] : void 0;
83
350
  if (typeof k !== "undefined" && k !== null) {
84
351
  map.set(String(k), rec);
85
352
  }
@@ -95,7 +362,7 @@ function reorderRecordsByFilterByTk(records, filterByTk, options) {
95
362
  }
96
363
  }
97
364
  for (const rec of records || []) {
98
- const k = rec == null ? void 0 : rec[orderKey];
365
+ const k = rec && typeof rec === "object" ? rec[orderKey] : void 0;
99
366
  const ks = typeof k === "undefined" || k === null ? void 0 : String(k);
100
367
  if (!ks || used.has(ks)) continue;
101
368
  ordered.push(rec);
@@ -120,6 +387,13 @@ async function fetchRecordOrRecordsJson(repo, params) {
120
387
  // Annotate the CommonJS export names for ESM import in node:
121
388
  0 && (module.exports = {
122
389
  fetchRecordOrRecordsJson,
390
+ fetchRecordWithRequestCache,
391
+ findRecordRequestCacheValue,
123
392
  getExtraKeyFieldsForSelect,
124
- mergeFieldsWithExtras
393
+ getRecordRequestCache,
394
+ isRecordParams,
395
+ mergeFieldsWithExtras,
396
+ prepareRecordQuery,
397
+ resolveRecordTarget,
398
+ setRecordRequestCache
125
399
  });