@birdcc/linter 0.0.1-alpha.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.
Files changed (60) hide show
  1. package/.oxfmtrc.json +16 -0
  2. package/LICENSE +674 -0
  3. package/README.md +210 -0
  4. package/dist/index.d.ts +21 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +93 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/rules/bgp.d.ts +5 -0
  9. package/dist/rules/bgp.d.ts.map +1 -0
  10. package/dist/rules/bgp.js +131 -0
  11. package/dist/rules/bgp.js.map +1 -0
  12. package/dist/rules/catalog.d.ts +14 -0
  13. package/dist/rules/catalog.d.ts.map +1 -0
  14. package/dist/rules/catalog.js +61 -0
  15. package/dist/rules/catalog.js.map +1 -0
  16. package/dist/rules/cfg.d.ts +5 -0
  17. package/dist/rules/cfg.d.ts.map +1 -0
  18. package/dist/rules/cfg.js +264 -0
  19. package/dist/rules/cfg.js.map +1 -0
  20. package/dist/rules/net.d.ts +5 -0
  21. package/dist/rules/net.d.ts.map +1 -0
  22. package/dist/rules/net.js +140 -0
  23. package/dist/rules/net.js.map +1 -0
  24. package/dist/rules/normalize.d.ts +6 -0
  25. package/dist/rules/normalize.d.ts.map +1 -0
  26. package/dist/rules/normalize.js +65 -0
  27. package/dist/rules/normalize.js.map +1 -0
  28. package/dist/rules/ospf.d.ts +5 -0
  29. package/dist/rules/ospf.d.ts.map +1 -0
  30. package/dist/rules/ospf.js +136 -0
  31. package/dist/rules/ospf.js.map +1 -0
  32. package/dist/rules/shared.d.ts +46 -0
  33. package/dist/rules/shared.d.ts.map +1 -0
  34. package/dist/rules/shared.js +184 -0
  35. package/dist/rules/shared.js.map +1 -0
  36. package/dist/rules/sym.d.ts +5 -0
  37. package/dist/rules/sym.d.ts.map +1 -0
  38. package/dist/rules/sym.js +188 -0
  39. package/dist/rules/sym.js.map +1 -0
  40. package/dist/rules/type.d.ts +5 -0
  41. package/dist/rules/type.d.ts.map +1 -0
  42. package/dist/rules/type.js +130 -0
  43. package/dist/rules/type.js.map +1 -0
  44. package/package.json +41 -0
  45. package/src/index.ts +155 -0
  46. package/src/rules/bgp.ts +239 -0
  47. package/src/rules/catalog.ts +80 -0
  48. package/src/rules/cfg.ts +562 -0
  49. package/src/rules/net.ts +262 -0
  50. package/src/rules/normalize.ts +90 -0
  51. package/src/rules/ospf.ts +221 -0
  52. package/src/rules/shared.ts +354 -0
  53. package/src/rules/sym.ts +342 -0
  54. package/src/rules/type.ts +210 -0
  55. package/test/linter.bgp-ospf.test.ts +129 -0
  56. package/test/linter.migration.test.ts +66 -0
  57. package/test/linter.net-type.test.ts +132 -0
  58. package/test/linter.sym-cfg.test.ts +224 -0
  59. package/test/linter.test.ts +21 -0
  60. package/tsconfig.json +8 -0
@@ -0,0 +1,224 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { lintBirdConfig } from "../src/index.js";
3
+
4
+ const codesOf = async (text: string): Promise<string[]> => {
5
+ const result = await lintBirdConfig(text);
6
+ return result.diagnostics.map((item) => item.code);
7
+ };
8
+
9
+ describe("@birdcc/linter sym+cfg rules", () => {
10
+ it("hits sym/duplicate", async () => {
11
+ const codes = await codesOf(`
12
+ filter dup_policy { accept; }
13
+ filter dup_policy { reject; }
14
+ `);
15
+
16
+ expect(codes).toContain("sym/duplicate");
17
+ });
18
+
19
+ it("hits sym/undefined", async () => {
20
+ const codes = await codesOf(`
21
+ protocol bgp edge from missing_tpl {
22
+ local as 65001;
23
+ neighbor 192.0.2.1 as 65002;
24
+ }
25
+ `);
26
+
27
+ expect(codes).toContain("sym/undefined");
28
+ });
29
+
30
+ it("hits sym/proto-type-mismatch", async () => {
31
+ const codes = await codesOf(`
32
+ template ospf base_tpl { }
33
+ protocol bgp edge from base_tpl {
34
+ local as 65001;
35
+ neighbor 192.0.2.1 as 65002;
36
+ }
37
+ `);
38
+
39
+ expect(codes).toContain("sym/proto-type-mismatch");
40
+ });
41
+
42
+ it("hits sym/filter-required", async () => {
43
+ const codes = await codesOf(`
44
+ protocol bgp edge {
45
+ local as 65001;
46
+ neighbor 192.0.2.1 as 65002;
47
+ import filter missing_policy;
48
+ }
49
+ `);
50
+
51
+ expect(codes).toContain("sym/filter-required");
52
+ });
53
+
54
+ it("does not hit sym/filter-required for built-in filter names", async () => {
55
+ const codes = await codesOf(`
56
+ protocol bgp edge {
57
+ local as 65001;
58
+ neighbor 192.0.2.1 as 65002;
59
+ import filter all;
60
+ }
61
+ `);
62
+
63
+ expect(codes).not.toContain("sym/filter-required");
64
+ });
65
+
66
+ it("hits sym/function-required", async () => {
67
+ const codes = await codesOf(`
68
+ filter f1 {
69
+ missing_fn(1);
70
+ accept;
71
+ }
72
+ `);
73
+
74
+ expect(codes).toContain("sym/function-required");
75
+ });
76
+
77
+ it("does not hit sym/function-required for method calls or builtins", async () => {
78
+ const codes = await codesOf(`
79
+ filter f1 {
80
+ bgp_community.contains((65000, 100));
81
+ len(bgp_path);
82
+ accept;
83
+ }
84
+ `);
85
+
86
+ expect(codes).not.toContain("sym/function-required");
87
+ });
88
+
89
+ it("hits sym/table-required", async () => {
90
+ const codes = await codesOf(`
91
+ protocol bgp edge {
92
+ local as 65001;
93
+ neighbor 192.0.2.1 as 65002;
94
+ ipv4 {
95
+ table missing_table;
96
+ };
97
+ }
98
+ `);
99
+
100
+ expect(codes).toContain("sym/table-required");
101
+ });
102
+
103
+ it("hits sym/variable-scope", async () => {
104
+ const codes = await codesOf(`
105
+ filter f1 {
106
+ x = 1;
107
+ accept;
108
+ }
109
+ `);
110
+
111
+ expect(codes).toContain("sym/variable-scope");
112
+ });
113
+
114
+ it("hits cfg/no-protocol and cfg/missing-router-id", async () => {
115
+ const codes = await codesOf(`
116
+ filter only_filter {
117
+ accept;
118
+ }
119
+ `);
120
+
121
+ expect(codes).toContain("cfg/no-protocol");
122
+ expect(codes).toContain("cfg/missing-router-id");
123
+ });
124
+
125
+ it("hits cfg/syntax-error", async () => {
126
+ const codes = await codesOf(`
127
+ protocol bgp edge {
128
+ local as 65001
129
+ `);
130
+
131
+ expect(codes).toContain("cfg/syntax-error");
132
+ });
133
+
134
+ it("hits cfg/value-out-of-range", async () => {
135
+ const codes = await codesOf(`
136
+ protocol bgp edge {
137
+ local as 5000000000;
138
+ neighbor 192.0.2.1 as 65002;
139
+ }
140
+ `);
141
+
142
+ expect(codes).toContain("cfg/value-out-of-range");
143
+ });
144
+
145
+ it("hits cfg/value-out-of-range for reserved ASN 4294967295", async () => {
146
+ const codes = await codesOf(`
147
+ protocol bgp edge {
148
+ local as 4294967295;
149
+ neighbor 192.0.2.1 as 65002;
150
+ }
151
+ `);
152
+
153
+ expect(codes).toContain("cfg/value-out-of-range");
154
+ });
155
+
156
+ it("hits cfg/switch-value-expected", async () => {
157
+ const codes = await codesOf(`
158
+ protocol bgp edge {
159
+ local as 65001;
160
+ neighbor 192.0.2.1 as 65002;
161
+ ipv4 {
162
+ import keep filtered maybe;
163
+ };
164
+ }
165
+ `);
166
+
167
+ expect(codes).toContain("cfg/switch-value-expected");
168
+ });
169
+
170
+ it("hits cfg/number-expected", async () => {
171
+ const codes = await codesOf(`
172
+ protocol bgp edge {
173
+ local as abc;
174
+ neighbor 192.0.2.1 as hello;
175
+ }
176
+ `);
177
+
178
+ expect(codes).toContain("cfg/number-expected");
179
+ });
180
+
181
+ it("hits cfg/incompatible-type", async () => {
182
+ const codes = await codesOf(`
183
+ protocol bgp edge {
184
+ local as 65001;
185
+ neighbor neighbor-host as 65002;
186
+ }
187
+ `);
188
+
189
+ expect(codes).toContain("cfg/incompatible-type");
190
+ });
191
+
192
+ it("hits cfg/ip-network-mismatch", async () => {
193
+ const codes = await codesOf(`
194
+ protocol bgp edge {
195
+ local as 65001;
196
+ neighbor 192.0.2.1 as 65002;
197
+ ipv4 {
198
+ next hop 2001:db8::1;
199
+ };
200
+ }
201
+ `);
202
+
203
+ expect(codes).toContain("cfg/ip-network-mismatch");
204
+ });
205
+
206
+ it("hits cfg/circular-template", async () => {
207
+ const codes = await codesOf(`
208
+ template bgp a from b { }
209
+ template bgp b from a { }
210
+ `);
211
+
212
+ expect(codes).toContain("cfg/circular-template");
213
+ });
214
+
215
+ it("hits cfg/circular-template for multi-hop cycle", async () => {
216
+ const codes = await codesOf(`
217
+ template bgp a from b { }
218
+ template bgp b from c { }
219
+ template bgp c from a { }
220
+ `);
221
+
222
+ expect(codes).toContain("cfg/circular-template");
223
+ });
224
+ });
@@ -0,0 +1,21 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { lintBirdConfig } from "../src/index.js";
3
+
4
+ describe("@birdcc/linter smoke", () => {
5
+ it("returns diagnostics in 32-rule taxonomy", async () => {
6
+ const sample = `
7
+ router id 192.0.2.1;
8
+ protocol bgp edge {
9
+ local as 65001;
10
+ neighbor 192.0.2.2 as 65002;
11
+ }
12
+ `;
13
+
14
+ const result = await lintBirdConfig(sample);
15
+
16
+ expect(Array.isArray(result.diagnostics)).toBe(true);
17
+ expect(result.diagnostics.every((item) => item.code.includes("/"))).toBe(
18
+ true,
19
+ );
20
+ });
21
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/**/*.ts"]
8
+ }