29 lines
1.5 KiB
TypeScript
29 lines
1.5 KiB
TypeScript
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<string, Json>; 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<string, Json>, 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<string, Json>, key};
|
|
else work.push({value: (value as Record<string, Json>)[key], depth: depth + 1});
|
|
}
|
|
}
|
|
}
|
|
export function* copyReferenceIds(data: Json, ref: {path: readonly string[]; many?: boolean}): Generator<string> {
|
|
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;
|
|
}
|
|
}
|
|
}
|