import { pathToJsonPointer } from '../utils/ref'; import type { IR } from './types'; export interface SchemaProcessor { /** Current inherited context (set by withContext) */ readonly context: { readonly anchor: string | undefined; readonly tags: ReadonlyArray | undefined; }; /** Check if pointer was already emitted */ hasEmitted: (path: ReadonlyArray) => boolean; /** Mark pointer as emitted. Returns false if already emitted. */ markEmitted: (path: ReadonlyArray) => boolean; /** Execute with inherited context for nested extractions */ withContext: (ctx: { anchor?: string; tags?: ReadonlyArray }, fn: () => T) => T; } export interface SchemaProcessorContext { meta: { resource: string; resourceId: string; role?: string }; namingAnchor?: string; path: ReadonlyArray; schema: IR.SchemaObject; tags?: ReadonlyArray; } export interface SchemaProcessorResult< Context extends SchemaProcessorContext = SchemaProcessorContext, > { process: (ctx: Context) => void; } export type SchemaExtractor = ( ctx: Context, ) => IR.SchemaObject; export function createSchemaProcessor(): SchemaProcessor { const emitted = new Set(); let contextTags: ReadonlyArray | undefined; let contextAnchor: string | undefined; return { get context() { return { anchor: contextAnchor, tags: contextTags, }; }, hasEmitted(path) { return emitted.has(pathToJsonPointer(path)); }, markEmitted(path) { const pointer = pathToJsonPointer(path); if (emitted.has(pointer)) return false; emitted.add(pointer); return true; }, withContext(ctx, fn) { const prevTags = contextTags; const prevAnchor = contextAnchor; contextTags = ctx.tags; contextAnchor = ctx.anchor; try { return fn(); } finally { contextTags = prevTags; contextAnchor = prevAnchor; } }, }; }