@nocobase/plugin-flow-engine 3.0.0-alpha.1 → 3.0.0-alpha.12

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 (53) 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/field-binding-registry.js +9 -8
  12. package/dist/server/flow-surfaces/navigation-targets.js +21 -40
  13. package/dist/server/flow-surfaces/service-utils.d.ts +1 -0
  14. package/dist/server/flow-surfaces/service-utils.js +17 -0
  15. package/dist/server/flow-surfaces/service.js +9 -0
  16. package/dist/server/index.d.ts +2 -0
  17. package/dist/server/index.js +7 -0
  18. package/dist/server/plugin.d.ts +13 -0
  19. package/dist/server/plugin.js +90 -3
  20. package/dist/server/repository.d.ts +12 -0
  21. package/dist/server/repository.js +39 -2
  22. package/dist/server/template/context-keys.d.ts +14 -0
  23. package/dist/server/template/context-keys.js +86 -0
  24. package/dist/server/template/contexts.d.ts +5 -8
  25. package/dist/server/template/contexts.js +43 -51
  26. package/dist/server/template/resolver.d.ts +5 -13
  27. package/dist/server/template/resolver.js +188 -257
  28. package/dist/server/template/variable-expression.d.ts +72 -0
  29. package/dist/server/template/variable-expression.js +597 -0
  30. package/dist/server/variables/allow-list.d.ts +49 -0
  31. package/dist/server/variables/allow-list.js +492 -0
  32. package/dist/server/variables/form-item-record-slot-resolvers.d.ts +10 -0
  33. package/dist/server/variables/form-item-record-slot-resolvers.js +280 -0
  34. package/dist/server/variables/record-bindings.d.ts +42 -0
  35. package/dist/server/variables/record-bindings.js +196 -0
  36. package/dist/server/variables/record-projection.d.ts +10 -0
  37. package/dist/server/variables/record-projection.js +140 -0
  38. package/dist/server/variables/record-slot-policy.d.ts +23 -0
  39. package/dist/server/variables/record-slot-policy.js +100 -0
  40. package/dist/server/variables/record-slot-resolvers.d.ts +46 -0
  41. package/dist/server/variables/record-slot-resolvers.js +115 -0
  42. package/dist/server/variables/records.d.ts +49 -2
  43. package/dist/server/variables/records.js +309 -18
  44. package/dist/server/variables/registry.d.ts +33 -10
  45. package/dist/server/variables/registry.js +197 -362
  46. package/dist/server/variables/resolve.d.ts +20 -2
  47. package/dist/server/variables/resolve.js +85 -13
  48. package/dist/server/variables/runjs-variable-dependencies.d.ts +23 -0
  49. package/dist/server/variables/runjs-variable-dependencies.js +407 -0
  50. package/dist/server/variables/selects.js +6 -3
  51. package/dist/server/variables/utils.d.ts +3 -6
  52. package/dist/server/variables/utils.js +50 -111
  53. package/package.json +2 -2
@@ -0,0 +1,280 @@
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 form_item_record_slot_resolvers_exports = {};
28
+ __export(form_item_record_slot_resolvers_exports, {
29
+ createFormItemRecordSlotResolvers: () => createFormItemRecordSlotResolvers
30
+ });
31
+ module.exports = __toCommonJS(form_item_record_slot_resolvers_exports);
32
+ function isObject(value) {
33
+ return !!value && typeof value === "object" && !Array.isArray(value);
34
+ }
35
+ function getModelOptions(value) {
36
+ if (!isObject(value)) return void 0;
37
+ return isObject(value.options) ? value.options : value;
38
+ }
39
+ function getNodeString(value, key) {
40
+ if (!isObject(value)) return void 0;
41
+ const nodeValue = value[key];
42
+ return typeof nodeValue === "string" && nodeValue ? nodeValue : void 0;
43
+ }
44
+ function getStepInit(options, flowKey) {
45
+ if (!isObject(options.stepParams)) return void 0;
46
+ const step = options.stepParams[flowKey];
47
+ return isObject(step) && isObject(step.init) ? step.init : void 0;
48
+ }
49
+ function readString(target, key) {
50
+ if (!isObject(target)) return void 0;
51
+ const direct = target[key];
52
+ if (typeof direct === "string" && direct.trim()) return direct.trim();
53
+ const options = target.options;
54
+ const nested = isObject(options) ? options[key] : void 0;
55
+ return typeof nested === "string" && nested.trim() ? nested.trim() : void 0;
56
+ }
57
+ function callMethod(target, name, ...args) {
58
+ if (!isObject(target) || typeof target[name] !== "function") return void 0;
59
+ return target[name].apply(target, args);
60
+ }
61
+ function getResourceTarget(options) {
62
+ const init = getStepInit(options, "resourceSettings");
63
+ const collection = typeof (init == null ? void 0 : init.collectionName) === "string" ? init.collectionName.trim() : "";
64
+ const dataSourceKey = typeof (init == null ? void 0 : init.dataSourceKey) === "string" ? init.dataSourceKey.trim() : "main";
65
+ const associationName = typeof (init == null ? void 0 : init.associationName) === "string" ? init.associationName.trim() : void 0;
66
+ if (!collection || !dataSourceKey || typeof (init == null ? void 0 : init.associationName) === "string" && !associationName) {
67
+ return void 0;
68
+ }
69
+ return { collection, dataSourceKey, ...associationName ? { associationName } : {} };
70
+ }
71
+ function getFieldTarget(input, source, fieldName) {
72
+ var _a;
73
+ const field = callMethod(source.collection, "getField", fieldName);
74
+ const association = callMethod(field, "isAssociationField") === true || callMethod(field, "isRelationField") === true;
75
+ if (!association) return void 0;
76
+ const rawTargetCollection = isObject(field) ? field.targetCollection : void 0;
77
+ const targetCollection = typeof rawTargetCollection === "function" ? rawTargetCollection.call(field) : rawTargetCollection;
78
+ const collectionName = readString(targetCollection, "name") || readString(field, "target");
79
+ const dataSourceKey = readString(targetCollection, "dataSourceKey") || source.dataSourceKey;
80
+ const collection = targetCollection || collectionName && ((_a = input.getCollection) == null ? void 0 : _a.call(input, dataSourceKey, collectionName));
81
+ if (!collectionName || !collection) return void 0;
82
+ return { source, target: { collection, collectionName, dataSourceKey } };
83
+ }
84
+ function resolveAssociationPath(input, source, path) {
85
+ let current = source;
86
+ let edge;
87
+ for (const segment of path) {
88
+ edge = getFieldTarget(input, current, segment);
89
+ if (!edge) return void 0;
90
+ current = edge.target;
91
+ }
92
+ return edge;
93
+ }
94
+ function resolveResourceCollection(input, resource) {
95
+ var _a, _b, _c;
96
+ let sourceName = resource.collection;
97
+ const associationPath = ((_a = resource.associationName) == null ? void 0 : _a.split(".").filter(Boolean)) || [];
98
+ if (associationPath.length > 1) sourceName = associationPath.shift() || sourceName;
99
+ const collection = (_b = input.getCollection) == null ? void 0 : _b.call(input, resource.dataSourceKey, sourceName);
100
+ if (!collection) return void 0;
101
+ const source = { collection, collectionName: sourceName, dataSourceKey: resource.dataSourceKey };
102
+ return associationPath.length ? (_c = resolveAssociationPath(input, source, associationPath)) == null ? void 0 : _c.target : source;
103
+ }
104
+ function getSubModel(options, name) {
105
+ return isObject(options.subModels) ? options.subModels[name] : void 0;
106
+ }
107
+ function getFormItems(form) {
108
+ const grid = getModelOptions(getSubModel(form, "grid"));
109
+ if (!grid) return void 0;
110
+ const items = getSubModel(grid, "items");
111
+ return Array.isArray(items) ? items.map(getModelOptions).filter((item) => !!item) : void 0;
112
+ }
113
+ function getFieldPath(options) {
114
+ const init = getStepInit(options, "fieldSettings");
115
+ const fieldPath = typeof (init == null ? void 0 : init.fieldPath) === "string" ? init.fieldPath.trim() : "";
116
+ const associationPath = typeof (init == null ? void 0 : init.associationPathName) === "string" ? init.associationPathName.trim() : "";
117
+ if (!fieldPath) return void 0;
118
+ if (!associationPath || fieldPath === associationPath || fieldPath.startsWith(`${associationPath}.`))
119
+ return fieldPath;
120
+ return `${associationPath}.${fieldPath}`;
121
+ }
122
+ function getFieldSource(input, options) {
123
+ var _a;
124
+ const init = getStepInit(options, "fieldSettings");
125
+ const collectionName = typeof (init == null ? void 0 : init.collectionName) === "string" ? init.collectionName.trim() : "";
126
+ const dataSourceKey = typeof (init == null ? void 0 : init.dataSourceKey) === "string" ? init.dataSourceKey.trim() : "main";
127
+ const collection = collectionName && dataSourceKey ? (_a = input.getCollection) == null ? void 0 : _a.call(input, dataSourceKey, collectionName) : void 0;
128
+ return collection ? { collection, collectionName, dataSourceKey } : void 0;
129
+ }
130
+ function isJsonField(field) {
131
+ return ["json", "jsonb"].includes(
132
+ String(readString(field, "type") || readString(field, "interface") || "").toLowerCase()
133
+ );
134
+ }
135
+ function pathStartsWith(path, prefix) {
136
+ return prefix.length <= path.length && prefix.every((segment, index) => path[index] === segment);
137
+ }
138
+ function resolved(slot) {
139
+ return { status: "resolved", slot };
140
+ }
141
+ async function getLineage(input) {
142
+ const ancestors = input.loadAncestors ? await input.loadAncestors() : [];
143
+ const lineage = [input.currentNode, ...ancestors].filter((node) => !!node);
144
+ for (let index = 0; index + 1 < lineage.length; index++) {
145
+ const parentId = getNodeString(lineage[index], "parentId");
146
+ const parentUid = getNodeString(lineage[index + 1], "uid");
147
+ if (parentId && parentUid && parentId !== parentUid) throw new Error("Invalid FlowModel lineage");
148
+ }
149
+ return lineage;
150
+ }
151
+ async function loadModelTree(input, node) {
152
+ const options = getModelOptions(node);
153
+ if (options && getFormItems(options)) return options;
154
+ const uid = isObject(node) && typeof node.uid === "string" ? node.uid : void 0;
155
+ if (!uid || !input.ctx) return void 0;
156
+ const repository = input.ctx.db.getCollection("flowModels").repository;
157
+ return getModelOptions(await repository.findModelById(uid, { includeAsyncNode: true }));
158
+ }
159
+ async function findFormProvider(input, lineage) {
160
+ for (const node of lineage) {
161
+ const options = getModelOptions(node);
162
+ if (!options || !getFormItems(options) && !getResourceTarget(options)) continue;
163
+ const form = await loadModelTree(input, node);
164
+ const items = form && getFormItems(form);
165
+ if (!form || !items) continue;
166
+ const resource = getResourceTarget(form);
167
+ return resource ? { collection: resolveResourceCollection(input, resource), items } : void 0;
168
+ }
169
+ return void 0;
170
+ }
171
+ function getConfiguredAssociationSlots(input, source, items) {
172
+ var _a;
173
+ const slots = /* @__PURE__ */ new Map();
174
+ const fieldPaths = [];
175
+ for (const item of items) {
176
+ const fieldPath = ((_a = getFieldPath(item)) == null ? void 0 : _a.split(".").filter(Boolean)) || [];
177
+ if (fieldPath.length) fieldPaths.push(fieldPath);
178
+ let current = source;
179
+ const prefix = [];
180
+ for (const segment of fieldPath) {
181
+ const edge = getFieldTarget(input, current, segment);
182
+ if (!edge) break;
183
+ prefix.push(segment);
184
+ slots.set(prefix.join("."), [...prefix]);
185
+ current = edge.target;
186
+ }
187
+ }
188
+ return { fieldPaths, slots: [...slots.values()] };
189
+ }
190
+ async function resolveFormValues(input) {
191
+ const lineage = await getLineage(input);
192
+ const provider = await findFormProvider(input, lineage);
193
+ if (!provider) return { status: "abstain" };
194
+ if (!provider.collection) return { status: "deny" };
195
+ const runtimePath = input.path.runtimeSegments;
196
+ const configuredSlots = getConfiguredAssociationSlots(input, provider.collection, provider.items);
197
+ let configured;
198
+ for (const slot of configuredSlots.slots) {
199
+ if (pathStartsWith(runtimePath, slot) && (!configured || slot.length > configured.length)) configured = slot;
200
+ }
201
+ if (configured) return resolved(configured);
202
+ const top = runtimePath[0];
203
+ if (typeof top !== "string") return runtimePath.length ? { status: "deny" } : resolved([]);
204
+ const field = callMethod(provider.collection.collection, "getField", top);
205
+ const configuredField = configuredSlots.fieldPaths.some(
206
+ (fieldPath) => pathStartsWith(runtimePath, fieldPath) || runtimePath.length <= fieldPath.length && runtimePath.every((segment, index) => fieldPath[index] === segment)
207
+ );
208
+ return field && !isJsonField(field) && !configuredField ? resolved([]) : { status: "deny" };
209
+ }
210
+ function sameCollection(left, right) {
211
+ return left.dataSourceKey === right.dataSourceKey && left.collectionName === right.collectionName;
212
+ }
213
+ function getAssociationEdge(input, options) {
214
+ var _a;
215
+ const source = getFieldSource(input, options);
216
+ const fullPath = (_a = getFieldPath(options)) == null ? void 0 : _a.split(".").filter(Boolean);
217
+ if (!source || !(fullPath == null ? void 0 : fullPath.length)) return void 0;
218
+ return resolveAssociationPath(input, source, fullPath);
219
+ }
220
+ function hasItemProviderBranch(lineage, providerIndex) {
221
+ return providerIndex === 0 || lineage.slice(0, providerIndex).some((node) => ["grid", "popup"].includes(getNodeString(node, "subKey") || ""));
222
+ }
223
+ function getItemCollectionChain(input, lineage) {
224
+ const edges = [];
225
+ for (const [index, node] of lineage.entries()) {
226
+ const options = getModelOptions(node);
227
+ const edge = options && getAssociationEdge(input, options);
228
+ if (!edge || !hasItemProviderBranch(lineage, index)) continue;
229
+ const previous = edges[edges.length - 1];
230
+ if (previous && sameCollection(previous.source, edge.source) && sameCollection(previous.target, edge.target))
231
+ continue;
232
+ edges.push(edge);
233
+ }
234
+ if (!edges.length) return [];
235
+ const collections = [edges[0].target, edges[0].source];
236
+ let edgeIndex = 1;
237
+ while (edgeIndex < edges.length) {
238
+ const parent = collections[collections.length - 1];
239
+ const matches = edges.slice(edgeIndex).filter((edge) => sameCollection(edge.target, parent));
240
+ if (!matches.length) break;
241
+ if (matches.some((edge) => !sameCollection(edge.source, matches[0].source))) return [];
242
+ collections.push(matches[0].source);
243
+ edgeIndex = edges.indexOf(matches[0], edgeIndex) + 1;
244
+ }
245
+ return collections;
246
+ }
247
+ async function resolveItem(input) {
248
+ const runtimePath = input.path.runtimeSegments;
249
+ let parentDepth = 0;
250
+ while (runtimePath[parentDepth] === "parentItem") parentDepth += 1;
251
+ if (runtimePath[parentDepth] !== "value") return { status: "abstain" };
252
+ const associationName = runtimePath[parentDepth + 1];
253
+ if (typeof associationName !== "string") return { status: "abstain" };
254
+ const collections = getItemCollectionChain(input, await getLineage(input));
255
+ const collection = collections[parentDepth];
256
+ if (!collection || !getFieldTarget(input, collection, associationName)) return { status: "deny" };
257
+ return resolved([...runtimePath.slice(0, parentDepth + 1), associationName]);
258
+ }
259
+ function createFormItemRecordSlotResolvers() {
260
+ return [
261
+ {
262
+ owner: "@nocobase/plugin-flow-engine",
263
+ id: "form:values",
264
+ match: (path) => path.varName === "formValues",
265
+ needsAncestors: true,
266
+ resolve: resolveFormValues
267
+ },
268
+ {
269
+ owner: "@nocobase/plugin-flow-engine",
270
+ id: "form:item",
271
+ match: (path) => path.varName === "item",
272
+ needsAncestors: true,
273
+ resolve: resolveItem
274
+ }
275
+ ];
276
+ }
277
+ // Annotate the CommonJS export names for ESM import in node:
278
+ 0 && (module.exports = {
279
+ createFormItemRecordSlotResolvers
280
+ });
@@ -0,0 +1,42 @@
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 { PathSegment, VariablePathRef } from '../template/variable-expression';
11
+ import { type RecordParams } from './records';
12
+ import type { RecordSlotPolicies } from './record-slot-policy';
13
+ export type AuthorizedRecordBinding = Readonly<{
14
+ params: RecordParams;
15
+ varName: string;
16
+ prefix: readonly PathSegment[];
17
+ relativePaths: readonly (readonly PathSegment[])[];
18
+ preferFullRecord: boolean;
19
+ contextKey: string;
20
+ contextLocation: readonly PathSegment[];
21
+ }>;
22
+ export type RecordBindingRejectionReason = 'protected-context-root' | 'protected-context-key';
23
+ export type RecordBindingRejection = Readonly<{
24
+ reason: RecordBindingRejectionReason;
25
+ varName: string;
26
+ prefix: readonly PathSegment[];
27
+ contextKey: string;
28
+ contextLocation: readonly PathSegment[];
29
+ }>;
30
+ export type RecordBindingPlan = Readonly<{
31
+ bindings: readonly AuthorizedRecordBinding[];
32
+ contextParams: Readonly<Record<string, unknown>>;
33
+ rejections: readonly RecordBindingRejection[];
34
+ }>;
35
+ export type RecordBindingUsage = Readonly<Record<string, readonly VariablePathRef[]>>;
36
+ export type PlanRecordBindingsOptions = Readonly<{
37
+ contextParams?: Readonly<Record<string, unknown>>;
38
+ koaCtx: ResourcerContext;
39
+ policies?: RecordSlotPolicies;
40
+ usage: RecordBindingUsage;
41
+ }>;
42
+ export declare function planRecordBindings(options: PlanRecordBindingsOptions): Promise<RecordBindingPlan>;
@@ -0,0 +1,196 @@
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_bindings_exports = {};
28
+ __export(record_bindings_exports, {
29
+ planRecordBindings: () => planRecordBindings
30
+ });
31
+ module.exports = __toCommonJS(record_bindings_exports);
32
+ var import_context_keys = require("../template/context-keys");
33
+ var import_records = require("./records");
34
+ const removedRecordParams = Symbol("removed-record-params");
35
+ const blockedBindingSegments = /* @__PURE__ */ new Set([...import_context_keys.SERVER_CONTEXT_PROTOTYPE_KEYS, ...import_context_keys.SERVER_CONTEXT_INTERNAL_KEYS]);
36
+ function normalizeFlatSegment(segment) {
37
+ return /^\d+$/.test(segment) ? Number(segment) : segment;
38
+ }
39
+ function splitFlatContextKey(key) {
40
+ const [varName, ...prefix] = key.split(".");
41
+ return [varName, ...prefix.map(normalizeFlatSegment)];
42
+ }
43
+ function cloneRecordParams(params) {
44
+ return Object.freeze({
45
+ ...params,
46
+ ...Array.isArray(params.fields) ? { fields: Object.freeze([...params.fields]) } : {},
47
+ ...Array.isArray(params.appends) ? { appends: Object.freeze([...params.appends]) } : {}
48
+ });
49
+ }
50
+ function isRecordDescriptor(value) {
51
+ return !!value && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, "collection") && Object.prototype.hasOwnProperty.call(value, "filterByTk");
52
+ }
53
+ function getRecordParams(value) {
54
+ if (!(0, import_records.isRecordParams)(value) || !value.collection) return void 0;
55
+ if (typeof value.dataSourceKey !== "undefined" && typeof value.dataSourceKey !== "string") return void 0;
56
+ if (typeof value.associationName !== "undefined" && typeof value.associationName !== "string") return void 0;
57
+ if (typeof value.fields !== "undefined" && (!Array.isArray(value.fields) || !value.fields.every((field) => typeof field === "string"))) {
58
+ return void 0;
59
+ }
60
+ if (typeof value.appends !== "undefined" && (!Array.isArray(value.appends) || !value.appends.every((append) => typeof append === "string"))) {
61
+ return void 0;
62
+ }
63
+ return cloneRecordParams(value);
64
+ }
65
+ function collectDescriptorsAndContextParams(contextParams) {
66
+ const descriptors = [];
67
+ const visit = (value, varName, prefix, contextKey, contextLocation) => {
68
+ if (isRecordDescriptor(value)) {
69
+ descriptors.push({
70
+ contextKey,
71
+ contextLocation: Object.freeze([...contextLocation]),
72
+ params: getRecordParams(value),
73
+ prefix: Object.freeze([...prefix]),
74
+ varName
75
+ });
76
+ return removedRecordParams;
77
+ }
78
+ if (Array.isArray(value)) {
79
+ return Object.freeze(
80
+ value.map((child, index) => {
81
+ const next = visit(child, varName, [...prefix, index], contextKey, [...contextLocation, index]);
82
+ return next === removedRecordParams ? void 0 : next;
83
+ })
84
+ );
85
+ }
86
+ if (!value || typeof value !== "object") return value;
87
+ const entries = [];
88
+ for (const [key, child] of Object.entries(value)) {
89
+ const next = visit(child, varName, [...prefix, key], contextKey, [...contextLocation, key]);
90
+ if (next !== removedRecordParams) entries.push([key, next]);
91
+ }
92
+ return Object.freeze(Object.fromEntries(entries));
93
+ };
94
+ const cleaned = [];
95
+ for (const [contextKey, value] of Object.entries(contextParams)) {
96
+ const [varName, ...prefix] = splitFlatContextKey(contextKey);
97
+ const next = visit(value, varName, prefix, contextKey, [contextKey]);
98
+ if (next !== removedRecordParams) cleaned.push([contextKey, next]);
99
+ }
100
+ return { contextParams: Object.freeze(Object.fromEntries(cleaned)), descriptors };
101
+ }
102
+ function getDescriptorKey(varName, prefix) {
103
+ return JSON.stringify([varName, ...prefix]);
104
+ }
105
+ function groupDescriptors(descriptors) {
106
+ const groups = /* @__PURE__ */ new Map();
107
+ for (const descriptor of descriptors) {
108
+ const key = getDescriptorKey(descriptor.varName, descriptor.prefix);
109
+ const group = groups.get(key) || [];
110
+ group.push(descriptor);
111
+ groups.set(key, group);
112
+ }
113
+ return groups;
114
+ }
115
+ function createRejection(descriptor, reason) {
116
+ return Object.freeze({
117
+ reason,
118
+ varName: descriptor.varName,
119
+ prefix: descriptor.prefix,
120
+ contextKey: descriptor.contextKey,
121
+ contextLocation: descriptor.contextLocation
122
+ });
123
+ }
124
+ function hasBlockedSegment(path) {
125
+ return path.some((segment) => typeof segment === "string" && blockedBindingSegments.has(segment));
126
+ }
127
+ function hasValidParams(descriptor) {
128
+ return !!descriptor.params;
129
+ }
130
+ function createBinding(descriptor, paths) {
131
+ const relativePaths = paths.map((path) => Object.freeze(path.runtimeSegments.slice(descriptor.prefix.length)));
132
+ return Object.freeze({
133
+ params: cloneRecordParams(descriptor.params),
134
+ varName: descriptor.varName,
135
+ prefix: descriptor.prefix,
136
+ relativePaths: Object.freeze(relativePaths),
137
+ preferFullRecord: relativePaths.some((path) => path.length === 0),
138
+ contextKey: descriptor.contextKey,
139
+ contextLocation: descriptor.contextLocation
140
+ });
141
+ }
142
+ async function planStrictBindings(options, descriptorGroups) {
143
+ var _a;
144
+ const blockedDescriptors = /* @__PURE__ */ new Set();
145
+ const rejections = [];
146
+ for (const descriptors of descriptorGroups.values()) {
147
+ for (const descriptor of descriptors) {
148
+ const reason = (0, import_context_keys.isProtectedServerContextKey)(descriptor.varName) ? "protected-context-root" : hasBlockedSegment(descriptor.prefix) ? "protected-context-key" : void 0;
149
+ if (!reason) continue;
150
+ blockedDescriptors.add(descriptor);
151
+ rejections.push(createRejection(descriptor, reason));
152
+ }
153
+ }
154
+ const groups = /* @__PURE__ */ new Map();
155
+ for (const refs of Object.values(options.usage)) {
156
+ for (const ref of refs) {
157
+ const policy = (_a = options.policies) == null ? void 0 : _a.get(ref.canonicalKey);
158
+ if (!policy) continue;
159
+ const descriptorKey = getDescriptorKey(ref.varName, policy.slot);
160
+ const existing = groups.get(descriptorKey);
161
+ const group = existing || {
162
+ descriptorKey,
163
+ paths: []
164
+ };
165
+ group.paths.push(ref);
166
+ groups.set(descriptorKey, group);
167
+ }
168
+ }
169
+ const bindings = [];
170
+ for (const group of groups.values()) {
171
+ const candidates = descriptorGroups.get(group.descriptorKey) || [];
172
+ if (candidates.length !== 1 || !hasValidParams(candidates[0])) continue;
173
+ const descriptor = candidates[0];
174
+ if (blockedDescriptors.has(descriptor)) continue;
175
+ if (group.paths.some((path) => hasBlockedSegment(path.runtimeSegments))) {
176
+ rejections.push(createRejection(descriptor, "protected-context-key"));
177
+ continue;
178
+ }
179
+ bindings.push(createBinding(descriptor, group.paths));
180
+ }
181
+ return { bindings, rejections };
182
+ }
183
+ async function planRecordBindings(options) {
184
+ const { contextParams, descriptors } = collectDescriptorsAndContextParams(options.contextParams ?? {});
185
+ const descriptorGroups = groupDescriptors(descriptors);
186
+ const { bindings, rejections } = await planStrictBindings(options, descriptorGroups);
187
+ return Object.freeze({
188
+ bindings: Object.freeze(bindings),
189
+ contextParams,
190
+ rejections: Object.freeze(rejections)
191
+ });
192
+ }
193
+ // Annotate the CommonJS export names for ESM import in node:
194
+ 0 && (module.exports = {
195
+ planRecordBindings
196
+ });
@@ -0,0 +1,10 @@
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 { PathSegment } from '../template/variable-expression';
10
+ export declare function projectRecord(raw: unknown, exposedPaths: readonly (readonly PathSegment[])[]): unknown;
@@ -0,0 +1,140 @@
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_projection_exports = {};
28
+ __export(record_projection_exports, {
29
+ projectRecord: () => projectRecord
30
+ });
31
+ module.exports = __toCommonJS(record_projection_exports);
32
+ const blockedKeys = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
33
+ const omitted = Symbol("omitted record projection value");
34
+ function createNode() {
35
+ return { whole: false, children: /* @__PURE__ */ new Map() };
36
+ }
37
+ function isValidSegment(segment) {
38
+ return typeof segment === "string" ? !blockedKeys.has(segment) : Number.isInteger(segment) && segment >= 0 && segment < 2 ** 32 - 1;
39
+ }
40
+ function addPath(root, path) {
41
+ if (!path.every(isValidSegment)) return;
42
+ let node = root;
43
+ for (const segment of path) {
44
+ if (node.whole) return;
45
+ let child = node.children.get(segment);
46
+ if (!child) {
47
+ child = createNode();
48
+ node.children.set(segment, child);
49
+ }
50
+ node = child;
51
+ }
52
+ node.whole = true;
53
+ node.children.clear();
54
+ }
55
+ function mergeNodes(left, right) {
56
+ if (left.whole || right.whole) return { whole: true, children: /* @__PURE__ */ new Map() };
57
+ const merged = createNode();
58
+ for (const [segment, child] of left.children) merged.children.set(segment, child);
59
+ for (const [segment, child] of right.children) {
60
+ const existing = merged.children.get(segment);
61
+ merged.children.set(segment, existing ? mergeNodes(existing, child) : child);
62
+ }
63
+ return merged;
64
+ }
65
+ function getOwnDataProperty(source, key) {
66
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
67
+ if (!(descriptor == null ? void 0 : descriptor.enumerable) || !("value" in descriptor) || typeof descriptor.value === "function") return;
68
+ return descriptor;
69
+ }
70
+ function defineDataProperty(target, key, value) {
71
+ Object.defineProperty(target, key, { configurable: true, enumerable: true, value, writable: true });
72
+ }
73
+ function copyWhole(value, seen) {
74
+ if (typeof value === "function") return omitted;
75
+ if (!value || typeof value !== "object") return value;
76
+ if (value instanceof Date) return new Date(value.getTime());
77
+ if (Buffer.isBuffer(value)) return Buffer.prototype.toJSON.call(value);
78
+ const cached = seen.get(value);
79
+ if (cached) return cached;
80
+ const result = Array.isArray(value) ? new Array(value.length) : /* @__PURE__ */ Object.create(null);
81
+ seen.set(value, result);
82
+ for (const key of Object.keys(value)) {
83
+ if (blockedKeys.has(key)) continue;
84
+ const descriptor = getOwnDataProperty(value, key);
85
+ if (!descriptor) continue;
86
+ const child = copyWhole(descriptor.value, seen);
87
+ if (child !== omitted) defineDataProperty(result, key, child);
88
+ }
89
+ return result;
90
+ }
91
+ function projectArray(source, node) {
92
+ const elementNode = createNode();
93
+ const exactNodes = /* @__PURE__ */ new Map();
94
+ for (const [segment, child] of node.children) {
95
+ if (typeof segment === "string") elementNode.children.set(segment, child);
96
+ else exactNodes.set(segment, child);
97
+ }
98
+ const projectAllElements = elementNode.children.size > 0;
99
+ const result = projectAllElements ? new Array(source.length) : [];
100
+ const indices = new Set(exactNodes.keys());
101
+ if (projectAllElements) {
102
+ for (const key of Object.keys(source)) {
103
+ const index = Number(key);
104
+ if (String(index) === key && isValidSegment(index)) indices.add(index);
105
+ }
106
+ }
107
+ for (const index of indices) {
108
+ const descriptor = getOwnDataProperty(source, String(index));
109
+ if (!descriptor) continue;
110
+ const exactNode = exactNodes.get(index);
111
+ const childNode = exactNode && projectAllElements ? mergeNodes(elementNode, exactNode) : exactNode || elementNode;
112
+ const child = projectValue(descriptor.value, childNode);
113
+ if (child !== omitted) defineDataProperty(result, String(index), child);
114
+ }
115
+ return result;
116
+ }
117
+ function projectValue(value, node) {
118
+ if (node.whole) return copyWhole(value, /* @__PURE__ */ new WeakMap());
119
+ if (Array.isArray(value)) return projectArray(value, node);
120
+ if (!value || typeof value !== "object") return omitted;
121
+ const result = /* @__PURE__ */ Object.create(null);
122
+ for (const [segment, childNode] of node.children) {
123
+ if (typeof segment !== "string") continue;
124
+ const descriptor = getOwnDataProperty(value, segment);
125
+ if (!descriptor) continue;
126
+ const child = projectValue(descriptor.value, childNode);
127
+ if (child !== omitted) defineDataProperty(result, segment, child);
128
+ }
129
+ return result;
130
+ }
131
+ function projectRecord(raw, exposedPaths) {
132
+ const root = createNode();
133
+ for (const path of exposedPaths) addPath(root, path);
134
+ const result = projectValue(raw, root);
135
+ return result === omitted ? void 0 : result;
136
+ }
137
+ // Annotate the CommonJS export names for ESM import in node:
138
+ 0 && (module.exports = {
139
+ projectRecord
140
+ });