Files
Portfolio/scripts/lib/merge-assets.mjs

38 lines
1.7 KiB
JavaScript

const idPattern = /^[a-z][a-z0-9-]{0,63}$/u;
const fileNamePattern = /^[a-z0-9][a-z0-9-]*\.(avif|jpg|png|svg|webp|mp4|webm|opus|mp3)$/u;
const sha256Pattern = /^[a-f0-9]{64}$/u;
const assertSafeRecord = (asset) => {
if (!asset || typeof asset !== 'object' || Array.isArray(asset) ||
typeof asset.id !== 'string' || !idPattern.test(asset.id) ||
typeof asset.fileName !== 'string' || !fileNamePattern.test(asset.fileName) ||
typeof asset.sha256 !== 'string' || !sha256Pattern.test(asset.sha256)) {
throw new Error('ASSET-MERGE-RECORD-001');
}
};
export const mergeAssetRecords = (baseAssets, generatedAssets, managedIds) => {
if (!Array.isArray(baseAssets) || !Array.isArray(generatedAssets) || !(managedIds instanceof Set)) {
throw new Error('ASSET-MERGE-RECORD-001');
}
for (const asset of [...baseAssets, ...generatedAssets]) {
if (!asset || typeof asset !== 'object' || Array.isArray(asset) ||
typeof asset.id !== 'string' || typeof asset.fileName !== 'string') {
throw new Error('ASSET-MERGE-RECORD-001');
}
}
const retained = baseAssets.filter(({ id }) => !managedIds.has(id));
const combined = [...retained, ...generatedAssets];
const ids = new Set();
const names = new Set();
for (const asset of combined) {
const folded = asset.fileName.toLocaleLowerCase('en-US');
if (ids.has(asset.id)) throw new Error('ASSET-MERGE-ID-001');
if (names.has(folded)) throw new Error('ASSET-MERGE-NAME-001');
ids.add(asset.id);
names.add(folded);
}
for (const asset of [...baseAssets, ...generatedAssets]) assertSafeRecord(asset);
return combined.sort((left, right) => left.fileName.localeCompare(right.fileName, 'en-US'));
};