@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.
- package/dist/externalVersion.js +9 -9
- package/dist/node_modules/@ant-design/icons-svg/package.json +1 -1
- package/dist/node_modules/acorn/package.json +1 -1
- package/dist/node_modules/acorn-jsx/package.json +1 -1
- package/dist/node_modules/acorn-walk/package.json +1 -1
- package/dist/node_modules/ses/package.json +1 -1
- package/dist/node_modules/zod/package.json +1 -1
- package/dist/server/flow-surfaces/builder.js +5 -0
- package/dist/server/flow-surfaces/catalog.js +27 -8
- package/dist/server/flow-surfaces/configure-options.js +17 -1
- package/dist/server/flow-surfaces/service-utils.d.ts +1 -0
- package/dist/server/flow-surfaces/service-utils.js +17 -0
- package/dist/server/flow-surfaces/service.js +9 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +7 -0
- package/dist/server/plugin.d.ts +11 -0
- package/dist/server/plugin.js +85 -3
- package/dist/server/repository.d.ts +8 -0
- package/dist/server/repository.js +29 -0
- package/dist/server/template/context-keys.d.ts +14 -0
- package/dist/server/template/context-keys.js +86 -0
- package/dist/server/template/contexts.d.ts +5 -8
- package/dist/server/template/contexts.js +43 -51
- package/dist/server/template/resolver.d.ts +5 -13
- package/dist/server/template/resolver.js +188 -257
- package/dist/server/template/variable-expression.d.ts +72 -0
- package/dist/server/template/variable-expression.js +597 -0
- package/dist/server/variables/allow-list.d.ts +48 -0
- package/dist/server/variables/allow-list.js +374 -0
- package/dist/server/variables/form-item-record-slot-resolvers.d.ts +10 -0
- package/dist/server/variables/form-item-record-slot-resolvers.js +280 -0
- package/dist/server/variables/record-bindings.d.ts +42 -0
- package/dist/server/variables/record-bindings.js +196 -0
- package/dist/server/variables/record-projection.d.ts +10 -0
- package/dist/server/variables/record-projection.js +140 -0
- package/dist/server/variables/record-slot-policy.d.ts +23 -0
- package/dist/server/variables/record-slot-policy.js +100 -0
- package/dist/server/variables/record-slot-resolvers.d.ts +46 -0
- package/dist/server/variables/record-slot-resolvers.js +115 -0
- package/dist/server/variables/records.d.ts +47 -1
- package/dist/server/variables/records.js +279 -5
- package/dist/server/variables/registry.d.ts +33 -10
- package/dist/server/variables/registry.js +197 -362
- package/dist/server/variables/resolve.d.ts +18 -2
- package/dist/server/variables/resolve.js +79 -13
- package/dist/server/variables/runjs-variable-dependencies.d.ts +21 -0
- package/dist/server/variables/runjs-variable-dependencies.js +313 -0
- package/dist/server/variables/utils.d.ts +3 -6
- package/dist/server/variables/utils.js +50 -111
- 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:
|
|
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
|
-
|
|
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) ||
|
|
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
|
|
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
|
|
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
|
-
|
|
393
|
+
getRecordRequestCache,
|
|
394
|
+
isRecordParams,
|
|
395
|
+
mergeFieldsWithExtras,
|
|
396
|
+
prepareRecordQuery,
|
|
397
|
+
resolveRecordTarget,
|
|
398
|
+
setRecordRequestCache
|
|
125
399
|
});
|