@lexbuild/ecfr 1.9.4 → 1.10.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/README.md CHANGED
@@ -1,29 +1,41 @@
1
1
  # @lexbuild/ecfr
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/%40lexbuild%2Fecfr?style=for-the-badge)](https://www.npmjs.com/package/@lexbuild/ecfr)
4
- [![license](https://img.shields.io/github/license/chris-c-thomas/LexBuild?style=for-the-badge)](https://github.com/chris-c-thomas/LexBuild)
4
+ [![license](https://img.shields.io/github/license/chris-c-thomas/LexBuild?style=for-the-badge)](https://github.com/chris-c-thomas/LexBuild/blob/main/LICENSE)
5
5
 
6
- This package is part of the [LexBuild](https://github.com/chris-c-thomas/LexBuild) monorepo, a tool that converts U.S. legal XML into structured Markdown optimized for AI, RAG pipelines, and semantic search. See the monorepo for full documentation, architecture details, and contribution guidelines.
6
+ Converts [Electronic Code of Federal Regulations](https://www.ecfr.gov/) (eCFR) XML into structured Markdown optimized for AI, RAG pipelines, and semantic search. Supports two download sources: the ecfr.gov API (daily-updated, default) and govinfo.gov bulk data (fallback).
7
7
 
8
- It converts [eCFR](https://www.ecfr.gov/) (Electronic Code of Federal Regulations) bulk XML from [govinfo.gov](https://www.govinfo.gov/bulkdata/ECFR) into structured Markdown and is built on [`@lexbuild/core`](https://www.npmjs.com/package/@lexbuild/core) for shared parsing and rendering infrastructure. It also provides a downloader for fetching the XML directly from govinfo. End users typically interact with this package through [`@lexbuild/cli`](https://www.npmjs.com/package/@lexbuild/cli).
8
+ > **Tip:** For command-line usage, install [`@lexbuild/cli`](https://www.npmjs.com/package/@lexbuild/cli) instead. This package is the programmatic API.
9
9
 
10
10
  ## Install
11
11
 
12
12
  ```bash
13
13
  npm install @lexbuild/ecfr
14
+ # or
15
+ pnpm add @lexbuild/ecfr
14
16
  ```
15
17
 
16
- ## Usage
18
+ **Peer dependency:** [`@lexbuild/core`](https://www.npmjs.com/package/@lexbuild/core) (installed automatically via workspace protocol in the monorepo).
17
19
 
18
- ### Convert a Title
20
+ ## Quick Start
21
+
22
+ ### Download and Convert
19
23
 
20
24
  ```ts
21
- import { convertEcfrTitle } from "@lexbuild/ecfr";
25
+ import { downloadEcfrTitlesFromApi, convertEcfrTitle } from "@lexbuild/ecfr";
26
+
27
+ // Download Title 17 from the eCFR API (daily-updated)
28
+ const download = await downloadEcfrTitlesFromApi({
29
+ output: "./downloads/ecfr/xml",
30
+ titles: [17],
31
+ });
32
+ console.log(`As of: ${download.asOfDate}`);
22
33
 
34
+ // Convert to section-level Markdown
23
35
  const result = await convertEcfrTitle({
24
36
  input: "./downloads/ecfr/xml/ECFR-title17.xml",
25
37
  output: "./output",
26
- granularity: "section", // or "part", "chapter", or "title"
38
+ granularity: "section",
27
39
  linkStyle: "plaintext",
28
40
  includeSourceCredits: true,
29
41
  includeNotes: true,
@@ -33,29 +45,43 @@ const result = await convertEcfrTitle({
33
45
  dryRun: false,
34
46
  });
35
47
 
36
- console.log(`Wrote ${result.sectionsWritten} sections`);
37
- console.log(`Parts: ${result.partCount}`);
38
- console.log(`Estimated tokens: ${result.totalTokenEstimate}`);
48
+ console.log(`${result.sectionsWritten} sections, ${result.totalTokenEstimate} est. tokens`);
39
49
  ```
40
50
 
41
- ### Download Titles from govinfo
51
+ ### Point-in-Time Downloads
42
52
 
43
53
  ```ts
44
- import { downloadEcfrTitles } from "@lexbuild/ecfr";
54
+ import { downloadEcfrTitlesFromApi } from "@lexbuild/ecfr";
45
55
 
46
- // Download specific titles
47
- const result = await downloadEcfrTitles({
56
+ // Download the CFR as it existed on a specific date
57
+ const result = await downloadEcfrTitlesFromApi({
48
58
  output: "./downloads/ecfr/xml",
49
- titles: [1, 17, 26],
59
+ titles: [17],
60
+ date: "2025-01-01",
50
61
  });
62
+ ```
63
+
64
+ ### Title Metadata
65
+
66
+ ```ts
67
+ import { fetchEcfrTitlesMeta } from "@lexbuild/ecfr";
68
+
69
+ const meta = await fetchEcfrTitlesMeta();
70
+ console.log(`Data current as of: ${meta.date}`);
71
+ for (const title of meta.titles) {
72
+ console.log(`Title ${title.number}: ${title.name} (amended ${title.latestAmendedOn})`);
73
+ }
74
+ ```
75
+
76
+ ### Govinfo Bulk Data (Fallback)
77
+
78
+ ```ts
79
+ import { downloadEcfrTitles } from "@lexbuild/ecfr";
51
80
 
52
- // Download all 50 titles
53
- const all = await downloadEcfrTitles({
81
+ const result = await downloadEcfrTitles({
54
82
  output: "./downloads/ecfr/xml",
83
+ titles: [1, 17],
55
84
  });
56
-
57
- console.log(`Downloaded ${result.titlesDownloaded} files`);
58
- console.log(`Total size: ${result.totalBytes} bytes`);
59
85
  ```
60
86
 
61
87
  ## API Reference
@@ -64,50 +90,57 @@ console.log(`Total size: ${result.totalBytes} bytes`);
64
90
 
65
91
  | Export | Description |
66
92
  |--------|-------------|
67
- | `convertEcfrTitle(options)` | Convert an eCFR XML file to section-level Markdown |
68
- | `downloadEcfrTitles(options)` | Download eCFR XML from govinfo |
69
- | `buildEcfrDownloadUrl(titleNumber)` | Build URL for a single title XML file |
93
+ | `convertEcfrTitle(options)` | Convert an eCFR XML file to Markdown at any granularity |
94
+ | `downloadEcfrTitlesFromApi(options)` | Download from ecfr.gov API (daily-updated, point-in-time) |
95
+ | `downloadEcfrTitles(options)` | Download from govinfo.gov bulk data (fallback) |
96
+ | `fetchEcfrTitlesMeta()` | Fetch title metadata and currency dates from the eCFR API |
97
+ | `buildEcfrApiDownloadUrl(titleNumber, date)` | Build ecfr.gov API download URL |
98
+ | `buildEcfrDownloadUrl(titleNumber)` | Build govinfo bulk download URL |
70
99
 
71
100
  ### Types
72
101
 
73
102
  | Export | Description |
74
103
  |--------|-------------|
75
- | `EcfrConvertOptions` | Options for `convertEcfrTitle()` |
76
- | `EcfrConvertResult` | Result of a conversion (sections, parts, tokens, files) |
77
- | `EcfrDownloadOptions` | Options for `downloadEcfrTitles()` |
78
- | `EcfrDownloadResult` | Result of a download (files, bytes) |
79
- | `EcfrDownloadedFile` | Info about a downloaded file (title, path, size) |
80
- | `EcfrDownloadError` | Info about a failed download |
104
+ | `EcfrConvertOptions` | Options for `convertEcfrTitle()` — input, output, granularity, link style, note filters |
105
+ | `EcfrConvertResult` | Conversion result sections written, parts, files, token estimate |
106
+ | `EcfrApiDownloadOptions` | Options for `downloadEcfrTitlesFromApi()` — output, titles, optional date |
107
+ | `EcfrApiDownloadResult` | API download result files, bytes, as-of date |
108
+ | `EcfrApiDownloadedFile` | Single API-downloaded file metadata |
109
+ | `EcfrDownloadOptions` | Options for `downloadEcfrTitles()` (govinfo) |
110
+ | `EcfrDownloadResult` | Govinfo download result |
111
+ | `EcfrDownloadedFile` | Single govinfo-downloaded file metadata |
112
+ | `EcfrTitleMeta` | Per-title metadata from the eCFR API |
113
+ | `EcfrTitlesResponse` | Full response from the titles metadata endpoint |
81
114
 
82
115
  ### Classes
83
116
 
84
117
  | Export | Description |
85
118
  |--------|-------------|
86
- | `EcfrASTBuilder` | SAXAST builder for eCFR GPO/SGML XML |
119
+ | `EcfrASTBuilder` | SAX-to-AST builder for eCFR GPO/SGML XML. Handles both ecfr.gov API and govinfo bulk XML formats transparently. |
87
120
 
88
121
  ### Constants
89
122
 
90
123
  | Export | Description |
91
124
  |--------|-------------|
92
- | `ECFR_TITLE_COUNT` | Total number of eCFR titles (`50`) |
93
- | `ECFR_TITLE_NUMBERS` | Array of valid title numbers (150) |
94
- | `ECFR_TYPE_TO_LEVEL` | Map from DIV TYPE attributes to LexBuild level types |
95
- | `ECFR_EMPHASIS_MAP` | Map from E element T attribute to inline types |
125
+ | `ECFR_TITLE_COUNT` | Total number of CFR titles (`50`) |
126
+ | `ECFR_TITLE_NUMBERS` | Array of valid title numbers `[1, 2, ..., 50]` |
127
+ | `ECFR_TYPE_TO_LEVEL` | Map from DIV `TYPE` attributes to LexBuild level types |
128
+ | `ECFR_EMPHASIS_MAP` | Map from `E` element `T` attribute to inline formatting types |
96
129
 
97
130
  ## Output
98
131
 
99
- Each title produces Markdown files with YAML frontmatter. The output structure depends on the granularity setting:
132
+ Each title produces Markdown files with YAML frontmatter. The structure depends on granularity:
100
133
 
101
- | Granularity | Output | Metadata |
134
+ | Granularity | Output Path | Sidecar Files |
102
135
  |---|---|---|
103
- | `section` (default) | `ecfr/title-NN/chapter-X/part-N/section-N.N.md` | `_meta.json` per part + title, `README.md` per title |
104
- | `part` | `ecfr/title-NN/chapter-X/part-N.md` | — |
105
- | `chapter` | `ecfr/title-NN/chapter-X/chapter-X.md` | — |
106
- | `title` | `ecfr/title-NN.md` | — |
136
+ | `section` (default) | `ecfr/title-17/chapter-IV/part-240/section-240.10b-5.md` | `_meta.json` per part + title, `README.md` per title |
137
+ | `part` | `ecfr/title-17/chapter-IV/part-240.md` | — |
138
+ | `chapter` | `ecfr/title-17/chapter-IV/chapter-IV.md` | — |
139
+ | `title` | `ecfr/title-17.md` | — |
107
140
 
108
141
  ### Frontmatter
109
142
 
110
- eCFR sections include standard LexBuild frontmatter fields plus source-specific metadata:
143
+ eCFR sections include source-specific metadata alongside standard LexBuild fields:
111
144
 
112
145
  ```yaml
113
146
  ---
@@ -116,21 +149,47 @@ source: "ecfr"
116
149
  legal_status: "authoritative_unofficial"
117
150
  title: "17 CFR § 240.10b-5 - Employment of manipulative and deceptive devices"
118
151
  title_number: 17
119
- title_name: "Commodity and Securities Exchanges"
120
152
  section_number: "240.10b-5"
121
- section_name: "Employment of manipulative and deceptive devices"
122
- part_number: "240"
123
- part_name: "GENERAL RULES AND REGULATIONS, SECURITIES EXCHANGE ACT OF 1934"
124
153
  positive_law: false
125
- currency: "2025-03-13"
126
- last_updated: "2025-03-13"
127
- format_version: "1.1.0"
128
- generator: "lexbuild@1.8.0"
129
154
  authority: "15 U.S.C. 78a et seq., ..."
130
155
  cfr_part: "240"
131
156
  ---
132
157
  ```
133
158
 
159
+ ## Data Sources
160
+
161
+ | Source | URL | Update Frequency | Point-in-Time |
162
+ |--------|-----|-----------------|---------------|
163
+ | **eCFR API** (default) | `ecfr.gov/api/versioner/v1/` | Daily | Yes |
164
+ | **govinfo bulk** (fallback) | `govinfo.gov/bulkdata/ECFR/` | Irregular | No |
165
+
166
+ Both sources produce the same GPO/SGML-derived XML element vocabulary. The builder handles format differences (wrapper elements, attribute variations) transparently — the converter works identically regardless of source.
167
+
168
+ Title 35 (Panama Canal) is reserved and has no data from either source.
169
+
170
+ ## Compatibility
171
+
172
+ - **Node.js** >= 22
173
+ - **ESM only** — no CommonJS build
174
+ - **TypeScript** — ships `.d.ts` type declarations
175
+
176
+ ## Monorepo Context
177
+
178
+ Part of the [LexBuild](https://github.com/chris-c-thomas/LexBuild) monorepo. Depends on `@lexbuild/core` for XML parsing, AST types, and Markdown rendering.
179
+
180
+ ```bash
181
+ pnpm turbo build --filter=@lexbuild/ecfr
182
+ pnpm turbo test --filter=@lexbuild/ecfr
183
+ ```
184
+
185
+ ## Related Packages
186
+
187
+ | Package | Description |
188
+ |---------|-------------|
189
+ | [`@lexbuild/cli`](https://www.npmjs.com/package/@lexbuild/cli) | CLI tool — the easiest way to use LexBuild |
190
+ | [`@lexbuild/core`](https://www.npmjs.com/package/@lexbuild/core) | Shared parsing, AST, and rendering infrastructure |
191
+ | [`@lexbuild/usc`](https://www.npmjs.com/package/@lexbuild/usc) | U.S. Code (USLM XML) converter |
192
+
134
193
  ## License
135
194
 
136
195
  [MIT](https://github.com/chris-c-thomas/LexBuild/blob/main/LICENSE)
package/dist/index.d.ts CHANGED
@@ -107,6 +107,92 @@ declare function buildEcfrDownloadUrl(titleNumber: number): string;
107
107
  */
108
108
  declare function downloadEcfrTitles(options: EcfrDownloadOptions): Promise<EcfrDownloadResult>;
109
109
 
110
+ /**
111
+ * eCFR API downloader.
112
+ *
113
+ * Downloads individual title XML files from the ecfr.gov versioner API.
114
+ * Unlike the govinfo bulk downloader, this source provides daily-updated,
115
+ * point-in-time data and supports fetching the CFR as of any specific date.
116
+ *
117
+ * API base: https://www.ecfr.gov/api/versioner/v1/
118
+ */
119
+ /** Metadata for a single CFR title from the eCFR API */
120
+ interface EcfrTitleMeta {
121
+ /** Title number (1-50) */
122
+ number: number;
123
+ /** Title name */
124
+ name: string;
125
+ /** Date of the most recent amendment */
126
+ latestAmendedOn: string;
127
+ /** Date of the most recent Federal Register issue incorporated */
128
+ latestIssueDate: string;
129
+ /** Currency date — how current the data is */
130
+ upToDateAsOf: string;
131
+ /** Whether this title is reserved (e.g., Title 35) */
132
+ reserved: boolean;
133
+ }
134
+ /** Response from the /titles endpoint */
135
+ interface EcfrTitlesResponse {
136
+ /** Currency date for the dataset */
137
+ date: string;
138
+ /** Whether a data import is currently in progress */
139
+ importInProgress: boolean;
140
+ /** Per-title metadata */
141
+ titles: EcfrTitleMeta[];
142
+ }
143
+ /**
144
+ * Fetch metadata for all CFR titles from the eCFR API.
145
+ *
146
+ * Returns currency dates and amendment info for each title.
147
+ * Useful for staleness detection without downloading XML.
148
+ */
149
+ declare function fetchEcfrTitlesMeta(): Promise<EcfrTitlesResponse>;
150
+ /** Options for downloading eCFR titles from the API */
151
+ interface EcfrApiDownloadOptions {
152
+ /** Download directory */
153
+ output: string;
154
+ /** Specific titles to download (1-50), or undefined for all */
155
+ titles?: number[] | undefined;
156
+ /** Point-in-time date (YYYY-MM-DD). Defaults to the current currency date. */
157
+ date?: string | undefined;
158
+ }
159
+ /** Result of a download from the eCFR API */
160
+ interface EcfrApiDownloadResult {
161
+ /** Number of titles successfully downloaded */
162
+ titlesDownloaded: number;
163
+ /** Paths of downloaded files */
164
+ files: EcfrApiDownloadedFile[];
165
+ /** Total bytes downloaded */
166
+ totalBytes: number;
167
+ /** The date used for point-in-time downloads */
168
+ asOfDate: string;
169
+ }
170
+ /** Metadata for a single downloaded file from the eCFR API */
171
+ interface EcfrApiDownloadedFile {
172
+ /** Absolute path to the downloaded file */
173
+ path: string;
174
+ /** Title number */
175
+ titleNumber: number;
176
+ /** File size in bytes */
177
+ size: number;
178
+ /** The point-in-time date used */
179
+ asOfDate: string;
180
+ }
181
+ /**
182
+ * Build the download URL for a full title XML from the eCFR API.
183
+ *
184
+ * @param titleNumber - CFR title number (1-50)
185
+ * @param date - Point-in-time date in YYYY-MM-DD format
186
+ */
187
+ declare function buildEcfrApiDownloadUrl(titleNumber: number, date: string): string;
188
+ /**
189
+ * Download eCFR XML files from the ecfr.gov versioner API.
190
+ *
191
+ * When no date is specified, fetches the current currency date from the
192
+ * /titles endpoint and uses that for all downloads.
193
+ */
194
+ declare function downloadEcfrTitlesFromApi(options: EcfrApiDownloadOptions): Promise<EcfrApiDownloadResult>;
195
+
110
196
  /**
111
197
  * eCFR AST Builder — converts SAX events from GPO/SGML-derived XML into AST nodes.
112
198
  *
@@ -205,4 +291,4 @@ declare const ECFR_REF_ELEMENTS: Set<string>;
205
291
  /** Table elements (HTML-style) */
206
292
  declare const ECFR_TABLE_ELEMENTS: Set<string>;
207
293
 
208
- export { ECFR_BLOCK_ELEMENTS, ECFR_CONTENT_ELEMENTS, ECFR_DIV_ELEMENTS, ECFR_EMPHASIS_MAP, ECFR_HEADING_ELEMENTS, ECFR_IGNORE_ELEMENTS, ECFR_INLINE_ELEMENTS, ECFR_NOTE_ELEMENTS, ECFR_PASSTHROUGH_ELEMENTS, ECFR_REF_ELEMENTS, ECFR_SKIP_ELEMENTS, ECFR_TABLE_ELEMENTS, ECFR_TITLE_COUNT, ECFR_TITLE_NUMBERS, ECFR_TYPE_TO_LEVEL, EcfrASTBuilder, type EcfrASTBuilderOptions, type EcfrConvertOptions, type EcfrConvertResult, type EcfrDownloadError, type EcfrDownloadOptions, type EcfrDownloadResult, type EcfrDownloadedFile, buildEcfrDownloadUrl, convertEcfrTitle, downloadEcfrTitles };
294
+ export { ECFR_BLOCK_ELEMENTS, ECFR_CONTENT_ELEMENTS, ECFR_DIV_ELEMENTS, ECFR_EMPHASIS_MAP, ECFR_HEADING_ELEMENTS, ECFR_IGNORE_ELEMENTS, ECFR_INLINE_ELEMENTS, ECFR_NOTE_ELEMENTS, ECFR_PASSTHROUGH_ELEMENTS, ECFR_REF_ELEMENTS, ECFR_SKIP_ELEMENTS, ECFR_TABLE_ELEMENTS, ECFR_TITLE_COUNT, ECFR_TITLE_NUMBERS, ECFR_TYPE_TO_LEVEL, EcfrASTBuilder, type EcfrASTBuilderOptions, type EcfrApiDownloadOptions, type EcfrApiDownloadResult, type EcfrApiDownloadedFile, type EcfrConvertOptions, type EcfrConvertResult, type EcfrDownloadError, type EcfrDownloadOptions, type EcfrDownloadResult, type EcfrDownloadedFile, type EcfrTitleMeta, type EcfrTitlesResponse, buildEcfrApiDownloadUrl, buildEcfrDownloadUrl, convertEcfrTitle, downloadEcfrTitles, downloadEcfrTitlesFromApi, fetchEcfrTitlesMeta };
package/dist/index.js CHANGED
@@ -121,7 +121,14 @@ var ECFR_IGNORE_ELEMENTS = /* @__PURE__ */ new Set([
121
121
  "HEADER"
122
122
  // File metadata header (skip subtree)
123
123
  ]);
124
- var ECFR_PASSTHROUGH_ELEMENTS = /* @__PURE__ */ new Set(["DLPSTEXTCLASS", "TEXT", "BODY", "ECFRBRWS"]);
124
+ var ECFR_PASSTHROUGH_ELEMENTS = /* @__PURE__ */ new Set([
125
+ "DLPSTEXTCLASS",
126
+ "TEXT",
127
+ "BODY",
128
+ "ECFRBRWS",
129
+ "ECFR"
130
+ // Root element in eCFR API XML (replaces DLPSTEXTCLASS)
131
+ ]);
125
132
  var ECFR_SKIP_ELEMENTS = /* @__PURE__ */ new Set([
126
133
  "PTHD",
127
134
  // Part heading in TOC
@@ -137,8 +144,10 @@ var ECFR_SKIP_ELEMENTS = /* @__PURE__ */ new Set([
137
144
  // Page number
138
145
  "STARS",
139
146
  // Visual separator
140
- "AMDDATE"
147
+ "AMDDATE",
141
148
  // Amendment date
149
+ "VOLUME"
150
+ // Volume metadata in eCFR API XML
142
151
  ]);
143
152
  var ECFR_REF_ELEMENTS = /* @__PURE__ */ new Set([
144
153
  "XREF",
@@ -1321,6 +1330,69 @@ async function downloadEcfrTitles(options) {
1321
1330
  }
1322
1331
  return { titlesDownloaded: files.length, files, totalBytes };
1323
1332
  }
1333
+
1334
+ // src/ecfr-api-downloader.ts
1335
+ import { createWriteStream as createWriteStream2 } from "fs";
1336
+ import { mkdir as mkdir3, stat as stat2 } from "fs/promises";
1337
+ import { join as join4 } from "path";
1338
+ import { pipeline as pipeline2 } from "stream/promises";
1339
+ import { Readable as Readable2 } from "stream";
1340
+ var ECFR_API_BASE = "https://www.ecfr.gov/api/versioner/v1";
1341
+ var RESERVED_TITLES2 = /* @__PURE__ */ new Set([35]);
1342
+ async function fetchEcfrTitlesMeta() {
1343
+ const response = await fetch(`${ECFR_API_BASE}/titles`);
1344
+ if (!response.ok) {
1345
+ throw new Error(`Failed to fetch eCFR titles metadata: HTTP ${response.status}`);
1346
+ }
1347
+ const data = await response.json();
1348
+ const titles = data.titles.map((t) => ({
1349
+ number: t.number,
1350
+ name: t.name,
1351
+ latestAmendedOn: t.latest_amended_on,
1352
+ latestIssueDate: t.latest_issue_date,
1353
+ upToDateAsOf: t.up_to_date_as_of,
1354
+ reserved: t.reserved
1355
+ }));
1356
+ return {
1357
+ date: data.meta.date,
1358
+ importInProgress: data.meta.import_in_progress,
1359
+ titles
1360
+ };
1361
+ }
1362
+ function buildEcfrApiDownloadUrl(titleNumber, date) {
1363
+ return `${ECFR_API_BASE}/full/${date}/title-${titleNumber}.xml`;
1364
+ }
1365
+ async function downloadEcfrTitlesFromApi(options) {
1366
+ const { output } = options;
1367
+ const titles = options.titles ?? ECFR_TITLE_NUMBERS;
1368
+ let asOfDate = options.date;
1369
+ if (!asOfDate) {
1370
+ const meta = await fetchEcfrTitlesMeta();
1371
+ asOfDate = meta.date;
1372
+ }
1373
+ await mkdir3(output, { recursive: true });
1374
+ const files = [];
1375
+ let totalBytes = 0;
1376
+ for (const titleNum of titles) {
1377
+ if (RESERVED_TITLES2.has(titleNum)) continue;
1378
+ const url = buildEcfrApiDownloadUrl(titleNum, asOfDate);
1379
+ const filePath = join4(output, `ECFR-title${titleNum}.xml`);
1380
+ const response = await fetch(url);
1381
+ if (!response.ok) {
1382
+ console.warn(`Failed to download eCFR Title ${titleNum} from API: ${response.status}`);
1383
+ continue;
1384
+ }
1385
+ const body = response.body;
1386
+ if (!body) continue;
1387
+ const dest = createWriteStream2(filePath);
1388
+ await pipeline2(Readable2.fromWeb(body), dest);
1389
+ const fileStat = await stat2(filePath);
1390
+ const size = fileStat.size;
1391
+ totalBytes += size;
1392
+ files.push({ path: filePath, titleNumber: titleNum, size, asOfDate });
1393
+ }
1394
+ return { titlesDownloaded: files.length, files, totalBytes, asOfDate };
1395
+ }
1324
1396
  export {
1325
1397
  ECFR_BLOCK_ELEMENTS,
1326
1398
  ECFR_CONTENT_ELEMENTS,
@@ -1338,8 +1410,11 @@ export {
1338
1410
  ECFR_TITLE_NUMBERS,
1339
1411
  ECFR_TYPE_TO_LEVEL,
1340
1412
  EcfrASTBuilder,
1413
+ buildEcfrApiDownloadUrl,
1341
1414
  buildEcfrDownloadUrl,
1342
1415
  convertEcfrTitle,
1343
- downloadEcfrTitles
1416
+ downloadEcfrTitles,
1417
+ downloadEcfrTitlesFromApi,
1418
+ fetchEcfrTitlesMeta
1344
1419
  };
1345
1420
  //# sourceMappingURL=index.js.map
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"],"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([\"DLPSTEXTCLASS\", \"TEXT\", \"BODY\", \"ECFRBRWS\"]);\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]);\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 part (used for _meta.json placement).\n */\nexport function buildPartDir(context: EmitContext, partNum: string, outputRoot: string): string {\n const titleNum = findAncestorValue(context, \"title\") ?? \"0\";\n const chapterNum = findAncestorValue(context, \"chapter\");\n const titleDir = `title-${padTwo(titleNum)}`;\n const segments = [outputRoot, \"ecfr\", titleDir];\n if (chapterNum) {\n segments.push(`chapter-${chapterNum}`);\n }\n segments.push(`part-${partNum}`);\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/** 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}\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}\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 } = options;\n const titles = options.titles ?? ECFR_TITLE_NUMBERS;\n\n await mkdir(output, { recursive: true });\n const files: EcfrDownloadedFile[] = [];\n let totalBytes = 0;\n\n for (const titleNum of titles) {\n // Skip reserved titles (e.g., Title 35 — Panama Canal) that have no bulk XML\n if (RESERVED_TITLES.has(titleNum)) continue;\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 console.warn(`Failed to download eCFR Title ${titleNum}: ${response.status}`);\n continue;\n }\n\n const body = response.body;\n if (!body) continue;\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 };\n}\n"],"mappings":"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","names":["join","files","join","mkdir","join"]}
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 part (used for _meta.json placement).\n */\nexport function buildPartDir(context: EmitContext, partNum: string, outputRoot: string): string {\n const titleNum = findAncestorValue(context, \"title\") ?? \"0\";\n const chapterNum = findAncestorValue(context, \"chapter\");\n const titleDir = `title-${padTwo(titleNum)}`;\n const segments = [outputRoot, \"ecfr\", titleDir];\n if (chapterNum) {\n segments.push(`chapter-${chapterNum}`);\n }\n segments.push(`part-${partNum}`);\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/** 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}\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}\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 } = options;\n const titles = options.titles ?? ECFR_TITLE_NUMBERS;\n\n await mkdir(output, { recursive: true });\n const files: EcfrDownloadedFile[] = [];\n let totalBytes = 0;\n\n for (const titleNum of titles) {\n // Skip reserved titles (e.g., Title 35 — Panama Canal) that have no bulk XML\n if (RESERVED_TITLES.has(titleNum)) continue;\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 console.warn(`Failed to download eCFR Title ${titleNum}: ${response.status}`);\n continue;\n }\n\n const body = response.body;\n if (!body) continue;\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 };\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 * 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 } 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// ---------------------------------------------------------------------------\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}\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 }));\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 the current currency date. */\n date?: string | 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 date used for point-in-time downloads */\n asOfDate: string;\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 */\n asOfDate: 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 * When no date is specified, fetches the current currency date from the\n * /titles endpoint and uses that for all downloads.\n */\nexport async function downloadEcfrTitlesFromApi(\n options: EcfrApiDownloadOptions,\n): Promise<EcfrApiDownloadResult> {\n const { output } = options;\n const titles = options.titles ?? ECFR_TITLE_NUMBERS;\n\n // Resolve the date to use\n let asOfDate = options.date;\n if (!asOfDate) {\n const meta = await fetchEcfrTitlesMeta();\n asOfDate = meta.date;\n }\n\n await mkdir(output, { recursive: true });\n const files: EcfrApiDownloadedFile[] = [];\n let totalBytes = 0;\n\n for (const titleNum of titles) {\n // Skip reserved titles (e.g., Title 35 — Panama Canal)\n if (RESERVED_TITLES.has(titleNum)) continue;\n\n const url = buildEcfrApiDownloadUrl(titleNum, asOfDate);\n // Use the same filename as govinfo downloads for converter compatibility\n const filePath = join(output, `ECFR-title${titleNum}.xml`);\n\n const response = await fetch(url);\n if (!response.ok) {\n console.warn(`Failed to download eCFR Title ${titleNum} from API: ${response.status}`);\n continue;\n }\n\n const body = response.body;\n if (!body) continue;\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 const size = fileStat.size;\n totalBytes += size;\n\n files.push({ path: filePath, titleNumber: titleNum, size, asOfDate });\n }\n\n return { titlesDownloaded: files.length, files, totalBytes, asOfDate };\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.9.4",
3
+ "version": "1.10.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.9.4"
45
+ "@lexbuild/core": "1.10.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/node": "^25.3.2",