@ryangarber/markitdown-ts 0.1.4 → 0.2.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 +22 -10
- package/dist/cli.mjs +516 -342
- package/dist/index.cjs +12057 -429
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -34
- package/dist/index.d.ts +19 -34
- package/dist/index.js +490 -329
- package/dist/index.js.map +1 -1
- package/dist/markitdown.1 +30 -0
- package/package.json +40 -20
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import turndownPluginGfm from "@joplin/turndown-plugin-gfm";
|
|
2
|
-
import TurndownService from "turndown";
|
|
3
1
|
import { parseHTML } from "linkedom";
|
|
2
|
+
import TurndownPluginGfm from "@truto/turndown-plugin-gfm";
|
|
3
|
+
import TurndownService from "turndown";
|
|
4
|
+
import { fileTypeFromBuffer } from "file-type";
|
|
5
|
+
import { Mime } from "mime";
|
|
6
|
+
import otherTypes from "mime/types/other.js";
|
|
7
|
+
import standardTypes from "mime/types/standard.js";
|
|
4
8
|
import mammoth from "mammoth";
|
|
5
9
|
import { extractText, getDocumentProxy, getMeta } from "unpdf";
|
|
6
|
-
import
|
|
7
|
-
import { DOMParser } from "@xmldom/xmldom";
|
|
10
|
+
import { pptxToHtml } from "@jvmr/pptx-to-html";
|
|
11
|
+
import { DOMParser, Document, Element, Node } from "@xmldom/xmldom";
|
|
12
|
+
import readExcelFile from "read-excel-file/universal";
|
|
8
13
|
import { unzipSync } from "fflate";
|
|
9
|
-
|
|
14
|
+
// src/utils/bytes.ts
|
|
10
15
|
async function toUint8Array(source) {
|
|
11
16
|
if (source instanceof Uint8Array) return source;
|
|
12
17
|
if (isBlob(source)) return new Uint8Array(await source.arrayBuffer());
|
|
@@ -22,12 +27,11 @@ function decodeText(source) {
|
|
|
22
27
|
function isBlob(value) {
|
|
23
28
|
return typeof Blob !== "undefined" && value instanceof Blob;
|
|
24
29
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
var CustomTurnDown = class {
|
|
30
|
+
// src/utils/turndown.ts
|
|
31
|
+
var Turndown = class {
|
|
28
32
|
convertSoup(document) {
|
|
29
33
|
const service = new TurndownService({ headingStyle: "atx" });
|
|
30
|
-
service.use(
|
|
34
|
+
service.use(TurndownPluginGfm);
|
|
31
35
|
service.addRule("anchor tags", {
|
|
32
36
|
filter: ["a"],
|
|
33
37
|
replacement(content, node) {
|
|
@@ -62,48 +66,166 @@ var CustomTurnDown = class {
|
|
|
62
66
|
return ``;
|
|
63
67
|
}
|
|
64
68
|
});
|
|
65
|
-
return service.turndown(document);
|
|
69
|
+
return service.turndown(serializeNode(document));
|
|
66
70
|
}
|
|
67
71
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Turndown and its GFM plugin rely on DOM APIs (e.g. `HTMLTableElement.rows`, `Element.closest`)
|
|
74
|
+
* that linkedom does not implement, which silently drops content such as tables. Serializing
|
|
75
|
+
* lets Turndown re-parse the markup with its own spec-compliant DOM.
|
|
76
|
+
*/
|
|
77
|
+
function serializeNode(node) {
|
|
78
|
+
if (typeof node === "string") return node;
|
|
79
|
+
if (node.nodeType === node.DOCUMENT_NODE) return node.documentElement?.outerHTML ?? "";
|
|
80
|
+
if (node.nodeType === node.ELEMENT_NODE) return node.outerHTML;
|
|
81
|
+
if (node.nodeType === node.TEXT_NODE) return escapeHtml$1(node.textContent ?? "");
|
|
82
|
+
return [...node.childNodes].map(serializeNode).join("");
|
|
83
|
+
}
|
|
84
|
+
function escapeHtml$1(value) {
|
|
85
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
86
|
+
}
|
|
87
|
+
// src/utils/html.ts
|
|
88
|
+
function parseHtmlDocument(html, transform) {
|
|
89
|
+
const document = parseHTML(wrapFragment(html)).document;
|
|
90
|
+
return Object.fromEntries(Object.entries(transform(document)).map(([key, value]) => {
|
|
91
|
+
if (value.type === "node") return [key, (Array.isArray(value.value) ? value.value : [value.value]).map((node) => new Turndown().convertSoup(node).split(/\n+/).map((line) => line.trim()).filter(Boolean).join("\n\n")).join("\n\n")];
|
|
92
|
+
return [key, value.value];
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* linkedom only parses complete documents: given a fragment (e.g. mammoth output or an RSS
|
|
97
|
+
* description), it promotes the first element to `documentElement` and drops its siblings
|
|
98
|
+
* from the tree, so wrap fragments in a full document first.
|
|
99
|
+
*/
|
|
100
|
+
function wrapFragment(html) {
|
|
101
|
+
if (/<html[\s>]/i.test(html)) return html;
|
|
102
|
+
if (/<(head|body)[\s>]/i.test(html)) return `<html>${html}</html>`;
|
|
103
|
+
return `<html><head></head><body>${html}</body></html>`;
|
|
104
|
+
}
|
|
105
|
+
// src/utils/mime.ts
|
|
106
|
+
const mime = new Mime(standardTypes, otherTypes);
|
|
107
|
+
function extensionFromMime(contentType) {
|
|
108
|
+
if (!contentType) return null;
|
|
109
|
+
return extensionNormalized(mime.getExtension(contentType));
|
|
72
110
|
}
|
|
73
|
-
|
|
74
|
-
|
|
111
|
+
async function extensionFromBuffer(buffer) {
|
|
112
|
+
const type = await fileTypeFromBuffer(buffer);
|
|
113
|
+
if (!type) return null;
|
|
114
|
+
return extensionNormalized(type.ext);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Sniffs text formats that have no magic number. Returns null for binary content, and `txt`
|
|
118
|
+
* for text that is not recognized as anything more specific.
|
|
119
|
+
*/
|
|
120
|
+
function extensionFromText(buffer) {
|
|
121
|
+
let text;
|
|
122
|
+
try {
|
|
123
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(buffer);
|
|
124
|
+
} catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
if (hasBinaryControlCharacters(text)) return null;
|
|
128
|
+
const start = text.trimStart().slice(0, 1024).toLowerCase();
|
|
129
|
+
if (start.startsWith("<")) {
|
|
130
|
+
if (/<rss[\s>]/.test(start)) return "rss";
|
|
131
|
+
if (/<feed[\s>]/.test(start)) return "atom";
|
|
132
|
+
if (/^<!doctype html|<(html|head|body)[\s>]/.test(start)) return "html";
|
|
133
|
+
if (start.startsWith("<?xml")) return "xml";
|
|
134
|
+
}
|
|
135
|
+
if (start.startsWith("{") && /"nbformat"\s*:/.test(text) && /"cells"\s*:/.test(text)) return "ipynb";
|
|
136
|
+
return "txt";
|
|
137
|
+
}
|
|
138
|
+
function hasBinaryControlCharacters(text) {
|
|
139
|
+
for (let index = 0; index < text.length; index++) {
|
|
140
|
+
const code = text.charCodeAt(index);
|
|
141
|
+
if (code < 32 && (code < 9 || code > 13) && code !== 27) return true;
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
function extensionFromPathname(pathname) {
|
|
146
|
+
const filename = pathname.split("/").pop() ?? "";
|
|
147
|
+
const dot = filename.lastIndexOf(".");
|
|
148
|
+
return dot > 0 ? extensionNormalized(filename.slice(dot)) : null;
|
|
149
|
+
}
|
|
150
|
+
/** Reads the filename from a Content-Disposition header, preferring the RFC 5987 form. */
|
|
151
|
+
function filenameFromDisposition(disposition) {
|
|
152
|
+
if (!disposition) return null;
|
|
153
|
+
const extended = disposition.match(/filename\*\s*=\s*[\w-]*'[^']*'([^;]+)/i)?.[1]?.trim();
|
|
154
|
+
if (extended) try {
|
|
155
|
+
return decodeURIComponent(extended);
|
|
156
|
+
} catch {
|
|
157
|
+
return extended;
|
|
158
|
+
}
|
|
159
|
+
const plain = disposition.match(/filename\s*=\s*(?:"([^"]*)"|([^;]+))/i);
|
|
160
|
+
return (plain?.[1] ?? plain?.[2])?.trim() || null;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Lists candidate extensions for the content, most reliable first: explicit extensions, then
|
|
164
|
+
* names, then declared MIME types, then the content itself.
|
|
165
|
+
*/
|
|
166
|
+
async function detectExtensions(buffer, { extension, filename, urls = [], contentType } = {}) {
|
|
167
|
+
const candidates = [
|
|
168
|
+
extensionNormalized(extension),
|
|
169
|
+
filename ? extensionFromPathname(filename) : null,
|
|
170
|
+
...urls.map((url) => url ? extensionFromUrl(url) : null),
|
|
171
|
+
extensionFromMime(contentType),
|
|
172
|
+
await extensionFromBuffer(buffer) ?? extensionFromText(buffer)
|
|
173
|
+
];
|
|
174
|
+
return [...new Set(candidates.filter((candidate) => !!candidate))];
|
|
175
|
+
}
|
|
176
|
+
function extensionFromUrl(url) {
|
|
177
|
+
try {
|
|
178
|
+
return extensionFromPathname(decodeURIComponent(new URL(url).pathname));
|
|
179
|
+
} catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function extensionNormalized(extension, withDot = false) {
|
|
184
|
+
if (!extension) return "";
|
|
185
|
+
const normalized = extension.trim().toLowerCase().replace(/^[.-]+/, "");
|
|
186
|
+
return withDot ? `.${normalized}` : normalized;
|
|
187
|
+
}
|
|
188
|
+
function isTextExtension(extension) {
|
|
189
|
+
const type = mime.getType(extension);
|
|
190
|
+
if (!type) return false;
|
|
191
|
+
return type.startsWith("text/");
|
|
192
|
+
}
|
|
193
|
+
// src/converters/bingserp.ts
|
|
75
194
|
const BING_SEARCH_URL = /^https:\/\/www\.bing\.com\/search\?q=/i;
|
|
76
195
|
const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
77
196
|
var BingSerpConverter = class {
|
|
78
197
|
async convert(source, options = {}) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (element.textContent) element.textContent += " ";
|
|
86
|
-
});
|
|
87
|
-
document.querySelectorAll(".algoSlug_icon").forEach((element) => {
|
|
88
|
-
element.remove();
|
|
89
|
-
});
|
|
90
|
-
const turndown = new CustomTurnDown();
|
|
91
|
-
const markdown = `## A Bing search for '${query}' found the following results:\n\n${[...document.querySelectorAll(".b_algo")].map((result) => {
|
|
92
|
-
result.querySelectorAll("a[href]").forEach((anchor) => {
|
|
93
|
-
const href = anchor.getAttribute("href");
|
|
94
|
-
if (!href) return;
|
|
95
|
-
try {
|
|
96
|
-
const encoded = new URL(href, url).searchParams.get("u");
|
|
97
|
-
if (encoded) anchor.setAttribute("href", decodeBingUrl(encoded));
|
|
98
|
-
} catch {}
|
|
198
|
+
if (options.extension !== "html" && options.extension !== "htm" || !BING_SEARCH_URL.test(options.url ?? "")) return null;
|
|
199
|
+
let query = new URL(options.url ?? "").searchParams.get("q") || null;
|
|
200
|
+
if (query) query = `Search results for "${query}"`;
|
|
201
|
+
return parseHtmlDocument(decodeText(source), (document) => {
|
|
202
|
+
document.querySelectorAll(".tptt").forEach((element) => {
|
|
203
|
+
if (element.textContent) element.textContent += " ";
|
|
99
204
|
});
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
205
|
+
document.querySelectorAll(".algoSlug_icon").forEach((element) => {
|
|
206
|
+
element.remove();
|
|
207
|
+
});
|
|
208
|
+
return {
|
|
209
|
+
title: {
|
|
210
|
+
type: "metadata",
|
|
211
|
+
value: query || document.title || null
|
|
212
|
+
},
|
|
213
|
+
markdown: {
|
|
214
|
+
type: "node",
|
|
215
|
+
value: [...document.querySelectorAll(".b_algo")].map((result) => {
|
|
216
|
+
result.querySelectorAll("a[href]").forEach((anchor) => {
|
|
217
|
+
const href = anchor.getAttribute("href");
|
|
218
|
+
if (!href) return;
|
|
219
|
+
try {
|
|
220
|
+
const encoded = new URL(href, options.url ?? "").searchParams.get("u");
|
|
221
|
+
if (encoded) anchor.setAttribute("href", decodeBingUrl(encoded));
|
|
222
|
+
} catch {}
|
|
223
|
+
});
|
|
224
|
+
return result;
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
});
|
|
107
229
|
}
|
|
108
230
|
};
|
|
109
231
|
function decodeBingUrl(encoded) {
|
|
@@ -123,52 +245,46 @@ function decodeBingUrl(encoded) {
|
|
|
123
245
|
}
|
|
124
246
|
return new TextDecoder().decode(new Uint8Array(bytes));
|
|
125
247
|
}
|
|
126
|
-
|
|
127
|
-
//#region src/converters/html.ts
|
|
248
|
+
// src/converters/html.ts
|
|
128
249
|
var HtmlConverter = class {
|
|
129
250
|
async convert(source, options) {
|
|
130
|
-
|
|
131
|
-
if (extension !== ".html" && extension !== ".htm") return null;
|
|
251
|
+
if (options.extension !== "html" && options.extension !== "htm") return null;
|
|
132
252
|
return this.convertHtml(decodeText(source));
|
|
133
253
|
}
|
|
134
254
|
async convertHtml(html) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
255
|
+
return parseHtmlDocument(html, (document) => {
|
|
256
|
+
const title = document.title || null;
|
|
257
|
+
document.querySelectorAll("script, style, title").forEach((element) => {
|
|
258
|
+
element.remove();
|
|
259
|
+
});
|
|
260
|
+
return {
|
|
261
|
+
title: {
|
|
262
|
+
type: "metadata",
|
|
263
|
+
value: title
|
|
264
|
+
},
|
|
265
|
+
markdown: {
|
|
266
|
+
type: "node",
|
|
267
|
+
value: document
|
|
268
|
+
}
|
|
269
|
+
};
|
|
139
270
|
});
|
|
140
|
-
const markdown = new CustomTurnDown().convertSoup(document);
|
|
141
|
-
return {
|
|
142
|
-
title: title || null,
|
|
143
|
-
markdown,
|
|
144
|
-
text_content: markdown
|
|
145
|
-
};
|
|
146
271
|
}
|
|
147
272
|
};
|
|
148
|
-
|
|
149
|
-
//#region src/converters/docx.ts
|
|
273
|
+
// src/converters/docx.ts
|
|
150
274
|
var DocxConverter = class extends HtmlConverter {
|
|
151
275
|
async convert(source, options) {
|
|
152
|
-
if (options.
|
|
276
|
+
if (options.extension !== "docx") return null;
|
|
153
277
|
let mammothInput = { arrayBuffer: toArrayBuffer(source) };
|
|
154
278
|
if (typeof process !== "undefined" && process.versions !== null && process.versions.node !== null) mammothInput = { buffer: Buffer.from(source) };
|
|
155
|
-
const mammothOptions = {
|
|
156
|
-
styleMap: options.styleMap,
|
|
157
|
-
includeEmbeddedStyleMap: options.includeEmbeddedStyleMap,
|
|
158
|
-
includeDefaultStyleMap: options.includeDefaultStyleMap,
|
|
159
|
-
ignoreEmptyParagraphs: options.ignoreEmptyParagraphs,
|
|
160
|
-
idPrefix: options.idPrefix
|
|
161
|
-
};
|
|
162
|
-
if (options.transformDocument) mammothOptions.transformDocument = options.transformDocument;
|
|
279
|
+
const mammothOptions = Object.fromEntries(Object.entries(options.mammoth ?? {}).flatMap(([key, value]) => value !== void 0 ? [[key, value]] : []));
|
|
163
280
|
const html = await mammoth.convertToHtml(mammothInput, mammothOptions);
|
|
164
281
|
return this.convertHtml(html.value);
|
|
165
282
|
}
|
|
166
283
|
};
|
|
167
|
-
|
|
168
|
-
//#region src/converters/ipynb.ts
|
|
284
|
+
// src/converters/ipynb.ts
|
|
169
285
|
var IpynbConverter = class {
|
|
170
286
|
async convert(source, options = {}) {
|
|
171
|
-
if (options.
|
|
287
|
+
if (options.extension !== "ipynb") return null;
|
|
172
288
|
const notebook = JSON.parse(decodeText(source));
|
|
173
289
|
const output = [];
|
|
174
290
|
let title = notebook.metadata?.title ?? null;
|
|
@@ -183,267 +299,290 @@ var IpynbConverter = class {
|
|
|
183
299
|
const markdown = output.join("\n\n");
|
|
184
300
|
return {
|
|
185
301
|
title,
|
|
186
|
-
markdown
|
|
187
|
-
text_content: markdown
|
|
302
|
+
markdown
|
|
188
303
|
};
|
|
189
304
|
}
|
|
190
305
|
};
|
|
191
|
-
|
|
192
|
-
//#region src/converters/pdf.ts
|
|
306
|
+
// src/converters/pdf.ts
|
|
193
307
|
var PdfConverter = class {
|
|
194
308
|
async convert(source, options) {
|
|
195
|
-
if (options.
|
|
309
|
+
if (options.extension !== "pdf") return null;
|
|
196
310
|
const pdf = await getDocumentProxy(source.slice());
|
|
197
311
|
try {
|
|
198
312
|
const [{ text }, { info }] = await Promise.all([extractText(pdf, { mergePages: true }), getMeta(pdf)]);
|
|
199
313
|
return {
|
|
200
314
|
title: typeof info.Title === "string" && info.Title.trim() ? info.Title : null,
|
|
201
|
-
markdown: text
|
|
202
|
-
text_content: text
|
|
315
|
+
markdown: text
|
|
203
316
|
};
|
|
204
317
|
} finally {
|
|
205
318
|
await pdf.loadingTask.destroy();
|
|
206
319
|
}
|
|
207
320
|
}
|
|
208
321
|
};
|
|
209
|
-
|
|
210
|
-
//#region src/mime.ts
|
|
211
|
-
const MIME_EXTENSIONS = {
|
|
212
|
-
"application/atom+xml": ".atom",
|
|
213
|
-
"application/json": ".json",
|
|
214
|
-
"application/pdf": ".pdf",
|
|
215
|
-
"application/zip": ".zip",
|
|
216
|
-
"application/rss+xml": ".rss",
|
|
217
|
-
"application/x-pdf": ".pdf",
|
|
218
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
|
|
219
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
|
|
220
|
-
"application/xml": ".xml",
|
|
221
|
-
"text/csv": ".csv",
|
|
222
|
-
"text/html": ".html",
|
|
223
|
-
"text/markdown": ".md",
|
|
224
|
-
"text/plain": ".txt",
|
|
225
|
-
"text/xml": ".xml"
|
|
226
|
-
};
|
|
227
|
-
const TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
228
|
-
".csv",
|
|
229
|
-
".json",
|
|
230
|
-
".log",
|
|
231
|
-
".markdown",
|
|
232
|
-
".md",
|
|
233
|
-
".text",
|
|
234
|
-
".txt"
|
|
235
|
-
]);
|
|
236
|
-
function extensionFromMime(contentType) {
|
|
237
|
-
if (!contentType) return null;
|
|
238
|
-
const mime = contentType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
239
|
-
return MIME_EXTENSIONS[mime] ?? (mime.startsWith("text/") ? ".txt" : null);
|
|
240
|
-
}
|
|
241
|
-
function isTextExtension(extension) {
|
|
242
|
-
return TEXT_EXTENSIONS.has(extension.toLowerCase());
|
|
243
|
-
}
|
|
244
|
-
function extensionFromPathname(pathname) {
|
|
245
|
-
const filename = pathname.split("/").pop() ?? "";
|
|
246
|
-
const dot = filename.lastIndexOf(".");
|
|
247
|
-
return dot > 0 ? filename.slice(dot).toLowerCase() : null;
|
|
248
|
-
}
|
|
249
|
-
//#endregion
|
|
250
|
-
//#region src/converters/plain-text.ts
|
|
322
|
+
// src/converters/plain-text.ts
|
|
251
323
|
var PlainTextConverter = class {
|
|
252
324
|
async convert(source, options = {}) {
|
|
253
|
-
if (!isTextExtension(options.
|
|
254
|
-
const content = decodeText(source);
|
|
325
|
+
if (!isTextExtension(options.extension ?? "")) return null;
|
|
255
326
|
return {
|
|
256
327
|
title: null,
|
|
257
|
-
markdown:
|
|
258
|
-
text_content: content
|
|
328
|
+
markdown: decodeText(source)
|
|
259
329
|
};
|
|
260
330
|
}
|
|
261
331
|
};
|
|
262
|
-
|
|
263
|
-
|
|
332
|
+
// src/converters/pptx.ts
|
|
333
|
+
var PptxConverter = class extends HtmlConverter {
|
|
334
|
+
async convert(source, options) {
|
|
335
|
+
if (options.extension !== "pptx") return null;
|
|
336
|
+
const slides = await pptxToHtml(toArrayBuffer(source), { domParserFactory: createPptxDomParser });
|
|
337
|
+
const sections = [];
|
|
338
|
+
for (const [index, html] of slides.entries()) {
|
|
339
|
+
const result = await this.convertHtml(`<html><body>${html}</body></html>`);
|
|
340
|
+
sections.push(`<!-- Slide number: ${index + 1} -->\n${result?.markdown.trim() ?? ""}`);
|
|
341
|
+
}
|
|
342
|
+
return {
|
|
343
|
+
title: null,
|
|
344
|
+
markdown: sections.join("\n\n")
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
function createPptxDomParser() {
|
|
349
|
+
if (typeof globalThis.DOMParser === "function") return new globalThis.DOMParser();
|
|
350
|
+
patchXmlDom();
|
|
351
|
+
return new DOMParser();
|
|
352
|
+
}
|
|
353
|
+
let xmlDomPatched = false;
|
|
354
|
+
function patchXmlDom() {
|
|
355
|
+
if (xmlDomPatched) return;
|
|
356
|
+
xmlDomPatched = true;
|
|
357
|
+
defineMethod(Element.prototype, "querySelector", querySelector);
|
|
358
|
+
defineMethod(Element.prototype, "querySelectorAll", querySelectorAll);
|
|
359
|
+
defineMethod(Document.prototype, "querySelector", querySelector);
|
|
360
|
+
defineMethod(Document.prototype, "querySelectorAll", querySelectorAll);
|
|
361
|
+
defineGetter(Element.prototype, "children", function() {
|
|
362
|
+
return [...this.childNodes].filter((node) => node.nodeType === 1);
|
|
363
|
+
});
|
|
364
|
+
defineGetter(Node.prototype, "parentElement", function() {
|
|
365
|
+
const parent = this.parentNode;
|
|
366
|
+
return parent?.nodeType === 1 ? parent : null;
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
function querySelector(selector) {
|
|
370
|
+
for (const group of selector.split(",")) {
|
|
371
|
+
const match = queryByPath(this, group.trim());
|
|
372
|
+
if (match) return match;
|
|
373
|
+
}
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
function querySelectorAll() {
|
|
377
|
+
return [];
|
|
378
|
+
}
|
|
379
|
+
function queryByPath(root, selector) {
|
|
380
|
+
const tokens = selector.split(/\s+/).filter(Boolean);
|
|
381
|
+
let current = [root];
|
|
382
|
+
for (const token of tokens) {
|
|
383
|
+
const localName = selectorLocalName(token);
|
|
384
|
+
const next = [];
|
|
385
|
+
for (const node of current) next.push(...Array.from(node.getElementsByTagNameNS("*", localName)));
|
|
386
|
+
current = next;
|
|
387
|
+
}
|
|
388
|
+
return current[0] ?? null;
|
|
389
|
+
}
|
|
390
|
+
function selectorLocalName(token) {
|
|
391
|
+
if (token.includes("|")) return token.slice(token.lastIndexOf("|") + 1);
|
|
392
|
+
return token.replace(/\\:/g, ":").split(":").pop() ?? token;
|
|
393
|
+
}
|
|
394
|
+
function defineMethod(target, name, value) {
|
|
395
|
+
Object.defineProperty(target, name, {
|
|
396
|
+
configurable: true,
|
|
397
|
+
writable: true,
|
|
398
|
+
value
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
function defineGetter(target, name, get) {
|
|
402
|
+
if (Object.getOwnPropertyDescriptor(target, name)) return;
|
|
403
|
+
Object.defineProperty(target, name, {
|
|
404
|
+
configurable: true,
|
|
405
|
+
get
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
// src/converters/wikipedia.ts
|
|
264
409
|
const WIKIPEDIA_URL = /^https?:\/\/[a-z]{2,3}\.wikipedia\.org\//i;
|
|
265
410
|
var WikipediaConverter = class {
|
|
266
411
|
async convert(source, options = {}) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
412
|
+
if (options.extension !== "html" && options.extension !== "htm" || !WIKIPEDIA_URL.test(options.url ?? "")) return null;
|
|
413
|
+
return parseHtmlDocument(decodeText(source), (document) => {
|
|
414
|
+
document.querySelectorAll("script, style").forEach((element) => {
|
|
415
|
+
element.remove();
|
|
416
|
+
});
|
|
417
|
+
const body = document.querySelector("div#mw-content-text");
|
|
418
|
+
return {
|
|
419
|
+
title: {
|
|
420
|
+
type: "metadata",
|
|
421
|
+
value: document.querySelector("span.mw-page-title-main")?.textContent?.trim() || document.title || null
|
|
422
|
+
},
|
|
423
|
+
markdown: {
|
|
424
|
+
type: "node",
|
|
425
|
+
value: body || document
|
|
426
|
+
}
|
|
427
|
+
};
|
|
272
428
|
});
|
|
273
|
-
const body = document.querySelector("div#mw-content-text");
|
|
274
|
-
const title = document.querySelector("span.mw-page-title-main")?.textContent?.trim() || document.title || null;
|
|
275
|
-
const markdown = body ? `# ${title ?? "Wikipedia"}\n\n${new CustomTurnDown().convertSoup(body)}` : new CustomTurnDown().convertSoup(document);
|
|
276
|
-
return {
|
|
277
|
-
title,
|
|
278
|
-
markdown,
|
|
279
|
-
text_content: markdown
|
|
280
|
-
};
|
|
281
429
|
}
|
|
282
430
|
};
|
|
283
|
-
|
|
284
|
-
//#region src/converters/xlsx.ts
|
|
285
|
-
const { Workbook } = ExcelJS;
|
|
431
|
+
// src/converters/xlsx.ts
|
|
286
432
|
var XlsxConverter = class extends HtmlConverter {
|
|
287
433
|
async convert(source, options) {
|
|
288
|
-
if (options.
|
|
289
|
-
const
|
|
290
|
-
await workbook.xlsx.load(toArrayBuffer(source));
|
|
434
|
+
if (options.extension !== "xlsx") return null;
|
|
435
|
+
const sheets = await readExcelFile(toArrayBuffer(source));
|
|
291
436
|
const sections = [];
|
|
292
|
-
for (const
|
|
293
|
-
const html =
|
|
437
|
+
for (const { sheet, data } of sheets) {
|
|
438
|
+
const html = sheetToHtml(data);
|
|
294
439
|
if (!html) continue;
|
|
295
440
|
const result = await this.convertHtml(html);
|
|
296
|
-
sections.push(`## ${
|
|
441
|
+
sections.push(`## ${sheet}\n${result?.markdown.trim() ?? ""}`);
|
|
297
442
|
}
|
|
298
|
-
const markdown = sections.join("\n\n");
|
|
299
443
|
return {
|
|
300
|
-
title:
|
|
301
|
-
markdown
|
|
302
|
-
text_content: markdown
|
|
444
|
+
title: null,
|
|
445
|
+
markdown: sections.join("\n\n")
|
|
303
446
|
};
|
|
304
447
|
}
|
|
305
448
|
};
|
|
306
|
-
function
|
|
307
|
-
const columnCount =
|
|
308
|
-
if (!columnCount ||
|
|
309
|
-
|
|
310
|
-
worksheet.eachRow({ includeEmpty: true }, (row) => {
|
|
449
|
+
function sheetToHtml(rows) {
|
|
450
|
+
const columnCount = rows.reduce((max, row) => Math.max(max, row.length), 0);
|
|
451
|
+
if (!columnCount || rows.length === 0) return "";
|
|
452
|
+
return `<html><body><table>${rows.map((row) => {
|
|
311
453
|
const cells = [];
|
|
312
|
-
for (let column =
|
|
313
|
-
|
|
314
|
-
})
|
|
315
|
-
return `<html><body><table>${rows.join("")}</table></body></html>`;
|
|
454
|
+
for (let column = 0; column < columnCount; column++) cells.push(`<td>${escapeHtml(cellText(row[column]))}</td>`);
|
|
455
|
+
return `<tr>${cells.join("")}</tr>`;
|
|
456
|
+
}).join("")}</table></body></html>`;
|
|
316
457
|
}
|
|
317
|
-
function cellText(
|
|
318
|
-
if (
|
|
319
|
-
return
|
|
458
|
+
function cellText(value) {
|
|
459
|
+
if (value == null) return "";
|
|
460
|
+
if (value instanceof Date) return value.toISOString();
|
|
461
|
+
return String(value);
|
|
320
462
|
}
|
|
321
463
|
function escapeHtml(value) {
|
|
322
464
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
323
465
|
}
|
|
324
|
-
|
|
325
|
-
//#region src/converters/xml-rss-atom.ts
|
|
466
|
+
// src/converters/xml-rss-atom.ts
|
|
326
467
|
var RSSConverter = class {
|
|
327
468
|
async convert(source, options = {}) {
|
|
328
|
-
|
|
329
|
-
if (![
|
|
330
|
-
".xml",
|
|
331
|
-
".rss",
|
|
332
|
-
".atom"
|
|
333
|
-
].includes(extension)) return null;
|
|
469
|
+
if (options.extension !== "xml" && options.extension !== "rss" && options.extension !== "atom") return null;
|
|
334
470
|
const document = new DOMParser().parseFromString(decodeText(source), "text/xml");
|
|
335
|
-
if (document.getElementsByTagName("rss").length > 0) return
|
|
336
|
-
if (document.getElementsByTagName("feed")[0]?.getElementsByTagName("entry").length) return
|
|
471
|
+
if (document.getElementsByTagName("rss").length > 0) return parseRss(document);
|
|
472
|
+
if (document.getElementsByTagName("feed")[0]?.getElementsByTagName("entry").length) return parseAtom(document);
|
|
337
473
|
return null;
|
|
338
474
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
markdown,
|
|
362
|
-
text_content: markdown
|
|
363
|
-
};
|
|
475
|
+
};
|
|
476
|
+
function parseAtom(document) {
|
|
477
|
+
const root = document.getElementsByTagName("feed")[0];
|
|
478
|
+
if (!root) return null;
|
|
479
|
+
const title = getText(root, "title");
|
|
480
|
+
const subtitle = getText(root, "subtitle");
|
|
481
|
+
const sections = [`# ${title ?? "Feed"}`];
|
|
482
|
+
if (subtitle) sections.push(subtitle);
|
|
483
|
+
for (const entry of [...root.getElementsByTagName("entry")]) {
|
|
484
|
+
const entryTitle = getText(entry, "title");
|
|
485
|
+
const updated = getText(entry, "updated");
|
|
486
|
+
const summary = getText(entry, "summary");
|
|
487
|
+
const content = getText(entry, "content");
|
|
488
|
+
const lines = [];
|
|
489
|
+
if (entryTitle) lines.push(`## ${entryTitle}`);
|
|
490
|
+
if (updated) lines.push(`Updated on: ${updated}`);
|
|
491
|
+
const node = summary || content;
|
|
492
|
+
if (node) lines.push(parseHtmlDocument(node, (document) => ({ markdown: {
|
|
493
|
+
type: "node",
|
|
494
|
+
value: document.documentElement
|
|
495
|
+
} })).markdown);
|
|
496
|
+
sections.push(lines.join("\n\n"));
|
|
364
497
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
};
|
|
498
|
+
return {
|
|
499
|
+
title,
|
|
500
|
+
markdown: sections.join("\n\n")
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function parseRss(document) {
|
|
504
|
+
const channel = document.getElementsByTagName("rss")[0]?.getElementsByTagName("channel")[0];
|
|
505
|
+
if (!channel) return null;
|
|
506
|
+
const title = getText(channel, "title");
|
|
507
|
+
const description = getText(channel, "description");
|
|
508
|
+
const sections = title ? [`# ${title}`] : [];
|
|
509
|
+
if (description) sections.push(description);
|
|
510
|
+
for (const item of [...channel.getElementsByTagName("item")]) {
|
|
511
|
+
const itemTitle = getText(item, "title");
|
|
512
|
+
const published = getText(item, "pubDate");
|
|
513
|
+
const itemDescription = getText(item, "description");
|
|
514
|
+
const content = getText(item, "content:encoded");
|
|
515
|
+
const lines = [];
|
|
516
|
+
if (itemTitle) lines.push(`## ${itemTitle}`);
|
|
517
|
+
if (published) lines.push(`Published on: ${published}`);
|
|
518
|
+
const node = itemDescription || content;
|
|
519
|
+
if (node) lines.push(parseHtmlDocument(node, (document) => ({ markdown: {
|
|
520
|
+
type: "node",
|
|
521
|
+
value: document.documentElement
|
|
522
|
+
} })).markdown);
|
|
523
|
+
sections.push(lines.join("\n\n"));
|
|
390
524
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
525
|
+
return {
|
|
526
|
+
title,
|
|
527
|
+
markdown: sections.join("\n\n")
|
|
528
|
+
};
|
|
394
529
|
}
|
|
395
530
|
function getText(element, tagName) {
|
|
396
531
|
return element.getElementsByTagName(tagName)[0]?.textContent?.trim() || null;
|
|
397
532
|
}
|
|
398
|
-
|
|
399
|
-
|
|
533
|
+
// src/converters/youtube.ts
|
|
534
|
+
const YOUTUBE_URL = /^https?:\/\/(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=|shorts\/)?([a-zA-Z0-9_-]+)/i;
|
|
400
535
|
var YouTubeConverter = class {
|
|
401
536
|
async convert(source, options = {}) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
537
|
+
if (options.extension !== "html" && options.extension !== "htm" || !YOUTUBE_URL.test(options.url ?? "")) return null;
|
|
538
|
+
return parseHtmlDocument(decodeText(source), (document) => {
|
|
539
|
+
const metadata = {};
|
|
540
|
+
document.querySelectorAll("meta").forEach((meta) => {
|
|
541
|
+
const content = meta.getAttribute("content");
|
|
542
|
+
const key = meta.getAttribute("itemprop") ?? meta.getAttribute("property") ?? meta.getAttribute("name");
|
|
543
|
+
if (key && content) metadata[key] = content;
|
|
544
|
+
});
|
|
545
|
+
try {
|
|
546
|
+
for (const script of document.querySelectorAll("script")) {
|
|
547
|
+
const content = script.textContent ?? "";
|
|
548
|
+
if (!content.includes("ytInitialData")) continue;
|
|
549
|
+
const start = content.indexOf("{");
|
|
550
|
+
const end = content.lastIndexOf("}");
|
|
551
|
+
if (start >= 0 && end > start) {
|
|
552
|
+
const description = findKey(JSON.parse(content.slice(start, end + 1)), "attributedDescriptionBodyText");
|
|
553
|
+
if (isRecord(description) && typeof description.content === "string") metadata.description = description.content;
|
|
554
|
+
}
|
|
555
|
+
break;
|
|
420
556
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
"
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
557
|
+
} catch {}
|
|
558
|
+
const title = first(metadata, [
|
|
559
|
+
"title",
|
|
560
|
+
"og:title",
|
|
561
|
+
"name"
|
|
562
|
+
]) || document.title || null;
|
|
563
|
+
const lines = ["# YouTube"];
|
|
564
|
+
if (title) lines.push(`## ${title}`);
|
|
565
|
+
const stats = [];
|
|
566
|
+
const views = first(metadata, ["interactionCount"]);
|
|
567
|
+
const keywords = first(metadata, ["keywords"]);
|
|
568
|
+
const runtime = first(metadata, ["duration"]);
|
|
569
|
+
if (views) stats.push(`- **Views:** ${views}`);
|
|
570
|
+
if (keywords) stats.push(`- **Keywords:** ${keywords}`);
|
|
571
|
+
if (runtime) stats.push(`- **Runtime:** ${runtime}`);
|
|
572
|
+
if (stats.length > 0) lines.push(`### Video Metadata\n\n${stats.join("\n")}`);
|
|
573
|
+
const description = first(metadata, ["description", "og:description"]);
|
|
574
|
+
if (description) lines.push(`### Description\n\n${description}`);
|
|
575
|
+
return {
|
|
576
|
+
title: {
|
|
577
|
+
type: "metadata",
|
|
578
|
+
value: title
|
|
579
|
+
},
|
|
580
|
+
markdown: {
|
|
581
|
+
type: "metadata",
|
|
582
|
+
value: lines.join("\n\n")
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
});
|
|
447
586
|
}
|
|
448
587
|
};
|
|
449
588
|
function first(metadata, keys) {
|
|
@@ -466,23 +605,20 @@ function findKey(value, key) {
|
|
|
466
605
|
function isRecord(value) {
|
|
467
606
|
return typeof value === "object" && value !== null;
|
|
468
607
|
}
|
|
469
|
-
|
|
470
|
-
//#region src/converters/zip.ts
|
|
608
|
+
// src/converters/zip.ts
|
|
471
609
|
var ZipConverter = class {
|
|
472
610
|
async convert(source, options = {}) {
|
|
473
|
-
if (options.
|
|
611
|
+
if (options.extension !== "zip") return null;
|
|
474
612
|
const files = unzipSync(source, { filter: (file) => !file.name.endsWith("/") });
|
|
475
|
-
const converters = options.
|
|
613
|
+
const converters = options._converters ?? [];
|
|
476
614
|
const sections = [`Content from the zip file \`${options.url ?? "archive.zip"}\`:`];
|
|
477
615
|
for (const [name, bytes] of Object.entries(files)) {
|
|
478
616
|
const result = await convertEntry(bytes, name, converters, options);
|
|
479
617
|
if (result) sections.push(`## File: ${name}\n\n${result.markdown}`);
|
|
480
618
|
}
|
|
481
|
-
const markdown = sections.join("\n\n");
|
|
482
619
|
return {
|
|
483
620
|
title: null,
|
|
484
|
-
markdown
|
|
485
|
-
text_content: markdown
|
|
621
|
+
markdown: sections.join("\n\n")
|
|
486
622
|
};
|
|
487
623
|
}
|
|
488
624
|
};
|
|
@@ -491,9 +627,9 @@ async function convertEntry(source, name, converters, options) {
|
|
|
491
627
|
if (!extension) return null;
|
|
492
628
|
const entryOptions = {
|
|
493
629
|
...options,
|
|
494
|
-
|
|
630
|
+
extension,
|
|
495
631
|
url: void 0,
|
|
496
|
-
|
|
632
|
+
_converters: converters
|
|
497
633
|
};
|
|
498
634
|
for (const converter of converters) try {
|
|
499
635
|
const result = await converter.convert(source, entryOptions);
|
|
@@ -501,41 +637,72 @@ async function convertEntry(source, name, converters, options) {
|
|
|
501
637
|
} catch {}
|
|
502
638
|
return null;
|
|
503
639
|
}
|
|
504
|
-
|
|
505
|
-
|
|
640
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/typeof.js
|
|
641
|
+
function _typeof(o) {
|
|
642
|
+
"@babel/helpers - typeof";
|
|
643
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
644
|
+
return typeof o;
|
|
645
|
+
} : function(o) {
|
|
646
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
647
|
+
}, _typeof(o);
|
|
648
|
+
}
|
|
649
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/toPrimitive.js
|
|
650
|
+
function toPrimitive(t, r) {
|
|
651
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
652
|
+
var e = t[Symbol.toPrimitive];
|
|
653
|
+
if (void 0 !== e) {
|
|
654
|
+
var i = e.call(t, r || "default");
|
|
655
|
+
if ("object" != _typeof(i)) return i;
|
|
656
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
657
|
+
}
|
|
658
|
+
return ("string" === r ? String : Number)(t);
|
|
659
|
+
}
|
|
660
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/toPropertyKey.js
|
|
661
|
+
function toPropertyKey(t) {
|
|
662
|
+
var i = toPrimitive(t, "string");
|
|
663
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
664
|
+
}
|
|
665
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/defineProperty.js
|
|
666
|
+
function _defineProperty(e, r, t) {
|
|
667
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
668
|
+
value: t,
|
|
669
|
+
enumerable: !0,
|
|
670
|
+
configurable: !0,
|
|
671
|
+
writable: !0
|
|
672
|
+
}) : e[r] = t, e;
|
|
673
|
+
}
|
|
674
|
+
// src/markitdown.ts
|
|
506
675
|
var MarkItDown = class {
|
|
507
676
|
constructor() {
|
|
508
|
-
this
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
677
|
+
_defineProperty(this, "converters", []);
|
|
678
|
+
const ordered = [
|
|
679
|
+
PlainTextConverter,
|
|
680
|
+
HtmlConverter,
|
|
681
|
+
ZipConverter,
|
|
682
|
+
RSSConverter,
|
|
683
|
+
WikipediaConverter,
|
|
684
|
+
YouTubeConverter,
|
|
685
|
+
BingSerpConverter,
|
|
686
|
+
DocxConverter,
|
|
687
|
+
XlsxConverter,
|
|
688
|
+
PptxConverter,
|
|
689
|
+
PdfConverter,
|
|
690
|
+
IpynbConverter
|
|
691
|
+
];
|
|
692
|
+
for (const converter of ordered) this.registerConverter(new converter());
|
|
520
693
|
}
|
|
521
694
|
/** Convert a URL, response, blob, array buffer, data view, or typed array. */
|
|
522
695
|
async convert(source, options = {}) {
|
|
523
696
|
if (typeof source === "string" || source instanceof URL) return this.convertUrl(source.toString(), options);
|
|
524
697
|
if (isResponse(source)) return this.convertResponse(source, options);
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
return this.convertBytes(
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
|
-
* @deprecated Pass the byte source directly to `convert`.
|
|
536
|
-
*/
|
|
537
|
-
async convertBuffer(source, options) {
|
|
538
|
-
return this.convert(source, options);
|
|
698
|
+
const bytes = await toUint8Array(source);
|
|
699
|
+
const extensions = await detectExtensions(bytes, {
|
|
700
|
+
extension: options.extension,
|
|
701
|
+
filename: isBlob(source) && "name" in source ? String(source.name) : null,
|
|
702
|
+
urls: [options.url],
|
|
703
|
+
contentType: isBlob(source) ? source.type : null
|
|
704
|
+
});
|
|
705
|
+
return this.convertBytes(bytes, extensions, options);
|
|
539
706
|
}
|
|
540
707
|
async convertUrl(source, { fetch: fetchImplementation = globalThis.fetch, ...options }) {
|
|
541
708
|
const url = new URL(source);
|
|
@@ -549,44 +716,39 @@ var MarkItDown = class {
|
|
|
549
716
|
});
|
|
550
717
|
}
|
|
551
718
|
async convertResponse(response, options) {
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
return this.convertBytes(new Uint8Array(await response.arrayBuffer()), extensions, {
|
|
719
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
720
|
+
const extensions = await detectExtensions(bytes, {
|
|
721
|
+
extension: options.extension,
|
|
722
|
+
filename: filenameFromDisposition(response.headers.get("content-disposition")),
|
|
723
|
+
urls: [options.url, response.url],
|
|
724
|
+
contentType: response.headers.get("content-type")
|
|
725
|
+
});
|
|
726
|
+
return this.convertBytes(bytes, extensions, {
|
|
561
727
|
...options,
|
|
562
|
-
url:
|
|
728
|
+
url: options.url || response.url || void 0
|
|
563
729
|
});
|
|
564
730
|
}
|
|
565
731
|
async convertBytes(source, extensions, options) {
|
|
732
|
+
if (extensions.length === 0) throw new Error("Could not determine the file type. Provide `extension` for better results.");
|
|
566
733
|
let lastError;
|
|
567
734
|
for (const extension of extensions) for (const converter of this.converters) try {
|
|
568
735
|
const result = await converter.convert(source, {
|
|
569
736
|
...options,
|
|
570
|
-
|
|
571
|
-
|
|
737
|
+
extension,
|
|
738
|
+
_converters: this.converters
|
|
572
739
|
});
|
|
573
740
|
if (result) {
|
|
574
741
|
result.markdown = result.markdown.replace(/\r\n|\r|\n/g, "\n").trim().replace(/\n{3,}/g, "\n\n");
|
|
575
|
-
result.text_content = result.markdown;
|
|
576
742
|
return result;
|
|
577
743
|
}
|
|
578
744
|
} catch (error) {
|
|
579
745
|
if (options.debug) console.error("Converter threw error:", error);
|
|
580
746
|
lastError = error;
|
|
581
747
|
}
|
|
582
|
-
const formats =
|
|
748
|
+
const formats = extensions.join(", ");
|
|
583
749
|
if (lastError) throw new Error(`Could not convert ${formats} to Markdown: ${String(lastError)}`);
|
|
584
750
|
throw new Error(`Unsupported file type: ${formats}.`);
|
|
585
751
|
}
|
|
586
|
-
addExtension(extensions, extension) {
|
|
587
|
-
if (!extension) return;
|
|
588
|
-
extensions.add(extension.startsWith(".") ? extension.toLowerCase() : `.${extension.toLowerCase()}`);
|
|
589
|
-
}
|
|
590
752
|
registerConverter(converter) {
|
|
591
753
|
this.converters.unshift(converter);
|
|
592
754
|
}
|
|
@@ -594,7 +756,6 @@ var MarkItDown = class {
|
|
|
594
756
|
function isResponse(value) {
|
|
595
757
|
return typeof Response !== "undefined" && value instanceof Response;
|
|
596
758
|
}
|
|
597
|
-
//#endregion
|
|
598
759
|
export { MarkItDown };
|
|
599
760
|
|
|
600
761
|
//# sourceMappingURL=index.js.map
|