Stripping new uploads does nothing for the catalogue that is already on the storefront, which is where the exposure actually lives today. This is the other half. Reports by default and rewrites nothing without --apply, because the transform is lossy and there is no undo. Idempotency comes from `needsProcessing` rather than from a marker or a schema change: a file with no EXIF already inside the bounds is already in its final state, so a second run skips it instead of putting it through another lossy pass. Proven rather than assumed — a second --apply immediately after the first reports skipped 1, processed 0. Verified end to end against a real row and a real file. 3000x2000 carrying GPS EXIF became 2000x1333 with the metadata gone, 35760 bytes down to 16019, the format preserved, no temporary file left behind, and `item_images.image_path` untouched. That last part is what preserving the format bought: the backfill rewrites bytes and writes nothing to the database, so there is no window where a row points at a file that no longer exists. Both degenerate branches are exercised too, since a script that dies partway through a catalogue leaves the rest of it exposed: a row pointing at a missing file and a row with an extension the application would refuse to serve are each reported and counted, and the run continues. `handleRow` is split out of `run` for cognitive complexity, and while doing that a miscount was introduced and caught — incrementing `processed` before the rewrite meant a file that threw would have been counted as both processed and failed, which makes the summary unreadable at the moment it matters most. Ref #226
66 lines
2.2 KiB
JSON
Executable File
66 lines
2.2 KiB
JSON
Executable File
{
|
|
"name": "redefined-designs-backend",
|
|
"version": "1.0.0",
|
|
"private": true,
|
|
"main": "dist/server.js",
|
|
"engines": {
|
|
"node": ">=20.9.0"
|
|
},
|
|
"scripts": {
|
|
"build": "tsc",
|
|
"lint": "eslint src scripts",
|
|
"start": "node dist/server.js",
|
|
"dev": "tsx watch src/server.ts",
|
|
"test": "npm run test:unit",
|
|
"test:unit": "jest -c jest.unit.config.js",
|
|
"test:unit:json": "jest -c jest.unit.config.js --json --outputFile=unit-results.json",
|
|
"test:unit:cov": "jest -c jest.unit.config.js --coverage",
|
|
"test:integration": "jest -c jest.integration.config.js --runInBand",
|
|
"test:integration:json": "jest -c jest.integration.config.js --runInBand --json --outputFile=integration-results.json",
|
|
"test:integration:cov": "jest -c jest.integration.config.js --runInBand --coverage --forceExit",
|
|
"bench:hashing": "tsx scripts/bench-hash-latency.ts",
|
|
"backfill:images": "tsx scripts/backfill-image-reencode.ts",
|
|
"db:test:up": "docker compose -f docker-compose.test.yml up -d",
|
|
"db:test:down": "docker compose -f docker-compose.test.yml down -v",
|
|
"migrate:up": "node migrate.js up",
|
|
"migrate:down": "node migrate.js down",
|
|
"migrate:create": "node-pg-migrate create --migration-file-language js"
|
|
},
|
|
"dependencies": {
|
|
"@types/markdown-it": "^14.2.0",
|
|
"bcryptjs": "^2.4.3",
|
|
"cookie-parser": "^1.4.6",
|
|
"express": "^4.19.2",
|
|
"express-rate-limit": "^8.6.2",
|
|
"markdown-it": "^15.0.0",
|
|
"multer": "^1.4.5-lts.1",
|
|
"node-cron": "^3.0.3",
|
|
"node-pg-migrate": "^7.6.1",
|
|
"nodemailer": "^6.9.14",
|
|
"pg": "^8.12.0",
|
|
"sharp": "^0.35.4"
|
|
},
|
|
"devDependencies": {
|
|
"@eslint/js": "^9.39.5",
|
|
"@types/bcryptjs": "^2.4.6",
|
|
"@types/cookie-parser": "^1.4.7",
|
|
"@types/express": "^4.17.21",
|
|
"@types/jest": "^29.5.12",
|
|
"@types/multer": "^1.4.11",
|
|
"@types/node": "^20.14.15",
|
|
"@types/node-cron": "^3.0.11",
|
|
"@types/nodemailer": "^6.4.15",
|
|
"@types/pg": "^8.11.6",
|
|
"@types/supertest": "^6.0.2",
|
|
"eslint": "^9.39.5",
|
|
"eslint-plugin-sonarjs": "^4.2.0",
|
|
"globals": "^17.11.0",
|
|
"jest": "^29.7.0",
|
|
"supertest": "^7.0.0",
|
|
"ts-jest": "^29.2.4",
|
|
"tsx": "^4.16.5",
|
|
"typescript": "^5.5.4",
|
|
"typescript-eslint": "^8.67.0"
|
|
}
|
|
}
|