@nocobase/plugin-flow-engine 2.2.0-beta.1 → 2.2.0-beta.10
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/client/index.js +1 -1
- package/dist/externalVersion.js +9 -9
- package/dist/locale/en-US.json +1 -0
- package/dist/locale/index.d.ts +2 -0
- package/dist/locale/zh-CN.json +1 -0
- 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/{runjs-authoring/blocked-global-stop/index.d.ts → approval/authoring-validation.d.ts} +1 -2
- package/dist/server/flow-surfaces/approval/authoring-validation.js +1151 -0
- package/dist/server/flow-surfaces/approval/blueprint-service.js +2 -0
- package/dist/server/flow-surfaces/authoring-validation.d.ts +2 -1
- package/dist/server/flow-surfaces/authoring-validation.js +495 -57
- package/dist/server/flow-surfaces/blueprint/compile-plan.js +19 -15
- package/dist/server/flow-surfaces/blueprint/export-document.d.ts +50 -0
- package/dist/server/flow-surfaces/blueprint/export-document.js +2510 -0
- package/dist/server/flow-surfaces/blueprint/normalize-document.js +2 -1
- package/dist/server/flow-surfaces/blueprint/public-types.d.ts +1 -0
- package/dist/server/flow-surfaces/builder.js +40 -5
- package/dist/server/flow-surfaces/catalog.js +13 -2
- package/dist/server/flow-surfaces/configure-options.js +5 -3
- package/dist/server/flow-surfaces/constants.d.ts +9 -2
- package/dist/server/flow-surfaces/constants.js +1 -0
- package/dist/server/flow-surfaces/contract-guard.js +24 -2
- package/dist/server/flow-surfaces/default-action-popup.d.ts +2 -0
- package/dist/server/flow-surfaces/default-action-popup.js +27 -13
- package/dist/server/flow-surfaces/filter-group.d.ts +19 -2
- package/dist/server/flow-surfaces/filter-group.js +232 -3
- package/dist/server/flow-surfaces/planning/compiler.d.ts +1 -0
- package/dist/server/flow-surfaces/planning/compiler.js +205 -82
- package/dist/server/flow-surfaces/planning/key-registry.d.ts +1 -1
- package/dist/server/flow-surfaces/planning/runtime.js +1 -0
- package/dist/server/flow-surfaces/planning/step-link.d.ts +1 -1
- package/dist/server/flow-surfaces/planning/step-link.js +22 -3
- package/dist/server/flow-surfaces/public-data-surface-default-filter.js +0 -9
- package/dist/server/flow-surfaces/reaction/field-value.d.ts +9 -0
- package/dist/server/flow-surfaces/reaction/field-value.js +51 -2
- package/dist/server/flow-surfaces/reaction/linkage.d.ts +6 -1
- package/dist/server/flow-surfaces/reaction/linkage.js +18 -3
- package/dist/server/flow-surfaces/reaction/meta.js +19 -1
- package/dist/server/flow-surfaces/reaction/types.d.ts +19 -0
- package/dist/server/flow-surfaces/relation-backing-foreign-key.d.ts +10 -0
- package/dist/server/flow-surfaces/relation-backing-foreign-key.js +79 -0
- package/dist/server/flow-surfaces/runjs-authoring/ast/source.d.ts +1 -0
- package/dist/server/flow-surfaces/runjs-authoring/ast/source.js +20 -0
- package/dist/server/flow-surfaces/runjs-authoring/rules.js +0 -2
- package/dist/server/flow-surfaces/runjs-authoring/runtime/constants.d.ts +0 -1
- package/dist/server/flow-surfaces/runjs-authoring/runtime/constants.js +11 -19
- package/dist/server/flow-surfaces/runjs-authoring/runtime/types.d.ts +0 -12
- package/dist/server/flow-surfaces/runjs-authoring/scan/filter.js +12 -2
- package/dist/server/flow-surfaces/runjs-authoring/scan/index.d.ts +0 -14
- package/dist/server/flow-surfaces/runjs-authoring/scan/index.js +10 -8
- package/dist/server/flow-surfaces/runjs-authoring/scan/source-patterns.d.ts +0 -14
- package/dist/server/flow-surfaces/runjs-authoring/scan/source-patterns.js +18 -73
- package/dist/server/flow-surfaces/runjs-authoring/types.d.ts +1 -1
- package/dist/server/flow-surfaces/runjs-authoring/validators/index.js +9 -53
- package/dist/server/flow-surfaces/service.d.ts +53 -0
- package/dist/server/flow-surfaces/service.js +990 -122
- package/dist/server/flow-surfaces/types.d.ts +2 -1
- package/dist/server/variables/registry.js +3 -0
- package/dist/swagger/flow-surfaces.d.ts +193 -0
- package/dist/swagger/flow-surfaces.examples.d.ts +11 -0
- package/dist/swagger/flow-surfaces.examples.js +11 -0
- package/dist/swagger/flow-surfaces.js +179 -10
- package/dist/swagger/index.d.ts +193 -0
- package/package.json +2 -2
- package/dist/server/flow-surfaces/runjs-authoring/blocked-global-stop/index.js +0 -40
|
@@ -0,0 +1,1151 @@
|
|
|
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 __create = Object.create;
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
13
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
15
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
|
+
var __export = (target, all) => {
|
|
17
|
+
for (var name in all)
|
|
18
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
+
};
|
|
20
|
+
var __copyProps = (to, from, except, desc) => {
|
|
21
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
22
|
+
for (let key of __getOwnPropNames(from))
|
|
23
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
24
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
25
|
+
}
|
|
26
|
+
return to;
|
|
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
|
+
));
|
|
36
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
37
|
+
var authoring_validation_exports = {};
|
|
38
|
+
__export(authoring_validation_exports, {
|
|
39
|
+
assertFlowSurfaceApprovalBlueprintAuthoringPayload: () => assertFlowSurfaceApprovalBlueprintAuthoringPayload
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(authoring_validation_exports);
|
|
42
|
+
var import_lodash = __toESM(require("lodash"));
|
|
43
|
+
var import_errors = require("../errors");
|
|
44
|
+
var import_service_utils = require("../service-utils");
|
|
45
|
+
var import_blueprint = require("./blueprint");
|
|
46
|
+
var import_catalog_specs = require("./catalog-specs");
|
|
47
|
+
var import_runtime_config = require("./runtime-config");
|
|
48
|
+
const APPROVAL_SURFACE_SET = new Set(import_blueprint.FLOW_SURFACE_APPROVAL_BLUEPRINT_SURFACES);
|
|
49
|
+
const APPROVAL_ACTION_USE_BY_PUBLIC_KEY = new Map(
|
|
50
|
+
import_catalog_specs.APPROVAL_ACTION_CATALOG_SPECS.map((item) => [item.publicKey, item.use])
|
|
51
|
+
);
|
|
52
|
+
const APPROVAL_ACTION_PUBLIC_KEYS_BY_USE = new Map(
|
|
53
|
+
import_catalog_specs.APPROVAL_ACTION_CATALOG_SPECS.map((item) => [item.use, item.publicKey])
|
|
54
|
+
);
|
|
55
|
+
const APPROVAL_BLOCK_PUBLIC_KEY_SET = new Set(import_catalog_specs.APPROVAL_BLOCK_CATALOG_SPECS.map((item) => item.key));
|
|
56
|
+
const APPROVAL_BLOCK_ALLOWED_CONTAINER_USES_BY_PUBLIC_KEY = new Map(
|
|
57
|
+
import_catalog_specs.APPROVAL_BLOCK_CATALOG_SPECS.map((item) => [item.key, item.allowedContainerUses])
|
|
58
|
+
);
|
|
59
|
+
const APPROVAL_PAGE_GRID_USE_BY_SURFACE = {
|
|
60
|
+
initiator: "TriggerBlockGridModel",
|
|
61
|
+
approver: "ApprovalBlockGridModel"
|
|
62
|
+
};
|
|
63
|
+
const APPROVAL_DEFAULT_ACTION_PUBLIC_KEYS_BY_BLOCK_TYPE = /* @__PURE__ */ new Map([
|
|
64
|
+
["approvalInitiator", /* @__PURE__ */ new Set(["approvalSubmit"])]
|
|
65
|
+
]);
|
|
66
|
+
const APPROVAL_ALLOWED_ACTION_PUBLIC_KEYS_BY_BLOCK_TYPE = new Map(
|
|
67
|
+
import_catalog_specs.APPROVAL_BLOCK_CATALOG_SPECS.map((block) => [
|
|
68
|
+
block.key,
|
|
69
|
+
new Set(
|
|
70
|
+
import_catalog_specs.APPROVAL_ACTION_CATALOG_SPECS.filter((action) => action.allowedContainerUses.includes(block.use)).map(
|
|
71
|
+
(action) => action.publicKey
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
])
|
|
75
|
+
);
|
|
76
|
+
const COMPOSE_RAW_BLOCK_KEYS = [
|
|
77
|
+
"use",
|
|
78
|
+
"fieldUse",
|
|
79
|
+
"stepParams",
|
|
80
|
+
"props",
|
|
81
|
+
"decoratorProps",
|
|
82
|
+
"flowRegistry"
|
|
83
|
+
];
|
|
84
|
+
const COMPOSE_RAW_ACTION_KEYS = [
|
|
85
|
+
"use",
|
|
86
|
+
"fieldUse",
|
|
87
|
+
"stepParams",
|
|
88
|
+
"props",
|
|
89
|
+
"decoratorProps",
|
|
90
|
+
"flowRegistry"
|
|
91
|
+
];
|
|
92
|
+
function buildAuthoringError(input) {
|
|
93
|
+
return {
|
|
94
|
+
path: input.path,
|
|
95
|
+
ruleId: input.ruleId,
|
|
96
|
+
message: input.message,
|
|
97
|
+
details: {
|
|
98
|
+
retryAction: "fix_payload_and_retry_same_applyApprovalBlueprint_write",
|
|
99
|
+
...input.details || {}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function normalizeOptionalBindingId(value) {
|
|
104
|
+
if (import_lodash.default.isUndefined(value) || import_lodash.default.isNull(value) || String(value).trim() === "") {
|
|
105
|
+
return {
|
|
106
|
+
present: false,
|
|
107
|
+
valid: true
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
if (typeof value === "number") {
|
|
111
|
+
return {
|
|
112
|
+
present: true,
|
|
113
|
+
valid: Number.isFinite(value)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
present: true,
|
|
118
|
+
valid: /^\d+$/.test(String(value).trim())
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function getSurface(value) {
|
|
122
|
+
const normalized = String(value || "").trim();
|
|
123
|
+
return APPROVAL_SURFACE_SET.has(normalized) ? normalized : void 0;
|
|
124
|
+
}
|
|
125
|
+
function readActionType(action) {
|
|
126
|
+
if (typeof action === "string") {
|
|
127
|
+
return String(action || "").trim();
|
|
128
|
+
}
|
|
129
|
+
if (import_lodash.default.isPlainObject(action)) {
|
|
130
|
+
return String(action.type || "").trim();
|
|
131
|
+
}
|
|
132
|
+
return "";
|
|
133
|
+
}
|
|
134
|
+
function readActionKey(action, actionType) {
|
|
135
|
+
if (import_lodash.default.isPlainObject(action)) {
|
|
136
|
+
const key = String(action.key || "").trim();
|
|
137
|
+
return key || actionType;
|
|
138
|
+
}
|
|
139
|
+
return actionType;
|
|
140
|
+
}
|
|
141
|
+
function readErrorMessage(error) {
|
|
142
|
+
return error instanceof Error ? error.message : String(error);
|
|
143
|
+
}
|
|
144
|
+
function collectThrownAuthoringError(errors, input, run) {
|
|
145
|
+
try {
|
|
146
|
+
return run();
|
|
147
|
+
} catch (error) {
|
|
148
|
+
errors.push(
|
|
149
|
+
buildAuthoringError({
|
|
150
|
+
path: input.path,
|
|
151
|
+
ruleId: input.ruleId,
|
|
152
|
+
message: readErrorMessage(error),
|
|
153
|
+
details: input.details
|
|
154
|
+
})
|
|
155
|
+
);
|
|
156
|
+
return void 0;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function collectTopLevelShapeErrors(payload, errors) {
|
|
160
|
+
const version = import_lodash.default.isUndefined(payload.version) ? "1" : String(payload.version || "").trim();
|
|
161
|
+
if (version !== "1") {
|
|
162
|
+
errors.push(
|
|
163
|
+
buildAuthoringError({
|
|
164
|
+
path: "$.version",
|
|
165
|
+
ruleId: "approval-blueprint-version-unsupported",
|
|
166
|
+
message: "flowSurfaces applyApprovalBlueprint only supports version '1'",
|
|
167
|
+
details: {
|
|
168
|
+
invalidVersion: payload.version,
|
|
169
|
+
suggestedFix: "Set version to '1' or omit it."
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
const mode = import_lodash.default.isUndefined(payload.mode) ? "replace" : String(payload.mode || "").trim();
|
|
175
|
+
if (mode !== "replace") {
|
|
176
|
+
errors.push(
|
|
177
|
+
buildAuthoringError({
|
|
178
|
+
path: "$.mode",
|
|
179
|
+
ruleId: "approval-blueprint-mode-unsupported",
|
|
180
|
+
message: "flowSurfaces applyApprovalBlueprint only supports mode 'replace'",
|
|
181
|
+
details: {
|
|
182
|
+
invalidMode: payload.mode,
|
|
183
|
+
suggestedFix: "Set mode to 'replace' or omit it."
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
const surface = getSurface(payload.surface);
|
|
189
|
+
if (!surface) {
|
|
190
|
+
errors.push(
|
|
191
|
+
buildAuthoringError({
|
|
192
|
+
path: "$.surface",
|
|
193
|
+
ruleId: "approval-blueprint-surface-invalid",
|
|
194
|
+
message: `flowSurfaces applyApprovalBlueprint surface must be one of ${import_blueprint.FLOW_SURFACE_APPROVAL_BLUEPRINT_SURFACES.map(
|
|
195
|
+
(item) => `'${item}'`
|
|
196
|
+
).join(", ")}`,
|
|
197
|
+
details: {
|
|
198
|
+
invalidSurface: payload.surface,
|
|
199
|
+
allowedSurfaces: import_blueprint.FLOW_SURFACE_APPROVAL_BLUEPRINT_SURFACES,
|
|
200
|
+
suggestedFix: "Use surface 'initiator', 'approver', or 'taskCard'."
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
if (!import_lodash.default.isUndefined(payload.layout) && !import_lodash.default.isPlainObject(payload.layout)) {
|
|
206
|
+
errors.push(
|
|
207
|
+
buildAuthoringError({
|
|
208
|
+
path: "$.layout",
|
|
209
|
+
ruleId: "approval-blueprint-layout-shape",
|
|
210
|
+
message: "flowSurfaces applyApprovalBlueprint layout must be an object",
|
|
211
|
+
details: {
|
|
212
|
+
suggestedFix: "Pass a layout object such as { rows: [[...]] }, or omit layout."
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
return surface;
|
|
218
|
+
}
|
|
219
|
+
function collectBindingErrors(payload, surface, errors) {
|
|
220
|
+
if (!surface) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const workflowId = normalizeOptionalBindingId(payload.workflowId);
|
|
224
|
+
const nodeId = normalizeOptionalBindingId(payload.nodeId);
|
|
225
|
+
if (workflowId.present && !workflowId.valid) {
|
|
226
|
+
errors.push(
|
|
227
|
+
buildAuthoringError({
|
|
228
|
+
path: "$.workflowId",
|
|
229
|
+
ruleId: "approval-blueprint-workflow-id-invalid",
|
|
230
|
+
message: "flowSurfaces applyApprovalBlueprint workflowId must be an integer identifier",
|
|
231
|
+
details: {
|
|
232
|
+
invalidWorkflowId: payload.workflowId,
|
|
233
|
+
suggestedFix: "Pass an integer workflowId, or remove workflowId when the selected surface does not accept it."
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
if (nodeId.present && !nodeId.valid) {
|
|
239
|
+
errors.push(
|
|
240
|
+
buildAuthoringError({
|
|
241
|
+
path: "$.nodeId",
|
|
242
|
+
ruleId: "approval-blueprint-node-id-invalid",
|
|
243
|
+
message: "flowSurfaces applyApprovalBlueprint nodeId must be an integer identifier",
|
|
244
|
+
details: {
|
|
245
|
+
invalidNodeId: payload.nodeId,
|
|
246
|
+
suggestedFix: "Pass an integer nodeId, or remove nodeId when the selected surface does not accept it."
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
if (surface === "initiator") {
|
|
252
|
+
if (!workflowId.present) {
|
|
253
|
+
errors.push(
|
|
254
|
+
buildAuthoringError({
|
|
255
|
+
path: "$.workflowId",
|
|
256
|
+
ruleId: "approval-initiator-workflow-id-required",
|
|
257
|
+
message: "flowSurfaces applyApprovalBlueprint surface 'initiator' requires workflowId",
|
|
258
|
+
details: {
|
|
259
|
+
surface,
|
|
260
|
+
suggestedFix: "Pass workflowId for initiator surfaces."
|
|
261
|
+
}
|
|
262
|
+
})
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
if (nodeId.present) {
|
|
266
|
+
errors.push(
|
|
267
|
+
buildAuthoringError({
|
|
268
|
+
path: "$.nodeId",
|
|
269
|
+
ruleId: "approval-initiator-node-id-forbidden",
|
|
270
|
+
message: "flowSurfaces applyApprovalBlueprint surface 'initiator' does not accept nodeId",
|
|
271
|
+
details: {
|
|
272
|
+
surface,
|
|
273
|
+
suggestedFix: "Remove nodeId; initiator surfaces bind to the workflow trigger by workflowId."
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
if (!import_lodash.default.isUndefined(payload.fields)) {
|
|
279
|
+
errors.push(
|
|
280
|
+
buildAuthoringError({
|
|
281
|
+
path: "$.fields",
|
|
282
|
+
ruleId: "approval-page-fields-forbidden",
|
|
283
|
+
message: "flowSurfaces applyApprovalBlueprint surface 'initiator' does not accept fields",
|
|
284
|
+
details: {
|
|
285
|
+
surface,
|
|
286
|
+
suggestedFix: "Use blocks[].fields for initiator form fields."
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (surface === "approver") {
|
|
294
|
+
if (!nodeId.present) {
|
|
295
|
+
errors.push(
|
|
296
|
+
buildAuthoringError({
|
|
297
|
+
path: "$.nodeId",
|
|
298
|
+
ruleId: "approval-approver-node-id-required",
|
|
299
|
+
message: "flowSurfaces applyApprovalBlueprint surface 'approver' requires nodeId",
|
|
300
|
+
details: {
|
|
301
|
+
surface,
|
|
302
|
+
suggestedFix: "Pass nodeId for approver surfaces."
|
|
303
|
+
}
|
|
304
|
+
})
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
if (workflowId.present) {
|
|
308
|
+
errors.push(
|
|
309
|
+
buildAuthoringError({
|
|
310
|
+
path: "$.workflowId",
|
|
311
|
+
ruleId: "approval-approver-workflow-id-forbidden",
|
|
312
|
+
message: "flowSurfaces applyApprovalBlueprint surface 'approver' does not accept workflowId",
|
|
313
|
+
details: {
|
|
314
|
+
surface,
|
|
315
|
+
suggestedFix: "Remove workflowId; approver surfaces bind to one approval node by nodeId."
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
if (!import_lodash.default.isUndefined(payload.fields)) {
|
|
321
|
+
errors.push(
|
|
322
|
+
buildAuthoringError({
|
|
323
|
+
path: "$.fields",
|
|
324
|
+
ruleId: "approval-page-fields-forbidden",
|
|
325
|
+
message: "flowSurfaces applyApprovalBlueprint surface 'approver' does not accept fields",
|
|
326
|
+
details: {
|
|
327
|
+
surface,
|
|
328
|
+
suggestedFix: "Use blocks[].fields for approver form/details fields."
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (workflowId.present === nodeId.present) {
|
|
336
|
+
errors.push(
|
|
337
|
+
buildAuthoringError({
|
|
338
|
+
path: "$",
|
|
339
|
+
ruleId: "approval-task-card-binding-exclusive",
|
|
340
|
+
message: "flowSurfaces applyApprovalBlueprint surface 'taskCard' requires exactly one of workflowId or nodeId",
|
|
341
|
+
details: {
|
|
342
|
+
surface,
|
|
343
|
+
suggestedFix: "Pass exactly one binding id: workflowId for submitter task cards, or nodeId for approver task cards."
|
|
344
|
+
}
|
|
345
|
+
})
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
if (!import_lodash.default.isUndefined(payload.blocks)) {
|
|
349
|
+
errors.push(
|
|
350
|
+
buildAuthoringError({
|
|
351
|
+
path: "$.blocks",
|
|
352
|
+
ruleId: "approval-task-card-blocks-forbidden",
|
|
353
|
+
message: "flowSurfaces applyApprovalBlueprint surface 'taskCard' does not accept blocks",
|
|
354
|
+
details: {
|
|
355
|
+
surface,
|
|
356
|
+
suggestedFix: "Use fields + layout for taskCard surfaces instead of blocks."
|
|
357
|
+
}
|
|
358
|
+
})
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function collectCollectionShapeErrors(payload, surface, errors) {
|
|
363
|
+
if (surface === "initiator" || surface === "approver") {
|
|
364
|
+
if (!import_lodash.default.isUndefined(payload.blocks) && !Array.isArray(payload.blocks)) {
|
|
365
|
+
errors.push(
|
|
366
|
+
buildAuthoringError({
|
|
367
|
+
path: "$.blocks",
|
|
368
|
+
ruleId: "approval-blueprint-blocks-shape",
|
|
369
|
+
message: "flowSurfaces applyApprovalBlueprint blocks must be an array",
|
|
370
|
+
details: {
|
|
371
|
+
surface,
|
|
372
|
+
suggestedFix: "Pass blocks as an array, or omit blocks."
|
|
373
|
+
}
|
|
374
|
+
})
|
|
375
|
+
);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const blockKeys = /* @__PURE__ */ new Map();
|
|
379
|
+
const normalizedBlocks = [];
|
|
380
|
+
import_lodash.default.castArray(payload.blocks || []).forEach((block, index) => {
|
|
381
|
+
if (!import_lodash.default.isPlainObject(block)) {
|
|
382
|
+
errors.push(
|
|
383
|
+
buildAuthoringError({
|
|
384
|
+
path: `$.blocks[${index}]`,
|
|
385
|
+
ruleId: "approval-blueprint-block-shape",
|
|
386
|
+
message: `flowSurfaces applyApprovalBlueprint blocks[${index}] must be an object`,
|
|
387
|
+
details: {
|
|
388
|
+
surface,
|
|
389
|
+
blockIndex: index,
|
|
390
|
+
suggestedFix: "Pass each block as an object with key and type/template."
|
|
391
|
+
}
|
|
392
|
+
})
|
|
393
|
+
);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const normalizedBlock = collectBlockIdentityErrors(
|
|
397
|
+
block,
|
|
398
|
+
index,
|
|
399
|
+
surface,
|
|
400
|
+
blockKeys,
|
|
401
|
+
errors
|
|
402
|
+
);
|
|
403
|
+
if (normalizedBlock) {
|
|
404
|
+
normalizedBlocks.push(normalizedBlock);
|
|
405
|
+
}
|
|
406
|
+
collectBlockPublicShapeErrors(block, index, surface, errors);
|
|
407
|
+
collectBlockFieldErrors(block, index, surface, errors);
|
|
408
|
+
collectBlockSurfaceErrors(block, index, surface, errors);
|
|
409
|
+
collectBlockActionErrors(block, index, surface, errors);
|
|
410
|
+
});
|
|
411
|
+
collectLayoutReferenceErrors({
|
|
412
|
+
layout: payload.layout,
|
|
413
|
+
itemKeys: new Set(normalizedBlocks.map((item) => item.key).filter((key) => !!key)),
|
|
414
|
+
path: "$.layout",
|
|
415
|
+
surface,
|
|
416
|
+
itemKind: "block",
|
|
417
|
+
errors
|
|
418
|
+
});
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (surface === "taskCard") {
|
|
422
|
+
const normalizedFields = collectFieldListErrors({
|
|
423
|
+
input: payload.fields,
|
|
424
|
+
path: "$.fields",
|
|
425
|
+
surface,
|
|
426
|
+
context: "flowSurfaces applyApprovalBlueprint taskCard fields",
|
|
427
|
+
disallowTarget: true,
|
|
428
|
+
errors
|
|
429
|
+
});
|
|
430
|
+
collectLayoutReferenceErrors({
|
|
431
|
+
layout: payload.layout,
|
|
432
|
+
itemKeys: new Set(normalizedFields.map((field) => field.key).filter((key) => !!key)),
|
|
433
|
+
path: "$.layout",
|
|
434
|
+
surface,
|
|
435
|
+
itemKind: "field",
|
|
436
|
+
errors
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function collectBlockIdentityErrors(block, blockIndex, surface, seenKeys, errors) {
|
|
441
|
+
const path = `$.blocks[${blockIndex}]`;
|
|
442
|
+
const key = collectThrownAuthoringError(
|
|
443
|
+
errors,
|
|
444
|
+
{
|
|
445
|
+
path: `${path}.key`,
|
|
446
|
+
ruleId: "approval-blueprint-block-key-invalid",
|
|
447
|
+
details: {
|
|
448
|
+
surface,
|
|
449
|
+
blockIndex,
|
|
450
|
+
suggestedFix: "Provide a non-empty, unique block key."
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
() => (0, import_service_utils.normalizeFlowSurfaceComposeKey)(block.key, `flowSurfaces compose block #${blockIndex + 1}`)
|
|
454
|
+
);
|
|
455
|
+
const type = String(block.type || "").trim();
|
|
456
|
+
const hasTemplate = !import_lodash.default.isUndefined(block.template);
|
|
457
|
+
if (!type && !hasTemplate) {
|
|
458
|
+
errors.push(
|
|
459
|
+
buildAuthoringError({
|
|
460
|
+
path,
|
|
461
|
+
ruleId: "approval-blueprint-block-type-or-template-required",
|
|
462
|
+
message: `flowSurfaces compose block #${blockIndex + 1} requires key and either type or template`,
|
|
463
|
+
details: {
|
|
464
|
+
surface,
|
|
465
|
+
blockIndex,
|
|
466
|
+
suggestedFix: "Set either block.type or block.template."
|
|
467
|
+
}
|
|
468
|
+
})
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
if (!key) {
|
|
472
|
+
return void 0;
|
|
473
|
+
}
|
|
474
|
+
const previousIndex = seenKeys.get(key);
|
|
475
|
+
if (typeof previousIndex !== "undefined") {
|
|
476
|
+
errors.push(
|
|
477
|
+
buildAuthoringError({
|
|
478
|
+
path: `${path}.key`,
|
|
479
|
+
ruleId: "approval-blueprint-block-key-duplicate",
|
|
480
|
+
message: `flowSurfaces compose blocks key '${key}' is duplicated at #${previousIndex + 1} and #${blockIndex + 1}`,
|
|
481
|
+
details: {
|
|
482
|
+
surface,
|
|
483
|
+
key,
|
|
484
|
+
firstIndex: previousIndex,
|
|
485
|
+
duplicateIndex: blockIndex,
|
|
486
|
+
suggestedFix: "Use a unique key for each block and update layout references accordingly."
|
|
487
|
+
}
|
|
488
|
+
})
|
|
489
|
+
);
|
|
490
|
+
return void 0;
|
|
491
|
+
}
|
|
492
|
+
seenKeys.set(key, blockIndex);
|
|
493
|
+
return {
|
|
494
|
+
key
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
function collectBlockPublicShapeErrors(block, blockIndex, surface, errors) {
|
|
498
|
+
const rawKey = COMPOSE_RAW_BLOCK_KEYS.find((key) => !import_lodash.default.isUndefined(block[key]));
|
|
499
|
+
if (rawKey) {
|
|
500
|
+
errors.push(
|
|
501
|
+
buildAuthoringError({
|
|
502
|
+
path: `$.blocks[${blockIndex}].${rawKey}`,
|
|
503
|
+
ruleId: "approval-blueprint-block-public-fields-only",
|
|
504
|
+
message: "flowSurfaces compose only accepts public semantic block fields",
|
|
505
|
+
details: {
|
|
506
|
+
surface,
|
|
507
|
+
blockIndex,
|
|
508
|
+
rawKey,
|
|
509
|
+
suggestedFix: "Remove raw FlowModel fields and use public block settings/fields/actions instead."
|
|
510
|
+
}
|
|
511
|
+
})
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
if (!import_lodash.default.isUndefined(block.template) && !import_lodash.default.isPlainObject(block.template)) {
|
|
515
|
+
errors.push(
|
|
516
|
+
buildAuthoringError({
|
|
517
|
+
path: `$.blocks[${blockIndex}].template`,
|
|
518
|
+
ruleId: "approval-blueprint-block-template-shape",
|
|
519
|
+
message: `flowSurfaces compose block #${blockIndex + 1} template must be an object`,
|
|
520
|
+
details: {
|
|
521
|
+
surface,
|
|
522
|
+
blockIndex,
|
|
523
|
+
suggestedFix: "Use template: { uid, mode } or remove template."
|
|
524
|
+
}
|
|
525
|
+
})
|
|
526
|
+
);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (!import_lodash.default.isPlainObject(block.template)) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const template = block.template;
|
|
533
|
+
const templateUid = String(template.uid || "").trim();
|
|
534
|
+
if (!templateUid) {
|
|
535
|
+
errors.push(
|
|
536
|
+
buildAuthoringError({
|
|
537
|
+
path: `$.blocks[${blockIndex}].template.uid`,
|
|
538
|
+
ruleId: "approval-blueprint-block-template-uid-required",
|
|
539
|
+
message: "flowSurfaces compose template.uid is required",
|
|
540
|
+
details: {
|
|
541
|
+
surface,
|
|
542
|
+
blockIndex,
|
|
543
|
+
suggestedFix: "Set template.uid to the saved block template uid."
|
|
544
|
+
}
|
|
545
|
+
})
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
const templateMode = String(template.mode || "reference").trim() || "reference";
|
|
549
|
+
if (templateMode !== "reference" && templateMode !== "copy") {
|
|
550
|
+
errors.push(
|
|
551
|
+
buildAuthoringError({
|
|
552
|
+
path: `$.blocks[${blockIndex}].template.mode`,
|
|
553
|
+
ruleId: "approval-blueprint-block-template-mode-unsupported",
|
|
554
|
+
message: "flowSurfaces compose template.mode only supports 'reference' or 'copy'",
|
|
555
|
+
details: {
|
|
556
|
+
surface,
|
|
557
|
+
blockIndex,
|
|
558
|
+
mode: template.mode,
|
|
559
|
+
suggestedFix: "Use template.mode 'reference' or 'copy'."
|
|
560
|
+
}
|
|
561
|
+
})
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
const templateUsage = String(template.usage || "block").trim() || "block";
|
|
565
|
+
if (templateUsage !== "block" && templateUsage !== "fields") {
|
|
566
|
+
errors.push(
|
|
567
|
+
buildAuthoringError({
|
|
568
|
+
path: `$.blocks[${blockIndex}].template.usage`,
|
|
569
|
+
ruleId: "approval-blueprint-block-template-usage-unsupported",
|
|
570
|
+
message: "flowSurfaces compose template.usage only supports 'block' or 'fields'",
|
|
571
|
+
details: {
|
|
572
|
+
surface,
|
|
573
|
+
blockIndex,
|
|
574
|
+
usage: template.usage,
|
|
575
|
+
suggestedFix: "Use template.usage 'block' or 'fields', or omit usage."
|
|
576
|
+
}
|
|
577
|
+
})
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
if (templateUsage === "block") {
|
|
581
|
+
const conflictKey = ["type", "use", "resource", "resourceInit"].find(
|
|
582
|
+
(key) => !import_lodash.default.isUndefined(block[key])
|
|
583
|
+
);
|
|
584
|
+
if (conflictKey) {
|
|
585
|
+
errors.push(
|
|
586
|
+
buildAuthoringError({
|
|
587
|
+
path: `$.blocks[${blockIndex}].${conflictKey}`,
|
|
588
|
+
ruleId: "approval-blueprint-block-template-block-conflict",
|
|
589
|
+
message: "flowSurfaces addBlock template.usage='block' does not allow type, use, resource or resourceInit",
|
|
590
|
+
details: {
|
|
591
|
+
surface,
|
|
592
|
+
blockIndex,
|
|
593
|
+
conflictKey,
|
|
594
|
+
suggestedFix: "Remove type/resource/resourceInit when using template.usage='block', or use template.usage='fields' when importing only fields into a concrete block."
|
|
595
|
+
}
|
|
596
|
+
})
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (templateUsage === "fields") {
|
|
601
|
+
const conflictKey = ["resource", "resourceInit"].find(
|
|
602
|
+
(key) => !import_lodash.default.isUndefined(block[key])
|
|
603
|
+
);
|
|
604
|
+
if (conflictKey) {
|
|
605
|
+
errors.push(
|
|
606
|
+
buildAuthoringError({
|
|
607
|
+
path: `$.blocks[${blockIndex}].${conflictKey}`,
|
|
608
|
+
ruleId: "approval-blueprint-block-template-fields-resource-conflict",
|
|
609
|
+
message: "flowSurfaces addBlock template.usage='fields' does not allow resource or resourceInit overrides",
|
|
610
|
+
details: {
|
|
611
|
+
surface,
|
|
612
|
+
blockIndex,
|
|
613
|
+
conflictKey,
|
|
614
|
+
suggestedFix: "Remove resource/resourceInit when using template.usage='fields'."
|
|
615
|
+
}
|
|
616
|
+
})
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
if (templateUsage === "fields" && import_lodash.default.castArray(block.fields || []).length) {
|
|
621
|
+
errors.push(
|
|
622
|
+
buildAuthoringError({
|
|
623
|
+
path: `$.blocks[${blockIndex}].fields`,
|
|
624
|
+
ruleId: "approval-blueprint-block-template-fields-mixed",
|
|
625
|
+
message: `flowSurfaces compose block #${blockIndex + 1} cannot mix template.usage='fields' with fields[]`,
|
|
626
|
+
details: {
|
|
627
|
+
surface,
|
|
628
|
+
blockIndex,
|
|
629
|
+
suggestedFix: "Remove fields[] when using template.usage='fields'."
|
|
630
|
+
}
|
|
631
|
+
})
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
if (templateMode !== "copy" && import_lodash.default.isPlainObject(block.settings) && Object.keys(block.settings).length) {
|
|
635
|
+
errors.push(
|
|
636
|
+
buildAuthoringError({
|
|
637
|
+
path: `$.blocks[${blockIndex}].settings`,
|
|
638
|
+
ruleId: "approval-blueprint-block-template-reference-settings",
|
|
639
|
+
message: `flowSurfaces compose block #${blockIndex + 1} does not allow settings when template.mode is 'reference'`,
|
|
640
|
+
details: {
|
|
641
|
+
surface,
|
|
642
|
+
blockIndex,
|
|
643
|
+
suggestedFix: "Use template.mode='copy' when overriding settings, or remove settings."
|
|
644
|
+
}
|
|
645
|
+
})
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function collectBlockFieldErrors(block, blockIndex, surface, errors) {
|
|
650
|
+
collectFieldListErrors({
|
|
651
|
+
input: block.fields,
|
|
652
|
+
path: `$.blocks[${blockIndex}].fields`,
|
|
653
|
+
surface,
|
|
654
|
+
context: `flowSurfaces compose block #${blockIndex + 1} fields`,
|
|
655
|
+
disallowTarget: false,
|
|
656
|
+
errors
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
function collectFieldListErrors(input) {
|
|
660
|
+
if (import_lodash.default.isUndefined(input.input)) {
|
|
661
|
+
return [];
|
|
662
|
+
}
|
|
663
|
+
if (!Array.isArray(input.input)) {
|
|
664
|
+
input.errors.push(
|
|
665
|
+
buildAuthoringError({
|
|
666
|
+
path: input.path,
|
|
667
|
+
ruleId: "approval-blueprint-fields-shape",
|
|
668
|
+
message: "flowSurfaces applyApprovalBlueprint fields must be an array",
|
|
669
|
+
details: {
|
|
670
|
+
surface: input.surface,
|
|
671
|
+
suggestedFix: "Pass fields as an array, or omit fields."
|
|
672
|
+
}
|
|
673
|
+
})
|
|
674
|
+
);
|
|
675
|
+
return [];
|
|
676
|
+
}
|
|
677
|
+
const seenKeys = /* @__PURE__ */ new Map();
|
|
678
|
+
const normalizedFields = [];
|
|
679
|
+
input.input.forEach((field, fieldIndex) => {
|
|
680
|
+
const normalizedField = collectThrownAuthoringError(
|
|
681
|
+
input.errors,
|
|
682
|
+
{
|
|
683
|
+
path: `${input.path}[${fieldIndex}]`,
|
|
684
|
+
ruleId: "approval-blueprint-field-invalid",
|
|
685
|
+
details: {
|
|
686
|
+
surface: input.surface,
|
|
687
|
+
fieldIndex,
|
|
688
|
+
suggestedFix: "Use a non-empty field path string or a public field object with fieldPath/type."
|
|
689
|
+
}
|
|
690
|
+
},
|
|
691
|
+
() => (0, import_service_utils.normalizeComposeFieldSpec)(field, fieldIndex)
|
|
692
|
+
);
|
|
693
|
+
if (!normalizedField) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (input.disallowTarget && normalizedField.target) {
|
|
697
|
+
input.errors.push(
|
|
698
|
+
buildAuthoringError({
|
|
699
|
+
path: `${input.path}[${fieldIndex}].target`,
|
|
700
|
+
ruleId: "approval-task-card-field-target-forbidden",
|
|
701
|
+
message: "flowSurfaces applyApprovalBlueprint taskCard fields do not support target",
|
|
702
|
+
details: {
|
|
703
|
+
surface: input.surface,
|
|
704
|
+
fieldIndex,
|
|
705
|
+
target: normalizedField.target,
|
|
706
|
+
suggestedFix: "Remove target from taskCard fields; every field is added to the task-card details grid."
|
|
707
|
+
}
|
|
708
|
+
})
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
const key = normalizedField.key;
|
|
712
|
+
const previousIndex = seenKeys.get(key);
|
|
713
|
+
if (typeof previousIndex !== "undefined") {
|
|
714
|
+
input.errors.push(
|
|
715
|
+
buildAuthoringError({
|
|
716
|
+
path: `${input.path}[${fieldIndex}]`,
|
|
717
|
+
ruleId: "approval-blueprint-field-key-duplicate",
|
|
718
|
+
message: `${input.context} key '${key}' is duplicated at #${previousIndex + 1} and #${fieldIndex + 1}`,
|
|
719
|
+
details: {
|
|
720
|
+
surface: input.surface,
|
|
721
|
+
key,
|
|
722
|
+
firstIndex: previousIndex,
|
|
723
|
+
duplicateIndex: fieldIndex,
|
|
724
|
+
suggestedFix: "Use a unique key for each field and update layout references accordingly."
|
|
725
|
+
}
|
|
726
|
+
})
|
|
727
|
+
);
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
seenKeys.set(key, fieldIndex);
|
|
731
|
+
normalizedFields.push(normalizedField);
|
|
732
|
+
});
|
|
733
|
+
return normalizedFields;
|
|
734
|
+
}
|
|
735
|
+
function collectLayoutReferenceErrors(input) {
|
|
736
|
+
if (!import_lodash.default.isPlainObject(input.layout)) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
const rows = input.layout.rows;
|
|
740
|
+
if (import_lodash.default.isUndefined(rows)) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (!Array.isArray(rows)) {
|
|
744
|
+
input.errors.push(
|
|
745
|
+
buildAuthoringError({
|
|
746
|
+
path: `${input.path}.rows`,
|
|
747
|
+
ruleId: "approval-blueprint-layout-rows-shape",
|
|
748
|
+
message: "flowSurfaces compose layout rows must be an array",
|
|
749
|
+
details: {
|
|
750
|
+
surface: input.surface,
|
|
751
|
+
suggestedFix: "Use layout.rows as an array of non-empty rows."
|
|
752
|
+
}
|
|
753
|
+
})
|
|
754
|
+
);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
const mentionedKeys = /* @__PURE__ */ new Map();
|
|
758
|
+
rows.forEach((row, rowIndex) => {
|
|
759
|
+
if (!Array.isArray(row) || !row.length) {
|
|
760
|
+
input.errors.push(
|
|
761
|
+
buildAuthoringError({
|
|
762
|
+
path: `${input.path}.rows[${rowIndex}]`,
|
|
763
|
+
ruleId: "approval-blueprint-layout-row-shape",
|
|
764
|
+
message: `flowSurfaces compose layout row #${rowIndex + 1} must be a non-empty array`,
|
|
765
|
+
details: {
|
|
766
|
+
surface: input.surface,
|
|
767
|
+
rowIndex,
|
|
768
|
+
suggestedFix: "Use each layout row as a non-empty array of block/field keys."
|
|
769
|
+
}
|
|
770
|
+
})
|
|
771
|
+
);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
row.forEach(
|
|
775
|
+
(cell, cellIndex) => collectLayoutCellReferenceErrors({
|
|
776
|
+
cell,
|
|
777
|
+
itemKeys: input.itemKeys,
|
|
778
|
+
mentionedKeys,
|
|
779
|
+
path: `${input.path}.rows[${rowIndex}][${cellIndex}]`,
|
|
780
|
+
surface: input.surface,
|
|
781
|
+
itemKind: input.itemKind,
|
|
782
|
+
rowIndex,
|
|
783
|
+
cellIndex,
|
|
784
|
+
errors: input.errors
|
|
785
|
+
})
|
|
786
|
+
);
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
function collectLayoutCellReferenceErrors(input) {
|
|
790
|
+
if (import_lodash.default.isPlainObject(input.cell)) {
|
|
791
|
+
const cell = input.cell;
|
|
792
|
+
const key2 = String(cell.composeKey || cell.key || "").trim();
|
|
793
|
+
if (!key2) {
|
|
794
|
+
input.errors.push(
|
|
795
|
+
buildAuthoringError({
|
|
796
|
+
path: input.path,
|
|
797
|
+
ruleId: "approval-blueprint-layout-cell-key-empty",
|
|
798
|
+
message: `flowSurfaces compose layout row #${input.rowIndex + 1} contains an empty key`,
|
|
799
|
+
details: {
|
|
800
|
+
surface: input.surface,
|
|
801
|
+
rowIndex: input.rowIndex,
|
|
802
|
+
cellIndex: input.cellIndex,
|
|
803
|
+
suggestedFix: "Use a non-empty key/composeKey, or a uid for an existing item."
|
|
804
|
+
}
|
|
805
|
+
})
|
|
806
|
+
);
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
collectLayoutKeyReferenceError({ ...input, key: key2 });
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
const key = String(input.cell || "").trim();
|
|
813
|
+
if (!key) {
|
|
814
|
+
input.errors.push(
|
|
815
|
+
buildAuthoringError({
|
|
816
|
+
path: input.path,
|
|
817
|
+
ruleId: "approval-blueprint-layout-cell-key-empty",
|
|
818
|
+
message: `flowSurfaces compose layout row #${input.rowIndex + 1} contains an empty key`,
|
|
819
|
+
details: {
|
|
820
|
+
surface: input.surface,
|
|
821
|
+
rowIndex: input.rowIndex,
|
|
822
|
+
cellIndex: input.cellIndex,
|
|
823
|
+
suggestedFix: "Use a non-empty block or field key."
|
|
824
|
+
}
|
|
825
|
+
})
|
|
826
|
+
);
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
collectLayoutKeyReferenceError({ ...input, key });
|
|
830
|
+
}
|
|
831
|
+
function collectLayoutKeyReferenceError(input) {
|
|
832
|
+
const previous = input.mentionedKeys.get(input.key);
|
|
833
|
+
if (previous) {
|
|
834
|
+
input.errors.push(
|
|
835
|
+
buildAuthoringError({
|
|
836
|
+
path: input.path,
|
|
837
|
+
ruleId: "approval-blueprint-layout-key-duplicate",
|
|
838
|
+
message: `flowSurfaces compose layout key '${input.key}' is duplicated`,
|
|
839
|
+
details: {
|
|
840
|
+
surface: input.surface,
|
|
841
|
+
rowIndex: input.rowIndex,
|
|
842
|
+
cellIndex: input.cellIndex,
|
|
843
|
+
key: input.key,
|
|
844
|
+
firstRowIndex: previous.rowIndex,
|
|
845
|
+
firstCellIndex: previous.cellIndex,
|
|
846
|
+
suggestedFix: "Reference each block or field key at most once in layout.rows."
|
|
847
|
+
}
|
|
848
|
+
})
|
|
849
|
+
);
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
input.mentionedKeys.set(input.key, {
|
|
853
|
+
rowIndex: input.rowIndex,
|
|
854
|
+
cellIndex: input.cellIndex
|
|
855
|
+
});
|
|
856
|
+
if (input.itemKeys.has(input.key)) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
input.errors.push(
|
|
860
|
+
buildAuthoringError({
|
|
861
|
+
path: input.path,
|
|
862
|
+
ruleId: "approval-blueprint-layout-key-unknown",
|
|
863
|
+
message: `flowSurfaces compose layout key '${input.key}' does not match a created ${input.itemKind} key`,
|
|
864
|
+
details: {
|
|
865
|
+
surface: input.surface,
|
|
866
|
+
rowIndex: input.rowIndex,
|
|
867
|
+
cellIndex: input.cellIndex,
|
|
868
|
+
key: input.key,
|
|
869
|
+
itemKind: input.itemKind,
|
|
870
|
+
availableKeys: Array.from(input.itemKeys),
|
|
871
|
+
suggestedFix: `Reference an existing ${input.itemKind} key from this applyApprovalBlueprint payload, or remove the layout cell.`
|
|
872
|
+
}
|
|
873
|
+
})
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
function collectBlockSurfaceErrors(block, blockIndex, surface, errors) {
|
|
877
|
+
const blockType = String(block.type || "").trim();
|
|
878
|
+
const expectedGridUse = APPROVAL_PAGE_GRID_USE_BY_SURFACE[surface];
|
|
879
|
+
const allowedContainerUses = APPROVAL_BLOCK_ALLOWED_CONTAINER_USES_BY_PUBLIC_KEY.get(blockType);
|
|
880
|
+
if (!blockType || !expectedGridUse || !allowedContainerUses || allowedContainerUses.includes(expectedGridUse)) {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
errors.push(
|
|
884
|
+
buildAuthoringError({
|
|
885
|
+
path: `$.blocks[${blockIndex}].type`,
|
|
886
|
+
ruleId: "approval-block-not-allowed-for-surface",
|
|
887
|
+
message: `flowSurfaces applyApprovalBlueprint block '${blockType}' is not allowed on surface '${surface}'`,
|
|
888
|
+
details: {
|
|
889
|
+
surface,
|
|
890
|
+
blockIndex,
|
|
891
|
+
blockType,
|
|
892
|
+
expectedContainerUse: expectedGridUse,
|
|
893
|
+
allowedContainerUses,
|
|
894
|
+
suggestedFix: "Use an approval block type that is available for this surface, or move the block to the matching approval surface."
|
|
895
|
+
}
|
|
896
|
+
})
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
function collectBlockActionErrors(block, blockIndex, surface, errors) {
|
|
900
|
+
const blockType = String(block.type || "").trim();
|
|
901
|
+
const actionsPath = `$.blocks[${blockIndex}].actions`;
|
|
902
|
+
if (import_lodash.default.isUndefined(block.actions)) {
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
if (!Array.isArray(block.actions)) {
|
|
906
|
+
errors.push(
|
|
907
|
+
buildAuthoringError({
|
|
908
|
+
path: actionsPath,
|
|
909
|
+
ruleId: "approval-blueprint-actions-shape",
|
|
910
|
+
message: `flowSurfaces applyApprovalBlueprint blocks[${blockIndex}].actions must be an array`,
|
|
911
|
+
details: {
|
|
912
|
+
surface,
|
|
913
|
+
blockIndex,
|
|
914
|
+
blockType,
|
|
915
|
+
suggestedFix: "Pass actions as an array of approval action keys or action objects."
|
|
916
|
+
}
|
|
917
|
+
})
|
|
918
|
+
);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
if (!blockType) {
|
|
922
|
+
errors.push(
|
|
923
|
+
buildAuthoringError({
|
|
924
|
+
path: `$.blocks[${blockIndex}].type`,
|
|
925
|
+
ruleId: "approval-blueprint-actions-require-block-type",
|
|
926
|
+
message: `flowSurfaces applyApprovalBlueprint blocks[${blockIndex}] requires type when actions are provided`,
|
|
927
|
+
details: {
|
|
928
|
+
surface,
|
|
929
|
+
blockIndex,
|
|
930
|
+
suggestedFix: "Set an approval block type before declaring actions, or remove actions from template blocks."
|
|
931
|
+
}
|
|
932
|
+
})
|
|
933
|
+
);
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
if (!APPROVAL_BLOCK_PUBLIC_KEY_SET.has(blockType)) {
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
const defaultActions = APPROVAL_DEFAULT_ACTION_PUBLIC_KEYS_BY_BLOCK_TYPE.get(blockType) || /* @__PURE__ */ new Set();
|
|
940
|
+
const allowedActions = APPROVAL_ALLOWED_ACTION_PUBLIC_KEYS_BY_BLOCK_TYPE.get(blockType) || /* @__PURE__ */ new Set();
|
|
941
|
+
const seenSingletonActions = /* @__PURE__ */ new Map();
|
|
942
|
+
const seenActionKeys = /* @__PURE__ */ new Map();
|
|
943
|
+
block.actions.forEach((action, actionIndex) => {
|
|
944
|
+
const actionPath = `${actionsPath}[${actionIndex}]`;
|
|
945
|
+
if (typeof action !== "string" && !import_lodash.default.isPlainObject(action)) {
|
|
946
|
+
errors.push(
|
|
947
|
+
buildAuthoringError({
|
|
948
|
+
path: actionPath,
|
|
949
|
+
ruleId: "approval-blueprint-action-shape",
|
|
950
|
+
message: `flowSurfaces applyApprovalBlueprint blocks[${blockIndex}].actions[${actionIndex}] must be a string or object`,
|
|
951
|
+
details: {
|
|
952
|
+
surface,
|
|
953
|
+
blockIndex,
|
|
954
|
+
actionIndex,
|
|
955
|
+
suggestedFix: "Use an approval action key string, or an object with type/settings."
|
|
956
|
+
}
|
|
957
|
+
})
|
|
958
|
+
);
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
if (import_lodash.default.isPlainObject(action)) {
|
|
962
|
+
const rawKey = COMPOSE_RAW_ACTION_KEYS.find(
|
|
963
|
+
(key) => !import_lodash.default.isUndefined(action[key])
|
|
964
|
+
);
|
|
965
|
+
if (rawKey) {
|
|
966
|
+
errors.push(
|
|
967
|
+
buildAuthoringError({
|
|
968
|
+
path: `${actionPath}.${rawKey}`,
|
|
969
|
+
ruleId: "approval-blueprint-action-public-fields-only",
|
|
970
|
+
message: "flowSurfaces compose action only accepts public semantic action fields",
|
|
971
|
+
details: {
|
|
972
|
+
surface,
|
|
973
|
+
blockIndex,
|
|
974
|
+
actionIndex,
|
|
975
|
+
rawKey,
|
|
976
|
+
suggestedFix: "Remove raw FlowModel fields and use public action settings instead."
|
|
977
|
+
}
|
|
978
|
+
})
|
|
979
|
+
);
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
const actionType = readActionType(action);
|
|
984
|
+
if (!actionType) {
|
|
985
|
+
errors.push(
|
|
986
|
+
buildAuthoringError({
|
|
987
|
+
path: actionPath,
|
|
988
|
+
ruleId: "approval-blueprint-action-type-required",
|
|
989
|
+
message: `flowSurfaces applyApprovalBlueprint blocks[${blockIndex}].actions[${actionIndex}] requires type`,
|
|
990
|
+
details: {
|
|
991
|
+
surface,
|
|
992
|
+
blockIndex,
|
|
993
|
+
actionIndex,
|
|
994
|
+
suggestedFix: "Use a non-empty approval action type."
|
|
995
|
+
}
|
|
996
|
+
})
|
|
997
|
+
);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
const actionKey = collectThrownAuthoringError(
|
|
1001
|
+
errors,
|
|
1002
|
+
{
|
|
1003
|
+
path: import_lodash.default.isPlainObject(action) ? `${actionPath}.key` : actionPath,
|
|
1004
|
+
ruleId: "approval-blueprint-action-key-invalid",
|
|
1005
|
+
details: {
|
|
1006
|
+
surface,
|
|
1007
|
+
blockIndex,
|
|
1008
|
+
actionIndex,
|
|
1009
|
+
suggestedFix: "Use a non-empty, unique action key."
|
|
1010
|
+
}
|
|
1011
|
+
},
|
|
1012
|
+
() => (0, import_service_utils.normalizeFlowSurfaceComposeKey)(
|
|
1013
|
+
readActionKey(action, actionType),
|
|
1014
|
+
`flowSurfaces compose action #${actionIndex + 1}`
|
|
1015
|
+
)
|
|
1016
|
+
);
|
|
1017
|
+
if (!actionKey) {
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
const previousActionKeyIndex = seenActionKeys.get(actionKey);
|
|
1021
|
+
if (typeof previousActionKeyIndex !== "undefined") {
|
|
1022
|
+
errors.push(
|
|
1023
|
+
buildAuthoringError({
|
|
1024
|
+
path: import_lodash.default.isPlainObject(action) ? `${actionPath}.key` : actionPath,
|
|
1025
|
+
ruleId: "approval-blueprint-action-key-duplicate",
|
|
1026
|
+
message: `flowSurfaces compose block #${blockIndex + 1} actions key '${actionKey}' is duplicated at #${previousActionKeyIndex + 1} and #${actionIndex + 1}`,
|
|
1027
|
+
details: {
|
|
1028
|
+
surface,
|
|
1029
|
+
blockIndex,
|
|
1030
|
+
actionKey,
|
|
1031
|
+
firstIndex: previousActionKeyIndex,
|
|
1032
|
+
duplicateIndex: actionIndex,
|
|
1033
|
+
suggestedFix: "Use a unique key for each action in this block."
|
|
1034
|
+
}
|
|
1035
|
+
})
|
|
1036
|
+
);
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
seenActionKeys.set(actionKey, actionIndex);
|
|
1040
|
+
const actionUse = APPROVAL_ACTION_USE_BY_PUBLIC_KEY.get(actionType);
|
|
1041
|
+
if (!actionUse) {
|
|
1042
|
+
errors.push(
|
|
1043
|
+
buildAuthoringError({
|
|
1044
|
+
path: actionPath,
|
|
1045
|
+
ruleId: "approval-blueprint-action-type-unsupported",
|
|
1046
|
+
message: `flowSurfaces applyApprovalBlueprint action '${actionType}' is not an approval action`,
|
|
1047
|
+
details: {
|
|
1048
|
+
surface,
|
|
1049
|
+
blockIndex,
|
|
1050
|
+
actionIndex,
|
|
1051
|
+
actionType,
|
|
1052
|
+
allowedActions: Array.from(allowedActions),
|
|
1053
|
+
suggestedFix: "Use an approval action from the allowedActions list for this block."
|
|
1054
|
+
}
|
|
1055
|
+
})
|
|
1056
|
+
);
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
if (!allowedActions.has(actionType)) {
|
|
1060
|
+
errors.push(
|
|
1061
|
+
buildAuthoringError({
|
|
1062
|
+
path: actionPath,
|
|
1063
|
+
ruleId: "approval-action-not-allowed-for-block",
|
|
1064
|
+
message: `flowSurfaces applyApprovalBlueprint action '${actionType}' is not allowed on '${blockType}'`,
|
|
1065
|
+
details: {
|
|
1066
|
+
surface,
|
|
1067
|
+
blockIndex,
|
|
1068
|
+
actionIndex,
|
|
1069
|
+
blockType,
|
|
1070
|
+
actionType,
|
|
1071
|
+
allowedActions: Array.from(allowedActions),
|
|
1072
|
+
suggestedFix: "Move this action to a compatible approval block or remove it."
|
|
1073
|
+
}
|
|
1074
|
+
})
|
|
1075
|
+
);
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
if (defaultActions.has(actionType)) {
|
|
1079
|
+
errors.push(
|
|
1080
|
+
buildAuthoringError({
|
|
1081
|
+
path: actionsPath,
|
|
1082
|
+
ruleId: "approval-initiator-submit-action-default",
|
|
1083
|
+
message: "approvalInitiator creates approvalSubmit by default; do not include approvalSubmit in actions.",
|
|
1084
|
+
details: {
|
|
1085
|
+
surface,
|
|
1086
|
+
blockIndex,
|
|
1087
|
+
blockType,
|
|
1088
|
+
invalidAction: actionType,
|
|
1089
|
+
defaultActionUse: actionUse,
|
|
1090
|
+
suggestedFix: "Remove approvalSubmit from this actions array and retry the same applyApprovalBlueprint request."
|
|
1091
|
+
}
|
|
1092
|
+
})
|
|
1093
|
+
);
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
if (!import_runtime_config.APPROVAL_SINGLETON_ACTION_USES.has(actionUse)) {
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
const previousIndex = seenSingletonActions.get(actionUse);
|
|
1100
|
+
if (typeof previousIndex !== "undefined") {
|
|
1101
|
+
errors.push(
|
|
1102
|
+
buildAuthoringError({
|
|
1103
|
+
path: actionPath,
|
|
1104
|
+
ruleId: "approval-action-singleton-duplicate",
|
|
1105
|
+
message: `flowSurfaces applyApprovalBlueprint action '${actionType}' is duplicated in '${blockType}'`,
|
|
1106
|
+
details: {
|
|
1107
|
+
surface,
|
|
1108
|
+
blockIndex,
|
|
1109
|
+
blockType,
|
|
1110
|
+
actionType,
|
|
1111
|
+
actionUse,
|
|
1112
|
+
actionPublicKey: APPROVAL_ACTION_PUBLIC_KEYS_BY_USE.get(actionUse),
|
|
1113
|
+
firstIndex: previousIndex,
|
|
1114
|
+
duplicateIndex: actionIndex,
|
|
1115
|
+
duplicateKey: readActionKey(action, actionType),
|
|
1116
|
+
suggestedFix: "Keep only one instance of this approval action in the actions array."
|
|
1117
|
+
}
|
|
1118
|
+
})
|
|
1119
|
+
);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
seenSingletonActions.set(actionUse, actionIndex);
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
function assertFlowSurfaceApprovalBlueprintAuthoringPayload(values) {
|
|
1126
|
+
const errors = [];
|
|
1127
|
+
if (!import_lodash.default.isPlainObject(values)) {
|
|
1128
|
+
errors.push(
|
|
1129
|
+
buildAuthoringError({
|
|
1130
|
+
path: "$",
|
|
1131
|
+
ruleId: "approval-blueprint-payload-shape",
|
|
1132
|
+
message: "flowSurfaces applyApprovalBlueprint payload must be an object",
|
|
1133
|
+
details: {
|
|
1134
|
+
suggestedFix: "Send a JSON object as the applyApprovalBlueprint payload."
|
|
1135
|
+
}
|
|
1136
|
+
})
|
|
1137
|
+
);
|
|
1138
|
+
(0, import_errors.throwAggregateBadRequest)(errors);
|
|
1139
|
+
}
|
|
1140
|
+
const payload = values;
|
|
1141
|
+
const surface = collectTopLevelShapeErrors(payload, errors);
|
|
1142
|
+
collectBindingErrors(payload, surface, errors);
|
|
1143
|
+
collectCollectionShapeErrors(payload, surface, errors);
|
|
1144
|
+
if (errors.length) {
|
|
1145
|
+
(0, import_errors.throwAggregateBadRequest)(errors);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1149
|
+
0 && (module.exports = {
|
|
1150
|
+
assertFlowSurfaceApprovalBlueprintAuthoringPayload
|
|
1151
|
+
});
|