flowlint 0.3.0 → 0.3.2

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/cli.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * FlowLint CLI
4
+ *
5
+ * A command-line tool for static analysis of n8n workflow files.
6
+ * Can be used locally, in CI pipelines, or pre-commit hooks.
7
+ */
8
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * FlowLint CLI
5
+ *
6
+ * A command-line tool for static analysis of n8n workflow files.
7
+ * Can be used locally, in CI pipelines, or pre-commit hooks.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ const commander_1 = require("commander");
11
+ const scan_1 = require("./commands/scan");
12
+ const init_1 = require("./commands/init");
13
+ const program = new commander_1.Command();
14
+ program
15
+ .name('flowlint')
16
+ .description('Static analysis tool for n8n workflows')
17
+ .version('0.3.0');
18
+ // Register commands
19
+ program.addCommand(scan_1.scanCommand);
20
+ program.addCommand(init_1.initCommand);
21
+ // Parse and execute
22
+ program.parse(process.argv);
23
+ // Show help if no command provided
24
+ if (!process.argv.slice(2).length) {
25
+ program.outputHelp();
26
+ }
27
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../../apps/cli/src/cli.ts"],"names":[],"mappings":";;AAEA;;;;;GAKG;;AAEH,yCAAoC;AACpC,0CAA8C;AAC9C,0CAA8C;AAE9C,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,wCAAwC,CAAC;KACrD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,oBAAoB;AACpB,OAAO,CAAC,UAAU,CAAC,kBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,kBAAW,CAAC,CAAC;AAEhC,oBAAoB;AACpB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5B,mCAAmC;AACnC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAClC,OAAO,CAAC,UAAU,EAAE,CAAC;AACvB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * init command - Create a .flowlint.yml configuration file interactively
3
+ *
4
+ * Usage:
5
+ * flowlint init
6
+ */
7
+ import { Command } from 'commander';
8
+ export declare const initCommand: Command;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ /**
3
+ * init command - Create a .flowlint.yml configuration file interactively
4
+ *
5
+ * Usage:
6
+ * flowlint init
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.initCommand = void 0;
13
+ const commander_1 = require("commander");
14
+ const fs_1 = __importDefault(require("fs"));
15
+ const path_1 = __importDefault(require("path"));
16
+ const yaml_1 = __importDefault(require("yaml"));
17
+ const flowlint_config_1 = require('../../../../packages/config/flowlint-config');
18
+ exports.initCommand = new commander_1.Command('init')
19
+ .description('Create a .flowlint.yml configuration file')
20
+ .action(async () => {
21
+ const configPath = path_1.default.join(process.cwd(), '.flowlint.yml');
22
+ // Check if config already exists
23
+ if (fs_1.default.existsSync(configPath)) {
24
+ console.log('.flowlint.yml already exists. Skipping initialization.');
25
+ return;
26
+ }
27
+ // For now, just write the default config
28
+ // In the future, this could be interactive
29
+ const yaml = yaml_1.default.stringify(flowlint_config_1.defaultConfig, { lineWidth: 120 });
30
+ fs_1.default.writeFileSync(configPath, yaml, 'utf8');
31
+ console.log(`✓ Created .flowlint.yml with default configuration`);
32
+ console.log(` Edit the file to customize linting rules and patterns`);
33
+ });
34
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../../../apps/cli/src/commands/init.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;AAEH,yCAAoC;AACpC,4CAAoB;AACpB,gDAAwB;AACxB,gDAAwB;AACxB,qEAAgE;AAEnD,QAAA,WAAW,GAAG,IAAI,mBAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,2CAA2C,CAAC;KACxD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,eAAe,CAAC,CAAC;IAE7D,iCAAiC;IACjC,IAAI,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;QACtE,OAAO;IACT,CAAC;IAED,yCAAyC;IACzC,2CAA2C;IAC3C,MAAM,IAAI,GAAG,cAAI,CAAC,SAAS,CAAC,+BAAa,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/D,YAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAE3C,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;IAClE,OAAO,CAAC,GAAG,CAAC,yDAAyD,CAAC,CAAC;AACzE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * scan command - Analyze n8n workflow files in a directory
3
+ *
4
+ * Usage:
5
+ * flowlint scan [path]
6
+ * flowlint scan . --config .flowlint.yml
7
+ * flowlint scan --format json --out-file report.json
8
+ * flowlint scan --fail-on-error
9
+ */
10
+ import { Command } from 'commander';
11
+ export declare const scanCommand: Command;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /**
3
+ * scan command - Analyze n8n workflow files in a directory
4
+ *
5
+ * Usage:
6
+ * flowlint scan [path]
7
+ * flowlint scan . --config .flowlint.yml
8
+ * flowlint scan --format json --out-file report.json
9
+ * flowlint scan --fail-on-error
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.scanCommand = void 0;
16
+ const commander_1 = require("commander");
17
+ const path_1 = __importDefault(require("path"));
18
+ const local_file_source_1 = require("../providers/local-file-source");
19
+ const local_config_provider_1 = require("../providers/local-config-provider");
20
+ const console_reporter_1 = require("../reporters/console-reporter");
21
+ const json_reporter_1 = require("../reporters/json-reporter");
22
+ const review_1 = require('../../../../packages/review');
23
+ exports.scanCommand = new commander_1.Command('scan')
24
+ .description('Scan workflow files for issues')
25
+ .argument('[path]', 'Directory to scan', '.')
26
+ .option('--config <path>', 'Path to .flowlint.yml config file')
27
+ .option('--format <format>', 'Output format: stylish|json', 'stylish')
28
+ .option('--out-file <path>', 'Write JSON results to file (implies --format json)')
29
+ .option('--fail-on-error', 'Exit with code 1 if errors found')
30
+ .action(async (scanPath, options) => {
31
+ try {
32
+ const absolutePath = path_1.default.resolve(process.cwd(), scanPath);
33
+ const configPath = options.config ? path_1.default.resolve(process.cwd(), options.config) : undefined;
34
+ const format = options.outFile ? 'json' : options.format;
35
+ // Initialize providers
36
+ const fileSource = new local_file_source_1.LocalFileSource(absolutePath);
37
+ const configProvider = new local_config_provider_1.LocalConfigProvider(configPath);
38
+ // Choose reporter based on format
39
+ let reporter;
40
+ if (format === 'json' || options.outFile) {
41
+ reporter = new json_reporter_1.JsonReporter(options.outFile);
42
+ }
43
+ else {
44
+ reporter = new console_reporter_1.ConsoleReporter();
45
+ }
46
+ // Run analysis
47
+ const engine = new review_1.DefaultAnalysisEngine(fileSource, configProvider, reporter);
48
+ const summary = await engine.analyze();
49
+ // Exit with appropriate code
50
+ if (options.failOnError && summary.hasBlockingIssues) {
51
+ process.exit(1);
52
+ }
53
+ }
54
+ catch (error) {
55
+ console.error('Error:', error instanceof Error ? error.message : String(error));
56
+ process.exit(2);
57
+ }
58
+ });
59
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../../../../apps/cli/src/commands/scan.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;;;;AAEH,yCAAoC;AACpC,gDAAwB;AACxB,sEAAiE;AACjE,8EAAyE;AACzE,oEAAgE;AAChE,8DAA0D;AAC1D,4CAAwD;AAE3C,QAAA,WAAW,GAAG,IAAI,mBAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,gCAAgC,CAAC;KAC7C,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,CAAC;KAC5C,MAAM,CAAC,iBAAiB,EAAE,mCAAmC,CAAC;KAC9D,MAAM,CAAC,mBAAmB,EAAE,6BAA6B,EAAE,SAAS,CAAC;KACrE,MAAM,CAAC,mBAAmB,EAAE,oDAAoD,CAAC;KACjF,MAAM,CAAC,iBAAiB,EAAE,kCAAkC,CAAC;KAC7D,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,OAAY,EAAE,EAAE;IAC/C,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,cAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;QAC3D,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,cAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5F,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QAEzD,uBAAuB;QACvB,MAAM,UAAU,GAAG,IAAI,mCAAe,CAAC,YAAY,CAAC,CAAC;QACrD,MAAM,cAAc,GAAG,IAAI,2CAAmB,CAAC,UAAU,CAAC,CAAC;QAE3D,kCAAkC;QAClC,IAAI,QAAQ,CAAC;QACb,IAAI,MAAM,KAAK,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACzC,QAAQ,GAAG,IAAI,4BAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,IAAI,kCAAe,EAAE,CAAC;QACnC,CAAC;QAED,eAAe;QACf,MAAM,MAAM,GAAG,IAAI,8BAAqB,CAAC,UAAU,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAC;QAC/E,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QAEvC,6BAA6B;QAC7B,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;YACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAChF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Local Config Provider
3
+ * Loads .flowlint.yml from the local filesystem
4
+ */
5
+ import { type FlowLintConfig } from 'packages/config/flowlint-config';
6
+ import type { ConfigProvider } from 'packages/review/providers';
7
+ export declare class LocalConfigProvider implements ConfigProvider {
8
+ private configPath?;
9
+ constructor(configPath?: string | undefined);
10
+ load(): Promise<FlowLintConfig>;
11
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * Local Config Provider
4
+ * Loads .flowlint.yml from the local filesystem
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.LocalConfigProvider = void 0;
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const yaml_1 = __importDefault(require("yaml"));
14
+ const flowlint_config_1 = require('../../../../packages/config/flowlint-config');
15
+ const merge_1 = require('../../../../packages/review/utils/merge');
16
+ class LocalConfigProvider {
17
+ constructor(configPath) {
18
+ this.configPath = configPath;
19
+ }
20
+ async load() {
21
+ // Determine config file path
22
+ const configFile = this.configPath || path_1.default.join(process.cwd(), '.flowlint.yml');
23
+ // If config doesn't exist, return defaults
24
+ if (!fs_1.default.existsSync(configFile)) {
25
+ return flowlint_config_1.defaultConfig;
26
+ }
27
+ try {
28
+ const content = fs_1.default.readFileSync(configFile, 'utf8');
29
+ const parsed = yaml_1.default.parse(content) || {};
30
+ return (0, merge_1.deepMerge)(flowlint_config_1.defaultConfig, parsed);
31
+ }
32
+ catch (error) {
33
+ console.warn(`Failed to load config from ${configFile}: ${error}`);
34
+ return flowlint_config_1.defaultConfig;
35
+ }
36
+ }
37
+ }
38
+ exports.LocalConfigProvider = LocalConfigProvider;
39
+ //# sourceMappingURL=local-config-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-config-provider.js","sourceRoot":"","sources":["../../../../../apps/cli/src/providers/local-config-provider.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;AAEH,4CAAoB;AACpB,gDAAwB;AACxB,gDAAwB;AACxB,qEAAqF;AAErF,uDAAwD;AAExD,MAAa,mBAAmB;IAC9B,YAAoB,UAAmB;QAAnB,eAAU,GAAV,UAAU,CAAS;IAAG,CAAC;IAE3C,KAAK,CAAC,IAAI;QACR,6BAA6B;QAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,eAAe,CAAC,CAAC;QAEhF,2CAA2C;QAC3C,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/B,OAAO,+BAAa,CAAC;QACvB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACpD,MAAM,MAAM,GAAI,cAAI,CAAC,KAAK,CAAC,OAAO,CAA6B,IAAI,EAAE,CAAC;YACtE,OAAO,IAAA,iBAAS,EAAC,+BAAa,EAAE,MAAM,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,8BAA8B,UAAU,KAAK,KAAK,EAAE,CAAC,CAAC;YACnE,OAAO,+BAAa,CAAC;QACvB,CAAC;IACH,CAAC;CACF;AArBD,kDAqBC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Local File Source Provider
3
+ * Reads workflow files from the local filesystem
4
+ */
5
+ import type { FileSource, LintableFile } from 'packages/review/providers';
6
+ export declare class LocalFileSource implements FileSource {
7
+ private basePath;
8
+ constructor(basePath: string);
9
+ getFiles(patterns: {
10
+ include: string[];
11
+ ignore: string[];
12
+ }): Promise<LintableFile[]>;
13
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /**
3
+ * Local File Source Provider
4
+ * Reads workflow files from the local filesystem
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.LocalFileSource = void 0;
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const glob_1 = require("glob");
14
+ class LocalFileSource {
15
+ constructor(basePath) {
16
+ this.basePath = basePath;
17
+ }
18
+ async getFiles(patterns) {
19
+ const files = [];
20
+ for (const pattern of patterns.include) {
21
+ // Construct glob pattern relative to basePath
22
+ const globPattern = path_1.default.join(this.basePath, pattern);
23
+ const matches = (0, glob_1.globSync)(globPattern, {
24
+ ignore: patterns.ignore.map((ignorePattern) => path_1.default.join(this.basePath, ignorePattern)),
25
+ nodir: true,
26
+ });
27
+ for (const filePath of matches) {
28
+ try {
29
+ const content = fs_1.default.readFileSync(filePath, 'utf8');
30
+ const relativePath = path_1.default.relative(this.basePath, filePath);
31
+ files.push({
32
+ path: relativePath,
33
+ content,
34
+ });
35
+ }
36
+ catch (error) {
37
+ console.warn(`Failed to read file ${filePath}: ${error}`);
38
+ }
39
+ }
40
+ }
41
+ // Remove duplicates (same file matched multiple patterns)
42
+ const uniqueFiles = Array.from(new Map(files.map((f) => [f.path, f])).values());
43
+ return uniqueFiles;
44
+ }
45
+ }
46
+ exports.LocalFileSource = LocalFileSource;
47
+ //# sourceMappingURL=local-file-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-file-source.js","sourceRoot":"","sources":["../../../../../apps/cli/src/providers/local-file-source.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;AAEH,4CAAoB;AACpB,gDAAwB;AACxB,+BAAgC;AAGhC,MAAa,eAAe;IAC1B,YAAoB,QAAgB;QAAhB,aAAQ,GAAR,QAAQ,CAAQ;IAAG,CAAC;IAExC,KAAK,CAAC,QAAQ,CAAC,QAAiD;QAC9D,MAAM,KAAK,GAAmB,EAAE,CAAC;QAEjC,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACvC,8CAA8C;YAC9C,MAAM,WAAW,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAEtD,MAAM,OAAO,GAAG,IAAA,eAAQ,EAAC,WAAW,EAAE;gBACpC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;gBACvF,KAAK,EAAE,IAAI;aACZ,CAAC,CAAC;YAEH,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE,CAAC;gBAC/B,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;oBAClD,MAAM,YAAY,GAAG,cAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;oBAE5D,KAAK,CAAC,IAAI,CAAC;wBACT,IAAI,EAAE,YAAY;wBAClB,OAAO;qBACR,CAAC,CAAC;gBACL,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,IAAI,CAAC,uBAAuB,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;gBAC5D,CAAC;YACH,CAAC;QACH,CAAC;QAED,0DAA0D;QAC1D,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAC5B,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAChD,CAAC;QAEF,OAAO,WAAW,CAAC;IACrB,CAAC;CACF;AArCD,0CAqCC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Console Reporter
3
+ * Outputs findings to the terminal with colors and formatting
4
+ */
5
+ import type { AnalysisResult, Reporter } from 'packages/review/providers';
6
+ export declare class ConsoleReporter implements Reporter {
7
+ report(results: AnalysisResult[]): Promise<void>;
8
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /**
3
+ * Console Reporter
4
+ * Outputs findings to the terminal with colors and formatting
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ConsoleReporter = void 0;
8
+ const findings_1 = require('../../../../packages/review/utils/findings');
9
+ const colors = {
10
+ reset: '\x1b[0m',
11
+ bold: '\x1b[1m',
12
+ red: '\x1b[31m',
13
+ yellow: '\x1b[33m',
14
+ blue: '\x1b[36m',
15
+ gray: '\x1b[90m',
16
+ };
17
+ class ConsoleReporter {
18
+ async report(results) {
19
+ const allFindings = results.flatMap((r) => r.findings);
20
+ // Print header
21
+ console.log();
22
+ console.log(`${colors.blue}${colors.bold}FlowLint Analysis Report${colors.reset} ${colors.gray}${new Date().toLocaleTimeString()}${colors.reset}`);
23
+ console.log(colors.gray + '─'.repeat(80) + colors.reset);
24
+ console.log();
25
+ // Print findings grouped by file
26
+ for (const result of results) {
27
+ if (result.findings.length === 0 && !result.errors)
28
+ continue;
29
+ const { path } = result.file;
30
+ console.log(`${colors.bold}${path}${colors.reset}`);
31
+ const sortedFindings = (0, findings_1.sortFindingsBySeverity)(result.findings);
32
+ for (const finding of sortedFindings) {
33
+ const severityColor = finding.severity === 'must'
34
+ ? colors.red
35
+ : finding.severity === 'should'
36
+ ? colors.yellow
37
+ : colors.gray;
38
+ const line = finding.line ? `:${finding.line}` : '';
39
+ const severity = finding.severity.toUpperCase();
40
+ console.log(` ${severityColor}${severity}${colors.reset} ${colors.bold}${finding.rule}${colors.reset} ${finding.message}`);
41
+ console.log(` at ${colors.gray}${path}${line}${colors.reset}`);
42
+ if (finding.documentationUrl) {
43
+ console.log(` ${colors.blue}→ See examples: ${finding.documentationUrl}${colors.reset}`);
44
+ }
45
+ if (finding.raw_details) {
46
+ const details = finding.raw_details.split('\n').slice(0, 3);
47
+ for (const detail of details) {
48
+ console.log(` ${colors.gray}${detail}${colors.reset}`);
49
+ }
50
+ }
51
+ }
52
+ console.log();
53
+ }
54
+ // Print summary
55
+ const summary = (0, findings_1.countFindingsBySeverity)(allFindings);
56
+ console.log(colors.gray + '─'.repeat(80) + colors.reset);
57
+ console.log();
58
+ if (summary.total === 0) {
59
+ console.log(`${colors.bold}✓ No issues found${colors.reset}`);
60
+ }
61
+ else {
62
+ const summaryParts = [];
63
+ if (summary.must > 0)
64
+ summaryParts.push(`${colors.red}${summary.must} must-fix${colors.reset}`);
65
+ if (summary.should > 0)
66
+ summaryParts.push(`${colors.yellow}${summary.should} should-fix${colors.reset}`);
67
+ if (summary.nit > 0)
68
+ summaryParts.push(`${colors.gray}${summary.nit} nit${colors.reset}`);
69
+ console.log(`${colors.bold}Found: ${summaryParts.join(', ')}${colors.reset}`);
70
+ }
71
+ console.log();
72
+ }
73
+ }
74
+ exports.ConsoleReporter = ConsoleReporter;
75
+ //# sourceMappingURL=console-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console-reporter.js","sourceRoot":"","sources":["../../../../../apps/cli/src/reporters/console-reporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * JSON Reporter
3
+ * Outputs findings as structured JSON
4
+ * Suitable for:
5
+ * - Integration with other tools
6
+ * - Uploading to Web Dashboard (future)
7
+ * - Programmatic processing
8
+ */
9
+ import type { AnalysisResult, Reporter } from 'packages/review/providers';
10
+ export declare class JsonReporter implements Reporter {
11
+ private outputFile?;
12
+ constructor(outputFile?: string | undefined);
13
+ report(results: AnalysisResult[]): Promise<void>;
14
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /**
3
+ * JSON Reporter
4
+ * Outputs findings as structured JSON
5
+ * Suitable for:
6
+ * - Integration with other tools
7
+ * - Uploading to Web Dashboard (future)
8
+ * - Programmatic processing
9
+ */
10
+ var __importDefault = (this && this.__importDefault) || function (mod) {
11
+ return (mod && mod.__esModule) ? mod : { "default": mod };
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.JsonReporter = void 0;
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const findings_1 = require('../../../../packages/review/utils/findings');
17
+ class JsonReporter {
18
+ constructor(outputFile) {
19
+ this.outputFile = outputFile;
20
+ }
21
+ async report(results) {
22
+ const allFindings = results.flatMap((r) => r.findings);
23
+ const severitySummary = (0, findings_1.countFindingsBySeverity)(allFindings);
24
+ const report = {
25
+ version: '1.0',
26
+ timestamp: new Date().toISOString(),
27
+ summary: {
28
+ totalFiles: results.length,
29
+ totalFindings: severitySummary.total,
30
+ findingsBySeverity: {
31
+ must: severitySummary.must,
32
+ should: severitySummary.should,
33
+ nit: severitySummary.nit,
34
+ },
35
+ },
36
+ results: results.map((result) => ({
37
+ file: {
38
+ path: result.file.path,
39
+ },
40
+ findings: result.findings,
41
+ errors: result.errors,
42
+ })),
43
+ };
44
+ const json = JSON.stringify(report, null, 2);
45
+ if (this.outputFile) {
46
+ // Write to file
47
+ fs_1.default.writeFileSync(this.outputFile, json, 'utf8');
48
+ console.log(`✓ Report written to ${this.outputFile}`);
49
+ }
50
+ else {
51
+ // Print to stdout
52
+ console.log(json);
53
+ }
54
+ }
55
+ }
56
+ exports.JsonReporter = JsonReporter;
57
+ //# sourceMappingURL=json-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-reporter.js","sourceRoot":"","sources":["../../../../../apps/cli/src/reporters/json-reporter.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;;;;AAEH,4CAAoB;AAEpB,6DAAyE;AAkCzE,MAAa,YAAY;IACvB,YAAoB,UAAmB;QAAnB,eAAU,GAAV,UAAU,CAAS;IAAG,CAAC;IAE3C,KAAK,CAAC,MAAM,CAAC,OAAyB;QACpC,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACvD,MAAM,eAAe,GAAG,IAAA,kCAAuB,EAAC,WAAW,CAAC,CAAC;QAE7D,MAAM,MAAM,GAAe;YACzB,OAAO,EAAE,KAAK;YACd,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,OAAO,EAAE;gBACP,UAAU,EAAE,OAAO,CAAC,MAAM;gBAC1B,aAAa,EAAE,eAAe,CAAC,KAAK;gBACpC,kBAAkB,EAAE;oBAClB,IAAI,EAAE,eAAe,CAAC,IAAI;oBAC1B,MAAM,EAAE,eAAe,CAAC,MAAM;oBAC9B,GAAG,EAAE,eAAe,CAAC,GAAG;iBACzB;aACF;YACD,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBAChC,IAAI,EAAE;oBACJ,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI;iBACvB;gBACD,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,MAAM,EAAE,MAAM,CAAC,MAAM;aACtB,CAAC,CAAC;SACJ,CAAC;QAEF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAE7C,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,gBAAgB;YAChB,YAAE,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAChD,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QACxD,CAAC;aAAM,CAAC;YACN,kBAAkB;YAClB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;CACF;AAvCD,oCAuCC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowlint",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Static analysis tool for n8n workflows - detect issues early, fix them faster",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -18,6 +18,15 @@
18
18
  "build": "tsc --project ../../tsconfig.json",
19
19
  "test": "vitest run"
20
20
  },
21
+ "dependencies": {
22
+ "commander": "^12.1.0",
23
+ "dotenv": "^16.4.0",
24
+ "glob": "^10.3.10",
25
+ "micromatch": "^4.0.8",
26
+ "pino": "^10.1.0",
27
+ "pino-pretty": "^13.1.2",
28
+ "yaml": "^2.4.0"
29
+ },
21
30
  "keywords": [
22
31
  "n8n",
23
32
  "workflow",