cxtms 1.9.13
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/README.md +384 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +4784 -0
- package/dist/cli.js.map +1 -0
- package/dist/extractUtils.d.ts +11 -0
- package/dist/extractUtils.d.ts.map +1 -0
- package/dist/extractUtils.js +19 -0
- package/dist/extractUtils.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +129 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/schemaLoader.d.ts +17 -0
- package/dist/utils/schemaLoader.d.ts.map +1 -0
- package/dist/utils/schemaLoader.js +134 -0
- package/dist/utils/schemaLoader.js.map +1 -0
- package/dist/validator.d.ts +72 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +432 -0
- package/dist/validator.js.map +1 -0
- package/dist/workflowValidator.d.ts +103 -0
- package/dist/workflowValidator.d.ts.map +1 -0
- package/dist/workflowValidator.js +753 -0
- package/dist/workflowValidator.js.map +1 -0
- package/package.json +51 -0
- package/schemas/actions/all.json +27 -0
- package/schemas/actions/clipboard.json +46 -0
- package/schemas/actions/confirm.json +21 -0
- package/schemas/actions/consoleLog.json +16 -0
- package/schemas/actions/dialog.json +25 -0
- package/schemas/actions/fileDownload.json +16 -0
- package/schemas/actions/forEach.json +31 -0
- package/schemas/actions/if.json +12 -0
- package/schemas/actions/mutation.json +25 -0
- package/schemas/actions/navigate.json +18 -0
- package/schemas/actions/navigateBack.json +22 -0
- package/schemas/actions/navigateBackOrClose.json +21 -0
- package/schemas/actions/notification.json +19 -0
- package/schemas/actions/openBarcodeScanner.json +104 -0
- package/schemas/actions/query.json +32 -0
- package/schemas/actions/refresh.json +13 -0
- package/schemas/actions/resetDirtyState.json +22 -0
- package/schemas/actions/setFields.json +21 -0
- package/schemas/actions/setStore.json +13 -0
- package/schemas/actions/validateForm.json +15 -0
- package/schemas/actions/workflow.json +24 -0
- package/schemas/components/README.md +147 -0
- package/schemas/components/appComponent.json +58 -0
- package/schemas/components/barcodeScanner.json +69 -0
- package/schemas/components/button.json +123 -0
- package/schemas/components/calendar.json +489 -0
- package/schemas/components/card.json +176 -0
- package/schemas/components/collection.json +54 -0
- package/schemas/components/dataGrid.json +119 -0
- package/schemas/components/datasource.json +151 -0
- package/schemas/components/dropdown.json +57 -0
- package/schemas/components/field-collection.json +618 -0
- package/schemas/components/field.json +265 -0
- package/schemas/components/form.json +234 -0
- package/schemas/components/index.json +71 -0
- package/schemas/components/layout.json +69 -0
- package/schemas/components/module.json +167 -0
- package/schemas/components/navDropdown.json +36 -0
- package/schemas/components/navbar.json +78 -0
- package/schemas/components/navbarItem.json +28 -0
- package/schemas/components/navbarLink.json +36 -0
- package/schemas/components/row.json +31 -0
- package/schemas/components/slot.json +30 -0
- package/schemas/components/tab.json +34 -0
- package/schemas/components/tabs.json +35 -0
- package/schemas/components/timeline.json +172 -0
- package/schemas/components/timelineGrid.json +328 -0
- package/schemas/fields/README.md +66 -0
- package/schemas/fields/attachment.json +156 -0
- package/schemas/fields/autocomplete-googleplaces.json +130 -0
- package/schemas/fields/checkbox.json +82 -0
- package/schemas/fields/date.json +88 -0
- package/schemas/fields/datetime.json +75 -0
- package/schemas/fields/email.json +75 -0
- package/schemas/fields/index.json +53 -0
- package/schemas/fields/number.json +91 -0
- package/schemas/fields/password.json +70 -0
- package/schemas/fields/radio.json +94 -0
- package/schemas/fields/rangedatetime.json +56 -0
- package/schemas/fields/select-async.json +334 -0
- package/schemas/fields/select.json +115 -0
- package/schemas/fields/tel.json +79 -0
- package/schemas/fields/text.json +86 -0
- package/schemas/fields/textarea.json +95 -0
- package/schemas/fields/time.json +91 -0
- package/schemas/fields/url.json +74 -0
- package/schemas/schema.graphql +12248 -0
- package/schemas/schemas.json +610 -0
- package/schemas/workflows/activity.json +96 -0
- package/schemas/workflows/common/condition.json +48 -0
- package/schemas/workflows/common/expression.json +76 -0
- package/schemas/workflows/common/mapping.json +173 -0
- package/schemas/workflows/common/step.json +38 -0
- package/schemas/workflows/flow/aggregation.json +44 -0
- package/schemas/workflows/flow/entity.json +129 -0
- package/schemas/workflows/flow/state.json +105 -0
- package/schemas/workflows/flow/transition.json +143 -0
- package/schemas/workflows/input.json +122 -0
- package/schemas/workflows/output.json +61 -0
- package/schemas/workflows/schedule.json +26 -0
- package/schemas/workflows/tasks/accounting-transaction.json +95 -0
- package/schemas/workflows/tasks/action-event.json +65 -0
- package/schemas/workflows/tasks/all.json +152 -0
- package/schemas/workflows/tasks/appmodule.json +56 -0
- package/schemas/workflows/tasks/attachment.json +97 -0
- package/schemas/workflows/tasks/authentication.json +86 -0
- package/schemas/workflows/tasks/caching.json +68 -0
- package/schemas/workflows/tasks/charge.json +92 -0
- package/schemas/workflows/tasks/commodity.json +92 -0
- package/schemas/workflows/tasks/contact-address.json +72 -0
- package/schemas/workflows/tasks/contact-payment-method.json +72 -0
- package/schemas/workflows/tasks/contact.json +82 -0
- package/schemas/workflows/tasks/csv.json +81 -0
- package/schemas/workflows/tasks/document-render.json +105 -0
- package/schemas/workflows/tasks/document-send.json +84 -0
- package/schemas/workflows/tasks/edi.json +157 -0
- package/schemas/workflows/tasks/email-send.json +110 -0
- package/schemas/workflows/tasks/error.json +72 -0
- package/schemas/workflows/tasks/export.json +90 -0
- package/schemas/workflows/tasks/filetransfer.json +102 -0
- package/schemas/workflows/tasks/flow-transition.json +68 -0
- package/schemas/workflows/tasks/foreach.json +69 -0
- package/schemas/workflows/tasks/generic.json +47 -0
- package/schemas/workflows/tasks/graphql.json +78 -0
- package/schemas/workflows/tasks/httpRequest.json +161 -0
- package/schemas/workflows/tasks/import.json +64 -0
- package/schemas/workflows/tasks/inventory.json +67 -0
- package/schemas/workflows/tasks/job.json +88 -0
- package/schemas/workflows/tasks/log.json +73 -0
- package/schemas/workflows/tasks/map.json +58 -0
- package/schemas/workflows/tasks/movement.json +54 -0
- package/schemas/workflows/tasks/note.json +59 -0
- package/schemas/workflows/tasks/number.json +65 -0
- package/schemas/workflows/tasks/order-tracking-event.json +109 -0
- package/schemas/workflows/tasks/order.json +139 -0
- package/schemas/workflows/tasks/payment.json +85 -0
- package/schemas/workflows/tasks/pdf-document.json +60 -0
- package/schemas/workflows/tasks/postal-codes.json +92 -0
- package/schemas/workflows/tasks/resolve-timezone.json +65 -0
- package/schemas/workflows/tasks/setVariable.json +76 -0
- package/schemas/workflows/tasks/switch.json +75 -0
- package/schemas/workflows/tasks/template.json +73 -0
- package/schemas/workflows/tasks/tracking-event.json +137 -0
- package/schemas/workflows/tasks/transmission.json +185 -0
- package/schemas/workflows/tasks/unzip-file.json +68 -0
- package/schemas/workflows/tasks/user.json +70 -0
- package/schemas/workflows/tasks/validation.json +99 -0
- package/schemas/workflows/tasks/while.json +53 -0
- package/schemas/workflows/tasks/workflow-execute.json +82 -0
- package/schemas/workflows/trigger.json +90 -0
- package/schemas/workflows/variable.json +46 -0
- package/schemas/workflows/workflow.json +335 -0
- package/scripts/postinstall.js +291 -0
- package/scripts/setup-vscode.js +80 -0
- package/skills/cxtms-developer/SKILL.md +118 -0
- package/skills/cxtms-developer/ref-cli-auth.md +120 -0
- package/skills/cxtms-developer/ref-entity-accounting.md +180 -0
- package/skills/cxtms-developer/ref-entity-commodity.md +239 -0
- package/skills/cxtms-developer/ref-entity-contact.md +163 -0
- package/skills/cxtms-developer/ref-entity-geography.md +154 -0
- package/skills/cxtms-developer/ref-entity-job.md +77 -0
- package/skills/cxtms-developer/ref-entity-notification.md +85 -0
- package/skills/cxtms-developer/ref-entity-order-sub.md +160 -0
- package/skills/cxtms-developer/ref-entity-order.md +183 -0
- package/skills/cxtms-developer/ref-entity-organization.md +41 -0
- package/skills/cxtms-developer/ref-entity-rate.md +182 -0
- package/skills/cxtms-developer/ref-entity-shared.md +176 -0
- package/skills/cxtms-developer/ref-entity-warehouse.md +115 -0
- package/skills/cxtms-developer/ref-graphql-query.md +309 -0
- package/skills/cxtms-module-builder/SKILL.md +477 -0
- package/skills/cxtms-module-builder/ref-components-data.md +293 -0
- package/skills/cxtms-module-builder/ref-components-display.md +411 -0
- package/skills/cxtms-module-builder/ref-components-forms.md +369 -0
- package/skills/cxtms-module-builder/ref-components-interactive.md +317 -0
- package/skills/cxtms-module-builder/ref-components-layout.md +390 -0
- package/skills/cxtms-module-builder/ref-components-specialized.md +477 -0
- package/skills/cxtms-workflow-builder/SKILL.md +438 -0
- package/skills/cxtms-workflow-builder/ref-accounting.md +66 -0
- package/skills/cxtms-workflow-builder/ref-communication.md +169 -0
- package/skills/cxtms-workflow-builder/ref-entity.md +342 -0
- package/skills/cxtms-workflow-builder/ref-expressions-ncalc.md +128 -0
- package/skills/cxtms-workflow-builder/ref-expressions-template.md +161 -0
- package/skills/cxtms-workflow-builder/ref-filetransfer.md +80 -0
- package/skills/cxtms-workflow-builder/ref-flow.md +210 -0
- package/skills/cxtms-workflow-builder/ref-other.md +157 -0
- package/skills/cxtms-workflow-builder/ref-query.md +105 -0
- package/skills/cxtms-workflow-builder/ref-utilities.md +417 -0
- package/templates/module-configuration.yaml +44 -0
- package/templates/module-form.yaml +152 -0
- package/templates/module-grid.yaml +229 -0
- package/templates/module-select.yaml +139 -0
- package/templates/module.yaml +84 -0
- package/templates/workflow-api-tracking.yaml +189 -0
- package/templates/workflow-basic.yaml +76 -0
- package/templates/workflow-document.yaml +155 -0
- package/templates/workflow-entity-trigger.yaml +90 -0
- package/templates/workflow-ftp-edi.yaml +158 -0
- package/templates/workflow-ftp-tracking.yaml +161 -0
- package/templates/workflow-mcp-tool.yaml +112 -0
- package/templates/workflow-public-api.yaml +135 -0
- package/templates/workflow-scheduled-execute.yaml +75 -0
- package/templates/workflow-scheduled.yaml +125 -0
- package/templates/workflow-utility.yaml +96 -0
- package/templates/workflow-webhook.yaml +128 -0
- package/templates/workflow.yaml +140 -0
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Workflow validator for CargoXplorer YAML workflow files
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.WorkflowValidator = void 0;
|
|
43
|
+
const ajv_1 = __importDefault(require("ajv"));
|
|
44
|
+
const ajv_formats_1 = __importDefault(require("ajv-formats"));
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
48
|
+
class WorkflowValidator {
|
|
49
|
+
constructor(options = {}) {
|
|
50
|
+
this.schemasDir = options.schemasPath || path.join(__dirname, '../schemas/workflows');
|
|
51
|
+
this.options = {
|
|
52
|
+
schemasPath: this.schemasDir,
|
|
53
|
+
strictMode: options.strictMode ?? true,
|
|
54
|
+
includeWarnings: options.includeWarnings ?? true,
|
|
55
|
+
validateTasks: options.validateTasks ?? true,
|
|
56
|
+
validateExpressions: options.validateExpressions ?? false
|
|
57
|
+
};
|
|
58
|
+
// Initialize Ajv with Draft 7 support
|
|
59
|
+
this.ajv = new ajv_1.default({
|
|
60
|
+
strict: false,
|
|
61
|
+
allErrors: true,
|
|
62
|
+
verbose: true,
|
|
63
|
+
validateFormats: true,
|
|
64
|
+
allowUnionTypes: true
|
|
65
|
+
});
|
|
66
|
+
// Add format validators
|
|
67
|
+
(0, ajv_formats_1.default)(this.ajv);
|
|
68
|
+
// Load all workflow schemas
|
|
69
|
+
this.schemas = this.loadWorkflowSchemas(this.schemasDir);
|
|
70
|
+
// Register schemas with Ajv
|
|
71
|
+
this.registerSchemas();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Load all workflow schemas from the schemas/workflows directory
|
|
75
|
+
*/
|
|
76
|
+
loadWorkflowSchemas(schemasDir) {
|
|
77
|
+
const schemas = new Map();
|
|
78
|
+
// Load main workflow.json
|
|
79
|
+
const mainSchemaPath = path.join(schemasDir, 'workflow.json');
|
|
80
|
+
if (fs.existsSync(mainSchemaPath)) {
|
|
81
|
+
const schema = JSON.parse(fs.readFileSync(mainSchemaPath, 'utf-8'));
|
|
82
|
+
schemas.set('workflow.json', {
|
|
83
|
+
schema,
|
|
84
|
+
uri: `file:///${mainSchemaPath.replace(/\\/g, '/')}`
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// Load workflow sub-schemas
|
|
88
|
+
const subSchemas = ['input.json', 'output.json', 'variable.json', 'trigger.json', 'schedule.json', 'activity.json'];
|
|
89
|
+
for (const schemaFile of subSchemas) {
|
|
90
|
+
const schemaPath = path.join(schemasDir, schemaFile);
|
|
91
|
+
if (fs.existsSync(schemaPath)) {
|
|
92
|
+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf-8'));
|
|
93
|
+
schemas.set(schemaFile, {
|
|
94
|
+
schema,
|
|
95
|
+
uri: `file:///${schemaPath.replace(/\\/g, '/')}`
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Load task schemas from tasks/ subdirectory
|
|
100
|
+
const tasksDir = path.join(schemasDir, 'tasks');
|
|
101
|
+
if (fs.existsSync(tasksDir)) {
|
|
102
|
+
this.loadSchemasFromDir(tasksDir, 'tasks', schemas);
|
|
103
|
+
}
|
|
104
|
+
// Load common schemas from common/ subdirectory
|
|
105
|
+
const commonDir = path.join(schemasDir, 'common');
|
|
106
|
+
if (fs.existsSync(commonDir)) {
|
|
107
|
+
this.loadSchemasFromDir(commonDir, 'common', schemas);
|
|
108
|
+
}
|
|
109
|
+
// Load flow schemas from flow/ subdirectory
|
|
110
|
+
const flowDir = path.join(schemasDir, 'flow');
|
|
111
|
+
if (fs.existsSync(flowDir)) {
|
|
112
|
+
this.loadSchemasFromDir(flowDir, 'flow', schemas);
|
|
113
|
+
}
|
|
114
|
+
return schemas;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Recursively load schemas from a directory
|
|
118
|
+
*/
|
|
119
|
+
loadSchemasFromDir(dir, relativePath, schemas) {
|
|
120
|
+
const files = fs.readdirSync(dir);
|
|
121
|
+
for (const file of files) {
|
|
122
|
+
const filePath = path.join(dir, file);
|
|
123
|
+
const stat = fs.statSync(filePath);
|
|
124
|
+
if (stat.isDirectory()) {
|
|
125
|
+
this.loadSchemasFromDir(filePath, `${relativePath}/${file}`, schemas);
|
|
126
|
+
}
|
|
127
|
+
else if (file.endsWith('.json')) {
|
|
128
|
+
try {
|
|
129
|
+
const schema = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
130
|
+
const key = `${relativePath}/${file}`;
|
|
131
|
+
schemas.set(key, {
|
|
132
|
+
schema,
|
|
133
|
+
uri: `file:///${filePath.replace(/\\/g, '/')}`
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
console.error(`Error loading schema ${filePath}:`, error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Register all loaded schemas with Ajv
|
|
144
|
+
*/
|
|
145
|
+
registerSchemas() {
|
|
146
|
+
for (const [key, entry] of this.schemas.entries()) {
|
|
147
|
+
try {
|
|
148
|
+
this.ajv.addSchema(entry.schema, key);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
console.error(`Error adding schema ${key}:`, error);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Validate a YAML workflow file
|
|
157
|
+
*/
|
|
158
|
+
async validateWorkflow(filePath) {
|
|
159
|
+
const errors = [];
|
|
160
|
+
const warnings = [];
|
|
161
|
+
try {
|
|
162
|
+
// Check if file exists
|
|
163
|
+
if (!fs.existsSync(filePath)) {
|
|
164
|
+
errors.push({
|
|
165
|
+
type: 'file_not_found',
|
|
166
|
+
path: filePath,
|
|
167
|
+
message: `File not found: ${filePath}`
|
|
168
|
+
});
|
|
169
|
+
return this.createResult(filePath, errors, warnings);
|
|
170
|
+
}
|
|
171
|
+
// Read and parse YAML
|
|
172
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
173
|
+
let workflowData;
|
|
174
|
+
try {
|
|
175
|
+
workflowData = yaml_1.default.parse(content);
|
|
176
|
+
}
|
|
177
|
+
catch (yamlError) {
|
|
178
|
+
errors.push({
|
|
179
|
+
type: 'yaml_syntax_error',
|
|
180
|
+
path: filePath,
|
|
181
|
+
message: `YAML syntax error: ${yamlError.message}`
|
|
182
|
+
});
|
|
183
|
+
return this.createResult(filePath, errors, warnings);
|
|
184
|
+
}
|
|
185
|
+
// Validate workflow structure
|
|
186
|
+
this.validateWorkflowStructure(workflowData, errors, warnings, filePath);
|
|
187
|
+
// Validate inputs and variables
|
|
188
|
+
this.validateInputs(workflowData, errors);
|
|
189
|
+
this.validateVariables(workflowData, errors);
|
|
190
|
+
// Validate against main workflow schema
|
|
191
|
+
const validate = this.ajv.getSchema('workflow.json');
|
|
192
|
+
if (validate && !validate(workflowData)) {
|
|
193
|
+
this.addAjvErrors(validate.errors, '', errors);
|
|
194
|
+
}
|
|
195
|
+
const isFlowWorkflow = workflowData.workflow?.workflowType === 'Flow';
|
|
196
|
+
if (isFlowWorkflow) {
|
|
197
|
+
// Validate Flow-specific sections
|
|
198
|
+
this.validateFlowWorkflow(workflowData, errors, warnings);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// Validate activities recursively (standard workflows)
|
|
202
|
+
if (workflowData.activities && Array.isArray(workflowData.activities)) {
|
|
203
|
+
this.validateActivities(workflowData.activities, 'activities', errors, warnings);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Validate workflow-level event handler steps
|
|
207
|
+
this.validateEventSteps(workflowData.events, 'events', ['onWorkflowStarted', 'onWorkflowCompleted', 'onWorkflowExecuted', 'onWorkflowFailed'], errors, warnings);
|
|
208
|
+
// Warn on deprecated onWorkflowExecuted
|
|
209
|
+
if (workflowData.events && !Array.isArray(workflowData.events) && workflowData.events.onWorkflowExecuted) {
|
|
210
|
+
warnings.push({
|
|
211
|
+
type: 'deprecated_property',
|
|
212
|
+
path: 'events.onWorkflowExecuted',
|
|
213
|
+
message: 'Use "onWorkflowCompleted" instead of "onWorkflowExecuted"'
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return this.createResult(filePath, errors, warnings);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
errors.push({
|
|
220
|
+
type: 'unexpected_error',
|
|
221
|
+
path: filePath,
|
|
222
|
+
message: `Unexpected error: ${error.message}`
|
|
223
|
+
});
|
|
224
|
+
return this.createResult(filePath, errors, warnings);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Normalize file path: forward slashes, strip leading ./
|
|
229
|
+
*/
|
|
230
|
+
normalizeFilePath(p) {
|
|
231
|
+
return p.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Validate top-level workflow structure
|
|
235
|
+
*/
|
|
236
|
+
validateWorkflowStructure(workflowData, errors, warnings, filePath) {
|
|
237
|
+
// Check required top-level properties
|
|
238
|
+
if (!workflowData.workflow) {
|
|
239
|
+
errors.push({
|
|
240
|
+
type: 'missing_property',
|
|
241
|
+
path: 'workflow',
|
|
242
|
+
message: 'Missing required property: workflow'
|
|
243
|
+
});
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const isFlowWorkflow = workflowData.workflow?.workflowType === 'Flow';
|
|
247
|
+
if (isFlowWorkflow) {
|
|
248
|
+
if (!workflowData.entity) {
|
|
249
|
+
errors.push({
|
|
250
|
+
type: 'missing_property',
|
|
251
|
+
path: 'entity',
|
|
252
|
+
message: 'Missing required property: entity (required for Flow workflows)'
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
if (!workflowData.activities) {
|
|
258
|
+
errors.push({
|
|
259
|
+
type: 'missing_property',
|
|
260
|
+
path: 'activities',
|
|
261
|
+
message: 'Missing required property: activities'
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Validate workflow metadata
|
|
266
|
+
const workflow = workflowData.workflow;
|
|
267
|
+
if (!workflow.workflowId) {
|
|
268
|
+
errors.push({
|
|
269
|
+
type: 'missing_property',
|
|
270
|
+
path: 'workflow.workflowId',
|
|
271
|
+
message: 'Missing required property: workflow.workflowId'
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
if (!workflow.name) {
|
|
275
|
+
errors.push({
|
|
276
|
+
type: 'missing_property',
|
|
277
|
+
path: 'workflow.name',
|
|
278
|
+
message: 'Missing required property: workflow.name'
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
// Check for deprecated properties
|
|
282
|
+
this.checkDeprecatedProperties(workflow, 'workflow', warnings);
|
|
283
|
+
// filePath / fileName deprecation and validation
|
|
284
|
+
if (workflow.fileName && !workflow.filePath) {
|
|
285
|
+
warnings.push({
|
|
286
|
+
type: 'deprecated_property',
|
|
287
|
+
path: 'workflow.fileName',
|
|
288
|
+
message: 'Use "filePath" instead of "fileName" in workflow section'
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
const declaredPath = workflow.filePath ?? workflow.fileName;
|
|
292
|
+
if (declaredPath && filePath) {
|
|
293
|
+
const normalizedActual = this.normalizeFilePath(filePath);
|
|
294
|
+
const normalizedDeclared = this.normalizeFilePath(declaredPath);
|
|
295
|
+
if (!normalizedActual.endsWith(normalizedDeclared)) {
|
|
296
|
+
warnings.push({
|
|
297
|
+
type: 'file_path_mismatch',
|
|
298
|
+
path: 'workflow.filePath',
|
|
299
|
+
message: `Declared filePath "${normalizedDeclared}" does not match actual file path "${normalizedActual}"`
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Validate workflow inputs.
|
|
306
|
+
* Top-level input properties are: name, type, props.
|
|
307
|
+
* Settings like required, displayName, description belong inside props.
|
|
308
|
+
*/
|
|
309
|
+
validateInputs(workflowData, errors) {
|
|
310
|
+
const inputs = workflowData.inputs;
|
|
311
|
+
if (!inputs || !Array.isArray(inputs))
|
|
312
|
+
return;
|
|
313
|
+
const propsOnlyFields = ['required', 'isRequired', 'displayName', 'description', 'multiple', 'visible', 'defaultValue', 'mapping', 'filter', 'options'];
|
|
314
|
+
inputs.forEach((input, index) => {
|
|
315
|
+
const inputPath = `inputs[${index}]`;
|
|
316
|
+
for (const field of propsOnlyFields) {
|
|
317
|
+
if (field in input) {
|
|
318
|
+
errors.push({
|
|
319
|
+
type: 'schema_violation',
|
|
320
|
+
path: `${inputPath}.${field}`,
|
|
321
|
+
message: `Invalid top-level property '${field}'. Move it inside 'props'`
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Validate workflow variables.
|
|
329
|
+
* Top-level variable properties are: name, value, fromConfig.
|
|
330
|
+
*/
|
|
331
|
+
validateVariables(workflowData, errors) {
|
|
332
|
+
const variables = workflowData.variables;
|
|
333
|
+
if (!variables || !Array.isArray(variables))
|
|
334
|
+
return;
|
|
335
|
+
variables.forEach((variable, index) => {
|
|
336
|
+
const varPath = `variables[${index}]`;
|
|
337
|
+
if ('type' in variable) {
|
|
338
|
+
errors.push({
|
|
339
|
+
type: 'schema_violation',
|
|
340
|
+
path: `${varPath}.type`,
|
|
341
|
+
message: `Invalid property 'type' on variable. Variables only support 'name', 'value', and 'fromConfig'`
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Validate activities array recursively
|
|
348
|
+
*/
|
|
349
|
+
validateActivities(activities, basePath, errors, warnings) {
|
|
350
|
+
activities.forEach((activity, index) => {
|
|
351
|
+
const activityPath = `${basePath}[${index}]`;
|
|
352
|
+
this.validateActivity(activity, activityPath, errors, warnings);
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Validate a single activity
|
|
357
|
+
*/
|
|
358
|
+
validateActivity(activity, activityPath, errors, warnings) {
|
|
359
|
+
if (!activity || typeof activity !== 'object') {
|
|
360
|
+
errors.push({
|
|
361
|
+
type: 'invalid_activity',
|
|
362
|
+
path: activityPath,
|
|
363
|
+
message: 'Activity must be an object'
|
|
364
|
+
});
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
// Check for required properties
|
|
368
|
+
if (!activity.name) {
|
|
369
|
+
errors.push({
|
|
370
|
+
type: 'missing_property',
|
|
371
|
+
path: `${activityPath}.name`,
|
|
372
|
+
message: 'Activity must have a name property'
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
if (!activity.steps || !Array.isArray(activity.steps)) {
|
|
376
|
+
errors.push({
|
|
377
|
+
type: 'missing_property',
|
|
378
|
+
path: `${activityPath}.steps`,
|
|
379
|
+
message: 'Activity must have a steps array'
|
|
380
|
+
});
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
// Validate each step
|
|
384
|
+
activity.steps.forEach((step, stepIndex) => {
|
|
385
|
+
const stepPath = `${activityPath}.steps[${stepIndex}]`;
|
|
386
|
+
this.validateStep(step, stepPath, errors, warnings);
|
|
387
|
+
});
|
|
388
|
+
// Validate activity-level event handler steps
|
|
389
|
+
this.validateEventSteps(activity.events, `${activityPath}.events`, ['onActivityStarted', 'onActivityCompleted', 'onActivityFailed'], errors, warnings);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Validate steps inside event handlers (object format)
|
|
393
|
+
*/
|
|
394
|
+
validateEventSteps(events, basePath, eventNames, errors, warnings) {
|
|
395
|
+
if (!events || typeof events !== 'object' || Array.isArray(events))
|
|
396
|
+
return;
|
|
397
|
+
for (const eventName of eventNames) {
|
|
398
|
+
const steps = events[eventName];
|
|
399
|
+
if (steps && Array.isArray(steps)) {
|
|
400
|
+
steps.forEach((step, index) => {
|
|
401
|
+
this.validateStep(step, `${basePath}.${eventName}[${index}]`, errors, warnings);
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Validate a single step (task)
|
|
408
|
+
*/
|
|
409
|
+
validateStep(step, stepPath, errors, warnings) {
|
|
410
|
+
if (!step || typeof step !== 'object') {
|
|
411
|
+
errors.push({
|
|
412
|
+
type: 'schema_violation',
|
|
413
|
+
path: stepPath,
|
|
414
|
+
message: 'Step must be an object'
|
|
415
|
+
});
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
// Check for task type
|
|
419
|
+
if (!step.task) {
|
|
420
|
+
errors.push({
|
|
421
|
+
type: 'missing_property',
|
|
422
|
+
path: `${stepPath}.task`,
|
|
423
|
+
message: 'Step must have a task property'
|
|
424
|
+
});
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
// Validate nested structures (foreach, switch, while)
|
|
428
|
+
this.validateNestedSteps(step, stepPath, errors, warnings);
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Validate nested structures within control flow tasks
|
|
432
|
+
*/
|
|
433
|
+
validateNestedSteps(step, stepPath, errors, warnings) {
|
|
434
|
+
const taskType = step.task;
|
|
435
|
+
// Handle foreach
|
|
436
|
+
if (taskType === 'foreach') {
|
|
437
|
+
if (step.steps && Array.isArray(step.steps)) {
|
|
438
|
+
step.steps.forEach((nestedStep, index) => {
|
|
439
|
+
this.validateStep(nestedStep, `${stepPath}.steps[${index}]`, errors, warnings);
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
// Handle switch
|
|
444
|
+
if (taskType === 'switch') {
|
|
445
|
+
if (step.cases && Array.isArray(step.cases)) {
|
|
446
|
+
step.cases.forEach((caseItem, caseIndex) => {
|
|
447
|
+
if (caseItem.steps && Array.isArray(caseItem.steps)) {
|
|
448
|
+
caseItem.steps.forEach((nestedStep, stepIndex) => {
|
|
449
|
+
this.validateStep(nestedStep, `${stepPath}.cases[${caseIndex}].steps[${stepIndex}]`, errors, warnings);
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
if (step.default && step.default.steps && Array.isArray(step.default.steps)) {
|
|
455
|
+
step.default.steps.forEach((nestedStep, index) => {
|
|
456
|
+
this.validateStep(nestedStep, `${stepPath}.default.steps[${index}]`, errors, warnings);
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// Handle while
|
|
461
|
+
if (taskType === 'while') {
|
|
462
|
+
if (step.steps && Array.isArray(step.steps)) {
|
|
463
|
+
step.steps.forEach((nestedStep, index) => {
|
|
464
|
+
this.validateStep(nestedStep, `${stepPath}.steps[${index}]`, errors, warnings);
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Validate Flow workflow sections (entity, states, transitions, aggregations)
|
|
471
|
+
*/
|
|
472
|
+
validateFlowWorkflow(workflowData, errors, warnings) {
|
|
473
|
+
this.validateFlowEntity(workflowData, errors);
|
|
474
|
+
const stateNames = this.validateFlowStates(workflowData, errors, warnings);
|
|
475
|
+
this.validateFlowTransitions(workflowData, stateNames, errors, warnings);
|
|
476
|
+
this.validateFlowAggregations(workflowData, errors);
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Validate Flow entity section
|
|
480
|
+
*/
|
|
481
|
+
validateFlowEntity(workflowData, errors) {
|
|
482
|
+
const entity = workflowData.entity;
|
|
483
|
+
if (!entity)
|
|
484
|
+
return;
|
|
485
|
+
if (!entity.name) {
|
|
486
|
+
errors.push({
|
|
487
|
+
type: 'missing_property',
|
|
488
|
+
path: 'entity.name',
|
|
489
|
+
message: 'Entity name is required for Flow workflows'
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Validate Flow states and return set of state names
|
|
495
|
+
*/
|
|
496
|
+
validateFlowStates(workflowData, errors, warnings) {
|
|
497
|
+
const stateNames = new Set();
|
|
498
|
+
const states = workflowData.states;
|
|
499
|
+
if (!states || !Array.isArray(states))
|
|
500
|
+
return stateNames;
|
|
501
|
+
let initialStateCount = 0;
|
|
502
|
+
const parentStates = new Set();
|
|
503
|
+
// First pass: collect state names and parents
|
|
504
|
+
for (const state of states) {
|
|
505
|
+
if (state.name)
|
|
506
|
+
stateNames.add(state.name);
|
|
507
|
+
if (state.parent)
|
|
508
|
+
parentStates.add(state.parent);
|
|
509
|
+
}
|
|
510
|
+
// Second pass: validate each state
|
|
511
|
+
states.forEach((state, index) => {
|
|
512
|
+
const statePath = `states[${index}]`;
|
|
513
|
+
if (!state.name) {
|
|
514
|
+
errors.push({
|
|
515
|
+
type: 'missing_property',
|
|
516
|
+
path: `${statePath}.name`,
|
|
517
|
+
message: 'State name is required'
|
|
518
|
+
});
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
// Check for duplicate names
|
|
522
|
+
const duplicates = states.filter((s) => s.name?.toLowerCase() === state.name?.toLowerCase());
|
|
523
|
+
if (duplicates.length > 1) {
|
|
524
|
+
errors.push({
|
|
525
|
+
type: 'schema_violation',
|
|
526
|
+
path: `${statePath}.name`,
|
|
527
|
+
message: `Duplicate state name '${state.name}'`
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
if (state.isInitial)
|
|
531
|
+
initialStateCount++;
|
|
532
|
+
// Validate parent reference
|
|
533
|
+
if (state.parent && !stateNames.has(state.parent)) {
|
|
534
|
+
errors.push({
|
|
535
|
+
type: 'schema_violation',
|
|
536
|
+
path: `${statePath}.parent`,
|
|
537
|
+
message: `Parent state '${state.parent}' not found`
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
// Validate onEnter/onExit steps
|
|
541
|
+
if (state.onEnter && Array.isArray(state.onEnter)) {
|
|
542
|
+
state.onEnter.forEach((step, stepIndex) => {
|
|
543
|
+
this.validateStep(step, `${statePath}.onEnter[${stepIndex}]`, errors, warnings);
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
if (state.onExit && Array.isArray(state.onExit)) {
|
|
547
|
+
state.onExit.forEach((step, stepIndex) => {
|
|
548
|
+
this.validateStep(step, `${statePath}.onExit[${stepIndex}]`, errors, warnings);
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
if (initialStateCount > 1) {
|
|
553
|
+
errors.push({
|
|
554
|
+
type: 'schema_violation',
|
|
555
|
+
path: 'states',
|
|
556
|
+
message: `Found ${initialStateCount} initial states. At most one state can be marked as initial.`
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
return stateNames;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Validate Flow transitions
|
|
563
|
+
*/
|
|
564
|
+
validateFlowTransitions(workflowData, stateNames, errors, warnings) {
|
|
565
|
+
const transitions = workflowData.transitions;
|
|
566
|
+
if (!transitions || !Array.isArray(transitions))
|
|
567
|
+
return;
|
|
568
|
+
const transitionNames = new Set();
|
|
569
|
+
transitions.forEach((transition, index) => {
|
|
570
|
+
const transPath = `transitions[${index}]`;
|
|
571
|
+
if (!transition.name) {
|
|
572
|
+
errors.push({
|
|
573
|
+
type: 'missing_property',
|
|
574
|
+
path: `${transPath}.name`,
|
|
575
|
+
message: 'Transition name is required'
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
else if (transitionNames.has(transition.name.toLowerCase())) {
|
|
579
|
+
errors.push({
|
|
580
|
+
type: 'schema_violation',
|
|
581
|
+
path: `${transPath}.name`,
|
|
582
|
+
message: `Duplicate transition name '${transition.name}'`
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
transitionNames.add(transition.name.toLowerCase());
|
|
587
|
+
}
|
|
588
|
+
// Validate from states
|
|
589
|
+
if (transition.from && stateNames.size > 0) {
|
|
590
|
+
const fromStates = Array.isArray(transition.from) ? transition.from : [transition.from];
|
|
591
|
+
for (const fromState of fromStates) {
|
|
592
|
+
if (fromState !== '*' && !stateNames.has(fromState)) {
|
|
593
|
+
errors.push({
|
|
594
|
+
type: 'schema_violation',
|
|
595
|
+
path: `${transPath}.from`,
|
|
596
|
+
message: `Source state '${fromState}' not found in states`
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
// Validate to state
|
|
602
|
+
if (transition.to && stateNames.size > 0 && !stateNames.has(transition.to)) {
|
|
603
|
+
errors.push({
|
|
604
|
+
type: 'schema_violation',
|
|
605
|
+
path: `${transPath}.to`,
|
|
606
|
+
message: `Target state '${transition.to}' not found in states`
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
// Validate trigger
|
|
610
|
+
const validTriggers = ['auto', 'manual', 'event'];
|
|
611
|
+
if (transition.trigger && !validTriggers.includes(transition.trigger)) {
|
|
612
|
+
errors.push({
|
|
613
|
+
type: 'schema_violation',
|
|
614
|
+
path: `${transPath}.trigger`,
|
|
615
|
+
message: `Invalid trigger '${transition.trigger}'. Valid triggers: ${validTriggers.join(', ')}`
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
// Validate event trigger requires eventName
|
|
619
|
+
if (transition.trigger === 'event' && !transition.eventName) {
|
|
620
|
+
errors.push({
|
|
621
|
+
type: 'missing_property',
|
|
622
|
+
path: `${transPath}.eventName`,
|
|
623
|
+
message: "eventName is required when trigger is 'event'"
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
// Validate transition steps
|
|
627
|
+
if (transition.steps && Array.isArray(transition.steps)) {
|
|
628
|
+
transition.steps.forEach((step, stepIndex) => {
|
|
629
|
+
this.validateStep(step, `${transPath}.steps[${stepIndex}]`, errors, warnings);
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Validate Flow aggregations
|
|
636
|
+
*/
|
|
637
|
+
validateFlowAggregations(workflowData, errors) {
|
|
638
|
+
const aggregations = workflowData.aggregations;
|
|
639
|
+
if (!aggregations || !Array.isArray(aggregations))
|
|
640
|
+
return;
|
|
641
|
+
const validFunctions = ['all', 'any', 'sum', 'count', 'first', 'last', 'distinct', 'groupBy'];
|
|
642
|
+
const aggregationNames = new Set();
|
|
643
|
+
aggregations.forEach((aggregation, index) => {
|
|
644
|
+
const aggPath = `aggregations[${index}]`;
|
|
645
|
+
if (!aggregation.name) {
|
|
646
|
+
errors.push({
|
|
647
|
+
type: 'missing_property',
|
|
648
|
+
path: `${aggPath}.name`,
|
|
649
|
+
message: 'Aggregation name is required'
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
else if (aggregationNames.has(aggregation.name.toLowerCase())) {
|
|
653
|
+
errors.push({
|
|
654
|
+
type: 'schema_violation',
|
|
655
|
+
path: `${aggPath}.name`,
|
|
656
|
+
message: `Duplicate aggregation name '${aggregation.name}'`
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
aggregationNames.add(aggregation.name.toLowerCase());
|
|
661
|
+
}
|
|
662
|
+
if (!aggregation.expression) {
|
|
663
|
+
errors.push({
|
|
664
|
+
type: 'missing_property',
|
|
665
|
+
path: `${aggPath}.expression`,
|
|
666
|
+
message: 'Aggregation expression is required'
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
const fnMatch = aggregation.expression.match(/^(\w+)\s*\(/);
|
|
671
|
+
if (fnMatch) {
|
|
672
|
+
if (!validFunctions.includes(fnMatch[1].toLowerCase())) {
|
|
673
|
+
errors.push({
|
|
674
|
+
type: 'schema_violation',
|
|
675
|
+
path: `${aggPath}.expression`,
|
|
676
|
+
message: `Invalid aggregation function '${fnMatch[1]}'. Valid functions: ${validFunctions.join(', ')}`
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
errors.push({
|
|
682
|
+
type: 'schema_violation',
|
|
683
|
+
path: `${aggPath}.expression`,
|
|
684
|
+
message: 'Aggregation expression must start with a function call'
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Convert Ajv errors to our error format
|
|
692
|
+
*/
|
|
693
|
+
addAjvErrors(ajvErrors, basePath, errors) {
|
|
694
|
+
if (!ajvErrors)
|
|
695
|
+
return;
|
|
696
|
+
for (const error of ajvErrors) {
|
|
697
|
+
const errorPath = basePath ? `${basePath}${error.instancePath}` : error.instancePath.slice(1);
|
|
698
|
+
errors.push({
|
|
699
|
+
type: 'schema_violation',
|
|
700
|
+
path: errorPath || '/',
|
|
701
|
+
message: error.message || 'Schema validation failed',
|
|
702
|
+
schemaPath: error.schemaPath
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Check for deprecated properties
|
|
708
|
+
*/
|
|
709
|
+
checkDeprecatedProperties(obj, path, warnings) {
|
|
710
|
+
const deprecations = {
|
|
711
|
+
// Add deprecated workflow properties here as needed
|
|
712
|
+
};
|
|
713
|
+
for (const [oldProp, message] of Object.entries(deprecations)) {
|
|
714
|
+
if (oldProp in obj) {
|
|
715
|
+
warnings.push({
|
|
716
|
+
type: 'deprecated_property',
|
|
717
|
+
path: `${path}.${oldProp}`,
|
|
718
|
+
message
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Create validation result
|
|
725
|
+
*/
|
|
726
|
+
createResult(filePath, errors, warnings) {
|
|
727
|
+
const errorsByType = {};
|
|
728
|
+
errors.forEach(error => {
|
|
729
|
+
errorsByType[error.type] = (errorsByType[error.type] || 0) + 1;
|
|
730
|
+
});
|
|
731
|
+
return {
|
|
732
|
+
isValid: errors.length === 0,
|
|
733
|
+
errors,
|
|
734
|
+
warnings: this.options.includeWarnings ? warnings : [],
|
|
735
|
+
summary: {
|
|
736
|
+
file: filePath,
|
|
737
|
+
timestamp: new Date().toISOString(),
|
|
738
|
+
status: errors.length === 0 ? 'PASSED' : 'FAILED',
|
|
739
|
+
errorCount: errors.length,
|
|
740
|
+
warningCount: warnings.length,
|
|
741
|
+
errorsByType
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Get all loaded schema keys
|
|
747
|
+
*/
|
|
748
|
+
getLoadedSchemas() {
|
|
749
|
+
return Array.from(this.schemas.keys());
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
exports.WorkflowValidator = WorkflowValidator;
|
|
753
|
+
//# sourceMappingURL=workflowValidator.js.map
|