@empline/preflight 1.1.58 → 1.1.59
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/checks/accessibility/accessibility-validation.d.ts.map +1 -1
- package/dist/checks/accessibility/accessibility-validation.js +131 -14
- package/dist/checks/accessibility/accessibility-validation.js.map +1 -1
- package/dist/checks/accessibility/wcag-advanced-validation.d.ts +10 -0
- package/dist/checks/accessibility/wcag-advanced-validation.d.ts.map +1 -0
- package/dist/checks/accessibility/wcag-advanced-validation.js +622 -0
- package/dist/checks/accessibility/wcag-advanced-validation.js.map +1 -0
- package/dist/checks/database/query-performance-validation.d.ts +10 -0
- package/dist/checks/database/query-performance-validation.d.ts.map +1 -0
- package/dist/checks/database/query-performance-validation.js +544 -0
- package/dist/checks/database/query-performance-validation.js.map +1 -0
- package/dist/checks/react/react-patterns-validation.d.ts +10 -0
- package/dist/checks/react/react-patterns-validation.d.ts.map +1 -0
- package/dist/checks/react/react-patterns-validation.js +559 -0
- package/dist/checks/react/react-patterns-validation.js.map +1 -0
- package/dist/checks/security/security-headers-validation.d.ts +10 -0
- package/dist/checks/security/security-headers-validation.d.ts.map +1 -0
- package/dist/checks/security/security-headers-validation.js +594 -0
- package/dist/checks/security/security-headers-validation.js.map +1 -0
- package/dist/reporters/github-reporter.d.ts +35 -0
- package/dist/reporters/github-reporter.d.ts.map +1 -0
- package/dist/reporters/github-reporter.js +397 -0
- package/dist/reporters/github-reporter.js.map +1 -0
- package/dist/reporters/html-report.d.ts +12 -0
- package/dist/reporters/html-report.d.ts.map +1 -0
- package/dist/reporters/html-report.js +469 -0
- package/dist/reporters/html-report.js.map +1 -0
- package/dist/reporters/index.d.ts +8 -0
- package/dist/reporters/index.d.ts.map +1 -0
- package/dist/reporters/index.js +18 -0
- package/dist/reporters/index.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.tags = exports.blocking = exports.category = exports.description = exports.name = exports.id = void 0;
|
|
38
|
+
exports.run = run;
|
|
39
|
+
/**
|
|
40
|
+
* Query Performance Validation Preflight (BLOCKING)
|
|
41
|
+
*
|
|
42
|
+
* Validates database query patterns for performance issues:
|
|
43
|
+
*
|
|
44
|
+
* 1. N+1 Query Detection:
|
|
45
|
+
* - Queries in loops
|
|
46
|
+
* - Missing includes/joins
|
|
47
|
+
* - Sequential related queries
|
|
48
|
+
*
|
|
49
|
+
* 2. Missing Indexes:
|
|
50
|
+
* - Queries filtering on non-indexed fields
|
|
51
|
+
* - OrderBy on non-indexed columns
|
|
52
|
+
* - Frequent lookups by field
|
|
53
|
+
*
|
|
54
|
+
* 3. Query Optimization:
|
|
55
|
+
* - Select * patterns (over-fetching)
|
|
56
|
+
* - Missing pagination
|
|
57
|
+
* - Large result set handling
|
|
58
|
+
*
|
|
59
|
+
* 4. Transaction Safety:
|
|
60
|
+
* - Multi-step operations without transactions
|
|
61
|
+
* - Inconsistent transaction handling
|
|
62
|
+
*
|
|
63
|
+
* 5. Cascade Operations:
|
|
64
|
+
* - Dangerous cascade deletes
|
|
65
|
+
* - Missing soft delete patterns
|
|
66
|
+
*/
|
|
67
|
+
const fs = __importStar(require("node:fs"));
|
|
68
|
+
const path = __importStar(require("node:path"));
|
|
69
|
+
const glob_1 = require("glob");
|
|
70
|
+
const console_chars_1 = require("../../utils/console-chars");
|
|
71
|
+
exports.id = "database/query-performance-validation";
|
|
72
|
+
exports.name = "Query Performance Validation";
|
|
73
|
+
exports.description = "Validates database query patterns for N+1, missing indexes, and performance issues";
|
|
74
|
+
exports.category = "database";
|
|
75
|
+
exports.blocking = true;
|
|
76
|
+
exports.tags = ["database", "prisma", "performance", "n+1", "indexes", "queries"];
|
|
77
|
+
function getLineNumber(content, index) {
|
|
78
|
+
return content.substring(0, index).split("\n").length;
|
|
79
|
+
}
|
|
80
|
+
async function checkNPlusOneQueries() {
|
|
81
|
+
const issues = [];
|
|
82
|
+
const files = await (0, glob_1.glob)([
|
|
83
|
+
"app/**/*.ts",
|
|
84
|
+
"app/**/*.tsx",
|
|
85
|
+
"lib/**/*.ts",
|
|
86
|
+
], {
|
|
87
|
+
cwd: process.cwd(),
|
|
88
|
+
ignore: ["**/node_modules/**", "**/*.test.ts", "**/*.spec.ts"],
|
|
89
|
+
absolute: true,
|
|
90
|
+
});
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
if (!fs.existsSync(file))
|
|
93
|
+
continue;
|
|
94
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
95
|
+
const relativePath = path.relative(process.cwd(), file);
|
|
96
|
+
// Pattern 1: Query inside a loop
|
|
97
|
+
const queryInLoopPatterns = [
|
|
98
|
+
/for\s*\([^)]*\)\s*\{[^}]*(?:prisma|db)\.\w+\.(?:find|create|update|delete)/gs,
|
|
99
|
+
/\.forEach\s*\([^)]*=>\s*\{[^}]*(?:prisma|db)\.\w+\.(?:find|create|update|delete)/gs,
|
|
100
|
+
/\.map\s*\([^)]*=>\s*\{[^}]*await\s+(?:prisma|db)\.\w+\.(?:find|create|update|delete)/gs,
|
|
101
|
+
/while\s*\([^)]*\)\s*\{[^}]*(?:prisma|db)\.\w+\.(?:find|create|update|delete)/gs,
|
|
102
|
+
];
|
|
103
|
+
for (const pattern of queryInLoopPatterns) {
|
|
104
|
+
let match;
|
|
105
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
106
|
+
issues.push({
|
|
107
|
+
rule: "n-plus-one-loop",
|
|
108
|
+
message: "Database query inside loop (potential N+1)",
|
|
109
|
+
file: relativePath,
|
|
110
|
+
line: getLineNumber(content, match.index),
|
|
111
|
+
severity: "warning",
|
|
112
|
+
suggestion: "Use include/select to fetch related data in single query, or use createMany/updateMany",
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Pattern 2: Sequential queries for related data (could use include)
|
|
117
|
+
const sequentialQueryPattern = /await\s+(?:prisma|db)\.(\w+)\.find\w+[^;]*;\s*(?:const|let|var)\s+\w+\s*=\s*await\s+(?:prisma|db)\.(\w+)\.find/g;
|
|
118
|
+
let match;
|
|
119
|
+
while ((match = sequentialQueryPattern.exec(content)) !== null) {
|
|
120
|
+
const model1 = match[1];
|
|
121
|
+
const model2 = match[2];
|
|
122
|
+
// Check if these might be related (simple heuristic)
|
|
123
|
+
const surroundingCode = content.substring(match.index, match.index + 500);
|
|
124
|
+
const usesRelation = new RegExp(`${model1}.*${model2}|${model2}.*${model1}|\\b${model1}Id\\b|\\b${model2}Id\\b`, "i").test(surroundingCode);
|
|
125
|
+
if (usesRelation) {
|
|
126
|
+
issues.push({
|
|
127
|
+
rule: "n-plus-one-sequential",
|
|
128
|
+
message: `Sequential queries for ${model1} and ${model2} (may be related)`,
|
|
129
|
+
file: relativePath,
|
|
130
|
+
line: getLineNumber(content, match.index),
|
|
131
|
+
severity: "info",
|
|
132
|
+
suggestion: "Consider using include to fetch related data in one query",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Pattern 3: Promise.all with individual queries
|
|
137
|
+
const promiseAllQueriesPattern = /Promise\.all\s*\(\s*\[[^\]]*\.map\s*\([^)]*=>[^)]*(?:prisma|db)\.\w+\.find/g;
|
|
138
|
+
while ((match = promiseAllQueriesPattern.exec(content)) !== null) {
|
|
139
|
+
issues.push({
|
|
140
|
+
rule: "n-plus-one-promise-all",
|
|
141
|
+
message: "Promise.all with mapped queries (N+1 pattern)",
|
|
142
|
+
file: relativePath,
|
|
143
|
+
line: getLineNumber(content, match.index),
|
|
144
|
+
severity: "warning",
|
|
145
|
+
suggestion: "Use findMany with where: { id: { in: ids } } instead",
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return issues;
|
|
150
|
+
}
|
|
151
|
+
async function checkMissingIncludes() {
|
|
152
|
+
const issues = [];
|
|
153
|
+
const files = await (0, glob_1.glob)([
|
|
154
|
+
"app/**/*.ts",
|
|
155
|
+
"app/**/*.tsx",
|
|
156
|
+
"lib/**/*.ts",
|
|
157
|
+
], {
|
|
158
|
+
cwd: process.cwd(),
|
|
159
|
+
ignore: ["**/node_modules/**", "**/*.test.ts", "**/*.spec.ts"],
|
|
160
|
+
absolute: true,
|
|
161
|
+
});
|
|
162
|
+
for (const file of files) {
|
|
163
|
+
if (!fs.existsSync(file))
|
|
164
|
+
continue;
|
|
165
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
166
|
+
const relativePath = path.relative(process.cwd(), file);
|
|
167
|
+
// Find queries that access relations without include
|
|
168
|
+
// Pattern: findUnique/findFirst followed by relation access
|
|
169
|
+
const queryWithRelationAccessPattern = /(?:const|let)\s+(\w+)\s*=\s*await\s+(?:prisma|db)\.(\w+)\.(?:findUnique|findFirst|findMany)\s*\([^)]*\)[^;]*;[^}]*\1\.(\w+)\./g;
|
|
170
|
+
let match;
|
|
171
|
+
while ((match = queryWithRelationAccessPattern.exec(content)) !== null) {
|
|
172
|
+
const varName = match[1];
|
|
173
|
+
const model = match[2];
|
|
174
|
+
const relationAccess = match[3];
|
|
175
|
+
// Check if the query has include
|
|
176
|
+
const querySection = content.substring(match.index, match.index + 300);
|
|
177
|
+
const hasInclude = /include\s*:/.test(querySection);
|
|
178
|
+
if (!hasInclude && relationAccess !== "id" && relationAccess !== "createdAt" && relationAccess !== "updatedAt") {
|
|
179
|
+
issues.push({
|
|
180
|
+
rule: "missing-include",
|
|
181
|
+
message: `Query on ${model} accesses .${relationAccess} without include`,
|
|
182
|
+
file: relativePath,
|
|
183
|
+
line: getLineNumber(content, match.index),
|
|
184
|
+
severity: "info",
|
|
185
|
+
suggestion: `Add include: { ${relationAccess}: true } to avoid additional query`,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return issues;
|
|
191
|
+
}
|
|
192
|
+
async function checkOverFetching() {
|
|
193
|
+
const issues = [];
|
|
194
|
+
const files = await (0, glob_1.glob)([
|
|
195
|
+
"app/**/*.ts",
|
|
196
|
+
"app/**/*.tsx",
|
|
197
|
+
"lib/**/*.ts",
|
|
198
|
+
], {
|
|
199
|
+
cwd: process.cwd(),
|
|
200
|
+
ignore: ["**/node_modules/**", "**/*.test.ts", "**/*.spec.ts"],
|
|
201
|
+
absolute: true,
|
|
202
|
+
});
|
|
203
|
+
for (const file of files) {
|
|
204
|
+
if (!fs.existsSync(file))
|
|
205
|
+
continue;
|
|
206
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
207
|
+
const relativePath = path.relative(process.cwd(), file);
|
|
208
|
+
// Check for findMany without take/limit
|
|
209
|
+
const findManyWithoutLimitPattern = /(?:prisma|db)\.\w+\.findMany\s*\(\s*\{(?![^}]*(?:take|cursor|limit))[^}]*\}\s*\)/g;
|
|
210
|
+
let match;
|
|
211
|
+
while ((match = findManyWithoutLimitPattern.exec(content)) !== null) {
|
|
212
|
+
// Skip if it's a count or aggregate
|
|
213
|
+
if (/count|aggregate/i.test(match[0]))
|
|
214
|
+
continue;
|
|
215
|
+
issues.push({
|
|
216
|
+
rule: "findmany-no-limit",
|
|
217
|
+
message: "findMany without take/limit may return unbounded results",
|
|
218
|
+
file: relativePath,
|
|
219
|
+
line: getLineNumber(content, match.index),
|
|
220
|
+
severity: "info",
|
|
221
|
+
suggestion: "Add take: N to limit results, or use cursor pagination",
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
// Check for queries that could use select but fetch all fields
|
|
225
|
+
const findWithoutSelectPattern = /(?:prisma|db)\.\w+\.(?:findUnique|findFirst|findMany)\s*\(\s*\{(?![^}]*select)[^}]*\}\s*\)/g;
|
|
226
|
+
let queryCount = 0;
|
|
227
|
+
while ((match = findWithoutSelectPattern.exec(content)) !== null) {
|
|
228
|
+
queryCount++;
|
|
229
|
+
}
|
|
230
|
+
// Only warn if there are many queries without select
|
|
231
|
+
if (queryCount > 5) {
|
|
232
|
+
issues.push({
|
|
233
|
+
rule: "consider-select",
|
|
234
|
+
message: `${queryCount} queries without select (may be over-fetching)`,
|
|
235
|
+
file: relativePath,
|
|
236
|
+
severity: "info",
|
|
237
|
+
suggestion: "Use select to fetch only needed fields for better performance",
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return issues;
|
|
242
|
+
}
|
|
243
|
+
async function checkTransactionSafety() {
|
|
244
|
+
const issues = [];
|
|
245
|
+
const files = await (0, glob_1.glob)([
|
|
246
|
+
"app/api/**/*.ts",
|
|
247
|
+
"lib/**/*.ts",
|
|
248
|
+
], {
|
|
249
|
+
cwd: process.cwd(),
|
|
250
|
+
ignore: ["**/node_modules/**", "**/*.test.ts", "**/*.spec.ts"],
|
|
251
|
+
absolute: true,
|
|
252
|
+
});
|
|
253
|
+
for (const file of files) {
|
|
254
|
+
if (!fs.existsSync(file))
|
|
255
|
+
continue;
|
|
256
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
257
|
+
const relativePath = path.relative(process.cwd(), file);
|
|
258
|
+
// Check for multiple writes without transaction
|
|
259
|
+
// Pattern: create followed by another create/update without $transaction
|
|
260
|
+
const multiWritePattern = /await\s+(?:prisma|db)\.\w+\.(?:create|update|delete)\s*\([^;]+;\s*await\s+(?:prisma|db)\.\w+\.(?:create|update|delete)/g;
|
|
261
|
+
let match;
|
|
262
|
+
while ((match = multiWritePattern.exec(content)) !== null) {
|
|
263
|
+
// Check if wrapped in transaction
|
|
264
|
+
const beforeCode = content.substring(Math.max(0, match.index - 200), match.index);
|
|
265
|
+
const isInTransaction = /\$transaction|transaction\(|BEGIN/i.test(beforeCode);
|
|
266
|
+
if (!isInTransaction) {
|
|
267
|
+
issues.push({
|
|
268
|
+
rule: "multi-write-no-transaction",
|
|
269
|
+
message: "Multiple database writes without transaction",
|
|
270
|
+
file: relativePath,
|
|
271
|
+
line: getLineNumber(content, match.index),
|
|
272
|
+
severity: "warning",
|
|
273
|
+
suggestion: "Wrap related writes in prisma.$transaction() for atomicity",
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Check for create + update pattern (common for counters)
|
|
278
|
+
const createUpdatePattern = /\.create\s*\([^)]+\)[^;]*;[^}]*\.update\s*\([^)]+increment/gi;
|
|
279
|
+
while ((match = createUpdatePattern.exec(content)) !== null) {
|
|
280
|
+
const beforeCode = content.substring(Math.max(0, match.index - 200), match.index);
|
|
281
|
+
if (!/\$transaction/i.test(beforeCode)) {
|
|
282
|
+
issues.push({
|
|
283
|
+
rule: "create-update-no-transaction",
|
|
284
|
+
message: "Create followed by update/increment without transaction",
|
|
285
|
+
file: relativePath,
|
|
286
|
+
line: getLineNumber(content, match.index),
|
|
287
|
+
severity: "warning",
|
|
288
|
+
suggestion: "Use $transaction to ensure atomicity of create + update",
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return issues;
|
|
294
|
+
}
|
|
295
|
+
async function checkCascadeOperations() {
|
|
296
|
+
const issues = [];
|
|
297
|
+
// Check Prisma schema for cascade deletes
|
|
298
|
+
const schemaPath = path.join(process.cwd(), "prisma", "schema.prisma");
|
|
299
|
+
if (fs.existsSync(schemaPath)) {
|
|
300
|
+
const content = fs.readFileSync(schemaPath, "utf-8");
|
|
301
|
+
// Find cascade delete relations
|
|
302
|
+
const cascadeDeletePattern = /onDelete:\s*Cascade/gi;
|
|
303
|
+
let match;
|
|
304
|
+
let cascadeCount = 0;
|
|
305
|
+
while ((match = cascadeDeletePattern.exec(content)) !== null) {
|
|
306
|
+
cascadeCount++;
|
|
307
|
+
// Check the context - which model/field
|
|
308
|
+
const lineStart = content.lastIndexOf("\n", match.index) + 1;
|
|
309
|
+
const lineEnd = content.indexOf("\n", match.index);
|
|
310
|
+
const line = content.substring(lineStart, lineEnd);
|
|
311
|
+
// Extract relation info if possible
|
|
312
|
+
const relationMatch = line.match(/(\w+)\s+(\w+)/);
|
|
313
|
+
if (relationMatch) {
|
|
314
|
+
issues.push({
|
|
315
|
+
rule: "cascade-delete-review",
|
|
316
|
+
message: `Cascade delete configured (ensure this is intentional)`,
|
|
317
|
+
file: "prisma/schema.prisma",
|
|
318
|
+
line: getLineNumber(content, match.index),
|
|
319
|
+
severity: "info",
|
|
320
|
+
suggestion: "Verify cascade delete won't accidentally remove important data",
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (cascadeCount > 5) {
|
|
325
|
+
issues.push({
|
|
326
|
+
rule: "many-cascade-deletes",
|
|
327
|
+
message: `${cascadeCount} cascade delete relations - review for safety`,
|
|
328
|
+
file: "prisma/schema.prisma",
|
|
329
|
+
severity: "warning",
|
|
330
|
+
suggestion: "Consider soft deletes for important data preservation",
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Check for deleteMany without where clause
|
|
335
|
+
const files = await (0, glob_1.glob)([
|
|
336
|
+
"app/api/**/*.ts",
|
|
337
|
+
"lib/**/*.ts",
|
|
338
|
+
], {
|
|
339
|
+
cwd: process.cwd(),
|
|
340
|
+
ignore: ["**/node_modules/**"],
|
|
341
|
+
absolute: true,
|
|
342
|
+
});
|
|
343
|
+
for (const file of files) {
|
|
344
|
+
if (!fs.existsSync(file))
|
|
345
|
+
continue;
|
|
346
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
347
|
+
const relativePath = path.relative(process.cwd(), file);
|
|
348
|
+
// Check for deleteMany with empty or minimal where
|
|
349
|
+
const deleteManyPattern = /\.deleteMany\s*\(\s*(?:\{\s*\}|\{\s*where\s*:\s*\{\s*\}\s*\})\s*\)/g;
|
|
350
|
+
let match;
|
|
351
|
+
while ((match = deleteManyPattern.exec(content)) !== null) {
|
|
352
|
+
issues.push({
|
|
353
|
+
rule: "delete-many-all",
|
|
354
|
+
message: "deleteMany with empty where clause deletes ALL records",
|
|
355
|
+
file: relativePath,
|
|
356
|
+
line: getLineNumber(content, match.index),
|
|
357
|
+
severity: "error",
|
|
358
|
+
suggestion: "Add specific where conditions or use delete for single record",
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return issues;
|
|
363
|
+
}
|
|
364
|
+
async function checkIndexUsage() {
|
|
365
|
+
const issues = [];
|
|
366
|
+
// Parse Prisma schema for indexes
|
|
367
|
+
const schemaPath = path.join(process.cwd(), "prisma", "schema.prisma");
|
|
368
|
+
if (!fs.existsSync(schemaPath)) {
|
|
369
|
+
return issues;
|
|
370
|
+
}
|
|
371
|
+
const schemaContent = fs.readFileSync(schemaPath, "utf-8");
|
|
372
|
+
// Extract indexed fields
|
|
373
|
+
const indexedFields = new Set();
|
|
374
|
+
const uniqueFields = new Set();
|
|
375
|
+
// @@index([field1, field2])
|
|
376
|
+
const indexPattern = /@@index\s*\(\s*\[([^\]]+)\]/g;
|
|
377
|
+
let match;
|
|
378
|
+
while ((match = indexPattern.exec(schemaContent)) !== null) {
|
|
379
|
+
const fields = match[1].split(",").map(f => f.trim());
|
|
380
|
+
fields.forEach(f => indexedFields.add(f));
|
|
381
|
+
}
|
|
382
|
+
// @@unique([field])
|
|
383
|
+
const uniquePattern = /@@unique\s*\(\s*\[([^\]]+)\]/g;
|
|
384
|
+
while ((match = uniquePattern.exec(schemaContent)) !== null) {
|
|
385
|
+
const fields = match[1].split(",").map(f => f.trim());
|
|
386
|
+
fields.forEach(f => uniqueFields.add(f));
|
|
387
|
+
}
|
|
388
|
+
// @unique
|
|
389
|
+
const fieldUniquePattern = /(\w+)\s+\w+.*@unique/g;
|
|
390
|
+
while ((match = fieldUniquePattern.exec(schemaContent)) !== null) {
|
|
391
|
+
uniqueFields.add(match[1]);
|
|
392
|
+
}
|
|
393
|
+
// @id is always indexed
|
|
394
|
+
const idPattern = /(\w+)\s+\w+.*@id/g;
|
|
395
|
+
while ((match = idPattern.exec(schemaContent)) !== null) {
|
|
396
|
+
indexedFields.add(match[1]);
|
|
397
|
+
}
|
|
398
|
+
// Now check queries for common filter patterns
|
|
399
|
+
const files = await (0, glob_1.glob)([
|
|
400
|
+
"app/api/**/*.ts",
|
|
401
|
+
"lib/**/*.ts",
|
|
402
|
+
], {
|
|
403
|
+
cwd: process.cwd(),
|
|
404
|
+
ignore: ["**/node_modules/**"],
|
|
405
|
+
absolute: true,
|
|
406
|
+
});
|
|
407
|
+
const filterFieldCounts = new Map();
|
|
408
|
+
for (const file of files) {
|
|
409
|
+
if (!fs.existsSync(file))
|
|
410
|
+
continue;
|
|
411
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
412
|
+
// Find where clauses
|
|
413
|
+
const wherePattern = /where\s*:\s*\{\s*(\w+)\s*:/g;
|
|
414
|
+
while ((match = wherePattern.exec(content)) !== null) {
|
|
415
|
+
const field = match[1];
|
|
416
|
+
// Skip common indexed fields
|
|
417
|
+
if (field === "id" || field === "AND" || field === "OR" || field === "NOT")
|
|
418
|
+
continue;
|
|
419
|
+
filterFieldCounts.set(field, (filterFieldCounts.get(field) || 0) + 1);
|
|
420
|
+
}
|
|
421
|
+
// Find orderBy
|
|
422
|
+
const orderByPattern = /orderBy\s*:\s*\{\s*(\w+)\s*:/g;
|
|
423
|
+
while ((match = orderByPattern.exec(content)) !== null) {
|
|
424
|
+
const field = match[1];
|
|
425
|
+
filterFieldCounts.set(`orderBy:${field}`, (filterFieldCounts.get(`orderBy:${field}`) || 0) + 1);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
// Report frequently used non-indexed fields
|
|
429
|
+
for (const [field, count] of filterFieldCounts) {
|
|
430
|
+
if (count >= 3 && !indexedFields.has(field) && !uniqueFields.has(field)) {
|
|
431
|
+
const isOrderBy = field.startsWith("orderBy:");
|
|
432
|
+
const fieldName = isOrderBy ? field.replace("orderBy:", "") : field;
|
|
433
|
+
issues.push({
|
|
434
|
+
rule: "consider-index",
|
|
435
|
+
message: `Field "${fieldName}" used in ${count} ${isOrderBy ? "orderBy" : "where"} clauses but not indexed`,
|
|
436
|
+
file: "prisma/schema.prisma",
|
|
437
|
+
severity: "info",
|
|
438
|
+
suggestion: `Consider adding @@index([${fieldName}]) for query performance`,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return issues;
|
|
443
|
+
}
|
|
444
|
+
async function run() {
|
|
445
|
+
const startTime = Date.now();
|
|
446
|
+
const allIssues = [];
|
|
447
|
+
// Run all query performance checks
|
|
448
|
+
allIssues.push(...await checkNPlusOneQueries());
|
|
449
|
+
allIssues.push(...await checkMissingIncludes());
|
|
450
|
+
allIssues.push(...await checkOverFetching());
|
|
451
|
+
allIssues.push(...await checkTransactionSafety());
|
|
452
|
+
allIssues.push(...await checkCascadeOperations());
|
|
453
|
+
allIssues.push(...await checkIndexUsage());
|
|
454
|
+
// Convert to findings
|
|
455
|
+
const findings = allIssues.map(issue => ({
|
|
456
|
+
level: issue.severity,
|
|
457
|
+
message: issue.message,
|
|
458
|
+
file: issue.file,
|
|
459
|
+
startLine: issue.line,
|
|
460
|
+
ruleId: issue.rule,
|
|
461
|
+
suggestion: issue.suggestion,
|
|
462
|
+
}));
|
|
463
|
+
const errors = findings.filter(f => f.level === "error");
|
|
464
|
+
const warnings = findings.filter(f => f.level === "warning");
|
|
465
|
+
const infos = findings.filter(f => f.level === "info");
|
|
466
|
+
return {
|
|
467
|
+
passed: errors.length === 0,
|
|
468
|
+
findings,
|
|
469
|
+
duration: Date.now() - startTime,
|
|
470
|
+
metadata: {
|
|
471
|
+
errors: errors.length,
|
|
472
|
+
warnings: warnings.length,
|
|
473
|
+
infos: infos.length,
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
async function main() {
|
|
478
|
+
console.log(`\n${console_chars_1.emoji.database} QUERY PERFORMANCE VALIDATION`);
|
|
479
|
+
console.log((0, console_chars_1.createDivider)(65, "heavy"));
|
|
480
|
+
const result = await run();
|
|
481
|
+
const { errors, warnings, infos } = result.metadata || {};
|
|
482
|
+
console.log(`\n${console_chars_1.emoji.search} Checking query patterns...`);
|
|
483
|
+
console.log(`\n${console_chars_1.emoji.chart} Summary:`);
|
|
484
|
+
console.log(` Errors: ${errors}`);
|
|
485
|
+
console.log(` Warnings: ${warnings}`);
|
|
486
|
+
console.log(` Info: ${infos}`);
|
|
487
|
+
if (result.passed && warnings === 0) {
|
|
488
|
+
console.log(`\n${console_chars_1.emoji.success} QUERY PERFORMANCE VALIDATION PASSED`);
|
|
489
|
+
console.log(`\nNo query performance issues detected.`);
|
|
490
|
+
process.exit(0);
|
|
491
|
+
}
|
|
492
|
+
// Group findings by rule
|
|
493
|
+
const findingsByRule = new Map();
|
|
494
|
+
for (const finding of result.findings) {
|
|
495
|
+
const ruleId = finding.ruleId || "unknown";
|
|
496
|
+
if (!findingsByRule.has(ruleId)) {
|
|
497
|
+
findingsByRule.set(ruleId, []);
|
|
498
|
+
}
|
|
499
|
+
findingsByRule.get(ruleId).push(finding);
|
|
500
|
+
}
|
|
501
|
+
// Print errors
|
|
502
|
+
const errorFindings = result.findings.filter(f => f.level === "error");
|
|
503
|
+
if (errorFindings.length > 0) {
|
|
504
|
+
console.log(`\n${console_chars_1.emoji.error} Errors (blocking):`);
|
|
505
|
+
for (const finding of errorFindings) {
|
|
506
|
+
console.log(`\n ${finding.file}${finding.startLine ? `:${finding.startLine}` : ""}`);
|
|
507
|
+
console.log(` ${finding.message}`);
|
|
508
|
+
if (finding.suggestion) {
|
|
509
|
+
console.log(` ${console_chars_1.emoji.hint} ${finding.suggestion}`);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
// Print warnings
|
|
514
|
+
const warningFindings = result.findings.filter(f => f.level === "warning");
|
|
515
|
+
if (warningFindings.length > 0) {
|
|
516
|
+
console.log(`\n${console_chars_1.emoji.warning} Warnings:`);
|
|
517
|
+
for (const finding of warningFindings.slice(0, 10)) {
|
|
518
|
+
console.log(`\n ${finding.file}${finding.startLine ? `:${finding.startLine}` : ""}`);
|
|
519
|
+
console.log(` ${finding.message}`);
|
|
520
|
+
}
|
|
521
|
+
if (warningFindings.length > 10) {
|
|
522
|
+
console.log(`\n ... and ${warningFindings.length - 10} more warnings`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
console.log(`\n${console_chars_1.emoji.info} Query Performance Tips:`);
|
|
526
|
+
console.log(` - Avoid queries in loops (N+1 problem)`);
|
|
527
|
+
console.log(` - Use include to fetch relations in one query`);
|
|
528
|
+
console.log(` - Add indexes for frequently filtered fields`);
|
|
529
|
+
console.log(` - Wrap related writes in transactions`);
|
|
530
|
+
console.log(` - Use take/cursor for pagination`);
|
|
531
|
+
if (!result.passed) {
|
|
532
|
+
console.log(`\n${console_chars_1.emoji.error} QUERY PERFORMANCE VALIDATION FAILED`);
|
|
533
|
+
process.exit(1);
|
|
534
|
+
}
|
|
535
|
+
console.log(`\n${console_chars_1.emoji.success} QUERY PERFORMANCE VALIDATION PASSED`);
|
|
536
|
+
process.exit(0);
|
|
537
|
+
}
|
|
538
|
+
if (require.main === module) {
|
|
539
|
+
main().catch((err) => {
|
|
540
|
+
console.error(`${console_chars_1.emoji.error} Preflight failed:`, err);
|
|
541
|
+
process.exit(1);
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
//# sourceMappingURL=query-performance-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-performance-validation.js","sourceRoot":"","sources":["../../../src/checks/database/query-performance-validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { PreflightCheckResult } from "../../core/types";
|
|
3
|
+
export declare const id = "react/react-patterns-validation";
|
|
4
|
+
export declare const name = "React Patterns Validation";
|
|
5
|
+
export declare const description = "Validates React patterns, hooks, Server Components, and performance best practices";
|
|
6
|
+
export declare const category = "react";
|
|
7
|
+
export declare const blocking = true;
|
|
8
|
+
export declare const tags: string[];
|
|
9
|
+
export declare function run(): Promise<PreflightCheckResult>;
|
|
10
|
+
//# sourceMappingURL=react-patterns-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-patterns-validation.d.ts","sourceRoot":"","sources":["../../../src/checks/react/react-patterns-validation.ts"],"names":[],"mappings":";AAmCA,OAAO,EAAE,oBAAoB,EAAoB,MAAM,kBAAkB,CAAC;AAG1E,eAAO,MAAM,EAAE,oCAAoC,CAAC;AACpD,eAAO,MAAM,IAAI,8BAA8B,CAAC;AAChD,eAAO,MAAM,WAAW,uFAAuF,CAAC;AAChH,eAAO,MAAM,QAAQ,UAAU,CAAC;AAChC,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,IAAI,UAAqE,CAAC;AAkcvF,wBAAsB,GAAG,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAoCzD"}
|