import type { Json } from './types.ts'; import { fault } from './values.ts'; /** Own JSON fields only. A * segment enumerates an array, never object keys. */ export function* copySlots(data: Json, path: readonly string[]): Generator<{parent: Record; key: string}> { const work: {value: Json; depth: number}[] = [{value: data, depth: 0}]; while (work.length) { const {value, depth} = work.pop()!, key = path[depth]; if (key === '*') { if (!Array.isArray(value)) throw fault('invalid_copy_reference'); if (depth === path.length - 1) { for (let i = 0; i < value.length; i++) yield {parent: value as unknown as Record, key: String(i)}; } else for (let i = value.length - 1; i >= 0; i--) work.push({value: value[i], depth: depth + 1}); } else if (value && typeof value === 'object' && Object.hasOwn(value, key)) { if (depth === path.length - 1) yield {parent: value as Record, key}; else work.push({value: (value as Record)[key], depth: depth + 1}); } } } export function* copyReferenceIds(data: Json, ref: {path: readonly string[]; many?: boolean}): Generator { for (const {parent, key} of copySlots(data, ref.path)) { const value = parent[key]; if (value === null) continue; if (ref.many && !Array.isArray(value)) throw fault('invalid_copy_reference'); for (const id of ref.many ? value as Json[] : [value]) { if (typeof id !== 'string' || !id) throw fault('invalid_copy_reference'); yield id; } } }