@replikanti/flowlint-core 0.6.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/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # @flowlint/core
2
+
3
+ Core linting engine for n8n workflows. This package provides the fundamental building blocks for analyzing and validating n8n workflow files.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @flowlint/core
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { parseN8n, runAllRules, loadConfig, defaultConfig } from '@flowlint/core';
15
+
16
+ // Parse a workflow from JSON string
17
+ const workflow = parseN8n(workflowJsonString);
18
+
19
+ // Run all linting rules
20
+ const findings = runAllRules(workflow, {
21
+ path: 'my-workflow.n8n.json',
22
+ cfg: defaultConfig,
23
+ });
24
+
25
+ // Process findings
26
+ findings.forEach(finding => {
27
+ console.log([${finding.severity.toUpperCase()}] ${finding.rule}: ${finding.message});
28
+ });
29
+ ```
30
+
31
+ ## API
32
+
33
+ ### Parser
34
+
35
+ - `parseN8n(doc: string): Graph` - Parse n8n workflow JSON/YAML into a graph structure
36
+
37
+ ### Linting
38
+
39
+ - `runAllRules(graph: Graph, ctx: RuleContext): Finding[]` - Run all enabled rules
40
+
41
+ ### Configuration
42
+
43
+ - `loadConfig(configPath?: string): FlowLintConfig` - Load configuration from file
44
+ - `defaultConfig: FlowLintConfig` - Default configuration
45
+ - `parseConfig(content: string): FlowLintConfig` - Parse config from YAML string
46
+
47
+ ### Validation
48
+
49
+ - `validateN8nWorkflow(data: unknown): void` - Validate workflow structure
50
+
51
+ ## Rules
52
+
53
+ This package includes 14 built-in rules:
54
+
55
+ | Rule | Description | Severity |
56
+ |------|-------------|----------|
57
+ | R1 | Rate limit retry | must |
58
+ | R2 | Error handling | must |
59
+ | R3 | Idempotency | should |
60
+ | R4 | Secrets exposure | must |
61
+ | R5 | Dead ends | nit |
62
+ | R6 | Long running | should |
63
+ | R7 | Alert/log enforcement | should |
64
+ | R8 | Unused data | nit |
65
+ | R9 | Config literals | should |
66
+ | R10 | Naming convention | nit |
67
+ | R11 | Deprecated nodes | should |
68
+ | R12 | Unhandled error path | must |
69
+ | R13 | Webhook acknowledgment | must |
70
+ | R14 | Retry-After compliance | should |
71
+
72
+ ## License
73
+
74
+ MIT
@@ -0,0 +1,230 @@
1
+ interface RateLimitRetryConfig {
2
+ enabled: boolean;
3
+ max_concurrency?: number;
4
+ default_retry?: {
5
+ count: number;
6
+ strategy: string;
7
+ base_ms: number;
8
+ };
9
+ }
10
+ interface ErrorHandlingConfig {
11
+ enabled: boolean;
12
+ forbid_continue_on_fail?: boolean;
13
+ }
14
+ interface IdempotencyConfig {
15
+ enabled: boolean;
16
+ key_field_candidates?: string[];
17
+ }
18
+ interface SecretsConfig {
19
+ enabled: boolean;
20
+ denylist_regex?: string[];
21
+ }
22
+ interface DeadEndsConfig {
23
+ enabled: boolean;
24
+ }
25
+ interface LongRunningConfig {
26
+ enabled: boolean;
27
+ max_iterations?: number;
28
+ timeout_ms?: number;
29
+ }
30
+ interface UnusedDataConfig {
31
+ enabled: boolean;
32
+ }
33
+ interface UnhandledErrorPathConfig {
34
+ enabled: boolean;
35
+ }
36
+ interface AlertLogEnforcementConfig {
37
+ enabled: boolean;
38
+ }
39
+ interface DeprecatedNodesConfig {
40
+ enabled: boolean;
41
+ }
42
+ interface NamingConventionConfig {
43
+ enabled: boolean;
44
+ generic_names?: string[];
45
+ }
46
+ interface ConfigLiteralsConfig {
47
+ enabled: boolean;
48
+ denylist_regex?: string[];
49
+ }
50
+ interface WebhookAcknowledgmentConfig {
51
+ enabled: boolean;
52
+ heavy_node_types?: string[];
53
+ }
54
+ interface RetryAfterComplianceConfig {
55
+ enabled: boolean;
56
+ suggest_exponential_backoff?: boolean;
57
+ suggest_jitter?: boolean;
58
+ }
59
+ interface RulesConfig {
60
+ rate_limit_retry: RateLimitRetryConfig;
61
+ error_handling: ErrorHandlingConfig;
62
+ idempotency: IdempotencyConfig;
63
+ secrets: SecretsConfig;
64
+ dead_ends: DeadEndsConfig;
65
+ long_running: LongRunningConfig;
66
+ unused_data: UnusedDataConfig;
67
+ unhandled_error_path: UnhandledErrorPathConfig;
68
+ alert_log_enforcement: AlertLogEnforcementConfig;
69
+ deprecated_nodes: DeprecatedNodesConfig;
70
+ naming_convention: NamingConventionConfig;
71
+ config_literals: ConfigLiteralsConfig;
72
+ webhook_acknowledgment: WebhookAcknowledgmentConfig;
73
+ retry_after_compliance: RetryAfterComplianceConfig;
74
+ }
75
+ interface FilesConfig {
76
+ include: string[];
77
+ ignore: string[];
78
+ }
79
+ interface ReportConfig {
80
+ annotations: boolean;
81
+ summary_limit: number;
82
+ }
83
+ interface FlowLintConfig {
84
+ files: FilesConfig;
85
+ report: ReportConfig;
86
+ rules: RulesConfig;
87
+ }
88
+ type RuleConfig = {
89
+ enabled: boolean;
90
+ [key: string]: unknown;
91
+ };
92
+ declare const defaultConfig: FlowLintConfig;
93
+
94
+ /**
95
+ * Isomorphic config loader for FlowLint
96
+ * Works in both Node.js and browser environments
97
+ */
98
+
99
+ /**
100
+ * Parse config from YAML string
101
+ */
102
+ declare function parseConfig(content: string): FlowLintConfig;
103
+ /**
104
+ * Load config - isomorphic function
105
+ * In browser: returns defaultConfig (no filesystem access)
106
+ * In Node.js: optionally loads from file path
107
+ */
108
+ declare function loadConfig(configPath?: string): FlowLintConfig;
109
+ /**
110
+ * Validate config structure
111
+ */
112
+ declare function validateConfig(config: unknown): config is FlowLintConfig;
113
+
114
+ /**
115
+ * Types module exports
116
+ */
117
+ type PRFile = {
118
+ filename: string;
119
+ status: string;
120
+ sha?: string;
121
+ patch?: string;
122
+ };
123
+ type FindingSeverity = 'must' | 'should' | 'nit';
124
+ type Finding = {
125
+ rule: string;
126
+ severity: FindingSeverity;
127
+ path: string;
128
+ message: string;
129
+ raw_details?: string;
130
+ nodeId?: string;
131
+ line?: number;
132
+ documentationUrl?: string;
133
+ };
134
+ type NodeRef = {
135
+ id: string;
136
+ type: string;
137
+ name?: string;
138
+ params?: Record<string, unknown>;
139
+ cred?: Record<string, unknown>;
140
+ flags?: {
141
+ continueOnFail?: boolean;
142
+ retryOnFail?: boolean | string;
143
+ waitBetweenTries?: number | string;
144
+ maxTries?: number | string;
145
+ };
146
+ };
147
+ type Edge = {
148
+ from: string;
149
+ to: string;
150
+ on?: 'success' | 'error' | 'timeout';
151
+ };
152
+ type Graph = {
153
+ nodes: NodeRef[];
154
+ edges: Edge[];
155
+ meta: Record<string, unknown>;
156
+ };
157
+ type RuleContext$1 = {
158
+ path: string;
159
+ cfg: FlowLintConfig;
160
+ nodeLines?: Record<string, number>;
161
+ };
162
+ type RuleRunner = (graph: Graph, ctx: RuleContext$1) => Finding[];
163
+
164
+ declare function parseN8n(doc: string): Graph;
165
+
166
+ type RuleContext = {
167
+ path: string;
168
+ cfg: FlowLintConfig;
169
+ nodeLines?: Record<string, number>;
170
+ };
171
+ declare function runAllRules(graph: Graph, ctx: RuleContext): Finding[];
172
+
173
+ declare class ValidationError extends Error {
174
+ errors: Array<{
175
+ path: string;
176
+ message: string;
177
+ suggestion?: string;
178
+ }>;
179
+ constructor(errors: Array<{
180
+ path: string;
181
+ message: string;
182
+ suggestion?: string;
183
+ }>);
184
+ }
185
+ /**
186
+ * Validate n8n workflow structure
187
+ * Throws ValidationError with detailed messages if validation fails
188
+ */
189
+ declare function validateN8nWorkflow(data: any): void;
190
+
191
+ /**
192
+ * Shared utility functions for workflow parsing and validation
193
+ */
194
+ /**
195
+ * Helper to flatten nested connection arrays from n8n workflow connections.
196
+ * Connections can be nested in various ways (arrays of arrays, objects with node properties).
197
+ * This recursively flattens them to a simple array of connection objects.
198
+ *
199
+ * @param value - The connection value to flatten (can be array, object, or primitive)
200
+ * @returns Array of connection objects with 'node' property
201
+ */
202
+ declare function flattenConnections(value: any): any[];
203
+ declare function isApiNode(type: string): boolean;
204
+ declare function isMutationNode(type: string): boolean;
205
+ declare function isErrorProneNode(type: string): boolean;
206
+ declare function isNotificationNode(type: string): boolean;
207
+ declare function isTerminalNode(type: string, name?: string): boolean;
208
+ declare function getExampleLink(ruleId: string): string;
209
+
210
+ /**
211
+ * Findings utilities
212
+ * Shared logic for processing and analyzing findings across both review engine and CLI
213
+ */
214
+
215
+ interface FindingsSummary {
216
+ must: number;
217
+ should: number;
218
+ nit: number;
219
+ total: number;
220
+ }
221
+ /**
222
+ * Count findings by severity level
223
+ */
224
+ declare function countFindingsBySeverity(findings: Finding[]): FindingsSummary;
225
+ /**
226
+ * Sort findings by severity
227
+ */
228
+ declare function sortFindingsBySeverity(findings: Finding[]): Finding[];
229
+
230
+ export { type Edge, type FilesConfig, type Finding, type FindingSeverity, type FlowLintConfig, type Graph, type NodeRef, type PRFile, type ReportConfig, type RuleConfig, type RuleContext$1 as RuleContext, type RuleRunner, ValidationError, countFindingsBySeverity, defaultConfig, flattenConnections, getExampleLink, isApiNode, isErrorProneNode, isMutationNode, isNotificationNode, isTerminalNode, loadConfig, parseConfig, parseN8n, runAllRules, sortFindingsBySeverity, validateConfig, validateN8nWorkflow };
@@ -0,0 +1,230 @@
1
+ interface RateLimitRetryConfig {
2
+ enabled: boolean;
3
+ max_concurrency?: number;
4
+ default_retry?: {
5
+ count: number;
6
+ strategy: string;
7
+ base_ms: number;
8
+ };
9
+ }
10
+ interface ErrorHandlingConfig {
11
+ enabled: boolean;
12
+ forbid_continue_on_fail?: boolean;
13
+ }
14
+ interface IdempotencyConfig {
15
+ enabled: boolean;
16
+ key_field_candidates?: string[];
17
+ }
18
+ interface SecretsConfig {
19
+ enabled: boolean;
20
+ denylist_regex?: string[];
21
+ }
22
+ interface DeadEndsConfig {
23
+ enabled: boolean;
24
+ }
25
+ interface LongRunningConfig {
26
+ enabled: boolean;
27
+ max_iterations?: number;
28
+ timeout_ms?: number;
29
+ }
30
+ interface UnusedDataConfig {
31
+ enabled: boolean;
32
+ }
33
+ interface UnhandledErrorPathConfig {
34
+ enabled: boolean;
35
+ }
36
+ interface AlertLogEnforcementConfig {
37
+ enabled: boolean;
38
+ }
39
+ interface DeprecatedNodesConfig {
40
+ enabled: boolean;
41
+ }
42
+ interface NamingConventionConfig {
43
+ enabled: boolean;
44
+ generic_names?: string[];
45
+ }
46
+ interface ConfigLiteralsConfig {
47
+ enabled: boolean;
48
+ denylist_regex?: string[];
49
+ }
50
+ interface WebhookAcknowledgmentConfig {
51
+ enabled: boolean;
52
+ heavy_node_types?: string[];
53
+ }
54
+ interface RetryAfterComplianceConfig {
55
+ enabled: boolean;
56
+ suggest_exponential_backoff?: boolean;
57
+ suggest_jitter?: boolean;
58
+ }
59
+ interface RulesConfig {
60
+ rate_limit_retry: RateLimitRetryConfig;
61
+ error_handling: ErrorHandlingConfig;
62
+ idempotency: IdempotencyConfig;
63
+ secrets: SecretsConfig;
64
+ dead_ends: DeadEndsConfig;
65
+ long_running: LongRunningConfig;
66
+ unused_data: UnusedDataConfig;
67
+ unhandled_error_path: UnhandledErrorPathConfig;
68
+ alert_log_enforcement: AlertLogEnforcementConfig;
69
+ deprecated_nodes: DeprecatedNodesConfig;
70
+ naming_convention: NamingConventionConfig;
71
+ config_literals: ConfigLiteralsConfig;
72
+ webhook_acknowledgment: WebhookAcknowledgmentConfig;
73
+ retry_after_compliance: RetryAfterComplianceConfig;
74
+ }
75
+ interface FilesConfig {
76
+ include: string[];
77
+ ignore: string[];
78
+ }
79
+ interface ReportConfig {
80
+ annotations: boolean;
81
+ summary_limit: number;
82
+ }
83
+ interface FlowLintConfig {
84
+ files: FilesConfig;
85
+ report: ReportConfig;
86
+ rules: RulesConfig;
87
+ }
88
+ type RuleConfig = {
89
+ enabled: boolean;
90
+ [key: string]: unknown;
91
+ };
92
+ declare const defaultConfig: FlowLintConfig;
93
+
94
+ /**
95
+ * Isomorphic config loader for FlowLint
96
+ * Works in both Node.js and browser environments
97
+ */
98
+
99
+ /**
100
+ * Parse config from YAML string
101
+ */
102
+ declare function parseConfig(content: string): FlowLintConfig;
103
+ /**
104
+ * Load config - isomorphic function
105
+ * In browser: returns defaultConfig (no filesystem access)
106
+ * In Node.js: optionally loads from file path
107
+ */
108
+ declare function loadConfig(configPath?: string): FlowLintConfig;
109
+ /**
110
+ * Validate config structure
111
+ */
112
+ declare function validateConfig(config: unknown): config is FlowLintConfig;
113
+
114
+ /**
115
+ * Types module exports
116
+ */
117
+ type PRFile = {
118
+ filename: string;
119
+ status: string;
120
+ sha?: string;
121
+ patch?: string;
122
+ };
123
+ type FindingSeverity = 'must' | 'should' | 'nit';
124
+ type Finding = {
125
+ rule: string;
126
+ severity: FindingSeverity;
127
+ path: string;
128
+ message: string;
129
+ raw_details?: string;
130
+ nodeId?: string;
131
+ line?: number;
132
+ documentationUrl?: string;
133
+ };
134
+ type NodeRef = {
135
+ id: string;
136
+ type: string;
137
+ name?: string;
138
+ params?: Record<string, unknown>;
139
+ cred?: Record<string, unknown>;
140
+ flags?: {
141
+ continueOnFail?: boolean;
142
+ retryOnFail?: boolean | string;
143
+ waitBetweenTries?: number | string;
144
+ maxTries?: number | string;
145
+ };
146
+ };
147
+ type Edge = {
148
+ from: string;
149
+ to: string;
150
+ on?: 'success' | 'error' | 'timeout';
151
+ };
152
+ type Graph = {
153
+ nodes: NodeRef[];
154
+ edges: Edge[];
155
+ meta: Record<string, unknown>;
156
+ };
157
+ type RuleContext$1 = {
158
+ path: string;
159
+ cfg: FlowLintConfig;
160
+ nodeLines?: Record<string, number>;
161
+ };
162
+ type RuleRunner = (graph: Graph, ctx: RuleContext$1) => Finding[];
163
+
164
+ declare function parseN8n(doc: string): Graph;
165
+
166
+ type RuleContext = {
167
+ path: string;
168
+ cfg: FlowLintConfig;
169
+ nodeLines?: Record<string, number>;
170
+ };
171
+ declare function runAllRules(graph: Graph, ctx: RuleContext): Finding[];
172
+
173
+ declare class ValidationError extends Error {
174
+ errors: Array<{
175
+ path: string;
176
+ message: string;
177
+ suggestion?: string;
178
+ }>;
179
+ constructor(errors: Array<{
180
+ path: string;
181
+ message: string;
182
+ suggestion?: string;
183
+ }>);
184
+ }
185
+ /**
186
+ * Validate n8n workflow structure
187
+ * Throws ValidationError with detailed messages if validation fails
188
+ */
189
+ declare function validateN8nWorkflow(data: any): void;
190
+
191
+ /**
192
+ * Shared utility functions for workflow parsing and validation
193
+ */
194
+ /**
195
+ * Helper to flatten nested connection arrays from n8n workflow connections.
196
+ * Connections can be nested in various ways (arrays of arrays, objects with node properties).
197
+ * This recursively flattens them to a simple array of connection objects.
198
+ *
199
+ * @param value - The connection value to flatten (can be array, object, or primitive)
200
+ * @returns Array of connection objects with 'node' property
201
+ */
202
+ declare function flattenConnections(value: any): any[];
203
+ declare function isApiNode(type: string): boolean;
204
+ declare function isMutationNode(type: string): boolean;
205
+ declare function isErrorProneNode(type: string): boolean;
206
+ declare function isNotificationNode(type: string): boolean;
207
+ declare function isTerminalNode(type: string, name?: string): boolean;
208
+ declare function getExampleLink(ruleId: string): string;
209
+
210
+ /**
211
+ * Findings utilities
212
+ * Shared logic for processing and analyzing findings across both review engine and CLI
213
+ */
214
+
215
+ interface FindingsSummary {
216
+ must: number;
217
+ should: number;
218
+ nit: number;
219
+ total: number;
220
+ }
221
+ /**
222
+ * Count findings by severity level
223
+ */
224
+ declare function countFindingsBySeverity(findings: Finding[]): FindingsSummary;
225
+ /**
226
+ * Sort findings by severity
227
+ */
228
+ declare function sortFindingsBySeverity(findings: Finding[]): Finding[];
229
+
230
+ export { type Edge, type FilesConfig, type Finding, type FindingSeverity, type FlowLintConfig, type Graph, type NodeRef, type PRFile, type ReportConfig, type RuleConfig, type RuleContext$1 as RuleContext, type RuleRunner, ValidationError, countFindingsBySeverity, defaultConfig, flattenConnections, getExampleLink, isApiNode, isErrorProneNode, isMutationNode, isNotificationNode, isTerminalNode, loadConfig, parseConfig, parseN8n, runAllRules, sortFindingsBySeverity, validateConfig, validateN8nWorkflow };