@statewalker/content-extractors 0.2.2 → 0.3.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.
Files changed (73) hide show
  1. package/README.md +105 -10
  2. package/dist/collect-bytes.d.ts +10 -2
  3. package/dist/collect-bytes.d.ts.map +1 -1
  4. package/dist/collect-bytes.js +6 -4
  5. package/dist/collect-bytes.js.map +1 -1
  6. package/dist/extractor-registry.d.ts +2 -1
  7. package/dist/extractor-registry.d.ts.map +1 -1
  8. package/dist/extractor-registry.js +3 -2
  9. package/dist/extractor-registry.js.map +1 -1
  10. package/dist/extractors/docx-extractor.d.ts +5 -1
  11. package/dist/extractors/docx-extractor.d.ts.map +1 -1
  12. package/dist/extractors/docx-extractor.js +20 -11
  13. package/dist/extractors/docx-extractor.js.map +1 -1
  14. package/dist/extractors/html-extractor.d.ts +3 -0
  15. package/dist/extractors/html-extractor.d.ts.map +1 -1
  16. package/dist/extractors/html-extractor.js +8 -5
  17. package/dist/extractors/html-extractor.js.map +1 -1
  18. package/dist/extractors/index.d.ts +12 -9
  19. package/dist/extractors/index.d.ts.map +1 -1
  20. package/dist/extractors/index.js +23 -18
  21. package/dist/extractors/index.js.map +1 -1
  22. package/dist/extractors/markdown-extractor.d.ts +3 -0
  23. package/dist/extractors/markdown-extractor.d.ts.map +1 -1
  24. package/dist/extractors/markdown-extractor.js +8 -4
  25. package/dist/extractors/markdown-extractor.js.map +1 -1
  26. package/dist/extractors/pdf-extractor.d.ts +3 -0
  27. package/dist/extractors/pdf-extractor.d.ts.map +1 -1
  28. package/dist/extractors/pdf-extractor.js +17 -11
  29. package/dist/extractors/pdf-extractor.js.map +1 -1
  30. package/dist/extractors/text-extractor.d.ts +3 -0
  31. package/dist/extractors/text-extractor.d.ts.map +1 -1
  32. package/dist/extractors/text-extractor.js +8 -4
  33. package/dist/extractors/text-extractor.js.map +1 -1
  34. package/dist/extractors/xlsx-extractor.d.ts +5 -1
  35. package/dist/extractors/xlsx-extractor.d.ts.map +1 -1
  36. package/dist/extractors/xlsx-extractor.js +56 -36
  37. package/dist/extractors/xlsx-extractor.js.map +1 -1
  38. package/dist/html-to-markdown.d.ts +3 -1
  39. package/dist/html-to-markdown.d.ts.map +1 -1
  40. package/dist/html-to-markdown.js +9 -1
  41. package/dist/html-to-markdown.js.map +1 -1
  42. package/dist/index.d.ts +11 -9
  43. package/dist/index.js +9 -7
  44. package/dist/limits.d.ts +20 -0
  45. package/dist/limits.d.ts.map +1 -0
  46. package/dist/limits.js +9 -0
  47. package/dist/limits.js.map +1 -0
  48. package/dist/markdown-to-html.d.ts +11 -3
  49. package/dist/markdown-to-html.d.ts.map +1 -1
  50. package/dist/markdown-to-html.js +13 -6
  51. package/dist/markdown-to-html.js.map +1 -1
  52. package/dist/mime-utils.js +3 -2
  53. package/dist/mime-utils.js.map +1 -1
  54. package/dist/zip-size.d.ts +16 -0
  55. package/dist/zip-size.d.ts.map +1 -0
  56. package/dist/zip-size.js +90 -0
  57. package/dist/zip-size.js.map +1 -0
  58. package/package.json +8 -4
  59. package/src/collect-bytes.ts +17 -2
  60. package/src/extractor-registry.ts +3 -2
  61. package/src/extractors/docx-extractor.ts +26 -11
  62. package/src/extractors/html-extractor.ts +8 -4
  63. package/src/extractors/index.ts +29 -23
  64. package/src/extractors/markdown-extractor.ts +8 -3
  65. package/src/extractors/pdf-extractor.ts +20 -11
  66. package/src/extractors/text-extractor.ts +8 -3
  67. package/src/extractors/xlsx-extractor.ts +72 -41
  68. package/src/html-to-markdown.ts +4 -1
  69. package/src/index.ts +2 -0
  70. package/src/limits.ts +20 -0
  71. package/src/markdown-to-html.ts +16 -10
  72. package/src/mime-utils.ts +3 -2
  73. package/src/zip-size.ts +113 -0
package/README.md CHANGED
@@ -1,26 +1,121 @@
1
1
  # @statewalker/content-extractors
2
2
 
3
- Content extractors: turn PDF, DOCX, XLSX, Markdown, and HTML into markdown **text** via a mime-aware registry.
3
+ ## What it is
4
4
 
5
- ## Installation
5
+ Turns files into Markdown text for indexing. Each extractor takes the file bytes as an iterable of `Uint8Array` chunks and returns text: PDF (page by page), DOCX (via HTML), XLSX (one Markdown table per sheet), HTML, Markdown and plain text. An `ExtractorRegistry` picks the extractor from the file name, with an optional MIME-type fallback.
6
+
7
+ ## Why it exists
8
+
9
+ An indexer needs text, while documents arrive as PDF, Office files or HTML. Each format needs a different parser; this package wraps them behind one function type, `ContentExtractor`, so an ingestion pipeline can route any file by name without knowing the parsers. Producing Markdown keeps headings and tables, which `@statewalker/indexer-chunker` then uses as cut points.
10
+
11
+ ## How to use
6
12
 
7
13
  ```sh
8
14
  pnpm add @statewalker/content-extractors
9
15
  ```
10
16
 
11
- ## Usage
17
+ | Import | Gives |
18
+ | --- | --- |
19
+ | `@statewalker/content-extractors` | Everything below. |
20
+ | `@statewalker/content-extractors/extractors` | The extractors and `createDefaultRegistry` only. |
21
+ | `@statewalker/content-extractors/types` | `ContentExtractor`, `ContentNormalizer` (types only). |
22
+
23
+ - `createDefaultRegistry(limits?)` — registry for `*.md`, `*.markdown`, `*.txt`, `*.html`, `*.htm`, `*.pdf`, `*.docx`, `*.docm`, `*.xlsx`; `limits` (`{ maxBytes?, maxUncompressedBytes? }`) applies to every extractor in it.
24
+ - `ExtractorRegistry` — `registerByPattern(pattern, extractor)`, `registerByMime(mime, extractor)`, `getByPath(path)`, `getByMime(mime)`, `get(path, mime?)`.
25
+ - Extractors with the default limits: `pdfExtractor`, `docxExtractor`, `xlsxExtractor`, `htmlExtractor`, `markdownExtractor`, `textExtractor`.
26
+ - Extractor factories with your own limits: `createPdfExtractor({ maxBytes? })`, `createDocxExtractor({ maxBytes?, maxUncompressedBytes? })`, `createXlsxExtractor({ maxBytes?, maxUncompressedBytes? })`, `createHtmlExtractor({ maxBytes? })`, `createMarkdownExtractor({ maxBytes? })`, `createTextExtractor({ maxBytes? })`.
27
+ - Limits: `DEFAULT_MAX_BYTES` (100 MiB), `DEFAULT_MAX_UNCOMPRESSED_BYTES` (500 MiB), types `ExtractorLimits`, `ZipExtractorLimits`.
28
+ - Helpers: `htmlToMarkdown(html)`, `markdownToHtml(markdown, { html? })`, `getMimeType(path)`, `collectBytes(chunks, { maxBytes? })`, `collectText(chunks, { maxBytes? })`, `zipUncompressedSize(bytes)`, `assertZipUncompressedSize(bytes, maxBytes)`.
29
+
30
+ ## Examples
31
+
32
+ Extract a file by name:
12
33
 
13
34
  ```ts
35
+ import { readFile } from "node:fs/promises";
14
36
  import { createDefaultRegistry } from "@statewalker/content-extractors";
15
37
 
16
38
  const registry = createDefaultRegistry();
17
- const extractor = registry.get("report.pdf");
18
- const markdown = await extractor?.(pdfBytes); // string
39
+ const extract = registry.get("report.pdf");
40
+ if (!extract) throw new Error("unsupported file type");
41
+ const markdown = (await extract([await readFile("report.pdf")])) as string;
42
+ ```
43
+
44
+ Streamed input works the same way (`AsyncIterable<Uint8Array>`, for example a `fetch` body):
45
+
46
+ ```ts
47
+ const response = await fetch(url);
48
+ const extract = registry.get(url, response.headers.get("content-type") ?? undefined);
49
+ const text = await extract?.(response.body as AsyncIterable<Uint8Array>);
19
50
  ```
20
51
 
21
- ## API
52
+ Route files without an extension by MIME type:
53
+
54
+ ```ts
55
+ import { createDefaultRegistry, htmlExtractor, pdfExtractor } from "@statewalker/content-extractors";
56
+
57
+ const registry = createDefaultRegistry();
58
+ registry.registerByMime("application/pdf", pdfExtractor);
59
+ registry.registerByMime("text/html", htmlExtractor);
60
+ registry.get("upload-1234", "application/pdf"); // pdfExtractor
61
+ ```
62
+
63
+ Change the size limits (see [Input size limits](#input-size-limits)):
64
+
65
+ ```ts
66
+ import { createDefaultRegistry, createXlsxExtractor } from "@statewalker/content-extractors";
67
+
68
+ const registry = createDefaultRegistry({ maxBytes: 20 * 1024 * 1024 }); // 20 MiB per file
69
+ registry.registerByPattern("*.xlsx", createXlsxExtractor({ maxUncompressedBytes: 2 * 1024 ** 3 }));
70
+ ```
71
+
72
+ Helpers:
73
+
74
+ ```ts
75
+ import { getMimeType, htmlToMarkdown, markdownToHtml } from "@statewalker/content-extractors";
76
+
77
+ getMimeType("a.PDF"); // "application/pdf"
78
+ htmlToMarkdown("<h1>T</h1>"); // "# T"
79
+ htmlToMarkdown("<p>hi</p><script>alert(1)</script>"); // "hi": script, style, noscript, template are dropped
80
+ markdownToHtml("**b**"); // "<p><strong>b</strong></p>\n"
81
+ markdownToHtml("<b>x</b>"); // "<p>&lt;b&gt;x&lt;/b&gt;</p>\n": raw HTML is escaped unless { html: true }
82
+ ```
83
+
84
+ ## Internals
85
+
86
+ ### What each format produces
87
+
88
+ | Format | Parser | Output |
89
+ | --- | --- | --- |
90
+ | PDF | `unpdf` | `## Page N` section per non-empty page |
91
+ | DOCX, DOCM | `mammoth` to HTML, then `htmlToMarkdown` | Markdown with headings, lists, tables |
92
+ | XLSX | `exceljs` | `## <sheet name>` and a Markdown table per sheet; first row is the header; pipes in cells escaped, line breaks as spaces |
93
+ | HTML | `turndown` + GFM plugin | ATX headings, fenced code, GFM tables; `<script>`, `<style>`, `<noscript>`, `<template>` dropped |
94
+ | Markdown, text | none | UTF-8 decoded text as-is |
95
+
96
+ ### Input size limits
97
+
98
+ Every built-in extractor reads at most `maxBytes` of input (default `DEFAULT_MAX_BYTES`, 100 MiB) and throws `Input exceeds the limit of N bytes` beyond it; reading stops as soon as the limit is passed, so an endless stream is not buffered. The DOCX, DOCM and XLSX extractors also read the ZIP central directory before handing the archive to mammoth or exceljs, add up the declared uncompressed sizes, and throw `Archive uncompressed size … exceeds the limit …` when the sum is over `maxUncompressedBytes` (default `DEFAULT_MAX_UNCOMPRESSED_BYTES`, 500 MiB). This refuses a small "zip bomb" before it is inflated. Bytes that are not a readable ZIP throw `Invalid ZIP archive: …`.
99
+
100
+ Pass limits to `createDefaultRegistry(limits)` or to a `create*Extractor(limits)` factory; `Infinity` turns a limit off. `collectBytes` and `collectText` have no limit unless `maxBytes` is given.
101
+
102
+ The uncompressed check trusts the sizes the archive declares. An archive that lies about them is still caught by JSZip (inside mammoth and exceljs), which compares the inflated size with the declared one, but only after inflating that entry.
103
+
104
+ ### Things that surprise
105
+
106
+ - **The default registry has no MIME routes.** `createDefaultRegistry().get("upload-1234", "application/pdf")` returns `undefined`; register MIME types yourself as shown above.
107
+ - **Path matching is by suffix, longest first.** `*.test.html` beats `*.html`. Matching is case-insensitive on both the file name and the pattern (`*.PDF` matches `report.pdf`), and only the part after the last `/` is used. `getMimeType` also looks only after the last `/`: `notes.md/LICENSE` has no extension.
108
+ - **XLSX cells are flattened to one line.** A `|` becomes `\|` and each line break (CR, LF, CRLF) becomes one space, so a cell never adds a column or splits a row.
109
+ - **XLSX formula and rich-text cells come out as `[object Object]`**, and dates as `Date.toString()` in the local time zone. Plain numbers and strings are fine.
110
+ - **`ContentExtractor` returns `Promise<string | unknown>`.** All built-in extractors return a string; cast or check before use.
111
+ - **`markdownToHtml` escapes raw HTML by default.** Extracted files often come from untrusted uploads, so `<script>` or `<img onerror>` in them is shown as text. `{ html: true }` passes raw HTML through; use it only for trusted input, or sanitise the output before rendering it.
112
+ - **Everything is read into memory.** PDF, DOCX and XLSX parsers need one contiguous buffer, so `collectBytes` joins the chunks first, up to the `maxBytes` limit.
113
+ - `ContentNormalizer` is only an interface: a slot for a post-processing step (for example an LLM clean-up) that this package does not implement.
114
+
115
+ ### Dependencies
116
+
117
+ `unpdf` (PDF text), `mammoth` (DOCX to HTML), `exceljs` (XLSX), `turndown`, `@joplin/turndown-plugin-gfm` and `@types/turndown` (HTML to Markdown), `markdown-it` (Markdown to HTML).
118
+
119
+ ## License
22
120
 
23
- - `ContentExtractor` — the core contract: `bytes → Promise<string>`.
24
- - `ExtractorRegistry` / `createDefaultRegistry` — resolve an extractor by path or MIME type.
25
- - Format handlers under `./extractors` (PDF, DOCX, XLSX, Markdown, HTML).
26
- - `html-to-markdown`, `markdown-to-html`, `mime-utils`, `collect-bytes` helpers.
121
+ MIT
@@ -1,16 +1,24 @@
1
1
  //#region src/collect-bytes.d.ts
2
+ /** Options for {@link collectBytes} and {@link collectText}. */
3
+ export interface CollectOptions {
4
+ /**
5
+ * Largest input accepted, in bytes. Reading stops and an error is thrown as soon as the total
6
+ * exceeds it, so an oversized or endless stream is never held in memory. No limit when omitted.
7
+ */
8
+ maxBytes?: number;
9
+ }
2
10
  /**
3
11
  * Bridges the streaming world (chunked iterables from fetch, file reads,
4
12
  * etc.) with libraries that require a single contiguous buffer (pdfjs-dist,
5
13
  * mammoth). Returns the original chunk when only one arrives, avoiding
6
14
  * an extra copy in the common single-read case.
7
15
  */
8
- export declare function collectBytes(content: AsyncIterable<Uint8Array> | Iterable<Uint8Array>): Promise<Uint8Array>;
16
+ export declare function collectBytes(content: AsyncIterable<Uint8Array> | Iterable<Uint8Array>, options?: CollectOptions): Promise<Uint8Array>;
9
17
  /**
10
18
  * Shorthand for text-based extractors (plain text, markdown, HTML) that
11
19
  * need the full content as a string. Avoids duplicating the
12
20
  * collect-then-decode pattern across every text extractor.
13
21
  */
14
- export declare function collectText(content: AsyncIterable<Uint8Array> | Iterable<Uint8Array>): Promise<string>;
22
+ export declare function collectText(content: AsyncIterable<Uint8Array> | Iterable<Uint8Array>, options?: CollectOptions): Promise<string>;
15
23
  //#endregion
16
24
  //# sourceMappingURL=collect-bytes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"collect-bytes.d.ts","names":[],"sources":["../src/collect-bytes.ts"],"mappings":";;;;;;;wBAMsB,aACpB,SAAS,cAAc,cAAc,SAAS,cAC7C,QAAQ;;;;;;wBAyBW,YACpB,SAAS,cAAc,cAAc,SAAS,cAC7C"}
1
+ {"version":3,"file":"collect-bytes.d.ts","names":[],"sources":["../src/collect-bytes.ts"],"mappings":";;iBACiB;;;;;EAKf;;;;;;;;wBASoB,aACpB,SAAS,cAAc,cAAc,SAAS,aAC9C,UAAS,iBACR,QAAQ;;;;;;wBA6BW,YACpB,SAAS,cAAc,cAAc,SAAS,aAC9C,UAAS,iBACR"}
@@ -5,12 +5,14 @@
5
5
  * mammoth). Returns the original chunk when only one arrives, avoiding
6
6
  * an extra copy in the common single-read case.
7
7
  */
8
- async function collectBytes(content) {
8
+ async function collectBytes(content, options = {}) {
9
+ const { maxBytes = Number.POSITIVE_INFINITY } = options;
9
10
  const chunks = [];
10
11
  let totalLength = 0;
11
12
  for await (const chunk of content) {
12
- chunks.push(chunk);
13
13
  totalLength += chunk.length;
14
+ if (totalLength > maxBytes) throw new Error(`Input exceeds the limit of ${maxBytes} bytes`);
15
+ chunks.push(chunk);
14
16
  }
15
17
  const first = chunks[0];
16
18
  if (chunks.length === 1 && first) return first;
@@ -27,8 +29,8 @@ async function collectBytes(content) {
27
29
  * need the full content as a string. Avoids duplicating the
28
30
  * collect-then-decode pattern across every text extractor.
29
31
  */
30
- async function collectText(content) {
31
- const bytes = await collectBytes(content);
32
+ async function collectText(content, options = {}) {
33
+ const bytes = await collectBytes(content, options);
32
34
  return new TextDecoder().decode(bytes);
33
35
  }
34
36
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"collect-bytes.js","names":[],"sources":["../src/collect-bytes.ts"],"sourcesContent":["/**\n * Bridges the streaming world (chunked iterables from fetch, file reads,\n * etc.) with libraries that require a single contiguous buffer (pdfjs-dist,\n * mammoth). Returns the original chunk when only one arrives, avoiding\n * an extra copy in the common single-read case.\n */\nexport async function collectBytes(\n content: AsyncIterable<Uint8Array> | Iterable<Uint8Array>,\n): Promise<Uint8Array> {\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n for await (const chunk of content) {\n chunks.push(chunk);\n totalLength += chunk.length;\n }\n const first = chunks[0];\n if (chunks.length === 1 && first) {\n return first;\n }\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n return result;\n}\n\n/**\n * Shorthand for text-based extractors (plain text, markdown, HTML) that\n * need the full content as a string. Avoids duplicating the\n * collect-then-decode pattern across every text extractor.\n */\nexport async function collectText(\n content: AsyncIterable<Uint8Array> | Iterable<Uint8Array>,\n): Promise<string> {\n const bytes = await collectBytes(content);\n return new TextDecoder().decode(bytes);\n}\n"],"mappings":";;;;;;;AAMA,eAAsB,aACpB,SACqB;CACrB,MAAM,SAAuB,CAAC;CAC9B,IAAI,cAAc;CAClB,WAAW,MAAM,SAAS,SAAS;EACjC,OAAO,KAAK,KAAK;EACjB,eAAe,MAAM;CACvB;CACA,MAAM,QAAQ,OAAO;CACrB,IAAI,OAAO,WAAW,KAAK,OACzB,OAAO;CAET,MAAM,SAAS,IAAI,WAAW,WAAW;CACzC,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,QAAQ;EAC1B,OAAO,IAAI,OAAO,MAAM;EACxB,UAAU,MAAM;CAClB;CACA,OAAO;AACT;;;;;;AAOA,eAAsB,YACpB,SACiB;CACjB,MAAM,QAAQ,MAAM,aAAa,OAAO;CACxC,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK;AACvC"}
1
+ {"version":3,"file":"collect-bytes.js","names":[],"sources":["../src/collect-bytes.ts"],"sourcesContent":["/** Options for {@link collectBytes} and {@link collectText}. */\nexport interface CollectOptions {\n /**\n * Largest input accepted, in bytes. Reading stops and an error is thrown as soon as the total\n * exceeds it, so an oversized or endless stream is never held in memory. No limit when omitted.\n */\n maxBytes?: number;\n}\n\n/**\n * Bridges the streaming world (chunked iterables from fetch, file reads,\n * etc.) with libraries that require a single contiguous buffer (pdfjs-dist,\n * mammoth). Returns the original chunk when only one arrives, avoiding\n * an extra copy in the common single-read case.\n */\nexport async function collectBytes(\n content: AsyncIterable<Uint8Array> | Iterable<Uint8Array>,\n options: CollectOptions = {},\n): Promise<Uint8Array> {\n const { maxBytes = Number.POSITIVE_INFINITY } = options;\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n for await (const chunk of content) {\n totalLength += chunk.length;\n if (totalLength > maxBytes) {\n throw new Error(`Input exceeds the limit of ${maxBytes} bytes`);\n }\n chunks.push(chunk);\n }\n const first = chunks[0];\n if (chunks.length === 1 && first) {\n return first;\n }\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n return result;\n}\n\n/**\n * Shorthand for text-based extractors (plain text, markdown, HTML) that\n * need the full content as a string. Avoids duplicating the\n * collect-then-decode pattern across every text extractor.\n */\nexport async function collectText(\n content: AsyncIterable<Uint8Array> | Iterable<Uint8Array>,\n options: CollectOptions = {},\n): Promise<string> {\n const bytes = await collectBytes(content, options);\n return new TextDecoder().decode(bytes);\n}\n"],"mappings":";;;;;;;AAeA,eAAsB,aACpB,SACA,UAA0B,CAAC,GACN;CACrB,MAAM,EAAE,WAAW,OAAO,sBAAsB;CAChD,MAAM,SAAuB,CAAC;CAC9B,IAAI,cAAc;CAClB,WAAW,MAAM,SAAS,SAAS;EACjC,eAAe,MAAM;EACrB,IAAI,cAAc,UAChB,MAAM,IAAI,MAAM,8BAA8B,SAAS,OAAO;EAEhE,OAAO,KAAK,KAAK;CACnB;CACA,MAAM,QAAQ,OAAO;CACrB,IAAI,OAAO,WAAW,KAAK,OACzB,OAAO;CAET,MAAM,SAAS,IAAI,WAAW,WAAW;CACzC,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,QAAQ;EAC1B,OAAO,IAAI,OAAO,MAAM;EACxB,UAAU,MAAM;CAClB;CACA,OAAO;AACT;;;;;;AAOA,eAAsB,YACpB,SACA,UAA0B,CAAC,GACV;CACjB,MAAM,QAAQ,MAAM,aAAa,SAAS,OAAO;CACjD,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK;AACvC"}
@@ -12,7 +12,8 @@ export declare class ExtractorRegistry {
12
12
  private mimeMap;
13
13
  /**
14
14
  * Associates a suffix pattern with an extractor so the registry can
15
- * route files to the right handler based on their filename.
15
+ * route files to the right handler based on their filename. Patterns are
16
+ * case-insensitive, like the file names they are matched against.
16
17
  */
17
18
  registerByPattern(pattern: string, extractor: ContentExtractor): void;
18
19
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"extractor-registry.d.ts","names":[],"sources":["../src/extractor-registry.ts"],"mappings":";;;;;;;;;qBASa;UACH;UACA;;;;;EAMR,kBAAkB,iBAAiB,WAAW;;;;;EAQ9C,eAAe,kBAAkB,WAAW;;;;;EAQ5C,UAAU,eAAe;;EAoBzB,UAAU,mBAAmB;;;;;;EAS7B,IAAI,cAAc,oBAAoB"}
1
+ {"version":3,"file":"extractor-registry.d.ts","names":[],"sources":["../src/extractor-registry.ts"],"mappings":";;;;;;;;;qBASa;UACH;UACA;;;;;;EAOR,kBAAkB,iBAAiB,WAAW;;;;;EAQ9C,eAAe,kBAAkB,WAAW;;;;;EAQ5C,UAAU,eAAe;;EAoBzB,UAAU,mBAAmB;;;;;;EAS7B,IAAI,cAAc,oBAAoB"}
@@ -11,10 +11,11 @@ var ExtractorRegistry = class {
11
11
  mimeMap = /* @__PURE__ */ new Map();
12
12
  /**
13
13
  * Associates a suffix pattern with an extractor so the registry can
14
- * route files to the right handler based on their filename.
14
+ * route files to the right handler based on their filename. Patterns are
15
+ * case-insensitive, like the file names they are matched against.
15
16
  */
16
17
  registerByPattern(pattern, extractor) {
17
- this.patternMap.set(pattern, extractor);
18
+ this.patternMap.set(pattern.toLowerCase(), extractor);
18
19
  }
19
20
  /**
20
21
  * Provides a fallback route for files that lack a recognizable extension
@@ -1 +1 @@
1
- {"version":3,"file":"extractor-registry.js","names":[],"sources":["../src/extractor-registry.ts"],"sourcesContent":["import type { ContentExtractor } from \"./types.js\";\n\n/**\n * Central routing table that decouples file-format knowledge from the\n * extraction pipeline. New formats can be added without touching pipeline\n * code. Longest-suffix matching ensures compound extensions (e.g.\n * `*.tar.gz`) win over shorter ones, and MIME-type fallback handles\n * files that arrive without a recognizable extension.\n */\nexport class ExtractorRegistry {\n private patternMap = new Map<string, ContentExtractor>();\n private mimeMap = new Map<string, ContentExtractor>();\n\n /**\n * Associates a suffix pattern with an extractor so the registry can\n * route files to the right handler based on their filename.\n */\n registerByPattern(pattern: string, extractor: ContentExtractor): void {\n this.patternMap.set(pattern, extractor);\n }\n\n /**\n * Provides a fallback route for files that lack a recognizable extension\n * (e.g. temp files or HTTP responses identified only by Content-Type).\n */\n registerByMime(mimeType: string, extractor: ContentExtractor): void {\n this.mimeMap.set(mimeType, extractor);\n }\n\n /**\n * Resolves by filename suffix, preferring the most specific match so\n * compound extensions like `*.test.html` beat `*.html` when both exist.\n */\n getByPath(path: string): ContentExtractor | undefined {\n const filename = path.includes(\"/\") ? path.slice(path.lastIndexOf(\"/\") + 1) : path;\n const lowerFilename = filename.toLowerCase();\n\n let bestMatch: ContentExtractor | undefined;\n let bestLength = 0;\n\n for (const [pattern, extractor] of this.patternMap) {\n // Pattern format: *.ext or *.compound.ext\n const suffix = pattern.startsWith(\"*\") ? pattern.slice(1) : pattern;\n if (lowerFilename.endsWith(suffix) && suffix.length > bestLength) {\n bestMatch = extractor;\n bestLength = suffix.length;\n }\n }\n\n return bestMatch;\n }\n\n /** Direct MIME lookup -- used internally by `get` as the fallback path. */\n getByMime(mimeType: string): ContentExtractor | undefined {\n return this.mimeMap.get(mimeType);\n }\n\n /**\n * Single entry point for the pipeline -- tries the most reliable signal\n * (file extension) first, then falls back to MIME type so callers never\n * need to worry about the resolution strategy.\n */\n get(path: string, mimeType?: string): ContentExtractor | undefined {\n const byPath = this.getByPath(path);\n if (byPath) return byPath;\n if (mimeType) return this.getByMime(mimeType);\n return undefined;\n }\n}\n"],"mappings":";;;;;;;;AASA,IAAa,oBAAb,MAA+B;CAC7B,6BAAqB,IAAI,IAA8B;CACvD,0BAAkB,IAAI,IAA8B;;;;;CAMpD,kBAAkB,SAAiB,WAAmC;EACpE,KAAK,WAAW,IAAI,SAAS,SAAS;CACxC;;;;;CAMA,eAAe,UAAkB,WAAmC;EAClE,KAAK,QAAQ,IAAI,UAAU,SAAS;CACtC;;;;;CAMA,UAAU,MAA4C;EAEpD,MAAM,iBADW,KAAK,SAAS,GAAG,IAAI,KAAK,MAAM,KAAK,YAAY,GAAG,IAAI,CAAC,IAAI,KAAA,CAC/C,YAAY;EAE3C,IAAI;EACJ,IAAI,aAAa;EAEjB,KAAK,MAAM,CAAC,SAAS,cAAc,KAAK,YAAY;GAElD,MAAM,SAAS,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;GAC5D,IAAI,cAAc,SAAS,MAAM,KAAK,OAAO,SAAS,YAAY;IAChE,YAAY;IACZ,aAAa,OAAO;GACtB;EACF;EAEA,OAAO;CACT;;CAGA,UAAU,UAAgD;EACxD,OAAO,KAAK,QAAQ,IAAI,QAAQ;CAClC;;;;;;CAOA,IAAI,MAAc,UAAiD;EACjE,MAAM,SAAS,KAAK,UAAU,IAAI;EAClC,IAAI,QAAQ,OAAO;EACnB,IAAI,UAAU,OAAO,KAAK,UAAU,QAAQ;CAE9C;AACF"}
1
+ {"version":3,"file":"extractor-registry.js","names":[],"sources":["../src/extractor-registry.ts"],"sourcesContent":["import type { ContentExtractor } from \"./types.js\";\n\n/**\n * Central routing table that decouples file-format knowledge from the\n * extraction pipeline. New formats can be added without touching pipeline\n * code. Longest-suffix matching ensures compound extensions (e.g.\n * `*.tar.gz`) win over shorter ones, and MIME-type fallback handles\n * files that arrive without a recognizable extension.\n */\nexport class ExtractorRegistry {\n private patternMap = new Map<string, ContentExtractor>();\n private mimeMap = new Map<string, ContentExtractor>();\n\n /**\n * Associates a suffix pattern with an extractor so the registry can\n * route files to the right handler based on their filename. Patterns are\n * case-insensitive, like the file names they are matched against.\n */\n registerByPattern(pattern: string, extractor: ContentExtractor): void {\n this.patternMap.set(pattern.toLowerCase(), extractor);\n }\n\n /**\n * Provides a fallback route for files that lack a recognizable extension\n * (e.g. temp files or HTTP responses identified only by Content-Type).\n */\n registerByMime(mimeType: string, extractor: ContentExtractor): void {\n this.mimeMap.set(mimeType, extractor);\n }\n\n /**\n * Resolves by filename suffix, preferring the most specific match so\n * compound extensions like `*.test.html` beat `*.html` when both exist.\n */\n getByPath(path: string): ContentExtractor | undefined {\n const filename = path.includes(\"/\") ? path.slice(path.lastIndexOf(\"/\") + 1) : path;\n const lowerFilename = filename.toLowerCase();\n\n let bestMatch: ContentExtractor | undefined;\n let bestLength = 0;\n\n for (const [pattern, extractor] of this.patternMap) {\n // Pattern format: *.ext or *.compound.ext\n const suffix = pattern.startsWith(\"*\") ? pattern.slice(1) : pattern;\n if (lowerFilename.endsWith(suffix) && suffix.length > bestLength) {\n bestMatch = extractor;\n bestLength = suffix.length;\n }\n }\n\n return bestMatch;\n }\n\n /** Direct MIME lookup -- used internally by `get` as the fallback path. */\n getByMime(mimeType: string): ContentExtractor | undefined {\n return this.mimeMap.get(mimeType);\n }\n\n /**\n * Single entry point for the pipeline -- tries the most reliable signal\n * (file extension) first, then falls back to MIME type so callers never\n * need to worry about the resolution strategy.\n */\n get(path: string, mimeType?: string): ContentExtractor | undefined {\n const byPath = this.getByPath(path);\n if (byPath) return byPath;\n if (mimeType) return this.getByMime(mimeType);\n return undefined;\n }\n}\n"],"mappings":";;;;;;;;AASA,IAAa,oBAAb,MAA+B;CAC7B,6BAAqB,IAAI,IAA8B;CACvD,0BAAkB,IAAI,IAA8B;;;;;;CAOpD,kBAAkB,SAAiB,WAAmC;EACpE,KAAK,WAAW,IAAI,QAAQ,YAAY,GAAG,SAAS;CACtD;;;;;CAMA,eAAe,UAAkB,WAAmC;EAClE,KAAK,QAAQ,IAAI,UAAU,SAAS;CACtC;;;;;CAMA,UAAU,MAA4C;EAEpD,MAAM,iBADW,KAAK,SAAS,GAAG,IAAI,KAAK,MAAM,KAAK,YAAY,GAAG,IAAI,CAAC,IAAI,KAAA,CAC/C,YAAY;EAE3C,IAAI;EACJ,IAAI,aAAa;EAEjB,KAAK,MAAM,CAAC,SAAS,cAAc,KAAK,YAAY;GAElD,MAAM,SAAS,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;GAC5D,IAAI,cAAc,SAAS,MAAM,KAAK,OAAO,SAAS,YAAY;IAChE,YAAY;IACZ,aAAa,OAAO;GACtB;EACF;EAEA,OAAO;CACT;;CAGA,UAAU,UAAgD;EACxD,OAAO,KAAK,QAAQ,IAAI,QAAQ;CAClC;;;;;;CAOA,IAAI,MAAc,UAAiD;EACjE,MAAM,SAAS,KAAK,UAAU,IAAI;EAClC,IAAI,QAAQ,OAAO;EACnB,IAAI,UAAU,OAAO,KAAK,UAAU,QAAQ;CAE9C;AACF"}
@@ -1,11 +1,15 @@
1
+ import { ZipExtractorLimits } from "../limits.js";
1
2
  import { ContentExtractor } from "../types.js";
2
3
  //#region src/extractors/docx-extractor.d.ts
3
4
  /**
4
5
  * Unlocks Word document content for text-based pipelines via a two-stage
5
6
  * conversion (DOCX -> HTML -> markdown). Supplies both `buffer` and
6
7
  * `arrayBuffer` to mammoth so the same code runs in Node.js and browsers
7
- * without environment detection.
8
+ * without environment detection. The input size and the archive's declared
9
+ * uncompressed size are checked before mammoth inflates it.
8
10
  */
11
+ export declare function createDocxExtractor(limits?: ZipExtractorLimits): ContentExtractor;
12
+ /** DOCX/DOCM extractor with the default limits. */
9
13
  export declare const docxExtractor: ContentExtractor;
10
14
  //#endregion
11
15
  //# sourceMappingURL=docx-extractor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"docx-extractor.d.ts","names":[],"sources":["../../src/extractors/docx-extractor.ts"],"mappings":";;;;;;;;qBAWa,eAAe"}
1
+ {"version":3,"file":"docx-extractor.d.ts","names":[],"sources":["../../src/extractors/docx-extractor.ts"],"mappings":";;;;;;;;;;wBAkBgB,oBAAoB,SAAQ,qBAA0B;;qBAiBzD,eAAe"}
@@ -1,23 +1,32 @@
1
1
  import { collectBytes } from "../collect-bytes.js";
2
2
  import { htmlToMarkdown } from "../html-to-markdown.js";
3
+ import { DEFAULT_MAX_BYTES, DEFAULT_MAX_UNCOMPRESSED_BYTES } from "../limits.js";
4
+ import { assertZipUncompressedSize } from "../zip-size.js";
3
5
  import mammoth from "mammoth";
4
6
  //#region src/extractors/docx-extractor.ts
5
7
  /**
6
8
  * Unlocks Word document content for text-based pipelines via a two-stage
7
9
  * conversion (DOCX -> HTML -> markdown). Supplies both `buffer` and
8
10
  * `arrayBuffer` to mammoth so the same code runs in Node.js and browsers
9
- * without environment detection.
11
+ * without environment detection. The input size and the archive's declared
12
+ * uncompressed size are checked before mammoth inflates it.
10
13
  */
11
- const docxExtractor = async (content) => {
12
- const bytes = await collectBytes(content);
13
- const arrayBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
14
- const result = await mammoth.convertToHtml({
15
- buffer: arrayBuffer,
16
- arrayBuffer
17
- });
18
- return htmlToMarkdown(result.value);
19
- };
14
+ function createDocxExtractor(limits = {}) {
15
+ const { maxBytes = DEFAULT_MAX_BYTES, maxUncompressedBytes = DEFAULT_MAX_UNCOMPRESSED_BYTES } = limits;
16
+ return async (content) => {
17
+ const bytes = await collectBytes(content, { maxBytes });
18
+ assertZipUncompressedSize(bytes, maxUncompressedBytes);
19
+ const arrayBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
20
+ const result = await mammoth.convertToHtml({
21
+ buffer: arrayBuffer,
22
+ arrayBuffer
23
+ });
24
+ return htmlToMarkdown(result.value);
25
+ };
26
+ }
27
+ /** DOCX/DOCM extractor with the default limits. */
28
+ const docxExtractor = createDocxExtractor();
20
29
  //#endregion
21
- export { docxExtractor };
30
+ export { createDocxExtractor, docxExtractor };
22
31
 
23
32
  //# sourceMappingURL=docx-extractor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"docx-extractor.js","names":[],"sources":["../../src/extractors/docx-extractor.ts"],"sourcesContent":["import mammoth from \"mammoth\";\nimport { collectBytes } from \"../collect-bytes.js\";\nimport { htmlToMarkdown } from \"../html-to-markdown.js\";\nimport type { ContentExtractor } from \"../types.js\";\n\n/**\n * Unlocks Word document content for text-based pipelines via a two-stage\n * conversion (DOCX -> HTML -> markdown). Supplies both `buffer` and\n * `arrayBuffer` to mammoth so the same code runs in Node.js and browsers\n * without environment detection.\n */\nexport const docxExtractor: ContentExtractor = async (content) => {\n const bytes = await collectBytes(content);\n const arrayBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);\n // mammoth uses \"buffer\" in Node.js and \"arrayBuffer\" in browsers\n const result = await mammoth.convertToHtml({\n buffer: arrayBuffer,\n arrayBuffer,\n } as Parameters<typeof mammoth.convertToHtml>[0]);\n return htmlToMarkdown(result.value);\n};\n"],"mappings":";;;;;;;;;;AAWA,MAAa,gBAAkC,OAAO,YAAY;CAChE,MAAM,QAAQ,MAAM,aAAa,OAAO;CACxC,MAAM,cAAc,MAAM,OAAO,MAAM,MAAM,YAAY,MAAM,aAAa,MAAM,UAAU;CAE5F,MAAM,SAAS,MAAM,QAAQ,cAAc;EACzC,QAAQ;EACR;CACF,CAAgD;CAChD,OAAO,eAAe,OAAO,KAAK;AACpC"}
1
+ {"version":3,"file":"docx-extractor.js","names":[],"sources":["../../src/extractors/docx-extractor.ts"],"sourcesContent":["import mammoth from \"mammoth\";\nimport { collectBytes } from \"../collect-bytes.js\";\nimport { htmlToMarkdown } from \"../html-to-markdown.js\";\nimport {\n DEFAULT_MAX_BYTES,\n DEFAULT_MAX_UNCOMPRESSED_BYTES,\n type ZipExtractorLimits,\n} from \"../limits.js\";\nimport type { ContentExtractor } from \"../types.js\";\nimport { assertZipUncompressedSize } from \"../zip-size.js\";\n\n/**\n * Unlocks Word document content for text-based pipelines via a two-stage\n * conversion (DOCX -> HTML -> markdown). Supplies both `buffer` and\n * `arrayBuffer` to mammoth so the same code runs in Node.js and browsers\n * without environment detection. The input size and the archive's declared\n * uncompressed size are checked before mammoth inflates it.\n */\nexport function createDocxExtractor(limits: ZipExtractorLimits = {}): ContentExtractor {\n const { maxBytes = DEFAULT_MAX_BYTES, maxUncompressedBytes = DEFAULT_MAX_UNCOMPRESSED_BYTES } =\n limits;\n return async (content) => {\n const bytes = await collectBytes(content, { maxBytes });\n assertZipUncompressedSize(bytes, maxUncompressedBytes);\n const arrayBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);\n // mammoth uses \"buffer\" in Node.js and \"arrayBuffer\" in browsers\n const result = await mammoth.convertToHtml({\n buffer: arrayBuffer,\n arrayBuffer,\n } as Parameters<typeof mammoth.convertToHtml>[0]);\n return htmlToMarkdown(result.value);\n };\n}\n\n/** DOCX/DOCM extractor with the default limits. */\nexport const docxExtractor: ContentExtractor = createDocxExtractor();\n"],"mappings":";;;;;;;;;;;;;AAkBA,SAAgB,oBAAoB,SAA6B,CAAC,GAAqB;CACrF,MAAM,EAAE,WAAW,mBAAmB,uBAAuB,mCAC3D;CACF,OAAO,OAAO,YAAY;EACxB,MAAM,QAAQ,MAAM,aAAa,SAAS,EAAE,SAAS,CAAC;EACtD,0BAA0B,OAAO,oBAAoB;EACrD,MAAM,cAAc,MAAM,OAAO,MAAM,MAAM,YAAY,MAAM,aAAa,MAAM,UAAU;EAE5F,MAAM,SAAS,MAAM,QAAQ,cAAc;GACzC,QAAQ;GACR;EACF,CAAgD;EAChD,OAAO,eAAe,OAAO,KAAK;CACpC;AACF;;AAGA,MAAa,gBAAkC,oBAAoB"}
@@ -1,3 +1,4 @@
1
+ import { ExtractorLimits } from "../limits.js";
1
2
  import { ContentExtractor } from "../types.js";
2
3
  //#region src/extractors/html-extractor.d.ts
3
4
  /**
@@ -5,6 +6,8 @@ import { ContentExtractor } from "../types.js";
5
6
  * text format. Delegates to `htmlToMarkdown` for the actual conversion,
6
7
  * keeping this extractor focused on the bytes-to-string bridging step.
7
8
  */
9
+ export declare function createHtmlExtractor(limits?: ExtractorLimits): ContentExtractor;
10
+ /** HTML extractor with the default limits. */
8
11
  export declare const htmlExtractor: ContentExtractor;
9
12
  //#endregion
10
13
  //# sourceMappingURL=html-extractor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"html-extractor.d.ts","names":[],"sources":["../../src/extractors/html-extractor.ts"],"mappings":";;;;;;;qBASa,eAAe"}
1
+ {"version":3,"file":"html-extractor.d.ts","names":[],"sources":["../../src/extractors/html-extractor.ts"],"mappings":";;;;;;;;wBAUgB,oBAAoB,SAAQ,kBAAuB;;qBAMtD,eAAe"}
@@ -1,16 +1,19 @@
1
1
  import { collectText } from "../collect-bytes.js";
2
2
  import { htmlToMarkdown } from "../html-to-markdown.js";
3
+ import { DEFAULT_MAX_BYTES } from "../limits.js";
3
4
  //#region src/extractors/html-extractor.ts
4
5
  /**
5
6
  * Converts HTML files to markdown so the pipeline outputs a uniform
6
7
  * text format. Delegates to `htmlToMarkdown` for the actual conversion,
7
8
  * keeping this extractor focused on the bytes-to-string bridging step.
8
9
  */
9
- const htmlExtractor = async (content) => {
10
- const html = await collectText(content);
11
- return htmlToMarkdown(html);
12
- };
10
+ function createHtmlExtractor(limits = {}) {
11
+ const { maxBytes = DEFAULT_MAX_BYTES } = limits;
12
+ return async (content) => htmlToMarkdown(await collectText(content, { maxBytes }));
13
+ }
14
+ /** HTML extractor with the default limits. */
15
+ const htmlExtractor = createHtmlExtractor();
13
16
  //#endregion
14
- export { htmlExtractor };
17
+ export { createHtmlExtractor, htmlExtractor };
15
18
 
16
19
  //# sourceMappingURL=html-extractor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"html-extractor.js","names":[],"sources":["../../src/extractors/html-extractor.ts"],"sourcesContent":["import { collectText } from \"../collect-bytes.js\";\nimport { htmlToMarkdown } from \"../html-to-markdown.js\";\nimport type { ContentExtractor } from \"../types.js\";\n\n/**\n * Converts HTML files to markdown so the pipeline outputs a uniform\n * text format. Delegates to `htmlToMarkdown` for the actual conversion,\n * keeping this extractor focused on the bytes-to-string bridging step.\n */\nexport const htmlExtractor: ContentExtractor = async (content) => {\n const html = await collectText(content);\n return htmlToMarkdown(html);\n};\n"],"mappings":";;;;;;;;AASA,MAAa,gBAAkC,OAAO,YAAY;CAChE,MAAM,OAAO,MAAM,YAAY,OAAO;CACtC,OAAO,eAAe,IAAI;AAC5B"}
1
+ {"version":3,"file":"html-extractor.js","names":[],"sources":["../../src/extractors/html-extractor.ts"],"sourcesContent":["import { collectText } from \"../collect-bytes.js\";\nimport { htmlToMarkdown } from \"../html-to-markdown.js\";\nimport { DEFAULT_MAX_BYTES, type ExtractorLimits } from \"../limits.js\";\nimport type { ContentExtractor } from \"../types.js\";\n\n/**\n * Converts HTML files to markdown so the pipeline outputs a uniform\n * text format. Delegates to `htmlToMarkdown` for the actual conversion,\n * keeping this extractor focused on the bytes-to-string bridging step.\n */\nexport function createHtmlExtractor(limits: ExtractorLimits = {}): ContentExtractor {\n const { maxBytes = DEFAULT_MAX_BYTES } = limits;\n return async (content) => htmlToMarkdown(await collectText(content, { maxBytes }));\n}\n\n/** HTML extractor with the default limits. */\nexport const htmlExtractor: ContentExtractor = createHtmlExtractor();\n"],"mappings":";;;;;;;;;AAUA,SAAgB,oBAAoB,SAA0B,CAAC,GAAqB;CAClF,MAAM,EAAE,WAAW,sBAAsB;CACzC,OAAO,OAAO,YAAY,eAAe,MAAM,YAAY,SAAS,EAAE,SAAS,CAAC,CAAC;AACnF;;AAGA,MAAa,gBAAkC,oBAAoB"}
@@ -1,18 +1,21 @@
1
- import { docxExtractor } from "./docx-extractor.js";
2
- import { htmlExtractor } from "./html-extractor.js";
3
- import { markdownExtractor } from "./markdown-extractor.js";
4
- import { pdfExtractor } from "./pdf-extractor.js";
5
- import { textExtractor } from "./text-extractor.js";
6
- import { xlsxExtractor } from "./xlsx-extractor.js";
1
+ import { ZipExtractorLimits } from "../limits.js";
2
+ import { createDocxExtractor, docxExtractor } from "./docx-extractor.js";
3
+ import { createHtmlExtractor, htmlExtractor } from "./html-extractor.js";
4
+ import { createMarkdownExtractor, markdownExtractor } from "./markdown-extractor.js";
5
+ import { createPdfExtractor, pdfExtractor } from "./pdf-extractor.js";
6
+ import { createTextExtractor, textExtractor } from "./text-extractor.js";
7
+ import { createXlsxExtractor, xlsxExtractor } from "./xlsx-extractor.js";
7
8
  import { ExtractorRegistry } from "../extractor-registry.js";
8
9
  //#region src/extractors/index.d.ts
9
10
  /**
10
11
  * Provides a batteries-included registry so most consumers can start
11
12
  * extracting immediately without manual format registration. Custom
12
13
  * registries are still supported for apps that need to override or
13
- * limit the built-in set.
14
+ * limit the built-in set. `limits` applies to every registered extractor
15
+ * (`maxUncompressedBytes` only to DOCX, DOCM and XLSX); omitted limits
16
+ * take the defaults.
14
17
  */
15
- export declare function createDefaultRegistry(): ExtractorRegistry;
18
+ export declare function createDefaultRegistry(limits?: ZipExtractorLimits): ExtractorRegistry;
16
19
  //#endregion
17
- export { docxExtractor, htmlExtractor, markdownExtractor, pdfExtractor, textExtractor, xlsxExtractor };
20
+ export { createDocxExtractor, createHtmlExtractor, createMarkdownExtractor, createPdfExtractor, createTextExtractor, createXlsxExtractor, docxExtractor, htmlExtractor, markdownExtractor, pdfExtractor, textExtractor, xlsxExtractor };
18
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/extractors/index.ts"],"mappings":";;;;;;;;;;;;;;wBAqBgB,yBAAyB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/extractors/index.ts"],"mappings":";;;;;;;;;;;;;;;;;wBAwBgB,sBAAsB,SAAQ,qBAA0B"}
@@ -1,31 +1,36 @@
1
- import { docxExtractor } from "./docx-extractor.js";
2
- import { htmlExtractor } from "./html-extractor.js";
3
- import { markdownExtractor } from "./markdown-extractor.js";
4
- import { pdfExtractor } from "./pdf-extractor.js";
5
- import { textExtractor } from "./text-extractor.js";
6
- import { xlsxExtractor } from "./xlsx-extractor.js";
1
+ import { createDocxExtractor, docxExtractor } from "./docx-extractor.js";
2
+ import { createHtmlExtractor, htmlExtractor } from "./html-extractor.js";
3
+ import { createMarkdownExtractor, markdownExtractor } from "./markdown-extractor.js";
4
+ import { createPdfExtractor, pdfExtractor } from "./pdf-extractor.js";
5
+ import { createTextExtractor, textExtractor } from "./text-extractor.js";
6
+ import { createXlsxExtractor, xlsxExtractor } from "./xlsx-extractor.js";
7
7
  import { ExtractorRegistry } from "../extractor-registry.js";
8
8
  //#region src/extractors/index.ts
9
9
  /**
10
10
  * Provides a batteries-included registry so most consumers can start
11
11
  * extracting immediately without manual format registration. Custom
12
12
  * registries are still supported for apps that need to override or
13
- * limit the built-in set.
13
+ * limit the built-in set. `limits` applies to every registered extractor
14
+ * (`maxUncompressedBytes` only to DOCX, DOCM and XLSX); omitted limits
15
+ * take the defaults.
14
16
  */
15
- function createDefaultRegistry() {
17
+ function createDefaultRegistry(limits = {}) {
18
+ const markdown = createMarkdownExtractor(limits);
19
+ const html = createHtmlExtractor(limits);
20
+ const docx = createDocxExtractor(limits);
16
21
  const registry = new ExtractorRegistry();
17
- registry.registerByPattern("*.md", markdownExtractor);
18
- registry.registerByPattern("*.markdown", markdownExtractor);
19
- registry.registerByPattern("*.txt", textExtractor);
20
- registry.registerByPattern("*.html", htmlExtractor);
21
- registry.registerByPattern("*.htm", htmlExtractor);
22
- registry.registerByPattern("*.pdf", pdfExtractor);
23
- registry.registerByPattern("*.docx", docxExtractor);
24
- registry.registerByPattern("*.docm", docxExtractor);
25
- registry.registerByPattern("*.xlsx", xlsxExtractor);
22
+ registry.registerByPattern("*.md", markdown);
23
+ registry.registerByPattern("*.markdown", markdown);
24
+ registry.registerByPattern("*.txt", createTextExtractor(limits));
25
+ registry.registerByPattern("*.html", html);
26
+ registry.registerByPattern("*.htm", html);
27
+ registry.registerByPattern("*.pdf", createPdfExtractor(limits));
28
+ registry.registerByPattern("*.docx", docx);
29
+ registry.registerByPattern("*.docm", docx);
30
+ registry.registerByPattern("*.xlsx", createXlsxExtractor(limits));
26
31
  return registry;
27
32
  }
28
33
  //#endregion
29
- export { createDefaultRegistry, docxExtractor, htmlExtractor, markdownExtractor, pdfExtractor, textExtractor, xlsxExtractor };
34
+ export { createDefaultRegistry, createDocxExtractor, createHtmlExtractor, createMarkdownExtractor, createPdfExtractor, createTextExtractor, createXlsxExtractor, docxExtractor, htmlExtractor, markdownExtractor, pdfExtractor, textExtractor, xlsxExtractor };
30
35
 
31
36
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/extractors/index.ts"],"sourcesContent":["export { docxExtractor } from \"./docx-extractor.js\";\nexport { htmlExtractor } from \"./html-extractor.js\";\nexport { markdownExtractor } from \"./markdown-extractor.js\";\nexport { pdfExtractor } from \"./pdf-extractor.js\";\nexport { textExtractor } from \"./text-extractor.js\";\nexport { xlsxExtractor } from \"./xlsx-extractor.js\";\n\nimport { ExtractorRegistry } from \"../extractor-registry.js\";\nimport { docxExtractor } from \"./docx-extractor.js\";\nimport { htmlExtractor } from \"./html-extractor.js\";\nimport { markdownExtractor } from \"./markdown-extractor.js\";\nimport { pdfExtractor } from \"./pdf-extractor.js\";\nimport { textExtractor } from \"./text-extractor.js\";\nimport { xlsxExtractor } from \"./xlsx-extractor.js\";\n\n/**\n * Provides a batteries-included registry so most consumers can start\n * extracting immediately without manual format registration. Custom\n * registries are still supported for apps that need to override or\n * limit the built-in set.\n */\nexport function createDefaultRegistry(): ExtractorRegistry {\n const registry = new ExtractorRegistry();\n registry.registerByPattern(\"*.md\", markdownExtractor);\n registry.registerByPattern(\"*.markdown\", markdownExtractor);\n registry.registerByPattern(\"*.txt\", textExtractor);\n registry.registerByPattern(\"*.html\", htmlExtractor);\n registry.registerByPattern(\"*.htm\", htmlExtractor);\n registry.registerByPattern(\"*.pdf\", pdfExtractor);\n registry.registerByPattern(\"*.docx\", docxExtractor);\n registry.registerByPattern(\"*.docm\", docxExtractor);\n registry.registerByPattern(\"*.xlsx\", xlsxExtractor);\n return registry;\n}\n"],"mappings":";;;;;;;;;;;;;;AAqBA,SAAgB,wBAA2C;CACzD,MAAM,WAAW,IAAI,kBAAkB;CACvC,SAAS,kBAAkB,QAAQ,iBAAiB;CACpD,SAAS,kBAAkB,cAAc,iBAAiB;CAC1D,SAAS,kBAAkB,SAAS,aAAa;CACjD,SAAS,kBAAkB,UAAU,aAAa;CAClD,SAAS,kBAAkB,SAAS,aAAa;CACjD,SAAS,kBAAkB,SAAS,YAAY;CAChD,SAAS,kBAAkB,UAAU,aAAa;CAClD,SAAS,kBAAkB,UAAU,aAAa;CAClD,SAAS,kBAAkB,UAAU,aAAa;CAClD,OAAO;AACT"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/extractors/index.ts"],"sourcesContent":["export { createDocxExtractor, docxExtractor } from \"./docx-extractor.js\";\nexport { createHtmlExtractor, htmlExtractor } from \"./html-extractor.js\";\nexport { createMarkdownExtractor, markdownExtractor } from \"./markdown-extractor.js\";\nexport { createPdfExtractor, pdfExtractor } from \"./pdf-extractor.js\";\nexport { createTextExtractor, textExtractor } from \"./text-extractor.js\";\nexport { createXlsxExtractor, xlsxExtractor } from \"./xlsx-extractor.js\";\n\nimport { ExtractorRegistry } from \"../extractor-registry.js\";\nimport type { ZipExtractorLimits } from \"../limits.js\";\nimport { createDocxExtractor } from \"./docx-extractor.js\";\nimport { createHtmlExtractor } from \"./html-extractor.js\";\nimport { createMarkdownExtractor } from \"./markdown-extractor.js\";\nimport { createPdfExtractor } from \"./pdf-extractor.js\";\nimport { createTextExtractor } from \"./text-extractor.js\";\nimport { createXlsxExtractor } from \"./xlsx-extractor.js\";\n\n/**\n * Provides a batteries-included registry so most consumers can start\n * extracting immediately without manual format registration. Custom\n * registries are still supported for apps that need to override or\n * limit the built-in set. `limits` applies to every registered extractor\n * (`maxUncompressedBytes` only to DOCX, DOCM and XLSX); omitted limits\n * take the defaults.\n */\nexport function createDefaultRegistry(limits: ZipExtractorLimits = {}): ExtractorRegistry {\n const markdown = createMarkdownExtractor(limits);\n const html = createHtmlExtractor(limits);\n const docx = createDocxExtractor(limits);\n const registry = new ExtractorRegistry();\n registry.registerByPattern(\"*.md\", markdown);\n registry.registerByPattern(\"*.markdown\", markdown);\n registry.registerByPattern(\"*.txt\", createTextExtractor(limits));\n registry.registerByPattern(\"*.html\", html);\n registry.registerByPattern(\"*.htm\", html);\n registry.registerByPattern(\"*.pdf\", createPdfExtractor(limits));\n registry.registerByPattern(\"*.docx\", docx);\n registry.registerByPattern(\"*.docm\", docx);\n registry.registerByPattern(\"*.xlsx\", createXlsxExtractor(limits));\n return registry;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAwBA,SAAgB,sBAAsB,SAA6B,CAAC,GAAsB;CACxF,MAAM,WAAW,wBAAwB,MAAM;CAC/C,MAAM,OAAO,oBAAoB,MAAM;CACvC,MAAM,OAAO,oBAAoB,MAAM;CACvC,MAAM,WAAW,IAAI,kBAAkB;CACvC,SAAS,kBAAkB,QAAQ,QAAQ;CAC3C,SAAS,kBAAkB,cAAc,QAAQ;CACjD,SAAS,kBAAkB,SAAS,oBAAoB,MAAM,CAAC;CAC/D,SAAS,kBAAkB,UAAU,IAAI;CACzC,SAAS,kBAAkB,SAAS,IAAI;CACxC,SAAS,kBAAkB,SAAS,mBAAmB,MAAM,CAAC;CAC9D,SAAS,kBAAkB,UAAU,IAAI;CACzC,SAAS,kBAAkB,UAAU,IAAI;CACzC,SAAS,kBAAkB,UAAU,oBAAoB,MAAM,CAAC;CAChE,OAAO;AACT"}
@@ -1,3 +1,4 @@
1
+ import { ExtractorLimits } from "../limits.js";
1
2
  import { ContentExtractor } from "../types.js";
2
3
  //#region src/extractors/markdown-extractor.d.ts
3
4
  /**
@@ -6,6 +7,8 @@ import { ContentExtractor } from "../types.js";
6
7
  * the registry can distinguish `.md` from `.txt` -- allowing future
7
8
  * markdown-specific processing without breaking existing registrations.
8
9
  */
10
+ export declare function createMarkdownExtractor(limits?: ExtractorLimits): ContentExtractor;
11
+ /** Markdown extractor with the default limits. */
9
12
  export declare const markdownExtractor: ContentExtractor;
10
13
  //#endregion
11
14
  //# sourceMappingURL=markdown-extractor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-extractor.d.ts","names":[],"sources":["../../src/extractors/markdown-extractor.ts"],"mappings":";;;;;;;;qBASa,mBAAmB"}
1
+ {"version":3,"file":"markdown-extractor.d.ts","names":[],"sources":["../../src/extractors/markdown-extractor.ts"],"mappings":";;;;;;;;;wBAUgB,wBAAwB,SAAQ,kBAAuB;;qBAM1D,mBAAmB"}
@@ -1,4 +1,5 @@
1
1
  import { collectText } from "../collect-bytes.js";
2
+ import { DEFAULT_MAX_BYTES } from "../limits.js";
2
3
  //#region src/extractors/markdown-extractor.ts
3
4
  /**
4
5
  * Passthrough extractor for markdown files. Functionally identical to
@@ -6,10 +7,13 @@ import { collectText } from "../collect-bytes.js";
6
7
  * the registry can distinguish `.md` from `.txt` -- allowing future
7
8
  * markdown-specific processing without breaking existing registrations.
8
9
  */
9
- const markdownExtractor = async (content) => {
10
- return collectText(content);
11
- };
10
+ function createMarkdownExtractor(limits = {}) {
11
+ const { maxBytes = DEFAULT_MAX_BYTES } = limits;
12
+ return (content) => collectText(content, { maxBytes });
13
+ }
14
+ /** Markdown extractor with the default limits. */
15
+ const markdownExtractor = createMarkdownExtractor();
12
16
  //#endregion
13
- export { markdownExtractor };
17
+ export { createMarkdownExtractor, markdownExtractor };
14
18
 
15
19
  //# sourceMappingURL=markdown-extractor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-extractor.js","names":[],"sources":["../../src/extractors/markdown-extractor.ts"],"sourcesContent":["import { collectText } from \"../collect-bytes.js\";\nimport type { ContentExtractor } from \"../types.js\";\n\n/**\n * Passthrough extractor for markdown files. Functionally identical to\n * `textExtractor` but registered under its own extension patterns so\n * the registry can distinguish `.md` from `.txt` -- allowing future\n * markdown-specific processing without breaking existing registrations.\n */\nexport const markdownExtractor: ContentExtractor = async (content) => {\n return collectText(content);\n};\n"],"mappings":";;;;;;;;AASA,MAAa,oBAAsC,OAAO,YAAY;CACpE,OAAO,YAAY,OAAO;AAC5B"}
1
+ {"version":3,"file":"markdown-extractor.js","names":[],"sources":["../../src/extractors/markdown-extractor.ts"],"sourcesContent":["import { collectText } from \"../collect-bytes.js\";\nimport { DEFAULT_MAX_BYTES, type ExtractorLimits } from \"../limits.js\";\nimport type { ContentExtractor } from \"../types.js\";\n\n/**\n * Passthrough extractor for markdown files. Functionally identical to\n * `textExtractor` but registered under its own extension patterns so\n * the registry can distinguish `.md` from `.txt` -- allowing future\n * markdown-specific processing without breaking existing registrations.\n */\nexport function createMarkdownExtractor(limits: ExtractorLimits = {}): ContentExtractor {\n const { maxBytes = DEFAULT_MAX_BYTES } = limits;\n return (content) => collectText(content, { maxBytes });\n}\n\n/** Markdown extractor with the default limits. */\nexport const markdownExtractor: ContentExtractor = createMarkdownExtractor();\n"],"mappings":";;;;;;;;;AAUA,SAAgB,wBAAwB,SAA0B,CAAC,GAAqB;CACtF,MAAM,EAAE,WAAW,sBAAsB;CACzC,QAAQ,YAAY,YAAY,SAAS,EAAE,SAAS,CAAC;AACvD;;AAGA,MAAa,oBAAsC,wBAAwB"}
@@ -1,3 +1,4 @@
1
+ import { ExtractorLimits } from "../limits.js";
1
2
  import { ContentExtractor } from "../types.js";
2
3
  //#region src/extractors/pdf-extractor.d.ts
3
4
  /**
@@ -5,6 +6,8 @@ import { ContentExtractor } from "../types.js";
5
6
  * section with a `## Page N` header. This preserves page boundaries in
6
7
  * the indexed content, making search results more precise.
7
8
  */
9
+ export declare function createPdfExtractor(limits?: ExtractorLimits): ContentExtractor;
10
+ /** PDF extractor with the default limits. */
8
11
  export declare const pdfExtractor: ContentExtractor;
9
12
  //#endregion
10
13
  //# sourceMappingURL=pdf-extractor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pdf-extractor.d.ts","names":[],"sources":["../../src/extractors/pdf-extractor.ts"],"mappings":";;;;;;;qBASa,cAAc"}
1
+ {"version":3,"file":"pdf-extractor.d.ts","names":[],"sources":["../../src/extractors/pdf-extractor.ts"],"mappings":";;;;;;;;wBAUgB,mBAAmB,SAAQ,kBAAuB;;qBAqBrD,cAAc"}