cloudflare-expression-lint 0.1.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/dist/cli.js ADDED
@@ -0,0 +1,363 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI for cloudflare-expression-lint.
4
+ *
5
+ * Usage:
6
+ * cf-expr-lint [options] <files...>
7
+ * cf-expr-lint --expression "http.host eq \"test.com\""
8
+ * cf-expr-lint --config .cf-expr-lint.json config/**\/*.yaml
9
+ *
10
+ * Options:
11
+ * --expression, -e Validate a single expression string
12
+ * --stdin Read expression from stdin
13
+ * --type, -t Expression type: filter, rewrite_url, rewrite_header, redirect_target
14
+ * --phase, -p Cloudflare phase (e.g., http_request_firewall_custom)
15
+ * --config, -c Path to config file (.json) with custom mappings
16
+ * --expr-key Add expression key mapping: key:type[:phase]
17
+ * --phase-map Add phase mapping: yaml_key:phase_name
18
+ * --format, -f Output format: text (default), json
19
+ * --quiet, -q Only output errors (suppress warnings)
20
+ * --help, -h Show this help message
21
+ */
22
+ import { readFileSync, existsSync } from 'node:fs';
23
+ import { resolve } from 'node:path';
24
+ import { glob } from 'glob';
25
+ import { validate } from './validator.js';
26
+ import { scanYaml } from './yaml-scanner.js';
27
+ function parseArgs(argv) {
28
+ const opts = {
29
+ files: [],
30
+ stdin: false,
31
+ type: 'filter',
32
+ exprKeys: [],
33
+ phaseMaps: [],
34
+ format: 'text',
35
+ quiet: false,
36
+ help: false,
37
+ };
38
+ let i = 0;
39
+ while (i < argv.length) {
40
+ const arg = argv[i];
41
+ switch (arg) {
42
+ case '--help':
43
+ case '-h':
44
+ opts.help = true;
45
+ break;
46
+ case '--expression':
47
+ case '-e':
48
+ opts.expression = argv[++i];
49
+ break;
50
+ case '--stdin':
51
+ opts.stdin = true;
52
+ break;
53
+ case '--type':
54
+ case '-t':
55
+ opts.type = argv[++i];
56
+ break;
57
+ case '--phase':
58
+ case '-p':
59
+ opts.phase = argv[++i];
60
+ break;
61
+ case '--config':
62
+ case '-c':
63
+ opts.configFile = argv[++i];
64
+ break;
65
+ case '--expr-key': {
66
+ // Format: key:type[:phase]
67
+ const val = argv[++i];
68
+ const parts = val.split(':');
69
+ if (parts.length >= 2) {
70
+ opts.exprKeys.push({
71
+ key: parts[0],
72
+ type: parts[1],
73
+ phase: parts[2],
74
+ });
75
+ }
76
+ break;
77
+ }
78
+ case '--phase-map': {
79
+ // Format: yaml_key:phase_name
80
+ const val = argv[++i];
81
+ const colonIdx = val.indexOf(':');
82
+ if (colonIdx > 0) {
83
+ opts.phaseMaps.push({
84
+ yamlKey: val.substring(0, colonIdx),
85
+ phase: val.substring(colonIdx + 1),
86
+ });
87
+ }
88
+ break;
89
+ }
90
+ case '--format':
91
+ case '-f':
92
+ opts.format = argv[++i];
93
+ break;
94
+ case '--quiet':
95
+ case '-q':
96
+ opts.quiet = true;
97
+ break;
98
+ default:
99
+ if (!arg.startsWith('-')) {
100
+ opts.files.push(arg);
101
+ }
102
+ break;
103
+ }
104
+ i++;
105
+ }
106
+ return opts;
107
+ }
108
+ /**
109
+ * Build ScannerOptions from CLI flags and config file.
110
+ */
111
+ function buildScannerOptions(opts) {
112
+ const scannerOpts = {};
113
+ let hasOptions = false;
114
+ // Load config file if specified (or auto-detect)
115
+ const configPath = opts.configFile ?? findConfigFile();
116
+ if (configPath) {
117
+ try {
118
+ const raw = readFileSync(resolve(configPath), 'utf-8');
119
+ const config = JSON.parse(raw);
120
+ if (config.expressionKeys) {
121
+ scannerOpts.expressionKeys = config.expressionKeys;
122
+ hasOptions = true;
123
+ }
124
+ if (config.phaseMappings) {
125
+ scannerOpts.phaseMappings = config.phaseMappings;
126
+ hasOptions = true;
127
+ }
128
+ }
129
+ catch (err) {
130
+ console.error(`Error reading config file ${configPath}: ${err instanceof Error ? err.message : err}`);
131
+ process.exit(1);
132
+ }
133
+ }
134
+ // Apply --expr-key flags (merge with config file)
135
+ if (opts.exprKeys.length > 0) {
136
+ scannerOpts.expressionKeys = scannerOpts.expressionKeys ?? {};
137
+ for (const ek of opts.exprKeys) {
138
+ scannerOpts.expressionKeys[ek.key] = {
139
+ type: ek.type,
140
+ phaseHint: ek.phase,
141
+ };
142
+ }
143
+ hasOptions = true;
144
+ }
145
+ // Apply --phase-map flags (merge with config file)
146
+ if (opts.phaseMaps.length > 0) {
147
+ scannerOpts.phaseMappings = scannerOpts.phaseMappings ?? {};
148
+ for (const pm of opts.phaseMaps) {
149
+ scannerOpts.phaseMappings[pm.yamlKey] = pm.phase;
150
+ }
151
+ hasOptions = true;
152
+ }
153
+ return hasOptions ? scannerOpts : undefined;
154
+ }
155
+ /**
156
+ * Auto-detect config file in the current directory.
157
+ */
158
+ function findConfigFile() {
159
+ const candidates = [
160
+ '.cf-expr-lint.json',
161
+ '.cf-expr-lint.yaml',
162
+ 'cf-expr-lint.config.json',
163
+ ];
164
+ for (const name of candidates) {
165
+ if (existsSync(name))
166
+ return name;
167
+ }
168
+ return undefined;
169
+ }
170
+ function printHelp() {
171
+ console.log(`
172
+ cloudflare-expression-lint - Validate Cloudflare Rules Language expressions
173
+
174
+ Usage:
175
+ cf-expr-lint [options] <files...>
176
+ cf-expr-lint -e "http.host eq \\"test.com\\""
177
+ echo 'http.host eq "test"' | cf-expr-lint --stdin
178
+
179
+ Options:
180
+ --expression, -e <expr> Validate a single expression string
181
+ --stdin Read expression from stdin
182
+ --type, -t <type> Expression type: filter (default), rewrite_url,
183
+ rewrite_header, redirect_target
184
+ --phase, -p <phase> Cloudflare phase for field validation
185
+ --config, -c <file> Config file with custom mappings (JSON)
186
+ --expr-key <key:type[:phase]>
187
+ Add a YAML key that contains an expression.
188
+ Can be specified multiple times.
189
+ --phase-map <yaml_key:phase>
190
+ Map a YAML parent key to a Cloudflare phase.
191
+ Can be specified multiple times.
192
+ --format, -f <fmt> Output format: text (default), json
193
+ --quiet, -q Only show errors (suppress warnings/info)
194
+ --help, -h Show this help
195
+
196
+ Config File:
197
+ Place a .cf-expr-lint.json in your project root (auto-detected), or
198
+ specify with --config. Example:
199
+
200
+ {
201
+ "expressionKeys": {
202
+ "rewrite_expression": { "type": "rewrite_url", "phaseHint": "http_request_transform" },
203
+ "source_url_expression": { "type": "filter", "phaseHint": "http_request_dynamic_redirect" },
204
+ "redirect_target": { "type": "redirect_target" }
205
+ },
206
+ "phaseMappings": {
207
+ "waf_rules": "http_request_firewall_custom",
208
+ "transform_rules": "http_request_late_transform",
209
+ "url_rewrite_rules": "http_request_transform"
210
+ }
211
+ }
212
+
213
+ Custom mappings are merged with built-in defaults. The built-in
214
+ expression key is "expression" (the Terraform provider attribute).
215
+ Built-in phase mappings include Cloudflare phase names and common
216
+ shorthands like "cache_rules" and "single_redirects".
217
+
218
+ Examples:
219
+ # Scan with defaults (detects "expression" keys, infers phase from context)
220
+ cf-expr-lint config/**/*.yaml
221
+
222
+ # Scan with custom expression keys and phase mappings
223
+ cf-expr-lint \\
224
+ --expr-key rewrite_expression:rewrite_url:http_request_transform \\
225
+ --expr-key source_url_expression:filter:http_request_dynamic_redirect \\
226
+ --phase-map waf_rules:http_request_firewall_custom \\
227
+ config/**/*.yaml
228
+
229
+ # Scan with config file
230
+ cf-expr-lint --config .cf-expr-lint.json config/**/*.yaml
231
+
232
+ # Validate a single expression
233
+ cf-expr-lint -e '(http.host eq "test.com")'
234
+ cf-expr-lint -e 'regex_replace(http.request.uri.path, "^/old/", "/")' -t rewrite_url
235
+ cf-expr-lint -e 'http.response.code eq 200' -p http_request_firewall_custom
236
+ `);
237
+ }
238
+ function formatDiagnostic(d) {
239
+ const prefix = d.severity === 'error' ? '✗' : d.severity === 'warning' ? '⚠' : 'ℹ';
240
+ const pos = d.position !== undefined ? ` (pos ${d.position})` : '';
241
+ return ` ${prefix} [${d.code}]${pos}: ${d.message}`;
242
+ }
243
+ async function main() {
244
+ const opts = parseArgs(process.argv.slice(2));
245
+ if (opts.help) {
246
+ printHelp();
247
+ process.exit(0);
248
+ }
249
+ let hasErrors = false;
250
+ const jsonResults = [];
251
+ // ── Single expression mode ────────────────────────────────────────
252
+ if (opts.expression || opts.stdin) {
253
+ let expr;
254
+ if (opts.stdin) {
255
+ expr = readFileSync(0, 'utf-8').trim();
256
+ }
257
+ else {
258
+ expr = opts.expression;
259
+ }
260
+ const ctx = {
261
+ expressionType: opts.type,
262
+ phase: opts.phase,
263
+ };
264
+ const result = validate(expr, ctx);
265
+ if (opts.format === 'json') {
266
+ console.log(JSON.stringify(result, null, 2));
267
+ }
268
+ else {
269
+ const filteredDiags = opts.quiet
270
+ ? result.diagnostics.filter(d => d.severity === 'error')
271
+ : result.diagnostics;
272
+ if (filteredDiags.length === 0) {
273
+ console.log(`✓ Expression is valid`);
274
+ }
275
+ else {
276
+ console.log(`Expression: ${expr.substring(0, 100)}${expr.length > 100 ? '...' : ''}`);
277
+ for (const d of filteredDiags) {
278
+ console.log(formatDiagnostic(d));
279
+ }
280
+ }
281
+ if (!result.valid)
282
+ hasErrors = true;
283
+ }
284
+ process.exit(hasErrors ? 1 : 0);
285
+ }
286
+ // ── File scanning mode ────────────────────────────────────────────
287
+ if (opts.files.length === 0) {
288
+ printHelp();
289
+ process.exit(1);
290
+ }
291
+ const scannerOpts = buildScannerOptions(opts);
292
+ // Expand globs
293
+ const expandedFiles = [];
294
+ for (const pattern of opts.files) {
295
+ const matches = await glob(pattern);
296
+ expandedFiles.push(...matches);
297
+ }
298
+ if (expandedFiles.length === 0) {
299
+ console.error('No files matched the given patterns');
300
+ process.exit(1);
301
+ }
302
+ let totalExpressions = 0;
303
+ let totalErrors = 0;
304
+ let totalWarnings = 0;
305
+ for (const file of expandedFiles) {
306
+ const absPath = resolve(file);
307
+ let content;
308
+ try {
309
+ content = readFileSync(absPath, 'utf-8');
310
+ }
311
+ catch (err) {
312
+ console.error(`Error reading ${file}: ${err instanceof Error ? err.message : err}`);
313
+ hasErrors = true;
314
+ continue;
315
+ }
316
+ const scanResult = scanYaml(content, file, scannerOpts);
317
+ if (scanResult.parseError) {
318
+ console.error(`Error parsing ${file}: ${scanResult.parseError}`);
319
+ hasErrors = true;
320
+ continue;
321
+ }
322
+ if (opts.format === 'json') {
323
+ jsonResults.push(scanResult);
324
+ continue;
325
+ }
326
+ for (const expr of scanResult.expressions) {
327
+ totalExpressions++;
328
+ const filteredDiags = opts.quiet
329
+ ? expr.result.diagnostics.filter(d => d.severity === 'error')
330
+ : expr.result.diagnostics;
331
+ const errors = expr.result.diagnostics.filter(d => d.severity === 'error').length;
332
+ const warnings = expr.result.diagnostics.filter(d => d.severity === 'warning').length;
333
+ totalErrors += errors;
334
+ totalWarnings += warnings;
335
+ if (filteredDiags.length > 0) {
336
+ console.log(`\n${file} → ${expr.yamlPath}`);
337
+ console.log(` Expression: ${expr.expression.substring(0, 120)}${expr.expression.length > 120 ? '...' : ''}`);
338
+ for (const d of filteredDiags) {
339
+ console.log(formatDiagnostic(d));
340
+ }
341
+ }
342
+ if (!expr.result.valid)
343
+ hasErrors = true;
344
+ }
345
+ }
346
+ if (opts.format === 'json') {
347
+ console.log(JSON.stringify(jsonResults, null, 2));
348
+ }
349
+ else {
350
+ console.log(`\n─────────────────────────────────────────────`);
351
+ console.log(`Scanned ${expandedFiles.length} files, ${totalExpressions} expressions`);
352
+ console.log(` ${totalErrors} errors, ${totalWarnings} warnings`);
353
+ if (!hasErrors) {
354
+ console.log(` ✓ All expressions valid`);
355
+ }
356
+ }
357
+ process.exit(hasErrors ? 1 : 0);
358
+ }
359
+ main().catch(err => {
360
+ console.error(err);
361
+ process.exit(1);
362
+ });
363
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAkB7C,SAAS,SAAS,CAAC,IAAc;IAC/B,MAAM,IAAI,GAAe;QACvB,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,EAAE;QACb,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,KAAK;KACZ,CAAC;IAEF,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,QAAQ,CAAC;YACd,KAAK,IAAI;gBACP,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBACjB,MAAM;YACR,KAAK,cAAc,CAAC;YACpB,KAAK,IAAI;gBACP,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC5B,MAAM;YACR,KAAK,SAAS;gBACZ,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;gBAClB,MAAM;YACR,KAAK,QAAQ,CAAC;YACd,KAAK,IAAI;gBACP,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAmB,CAAC;gBACxC,MAAM;YACR,KAAK,SAAS,CAAC;YACf,KAAK,IAAI;gBACP,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACvB,MAAM;YACR,KAAK,UAAU,CAAC;YAChB,KAAK,IAAI;gBACP,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC5B,MAAM;YACR,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,2BAA2B;gBAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACtB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC7B,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;oBACtB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;wBACjB,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;wBACb,IAAI,EAAE,KAAK,CAAC,CAAC,CAAmB;wBAChC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;qBAChB,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM;YACR,CAAC;YACD,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,8BAA8B;gBAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACtB,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAClC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;oBACjB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;wBAClB,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC;wBACnC,KAAK,EAAE,GAAG,CAAC,SAAS,CAAC,QAAQ,GAAG,CAAC,CAAC;qBACnC,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM;YACR,CAAC;YACD,KAAK,UAAU,CAAC;YAChB,KAAK,IAAI;gBACP,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAoB,CAAC;gBAC3C,MAAM;YACR,KAAK,SAAS,CAAC;YACf,KAAK,IAAI;gBACP,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;gBAClB,MAAM;YACR;gBACE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBACzB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACvB,CAAC;gBACD,MAAM;QACV,CAAC;QACD,CAAC,EAAE,CAAC;IACN,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAAC,IAAgB;IAC3C,MAAM,WAAW,GAAmB,EAAE,CAAC;IACvC,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,iDAAiD;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,cAAc,EAAE,CAAC;IACvD,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;YACvD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAG5B,CAAC;YACF,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;gBAC1B,WAAW,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;gBACnD,UAAU,GAAG,IAAI,CAAC;YACpB,CAAC;YACD,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;gBACzB,WAAW,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;gBACjD,UAAU,GAAG,IAAI,CAAC;YACpB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,6BAA6B,UAAU,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACtG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,kDAAkD;IAClD,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,WAAW,CAAC,cAAc,GAAG,WAAW,CAAC,cAAc,IAAI,EAAE,CAAC;QAC9D,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC/B,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG;gBACnC,IAAI,EAAE,EAAE,CAAC,IAAI;gBACb,SAAS,EAAE,EAAE,CAAC,KAAK;aACpB,CAAC;QACJ,CAAC;QACD,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,mDAAmD;IACnD,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,WAAW,CAAC,aAAa,GAAG,WAAW,CAAC,aAAa,IAAI,EAAE,CAAC;QAC5D,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,WAAW,CAAC,aAAa,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;QACnD,CAAC;QACD,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,OAAO,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,SAAS,cAAc;IACrB,MAAM,UAAU,GAAG;QACjB,oBAAoB;QACpB,oBAAoB;QACpB,0BAA0B;KAC3B,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IACpC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiEb,CAAC,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,CAAa;IACrC,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACnF,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,OAAO,KAAK,MAAM,KAAK,CAAC,CAAC,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;AACvD,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,WAAW,GAAc,EAAE,CAAC;IAElC,qEAAqE;IACrE,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAClC,IAAI,IAAY,CAAC;QACjB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,IAAI,GAAG,IAAI,CAAC,UAAW,CAAC;QAC1B,CAAC;QAED,MAAM,GAAG,GAAsB;YAC7B,cAAc,EAAE,IAAI,CAAC,IAAI;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC;QAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAEnC,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK;gBAC9B,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC;gBACxD,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC;YAEvB,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/B,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;YACvC,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACtF,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;oBAC9B,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,KAAK;gBAAE,SAAS,GAAG,IAAI,CAAC;QACtC,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,qEAAqE;IACrE,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,WAAW,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAE9C,eAAe;IACf,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,aAAa,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,aAAa,GAAG,CAAC,CAAC;IAEtB,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,iBAAiB,IAAI,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACpF,SAAS,GAAG,IAAI,CAAC;YACjB,SAAS;QACX,CAAC;QAED,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;QAExD,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,CAAC,iBAAiB,IAAI,KAAK,UAAU,CAAC,UAAU,EAAE,CAAC,CAAC;YACjE,SAAS,GAAG,IAAI,CAAC;YACjB,SAAS;QACX,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC3B,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC7B,SAAS;QACX,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;YAC1C,gBAAgB,EAAE,CAAC;YACnB,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK;gBAC9B,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC;gBAC7D,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;YAE5B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;YAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;YACtF,WAAW,IAAI,MAAM,CAAC;YACtB,aAAa,IAAI,QAAQ,CAAC;YAE1B,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC5C,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC9G,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;oBAC9B,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK;gBAAE,SAAS,GAAG,IAAI,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,WAAW,aAAa,CAAC,MAAM,WAAW,gBAAgB,cAAc,CAAC,CAAC;QACtF,OAAO,CAAC,GAAG,CAAC,KAAK,WAAW,YAAY,aAAa,WAAW,CAAC,CAAC;QAClE,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;IACjB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * ESLint plugin adapter for cloudflare-expression-lint.
3
+ *
4
+ * Provides a `validate-expression` rule that hooks into YAML AST nodes
5
+ * (via yaml-eslint-parser) and JSON files to detect and validate
6
+ * Cloudflare Rules Language expressions.
7
+ *
8
+ * yaml-eslint-parser and eslint are optional peer dependencies.
9
+ */
10
+ import type { ExpressionType, DiagnosticSeverity } from './types.js';
11
+ /**
12
+ * Default mapping of YAML/JSON key names to expression types.
13
+ */
14
+ export declare const DEFAULT_EXPRESSION_KEYS: Record<string, ExpressionType>;
15
+ /**
16
+ * Check whether a given key name is one of the known expression keys.
17
+ */
18
+ export declare function isExpressionKey(key: string, customMappings?: Record<string, ExpressionType>): boolean;
19
+ /**
20
+ * Infer the ExpressionType for a given key name.
21
+ * Custom mappings take precedence over defaults.
22
+ */
23
+ export declare function inferExpressionType(key: string, customMappings?: Record<string, ExpressionType>): ExpressionType;
24
+ /**
25
+ * Infer a phase hint from the key name, if one exists.
26
+ */
27
+ export declare function inferPhaseFromKey(key: string, customPhaseMappings?: Record<string, string>): string | undefined;
28
+ declare function diagnosticSeverityToEslint(severity: DiagnosticSeverity): 1 | 2;
29
+ export interface ValidateExpressionRuleOptions {
30
+ /** Custom key name to ExpressionType mappings */
31
+ customKeyMappings?: Record<string, ExpressionType>;
32
+ /** Custom key name to phase mappings */
33
+ customPhaseMappings?: Record<string, string>;
34
+ /** Default phase when none can be inferred */
35
+ defaultPhase?: string;
36
+ }
37
+ /**
38
+ * Creates the ESLint rule object for validate-expression.
39
+ * Factored out so it can be tested without requiring ESLint.
40
+ */
41
+ export declare function createValidateExpressionRule(): {
42
+ meta: Record<string, unknown>;
43
+ create: (context: any) => Record<string, (node: any) => void>;
44
+ };
45
+ declare const rule: {
46
+ meta: Record<string, unknown>;
47
+ create: (context: any) => Record<string, (node: any) => void>;
48
+ };
49
+ declare const plugin: {
50
+ rules: {
51
+ 'validate-expression': {
52
+ meta: Record<string, unknown>;
53
+ create: (context: any) => Record<string, (node: any) => void>;
54
+ };
55
+ };
56
+ configs: {
57
+ recommended: {
58
+ plugins: string[];
59
+ rules: {
60
+ 'cloudflare-expression-lint/validate-expression': string;
61
+ };
62
+ };
63
+ };
64
+ };
65
+ export default plugin;
66
+ export { plugin, rule as validateExpressionRule };
67
+ export { diagnosticSeverityToEslint };
@@ -0,0 +1,211 @@
1
+ /**
2
+ * ESLint plugin adapter for cloudflare-expression-lint.
3
+ *
4
+ * Provides a `validate-expression` rule that hooks into YAML AST nodes
5
+ * (via yaml-eslint-parser) and JSON files to detect and validate
6
+ * Cloudflare Rules Language expressions.
7
+ *
8
+ * yaml-eslint-parser and eslint are optional peer dependencies.
9
+ */
10
+ import { validate } from './validator.js';
11
+ // ── Expression key detection ────────────────────────────────────────────
12
+ /**
13
+ * Default mapping of YAML/JSON key names to expression types.
14
+ */
15
+ export const DEFAULT_EXPRESSION_KEYS = {
16
+ expression: 'filter',
17
+ rewrite_expression: 'rewrite_url',
18
+ source_url_expression: 'filter',
19
+ target_url_expression: 'redirect_target',
20
+ };
21
+ /**
22
+ * Check whether a given key name is one of the known expression keys.
23
+ */
24
+ export function isExpressionKey(key, customMappings) {
25
+ if (customMappings && key in customMappings)
26
+ return true;
27
+ return key in DEFAULT_EXPRESSION_KEYS;
28
+ }
29
+ /**
30
+ * Infer the ExpressionType for a given key name.
31
+ * Custom mappings take precedence over defaults.
32
+ */
33
+ export function inferExpressionType(key, customMappings) {
34
+ if (customMappings && key in customMappings) {
35
+ return customMappings[key];
36
+ }
37
+ return DEFAULT_EXPRESSION_KEYS[key] ?? 'filter';
38
+ }
39
+ // ── Phase inference from key name ───────────────────────────────────────
40
+ /**
41
+ * Mapping from expression key names to a phase hint.
42
+ */
43
+ const KEY_PHASE_HINTS = {
44
+ rewrite_expression: 'http_request_transform',
45
+ source_url_expression: 'http_request_dynamic_redirect',
46
+ target_url_expression: 'http_request_dynamic_redirect',
47
+ };
48
+ /**
49
+ * Infer a phase hint from the key name, if one exists.
50
+ */
51
+ export function inferPhaseFromKey(key, customPhaseMappings) {
52
+ if (customPhaseMappings && key in customPhaseMappings) {
53
+ return customPhaseMappings[key];
54
+ }
55
+ return KEY_PHASE_HINTS[key];
56
+ }
57
+ // ── ESLint severity mapping ─────────────────────────────────────────────
58
+ function diagnosticSeverityToEslint(severity) {
59
+ // ESLint only has warn (1) and error (2). Map info to warn.
60
+ switch (severity) {
61
+ case 'error':
62
+ return 2;
63
+ case 'warning':
64
+ case 'info':
65
+ return 1;
66
+ }
67
+ }
68
+ // ── ESLint rule definition ──────────────────────────────────────────────
69
+ /**
70
+ * Creates the ESLint rule object for validate-expression.
71
+ * Factored out so it can be tested without requiring ESLint.
72
+ */
73
+ export function createValidateExpressionRule() {
74
+ return {
75
+ meta: {
76
+ type: 'problem',
77
+ docs: {
78
+ description: 'Validate Cloudflare Rules Language expressions',
79
+ category: 'Possible Errors',
80
+ recommended: true,
81
+ },
82
+ schema: [
83
+ {
84
+ type: 'object',
85
+ properties: {
86
+ customKeyMappings: {
87
+ type: 'object',
88
+ additionalProperties: { type: 'string' },
89
+ },
90
+ customPhaseMappings: {
91
+ type: 'object',
92
+ additionalProperties: { type: 'string' },
93
+ },
94
+ defaultPhase: { type: 'string' },
95
+ },
96
+ additionalProperties: false,
97
+ },
98
+ ],
99
+ messages: {
100
+ expressionDiagnostic: '{{message}}',
101
+ },
102
+ },
103
+ create(context) {
104
+ const options = context.options?.[0] ?? {};
105
+ const { customKeyMappings, customPhaseMappings, defaultPhase } = options;
106
+ const filename = context.getFilename?.() ?? context.filename ?? '';
107
+ const visitors = {};
108
+ // ── YAML file handling (requires yaml-eslint-parser) ──────────
109
+ if (filename.endsWith('.yaml') || filename.endsWith('.yml')) {
110
+ visitors['YAMLPair'] = (node) => {
111
+ // YAMLPair has .key and .value nodes
112
+ const keyNode = node.key;
113
+ const valueNode = node.value;
114
+ if (!keyNode || !valueNode)
115
+ return;
116
+ // Get the key string
117
+ const keyName = keyNode.value ?? keyNode.raw;
118
+ if (typeof keyName !== 'string')
119
+ return;
120
+ // Check if this is an expression key
121
+ if (!isExpressionKey(keyName, customKeyMappings))
122
+ return;
123
+ // Get the value string
124
+ const valueStr = valueNode.value ?? valueNode.raw;
125
+ if (typeof valueStr !== 'string')
126
+ return;
127
+ const expression = valueStr.trim();
128
+ if (!expression)
129
+ return;
130
+ // Determine expression type and phase
131
+ const expressionType = inferExpressionType(keyName, customKeyMappings);
132
+ const phase = inferPhaseFromKey(keyName, customPhaseMappings) ?? defaultPhase;
133
+ // Validate
134
+ const result = validate(expression, {
135
+ expressionType,
136
+ phase,
137
+ allowPlaceholders: true,
138
+ });
139
+ // Report diagnostics
140
+ for (const diagnostic of result.diagnostics) {
141
+ context.report({
142
+ node: valueNode,
143
+ messageId: 'expressionDiagnostic',
144
+ data: { message: `[${diagnostic.code}] ${diagnostic.message}` },
145
+ // Use ESLint severity mapping (note: ESLint rule severity is
146
+ // configured at the config level, but we include our severity
147
+ // in the message for clarity)
148
+ });
149
+ }
150
+ };
151
+ }
152
+ // ── JSON file handling ────────────────────────────────────────
153
+ if (filename.endsWith('.json')) {
154
+ visitors['Property'] = (node) => {
155
+ const keyNode = node.key;
156
+ const valueNode = node.value;
157
+ if (!keyNode || !valueNode)
158
+ return;
159
+ // Get key name from JSON AST
160
+ const keyName = keyNode.value ?? keyNode.name;
161
+ if (typeof keyName !== 'string')
162
+ return;
163
+ if (!isExpressionKey(keyName, customKeyMappings))
164
+ return;
165
+ // Value must be a string literal
166
+ if (valueNode.type !== 'Literal' || typeof valueNode.value !== 'string')
167
+ return;
168
+ const expression = valueNode.value.trim();
169
+ if (!expression)
170
+ return;
171
+ const expressionType = inferExpressionType(keyName, customKeyMappings);
172
+ const phase = inferPhaseFromKey(keyName, customPhaseMappings) ?? defaultPhase;
173
+ const result = validate(expression, {
174
+ expressionType,
175
+ phase,
176
+ allowPlaceholders: true,
177
+ });
178
+ for (const diagnostic of result.diagnostics) {
179
+ context.report({
180
+ node: valueNode,
181
+ messageId: 'expressionDiagnostic',
182
+ data: { message: `[${diagnostic.code}] ${diagnostic.message}` },
183
+ });
184
+ }
185
+ };
186
+ }
187
+ return visitors;
188
+ },
189
+ };
190
+ }
191
+ // ── Plugin export ───────────────────────────────────────────────────────
192
+ const rule = createValidateExpressionRule();
193
+ const plugin = {
194
+ rules: {
195
+ 'validate-expression': rule,
196
+ },
197
+ configs: {
198
+ recommended: {
199
+ plugins: ['cloudflare-expression-lint'],
200
+ rules: {
201
+ 'cloudflare-expression-lint/validate-expression': 'warn',
202
+ },
203
+ },
204
+ },
205
+ };
206
+ export default plugin;
207
+ // Also export for named imports
208
+ export { plugin, rule as validateExpressionRule };
209
+ // Re-export the severity mapper for testing
210
+ export { diagnosticSeverityToEslint };
211
+ //# sourceMappingURL=eslint-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eslint-plugin.js","sourceRoot":"","sources":["../src/eslint-plugin.ts"],"names":[],"mappings":"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"}