@hatem427/code-guard-ci 3.0.0 → 3.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/config/fastify.config.ts +326 -0
- package/config/hono.config.ts +331 -0
- package/config/nestjs.config.ts +500 -0
- package/config/node.config.ts +425 -0
- package/config/python.config.ts +512 -0
- package/dist/config/fastify.config.d.ts +17 -0
- package/dist/config/fastify.config.d.ts.map +1 -0
- package/dist/config/fastify.config.js +279 -0
- package/dist/config/fastify.config.js.map +1 -0
- package/dist/config/hono.config.d.ts +17 -0
- package/dist/config/hono.config.d.ts.map +1 -0
- package/dist/config/hono.config.js +287 -0
- package/dist/config/hono.config.js.map +1 -0
- package/dist/config/nestjs.config.d.ts +17 -0
- package/dist/config/nestjs.config.d.ts.map +1 -0
- package/dist/config/nestjs.config.js +440 -0
- package/dist/config/nestjs.config.js.map +1 -0
- package/dist/config/node.config.d.ts +17 -0
- package/dist/config/node.config.d.ts.map +1 -0
- package/dist/config/node.config.js +363 -0
- package/dist/config/node.config.js.map +1 -0
- package/dist/config/python.config.d.ts +15 -0
- package/dist/config/python.config.d.ts.map +1 -0
- package/dist/config/python.config.js +475 -0
- package/dist/config/python.config.js.map +1 -0
- package/dist/scripts/auto-fix.d.ts +5 -0
- package/dist/scripts/auto-fix.d.ts.map +1 -1
- package/dist/scripts/auto-fix.js +5 -0
- package/dist/scripts/auto-fix.js.map +1 -1
- package/dist/scripts/cli.js +2 -2
- package/dist/scripts/cli.js.map +1 -1
- package/dist/scripts/config-generators/ai-config-generator.d.ts.map +1 -1
- package/dist/scripts/config-generators/ai-config-generator.js +6 -0
- package/dist/scripts/config-generators/ai-config-generator.js.map +1 -1
- package/dist/scripts/config-generators/eslint-generator.d.ts.map +1 -1
- package/dist/scripts/config-generators/eslint-generator.js +108 -0
- package/dist/scripts/config-generators/eslint-generator.js.map +1 -1
- package/dist/scripts/config-generators/frameworks/fastify.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/fastify.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/fastify.js +68 -0
- package/dist/scripts/config-generators/frameworks/fastify.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/hono.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/hono.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/hono.js +63 -0
- package/dist/scripts/config-generators/frameworks/hono.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/index.d.ts +3 -0
- package/dist/scripts/config-generators/frameworks/index.d.ts.map +1 -1
- package/dist/scripts/config-generators/frameworks/index.js +7 -1
- package/dist/scripts/config-generators/frameworks/index.js.map +1 -1
- package/dist/scripts/config-generators/frameworks/nestjs.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/nestjs.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/nestjs.js +83 -0
- package/dist/scripts/config-generators/frameworks/nestjs.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/node.d.ts +2 -2
- package/dist/scripts/config-generators/frameworks/node.d.ts.map +1 -1
- package/dist/scripts/config-generators/frameworks/node.js +56 -11
- package/dist/scripts/config-generators/frameworks/node.js.map +1 -1
- package/dist/scripts/config-generators/typescript-generator.d.ts.map +1 -1
- package/dist/scripts/config-generators/typescript-generator.js +33 -0
- package/dist/scripts/config-generators/typescript-generator.js.map +1 -1
- package/dist/scripts/config-generators/vscode-generator.d.ts.map +1 -1
- package/dist/scripts/config-generators/vscode-generator.js +73 -0
- package/dist/scripts/config-generators/vscode-generator.js.map +1 -1
- package/dist/scripts/generate-pr-checklist.d.ts +5 -0
- package/dist/scripts/generate-pr-checklist.d.ts.map +1 -1
- package/dist/scripts/generate-pr-checklist.js +6 -1
- package/dist/scripts/generate-pr-checklist.js.map +1 -1
- package/dist/scripts/postinstall.js +38 -0
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/scripts/precommit-check.d.ts +13 -0
- package/dist/scripts/precommit-check.d.ts.map +1 -1
- package/dist/scripts/precommit-check.js +288 -5
- package/dist/scripts/precommit-check.js.map +1 -1
- package/dist/scripts/utils/naming-validator.d.ts.map +1 -1
- package/dist/scripts/utils/naming-validator.js +96 -2
- package/dist/scripts/utils/naming-validator.js.map +1 -1
- package/dist/scripts/utils/project-detector.d.ts +12 -9
- package/dist/scripts/utils/project-detector.d.ts.map +1 -1
- package/dist/scripts/utils/project-detector.js +63 -11
- package/dist/scripts/utils/project-detector.js.map +1 -1
- package/dist/scripts/utils/structure-validator.d.ts.map +1 -1
- package/dist/scripts/utils/structure-validator.js +50 -0
- package/dist/scripts/utils/structure-validator.js.map +1 -1
- package/package.json +10 -3
- package/scripts/auto-fix.ts +5 -0
- package/scripts/cli.ts +2 -2
- package/scripts/config-generators/ai-config-generator.ts +9 -0
- package/scripts/config-generators/eslint-generator.ts +110 -0
- package/scripts/config-generators/frameworks/fastify.ts +65 -0
- package/scripts/config-generators/frameworks/hono.ts +60 -0
- package/scripts/config-generators/frameworks/index.ts +3 -0
- package/scripts/config-generators/frameworks/nestjs.ts +80 -0
- package/scripts/config-generators/frameworks/node.ts +57 -11
- package/scripts/config-generators/typescript-generator.ts +36 -0
- package/scripts/config-generators/vscode-generator.ts +84 -0
- package/scripts/generate-pr-checklist.ts +6 -1
- package/scripts/postinstall.ts +38 -0
- package/scripts/precommit-check.ts +334 -6
- package/scripts/utils/naming-validator.ts +104 -2
- package/scripts/utils/project-detector.ts +78 -11
- package/scripts/utils/structure-validator.ts +54 -0
- package/templates/feature-doc-backend.md +114 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* fastify.config.ts — Fastify-specific coding rules
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*
|
|
7
|
+
* Registers rules that apply to Fastify projects:
|
|
8
|
+
* - Schema-first validation enforcement
|
|
9
|
+
* - Plugin scope correctness
|
|
10
|
+
* - Async handler patterns
|
|
11
|
+
* - Security: CORS, helmet, rate limiting
|
|
12
|
+
* - Error response shape
|
|
13
|
+
* - Hook lifecycle usage
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.fastifyRules = void 0;
|
|
17
|
+
const guidelines_config_1 = require("./guidelines.config");
|
|
18
|
+
const fastifyRules = [
|
|
19
|
+
// ── Schema-First Validation ───────────────────────────────────────────────
|
|
20
|
+
// ─────────────────────────────────────────
|
|
21
|
+
// RULE: fastify-schema-required
|
|
22
|
+
// ROLE: Enforce schema definitions on all routes
|
|
23
|
+
// PURPOSE: Fastify uses JSON Schema / TypeBox for both request validation
|
|
24
|
+
// and response serialization. Without schemas, inputs are not
|
|
25
|
+
// validated and responses include all properties (potential data leak).
|
|
26
|
+
// Schemas also enable Fastify's fast-json-stringify for ~2× serialization speed.
|
|
27
|
+
// EXAMPLE:
|
|
28
|
+
// WRONG:
|
|
29
|
+
// fastify.post('/users', async (req, reply) => {
|
|
30
|
+
// return userService.create(req.body);
|
|
31
|
+
// });
|
|
32
|
+
// RIGHT:
|
|
33
|
+
// fastify.post('/users', {
|
|
34
|
+
// schema: {
|
|
35
|
+
// body: CreateUserSchema,
|
|
36
|
+
// response: { 201: UserResponseSchema },
|
|
37
|
+
// },
|
|
38
|
+
// }, async (req, reply) => {
|
|
39
|
+
// return userService.create(req.body);
|
|
40
|
+
// });
|
|
41
|
+
// ─────────────────────────────────────────
|
|
42
|
+
{
|
|
43
|
+
id: 'fastify-schema-required',
|
|
44
|
+
label: 'Define schema for every route (body, params, response)',
|
|
45
|
+
description: 'Every Fastify route must define a schema object with at minimum body/params validation and response schemas. This enables validation, serialization speed, and prevents data leaks.',
|
|
46
|
+
severity: 'error',
|
|
47
|
+
fileExtensions: ['ts', 'js'],
|
|
48
|
+
pattern: null,
|
|
49
|
+
customCheck: (file) => {
|
|
50
|
+
const violations = [];
|
|
51
|
+
if (!file.relativePath.includes('/routes/') &&
|
|
52
|
+
!file.relativePath.endsWith('.route.ts') &&
|
|
53
|
+
!file.relativePath.endsWith('.routes.ts')) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
// Look for fastify.post/put/patch/delete without a schema key
|
|
57
|
+
const routeRegex = /fastify\.\s*(?:post|put|patch|delete)\s*\(\s*['"`][^'"]+['"`]\s*,\s*async/g;
|
|
58
|
+
let match;
|
|
59
|
+
while ((match = routeRegex.exec(file.content)) !== null) {
|
|
60
|
+
const near = file.content.slice(Math.max(0, match.index - 200), match.index + 50);
|
|
61
|
+
if (!near.includes('schema')) {
|
|
62
|
+
const lineNum = file.content.slice(0, match.index).split('\n').length;
|
|
63
|
+
violations.push({
|
|
64
|
+
line: lineNum,
|
|
65
|
+
message: 'Mutating route (POST/PUT/PATCH/DELETE) is missing a schema definition. Add body validation schema and response schema.',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return violations;
|
|
70
|
+
},
|
|
71
|
+
applicableTo: ['fastify'],
|
|
72
|
+
category: 'Validation',
|
|
73
|
+
},
|
|
74
|
+
// ─────────────────────────────────────────
|
|
75
|
+
// RULE: fastify-response-schema
|
|
76
|
+
// ROLE: Enforce response schema definitions
|
|
77
|
+
// PURPOSE: Without a response schema, Fastify falls back to slow JSON.stringify
|
|
78
|
+
// and may expose internal fields. Response schemas activate fast-json-stringify
|
|
79
|
+
// and act as a serialisation allowlist.
|
|
80
|
+
// EXAMPLE:
|
|
81
|
+
// WRONG:
|
|
82
|
+
// fastify.get('/users/:id', async (req, reply) => { return user; });
|
|
83
|
+
// RIGHT:
|
|
84
|
+
// fastify.get('/users/:id', {
|
|
85
|
+
// schema: { response: { 200: UserResponseSchema } },
|
|
86
|
+
// }, async (req, reply) => { return user; });
|
|
87
|
+
// ─────────────────────────────────────────
|
|
88
|
+
{
|
|
89
|
+
id: 'fastify-response-schema',
|
|
90
|
+
label: 'Always define a response schema',
|
|
91
|
+
description: 'Define response schemas for all routes to enable fast-json-stringify serialization and prevent accidental exposure of sensitive fields.',
|
|
92
|
+
severity: 'warning',
|
|
93
|
+
fileExtensions: ['ts', 'js'],
|
|
94
|
+
pattern: null,
|
|
95
|
+
customCheck: (file) => {
|
|
96
|
+
const violations = [];
|
|
97
|
+
if (!file.relativePath.includes('/routes/') &&
|
|
98
|
+
!file.relativePath.endsWith('.route.ts') &&
|
|
99
|
+
!file.relativePath.endsWith('.routes.ts')) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
const hasSchema = /schema\s*:\s*\{/.test(file.content);
|
|
103
|
+
const hasResponseSchema = /response\s*:\s*\{/.test(file.content);
|
|
104
|
+
if (hasSchema && !hasResponseSchema) {
|
|
105
|
+
violations.push({
|
|
106
|
+
line: null,
|
|
107
|
+
message: 'Route schema found but no response schema defined. Add response: { 200: Schema } to enable fast serialization and prevent data leaks.',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return violations;
|
|
111
|
+
},
|
|
112
|
+
applicableTo: ['fastify'],
|
|
113
|
+
category: 'Validation',
|
|
114
|
+
},
|
|
115
|
+
// ── Plugin Architecture ────────────────────────────────────────────────────
|
|
116
|
+
// ─────────────────────────────────────────
|
|
117
|
+
// RULE: fastify-use-fastify-plugin
|
|
118
|
+
// ROLE: Enforce plugin scope correctness
|
|
119
|
+
// PURPOSE: In Fastify, plugins registered without fastify-plugin are
|
|
120
|
+
// scoped to a child context — decorations, hooks, and routes
|
|
121
|
+
// added inside won't be visible to siblings or the root instance.
|
|
122
|
+
// Shared utilities (auth, db) must use fastify-plugin to break scope.
|
|
123
|
+
// EXAMPLE:
|
|
124
|
+
// WRONG:
|
|
125
|
+
// export async function authPlugin(fastify: FastifyInstance) {
|
|
126
|
+
// fastify.decorate('verifyToken', verifyToken);
|
|
127
|
+
// }
|
|
128
|
+
// RIGHT:
|
|
129
|
+
// import fp from 'fastify-plugin';
|
|
130
|
+
// export default fp(async function authPlugin(fastify: FastifyInstance) {
|
|
131
|
+
// fastify.decorate('verifyToken', verifyToken);
|
|
132
|
+
// });
|
|
133
|
+
// ─────────────────────────────────────────
|
|
134
|
+
{
|
|
135
|
+
id: 'fastify-use-fastify-plugin',
|
|
136
|
+
label: 'Shared plugins must use fastify-plugin (fp)',
|
|
137
|
+
description: 'Plugins that add decorations, hooks, or services used by other plugins must be wrapped with fastify-plugin (fp) to share scope with the parent context.',
|
|
138
|
+
severity: 'warning',
|
|
139
|
+
fileExtensions: ['ts', 'js'],
|
|
140
|
+
pattern: null,
|
|
141
|
+
customCheck: (file) => {
|
|
142
|
+
if (!file.relativePath.includes('/plugins/') &&
|
|
143
|
+
!file.relativePath.endsWith('.plugin.ts') &&
|
|
144
|
+
!file.relativePath.endsWith('.plugin.js')) {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
const usesDecorate = /fastify\.decorate\s*\(/.test(file.content);
|
|
148
|
+
const usesFastifyPlugin = /fastify-plugin|fp\s*\(/.test(file.content);
|
|
149
|
+
if (usesDecorate && !usesFastifyPlugin) {
|
|
150
|
+
return [
|
|
151
|
+
{
|
|
152
|
+
line: null,
|
|
153
|
+
message: 'Plugin adds decorators but is not wrapped with fastify-plugin (fp). Other plugins/routes will not see these decorations.',
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
}
|
|
157
|
+
return [];
|
|
158
|
+
},
|
|
159
|
+
applicableTo: ['fastify'],
|
|
160
|
+
category: 'Architecture',
|
|
161
|
+
},
|
|
162
|
+
// ── Async Handler Patterns ────────────────────────────────────────────────
|
|
163
|
+
// ─────────────────────────────────────────
|
|
164
|
+
// RULE: fastify-async-handler
|
|
165
|
+
// ROLE: Enforce async handler pattern
|
|
166
|
+
// PURPOSE: Mixing reply.send() with async return values causes double-send
|
|
167
|
+
// errors in Fastify. Async handlers should return the value;
|
|
168
|
+
// Fastify handles serialization. Use reply.send() OR return, not both.
|
|
169
|
+
// EXAMPLE:
|
|
170
|
+
// WRONG:
|
|
171
|
+
// fastify.get('/users', async (req, reply) => {
|
|
172
|
+
// const users = await userService.findAll();
|
|
173
|
+
// reply.send(users); // + implicit return → double send!
|
|
174
|
+
// });
|
|
175
|
+
// RIGHT:
|
|
176
|
+
// fastify.get('/users', async (req, reply) => {
|
|
177
|
+
// return userService.findAll(); // Fastify handles send automatically
|
|
178
|
+
// });
|
|
179
|
+
// ─────────────────────────────────────────
|
|
180
|
+
{
|
|
181
|
+
id: 'fastify-async-handler',
|
|
182
|
+
label: 'Return values from async handlers — avoid reply.send() + return',
|
|
183
|
+
description: 'In async Fastify handlers, return the response value directly. Calling reply.send() AND returning a value causes a double-send error.',
|
|
184
|
+
severity: 'error',
|
|
185
|
+
fileExtensions: ['ts', 'js'],
|
|
186
|
+
pattern: null,
|
|
187
|
+
customCheck: (file) => {
|
|
188
|
+
const violations = [];
|
|
189
|
+
if (!file.relativePath.includes('/routes/') &&
|
|
190
|
+
!file.relativePath.endsWith('.route.ts')) {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
194
|
+
if (/reply\.send\s*\(/.test(file.lines[i])) {
|
|
195
|
+
// Check if there's a return after send on same or next line
|
|
196
|
+
const nearby = file.lines.slice(i, i + 3).join(' ');
|
|
197
|
+
if (/reply\.send[^;]*;\s*(?:return|})/.test(nearby)) {
|
|
198
|
+
violations.push({
|
|
199
|
+
line: i + 1,
|
|
200
|
+
message: 'reply.send() followed by implicit return in async handler. Use: return myValue; instead of reply.send(myValue).',
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return violations;
|
|
206
|
+
},
|
|
207
|
+
applicableTo: ['fastify'],
|
|
208
|
+
category: 'Async Patterns',
|
|
209
|
+
},
|
|
210
|
+
// ── Security ──────────────────────────────────────────────────────────────
|
|
211
|
+
// ─────────────────────────────────────────
|
|
212
|
+
// RULE: fastify-use-helmet
|
|
213
|
+
// ROLE: Enforce security headers
|
|
214
|
+
// PURPOSE: @fastify/helmet sets a baseline of secure HTTP headers.
|
|
215
|
+
// Without it, Fastify serves responses without X-Frame-Options,
|
|
216
|
+
// Content-Security-Policy, or HSTS headers.
|
|
217
|
+
// EXAMPLE:
|
|
218
|
+
// WRONG:
|
|
219
|
+
// const fastify = Fastify();
|
|
220
|
+
// fastify.register(cors);
|
|
221
|
+
// RIGHT:
|
|
222
|
+
// const fastify = Fastify();
|
|
223
|
+
// fastify.register(helmet);
|
|
224
|
+
// fastify.register(cors, { origin: allowedOrigins });
|
|
225
|
+
// ─────────────────────────────────────────
|
|
226
|
+
{
|
|
227
|
+
id: 'fastify-use-helmet',
|
|
228
|
+
label: 'Register @fastify/helmet for security headers',
|
|
229
|
+
description: 'Register @fastify/helmet to add baseline security headers (HSTS, X-Frame-Options, CSP, etc.) to all responses.',
|
|
230
|
+
severity: 'error',
|
|
231
|
+
fileExtensions: ['ts', 'js'],
|
|
232
|
+
pattern: null,
|
|
233
|
+
customCheck: (file) => {
|
|
234
|
+
if (!file.relativePath.endsWith('server.ts') &&
|
|
235
|
+
!file.relativePath.endsWith('app.ts') &&
|
|
236
|
+
!file.relativePath.endsWith('index.ts')) {
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
const hasFastify = /Fastify\s*\(|fastify\s*\(\s*\)/.test(file.content);
|
|
240
|
+
const hasHelmet = /helmet/.test(file.content);
|
|
241
|
+
if (hasFastify && !hasHelmet) {
|
|
242
|
+
return [
|
|
243
|
+
{
|
|
244
|
+
line: null,
|
|
245
|
+
message: 'Fastify server missing @fastify/helmet. Register it: fastify.register(import("@fastify/helmet")).',
|
|
246
|
+
},
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
return [];
|
|
250
|
+
},
|
|
251
|
+
applicableTo: ['fastify'],
|
|
252
|
+
category: 'Security',
|
|
253
|
+
},
|
|
254
|
+
// ─────────────────────────────────────────
|
|
255
|
+
// RULE: fastify-cors-options
|
|
256
|
+
// ROLE: Enforce CORS configuration
|
|
257
|
+
// PURPOSE: Registering @fastify/cors without an origin restriction allows
|
|
258
|
+
// cross-origin requests from any domain, defeating CORS entirely.
|
|
259
|
+
// EXAMPLE:
|
|
260
|
+
// WRONG:
|
|
261
|
+
// fastify.register(cors); // allows all origins
|
|
262
|
+
// RIGHT:
|
|
263
|
+
// fastify.register(cors, { origin: ['https://myapp.com'] });
|
|
264
|
+
// ─────────────────────────────────────────
|
|
265
|
+
{
|
|
266
|
+
id: 'fastify-cors-options',
|
|
267
|
+
label: '@fastify/cors must restrict origin',
|
|
268
|
+
description: 'Do not register @fastify/cors without an explicit origin allowlist. An unrestricted CORS policy allows any website to make authenticated requests to your API.',
|
|
269
|
+
severity: 'warning',
|
|
270
|
+
fileExtensions: ['ts', 'js'],
|
|
271
|
+
pattern: /register\s*\(\s*cors\s*\)/g,
|
|
272
|
+
applicableTo: ['fastify'],
|
|
273
|
+
category: 'Security',
|
|
274
|
+
},
|
|
275
|
+
];
|
|
276
|
+
exports.fastifyRules = fastifyRules;
|
|
277
|
+
// Register all Fastify-specific rules
|
|
278
|
+
(0, guidelines_config_1.registerRules)('fastify', fastifyRules);
|
|
279
|
+
//# sourceMappingURL=fastify.config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastify.config.js","sourceRoot":"","sources":["../../config/fastify.config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* hono.config.ts — Hono-specific coding rules
|
|
4
|
+
* ============================================================================
|
|
5
|
+
*
|
|
6
|
+
* Registers rules that apply to Hono (edge + server) projects:
|
|
7
|
+
* - Zod validation enforcement
|
|
8
|
+
* - Context variable typing
|
|
9
|
+
* - Middleware chaining patterns
|
|
10
|
+
* - HTTPException usage
|
|
11
|
+
* - Environment access patterns
|
|
12
|
+
* - Stateless / edge-first patterns
|
|
13
|
+
*/
|
|
14
|
+
import { Rule } from './guidelines.config';
|
|
15
|
+
declare const honoRules: Rule[];
|
|
16
|
+
export { honoRules };
|
|
17
|
+
//# sourceMappingURL=hono.config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.config.d.ts","sourceRoot":"","sources":["../../config/hono.config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAiB,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAE1D,QAAA,MAAM,SAAS,EAAE,IAAI,EAqTpB,CAAC;AAKF,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* hono.config.ts — Hono-specific coding rules
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*
|
|
7
|
+
* Registers rules that apply to Hono (edge + server) projects:
|
|
8
|
+
* - Zod validation enforcement
|
|
9
|
+
* - Context variable typing
|
|
10
|
+
* - Middleware chaining patterns
|
|
11
|
+
* - HTTPException usage
|
|
12
|
+
* - Environment access patterns
|
|
13
|
+
* - Stateless / edge-first patterns
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.honoRules = void 0;
|
|
17
|
+
const guidelines_config_1 = require("./guidelines.config");
|
|
18
|
+
const honoRules = [
|
|
19
|
+
// ── Validation ────────────────────────────────────────────────────────────
|
|
20
|
+
// ─────────────────────────────────────────
|
|
21
|
+
// RULE: hono-use-zod-validator
|
|
22
|
+
// ROLE: Enforce schema-based request validation
|
|
23
|
+
// PURPOSE: Accessing c.req.json() or c.req.query() without validation
|
|
24
|
+
// passes raw untyped data to your handlers. Using @hono/zod-validator
|
|
25
|
+
// ensures type safety and returns a 400 automatically on bad input.
|
|
26
|
+
// EXAMPLE:
|
|
27
|
+
// WRONG:
|
|
28
|
+
// app.post('/users', async (c) => {
|
|
29
|
+
// const body = await c.req.json(); // untyped, unvalidated
|
|
30
|
+
// });
|
|
31
|
+
// RIGHT:
|
|
32
|
+
// import { zValidator } from '@hono/zod-validator';
|
|
33
|
+
// app.post('/users', zValidator('json', CreateUserSchema), async (c) => {
|
|
34
|
+
// const body = c.req.valid('json'); // typed and validated
|
|
35
|
+
// });
|
|
36
|
+
// ─────────────────────────────────────────
|
|
37
|
+
{
|
|
38
|
+
id: 'hono-use-zod-validator',
|
|
39
|
+
label: 'Use zValidator for request body validation',
|
|
40
|
+
description: 'Use @hono/zod-validator middleware on routes that accept request bodies. Calling c.req.json() directly bypasses type safety and validation.',
|
|
41
|
+
severity: 'error',
|
|
42
|
+
fileExtensions: ['ts'],
|
|
43
|
+
pattern: null,
|
|
44
|
+
customCheck: (file) => {
|
|
45
|
+
const violations = [];
|
|
46
|
+
if (!file.relativePath.includes('/routes/') &&
|
|
47
|
+
!file.relativePath.endsWith('.route.ts') &&
|
|
48
|
+
!file.relativePath.endsWith('.routes.ts')) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
// Detect routes that use c.req.json() without zValidator
|
|
52
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
53
|
+
const line = file.lines[i];
|
|
54
|
+
if (/c\.req\.json\s*\(/.test(line)) {
|
|
55
|
+
// Check surrounding context for zValidator
|
|
56
|
+
const context = file.lines.slice(Math.max(0, i - 10), i).join('\n');
|
|
57
|
+
if (!context.includes('zValidator') && !context.includes('z.object')) {
|
|
58
|
+
violations.push({
|
|
59
|
+
line: i + 1,
|
|
60
|
+
message: 'c.req.json() used without zValidator middleware. Use zValidator("json", schema) before the handler and access via c.req.valid("json").',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return violations;
|
|
66
|
+
},
|
|
67
|
+
applicableTo: ['hono'],
|
|
68
|
+
category: 'Validation',
|
|
69
|
+
},
|
|
70
|
+
// ─────────────────────────────────────────
|
|
71
|
+
// RULE: hono-use-valid-not-json
|
|
72
|
+
// ROLE: Enforce validated data access after zValidator
|
|
73
|
+
// PURPOSE: After applying zValidator middleware, handlers must use
|
|
74
|
+
// c.req.valid() to access the validated, typed data.
|
|
75
|
+
// c.req.json() bypasses validation and returns raw untyped data.
|
|
76
|
+
// EXAMPLE:
|
|
77
|
+
// WRONG:
|
|
78
|
+
// app.post('/user', zValidator('json', schema), async (c) => {
|
|
79
|
+
// const body = await c.req.json(); // still untyped!
|
|
80
|
+
// });
|
|
81
|
+
// RIGHT:
|
|
82
|
+
// app.post('/user', zValidator('json', schema), async (c) => {
|
|
83
|
+
// const body = c.req.valid('json'); // typed + validated
|
|
84
|
+
// });
|
|
85
|
+
// ─────────────────────────────────────────
|
|
86
|
+
{
|
|
87
|
+
id: 'hono-use-valid-not-json',
|
|
88
|
+
label: 'After zValidator, use c.req.valid() not c.req.json()',
|
|
89
|
+
description: 'When zValidator middleware is applied, access validated data via c.req.valid("json") instead of await c.req.json(). The latter bypasses type narrowing.',
|
|
90
|
+
severity: 'warning',
|
|
91
|
+
fileExtensions: ['ts'],
|
|
92
|
+
pattern: null,
|
|
93
|
+
customCheck: (file) => {
|
|
94
|
+
const violations = [];
|
|
95
|
+
if (!file.content.includes('zValidator'))
|
|
96
|
+
return [];
|
|
97
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
98
|
+
if (/await c\.req\.json\s*\(/.test(file.lines[i])) {
|
|
99
|
+
violations.push({
|
|
100
|
+
line: i + 1,
|
|
101
|
+
message: 'zValidator is used in this file. Replace await c.req.json() with c.req.valid("json") to access the typed validated data.',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return violations;
|
|
106
|
+
},
|
|
107
|
+
applicableTo: ['hono'],
|
|
108
|
+
category: 'Validation',
|
|
109
|
+
},
|
|
110
|
+
// ── Error Handling ────────────────────────────────────────────────────────
|
|
111
|
+
// ─────────────────────────────────────────
|
|
112
|
+
// RULE: hono-use-http-exception
|
|
113
|
+
// ROLE: Enforce HTTPException for expected errors
|
|
114
|
+
// PURPOSE: Throwing a raw Error in a Hono handler produces a generic 500
|
|
115
|
+
// response with no meaningful body. HTTPException allows you to set
|
|
116
|
+
// a specific status code and message that will be formatted by
|
|
117
|
+
// Hono's onError handler consistently.
|
|
118
|
+
// EXAMPLE:
|
|
119
|
+
// WRONG:
|
|
120
|
+
// throw new Error('User not found');
|
|
121
|
+
// RIGHT:
|
|
122
|
+
// throw new HTTPException(404, { message: 'User not found' });
|
|
123
|
+
// ─────────────────────────────────────────
|
|
124
|
+
{
|
|
125
|
+
id: 'hono-use-http-exception',
|
|
126
|
+
label: 'Use HTTPException instead of raw Error',
|
|
127
|
+
description: 'Throw HTTPException from hono with a status code and message for expected errors (404, 401, 400, etc.). Raw Error objects produce unformatted 500 responses.',
|
|
128
|
+
severity: 'warning',
|
|
129
|
+
fileExtensions: ['ts'],
|
|
130
|
+
pattern: null,
|
|
131
|
+
customCheck: (file) => {
|
|
132
|
+
const violations = [];
|
|
133
|
+
if (!file.relativePath.includes('/routes/') &&
|
|
134
|
+
!file.relativePath.includes('/handlers/') &&
|
|
135
|
+
!file.relativePath.endsWith('.route.ts') &&
|
|
136
|
+
!file.relativePath.endsWith('.handler.ts')) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
140
|
+
if (/throw\s+new\s+Error\s*\(/.test(file.lines[i])) {
|
|
141
|
+
violations.push({
|
|
142
|
+
line: i + 1,
|
|
143
|
+
message: 'Raw Error thrown. Use HTTPException: throw new HTTPException(404, { message: "Not found" })',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return violations;
|
|
148
|
+
},
|
|
149
|
+
applicableTo: ['hono'],
|
|
150
|
+
category: 'Error Handling',
|
|
151
|
+
},
|
|
152
|
+
// ── Context Variables ─────────────────────────────────────────────────────
|
|
153
|
+
// ─────────────────────────────────────────
|
|
154
|
+
// RULE: hono-type-context-variables
|
|
155
|
+
// ROLE: Enforce typed context variables
|
|
156
|
+
// PURPOSE: Using c.get() and c.set() without typed Variables means the
|
|
157
|
+
// TypeScript compiler cannot verify keys exist, leading to
|
|
158
|
+
// undefined value bugs at runtime.
|
|
159
|
+
// EXAMPLE:
|
|
160
|
+
// WRONG:
|
|
161
|
+
// app.use(async (c, next) => {
|
|
162
|
+
// c.set('user', user); // no type checking on 'user' key
|
|
163
|
+
// });
|
|
164
|
+
// RIGHT:
|
|
165
|
+
// type Variables = { user: User };
|
|
166
|
+
// const app = new Hono<{ Variables: Variables }>();
|
|
167
|
+
// app.use(async (c, next) => { c.set('user', user); });
|
|
168
|
+
// ─────────────────────────────────────────
|
|
169
|
+
{
|
|
170
|
+
id: 'hono-type-context-variables',
|
|
171
|
+
label: 'Type context Variables for c.get() / c.set()',
|
|
172
|
+
description: 'Declare a typed Variables interface and pass it as a generic to new Hono<{ Variables: Variables }>(). This enables TypeScript to validate context variable keys and values.',
|
|
173
|
+
severity: 'warning',
|
|
174
|
+
fileExtensions: ['ts'],
|
|
175
|
+
pattern: null,
|
|
176
|
+
customCheck: (file) => {
|
|
177
|
+
const hasContextSet = /c\.set\s*\(/.test(file.content);
|
|
178
|
+
const hasHonoGeneric = /new\s+Hono\s*</.test(file.content);
|
|
179
|
+
const hasVariablesType = /Variables\s*=\s*\{/.test(file.content);
|
|
180
|
+
if (hasContextSet && !hasHonoGeneric && !hasVariablesType) {
|
|
181
|
+
return [
|
|
182
|
+
{
|
|
183
|
+
line: null,
|
|
184
|
+
message: 'c.set() is used without typed Variables. Define: type Variables = { ... } and use new Hono<{ Variables: Variables }>().',
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
}
|
|
188
|
+
return [];
|
|
189
|
+
},
|
|
190
|
+
applicableTo: ['hono'],
|
|
191
|
+
category: 'Type Safety',
|
|
192
|
+
},
|
|
193
|
+
// ── Environment Access ────────────────────────────────────────────────────
|
|
194
|
+
// ─────────────────────────────────────────
|
|
195
|
+
// RULE: hono-use-c-env
|
|
196
|
+
// ROLE: Enforce correct env access on edge runtimes
|
|
197
|
+
// PURPOSE: On Cloudflare Workers and similar runtimes, process.env is
|
|
198
|
+
// unavailable. Environment bindings are accessed via c.env.
|
|
199
|
+
// Even on Node.js, centralising env access via c.env makes
|
|
200
|
+
// the app portable across runtimes.
|
|
201
|
+
// EXAMPLE:
|
|
202
|
+
// WRONG:
|
|
203
|
+
// const secret = process.env['JWT_SECRET'];
|
|
204
|
+
// RIGHT:
|
|
205
|
+
// // In Hono handler:
|
|
206
|
+
// const secret = c.env.JWT_SECRET;
|
|
207
|
+
// ─────────────────────────────────────────
|
|
208
|
+
{
|
|
209
|
+
id: 'hono-use-c-env',
|
|
210
|
+
label: 'Use c.env for environment variables (edge-compatible)',
|
|
211
|
+
description: 'Prefer c.env over process.env for environment variables. process.env is not available in Cloudflare Workers or Deno and makes apps runtime-specific.',
|
|
212
|
+
severity: 'warning',
|
|
213
|
+
fileExtensions: ['ts'],
|
|
214
|
+
pattern: null,
|
|
215
|
+
customCheck: (file) => {
|
|
216
|
+
const violations = [];
|
|
217
|
+
if (!file.relativePath.includes('/routes/') &&
|
|
218
|
+
!file.relativePath.includes('/handlers/') &&
|
|
219
|
+
!file.relativePath.endsWith('.route.ts') &&
|
|
220
|
+
!file.relativePath.endsWith('.handler.ts') &&
|
|
221
|
+
!file.relativePath.endsWith('.middleware.ts')) {
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
225
|
+
if (/process\.env\b/.test(file.lines[i])) {
|
|
226
|
+
violations.push({
|
|
227
|
+
line: i + 1,
|
|
228
|
+
message: 'process.env used in Hono handler/route. Use c.env.VARIABLE_NAME for edge-runtime portability.',
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return violations;
|
|
233
|
+
},
|
|
234
|
+
applicableTo: ['hono'],
|
|
235
|
+
category: 'Security',
|
|
236
|
+
},
|
|
237
|
+
// ── Route Organisation ────────────────────────────────────────────────────
|
|
238
|
+
// ─────────────────────────────────────────
|
|
239
|
+
// RULE: hono-use-sub-apps
|
|
240
|
+
// ROLE: Enforce composable route architecture
|
|
241
|
+
// PURPOSE: Defining all routes on a single root Hono instance leads to a
|
|
242
|
+
// monolithic file. Sub-apps (separate Hono instances per domain)
|
|
243
|
+
// composed via app.route() enable feature isolation and per-domain
|
|
244
|
+
// middleware application.
|
|
245
|
+
// EXAMPLE:
|
|
246
|
+
// WRONG:
|
|
247
|
+
// app.get('/users', ...)
|
|
248
|
+
// app.post('/users', ...)
|
|
249
|
+
// app.get('/products', ...) // all in one file
|
|
250
|
+
// RIGHT:
|
|
251
|
+
// // users.route.ts
|
|
252
|
+
// const users = new Hono();
|
|
253
|
+
// users.get('/', ...)
|
|
254
|
+
// export default users;
|
|
255
|
+
// // app.ts
|
|
256
|
+
// app.route('/users', users);
|
|
257
|
+
// ─────────────────────────────────────────
|
|
258
|
+
{
|
|
259
|
+
id: 'hono-use-sub-apps',
|
|
260
|
+
label: 'Organise routes as Hono sub-apps using app.route()',
|
|
261
|
+
description: 'Define each domain\'s routes in a separate Hono sub-app and compose them via app.route("/prefix", subApp). Avoid putting all routes on a single root instance.',
|
|
262
|
+
severity: 'info',
|
|
263
|
+
fileExtensions: ['ts'],
|
|
264
|
+
pattern: null,
|
|
265
|
+
customCheck: (file) => {
|
|
266
|
+
if (!file.relativePath.endsWith('index.ts') && !file.relativePath.endsWith('app.ts')) {
|
|
267
|
+
return [];
|
|
268
|
+
}
|
|
269
|
+
const routeCount = (file.content.match(/app\.\s*(?:get|post|put|patch|delete)\s*\(/g) || []).length;
|
|
270
|
+
if (routeCount > 5) {
|
|
271
|
+
return [
|
|
272
|
+
{
|
|
273
|
+
line: null,
|
|
274
|
+
message: `${routeCount} routes defined on a single Hono instance. Extract into domain sub-apps and use app.route('/prefix', subApp).`,
|
|
275
|
+
},
|
|
276
|
+
];
|
|
277
|
+
}
|
|
278
|
+
return [];
|
|
279
|
+
},
|
|
280
|
+
applicableTo: ['hono'],
|
|
281
|
+
category: 'Architecture',
|
|
282
|
+
},
|
|
283
|
+
];
|
|
284
|
+
exports.honoRules = honoRules;
|
|
285
|
+
// Register all Hono-specific rules
|
|
286
|
+
(0, guidelines_config_1.registerRules)('hono', honoRules);
|
|
287
|
+
//# sourceMappingURL=hono.config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.config.js","sourceRoot":"","sources":["../../config/hono.config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* nestjs.config.ts — NestJS-specific coding rules
|
|
4
|
+
* ============================================================================
|
|
5
|
+
*
|
|
6
|
+
* Registers rules that only apply to NestJS projects:
|
|
7
|
+
* - Module / controller / service separation
|
|
8
|
+
* - DTO validation enforcement
|
|
9
|
+
* - Security patterns (no raw process.env, no eval)
|
|
10
|
+
* - Error handling with HttpException
|
|
11
|
+
* - Dependency injection correctness
|
|
12
|
+
* - Async/await patterns
|
|
13
|
+
*/
|
|
14
|
+
import { Rule } from './guidelines.config';
|
|
15
|
+
declare const nestjsRules: Rule[];
|
|
16
|
+
export { nestjsRules };
|
|
17
|
+
//# sourceMappingURL=nestjs.config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nestjs.config.d.ts","sourceRoot":"","sources":["../../config/nestjs.config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAiB,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAE1D,QAAA,MAAM,WAAW,EAAE,IAAI,EA8dtB,CAAC;AAKF,OAAO,EAAE,WAAW,EAAE,CAAC"}
|