@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.
- package/dist/checks/cart/cart-guest-merge.d.ts +14 -0
- package/dist/checks/cart/cart-guest-merge.d.ts.map +1 -0
- package/dist/checks/cart/cart-guest-merge.js +217 -0
- package/dist/checks/cart/cart-guest-merge.js.map +1 -0
- package/dist/checks/cart/cart-quantity-validation.d.ts +14 -0
- package/dist/checks/cart/cart-quantity-validation.d.ts.map +1 -0
- package/dist/checks/cart/cart-quantity-validation.js +211 -0
- package/dist/checks/cart/cart-quantity-validation.js.map +1 -0
- package/dist/checks/cart/cart-stock-validation.d.ts +14 -0
- package/dist/checks/cart/cart-stock-validation.d.ts.map +1 -0
- package/dist/checks/cart/cart-stock-validation.js +211 -0
- package/dist/checks/cart/cart-stock-validation.js.map +1 -0
- package/dist/checks/checkout/checkout-multi-store.d.ts +14 -0
- package/dist/checks/checkout/checkout-multi-store.d.ts.map +1 -0
- package/dist/checks/checkout/checkout-multi-store.js +209 -0
- package/dist/checks/checkout/checkout-multi-store.js.map +1 -0
- package/dist/checks/checkout/checkout-tax-consistency.d.ts +14 -0
- package/dist/checks/checkout/checkout-tax-consistency.d.ts.map +1 -0
- package/dist/checks/checkout/checkout-tax-consistency.js +212 -0
- package/dist/checks/checkout/checkout-tax-consistency.js.map +1 -0
- package/dist/checks/framework/prisma-version-bugs.d.ts +47 -0
- package/dist/checks/framework/prisma-version-bugs.d.ts.map +1 -0
- package/dist/checks/framework/prisma-version-bugs.js +232 -0
- package/dist/checks/framework/prisma-version-bugs.js.map +1 -0
- package/dist/checks/framework/turbopack-prisma-external.d.ts +37 -0
- package/dist/checks/framework/turbopack-prisma-external.d.ts.map +1 -0
- package/dist/checks/framework/turbopack-prisma-external.js +144 -0
- package/dist/checks/framework/turbopack-prisma-external.js.map +1 -0
- package/dist/checks/listing-approval/image-processing-validation.d.ts +14 -0
- package/dist/checks/listing-approval/image-processing-validation.d.ts.map +1 -0
- package/dist/checks/listing-approval/image-processing-validation.js +198 -0
- package/dist/checks/listing-approval/image-processing-validation.js.map +1 -0
- package/dist/checks/listing-approval/r2-metadata-application.d.ts +14 -0
- package/dist/checks/listing-approval/r2-metadata-application.d.ts.map +1 -0
- package/dist/checks/listing-approval/r2-metadata-application.js +261 -0
- package/dist/checks/listing-approval/r2-metadata-application.js.map +1 -0
- package/dist/checks/listing-approval/seo-metadata-consistency.d.ts +14 -0
- package/dist/checks/listing-approval/seo-metadata-consistency.d.ts.map +1 -0
- package/dist/checks/listing-approval/seo-metadata-consistency.js +408 -0
- package/dist/checks/listing-approval/seo-metadata-consistency.js.map +1 -0
- package/dist/checks/order/order-inventory-guard.d.ts +14 -0
- package/dist/checks/order/order-inventory-guard.d.ts.map +1 -0
- package/dist/checks/order/order-inventory-guard.js +212 -0
- package/dist/checks/order/order-inventory-guard.js.map +1 -0
- package/dist/checks/order/order-status-validation.d.ts +14 -0
- package/dist/checks/order/order-status-validation.d.ts.map +1 -0
- package/dist/checks/order/order-status-validation.js +218 -0
- package/dist/checks/order/order-status-validation.js.map +1 -0
- package/dist/checks/order/shipping-address-validation.d.ts +14 -0
- package/dist/checks/order/shipping-address-validation.d.ts.map +1 -0
- package/dist/checks/order/shipping-address-validation.js +213 -0
- package/dist/checks/order/shipping-address-validation.js.map +1 -0
- package/dist/checks/ui/batch-progress-consistency.d.ts +13 -0
- package/dist/checks/ui/batch-progress-consistency.d.ts.map +1 -0
- package/dist/checks/ui/batch-progress-consistency.js +200 -0
- package/dist/checks/ui/batch-progress-consistency.js.map +1 -0
- package/dist/checks/ui/bulk-action-progress-feedback.d.ts +13 -0
- package/dist/checks/ui/bulk-action-progress-feedback.d.ts.map +1 -0
- package/dist/checks/ui/bulk-action-progress-feedback.js +273 -0
- package/dist/checks/ui/bulk-action-progress-feedback.js.map +1 -0
- 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
|