50 lines
1.5 KiB
JavaScript
50 lines
1.5 KiB
JavaScript
export class TrustError extends Error {
|
|
constructor(message, code = 'TRUST_INVALID') {
|
|
super(message)
|
|
this.name = 'TrustError'
|
|
this.code = code
|
|
}
|
|
}
|
|
|
|
export function plainObject(value, label) {
|
|
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
throw new TrustError(`${label} must be an object`)
|
|
}
|
|
return value
|
|
}
|
|
|
|
export function exactKeys(value, expected, label) {
|
|
const actual = Object.keys(value).sort()
|
|
const wanted = [...expected].sort()
|
|
if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) {
|
|
throw new TrustError(`${label} fields must be exactly: ${wanted.join(', ')}`)
|
|
}
|
|
}
|
|
|
|
export function positiveSafeInteger(value, label) {
|
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
throw new TrustError(`${label} must be a positive safe integer`)
|
|
}
|
|
return value
|
|
}
|
|
|
|
export function isoTimestamp(value, label) {
|
|
if (typeof value !== 'string') throw new TrustError(`${label} must be an ISO timestamp`)
|
|
const parsed = new Date(value)
|
|
if (!Number.isFinite(parsed.valueOf()) || parsed.toISOString() !== value) {
|
|
throw new TrustError(`${label} must be a canonical ISO timestamp`)
|
|
}
|
|
return value
|
|
}
|
|
|
|
export function parseJsonDocument(bytes, label) {
|
|
if (!Buffer.isBuffer(bytes)) throw new TrustError(`${label} must be bytes`)
|
|
let value
|
|
try {
|
|
value = JSON.parse(bytes.toString('utf8'))
|
|
} catch {
|
|
throw new TrustError(`${label} must contain valid JSON`)
|
|
}
|
|
return plainObject(value, label)
|
|
}
|