@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.
- 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/field-binding-registry.js +9 -8
- package/dist/server/flow-surfaces/navigation-targets.js +21 -40
- 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 +13 -0
- package/dist/server/plugin.js +90 -3
- package/dist/server/repository.d.ts +12 -0
- package/dist/server/repository.js +39 -2
- 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 +49 -0
- package/dist/server/variables/allow-list.js +492 -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 +49 -2
- package/dist/server/variables/records.js +309 -18
- package/dist/server/variables/registry.d.ts +33 -10
- package/dist/server/variables/registry.js +197 -362
- package/dist/server/variables/resolve.d.ts +20 -2
- package/dist/server/variables/resolve.js +85 -13
- package/dist/server/variables/runjs-variable-dependencies.d.ts +23 -0
- package/dist/server/variables/runjs-variable-dependencies.js +407 -0
- package/dist/server/variables/selects.js +6 -3
- package/dist/server/variables/utils.d.ts +3 -6
- package/dist/server/variables/utils.js +50 -111
- package/package.json +2 -2
package/dist/server/plugin.js
CHANGED
|
@@ -43,8 +43,13 @@ module.exports = __toCommonJS(plugin_exports);
|
|
|
43
43
|
var import_utils = require("@nocobase/utils");
|
|
44
44
|
var import_flow_surfaces = require("./flow-surfaces");
|
|
45
45
|
var import_server = __toESM(require("./server"));
|
|
46
|
+
var import_allow_list = require("./variables/allow-list");
|
|
47
|
+
var import_form_item_record_slot_resolvers = require("./variables/form-item-record-slot-resolvers");
|
|
48
|
+
var import_record_slot_policy = require("./variables/record-slot-policy");
|
|
49
|
+
var import_record_slot_resolvers = require("./variables/record-slot-resolvers");
|
|
46
50
|
var import_resolve = require("./variables/resolve");
|
|
47
51
|
class PluginFlowEngineServer extends import_server.default {
|
|
52
|
+
recordSlotResolverDisposers = [];
|
|
48
53
|
async afterAdd() {
|
|
49
54
|
}
|
|
50
55
|
async beforeLoad() {
|
|
@@ -71,8 +76,16 @@ class PluginFlowEngineServer extends import_server.default {
|
|
|
71
76
|
}
|
|
72
77
|
return await cm.db.runSQL(sql, options);
|
|
73
78
|
}
|
|
79
|
+
async resolveFlowModelVariablesTemplate(ctx, options) {
|
|
80
|
+
this.ensureRecordSlotResolvers(ctx.app);
|
|
81
|
+
return await (0, import_resolve.resolveFlowModelVariablesTemplate)(ctx, options);
|
|
82
|
+
}
|
|
83
|
+
isLegacyVariableTemplateSafe(template) {
|
|
84
|
+
return (0, import_resolve.isLegacyVariableTemplateSafe)(template);
|
|
85
|
+
}
|
|
74
86
|
async load() {
|
|
75
87
|
await super.load();
|
|
88
|
+
this.registerRecordSlotResolvers();
|
|
76
89
|
(0, import_flow_surfaces.registerFlowSurfacesResource)(this);
|
|
77
90
|
this.app.auditManager.registerAction("flowSql:save");
|
|
78
91
|
this.app.auditManager.registerAction("flowModels:save");
|
|
@@ -86,12 +99,56 @@ class PluginFlowEngineServer extends import_server.default {
|
|
|
86
99
|
actions: {
|
|
87
100
|
// 解析 JSON 模板中的 ctx 变量
|
|
88
101
|
resolve: async (ctx, next) => {
|
|
89
|
-
var _a, _b;
|
|
102
|
+
var _a, _b, _c;
|
|
103
|
+
this.ensureRecordSlotResolvers(ctx.app);
|
|
90
104
|
const raw = ((_b = (_a = ctx.action) == null ? void 0 : _a.params) == null ? void 0 : _b.values) ?? {};
|
|
91
105
|
const values = typeof (raw == null ? void 0 : raw.values) !== "undefined" ? raw.values : raw;
|
|
92
106
|
if (Array.isArray(values == null ? void 0 : values.batch)) {
|
|
93
107
|
const batchItems = values.batch;
|
|
94
|
-
const
|
|
108
|
+
const authorizedItems = [];
|
|
109
|
+
const passthroughResults = [];
|
|
110
|
+
for (const [index, item] of batchItems.entries()) {
|
|
111
|
+
const template2 = (item == null ? void 0 : item.template) ?? {};
|
|
112
|
+
const authorization2 = await (0, import_allow_list.authorizeVariablesResolve)(ctx, {
|
|
113
|
+
contractRd: item == null ? void 0 : item.contractRd,
|
|
114
|
+
contextParams: (item == null ? void 0 : item.contextParams) || {},
|
|
115
|
+
rd: item == null ? void 0 : item.rd,
|
|
116
|
+
template: template2
|
|
117
|
+
});
|
|
118
|
+
if (authorization2.allowed) {
|
|
119
|
+
const authorizedItem = {
|
|
120
|
+
analysis: authorization2.analysis,
|
|
121
|
+
bindingPlan: authorization2.bindingPlan,
|
|
122
|
+
id: item == null ? void 0 : item.id,
|
|
123
|
+
index,
|
|
124
|
+
policy: authorization2.policy,
|
|
125
|
+
template: template2
|
|
126
|
+
};
|
|
127
|
+
authorizedItems.push(authorizedItem);
|
|
128
|
+
} else {
|
|
129
|
+
passthroughResults[index] = { id: item == null ? void 0 : item.id, data: template2 };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const resolvedItems = await (0, import_resolve.resolveAnalyzedVariablesBatch)(
|
|
133
|
+
ctx,
|
|
134
|
+
authorizedItems.map((item) => ({
|
|
135
|
+
analysis: item.analysis,
|
|
136
|
+
bindingPlan: item.bindingPlan,
|
|
137
|
+
id: item.index,
|
|
138
|
+
template: item.template,
|
|
139
|
+
policy: item.policy
|
|
140
|
+
}))
|
|
141
|
+
);
|
|
142
|
+
for (const item of resolvedItems) {
|
|
143
|
+
const index = Number(item.id);
|
|
144
|
+
passthroughResults[index] = {
|
|
145
|
+
id: (_c = batchItems[index]) == null ? void 0 : _c.id,
|
|
146
|
+
data: item.data
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const results = passthroughResults.filter(
|
|
150
|
+
(item) => !!item
|
|
151
|
+
);
|
|
95
152
|
ctx.body = { results };
|
|
96
153
|
await next();
|
|
97
154
|
return;
|
|
@@ -104,7 +161,18 @@ class PluginFlowEngineServer extends import_server.default {
|
|
|
104
161
|
}
|
|
105
162
|
const template = values.template;
|
|
106
163
|
const contextParams = (values == null ? void 0 : values.contextParams) || {};
|
|
107
|
-
|
|
164
|
+
const authorization = await (0, import_allow_list.authorizeVariablesResolve)(ctx, {
|
|
165
|
+
contractRd: values == null ? void 0 : values.contractRd,
|
|
166
|
+
contextParams,
|
|
167
|
+
rd: values == null ? void 0 : values.rd,
|
|
168
|
+
template
|
|
169
|
+
});
|
|
170
|
+
ctx.body = authorization.allowed ? await (0, import_resolve.resolveAnalyzedVariablesTemplate)(
|
|
171
|
+
ctx,
|
|
172
|
+
authorization.analysis,
|
|
173
|
+
authorization.policy,
|
|
174
|
+
authorization.bindingPlan
|
|
175
|
+
) : template;
|
|
108
176
|
await next();
|
|
109
177
|
}
|
|
110
178
|
}
|
|
@@ -162,10 +230,29 @@ class PluginFlowEngineServer extends import_server.default {
|
|
|
162
230
|
async install() {
|
|
163
231
|
}
|
|
164
232
|
async afterEnable() {
|
|
233
|
+
this.registerRecordSlotResolvers();
|
|
165
234
|
}
|
|
166
235
|
async afterDisable() {
|
|
236
|
+
this.disposeRecordSlotResolvers();
|
|
167
237
|
}
|
|
168
238
|
async remove() {
|
|
239
|
+
this.disposeRecordSlotResolvers();
|
|
240
|
+
}
|
|
241
|
+
disposeRecordSlotResolvers() {
|
|
242
|
+
this.recordSlotResolverDisposers.forEach((dispose) => dispose());
|
|
243
|
+
this.recordSlotResolverDisposers = [];
|
|
244
|
+
}
|
|
245
|
+
registerRecordSlotResolvers() {
|
|
246
|
+
this.disposeRecordSlotResolvers();
|
|
247
|
+
this.ensureRecordSlotResolvers(this.app);
|
|
248
|
+
}
|
|
249
|
+
ensureRecordSlotResolvers(app) {
|
|
250
|
+
const registry = (0, import_record_slot_resolvers.getRecordSlotResolverRegistry)(app);
|
|
251
|
+
for (const resolver of [...(0, import_record_slot_policy.createBuiltInRecordSlotResolvers)(), ...(0, import_form_item_record_slot_resolvers.createFormItemRecordSlotResolvers)()]) {
|
|
252
|
+
if (!registry.has(resolver.owner, resolver.id)) {
|
|
253
|
+
this.recordSlotResolverDisposers.push(registry.register(resolver));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
169
256
|
}
|
|
170
257
|
}
|
|
171
258
|
var plugin_default = PluginFlowEngineServer;
|
|
@@ -18,6 +18,13 @@ export interface GetPropertiesOptions {
|
|
|
18
18
|
readFromCache?: boolean;
|
|
19
19
|
transaction?: Transaction;
|
|
20
20
|
}
|
|
21
|
+
export interface FlowModelNodeSnapshot {
|
|
22
|
+
uid: string;
|
|
23
|
+
options: Record<string, unknown>;
|
|
24
|
+
parentId: string | null;
|
|
25
|
+
subKey: string | null;
|
|
26
|
+
async: boolean;
|
|
27
|
+
}
|
|
21
28
|
export type FlowModelAttachPosition = 'first' | 'last' | TargetPosition;
|
|
22
29
|
export interface FlowModelAttachOptions {
|
|
23
30
|
parentId: string;
|
|
@@ -132,6 +139,11 @@ export declare class FlowModelRepository extends Repository {
|
|
|
132
139
|
updateSingleNode(node: SchemaNode, options?: Transactionable): Promise<boolean>;
|
|
133
140
|
upsertModel(model: any, options?: Transactionable): Promise<string>;
|
|
134
141
|
findModelById(uid: string, options?: GetJsonSchemaOptions): Promise<any>;
|
|
142
|
+
findModelNodeSnapshotById(uid: string, options?: Transactionable): Promise<FlowModelNodeSnapshot | null>;
|
|
143
|
+
private findModelUidByParentId;
|
|
144
|
+
findModelNodeSnapshotByParentId(parentUid: string, options?: Transactionable & {
|
|
145
|
+
subKey?: string;
|
|
146
|
+
}): Promise<FlowModelNodeSnapshot | null>;
|
|
135
147
|
findModelByParentId(parentUid: string, options?: GetJsonSchemaOptions & {
|
|
136
148
|
subKey?: string;
|
|
137
149
|
}): Promise<any>;
|
|
@@ -1411,7 +1411,36 @@ WHERE TreeTable.depth = 1 AND TreeTable.ancestor = :ancestor and TreeTable.sort
|
|
|
1411
1411
|
const nodes = await this.findNodesById(uid2, options);
|
|
1412
1412
|
return _FlowModelRepository.nodesToModel(nodes, uid2);
|
|
1413
1413
|
}
|
|
1414
|
-
async
|
|
1414
|
+
async findModelNodeSnapshotById(uid2, options) {
|
|
1415
|
+
const transaction2 = options == null ? void 0 : options.transaction;
|
|
1416
|
+
const model = await this.model.findByPk(uid2, { transaction: transaction2 });
|
|
1417
|
+
if (!model) {
|
|
1418
|
+
return null;
|
|
1419
|
+
}
|
|
1420
|
+
const paths = await this.database.getRepository("flowModelTreePath").find({
|
|
1421
|
+
filter: {
|
|
1422
|
+
descendant: uid2,
|
|
1423
|
+
depth: { $in: [0, 1] }
|
|
1424
|
+
},
|
|
1425
|
+
transaction: transaction2
|
|
1426
|
+
});
|
|
1427
|
+
const nodePath = paths.find((path) => path.get("depth") === 0);
|
|
1428
|
+
if (!nodePath) {
|
|
1429
|
+
return null;
|
|
1430
|
+
}
|
|
1431
|
+
const parentPath = paths.find((path) => path.get("depth") === 1);
|
|
1432
|
+
const nodeOptions = import_lodash.default.cloneDeep(
|
|
1433
|
+
import_lodash.default.omit(_FlowModelRepository.optionsToJson(model.get("options") || {}), "subModels")
|
|
1434
|
+
);
|
|
1435
|
+
return {
|
|
1436
|
+
uid: uid2,
|
|
1437
|
+
options: nodeOptions,
|
|
1438
|
+
parentId: parentPath ? parentPath.get("ancestor") : null,
|
|
1439
|
+
subKey: nodePath.get("type") || null,
|
|
1440
|
+
async: !!nodePath.get("async")
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
async findModelUidByParentId(parentUid, options) {
|
|
1415
1444
|
const r = this.database.getRepository("flowModelTreePath");
|
|
1416
1445
|
const treePaths = await r.model.findAll({
|
|
1417
1446
|
where: {
|
|
@@ -1433,10 +1462,18 @@ WHERE TreeTable.depth = 1 AND TreeTable.ancestor = :ancestor and TreeTable.sort
|
|
|
1433
1462
|
transaction: options == null ? void 0 : options.transaction
|
|
1434
1463
|
});
|
|
1435
1464
|
if (treePath == null ? void 0 : treePath["descendant"]) {
|
|
1436
|
-
return
|
|
1465
|
+
return treePath["descendant"];
|
|
1437
1466
|
}
|
|
1438
1467
|
return null;
|
|
1439
1468
|
}
|
|
1469
|
+
async findModelNodeSnapshotByParentId(parentUid, options) {
|
|
1470
|
+
const uid2 = await this.findModelUidByParentId(parentUid, options);
|
|
1471
|
+
return uid2 ? this.findModelNodeSnapshotById(uid2, options) : null;
|
|
1472
|
+
}
|
|
1473
|
+
async findModelByParentId(parentUid, options) {
|
|
1474
|
+
const uid2 = await this.findModelUidByParentId(parentUid, options);
|
|
1475
|
+
return uid2 ? this.findModelById(uid2, options) : null;
|
|
1476
|
+
}
|
|
1440
1477
|
async attach(uid2, attachOptions, options) {
|
|
1441
1478
|
const { transaction: transaction2 } = options || {};
|
|
1442
1479
|
const modelUid = String(uid2 || "").trim();
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
export declare const GLOBAL_CONTEXT_KEYS: readonly ["now", "timestamp", "env"];
|
|
10
|
+
export declare const HTTP_REQUEST_CONTEXT_KEYS: readonly ["user", "roleName", "locale", "ip", "headers", "query", "params"];
|
|
11
|
+
export declare const SERVER_CONTEXT_PROTOTYPE_KEYS: readonly ["__proto__", "prototype", "constructor", "then", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "valueOf"];
|
|
12
|
+
export declare const SERVER_CONTEXT_INTERNAL_KEYS: readonly ["_props", "_cache", "_delegates", "_proxy", "_getOwn", "defineProperty", "delegate", "clearDelegates", "getSandboxKeys", "getSandboxValue", "createProxy"];
|
|
13
|
+
export declare const PROTECTED_SERVER_CONTEXT_KEYS: readonly ["now", "timestamp", "env", "user", "roleName", "locale", "ip", "headers", "query", "params", "__proto__", "prototype", "constructor", "then", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "valueOf", "_props", "_cache", "_delegates", "_proxy", "_getOwn", "defineProperty", "delegate", "clearDelegates", "getSandboxKeys", "getSandboxValue", "createProxy"];
|
|
14
|
+
export declare function isProtectedServerContextKey(key: string): boolean;
|
|
@@ -0,0 +1,86 @@
|
|
|
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 context_keys_exports = {};
|
|
28
|
+
__export(context_keys_exports, {
|
|
29
|
+
GLOBAL_CONTEXT_KEYS: () => GLOBAL_CONTEXT_KEYS,
|
|
30
|
+
HTTP_REQUEST_CONTEXT_KEYS: () => HTTP_REQUEST_CONTEXT_KEYS,
|
|
31
|
+
PROTECTED_SERVER_CONTEXT_KEYS: () => PROTECTED_SERVER_CONTEXT_KEYS,
|
|
32
|
+
SERVER_CONTEXT_INTERNAL_KEYS: () => SERVER_CONTEXT_INTERNAL_KEYS,
|
|
33
|
+
SERVER_CONTEXT_PROTOTYPE_KEYS: () => SERVER_CONTEXT_PROTOTYPE_KEYS,
|
|
34
|
+
isProtectedServerContextKey: () => isProtectedServerContextKey
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(context_keys_exports);
|
|
37
|
+
const GLOBAL_CONTEXT_KEYS = ["now", "timestamp", "env"];
|
|
38
|
+
const HTTP_REQUEST_CONTEXT_KEYS = ["user", "roleName", "locale", "ip", "headers", "query", "params"];
|
|
39
|
+
const SERVER_CONTEXT_PROTOTYPE_KEYS = [
|
|
40
|
+
"__proto__",
|
|
41
|
+
"prototype",
|
|
42
|
+
"constructor",
|
|
43
|
+
"then",
|
|
44
|
+
"__defineGetter__",
|
|
45
|
+
"__defineSetter__",
|
|
46
|
+
"__lookupGetter__",
|
|
47
|
+
"__lookupSetter__",
|
|
48
|
+
"hasOwnProperty",
|
|
49
|
+
"isPrototypeOf",
|
|
50
|
+
"propertyIsEnumerable",
|
|
51
|
+
"toLocaleString",
|
|
52
|
+
"toString",
|
|
53
|
+
"valueOf"
|
|
54
|
+
];
|
|
55
|
+
const SERVER_CONTEXT_INTERNAL_KEYS = [
|
|
56
|
+
"_props",
|
|
57
|
+
"_cache",
|
|
58
|
+
"_delegates",
|
|
59
|
+
"_proxy",
|
|
60
|
+
"_getOwn",
|
|
61
|
+
"defineProperty",
|
|
62
|
+
"delegate",
|
|
63
|
+
"clearDelegates",
|
|
64
|
+
"getSandboxKeys",
|
|
65
|
+
"getSandboxValue",
|
|
66
|
+
"createProxy"
|
|
67
|
+
];
|
|
68
|
+
const PROTECTED_SERVER_CONTEXT_KEYS = [
|
|
69
|
+
...GLOBAL_CONTEXT_KEYS,
|
|
70
|
+
...HTTP_REQUEST_CONTEXT_KEYS,
|
|
71
|
+
...SERVER_CONTEXT_PROTOTYPE_KEYS,
|
|
72
|
+
...SERVER_CONTEXT_INTERNAL_KEYS
|
|
73
|
+
];
|
|
74
|
+
const protectedServerContextKeys = new Set(PROTECTED_SERVER_CONTEXT_KEYS);
|
|
75
|
+
function isProtectedServerContextKey(key) {
|
|
76
|
+
return protectedServerContextKeys.has(key);
|
|
77
|
+
}
|
|
78
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
79
|
+
0 && (module.exports = {
|
|
80
|
+
GLOBAL_CONTEXT_KEYS,
|
|
81
|
+
HTTP_REQUEST_CONTEXT_KEYS,
|
|
82
|
+
PROTECTED_SERVER_CONTEXT_KEYS,
|
|
83
|
+
SERVER_CONTEXT_INTERNAL_KEYS,
|
|
84
|
+
SERVER_CONTEXT_PROTOTYPE_KEYS,
|
|
85
|
+
isProtectedServerContextKey
|
|
86
|
+
});
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { ResourcerContext } from '@nocobase/resourcer';
|
|
10
10
|
type Getter<T = any> = (ctx: ServerBaseContext) => T | Promise<T>;
|
|
11
|
+
export declare const SERVER_CONTEXT_PROVIDER_TOKEN: unique symbol;
|
|
11
12
|
export interface PropertyOptions {
|
|
12
13
|
/** 固定值,优先级高于 get */
|
|
13
14
|
value?: any;
|
|
@@ -21,13 +22,11 @@ export interface PropertyOptions {
|
|
|
21
22
|
/**
|
|
22
23
|
* 服务器端上下文基础类。
|
|
23
24
|
* - 支持以 defineProperty 定义惰性/常量属性,并可选择缓存
|
|
24
|
-
* -
|
|
25
|
-
* -
|
|
26
|
-
* - 通过 Proxy 实现按需求值与 this 绑定
|
|
25
|
+
* - 支持 delegate 机制,属性可回退到被委托的上游上下文
|
|
26
|
+
* - 通过 Proxy 实现按需求值
|
|
27
27
|
*/
|
|
28
28
|
export declare class ServerBaseContext {
|
|
29
29
|
protected _props: Record<string, PropertyOptions>;
|
|
30
|
-
protected _methods: Record<string, (...args: any[]) => any>;
|
|
31
30
|
protected _cache: Record<string, any>;
|
|
32
31
|
protected _delegates: ServerBaseContext[];
|
|
33
32
|
protected _proxy?: ServerBaseContext;
|
|
@@ -40,12 +39,10 @@ export declare class ServerBaseContext {
|
|
|
40
39
|
* - get:惰性 getter(默认带缓存)
|
|
41
40
|
* - once:已存在时忽略重复定义
|
|
42
41
|
*/
|
|
43
|
-
defineProperty(key: string, options: PropertyOptions): void;
|
|
44
|
-
/** 定义一个方法(不可枚举、不可写),访问时会自动绑定到对应实例 */
|
|
45
|
-
defineMethod(name: string, fn: (...args: any[]) => any): void;
|
|
42
|
+
defineProperty(key: string, options: PropertyOptions, providerToken?: typeof SERVER_CONTEXT_PROVIDER_TOKEN): void;
|
|
46
43
|
/**
|
|
47
44
|
* 委托到另一个 ServerBaseContext:
|
|
48
|
-
* -
|
|
45
|
+
* - 访问属性找不到时,会回退到被委托者进行解析
|
|
49
46
|
*/
|
|
50
47
|
delegate(ctx: ServerBaseContext): void;
|
|
51
48
|
/** 清空所有委托 */
|
|
@@ -38,17 +38,21 @@ var contexts_exports = {};
|
|
|
38
38
|
__export(contexts_exports, {
|
|
39
39
|
GlobalContext: () => GlobalContext,
|
|
40
40
|
HttpRequestContext: () => HttpRequestContext,
|
|
41
|
+
SERVER_CONTEXT_PROVIDER_TOKEN: () => SERVER_CONTEXT_PROVIDER_TOKEN,
|
|
41
42
|
ServerBaseContext: () => ServerBaseContext
|
|
42
43
|
});
|
|
43
44
|
module.exports = __toCommonJS(contexts_exports);
|
|
44
45
|
var import_lodash = __toESM(require("lodash"));
|
|
45
|
-
|
|
46
|
+
var import_context_keys = require("./context-keys");
|
|
47
|
+
const SERVER_CONTEXT_PROVIDER_TOKEN = Symbol("server-context-provider");
|
|
48
|
+
const PROVIDER_KEYS = /* @__PURE__ */ new Set([...import_context_keys.GLOBAL_CONTEXT_KEYS, ...import_context_keys.HTTP_REQUEST_CONTEXT_KEYS]);
|
|
49
|
+
const INTERNAL_KEYS = new Set(import_context_keys.SERVER_CONTEXT_INTERNAL_KEYS);
|
|
50
|
+
const BLOCKED_SANDBOX_KEYS = /* @__PURE__ */ new Set([...import_context_keys.SERVER_CONTEXT_PROTOTYPE_KEYS, ...import_context_keys.SERVER_CONTEXT_INTERNAL_KEYS]);
|
|
46
51
|
function isBlockedSandboxKey(key) {
|
|
47
52
|
return BLOCKED_SANDBOX_KEYS.has(key);
|
|
48
53
|
}
|
|
49
54
|
class ServerBaseContext {
|
|
50
55
|
_props = {};
|
|
51
|
-
_methods = {};
|
|
52
56
|
_cache = {};
|
|
53
57
|
_delegates = [];
|
|
54
58
|
_proxy;
|
|
@@ -74,10 +78,14 @@ class ServerBaseContext {
|
|
|
74
78
|
* - get:惰性 getter(默认带缓存)
|
|
75
79
|
* - once:已存在时忽略重复定义
|
|
76
80
|
*/
|
|
77
|
-
defineProperty(key, options) {
|
|
81
|
+
defineProperty(key, options, providerToken) {
|
|
78
82
|
var _a;
|
|
79
|
-
if (
|
|
80
|
-
|
|
83
|
+
if (BLOCKED_SANDBOX_KEYS.has(key)) return;
|
|
84
|
+
if (PROVIDER_KEYS.has(key) && (this instanceof HttpRequestContext || this instanceof GlobalContext) && providerToken !== SERVER_CONTEXT_PROVIDER_TOKEN) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (((_a = this._props[key]) == null ? void 0 : _a.once) && providerToken !== SERVER_CONTEXT_PROVIDER_TOKEN) return;
|
|
88
|
+
this._props[key] = providerToken === SERVER_CONTEXT_PROVIDER_TOKEN ? { ...options, once: true } : options;
|
|
81
89
|
delete this._cache[key];
|
|
82
90
|
Object.defineProperty(this, key, {
|
|
83
91
|
configurable: true,
|
|
@@ -85,20 +93,9 @@ class ServerBaseContext {
|
|
|
85
93
|
get: () => this._getOwn(key, this.createProxy())
|
|
86
94
|
});
|
|
87
95
|
}
|
|
88
|
-
/** 定义一个方法(不可枚举、不可写),访问时会自动绑定到对应实例 */
|
|
89
|
-
defineMethod(name, fn) {
|
|
90
|
-
this._methods[name] = fn;
|
|
91
|
-
Object.defineProperty(this, name, {
|
|
92
|
-
configurable: true,
|
|
93
|
-
enumerable: false,
|
|
94
|
-
writable: false,
|
|
95
|
-
value: fn
|
|
96
|
-
// bind at proxy access to keep delegate binding consistent
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
96
|
/**
|
|
100
97
|
* 委托到另一个 ServerBaseContext:
|
|
101
|
-
* -
|
|
98
|
+
* - 访问属性找不到时,会回退到被委托者进行解析
|
|
102
99
|
*/
|
|
103
100
|
delegate(ctx) {
|
|
104
101
|
if (!(ctx instanceof ServerBaseContext)) {
|
|
@@ -115,9 +112,6 @@ class ServerBaseContext {
|
|
|
115
112
|
for (const key of Object.keys(this._props)) {
|
|
116
113
|
if (!isBlockedSandboxKey(key)) keys.add(key);
|
|
117
114
|
}
|
|
118
|
-
for (const key of Object.keys(this._methods)) {
|
|
119
|
-
if (!isBlockedSandboxKey(key)) keys.add(key);
|
|
120
|
-
}
|
|
121
115
|
for (const d of this._delegates) {
|
|
122
116
|
for (const key of d.getSandboxKeys()) {
|
|
123
117
|
if (!isBlockedSandboxKey(key)) keys.add(key);
|
|
@@ -130,10 +124,6 @@ class ServerBaseContext {
|
|
|
130
124
|
if (Object.prototype.hasOwnProperty.call(this._props, key)) {
|
|
131
125
|
return this._getOwn(key, current);
|
|
132
126
|
}
|
|
133
|
-
if (Object.prototype.hasOwnProperty.call(this._methods, key)) {
|
|
134
|
-
const fn = this._methods[key];
|
|
135
|
-
return typeof fn === "function" ? fn.bind(this) : fn;
|
|
136
|
-
}
|
|
137
127
|
for (const d of this._delegates) {
|
|
138
128
|
if (!d.getSandboxKeys().includes(key)) continue;
|
|
139
129
|
return d.getSandboxValue(key, current);
|
|
@@ -145,8 +135,11 @@ class ServerBaseContext {
|
|
|
145
135
|
if (this._proxy) return this._proxy;
|
|
146
136
|
this._proxy = new Proxy(this, {
|
|
147
137
|
get: (target, key, receiver) => {
|
|
148
|
-
var _a;
|
|
149
138
|
if (typeof key !== "string") return Reflect.get(target, key, receiver);
|
|
139
|
+
if (import_context_keys.SERVER_CONTEXT_PROTOTYPE_KEYS.includes(key)) {
|
|
140
|
+
return void 0;
|
|
141
|
+
}
|
|
142
|
+
if (INTERNAL_KEYS.has(key) && key.startsWith("_")) return void 0;
|
|
150
143
|
if (Reflect.has(target, key)) {
|
|
151
144
|
const v = Reflect.get(target, key, receiver);
|
|
152
145
|
return typeof v === "function" ? v.bind(target) : v;
|
|
@@ -154,28 +147,18 @@ class ServerBaseContext {
|
|
|
154
147
|
if (Object.prototype.hasOwnProperty.call(target._props, key)) {
|
|
155
148
|
return target._getOwn(key, this.createProxy());
|
|
156
149
|
}
|
|
157
|
-
if (Object.prototype.hasOwnProperty.call(target._methods, key)) {
|
|
158
|
-
const fn = target._methods[key];
|
|
159
|
-
return typeof fn === "function" ? fn.bind(target) : fn;
|
|
160
|
-
}
|
|
161
150
|
for (const d of target._delegates) {
|
|
162
|
-
const candidate =
|
|
151
|
+
const candidate = d.getSandboxValue(key, this.createProxy());
|
|
163
152
|
if (typeof candidate !== "undefined") return candidate;
|
|
164
|
-
if (Object.prototype.hasOwnProperty.call(d._methods || {}, key)) {
|
|
165
|
-
const fn = d._methods[key];
|
|
166
|
-
return typeof fn === "function" ? fn.bind(d) : fn;
|
|
167
|
-
}
|
|
168
153
|
}
|
|
169
154
|
return void 0;
|
|
170
155
|
},
|
|
171
156
|
has: (target, key) => {
|
|
172
157
|
if (typeof key !== "string") return Reflect.has(target, key);
|
|
158
|
+
if (isBlockedSandboxKey(key)) return false;
|
|
173
159
|
if (Reflect.has(target, key)) return true;
|
|
174
160
|
if (Object.prototype.hasOwnProperty.call(target._props, key)) return true;
|
|
175
|
-
|
|
176
|
-
return target._delegates.some(
|
|
177
|
-
(d) => Object.prototype.hasOwnProperty.call(d._props, key) || Object.prototype.hasOwnProperty.call(d._methods, key)
|
|
178
|
-
);
|
|
161
|
+
return target._delegates.some((d) => d.getSandboxKeys().includes(key));
|
|
179
162
|
}
|
|
180
163
|
});
|
|
181
164
|
return this._proxy;
|
|
@@ -184,9 +167,9 @@ class ServerBaseContext {
|
|
|
184
167
|
class GlobalContext extends ServerBaseContext {
|
|
185
168
|
constructor(env) {
|
|
186
169
|
super();
|
|
187
|
-
this.defineProperty("now", { get: () => (/* @__PURE__ */ new Date()).toISOString(), cache: false });
|
|
188
|
-
this.defineProperty("timestamp", { get: () => Date.now(), cache: false });
|
|
189
|
-
if (env) this.defineProperty("env", { value: filterEnv(env) });
|
|
170
|
+
this.defineProperty("now", { get: () => (/* @__PURE__ */ new Date()).toISOString(), cache: false }, SERVER_CONTEXT_PROVIDER_TOKEN);
|
|
171
|
+
this.defineProperty("timestamp", { get: () => Date.now(), cache: false }, SERVER_CONTEXT_PROVIDER_TOKEN);
|
|
172
|
+
if (env) this.defineProperty("env", { value: filterEnv(env) }, SERVER_CONTEXT_PROVIDER_TOKEN);
|
|
190
173
|
}
|
|
191
174
|
}
|
|
192
175
|
class HttpRequestContext extends ServerBaseContext {
|
|
@@ -194,16 +177,24 @@ class HttpRequestContext extends ServerBaseContext {
|
|
|
194
177
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
195
178
|
super();
|
|
196
179
|
this.koaCtx = koaCtx;
|
|
197
|
-
this.defineProperty(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
this.defineProperty("
|
|
206
|
-
this.defineProperty("
|
|
180
|
+
this.defineProperty(
|
|
181
|
+
"user",
|
|
182
|
+
{ get: async () => {
|
|
183
|
+
var _a2, _b2;
|
|
184
|
+
return (_b2 = (_a2 = this.koaCtx) == null ? void 0 : _a2.auth) == null ? void 0 : _b2.user;
|
|
185
|
+
}, cache: true },
|
|
186
|
+
SERVER_CONTEXT_PROVIDER_TOKEN
|
|
187
|
+
);
|
|
188
|
+
this.defineProperty("roleName", { value: (_b = (_a = this.koaCtx) == null ? void 0 : _a.auth) == null ? void 0 : _b.role }, SERVER_CONTEXT_PROVIDER_TOKEN);
|
|
189
|
+
this.defineProperty("locale", { value: (_d = (_c = this.koaCtx) == null ? void 0 : _c.getCurrentLocale) == null ? void 0 : _d.call(_c) }, SERVER_CONTEXT_PROVIDER_TOKEN);
|
|
190
|
+
this.defineProperty(
|
|
191
|
+
"ip",
|
|
192
|
+
{ value: ((_f = (_e = this.koaCtx) == null ? void 0 : _e.state) == null ? void 0 : _f.clientIp) || ((_h = (_g = this.koaCtx) == null ? void 0 : _g.request) == null ? void 0 : _h.ip) },
|
|
193
|
+
SERVER_CONTEXT_PROVIDER_TOKEN
|
|
194
|
+
);
|
|
195
|
+
this.defineProperty("headers", { value: (_i = this.koaCtx) == null ? void 0 : _i.headers }, SERVER_CONTEXT_PROVIDER_TOKEN);
|
|
196
|
+
this.defineProperty("query", { value: (_k = (_j = this.koaCtx) == null ? void 0 : _j.request) == null ? void 0 : _k.query }, SERVER_CONTEXT_PROVIDER_TOKEN);
|
|
197
|
+
this.defineProperty("params", { value: import_lodash.default.get(this.koaCtx, "action.params") }, SERVER_CONTEXT_PROVIDER_TOKEN);
|
|
207
198
|
}
|
|
208
199
|
}
|
|
209
200
|
const DEFAULT_ENV_PREFIXES = ["PUBLIC_", "NEXT_PUBLIC_", "NCB_PUBLIC_"];
|
|
@@ -224,5 +215,6 @@ function filterEnv(env, allowedPrefixes = DEFAULT_ENV_PREFIXES) {
|
|
|
224
215
|
0 && (module.exports = {
|
|
225
216
|
GlobalContext,
|
|
226
217
|
HttpRequestContext,
|
|
218
|
+
SERVER_CONTEXT_PROVIDER_TOKEN,
|
|
227
219
|
ServerBaseContext
|
|
228
220
|
});
|
|
@@ -7,24 +7,16 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import 'ses';
|
|
10
|
-
|
|
10
|
+
import { type AnalyzedTemplate, type ResolvePathPolicy } from './variable-expression';
|
|
11
|
+
export type JSONValue = string | number | boolean | null | {
|
|
11
12
|
[key: string]: JSONValue;
|
|
12
13
|
} | JSONValue[];
|
|
13
14
|
/**
|
|
14
15
|
* 解析 JSON 模板中形如 {{ ... }} 的占位符(服务端解析)。
|
|
15
|
-
* 仅支持以 ctx 开头的路径与表达式(如:{{ ctx.user.id }}、{{ ctx.record.roles[0].name }})。
|
|
16
16
|
* 无法解析或不受支持的表达式将原样保留。
|
|
17
|
-
*
|
|
18
|
-
* @param template 要解析的对象/数组/字符串模板
|
|
19
|
-
* @param ctx 变量上下文(实现了所需属性/方法的代理对象)
|
|
20
|
-
* @returns 解析后的结果,与输入结构相同
|
|
21
17
|
*/
|
|
22
|
-
export declare function resolveJsonTemplate(template: JSONValue, ctx:
|
|
18
|
+
export declare function resolveJsonTemplate(template: JSONValue, ctx: unknown): Promise<any>;
|
|
23
19
|
/**
|
|
24
|
-
*
|
|
25
|
-
* 例如:ctx.user.id + 1 => (await __get('user', '.id')) + 1
|
|
26
|
-
*
|
|
27
|
-
* @param expression 原始表达式字符串
|
|
28
|
-
* @returns 改写后的表达式字符串
|
|
20
|
+
* 执行已分析的模板。授权、挂载和执行链路应复用同一个 analysis 与 policy。
|
|
29
21
|
*/
|
|
30
|
-
export declare function
|
|
22
|
+
export declare function resolveAnalyzedJsonTemplate(analysis: AnalyzedTemplate, ctx: unknown, policy: ResolvePathPolicy): Promise<any>;
|