n8n-workflow-validator 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Yigit Konur
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # n8n Workflow Validator
2
+
3
+ [![npm version](https://img.shields.io/npm/v/n8n-workflow-validator.svg)](https://www.npmjs.com/package/n8n-workflow-validator)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Standalone CLI tool that validates n8n workflow JSON files using the **exact same validation logic** as the official n8n editor.
7
+
8
+ ## Quick Start
9
+
10
+ ```bash
11
+ # No installation needed - use with npx
12
+ npx n8n-workflow-validator workflow.json
13
+
14
+ # Auto-fix issues
15
+ npx n8n-workflow-validator workflow.json --fix --out fixed.json
16
+ ```
17
+
18
+ ## Installation
19
+
20
+ ### Global Installation
21
+
22
+ ```bash
23
+ npm install -g n8n-workflow-validator
24
+ n8n-validate workflow.json
25
+ ```
26
+
27
+ ### Local Development
28
+
29
+ ```bash
30
+ git clone https://github.com/yigitkonur/n8n-workflow-validator.git
31
+ cd n8n-workflow-validator
32
+ npm install
33
+ npm run build
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ### With npx (Recommended)
39
+
40
+ ```bash
41
+ # Validate a workflow file
42
+ npx n8n-workflow-validator workflow.json
43
+
44
+ # Auto-fix issues
45
+ npx n8n-workflow-validator workflow.json --fix --out fixed.json
46
+
47
+ # From URL
48
+ npx n8n-workflow-validator "https://example.com/workflow.json"
49
+
50
+ # JSON output (for CI/CD)
51
+ npx n8n-workflow-validator workflow.json --json
52
+
53
+ # With repair for malformed JSON
54
+ npx n8n-workflow-validator broken.json --repair --fix
55
+ ```
56
+
57
+ ### After Global Install
58
+
59
+ ```bash
60
+ n8n-validate workflow.json
61
+ n8n-validate workflow.json --fix --out fixed.json
62
+ ```
63
+
64
+ ### From Source
65
+
66
+ ```bash
67
+ node dist/cli.js workflow.json
68
+ ```
69
+
70
+ ## What It Validates
71
+
72
+ Mirrors n8n's 4-stage import validation pipeline:
73
+
74
+ 1. **JSON Parsing** - 3-tier fallback (standard → JS object → repair)
75
+ 2. **Structure** - nodes array + connections object
76
+ 3. **Node Fields** - type, typeVersion, position, parameters
77
+ 4. **Known Issues** - Invalid `options` fields in IF/Switch nodes
78
+
79
+ ## What It Fixes
80
+
81
+ - ✅ Invalid empty `options` field in IF/Switch nodes (causes "Could not find property option" error)
82
+ - ✅ Missing node names (auto-generated)
83
+ - ✅ Duplicate webhookIds (regenerated)
84
+ - ✅ Missing/duplicate node IDs (regenerated)
85
+
86
+ ## Options
87
+
88
+ ```
89
+ --repair Repair malformed JSON using jsonrepair
90
+ --accept-js-object Accept JavaScript object literals
91
+ --fix Auto-fix known issues
92
+ --no-sanitize Skip name/ID generation
93
+ --no-regenerate-ids Keep existing IDs
94
+ --json JSON output
95
+ --out FILE Write fixed workflow to FILE
96
+ -h, --help Show help
97
+ ```
98
+
99
+ ## Exit Codes
100
+
101
+ - `0` - Valid
102
+ - `1` - Invalid or errors
103
+
104
+ ## Architecture
105
+
106
+ ```
107
+ src/
108
+ ├── core/
109
+ │ ├── json-parser.ts # 3-tier JSON parsing (from n8n-workflow)
110
+ │ ├── types.ts # TypeScript interfaces
111
+ │ ├── validator.ts # Structure validation (from workflows.controller.ts)
112
+ │ ├── fixer.ts # Auto-fix invalid options fields
113
+ │ └── sanitizer.ts # Name/ID generation (from useCanvasOperations.ts)
114
+ ├── utils/
115
+ │ ├── input-reader.ts # File/URL/stdin handling
116
+ │ └── output.ts # Formatted output
117
+ ├── index.ts # Public API
118
+ └── cli.ts # CLI entry point
119
+ ```
120
+
121
+ ## Dependencies
122
+
123
+ - `esprima-next` - Parse JavaScript object literals
124
+ - `jsonrepair` - Repair malformed JSON
125
+
126
+ No n8n dependencies - fully standalone.
127
+
128
+ ## Example
129
+
130
+ ```bash
131
+ $ node dist/cli.js workflow.json --fix
132
+
133
+ ✅ VALID: workflow.json
134
+ 🔧 Fixed 4 issue(s)
135
+ Warnings:
136
+ - Fixed node "check-document-exists": Removed invalid empty 'options' field
137
+ - Fixed node "check-status-pending": Removed invalid empty 'options' field
138
+ - Fixed node "route-by-format": Removed invalid empty 'options' field
139
+ - Fixed node "check-processing-success": Removed invalid empty 'options' field
140
+ ```
141
+
142
+ ## License
143
+
144
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+ import { writeFile } from 'node:fs/promises';
3
+ import { resolve } from 'node:path';
4
+ import { jsonParse } from './core/json-parser.js';
5
+ import { validateWorkflowStructure } from './core/validator.js';
6
+ import { fixInvalidOptionsFields } from './core/fixer.js';
7
+ import { sanitizeWorkflow } from './core/sanitizer.js';
8
+ import { classifyInput, readInput } from './utils/input-reader.js';
9
+ import { outputSummary } from './utils/output.js';
10
+ function parseArgs(argv) {
11
+ const args = argv.slice(2);
12
+ const options = {
13
+ repairJSON: false,
14
+ acceptJSObject: false,
15
+ sanitize: true,
16
+ regenerateIds: true,
17
+ jsonOutput: false,
18
+ outputFile: undefined,
19
+ fix: false,
20
+ };
21
+ let input;
22
+ for (let i = 0; i < args.length; i++) {
23
+ const arg = args[i];
24
+ switch (arg) {
25
+ case '--repair':
26
+ options.repairJSON = true;
27
+ break;
28
+ case '--accept-js-object':
29
+ options.acceptJSObject = true;
30
+ break;
31
+ case '--no-sanitize':
32
+ options.sanitize = false;
33
+ break;
34
+ case '--no-regenerate-ids':
35
+ options.regenerateIds = false;
36
+ break;
37
+ case '--json':
38
+ options.jsonOutput = true;
39
+ break;
40
+ case '--fix':
41
+ options.fix = true;
42
+ break;
43
+ case '--out':
44
+ case '--output':
45
+ options.outputFile = args[i + 1];
46
+ i++;
47
+ break;
48
+ case '--help':
49
+ case '-h':
50
+ printHelp();
51
+ process.exit(0);
52
+ default:
53
+ if (!input && !arg.startsWith('-')) {
54
+ input = arg;
55
+ }
56
+ break;
57
+ }
58
+ }
59
+ return { input, options };
60
+ }
61
+ function printHelp() {
62
+ console.log(`
63
+ n8n Workflow JSON Validator
64
+
65
+ Usage:
66
+ n8n-validate <file-or-url> [options]
67
+
68
+ Options:
69
+ --repair Attempt to repair malformed JSON
70
+ --accept-js-object Accept JavaScript object literal syntax
71
+ --fix Auto-fix known issues (invalid options fields)
72
+ --no-sanitize Disable workflow sanitization
73
+ --no-regenerate-ids Do not regenerate node IDs
74
+ --json Output result as JSON
75
+ --out, --output FILE Write fixed workflow to FILE
76
+ -h, --help Show this help
77
+
78
+ Examples:
79
+ n8n-validate workflow.json
80
+ n8n-validate workflow.json --fix --out fixed.json
81
+ n8n-validate "https://example.com/workflow.json" --json
82
+ cat workflow.json | n8n-validate --json
83
+ `);
84
+ }
85
+ async function main() {
86
+ const { input, options } = parseArgs(process.argv);
87
+ const sourceType = classifyInput(input);
88
+ const label = input ?? '<stdin>';
89
+ let raw;
90
+ try {
91
+ raw = await readInput(input, sourceType);
92
+ }
93
+ catch (error) {
94
+ const summary = {
95
+ input: label,
96
+ sourceType,
97
+ valid: false,
98
+ errors: [`Failed to read input: ${error?.message ?? String(error)}`],
99
+ warnings: [],
100
+ sanitized: false,
101
+ };
102
+ outputSummary(summary, options.jsonOutput);
103
+ process.exit(1);
104
+ }
105
+ const parsed = jsonParse(raw.trim(), {
106
+ acceptJSObject: options.acceptJSObject,
107
+ repairJSON: options.repairJSON,
108
+ fallbackValue: null,
109
+ });
110
+ if (!parsed) {
111
+ const summary = {
112
+ input: label,
113
+ sourceType,
114
+ valid: false,
115
+ errors: ['Could not parse workflow JSON'],
116
+ warnings: [],
117
+ sanitized: false,
118
+ };
119
+ outputSummary(summary, options.jsonOutput);
120
+ process.exit(1);
121
+ }
122
+ let fixedCount = 0;
123
+ const allWarnings = [];
124
+ if (options.fix) {
125
+ const fixResult = fixInvalidOptionsFields(parsed);
126
+ fixedCount = fixResult.fixed;
127
+ allWarnings.push(...fixResult.warnings);
128
+ }
129
+ const structure = validateWorkflowStructure(parsed);
130
+ const allErrors = [...structure.errors];
131
+ allWarnings.push(...structure.warnings);
132
+ if (structure.nodeTypeIssues) {
133
+ allErrors.push(...structure.nodeTypeIssues);
134
+ }
135
+ let workflowOut = parsed;
136
+ if (structure.valid && options.sanitize) {
137
+ const sanitized = sanitizeWorkflow(parsed, { regenerateIds: options.regenerateIds });
138
+ workflowOut = sanitized.workflow;
139
+ allWarnings.push(...sanitized.warnings);
140
+ }
141
+ const summary = {
142
+ input: label,
143
+ sourceType,
144
+ valid: allErrors.length === 0,
145
+ errors: allErrors,
146
+ warnings: allWarnings,
147
+ sanitized: structure.valid && options.sanitize,
148
+ fixed: fixedCount > 0 ? fixedCount : undefined,
149
+ };
150
+ if (options.outputFile && allErrors.length === 0) {
151
+ const outPath = resolve(process.cwd(), options.outputFile);
152
+ await writeFile(outPath, JSON.stringify(workflowOut, null, 2), 'utf8');
153
+ }
154
+ outputSummary(summary, options.jsonOutput);
155
+ process.exit(summary.valid ? 0 : 1);
156
+ }
157
+ main().catch((error) => {
158
+ console.error('Fatal error:', error);
159
+ process.exit(1);
160
+ });
161
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Workflow } from './types.js';
2
+ export interface FixResult {
3
+ fixed: number;
4
+ warnings: string[];
5
+ }
6
+ export declare function fixInvalidOptionsFields(workflow: Workflow): FixResult;
7
+ //# sourceMappingURL=fixer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixer.d.ts","sourceRoot":"","sources":["../../src/core/fixer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CA0BrE"}
@@ -0,0 +1,25 @@
1
+ export function fixInvalidOptionsFields(workflow) {
2
+ const warnings = [];
3
+ let fixed = 0;
4
+ if (!Array.isArray(workflow.nodes)) {
5
+ return { fixed, warnings };
6
+ }
7
+ for (const node of workflow.nodes) {
8
+ if (!node || typeof node !== 'object')
9
+ continue;
10
+ if (node.type === 'n8n-nodes-base.if' || node.type === 'n8n-nodes-base.switch') {
11
+ if (node.parameters &&
12
+ typeof node.parameters === 'object' &&
13
+ 'options' in node.parameters &&
14
+ typeof node.parameters.options === 'object' &&
15
+ node.parameters.options !== null &&
16
+ Object.keys(node.parameters.options).length === 0) {
17
+ delete node.parameters.options;
18
+ fixed++;
19
+ warnings.push(`Fixed node "${node.name}": Removed invalid empty 'options' field from parameters root`);
20
+ }
21
+ }
22
+ }
23
+ return { fixed, warnings };
24
+ }
25
+ //# sourceMappingURL=fixer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixer.js","sourceRoot":"","sources":["../../src/core/fixer.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,uBAAuB,CAAC,QAAkB;IACxD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAC7B,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,SAAS;QAEhD,IAAI,IAAI,CAAC,IAAI,KAAK,mBAAmB,IAAI,IAAI,CAAC,IAAI,KAAK,uBAAuB,EAAE,CAAC;YAC/E,IAAI,IAAI,CAAC,UAAU;gBACf,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ;gBACnC,SAAS,IAAI,IAAI,CAAC,UAAU;gBAC5B,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,KAAK,QAAQ;gBAC3C,IAAI,CAAC,UAAU,CAAC,OAAO,KAAK,IAAI;gBAChC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACtD,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;gBAC/B,KAAK,EAAE,CAAC;gBACR,QAAQ,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,+DAA+D,CAAC,CAAC;YACzG,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,9 @@
1
+ interface JSONParseOptions<T> {
2
+ acceptJSObject?: boolean;
3
+ repairJSON?: boolean;
4
+ errorMessage?: string;
5
+ fallbackValue?: T;
6
+ }
7
+ export declare function jsonParse<T>(jsonString: string, options?: JSONParseOptions<T>): T;
8
+ export {};
9
+ //# sourceMappingURL=json-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-parser.d.ts","sourceRoot":"","sources":["../../src/core/json-parser.ts"],"names":[],"mappings":"AAIA,UAAU,gBAAgB,CAAC,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,CAAC;CACnB;AA8BD,wBAAgB,SAAS,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CA6BjF"}
@@ -0,0 +1,56 @@
1
+ import { parse as esprimaParse, Syntax } from 'esprima-next';
2
+ import { jsonrepair } from 'jsonrepair';
3
+ function syntaxNodeToValue(expression) {
4
+ switch (expression?.type) {
5
+ case Syntax.ObjectExpression:
6
+ return Object.fromEntries(expression.properties
7
+ .filter((prop) => prop.type === Syntax.Property)
8
+ .map(({ key, value }) => [syntaxNodeToValue(key), syntaxNodeToValue(value)]));
9
+ case Syntax.Identifier:
10
+ return expression.name;
11
+ case Syntax.Literal:
12
+ return expression.value;
13
+ case Syntax.ArrayExpression:
14
+ return expression.elements.map((exp) => syntaxNodeToValue(exp));
15
+ default:
16
+ return undefined;
17
+ }
18
+ }
19
+ function parseJSObject(objectAsString) {
20
+ const jsExpression = esprimaParse(`(${objectAsString})`).body.find((node) => node.type === Syntax.ExpressionStatement && node.expression.type === Syntax.ObjectExpression);
21
+ return syntaxNodeToValue(jsExpression?.expression);
22
+ }
23
+ export function jsonParse(jsonString, options) {
24
+ try {
25
+ return JSON.parse(jsonString);
26
+ }
27
+ catch (error) {
28
+ if (options?.acceptJSObject) {
29
+ try {
30
+ const jsonStringCleaned = parseJSObject(jsonString);
31
+ return jsonStringCleaned;
32
+ }
33
+ catch (e) {
34
+ }
35
+ }
36
+ if (options?.repairJSON) {
37
+ try {
38
+ const jsonStringCleaned = jsonrepair(jsonString);
39
+ return JSON.parse(jsonStringCleaned);
40
+ }
41
+ catch (e) {
42
+ }
43
+ }
44
+ if (options?.fallbackValue !== undefined) {
45
+ if (options.fallbackValue instanceof Function) {
46
+ return options.fallbackValue();
47
+ }
48
+ return options.fallbackValue;
49
+ }
50
+ else if (options?.errorMessage) {
51
+ throw new Error(options.errorMessage);
52
+ }
53
+ throw error;
54
+ }
55
+ }
56
+ //# sourceMappingURL=json-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-parser.js","sourceRoot":"","sources":["../../src/core/json-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,YAAY,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE7D,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AASxC,SAAS,iBAAiB,CAAC,UAA8B;IACvD,QAAQ,UAAU,EAAE,IAAI,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,gBAAgB;YAC1B,OAAO,MAAM,CAAC,WAAW,CACvB,UAAU,CAAC,UAAU;iBAClB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,QAAQ,CAAC;iBAC/C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,CAC/E,CAAC;QACJ,KAAK,MAAM,CAAC,UAAU;YACpB,OAAO,UAAU,CAAC,IAAI,CAAC;QACzB,KAAK,MAAM,CAAC,OAAO;YACjB,OAAO,UAAU,CAAC,KAAK,CAAC;QAC1B,KAAK,MAAM,CAAC,eAAe;YACzB,OAAO,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;QAClE;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,cAAsB;IAC3C,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAChE,CAAC,IAAI,EAA+B,EAAE,CACpC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,MAAM,CAAC,gBAAgB,CAC/F,CAAC;IAEF,OAAO,iBAAiB,CAAC,YAAY,EAAE,UAAU,CAAW,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,SAAS,CAAI,UAAkB,EAAE,OAA6B;IAC5E,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAM,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,EAAE,cAAc,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,iBAAiB,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;gBACpD,OAAO,iBAAsB,CAAC;YAChC,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;YACb,CAAC;QACH,CAAC;QACD,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC;YACxB,IAAI,CAAC;gBACH,MAAM,iBAAiB,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;gBACjD,OAAO,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAM,CAAC;YAC5C,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;YACb,CAAC;QACH,CAAC;QACD,IAAI,OAAO,EAAE,aAAa,KAAK,SAAS,EAAE,CAAC;YACzC,IAAI,OAAO,CAAC,aAAa,YAAY,QAAQ,EAAE,CAAC;gBAC9C,OAAO,OAAO,CAAC,aAAa,EAAE,CAAC;YACjC,CAAC;YACD,OAAO,OAAO,CAAC,aAAa,CAAC;QAC/B,CAAC;aAAM,IAAI,OAAO,EAAE,YAAY,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { Workflow } from './types.js';
2
+ export interface SanitizeOptions {
3
+ regenerateIds: boolean;
4
+ }
5
+ export interface SanitizeResult {
6
+ workflow: Workflow;
7
+ warnings: string[];
8
+ }
9
+ export declare function sanitizeWorkflow(workflow: Workflow, options: SanitizeOptions): SanitizeResult;
10
+ //# sourceMappingURL=sanitizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitizer.d.ts","sourceRoot":"","sources":["../../src/core/sanitizer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAaD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,GAAG,cAAc,CAsD7F"}
@@ -0,0 +1,58 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ function generateUniqueName(base, existing) {
3
+ const cleanBase = base.split('.').pop() || base || 'Node';
4
+ let candidate = cleanBase;
5
+ let counter = 1;
6
+ while (existing.has(candidate)) {
7
+ candidate = `${cleanBase} ${counter}`;
8
+ counter += 1;
9
+ }
10
+ return candidate;
11
+ }
12
+ export function sanitizeWorkflow(workflow, options) {
13
+ const warnings = [];
14
+ if (!Array.isArray(workflow.nodes)) {
15
+ return { workflow, warnings };
16
+ }
17
+ const existingNames = new Set();
18
+ const webhookIdCounts = new Map();
19
+ for (const node of workflow.nodes) {
20
+ if (typeof node.name === 'string') {
21
+ existingNames.add(node.name);
22
+ }
23
+ if (node.webhookId && typeof node.webhookId === 'string') {
24
+ webhookIdCounts.set(node.webhookId, (webhookIdCounts.get(node.webhookId) ?? 0) + 1);
25
+ }
26
+ }
27
+ const duplicateWebhookIds = new Set(Array.from(webhookIdCounts.entries())
28
+ .filter(([, count]) => count > 1)
29
+ .map(([id]) => id));
30
+ for (const node of workflow.nodes) {
31
+ if (!node.name || typeof node.name !== 'string') {
32
+ const base = typeof node.type === 'string' ? node.type : 'Node';
33
+ const newName = generateUniqueName(base, existingNames);
34
+ node.name = newName;
35
+ existingNames.add(newName);
36
+ warnings.push(`Node without name received generated name: ${newName}`);
37
+ }
38
+ if (node.webhookId && typeof node.webhookId === 'string' && duplicateWebhookIds.has(node.webhookId)) {
39
+ const oldId = node.webhookId;
40
+ const newId = randomUUID();
41
+ node.webhookId = newId;
42
+ if (node.parameters && typeof node.parameters === 'object' && 'path' in node.parameters) {
43
+ node.parameters.path = newId;
44
+ }
45
+ warnings.push(`WebhookId ${oldId} duplicated in workflow, regenerated to ${newId}`);
46
+ }
47
+ if (options.regenerateIds) {
48
+ if (node.id && typeof node.id === 'string') {
49
+ node.id = randomUUID();
50
+ }
51
+ else if (!node.id) {
52
+ node.id = randomUUID();
53
+ }
54
+ }
55
+ }
56
+ return { workflow, warnings };
57
+ }
58
+ //# sourceMappingURL=sanitizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitizer.js","sourceRoot":"","sources":["../../src/core/sanitizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export interface WorkflowNode {
2
+ id?: string;
3
+ name?: string;
4
+ type: string;
5
+ typeVersion: number;
6
+ position: [number, number];
7
+ parameters: Record<string, unknown>;
8
+ credentials?: Record<string, unknown>;
9
+ disabled?: boolean;
10
+ webhookId?: string;
11
+ [key: string]: unknown;
12
+ }
13
+ export interface Workflow {
14
+ name?: string;
15
+ nodes: WorkflowNode[];
16
+ connections: Record<string, unknown>;
17
+ active?: boolean;
18
+ settings?: Record<string, unknown>;
19
+ pinData?: Record<string, unknown>;
20
+ meta?: Record<string, unknown>;
21
+ [key: string]: unknown;
22
+ }
23
+ export interface ValidationResult {
24
+ valid: boolean;
25
+ errors: string[];
26
+ warnings: string[];
27
+ nodeTypeIssues?: string[];
28
+ }
29
+ export interface ValidationSummary {
30
+ input: string;
31
+ sourceType: 'file' | 'url' | 'stdin';
32
+ valid: boolean;
33
+ errors: string[];
34
+ warnings: string[];
35
+ sanitized: boolean;
36
+ fixed?: number;
37
+ }
38
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ import type { ValidationResult } from './types.js';
2
+ export declare function validateWorkflowStructure(data: unknown): ValidationResult;
3
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/core/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE7D,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG,gBAAgB,CAyFzE"}
@@ -0,0 +1,88 @@
1
+ export function validateWorkflowStructure(data) {
2
+ const errors = [];
3
+ const warnings = [];
4
+ const nodeTypeIssues = [];
5
+ if (typeof data !== 'object' || data === null) {
6
+ errors.push('Workflow must be a JSON object');
7
+ return { valid: false, errors, warnings, nodeTypeIssues };
8
+ }
9
+ const wf = data;
10
+ if (wf.nodes === undefined) {
11
+ errors.push('Missing required property: nodes');
12
+ }
13
+ else if (!Array.isArray(wf.nodes)) {
14
+ errors.push('Property "nodes" must be an array');
15
+ }
16
+ if (wf.connections === undefined) {
17
+ errors.push('Missing required property: connections');
18
+ }
19
+ else if (typeof wf.connections !== 'object' || wf.connections === null) {
20
+ errors.push('Property "connections" must be an object');
21
+ }
22
+ else if (Array.isArray(wf.connections)) {
23
+ errors.push('Property "connections" must be an object, not an array');
24
+ }
25
+ if (Array.isArray(wf.nodes)) {
26
+ for (let i = 0; i < wf.nodes.length; i++) {
27
+ const node = wf.nodes[i];
28
+ if (!node || typeof node !== 'object') {
29
+ errors.push(`Node at index ${i} is not an object`);
30
+ continue;
31
+ }
32
+ if (!node.type) {
33
+ errors.push(`Node at index ${i} (${node.name || 'unnamed'}) missing required field: type`);
34
+ }
35
+ else if (typeof node.type !== 'string') {
36
+ errors.push(`Node at index ${i} (${node.name || 'unnamed'}) field 'type' must be a string`);
37
+ }
38
+ else {
39
+ if (!node.type.includes('.')) {
40
+ nodeTypeIssues.push(`Node "${node.name || 'unnamed'}" has invalid type "${node.type}" - must include package prefix (e.g., "n8n-nodes-base.webhook")`);
41
+ }
42
+ else if (node.type.startsWith('nodes-base.')) {
43
+ nodeTypeIssues.push(`Node "${node.name || 'unnamed'}" has invalid type "${node.type}" - should be "n8n-${node.type}"`);
44
+ }
45
+ }
46
+ if (!node.name) {
47
+ warnings.push(`Node at index ${i} (type: ${node.type || 'unknown'}) missing 'name' field - will be auto-generated`);
48
+ }
49
+ if (node.typeVersion === undefined) {
50
+ errors.push(`Node "${node.name || 'unnamed'}" missing required field: typeVersion`);
51
+ }
52
+ else if (typeof node.typeVersion !== 'number') {
53
+ errors.push(`Node "${node.name || 'unnamed'}" field 'typeVersion' must be a number`);
54
+ }
55
+ if (!node.position) {
56
+ errors.push(`Node "${node.name || 'unnamed'}" missing required field: position`);
57
+ }
58
+ else if (!Array.isArray(node.position) || node.position.length !== 2) {
59
+ errors.push(`Node "${node.name || 'unnamed'}" field 'position' must be an array of [x, y]`);
60
+ }
61
+ if (node.parameters === undefined) {
62
+ errors.push(`Node "${node.name || 'unnamed'}" missing required field: parameters`);
63
+ }
64
+ else if (typeof node.parameters !== 'object' || node.parameters === null) {
65
+ errors.push(`Node "${node.name || 'unnamed'}" field 'parameters' must be an object`);
66
+ }
67
+ else {
68
+ if (node.type === 'n8n-nodes-base.if' || node.type === 'n8n-nodes-base.switch') {
69
+ if ('options' in node.parameters &&
70
+ typeof node.parameters.options === 'object' &&
71
+ node.parameters.options !== null &&
72
+ Object.keys(node.parameters.options).length === 0) {
73
+ errors.push(`Node "${node.name || 'unnamed'}" (${node.type}): Invalid empty 'options' field at parameters root. ` +
74
+ `Remove 'parameters.options' - the 'options' field should only exist inside 'conditions', not at root level. ` +
75
+ `This causes "Could not find property option" error when pasting into n8n editor.`);
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ return {
82
+ valid: errors.length === 0,
83
+ errors,
84
+ warnings,
85
+ nodeTypeIssues: nodeTypeIssues.length > 0 ? nodeTypeIssues : undefined
86
+ };
87
+ }
88
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/core/validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { jsonParse } from './core/json-parser.js';
2
+ export { validateWorkflowStructure } from './core/validator.js';
3
+ export { fixInvalidOptionsFields } from './core/fixer.js';
4
+ export { sanitizeWorkflow } from './core/sanitizer.js';
5
+ export type { Workflow, WorkflowNode, ValidationResult, ValidationSummary } from './core/types.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { jsonParse } from './core/json-parser.js';
2
+ export { validateWorkflowStructure } from './core/validator.js';
3
+ export { fixInvalidOptionsFields } from './core/fixer.js';
4
+ export { sanitizeWorkflow } from './core/sanitizer.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export type InputType = 'file' | 'url' | 'stdin';
2
+ export declare function classifyInput(input?: string): InputType;
3
+ export declare function readInput(input: string | undefined, sourceType: InputType): Promise<string>;
4
+ //# sourceMappingURL=input-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-reader.d.ts","sourceRoot":"","sources":["../../src/utils/input-reader.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;AAEjD,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAIvD;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBjG"}
@@ -0,0 +1,33 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ const URL_REGEX = /^https?:\/\/.+/i;
4
+ export function classifyInput(input) {
5
+ if (!input)
6
+ return 'stdin';
7
+ const trimmed = input.trim();
8
+ return URL_REGEX.test(trimmed) ? 'url' : 'file';
9
+ }
10
+ export async function readInput(input, sourceType) {
11
+ if (sourceType === 'stdin') {
12
+ return new Promise((resolve, reject) => {
13
+ let data = '';
14
+ process.stdin.setEncoding('utf8');
15
+ process.stdin.on('data', chunk => { data += chunk; });
16
+ process.stdin.on('end', () => resolve(data));
17
+ process.stdin.on('error', reject);
18
+ });
19
+ }
20
+ if (!input) {
21
+ throw new Error('No input provided');
22
+ }
23
+ if (sourceType === 'url') {
24
+ const response = await fetch(input);
25
+ if (!response.ok) {
26
+ throw new Error(`Failed to fetch URL: ${response.status} ${response.statusText}`);
27
+ }
28
+ return response.text();
29
+ }
30
+ const absPath = resolve(process.cwd(), input);
31
+ return readFile(absPath, 'utf8');
32
+ }
33
+ //# sourceMappingURL=input-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-reader.js","sourceRoot":"","sources":["../../src/utils/input-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,SAAS,GAAG,iBAAiB,CAAC;AAIpC,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,CAAC,KAAK;QAAE,OAAO,OAAO,CAAC;IAC3B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,OAAO,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAyB,EAAE,UAAqB;IAC9E,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;QAC3B,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC7C,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACtD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YAC7C,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,wBAAwB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QACpF,CAAC;QACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;IAC9C,OAAO,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ValidationSummary } from '../core/types.js';
2
+ export declare function outputSummary(summary: ValidationSummary, jsonOutput: boolean): void;
3
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,wBAAgB,aAAa,CAAC,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,OAAO,GAAG,IAAI,CA0BnF"}
@@ -0,0 +1,24 @@
1
+ export function outputSummary(summary, jsonOutput) {
2
+ if (jsonOutput) {
3
+ console.log(JSON.stringify(summary, null, 2));
4
+ return;
5
+ }
6
+ const prefix = summary.valid ? '✅ VALID' : '❌ INVALID';
7
+ console.log(`${prefix}: ${summary.input}`);
8
+ if (summary.fixed && summary.fixed > 0) {
9
+ console.log(` 🔧 Fixed ${summary.fixed} issue(s)`);
10
+ }
11
+ if (summary.errors.length) {
12
+ console.log(' Errors:');
13
+ for (const err of summary.errors) {
14
+ console.log(` - ${err}`);
15
+ }
16
+ }
17
+ if (summary.warnings.length) {
18
+ console.log(' Warnings:');
19
+ for (const warn of summary.warnings) {
20
+ console.log(` - ${warn}`);
21
+ }
22
+ }
23
+ }
24
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa,CAAC,OAA0B,EAAE,UAAmB;IAC3E,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9C,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAE3C,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,cAAc,OAAO,CAAC,KAAK,WAAW,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACzB,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QAC3B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "n8n-workflow-validator",
3
+ "version": "1.0.0",
4
+ "description": "Standalone CLI validator for n8n workflow JSON files - mirrors official n8n import validation",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "n8n-validate": "dist/cli.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "dev": "tsc --watch",
19
+ "validate": "node dist/cli.js",
20
+ "test": "node dist/cli.js test/fixtures/*.json"
21
+ },
22
+ "keywords": [
23
+ "n8n",
24
+ "workflow",
25
+ "validation",
26
+ "cli",
27
+ "json",
28
+ "validator",
29
+ "automation"
30
+ ],
31
+ "author": "Yigit Konur",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/yigitkonur/n8n-workflow-validator.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/yigitkonur/n8n-workflow-validator/issues"
39
+ },
40
+ "homepage": "https://github.com/yigitkonur/n8n-workflow-validator#readme",
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ },
44
+ "dependencies": {
45
+ "esprima-next": "^5.8.4",
46
+ "jsonrepair": "^3.8.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^20.0.0",
50
+ "typescript": "^5.3.0"
51
+ }
52
+ }