@empline/preflight 1.1.14 → 1.1.15
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/auth/session-provider-wrapper.d.ts +47 -0
- package/dist/checks/auth/session-provider-wrapper.d.ts.map +1 -0
- package/dist/checks/auth/session-provider-wrapper.js +286 -0
- package/dist/checks/auth/session-provider-wrapper.js.map +1 -0
- package/dist/checks/database/prisma-upsert-safety.d.ts +39 -0
- package/dist/checks/database/prisma-upsert-safety.d.ts.map +1 -0
- package/dist/checks/database/prisma-upsert-safety.js +220 -0
- package/dist/checks/database/prisma-upsert-safety.js.map +1 -0
- package/dist/checks/dependencies/dependency-health-monitor.d.ts +49 -0
- package/dist/checks/dependencies/dependency-health-monitor.d.ts.map +1 -0
- package/dist/checks/dependencies/dependency-health-monitor.js +323 -0
- package/dist/checks/dependencies/dependency-health-monitor.js.map +1 -0
- package/dist/checks/file-hygiene-validation.d.ts +31 -0
- package/dist/checks/file-hygiene-validation.d.ts.map +1 -0
- package/dist/checks/file-hygiene-validation.js +934 -0
- package/dist/checks/file-hygiene-validation.js.map +1 -0
- package/dist/checks/organization/file-cleanup-validation.d.ts +22 -0
- package/dist/checks/organization/file-cleanup-validation.d.ts.map +1 -0
- package/dist/checks/organization/file-cleanup-validation.js +1121 -0
- package/dist/checks/organization/file-cleanup-validation.js.map +1 -0
- package/dist/checks/runtime/tailwind-runtime-check.d.ts +36 -0
- package/dist/checks/runtime/tailwind-runtime-check.d.ts.map +1 -0
- package/dist/checks/runtime/tailwind-runtime-check.js +264 -0
- package/dist/checks/runtime/tailwind-runtime-check.js.map +1 -0
- package/dist/checks/shipping-integration-validation.d.ts +28 -0
- package/dist/checks/shipping-integration-validation.d.ts.map +1 -0
- package/dist/checks/shipping-integration-validation.js +409 -0
- package/dist/checks/shipping-integration-validation.js.map +1 -0
- package/dist/checks/system/layout-constants-sync.d.ts +36 -0
- package/dist/checks/system/layout-constants-sync.d.ts.map +1 -0
- package/dist/checks/system/layout-constants-sync.js +642 -0
- package/dist/checks/system/layout-constants-sync.js.map +1 -0
- package/dist/checks/system/preflight-circular-dependency-detector.d.ts +26 -0
- package/dist/checks/system/preflight-circular-dependency-detector.d.ts.map +1 -0
- package/dist/checks/system/preflight-circular-dependency-detector.js +310 -0
- package/dist/checks/system/preflight-circular-dependency-detector.js.map +1 -0
- package/dist/checks/system/preflight-execution-benchmarks.d.ts +24 -0
- package/dist/checks/system/preflight-execution-benchmarks.d.ts.map +1 -0
- package/dist/checks/system/preflight-execution-benchmarks.js +282 -0
- package/dist/checks/system/preflight-execution-benchmarks.js.map +1 -0
- package/dist/checks/system/preflight-tag-taxonomy-validator.d.ts +27 -0
- package/dist/checks/system/preflight-tag-taxonomy-validator.d.ts.map +1 -0
- package/dist/checks/system/preflight-tag-taxonomy-validator.js +361 -0
- package/dist/checks/system/preflight-tag-taxonomy-validator.js.map +1 -0
- package/dist/utils/console-chars.d.ts +16 -0
- package/dist/utils/console-chars.d.ts.map +1 -1
- package/dist/utils/console-chars.js +10 -0
- package/dist/utils/console-chars.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preflight: Session Provider Wrapper Check
|
|
3
|
+
*
|
|
4
|
+
* Detects useSession() calls in components that may not be wrapped in SessionProvider.
|
|
5
|
+
* This catches the common error: "[next-auth]: useSession must be wrapped in a <SessionProvider />"
|
|
6
|
+
*
|
|
7
|
+
* Checks:
|
|
8
|
+
* 1. Files using useSession() must have "use client" directive
|
|
9
|
+
* 2. Files using useSession() should not be in locations that bypass the provider tree
|
|
10
|
+
* 3. Warns about useSession() in error boundaries or fallback components
|
|
11
|
+
* 4. Detects deprecated getSession() usage (should use auth() server-side)
|
|
12
|
+
* 5. Detects conditional SessionProvider wrapping patterns
|
|
13
|
+
* 6. Detects useSession in server components (async function exports without "use client")
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* pnpm preflight:session-provider-wrapper
|
|
17
|
+
* pnpm preflight:session-provider-wrapper --fix # Auto-fix missing "use client"
|
|
18
|
+
*
|
|
19
|
+
* @blocking - This check blocks builds because missing SessionProvider causes runtime crashes
|
|
20
|
+
*/
|
|
21
|
+
export declare const id = "auth/session-provider-wrapper";
|
|
22
|
+
export declare const name = "Session Provider Wrapper";
|
|
23
|
+
export declare const category = "auth";
|
|
24
|
+
export declare const blocking = true;
|
|
25
|
+
export declare const description = "Preflight: Session Provider Wrapper Check";
|
|
26
|
+
export declare const tags: string[];
|
|
27
|
+
interface Issue {
|
|
28
|
+
file: string;
|
|
29
|
+
line: number;
|
|
30
|
+
type: "error" | "warning";
|
|
31
|
+
code: string;
|
|
32
|
+
message: string;
|
|
33
|
+
suggestion?: string;
|
|
34
|
+
fixable?: boolean;
|
|
35
|
+
}
|
|
36
|
+
interface CheckResult {
|
|
37
|
+
issues: Issue[];
|
|
38
|
+
fixApplied?: boolean;
|
|
39
|
+
}
|
|
40
|
+
declare function checkFile(filePath: string, autoFix?: boolean): CheckResult;
|
|
41
|
+
export declare function run(): Promise<{
|
|
42
|
+
errors: number;
|
|
43
|
+
warnings: number;
|
|
44
|
+
fixed: number;
|
|
45
|
+
}>;
|
|
46
|
+
export { checkFile };
|
|
47
|
+
//# sourceMappingURL=session-provider-wrapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-provider-wrapper.d.ts","sourceRoot":"","sources":["../../../src/checks/auth/session-provider-wrapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAWH,eAAO,MAAM,EAAE,kCAAkC,CAAC;AAClD,eAAO,MAAM,IAAI,6BAA6B,CAAC;AAC/C,eAAO,MAAM,QAAQ,SAAS,CAAC;AAC/B,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,WAAW,8CAA8C,CAAC;AACvE,eAAO,MAAM,IAAI,UAAW,CAAC;AA2C7B,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAkBD,iBAAS,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,OAAe,GAAG,WAAW,CA2H1E;AAED,wBAAsB,GAAG,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CA0ExF;AAYD,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Preflight: Session Provider Wrapper Check
|
|
4
|
+
*
|
|
5
|
+
* Detects useSession() calls in components that may not be wrapped in SessionProvider.
|
|
6
|
+
* This catches the common error: "[next-auth]: useSession must be wrapped in a <SessionProvider />"
|
|
7
|
+
*
|
|
8
|
+
* Checks:
|
|
9
|
+
* 1. Files using useSession() must have "use client" directive
|
|
10
|
+
* 2. Files using useSession() should not be in locations that bypass the provider tree
|
|
11
|
+
* 3. Warns about useSession() in error boundaries or fallback components
|
|
12
|
+
* 4. Detects deprecated getSession() usage (should use auth() server-side)
|
|
13
|
+
* 5. Detects conditional SessionProvider wrapping patterns
|
|
14
|
+
* 6. Detects useSession in server components (async function exports without "use client")
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* pnpm preflight:session-provider-wrapper
|
|
18
|
+
* pnpm preflight:session-provider-wrapper --fix # Auto-fix missing "use client"
|
|
19
|
+
*
|
|
20
|
+
* @blocking - This check blocks builds because missing SessionProvider causes runtime crashes
|
|
21
|
+
*/
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.tags = exports.description = exports.blocking = exports.category = exports.name = exports.id = void 0;
|
|
27
|
+
exports.run = run;
|
|
28
|
+
exports.checkFile = checkFile;
|
|
29
|
+
const fs_1 = __importDefault(require("fs"));
|
|
30
|
+
const path_1 = __importDefault(require("path"));
|
|
31
|
+
const console_chars_1 = require("../../utils/console-chars");
|
|
32
|
+
const glob_1 = require("glob");
|
|
33
|
+
const universal_progress_reporter_1 = require("../system/universal-progress-reporter");
|
|
34
|
+
// Check metadata
|
|
35
|
+
exports.id = "auth/session-provider-wrapper";
|
|
36
|
+
exports.name = "Session Provider Wrapper";
|
|
37
|
+
exports.category = "auth";
|
|
38
|
+
exports.blocking = true;
|
|
39
|
+
exports.description = "Preflight: Session Provider Wrapper Check";
|
|
40
|
+
exports.tags = ["auth"];
|
|
41
|
+
const SCAN_PATTERNS = [
|
|
42
|
+
"app/**/*.tsx",
|
|
43
|
+
"app/**/*.ts",
|
|
44
|
+
"components/**/*.tsx",
|
|
45
|
+
"components/**/*.ts",
|
|
46
|
+
"contexts/**/*.tsx",
|
|
47
|
+
"hooks/**/*.tsx",
|
|
48
|
+
"hooks/**/*.ts",
|
|
49
|
+
"lib/**/*.tsx",
|
|
50
|
+
"lib/**/*.ts",
|
|
51
|
+
];
|
|
52
|
+
const EXCLUDE_PATTERNS = [
|
|
53
|
+
"**/node_modules/**",
|
|
54
|
+
"**/.next/**",
|
|
55
|
+
"**/dist/**",
|
|
56
|
+
"**/coverage/**",
|
|
57
|
+
"**/*.test.tsx",
|
|
58
|
+
"**/*.test.ts",
|
|
59
|
+
"**/*.spec.tsx",
|
|
60
|
+
"**/*.spec.ts",
|
|
61
|
+
"**/__tests__/**",
|
|
62
|
+
"**/__mocks__/**",
|
|
63
|
+
];
|
|
64
|
+
const UNSAFE_LOCATIONS = [
|
|
65
|
+
{ pattern: /error\.tsx$/, reason: "Error boundaries render outside the normal component tree" },
|
|
66
|
+
{ pattern: /not-found\.tsx$/, reason: "Not-found pages may render before providers are mounted" },
|
|
67
|
+
{
|
|
68
|
+
pattern: /loading\.tsx$/,
|
|
69
|
+
reason: "Loading states render during Suspense, before providers may be ready",
|
|
70
|
+
},
|
|
71
|
+
{ pattern: /global-error\.tsx$/, reason: "Global error boundary renders at the root level" },
|
|
72
|
+
];
|
|
73
|
+
const CONDITIONAL_PROVIDER_PATTERNS = [
|
|
74
|
+
/\{[^}]*&&\s*<SessionProvider/,
|
|
75
|
+
/\{[^}]*\?\s*<SessionProvider/,
|
|
76
|
+
/<SessionProvider[^>]*>\s*\{[^}]*&&/,
|
|
77
|
+
];
|
|
78
|
+
function findLineNumber(content, searchStr) {
|
|
79
|
+
const lines = content.split("\n");
|
|
80
|
+
for (let i = 0; i < lines.length; i++) {
|
|
81
|
+
if (lines[i].includes(searchStr))
|
|
82
|
+
return i + 1;
|
|
83
|
+
}
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
86
|
+
function isServerComponent(content) {
|
|
87
|
+
const hasUseClient = /^["']use client["'];?\s*$/m.test(content);
|
|
88
|
+
if (hasUseClient)
|
|
89
|
+
return false;
|
|
90
|
+
const hasAsyncExport = /export\s+(default\s+)?async\s+function/.test(content);
|
|
91
|
+
const hasDefaultExport = /export\s+default/.test(content);
|
|
92
|
+
return hasAsyncExport || hasDefaultExport;
|
|
93
|
+
}
|
|
94
|
+
function checkFile(filePath, autoFix = false) {
|
|
95
|
+
const issues = [];
|
|
96
|
+
let content = fs_1.default.readFileSync(filePath, "utf-8");
|
|
97
|
+
let fixApplied = false;
|
|
98
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
99
|
+
const useSessionMatch = content.match(/useSession\s*\(/);
|
|
100
|
+
if (useSessionMatch) {
|
|
101
|
+
const useSessionLine = findLineNumber(content, "useSession");
|
|
102
|
+
const hasUseClient = /^["']use client["'];?\s*$/m.test(content);
|
|
103
|
+
if (!hasUseClient) {
|
|
104
|
+
if (isServerComponent(content)) {
|
|
105
|
+
issues.push({
|
|
106
|
+
file: filePath,
|
|
107
|
+
line: useSessionLine,
|
|
108
|
+
type: "error",
|
|
109
|
+
code: "USE_SESSION_IN_SERVER_COMPONENT",
|
|
110
|
+
message: "useSession() called in what appears to be a Server Component",
|
|
111
|
+
suggestion: "Use server-side auth() from @/lib/auth instead of useSession()",
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
issues.push({
|
|
116
|
+
file: filePath,
|
|
117
|
+
line: useSessionLine,
|
|
118
|
+
type: "error",
|
|
119
|
+
code: "MISSING_USE_CLIENT",
|
|
120
|
+
message: 'useSession() requires "use client" directive',
|
|
121
|
+
suggestion: 'Add "use client"; at the top of the file',
|
|
122
|
+
fixable: true,
|
|
123
|
+
});
|
|
124
|
+
if (autoFix) {
|
|
125
|
+
content = '"use client";\n\n' + content;
|
|
126
|
+
fs_1.default.writeFileSync(filePath, content, "utf-8");
|
|
127
|
+
fixApplied = true;
|
|
128
|
+
console.log(" " + console_chars_1.emoji.wrench + ' Fixed: Added "use client" to ' + filePath);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
for (const unsafe of UNSAFE_LOCATIONS) {
|
|
133
|
+
if (unsafe.pattern.test(normalizedPath)) {
|
|
134
|
+
issues.push({
|
|
135
|
+
file: filePath,
|
|
136
|
+
line: useSessionLine,
|
|
137
|
+
type: "error",
|
|
138
|
+
code: "USE_SESSION_UNSAFE_LOCATION",
|
|
139
|
+
message: "useSession() used in " + path_1.default.basename(filePath) + " - " + unsafe.reason,
|
|
140
|
+
suggestion: "Use server-side auth() from @/lib/auth instead, or pass session as a prop",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const providesSession = /<SessionProvider[\s>]/.test(content);
|
|
145
|
+
if (providesSession) {
|
|
146
|
+
issues.push({
|
|
147
|
+
file: filePath,
|
|
148
|
+
line: useSessionLine,
|
|
149
|
+
type: "warning",
|
|
150
|
+
code: "PROVIDER_CONSUMER_SAME_FILE",
|
|
151
|
+
message: "Component both provides SessionProvider and uses useSession()",
|
|
152
|
+
suggestion: "Split into separate provider and consumer components",
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const getSessionImport = content.match(/import\s*\{[^}]*\bgetSession\b[^}]*\}\s*from\s*["']next-auth\/react["']/);
|
|
157
|
+
const getSessionUsage = content.match(/\bgetSession\s*\(/);
|
|
158
|
+
if (getSessionImport || getSessionUsage) {
|
|
159
|
+
const line = findLineNumber(content, "getSession");
|
|
160
|
+
issues.push({
|
|
161
|
+
file: filePath,
|
|
162
|
+
line,
|
|
163
|
+
type: "error",
|
|
164
|
+
code: "DEPRECATED_GET_SESSION",
|
|
165
|
+
message: "getSession() from next-auth/react is deprecated",
|
|
166
|
+
suggestion: "Use auth() from @/lib/auth for server-side, or useSession() for client-side",
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
for (const pattern of CONDITIONAL_PROVIDER_PATTERNS) {
|
|
170
|
+
if (pattern.test(content)) {
|
|
171
|
+
const line = findLineNumber(content, "SessionProvider");
|
|
172
|
+
issues.push({
|
|
173
|
+
file: filePath,
|
|
174
|
+
line,
|
|
175
|
+
type: "error",
|
|
176
|
+
code: "CONDITIONAL_SESSION_PROVIDER",
|
|
177
|
+
message: "SessionProvider is conditionally rendered - this causes useSession errors",
|
|
178
|
+
suggestion: "Always render SessionProvider unconditionally at the app root",
|
|
179
|
+
});
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const authActionsMatch = content.match(/\b(signIn|signOut)\s*\(/);
|
|
184
|
+
if (authActionsMatch) {
|
|
185
|
+
const hasUseClient = /^["']use client["'];?\s*$/m.test(content);
|
|
186
|
+
const fromReact = content.includes('from "next-auth/react"') || content.includes("from 'next-auth/react'");
|
|
187
|
+
if (fromReact && !hasUseClient) {
|
|
188
|
+
const line = findLineNumber(content, authActionsMatch[1]);
|
|
189
|
+
issues.push({
|
|
190
|
+
file: filePath,
|
|
191
|
+
line,
|
|
192
|
+
type: "error",
|
|
193
|
+
code: "AUTH_ACTION_NO_USE_CLIENT",
|
|
194
|
+
message: authActionsMatch[1] + '() from next-auth/react requires "use client" directive',
|
|
195
|
+
suggestion: 'Add "use client"; at the top of the file, or use server actions',
|
|
196
|
+
fixable: true,
|
|
197
|
+
});
|
|
198
|
+
if (autoFix && !fixApplied) {
|
|
199
|
+
content = '"use client";\n\n' + content;
|
|
200
|
+
fs_1.default.writeFileSync(filePath, content, "utf-8");
|
|
201
|
+
fixApplied = true;
|
|
202
|
+
console.log(" " + console_chars_1.emoji.wrench + ' Fixed: Added "use client" to ' + filePath);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return { issues, fixApplied };
|
|
207
|
+
}
|
|
208
|
+
async function run() {
|
|
209
|
+
const reporter = (0, universal_progress_reporter_1.createUniversalProgressReporter)(exports.name);
|
|
210
|
+
const args = process.argv.slice(2);
|
|
211
|
+
const autoFix = args.includes("--fix");
|
|
212
|
+
console.log("\n" + console_chars_1.emoji.search + " Checking SessionProvider wrapper requirements...");
|
|
213
|
+
if (autoFix)
|
|
214
|
+
console.log(console_chars_1.emoji.wrench + " Auto-fix mode enabled\n");
|
|
215
|
+
else
|
|
216
|
+
console.log("");
|
|
217
|
+
const files = [];
|
|
218
|
+
for (const pattern of SCAN_PATTERNS) {
|
|
219
|
+
const matches = glob_1.glob.sync(pattern, { ignore: EXCLUDE_PATTERNS, nodir: true });
|
|
220
|
+
files.push(...matches);
|
|
221
|
+
}
|
|
222
|
+
const allIssues = [];
|
|
223
|
+
let totalFixed = 0;
|
|
224
|
+
for (const file of files) {
|
|
225
|
+
const result = checkFile(file, autoFix);
|
|
226
|
+
allIssues.push(...result.issues);
|
|
227
|
+
if (result.fixApplied)
|
|
228
|
+
totalFixed++;
|
|
229
|
+
}
|
|
230
|
+
const errors = allIssues.filter((i) => i.type === "error");
|
|
231
|
+
const warnings = allIssues.filter((i) => i.type === "warning");
|
|
232
|
+
const fixableCount = allIssues.filter((i) => i.fixable).length;
|
|
233
|
+
if (allIssues.length === 0) {
|
|
234
|
+
console.log(console_chars_1.emoji.success + " All session/auth patterns are correct\n");
|
|
235
|
+
console.log(" Scanned " + files.length + " files\n");
|
|
236
|
+
return { errors: 0, warnings: 0, fixed: totalFixed };
|
|
237
|
+
}
|
|
238
|
+
if (errors.length > 0) {
|
|
239
|
+
console.log(console_chars_1.emoji.error + " Found " + errors.length + " error(s):\n");
|
|
240
|
+
for (const issue of errors) {
|
|
241
|
+
console.log(" " + issue.file + ":" + issue.line);
|
|
242
|
+
console.log(" " + console_chars_1.emoji.error + " [" + issue.code + "] " + issue.message);
|
|
243
|
+
if (issue.suggestion)
|
|
244
|
+
console.log(" " + console_chars_1.emoji.hint + " " + issue.suggestion);
|
|
245
|
+
if (issue.fixable && !autoFix)
|
|
246
|
+
console.log(" " + console_chars_1.emoji.wrench + " Fixable with --fix");
|
|
247
|
+
console.log("");
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (warnings.length > 0) {
|
|
251
|
+
console.log(console_chars_1.emoji.warning + " Found " + warnings.length + " warning(s):\n");
|
|
252
|
+
for (const issue of warnings) {
|
|
253
|
+
console.log(" " + issue.file + ":" + issue.line);
|
|
254
|
+
console.log(" " + console_chars_1.emoji.warning + " [" + issue.code + "] " + issue.message);
|
|
255
|
+
if (issue.suggestion)
|
|
256
|
+
console.log(" " + console_chars_1.emoji.hint + " " + issue.suggestion);
|
|
257
|
+
console.log("");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
console.log((0, console_chars_1.createDivider)(60));
|
|
261
|
+
if (totalFixed > 0)
|
|
262
|
+
console.log("\n" + console_chars_1.emoji.success + " Auto-fixed " + totalFixed + " file(s)");
|
|
263
|
+
if (fixableCount > 0 && !autoFix) {
|
|
264
|
+
console.log("\n" +
|
|
265
|
+
console_chars_1.emoji.hint +
|
|
266
|
+
" " +
|
|
267
|
+
fixableCount +
|
|
268
|
+
" issue(s) can be auto-fixed with: pnpm preflight:session-provider-wrapper --fix");
|
|
269
|
+
}
|
|
270
|
+
console.log("\n" + console_chars_1.emoji.info + " Why this matters:");
|
|
271
|
+
console.log(" - useSession() must be called within a <SessionProvider> tree");
|
|
272
|
+
console.log(" - Components in error.tsx, not-found.tsx render outside providers");
|
|
273
|
+
console.log(" - getSession() is deprecated - use auth() server-side");
|
|
274
|
+
console.log(" - Conditional SessionProvider causes hydration mismatches\n");
|
|
275
|
+
return { errors: errors.length, warnings: warnings.length, fixed: totalFixed };
|
|
276
|
+
}
|
|
277
|
+
// Allow direct execution
|
|
278
|
+
if (require.main === module) {
|
|
279
|
+
run()
|
|
280
|
+
.then((result) => process.exit(result.errors > 0 ? 1 : 0))
|
|
281
|
+
.catch((err) => {
|
|
282
|
+
console.error("Error:", err);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=session-provider-wrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-provider-wrapper.js","sourceRoot":"","sources":["../../../src/checks/auth/session-provider-wrapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Prisma Upsert Safety Preflight
|
|
4
|
+
*
|
|
5
|
+
* Detects unsafe upsert patterns that can cause duplicate records:
|
|
6
|
+
* 1. Upserts using non-unique fields in the where clause
|
|
7
|
+
* 2. Upserts with dynamic/generated IDs that won't match existing records
|
|
8
|
+
* 3. Missing unique constraints on fields used for upsert lookups
|
|
9
|
+
*
|
|
10
|
+
* The root cause: Using `upsert` with a `where` clause that doesn't match
|
|
11
|
+
* a unique constraint will always create new records instead of updating.
|
|
12
|
+
*
|
|
13
|
+
* Example of BAD pattern:
|
|
14
|
+
* await tx.model.upsert({
|
|
15
|
+
* where: { id: card.id || `temp_${card.clientPairId}` }, // temp_ never matches!
|
|
16
|
+
* create: { ... },
|
|
17
|
+
* update: { ... },
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* Example of GOOD pattern:
|
|
21
|
+
* const existing = await tx.model.findFirst({ where: { clientPairId } });
|
|
22
|
+
* if (existing) {
|
|
23
|
+
* await tx.model.update({ where: { id: existing.id }, data: { ... } });
|
|
24
|
+
* } else {
|
|
25
|
+
* await tx.model.create({ data: { ... } });
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* Usage:
|
|
29
|
+
* pnpm preflight:prisma-upsert-safety
|
|
30
|
+
* pnpm preflight:prisma-upsert-safety --verbose
|
|
31
|
+
*/
|
|
32
|
+
export declare const id = "database/prisma-upsert-safety";
|
|
33
|
+
export declare const name = "Prisma Upsert Safety";
|
|
34
|
+
export declare const category = "database";
|
|
35
|
+
export declare const blocking = true;
|
|
36
|
+
export declare const description = "Prisma Upsert Safety Preflight";
|
|
37
|
+
export declare const tags: string[];
|
|
38
|
+
export declare function run(): Promise<void>;
|
|
39
|
+
//# sourceMappingURL=prisma-upsert-safety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-upsert-safety.d.ts","sourceRoot":"","sources":["../../../src/checks/database/prisma-upsert-safety.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAWH,eAAO,MAAM,EAAE,kCAAkC,CAAC;AAClD,eAAO,MAAM,IAAI,yBAAyB,CAAC;AAC3C,eAAO,MAAM,QAAQ,aAAa,CAAC;AACnC,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,WAAW,mCAAmC,CAAC;AAC5D,eAAO,MAAM,IAAI,UAAe,CAAC;AAkDjC,wBAAsB,GAAG,kBAyHxB"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Prisma Upsert Safety Preflight
|
|
5
|
+
*
|
|
6
|
+
* Detects unsafe upsert patterns that can cause duplicate records:
|
|
7
|
+
* 1. Upserts using non-unique fields in the where clause
|
|
8
|
+
* 2. Upserts with dynamic/generated IDs that won't match existing records
|
|
9
|
+
* 3. Missing unique constraints on fields used for upsert lookups
|
|
10
|
+
*
|
|
11
|
+
* The root cause: Using `upsert` with a `where` clause that doesn't match
|
|
12
|
+
* a unique constraint will always create new records instead of updating.
|
|
13
|
+
*
|
|
14
|
+
* Example of BAD pattern:
|
|
15
|
+
* await tx.model.upsert({
|
|
16
|
+
* where: { id: card.id || `temp_${card.clientPairId}` }, // temp_ never matches!
|
|
17
|
+
* create: { ... },
|
|
18
|
+
* update: { ... },
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* Example of GOOD pattern:
|
|
22
|
+
* const existing = await tx.model.findFirst({ where: { clientPairId } });
|
|
23
|
+
* if (existing) {
|
|
24
|
+
* await tx.model.update({ where: { id: existing.id }, data: { ... } });
|
|
25
|
+
* } else {
|
|
26
|
+
* await tx.model.create({ data: { ... } });
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* Usage:
|
|
30
|
+
* pnpm preflight:prisma-upsert-safety
|
|
31
|
+
* pnpm preflight:prisma-upsert-safety --verbose
|
|
32
|
+
*/
|
|
33
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
34
|
+
if (k2 === undefined) k2 = k;
|
|
35
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
36
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
37
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
38
|
+
}
|
|
39
|
+
Object.defineProperty(o, k2, desc);
|
|
40
|
+
}) : (function(o, m, k, k2) {
|
|
41
|
+
if (k2 === undefined) k2 = k;
|
|
42
|
+
o[k2] = m[k];
|
|
43
|
+
}));
|
|
44
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
45
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
46
|
+
}) : function(o, v) {
|
|
47
|
+
o["default"] = v;
|
|
48
|
+
});
|
|
49
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
50
|
+
var ownKeys = function(o) {
|
|
51
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
52
|
+
var ar = [];
|
|
53
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
54
|
+
return ar;
|
|
55
|
+
};
|
|
56
|
+
return ownKeys(o);
|
|
57
|
+
};
|
|
58
|
+
return function (mod) {
|
|
59
|
+
if (mod && mod.__esModule) return mod;
|
|
60
|
+
var result = {};
|
|
61
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
62
|
+
__setModuleDefault(result, mod);
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
65
|
+
})();
|
|
66
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
67
|
+
exports.tags = exports.description = exports.blocking = exports.category = exports.name = exports.id = void 0;
|
|
68
|
+
exports.run = run;
|
|
69
|
+
const fs = __importStar(require("fs"));
|
|
70
|
+
const path = __importStar(require("path"));
|
|
71
|
+
const console_chars_1 = require("../../utils/console-chars");
|
|
72
|
+
const universal_progress_reporter_1 = require("../system/universal-progress-reporter");
|
|
73
|
+
const glob_1 = require("glob");
|
|
74
|
+
// Check metadata
|
|
75
|
+
exports.id = "database/prisma-upsert-safety";
|
|
76
|
+
exports.name = "Prisma Upsert Safety";
|
|
77
|
+
exports.category = "database";
|
|
78
|
+
exports.blocking = true;
|
|
79
|
+
exports.description = "Prisma Upsert Safety Preflight";
|
|
80
|
+
exports.tags = ["database"];
|
|
81
|
+
function isVerbose() {
|
|
82
|
+
return process.argv.includes("--verbose");
|
|
83
|
+
}
|
|
84
|
+
// Patterns that indicate unsafe upsert usage
|
|
85
|
+
const UNSAFE_PATTERNS = [
|
|
86
|
+
{
|
|
87
|
+
// Upsert with fallback ID pattern: id: value || `temp_${...}` or id: value || generateId()
|
|
88
|
+
// This is the most dangerous pattern - temp IDs will never match
|
|
89
|
+
pattern: /\.upsert\s*\(\s*\{[\s\S]*?where\s*:\s*\{[\s\S]*?id\s*:\s*[^,}]+\|\|[^,}]+/g,
|
|
90
|
+
type: "error",
|
|
91
|
+
message: "Upsert with fallback ID pattern - temp IDs will never match existing records",
|
|
92
|
+
suggestion: "Use findFirst + update/create pattern instead of upsert with fallback IDs",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
// Upsert with template literal in where clause for ID field specifically
|
|
96
|
+
// But NOT for composite keys like storeId_date which are safe
|
|
97
|
+
pattern: /\.upsert\s*\(\s*\{[\s\S]*?where\s*:\s*\{\s*id\s*:\s*`[^`]*\$\{/g,
|
|
98
|
+
type: "error",
|
|
99
|
+
message: "Upsert with dynamic template literal ID - may not match existing records",
|
|
100
|
+
suggestion: "Use findFirst to check existence before create/update",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
// Upsert with generateId/generatePrefixedId directly in where clause for id field
|
|
104
|
+
// This is always wrong - generated IDs won't match existing records
|
|
105
|
+
pattern: /\.upsert\s*\(\s*\{[\s\S]*?where\s*:\s*\{\s*id\s*:\s*[^,}]*generate(?:Prefixed)?Id/g,
|
|
106
|
+
type: "error",
|
|
107
|
+
message: "Upsert with generated ID in where clause - will always create new records",
|
|
108
|
+
suggestion: "Generate ID only in create block, use existing field for where clause",
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
// Patterns that are acceptable
|
|
112
|
+
const SAFE_PATTERNS = [
|
|
113
|
+
// Upsert with @@unique composite key lookup
|
|
114
|
+
/\.upsert\s*\(\s*\{[\s\S]*?where\s*:\s*\{[\s\S]*?_[a-zA-Z]+_[a-zA-Z]+\s*:/,
|
|
115
|
+
// Upsert preceded by findFirst check
|
|
116
|
+
/findFirst[\s\S]{0,200}\.upsert/,
|
|
117
|
+
];
|
|
118
|
+
async function run() {
|
|
119
|
+
const reporter = (0, universal_progress_reporter_1.createUniversalProgressReporter)(exports.name);
|
|
120
|
+
console.log(`${console_chars_1.emoji.database} Prisma Upsert Safety Preflight`);
|
|
121
|
+
console.log((0, console_chars_1.createDivider)(60, "heavy"));
|
|
122
|
+
const issues = [];
|
|
123
|
+
// Find all TypeScript files in app/ and lib/ directories
|
|
124
|
+
const files = await (0, glob_1.glob)(["app/**/*.ts", "app/**/*.tsx", "lib/**/*.ts"], {
|
|
125
|
+
ignore: ["**/node_modules/**", "**/*.d.ts", "**/*.test.ts", "**/*.spec.ts"],
|
|
126
|
+
cwd: process.cwd(),
|
|
127
|
+
});
|
|
128
|
+
console.log(`\n${console_chars_1.emoji.search} Scanning ${files.length} files for unsafe upsert patterns...\n`);
|
|
129
|
+
let filesChecked = 0;
|
|
130
|
+
let upsertsFound = 0;
|
|
131
|
+
for (const file of files) {
|
|
132
|
+
const filePath = path.join(process.cwd(), file);
|
|
133
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
134
|
+
// Skip files without upsert
|
|
135
|
+
if (!content.includes(".upsert")) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
filesChecked++;
|
|
139
|
+
const lines = content.split("\n");
|
|
140
|
+
// Check if file has safe patterns that make upserts okay
|
|
141
|
+
const hasSafePattern = SAFE_PATTERNS.some((pattern) => pattern.test(content));
|
|
142
|
+
for (const check of UNSAFE_PATTERNS) {
|
|
143
|
+
const matches = content.matchAll(check.pattern);
|
|
144
|
+
for (const match of matches) {
|
|
145
|
+
upsertsFound++;
|
|
146
|
+
// Find line number
|
|
147
|
+
const beforeMatch = content.substring(0, match.index);
|
|
148
|
+
const lineNum = beforeMatch.split("\n").length;
|
|
149
|
+
// Skip if file has compensating safe patterns (only for non-critical issues)
|
|
150
|
+
if (hasSafePattern && check.type === "warning") {
|
|
151
|
+
if (isVerbose()) {
|
|
152
|
+
console.log(` ${console_chars_1.emoji.info} ${file}:${lineNum} - Safe pattern detected, skipping`);
|
|
153
|
+
}
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
const codeLine = lines[lineNum - 1]?.trim() || "";
|
|
157
|
+
issues.push({
|
|
158
|
+
file,
|
|
159
|
+
line: lineNum,
|
|
160
|
+
type: check.type,
|
|
161
|
+
message: check.message,
|
|
162
|
+
code: codeLine.substring(0, 80),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (isVerbose() && filesChecked % 50 === 0) {
|
|
167
|
+
console.log(` Checked ${filesChecked} files with upserts...`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
console.log(`${console_chars_1.emoji.chart} Scanned ${filesChecked} files containing upsert operations`);
|
|
171
|
+
console.log(` Found ${upsertsFound} upsert patterns to analyze\n`);
|
|
172
|
+
// Report issues
|
|
173
|
+
if (issues.length === 0) {
|
|
174
|
+
console.log(`${console_chars_1.emoji.success} No unsafe upsert patterns detected`);
|
|
175
|
+
process.exit(0);
|
|
176
|
+
}
|
|
177
|
+
console.log((0, console_chars_1.createDivider)(60, "heavy"));
|
|
178
|
+
console.log(`${console_chars_1.emoji.clipboard} Unsafe Upsert Patterns Found:`);
|
|
179
|
+
console.log((0, console_chars_1.createDivider)(60, "heavy"));
|
|
180
|
+
let errors = 0;
|
|
181
|
+
let warnings = 0;
|
|
182
|
+
for (const issue of issues) {
|
|
183
|
+
const icon = issue.type === "error" ? `${console_chars_1.emoji.error}` : `${console_chars_1.emoji.warning}`;
|
|
184
|
+
console.log(`\n${icon} ${issue.file}:${issue.line}`);
|
|
185
|
+
console.log(` ${issue.message}`);
|
|
186
|
+
console.log(` Code: ${issue.code}...`);
|
|
187
|
+
if (issue.type === "error") {
|
|
188
|
+
errors++;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
warnings++;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
console.log("\n" + (0, console_chars_1.createDivider)(60, "heavy"));
|
|
195
|
+
console.log(`${console_chars_1.emoji.chart} Summary:`);
|
|
196
|
+
console.log(` Errors: ${errors}`);
|
|
197
|
+
console.log(` Warnings: ${warnings}`);
|
|
198
|
+
if (errors > 0) {
|
|
199
|
+
console.log(`\n${console_chars_1.emoji.error} FAILED: Unsafe upsert patterns detected`);
|
|
200
|
+
console.log("\n Fix by using findFirst + update/create pattern:");
|
|
201
|
+
console.log(" ```typescript");
|
|
202
|
+
console.log(" const existing = await tx.model.findFirst({");
|
|
203
|
+
console.log(" where: { uniqueField: value },");
|
|
204
|
+
console.log(" });");
|
|
205
|
+
console.log(" if (existing) {");
|
|
206
|
+
console.log(" await tx.model.update({ where: { id: existing.id }, data });");
|
|
207
|
+
console.log(" } else {");
|
|
208
|
+
console.log(" await tx.model.create({ data: { id: generateId(), ...data } });");
|
|
209
|
+
console.log(" }");
|
|
210
|
+
console.log(" ```");
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
console.log(`\n${console_chars_1.emoji.success} Passed with ${warnings} warnings`);
|
|
214
|
+
process.exit(0);
|
|
215
|
+
}
|
|
216
|
+
// Allow direct execution
|
|
217
|
+
if (require.main === module) {
|
|
218
|
+
run().catch(console.error);
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=prisma-upsert-safety.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-upsert-safety.js","sourceRoot":"","sources":["../../../src/checks/database/prisma-upsert-safety.ts"],"names":[],"mappings":"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"}
|