@rcrsr/rill-cli 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/LICENSE +21 -0
- package/dist/check/config.d.ts +20 -0
- package/dist/check/config.d.ts.map +1 -0
- package/dist/check/config.js +151 -0
- package/dist/check/config.js.map +1 -0
- package/dist/check/fixer.d.ts +39 -0
- package/dist/check/fixer.d.ts.map +1 -0
- package/dist/check/fixer.js +119 -0
- package/dist/check/fixer.js.map +1 -0
- package/dist/check/index.d.ts +10 -0
- package/dist/check/index.d.ts.map +1 -0
- package/dist/check/index.js +21 -0
- package/dist/check/index.js.map +1 -0
- package/dist/check/rules/anti-patterns.d.ts +65 -0
- package/dist/check/rules/anti-patterns.d.ts.map +1 -0
- package/dist/check/rules/anti-patterns.js +481 -0
- package/dist/check/rules/anti-patterns.js.map +1 -0
- package/dist/check/rules/closures.d.ts +66 -0
- package/dist/check/rules/closures.d.ts.map +1 -0
- package/dist/check/rules/closures.js +370 -0
- package/dist/check/rules/closures.js.map +1 -0
- package/dist/check/rules/collections.d.ts +90 -0
- package/dist/check/rules/collections.d.ts.map +1 -0
- package/dist/check/rules/collections.js +373 -0
- package/dist/check/rules/collections.js.map +1 -0
- package/dist/check/rules/conditionals.d.ts +41 -0
- package/dist/check/rules/conditionals.d.ts.map +1 -0
- package/dist/check/rules/conditionals.js +134 -0
- package/dist/check/rules/conditionals.js.map +1 -0
- package/dist/check/rules/flow.d.ts +46 -0
- package/dist/check/rules/flow.d.ts.map +1 -0
- package/dist/check/rules/flow.js +206 -0
- package/dist/check/rules/flow.js.map +1 -0
- package/dist/check/rules/formatting.d.ts +143 -0
- package/dist/check/rules/formatting.d.ts.map +1 -0
- package/dist/check/rules/formatting.js +656 -0
- package/dist/check/rules/formatting.js.map +1 -0
- package/dist/check/rules/helpers.d.ts +26 -0
- package/dist/check/rules/helpers.d.ts.map +1 -0
- package/dist/check/rules/helpers.js +66 -0
- package/dist/check/rules/helpers.js.map +1 -0
- package/dist/check/rules/index.d.ts +21 -0
- package/dist/check/rules/index.d.ts.map +1 -0
- package/dist/check/rules/index.js +78 -0
- package/dist/check/rules/index.js.map +1 -0
- package/dist/check/rules/loops.d.ts +77 -0
- package/dist/check/rules/loops.d.ts.map +1 -0
- package/dist/check/rules/loops.js +310 -0
- package/dist/check/rules/loops.js.map +1 -0
- package/dist/check/rules/naming.d.ts +21 -0
- package/dist/check/rules/naming.d.ts.map +1 -0
- package/dist/check/rules/naming.js +174 -0
- package/dist/check/rules/naming.js.map +1 -0
- package/dist/check/rules/strings.d.ts +28 -0
- package/dist/check/rules/strings.d.ts.map +1 -0
- package/dist/check/rules/strings.js +79 -0
- package/dist/check/rules/strings.js.map +1 -0
- package/dist/check/rules/types.d.ts +41 -0
- package/dist/check/rules/types.d.ts.map +1 -0
- package/dist/check/rules/types.js +167 -0
- package/dist/check/rules/types.js.map +1 -0
- package/dist/check/types.d.ts +112 -0
- package/dist/check/types.d.ts.map +1 -0
- package/dist/check/types.js +6 -0
- package/dist/check/types.js.map +1 -0
- package/dist/check/validator.d.ts +18 -0
- package/dist/check/validator.d.ts.map +1 -0
- package/dist/check/validator.js +110 -0
- package/dist/check/validator.js.map +1 -0
- package/dist/check/visitor.d.ts +33 -0
- package/dist/check/visitor.d.ts.map +1 -0
- package/dist/check/visitor.js +259 -0
- package/dist/check/visitor.js.map +1 -0
- package/dist/cli-check.d.ts +43 -0
- package/dist/cli-check.d.ts.map +1 -0
- package/dist/cli-check.js +366 -0
- package/dist/cli-check.js.map +1 -0
- package/dist/cli-error-enrichment.d.ts +73 -0
- package/dist/cli-error-enrichment.d.ts.map +1 -0
- package/dist/cli-error-enrichment.js +205 -0
- package/dist/cli-error-enrichment.js.map +1 -0
- package/dist/cli-error-formatter.d.ts +45 -0
- package/dist/cli-error-formatter.d.ts.map +1 -0
- package/dist/cli-error-formatter.js +218 -0
- package/dist/cli-error-formatter.js.map +1 -0
- package/dist/cli-eval.d.ts +15 -0
- package/dist/cli-eval.d.ts.map +1 -0
- package/dist/cli-eval.js +116 -0
- package/dist/cli-eval.js.map +1 -0
- package/dist/cli-exec.d.ts +58 -0
- package/dist/cli-exec.d.ts.map +1 -0
- package/dist/cli-exec.js +326 -0
- package/dist/cli-exec.js.map +1 -0
- package/dist/cli-explain.d.ts +24 -0
- package/dist/cli-explain.d.ts.map +1 -0
- package/dist/cli-explain.js +68 -0
- package/dist/cli-explain.js.map +1 -0
- package/dist/cli-lsp-diagnostic.d.ts +35 -0
- package/dist/cli-lsp-diagnostic.d.ts.map +1 -0
- package/dist/cli-lsp-diagnostic.js +98 -0
- package/dist/cli-lsp-diagnostic.js.map +1 -0
- package/dist/cli-module-loader.d.ts +19 -0
- package/dist/cli-module-loader.d.ts.map +1 -0
- package/dist/cli-module-loader.js +83 -0
- package/dist/cli-module-loader.js.map +1 -0
- package/dist/cli-shared.d.ts +62 -0
- package/dist/cli-shared.d.ts.map +1 -0
- package/dist/cli-shared.js +158 -0
- package/dist/cli-shared.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +62 -0
- package/dist/cli.js.map +1 -0
- package/dist/test-internal-import.d.ts +2 -0
- package/dist/test-internal-import.d.ts.map +1 -0
- package/dist/test-internal-import.js +7 -0
- package/dist/test-internal-import.js.map +1 -0
- package/package.json +24 -0
- package/src/check/config.ts +202 -0
- package/src/check/fixer.ts +174 -0
- package/src/check/index.ts +39 -0
- package/src/check/rules/anti-patterns.ts +585 -0
- package/src/check/rules/closures.ts +445 -0
- package/src/check/rules/collections.ts +437 -0
- package/src/check/rules/conditionals.ts +155 -0
- package/src/check/rules/flow.ts +262 -0
- package/src/check/rules/formatting.ts +811 -0
- package/src/check/rules/helpers.ts +89 -0
- package/src/check/rules/index.ts +140 -0
- package/src/check/rules/loops.ts +372 -0
- package/src/check/rules/naming.ts +242 -0
- package/src/check/rules/strings.ts +104 -0
- package/src/check/rules/types.ts +214 -0
- package/src/check/types.ts +163 -0
- package/src/check/validator.ts +136 -0
- package/src/check/visitor.ts +338 -0
- package/src/cli-check.ts +456 -0
- package/src/cli-error-enrichment.ts +274 -0
- package/src/cli-error-formatter.ts +313 -0
- package/src/cli-eval.ts +145 -0
- package/src/cli-exec.ts +408 -0
- package/src/cli-explain.ts +76 -0
- package/src/cli-lsp-diagnostic.ts +132 -0
- package/src/cli-module-loader.ts +101 -0
- package/src/cli-shared.ts +187 -0
- package/tests/check/cli-check.test.ts +189 -0
- package/tests/check/config.test.ts +350 -0
- package/tests/check/fixer.test.ts +373 -0
- package/tests/check/format-diagnostics.test.ts +327 -0
- package/tests/check/rules/anti-patterns.test.ts +467 -0
- package/tests/check/rules/closures.test.ts +192 -0
- package/tests/check/rules/collections.test.ts +380 -0
- package/tests/check/rules/conditionals.test.ts +185 -0
- package/tests/check/rules/flow.test.ts +250 -0
- package/tests/check/rules/formatting.test.ts +755 -0
- package/tests/check/rules/loops.test.ts +334 -0
- package/tests/check/rules/naming.test.ts +336 -0
- package/tests/check/rules/strings.test.ts +129 -0
- package/tests/check/rules/types.test.ts +257 -0
- package/tests/check/validator.test.ts +444 -0
- package/tests/check/visitor.test.ts +171 -0
- package/tests/cli/check.test.ts +801 -0
- package/tests/cli/error-enrichment.test.ts +510 -0
- package/tests/cli/error-formatter.test.ts +631 -0
- package/tests/cli/eval.test.ts +85 -0
- package/tests/cli/exec.test.ts +537 -0
- package/tests/cli-explain.test.ts +249 -0
- package/tests/cli-lsp-diagnostic.test.ts +202 -0
- package/tests/cli-shared.test.ts +439 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow and Capture Rules Tests
|
|
3
|
+
* Verify flow and capture convention enforcement.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect } from 'vitest';
|
|
7
|
+
import { parse } from '@rcrsr/rill';
|
|
8
|
+
import { validateScript } from '../../../src/check/validator.js';
|
|
9
|
+
import type { CheckConfig } from '../../../src/check/types.js';
|
|
10
|
+
|
|
11
|
+
// ============================================================
|
|
12
|
+
// TEST HELPERS
|
|
13
|
+
// ============================================================
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create a config with flow rules enabled.
|
|
17
|
+
*/
|
|
18
|
+
function createConfig(rules: Record<string, 'on' | 'off'> = {}): CheckConfig {
|
|
19
|
+
return {
|
|
20
|
+
rules: {
|
|
21
|
+
CAPTURE_INLINE_CHAIN: 'on',
|
|
22
|
+
CAPTURE_BEFORE_BRANCH: 'on',
|
|
23
|
+
...rules,
|
|
24
|
+
},
|
|
25
|
+
severity: {},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Validate source and extract diagnostic messages.
|
|
31
|
+
*/
|
|
32
|
+
function getDiagnostics(source: string, config?: CheckConfig): string[] {
|
|
33
|
+
const ast = parse(source);
|
|
34
|
+
const diagnostics = validateScript(ast, source, config ?? createConfig());
|
|
35
|
+
return diagnostics.map((d) => d.message);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Validate source and check for violations.
|
|
40
|
+
*/
|
|
41
|
+
function hasViolations(source: string, config?: CheckConfig): boolean {
|
|
42
|
+
const ast = parse(source);
|
|
43
|
+
const diagnostics = validateScript(ast, source, config ?? createConfig());
|
|
44
|
+
return diagnostics.length > 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Validate source and get diagnostic codes.
|
|
49
|
+
*/
|
|
50
|
+
function getCodes(source: string, config?: CheckConfig): string[] {
|
|
51
|
+
const ast = parse(source);
|
|
52
|
+
const diagnostics = validateScript(ast, source, config ?? createConfig());
|
|
53
|
+
return diagnostics.map((d) => d.code);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ============================================================
|
|
57
|
+
// CAPTURE_INLINE_CHAIN TESTS
|
|
58
|
+
// ============================================================
|
|
59
|
+
|
|
60
|
+
describe('CAPTURE_INLINE_CHAIN', () => {
|
|
61
|
+
const config = createConfig({ CAPTURE_BEFORE_BRANCH: 'off' });
|
|
62
|
+
|
|
63
|
+
it('accepts inline capture with continuation', () => {
|
|
64
|
+
expect(hasViolations('prompt("test") => $raw -> log', config)).toBe(false);
|
|
65
|
+
expect(
|
|
66
|
+
hasViolations('prompt("test") => $raw -> .contains("ERROR")', config)
|
|
67
|
+
).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('accepts capture without immediate continuation', () => {
|
|
71
|
+
expect(hasViolations('prompt("test") => $raw', config)).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('detects separate capture and usage on next line', () => {
|
|
75
|
+
const source = `
|
|
76
|
+
prompt("Read file") => $raw
|
|
77
|
+
$raw -> log
|
|
78
|
+
`.trim();
|
|
79
|
+
|
|
80
|
+
const messages = getDiagnostics(source, config);
|
|
81
|
+
expect(messages.length).toBeGreaterThan(0);
|
|
82
|
+
expect(messages[0]).toContain('inline capture');
|
|
83
|
+
expect(messages[0]).toContain('=> $raw ->');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('detects separate capture followed by method chain', () => {
|
|
87
|
+
const source = `
|
|
88
|
+
checkStatus() => $result
|
|
89
|
+
$result -> .contains("OK")
|
|
90
|
+
`.trim();
|
|
91
|
+
|
|
92
|
+
const messages = getDiagnostics(source, config);
|
|
93
|
+
expect(messages.length).toBeGreaterThan(0);
|
|
94
|
+
expect(messages[0]).toContain('inline capture');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('accepts capture with different variable used next', () => {
|
|
98
|
+
const source = `
|
|
99
|
+
prompt("test") => $raw
|
|
100
|
+
$other -> log
|
|
101
|
+
`.trim();
|
|
102
|
+
|
|
103
|
+
expect(hasViolations(source, config)).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('accepts capture when next statement is not a pipe chain', () => {
|
|
107
|
+
const source = `
|
|
108
|
+
prompt("test") => $raw
|
|
109
|
+
"constant"
|
|
110
|
+
`.trim();
|
|
111
|
+
|
|
112
|
+
expect(hasViolations(source, config)).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('accepts capture at end of script', () => {
|
|
116
|
+
const source = 'prompt("test") => $raw';
|
|
117
|
+
expect(hasViolations(source, config)).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('has info severity', () => {
|
|
121
|
+
const source = `
|
|
122
|
+
prompt("test") => $raw
|
|
123
|
+
$raw -> log
|
|
124
|
+
`.trim();
|
|
125
|
+
|
|
126
|
+
const ast = parse(source);
|
|
127
|
+
const diagnostics = validateScript(ast, source, config);
|
|
128
|
+
expect(diagnostics[0]?.severity).toBe('info');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// ============================================================
|
|
133
|
+
// CAPTURE_BEFORE_BRANCH TESTS
|
|
134
|
+
// ============================================================
|
|
135
|
+
|
|
136
|
+
describe('CAPTURE_BEFORE_BRANCH', () => {
|
|
137
|
+
const config = createConfig({ CAPTURE_INLINE_CHAIN: 'off' });
|
|
138
|
+
|
|
139
|
+
it('accepts simple variable in conditional input', () => {
|
|
140
|
+
const source = `
|
|
141
|
+
checkStatus() => $result
|
|
142
|
+
$result -> .contains("OK") ? { "Success" } ! { "Failed" }
|
|
143
|
+
`.trim();
|
|
144
|
+
|
|
145
|
+
expect(hasViolations(source, config)).toBe(false);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('detects piped value used in both branches', () => {
|
|
149
|
+
const source = `
|
|
150
|
+
checkStatus() -> .contains("OK") ? {
|
|
151
|
+
$ -> log
|
|
152
|
+
} ! {
|
|
153
|
+
$ -> log
|
|
154
|
+
}
|
|
155
|
+
`.trim();
|
|
156
|
+
|
|
157
|
+
const messages = getDiagnostics(source, config);
|
|
158
|
+
expect(messages.length).toBeGreaterThan(0);
|
|
159
|
+
expect(messages[0]).toContain('capturing value before conditional');
|
|
160
|
+
expect(messages[0]).toContain('multiple branches');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('accepts conditional with single branch', () => {
|
|
164
|
+
const source = `
|
|
165
|
+
checkStatus() -> .contains("OK") ? { $ -> log }
|
|
166
|
+
`.trim();
|
|
167
|
+
|
|
168
|
+
expect(hasViolations(source, config)).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('accepts conditional without input expression', () => {
|
|
172
|
+
const source = `
|
|
173
|
+
true ? { "yes" } ! { "no" }
|
|
174
|
+
`.trim();
|
|
175
|
+
|
|
176
|
+
expect(hasViolations(source, config)).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('accepts branches that do not reference piped value', () => {
|
|
180
|
+
const source = `
|
|
181
|
+
checkStatus() -> .contains("OK") ? { "Success" } ! { "Failed" }
|
|
182
|
+
`.trim();
|
|
183
|
+
|
|
184
|
+
expect(hasViolations(source, config)).toBe(false);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('detects value used in then branch only', () => {
|
|
188
|
+
const source = `
|
|
189
|
+
checkStatus() -> .contains("OK") ? {
|
|
190
|
+
$ -> log
|
|
191
|
+
} ! {
|
|
192
|
+
"other"
|
|
193
|
+
}
|
|
194
|
+
`.trim();
|
|
195
|
+
|
|
196
|
+
// Should not trigger - only in one branch
|
|
197
|
+
expect(hasViolations(source, config)).toBe(false);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('has info severity', () => {
|
|
201
|
+
const source = `
|
|
202
|
+
checkStatus() -> .contains("OK") ? {
|
|
203
|
+
$ -> log
|
|
204
|
+
} ! {
|
|
205
|
+
$ -> log
|
|
206
|
+
}
|
|
207
|
+
`.trim();
|
|
208
|
+
|
|
209
|
+
const ast = parse(source);
|
|
210
|
+
const diagnostics = validateScript(ast, source, config);
|
|
211
|
+
if (diagnostics.length > 0) {
|
|
212
|
+
expect(diagnostics[0]?.severity).toBe('info');
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// ============================================================
|
|
218
|
+
// COMBINED RULES TESTS
|
|
219
|
+
// ============================================================
|
|
220
|
+
|
|
221
|
+
describe('flow rules combined', () => {
|
|
222
|
+
it('can detect both rules in same source', () => {
|
|
223
|
+
const source = `
|
|
224
|
+
prompt("test") => $raw
|
|
225
|
+
$raw -> .contains("OK") ? {
|
|
226
|
+
$ -> log
|
|
227
|
+
} ! {
|
|
228
|
+
$ -> log
|
|
229
|
+
}
|
|
230
|
+
`.trim();
|
|
231
|
+
|
|
232
|
+
const codes = getCodes(source);
|
|
233
|
+
// May trigger CAPTURE_INLINE_CHAIN
|
|
234
|
+
expect(codes.length).toBeGreaterThan(0);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('respects rule configuration', () => {
|
|
238
|
+
const source = `
|
|
239
|
+
prompt("test") => $raw
|
|
240
|
+
$raw -> log
|
|
241
|
+
`.trim();
|
|
242
|
+
|
|
243
|
+
const disabledConfig = createConfig({
|
|
244
|
+
CAPTURE_INLINE_CHAIN: 'off',
|
|
245
|
+
CAPTURE_BEFORE_BRANCH: 'off',
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
expect(hasViolations(source, disabledConfig)).toBe(false);
|
|
249
|
+
});
|
|
250
|
+
});
|