@ryangarber/markitdown-ts 0.1.5 → 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 +21 -10
- package/dist/cli.mjs +423 -328
- package/dist/index.cjs +788 -645
- 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 +398 -316
- package/dist/index.js.map +1 -1
- package/dist/markitdown.1 +30 -0
- package/package.json +39 -21
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +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
10
|
import { pptxToHtml } from "@jvmr/pptx-to-html";
|
|
7
11
|
import { DOMParser, Document, Element, Node } from "@xmldom/xmldom";
|
|
8
12
|
import readExcelFile from "read-excel-file/universal";
|
|
9
13
|
import { unzipSync } from "fflate";
|
|
10
|
-
|
|
14
|
+
// src/utils/bytes.ts
|
|
11
15
|
async function toUint8Array(source) {
|
|
12
16
|
if (source instanceof Uint8Array) return source;
|
|
13
17
|
if (isBlob(source)) return new Uint8Array(await source.arrayBuffer());
|
|
@@ -23,12 +27,11 @@ function decodeText(source) {
|
|
|
23
27
|
function isBlob(value) {
|
|
24
28
|
return typeof Blob !== "undefined" && value instanceof Blob;
|
|
25
29
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
var CustomTurnDown = class {
|
|
30
|
+
// src/utils/turndown.ts
|
|
31
|
+
var Turndown = class {
|
|
29
32
|
convertSoup(document) {
|
|
30
33
|
const service = new TurndownService({ headingStyle: "atx" });
|
|
31
|
-
service.use(
|
|
34
|
+
service.use(TurndownPluginGfm);
|
|
32
35
|
service.addRule("anchor tags", {
|
|
33
36
|
filter: ["a"],
|
|
34
37
|
replacement(content, node) {
|
|
@@ -63,48 +66,166 @@ var CustomTurnDown = class {
|
|
|
63
66
|
return ``;
|
|
64
67
|
}
|
|
65
68
|
});
|
|
66
|
-
return service.turndown(document);
|
|
69
|
+
return service.turndown(serializeNode(document));
|
|
67
70
|
}
|
|
68
71
|
};
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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));
|
|
110
|
+
}
|
|
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/");
|
|
73
192
|
}
|
|
74
|
-
|
|
75
|
-
//#region src/converters/bingserp.ts
|
|
193
|
+
// src/converters/bingserp.ts
|
|
76
194
|
const BING_SEARCH_URL = /^https:\/\/www\.bing\.com\/search\?q=/i;
|
|
77
195
|
const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
78
196
|
var BingSerpConverter = class {
|
|
79
197
|
async convert(source, options = {}) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (element.textContent) element.textContent += " ";
|
|
87
|
-
});
|
|
88
|
-
document.querySelectorAll(".algoSlug_icon").forEach((element) => {
|
|
89
|
-
element.remove();
|
|
90
|
-
});
|
|
91
|
-
const turndown = new CustomTurnDown();
|
|
92
|
-
const markdown = `## A Bing search for '${query}' found the following results:\n\n${[...document.querySelectorAll(".b_algo")].map((result) => {
|
|
93
|
-
result.querySelectorAll("a[href]").forEach((anchor) => {
|
|
94
|
-
const href = anchor.getAttribute("href");
|
|
95
|
-
if (!href) return;
|
|
96
|
-
try {
|
|
97
|
-
const encoded = new URL(href, url).searchParams.get("u");
|
|
98
|
-
if (encoded) anchor.setAttribute("href", decodeBingUrl(encoded));
|
|
99
|
-
} 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 += " ";
|
|
100
204
|
});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
+
});
|
|
108
229
|
}
|
|
109
230
|
};
|
|
110
231
|
function decodeBingUrl(encoded) {
|
|
@@ -124,52 +245,46 @@ function decodeBingUrl(encoded) {
|
|
|
124
245
|
}
|
|
125
246
|
return new TextDecoder().decode(new Uint8Array(bytes));
|
|
126
247
|
}
|
|
127
|
-
|
|
128
|
-
//#region src/converters/html.ts
|
|
248
|
+
// src/converters/html.ts
|
|
129
249
|
var HtmlConverter = class {
|
|
130
250
|
async convert(source, options) {
|
|
131
|
-
|
|
132
|
-
if (extension !== ".html" && extension !== ".htm") return null;
|
|
251
|
+
if (options.extension !== "html" && options.extension !== "htm") return null;
|
|
133
252
|
return this.convertHtml(decodeText(source));
|
|
134
253
|
}
|
|
135
254
|
async convertHtml(html) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
+
};
|
|
140
270
|
});
|
|
141
|
-
const markdown = new CustomTurnDown().convertSoup(document);
|
|
142
|
-
return {
|
|
143
|
-
title: title || null,
|
|
144
|
-
markdown,
|
|
145
|
-
text_content: markdown
|
|
146
|
-
};
|
|
147
271
|
}
|
|
148
272
|
};
|
|
149
|
-
|
|
150
|
-
//#region src/converters/docx.ts
|
|
273
|
+
// src/converters/docx.ts
|
|
151
274
|
var DocxConverter = class extends HtmlConverter {
|
|
152
275
|
async convert(source, options) {
|
|
153
|
-
if (options.
|
|
276
|
+
if (options.extension !== "docx") return null;
|
|
154
277
|
let mammothInput = { arrayBuffer: toArrayBuffer(source) };
|
|
155
278
|
if (typeof process !== "undefined" && process.versions !== null && process.versions.node !== null) mammothInput = { buffer: Buffer.from(source) };
|
|
156
|
-
const mammothOptions = {
|
|
157
|
-
styleMap: options.styleMap,
|
|
158
|
-
includeEmbeddedStyleMap: options.includeEmbeddedStyleMap,
|
|
159
|
-
includeDefaultStyleMap: options.includeDefaultStyleMap,
|
|
160
|
-
ignoreEmptyParagraphs: options.ignoreEmptyParagraphs,
|
|
161
|
-
idPrefix: options.idPrefix
|
|
162
|
-
};
|
|
163
|
-
if (options.transformDocument) mammothOptions.transformDocument = options.transformDocument;
|
|
279
|
+
const mammothOptions = Object.fromEntries(Object.entries(options.mammoth ?? {}).flatMap(([key, value]) => value !== void 0 ? [[key, value]] : []));
|
|
164
280
|
const html = await mammoth.convertToHtml(mammothInput, mammothOptions);
|
|
165
281
|
return this.convertHtml(html.value);
|
|
166
282
|
}
|
|
167
283
|
};
|
|
168
|
-
|
|
169
|
-
//#region src/converters/ipynb.ts
|
|
284
|
+
// src/converters/ipynb.ts
|
|
170
285
|
var IpynbConverter = class {
|
|
171
286
|
async convert(source, options = {}) {
|
|
172
|
-
if (options.
|
|
287
|
+
if (options.extension !== "ipynb") return null;
|
|
173
288
|
const notebook = JSON.parse(decodeText(source));
|
|
174
289
|
const output = [];
|
|
175
290
|
let title = notebook.metadata?.title ?? null;
|
|
@@ -184,99 +299,49 @@ var IpynbConverter = class {
|
|
|
184
299
|
const markdown = output.join("\n\n");
|
|
185
300
|
return {
|
|
186
301
|
title,
|
|
187
|
-
markdown
|
|
188
|
-
text_content: markdown
|
|
302
|
+
markdown
|
|
189
303
|
};
|
|
190
304
|
}
|
|
191
305
|
};
|
|
192
|
-
|
|
193
|
-
//#region src/converters/pdf.ts
|
|
306
|
+
// src/converters/pdf.ts
|
|
194
307
|
var PdfConverter = class {
|
|
195
308
|
async convert(source, options) {
|
|
196
|
-
if (options.
|
|
309
|
+
if (options.extension !== "pdf") return null;
|
|
197
310
|
const pdf = await getDocumentProxy(source.slice());
|
|
198
311
|
try {
|
|
199
312
|
const [{ text }, { info }] = await Promise.all([extractText(pdf, { mergePages: true }), getMeta(pdf)]);
|
|
200
313
|
return {
|
|
201
314
|
title: typeof info.Title === "string" && info.Title.trim() ? info.Title : null,
|
|
202
|
-
markdown: text
|
|
203
|
-
text_content: text
|
|
315
|
+
markdown: text
|
|
204
316
|
};
|
|
205
317
|
} finally {
|
|
206
318
|
await pdf.loadingTask.destroy();
|
|
207
319
|
}
|
|
208
320
|
}
|
|
209
321
|
};
|
|
210
|
-
|
|
211
|
-
//#region src/mime.ts
|
|
212
|
-
const MIME_EXTENSIONS = {
|
|
213
|
-
"application/atom+xml": ".atom",
|
|
214
|
-
"application/json": ".json",
|
|
215
|
-
"application/pdf": ".pdf",
|
|
216
|
-
"application/zip": ".zip",
|
|
217
|
-
"application/rss+xml": ".rss",
|
|
218
|
-
"application/x-pdf": ".pdf",
|
|
219
|
-
"application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx",
|
|
220
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
|
|
221
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
|
|
222
|
-
"application/xml": ".xml",
|
|
223
|
-
"text/csv": ".csv",
|
|
224
|
-
"text/html": ".html",
|
|
225
|
-
"text/markdown": ".md",
|
|
226
|
-
"text/plain": ".txt",
|
|
227
|
-
"text/xml": ".xml"
|
|
228
|
-
};
|
|
229
|
-
const TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
230
|
-
".csv",
|
|
231
|
-
".json",
|
|
232
|
-
".log",
|
|
233
|
-
".markdown",
|
|
234
|
-
".md",
|
|
235
|
-
".text",
|
|
236
|
-
".txt"
|
|
237
|
-
]);
|
|
238
|
-
function extensionFromMime(contentType) {
|
|
239
|
-
if (!contentType) return null;
|
|
240
|
-
const mime = contentType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
241
|
-
return MIME_EXTENSIONS[mime] ?? (mime.startsWith("text/") ? ".txt" : null);
|
|
242
|
-
}
|
|
243
|
-
function isTextExtension(extension) {
|
|
244
|
-
return TEXT_EXTENSIONS.has(extension.toLowerCase());
|
|
245
|
-
}
|
|
246
|
-
function extensionFromPathname(pathname) {
|
|
247
|
-
const filename = pathname.split("/").pop() ?? "";
|
|
248
|
-
const dot = filename.lastIndexOf(".");
|
|
249
|
-
return dot > 0 ? filename.slice(dot).toLowerCase() : null;
|
|
250
|
-
}
|
|
251
|
-
//#endregion
|
|
252
|
-
//#region src/converters/plain-text.ts
|
|
322
|
+
// src/converters/plain-text.ts
|
|
253
323
|
var PlainTextConverter = class {
|
|
254
324
|
async convert(source, options = {}) {
|
|
255
|
-
if (!isTextExtension(options.
|
|
256
|
-
const content = decodeText(source);
|
|
325
|
+
if (!isTextExtension(options.extension ?? "")) return null;
|
|
257
326
|
return {
|
|
258
327
|
title: null,
|
|
259
|
-
markdown:
|
|
260
|
-
text_content: content
|
|
328
|
+
markdown: decodeText(source)
|
|
261
329
|
};
|
|
262
330
|
}
|
|
263
331
|
};
|
|
264
|
-
|
|
265
|
-
//#region src/converters/pptx.ts
|
|
332
|
+
// src/converters/pptx.ts
|
|
266
333
|
var PptxConverter = class extends HtmlConverter {
|
|
267
334
|
async convert(source, options) {
|
|
268
|
-
if (options.
|
|
335
|
+
if (options.extension !== "pptx") return null;
|
|
269
336
|
const slides = await pptxToHtml(toArrayBuffer(source), { domParserFactory: createPptxDomParser });
|
|
270
337
|
const sections = [];
|
|
271
338
|
for (const [index, html] of slides.entries()) {
|
|
272
339
|
const result = await this.convertHtml(`<html><body>${html}</body></html>`);
|
|
273
340
|
sections.push(`<!-- Slide number: ${index + 1} -->\n${result?.markdown.trim() ?? ""}`);
|
|
274
341
|
}
|
|
275
|
-
const markdown = sections.join("\n\n");
|
|
276
342
|
return {
|
|
277
343
|
title: null,
|
|
278
|
-
markdown
|
|
279
|
-
text_content: markdown
|
|
344
|
+
markdown: sections.join("\n\n")
|
|
280
345
|
};
|
|
281
346
|
}
|
|
282
347
|
};
|
|
@@ -340,32 +405,33 @@ function defineGetter(target, name, get) {
|
|
|
340
405
|
get
|
|
341
406
|
});
|
|
342
407
|
}
|
|
343
|
-
|
|
344
|
-
//#region src/converters/wikipedia.ts
|
|
408
|
+
// src/converters/wikipedia.ts
|
|
345
409
|
const WIKIPEDIA_URL = /^https?:\/\/[a-z]{2,3}\.wikipedia\.org\//i;
|
|
346
410
|
var WikipediaConverter = class {
|
|
347
411
|
async convert(source, options = {}) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
+
};
|
|
353
428
|
});
|
|
354
|
-
const body = document.querySelector("div#mw-content-text");
|
|
355
|
-
const title = document.querySelector("span.mw-page-title-main")?.textContent?.trim() || document.title || null;
|
|
356
|
-
const markdown = body ? `# ${title ?? "Wikipedia"}\n\n${new CustomTurnDown().convertSoup(body)}` : new CustomTurnDown().convertSoup(document);
|
|
357
|
-
return {
|
|
358
|
-
title,
|
|
359
|
-
markdown,
|
|
360
|
-
text_content: markdown
|
|
361
|
-
};
|
|
362
429
|
}
|
|
363
430
|
};
|
|
364
|
-
|
|
365
|
-
//#region src/converters/xlsx.ts
|
|
431
|
+
// src/converters/xlsx.ts
|
|
366
432
|
var XlsxConverter = class extends HtmlConverter {
|
|
367
433
|
async convert(source, options) {
|
|
368
|
-
if (options.
|
|
434
|
+
if (options.extension !== "xlsx") return null;
|
|
369
435
|
const sheets = await readExcelFile(toArrayBuffer(source));
|
|
370
436
|
const sections = [];
|
|
371
437
|
for (const { sheet, data } of sheets) {
|
|
@@ -374,11 +440,9 @@ var XlsxConverter = class extends HtmlConverter {
|
|
|
374
440
|
const result = await this.convertHtml(html);
|
|
375
441
|
sections.push(`## ${sheet}\n${result?.markdown.trim() ?? ""}`);
|
|
376
442
|
}
|
|
377
|
-
const markdown = sections.join("\n\n");
|
|
378
443
|
return {
|
|
379
444
|
title: null,
|
|
380
|
-
markdown
|
|
381
|
-
text_content: markdown
|
|
445
|
+
markdown: sections.join("\n\n")
|
|
382
446
|
};
|
|
383
447
|
}
|
|
384
448
|
};
|
|
@@ -399,129 +463,126 @@ function cellText(value) {
|
|
|
399
463
|
function escapeHtml(value) {
|
|
400
464
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
401
465
|
}
|
|
402
|
-
|
|
403
|
-
//#region src/converters/xml-rss-atom.ts
|
|
466
|
+
// src/converters/xml-rss-atom.ts
|
|
404
467
|
var RSSConverter = class {
|
|
405
468
|
async convert(source, options = {}) {
|
|
406
|
-
|
|
407
|
-
if (![
|
|
408
|
-
".xml",
|
|
409
|
-
".rss",
|
|
410
|
-
".atom"
|
|
411
|
-
].includes(extension)) return null;
|
|
469
|
+
if (options.extension !== "xml" && options.extension !== "rss" && options.extension !== "atom") return null;
|
|
412
470
|
const document = new DOMParser().parseFromString(decodeText(source), "text/xml");
|
|
413
|
-
if (document.getElementsByTagName("rss").length > 0) return
|
|
414
|
-
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);
|
|
415
473
|
return null;
|
|
416
474
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
markdown,
|
|
440
|
-
text_content: markdown
|
|
441
|
-
};
|
|
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"));
|
|
442
497
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
};
|
|
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"));
|
|
468
524
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
525
|
+
return {
|
|
526
|
+
title,
|
|
527
|
+
markdown: sections.join("\n\n")
|
|
528
|
+
};
|
|
472
529
|
}
|
|
473
530
|
function getText(element, tagName) {
|
|
474
531
|
return element.getElementsByTagName(tagName)[0]?.textContent?.trim() || null;
|
|
475
532
|
}
|
|
476
|
-
|
|
477
|
-
|
|
533
|
+
// src/converters/youtube.ts
|
|
534
|
+
const YOUTUBE_URL = /^https?:\/\/(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=|shorts\/)?([a-zA-Z0-9_-]+)/i;
|
|
478
535
|
var YouTubeConverter = class {
|
|
479
536
|
async convert(source, options = {}) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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;
|
|
498
556
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
"
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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
|
+
});
|
|
525
586
|
}
|
|
526
587
|
};
|
|
527
588
|
function first(metadata, keys) {
|
|
@@ -544,23 +605,20 @@ function findKey(value, key) {
|
|
|
544
605
|
function isRecord(value) {
|
|
545
606
|
return typeof value === "object" && value !== null;
|
|
546
607
|
}
|
|
547
|
-
|
|
548
|
-
//#region src/converters/zip.ts
|
|
608
|
+
// src/converters/zip.ts
|
|
549
609
|
var ZipConverter = class {
|
|
550
610
|
async convert(source, options = {}) {
|
|
551
|
-
if (options.
|
|
611
|
+
if (options.extension !== "zip") return null;
|
|
552
612
|
const files = unzipSync(source, { filter: (file) => !file.name.endsWith("/") });
|
|
553
|
-
const converters = options.
|
|
613
|
+
const converters = options._converters ?? [];
|
|
554
614
|
const sections = [`Content from the zip file \`${options.url ?? "archive.zip"}\`:`];
|
|
555
615
|
for (const [name, bytes] of Object.entries(files)) {
|
|
556
616
|
const result = await convertEntry(bytes, name, converters, options);
|
|
557
617
|
if (result) sections.push(`## File: ${name}\n\n${result.markdown}`);
|
|
558
618
|
}
|
|
559
|
-
const markdown = sections.join("\n\n");
|
|
560
619
|
return {
|
|
561
620
|
title: null,
|
|
562
|
-
markdown
|
|
563
|
-
text_content: markdown
|
|
621
|
+
markdown: sections.join("\n\n")
|
|
564
622
|
};
|
|
565
623
|
}
|
|
566
624
|
};
|
|
@@ -569,9 +627,9 @@ async function convertEntry(source, name, converters, options) {
|
|
|
569
627
|
if (!extension) return null;
|
|
570
628
|
const entryOptions = {
|
|
571
629
|
...options,
|
|
572
|
-
|
|
630
|
+
extension,
|
|
573
631
|
url: void 0,
|
|
574
|
-
|
|
632
|
+
_converters: converters
|
|
575
633
|
};
|
|
576
634
|
for (const converter of converters) try {
|
|
577
635
|
const result = await converter.convert(source, entryOptions);
|
|
@@ -579,42 +637,72 @@ async function convertEntry(source, name, converters, options) {
|
|
|
579
637
|
} catch {}
|
|
580
638
|
return null;
|
|
581
639
|
}
|
|
582
|
-
|
|
583
|
-
|
|
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
|
|
584
675
|
var MarkItDown = class {
|
|
585
676
|
constructor() {
|
|
586
|
-
this
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
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());
|
|
599
693
|
}
|
|
600
694
|
/** Convert a URL, response, blob, array buffer, data view, or typed array. */
|
|
601
695
|
async convert(source, options = {}) {
|
|
602
696
|
if (typeof source === "string" || source instanceof URL) return this.convertUrl(source.toString(), options);
|
|
603
697
|
if (isResponse(source)) return this.convertResponse(source, options);
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
return this.convertBytes(
|
|
612
|
-
}
|
|
613
|
-
/**
|
|
614
|
-
* @deprecated Pass the byte source directly to `convert`.
|
|
615
|
-
*/
|
|
616
|
-
async convertBuffer(source, options) {
|
|
617
|
-
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);
|
|
618
706
|
}
|
|
619
707
|
async convertUrl(source, { fetch: fetchImplementation = globalThis.fetch, ...options }) {
|
|
620
708
|
const url = new URL(source);
|
|
@@ -628,44 +716,39 @@ var MarkItDown = class {
|
|
|
628
716
|
});
|
|
629
717
|
}
|
|
630
718
|
async convertResponse(response, options) {
|
|
631
|
-
const
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
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, {
|
|
640
727
|
...options,
|
|
641
|
-
url:
|
|
728
|
+
url: options.url || response.url || void 0
|
|
642
729
|
});
|
|
643
730
|
}
|
|
644
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.");
|
|
645
733
|
let lastError;
|
|
646
734
|
for (const extension of extensions) for (const converter of this.converters) try {
|
|
647
735
|
const result = await converter.convert(source, {
|
|
648
736
|
...options,
|
|
649
|
-
|
|
650
|
-
|
|
737
|
+
extension,
|
|
738
|
+
_converters: this.converters
|
|
651
739
|
});
|
|
652
740
|
if (result) {
|
|
653
741
|
result.markdown = result.markdown.replace(/\r\n|\r|\n/g, "\n").trim().replace(/\n{3,}/g, "\n\n");
|
|
654
|
-
result.text_content = result.markdown;
|
|
655
742
|
return result;
|
|
656
743
|
}
|
|
657
744
|
} catch (error) {
|
|
658
745
|
if (options.debug) console.error("Converter threw error:", error);
|
|
659
746
|
lastError = error;
|
|
660
747
|
}
|
|
661
|
-
const formats =
|
|
748
|
+
const formats = extensions.join(", ");
|
|
662
749
|
if (lastError) throw new Error(`Could not convert ${formats} to Markdown: ${String(lastError)}`);
|
|
663
750
|
throw new Error(`Unsupported file type: ${formats}.`);
|
|
664
751
|
}
|
|
665
|
-
addExtension(extensions, extension) {
|
|
666
|
-
if (!extension) return;
|
|
667
|
-
extensions.add(extension.startsWith(".") ? extension.toLowerCase() : `.${extension.toLowerCase()}`);
|
|
668
|
-
}
|
|
669
752
|
registerConverter(converter) {
|
|
670
753
|
this.converters.unshift(converter);
|
|
671
754
|
}
|
|
@@ -673,7 +756,6 @@ var MarkItDown = class {
|
|
|
673
756
|
function isResponse(value) {
|
|
674
757
|
return typeof Response !== "undefined" && value instanceof Response;
|
|
675
758
|
}
|
|
676
|
-
//#endregion
|
|
677
759
|
export { MarkItDown };
|
|
678
760
|
|
|
679
761
|
//# sourceMappingURL=index.js.map
|