docling.rs 0.20.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 ADDED
@@ -0,0 +1,257 @@
1
+ # docling.rs (Node.js / Bun bindings)
2
+
3
+ Native [Node.js](https://nodejs.org) / [Bun](https://bun.sh) bindings for
4
+ [docling.rs](https://github.com/artiz/docling.rs) — a Rust port of
5
+ [docling](https://github.com/docling-project/docling). Convert Markdown, HTML,
6
+ DOCX, PPTX, XLSX, EPUB, ODF, LaTeX, email, PDF, images and more into a unified
7
+ `DoclingDocument`, and export it as **Markdown** or docling-core **JSON**.
8
+
9
+ Built with [napi-rs](https://napi.rs), so it ships a real native addon (`.node`)
10
+ that loads in both Node.js and Bun (Bun implements N-API) — the same binary, no
11
+ rebuild between runtimes.
12
+
13
+ ## Install
14
+
15
+ Released versions ship **prebuilt** native binaries, so no Rust toolchain is
16
+ needed to use the package:
17
+
18
+ ```bash
19
+ npm install docling.rs # or: bun add docling.rs
20
+ ```
21
+
22
+ Prebuilt platforms: Linux x64 / arm64 (glibc) and Windows x64. (macOS isn't
23
+ prebuilt — build from source, see below.) The right binary is pulled in
24
+ automatically as a platform-specific `optionalDependency` (`docling.rs-<triple>`). Releases are published to npm by
25
+ manually running the `npm publish` workflow
26
+ (`.github/workflows/npm-publish.yml`) — by default it builds the latest master
27
+ (the workspace version); optionally pass a release tag to build that instead.
28
+ Decoupled from the crates.io release.
29
+
30
+ ## Build from source
31
+
32
+ This package lives in the docling.rs Cargo workspace and can also build the
33
+ addon from Rust source — needed for local development or an unsupported
34
+ platform. You need a Rust toolchain (1.82+) and Node.js 14+ (or Bun).
35
+
36
+ ```bash
37
+ cd crates/docling-node
38
+ npm install # installs @napi-rs/cli
39
+ npm run build # release build → docling.rs.<platform>.node + native.js/.d.ts
40
+ # npm run build:debug # faster, unoptimized
41
+ ```
42
+
43
+ > The addon statically links the ONNX runtime used by the PDF/image pipeline, so
44
+ > the built `.node` is large. Declarative formats (Markdown, HTML, DOCX, …) don't
45
+ > touch it; only PDF/image conversion loads the ML models (downloaded on first
46
+ > use, like the CLI).
47
+
48
+ ## Quick start
49
+
50
+ ```js
51
+ import { convertFile, convert, DocumentConverter } from 'docling.rs'
52
+
53
+ // Convert a file — format detected from the extension.
54
+ const { content } = convertFile('report.docx')
55
+ console.log(content) // Markdown
56
+
57
+ // Convert in-memory bytes (e.g. an upload) — pass the format explicitly.
58
+ const md = convert({ name: 'notes', data: Buffer.from('# Hi\n'), format: 'md' })
59
+
60
+ // docling-core JSON instead of Markdown.
61
+ const json = convertFile('report.docx', { to: 'json' })
62
+
63
+ // Reuse a converter across many documents.
64
+ const converter = new DocumentConverter({ strict: true })
65
+ const a = converter.convert({ name: 'a.md', data: Buffer.from('# A\n') })
66
+ ```
67
+
68
+ CommonJS works too: `const { convertFile } = require('docling.rs')`.
69
+
70
+ ### Async (off the event loop)
71
+
72
+ Conversion is CPU-bound; the `*Async` variants run it on the libuv thread pool
73
+ so the event loop stays free. Prefer these for PDF/image and for servers.
74
+
75
+ ```js
76
+ import { convertFileAsync } from 'docling.rs'
77
+
78
+ const res = await convertFileAsync('paper.pdf', { to: 'json' })
79
+ ```
80
+
81
+ ### Streaming Markdown
82
+
83
+ `streamFileMarkdown` yields Markdown chunks in document order as conversion
84
+ progresses. For PDF (whose pages convert in parallel) output starts flowing
85
+ before the whole document is done; concatenating the chunks reproduces the
86
+ buffered `content` byte-for-byte.
87
+
88
+ ```js
89
+ import { streamFileMarkdown } from 'docling.rs'
90
+
91
+ for await (const chunk of streamFileMarkdown('paper.pdf')) {
92
+ process.stdout.write(chunk)
93
+ }
94
+ ```
95
+
96
+ ### PDF / images: getting the ML models
97
+
98
+ Declarative formats (Markdown, HTML, DOCX, XLSX, …) are pure Rust and need
99
+ nothing. The **PDF/image** path needs native assets that are *not* bundled in the
100
+ addon — pdfium plus the ONNX models (layout, OCR, TableFormer). Converting a
101
+ PDF/image/METS input **throws** until they're on disk. Fetch them with a
102
+ one-liner from your app's directory (where you'll `npm install docling.rs`):
103
+
104
+ ```bash
105
+ curl -fsSL https://raw.githubusercontent.com/artiz/docling.rs/master/scripts/download_dependencies.sh | sh
106
+ ```
107
+
108
+ ```js
109
+ import { convertFileAsync } from 'docling.rs'
110
+
111
+ const res = await convertFileAsync('paper.pdf', { to: 'markdown' }) // ✅ works
112
+ ```
113
+
114
+ `scripts/download_dependencies.sh` fetches everything from this repo's
115
+ [GitHub Releases](https://github.com/artiz/docling.rs/releases) straight into
116
+ `./models` and `./.pdfium` — which this package (and the Rust CLI) look for by
117
+ default, relative to the process's current directory, so no env vars or setup
118
+ call are needed afterwards:
119
+
120
+ | Asset | Destination |
121
+ | --- | --- |
122
+ | **pdfium** | `.pdfium/lib/libpdfium.so` |
123
+ | **layout** (`layout_heron.onnx`) | `models/layout_heron.onnx` |
124
+ | **OCR** rec model + dictionary | `models/ocr_rec.onnx`, `models/ppocr_keys_v1.txt` |
125
+ | **TableFormer** | `models/tableformer/{encoder,decoder,bbox}.onnx` |
126
+
127
+ > **layout + TableFormer are PyTorch→ONNX exports**
128
+ > (`docling-project/docling-layout-heron`, Apache-2.0;
129
+ > `docling-project/docling-models`, CDLA-Permissive-2.0/Apache-2.0 — see
130
+ > [`MODELS_NOTICE.md`](../../MODELS_NOTICE.md) for full attribution), not
131
+ > docling.rs's own weights — docling.rs hosts the converted `.onnx` as a
132
+ > GitHub Release purely so you don't need a local Python/torch toolchain.
133
+ > pdfium and the OCR model are re-hosted, unmodified, from their own public
134
+ > releases, on the same host for convenience.
135
+ >
136
+ > Run it from wherever your app lives — the script only writes to `./models`
137
+ > and `./.pdfium` under the current directory, e.g. in a container build step:
138
+ > ```bash
139
+ > cd /path/to/your/app && curl -fsSL https://raw.githubusercontent.com/artiz/docling.rs/master/scripts/download_dependencies.sh | sh
140
+ > ```
141
+ >
142
+ > To use your own export/host instead, point the env vars at it directly:
143
+ > `DOCLING_LAYOUT_ONNX`, `DOCLING_OCR_REC_ONNX`, `DOCLING_OCR_DICT`,
144
+ > `DOCLING_TABLEFORMER_{ENCODER,DECODER,BBOX}`, `PDFIUM_DYNAMIC_LIB_PATH` — an
145
+ > env var always wins over the `./models` / `./.pdfium` default.
146
+
147
+ ```js
148
+ checkDependencies() // { home, pdfium, layout, ocr, tableformer, ready, missing }
149
+ ```
150
+
151
+ ### Reusing a warm `Pipeline` (many PDFs)
152
+
153
+ The one-shot `convertFile` / `convertFileAsync` rebuild the pipeline — reloading
154
+ every ONNX model — on each call. To convert many PDFs/images, reuse a `Pipeline`
155
+ so the models load **once**:
156
+
157
+ ```js
158
+ import { Pipeline } from 'docling.rs'
159
+
160
+ const pipeline = new Pipeline({ strict: true })
161
+ for (const path of pdfPaths) {
162
+ const { content } = pipeline.convertFile(path, { to: 'markdown' }) // warm models
163
+ }
164
+ ```
165
+
166
+ `Pipeline` handles `pdf` and `image` inputs (the ML pipeline) and is synchronous
167
+ — reuse one instance behind a job queue.
168
+
169
+ ### Images
170
+
171
+ Pick how pictures render in Markdown with `imageMode`:
172
+
173
+ ```js
174
+ // Inline, self-contained: ![Image](data:image/png;base64,…)
175
+ convertFile('slides.pptx', { imageMode: 'embedded' })
176
+
177
+ // Referenced: links + the image bytes to write yourself.
178
+ const res = convertFile('slides.pptx', { imageMode: 'referenced', artifactsDir: 'assets' })
179
+ for (const img of res.images) {
180
+ await fs.writeFile(img.path, img.data) // e.g. assets/image_000000.png
181
+ }
182
+ ```
183
+
184
+ JSON output always embeds extracted images as data URIs.
185
+
186
+ ## API
187
+
188
+ ### Functions
189
+
190
+ | Function | Returns | Notes |
191
+ | --- | --- | --- |
192
+ | `convertFile(path, options?)` | `ConvertResult` | Detects format from the extension. |
193
+ | `convert(input, options?)` | `ConvertResult` | In-memory bytes (`{ name, data, format? }`). |
194
+ | `convertFileAsync(path, options?)` | `Promise<ConvertResult>` | Off the event loop. |
195
+ | `convertAsync(input, options?)` | `Promise<ConvertResult>` | Off the event loop. |
196
+ | `streamFileMarkdown(path, options?)` | `AsyncGenerator<string>` | Markdown chunks in document order. |
197
+ | `supportedFormats()` | `string[]` | Supported input format ids. |
198
+ | `formatFromName(name)` | `string \| null` | Detect a format id from a filename/extension. |
199
+ | `checkDependencies(options?)` | `DependencyStatus` | Report which PDF/image deps are present. |
200
+
201
+ `Pipeline` is the reusable warm PDF/image converter: `new Pipeline(converterOptions)`
202
+ then `convertFile` / `convert`.
203
+
204
+ `DocumentConverter` is the reusable form: `new DocumentConverter(converterOptions)`
205
+ then `convert` / `convertFile` / `convertFileAsync` / `convertAsync` /
206
+ `convertFileStreaming`. Converter config (`strict`, `fetchImages`,
207
+ `allowedFormats`) is set once on the constructor; output options (`to`,
208
+ `imageMode`, `artifactsDir`) are per call.
209
+
210
+ ### Options
211
+
212
+ - `to`: `"markdown"` (default) or `"json"`.
213
+ - `imageMode`: `"placeholder"` (default), `"embedded"`, or `"referenced"`.
214
+ - `artifactsDir`: directory name used in `referenced` links (default `"artifacts"`).
215
+ - `strict`: cleaner, more conformant Markdown instead of docling's byte-for-byte
216
+ legacy output (Markdown only).
217
+ - `fetchImages`: for HTML/EPUB, resolve and embed external `<img src>`. Off by
218
+ default; fetches http(s) URLs over the network — enable only for trusted input.
219
+ - `allowedFormats`: restrict the converter to these format ids/extensions.
220
+
221
+ ### `ConvertResult`
222
+
223
+ ```ts
224
+ interface ConvertResult {
225
+ content: string // Markdown or JSON, per `to`
226
+ format: string // detected input format id
227
+ status: string // "success" | "partial_success" | "failure"
228
+ inputName: string
229
+ images: { path: string; data: Buffer }[] // for the `referenced` image mode
230
+ }
231
+ ```
232
+
233
+ Full TypeScript types are generated into `index.d.ts` / `native.d.ts`.
234
+
235
+ ## Examples
236
+
237
+ The [`examples/`](examples) folder is a self-contained project that depends on
238
+ the published `docling.rs` package — `npm install` there, then run any of them:
239
+
240
+ ```bash
241
+ cd examples
242
+ npm install
243
+ node node-basic.mjs # ESM: file, bytes, JSON, reuse
244
+ bun run bun-basic.ts # Bun + TypeScript: async + streaming
245
+ node pdf-pipeline.mjs # warm Pipeline for PDFs (run scripts/download_dependencies.sh first)
246
+ ```
247
+
248
+ - [`examples/node-basic.mjs`](examples/node-basic.mjs) — Node.js (ESM): file, bytes, JSON, reuse.
249
+ - [`examples/bun-basic.ts`](examples/bun-basic.ts) — Bun + TypeScript, with async and streaming.
250
+ - [`examples/pdf-pipeline.mjs`](examples/pdf-pipeline.mjs) — warm `Pipeline` for PDFs.
251
+
252
+ The smoke test exercises the locally-built addon instead: `npm run build` once at
253
+ the package root, then `node test/smoke.mjs` (or `bun test/smoke.mjs`).
254
+
255
+ ## License
256
+
257
+ MIT, same as the rest of docling.rs.
package/deps.js ADDED
@@ -0,0 +1,188 @@
1
+ // Dependency *resolution* for the PDF/image ML pipeline.
2
+ //
3
+ // The declarative backends (Markdown, HTML, DOCX, XLSX, …) are pure Rust and
4
+ // need nothing. The PDF/image path needs native assets that are NOT bundled in
5
+ // the addon (they're large and licensed separately from docling.rs's own MIT
6
+ // code):
7
+ //
8
+ // - libpdfium (PDF text extraction + page rasterization) — required for PDF
9
+ // - RT-DETR layout model (models/layout_heron.onnx) — required for PDF & image
10
+ // - PP-OCR rec + dict (models/ocr_rec.onnx, ppocr_keys_v1.txt) — used for pages with no text layer
11
+ // - TableFormer (models/tableformer/{encoder,decoder,bbox}.onnx) — optional; geometric fallback otherwise
12
+ //
13
+ // This module does NOT download anything — `scripts/download_dependencies.sh`
14
+ // does that, fetching everything from this repo's GitHub Releases straight
15
+ // into `./models` and `./.pdfium` (see MODELS_NOTICE.md for attribution: the
16
+ // layout model and TableFormer are PyTorch→ONNX exports of docling-project's
17
+ // own models, re-hosted here as a convenience). This module just resolves
18
+ // where those files (or an explicit `DOCLING_*` / `PDFIUM_DYNAMIC_LIB_PATH`
19
+ // override) should live, reports whether they're present, and wires the
20
+ // matching env vars in-process so the native pipeline finds them — mirroring
21
+ // the CWD-relative defaults already baked into the Rust pipeline itself, so a
22
+ // plain `convertFileAsync(...)` call needs no explicit setup once
23
+ // `download_dependencies.sh` has run.
24
+
25
+ 'use strict'
26
+
27
+ const fs = require('fs')
28
+ const os = require('os')
29
+ const path = require('path')
30
+
31
+ // Formats whose conversion requires the ML models + native libs above.
32
+ const ML_FORMATS = new Set(['pdf', 'image', 'mets_gbs'])
33
+
34
+ // pdfium's shared-library filename, by platform.
35
+ function pdfiumLibName() {
36
+ switch (process.platform) {
37
+ case 'linux':
38
+ return 'libpdfium.so'
39
+ case 'darwin':
40
+ return 'libpdfium.dylib'
41
+ case 'win32':
42
+ return 'pdfium.dll'
43
+ default:
44
+ throw new Error(`unsupported platform for pdfium: ${process.platform}/${process.arch}`)
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Resolve the install home directory (absolute), and which `pdfium/`-vs-
50
+ * `.pdfium/` layout it uses. Precedence: an explicit `dir` > `$DOCLING_RS_HOME`
51
+ * > the current directory, *if* it already has a local `models/` or `.pdfium/`
52
+ * (the layout `scripts/download_dependencies.sh` and `scripts/pdf_setup.sh`
53
+ * both produce, and the one the native Rust pipeline's own env-var-less
54
+ * defaults already resolve — `models/layout_heron.onnx`, `.pdfium/lib/…` —
55
+ * relative to *its* CWD) > `~/.cache/docling.rs`. This lets a plain
56
+ * `convertFileAsync(...)` call succeed with zero setup (no env vars) whenever
57
+ * the app is run from a directory that already has the dependencies
58
+ * downloaded next to it.
59
+ */
60
+ function homeDir(dir) {
61
+ if (dir) return { home: path.resolve(dir), dotPdfium: false }
62
+ if (process.env.DOCLING_RS_HOME) return { home: path.resolve(process.env.DOCLING_RS_HOME), dotPdfium: false }
63
+ const cwd = process.cwd()
64
+ const hasLocal =
65
+ fs.existsSync(path.join(cwd, 'models', 'layout_heron.onnx')) ||
66
+ fs.existsSync(path.join(cwd, '.pdfium', 'lib', pdfiumLibName()))
67
+ if (hasLocal) return { home: cwd, dotPdfium: true }
68
+ return { home: path.join(os.homedir(), '.cache', 'docling.rs'), dotPdfium: false }
69
+ }
70
+
71
+ /**
72
+ * The resolved on-disk location of each dependency: an existing `DOCLING_*` /
73
+ * `PDFIUM_DYNAMIC_LIB_PATH` environment variable wins (so a local Python export
74
+ * is honored), else the path under the install home directory.
75
+ */
76
+ function resolvePaths(dir) {
77
+ const { home, dotPdfium } = homeDir(dir)
78
+ const models = path.join(home, 'models')
79
+
80
+ const pdfiumLibDir =
81
+ process.env.PDFIUM_DYNAMIC_LIB_PATH || path.join(home, dotPdfium ? '.pdfium' : 'pdfium', 'lib')
82
+ return {
83
+ home,
84
+ models,
85
+ pdfiumLibDir,
86
+ pdfiumLib: path.join(pdfiumLibDir, pdfiumLibName()),
87
+ layout: process.env.DOCLING_LAYOUT_ONNX || path.join(models, 'layout_heron.onnx'),
88
+ ocrRec: process.env.DOCLING_OCR_REC_ONNX || path.join(models, 'ocr_rec.onnx'),
89
+ ocrDict: process.env.DOCLING_OCR_DICT || path.join(models, 'ppocr_keys_v1.txt'),
90
+ tfEncoder:
91
+ process.env.DOCLING_TABLEFORMER_ENCODER || path.join(models, 'tableformer', 'encoder.onnx'),
92
+ tfDecoder:
93
+ process.env.DOCLING_TABLEFORMER_DECODER || path.join(models, 'tableformer', 'decoder.onnx'),
94
+ tfBbox: process.env.DOCLING_TABLEFORMER_BBOX || path.join(models, 'tableformer', 'bbox.onnx'),
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Report which dependencies are present on disk. `ready` is true when the
100
+ * minimum for PDF (pdfium + layout) is present.
101
+ */
102
+ function checkDependencies(options = {}) {
103
+ const p = resolvePaths(options.dir)
104
+ const has = (f) => fs.existsSync(f)
105
+ const status = {
106
+ home: p.home,
107
+ pdfium: has(p.pdfiumLib),
108
+ layout: has(p.layout),
109
+ ocr: has(p.ocrRec) && has(p.ocrDict),
110
+ tableformer: has(p.tfEncoder) && has(p.tfDecoder) && has(p.tfBbox),
111
+ }
112
+ status.ready = status.pdfium && status.layout
113
+ status.missing = [
114
+ !status.pdfium && 'pdfium',
115
+ !status.layout && 'layout_heron.onnx',
116
+ ].filter(Boolean)
117
+ return status
118
+ }
119
+
120
+ /** Point the current process at installed assets (so the native pipeline finds them). */
121
+ function exportEnv(p) {
122
+ if (fs.existsSync(p.pdfiumLib)) process.env.PDFIUM_DYNAMIC_LIB_PATH = p.pdfiumLibDir
123
+ if (fs.existsSync(p.layout)) process.env.DOCLING_LAYOUT_ONNX = p.layout
124
+ if (fs.existsSync(p.ocrRec)) process.env.DOCLING_OCR_REC_ONNX = p.ocrRec
125
+ if (fs.existsSync(p.ocrDict)) process.env.DOCLING_OCR_DICT = p.ocrDict
126
+ if (fs.existsSync(p.tfEncoder)) process.env.DOCLING_TABLEFORMER_ENCODER = p.tfEncoder
127
+ if (fs.existsSync(p.tfDecoder)) process.env.DOCLING_TABLEFORMER_DECODER = p.tfDecoder
128
+ if (fs.existsSync(p.tfBbox)) process.env.DOCLING_TABLEFORMER_BBOX = p.tfBbox
129
+ }
130
+
131
+ /**
132
+ * A copy-pasteable next step, shown when a PDF/image/METS conversion is
133
+ * attempted without the dependencies on disk.
134
+ */
135
+ function downloadGuide() {
136
+ return [
137
+ 'Run this once from your app\'s directory (fetches pdfium + the ONNX',
138
+ 'models — layout, OCR, TableFormer — from this repo\'s GitHub Releases',
139
+ 'straight into ./models and ./.pdfium, which this package looks for by',
140
+ 'default; no env vars needed afterwards):',
141
+ '',
142
+ ' curl -fsSL https://raw.githubusercontent.com/artiz/docling.rs/master/scripts/download_dependencies.sh | sh',
143
+ '',
144
+ 'or, from a checkout of the repo:',
145
+ '',
146
+ ' scripts/download_dependencies.sh',
147
+ '',
148
+ 'TableFormer is optional (tables fall back to geometric reconstruction',
149
+ 'without it). To use your own export/host instead, point the DOCLING_*',
150
+ 'env vars at it directly: DOCLING_LAYOUT_ONNX, DOCLING_OCR_REC_ONNX,',
151
+ 'DOCLING_OCR_DICT, DOCLING_TABLEFORMER_{ENCODER,DECODER,BBOX},',
152
+ 'PDFIUM_DYNAMIC_LIB_PATH — see MODELS_NOTICE.md for licensing.',
153
+ '',
154
+ 'Declarative formats (md, html, docx, xlsx, …) need none of this — only',
155
+ 'PDF, image and METS conversion do.',
156
+ ].join('\n')
157
+ }
158
+
159
+ /**
160
+ * Throw a clear, actionable error if `format` needs the ML pipeline but its
161
+ * dependencies aren't installed. Called before ML conversions; also wires up
162
+ * the `DOCLING_*` / `PDFIUM_DYNAMIC_LIB_PATH` env vars for whatever is present,
163
+ * so a checkout with `scripts/download_dependencies.sh` already run just works.
164
+ */
165
+ function assertMlReady(format, dir) {
166
+ if (!ML_FORMATS.has(format)) return
167
+ const p = resolvePaths(dir)
168
+ exportEnv(p)
169
+ const status = checkDependencies({ dir })
170
+ // Image needs layout (+OCR), but not pdfium; PDF/METS need both.
171
+ const needPdfium = format !== 'image'
172
+ const missing = [!status.layout && 'layout_heron.onnx', needPdfium && !status.pdfium && 'pdfium'].filter(
173
+ Boolean,
174
+ )
175
+ if (missing.length === 0) return
176
+ throw new Error(
177
+ `Converting '${format}' requires the PDF/ML dependencies, which are not installed: ` +
178
+ `${missing.join(', ')}.\n\n${downloadGuide()}`,
179
+ )
180
+ }
181
+
182
+ module.exports = {
183
+ ML_FORMATS,
184
+ checkDependencies,
185
+ assertMlReady,
186
+ resolvePaths,
187
+ exportEnv,
188
+ }
package/index.d.ts ADDED
@@ -0,0 +1,97 @@
1
+ // Public type surface for the `docling.rs` npm package. Re-exports the native
2
+ // binding's option/result types and unguarded functions, and declares the
3
+ // JS-wrapped classes, the dependency API, and the streaming helper.
4
+
5
+ import type {
6
+ ConverterOptions,
7
+ OutputOptions,
8
+ ConvertOptions,
9
+ ConvertInput,
10
+ ConvertResult,
11
+ } from './native'
12
+
13
+ export type { ConverterOptions, OutputOptions, ConvertOptions, ConvertInput, ConvertResult }
14
+
15
+ // Format helpers pass straight through from the native binding.
16
+ export { supportedFormats, formatFromName } from './native'
17
+
18
+ /** Callback form used by the native streaming API (prefer {@link streamFileMarkdown}). */
19
+ export type StreamCallback = (err: Error | null, chunk: string | undefined | null) => void
20
+
21
+ /** Convert a file on disk (format detected from the extension). Throws for PDF/image/METS if deps aren't installed. */
22
+ export declare function convertFile(path: string, options?: ConvertOptions | null): ConvertResult
23
+ /** Convert in-memory bytes. Throws for PDF/image/METS if deps aren't installed. */
24
+ export declare function convert(input: ConvertInput, options?: ConvertOptions | null): ConvertResult
25
+ /** Async (Promise) file conversion, off the event loop. Rejects for PDF/image/METS if deps aren't installed. */
26
+ export declare function convertFileAsync(path: string, options?: ConvertOptions | null): Promise<ConvertResult>
27
+ /** Async (Promise) bytes conversion, off the event loop. */
28
+ export declare function convertAsync(input: ConvertInput, options?: ConvertOptions | null): Promise<ConvertResult>
29
+
30
+ /** A reusable converter holding config (strict / fetchImages / allowedFormats). */
31
+ export declare class DocumentConverter {
32
+ constructor(options?: ConverterOptions | null)
33
+ convertFile(path: string, options?: OutputOptions | null): ConvertResult
34
+ convert(input: ConvertInput, options?: OutputOptions | null): ConvertResult
35
+ convertFileAsync(path: string, options?: OutputOptions | null): Promise<ConvertResult>
36
+ convertAsync(input: ConvertInput, options?: OutputOptions | null): Promise<ConvertResult>
37
+ convertFileStreaming(path: string, callback: StreamCallback, options?: OutputOptions | null): void
38
+ }
39
+
40
+ /**
41
+ * A reusable PDF/image pipeline that keeps the ONNX models loaded across calls.
42
+ * Use instead of the per-call functions when converting many PDFs/images — the
43
+ * one-shot path reloads every model each call. Handles `pdf` and `image` inputs.
44
+ */
45
+ export declare class Pipeline {
46
+ constructor(options?: ConverterOptions | null)
47
+ convertFile(path: string, options?: OutputOptions | null): ConvertResult
48
+ convert(input: ConvertInput, options?: OutputOptions | null): ConvertResult
49
+ }
50
+
51
+ // --- dependency provisioning (PDF/image ML pipeline) -----------------------
52
+
53
+ /** Where installed dependencies live and which are present. */
54
+ export interface DependencyStatus {
55
+ /** Install home directory. */
56
+ home: string
57
+ /** libpdfium present. */
58
+ pdfium: boolean
59
+ /** Layout model (layout_heron.onnx) present. */
60
+ layout: boolean
61
+ /** OCR model + dictionary present. */
62
+ ocr: boolean
63
+ /** TableFormer encoder/decoder/bbox present. */
64
+ tableformer: boolean
65
+ /** True when the minimum for PDF (pdfium + layout) is present. */
66
+ ready: boolean
67
+ /** Human-readable list of the missing required assets. */
68
+ missing: string[]
69
+ }
70
+
71
+ /**
72
+ * Report which PDF/image dependencies are present on disk. Fetch them with
73
+ * `scripts/download_dependencies.sh` (see the package README) — this function
74
+ * only reports status, it does not download anything.
75
+ */
76
+ export declare function checkDependencies(options?: { dir?: string }): DependencyStatus
77
+
78
+ // --- streaming --------------------------------------------------------------
79
+
80
+ /** Options for {@link streamFileMarkdown} (converter config + streamable output). */
81
+ export interface StreamOptions {
82
+ strict?: boolean
83
+ fetchImages?: boolean
84
+ allowedFormats?: string[]
85
+ imageMode?: 'placeholder' | 'embedded'
86
+ artifactsDir?: string
87
+ }
88
+
89
+ /**
90
+ * Stream a file's Markdown in chunks, in document order, as conversion
91
+ * progresses — the win for PDF, whose pages convert in parallel. Concatenating
92
+ * the chunks reproduces the buffered `convertFile(path).content` byte-for-byte.
93
+ */
94
+ export declare function streamFileMarkdown(
95
+ filePath: string,
96
+ options?: StreamOptions,
97
+ ): AsyncGenerator<string, void, unknown>
package/index.js ADDED
@@ -0,0 +1,172 @@
1
+ // Public entry point for the `docling.rs` npm package.
2
+ //
3
+ // Wraps the native N-API binding (loaded by `native.js`, which picks the right
4
+ // prebuilt `.node` for the host platform) with two things:
5
+ // 1. dependency guards — converting a PDF/image/METS input throws a clear
6
+ // error unless the ML models + pdfium are on disk (see
7
+ // scripts/download_dependencies.sh);
8
+ // 2. a `streamFileMarkdown` async generator over Markdown chunks.
9
+ //
10
+ // Works in Node.js and Bun (Bun implements N-API).
11
+
12
+ 'use strict'
13
+
14
+ const native = require('./native.js')
15
+ const { checkDependencies, assertMlReady } = require('./deps.js')
16
+
17
+ // Resolve the format id of an input for the ML guard. Uses the native
18
+ // extension→format map; falls back to an explicitly-passed format string.
19
+ function mlFormatOf(name, format) {
20
+ if (format) {
21
+ return native.formatFromName(`x.${String(format).replace(/^\./, '')}`) || String(format)
22
+ }
23
+ return native.formatFromName(name || '') || ''
24
+ }
25
+
26
+ // --- guarded one-shot functions --------------------------------------------
27
+
28
+ function convertFile(path, options) {
29
+ assertMlReady(mlFormatOf(path))
30
+ return native.convertFile(path, options)
31
+ }
32
+
33
+ function convert(input, options) {
34
+ assertMlReady(mlFormatOf(input && input.name, input && input.format))
35
+ return native.convert(input, options)
36
+ }
37
+
38
+ // async so a guard failure surfaces as a rejected promise, not a sync throw.
39
+ async function convertFileAsync(path, options) {
40
+ assertMlReady(mlFormatOf(path))
41
+ return native.convertFileAsync(path, options)
42
+ }
43
+
44
+ async function convertAsync(input, options) {
45
+ assertMlReady(mlFormatOf(input && input.name, input && input.format))
46
+ return native.convertAsync(input, options)
47
+ }
48
+
49
+ // --- guarded classes --------------------------------------------------------
50
+
51
+ class DocumentConverter {
52
+ constructor(options) {
53
+ this._inner = new native.DocumentConverter(options)
54
+ }
55
+
56
+ convertFile(path, options) {
57
+ assertMlReady(mlFormatOf(path))
58
+ return this._inner.convertFile(path, options)
59
+ }
60
+
61
+ convert(input, options) {
62
+ assertMlReady(mlFormatOf(input && input.name, input && input.format))
63
+ return this._inner.convert(input, options)
64
+ }
65
+
66
+ async convertFileAsync(path, options) {
67
+ assertMlReady(mlFormatOf(path))
68
+ return this._inner.convertFileAsync(path, options)
69
+ }
70
+
71
+ async convertAsync(input, options) {
72
+ assertMlReady(mlFormatOf(input && input.name, input && input.format))
73
+ return this._inner.convertAsync(input, options)
74
+ }
75
+
76
+ convertFileStreaming(path, callback, options) {
77
+ assertMlReady(mlFormatOf(path))
78
+ return this._inner.convertFileStreaming(path, callback, options)
79
+ }
80
+ }
81
+
82
+ // The warm PDF/image pipeline is inherently ML — always guarded.
83
+ class Pipeline {
84
+ constructor(options) {
85
+ this._inner = new native.Pipeline(options)
86
+ }
87
+
88
+ convertFile(path, options) {
89
+ assertMlReady(mlFormatOf(path))
90
+ return this._inner.convertFile(path, options)
91
+ }
92
+
93
+ convert(input, options) {
94
+ assertMlReady(mlFormatOf(input && input.name, input && input.format))
95
+ return this._inner.convert(input, options)
96
+ }
97
+ }
98
+
99
+ // --- streaming --------------------------------------------------------------
100
+
101
+ /**
102
+ * Stream a file's Markdown in chunks, in document order, as conversion
103
+ * progresses — the win for PDF, whose pages convert in parallel.
104
+ *
105
+ * Yields each Markdown chunk; concatenating every chunk reproduces the buffered
106
+ * `convertFile(path).content` byte-for-byte.
107
+ *
108
+ * @param {string} filePath
109
+ * @param {object} [options]
110
+ * @returns {AsyncGenerator<string, void, unknown>}
111
+ */
112
+ async function* streamFileMarkdown(filePath, options = {}) {
113
+ assertMlReady(mlFormatOf(filePath))
114
+ const { strict, fetchImages, allowedFormats, imageMode, artifactsDir } = options
115
+ const converter = new native.DocumentConverter({ strict, fetchImages, allowedFormats })
116
+
117
+ // Bridge the native (err, chunk) callback into an async generator. Chunks are
118
+ // delivered on the event loop (via a threadsafe function); a null chunk ends
119
+ // the stream, a non-null err ends it with a throw.
120
+ const queue = []
121
+ let done = false
122
+ let failure = null
123
+ let notify = null
124
+ const wake = () => {
125
+ if (notify) {
126
+ const n = notify
127
+ notify = null
128
+ n()
129
+ }
130
+ }
131
+
132
+ converter.convertFileStreaming(
133
+ filePath,
134
+ (err, chunk) => {
135
+ if (err) {
136
+ failure = err
137
+ done = true
138
+ } else if (chunk === null || chunk === undefined) {
139
+ done = true
140
+ } else {
141
+ queue.push(chunk)
142
+ }
143
+ wake()
144
+ },
145
+ { imageMode, artifactsDir },
146
+ )
147
+
148
+ while (true) {
149
+ if (queue.length > 0) {
150
+ yield queue.shift()
151
+ continue
152
+ }
153
+ if (failure) throw failure
154
+ if (done) return
155
+ await new Promise((resolve) => {
156
+ notify = resolve
157
+ })
158
+ }
159
+ }
160
+
161
+ // --- exports (explicit, so ESM named imports work in Node and Bun) ----------
162
+
163
+ module.exports.convert = convert
164
+ module.exports.convertFile = convertFile
165
+ module.exports.convertAsync = convertAsync
166
+ module.exports.convertFileAsync = convertFileAsync
167
+ module.exports.DocumentConverter = DocumentConverter
168
+ module.exports.Pipeline = Pipeline
169
+ module.exports.streamFileMarkdown = streamFileMarkdown
170
+ module.exports.checkDependencies = checkDependencies
171
+ module.exports.supportedFormats = native.supportedFormats
172
+ module.exports.formatFromName = native.formatFromName
package/native.d.ts ADDED
@@ -0,0 +1,157 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ /** Config for a reusable [`DocumentConverter`]. */
7
+ export interface ConverterOptions {
8
+ /**
9
+ * Emit cleaner, more conformant Markdown (code-fence languages preserved,
10
+ * no inline-run spacing artifacts) instead of docling's byte-for-byte
11
+ * legacy output. Markdown only. Default `false`.
12
+ */
13
+ strict?: boolean
14
+ /**
15
+ * For HTML/EPUB, resolve external `<img src>` (data: URIs, local files,
16
+ * http(s) URLs, EPUB entries) and embed the bytes. Off by default; when on,
17
+ * http(s) URLs are fetched over the network — enable only for trusted input.
18
+ */
19
+ fetchImages?: boolean
20
+ /**
21
+ * Restrict the converter to these formats (ids like `"md"`, `"pdf"`, or
22
+ * extensions like `".html"`); anything else is rejected. Default: accept all.
23
+ */
24
+ allowedFormats?: Array<string>
25
+ }
26
+ /** Per-call output options (how to render the converted document). */
27
+ export interface OutputOptions {
28
+ /** `"markdown"` (default) or `"json"` (docling-core DoclingDocument wire format). */
29
+ to?: string
30
+ /**
31
+ * Picture handling for Markdown: `"placeholder"` (default), `"embedded"`
32
+ * (base64 data URIs inline), or `"referenced"` (returns image files in
33
+ * `images`). Ignored for JSON, which always embeds images as data URIs.
34
+ */
35
+ imageMode?: string
36
+ /** Directory name used in `referenced` image links. Default `"artifacts"`. */
37
+ artifactsDir?: string
38
+ }
39
+ /**
40
+ * All options for the one-shot module-level functions (converter config +
41
+ * output options in a single object).
42
+ */
43
+ export interface ConvertOptions {
44
+ strict?: boolean
45
+ fetchImages?: boolean
46
+ allowedFormats?: Array<string>
47
+ to?: string
48
+ imageMode?: string
49
+ artifactsDir?: string
50
+ }
51
+ /** In-memory input for [`DocumentConverter::convert`] / [`convert`]. */
52
+ export interface ConvertInput {
53
+ /** Logical document name (used as the docling document name). */
54
+ name: string
55
+ /** Raw file bytes. */
56
+ data: Buffer
57
+ /**
58
+ * Format id or extension (e.g. `"md"`, `"pdf"`, `".html"`). Omit to infer
59
+ * from an extension on `name`.
60
+ */
61
+ format?: string
62
+ }
63
+ /** One extracted image file, returned for the `referenced` image mode. */
64
+ export interface ImageArtifact {
65
+ /** Path relative to the Markdown file (e.g. `"artifacts/image_000000.png"`). */
66
+ path: string
67
+ /** The image bytes to write at `path`. */
68
+ data: Buffer
69
+ }
70
+ /** The result of a conversion. */
71
+ export interface ConvertResult {
72
+ /** The rendered document: Markdown or JSON, per `to`. */
73
+ content: string
74
+ /** Detected input format id (e.g. `"md"`, `"pdf"`). */
75
+ format: string
76
+ /** `"success"`, `"partial_success"`, or `"failure"`. */
77
+ status: string
78
+ /** The document name. */
79
+ inputName: string
80
+ /**
81
+ * For the `referenced` image mode, the image files to write next to the
82
+ * Markdown; empty otherwise.
83
+ */
84
+ images: Array<ImageArtifact>
85
+ }
86
+ /**
87
+ * Convert a file on disk. Detects the format from the extension and (for
88
+ * HTML/EPUB image fetching) resolves relative `<img src>` against the file's
89
+ * directory.
90
+ */
91
+ export declare function convertFile(path: string, options?: ConvertOptions | undefined | null): ConvertResult
92
+ /** Convert in-memory bytes. */
93
+ export declare function convert(input: ConvertInput, options?: ConvertOptions | undefined | null): ConvertResult
94
+ /**
95
+ * Async (Promise-returning) [`convert_file`]. The CPU-bound work runs on the
96
+ * libuv thread pool, keeping the event loop free — use this for PDF/image.
97
+ */
98
+ export declare function convertFileAsync(path: string, options?: ConvertOptions | undefined | null): Promise<ConvertResult>
99
+ /** Async (Promise-returning) [`convert`]. */
100
+ export declare function convertAsync(input: ConvertInput, options?: ConvertOptions | undefined | null): Promise<ConvertResult>
101
+ /** The list of supported input format ids. */
102
+ export declare function supportedFormats(): Array<string>
103
+ /**
104
+ * Detect a format id from a filename or extension (e.g. `"report.pdf"` →
105
+ * `"pdf"`). Returns `null` for unknown extensions.
106
+ */
107
+ export declare function formatFromName(name: string): string | null
108
+ /**
109
+ * A reusable converter. Holds config (strict / fetch-images / allowed formats)
110
+ * so you can convert many documents without re-parsing options each time —
111
+ * the analogue of the Rust `DocumentConverter`.
112
+ */
113
+ export declare class DocumentConverter {
114
+ constructor(options?: ConverterOptions | undefined | null)
115
+ /** Convert a file on disk (sync). */
116
+ convertFile(path: string, options?: OutputOptions | undefined | null): ConvertResult
117
+ /** Convert in-memory bytes (sync). */
118
+ convert(input: ConvertInput, options?: OutputOptions | undefined | null): ConvertResult
119
+ /** Async (Promise-returning) file conversion (runs off the event loop). */
120
+ convertFileAsync(path: string, options?: OutputOptions | undefined | null): Promise<ConvertResult>
121
+ /** Async (Promise-returning) bytes conversion (runs off the event loop). */
122
+ convertAsync(input: ConvertInput, options?: OutputOptions | undefined | null): Promise<ConvertResult>
123
+ /**
124
+ * Stream a file's Markdown in chunks, in document order, as conversion
125
+ * progresses (the headline win for PDF, whose pages convert in parallel).
126
+ *
127
+ * `callback` is invoked as `(err, chunk)`: once per Markdown chunk with
128
+ * `chunk` a string, once with `chunk === null` at the end, or once with a
129
+ * non-null `err` on failure. Only `placeholder` / `embedded` image modes
130
+ * stream; `referenced` is rejected. Prefer the `streamFileMarkdown`
131
+ * async-generator wrapper in JS over calling this directly.
132
+ */
133
+ convertFileStreaming(path: string, callback: (err: Error | null, arg: string | undefined | null) => any, options?: OutputOptions | undefined | null): void
134
+ }
135
+ /**
136
+ * A reusable PDF/image pipeline that keeps the ONNX models (layout, OCR,
137
+ * TableFormer) loaded across calls — the analogue of the Rust `Pipeline`. Use
138
+ * this instead of the per-call `convertFile` when converting many PDFs/images:
139
+ * the one-shot functions rebuild the pipeline (reloading every model) each
140
+ * call, whereas this loads them once.
141
+ *
142
+ * Handles `pdf` and `image` inputs (the ML pipeline). Models load lazily on
143
+ * first use, so constructing a `Pipeline` is cheap; the first conversion pays
144
+ * the model-load cost. Synchronous and single-threaded — reuse one instance
145
+ * for a sequence of documents (e.g. behind a job queue).
146
+ */
147
+ export declare class Pipeline {
148
+ /**
149
+ * Construct the pipeline. Only `strict` is read (cleaner Markdown);
150
+ * `fetchImages` / `allowedFormats` don't apply to the PDF/image pipeline.
151
+ */
152
+ constructor(options?: ConverterOptions | undefined | null)
153
+ /** Convert a PDF or image file, reusing the warm models. */
154
+ convertFile(path: string, options?: OutputOptions | undefined | null): ConvertResult
155
+ /** Convert PDF or image bytes, reusing the warm models. */
156
+ convert(input: ConvertInput, options?: OutputOptions | undefined | null): ConvertResult
157
+ }
package/native.js ADDED
@@ -0,0 +1,322 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /* prettier-ignore */
4
+
5
+ /* auto-generated by NAPI-RS */
6
+
7
+ const { existsSync, readFileSync } = require('fs')
8
+ const { join } = require('path')
9
+
10
+ const { platform, arch } = process
11
+
12
+ let nativeBinding = null
13
+ let localFileExisted = false
14
+ let loadError = null
15
+
16
+ function isMusl() {
17
+ // For Node 10
18
+ if (!process.report || typeof process.report.getReport !== 'function') {
19
+ try {
20
+ const lddPath = require('child_process').execSync('which ldd').toString().trim()
21
+ return readFileSync(lddPath, 'utf8').includes('musl')
22
+ } catch (e) {
23
+ return true
24
+ }
25
+ } else {
26
+ const { glibcVersionRuntime } = process.report.getReport().header
27
+ return !glibcVersionRuntime
28
+ }
29
+ }
30
+
31
+ switch (platform) {
32
+ case 'android':
33
+ switch (arch) {
34
+ case 'arm64':
35
+ localFileExisted = existsSync(join(__dirname, 'docling-rs.android-arm64.node'))
36
+ try {
37
+ if (localFileExisted) {
38
+ nativeBinding = require('./docling-rs.android-arm64.node')
39
+ } else {
40
+ nativeBinding = require('docling.rs-android-arm64')
41
+ }
42
+ } catch (e) {
43
+ loadError = e
44
+ }
45
+ break
46
+ case 'arm':
47
+ localFileExisted = existsSync(join(__dirname, 'docling-rs.android-arm-eabi.node'))
48
+ try {
49
+ if (localFileExisted) {
50
+ nativeBinding = require('./docling-rs.android-arm-eabi.node')
51
+ } else {
52
+ nativeBinding = require('docling.rs-android-arm-eabi')
53
+ }
54
+ } catch (e) {
55
+ loadError = e
56
+ }
57
+ break
58
+ default:
59
+ throw new Error(`Unsupported architecture on Android ${arch}`)
60
+ }
61
+ break
62
+ case 'win32':
63
+ switch (arch) {
64
+ case 'x64':
65
+ localFileExisted = existsSync(
66
+ join(__dirname, 'docling-rs.win32-x64-msvc.node')
67
+ )
68
+ try {
69
+ if (localFileExisted) {
70
+ nativeBinding = require('./docling-rs.win32-x64-msvc.node')
71
+ } else {
72
+ nativeBinding = require('docling.rs-win32-x64-msvc')
73
+ }
74
+ } catch (e) {
75
+ loadError = e
76
+ }
77
+ break
78
+ case 'ia32':
79
+ localFileExisted = existsSync(
80
+ join(__dirname, 'docling-rs.win32-ia32-msvc.node')
81
+ )
82
+ try {
83
+ if (localFileExisted) {
84
+ nativeBinding = require('./docling-rs.win32-ia32-msvc.node')
85
+ } else {
86
+ nativeBinding = require('docling.rs-win32-ia32-msvc')
87
+ }
88
+ } catch (e) {
89
+ loadError = e
90
+ }
91
+ break
92
+ case 'arm64':
93
+ localFileExisted = existsSync(
94
+ join(__dirname, 'docling-rs.win32-arm64-msvc.node')
95
+ )
96
+ try {
97
+ if (localFileExisted) {
98
+ nativeBinding = require('./docling-rs.win32-arm64-msvc.node')
99
+ } else {
100
+ nativeBinding = require('docling.rs-win32-arm64-msvc')
101
+ }
102
+ } catch (e) {
103
+ loadError = e
104
+ }
105
+ break
106
+ default:
107
+ throw new Error(`Unsupported architecture on Windows: ${arch}`)
108
+ }
109
+ break
110
+ case 'darwin':
111
+ localFileExisted = existsSync(join(__dirname, 'docling-rs.darwin-universal.node'))
112
+ try {
113
+ if (localFileExisted) {
114
+ nativeBinding = require('./docling-rs.darwin-universal.node')
115
+ } else {
116
+ nativeBinding = require('docling.rs-darwin-universal')
117
+ }
118
+ break
119
+ } catch {}
120
+ switch (arch) {
121
+ case 'x64':
122
+ localFileExisted = existsSync(join(__dirname, 'docling-rs.darwin-x64.node'))
123
+ try {
124
+ if (localFileExisted) {
125
+ nativeBinding = require('./docling-rs.darwin-x64.node')
126
+ } else {
127
+ nativeBinding = require('docling.rs-darwin-x64')
128
+ }
129
+ } catch (e) {
130
+ loadError = e
131
+ }
132
+ break
133
+ case 'arm64':
134
+ localFileExisted = existsSync(
135
+ join(__dirname, 'docling-rs.darwin-arm64.node')
136
+ )
137
+ try {
138
+ if (localFileExisted) {
139
+ nativeBinding = require('./docling-rs.darwin-arm64.node')
140
+ } else {
141
+ nativeBinding = require('docling.rs-darwin-arm64')
142
+ }
143
+ } catch (e) {
144
+ loadError = e
145
+ }
146
+ break
147
+ default:
148
+ throw new Error(`Unsupported architecture on macOS: ${arch}`)
149
+ }
150
+ break
151
+ case 'freebsd':
152
+ if (arch !== 'x64') {
153
+ throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
154
+ }
155
+ localFileExisted = existsSync(join(__dirname, 'docling-rs.freebsd-x64.node'))
156
+ try {
157
+ if (localFileExisted) {
158
+ nativeBinding = require('./docling-rs.freebsd-x64.node')
159
+ } else {
160
+ nativeBinding = require('docling.rs-freebsd-x64')
161
+ }
162
+ } catch (e) {
163
+ loadError = e
164
+ }
165
+ break
166
+ case 'linux':
167
+ switch (arch) {
168
+ case 'x64':
169
+ if (isMusl()) {
170
+ localFileExisted = existsSync(
171
+ join(__dirname, 'docling-rs.linux-x64-musl.node')
172
+ )
173
+ try {
174
+ if (localFileExisted) {
175
+ nativeBinding = require('./docling-rs.linux-x64-musl.node')
176
+ } else {
177
+ nativeBinding = require('docling.rs-linux-x64-musl')
178
+ }
179
+ } catch (e) {
180
+ loadError = e
181
+ }
182
+ } else {
183
+ localFileExisted = existsSync(
184
+ join(__dirname, 'docling-rs.linux-x64-gnu.node')
185
+ )
186
+ try {
187
+ if (localFileExisted) {
188
+ nativeBinding = require('./docling-rs.linux-x64-gnu.node')
189
+ } else {
190
+ nativeBinding = require('docling.rs-linux-x64-gnu')
191
+ }
192
+ } catch (e) {
193
+ loadError = e
194
+ }
195
+ }
196
+ break
197
+ case 'arm64':
198
+ if (isMusl()) {
199
+ localFileExisted = existsSync(
200
+ join(__dirname, 'docling-rs.linux-arm64-musl.node')
201
+ )
202
+ try {
203
+ if (localFileExisted) {
204
+ nativeBinding = require('./docling-rs.linux-arm64-musl.node')
205
+ } else {
206
+ nativeBinding = require('docling.rs-linux-arm64-musl')
207
+ }
208
+ } catch (e) {
209
+ loadError = e
210
+ }
211
+ } else {
212
+ localFileExisted = existsSync(
213
+ join(__dirname, 'docling-rs.linux-arm64-gnu.node')
214
+ )
215
+ try {
216
+ if (localFileExisted) {
217
+ nativeBinding = require('./docling-rs.linux-arm64-gnu.node')
218
+ } else {
219
+ nativeBinding = require('docling.rs-linux-arm64-gnu')
220
+ }
221
+ } catch (e) {
222
+ loadError = e
223
+ }
224
+ }
225
+ break
226
+ case 'arm':
227
+ if (isMusl()) {
228
+ localFileExisted = existsSync(
229
+ join(__dirname, 'docling-rs.linux-arm-musleabihf.node')
230
+ )
231
+ try {
232
+ if (localFileExisted) {
233
+ nativeBinding = require('./docling-rs.linux-arm-musleabihf.node')
234
+ } else {
235
+ nativeBinding = require('docling.rs-linux-arm-musleabihf')
236
+ }
237
+ } catch (e) {
238
+ loadError = e
239
+ }
240
+ } else {
241
+ localFileExisted = existsSync(
242
+ join(__dirname, 'docling-rs.linux-arm-gnueabihf.node')
243
+ )
244
+ try {
245
+ if (localFileExisted) {
246
+ nativeBinding = require('./docling-rs.linux-arm-gnueabihf.node')
247
+ } else {
248
+ nativeBinding = require('docling.rs-linux-arm-gnueabihf')
249
+ }
250
+ } catch (e) {
251
+ loadError = e
252
+ }
253
+ }
254
+ break
255
+ case 'riscv64':
256
+ if (isMusl()) {
257
+ localFileExisted = existsSync(
258
+ join(__dirname, 'docling-rs.linux-riscv64-musl.node')
259
+ )
260
+ try {
261
+ if (localFileExisted) {
262
+ nativeBinding = require('./docling-rs.linux-riscv64-musl.node')
263
+ } else {
264
+ nativeBinding = require('docling.rs-linux-riscv64-musl')
265
+ }
266
+ } catch (e) {
267
+ loadError = e
268
+ }
269
+ } else {
270
+ localFileExisted = existsSync(
271
+ join(__dirname, 'docling-rs.linux-riscv64-gnu.node')
272
+ )
273
+ try {
274
+ if (localFileExisted) {
275
+ nativeBinding = require('./docling-rs.linux-riscv64-gnu.node')
276
+ } else {
277
+ nativeBinding = require('docling.rs-linux-riscv64-gnu')
278
+ }
279
+ } catch (e) {
280
+ loadError = e
281
+ }
282
+ }
283
+ break
284
+ case 's390x':
285
+ localFileExisted = existsSync(
286
+ join(__dirname, 'docling-rs.linux-s390x-gnu.node')
287
+ )
288
+ try {
289
+ if (localFileExisted) {
290
+ nativeBinding = require('./docling-rs.linux-s390x-gnu.node')
291
+ } else {
292
+ nativeBinding = require('docling.rs-linux-s390x-gnu')
293
+ }
294
+ } catch (e) {
295
+ loadError = e
296
+ }
297
+ break
298
+ default:
299
+ throw new Error(`Unsupported architecture on Linux: ${arch}`)
300
+ }
301
+ break
302
+ default:
303
+ throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
304
+ }
305
+
306
+ if (!nativeBinding) {
307
+ if (loadError) {
308
+ throw loadError
309
+ }
310
+ throw new Error(`Failed to load native binding`)
311
+ }
312
+
313
+ const { convertFile, convert, convertFileAsync, convertAsync, DocumentConverter, Pipeline, supportedFormats, formatFromName } = nativeBinding
314
+
315
+ module.exports.convertFile = convertFile
316
+ module.exports.convert = convert
317
+ module.exports.convertFileAsync = convertFileAsync
318
+ module.exports.convertAsync = convertAsync
319
+ module.exports.DocumentConverter = DocumentConverter
320
+ module.exports.Pipeline = Pipeline
321
+ module.exports.supportedFormats = supportedFormats
322
+ module.exports.formatFromName = formatFromName
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "docling.rs",
3
+ "version": "0.20.0",
4
+ "description": "Node.js / Bun bindings for docling.rs — a Rust port of docling. Convert Markdown, HTML, DOCX, PPTX, XLSX, PDF, images and more into a unified DoclingDocument (Markdown or docling-core JSON).",
5
+ "keywords": [
6
+ "docling",
7
+ "document",
8
+ "pdf",
9
+ "markdown",
10
+ "docx",
11
+ "converter",
12
+ "napi",
13
+ "bun"
14
+ ],
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/artiz/docling.rs.git",
19
+ "directory": "crates/docling-node"
20
+ },
21
+ "homepage": "https://github.com/artiz/docling.rs",
22
+ "main": "index.js",
23
+ "types": "index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./index.d.ts",
27
+ "import": "./index.js",
28
+ "require": "./index.js"
29
+ }
30
+ },
31
+ "napi": {
32
+ "name": "docling-rs",
33
+ "triples": {
34
+ "defaults": false,
35
+ "additional": [
36
+ "x86_64-unknown-linux-gnu",
37
+ "aarch64-unknown-linux-gnu",
38
+ "x86_64-pc-windows-msvc"
39
+ ]
40
+ }
41
+ },
42
+ "engines": {
43
+ "node": ">= 14"
44
+ },
45
+ "files": [
46
+ "index.js",
47
+ "index.d.ts",
48
+ "deps.js",
49
+ "native.js",
50
+ "native.d.ts",
51
+ "README.md"
52
+ ],
53
+ "scripts": {
54
+ "build": "napi build --platform --release --strip --js native.js --dts native.d.ts",
55
+ "build:debug": "napi build --platform --js native.js --dts native.d.ts",
56
+ "artifacts": "napi artifacts",
57
+ "prepublishOnly": "napi prepublish -t npm --skip-gh-release",
58
+ "version": "napi version",
59
+ "test": "node test/smoke.mjs",
60
+ "example": "npm --prefix examples install && node examples/node-basic.mjs",
61
+ "example:bun": "npm --prefix examples install && bun run examples/bun-basic.ts"
62
+ },
63
+ "devDependencies": {
64
+ "@napi-rs/cli": "^2.18.4"
65
+ },
66
+ "optionalDependencies": {
67
+ "docling.rs-linux-x64-gnu": "0.20.0",
68
+ "docling.rs-linux-arm64-gnu": "0.20.0",
69
+ "docling.rs-win32-x64-msvc": "0.20.0"
70
+ }
71
+ }