import type { Id, Json, ReadonlyValueSet } from './types.ts'; export function fault(code: string, targets: readonly Id[] = []): Error & { code: string; targets: readonly Id[] } { return Object.assign(new Error(code), { code, targets }); } export function reject(error: unknown) { const value = error as { code?: string; targets?: readonly string[] }; return Object.freeze({ status: 'rejected' as const, code: value?.code || 'invalid_command', targets: value?.targets || [] }); } export function freeze(value: T): T { if (value && typeof value === 'object' && !Object.isFrozen(value)) { for (const child of Object.values(value)) freeze(child); Object.freeze(value); } return value; } export function clone(value: T): T { if (value === undefined) return value; return JSON.parse(JSON.stringify(value)) as T; } export function equal(a: unknown, b: unknown): boolean { if (Object.is(a, b)) return true; if (!a || !b || typeof a !== 'object' || typeof b !== 'object' || Array.isArray(a) !== Array.isArray(b)) return false; const left = Object.keys(a), right = Object.keys(b); return left.length === right.length && left.every(key => Object.prototype.hasOwnProperty.call(b, key) && equal((a as Record)[key], (b as Record)[key])); } /** A capability-safe read view: unlike TS ReadonlyMap, no .set is exposed at runtime. */ export function readMap(input: ReadonlyMap): ReadonlyMap { const map = new Map(input); const view: ReadonlyMap = { get size() { return map.size; }, get: key => map.get(key), has: key => map.has(key), entries: () => map.entries(), keys: () => map.keys(), values: () => map.values(), [Symbol.iterator]: () => map[Symbol.iterator](), forEach: (callback, thisArg) => map.forEach((value, key) => callback.call(thisArg, value, key, view)), }; return Object.freeze(view); } export function readSet(input: Iterable): ReadonlyValueSet { const set = new Set(input); const view: ReadonlyValueSet = { get size() { return set.size; }, has: key => set.has(key), entries: () => set.entries(), keys: () => set.keys(), values: () => set.values(), [Symbol.iterator]: () => set[Symbol.iterator](), forEach: callback => set.forEach(value => callback(value, value, view)), }; return Object.freeze(view); } export function validJson(value: unknown, depth = 0): value is Json { if (depth > 64) return false; if (value === null || typeof value === 'string' || typeof value === 'boolean') return true; if (typeof value === 'number') return Number.isFinite(value); if (Array.isArray(value)) return value.every(item => validJson(item, depth + 1)); return !!value && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every(child => validJson(child, depth + 1)); }