@empline/preflight 1.1.33 → 1.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/checks/auth/client-only-protected-pages.d.ts +14 -0
  2. package/dist/checks/auth/client-only-protected-pages.d.ts.map +1 -0
  3. package/dist/checks/auth/client-only-protected-pages.js +182 -0
  4. package/dist/checks/auth/client-only-protected-pages.js.map +1 -0
  5. package/dist/checks/auth/empty-data-instead-of-redirect.d.ts +14 -0
  6. package/dist/checks/auth/empty-data-instead-of-redirect.d.ts.map +1 -0
  7. package/dist/checks/auth/empty-data-instead-of-redirect.js +200 -0
  8. package/dist/checks/auth/empty-data-instead-of-redirect.js.map +1 -0
  9. package/dist/checks/auth/store-id-fallback.d.ts +14 -0
  10. package/dist/checks/auth/store-id-fallback.d.ts.map +1 -0
  11. package/dist/checks/auth/store-id-fallback.js +217 -0
  12. package/dist/checks/auth/store-id-fallback.js.map +1 -0
  13. package/dist/checks/auth/store-page-auth-guard.d.ts +14 -0
  14. package/dist/checks/auth/store-page-auth-guard.d.ts.map +1 -0
  15. package/dist/checks/auth/store-page-auth-guard.js +255 -0
  16. package/dist/checks/auth/store-page-auth-guard.js.map +1 -0
  17. package/dist/checks/data-integrity/api-route-reference-validation.d.ts +14 -0
  18. package/dist/checks/data-integrity/api-route-reference-validation.d.ts.map +1 -0
  19. package/dist/checks/data-integrity/api-route-reference-validation.js +219 -0
  20. package/dist/checks/data-integrity/api-route-reference-validation.js.map +1 -0
  21. package/dist/checks/system/active-preflight-migration-tracker.d.ts +13 -0
  22. package/dist/checks/system/active-preflight-migration-tracker.d.ts.map +1 -0
  23. package/dist/checks/system/active-preflight-migration-tracker.js +244 -0
  24. package/dist/checks/system/active-preflight-migration-tracker.js.map +1 -0
  25. package/dist/checks/ui/filter-option-coverage.d.ts +14 -0
  26. package/dist/checks/ui/filter-option-coverage.d.ts.map +1 -0
  27. package/dist/checks/ui/filter-option-coverage.js +286 -0
  28. package/dist/checks/ui/filter-option-coverage.js.map +1 -0
  29. package/dist/checks/ui/query-param-state-sync.d.ts +14 -0
  30. package/dist/checks/ui/query-param-state-sync.d.ts.map +1 -0
  31. package/dist/checks/ui/query-param-state-sync.js +251 -0
  32. package/dist/checks/ui/query-param-state-sync.js.map +1 -0
  33. package/dist/checks/ui/unimplemented-action-handler.d.ts +14 -0
  34. package/dist/checks/ui/unimplemented-action-handler.d.ts.map +1 -0
  35. package/dist/checks/ui/unimplemented-action-handler.js +230 -0
  36. package/dist/checks/ui/unimplemented-action-handler.js.map +1 -0
  37. package/package.json +1 -1
@@ -0,0 +1,286 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.requires = exports.tags = exports.blocking = exports.category = exports.description = exports.name = exports.id = void 0;
8
+ exports.run = run;
9
+ /**
10
+ * Filter Option Coverage Preflight
11
+ *
12
+ * Detects when filter options don't cover all possible values. This causes
13
+ * UX issues where users can't filter to see certain items.
14
+ *
15
+ * Example problems this catches:
16
+ * - Status filter options are [ACTIVE, PENDING, SOLD] but API returns INACTIVE/REMOVED items
17
+ * - Category filter missing options that exist in the data
18
+ * - StatusCounts show items in statuses that aren't filterable
19
+ *
20
+ * Pattern detection:
21
+ * 1. Find filter option definitions (STATUS_OPTIONS, CATEGORY_OPTIONS, etc.)
22
+ * 2. Find where those options are used in UI filters
23
+ * 3. Find where status/category counts are displayed
24
+ * 4. Check for mismatches where counts show values not in filter options
25
+ */
26
+ const fs_1 = __importDefault(require("fs"));
27
+ const path_1 = __importDefault(require("path"));
28
+ const glob_1 = require("glob");
29
+ const console_chars_1 = require("../../utils/console-chars");
30
+ // METADATA
31
+ exports.id = "ui/filter-option-coverage";
32
+ exports.name = "Filter Option Coverage";
33
+ exports.description = "Detects filter options that don't cover all possible values";
34
+ exports.category = "ui";
35
+ exports.blocking = false; // Warning - may be intentional
36
+ exports.tags = ["ui", "filters", "options", "enum", "consistency"];
37
+ exports.requires = ["trading-card-system"];
38
+ /**
39
+ * Patterns to find option/enum definitions
40
+ */
41
+ const OPTION_DEFINITION_PATTERNS = [
42
+ // const STATUS_OPTIONS = [{ value: "ACTIVE" }, ...]
43
+ /const\s+([A-Z_]+_OPTIONS)\s*(?::\s*[^=]+)?\s*=\s*\[([^\]]+)\]/gs,
44
+ // export const LISTING_STATUS_OPTIONS = [...]
45
+ /export\s+const\s+([A-Z_]+_OPTIONS)\s*(?::\s*[^=]+)?\s*=\s*\[([^\]]+)\]/gs,
46
+ // const statusOptions = [...]
47
+ /const\s+(\w+Options)\s*(?::\s*[^=]+)?\s*=\s*\[([^\]]+)\]/gs,
48
+ ];
49
+ /**
50
+ * Patterns to find count field access (statusCounts.ACTIVE, etc.)
51
+ */
52
+ const COUNT_USAGE_PATTERNS = [
53
+ // statusCounts.ACTIVE, statusCounts["PENDING"]
54
+ /(\w+Counts?)\.([A-Z_]+)/g,
55
+ // counts.status.ACTIVE
56
+ /counts\.(\w+)\.([A-Z_]+)/g,
57
+ // Object.entries(statusCounts)
58
+ /Object\.(?:entries|keys)\s*\(\s*(\w+Counts?)\s*\)/g,
59
+ ];
60
+ /**
61
+ * Extract values from option array string
62
+ */
63
+ function extractOptionValues(arrayContent) {
64
+ const values = [];
65
+ // Match value: "X" or value: 'X' patterns
66
+ const valuePattern = /value:\s*["'`]([^"'`]+)["'`]/g;
67
+ let match;
68
+ while ((match = valuePattern.exec(arrayContent)) !== null) {
69
+ values.push(match[1]);
70
+ }
71
+ // Also match simple string arrays ["ACTIVE", "PENDING"]
72
+ if (values.length === 0) {
73
+ const simplePattern = /["'`]([A-Z][A-Z_]+)["'`]/g;
74
+ while ((match = simplePattern.exec(arrayContent)) !== null) {
75
+ values.push(match[1]);
76
+ }
77
+ }
78
+ return values;
79
+ }
80
+ /**
81
+ * Find all enum values from Prisma schema or TypeScript enums
82
+ */
83
+ async function findEnumValues(enumName) {
84
+ const values = [];
85
+ // Check Prisma schema
86
+ const schemaPath = path_1.default.join(process.cwd(), "prisma/schema.prisma");
87
+ if (fs_1.default.existsSync(schemaPath)) {
88
+ const schema = fs_1.default.readFileSync(schemaPath, "utf-8");
89
+ const enumPattern = new RegExp(`enum\\s+${enumName}\\s*\\{([^}]+)\\}`, "s");
90
+ const match = schema.match(enumPattern);
91
+ if (match) {
92
+ const enumBody = match[1];
93
+ const valuePattern = /^\s*([A-Z_]+)/gm;
94
+ let valueMatch;
95
+ while ((valueMatch = valuePattern.exec(enumBody)) !== null) {
96
+ values.push(valueMatch[1]);
97
+ }
98
+ }
99
+ }
100
+ // Check TypeScript enums in constants
101
+ const enumFiles = await (0, glob_1.glob)("lib/constants/**/*.ts", { cwd: process.cwd() });
102
+ for (const enumFile of enumFiles) {
103
+ const content = fs_1.default.readFileSync(path_1.default.join(process.cwd(), enumFile), "utf-8");
104
+ const tsEnumPattern = new RegExp(`enum\\s+${enumName}\\s*\\{([^}]+)\\}`, "s");
105
+ const match = content.match(tsEnumPattern);
106
+ if (match) {
107
+ const enumBody = match[1];
108
+ const valuePattern = /^\s*([A-Z_]+)\s*(?:=|,|$)/gm;
109
+ let valueMatch;
110
+ while ((valueMatch = valuePattern.exec(enumBody)) !== null) {
111
+ values.push(valueMatch[1]);
112
+ }
113
+ }
114
+ }
115
+ return values;
116
+ }
117
+ async function run() {
118
+ console.log(`\n${console_chars_1.emoji.search} FILTER OPTION COVERAGE CHECK`);
119
+ console.log((0, console_chars_1.createDivider)(65, "heavy"));
120
+ const issues = [];
121
+ const optionDefinitions = [];
122
+ const countUsages = [];
123
+ // Step 1: Find option definitions
124
+ console.log(`\n${console_chars_1.emoji.file} Scanning for filter option definitions...`);
125
+ const constantFiles = await (0, glob_1.glob)([
126
+ "lib/constants/**/*.ts",
127
+ "lib/enums.ts",
128
+ "constants/**/*.ts",
129
+ ], {
130
+ cwd: process.cwd(),
131
+ ignore: ["**/*.d.ts"],
132
+ });
133
+ for (const constFile of constantFiles) {
134
+ const filePath = path_1.default.join(process.cwd(), constFile);
135
+ if (!fs_1.default.existsSync(filePath))
136
+ continue;
137
+ const content = fs_1.default.readFileSync(filePath, "utf-8");
138
+ for (const pattern of OPTION_DEFINITION_PATTERNS) {
139
+ pattern.lastIndex = 0;
140
+ let match;
141
+ while ((match = pattern.exec(content)) !== null) {
142
+ const optionName = match[1];
143
+ const arrayContent = match[2];
144
+ const values = extractOptionValues(arrayContent);
145
+ const lineNum = content.substring(0, match.index).split("\n").length;
146
+ if (values.length > 0) {
147
+ optionDefinitions.push({
148
+ file: constFile,
149
+ line: lineNum,
150
+ name: optionName,
151
+ values,
152
+ });
153
+ }
154
+ }
155
+ }
156
+ }
157
+ console.log(` Found ${optionDefinitions.length} option definitions`);
158
+ // Step 2: Find where counts are used
159
+ console.log(`\n${console_chars_1.emoji.search} Scanning for count usages...`);
160
+ const uiFiles = await (0, glob_1.glob)([
161
+ "app/**/*Client.tsx",
162
+ "components/**/*.tsx",
163
+ ], {
164
+ cwd: process.cwd(),
165
+ ignore: ["**/*.d.ts"],
166
+ });
167
+ for (const uiFile of uiFiles) {
168
+ const filePath = path_1.default.join(process.cwd(), uiFile);
169
+ if (!fs_1.default.existsSync(filePath))
170
+ continue;
171
+ const content = fs_1.default.readFileSync(filePath, "utf-8");
172
+ // Find statusCounts.X patterns
173
+ const countPattern = /(\w+Counts?)\.([A-Z_]+)/g;
174
+ const countFields = [];
175
+ let countMatch;
176
+ while ((countMatch = countPattern.exec(content)) !== null) {
177
+ const countVar = countMatch[1];
178
+ const fieldName = countMatch[2];
179
+ // Skip if it's a common method/property
180
+ if (["length", "toString", "valueOf"].includes(fieldName))
181
+ continue;
182
+ countFields.push(fieldName);
183
+ }
184
+ if (countFields.length > 0) {
185
+ const lineNum = content.indexOf("Counts") !== -1
186
+ ? content.substring(0, content.indexOf("Counts")).split("\n").length
187
+ : 1;
188
+ countUsages.push({
189
+ file: uiFile,
190
+ line: lineNum,
191
+ countFields: [...new Set(countFields)],
192
+ });
193
+ }
194
+ }
195
+ console.log(` Found ${countUsages.length} files with count usages`);
196
+ // Step 3: Compare options to counts
197
+ console.log(`\n${console_chars_1.emoji.check} Comparing filter options to count fields...`);
198
+ // Map option names to their corresponding enum type
199
+ const optionToEnumMap = {
200
+ LISTING_STATUS_OPTIONS: "ListingStatus",
201
+ ORDER_STATUS_OPTIONS: "OrderStatus",
202
+ STATUS_OPTIONS: "Status",
203
+ PAYMENT_STATUS_OPTIONS: "PaymentStatus",
204
+ CATEGORY_OPTIONS: "Category",
205
+ };
206
+ for (const optionDef of optionDefinitions) {
207
+ // Get the expected enum values
208
+ const enumName = optionToEnumMap[optionDef.name];
209
+ const enumValues = enumName ? await findEnumValues(enumName) : [];
210
+ // Find counts that reference more values than the options have
211
+ for (const countUsage of countUsages) {
212
+ // Check if option name suggests it's for this type
213
+ const optionPrefix = optionDef.name.replace(/_OPTIONS$/, "").toLowerCase();
214
+ // Look for count fields that aren't in the options
215
+ const countFieldsForType = countUsage.countFields.filter((field) => {
216
+ // This is a heuristic - count fields often match option values
217
+ return enumValues.includes(field) || optionDef.values.includes(field);
218
+ });
219
+ const missingFromOptions = countFieldsForType.filter((field) => !optionDef.values.includes(field));
220
+ if (missingFromOptions.length > 0) {
221
+ issues.push({
222
+ file: countUsage.file,
223
+ line: countUsage.line,
224
+ optionName: optionDef.name,
225
+ missingValues: missingFromOptions,
226
+ description: `Counts display ${missingFromOptions.join(", ")} but filter options (${optionDef.name}) don't include them`,
227
+ });
228
+ }
229
+ }
230
+ // Also check against enum values from Prisma
231
+ if (enumValues.length > 0) {
232
+ const missingFromOptions = enumValues.filter((enumVal) => !optionDef.values.includes(enumVal));
233
+ if (missingFromOptions.length > 0) {
234
+ issues.push({
235
+ file: optionDef.file,
236
+ line: optionDef.line,
237
+ optionName: optionDef.name,
238
+ missingValues: missingFromOptions,
239
+ description: `Filter options missing enum values: ${missingFromOptions.join(", ")}`,
240
+ });
241
+ }
242
+ }
243
+ }
244
+ // Deduplicate
245
+ const uniqueIssues = issues.filter((issue, index, self) => index === self.findIndex((i) => i.optionName === issue.optionName &&
246
+ JSON.stringify(i.missingValues.sort()) === JSON.stringify(issue.missingValues.sort())));
247
+ // Summary
248
+ console.log(`\n${console_chars_1.emoji.chart} Summary:`);
249
+ console.log(` Option definitions: ${optionDefinitions.length}`);
250
+ console.log(` Count usage files: ${countUsages.length}`);
251
+ console.log(` Coverage issues: ${uniqueIssues.length}`);
252
+ if (uniqueIssues.length === 0) {
253
+ console.log(`\n${console_chars_1.emoji.success} FILTER OPTION COVERAGE CHECK PASSED`);
254
+ console.log(`\nAll filter options cover the available values.`);
255
+ return { success: true, errors: 0, warnings: 0 };
256
+ }
257
+ console.log(`\n${console_chars_1.emoji.warning} Filter option coverage issues:`);
258
+ for (const issue of uniqueIssues) {
259
+ console.log(`\n ${issue.optionName}:`);
260
+ console.log(` ${issue.description}`);
261
+ console.log(` Missing: ${issue.missingValues.join(", ")}`);
262
+ console.log(` Defined in: ${issue.file}:${issue.line}`);
263
+ }
264
+ console.log(`\n${console_chars_1.emoji.info} To fix filter coverage:`);
265
+ console.log(` 1. Add missing values to the options array`);
266
+ console.log(` 2. Or intentionally exclude with a comment: // Excluded: REMOVED (internal only)`);
267
+ console.log(` 3. Example:`);
268
+ console.log(` export const LISTING_STATUS_OPTIONS = [`);
269
+ console.log(` { value: "ACTIVE", label: "Active" },`);
270
+ console.log(` { value: "INACTIVE", label: "Inactive" }, // Add missing`);
271
+ console.log(` ];`);
272
+ console.log(`\n${console_chars_1.emoji.warning} FILTER OPTION COVERAGE CHECK COMPLETED WITH WARNINGS`);
273
+ return { success: true, errors: 0, warnings: uniqueIssues.length };
274
+ }
275
+ // Allow direct execution
276
+ if (require.main === module) {
277
+ run()
278
+ .then((result) => {
279
+ process.exit(result.success ? 0 : 1);
280
+ })
281
+ .catch((err) => {
282
+ console.error(`${console_chars_1.emoji.error} Preflight failed:`, err);
283
+ process.exit(1);
284
+ });
285
+ }
286
+ //# sourceMappingURL=filter-option-coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-option-coverage.js","sourceRoot":"","sources":["../../../src/checks/ui/filter-option-coverage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ export declare const id = "ui/query-param-state-sync";
3
+ export declare const name = "Query Param State Sync";
4
+ export declare const description = "Detects URL params that aren't synced with component state";
5
+ export declare const category = "ui";
6
+ export declare const blocking = false;
7
+ export declare const tags: string[];
8
+ export declare const requires: string[];
9
+ export declare function run(): Promise<{
10
+ success: boolean;
11
+ errors: number;
12
+ warnings: number;
13
+ }>;
14
+ //# sourceMappingURL=query-param-state-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-param-state-sync.d.ts","sourceRoot":"","sources":["../../../src/checks/ui/query-param-state-sync.ts"],"names":[],"mappings":";AAwBA,eAAO,MAAM,EAAE,8BAA8B,CAAC;AAC9C,eAAO,MAAM,IAAI,2BAA2B,CAAC;AAC7C,eAAO,MAAM,WAAW,+DAA+D,CAAC;AACxF,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAC9B,eAAO,MAAM,IAAI,UAAyD,CAAC;AAC3E,eAAO,MAAM,QAAQ,UAA0B,CAAC;AA6EhD,wBAAsB,GAAG,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA4K3F"}
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.requires = exports.tags = exports.blocking = exports.category = exports.description = exports.name = exports.id = void 0;
8
+ exports.run = run;
9
+ /**
10
+ * Query Param State Sync Preflight
11
+ *
12
+ * Detects when URL query parameters aren't properly synced with component state.
13
+ * This causes confusing UX where URL params have no effect on the page.
14
+ *
15
+ * Example problems this catches:
16
+ * - /orders?status=pending but filterStatus state doesn't read from URL
17
+ * - URL has ?page=2 but page state starts at 1
18
+ * - Link passes ?category=shoes but category filter ignores URL param
19
+ *
20
+ * Pattern detection:
21
+ * 1. Find URL params used in links/navigation
22
+ * 2. Find state variables that match those param names
23
+ * 3. Check if state is initialized from URL params
24
+ */
25
+ const fs_1 = __importDefault(require("fs"));
26
+ const path_1 = __importDefault(require("path"));
27
+ const glob_1 = require("glob");
28
+ const console_chars_1 = require("../../utils/console-chars");
29
+ // METADATA
30
+ exports.id = "ui/query-param-state-sync";
31
+ exports.name = "Query Param State Sync";
32
+ exports.description = "Detects URL params that aren't synced with component state";
33
+ exports.category = "ui";
34
+ exports.blocking = false; // Warning - not always a bug
35
+ exports.tags = ["ui", "url", "params", "state", "sync", "navigation"];
36
+ exports.requires = ["trading-card-system"];
37
+ /**
38
+ * Common filter/pagination param names
39
+ */
40
+ const COMMON_PARAMS = [
41
+ "status",
42
+ "page",
43
+ "limit",
44
+ "sort",
45
+ "order",
46
+ "filter",
47
+ "category",
48
+ "type",
49
+ "search",
50
+ "q",
51
+ "tab",
52
+ "view",
53
+ ];
54
+ /**
55
+ * State variable patterns to match params
56
+ */
57
+ const STATE_TO_PARAM_MAP = {
58
+ status: ["filterStatus", "selectedStatus", "status", "statusFilter"],
59
+ page: ["page", "currentPage", "pageNumber"],
60
+ limit: ["limit", "pageSize", "perPage", "itemsPerPage"],
61
+ sort: ["sort", "sortBy", "sortField", "orderBy"],
62
+ order: ["order", "sortOrder", "sortDirection"],
63
+ category: ["category", "selectedCategory", "categoryFilter"],
64
+ tab: ["tab", "activeTab", "selectedTab", "tabValue"],
65
+ search: ["search", "searchQuery", "query", "searchTerm"],
66
+ };
67
+ /**
68
+ * Patterns to find URL param usage
69
+ */
70
+ const URL_PARAM_PATTERNS = [
71
+ // Link href with query param: href={`/orders?status=${status}`}
72
+ /href\s*=\s*[{`]["'`][^`"']*\?([a-zA-Z]+)=/g,
73
+ // searchParams.get("status")
74
+ /searchParams\.get\s*\(\s*["'`]([a-zA-Z]+)["'`]\)/g,
75
+ // useSearchParams() and params.get("status")
76
+ /params\.get\s*\(\s*["'`]([a-zA-Z]+)["'`]\)/g,
77
+ // router.push with query params
78
+ /router\.push\s*\([^)]*\?([a-zA-Z]+)=/g,
79
+ // URLSearchParams set/append
80
+ /\.(?:set|append)\s*\(\s*["'`]([a-zA-Z]+)["'`]/g,
81
+ ];
82
+ /**
83
+ * Patterns to find state initialization from URL
84
+ */
85
+ const STATE_FROM_URL_PATTERNS = [
86
+ // useState(searchParams.get("status"))
87
+ /useState\s*\(\s*(?:searchParams|params)\.get\s*\(\s*["'`]([a-zA-Z]+)["'`]\)/g,
88
+ // const status = searchParams.get("status")
89
+ /const\s+([a-zA-Z]+)\s*=\s*(?:searchParams|params)\.get/g,
90
+ // Initial state from searchParams
91
+ /(?:initial|default)[A-Z]\w*\s*=\s*(?:searchParams|params)\.get\s*\(\s*["'`]([a-zA-Z]+)["'`]\)/g,
92
+ ];
93
+ async function run() {
94
+ console.log(`\n${console_chars_1.emoji.search} QUERY PARAM STATE SYNC CHECK`);
95
+ console.log((0, console_chars_1.createDivider)(65, "heavy"));
96
+ const issues = [];
97
+ const paramUsages = [];
98
+ const stateInitFromUrl = new Map(); // file -> params
99
+ // Scan client components
100
+ console.log(`\n${console_chars_1.emoji.file} Scanning client components...`);
101
+ const clientFiles = await (0, glob_1.glob)([
102
+ "app/**/*Client.tsx",
103
+ "app/**/*Client.ts",
104
+ "app/**/page.tsx",
105
+ "components/**/*.tsx",
106
+ ], {
107
+ cwd: process.cwd(),
108
+ ignore: ["**/*.d.ts", "**/node_modules/**", "app/api/**"],
109
+ });
110
+ if (clientFiles.length === 0) {
111
+ console.log(`\n${console_chars_1.emoji.warning} No client files found - skipping check`);
112
+ return { success: true, errors: 0, warnings: 1 };
113
+ }
114
+ for (const clientFile of clientFiles) {
115
+ const filePath = path_1.default.join(process.cwd(), clientFile);
116
+ if (!fs_1.default.existsSync(filePath))
117
+ continue;
118
+ const content = fs_1.default.readFileSync(filePath, "utf-8");
119
+ const lines = content.split("\n");
120
+ // Find URL params being used in links/navigation
121
+ for (const pattern of URL_PARAM_PATTERNS) {
122
+ pattern.lastIndex = 0;
123
+ let match;
124
+ while ((match = pattern.exec(content)) !== null) {
125
+ const paramName = match[1].toLowerCase();
126
+ const lineNum = content.substring(0, match.index).split("\n").length;
127
+ if (COMMON_PARAMS.includes(paramName)) {
128
+ paramUsages.push({
129
+ file: clientFile,
130
+ line: lineNum,
131
+ paramName,
132
+ context: "link",
133
+ });
134
+ }
135
+ }
136
+ }
137
+ // Find state initialized from URL params
138
+ const fileParamsFromUrl = new Set();
139
+ for (const pattern of STATE_FROM_URL_PATTERNS) {
140
+ pattern.lastIndex = 0;
141
+ let match;
142
+ while ((match = pattern.exec(content)) !== null) {
143
+ const paramName = match[1].toLowerCase();
144
+ fileParamsFromUrl.add(paramName);
145
+ }
146
+ }
147
+ stateInitFromUrl.set(clientFile, fileParamsFromUrl);
148
+ // Find useState calls that match param names but DON'T read from URL
149
+ const useStatePattern = /useState\s*(?:<[^>]+>)?\s*\(\s*([^)]+)\)/g;
150
+ let stateMatch;
151
+ while ((stateMatch = useStatePattern.exec(content)) !== null) {
152
+ const initialValue = stateMatch[1].trim();
153
+ const lineNum = content.substring(0, stateMatch.index).split("\n").length;
154
+ const lineContent = lines[lineNum - 1] || "";
155
+ // Extract state variable name from line
156
+ const varMatch = lineContent.match(/const\s+\[(\w+)/);
157
+ if (!varMatch)
158
+ continue;
159
+ const stateName = varMatch[1].toLowerCase();
160
+ // Check if this state name matches any common param
161
+ for (const [param, stateNames] of Object.entries(STATE_TO_PARAM_MAP)) {
162
+ if (stateNames.map((s) => s.toLowerCase()).includes(stateName)) {
163
+ // Check if initialized from URL
164
+ const isFromUrl = /searchParams|params\.get|useSearchParams/.test(initialValue);
165
+ if (!isFromUrl && !fileParamsFromUrl.has(param)) {
166
+ // Check if this file uses URL for this param elsewhere
167
+ const usesParamInUrl = content.includes(`?${param}=`) ||
168
+ content.includes(`searchParams.get("${param}")`);
169
+ if (usesParamInUrl) {
170
+ paramUsages.push({
171
+ file: clientFile,
172
+ line: lineNum,
173
+ paramName: param,
174
+ context: "state",
175
+ });
176
+ }
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+ // Analyze: Find params used in URLs but state not initialized from URL
183
+ for (const usage of paramUsages) {
184
+ if (usage.context === "link") {
185
+ const fileParamsFromUrl = stateInitFromUrl.get(usage.file) || new Set();
186
+ // Check if there's corresponding state that reads from URL
187
+ if (!fileParamsFromUrl.has(usage.paramName)) {
188
+ // Check if the file has state for this param at all
189
+ const content = fs_1.default.readFileSync(path_1.default.join(process.cwd(), usage.file), "utf-8");
190
+ const possibleStateNames = STATE_TO_PARAM_MAP[usage.paramName] || [usage.paramName];
191
+ const hasState = possibleStateNames.some((name) => new RegExp(`\\[${name}[,\\s]`, "i").test(content));
192
+ if (hasState) {
193
+ issues.push({
194
+ file: usage.file,
195
+ line: usage.line,
196
+ paramName: usage.paramName,
197
+ description: `URL param "${usage.paramName}" used in links but state isn't initialized from URL`,
198
+ severity: "warning",
199
+ });
200
+ }
201
+ }
202
+ }
203
+ else if (usage.context === "state") {
204
+ issues.push({
205
+ file: usage.file,
206
+ line: usage.line,
207
+ paramName: usage.paramName,
208
+ description: `State matches URL param "${usage.paramName}" but isn't synced with URL`,
209
+ severity: "warning",
210
+ });
211
+ }
212
+ }
213
+ // Deduplicate
214
+ const uniqueIssues = issues.filter((issue, index, self) => index === self.findIndex((i) => i.file === issue.file && i.paramName === issue.paramName));
215
+ // Summary
216
+ console.log(`\n${console_chars_1.emoji.chart} Summary:`);
217
+ console.log(` Files scanned: ${clientFiles.length}`);
218
+ console.log(` URL param usages found: ${paramUsages.length}`);
219
+ console.log(` Sync issues: ${uniqueIssues.length}`);
220
+ if (uniqueIssues.length === 0) {
221
+ console.log(`\n${console_chars_1.emoji.success} QUERY PARAM STATE SYNC CHECK PASSED`);
222
+ console.log(`\nAll URL params are properly synced with component state.`);
223
+ return { success: true, errors: 0, warnings: 0 };
224
+ }
225
+ console.log(`\n${console_chars_1.emoji.warning} URL param sync issues found:`);
226
+ for (const issue of uniqueIssues) {
227
+ console.log(`\n ${issue.file}:${issue.line}`);
228
+ console.log(` ${issue.description}`);
229
+ }
230
+ console.log(`\n${console_chars_1.emoji.info} To fix URL param sync:`);
231
+ console.log(` 1. Read initial state from searchParams: useState(searchParams.get("status"))`);
232
+ console.log(` 2. Or use useSearchParams() hook to keep state in URL`);
233
+ console.log(` 3. Sync state changes back to URL with router.push()`);
234
+ console.log(` 4. Example:`);
235
+ console.log(` const searchParams = useSearchParams();`);
236
+ console.log(` const [status, setStatus] = useState(searchParams.get("status") || "all");`);
237
+ console.log(`\n${console_chars_1.emoji.warning} QUERY PARAM STATE SYNC CHECK COMPLETED WITH WARNINGS`);
238
+ return { success: true, errors: 0, warnings: uniqueIssues.length };
239
+ }
240
+ // Allow direct execution
241
+ if (require.main === module) {
242
+ run()
243
+ .then((result) => {
244
+ process.exit(result.success ? 0 : 1);
245
+ })
246
+ .catch((err) => {
247
+ console.error(`${console_chars_1.emoji.error} Preflight failed:`, err);
248
+ process.exit(1);
249
+ });
250
+ }
251
+ //# sourceMappingURL=query-param-state-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-param-state-sync.js","sourceRoot":"","sources":["../../../src/checks/ui/query-param-state-sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ export declare const id = "ui/unimplemented-action-handler";
3
+ export declare const name = "Unimplemented Action Handler";
4
+ export declare const description = "Detects UI actions that have no implementation in handlers";
5
+ export declare const category = "ui";
6
+ export declare const blocking = true;
7
+ export declare const tags: string[];
8
+ export declare const requires: string[];
9
+ export declare function run(): Promise<{
10
+ success: boolean;
11
+ errors: number;
12
+ warnings: number;
13
+ }>;
14
+ //# sourceMappingURL=unimplemented-action-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unimplemented-action-handler.d.ts","sourceRoot":"","sources":["../../../src/checks/ui/unimplemented-action-handler.ts"],"names":[],"mappings":";AAwBA,eAAO,MAAM,EAAE,oCAAoC,CAAC;AACpD,eAAO,MAAM,IAAI,iCAAiC,CAAC;AACnD,eAAO,MAAM,WAAW,+DAA+D,CAAC;AACxF,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,IAAI,UAAqE,CAAC;AACvF,eAAO,MAAM,QAAQ,UAA0B,CAAC;AA8EhD,wBAAsB,GAAG,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAiK3F"}