@lexbuild/ecfr 1.15.2 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/converter.ts","../src/ecfr-builder.ts","../src/ecfr-elements.ts","../src/ecfr-frontmatter.ts","../src/ecfr-path.ts","../src/downloader.ts","../src/ecfr-api-downloader.ts"],"sourcesContent":["/**\n * eCFR conversion orchestrator.\n *\n * Follows the same collect-then-write pattern as the USC converter:\n * 1. Parse XML via SAX → feed EcfrASTBuilder\n * 2. Collect emitted sections/parts/titles\n * 3. Two-pass link registration (with duplicate detection)\n * 4. Write Markdown files, _meta.json, and README.md\n */\n\nimport { createReadStream } from \"node:fs\";\nimport { join, dirname, basename, relative } from \"node:path\";\nimport {\n XMLParser,\n renderDocument,\n createLinkResolver,\n FORMAT_VERSION,\n GENERATOR,\n writeFile,\n mkdir,\n} from \"@lexbuild/core\";\nimport type {\n LevelNode,\n LevelType,\n EmitContext,\n RenderOptions,\n NotesFilter,\n ASTNode,\n AncestorInfo,\n} from \"@lexbuild/core\";\nimport { EcfrASTBuilder } from \"./ecfr-builder.js\";\nimport { buildEcfrFrontmatter } from \"./ecfr-frontmatter.js\";\nimport { buildEcfrOutputPath, buildTitleDir } from \"./ecfr-path.js\";\n\n/** Options for converting an eCFR XML file */\nexport interface EcfrConvertOptions {\n /** Path to input eCFR XML file */\n input: string;\n /** Output root directory */\n output: string;\n /** Output granularity: section (default), part, chapter, or title */\n granularity: \"section\" | \"part\" | \"chapter\" | \"title\";\n /** Link style for cross-references */\n linkStyle: \"relative\" | \"canonical\" | \"plaintext\";\n /** Include source credits in output */\n includeSourceCredits: boolean;\n /** Include all notes */\n includeNotes: boolean;\n /** Selectively include editorial notes */\n includeEditorialNotes: boolean;\n /** Selectively include statutory/regulatory notes */\n includeStatutoryNotes: boolean;\n /** Selectively include amendment history */\n includeAmendments: boolean;\n /** Parse only, don't write files */\n dryRun: boolean;\n}\n\n/** Result of an eCFR conversion */\nexport interface EcfrConvertResult {\n /** Number of sections/parts/titles written */\n sectionsWritten: number;\n /** Paths of written files */\n files: string[];\n /** Title number from XML metadata */\n titleNumber: string;\n /** Title name from XML metadata */\n titleName: string;\n /** Whether this was a dry run */\n dryRun: boolean;\n /** Number of unique parts */\n partCount: number;\n /** Total estimated tokens */\n totalTokenEstimate: number;\n /** Peak RSS in bytes during conversion */\n peakMemoryBytes: number;\n}\n\n/** Internal collected section data */\ninterface CollectedSection {\n node: LevelNode;\n context: EmitContext;\n}\n\n/** Internal section metadata for _meta.json */\ninterface SectionMeta {\n identifier: string;\n number: string;\n name: string;\n fileName: string;\n relativeFile: string;\n contentLength: number;\n hasNotes: boolean;\n status: string;\n partIdentifier: string;\n partNumber: string;\n partName: string;\n}\n\n/**\n * Convert an eCFR XML file to structured Markdown.\n */\nexport async function convertEcfrTitle(options: EcfrConvertOptions): Promise<EcfrConvertResult> {\n const { input, output, granularity, dryRun } = options;\n let peakMemory = process.memoryUsage().rss;\n\n // Map granularity to emit level.\n // Chapter and section granularity both emit at section level — chapter mode\n // groups sections by chapter ancestor in the write phase.\n const emitAt: LevelType =\n granularity === \"title\" ? \"title\" : granularity === \"part\" ? \"part\" : \"section\";\n\n // Collect phase\n const collected: CollectedSection[] = [];\n const builder = new EcfrASTBuilder({\n emitAt,\n onEmit: (node, context) => {\n collected.push({ node, context });\n },\n });\n\n // Parse XML — no namespace (eCFR XML has no namespace declarations)\n const parser = new XMLParser({ defaultNamespace: \"\" });\n parser.on(\"openElement\", (name, attrs) => builder.onOpenElement(name, attrs));\n parser.on(\"closeElement\", (name) => builder.onCloseElement(name));\n parser.on(\"text\", (text) => builder.onText(text));\n\n const stream = createReadStream(input, \"utf-8\");\n await parser.parseStream(stream);\n\n // Track peak memory\n const rss = process.memoryUsage().rss;\n if (rss > peakMemory) peakMemory = rss;\n\n // Get part-level notes captured by the builder during parsing\n const partNotes = builder.getPartNotes();\n\n // Extract title info\n let titleNumber = \"0\";\n let titleName = \"\";\n const firstCollected = collected[0];\n if (firstCollected) {\n const firstCtx = firstCollected.context;\n const titleAncestor = firstCtx.ancestors.find((a) => a.levelType === \"title\");\n if (titleAncestor) {\n titleNumber = titleAncestor.numValue ?? \"0\";\n titleName = titleAncestor.heading ?? firstCtx.documentMeta.dcTitle ?? \"\";\n } else if (firstCollected.node.levelType === \"title\") {\n titleNumber = firstCollected.node.numValue ?? \"0\";\n titleName = firstCollected.node.heading ?? \"\";\n }\n }\n\n // Notes filter\n const notesFilter = buildNotesFilter(options);\n const renderOpts: RenderOptions = {\n headingOffset: 0,\n linkStyle: options.linkStyle,\n notesFilter,\n };\n\n if (dryRun) {\n return buildDryRunResult(collected, granularity, titleNumber, titleName, peakMemory);\n }\n\n // Two-pass link registration for section granularity\n const linkResolver = createLinkResolver();\n const sectionMetas: SectionMeta[] = [];\n\n if (granularity === \"section\") {\n // Pass 1: compute output paths, detect duplicates, and register all\n // identifiers with the link resolver BEFORE rendering. This ensures\n // both forward and backward cross-references can resolve.\n const counts = new Map<string, number>();\n for (const { node, context } of collected) {\n const partNum = context.ancestors.find((a) => a.levelType === \"part\")?.numValue ?? \"__root__\";\n const secNum = node.numValue ?? \"0\";\n const key = `${partNum}/${secNum}`;\n counts.set(key, (counts.get(key) ?? 0) + 1);\n }\n\n const seen = new Map<string, number>();\n const outputPaths: string[] = [];\n\n for (const { node, context } of collected) {\n const partNum = context.ancestors.find((a) => a.levelType === \"part\")?.numValue ?? \"__root__\";\n const secNum = node.numValue ?? \"0\";\n const key = `${partNum}/${secNum}`;\n const occurrence = (seen.get(key) ?? 0) + 1;\n seen.set(key, occurrence);\n\n const total = counts.get(key) ?? 1;\n const suffix = total > 1 && occurrence > 1 ? `-${occurrence}` : \"\";\n\n const filePath = buildEcfrOutputPath(node, context, output);\n const suffixedPath = suffix ? filePath.replace(/\\.md$/, `${suffix}.md`) : filePath;\n outputPaths.push(suffixedPath);\n\n // Register canonical identifier for first occurrence only\n if (node.identifier && occurrence === 1) {\n linkResolver.register(node.identifier, suffixedPath);\n }\n }\n\n // Pass 2: render and write files (all identifiers are now registered)\n for (let i = 0; i < collected.length; i++) {\n const item = collected[i];\n const suffixedPath = outputPaths[i];\n if (!item || !suffixedPath) continue;\n const { node, context } = item;\n\n const frontmatter = buildEcfrFrontmatter(node, context);\n // Enrich with part-level authority/source from builder's captured notes\n const partId = context.ancestors.find((a) => a.levelType === \"part\")?.identifier;\n if (partId && (!frontmatter.authority || !frontmatter.regulatory_source)) {\n const partNoteData = partNotes.get(partId);\n if (partNoteData) {\n if (!frontmatter.authority && partNoteData.authority) {\n frontmatter.authority = partNoteData.authority;\n }\n if (!frontmatter.regulatory_source && partNoteData.regulatorySource) {\n frontmatter.regulatory_source = partNoteData.regulatorySource;\n }\n }\n }\n\n const fromFile = suffixedPath;\n const markdown = renderDocument(node, frontmatter, {\n ...renderOpts,\n resolveLink: (identifier: string) => linkResolver.resolve(identifier, fromFile),\n });\n\n await mkdir(dirname(suffixedPath), { recursive: true });\n await writeFile(suffixedPath, markdown, \"utf-8\");\n\n const hasNotes = node.children.some((c) => c.type === \"note\" || c.type === \"notesContainer\");\n const secNum = node.numValue ?? \"0\";\n const partNum = context.ancestors.find((a) => a.levelType === \"part\")?.numValue ?? \"__root__\";\n\n sectionMetas.push({\n identifier: node.identifier ?? `/us/cfr/t${titleNumber}/s${secNum}`,\n number: secNum,\n name: node.heading?.trim() ?? \"\",\n fileName: basename(suffixedPath),\n relativeFile: relative(buildTitleDir(titleNumber, output), suffixedPath),\n contentLength: markdown.length,\n hasNotes,\n status: node.status ?? \"current\",\n partIdentifier: context.ancestors.find((a) => a.levelType === \"part\")?.identifier ?? \"\",\n partNumber: partNum,\n partName: context.ancestors.find((a) => a.levelType === \"part\")?.heading?.trim() ?? \"\",\n });\n\n // Track peak memory\n const currentRss = process.memoryUsage().rss;\n if (currentRss > peakMemory) peakMemory = currentRss;\n }\n\n // Write _meta.json and README (dryRun returns early above, so this always runs)\n await writeMetaFiles(sectionMetas, titleNumber, titleName, output, granularity, input);\n\n const files = sectionMetas.map((m) => join(buildTitleDir(titleNumber, output), m.relativeFile));\n\n return {\n sectionsWritten: sectionMetas.length,\n files,\n titleNumber,\n titleName,\n dryRun: false,\n partCount: new Set(sectionMetas.map((s) => s.partNumber)).size,\n totalTokenEstimate: Math.ceil(sectionMetas.reduce((sum, m) => sum + m.contentLength, 0) / 4),\n peakMemoryBytes: peakMemory,\n };\n }\n\n // Chapter, part, or title granularity\n const files: string[] = [];\n let totalLength = 0;\n\n if (granularity === \"chapter\") {\n // Chapter granularity: group emitted sections by chapter ancestor,\n // then render each chapter as a composite document with all sections inlined.\n const chapterMap = new Map<\n string,\n { sections: CollectedSection[]; chapterAncestor: AncestorInfo; firstContext: EmitContext }\n >();\n\n for (const item of collected) {\n const chapterAnc = item.context.ancestors.find((a) => a.levelType === \"chapter\");\n const chapterKey = chapterAnc?.numValue ?? \"__root__\";\n const existing = chapterMap.get(chapterKey);\n if (existing) {\n existing.sections.push(item);\n } else {\n chapterMap.set(chapterKey, {\n sections: [item],\n chapterAncestor: chapterAnc ?? { levelType: \"chapter\", numValue: chapterKey },\n firstContext: item.context,\n });\n }\n }\n\n for (const [_chapterKey, { sections, chapterAncestor, firstContext }] of chapterMap) {\n // Build a synthetic chapter LevelNode containing all sections\n const chapterNode: LevelNode = {\n type: \"level\",\n levelType: \"chapter\",\n num: chapterAncestor.numValue,\n numValue: chapterAncestor.numValue,\n heading: chapterAncestor.heading,\n identifier: chapterAncestor.identifier,\n children: sections.map((s) => s.node),\n };\n\n const frontmatter = buildEcfrFrontmatter(chapterNode, firstContext);\n const markdown = renderDocument(chapterNode, frontmatter, renderOpts);\n\n const filePath = buildEcfrOutputPath(chapterNode, firstContext, output);\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, markdown, \"utf-8\");\n files.push(filePath);\n totalLength += markdown.length;\n }\n } else {\n // Part or title granularity — filter to target level\n const targetLevel = emitAt;\n const filtered = collected.filter((c) => c.node.levelType === targetLevel);\n\n for (const { node, context } of filtered) {\n const frontmatter = buildEcfrFrontmatter(node, context);\n const markdown = renderDocument(node, frontmatter, renderOpts);\n\n const filePath = buildEcfrOutputPath(node, context, output);\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, markdown, \"utf-8\");\n files.push(filePath);\n totalLength += markdown.length;\n }\n }\n\n // Compute partCount based on granularity\n const partCount =\n granularity === \"part\"\n ? files.length\n : granularity === \"chapter\"\n ? new Set(\n collected\n .map((c) => c.context.ancestors.find((a) => a.levelType === \"part\")?.numValue)\n .filter(Boolean),\n ).size\n : 0;\n\n return {\n sectionsWritten: files.length,\n files,\n titleNumber,\n titleName,\n dryRun: false,\n partCount,\n totalTokenEstimate: Math.ceil(totalLength / 4),\n peakMemoryBytes: peakMemory,\n };\n}\n\nfunction buildDryRunResult(\n collected: CollectedSection[],\n granularity: string,\n titleNumber: string,\n titleName: string,\n peakMemory: number,\n): EcfrConvertResult {\n let totalEstimate = 0;\n let count: number;\n\n if (granularity === \"chapter\") {\n // Count unique chapter ancestors from section-level emissions\n const chapterKeys = new Set<string>();\n for (const { node, context } of collected) {\n const chapterAnc = context.ancestors.find((a) => a.levelType === \"chapter\");\n const key = chapterAnc?.numValue ?? \"__root__\";\n chapterKeys.add(key);\n totalEstimate += estimateTokens(node);\n }\n count = chapterKeys.size;\n } else {\n const targetLevel =\n granularity === \"title\" ? \"title\" : granularity === \"part\" ? \"part\" : \"section\";\n const filtered = collected.filter((c) => c.node.levelType === targetLevel);\n count = filtered.length;\n for (const { node } of filtered) {\n totalEstimate += estimateTokens(node);\n }\n }\n\n return {\n sectionsWritten: count,\n files: [],\n titleNumber,\n titleName,\n dryRun: true,\n partCount: 0,\n totalTokenEstimate: totalEstimate,\n peakMemoryBytes: peakMemory,\n };\n}\n\nfunction estimateTokens(node: LevelNode): number {\n let length = 0;\n\n function walk(n: ASTNode): void {\n if (n.type === \"inline\" && \"text\" in n && n.text) {\n length += (n.text as string).length;\n }\n if (\"children\" in n && Array.isArray(n.children)) {\n for (const child of n.children) {\n walk(child as ASTNode);\n }\n }\n }\n\n walk(node);\n return Math.ceil(length / 4);\n}\n\nfunction buildNotesFilter(options: EcfrConvertOptions): NotesFilter | undefined {\n if (options.includeNotes) return undefined; // Include all\n\n // Check if any selective flag is set\n const hasSelective =\n options.includeEditorialNotes || options.includeStatutoryNotes || options.includeAmendments;\n\n if (!hasSelective) {\n return { editorial: false, statutory: false, amendments: false };\n }\n\n return {\n editorial: options.includeEditorialNotes,\n statutory: options.includeStatutoryNotes,\n amendments: options.includeAmendments,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Metadata file generation (_meta.json + README.md)\n// ---------------------------------------------------------------------------\n\ninterface PartMeta {\n identifier: string;\n number: string;\n name: string;\n directory: string;\n sections: Array<{\n identifier: string;\n number: string;\n name: string;\n file: string;\n token_estimate: number;\n has_notes: boolean;\n status: string;\n }>;\n}\n\n/**\n * Write _meta.json and README.md files for the converted title.\n */\nasync function writeMetaFiles(\n sectionMetas: SectionMeta[],\n titleNumber: string,\n titleName: string,\n outputRoot: string,\n granularity: string,\n sourceXml: string,\n): Promise<void> {\n // Group sections by part\n const partMap = new Map<string, SectionMeta[]>();\n for (const meta of sectionMetas) {\n const key = meta.partNumber;\n const arr = partMap.get(key) ?? [];\n arr.push(meta);\n partMap.set(key, arr);\n }\n\n // Build part metas\n const parts: PartMeta[] = [];\n for (const [partNum, sections] of partMap) {\n const first = sections[0];\n if (!first) continue;\n parts.push({\n identifier: first.partIdentifier || `/us/cfr/t${titleNumber}/pt${partNum}`,\n number: partNum,\n name: first.partName,\n directory: `part-${partNum}`,\n sections: sections.map((s) => ({\n identifier: s.identifier,\n number: s.number,\n name: s.name,\n file: s.fileName,\n token_estimate: Math.ceil(s.contentLength / 4),\n has_notes: s.hasNotes,\n status: s.status,\n })),\n });\n }\n\n const titleDir = buildTitleDir(titleNumber, outputRoot);\n await mkdir(titleDir, { recursive: true });\n\n // Write part-level _meta.json files\n for (const part of parts) {\n const partDir = join(titleDir, getPartDirPath(sectionMetas, part.number));\n await mkdir(partDir, { recursive: true });\n\n const partMeta = {\n format_version: FORMAT_VERSION,\n identifier: part.identifier,\n part_number: part.number,\n part_name: part.name,\n title_number: parseInt(titleNumber, 10),\n section_count: part.sections.length,\n sections: part.sections,\n };\n\n await writeFile(join(partDir, \"_meta.json\"), JSON.stringify(partMeta, null, 2) + \"\\n\", \"utf-8\");\n }\n\n // Write title-level _meta.json\n const totalTokens = sectionMetas.reduce((sum, m) => sum + m.contentLength, 0);\n const titleMeta = {\n format_version: FORMAT_VERSION,\n generator: GENERATOR,\n generated_at: new Date().toISOString(),\n identifier: `/us/cfr/t${titleNumber}`,\n title_number: parseInt(titleNumber, 10),\n title_name: titleName,\n source: \"ecfr\",\n legal_status: \"authoritative_unofficial\",\n currency: new Date().toISOString().slice(0, 10),\n source_xml: basename(sourceXml),\n granularity,\n stats: {\n part_count: parts.length,\n section_count: sectionMetas.length,\n total_files: sectionMetas.length,\n total_tokens_estimate: Math.ceil(totalTokens / 4),\n },\n parts,\n };\n\n await writeFile(join(titleDir, \"_meta.json\"), JSON.stringify(titleMeta, null, 2) + \"\\n\", \"utf-8\");\n\n // Write README.md\n const readme = buildReadme(titleNumber, titleName, parts, sectionMetas, granularity);\n await writeFile(join(titleDir, \"README.md\"), readme, \"utf-8\");\n}\n\n/**\n * Determine the relative directory path for a part within the title dir.\n */\nfunction getPartDirPath(sectionMetas: SectionMeta[], partNumber: string): string {\n // Get the relative file path of the first section in this part\n const first = sectionMetas.find((m) => m.partNumber === partNumber);\n if (!first) return `part-${partNumber}`;\n\n // Extract the directory portion of the relative file path\n const dir = dirname(first.relativeFile);\n return dir === \".\" ? `part-${partNumber}` : dir;\n}\n\nfunction buildReadme(\n titleNumber: string,\n titleName: string,\n parts: PartMeta[],\n sectionMetas: SectionMeta[],\n granularity: string,\n): string {\n const totalTokens = Math.ceil(sectionMetas.reduce((sum, m) => sum + m.contentLength, 0) / 4);\n\n const lines: string[] = [];\n lines.push(`# Title ${titleNumber} — ${titleName}`);\n lines.push(\"\");\n lines.push(\"| Metric | Value |\");\n lines.push(\"|--------|-------|\");\n lines.push(`| Source | eCFR (govinfo.gov) |`);\n lines.push(`| Legal Status | Authoritative, unofficial |`);\n lines.push(`| Parts | ${parts.length.toLocaleString()} |`);\n lines.push(`| Sections | ${sectionMetas.length.toLocaleString()} |`);\n lines.push(`| Estimated Tokens | ${totalTokens.toLocaleString()} |`);\n lines.push(`| Granularity | ${granularity} |`);\n lines.push(\"\");\n lines.push(\"## Parts\");\n lines.push(\"\");\n\n for (const part of parts) {\n lines.push(`### Part ${part.number} — ${part.name} (${part.sections.length} sections)`);\n lines.push(\"\");\n }\n\n lines.push(\"---\");\n lines.push(\"\");\n lines.push(\"Generated by LexBuild\");\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n","/**\n * eCFR AST Builder — converts SAX events from GPO/SGML-derived XML into AST nodes.\n *\n * Follows the same stack-based, emit-at-level pattern as the USLM builder in core,\n * but dispatches on eCFR element names (DIV1-DIV9 with TYPE attributes, HEAD, P, etc.)\n * instead of USLM semantic element names.\n */\n\nimport type { Attributes } from \"@lexbuild/core\";\nimport type {\n LevelType,\n LevelNode,\n ContentNode,\n InlineNode,\n InlineType,\n NoteNode,\n SourceCreditNode,\n TableNode,\n ASTNode,\n AncestorInfo,\n DocumentMeta,\n EmitContext,\n} from \"@lexbuild/core\";\nimport { LEVEL_TYPES } from \"@lexbuild/core\";\nimport {\n ECFR_DIV_ELEMENTS,\n ECFR_TYPE_TO_LEVEL,\n ECFR_CONTENT_ELEMENTS,\n ECFR_INLINE_ELEMENTS,\n ECFR_EMPHASIS_MAP,\n ECFR_NOTE_ELEMENTS,\n ECFR_HEADING_ELEMENTS,\n ECFR_BLOCK_ELEMENTS,\n ECFR_IGNORE_ELEMENTS,\n ECFR_PASSTHROUGH_ELEMENTS,\n ECFR_SKIP_ELEMENTS,\n ECFR_REF_ELEMENTS,\n} from \"./ecfr-elements.js\";\n\n/** Options for configuring the eCFR AST builder */\nexport interface EcfrASTBuilderOptions {\n /** Emit completed nodes at this level instead of accumulating */\n emitAt: LevelType;\n /** Callback when a completed node is ready */\n onEmit: (node: LevelNode, context: EmitContext) => void | Promise<void>;\n}\n\n/** Frame kinds for the stack */\ntype FrameKind =\n | \"level\"\n | \"content\"\n | \"inline\"\n | \"note\"\n | \"heading\"\n | \"ignore\"\n | \"table\"\n | \"tableRow\"\n | \"tableCell\"\n | \"noteContent\"\n | \"block\";\n\n/** A stack frame tracking an in-progress element */\ninterface StackFrame {\n kind: FrameKind;\n elementName: string;\n node?: ASTNode;\n textBuffer: string;\n /** For table collection */\n headers?: string[][];\n rows?: string[][];\n currentRow?: string[];\n isHeaderRow?: boolean;\n}\n\n/**\n * eCFR AST Builder. Consumes SAX events and produces LexBuild AST nodes.\n */\nexport class EcfrASTBuilder {\n private readonly options: EcfrASTBuilderOptions;\n private readonly stack: StackFrame[] = [];\n private documentMeta: DocumentMeta = {};\n private readonly emitAtIndex: number;\n /** Track title number from metadata header */\n private titleNumber = \"\";\n /** Depth inside CFRTOC or other ignored container */\n private ignoredContainerDepth = 0;\n /** Part-level notes (authority/source) keyed by part identifier */\n private readonly partNotes = new Map<\n string,\n { authority?: string | undefined; regulatorySource?: string | undefined }\n >();\n\n constructor(options: EcfrASTBuilderOptions) {\n this.options = options;\n this.emitAtIndex = LEVEL_TYPES.indexOf(options.emitAt);\n }\n\n /** Get part-level notes (authority/source) captured during parsing */\n getPartNotes(): ReadonlyMap<\n string,\n { authority?: string | undefined; regulatorySource?: string | undefined }\n > {\n return this.partNotes;\n }\n\n /** Handle SAX open element */\n onOpenElement(name: string, attrs: Attributes): void {\n // Track ignored containers (skip entire subtree)\n if (this.ignoredContainerDepth > 0) {\n this.ignoredContainerDepth++;\n return;\n }\n\n // Full-subtree ignore elements (e.g., CFRTOC, HEADER)\n if (ECFR_IGNORE_ELEMENTS.has(name)) {\n this.ignoredContainerDepth = 1;\n return;\n }\n\n // Transparent pass-through elements — no frame needed\n if (ECFR_PASSTHROUGH_ELEMENTS.has(name)) {\n return;\n }\n\n // Self-contained skip elements — no subtree concerns\n if (ECFR_SKIP_ELEMENTS.has(name)) {\n this.ignoredContainerDepth = 1;\n return;\n }\n\n // DIV elements → level nodes\n if (ECFR_DIV_ELEMENTS.has(name)) {\n const divType = attrs[\"TYPE\"];\n if (divType) {\n const levelType = ECFR_TYPE_TO_LEVEL[divType];\n if (levelType) {\n this.openLevel(levelType, name, attrs);\n return;\n }\n }\n // DIV without recognized TYPE — treat as structural wrapper, push ignore\n this.stack.push({ kind: \"ignore\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // HEAD element → collect heading text\n if (name === \"HEAD\") {\n this.stack.push({ kind: \"heading\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // HED element (label inside AUTH/SOURCE/etc.) → collect as heading\n if (name === \"HED\") {\n this.stack.push({ kind: \"heading\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // PSPACE element (content inside AUTH/SOURCE/etc.) → collect text\n if (name === \"PSPACE\") {\n this.stack.push({ kind: \"noteContent\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // Content elements (P, FP, etc.)\n if (ECFR_CONTENT_ELEMENTS.has(name)) {\n this.openContent(name);\n return;\n }\n\n // Sub-headings within sections (HD1, HD2, HD3)\n if (ECFR_HEADING_ELEMENTS.has(name)) {\n this.openContent(name);\n return;\n }\n\n // Inline elements\n if (ECFR_INLINE_ELEMENTS.has(name)) {\n this.openInline(name, attrs);\n return;\n }\n\n // Cross-reference elements\n if (ECFR_REF_ELEMENTS.has(name)) {\n this.openRef(name, attrs);\n return;\n }\n\n // Note elements (AUTH, SOURCE, CITA, etc.)\n if (ECFR_NOTE_ELEMENTS.has(name)) {\n this.openNote(name, attrs);\n return;\n }\n\n // Block elements (EXTRACT, EXAMPLE)\n if (ECFR_BLOCK_ELEMENTS.has(name)) {\n this.stack.push({ kind: \"block\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // Table elements\n if (name === \"TABLE\") {\n this.stack.push({\n kind: \"table\",\n elementName: name,\n textBuffer: \"\",\n headers: [],\n rows: [],\n currentRow: [],\n isHeaderRow: false,\n });\n return;\n }\n if (name === \"TR\") {\n const tableFrame = this.findTableFrame();\n if (tableFrame) {\n tableFrame.currentRow = [];\n tableFrame.isHeaderRow = false;\n this.stack.push({ kind: \"tableRow\", elementName: name, textBuffer: \"\" });\n }\n return;\n }\n if (name === \"TH\") {\n const tableFrame = this.findTableFrame();\n if (tableFrame) {\n tableFrame.isHeaderRow = true;\n this.stack.push({ kind: \"tableCell\", elementName: name, textBuffer: \"\" });\n }\n return;\n }\n if (name === \"TD\") {\n this.stack.push({ kind: \"tableCell\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // Lowercase \"div\" wrapper for tables — ignore the wrapper\n if (name === \"DIV\" || name === \"div\") {\n this.stack.push({ kind: \"ignore\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // img elements — skip\n if (name === \"img\") {\n return;\n }\n\n // Unknown elements — push as ignore to maintain stack balance\n this.stack.push({ kind: \"ignore\", elementName: name, textBuffer: \"\" });\n }\n\n /** Handle SAX close element */\n onCloseElement(name: string): void {\n // Track ignored containers\n if (this.ignoredContainerDepth > 0) {\n this.ignoredContainerDepth--;\n return;\n }\n\n // Pass-through elements — no frame to pop\n if (ECFR_PASSTHROUGH_ELEMENTS.has(name)) {\n return;\n }\n\n // HEAD → set heading on parent level node\n if (name === \"HEAD\") {\n const frame = this.popFrame(name);\n if (frame) {\n const parentLevel = this.findParentLevel();\n if (parentLevel?.node && parentLevel.node.type === \"level\") {\n const levelNode = parentLevel.node as LevelNode;\n const headText = frame.textBuffer.trim();\n // Strip section number prefix from heading (e.g., \"§ 1.1 Definitions.\" → \"Definitions.\")\n if (levelNode.levelType === \"section\" && levelNode.numValue) {\n const prefix = `§ ${levelNode.numValue}`;\n let stripped = headText;\n if (stripped.startsWith(prefix)) {\n stripped = stripped\n .slice(prefix.length)\n .replace(/^[\\s.]+/, \"\")\n .trim();\n }\n levelNode.heading = stripped || headText;\n } else {\n // Strip level type prefixes (CHAPTER I—, PART 1—, SUBCHAPTER A—, etc.)\n levelNode.heading = stripLevelPrefix(headText);\n }\n }\n }\n return;\n }\n\n // HED (label inside notes) — just drop the text\n if (name === \"HED\") {\n this.popFrame(name);\n return;\n }\n\n // PSPACE (content inside notes)\n if (name === \"PSPACE\") {\n const frame = this.popFrame(name);\n if (frame) {\n const parentNote = this.findParentNote();\n if (parentNote?.node && parentNote.node.type === \"note\") {\n const noteNode = parentNote.node as NoteNode;\n // Add the text as inline content\n const textNode: InlineNode = {\n type: \"inline\",\n inlineType: \"text\",\n text: frame.textBuffer.trim(),\n };\n const contentNode: ContentNode = {\n type: \"content\",\n variant: \"content\",\n children: [textNode],\n };\n noteNode.children.push(contentNode);\n }\n }\n return;\n }\n\n // DIV elements → close level\n if (ECFR_DIV_ELEMENTS.has(name)) {\n this.closeLevel(name);\n return;\n }\n\n // Content elements\n if (ECFR_CONTENT_ELEMENTS.has(name) || ECFR_HEADING_ELEMENTS.has(name)) {\n this.closeContent(name);\n return;\n }\n\n // Inline elements\n if (ECFR_INLINE_ELEMENTS.has(name)) {\n this.closeInline(name);\n return;\n }\n\n // Cross-reference elements\n if (ECFR_REF_ELEMENTS.has(name)) {\n this.closeInline(name);\n return;\n }\n\n // Note elements\n if (ECFR_NOTE_ELEMENTS.has(name)) {\n this.closeNote(name);\n return;\n }\n\n // Block elements\n if (ECFR_BLOCK_ELEMENTS.has(name)) {\n this.popFrame(name);\n return;\n }\n\n // Table elements\n if (name === \"TABLE\") {\n this.closeTable();\n return;\n }\n if (name === \"TR\") {\n this.closeTableRow();\n return;\n }\n if (name === \"TH\" || name === \"TD\") {\n this.closeTableCell();\n return;\n }\n\n // img — self-closing, no pop needed\n if (name === \"img\") {\n return;\n }\n\n // Pop any remaining frames (ignore, div, etc.)\n if (this.stack.length > 0 && this.stack[this.stack.length - 1]?.elementName === name) {\n this.stack.pop();\n }\n }\n\n /** Handle SAX text content */\n onText(text: string): void {\n if (this.ignoredContainerDepth > 0) return;\n\n const frame = this.stack[this.stack.length - 1];\n if (!frame) return;\n\n // Accumulate text in the current frame\n if (frame.kind === \"heading\" || frame.kind === \"noteContent\" || frame.kind === \"tableCell\") {\n frame.textBuffer += text;\n return;\n }\n\n // For content frames, create text inline node\n if (frame.kind === \"content\" && frame.node?.type === \"content\") {\n const contentNode = frame.node as ContentNode;\n const trimmed = text;\n if (trimmed) {\n contentNode.children.push({\n type: \"inline\",\n inlineType: \"text\",\n text: trimmed,\n });\n }\n return;\n }\n\n // For inline frames, set text\n if (frame.kind === \"inline\" && frame.node?.type === \"inline\") {\n const inlineNode = frame.node as InlineNode;\n if (inlineNode.children) {\n inlineNode.children.push({\n type: \"inline\",\n inlineType: \"text\",\n text,\n });\n } else {\n inlineNode.text = (inlineNode.text ?? \"\") + text;\n }\n return;\n }\n\n // For note frames with direct text content (CITA, APPRO, SECAUTH)\n if (frame.kind === \"note\" && frame.node?.type === \"note\") {\n frame.textBuffer += text;\n return;\n }\n\n // For level frames (text directly in a DIV, outside P elements — rare but possible)\n if (frame.kind === \"level\") {\n // Don't accumulate whitespace-only text at level\n return;\n }\n }\n\n // ---- Private helpers ----\n\n private openLevel(levelType: LevelType, elementName: string, attrs: Attributes): void {\n const nAttr = attrs[\"N\"] ?? \"\";\n const nodeAttr = attrs[\"NODE\"] ?? \"\";\n\n // Parse num and numValue from N attribute\n let numValue = nAttr.replace(/^§\\s*/, \"\").trim();\n const num = nAttr.trim();\n\n // For title-level DIVs, the N attribute is the VOLUME number (not the title number).\n // Multi-volume titles (e.g., Title 17) have multiple DIV1 elements: N=\"1\", N=\"2\", etc.\n // The actual title number is the prefix of the NODE attribute (e.g., NODE=\"17:1\" → 17).\n if (levelType === \"title\") {\n const titleFromNode = nodeAttr.split(\":\")[0];\n if (titleFromNode) {\n numValue = titleFromNode;\n }\n }\n\n // Build identifier from title number and section number\n let identifier: string | undefined;\n if (levelType === \"title\") {\n identifier = `/us/cfr/t${numValue}`;\n this.titleNumber = numValue;\n } else if (levelType === \"section\") {\n identifier = `/us/cfr/t${this.titleNumber}/s${numValue}`;\n } else if (levelType === \"part\") {\n identifier = `/us/cfr/t${this.titleNumber}/pt${numValue}`;\n } else if (levelType === \"chapter\") {\n identifier = `/us/cfr/t${this.titleNumber}/ch${numValue}`;\n }\n\n const node: LevelNode = {\n type: \"level\",\n levelType,\n num: num || undefined,\n numValue: numValue || undefined,\n identifier,\n children: [],\n sourceElement: elementName,\n };\n\n this.stack.push({ kind: \"level\", elementName, node, textBuffer: \"\" });\n }\n\n private closeLevel(elementName: string): void {\n const frame = this.popFrame(elementName);\n if (!frame || frame.kind !== \"level\" || !frame.node) return;\n\n const levelNode = frame.node as LevelNode;\n const levelIndex = LEVEL_TYPES.indexOf(levelNode.levelType);\n\n // Capture part-level authority/source notes before the node is emitted or released\n if (levelNode.levelType === \"part\" && levelNode.identifier) {\n let authority: string | undefined;\n let regulatorySource: string | undefined;\n for (const child of levelNode.children) {\n if (child.type === \"note\") {\n const noteNode = child as NoteNode;\n if (noteNode.noteType === \"authority\" && !authority) {\n authority = this.extractNoteText(noteNode);\n }\n if (noteNode.noteType === \"regulatorySource\" && !regulatorySource) {\n regulatorySource = this.extractNoteText(noteNode);\n }\n }\n }\n if (authority || regulatorySource) {\n this.partNotes.set(levelNode.identifier, { authority, regulatorySource });\n }\n }\n\n // Should we emit this node?\n if (levelIndex >= 0 && levelIndex >= this.emitAtIndex) {\n // Build emit context\n const ancestors: AncestorInfo[] = [];\n for (const f of this.stack) {\n if (f.kind === \"level\" && f.node?.type === \"level\") {\n const ln = f.node as LevelNode;\n ancestors.push({\n levelType: ln.levelType,\n numValue: ln.numValue,\n heading: ln.heading,\n identifier: ln.identifier,\n });\n }\n }\n\n const context: EmitContext = {\n ancestors,\n documentMeta: { ...this.documentMeta },\n };\n\n this.options.onEmit(levelNode, context);\n } else {\n // Add to parent level\n const parentLevel = this.findParentLevel();\n if (parentLevel?.node && parentLevel.node.type === \"level\") {\n (parentLevel.node as LevelNode).children.push(levelNode);\n }\n }\n }\n\n private openContent(elementName: string): void {\n // Determine variant based on element type\n const variant: \"content\" | \"chapeau\" | \"continuation\" | \"proviso\" = \"content\";\n\n // HD elements become bold content to act as sub-headings\n const isSubHeading = ECFR_HEADING_ELEMENTS.has(elementName);\n\n const node: ContentNode = {\n type: \"content\",\n variant,\n children: [],\n };\n\n // For sub-headings, wrap content in bold\n if (isSubHeading) {\n node.children.push({\n type: \"inline\",\n inlineType: \"bold\",\n children: [],\n });\n }\n\n this.stack.push({ kind: \"content\", elementName, node, textBuffer: \"\" });\n }\n\n private closeContent(elementName: string): void {\n const frame = this.popFrame(elementName);\n if (!frame || !frame.node) return;\n\n const contentNode = frame.node as ContentNode;\n\n // For sub-headings, check if the bold wrapper has text\n if (ECFR_HEADING_ELEMENTS.has(elementName)) {\n const boldNode = contentNode.children[0];\n if (boldNode && boldNode.type === \"inline\" && boldNode.inlineType === \"bold\") {\n // If the bold node got text via inline child accumulation, good.\n // If text was added directly to content children (after bold), also good.\n // If neither, the bold node has no text — skip empty heading.\n if (\n !boldNode.text &&\n (!boldNode.children || boldNode.children.length === 0) &&\n contentNode.children.length <= 1\n ) {\n return;\n }\n }\n }\n\n // Add to parent level or note\n const parent = this.findParentLevel() ?? this.findParentNote();\n if (parent?.node) {\n if (parent.node.type === \"level\") {\n (parent.node as LevelNode).children.push(contentNode);\n } else if (parent.node.type === \"note\") {\n (parent.node as NoteNode).children.push(contentNode);\n }\n }\n }\n\n private openInline(elementName: string, attrs: Attributes): void {\n let inlineType: InlineType = \"text\";\n\n if (elementName === \"I\") {\n inlineType = \"italic\";\n } else if (elementName === \"B\") {\n inlineType = \"bold\";\n } else if (elementName === \"SU\") {\n inlineType = \"sup\";\n } else if (elementName === \"FR\") {\n inlineType = \"text\"; // Fractions render as text\n } else if (elementName === \"E\") {\n const tValue = attrs[\"T\"] ?? \"\";\n inlineType = ECFR_EMPHASIS_MAP[tValue] ?? \"italic\";\n }\n\n const node: InlineNode = {\n type: \"inline\",\n inlineType,\n children: [],\n };\n\n this.stack.push({ kind: \"inline\", elementName, node, textBuffer: \"\" });\n }\n\n private openRef(elementName: string, attrs: Attributes): void {\n if (elementName === \"FTREF\") {\n // Footnote reference — will get text like \"1\"\n const node: InlineNode = {\n type: \"inline\",\n inlineType: \"footnoteRef\",\n idref: attrs[\"ID\"],\n };\n this.stack.push({ kind: \"inline\", elementName, node, textBuffer: \"\" });\n } else {\n // XREF — cross-reference\n const node: InlineNode = {\n type: \"inline\",\n inlineType: \"ref\",\n href: attrs[\"ID\"],\n children: [],\n };\n this.stack.push({ kind: \"inline\", elementName, node, textBuffer: \"\" });\n }\n }\n\n private closeInline(elementName: string): void {\n const frame = this.popFrame(elementName);\n if (!frame || !frame.node) return;\n\n const inlineNode = frame.node as InlineNode;\n\n // For footnoteRef, set text from buffer if no children\n if (inlineNode.inlineType === \"footnoteRef\" && frame.textBuffer) {\n inlineNode.text = frame.textBuffer.trim();\n }\n\n // Find parent content or inline to attach to\n const parentFrame = this.stack[this.stack.length - 1];\n if (!parentFrame) return;\n\n if (parentFrame.kind === \"content\" && parentFrame.node?.type === \"content\") {\n const parentContent = parentFrame.node as ContentNode;\n // If parent is a sub-heading with a bold wrapper, add to the bold node\n if (\n ECFR_HEADING_ELEMENTS.has(parentFrame.elementName) &&\n parentContent.children.length > 0 &&\n parentContent.children[0]?.type === \"inline\" &&\n (parentContent.children[0] as InlineNode).inlineType === \"bold\"\n ) {\n const boldNode = parentContent.children[0] as InlineNode;\n if (boldNode.children) {\n boldNode.children.push(inlineNode);\n }\n } else {\n parentContent.children.push(inlineNode);\n }\n } else if (parentFrame.kind === \"inline\" && parentFrame.node?.type === \"inline\") {\n const parentInline = parentFrame.node as InlineNode;\n if (parentInline.children) {\n parentInline.children.push(inlineNode);\n }\n } else if (parentFrame.kind === \"note\") {\n // Text directly in a note element\n frame.textBuffer = \"\";\n }\n }\n\n private openNote(elementName: string, _attrs: Attributes): void {\n // Map element name to a noteType\n const noteTypeMap: Record<string, string> = {\n AUTH: \"authority\",\n SOURCE: \"regulatorySource\",\n EDNOTE: \"editorial\",\n EFFDNOT: \"effectiveDate\",\n CITA: \"citation\",\n APPRO: \"approval\",\n NOTE: \"general\",\n CROSSREF: \"crossReference\",\n SECAUTH: \"sectionAuthority\",\n FTNT: \"footnote\",\n };\n\n const noteType = noteTypeMap[elementName] ?? elementName.toLowerCase();\n\n // For SOURCE, also create a SourceCreditNode\n const node: NoteNode = {\n type: \"note\",\n noteType,\n children: [],\n };\n\n this.stack.push({ kind: \"note\", elementName, node, textBuffer: \"\" });\n }\n\n private closeNote(elementName: string): void {\n const frame = this.popFrame(elementName);\n if (!frame || !frame.node) return;\n\n const noteNode = frame.node as NoteNode;\n\n // For CITA, APPRO, SECAUTH — text was collected in textBuffer\n if (frame.textBuffer.trim() && noteNode.children.length === 0) {\n const textNode: InlineNode = {\n type: \"inline\",\n inlineType: \"text\",\n text: frame.textBuffer.trim(),\n };\n const contentNode: ContentNode = {\n type: \"content\",\n variant: \"content\",\n children: [textNode],\n };\n noteNode.children.push(contentNode);\n }\n\n // Add to parent level\n const parentLevel = this.findParentLevel();\n if (parentLevel?.node && parentLevel.node.type === \"level\") {\n const levelNode = parentLevel.node as LevelNode;\n\n // SOURCE notes also create a SourceCreditNode for compatibility\n if (noteNode.noteType === \"regulatorySource\") {\n const sourceText = this.extractNoteText(noteNode);\n if (sourceText) {\n const sourceCreditNode: SourceCreditNode = {\n type: \"sourceCredit\",\n children: [{ type: \"inline\", inlineType: \"text\", text: sourceText }],\n };\n levelNode.children.push(sourceCreditNode);\n }\n }\n\n levelNode.children.push(noteNode);\n }\n }\n\n private closeTable(): void {\n const frame = this.popFrame(\"TABLE\");\n if (!frame || frame.kind !== \"table\") return;\n\n const tableNode: TableNode = {\n type: \"table\",\n variant: \"xhtml\",\n headers: frame.headers ?? [],\n rows: frame.rows ?? [],\n };\n\n // Add to parent level\n const parentLevel = this.findParentLevel();\n if (parentLevel?.node && parentLevel.node.type === \"level\") {\n (parentLevel.node as LevelNode).children.push(tableNode);\n }\n }\n\n private closeTableRow(): void {\n const rowFrame = this.popFrame(\"TR\");\n if (!rowFrame) return;\n\n const tableFrame = this.findTableFrame();\n if (tableFrame && tableFrame.currentRow) {\n if (tableFrame.isHeaderRow) {\n tableFrame.headers?.push([...tableFrame.currentRow]);\n } else {\n tableFrame.rows?.push([...tableFrame.currentRow]);\n }\n tableFrame.currentRow = [];\n }\n }\n\n private closeTableCell(): void {\n const cellFrame = this.stack.pop();\n if (!cellFrame || cellFrame.kind !== \"tableCell\") return;\n\n const tableFrame = this.findTableFrame();\n if (tableFrame?.currentRow) {\n tableFrame.currentRow.push(cellFrame.textBuffer.trim());\n }\n }\n\n private popFrame(elementName: string): StackFrame | undefined {\n if (this.stack.length === 0) return undefined;\n\n // Find the matching frame (may not be exactly on top due to self-closing elements)\n for (let i = this.stack.length - 1; i >= 0; i--) {\n if (this.stack[i]?.elementName === elementName) {\n return this.stack.splice(i, 1)[0];\n }\n }\n\n // If no exact match, pop top frame\n return this.stack.pop();\n }\n\n private findParentLevel(): StackFrame | undefined {\n for (let i = this.stack.length - 1; i >= 0; i--) {\n if (this.stack[i]?.kind === \"level\") {\n return this.stack[i];\n }\n }\n return undefined;\n }\n\n private findParentNote(): StackFrame | undefined {\n for (let i = this.stack.length - 1; i >= 0; i--) {\n if (this.stack[i]?.kind === \"note\") {\n return this.stack[i];\n }\n }\n return undefined;\n }\n\n private findTableFrame(): StackFrame | undefined {\n for (let i = this.stack.length - 1; i >= 0; i--) {\n if (this.stack[i]?.kind === \"table\") {\n return this.stack[i];\n }\n }\n return undefined;\n }\n\n private extractNoteText(noteNode: NoteNode): string {\n const parts: string[] = [];\n for (const child of noteNode.children) {\n if (child.type === \"content\") {\n for (const inline of (child as ContentNode).children) {\n if (inline.text) parts.push(inline.text);\n }\n }\n }\n return parts.join(\"\").trim();\n }\n}\n\n/**\n * Strip common level-type prefixes from headings.\n * E.g., \"CHAPTER I—ADMINISTRATIVE COMMITTEE\" → \"ADMINISTRATIVE COMMITTEE\"\n * E.g., \"PART 1—DEFINITIONS\" → \"DEFINITIONS\"\n * E.g., \"SUBCHAPTER A—GENERAL\" → \"GENERAL\"\n */\nfunction stripLevelPrefix(heading: string): string {\n // Match: CHAPTER I—text, PART 1—text, SUBCHAPTER A—text, SUBTITLE A—text\n const match =\n /^(?:CHAPTER|PART|SUBCHAPTER|SUBPART|SUBTITLE|DIVISION|ARTICLE)\\s+[A-Za-z0-9]+\\s*[—–-]\\s*/i.exec(\n heading,\n );\n if (match) {\n const stripped = heading.slice(match[0].length).trim();\n return stripped || heading.trim();\n }\n\n // Handle \"Title N—text\" format\n const titleMatch = /^Title\\s+\\d+\\s*[—–-]\\s*/i.exec(heading);\n if (titleMatch) {\n let stripped = heading.slice(titleMatch[0].length).trim();\n // Strip volume suffix like \"--Volume 1\"\n const volIdx = stripped.search(/--Volume\\s/i);\n if (volIdx !== -1) {\n stripped = stripped.slice(0, volIdx).trim();\n }\n return stripped || heading.trim();\n }\n\n return heading.trim();\n}\n","/**\n * eCFR GPO/SGML-derived XML element classification.\n *\n * The eCFR XML uses a numbered DIV system (DIV1-DIV9) where the TYPE\n * attribute determines the semantic level, not the element name.\n *\n * This element vocabulary is shared by the eCFR bulk data and the\n * annual CFR bulk data on govinfo. If a future @lexbuild/cfr package\n * is created for the annual edition, it can import these classifications.\n */\n\nimport type { LevelType, InlineType } from \"@lexbuild/core\";\n\n/** Map from DIV TYPE attribute values to LexBuild level types */\nexport const ECFR_TYPE_TO_LEVEL: Readonly<Record<string, LevelType>> = {\n TITLE: \"title\",\n SUBTITLE: \"subtitle\",\n CHAPTER: \"chapter\",\n SUBCHAP: \"subchapter\",\n PART: \"part\",\n SUBPART: \"subpart\",\n SUBJGRP: \"subpart\", // Subject groups act like subparts\n SECTION: \"section\",\n APPENDIX: \"appendix\",\n};\n\n/** DIV element names (all route to the TYPE-based level mapping) */\nexport const ECFR_DIV_ELEMENTS = new Set([\n \"DIV1\",\n \"DIV2\",\n \"DIV3\",\n \"DIV4\",\n \"DIV5\",\n \"DIV6\",\n \"DIV7\",\n \"DIV8\",\n \"DIV9\",\n]);\n\n/** Elements that contain text content directly */\nexport const ECFR_CONTENT_ELEMENTS = new Set([\n \"P\", // Paragraph (primary content element)\n \"FP\", // Flush paragraph\n \"FP-1\", // Indented flush paragraph (level 1)\n \"FP-2\", // Indented flush paragraph (level 2)\n \"FP-DASH\", // Dash-leader flush paragraph (form lines)\n \"FP1-2\", // Alternative indented paragraph\n \"FRP\", // Flush right paragraph\n]);\n\n/** Elements that contain inline formatting */\nexport const ECFR_INLINE_ELEMENTS = new Set([\n \"I\", // Italic\n \"B\", // Bold\n \"E\", // Emphasis (type varies by T attribute)\n \"SU\", // Superscript\n \"FR\", // Fraction\n \"AC\", // Accent/diacritical\n]);\n\n/** Map from E element T attribute to InlineType */\nexport const ECFR_EMPHASIS_MAP: Readonly<Record<string, InlineType>> = {\n \"01\": \"bold\",\n \"02\": \"italic\",\n \"03\": \"bold\", // bold italic in print — treat as bold for Markdown\n \"04\": \"italic\", // italic in headings\n \"05\": \"italic\", // small caps — render as italic\n \"51\": \"sub\", // subscript\n \"52\": \"sub\", // subscript\n \"54\": \"sub\", // subscript (math)\n \"7462\": \"italic\", // special terms (et seq., De minimis)\n};\n\n/** Note-like elements */\nexport const ECFR_NOTE_ELEMENTS = new Set([\n \"AUTH\", // Authority citation\n \"SOURCE\", // Source/provenance note\n \"EDNOTE\", // Editorial note\n \"EFFDNOT\", // Effective date note\n \"CITA\", // Citation / amendment history\n \"APPRO\", // OMB approval note\n \"NOTE\", // General note\n \"CROSSREF\", // Cross-reference block\n \"SECAUTH\", // Section-level authority\n \"FTNT\", // Footnote\n]);\n\n/** Sub-heading elements within sections/appendices */\nexport const ECFR_HEADING_ELEMENTS = new Set([\"HD1\", \"HD2\", \"HD3\"]);\n\n/** Block-level elements that wrap content */\nexport const ECFR_BLOCK_ELEMENTS = new Set([\n \"EXTRACT\", // Extracted/quoted text\n \"EXAMPLE\", // Example text\n]);\n\n/** Elements to fully ignore (skip entire subtree) */\nexport const ECFR_IGNORE_ELEMENTS = new Set([\n \"CFRTOC\", // Table of contents (skip subtree)\n \"HEADER\", // File metadata header (skip subtree)\n]);\n\n/** Elements that are transparent wrappers — pass through without creating frames */\nexport const ECFR_PASSTHROUGH_ELEMENTS = new Set([\n \"DLPSTEXTCLASS\",\n \"TEXT\",\n \"BODY\",\n \"ECFRBRWS\",\n \"ECFR\", // Root element in eCFR API XML (replaces DLPSTEXTCLASS)\n]);\n\n/** Self-contained elements to skip (no subtree concerns) */\nexport const ECFR_SKIP_ELEMENTS = new Set([\n \"PTHD\", // Part heading in TOC\n \"CHAPTI\", // Chapter item in TOC\n \"SECHD\", // Section heading in TOC\n \"SUBJECT\", // Subject text in TOC\n \"RESERVED\", // Reserved placeholder\n \"PG\", // Page number\n \"STARS\", // Visual separator\n \"AMDDATE\", // Amendment date\n \"VOLUME\", // Volume metadata in eCFR API XML\n]);\n\n/** Cross-reference elements */\nexport const ECFR_REF_ELEMENTS = new Set([\n \"XREF\", // Cross-reference link\n \"FTREF\", // Footnote reference marker\n]);\n\n/** Table elements (HTML-style) */\nexport const ECFR_TABLE_ELEMENTS = new Set([\"TABLE\", \"TR\", \"TH\", \"TD\"]);\n","/**\n * eCFR frontmatter builder.\n *\n * Constructs FrontmatterData from an emitted eCFR AST node and its context.\n */\n\nimport type { LevelNode, EmitContext, FrontmatterData, ASTNode } from \"@lexbuild/core\";\n\n/**\n * Build FrontmatterData from an eCFR section/part/title node.\n */\nexport function buildEcfrFrontmatter(node: LevelNode, context: EmitContext): FrontmatterData {\n const titleAncestor = context.ancestors.find((a) => a.levelType === \"title\");\n const partAncestor = context.ancestors.find((a) => a.levelType === \"part\");\n const chapterAncestor = context.ancestors.find((a) => a.levelType === \"chapter\");\n const subchapterAncestor = context.ancestors.find((a) => a.levelType === \"subchapter\");\n\n const titleNum = parseInt(titleAncestor?.numValue ?? node.numValue ?? \"0\", 10);\n const sectionNum = node.numValue ?? \"0\";\n const sectionName = node.heading?.trim() ?? \"\";\n const titleName = titleAncestor?.heading?.trim() ?? context.documentMeta.dcTitle ?? \"\";\n\n // Build display title based on level type\n let displayTitle: string;\n if (node.levelType === \"title\") {\n displayTitle = `Title ${titleNum} — ${titleName}`;\n } else if (node.levelType === \"part\") {\n displayTitle = `${titleNum} CFR Part ${sectionNum} - ${sectionName}`;\n } else {\n displayTitle = `${titleNum} CFR § ${sectionNum} - ${sectionName}`;\n }\n\n // Extract authority and source from note children\n const authority = extractNoteText(node, \"authority\");\n const regulatorySource = extractNoteText(node, \"regulatorySource\");\n\n // Also check part-level ancestors for authority/source\n // (AUTH and SOURCE appear at part level, not section level)\n const partAuthority = authority ?? extractNoteTextFromAncestors(context, \"authority\");\n const partSource = regulatorySource ?? extractNoteTextFromAncestors(context, \"regulatorySource\");\n\n // Extract source credit text (from SourceCreditNode children)\n const sourceCredit = extractSourceCreditText(node);\n\n const today = new Date().toISOString().slice(0, 10);\n\n const fm: FrontmatterData = {\n source: \"ecfr\",\n legal_status: \"authoritative_unofficial\",\n identifier: node.identifier ?? `/us/cfr/t${titleNum}/s${sectionNum}`,\n title: displayTitle,\n title_number: titleNum,\n title_name: titleName,\n positive_law: false, // Regulations, not legislation\n currency: today,\n last_updated: today,\n };\n\n if (node.levelType === \"section\" || node.levelType === \"part\") {\n fm.section_number = sectionNum;\n fm.section_name = sectionName;\n }\n\n if (chapterAncestor?.numValue) {\n // chapter_number is typed as number — only set for numeric chapters.\n // CFR chapters use Roman numerals (I, II, IV) which won't parse;\n // those are captured in chapter_name instead.\n const parsed = parseInt(chapterAncestor.numValue, 10);\n if (!isNaN(parsed)) {\n fm.chapter_number = parsed;\n }\n }\n if (chapterAncestor?.heading) {\n fm.chapter_name = chapterAncestor.heading.trim();\n }\n if (subchapterAncestor?.numValue) {\n fm.subchapter_number = subchapterAncestor.numValue;\n }\n if (subchapterAncestor?.heading) {\n fm.subchapter_name = subchapterAncestor.heading.trim();\n }\n if (partAncestor?.numValue) {\n fm.part_number = partAncestor.numValue;\n fm.cfr_part = partAncestor.numValue;\n } else if (node.levelType === \"part\") {\n fm.part_number = sectionNum;\n fm.cfr_part = sectionNum;\n }\n if (partAncestor?.heading) {\n fm.part_name = partAncestor.heading.trim();\n } else if (node.levelType === \"part\") {\n fm.part_name = sectionName;\n }\n\n if (partAuthority) {\n fm.authority = partAuthority;\n }\n if (partSource) {\n fm.regulatory_source = partSource;\n }\n if (sourceCredit) {\n fm.source_credit = sourceCredit;\n }\n if (node.status) {\n fm.status = node.status;\n }\n\n return fm;\n}\n\n/**\n * Extract text from a NoteNode child of the given type.\n */\nfunction extractNoteText(node: LevelNode, noteType: string): string | undefined {\n for (const child of node.children) {\n if (child.type === \"note\" && (child as { noteType?: string }).noteType === noteType) {\n return flattenNoteText(child);\n }\n }\n return undefined;\n}\n\n/**\n * Try to find note text from part-level ancestor context.\n * This is a best-effort extraction since we only have ancestor info, not the full AST.\n */\nfunction extractNoteTextFromAncestors(\n _context: EmitContext,\n _noteType: string,\n): string | undefined {\n // Ancestor info doesn't include note children — this would require\n // the converter to pass enriched context. Return undefined for now;\n // authority/source will be populated from part-level notes during\n // the converter's write phase.\n return undefined;\n}\n\n/**\n * Extract source credit text from SourceCreditNode children.\n */\nfunction extractSourceCreditText(node: LevelNode): string | undefined {\n for (const child of node.children) {\n if (child.type === \"sourceCredit\") {\n const parts: string[] = [];\n for (const inline of (child as { children: ASTNode[] }).children) {\n if (inline.type === \"inline\" && \"text\" in inline) {\n parts.push(inline.text as string);\n }\n }\n const text = parts.join(\"\").trim();\n return text || undefined;\n }\n }\n return undefined;\n}\n\n/**\n * Flatten text content from a note node and its children.\n */\nfunction flattenNoteText(node: ASTNode): string {\n const parts: string[] = [];\n\n if (\"children\" in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n if (child.type === \"content\" && \"children\" in child) {\n for (const inline of (child as { children: ASTNode[] }).children) {\n if (inline.type === \"inline\" && \"text\" in inline && inline.text) {\n parts.push(inline.text as string);\n }\n }\n } else if (child.type === \"inline\" && \"text\" in child && child.text) {\n parts.push(child.text as string);\n } else {\n parts.push(flattenNoteText(child));\n }\n }\n }\n\n return parts.join(\"\").trim();\n}\n","/**\n * Output path builder for eCFR directory structure.\n *\n * eCFR path structure:\n * output/ecfr/title-17/chapter-I/part-240/section-240.10b-5.md\n */\n\nimport { join } from \"node:path\";\nimport type { LevelNode, EmitContext } from \"@lexbuild/core\";\n\n/**\n * Build the output file path for an eCFR section.\n */\nexport function buildEcfrOutputPath(\n node: LevelNode,\n context: EmitContext,\n outputRoot: string,\n): string {\n const titleNum = findAncestorValue(context, \"title\") ?? node.numValue ?? \"0\";\n const chapterNum = findAncestorValue(context, \"chapter\");\n const partNum = findAncestorValue(context, \"part\");\n\n const titleDir = `title-${padTwo(titleNum)}`;\n const segments = [outputRoot, \"ecfr\", titleDir];\n\n if (chapterNum) {\n segments.push(`chapter-${chapterNum}`);\n }\n\n if (node.levelType === \"title\") {\n // Title-level file — flat file\n return join(outputRoot, \"ecfr\", `${titleDir}.md`);\n } else if (node.levelType === \"chapter\") {\n // Chapter-level file — directly inside title dir (no chapter subdirectory)\n const chapNum = node.numValue ?? \"0\";\n return join(outputRoot, \"ecfr\", titleDir, `chapter-${chapNum}.md`);\n } else if (node.levelType === \"part\") {\n // Part-level file — one file per part inside chapter dir\n segments.push(`part-${node.numValue ?? \"0\"}.md`);\n } else if (node.levelType === \"appendix\") {\n // Appendix — use sanitized name\n const appendixName = sanitizeFilename(node.numValue ?? node.heading ?? \"appendix\");\n if (partNum) {\n segments.push(`part-${partNum}`);\n }\n segments.push(`${appendixName}.md`);\n } else {\n // Section granularity\n if (partNum) {\n segments.push(`part-${partNum}`);\n }\n const sectionNum = node.numValue ?? \"0\";\n segments.push(`section-${sectionNum}.md`);\n }\n\n return join(...segments);\n}\n\n/**\n * Build the directory path for a title (used for _meta.json placement).\n */\nexport function buildTitleDir(titleNum: string, outputRoot: string): string {\n return join(outputRoot, \"ecfr\", `title-${padTwo(titleNum)}`);\n}\n\nfunction findAncestorValue(context: EmitContext, levelType: string): string | undefined {\n return context.ancestors.find((a) => a.levelType === levelType)?.numValue;\n}\n\nfunction padTwo(num: string): string {\n const n = parseInt(num, 10);\n return isNaN(n) ? num : String(n).padStart(2, \"0\");\n}\n\nfunction sanitizeFilename(name: string): string {\n const sanitized = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, 50);\n return sanitized || \"appendix\";\n}\n","/**\n * eCFR bulk XML downloader.\n *\n * Downloads individual title XML files from govinfo.gov's bulk data repository.\n * Unlike the USC downloader, eCFR files are plain XML (not ZIP archives).\n */\n\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { Readable } from \"node:stream\";\n\n/** Base URL for eCFR bulk XML on govinfo */\nconst ECFR_BULK_BASE = \"https://www.govinfo.gov/bulkdata/ECFR\";\n\n/** Total number of CFR titles */\nexport const ECFR_TITLE_COUNT = 50;\n\n/** All eCFR title numbers (1-50) */\nexport const ECFR_TITLE_NUMBERS = Array.from({ length: ECFR_TITLE_COUNT }, (_, i) => i + 1);\n\n/** Titles that are reserved and have no bulk XML on govinfo */\nconst RESERVED_TITLES = new Set([35]);\n\n/** Progress event emitted during download */\nexport interface EcfrDownloadProgress {\n /** Current item index (1-based) */\n current: number;\n /** Total items to process */\n total: number;\n /** Title number being processed */\n titleNumber: number;\n}\n\n/** Options for downloading eCFR titles */\nexport interface EcfrDownloadOptions {\n /** Download directory */\n output: string;\n /** Specific titles to download (1-50), or undefined for all */\n titles?: number[] | undefined;\n /** Progress callback invoked before each title download */\n onProgress?: ((progress: EcfrDownloadProgress) => void) | undefined;\n}\n\n/** Result of a successful download */\nexport interface EcfrDownloadResult {\n /** Number of titles successfully downloaded */\n titlesDownloaded: number;\n /** Paths of downloaded files */\n files: EcfrDownloadedFile[];\n /** Total bytes downloaded */\n totalBytes: number;\n /** Titles that failed to download */\n errors: EcfrDownloadError[];\n}\n\n/** Metadata for a single downloaded file */\nexport interface EcfrDownloadedFile {\n /** Absolute path to the downloaded file */\n path: string;\n /** Title number */\n titleNumber: number;\n /** File size in bytes */\n size: number;\n}\n\n/** Error for a failed download */\nexport interface EcfrDownloadError {\n /** Title number that failed */\n titleNumber: number;\n /** HTTP status code or error message */\n error: string;\n}\n\n/**\n * Build the download URL for an eCFR title.\n */\nexport function buildEcfrDownloadUrl(titleNumber: number): string {\n return `${ECFR_BULK_BASE}/title-${titleNumber}/ECFR-title${titleNumber}.xml`;\n}\n\n/**\n * Download eCFR XML files from govinfo bulk data.\n */\nexport async function downloadEcfrTitles(\n options: EcfrDownloadOptions,\n): Promise<EcfrDownloadResult> {\n const { output, onProgress } = options;\n const titles = options.titles ?? ECFR_TITLE_NUMBERS;\n const downloadable = titles.filter((t) => !RESERVED_TITLES.has(t));\n\n await mkdir(output, { recursive: true });\n const files: EcfrDownloadedFile[] = [];\n const errors: EcfrDownloadError[] = [];\n let totalBytes = 0;\n\n for (const [i, titleNum] of downloadable.entries()) {\n onProgress?.({ current: i + 1, total: downloadable.length, titleNumber: titleNum });\n\n const url = buildEcfrDownloadUrl(titleNum);\n const filePath = join(output, `ECFR-title${titleNum}.xml`);\n\n const response = await fetch(url);\n if (!response.ok) {\n errors.push({ titleNumber: titleNum, error: `HTTP ${response.status}` });\n continue;\n }\n\n const body = response.body;\n if (!body) {\n errors.push({ titleNumber: titleNum, error: \"Empty response body\" });\n continue;\n }\n\n const dest = createWriteStream(filePath);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await pipeline(Readable.fromWeb(body as any), dest);\n\n const fileStat = await stat(filePath);\n const size = fileStat.size;\n totalBytes += size;\n\n files.push({ path: filePath, titleNumber: titleNum, size });\n }\n\n return { titlesDownloaded: files.length, files, totalBytes, errors };\n}\n","/**\n * eCFR API downloader.\n *\n * Downloads individual title XML files from the ecfr.gov versioner API.\n * Unlike the govinfo bulk downloader, this source provides daily-updated,\n * point-in-time data and supports fetching the CFR as of any specific date.\n *\n * Uses per-title currency dates from the /titles metadata endpoint to ensure\n * every title downloads successfully, even when individual titles are being\n * processed or the global import is in progress.\n *\n * API base: https://www.ecfr.gov/api/versioner/v1/\n */\n\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { Readable } from \"node:stream\";\nimport { ECFR_TITLE_NUMBERS, type EcfrDownloadProgress } from \"./downloader.js\";\n\n/** Base URL for the eCFR versioner API */\nconst ECFR_API_BASE = \"https://www.ecfr.gov/api/versioner/v1\";\n\n/** Titles that are reserved and return 404 from the API */\nconst RESERVED_TITLES = new Set([35]);\n\n/** Maximum retry attempts for transient errors (503, 504) */\nconst MAX_RETRIES = 2;\n\n/** Base delay between retries in milliseconds */\nconst RETRY_BASE_DELAY_MS = 3000;\n\n// ---------------------------------------------------------------------------\n// Title metadata\n// ---------------------------------------------------------------------------\n\n/** Metadata for a single CFR title from the eCFR API */\nexport interface EcfrTitleMeta {\n /** Title number (1-50) */\n number: number;\n /** Title name */\n name: string;\n /** Date of the most recent amendment */\n latestAmendedOn: string;\n /** Date of the most recent Federal Register issue incorporated */\n latestIssueDate: string;\n /** Currency date — how current the data is */\n upToDateAsOf: string;\n /** Whether this title is reserved (e.g., Title 35) */\n reserved: boolean;\n /** Whether this title is currently being processed */\n processingInProgress: boolean;\n}\n\n/** Response from the /titles endpoint */\nexport interface EcfrTitlesResponse {\n /** Currency date for the dataset */\n date: string;\n /** Whether a data import is currently in progress */\n importInProgress: boolean;\n /** Per-title metadata */\n titles: EcfrTitleMeta[];\n}\n\n/**\n * Fetch metadata for all CFR titles from the eCFR API.\n *\n * Returns currency dates and amendment info for each title.\n * Useful for staleness detection without downloading XML.\n */\nexport async function fetchEcfrTitlesMeta(): Promise<EcfrTitlesResponse> {\n const response = await fetch(`${ECFR_API_BASE}/titles`);\n if (!response.ok) {\n throw new Error(`Failed to fetch eCFR titles metadata: HTTP ${response.status}`);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- raw API JSON shape\n const data = (await response.json()) as any;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- raw API JSON shape\n const titles: EcfrTitleMeta[] = data.titles.map((t: any) => ({\n number: t.number,\n name: t.name,\n latestAmendedOn: t.latest_amended_on,\n latestIssueDate: t.latest_issue_date,\n upToDateAsOf: t.up_to_date_as_of,\n reserved: t.reserved,\n processingInProgress: t.processing_in_progress ?? false,\n }));\n\n return {\n date: data.meta.date,\n importInProgress: data.meta.import_in_progress,\n titles,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Download options and result types\n// ---------------------------------------------------------------------------\n\n/** Options for downloading eCFR titles from the API */\nexport interface EcfrApiDownloadOptions {\n /** Download directory */\n output: string;\n /** Specific titles to download (1-50), or undefined for all */\n titles?: number[] | undefined;\n /** Point-in-time date (YYYY-MM-DD). Defaults to per-title currency dates. */\n date?: string | undefined;\n /** Pre-fetched title metadata (avoids a second /titles call) */\n titlesMeta?: EcfrTitlesResponse | undefined;\n /** Progress callback invoked before each title download */\n onProgress?: ((progress: EcfrDownloadProgress) => void) | undefined;\n}\n\n/** Result of a download from the eCFR API */\nexport interface EcfrApiDownloadResult {\n /** Number of titles successfully downloaded */\n titlesDownloaded: number;\n /** Paths of downloaded files */\n files: EcfrApiDownloadedFile[];\n /** Total bytes downloaded */\n totalBytes: number;\n /** The primary date used (most common across titles) */\n asOfDate: string;\n /** Titles that failed after retries */\n failed: EcfrDownloadFailure[];\n}\n\n/** Metadata for a single downloaded file from the eCFR API */\nexport interface EcfrApiDownloadedFile {\n /** Absolute path to the downloaded file */\n path: string;\n /** Title number */\n titleNumber: number;\n /** File size in bytes */\n size: number;\n /** The point-in-time date used for this title */\n asOfDate: string;\n}\n\n/** A title that failed to download */\ninterface EcfrDownloadFailure {\n /** Title number */\n titleNumber: number;\n /** HTTP status code of the final attempt */\n status: number;\n /** The date that was attempted */\n dateAttempted: string;\n}\n\n// ---------------------------------------------------------------------------\n// URL construction\n// ---------------------------------------------------------------------------\n\n/**\n * Build the download URL for a full title XML from the eCFR API.\n *\n * @param titleNumber - CFR title number (1-50)\n * @param date - Point-in-time date in YYYY-MM-DD format\n */\nexport function buildEcfrApiDownloadUrl(titleNumber: number, date: string): string {\n return `${ECFR_API_BASE}/full/${date}/title-${titleNumber}.xml`;\n}\n\n// ---------------------------------------------------------------------------\n// Download\n// ---------------------------------------------------------------------------\n\n/**\n * Download eCFR XML files from the ecfr.gov versioner API.\n *\n * Uses per-title currency dates from the /titles metadata to ensure every\n * title downloads successfully. Titles that are being processed get their\n * individual `up_to_date_as_of` date instead of the global date.\n *\n * Retries transient errors (503, 504) up to MAX_RETRIES times with\n * exponential backoff.\n */\nexport async function downloadEcfrTitlesFromApi(\n options: EcfrApiDownloadOptions,\n): Promise<EcfrApiDownloadResult> {\n const { output, onProgress } = options;\n const titles = options.titles ?? ECFR_TITLE_NUMBERS;\n\n // Fetch metadata (or use pre-fetched)\n const meta = options.titlesMeta ?? (await fetchEcfrTitlesMeta());\n\n // Build a map of title number → best available date\n const titleDateMap = new Map<number, string>();\n\n if (options.date) {\n // Explicit date: use it for all titles\n for (const num of titles) {\n titleDateMap.set(num, options.date);\n }\n } else {\n // Auto-detect: use each title's up_to_date_as_of for the most reliable date\n const globalDate = meta.importInProgress\n ? (() => {\n const prev = new Date(meta.date);\n prev.setDate(prev.getDate() - 1);\n return prev.toISOString().slice(0, 10);\n })()\n : meta.date;\n\n for (const num of titles) {\n const titleMeta = meta.titles.find((t) => t.number === num);\n if (titleMeta?.processingInProgress && titleMeta.upToDateAsOf) {\n // Title is being processed — use its individual currency date\n titleDateMap.set(num, titleMeta.upToDateAsOf);\n } else if (titleMeta?.upToDateAsOf) {\n // Use the title's own date if available, falling back to global\n titleDateMap.set(\n num,\n titleMeta.upToDateAsOf < globalDate ? titleMeta.upToDateAsOf : globalDate,\n );\n } else {\n titleDateMap.set(num, globalDate);\n }\n }\n }\n\n await mkdir(output, { recursive: true });\n const files: EcfrApiDownloadedFile[] = [];\n const failed: EcfrDownloadFailure[] = [];\n let totalBytes = 0;\n const downloadable = titles.filter((t) => !RESERVED_TITLES.has(t));\n\n for (const [i, titleNum] of downloadable.entries()) {\n onProgress?.({ current: i + 1, total: downloadable.length, titleNumber: titleNum });\n\n const titleDate = titleDateMap.get(titleNum) ?? meta.date;\n const filePath = join(output, `ECFR-title${titleNum}.xml`);\n\n const result = await downloadWithRetry(titleNum, titleDate, filePath);\n if (result.ok) {\n totalBytes += result.size;\n files.push({ path: filePath, titleNumber: titleNum, size: result.size, asOfDate: titleDate });\n } else {\n failed.push({ titleNumber: titleNum, status: result.status, dateAttempted: titleDate });\n }\n }\n\n // Determine the primary date (most common across downloaded files)\n const dateCounts = new Map<string, number>();\n for (const file of files) {\n dateCounts.set(file.asOfDate, (dateCounts.get(file.asOfDate) ?? 0) + 1);\n }\n const primaryDate =\n options.date ?? [...dateCounts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? meta.date;\n\n return { titlesDownloaded: files.length, files, totalBytes, asOfDate: primaryDate, failed };\n}\n\n/** Download a single title with retry on transient and network errors */\nasync function downloadWithRetry(\n titleNum: number,\n date: string,\n filePath: string,\n): Promise<{ ok: true; size: number } | { ok: false; status: number }> {\n const url = buildEcfrApiDownloadUrl(titleNum, date);\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n const response = await fetch(url);\n\n if (response.ok) {\n const body = response.body;\n if (!body) return { ok: false, status: 0 };\n\n const dest = createWriteStream(filePath);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ReadableStream type bridge\n await pipeline(Readable.fromWeb(body as any), dest);\n\n const fileStat = await stat(filePath);\n return { ok: true, size: fileStat.size };\n }\n\n // Retry on transient HTTP errors (503 Service Unavailable, 504 Gateway Timeout)\n if ((response.status === 503 || response.status === 504) && attempt < MAX_RETRIES) {\n const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n\n // Non-retryable HTTP error or retries exhausted\n return { ok: false, status: response.status };\n } catch {\n // Network-level error (DNS, TLS, connection reset) — retry\n if (attempt < MAX_RETRIES) {\n const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n return { ok: false, status: 0 };\n }\n }\n\n return { ok: false, status: 0 };\n}\n"],"mappings":"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","names":["join","files","join","mkdir","join","createWriteStream","mkdir","stat","join","pipeline","Readable","RESERVED_TITLES","mkdir","join","createWriteStream","pipeline","Readable","stat"]}
|
|
1
|
+
{"version":3,"sources":["../src/converter.ts","../src/ecfr-builder.ts","../src/ecfr-elements.ts","../src/ecfr-frontmatter.ts","../src/ecfr-path.ts","../src/downloader.ts","../src/ecfr-api-downloader.ts"],"sourcesContent":["/**\n * eCFR conversion orchestrator.\n *\n * Follows the same collect-then-write pattern as the USC converter:\n * 1. Parse XML via SAX → feed EcfrASTBuilder\n * 2. Collect emitted sections/parts/titles\n * 3. Two-pass link registration (with duplicate detection)\n * 4. Write Markdown files, _meta.json, and README.md\n */\n\nimport { createReadStream } from \"node:fs\";\nimport { join, dirname, basename, relative } from \"node:path\";\nimport {\n XMLParser,\n renderDocument,\n createLinkResolver,\n FORMAT_VERSION,\n GENERATOR,\n writeFile,\n mkdir,\n} from \"@lexbuild/core\";\nimport type {\n LevelNode,\n LevelType,\n EmitContext,\n RenderOptions,\n NotesFilter,\n ASTNode,\n AncestorInfo,\n} from \"@lexbuild/core\";\nimport { EcfrASTBuilder } from \"./ecfr-builder.js\";\nimport { buildEcfrFrontmatter } from \"./ecfr-frontmatter.js\";\nimport { buildEcfrOutputPath, buildTitleDir } from \"./ecfr-path.js\";\n\n/** Options for converting an eCFR XML file */\nexport interface EcfrConvertOptions {\n /** Path to input eCFR XML file */\n input: string;\n /** Output root directory */\n output: string;\n /** Output granularity: section (default), part, chapter, or title */\n granularity: \"section\" | \"part\" | \"chapter\" | \"title\";\n /** Link style for cross-references */\n linkStyle: \"relative\" | \"canonical\" | \"plaintext\";\n /** Include source credits in output */\n includeSourceCredits: boolean;\n /** Include all notes */\n includeNotes: boolean;\n /** Selectively include editorial notes */\n includeEditorialNotes: boolean;\n /** Selectively include statutory/regulatory notes */\n includeStatutoryNotes: boolean;\n /** Selectively include amendment history */\n includeAmendments: boolean;\n /** Parse only, don't write files */\n dryRun: boolean;\n}\n\n/** Result of an eCFR conversion */\nexport interface EcfrConvertResult {\n /** Number of sections/parts/titles written */\n sectionsWritten: number;\n /** Paths of written files */\n files: string[];\n /** Title number from XML metadata */\n titleNumber: string;\n /** Title name from XML metadata */\n titleName: string;\n /** Whether this was a dry run */\n dryRun: boolean;\n /** Number of unique parts */\n partCount: number;\n /** Total estimated tokens */\n totalTokenEstimate: number;\n /** Peak RSS in bytes during conversion */\n peakMemoryBytes: number;\n}\n\n/** Internal collected section data */\ninterface CollectedSection {\n node: LevelNode;\n context: EmitContext;\n}\n\n/** Internal section metadata for _meta.json */\ninterface SectionMeta {\n identifier: string;\n number: string;\n name: string;\n fileName: string;\n relativeFile: string;\n contentLength: number;\n hasNotes: boolean;\n status: string;\n partIdentifier: string;\n partNumber: string;\n partName: string;\n}\n\n/**\n * Convert an eCFR XML file to structured Markdown.\n */\nexport async function convertEcfrTitle(options: EcfrConvertOptions): Promise<EcfrConvertResult> {\n const { input, output, granularity, dryRun } = options;\n let peakMemory = process.memoryUsage().rss;\n\n // Map granularity to emit level.\n // Chapter and section granularity both emit at section level — chapter mode\n // groups sections by chapter ancestor in the write phase.\n const emitAt: LevelType =\n granularity === \"title\" ? \"title\" : granularity === \"part\" ? \"part\" : \"section\";\n\n // Collect phase\n const collected: CollectedSection[] = [];\n const builder = new EcfrASTBuilder({\n emitAt,\n onEmit: (node, context) => {\n collected.push({ node, context });\n },\n });\n\n // Parse XML — no namespace (eCFR XML has no namespace declarations)\n const parser = new XMLParser({ defaultNamespace: \"\" });\n parser.on(\"openElement\", (name, attrs) => builder.onOpenElement(name, attrs));\n parser.on(\"closeElement\", (name) => builder.onCloseElement(name));\n parser.on(\"text\", (text) => builder.onText(text));\n\n const stream = createReadStream(input, \"utf-8\");\n await parser.parseStream(stream);\n\n // Track peak memory\n const rss = process.memoryUsage().rss;\n if (rss > peakMemory) peakMemory = rss;\n\n // Get part-level notes captured by the builder during parsing\n const partNotes = builder.getPartNotes();\n\n // Extract title info\n let titleNumber = \"0\";\n let titleName = \"\";\n const firstCollected = collected[0];\n if (firstCollected) {\n const firstCtx = firstCollected.context;\n const titleAncestor = firstCtx.ancestors.find((a) => a.levelType === \"title\");\n if (titleAncestor) {\n titleNumber = titleAncestor.numValue ?? \"0\";\n titleName = titleAncestor.heading ?? firstCtx.documentMeta.dcTitle ?? \"\";\n } else if (firstCollected.node.levelType === \"title\") {\n titleNumber = firstCollected.node.numValue ?? \"0\";\n titleName = firstCollected.node.heading ?? \"\";\n }\n }\n\n // Notes filter\n const notesFilter = buildNotesFilter(options);\n const renderOpts: RenderOptions = {\n headingOffset: 0,\n linkStyle: options.linkStyle,\n notesFilter,\n };\n\n if (dryRun) {\n return buildDryRunResult(collected, granularity, titleNumber, titleName, peakMemory);\n }\n\n // Two-pass link registration for section granularity\n const linkResolver = createLinkResolver();\n const sectionMetas: SectionMeta[] = [];\n\n if (granularity === \"section\") {\n // Pass 1: compute output paths, detect duplicates, and register all\n // identifiers with the link resolver BEFORE rendering. This ensures\n // both forward and backward cross-references can resolve.\n const counts = new Map<string, number>();\n for (const { node, context } of collected) {\n const partNum = context.ancestors.find((a) => a.levelType === \"part\")?.numValue ?? \"__root__\";\n const secNum = node.numValue ?? \"0\";\n const key = `${partNum}/${secNum}`;\n counts.set(key, (counts.get(key) ?? 0) + 1);\n }\n\n const seen = new Map<string, number>();\n const outputPaths: string[] = [];\n\n for (const { node, context } of collected) {\n const partNum = context.ancestors.find((a) => a.levelType === \"part\")?.numValue ?? \"__root__\";\n const secNum = node.numValue ?? \"0\";\n const key = `${partNum}/${secNum}`;\n const occurrence = (seen.get(key) ?? 0) + 1;\n seen.set(key, occurrence);\n\n const total = counts.get(key) ?? 1;\n const suffix = total > 1 && occurrence > 1 ? `-${occurrence}` : \"\";\n\n const filePath = buildEcfrOutputPath(node, context, output);\n const suffixedPath = suffix ? filePath.replace(/\\.md$/, `${suffix}.md`) : filePath;\n outputPaths.push(suffixedPath);\n\n // Register canonical identifier for first occurrence only\n if (node.identifier && occurrence === 1) {\n linkResolver.register(node.identifier, suffixedPath);\n }\n }\n\n // Pass 2: render and write files (all identifiers are now registered)\n for (let i = 0; i < collected.length; i++) {\n const item = collected[i];\n const suffixedPath = outputPaths[i];\n if (!item || !suffixedPath) continue;\n const { node, context } = item;\n\n const frontmatter = buildEcfrFrontmatter(node, context);\n // Enrich with part-level authority/source from builder's captured notes\n const partId = context.ancestors.find((a) => a.levelType === \"part\")?.identifier;\n if (partId && (!frontmatter.authority || !frontmatter.regulatory_source)) {\n const partNoteData = partNotes.get(partId);\n if (partNoteData) {\n if (!frontmatter.authority && partNoteData.authority) {\n frontmatter.authority = partNoteData.authority;\n }\n if (!frontmatter.regulatory_source && partNoteData.regulatorySource) {\n frontmatter.regulatory_source = partNoteData.regulatorySource;\n }\n }\n }\n\n const fromFile = suffixedPath;\n const markdown = renderDocument(node, frontmatter, {\n ...renderOpts,\n resolveLink: (identifier: string) => linkResolver.resolve(identifier, fromFile),\n });\n\n await mkdir(dirname(suffixedPath), { recursive: true });\n await writeFile(suffixedPath, markdown, \"utf-8\");\n\n const hasNotes = node.children.some((c) => c.type === \"note\" || c.type === \"notesContainer\");\n const secNum = node.numValue ?? \"0\";\n const partNum = context.ancestors.find((a) => a.levelType === \"part\")?.numValue ?? \"__root__\";\n\n sectionMetas.push({\n identifier: node.identifier ?? `/us/cfr/t${titleNumber}/s${secNum}`,\n number: secNum,\n name: node.heading?.trim() ?? \"\",\n fileName: basename(suffixedPath),\n relativeFile: relative(buildTitleDir(titleNumber, output), suffixedPath),\n contentLength: markdown.length,\n hasNotes,\n status: node.status ?? \"current\",\n partIdentifier: context.ancestors.find((a) => a.levelType === \"part\")?.identifier ?? \"\",\n partNumber: partNum,\n partName: context.ancestors.find((a) => a.levelType === \"part\")?.heading?.trim() ?? \"\",\n });\n\n // Track peak memory\n const currentRss = process.memoryUsage().rss;\n if (currentRss > peakMemory) peakMemory = currentRss;\n }\n\n // Write _meta.json and README (dryRun returns early above, so this always runs)\n await writeMetaFiles(sectionMetas, titleNumber, titleName, output, granularity, input);\n\n const files = sectionMetas.map((m) => join(buildTitleDir(titleNumber, output), m.relativeFile));\n\n return {\n sectionsWritten: sectionMetas.length,\n files,\n titleNumber,\n titleName,\n dryRun: false,\n partCount: new Set(sectionMetas.map((s) => s.partNumber)).size,\n totalTokenEstimate: Math.ceil(sectionMetas.reduce((sum, m) => sum + m.contentLength, 0) / 4),\n peakMemoryBytes: peakMemory,\n };\n }\n\n // Chapter, part, or title granularity\n const files: string[] = [];\n let totalLength = 0;\n\n if (granularity === \"chapter\") {\n // Chapter granularity: group emitted sections by chapter ancestor,\n // then render each chapter as a composite document with all sections inlined.\n const chapterMap = new Map<\n string,\n { sections: CollectedSection[]; chapterAncestor: AncestorInfo; firstContext: EmitContext }\n >();\n\n for (const item of collected) {\n const chapterAnc = item.context.ancestors.find((a) => a.levelType === \"chapter\");\n const chapterKey = chapterAnc?.numValue ?? \"__root__\";\n const existing = chapterMap.get(chapterKey);\n if (existing) {\n existing.sections.push(item);\n } else {\n chapterMap.set(chapterKey, {\n sections: [item],\n chapterAncestor: chapterAnc ?? { levelType: \"chapter\", numValue: chapterKey },\n firstContext: item.context,\n });\n }\n }\n\n for (const [_chapterKey, { sections, chapterAncestor, firstContext }] of chapterMap) {\n // Build a synthetic chapter LevelNode containing all sections\n const chapterNode: LevelNode = {\n type: \"level\",\n levelType: \"chapter\",\n num: chapterAncestor.numValue,\n numValue: chapterAncestor.numValue,\n heading: chapterAncestor.heading,\n identifier: chapterAncestor.identifier,\n children: sections.map((s) => s.node),\n };\n\n const frontmatter = buildEcfrFrontmatter(chapterNode, firstContext);\n const markdown = renderDocument(chapterNode, frontmatter, renderOpts);\n\n const filePath = buildEcfrOutputPath(chapterNode, firstContext, output);\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, markdown, \"utf-8\");\n files.push(filePath);\n totalLength += markdown.length;\n }\n } else {\n // Part or title granularity — filter to target level\n const targetLevel = emitAt;\n const filtered = collected.filter((c) => c.node.levelType === targetLevel);\n\n for (const { node, context } of filtered) {\n const frontmatter = buildEcfrFrontmatter(node, context);\n const markdown = renderDocument(node, frontmatter, renderOpts);\n\n const filePath = buildEcfrOutputPath(node, context, output);\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, markdown, \"utf-8\");\n files.push(filePath);\n totalLength += markdown.length;\n }\n }\n\n // Compute partCount based on granularity\n const partCount =\n granularity === \"part\"\n ? files.length\n : granularity === \"chapter\"\n ? new Set(\n collected\n .map((c) => c.context.ancestors.find((a) => a.levelType === \"part\")?.numValue)\n .filter(Boolean),\n ).size\n : 0;\n\n return {\n sectionsWritten: files.length,\n files,\n titleNumber,\n titleName,\n dryRun: false,\n partCount,\n totalTokenEstimate: Math.ceil(totalLength / 4),\n peakMemoryBytes: peakMemory,\n };\n}\n\nfunction buildDryRunResult(\n collected: CollectedSection[],\n granularity: string,\n titleNumber: string,\n titleName: string,\n peakMemory: number,\n): EcfrConvertResult {\n let totalEstimate = 0;\n let count: number;\n\n if (granularity === \"chapter\") {\n // Count unique chapter ancestors from section-level emissions\n const chapterKeys = new Set<string>();\n for (const { node, context } of collected) {\n const chapterAnc = context.ancestors.find((a) => a.levelType === \"chapter\");\n const key = chapterAnc?.numValue ?? \"__root__\";\n chapterKeys.add(key);\n totalEstimate += estimateTokens(node);\n }\n count = chapterKeys.size;\n } else {\n const targetLevel =\n granularity === \"title\" ? \"title\" : granularity === \"part\" ? \"part\" : \"section\";\n const filtered = collected.filter((c) => c.node.levelType === targetLevel);\n count = filtered.length;\n for (const { node } of filtered) {\n totalEstimate += estimateTokens(node);\n }\n }\n\n return {\n sectionsWritten: count,\n files: [],\n titleNumber,\n titleName,\n dryRun: true,\n partCount: 0,\n totalTokenEstimate: totalEstimate,\n peakMemoryBytes: peakMemory,\n };\n}\n\nfunction estimateTokens(node: LevelNode): number {\n let length = 0;\n\n function walk(n: ASTNode): void {\n if (n.type === \"inline\" && \"text\" in n && n.text) {\n length += (n.text as string).length;\n }\n if (\"children\" in n && Array.isArray(n.children)) {\n for (const child of n.children) {\n walk(child as ASTNode);\n }\n }\n }\n\n walk(node);\n return Math.ceil(length / 4);\n}\n\nfunction buildNotesFilter(options: EcfrConvertOptions): NotesFilter | undefined {\n if (options.includeNotes) return undefined; // Include all\n\n // Check if any selective flag is set\n const hasSelective =\n options.includeEditorialNotes || options.includeStatutoryNotes || options.includeAmendments;\n\n if (!hasSelective) {\n return { editorial: false, statutory: false, amendments: false };\n }\n\n return {\n editorial: options.includeEditorialNotes,\n statutory: options.includeStatutoryNotes,\n amendments: options.includeAmendments,\n };\n}\n\n// --- Metadata file generation (_meta.json + README.md) ---\n\ninterface PartMeta {\n identifier: string;\n number: string;\n name: string;\n directory: string;\n sections: Array<{\n identifier: string;\n number: string;\n name: string;\n file: string;\n token_estimate: number;\n has_notes: boolean;\n status: string;\n }>;\n}\n\n/**\n * Write _meta.json and README.md files for the converted title.\n */\nasync function writeMetaFiles(\n sectionMetas: SectionMeta[],\n titleNumber: string,\n titleName: string,\n outputRoot: string,\n granularity: string,\n sourceXml: string,\n): Promise<void> {\n // Group sections by part\n const partMap = new Map<string, SectionMeta[]>();\n for (const meta of sectionMetas) {\n const key = meta.partNumber;\n const arr = partMap.get(key) ?? [];\n arr.push(meta);\n partMap.set(key, arr);\n }\n\n // Build part metas\n const parts: PartMeta[] = [];\n for (const [partNum, sections] of partMap) {\n const first = sections[0];\n if (!first) continue;\n parts.push({\n identifier: first.partIdentifier || `/us/cfr/t${titleNumber}/pt${partNum}`,\n number: partNum,\n name: first.partName,\n directory: `part-${partNum}`,\n sections: sections.map((s) => ({\n identifier: s.identifier,\n number: s.number,\n name: s.name,\n file: s.fileName,\n token_estimate: Math.ceil(s.contentLength / 4),\n has_notes: s.hasNotes,\n status: s.status,\n })),\n });\n }\n\n const titleDir = buildTitleDir(titleNumber, outputRoot);\n await mkdir(titleDir, { recursive: true });\n\n // Write part-level _meta.json files\n for (const part of parts) {\n const partDir = join(titleDir, getPartDirPath(sectionMetas, part.number));\n await mkdir(partDir, { recursive: true });\n\n const partMeta = {\n format_version: FORMAT_VERSION,\n identifier: part.identifier,\n part_number: part.number,\n part_name: part.name,\n title_number: parseInt(titleNumber, 10),\n section_count: part.sections.length,\n sections: part.sections,\n };\n\n await writeFile(join(partDir, \"_meta.json\"), JSON.stringify(partMeta, null, 2) + \"\\n\", \"utf-8\");\n }\n\n // Write title-level _meta.json\n const totalTokens = sectionMetas.reduce((sum, m) => sum + m.contentLength, 0);\n const titleMeta = {\n format_version: FORMAT_VERSION,\n generator: GENERATOR,\n generated_at: new Date().toISOString(),\n identifier: `/us/cfr/t${titleNumber}`,\n title_number: parseInt(titleNumber, 10),\n title_name: titleName,\n source: \"ecfr\",\n legal_status: \"authoritative_unofficial\",\n currency: new Date().toISOString().slice(0, 10),\n source_xml: basename(sourceXml),\n granularity,\n stats: {\n part_count: parts.length,\n section_count: sectionMetas.length,\n total_files: sectionMetas.length,\n total_tokens_estimate: Math.ceil(totalTokens / 4),\n },\n parts,\n };\n\n await writeFile(join(titleDir, \"_meta.json\"), JSON.stringify(titleMeta, null, 2) + \"\\n\", \"utf-8\");\n\n // Write README.md\n const readme = buildReadme(titleNumber, titleName, parts, sectionMetas, granularity);\n await writeFile(join(titleDir, \"README.md\"), readme, \"utf-8\");\n}\n\n/**\n * Determine the relative directory path for a part within the title dir.\n */\nfunction getPartDirPath(sectionMetas: SectionMeta[], partNumber: string): string {\n // Get the relative file path of the first section in this part\n const first = sectionMetas.find((m) => m.partNumber === partNumber);\n if (!first) return `part-${partNumber}`;\n\n // Extract the directory portion of the relative file path\n const dir = dirname(first.relativeFile);\n return dir === \".\" ? `part-${partNumber}` : dir;\n}\n\nfunction buildReadme(\n titleNumber: string,\n titleName: string,\n parts: PartMeta[],\n sectionMetas: SectionMeta[],\n granularity: string,\n): string {\n const totalTokens = Math.ceil(sectionMetas.reduce((sum, m) => sum + m.contentLength, 0) / 4);\n\n const lines: string[] = [];\n lines.push(`# Title ${titleNumber} — ${titleName}`);\n lines.push(\"\");\n lines.push(\"| Metric | Value |\");\n lines.push(\"|--------|-------|\");\n lines.push(`| Source | eCFR (govinfo.gov) |`);\n lines.push(`| Legal Status | Authoritative, unofficial |`);\n lines.push(`| Parts | ${parts.length.toLocaleString()} |`);\n lines.push(`| Sections | ${sectionMetas.length.toLocaleString()} |`);\n lines.push(`| Estimated Tokens | ${totalTokens.toLocaleString()} |`);\n lines.push(`| Granularity | ${granularity} |`);\n lines.push(\"\");\n lines.push(\"## Parts\");\n lines.push(\"\");\n\n for (const part of parts) {\n lines.push(`### Part ${part.number} — ${part.name} (${part.sections.length} sections)`);\n lines.push(\"\");\n }\n\n lines.push(\"---\");\n lines.push(\"\");\n lines.push(\"Generated by LexBuild\");\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n","/**\n * eCFR AST Builder — converts SAX events from GPO/SGML-derived XML into AST nodes.\n *\n * Follows the same stack-based, emit-at-level pattern as the USLM builder in core,\n * but dispatches on eCFR element names (DIV1-DIV9 with TYPE attributes, HEAD, P, etc.)\n * instead of USLM semantic element names.\n */\n\nimport type { Attributes } from \"@lexbuild/core\";\nimport type {\n LevelType,\n LevelNode,\n ContentNode,\n InlineNode,\n InlineType,\n NoteNode,\n SourceCreditNode,\n TableNode,\n ASTNode,\n AncestorInfo,\n DocumentMeta,\n EmitContext,\n} from \"@lexbuild/core\";\nimport { LEVEL_TYPES } from \"@lexbuild/core\";\nimport {\n ECFR_DIV_ELEMENTS,\n ECFR_TYPE_TO_LEVEL,\n ECFR_CONTENT_ELEMENTS,\n ECFR_INLINE_ELEMENTS,\n ECFR_EMPHASIS_MAP,\n ECFR_NOTE_ELEMENTS,\n ECFR_HEADING_ELEMENTS,\n ECFR_BLOCK_ELEMENTS,\n ECFR_IGNORE_ELEMENTS,\n ECFR_PASSTHROUGH_ELEMENTS,\n ECFR_SKIP_ELEMENTS,\n ECFR_REF_ELEMENTS,\n} from \"./ecfr-elements.js\";\n\n/** Options for configuring the eCFR AST builder */\nexport interface EcfrASTBuilderOptions {\n /** Emit completed nodes at this level instead of accumulating */\n emitAt: LevelType;\n /** Callback when a completed node is ready */\n onEmit: (node: LevelNode, context: EmitContext) => void | Promise<void>;\n}\n\n/** Frame kinds for the stack */\ntype FrameKind =\n | \"level\"\n | \"content\"\n | \"inline\"\n | \"note\"\n | \"heading\"\n | \"ignore\"\n | \"table\"\n | \"tableRow\"\n | \"tableCell\"\n | \"noteContent\"\n | \"block\";\n\n/** A stack frame tracking an in-progress element */\ninterface StackFrame {\n kind: FrameKind;\n elementName: string;\n node?: ASTNode;\n textBuffer: string;\n /** For table collection */\n headers?: string[][];\n rows?: string[][];\n currentRow?: string[];\n isHeaderRow?: boolean;\n}\n\n/**\n * eCFR AST Builder. Consumes SAX events and produces LexBuild AST nodes.\n */\nexport class EcfrASTBuilder {\n private readonly options: EcfrASTBuilderOptions;\n private readonly stack: StackFrame[] = [];\n private documentMeta: DocumentMeta = {};\n private readonly emitAtIndex: number;\n /** Track title number from metadata header */\n private titleNumber = \"\";\n /** Depth inside CFRTOC or other ignored container */\n private ignoredContainerDepth = 0;\n /** Part-level notes (authority/source) keyed by part identifier */\n private readonly partNotes = new Map<\n string,\n { authority?: string | undefined; regulatorySource?: string | undefined }\n >();\n\n constructor(options: EcfrASTBuilderOptions) {\n this.options = options;\n this.emitAtIndex = LEVEL_TYPES.indexOf(options.emitAt);\n }\n\n /** Get part-level notes (authority/source) captured during parsing */\n getPartNotes(): ReadonlyMap<\n string,\n { authority?: string | undefined; regulatorySource?: string | undefined }\n > {\n return this.partNotes;\n }\n\n /** Handle SAX open element */\n onOpenElement(name: string, attrs: Attributes): void {\n // Track ignored containers (skip entire subtree)\n if (this.ignoredContainerDepth > 0) {\n this.ignoredContainerDepth++;\n return;\n }\n\n // Full-subtree ignore elements (e.g., CFRTOC, HEADER)\n if (ECFR_IGNORE_ELEMENTS.has(name)) {\n this.ignoredContainerDepth = 1;\n return;\n }\n\n // Transparent pass-through elements — no frame needed\n if (ECFR_PASSTHROUGH_ELEMENTS.has(name)) {\n return;\n }\n\n // Self-contained skip elements — no subtree concerns\n if (ECFR_SKIP_ELEMENTS.has(name)) {\n this.ignoredContainerDepth = 1;\n return;\n }\n\n // DIV elements → level nodes\n if (ECFR_DIV_ELEMENTS.has(name)) {\n const divType = attrs[\"TYPE\"];\n if (divType) {\n const levelType = ECFR_TYPE_TO_LEVEL[divType];\n if (levelType) {\n this.openLevel(levelType, name, attrs);\n return;\n }\n }\n // DIV without recognized TYPE — treat as structural wrapper, push ignore\n this.stack.push({ kind: \"ignore\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // HEAD element → collect heading text\n if (name === \"HEAD\") {\n this.stack.push({ kind: \"heading\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // HED element (label inside AUTH/SOURCE/etc.) → collect as heading\n if (name === \"HED\") {\n this.stack.push({ kind: \"heading\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // PSPACE element (content inside AUTH/SOURCE/etc.) → collect text\n if (name === \"PSPACE\") {\n this.stack.push({ kind: \"noteContent\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // Content elements (P, FP, etc.)\n if (ECFR_CONTENT_ELEMENTS.has(name)) {\n this.openContent(name);\n return;\n }\n\n // Sub-headings within sections (HD1, HD2, HD3)\n if (ECFR_HEADING_ELEMENTS.has(name)) {\n this.openContent(name);\n return;\n }\n\n // Inline elements\n if (ECFR_INLINE_ELEMENTS.has(name)) {\n this.openInline(name, attrs);\n return;\n }\n\n // Cross-reference elements\n if (ECFR_REF_ELEMENTS.has(name)) {\n this.openRef(name, attrs);\n return;\n }\n\n // Note elements (AUTH, SOURCE, CITA, etc.)\n if (ECFR_NOTE_ELEMENTS.has(name)) {\n this.openNote(name, attrs);\n return;\n }\n\n // Block elements (EXTRACT, EXAMPLE)\n if (ECFR_BLOCK_ELEMENTS.has(name)) {\n this.stack.push({ kind: \"block\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // Table elements\n if (name === \"TABLE\") {\n this.stack.push({\n kind: \"table\",\n elementName: name,\n textBuffer: \"\",\n headers: [],\n rows: [],\n currentRow: [],\n isHeaderRow: false,\n });\n return;\n }\n if (name === \"TR\") {\n const tableFrame = this.findTableFrame();\n if (tableFrame) {\n tableFrame.currentRow = [];\n tableFrame.isHeaderRow = false;\n this.stack.push({ kind: \"tableRow\", elementName: name, textBuffer: \"\" });\n }\n return;\n }\n if (name === \"TH\") {\n const tableFrame = this.findTableFrame();\n if (tableFrame) {\n tableFrame.isHeaderRow = true;\n this.stack.push({ kind: \"tableCell\", elementName: name, textBuffer: \"\" });\n }\n return;\n }\n if (name === \"TD\") {\n this.stack.push({ kind: \"tableCell\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // Lowercase \"div\" wrapper for tables — ignore the wrapper\n if (name === \"DIV\" || name === \"div\") {\n this.stack.push({ kind: \"ignore\", elementName: name, textBuffer: \"\" });\n return;\n }\n\n // img elements — skip\n if (name === \"img\") {\n return;\n }\n\n // Unknown elements — push as ignore to maintain stack balance\n this.stack.push({ kind: \"ignore\", elementName: name, textBuffer: \"\" });\n }\n\n /** Handle SAX close element */\n onCloseElement(name: string): void {\n // Track ignored containers\n if (this.ignoredContainerDepth > 0) {\n this.ignoredContainerDepth--;\n return;\n }\n\n // Pass-through elements — no frame to pop\n if (ECFR_PASSTHROUGH_ELEMENTS.has(name)) {\n return;\n }\n\n // HEAD → set heading on parent level node\n if (name === \"HEAD\") {\n const frame = this.popFrame(name);\n if (frame) {\n const parentLevel = this.findParentLevel();\n if (parentLevel?.node && parentLevel.node.type === \"level\") {\n const levelNode = parentLevel.node as LevelNode;\n const headText = frame.textBuffer.trim();\n // Strip section number prefix from heading (e.g., \"§ 1.1 Definitions.\" → \"Definitions.\")\n if (levelNode.levelType === \"section\" && levelNode.numValue) {\n const prefix = `§ ${levelNode.numValue}`;\n let stripped = headText;\n if (stripped.startsWith(prefix)) {\n stripped = stripped\n .slice(prefix.length)\n .replace(/^[\\s.]+/, \"\")\n .trim();\n }\n levelNode.heading = stripped || headText;\n } else {\n // Strip level type prefixes (CHAPTER I—, PART 1—, SUBCHAPTER A—, etc.)\n levelNode.heading = stripLevelPrefix(headText);\n }\n }\n }\n return;\n }\n\n // HED (label inside notes) — just drop the text\n if (name === \"HED\") {\n this.popFrame(name);\n return;\n }\n\n // PSPACE (content inside notes)\n if (name === \"PSPACE\") {\n const frame = this.popFrame(name);\n if (frame) {\n const parentNote = this.findParentNote();\n if (parentNote?.node && parentNote.node.type === \"note\") {\n const noteNode = parentNote.node as NoteNode;\n // Add the text as inline content\n const textNode: InlineNode = {\n type: \"inline\",\n inlineType: \"text\",\n text: frame.textBuffer.trim(),\n };\n const contentNode: ContentNode = {\n type: \"content\",\n variant: \"content\",\n children: [textNode],\n };\n noteNode.children.push(contentNode);\n }\n }\n return;\n }\n\n // DIV elements → close level\n if (ECFR_DIV_ELEMENTS.has(name)) {\n this.closeLevel(name);\n return;\n }\n\n // Content elements\n if (ECFR_CONTENT_ELEMENTS.has(name) || ECFR_HEADING_ELEMENTS.has(name)) {\n this.closeContent(name);\n return;\n }\n\n // Inline elements\n if (ECFR_INLINE_ELEMENTS.has(name)) {\n this.closeInline(name);\n return;\n }\n\n // Cross-reference elements\n if (ECFR_REF_ELEMENTS.has(name)) {\n this.closeInline(name);\n return;\n }\n\n // Note elements\n if (ECFR_NOTE_ELEMENTS.has(name)) {\n this.closeNote(name);\n return;\n }\n\n // Block elements\n if (ECFR_BLOCK_ELEMENTS.has(name)) {\n this.popFrame(name);\n return;\n }\n\n // Table elements\n if (name === \"TABLE\") {\n this.closeTable();\n return;\n }\n if (name === \"TR\") {\n this.closeTableRow();\n return;\n }\n if (name === \"TH\" || name === \"TD\") {\n this.closeTableCell();\n return;\n }\n\n // img — self-closing, no pop needed\n if (name === \"img\") {\n return;\n }\n\n // Pop any remaining frames (ignore, div, etc.)\n if (this.stack.length > 0 && this.stack[this.stack.length - 1]?.elementName === name) {\n this.stack.pop();\n }\n }\n\n /** Handle SAX text content */\n onText(text: string): void {\n if (this.ignoredContainerDepth > 0) return;\n\n const frame = this.stack[this.stack.length - 1];\n if (!frame) return;\n\n // Accumulate text in the current frame\n if (frame.kind === \"heading\" || frame.kind === \"noteContent\" || frame.kind === \"tableCell\") {\n frame.textBuffer += text;\n return;\n }\n\n // For content frames, create text inline node\n if (frame.kind === \"content\" && frame.node?.type === \"content\") {\n const contentNode = frame.node as ContentNode;\n const trimmed = text;\n if (trimmed) {\n contentNode.children.push({\n type: \"inline\",\n inlineType: \"text\",\n text: trimmed,\n });\n }\n return;\n }\n\n // For inline frames, set text\n if (frame.kind === \"inline\" && frame.node?.type === \"inline\") {\n const inlineNode = frame.node as InlineNode;\n if (inlineNode.children) {\n inlineNode.children.push({\n type: \"inline\",\n inlineType: \"text\",\n text,\n });\n } else {\n inlineNode.text = (inlineNode.text ?? \"\") + text;\n }\n return;\n }\n\n // For note frames with direct text content (CITA, APPRO, SECAUTH)\n if (frame.kind === \"note\" && frame.node?.type === \"note\") {\n frame.textBuffer += text;\n return;\n }\n\n // For level frames (text directly in a DIV, outside P elements — rare but possible)\n if (frame.kind === \"level\") {\n // Don't accumulate whitespace-only text at level\n return;\n }\n }\n\n // ---- Private helpers ----\n\n private openLevel(levelType: LevelType, elementName: string, attrs: Attributes): void {\n const nAttr = attrs[\"N\"] ?? \"\";\n const nodeAttr = attrs[\"NODE\"] ?? \"\";\n\n // Parse num and numValue from N attribute\n let numValue = nAttr.replace(/^§\\s*/, \"\").trim();\n const num = nAttr.trim();\n\n // For title-level DIVs, the N attribute is the VOLUME number (not the title number).\n // Multi-volume titles (e.g., Title 17) have multiple DIV1 elements: N=\"1\", N=\"2\", etc.\n // The actual title number is the prefix of the NODE attribute (e.g., NODE=\"17:1\" → 17).\n if (levelType === \"title\") {\n const titleFromNode = nodeAttr.split(\":\")[0];\n if (titleFromNode) {\n numValue = titleFromNode;\n }\n }\n\n // Build identifier from title number and section number\n let identifier: string | undefined;\n if (levelType === \"title\") {\n identifier = `/us/cfr/t${numValue}`;\n this.titleNumber = numValue;\n } else if (levelType === \"section\") {\n identifier = `/us/cfr/t${this.titleNumber}/s${numValue}`;\n } else if (levelType === \"part\") {\n identifier = `/us/cfr/t${this.titleNumber}/pt${numValue}`;\n } else if (levelType === \"chapter\") {\n identifier = `/us/cfr/t${this.titleNumber}/ch${numValue}`;\n }\n\n const node: LevelNode = {\n type: \"level\",\n levelType,\n num: num || undefined,\n numValue: numValue || undefined,\n identifier,\n children: [],\n sourceElement: elementName,\n };\n\n this.stack.push({ kind: \"level\", elementName, node, textBuffer: \"\" });\n }\n\n private closeLevel(elementName: string): void {\n const frame = this.popFrame(elementName);\n if (!frame || frame.kind !== \"level\" || !frame.node) return;\n\n const levelNode = frame.node as LevelNode;\n const levelIndex = LEVEL_TYPES.indexOf(levelNode.levelType);\n\n // Capture part-level authority/source notes before the node is emitted or released\n if (levelNode.levelType === \"part\" && levelNode.identifier) {\n let authority: string | undefined;\n let regulatorySource: string | undefined;\n for (const child of levelNode.children) {\n if (child.type === \"note\") {\n const noteNode = child as NoteNode;\n if (noteNode.noteType === \"authority\" && !authority) {\n authority = this.extractNoteText(noteNode);\n }\n if (noteNode.noteType === \"regulatorySource\" && !regulatorySource) {\n regulatorySource = this.extractNoteText(noteNode);\n }\n }\n }\n if (authority || regulatorySource) {\n this.partNotes.set(levelNode.identifier, { authority, regulatorySource });\n }\n }\n\n // Should we emit this node?\n if (levelIndex >= 0 && levelIndex >= this.emitAtIndex) {\n // Build emit context\n const ancestors: AncestorInfo[] = [];\n for (const f of this.stack) {\n if (f.kind === \"level\" && f.node?.type === \"level\") {\n const ln = f.node as LevelNode;\n ancestors.push({\n levelType: ln.levelType,\n numValue: ln.numValue,\n heading: ln.heading,\n identifier: ln.identifier,\n });\n }\n }\n\n const context: EmitContext = {\n ancestors,\n documentMeta: { ...this.documentMeta },\n };\n\n this.options.onEmit(levelNode, context);\n } else {\n // Add to parent level\n const parentLevel = this.findParentLevel();\n if (parentLevel?.node && parentLevel.node.type === \"level\") {\n (parentLevel.node as LevelNode).children.push(levelNode);\n }\n }\n }\n\n private openContent(elementName: string): void {\n // Determine variant based on element type\n const variant: \"content\" | \"chapeau\" | \"continuation\" | \"proviso\" = \"content\";\n\n // HD elements become bold content to act as sub-headings\n const isSubHeading = ECFR_HEADING_ELEMENTS.has(elementName);\n\n const node: ContentNode = {\n type: \"content\",\n variant,\n children: [],\n };\n\n // For sub-headings, wrap content in bold\n if (isSubHeading) {\n node.children.push({\n type: \"inline\",\n inlineType: \"bold\",\n children: [],\n });\n }\n\n this.stack.push({ kind: \"content\", elementName, node, textBuffer: \"\" });\n }\n\n private closeContent(elementName: string): void {\n const frame = this.popFrame(elementName);\n if (!frame || !frame.node) return;\n\n const contentNode = frame.node as ContentNode;\n\n // For sub-headings, check if the bold wrapper has text\n if (ECFR_HEADING_ELEMENTS.has(elementName)) {\n const boldNode = contentNode.children[0];\n if (boldNode && boldNode.type === \"inline\" && boldNode.inlineType === \"bold\") {\n // If the bold node got text via inline child accumulation, good.\n // If text was added directly to content children (after bold), also good.\n // If neither, the bold node has no text — skip empty heading.\n if (\n !boldNode.text &&\n (!boldNode.children || boldNode.children.length === 0) &&\n contentNode.children.length <= 1\n ) {\n return;\n }\n }\n }\n\n // Add to parent level or note\n const parent = this.findParentLevel() ?? this.findParentNote();\n if (parent?.node) {\n if (parent.node.type === \"level\") {\n (parent.node as LevelNode).children.push(contentNode);\n } else if (parent.node.type === \"note\") {\n (parent.node as NoteNode).children.push(contentNode);\n }\n }\n }\n\n private openInline(elementName: string, attrs: Attributes): void {\n let inlineType: InlineType = \"text\";\n\n if (elementName === \"I\") {\n inlineType = \"italic\";\n } else if (elementName === \"B\") {\n inlineType = \"bold\";\n } else if (elementName === \"SU\") {\n inlineType = \"sup\";\n } else if (elementName === \"FR\") {\n inlineType = \"text\"; // Fractions render as text\n } else if (elementName === \"E\") {\n const tValue = attrs[\"T\"] ?? \"\";\n inlineType = ECFR_EMPHASIS_MAP[tValue] ?? \"italic\";\n }\n\n const node: InlineNode = {\n type: \"inline\",\n inlineType,\n children: [],\n };\n\n this.stack.push({ kind: \"inline\", elementName, node, textBuffer: \"\" });\n }\n\n private openRef(elementName: string, attrs: Attributes): void {\n if (elementName === \"FTREF\") {\n // Footnote reference — will get text like \"1\"\n const node: InlineNode = {\n type: \"inline\",\n inlineType: \"footnoteRef\",\n idref: attrs[\"ID\"],\n };\n this.stack.push({ kind: \"inline\", elementName, node, textBuffer: \"\" });\n } else {\n // XREF — cross-reference\n const node: InlineNode = {\n type: \"inline\",\n inlineType: \"ref\",\n href: attrs[\"ID\"],\n children: [],\n };\n this.stack.push({ kind: \"inline\", elementName, node, textBuffer: \"\" });\n }\n }\n\n private closeInline(elementName: string): void {\n const frame = this.popFrame(elementName);\n if (!frame || !frame.node) return;\n\n const inlineNode = frame.node as InlineNode;\n\n // For footnoteRef, set text from buffer if no children\n if (inlineNode.inlineType === \"footnoteRef\" && frame.textBuffer) {\n inlineNode.text = frame.textBuffer.trim();\n }\n\n // Find parent content or inline to attach to\n const parentFrame = this.stack[this.stack.length - 1];\n if (!parentFrame) return;\n\n if (parentFrame.kind === \"content\" && parentFrame.node?.type === \"content\") {\n const parentContent = parentFrame.node as ContentNode;\n // If parent is a sub-heading with a bold wrapper, add to the bold node\n if (\n ECFR_HEADING_ELEMENTS.has(parentFrame.elementName) &&\n parentContent.children.length > 0 &&\n parentContent.children[0]?.type === \"inline\" &&\n (parentContent.children[0] as InlineNode).inlineType === \"bold\"\n ) {\n const boldNode = parentContent.children[0] as InlineNode;\n if (boldNode.children) {\n boldNode.children.push(inlineNode);\n }\n } else {\n parentContent.children.push(inlineNode);\n }\n } else if (parentFrame.kind === \"inline\" && parentFrame.node?.type === \"inline\") {\n const parentInline = parentFrame.node as InlineNode;\n if (parentInline.children) {\n parentInline.children.push(inlineNode);\n }\n } else if (parentFrame.kind === \"note\") {\n // Text directly in a note element\n frame.textBuffer = \"\";\n }\n }\n\n private openNote(elementName: string, _attrs: Attributes): void {\n // Map element name to a noteType\n const noteTypeMap: Record<string, string> = {\n AUTH: \"authority\",\n SOURCE: \"regulatorySource\",\n EDNOTE: \"editorial\",\n EFFDNOT: \"effectiveDate\",\n CITA: \"citation\",\n APPRO: \"approval\",\n NOTE: \"general\",\n CROSSREF: \"crossReference\",\n SECAUTH: \"sectionAuthority\",\n FTNT: \"footnote\",\n };\n\n const noteType = noteTypeMap[elementName] ?? elementName.toLowerCase();\n\n // For SOURCE, also create a SourceCreditNode\n const node: NoteNode = {\n type: \"note\",\n noteType,\n children: [],\n };\n\n this.stack.push({ kind: \"note\", elementName, node, textBuffer: \"\" });\n }\n\n private closeNote(elementName: string): void {\n const frame = this.popFrame(elementName);\n if (!frame || !frame.node) return;\n\n const noteNode = frame.node as NoteNode;\n\n // For CITA, APPRO, SECAUTH — text was collected in textBuffer\n if (frame.textBuffer.trim() && noteNode.children.length === 0) {\n const textNode: InlineNode = {\n type: \"inline\",\n inlineType: \"text\",\n text: frame.textBuffer.trim(),\n };\n const contentNode: ContentNode = {\n type: \"content\",\n variant: \"content\",\n children: [textNode],\n };\n noteNode.children.push(contentNode);\n }\n\n // Add to parent level\n const parentLevel = this.findParentLevel();\n if (parentLevel?.node && parentLevel.node.type === \"level\") {\n const levelNode = parentLevel.node as LevelNode;\n\n // SOURCE notes also create a SourceCreditNode for compatibility\n if (noteNode.noteType === \"regulatorySource\") {\n const sourceText = this.extractNoteText(noteNode);\n if (sourceText) {\n const sourceCreditNode: SourceCreditNode = {\n type: \"sourceCredit\",\n children: [{ type: \"inline\", inlineType: \"text\", text: sourceText }],\n };\n levelNode.children.push(sourceCreditNode);\n }\n }\n\n levelNode.children.push(noteNode);\n }\n }\n\n private closeTable(): void {\n const frame = this.popFrame(\"TABLE\");\n if (!frame || frame.kind !== \"table\") return;\n\n const tableNode: TableNode = {\n type: \"table\",\n variant: \"xhtml\",\n headers: frame.headers ?? [],\n rows: frame.rows ?? [],\n };\n\n // Add to parent level\n const parentLevel = this.findParentLevel();\n if (parentLevel?.node && parentLevel.node.type === \"level\") {\n (parentLevel.node as LevelNode).children.push(tableNode);\n }\n }\n\n private closeTableRow(): void {\n const rowFrame = this.popFrame(\"TR\");\n if (!rowFrame) return;\n\n const tableFrame = this.findTableFrame();\n if (tableFrame && tableFrame.currentRow) {\n if (tableFrame.isHeaderRow) {\n tableFrame.headers?.push([...tableFrame.currentRow]);\n } else {\n tableFrame.rows?.push([...tableFrame.currentRow]);\n }\n tableFrame.currentRow = [];\n }\n }\n\n private closeTableCell(): void {\n const cellFrame = this.stack.pop();\n if (!cellFrame || cellFrame.kind !== \"tableCell\") return;\n\n const tableFrame = this.findTableFrame();\n if (tableFrame?.currentRow) {\n tableFrame.currentRow.push(cellFrame.textBuffer.trim());\n }\n }\n\n private popFrame(elementName: string): StackFrame | undefined {\n if (this.stack.length === 0) return undefined;\n\n // Find the matching frame (may not be exactly on top due to self-closing elements)\n for (let i = this.stack.length - 1; i >= 0; i--) {\n if (this.stack[i]?.elementName === elementName) {\n return this.stack.splice(i, 1)[0];\n }\n }\n\n // If no exact match, pop top frame\n return this.stack.pop();\n }\n\n private findParentLevel(): StackFrame | undefined {\n for (let i = this.stack.length - 1; i >= 0; i--) {\n if (this.stack[i]?.kind === \"level\") {\n return this.stack[i];\n }\n }\n return undefined;\n }\n\n private findParentNote(): StackFrame | undefined {\n for (let i = this.stack.length - 1; i >= 0; i--) {\n if (this.stack[i]?.kind === \"note\") {\n return this.stack[i];\n }\n }\n return undefined;\n }\n\n private findTableFrame(): StackFrame | undefined {\n for (let i = this.stack.length - 1; i >= 0; i--) {\n if (this.stack[i]?.kind === \"table\") {\n return this.stack[i];\n }\n }\n return undefined;\n }\n\n private extractNoteText(noteNode: NoteNode): string {\n const parts: string[] = [];\n for (const child of noteNode.children) {\n if (child.type === \"content\") {\n for (const inline of (child as ContentNode).children) {\n if (inline.text) parts.push(inline.text);\n }\n }\n }\n return parts.join(\"\").trim();\n }\n}\n\n/**\n * Strip common level-type prefixes from headings.\n * E.g., \"CHAPTER I—ADMINISTRATIVE COMMITTEE\" → \"ADMINISTRATIVE COMMITTEE\"\n * E.g., \"PART 1—DEFINITIONS\" → \"DEFINITIONS\"\n * E.g., \"SUBCHAPTER A—GENERAL\" → \"GENERAL\"\n */\nfunction stripLevelPrefix(heading: string): string {\n // Match: CHAPTER I—text, PART 1—text, SUBCHAPTER A—text, SUBTITLE A—text\n const match =\n /^(?:CHAPTER|PART|SUBCHAPTER|SUBPART|SUBTITLE|DIVISION|ARTICLE)\\s+[A-Za-z0-9]+\\s*[—–-]\\s*/i.exec(\n heading,\n );\n if (match) {\n const stripped = heading.slice(match[0].length).trim();\n return stripped || heading.trim();\n }\n\n // Handle \"Title N—text\" format\n const titleMatch = /^Title\\s+\\d+\\s*[—–-]\\s*/i.exec(heading);\n if (titleMatch) {\n let stripped = heading.slice(titleMatch[0].length).trim();\n // Strip volume suffix like \"--Volume 1\"\n const volIdx = stripped.search(/--Volume\\s/i);\n if (volIdx !== -1) {\n stripped = stripped.slice(0, volIdx).trim();\n }\n return stripped || heading.trim();\n }\n\n return heading.trim();\n}\n","/**\n * eCFR GPO/SGML-derived XML element classification.\n *\n * The eCFR XML uses a numbered DIV system (DIV1-DIV9) where the TYPE\n * attribute determines the semantic level, not the element name.\n *\n * This element vocabulary is shared by the eCFR bulk data and the\n * annual CFR bulk data on govinfo. If a future @lexbuild/cfr package\n * is created for the annual edition, it can import these classifications.\n */\n\nimport type { LevelType, InlineType } from \"@lexbuild/core\";\n\n/** Map from DIV TYPE attribute values to LexBuild level types */\nexport const ECFR_TYPE_TO_LEVEL: Readonly<Record<string, LevelType>> = {\n TITLE: \"title\",\n SUBTITLE: \"subtitle\",\n CHAPTER: \"chapter\",\n SUBCHAP: \"subchapter\",\n PART: \"part\",\n SUBPART: \"subpart\",\n SUBJGRP: \"subpart\", // Subject groups act like subparts\n SECTION: \"section\",\n APPENDIX: \"appendix\",\n};\n\n/** DIV element names (all route to the TYPE-based level mapping) */\nexport const ECFR_DIV_ELEMENTS = new Set([\n \"DIV1\",\n \"DIV2\",\n \"DIV3\",\n \"DIV4\",\n \"DIV5\",\n \"DIV6\",\n \"DIV7\",\n \"DIV8\",\n \"DIV9\",\n]);\n\n/** Elements that contain text content directly */\nexport const ECFR_CONTENT_ELEMENTS = new Set([\n \"P\", // Paragraph (primary content element)\n \"FP\", // Flush paragraph\n \"FP-1\", // Indented flush paragraph (level 1)\n \"FP-2\", // Indented flush paragraph (level 2)\n \"FP-DASH\", // Dash-leader flush paragraph (form lines)\n \"FP1-2\", // Alternative indented paragraph\n \"FRP\", // Flush right paragraph\n]);\n\n/** Elements that contain inline formatting */\nexport const ECFR_INLINE_ELEMENTS = new Set([\n \"I\", // Italic\n \"B\", // Bold\n \"E\", // Emphasis (type varies by T attribute)\n \"SU\", // Superscript\n \"FR\", // Fraction\n \"AC\", // Accent/diacritical\n]);\n\n/** Map from E element T attribute to InlineType */\nexport const ECFR_EMPHASIS_MAP: Readonly<Record<string, InlineType>> = {\n \"01\": \"bold\",\n \"02\": \"italic\",\n \"03\": \"bold\", // bold italic in print — treat as bold for Markdown\n \"04\": \"italic\", // italic in headings\n \"05\": \"italic\", // small caps — render as italic\n \"51\": \"sub\", // subscript\n \"52\": \"sub\", // subscript\n \"54\": \"sub\", // subscript (math)\n \"7462\": \"italic\", // special terms (et seq., De minimis)\n};\n\n/** Note-like elements */\nexport const ECFR_NOTE_ELEMENTS = new Set([\n \"AUTH\", // Authority citation\n \"SOURCE\", // Source/provenance note\n \"EDNOTE\", // Editorial note\n \"EFFDNOT\", // Effective date note\n \"CITA\", // Citation / amendment history\n \"APPRO\", // OMB approval note\n \"NOTE\", // General note\n \"CROSSREF\", // Cross-reference block\n \"SECAUTH\", // Section-level authority\n \"FTNT\", // Footnote\n]);\n\n/** Sub-heading elements within sections/appendices */\nexport const ECFR_HEADING_ELEMENTS = new Set([\"HD1\", \"HD2\", \"HD3\"]);\n\n/** Block-level elements that wrap content */\nexport const ECFR_BLOCK_ELEMENTS = new Set([\n \"EXTRACT\", // Extracted/quoted text\n \"EXAMPLE\", // Example text\n]);\n\n/** Elements to fully ignore (skip entire subtree) */\nexport const ECFR_IGNORE_ELEMENTS = new Set([\n \"CFRTOC\", // Table of contents (skip subtree)\n \"HEADER\", // File metadata header (skip subtree)\n]);\n\n/** Elements that are transparent wrappers — pass through without creating frames */\nexport const ECFR_PASSTHROUGH_ELEMENTS = new Set([\n \"DLPSTEXTCLASS\",\n \"TEXT\",\n \"BODY\",\n \"ECFRBRWS\",\n \"ECFR\", // Root element in eCFR API XML (replaces DLPSTEXTCLASS)\n]);\n\n/** Self-contained elements to skip (no subtree concerns) */\nexport const ECFR_SKIP_ELEMENTS = new Set([\n \"PTHD\", // Part heading in TOC\n \"CHAPTI\", // Chapter item in TOC\n \"SECHD\", // Section heading in TOC\n \"SUBJECT\", // Subject text in TOC\n \"RESERVED\", // Reserved placeholder\n \"PG\", // Page number\n \"STARS\", // Visual separator\n \"AMDDATE\", // Amendment date\n \"VOLUME\", // Volume metadata in eCFR API XML\n]);\n\n/** Cross-reference elements */\nexport const ECFR_REF_ELEMENTS = new Set([\n \"XREF\", // Cross-reference link\n \"FTREF\", // Footnote reference marker\n]);\n\n/** Table elements (HTML-style) */\nexport const ECFR_TABLE_ELEMENTS = new Set([\"TABLE\", \"TR\", \"TH\", \"TD\"]);\n","/**\n * eCFR frontmatter builder.\n *\n * Constructs FrontmatterData from an emitted eCFR AST node and its context.\n */\n\nimport type { LevelNode, EmitContext, FrontmatterData, ASTNode } from \"@lexbuild/core\";\n\n/**\n * Build FrontmatterData from an eCFR section/part/title node.\n */\nexport function buildEcfrFrontmatter(node: LevelNode, context: EmitContext): FrontmatterData {\n const titleAncestor = context.ancestors.find((a) => a.levelType === \"title\");\n const partAncestor = context.ancestors.find((a) => a.levelType === \"part\");\n const chapterAncestor = context.ancestors.find((a) => a.levelType === \"chapter\");\n const subchapterAncestor = context.ancestors.find((a) => a.levelType === \"subchapter\");\n\n const titleNum = parseInt(titleAncestor?.numValue ?? node.numValue ?? \"0\", 10);\n const sectionNum = node.numValue ?? \"0\";\n const sectionName = node.heading?.trim() ?? \"\";\n const titleName = titleAncestor?.heading?.trim() ?? context.documentMeta.dcTitle ?? \"\";\n\n // Build display title based on level type\n let displayTitle: string;\n if (node.levelType === \"title\") {\n displayTitle = `Title ${titleNum} — ${titleName}`;\n } else if (node.levelType === \"part\") {\n displayTitle = `${titleNum} CFR Part ${sectionNum} - ${sectionName}`;\n } else {\n displayTitle = `${titleNum} CFR § ${sectionNum} - ${sectionName}`;\n }\n\n // Extract authority and source from note children\n const authority = extractNoteText(node, \"authority\");\n const regulatorySource = extractNoteText(node, \"regulatorySource\");\n\n // Also check part-level ancestors for authority/source\n // (AUTH and SOURCE appear at part level, not section level)\n const partAuthority = authority ?? extractNoteTextFromAncestors(context, \"authority\");\n const partSource = regulatorySource ?? extractNoteTextFromAncestors(context, \"regulatorySource\");\n\n // Extract source credit text (from SourceCreditNode children)\n const sourceCredit = extractSourceCreditText(node);\n\n const today = new Date().toISOString().slice(0, 10);\n\n const fm: FrontmatterData = {\n source: \"ecfr\",\n legal_status: \"authoritative_unofficial\",\n identifier: node.identifier ?? `/us/cfr/t${titleNum}/s${sectionNum}`,\n title: displayTitle,\n title_number: titleNum,\n title_name: titleName,\n positive_law: false, // Regulations, not legislation\n currency: today,\n last_updated: today,\n };\n\n if (node.levelType === \"section\" || node.levelType === \"part\") {\n fm.section_number = sectionNum;\n fm.section_name = sectionName;\n }\n\n if (chapterAncestor?.numValue) {\n // chapter_number is typed as number — only set for numeric chapters.\n // CFR chapters use Roman numerals (I, II, IV) which won't parse;\n // those are captured in chapter_name instead.\n const parsed = parseInt(chapterAncestor.numValue, 10);\n if (!isNaN(parsed)) {\n fm.chapter_number = parsed;\n }\n }\n if (chapterAncestor?.heading) {\n fm.chapter_name = chapterAncestor.heading.trim();\n }\n if (subchapterAncestor?.numValue) {\n fm.subchapter_number = subchapterAncestor.numValue;\n }\n if (subchapterAncestor?.heading) {\n fm.subchapter_name = subchapterAncestor.heading.trim();\n }\n if (partAncestor?.numValue) {\n fm.part_number = partAncestor.numValue;\n fm.cfr_part = partAncestor.numValue;\n } else if (node.levelType === \"part\") {\n fm.part_number = sectionNum;\n fm.cfr_part = sectionNum;\n }\n if (partAncestor?.heading) {\n fm.part_name = partAncestor.heading.trim();\n } else if (node.levelType === \"part\") {\n fm.part_name = sectionName;\n }\n\n if (partAuthority) {\n fm.authority = partAuthority;\n }\n if (partSource) {\n fm.regulatory_source = partSource;\n }\n if (sourceCredit) {\n fm.source_credit = sourceCredit;\n }\n if (node.status) {\n fm.status = node.status;\n }\n\n return fm;\n}\n\n/**\n * Extract text from a NoteNode child of the given type.\n */\nfunction extractNoteText(node: LevelNode, noteType: string): string | undefined {\n for (const child of node.children) {\n if (child.type === \"note\" && (child as { noteType?: string }).noteType === noteType) {\n return flattenNoteText(child);\n }\n }\n return undefined;\n}\n\n/**\n * Try to find note text from part-level ancestor context.\n * This is a best-effort extraction since we only have ancestor info, not the full AST.\n */\nfunction extractNoteTextFromAncestors(\n _context: EmitContext,\n _noteType: string,\n): string | undefined {\n // Ancestor info doesn't include note children — this would require\n // the converter to pass enriched context. Return undefined for now;\n // authority/source will be populated from part-level notes during\n // the converter's write phase.\n return undefined;\n}\n\n/**\n * Extract source credit text from SourceCreditNode children.\n */\nfunction extractSourceCreditText(node: LevelNode): string | undefined {\n for (const child of node.children) {\n if (child.type === \"sourceCredit\") {\n const parts: string[] = [];\n for (const inline of (child as { children: ASTNode[] }).children) {\n if (inline.type === \"inline\" && \"text\" in inline) {\n parts.push(inline.text as string);\n }\n }\n const text = parts.join(\"\").trim();\n return text || undefined;\n }\n }\n return undefined;\n}\n\n/**\n * Flatten text content from a note node and its children.\n */\nfunction flattenNoteText(node: ASTNode): string {\n const parts: string[] = [];\n\n if (\"children\" in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n if (child.type === \"content\" && \"children\" in child) {\n for (const inline of (child as { children: ASTNode[] }).children) {\n if (inline.type === \"inline\" && \"text\" in inline && inline.text) {\n parts.push(inline.text as string);\n }\n }\n } else if (child.type === \"inline\" && \"text\" in child && child.text) {\n parts.push(child.text as string);\n } else {\n parts.push(flattenNoteText(child));\n }\n }\n }\n\n return parts.join(\"\").trim();\n}\n","/**\n * Output path builder for eCFR directory structure.\n *\n * eCFR path structure:\n * output/ecfr/title-17/chapter-I/part-240/section-240.10b-5.md\n */\n\nimport { join } from \"node:path\";\nimport type { LevelNode, EmitContext } from \"@lexbuild/core\";\n\n/**\n * Build the output file path for an eCFR section.\n */\nexport function buildEcfrOutputPath(\n node: LevelNode,\n context: EmitContext,\n outputRoot: string,\n): string {\n const titleNum = findAncestorValue(context, \"title\") ?? node.numValue ?? \"0\";\n const chapterNum = findAncestorValue(context, \"chapter\");\n const partNum = findAncestorValue(context, \"part\");\n\n const titleDir = `title-${padTwo(titleNum)}`;\n const segments = [outputRoot, \"ecfr\", titleDir];\n\n if (chapterNum) {\n segments.push(`chapter-${chapterNum}`);\n }\n\n if (node.levelType === \"title\") {\n // Title-level file — flat file\n return join(outputRoot, \"ecfr\", `${titleDir}.md`);\n } else if (node.levelType === \"chapter\") {\n // Chapter-level file — directly inside title dir (no chapter subdirectory)\n const chapNum = node.numValue ?? \"0\";\n return join(outputRoot, \"ecfr\", titleDir, `chapter-${chapNum}.md`);\n } else if (node.levelType === \"part\") {\n // Part-level file — one file per part inside chapter dir\n segments.push(`part-${node.numValue ?? \"0\"}.md`);\n } else if (node.levelType === \"appendix\") {\n // Appendix — use sanitized name\n const appendixName = sanitizeFilename(node.numValue ?? node.heading ?? \"appendix\");\n if (partNum) {\n segments.push(`part-${partNum}`);\n }\n segments.push(`${appendixName}.md`);\n } else {\n // Section granularity\n if (partNum) {\n segments.push(`part-${partNum}`);\n }\n const sectionNum = node.numValue ?? \"0\";\n segments.push(`section-${sectionNum}.md`);\n }\n\n return join(...segments);\n}\n\n/**\n * Build the directory path for a title (used for _meta.json placement).\n */\nexport function buildTitleDir(titleNum: string, outputRoot: string): string {\n return join(outputRoot, \"ecfr\", `title-${padTwo(titleNum)}`);\n}\n\nfunction findAncestorValue(context: EmitContext, levelType: string): string | undefined {\n return context.ancestors.find((a) => a.levelType === levelType)?.numValue;\n}\n\nfunction padTwo(num: string): string {\n const n = parseInt(num, 10);\n return isNaN(n) ? num : String(n).padStart(2, \"0\");\n}\n\nfunction sanitizeFilename(name: string): string {\n const sanitized = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, 50);\n return sanitized || \"appendix\";\n}\n","/**\n * eCFR bulk XML downloader.\n *\n * Downloads individual title XML files from govinfo.gov's bulk data repository.\n * Unlike the USC downloader, eCFR files are plain XML (not ZIP archives).\n */\n\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { Readable } from \"node:stream\";\n\n/** Base URL for eCFR bulk XML on govinfo */\nconst ECFR_BULK_BASE = \"https://www.govinfo.gov/bulkdata/ECFR\";\n\n/** Total number of CFR titles */\nexport const ECFR_TITLE_COUNT = 50;\n\n/** All eCFR title numbers (1-50) */\nexport const ECFR_TITLE_NUMBERS = Array.from({ length: ECFR_TITLE_COUNT }, (_, i) => i + 1);\n\n/** Titles that are reserved and have no bulk XML on govinfo */\nconst RESERVED_TITLES = new Set([35]);\n\n/** Progress event emitted during download */\nexport interface EcfrDownloadProgress {\n /** Current item index (1-based) */\n current: number;\n /** Total items to process */\n total: number;\n /** Title number being processed */\n titleNumber: number;\n}\n\n/** Options for downloading eCFR titles */\nexport interface EcfrDownloadOptions {\n /** Download directory */\n output: string;\n /** Specific titles to download (1-50), or undefined for all */\n titles?: number[] | undefined;\n /** Progress callback invoked before each title download */\n onProgress?: ((progress: EcfrDownloadProgress) => void) | undefined;\n}\n\n/** Result of a successful download */\nexport interface EcfrDownloadResult {\n /** Number of titles successfully downloaded */\n titlesDownloaded: number;\n /** Paths of downloaded files */\n files: EcfrDownloadedFile[];\n /** Total bytes downloaded */\n totalBytes: number;\n /** Titles that failed to download */\n errors: EcfrDownloadError[];\n}\n\n/** Metadata for a single downloaded file */\nexport interface EcfrDownloadedFile {\n /** Absolute path to the downloaded file */\n path: string;\n /** Title number */\n titleNumber: number;\n /** File size in bytes */\n size: number;\n}\n\n/** Error for a failed download */\nexport interface EcfrDownloadError {\n /** Title number that failed */\n titleNumber: number;\n /** HTTP status code or error message */\n error: string;\n}\n\n/**\n * Build the download URL for an eCFR title.\n */\nexport function buildEcfrDownloadUrl(titleNumber: number): string {\n return `${ECFR_BULK_BASE}/title-${titleNumber}/ECFR-title${titleNumber}.xml`;\n}\n\n/**\n * Download eCFR XML files from govinfo bulk data.\n */\nexport async function downloadEcfrTitles(\n options: EcfrDownloadOptions,\n): Promise<EcfrDownloadResult> {\n const { output, onProgress } = options;\n const titles = options.titles ?? ECFR_TITLE_NUMBERS;\n const downloadable = titles.filter((t) => !RESERVED_TITLES.has(t));\n\n await mkdir(output, { recursive: true });\n const files: EcfrDownloadedFile[] = [];\n const errors: EcfrDownloadError[] = [];\n let totalBytes = 0;\n\n for (const [i, titleNum] of downloadable.entries()) {\n onProgress?.({ current: i + 1, total: downloadable.length, titleNumber: titleNum });\n\n const url = buildEcfrDownloadUrl(titleNum);\n const filePath = join(output, `ECFR-title${titleNum}.xml`);\n\n const response = await fetch(url);\n if (!response.ok) {\n errors.push({ titleNumber: titleNum, error: `HTTP ${response.status}` });\n continue;\n }\n\n const body = response.body;\n if (!body) {\n errors.push({ titleNumber: titleNum, error: \"Empty response body\" });\n continue;\n }\n\n const dest = createWriteStream(filePath);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await pipeline(Readable.fromWeb(body as any), dest);\n\n const fileStat = await stat(filePath);\n const size = fileStat.size;\n totalBytes += size;\n\n files.push({ path: filePath, titleNumber: titleNum, size });\n }\n\n return { titlesDownloaded: files.length, files, totalBytes, errors };\n}\n","/**\n * eCFR API downloader.\n *\n * Downloads individual title XML files from the ecfr.gov versioner API.\n * Unlike the govinfo bulk downloader, this source provides daily-updated,\n * point-in-time data and supports fetching the CFR as of any specific date.\n *\n * Uses per-title currency dates from the /titles metadata endpoint to ensure\n * every title downloads successfully, even when individual titles are being\n * processed or the global import is in progress.\n *\n * API base: https://www.ecfr.gov/api/versioner/v1/\n */\n\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { Readable } from \"node:stream\";\nimport { ECFR_TITLE_NUMBERS, type EcfrDownloadProgress } from \"./downloader.js\";\n\n/** Base URL for the eCFR versioner API */\nconst ECFR_API_BASE = \"https://www.ecfr.gov/api/versioner/v1\";\n\n/** Titles that are reserved and return 404 from the API */\nconst RESERVED_TITLES = new Set([35]);\n\n/** Maximum retry attempts for transient errors (503, 504) */\nconst MAX_RETRIES = 2;\n\n/** Base delay between retries in milliseconds */\nconst RETRY_BASE_DELAY_MS = 3000;\n\n// --- Title metadata ---\n\n/** Metadata for a single CFR title from the eCFR API */\nexport interface EcfrTitleMeta {\n /** Title number (1-50) */\n number: number;\n /** Title name */\n name: string;\n /** Date of the most recent amendment */\n latestAmendedOn: string;\n /** Date of the most recent Federal Register issue incorporated */\n latestIssueDate: string;\n /** Currency date — how current the data is */\n upToDateAsOf: string;\n /** Whether this title is reserved (e.g., Title 35) */\n reserved: boolean;\n /** Whether this title is currently being processed */\n processingInProgress: boolean;\n}\n\n/** Response from the /titles endpoint */\nexport interface EcfrTitlesResponse {\n /** Currency date for the dataset */\n date: string;\n /** Whether a data import is currently in progress */\n importInProgress: boolean;\n /** Per-title metadata */\n titles: EcfrTitleMeta[];\n}\n\n/**\n * Fetch metadata for all CFR titles from the eCFR API.\n *\n * Returns currency dates and amendment info for each title.\n * Useful for staleness detection without downloading XML.\n */\nexport async function fetchEcfrTitlesMeta(): Promise<EcfrTitlesResponse> {\n const response = await fetch(`${ECFR_API_BASE}/titles`);\n if (!response.ok) {\n throw new Error(`Failed to fetch eCFR titles metadata: HTTP ${response.status}`);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- raw API JSON shape\n const data = (await response.json()) as any;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- raw API JSON shape\n const titles: EcfrTitleMeta[] = data.titles.map((t: any) => ({\n number: t.number,\n name: t.name,\n latestAmendedOn: t.latest_amended_on,\n latestIssueDate: t.latest_issue_date,\n upToDateAsOf: t.up_to_date_as_of,\n reserved: t.reserved,\n processingInProgress: t.processing_in_progress ?? false,\n }));\n\n return {\n date: data.meta.date,\n importInProgress: data.meta.import_in_progress,\n titles,\n };\n}\n\n// --- Download options and result types ---\n\n/** Options for downloading eCFR titles from the API */\nexport interface EcfrApiDownloadOptions {\n /** Download directory */\n output: string;\n /** Specific titles to download (1-50), or undefined for all */\n titles?: number[] | undefined;\n /** Point-in-time date (YYYY-MM-DD). Defaults to per-title currency dates. */\n date?: string | undefined;\n /** Pre-fetched title metadata (avoids a second /titles call) */\n titlesMeta?: EcfrTitlesResponse | undefined;\n /** Progress callback invoked before each title download */\n onProgress?: ((progress: EcfrDownloadProgress) => void) | undefined;\n}\n\n/** Result of a download from the eCFR API */\nexport interface EcfrApiDownloadResult {\n /** Number of titles successfully downloaded */\n titlesDownloaded: number;\n /** Paths of downloaded files */\n files: EcfrApiDownloadedFile[];\n /** Total bytes downloaded */\n totalBytes: number;\n /** The primary date used (most common across titles) */\n asOfDate: string;\n /** Titles that failed after retries */\n failed: EcfrDownloadFailure[];\n}\n\n/** Metadata for a single downloaded file from the eCFR API */\nexport interface EcfrApiDownloadedFile {\n /** Absolute path to the downloaded file */\n path: string;\n /** Title number */\n titleNumber: number;\n /** File size in bytes */\n size: number;\n /** The point-in-time date used for this title */\n asOfDate: string;\n}\n\n/** A title that failed to download */\ninterface EcfrDownloadFailure {\n /** Title number */\n titleNumber: number;\n /** HTTP status code of the final attempt */\n status: number;\n /** The date that was attempted */\n dateAttempted: string;\n}\n\n// --- URL construction ---\n\n/**\n * Build the download URL for a full title XML from the eCFR API.\n *\n * @param titleNumber - CFR title number (1-50)\n * @param date - Point-in-time date in YYYY-MM-DD format\n */\nexport function buildEcfrApiDownloadUrl(titleNumber: number, date: string): string {\n return `${ECFR_API_BASE}/full/${date}/title-${titleNumber}.xml`;\n}\n\n// --- Download ---\n\n/**\n * Download eCFR XML files from the ecfr.gov versioner API.\n *\n * Uses per-title currency dates from the /titles metadata to ensure every\n * title downloads successfully. Titles that are being processed get their\n * individual `up_to_date_as_of` date instead of the global date.\n *\n * Retries transient errors (503, 504) up to MAX_RETRIES times with\n * exponential backoff.\n */\nexport async function downloadEcfrTitlesFromApi(\n options: EcfrApiDownloadOptions,\n): Promise<EcfrApiDownloadResult> {\n const { output, onProgress } = options;\n const titles = options.titles ?? ECFR_TITLE_NUMBERS;\n\n // Fetch metadata (or use pre-fetched)\n const meta = options.titlesMeta ?? (await fetchEcfrTitlesMeta());\n\n // Build a map of title number → best available date\n const titleDateMap = new Map<number, string>();\n\n if (options.date) {\n // Explicit date: use it for all titles\n for (const num of titles) {\n titleDateMap.set(num, options.date);\n }\n } else {\n // Auto-detect: use each title's up_to_date_as_of for the most reliable date\n const globalDate = meta.importInProgress\n ? (() => {\n const prev = new Date(meta.date);\n prev.setDate(prev.getDate() - 1);\n return prev.toISOString().slice(0, 10);\n })()\n : meta.date;\n\n for (const num of titles) {\n const titleMeta = meta.titles.find((t) => t.number === num);\n if (titleMeta?.processingInProgress && titleMeta.upToDateAsOf) {\n // Title is being processed — use its individual currency date\n titleDateMap.set(num, titleMeta.upToDateAsOf);\n } else if (titleMeta?.upToDateAsOf) {\n // Use the title's own date if available, falling back to global\n titleDateMap.set(\n num,\n titleMeta.upToDateAsOf < globalDate ? titleMeta.upToDateAsOf : globalDate,\n );\n } else {\n titleDateMap.set(num, globalDate);\n }\n }\n }\n\n await mkdir(output, { recursive: true });\n const files: EcfrApiDownloadedFile[] = [];\n const failed: EcfrDownloadFailure[] = [];\n let totalBytes = 0;\n const downloadable = titles.filter((t) => !RESERVED_TITLES.has(t));\n\n for (const [i, titleNum] of downloadable.entries()) {\n onProgress?.({ current: i + 1, total: downloadable.length, titleNumber: titleNum });\n\n const titleDate = titleDateMap.get(titleNum) ?? meta.date;\n const filePath = join(output, `ECFR-title${titleNum}.xml`);\n\n const result = await downloadWithRetry(titleNum, titleDate, filePath);\n if (result.ok) {\n totalBytes += result.size;\n files.push({ path: filePath, titleNumber: titleNum, size: result.size, asOfDate: titleDate });\n } else {\n failed.push({ titleNumber: titleNum, status: result.status, dateAttempted: titleDate });\n }\n }\n\n // Determine the primary date (most common across downloaded files)\n const dateCounts = new Map<string, number>();\n for (const file of files) {\n dateCounts.set(file.asOfDate, (dateCounts.get(file.asOfDate) ?? 0) + 1);\n }\n const primaryDate =\n options.date ?? [...dateCounts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? meta.date;\n\n return { titlesDownloaded: files.length, files, totalBytes, asOfDate: primaryDate, failed };\n}\n\n/** Download a single title with retry on transient and network errors */\nasync function downloadWithRetry(\n titleNum: number,\n date: string,\n filePath: string,\n): Promise<{ ok: true; size: number } | { ok: false; status: number }> {\n const url = buildEcfrApiDownloadUrl(titleNum, date);\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n const response = await fetch(url);\n\n if (response.ok) {\n const body = response.body;\n if (!body) return { ok: false, status: 0 };\n\n const dest = createWriteStream(filePath);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ReadableStream type bridge\n await pipeline(Readable.fromWeb(body as any), dest);\n\n const fileStat = await stat(filePath);\n return { ok: true, size: fileStat.size };\n }\n\n // Retry on transient HTTP errors (503 Service Unavailable, 504 Gateway Timeout)\n if ((response.status === 503 || response.status === 504) && attempt < MAX_RETRIES) {\n const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n\n // Non-retryable HTTP error or retries exhausted\n return { ok: false, status: response.status };\n } catch {\n // Network-level error (DNS, TLS, connection reset) — retry\n if (attempt < MAX_RETRIES) {\n const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n return { ok: false, status: 0 };\n }\n }\n\n return { ok: false, status: 0 };\n}\n"],"mappings":"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","names":["join","files","join","mkdir","join","createWriteStream","mkdir","stat","join","pipeline","Readable","RESERVED_TITLES","mkdir","join","createWriteStream","pipeline","Readable","stat"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lexbuild/ecfr",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "Electronic Code of Federal Regulations (eCFR) to Markdown converter for LexBuild",
|
|
5
5
|
"author": "Chris Thomas",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dist"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@lexbuild/core": "1.
|
|
45
|
+
"@lexbuild/core": "1.16.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^25.3.2",
|