Improved harness - with card selection
This commit is contained in:
76
card-harness/scripts/surface-defaults.mjs
Normal file
76
card-harness/scripts/surface-defaults.mjs
Normal file
@@ -0,0 +1,76 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
import { readFile, rename, writeFile } from 'node:fs/promises'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url))
|
||||
export const surfaceDefaultsPath = resolve(here, '..', 'config', 'surface-defaults.json')
|
||||
const finishNames = ['Printed ink', 'Foil', 'Holographic', 'Gold leaf', 'Frosted glass']
|
||||
const materialNames = ['Paper', 'Linen', 'Metal', 'Wood', 'Leather']
|
||||
|
||||
function number(value, min, max, label) {
|
||||
if (typeof value !== 'number' || !Number.isFinite(value) || value < min || value > max) {
|
||||
throw new Error(`${label} must be between ${min} and ${max}`)
|
||||
}
|
||||
}
|
||||
|
||||
export function validateSurfaceDefaults(value) {
|
||||
if (!value || typeof value !== 'object' || value.schemaVersion !== 1 ||
|
||||
!value.finishes || typeof value.finishes !== 'object' ||
|
||||
!value.materials || typeof value.materials !== 'object') {
|
||||
throw new Error('Surface defaults use an unsupported format')
|
||||
}
|
||||
for (const name of finishNames) {
|
||||
const item = value.finishes[name]
|
||||
if (!item || typeof item !== 'object') throw new Error(`Missing defaults for finish ${name}`)
|
||||
if (Object.keys(item).some(key => key !== 'finishStrength')) throw new Error(`Unsupported setting for finish ${name}`)
|
||||
number(item.finishStrength, 0, 1, `${name} finish strength`)
|
||||
}
|
||||
for (const name of materialNames) {
|
||||
const item = value.materials[name]
|
||||
if (!item || typeof item !== 'object') throw new Error(`Missing defaults for material ${name}`)
|
||||
if (Object.keys(item).some(key => !['surfaceDetail', 'roughness', 'metalBrushHorizontal'].includes(key))) {
|
||||
throw new Error(`Unsupported setting for material ${name}`)
|
||||
}
|
||||
number(item.surfaceDetail, 0, 1, `${name} surface detail`)
|
||||
number(item.roughness, 0.05, 0.8, `${name} roughness`)
|
||||
if (item.metalBrushHorizontal !== undefined && typeof item.metalBrushHorizontal !== 'boolean') {
|
||||
throw new Error(`${name} metal grain orientation must be a boolean`)
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function revision(bytes) {
|
||||
return createHash('sha256').update(bytes).digest('hex').slice(0, 16)
|
||||
}
|
||||
|
||||
export async function readSurfaceDefaults(writable = false, path = surfaceDefaultsPath) {
|
||||
const bytes = await readFile(path)
|
||||
return { defaults: validateSurfaceDefaults(JSON.parse(bytes)), revision: revision(bytes), writable }
|
||||
}
|
||||
|
||||
export async function updateSurfaceDefaults(update, path = surfaceDefaultsPath) {
|
||||
const current = await readSurfaceDefaults(true, path)
|
||||
if (!update || update.revision !== current.revision) {
|
||||
const error = new Error('Surface defaults changed since this page loaded; refresh and try again')
|
||||
error.statusCode = 409
|
||||
throw error
|
||||
}
|
||||
const next = structuredClone(current.defaults)
|
||||
if (update.group === 'finish' && finishNames.includes(update.name)) {
|
||||
next.finishes[update.name] = update.values
|
||||
} else if (update.group === 'material' && materialNames.includes(update.name)) {
|
||||
next.materials[update.name] = update.values
|
||||
} else {
|
||||
const error = new Error('Unknown surface-default group or selection')
|
||||
error.statusCode = 400
|
||||
throw error
|
||||
}
|
||||
validateSurfaceDefaults(next)
|
||||
const bytes = `${JSON.stringify(next, null, 2)}\n`
|
||||
const temporary = `${path}.tmp`
|
||||
await writeFile(temporary, bytes)
|
||||
await rename(temporary, path)
|
||||
return { defaults: next, revision: revision(bytes), writable: true }
|
||||
}
|
||||
Reference in New Issue
Block a user