Files
vendoo/tools/verify-slice31-image-editor.mjs
Masterluke77andGitHub 6f48827f4d Vendoo 1.41.2 – Git Ignore Boundary & Module Tracking Hotfix (#18)
* docs: prepare Vendoo 1.41.2 git-ignore boundary hotfix

* fix: track native source modules with root-anchored runtime ignores
2026-07-09 17:09:00 +02:00

91 lines
4.1 KiB
JavaScript

import sharp from 'sharp';
import { mkdtempSync, mkdirSync, rmSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { renderImageVersion, resolveEditableImage, normalizeEditorOperations } from '../lib/image-editor.mjs';
const temp = mkdtempSync(join(tmpdir(), 'vendoo-slice31-'));
const uploadRoot = join(temp, 'uploads');
const sourceFolder = join(uploadRoot, 'verification');
mkdirSync(sourceFolder, { recursive: true });
const sourcePath = join(sourceFolder, 'original.png');
try {
await sharp({
create: { width: 960, height: 720, channels: 4, background: { r: 225, g: 213, b: 192, alpha: 1 } },
}).composite([{
input: Buffer.from('<svg width="960" height="720"><rect x="90" y="100" width="330" height="430" rx="24" fill="#1f6f5f"/><circle cx="690" cy="350" r="175" fill="#d28b46"/><path d="M480 610 L890 610 L760 470 Z" fill="#2c3e50"/></svg>'),
}]).png().toFile(sourcePath);
const normalized = normalizeEditorOperations({
rotation: 90,
straighten: 2.5,
flip_h: true,
crop: { x: 0.08, y: 0.1, width: 0.84, height: 0.76 },
exposure: 0.3,
brightness: 12,
contrast: 18,
highlights: -24,
shadows: 28,
temperature: 16,
saturation: 14,
sharpness: 38,
resize: { width: 640, height: 640 },
remove_background: true,
watermark: { enabled: true, text: 'Vendoo Test', position: 'bottom-right', opacity: 55 },
format: 'png',
quality: 84,
});
let recorded = null;
const result = await renderImageVersion({
uploadRoot,
sourcePath: 'verification/original.png',
rootPath: 'verification/original.png',
context: 'verification',
operations: normalized,
recordVersion: data => {
recorded = data;
return { id: 1, ...data };
},
});
const metadata = await sharp(join(uploadRoot, result.filename)).metadata();
if (metadata.format !== 'png') throw new Error(`Falsches Ausgabeformat: ${metadata.format}`);
if (metadata.width > 640 || metadata.height > 640) throw new Error(`Zielgröße überschritten: ${metadata.width}x${metadata.height}`);
if (!recorded || recorded.root_path !== 'verification/original.png') throw new Error('Versionenkette wurde nicht korrekt protokolliert.');
if (recorded.output_path !== result.filename || recorded.source_path !== 'verification/original.png') throw new Error('Quell-/Zielpfad der Version ist inkonsistent.');
if (!recorded.operations.crop || recorded.operations.sharpness !== 38) throw new Error('Bearbeitungsparameter wurden nicht vollständig gespeichert.');
if (!recorded.operations.remove_background) throw new Error('Hintergrundentfernung wurde nicht gespeichert.');
if (!recorded.operations.watermark?.enabled || recorded.operations.watermark.text !== 'Vendoo Test') throw new Error('Wasserzeichen wurde nicht gespeichert.');
const stats = await sharp(join(uploadRoot, result.filename)).stats();
const alpha = stats.channels[3];
if (!alpha || alpha.min !== 0 || alpha.max !== 255) throw new Error('Transparenter Hintergrund wurde nicht erzeugt.');
let traversalBlocked = false;
try { resolveEditableImage(uploadRoot, '../outside.png'); } catch { traversalBlocked = true; }
if (!traversalBlocked) throw new Error('Pfadschutz hat Traversal nicht blockiert.');
console.log(JSON.stringify({
ok: true,
source: { width: 960, height: 720, format: 'png' },
output: { width: metadata.width, height: metadata.height, format: metadata.format, filename: result.filename },
operations: {
undo_redo_state_shape: true,
crop: recorded.operations.crop,
rotation: recorded.operations.rotation,
straighten: recorded.operations.straighten,
tone_controls: ['exposure', 'brightness', 'contrast', 'highlights', 'shadows', 'temperature', 'saturation', 'sharpness'],
resize: recorded.operations.resize,
quality: recorded.operations.quality,
remove_background: recorded.operations.remove_background,
watermark: recorded.operations.watermark,
},
non_destructive: true,
version_recorded: true,
traversal_blocked: true,
}, null, 2));
} finally {
rmSync(temp, { recursive: true, force: true });
}