@empline/preflight 1.1.31 → 1.1.33

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 (61) hide show
  1. package/dist/checks/cart/cart-guest-merge.d.ts +14 -0
  2. package/dist/checks/cart/cart-guest-merge.d.ts.map +1 -0
  3. package/dist/checks/cart/cart-guest-merge.js +217 -0
  4. package/dist/checks/cart/cart-guest-merge.js.map +1 -0
  5. package/dist/checks/cart/cart-quantity-validation.d.ts +14 -0
  6. package/dist/checks/cart/cart-quantity-validation.d.ts.map +1 -0
  7. package/dist/checks/cart/cart-quantity-validation.js +211 -0
  8. package/dist/checks/cart/cart-quantity-validation.js.map +1 -0
  9. package/dist/checks/cart/cart-stock-validation.d.ts +14 -0
  10. package/dist/checks/cart/cart-stock-validation.d.ts.map +1 -0
  11. package/dist/checks/cart/cart-stock-validation.js +211 -0
  12. package/dist/checks/cart/cart-stock-validation.js.map +1 -0
  13. package/dist/checks/checkout/checkout-multi-store.d.ts +14 -0
  14. package/dist/checks/checkout/checkout-multi-store.d.ts.map +1 -0
  15. package/dist/checks/checkout/checkout-multi-store.js +209 -0
  16. package/dist/checks/checkout/checkout-multi-store.js.map +1 -0
  17. package/dist/checks/checkout/checkout-tax-consistency.d.ts +14 -0
  18. package/dist/checks/checkout/checkout-tax-consistency.d.ts.map +1 -0
  19. package/dist/checks/checkout/checkout-tax-consistency.js +212 -0
  20. package/dist/checks/checkout/checkout-tax-consistency.js.map +1 -0
  21. package/dist/checks/framework/prisma-version-bugs.d.ts +47 -0
  22. package/dist/checks/framework/prisma-version-bugs.d.ts.map +1 -0
  23. package/dist/checks/framework/prisma-version-bugs.js +232 -0
  24. package/dist/checks/framework/prisma-version-bugs.js.map +1 -0
  25. package/dist/checks/framework/turbopack-prisma-external.d.ts +37 -0
  26. package/dist/checks/framework/turbopack-prisma-external.d.ts.map +1 -0
  27. package/dist/checks/framework/turbopack-prisma-external.js +144 -0
  28. package/dist/checks/framework/turbopack-prisma-external.js.map +1 -0
  29. package/dist/checks/listing-approval/image-processing-validation.d.ts +14 -0
  30. package/dist/checks/listing-approval/image-processing-validation.d.ts.map +1 -0
  31. package/dist/checks/listing-approval/image-processing-validation.js +198 -0
  32. package/dist/checks/listing-approval/image-processing-validation.js.map +1 -0
  33. package/dist/checks/listing-approval/r2-metadata-application.d.ts +14 -0
  34. package/dist/checks/listing-approval/r2-metadata-application.d.ts.map +1 -0
  35. package/dist/checks/listing-approval/r2-metadata-application.js +261 -0
  36. package/dist/checks/listing-approval/r2-metadata-application.js.map +1 -0
  37. package/dist/checks/listing-approval/seo-metadata-consistency.d.ts +14 -0
  38. package/dist/checks/listing-approval/seo-metadata-consistency.d.ts.map +1 -0
  39. package/dist/checks/listing-approval/seo-metadata-consistency.js +408 -0
  40. package/dist/checks/listing-approval/seo-metadata-consistency.js.map +1 -0
  41. package/dist/checks/order/order-inventory-guard.d.ts +14 -0
  42. package/dist/checks/order/order-inventory-guard.d.ts.map +1 -0
  43. package/dist/checks/order/order-inventory-guard.js +212 -0
  44. package/dist/checks/order/order-inventory-guard.js.map +1 -0
  45. package/dist/checks/order/order-status-validation.d.ts +14 -0
  46. package/dist/checks/order/order-status-validation.d.ts.map +1 -0
  47. package/dist/checks/order/order-status-validation.js +218 -0
  48. package/dist/checks/order/order-status-validation.js.map +1 -0
  49. package/dist/checks/order/shipping-address-validation.d.ts +14 -0
  50. package/dist/checks/order/shipping-address-validation.d.ts.map +1 -0
  51. package/dist/checks/order/shipping-address-validation.js +213 -0
  52. package/dist/checks/order/shipping-address-validation.js.map +1 -0
  53. package/dist/checks/ui/batch-progress-consistency.d.ts +13 -0
  54. package/dist/checks/ui/batch-progress-consistency.d.ts.map +1 -0
  55. package/dist/checks/ui/batch-progress-consistency.js +200 -0
  56. package/dist/checks/ui/batch-progress-consistency.js.map +1 -0
  57. package/dist/checks/ui/bulk-action-progress-feedback.d.ts +13 -0
  58. package/dist/checks/ui/bulk-action-progress-feedback.d.ts.map +1 -0
  59. package/dist/checks/ui/bulk-action-progress-feedback.js +273 -0
  60. package/dist/checks/ui/bulk-action-progress-feedback.js.map +1 -0
  61. package/package.json +1 -1
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * Preflight Check: Turbopack Prisma External Packages
5
+ *
6
+ * Category: framework
7
+ * Severity: blocking
8
+ *
9
+ * Ensures that Prisma packages are properly excluded from Turbopack bundling
10
+ * in next.config.js via serverExternalPackages.
11
+ *
12
+ * WHY THIS MATTERS:
13
+ * When Turbopack bundles @prisma/client, it can mangle the internal column name
14
+ * resolution, causing runtime errors like:
15
+ * "The column `(not available)` does not exist in the current database"
16
+ *
17
+ * This happens because Prisma's query builder dynamically resolves column names
18
+ * at runtime using the generated client's metadata. Turbopack's bundling breaks
19
+ * this resolution, especially for columns using @map() in the schema.
20
+ *
21
+ * SOLUTION:
22
+ * Add "@prisma/client" and "prisma" to serverExternalPackages in next.config.js
23
+ * to prevent Turbopack from bundling them.
24
+ *
25
+ * @see https://nextjs.org/docs/app/api-reference/next-config-js/serverExternalPackages
26
+ */
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.tags = exports.description = exports.blocking = exports.category = exports.name = exports.id = void 0;
29
+ exports.run = run;
30
+ const fs_1 = require("fs");
31
+ const path_1 = require("path");
32
+ const console_chars_1 = require("../../utils/console-chars");
33
+ // Preflight metadata
34
+ exports.id = "framework/turbopack-prisma-external";
35
+ exports.name = "Turbopack Prisma External Packages";
36
+ exports.category = "framework";
37
+ exports.blocking = true;
38
+ exports.description = "Ensures Prisma packages are excluded from Turbopack bundling";
39
+ exports.tags = ["turbopack", "prisma", "next.js", "config"];
40
+ const NEXT_CONFIG_PATH = (0, path_1.join)(process.cwd(), "next.config.js");
41
+ const NEXT_CONFIG_TS_PATH = (0, path_1.join)(process.cwd(), "next.config.ts");
42
+ const NEXT_CONFIG_MJS_PATH = (0, path_1.join)(process.cwd(), "next.config.mjs");
43
+ const REQUIRED_EXTERNAL_PACKAGES = ["@prisma/client", "prisma"];
44
+ function findNextConfig() {
45
+ if ((0, fs_1.existsSync)(NEXT_CONFIG_PATH))
46
+ return NEXT_CONFIG_PATH;
47
+ if ((0, fs_1.existsSync)(NEXT_CONFIG_TS_PATH))
48
+ return NEXT_CONFIG_TS_PATH;
49
+ if ((0, fs_1.existsSync)(NEXT_CONFIG_MJS_PATH))
50
+ return NEXT_CONFIG_MJS_PATH;
51
+ return null;
52
+ }
53
+ function extractServerExternalPackages(content) {
54
+ // Match serverExternalPackages array in various formats:
55
+ // serverExternalPackages: ["sharp", "@prisma/client"]
56
+ // serverExternalPackages: ['sharp', '@prisma/client']
57
+ const patterns = [
58
+ /serverExternalPackages\s*:\s*\[([\s\S]*?)\]/,
59
+ /serverExternalPackages\s*=\s*\[([\s\S]*?)\]/,
60
+ ];
61
+ for (const pattern of patterns) {
62
+ const match = content.match(pattern);
63
+ if (match && match[1]) {
64
+ // Extract quoted strings from the array content
65
+ const arrayContent = match[1];
66
+ const packages = [];
67
+ // Match both single and double quoted strings
68
+ const stringPattern = /["']([^"']+)["']/g;
69
+ let stringMatch;
70
+ while ((stringMatch = stringPattern.exec(arrayContent)) !== null) {
71
+ packages.push(stringMatch[1]);
72
+ }
73
+ return packages;
74
+ }
75
+ }
76
+ return [];
77
+ }
78
+ function checkConfig() {
79
+ const configPath = findNextConfig();
80
+ if (!configPath) {
81
+ return {
82
+ passed: false,
83
+ configPath: null,
84
+ missingPackages: [...REQUIRED_EXTERNAL_PACKAGES],
85
+ foundPackages: [],
86
+ };
87
+ }
88
+ const content = (0, fs_1.readFileSync)(configPath, "utf-8");
89
+ const externalPackages = extractServerExternalPackages(content);
90
+ const missingPackages = REQUIRED_EXTERNAL_PACKAGES.filter((pkg) => !externalPackages.includes(pkg));
91
+ const foundPackages = REQUIRED_EXTERNAL_PACKAGES.filter((pkg) => externalPackages.includes(pkg));
92
+ return {
93
+ passed: missingPackages.length === 0,
94
+ configPath,
95
+ missingPackages,
96
+ foundPackages,
97
+ };
98
+ }
99
+ async function run() {
100
+ console.log(`\n${console_chars_1.emoji.search} TURBOPACK PRISMA EXTERNAL PACKAGES CHECK`);
101
+ console.log((0, console_chars_1.createDivider)(65, "heavy"));
102
+ const result = checkConfig();
103
+ if (!result.configPath) {
104
+ console.log(`\n${console_chars_1.emoji.error} No next.config.js/ts/mjs found in project root.`);
105
+ console.log(` This check requires a Next.js configuration file.`);
106
+ console.log(`\n${console_chars_1.emoji.error} TURBOPACK PRISMA CHECK FAILED`);
107
+ return { success: false, errors: 1, warnings: 0 };
108
+ }
109
+ console.log(`\n Config file: ${result.configPath}`);
110
+ if (result.foundPackages.length > 0) {
111
+ console.log(` ${console_chars_1.emoji.success} Found in serverExternalPackages: ${result.foundPackages.join(", ")}`);
112
+ }
113
+ if (result.passed) {
114
+ console.log(`\n${console_chars_1.emoji.success} TURBOPACK PRISMA CHECK PASSED`);
115
+ console.log(`\nPrisma packages are correctly excluded from Turbopack bundling.`);
116
+ return { success: true, errors: 0, warnings: 0 };
117
+ }
118
+ console.log(`\n${console_chars_1.emoji.error} Missing from serverExternalPackages: ${result.missingPackages.join(", ")}`);
119
+ console.log(`\n${console_chars_1.emoji.info} WHY THIS MATTERS:`);
120
+ console.log(` When Turbopack bundles @prisma/client, it mangles internal column`);
121
+ console.log(` name resolution, causing runtime errors like:`);
122
+ console.log(` "The column \`(not available)\` does not exist in the current database"`);
123
+ console.log(`\n${console_chars_1.emoji.info} FIX:`);
124
+ console.log(` Add the following to serverExternalPackages in next.config.js:`);
125
+ console.log(`\n serverExternalPackages: [`);
126
+ console.log(` "sharp",`);
127
+ console.log(` "@prisma/client",`);
128
+ console.log(` "prisma",`);
129
+ console.log(` ],`);
130
+ console.log(`\n${console_chars_1.emoji.error} TURBOPACK PRISMA CHECK FAILED`);
131
+ return { success: false, errors: result.missingPackages.length, warnings: 0 };
132
+ }
133
+ // Allow direct execution
134
+ if (require.main === module) {
135
+ run()
136
+ .then((result) => {
137
+ process.exit(result.success ? 0 : 1);
138
+ })
139
+ .catch((err) => {
140
+ console.error(`${console_chars_1.emoji.error} Preflight failed:`, err);
141
+ process.exit(1);
142
+ });
143
+ }
144
+ //# sourceMappingURL=turbopack-prisma-external.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turbopack-prisma-external.js","sourceRoot":"","sources":["../../../src/checks/framework/turbopack-prisma-external.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ export declare const id = "listing-approval/image-processing-validation";
3
+ export declare const name = "Image Processing Validation";
4
+ export declare const description = "Validates listing approval calls mandatory image processing pipeline";
5
+ export declare const category = "listing-approval";
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=image-processing-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-processing-validation.d.ts","sourceRoot":"","sources":["../../../src/checks/listing-approval/image-processing-validation.ts"],"names":[],"mappings":";AAuBA,eAAO,MAAM,EAAE,iDAAiD,CAAC;AACjE,eAAO,MAAM,IAAI,gCAAgC,CAAC;AAClD,eAAO,MAAM,WAAW,yEAAyE,CAAC;AAClG,eAAO,MAAM,QAAQ,qBAAqB,CAAC;AAC3C,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,IAAI,UAA6D,CAAC;AAC/E,eAAO,MAAM,QAAQ,UAA0B,CAAC;AA2EhD,wBAAsB,GAAG,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAqG3F"}
@@ -0,0 +1,198 @@
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
+ * Image Processing Validation Preflight
11
+ *
12
+ * Validates that the listing approval flow correctly calls the image
13
+ * processing pipeline. This ensures:
14
+ * 1. moveApprovedListingImages() is called during approval
15
+ * 2. Image optimization is mandatory (not optional/skippable)
16
+ * 3. All required image operations are present in the approval route
17
+ * 4. Error handling exists for image processing failures
18
+ *
19
+ * Critical because:
20
+ * - Images must be renamed with SEO-friendly filenames
21
+ * - Responsive variants must be generated for performance
22
+ * - IPTC/XMP metadata must be injected for SEO
23
+ * - R2 object metadata must be set for internal tracking
24
+ */
25
+ const fs_1 = __importDefault(require("fs"));
26
+ const path_1 = __importDefault(require("path"));
27
+ const console_chars_1 = require("../../utils/console-chars");
28
+ // METADATA - Required for plugin loader discovery
29
+ exports.id = "listing-approval/image-processing-validation";
30
+ exports.name = "Image Processing Validation";
31
+ exports.description = "Validates listing approval calls mandatory image processing pipeline";
32
+ exports.category = "listing-approval";
33
+ exports.blocking = true; // Critical - blocks build if image processing is broken
34
+ exports.tags = ["listing", "approval", "images", "r2", "seo", "critical"];
35
+ exports.requires = ["trading-card-system"];
36
+ /**
37
+ * Files to validate for image processing
38
+ */
39
+ const APPROVAL_FILES = [
40
+ "app/api/admin/listings/approve/route.ts",
41
+ "app/api/admin/listings/bulk-approve/route.ts",
42
+ ];
43
+ /**
44
+ * Required patterns that MUST exist in approval routes
45
+ */
46
+ const REQUIRED_PATTERNS = {
47
+ // Must call the image move function
48
+ imageMove: {
49
+ pattern: /moveApprovedListingImages\s*\(/,
50
+ description: "Call to moveApprovedListingImages() for image optimization",
51
+ critical: true,
52
+ },
53
+ // Must check for images before approval
54
+ imageValidation: {
55
+ pattern: /imageUrls\s*(?:&&|\?\?|\.length|===|!==)/,
56
+ description: "Validation that listing has images before approval",
57
+ critical: true,
58
+ },
59
+ // Must have error handling for image processing
60
+ imageErrorHandling: {
61
+ pattern: /imageMoveResult\.success\s*===?\s*false|!imageMoveResult\.success|imageMoveResult\.failed/,
62
+ description: "Error handling for image processing failures",
63
+ critical: true,
64
+ },
65
+ // Must update listing with new image URLs
66
+ imageUrlUpdate: {
67
+ pattern: /imageUrls:\s*(?:imageMoveResult\.newImageUrls|newImageUrls)/,
68
+ description: "Update listing with optimized image URLs",
69
+ critical: true,
70
+ },
71
+ // Must handle SEO metadata from image processing
72
+ seoMetadataHandling: {
73
+ pattern: /imageAltTexts|imageTitles|imageDescriptions|imageCaptions|seoMetadata/,
74
+ description: "SEO metadata extraction from image processing results",
75
+ critical: false, // Warning level - important but not blocking
76
+ },
77
+ };
78
+ /**
79
+ * Anti-patterns that should NOT exist
80
+ */
81
+ const FORBIDDEN_PATTERNS = {
82
+ // Should not skip image processing
83
+ skipImageProcessing: {
84
+ pattern: /skipImageProcessing|skipImages|noImageMove/i,
85
+ description: "Flag to skip image processing (image optimization is mandatory)",
86
+ },
87
+ // Should not make image processing optional
88
+ optionalImageProcessing: {
89
+ pattern: /if\s*\(\s*shouldOptimizeImages|if\s*\(\s*enableImageOptimization/i,
90
+ description: "Conditional image processing (should always run)",
91
+ },
92
+ // Should not have empty catch blocks for image processing
93
+ swallowedImageErrors: {
94
+ pattern: /moveApprovedListingImages[\s\S]{0,200}catch\s*\([^)]*\)\s*\{\s*\}/,
95
+ description: "Swallowed errors in image processing (errors should be handled)",
96
+ },
97
+ };
98
+ async function run() {
99
+ console.log(`\n${console_chars_1.emoji.ruler} IMAGE PROCESSING VALIDATION`);
100
+ console.log((0, console_chars_1.createDivider)(65, "heavy"));
101
+ const issues = [];
102
+ let filesChecked = 0;
103
+ for (const relativePath of APPROVAL_FILES) {
104
+ const filePath = path_1.default.join(process.cwd(), relativePath);
105
+ if (!fs_1.default.existsSync(filePath)) {
106
+ issues.push({
107
+ file: relativePath,
108
+ type: "file-missing",
109
+ pattern: "N/A",
110
+ description: `Approval route file not found: ${relativePath}`,
111
+ critical: true,
112
+ });
113
+ continue;
114
+ }
115
+ filesChecked++;
116
+ const content = fs_1.default.readFileSync(filePath, "utf-8");
117
+ console.log(`\n${console_chars_1.emoji.file} Checking ${relativePath}...`);
118
+ // Check required patterns
119
+ for (const [name, check] of Object.entries(REQUIRED_PATTERNS)) {
120
+ const hasPattern = check.pattern.test(content);
121
+ if (!hasPattern) {
122
+ issues.push({
123
+ file: relativePath,
124
+ type: "missing-required",
125
+ pattern: name,
126
+ description: check.description,
127
+ critical: check.critical,
128
+ });
129
+ console.log(` ${console_chars_1.emoji.error} Missing: ${check.description}`);
130
+ }
131
+ else {
132
+ console.log(` ${console_chars_1.emoji.success} Found: ${check.description}`);
133
+ }
134
+ }
135
+ // Check forbidden patterns
136
+ for (const [name, check] of Object.entries(FORBIDDEN_PATTERNS)) {
137
+ const hasPattern = check.pattern.test(content);
138
+ if (hasPattern) {
139
+ issues.push({
140
+ file: relativePath,
141
+ type: "forbidden-pattern",
142
+ pattern: name,
143
+ description: check.description,
144
+ critical: true,
145
+ });
146
+ console.log(` ${console_chars_1.emoji.error} Forbidden: ${check.description}`);
147
+ }
148
+ }
149
+ }
150
+ // Summary
151
+ console.log(`\n${console_chars_1.emoji.chart} Summary:`);
152
+ console.log(` Files checked: ${filesChecked}`);
153
+ console.log(` Issues found: ${issues.length}`);
154
+ const criticalIssues = issues.filter((i) => i.critical);
155
+ const warningIssues = issues.filter((i) => !i.critical);
156
+ if (issues.length === 0) {
157
+ console.log(`\n${console_chars_1.emoji.success} IMAGE PROCESSING VALIDATION PASSED`);
158
+ console.log(`\nAll approval routes correctly implement mandatory image processing.`);
159
+ return { success: true, errors: 0, warnings: 0 };
160
+ }
161
+ // Display issues
162
+ if (criticalIssues.length > 0) {
163
+ console.log(`\n${console_chars_1.emoji.error} Critical issues (${criticalIssues.length}):`);
164
+ for (const issue of criticalIssues) {
165
+ console.log(` ${issue.file}: ${issue.description}`);
166
+ }
167
+ }
168
+ if (warningIssues.length > 0) {
169
+ console.log(`\n${console_chars_1.emoji.warning} Warning issues (${warningIssues.length}):`);
170
+ for (const issue of warningIssues) {
171
+ console.log(` ${issue.file}: ${issue.description}`);
172
+ }
173
+ }
174
+ console.log(`\n${console_chars_1.emoji.info} Image processing requirements:`);
175
+ console.log(` 1. Call moveApprovedListingImages() for every approval`);
176
+ console.log(` 2. Validate listing has images before approval`);
177
+ console.log(` 3. Handle image processing errors (don't swallow them)`);
178
+ console.log(` 4. Update listing with new optimized image URLs`);
179
+ console.log(` 5. Extract and store SEO metadata from results`);
180
+ if (criticalIssues.length > 0) {
181
+ console.log(`\n${console_chars_1.emoji.error} IMAGE PROCESSING VALIDATION FAILED`);
182
+ return { success: false, errors: criticalIssues.length, warnings: warningIssues.length };
183
+ }
184
+ console.log(`\n${console_chars_1.emoji.warning} IMAGE PROCESSING VALIDATION PASSED WITH WARNINGS`);
185
+ return { success: true, errors: 0, warnings: warningIssues.length };
186
+ }
187
+ // Allow direct execution
188
+ if (require.main === module) {
189
+ run()
190
+ .then((result) => {
191
+ process.exit(result.success ? 0 : 1);
192
+ })
193
+ .catch((err) => {
194
+ console.error(`${console_chars_1.emoji.error} Preflight failed:`, err);
195
+ process.exit(1);
196
+ });
197
+ }
198
+ //# sourceMappingURL=image-processing-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-processing-validation.js","sourceRoot":"","sources":["../../../src/checks/listing-approval/image-processing-validation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ export declare const id = "listing-approval/r2-metadata-application";
3
+ export declare const name = "R2 Metadata Application";
4
+ export declare const description = "Validates R2 object metadata and IPTC/XMP injection during approval";
5
+ export declare const category = "listing-approval";
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=r2-metadata-application.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-metadata-application.d.ts","sourceRoot":"","sources":["../../../src/checks/listing-approval/r2-metadata-application.ts"],"names":[],"mappings":";AAqBA,eAAO,MAAM,EAAE,6CAA6C,CAAC;AAC7D,eAAO,MAAM,IAAI,4BAA4B,CAAC;AAC9C,eAAO,MAAM,WAAW,wEAAwE,CAAC;AACjG,eAAO,MAAM,QAAQ,qBAAqB,CAAC;AAC3C,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,IAAI,UAAkE,CAAC;AACpF,eAAO,MAAM,QAAQ,UAA0B,CAAC;AA0GhD,wBAAsB,GAAG,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA6I3F"}
@@ -0,0 +1,261 @@
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
+ * R2 Metadata Application Preflight
11
+ *
12
+ * Validates that the image management system correctly applies metadata
13
+ * to R2 objects during the approval process. This ensures:
14
+ * 1. R2 object metadata (HTTP headers) is set with SEO fields
15
+ * 2. IPTC/XMP metadata is injected into image bytes
16
+ * 3. All required metadata fields are included
17
+ * 4. Metadata limits are respected (R2 has size limits)
18
+ *
19
+ * Two layers of metadata:
20
+ * - R2 Object Metadata: Stored as HTTP headers, searchable by R2
21
+ * - IPTC/XMP Embedded: Injected into image bytes, readable by Google Images
22
+ */
23
+ const fs_1 = __importDefault(require("fs"));
24
+ const path_1 = __importDefault(require("path"));
25
+ const console_chars_1 = require("../../utils/console-chars");
26
+ // METADATA - Required for plugin loader discovery
27
+ exports.id = "listing-approval/r2-metadata-application";
28
+ exports.name = "R2 Metadata Application";
29
+ exports.description = "Validates R2 object metadata and IPTC/XMP injection during approval";
30
+ exports.category = "listing-approval";
31
+ exports.blocking = true; // Critical for SEO
32
+ exports.tags = ["listing", "approval", "r2", "metadata", "seo", "iptc", "xmp"];
33
+ exports.requires = ["trading-card-system"];
34
+ /**
35
+ * Files to validate for R2 metadata application
36
+ */
37
+ const IMAGE_MANAGEMENT_FILES = [
38
+ "lib/image-management.ts",
39
+ ];
40
+ const METADATA_INJECTION_FILES = [
41
+ "lib/image-metadata-injection.ts",
42
+ ];
43
+ /**
44
+ * Required patterns for R2 object metadata
45
+ */
46
+ const R2_METADATA_PATTERNS = {
47
+ // Must build metadata object for R2
48
+ metadataBuilder: {
49
+ pattern: /buildR2ImageMetadata|function\s+build\w*Metadata/,
50
+ description: "R2 metadata builder function",
51
+ critical: true,
52
+ },
53
+ // Must include core SEO fields
54
+ titleField: {
55
+ pattern: /baseMetadata\.title\s*=|metadata\.title\s*=|["']title["']\s*:/,
56
+ description: "Title field in R2 metadata",
57
+ critical: true,
58
+ },
59
+ descriptionField: {
60
+ pattern: /baseMetadata\.description\s*=|metadata\.description\s*=|["']description["']\s*:/,
61
+ description: "Description field in R2 metadata",
62
+ critical: true,
63
+ },
64
+ altTextField: {
65
+ pattern: /["']alt-text["']\s*:|altText|alt_text/,
66
+ description: "Alt text field in R2 metadata",
67
+ critical: true,
68
+ },
69
+ // Must include product-specific fields
70
+ productFields: {
71
+ pattern: /["']featured-player["']|["']year["']|["']brand["']|["']card-number["']/,
72
+ description: "Product-specific fields (player, year, brand, card number)",
73
+ critical: true,
74
+ },
75
+ // Must include linking fields
76
+ listingLink: {
77
+ pattern: /["']linked-listing["']|["']listing-id["']/,
78
+ description: "Linked listing ID in metadata",
79
+ critical: false,
80
+ },
81
+ // Must pass metadata to PutObjectCommand
82
+ putObjectMetadata: {
83
+ pattern: /PutObjectCommand[\s\S]*?Metadata\s*:/,
84
+ description: "Metadata passed to R2 PutObjectCommand",
85
+ critical: true,
86
+ },
87
+ };
88
+ /**
89
+ * Required patterns for IPTC/XMP injection
90
+ */
91
+ const IPTC_PATTERNS = {
92
+ // Must have IPTC metadata generation
93
+ iptcGenerator: {
94
+ pattern: /generateCardIPTCMetadata|generate\w*IPTC/,
95
+ description: "IPTC metadata generator function",
96
+ critical: true,
97
+ },
98
+ // Must inject metadata into image bytes
99
+ metadataInjection: {
100
+ pattern: /injectImageMetadata|injectIPTC|writeIPTC/,
101
+ description: "IPTC metadata injection into image bytes",
102
+ critical: true,
103
+ },
104
+ // Must handle standard IPTC fields
105
+ iptcFields: {
106
+ pattern: /ObjectName|Caption|Keywords|Byline|Copyright|Source/,
107
+ description: "Standard IPTC field handling",
108
+ critical: true,
109
+ },
110
+ // Should handle XMP for modern compatibility
111
+ xmpSupport: {
112
+ pattern: /XMP|xmp|dc:title|dc:description|dc:creator/i,
113
+ description: "XMP metadata support for modern compatibility",
114
+ critical: false,
115
+ },
116
+ };
117
+ /**
118
+ * Metadata length limits (R2 enforces these)
119
+ */
120
+ const METADATA_LIMITS = {
121
+ maxKeyLength: 1024,
122
+ maxValueLength: 2048,
123
+ maxTotalSize: 8192,
124
+ };
125
+ async function run() {
126
+ console.log(`\n${console_chars_1.emoji.ruler} R2 METADATA APPLICATION VALIDATION`);
127
+ console.log((0, console_chars_1.createDivider)(65, "heavy"));
128
+ const issues = [];
129
+ // Check R2 metadata patterns in image-management.ts
130
+ console.log(`\n${console_chars_1.emoji.search} Checking R2 object metadata patterns...`);
131
+ for (const relativePath of IMAGE_MANAGEMENT_FILES) {
132
+ const filePath = path_1.default.join(process.cwd(), relativePath);
133
+ if (!fs_1.default.existsSync(filePath)) {
134
+ issues.push({
135
+ file: relativePath,
136
+ type: "file-missing",
137
+ pattern: "N/A",
138
+ description: `Image management file not found: ${relativePath}`,
139
+ critical: true,
140
+ });
141
+ continue;
142
+ }
143
+ const content = fs_1.default.readFileSync(filePath, "utf-8");
144
+ console.log(`\n${console_chars_1.emoji.file} ${relativePath}:`);
145
+ for (const [name, check] of Object.entries(R2_METADATA_PATTERNS)) {
146
+ const hasPattern = check.pattern.test(content);
147
+ if (!hasPattern) {
148
+ issues.push({
149
+ file: relativePath,
150
+ type: "missing-r2-pattern",
151
+ pattern: name,
152
+ description: check.description,
153
+ critical: check.critical,
154
+ });
155
+ const icon = check.critical ? console_chars_1.emoji.error : console_chars_1.emoji.warning;
156
+ console.log(` ${icon} Missing: ${check.description}`);
157
+ }
158
+ else {
159
+ console.log(` ${console_chars_1.emoji.success} Found: ${check.description}`);
160
+ }
161
+ }
162
+ // Check for metadata length limit handling
163
+ const hasLimitHandling = /slice\s*\(\s*0\s*,\s*\d+\s*\)|\.substring\s*\(\s*0\s*,\s*\d+\s*\)|TITLE_MAX_LENGTH|DESCRIPTION_MAX_LENGTH/i.test(content);
164
+ if (!hasLimitHandling) {
165
+ issues.push({
166
+ file: relativePath,
167
+ type: "missing-r2-pattern",
168
+ pattern: "lengthLimits",
169
+ description: `Metadata length limit handling (R2 max: ${METADATA_LIMITS.maxValueLength} chars)`,
170
+ critical: false,
171
+ });
172
+ console.log(` ${console_chars_1.emoji.warning} Missing: Metadata length limit handling`);
173
+ }
174
+ else {
175
+ console.log(` ${console_chars_1.emoji.success} Found: Metadata length limit handling`);
176
+ }
177
+ }
178
+ // Check IPTC/XMP patterns in image-metadata-injection.ts
179
+ console.log(`\n${console_chars_1.emoji.search} Checking IPTC/XMP injection patterns...`);
180
+ for (const relativePath of METADATA_INJECTION_FILES) {
181
+ const filePath = path_1.default.join(process.cwd(), relativePath);
182
+ if (!fs_1.default.existsSync(filePath)) {
183
+ issues.push({
184
+ file: relativePath,
185
+ type: "file-missing",
186
+ pattern: "N/A",
187
+ description: `Metadata injection file not found: ${relativePath}`,
188
+ critical: true,
189
+ });
190
+ continue;
191
+ }
192
+ const content = fs_1.default.readFileSync(filePath, "utf-8");
193
+ console.log(`\n${console_chars_1.emoji.file} ${relativePath}:`);
194
+ for (const [name, check] of Object.entries(IPTC_PATTERNS)) {
195
+ const hasPattern = check.pattern.test(content);
196
+ if (!hasPattern) {
197
+ issues.push({
198
+ file: relativePath,
199
+ type: "missing-iptc-pattern",
200
+ pattern: name,
201
+ description: check.description,
202
+ critical: check.critical,
203
+ });
204
+ const icon = check.critical ? console_chars_1.emoji.error : console_chars_1.emoji.warning;
205
+ console.log(` ${icon} Missing: ${check.description}`);
206
+ }
207
+ else {
208
+ console.log(` ${console_chars_1.emoji.success} Found: ${check.description}`);
209
+ }
210
+ }
211
+ }
212
+ // Summary
213
+ const criticalIssues = issues.filter((i) => i.critical);
214
+ const warningIssues = issues.filter((i) => !i.critical);
215
+ console.log(`\n${console_chars_1.emoji.chart} Summary:`);
216
+ console.log(` Critical issues: ${criticalIssues.length}`);
217
+ console.log(` Warning issues: ${warningIssues.length}`);
218
+ if (issues.length === 0) {
219
+ console.log(`\n${console_chars_1.emoji.success} R2 METADATA APPLICATION VALIDATION PASSED`);
220
+ console.log(`\nBoth R2 object metadata and IPTC/XMP injection are correctly implemented.`);
221
+ return { success: true, errors: 0, warnings: 0 };
222
+ }
223
+ if (criticalIssues.length > 0) {
224
+ console.log(`\n${console_chars_1.emoji.error} Critical issues:`);
225
+ for (const issue of criticalIssues) {
226
+ console.log(` ${issue.file}: ${issue.description}`);
227
+ }
228
+ }
229
+ if (warningIssues.length > 0) {
230
+ console.log(`\n${console_chars_1.emoji.warning} Warning issues:`);
231
+ for (const issue of warningIssues) {
232
+ console.log(` ${issue.file}: ${issue.description}`);
233
+ }
234
+ }
235
+ console.log(`\n${console_chars_1.emoji.info} R2 metadata requirements:`);
236
+ console.log(` R2 Object Metadata (HTTP headers):`);
237
+ console.log(` - title, description, alt-text, caption`);
238
+ console.log(` - featured-player, year, brand, card-number`);
239
+ console.log(` - linked-listing, optimized-at`);
240
+ console.log(`\n IPTC/XMP Embedded Metadata:`);
241
+ console.log(` - ObjectName, Caption, Keywords, Byline`);
242
+ console.log(` - Copyright, Source, Creator`);
243
+ if (criticalIssues.length > 0) {
244
+ console.log(`\n${console_chars_1.emoji.error} R2 METADATA APPLICATION VALIDATION FAILED`);
245
+ return { success: false, errors: criticalIssues.length, warnings: warningIssues.length };
246
+ }
247
+ console.log(`\n${console_chars_1.emoji.warning} R2 METADATA APPLICATION VALIDATION PASSED WITH WARNINGS`);
248
+ return { success: true, errors: 0, warnings: warningIssues.length };
249
+ }
250
+ // Allow direct execution
251
+ if (require.main === module) {
252
+ run()
253
+ .then((result) => {
254
+ process.exit(result.success ? 0 : 1);
255
+ })
256
+ .catch((err) => {
257
+ console.error(`${console_chars_1.emoji.error} Preflight failed:`, err);
258
+ process.exit(1);
259
+ });
260
+ }
261
+ //# sourceMappingURL=r2-metadata-application.js.map