@aiquants/markdown-explorer 0.0.0-stage → 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/CHANGELOG.md +121 -0
- package/LICENSE +21 -0
- package/README.md +606 -2
- package/dist/ExplorerSplit-D2plJRRe.cjs +1 -0
- package/dist/ExplorerSplit-DB37U7Kh.js +1362 -0
- package/dist/MultiLayout-DCPDu1-l.js +909 -0
- package/dist/MultiLayout-UyWxs6Xy.cjs +1 -0
- package/dist/TreeLayout-BoyFelK2.js +22 -0
- package/dist/TreeLayout-CLWbWjOc.cjs +1 -0
- package/dist/client/MarkdownExplorer.d.cts +30 -0
- package/dist/client/MarkdownExplorer.d.ts +30 -0
- package/dist/client/storage.d.cts +11 -0
- package/dist/client/storage.d.ts +11 -0
- package/dist/core/config.d.cts +77 -0
- package/dist/core/config.d.ts +77 -0
- package/dist/core/contracts.d.cts +155 -0
- package/dist/core/contracts.d.ts +155 -0
- package/dist/core/geometry.d.cts +51 -0
- package/dist/core/geometry.d.ts +51 -0
- package/dist/core/labels.d.cts +180 -0
- package/dist/core/labels.d.ts +180 -0
- package/dist/core/location.d.cts +82 -0
- package/dist/core/location.d.ts +82 -0
- package/dist/core/paths.d.cts +17 -0
- package/dist/core/paths.d.ts +17 -0
- package/dist/core/revalidation.d.cts +2 -0
- package/dist/core/revalidation.d.ts +2 -0
- package/dist/core/theme.d.cts +6 -0
- package/dist/core/theme.d.ts +6 -0
- package/dist/core/treeSettings.d.cts +48 -0
- package/dist/core/treeSettings.d.ts +48 -0
- package/dist/core/viewSettings.d.cts +15 -0
- package/dist/core/viewSettings.d.ts +15 -0
- package/dist/dataRoute-Bkfwfp1R.js +194 -0
- package/dist/dataRoute-CKbdwkQ7.cjs +1 -0
- package/dist/index-BfuAu2GV.js +2107 -0
- package/dist/index-Cx3-jSna.cjs +3 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +44 -0
- package/dist/location-Oo21lTyV.cjs +1 -0
- package/dist/location-fVl2R1ee.js +395 -0
- package/dist/sanitizeWorker.cjs +1 -0
- package/dist/sanitizeWorker.js +281 -0
- package/dist/server/anonymous.d.cts +10 -0
- package/dist/server/anonymous.d.ts +10 -0
- package/dist/server/createMarkdownExplorer.d.cts +12 -0
- package/dist/server/createMarkdownExplorer.d.ts +12 -0
- package/dist/server/errors.d.cts +26 -0
- package/dist/server/errors.d.ts +26 -0
- package/dist/server/fileSystemSource.d.cts +38 -0
- package/dist/server/fileSystemSource.d.ts +38 -0
- package/dist/server/ports.d.cts +107 -0
- package/dist/server/ports.d.ts +107 -0
- package/dist/server/sanitizeProtocol.d.cts +17 -0
- package/dist/server/sanitizeProtocol.d.ts +17 -0
- package/dist/server/sanitizer.d.cts +51 -0
- package/dist/server/sanitizer.d.ts +51 -0
- package/dist/server/workerPool.d.cts +42 -0
- package/dist/server/workerPool.d.ts +42 -0
- package/dist/server.cjs +1 -0
- package/dist/server.d.cts +7 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +1253 -0
- package/dist/storage-Bkb2Axdi.cjs +1 -0
- package/dist/storage-DA4ItHzl.js +60 -0
- package/dist/storage.cjs +1 -0
- package/dist/storage.d.cts +1 -0
- package/dist/storage.d.ts +1 -0
- package/dist/storage.js +4 -0
- package/dist/styles/markdown-explorer.css +3 -0
- package/docs/behavior/dom-hooks.md +95 -0
- package/docs/behavior/layout.md +215 -0
- package/docs/behavior/localization.md +48 -0
- package/docs/behavior/security.md +138 -0
- package/docs/behavior/url-contract.md +135 -0
- package/package.json +141 -3
package/dist/server.js
ADDED
|
@@ -0,0 +1,1253 @@
|
|
|
1
|
+
import { a as et, b as Et, f as Re, c as yt, d as bt, g as St, h as Tt, E as xt, j as Ot, i as te, k as kt, r as tt, l as Rt, m as _t, n as rt, o as Nt, p as At, q as de, s as ee, e as It } from "./dataRoute-Bkfwfp1R.js";
|
|
2
|
+
import { D as Qn, M as qn, t as eo } from "./dataRoute-Bkfwfp1R.js";
|
|
3
|
+
import { createHash as Mt, createHmac as Pt } from "node:crypto";
|
|
4
|
+
import { data as Ie, redirect as vt } from "react-router";
|
|
5
|
+
import { w as $t, d as jt, a as Lt, i as zt, r as Dt, p as Ct, b as Ht, f as Bt, N as Ft } from "./location-fVl2R1ee.js";
|
|
6
|
+
import { existsSync as Ut, statSync as Gt, realpathSync as Xt, constants as ce } from "node:fs";
|
|
7
|
+
import { extname as Jt, isAbsolute as Me, relative as Pe, sep as ve, join as Wt, resolve as Yt } from "node:path";
|
|
8
|
+
import { fileURLToPath as be } from "node:url";
|
|
9
|
+
import { Worker as Kt } from "node:worker_threads";
|
|
10
|
+
import { open as nt, lstat as Vt, realpath as Zt, readdir as Qt } from "node:fs/promises";
|
|
11
|
+
import { Readable as qt } from "node:stream";
|
|
12
|
+
const $e = Object.freeze({ kind: "anonymous" }), Wn = Object.freeze({
|
|
13
|
+
authenticate: async () => ({ status: "signed-in", principal: $e }),
|
|
14
|
+
principalKey: () => $e.kind,
|
|
15
|
+
signedOutPage: (e, t) => new Response(null, { status: 401, headers: t })
|
|
16
|
+
}), er = "application/octet-stream", ot = "application/pdf", tr = /* @__PURE__ */ new Set([
|
|
17
|
+
"image/png",
|
|
18
|
+
"image/apng",
|
|
19
|
+
"image/jpeg",
|
|
20
|
+
"image/gif",
|
|
21
|
+
"image/webp",
|
|
22
|
+
"image/avif",
|
|
23
|
+
"image/bmp",
|
|
24
|
+
"image/x-icon",
|
|
25
|
+
"image/vnd.microsoft.icon",
|
|
26
|
+
"audio/mpeg",
|
|
27
|
+
"audio/mp4",
|
|
28
|
+
"audio/aac",
|
|
29
|
+
"audio/ogg",
|
|
30
|
+
"audio/opus",
|
|
31
|
+
"audio/wav",
|
|
32
|
+
"audio/wave",
|
|
33
|
+
"audio/x-wav",
|
|
34
|
+
"audio/webm",
|
|
35
|
+
"audio/flac",
|
|
36
|
+
"audio/x-flac",
|
|
37
|
+
"audio/x-m4a",
|
|
38
|
+
"video/mp4",
|
|
39
|
+
"video/webm",
|
|
40
|
+
"video/ogg",
|
|
41
|
+
"video/quicktime",
|
|
42
|
+
ot
|
|
43
|
+
]), st = "default-src 'none'; media-src 'self'; style-src 'unsafe-inline'", rr = `${st}; sandbox`, nr = `${st}; sandbox allow-same-origin`, je = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/u, or = /^[\t\n\r ]+|[\t\n\r ]+$/gu, _e = /^[\t ]+|[\t ]+$/gu, sr = (e) => {
|
|
44
|
+
const t = [];
|
|
45
|
+
let r = "", n = 0;
|
|
46
|
+
for (; ; ) {
|
|
47
|
+
for (; n < e.length && e[n] !== '"' && e[n] !== ","; )
|
|
48
|
+
r += e[n], n += 1;
|
|
49
|
+
if (n < e.length && e[n] === '"') {
|
|
50
|
+
const s = n;
|
|
51
|
+
for (n += 1; n < e.length && e[n] !== '"'; )
|
|
52
|
+
n += e[n] === "\\" ? 2 : 1;
|
|
53
|
+
if (n = Math.min(n + 1, e.length), r += e.slice(s, n), n < e.length) continue;
|
|
54
|
+
}
|
|
55
|
+
if (t.push(r.replace(_e, "")), r = "", n >= e.length) return t;
|
|
56
|
+
n += 1;
|
|
57
|
+
}
|
|
58
|
+
}, ir = (e) => {
|
|
59
|
+
const t = e.replace(or, ""), r = t.indexOf("/");
|
|
60
|
+
if (r < 0) return null;
|
|
61
|
+
const n = t.slice(0, r), s = t.indexOf(";", r + 1), o = t.slice(r + 1, s < 0 ? t.length : s).replace(/[\t\n\r ]+$/u, "");
|
|
62
|
+
return je.test(n) && je.test(o) ? `${n}/${o}`.toLowerCase() : null;
|
|
63
|
+
}, ar = (e) => {
|
|
64
|
+
let t = null;
|
|
65
|
+
for (const r of sr(e)) {
|
|
66
|
+
const n = ir(r);
|
|
67
|
+
n !== null && n !== "*/*" && (t = n);
|
|
68
|
+
}
|
|
69
|
+
return t;
|
|
70
|
+
}, cr = /['()*]/gu, ur = (e) => encodeURIComponent(e).replace(cr, (t) => `%${t.charCodeAt(0).toString(16).toUpperCase()}`), lr = Object.freeze({
|
|
71
|
+
"Cache-Control": "private, max-age=60, stale-while-revalidate=600, no-transform",
|
|
72
|
+
Vary: "Cookie"
|
|
73
|
+
}), dr = (e, t) => {
|
|
74
|
+
const r = ar(e), n = r !== null && tr.has(r), s = n && (r.startsWith("audio/") || r.startsWith("video/"));
|
|
75
|
+
return {
|
|
76
|
+
contentType: r === null ? er : r,
|
|
77
|
+
inline: n,
|
|
78
|
+
headers: {
|
|
79
|
+
"X-Content-Type-Options": "nosniff",
|
|
80
|
+
"Content-Disposition": `${n ? "inline" : "attachment"}; filename*=UTF-8''${ur(t)}`,
|
|
81
|
+
...r === ot ? {} : { "Content-Security-Policy": s ? nr : rr },
|
|
82
|
+
"Cross-Origin-Resource-Policy": "same-origin"
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}, fr = /^bytes=(?:(\d+)-(\d*)|-(\d+))$/iu, fe = Object.freeze({ kind: "whole" }), Le = Object.freeze({ kind: "unsatisfiable" }), mr = (e, t, r) => {
|
|
86
|
+
const n = e.get("Range");
|
|
87
|
+
if (n === null) return fe;
|
|
88
|
+
const s = e.get("If-Range");
|
|
89
|
+
if (s !== null && s !== r) return fe;
|
|
90
|
+
const o = fr.exec(n);
|
|
91
|
+
if (o === null) return fe;
|
|
92
|
+
const [, c, m, g] = o;
|
|
93
|
+
if (g !== void 0) {
|
|
94
|
+
const w = Number(g);
|
|
95
|
+
if (w === 0) return Le;
|
|
96
|
+
const _ = Math.max(0, t - w);
|
|
97
|
+
return { kind: "partial", range: { offset: _, length: t - _ } };
|
|
98
|
+
}
|
|
99
|
+
const T = Number(c), l = m === "" ? Number.POSITIVE_INFINITY : Number(m);
|
|
100
|
+
return l < T ? fe : T >= t ? Le : { kind: "partial", range: { offset: T, length: Math.min(l, t - 1) - T + 1 } };
|
|
101
|
+
}, ze = "W/", pr = (e) => {
|
|
102
|
+
const t = [];
|
|
103
|
+
let r = 0, n = !1;
|
|
104
|
+
for (let s = 0; s < e.length; s += 1)
|
|
105
|
+
e[s] === '"' ? n = !n : e[s] === "," && !n && (t.push(e.slice(r, s).replace(_e, "")), r = s + 1);
|
|
106
|
+
return t.push(e.slice(r).replace(_e, "")), t;
|
|
107
|
+
}, De = (e) => e.startsWith(ze) ? e.slice(ze.length) : e, wr = (e, t) => {
|
|
108
|
+
if (e === null) return !1;
|
|
109
|
+
const r = pr(e);
|
|
110
|
+
if (r.length === 1 && r[0] === "*") return !0;
|
|
111
|
+
const n = De(t);
|
|
112
|
+
return r.some((s) => De(s) === n);
|
|
113
|
+
}, hr = (e, t) => {
|
|
114
|
+
let r = t;
|
|
115
|
+
return e.pipeThrough(
|
|
116
|
+
new TransformStream({
|
|
117
|
+
transform: (n, s) => {
|
|
118
|
+
const o = n.byteLength > r ? n.subarray(0, r) : n;
|
|
119
|
+
r -= o.byteLength, s.enqueue(o), r === 0 && s.terminate();
|
|
120
|
+
},
|
|
121
|
+
flush: (n) => {
|
|
122
|
+
r > 0 && n.error(new RangeError("[markdown-explorer] the asset ended before its declared length"));
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
}, Se = async (e, t, r, n, s) => {
|
|
127
|
+
if (n !== null && r.set("Content-Length", String(n)), e.method.toUpperCase() === "HEAD") return new Response(null, { status: t, headers: r });
|
|
128
|
+
const o = await s();
|
|
129
|
+
return new Response(n === null ? o : hr(o, n), { status: t, headers: r });
|
|
130
|
+
}, gr = (e, t) => {
|
|
131
|
+
const r = new Headers(e);
|
|
132
|
+
for (const [n, s] of Object.entries({ "Accept-Ranges": "bytes", "Content-Range": `bytes */${t}`, "Cache-Control": "private, no-store", "X-Content-Type-Options": "nosniff" })) r.set(n, s);
|
|
133
|
+
return new Response(null, { status: 416, headers: r });
|
|
134
|
+
}, Er = async (e, t, r) => {
|
|
135
|
+
const n = `"${t.version}"`, s = dr(t.contentType, t.name), o = new Headers(r);
|
|
136
|
+
for (const [g, T] of Object.entries({ ...s.headers, ...lr, ETag: n })) o.set(g, T);
|
|
137
|
+
if (wr(e.headers.get("If-None-Match"), n)) return new Response(null, { status: 304, headers: o });
|
|
138
|
+
if (o.set("Content-Type", s.contentType), t.size === null) return Se(e, 200, o, null, () => t.open(null));
|
|
139
|
+
if (t.size === 0)
|
|
140
|
+
return o.set("Content-Length", "0"), new Response(null, { status: 200, headers: o });
|
|
141
|
+
o.set("Accept-Ranges", "bytes");
|
|
142
|
+
const c = mr(e.headers, t.size, n);
|
|
143
|
+
if (c.kind === "unsatisfiable") return gr(r, t.size);
|
|
144
|
+
if (c.kind === "whole") return Se(e, 200, o, t.size, () => t.open(null));
|
|
145
|
+
const { range: m } = c;
|
|
146
|
+
return o.set("Content-Range", `bytes ${m.offset}-${m.offset + m.length - 1}/${t.size}`), Se(e, 206, o, m.length, () => t.open(m));
|
|
147
|
+
}, Te = (e, t) => {
|
|
148
|
+
const r = new AbortController(), n = t(AbortSignal.any([e.signal, r.signal]));
|
|
149
|
+
return new Promise((s) => {
|
|
150
|
+
let o;
|
|
151
|
+
const c = setTimeout(() => {
|
|
152
|
+
s(e.late), o = setTimeout(() => r.abort(), e.handOverMs);
|
|
153
|
+
}, e.timeoutMs), m = () => {
|
|
154
|
+
clearTimeout(c), clearTimeout(o);
|
|
155
|
+
};
|
|
156
|
+
n.then(
|
|
157
|
+
(g) => {
|
|
158
|
+
m(), s(g);
|
|
159
|
+
},
|
|
160
|
+
() => {
|
|
161
|
+
m(), s(e.late);
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
}, yr = Object.freeze({
|
|
166
|
+
error: (e, t) => {
|
|
167
|
+
console.error(e, t);
|
|
168
|
+
}
|
|
169
|
+
}), it = /* @__PURE__ */ Symbol.for("@aiquants/markdown-explorer:ExplorerError");
|
|
170
|
+
class Ee extends Error {
|
|
171
|
+
reason;
|
|
172
|
+
code;
|
|
173
|
+
constructor(t, r = {}) {
|
|
174
|
+
if (!et(t)) throw new RangeError(`[markdown-explorer] ExplorerError needs a failure reason; got ${JSON.stringify(t)}`);
|
|
175
|
+
if (r.code !== void 0 && !Et(r.code)) throw new RangeError(`[markdown-explorer] an ExplorerError code must be 1 to 64 lowercase letters, digits or hyphens; got ${JSON.stringify(r.code)}`);
|
|
176
|
+
super(`[markdown-explorer] ${t}`, r.cause === void 0 ? void 0 : { cause: r.cause }), this.name = "ExplorerError", this.reason = t, this.code = r.code, Object.defineProperty(this, it, { value: !0 });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const br = (e) => e instanceof Error && Object.hasOwn(e, it) && et(e.reason), Sr = (e) => typeof e == "object" && e !== null && e.name === "AbortError", Ne = (e, t, r) => {
|
|
180
|
+
try {
|
|
181
|
+
e.error(t, r);
|
|
182
|
+
} catch (n) {
|
|
183
|
+
console.error("[markdown-explorer] the logger threw while reporting a failure", { operation: r.operation, error: n });
|
|
184
|
+
}
|
|
185
|
+
}, q = (e, t) => Ne(e, `[markdown-explorer] ${t.operation} failed`, t), Tr = (e, t) => Ne(e, `[markdown-explorer] ${t.operation} left out an entry`, t), xr = (e, t) => Ne(e, `[markdown-explorer] ${t.operation} refused a path the deny rules match`, { ...t, error: new RangeError("[markdown-explorer] the path matches a deny rule") }), Ce = (e, t) => {
|
|
186
|
+
e === null || typeof e != "object" && typeof e != "function" || Promise.resolve(e).then(void 0, t);
|
|
187
|
+
}, Q = (e, t, r, n) => br(e) ? Re(e.reason, e.code) : (Sr(e) || r.aborted || q(n, { ...t, error: e }), { reason: "failed" }), He = (e, t) => {
|
|
188
|
+
if (!Number.isSafeInteger(t) || t <= 0) throw new RangeError(`[markdown-explorer] a cache ${e} must be a positive integer; got ${String(t)}`);
|
|
189
|
+
}, at = (e, t) => {
|
|
190
|
+
He("capacity", e), t !== void 0 && He("weight budget", t.max);
|
|
191
|
+
const r = /* @__PURE__ */ new Map();
|
|
192
|
+
let n = 0;
|
|
193
|
+
const s = (o) => {
|
|
194
|
+
const c = r.get(o);
|
|
195
|
+
c !== void 0 && (r.delete(o), n -= c.weight);
|
|
196
|
+
};
|
|
197
|
+
return {
|
|
198
|
+
get: (o) => {
|
|
199
|
+
const c = r.get(o);
|
|
200
|
+
if (c !== void 0)
|
|
201
|
+
return r.delete(o), r.set(o, c), c.value;
|
|
202
|
+
},
|
|
203
|
+
set: (o, c) => {
|
|
204
|
+
s(o);
|
|
205
|
+
const m = t === void 0 ? 0 : t.of(o, c);
|
|
206
|
+
if (!(t !== void 0 && m > t.max)) {
|
|
207
|
+
r.set(o, { value: c, weight: m }), n += m;
|
|
208
|
+
for (const g of r.keys()) {
|
|
209
|
+
if (r.size <= e && (t === void 0 || n <= t.max)) break;
|
|
210
|
+
s(g);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
delete: s,
|
|
215
|
+
size: () => r.size,
|
|
216
|
+
weight: () => n
|
|
217
|
+
};
|
|
218
|
+
}, ct = (e) => Math.max(1, e - 1), Or = 2147483647, Be = Object.freeze({ status: "aborted" }), ne = (e, t, r) => {
|
|
219
|
+
const n = (e.get(t) ?? 0) + r;
|
|
220
|
+
n === 0 ? e.delete(t) : e.set(t, n);
|
|
221
|
+
}, ie = (e, t, r) => {
|
|
222
|
+
if (!Number.isSafeInteger(t) || t <= 0 || t > r) throw new RangeError(`[markdown-explorer] a worker pool's ${e} must be an integer in 1..${String(r)}; got ${String(t)}`);
|
|
223
|
+
}, kr = (e) => e !== null && typeof e == "object" && e.ready === !0 && !("id" in e), Rr = (e, t) => e !== null && typeof e == "object" && e.id === t && "output" in e, _r = ({ url: e, size: t, deadlineMs: r, jobsPerScope: n, maxQueuedJobs: s, heapLimits: o }) => {
|
|
224
|
+
ie("size", t, Number.MAX_SAFE_INTEGER), ie("deadline", r, Or), ie("jobsPerScope", n, Number.MAX_SAFE_INTEGER), ie("maxQueuedJobs", s, Number.MAX_SAFE_INTEGER), ie("maxOldGenerationSizeMb", o.maxOldGenerationSizeMb, Number.MAX_SAFE_INTEGER), ie("maxYoungGenerationSizeMb", o.maxYoungGenerationSizeMb, Number.MAX_SAFE_INTEGER);
|
|
225
|
+
const c = Object.freeze({ maxOldGenerationSizeMb: o.maxOldGenerationSizeMb, maxYoungGenerationSizeMb: o.maxYoungGenerationSizeMb }), m = /* @__PURE__ */ new Set(), g = [], T = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), w = ct(t);
|
|
226
|
+
let _ = 0;
|
|
227
|
+
const R = (u, a) => {
|
|
228
|
+
const { job: E } = u;
|
|
229
|
+
u.timer !== null && clearTimeout(u.timer), u.timer = null, u.job = null, u.sent = !1, E !== null && (ne(T, E.scope, -1), ne(l, E.scope, -1), E.settle(a));
|
|
230
|
+
}, I = (u) => {
|
|
231
|
+
const [a] = g.splice(u, 1);
|
|
232
|
+
a?.detach();
|
|
233
|
+
}, O = () => {
|
|
234
|
+
let u = -1, a = w, E = Number.POSITIVE_INFINITY;
|
|
235
|
+
for (const [b, k] of g.entries()) {
|
|
236
|
+
const A = T.get(k.scope) ?? 0, D = l.get(k.scope) ?? 0;
|
|
237
|
+
(A < a || A === a && u >= 0 && D < E) && (u = b, a = A, E = D);
|
|
238
|
+
}
|
|
239
|
+
return u;
|
|
240
|
+
}, x = (u, a) => {
|
|
241
|
+
m.has(u) && (m.delete(u), R(u, a), u.worker.terminate().catch(() => {
|
|
242
|
+
}), L());
|
|
243
|
+
}, v = (u) => {
|
|
244
|
+
u.timer = setTimeout(() => {
|
|
245
|
+
x(u, { status: "timeout" });
|
|
246
|
+
}, r);
|
|
247
|
+
}, y = (u, a) => u.sent ? { status: "crashed", error: a } : { status: "unavailable", error: a }, N = (u, a) => {
|
|
248
|
+
u.sent = !0, v(u);
|
|
249
|
+
try {
|
|
250
|
+
u.worker.postMessage({ id: a.id, input: a.input });
|
|
251
|
+
} catch (E) {
|
|
252
|
+
R(u, { status: "crashed", error: E }), u.worker.unref();
|
|
253
|
+
}
|
|
254
|
+
}, z = () => {
|
|
255
|
+
const u = { worker: new Kt(e, { resourceLimits: c }), job: null, sent: !1, timer: null, ready: !1 };
|
|
256
|
+
return u.worker.unref(), u.timer = setTimeout(() => {
|
|
257
|
+
x(u, { status: "unavailable", error: new Error(`[markdown-explorer] a worker did not start within ${String(r)} ms`) });
|
|
258
|
+
}, r), u.worker.on("message", (a) => {
|
|
259
|
+
if (!u.ready && kr(a)) {
|
|
260
|
+
u.ready = !0, u.timer !== null && clearTimeout(u.timer), u.timer = null, u.job === null ? u.worker.unref() : N(u, u.job), u.job === null && L();
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const { job: E } = u;
|
|
264
|
+
if (E === null || !Rr(a, E.id)) {
|
|
265
|
+
x(u, y(u, new Error("[markdown-explorer] a worker sent a message that answers no job")));
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
R(u, { status: "done", output: a.output }), u.worker.unref(), L();
|
|
269
|
+
}), u.worker.on("error", (a) => {
|
|
270
|
+
x(u, y(u, a));
|
|
271
|
+
}), u.worker.on("messageerror", (a) => {
|
|
272
|
+
x(u, y(u, a));
|
|
273
|
+
}), u.worker.on("exit", (a) => {
|
|
274
|
+
x(u, y(u, new Error(`[markdown-explorer] a worker exited with code ${String(a)}`)));
|
|
275
|
+
}), m.add(u), u;
|
|
276
|
+
}, L = () => {
|
|
277
|
+
for (; ; ) {
|
|
278
|
+
const u = O(), a = g[u];
|
|
279
|
+
if (a === void 0) return;
|
|
280
|
+
let E;
|
|
281
|
+
for (const k of m)
|
|
282
|
+
if (k.job === null) {
|
|
283
|
+
E = k;
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
if (E === void 0 && m.size < t)
|
|
287
|
+
try {
|
|
288
|
+
E = z();
|
|
289
|
+
} catch (k) {
|
|
290
|
+
I(u), ne(l, a.scope, -1), a.settle({ status: "unavailable", error: k });
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (E === void 0) return;
|
|
294
|
+
I(u), ne(T, a.scope, 1);
|
|
295
|
+
const b = E;
|
|
296
|
+
b.job = a, b.worker.ref(), b.ready && N(b, a);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
return {
|
|
300
|
+
run: (u, { scope: a, signal: E }) => new Promise((b) => {
|
|
301
|
+
if (E.aborted) {
|
|
302
|
+
b(Be);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if ((l.get(a) ?? 0) >= n) {
|
|
306
|
+
b({ status: "refused", limit: "scope" });
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
_ += 1;
|
|
310
|
+
const k = { id: _, input: u, scope: a, settle: b, detach: () => {
|
|
311
|
+
} };
|
|
312
|
+
g.push(k), ne(l, a, 1), L();
|
|
313
|
+
const A = g.indexOf(k);
|
|
314
|
+
if (A < 0) return;
|
|
315
|
+
if (g.length > s) {
|
|
316
|
+
I(A), ne(l, a, -1), b({ status: "refused", limit: "queue" });
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const D = () => {
|
|
320
|
+
const M = g.indexOf(k);
|
|
321
|
+
M < 0 || (I(M), ne(l, a, -1), b(Be));
|
|
322
|
+
};
|
|
323
|
+
E.addEventListener("abort", D, { once: !0 }), k.detach = () => {
|
|
324
|
+
E.removeEventListener("abort", D);
|
|
325
|
+
};
|
|
326
|
+
}),
|
|
327
|
+
workers: () => m.size
|
|
328
|
+
};
|
|
329
|
+
}, Ae = 1024 * 1024, Nr = 3e4, Ar = 4 * Ae, Ir = 64 * Ae, Mr = 3, Pr = 2, vr = (e, t) => e + ct(t) + Mr, $r = 4, jr = 32, Lr = 512, zr = 64, Dr = 4096, Cr = 32, Fe = 4096, Hr = 4, ut = ({ workerHeapMb: e, maxHtmlBytes: t }) => Math.floor(((e - zr) * Ae - t * Cr) / Dr), Br = ({ maxHtmlBytes: e }) => e, Fr = ({ maxHtmlBytes: e }) => Hr * e, Ur = 1024, Gr = 2147483647, Xr = 64, Jr = 2 ** 29, Wr = 1024 * 1024, Yr = 64, Ue = Object.freeze({ status: "rejected", reason: "too-large", cause: null }), xe = Object.freeze({ status: "rejected", reason: "failed", cause: null }), oe = (e, t, r, n) => {
|
|
330
|
+
if (t === void 0) return r;
|
|
331
|
+
if (typeof t != "number") throw new TypeError(`[markdown-explorer] sanitizer.${e} must be a number; got ${typeof t}`);
|
|
332
|
+
if (!Number.isSafeInteger(t) || t <= 0 || t > n) throw new RangeError(`[markdown-explorer] sanitizer.${e} must be an integer in 1..${String(n)}; got ${String(t)}`);
|
|
333
|
+
return t;
|
|
334
|
+
}, Kr = (e, t) => {
|
|
335
|
+
if (e !== void 0 && (e === null || typeof e != "object")) throw new TypeError("[markdown-explorer] sanitizer must be an object of { workers, deadlineMs, maxHtmlBytes, cacheBytes, workerHeapMb, jobsPerScope, maxQueuedJobs }");
|
|
336
|
+
const { workers: r, deadlineMs: n, maxHtmlBytes: s, cacheBytes: o, workerHeapMb: c, jobsPerScope: m, maxQueuedJobs: g } = e ?? {}, T = oe("workers", r, Pr, Xr), l = oe("jobsPerScope", m, vr(t, T), Number.MAX_SAFE_INTEGER), w = Object.freeze({
|
|
337
|
+
workers: T,
|
|
338
|
+
deadlineMs: oe("deadlineMs", n, Nr, Gr),
|
|
339
|
+
maxHtmlBytes: oe("maxHtmlBytes", s, Ar, Jr),
|
|
340
|
+
cacheBytes: oe("cacheBytes", o, Ir, Number.MAX_SAFE_INTEGER),
|
|
341
|
+
workerHeapMb: oe("workerHeapMb", c, Lr, Wr),
|
|
342
|
+
jobsPerScope: l,
|
|
343
|
+
maxQueuedJobs: oe("maxQueuedJobs", g, $r * l, Number.MAX_SAFE_INTEGER)
|
|
344
|
+
});
|
|
345
|
+
if (w.jobsPerScope < t)
|
|
346
|
+
throw new RangeError(`[markdown-explorer] sanitizer.jobsPerScope must be at least multiView.maxPanels (${String(t)}), because a page sends the document of every panel to the sanitizer at once for one reader; got ${String(w.jobsPerScope)}`);
|
|
347
|
+
if (w.maxQueuedJobs < t)
|
|
348
|
+
throw new RangeError(`[markdown-explorer] sanitizer.maxQueuedJobs must be at least multiView.maxPanels (${String(t)}), because a reader whose page finds every worker busy waits with the document of every panel; got ${String(w.maxQueuedJobs)}`);
|
|
349
|
+
if (ut(w) < Fe)
|
|
350
|
+
throw new RangeError(
|
|
351
|
+
`[markdown-explorer] sanitizer.workerHeapMb (${String(w.workerHeapMb)} MiB) must leave room for ${String(Fe)} parsed nodes beside HTML of sanitizer.maxHtmlBytes (${String(w.maxHtmlBytes)} bytes); raise workerHeapMb or lower maxHtmlBytes`
|
|
352
|
+
);
|
|
353
|
+
return w;
|
|
354
|
+
}, Vr = () => {
|
|
355
|
+
const e = import.meta.url, t = new URL(`./sanitizeWorker${Jt(be(e))}`, e);
|
|
356
|
+
if (!Ut(be(t))) throw new Error(`[markdown-explorer] the HTML sanitizer worker is missing: ${be(t)}`);
|
|
357
|
+
return t;
|
|
358
|
+
}, Ge = /* @__PURE__ */ new Map(), Zr = (e, t) => {
|
|
359
|
+
const r = [e.href, t.workers, t.deadlineMs, t.workerHeapMb, t.jobsPerScope, t.maxQueuedJobs].map(String).join("\0");
|
|
360
|
+
let n = Ge.get(r);
|
|
361
|
+
return n === void 0 && (n = _r({
|
|
362
|
+
url: e,
|
|
363
|
+
size: t.workers,
|
|
364
|
+
deadlineMs: t.deadlineMs,
|
|
365
|
+
jobsPerScope: t.jobsPerScope,
|
|
366
|
+
maxQueuedJobs: t.maxQueuedJobs,
|
|
367
|
+
heapLimits: { maxOldGenerationSizeMb: t.workerHeapMb, maxYoungGenerationSizeMb: jr }
|
|
368
|
+
}), Ge.set(r, n)), n;
|
|
369
|
+
}, Qr = (e) => {
|
|
370
|
+
if (e === null || typeof e != "object") return !1;
|
|
371
|
+
const { status: t, html: r, message: n } = e;
|
|
372
|
+
return t === "sanitized" && typeof r == "string" || t === "too-large" || t === "error" && typeof n == "string";
|
|
373
|
+
}, qr = (e) => e instanceof Error && e.code === "ERR_WORKER_OUT_OF_MEMORY", en = ({ iframeHosts: e, dataPath: t, options: r, pool: n }) => {
|
|
374
|
+
const s = Object.freeze([...e]), o = ut(r), c = Br(r), m = Fr(r), g = at(Ur, {
|
|
375
|
+
max: r.cacheBytes,
|
|
376
|
+
of: (O, x) => 2 * O.length + (x.status === "sanitized" ? 2 * x.html.length : Yr)
|
|
377
|
+
}), T = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), w = (O) => {
|
|
378
|
+
T.get(O.key) === O && T.delete(O.key);
|
|
379
|
+
const x = l.get(O.hash);
|
|
380
|
+
x?.delete(O), x?.size === 0 && l.delete(O.hash);
|
|
381
|
+
}, _ = async (O, x, v) => {
|
|
382
|
+
const y = await n.run({ html: O, iframeHosts: s, dataPath: t, maxNodes: o, maxAttributeChars: c, maxSanitizedChars: m }, { scope: x, signal: v });
|
|
383
|
+
if (y.status === "refused") {
|
|
384
|
+
const z = y.limit === "scope" ? `its reader already has ${String(r.jobsPerScope)} documents being sanitized or waiting` : `${String(r.maxQueuedJobs)} documents are already waiting`;
|
|
385
|
+
return { outcome: { status: "rejected", reason: "failed", cause: new Error(`[markdown-explorer] the HTML sanitizer refused a document: ${z}`) }, lasting: !1 };
|
|
386
|
+
}
|
|
387
|
+
if (y.status === "aborted") return { outcome: xe, lasting: !1 };
|
|
388
|
+
if (y.status === "timeout") return { outcome: { status: "rejected", reason: "too-large", cause: new Error(`[markdown-explorer] the HTML sanitizer did not finish within ${String(r.deadlineMs)} ms`) }, lasting: !0 };
|
|
389
|
+
if (y.status === "unavailable") return { outcome: { status: "rejected", reason: "failed", cause: new Error("[markdown-explorer] the HTML sanitizer worker could not start", { cause: y.error }) }, lasting: !1 };
|
|
390
|
+
if (y.status === "crashed" && qr(y.error))
|
|
391
|
+
return { outcome: { status: "rejected", reason: "too-large", cause: new Error(`[markdown-explorer] the HTML sanitizer ran out of its ${String(r.workerHeapMb)} MiB worker heap`, { cause: y.error }) }, lasting: !0 };
|
|
392
|
+
if (y.status === "crashed") return { outcome: { status: "rejected", reason: "failed", cause: new Error("[markdown-explorer] the HTML sanitizer worker failed", { cause: y.error }) }, lasting: !0 };
|
|
393
|
+
const { output: N } = y;
|
|
394
|
+
return Qr(N) ? N.status === "too-large" ? { outcome: Ue, lasting: !0 } : N.status === "error" ? { outcome: { status: "rejected", reason: "failed", cause: new Error(`[markdown-explorer] the HTML sanitizer failed: ${N.message}`) }, lasting: !0 } : N.html.length > m ? { outcome: { status: "rejected", reason: "too-large", cause: new Error(`[markdown-explorer] the HTML sanitizer's output of ${String(N.html.length)} characters exceeds its limit of ${String(m)}`) }, lasting: !0 } : { outcome: { status: "sanitized", html: N.html }, lasting: !0 } : { outcome: { status: "rejected", reason: "failed", cause: new Error("[markdown-explorer] the HTML sanitizer worker answered something that is not an outcome") }, lasting: !0 };
|
|
395
|
+
}, R = (O, x, v, y) => {
|
|
396
|
+
let N = () => {
|
|
397
|
+
};
|
|
398
|
+
const z = new Promise((a) => {
|
|
399
|
+
N = a;
|
|
400
|
+
}), L = { hash: x, key: v, outcome: z, answer: N, controller: new AbortController(), waiters: 0 };
|
|
401
|
+
T.set(v, L);
|
|
402
|
+
const u = l.get(x);
|
|
403
|
+
return u === void 0 ? l.set(x, /* @__PURE__ */ new Set([L])) : u.add(L), _(O, y, L.controller.signal).catch((a) => ({ outcome: { status: "rejected", reason: "failed", cause: a instanceof Error ? a : new Error(String(a)) }, lasting: !0 })).then(({ outcome: a, lasting: E }) => {
|
|
404
|
+
if (w(L), L.answer(a), !E) return;
|
|
405
|
+
const b = g.get(x), k = b ?? (a.status === "rejected" ? { ...a, cause: null } : a);
|
|
406
|
+
b === void 0 && g.set(x, k);
|
|
407
|
+
for (const A of l.get(x) ?? [])
|
|
408
|
+
w(A), A.answer(k), A.controller.abort();
|
|
409
|
+
}), L;
|
|
410
|
+
}, I = (O, x) => new Promise((v) => {
|
|
411
|
+
O.waiters += 1;
|
|
412
|
+
const y = () => {
|
|
413
|
+
O.waiters -= 1, v(xe), !(O.waiters > 0) && (w(O), O.controller.abort());
|
|
414
|
+
};
|
|
415
|
+
x.addEventListener("abort", y, { once: !0 }), O.outcome.then((N) => {
|
|
416
|
+
x.removeEventListener("abort", y), v(N);
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
return (O, { scope: x, signal: v }) => {
|
|
420
|
+
if (O.length > r.maxHtmlBytes || Buffer.byteLength(O, "utf8") > r.maxHtmlBytes) return Promise.resolve(Ue);
|
|
421
|
+
const y = Mt("sha256").update(O, "utf8").digest("base64"), N = g.get(y);
|
|
422
|
+
if (N !== void 0) return Promise.resolve(N);
|
|
423
|
+
if (v.aborted) return Promise.resolve(xe);
|
|
424
|
+
const z = `${y}\0${x}`, L = T.get(z);
|
|
425
|
+
return I(L === void 0 ? R(O, y, z, x) : L, v);
|
|
426
|
+
};
|
|
427
|
+
}, ye = (e) => ({ result: e, json: JSON.stringify(e) }), Oe = Object.freeze(ye(Object.freeze({ status: "error", failure: Object.freeze({ reason: "failed" }) }))), tn = (e) => e.status === "ok" && e.entries.length > 0, rn = ({ ttlMs: e, maxEntries: t, maxBytes: r, now: n }) => {
|
|
428
|
+
const s = at(t, { max: r, of: (l, w) => 2 * w.tree.json.length }), o = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map(), m = (l, w) => {
|
|
429
|
+
const _ = new AbortController(), R = { waiters: 0, superseded: !1, started: !1, loadTree: w }, I = c.get(l), O = (z, L) => {
|
|
430
|
+
const u = ye(z);
|
|
431
|
+
return !(R.superseded || _.signal.aborted) && tn(z) && s.set(l, { tree: u, loadedAt: L }), u;
|
|
432
|
+
}, v = (async () => {
|
|
433
|
+
if (I !== void 0 && await I, _.signal.aborted) return Oe;
|
|
434
|
+
const z = n();
|
|
435
|
+
return R.started = !0, O(await R.loadTree(_.signal, z), z);
|
|
436
|
+
})().finally(() => {
|
|
437
|
+
o.get(l) === y && o.delete(l);
|
|
438
|
+
}), y = { promise: v, controller: _, state: R };
|
|
439
|
+
o.set(l, y);
|
|
440
|
+
const N = v.then(
|
|
441
|
+
() => {
|
|
442
|
+
},
|
|
443
|
+
() => {
|
|
444
|
+
}
|
|
445
|
+
);
|
|
446
|
+
return c.set(l, N), N.then(() => {
|
|
447
|
+
c.get(l) === N && c.delete(l);
|
|
448
|
+
}), y;
|
|
449
|
+
}, g = (l, w, _) => {
|
|
450
|
+
w.state.waiters += 1;
|
|
451
|
+
const R = () => {
|
|
452
|
+
w.state.waiters -= 1, !(w.state.waiters > 0) && (o.get(l) === w && o.delete(l), w.controller.abort());
|
|
453
|
+
};
|
|
454
|
+
return _.addEventListener("abort", R, { once: !0 }), w.promise.finally(() => {
|
|
455
|
+
_.removeEventListener("abort", R);
|
|
456
|
+
});
|
|
457
|
+
}, T = (l) => {
|
|
458
|
+
s.delete(l);
|
|
459
|
+
const w = o.get(l);
|
|
460
|
+
w?.state.started && (w.state.superseded = !0, o.delete(l));
|
|
461
|
+
};
|
|
462
|
+
return {
|
|
463
|
+
load: (l, w, _) => {
|
|
464
|
+
if (w.aborted) return Promise.resolve(Oe);
|
|
465
|
+
const R = s.get(l);
|
|
466
|
+
if (R !== void 0) {
|
|
467
|
+
if (n() - R.loadedAt < e) return Promise.resolve(R.tree);
|
|
468
|
+
s.delete(l);
|
|
469
|
+
}
|
|
470
|
+
const I = o.get(l);
|
|
471
|
+
return g(l, I === void 0 ? m(l, _) : I, w);
|
|
472
|
+
},
|
|
473
|
+
refresh: (l, w, _) => {
|
|
474
|
+
if (w.aborted) return Promise.resolve(Oe);
|
|
475
|
+
T(l);
|
|
476
|
+
const R = o.get(l);
|
|
477
|
+
return R === void 0 ? g(l, m(l, _), w) : (R.state.loadTree = _, g(l, R, w));
|
|
478
|
+
},
|
|
479
|
+
forget: T
|
|
480
|
+
};
|
|
481
|
+
}, nn = (e) => ({
|
|
482
|
+
load: (t, r, n) => n(r, e()).then(ye),
|
|
483
|
+
refresh: (t, r, n) => n(r, e()).then(ye),
|
|
484
|
+
forget: () => {
|
|
485
|
+
}
|
|
486
|
+
}), P = (e) => {
|
|
487
|
+
throw new TypeError(`[markdown-explorer] ${e}`);
|
|
488
|
+
}, on = (e) => {
|
|
489
|
+
if (e === null || typeof e != "object") return null;
|
|
490
|
+
const { name: t, path: r } = e;
|
|
491
|
+
return typeof t != "string" || typeof r != "string" ? null : t === "" ? { path: r, reason: "an empty name" } : te(r) ? null : { path: r, reason: "a path the browser cannot address (empty, too long, a lone surrogate, a control character or a backslash)" };
|
|
492
|
+
}, sn = (e, t, r, n, s) => {
|
|
493
|
+
const o = /* @__PURE__ */ new Set(), c = (g) => {
|
|
494
|
+
if (!Array.isArray(g)) return P("a source returned entries that are not an array");
|
|
495
|
+
const T = [];
|
|
496
|
+
for (const l of g) {
|
|
497
|
+
const w = m(l);
|
|
498
|
+
w !== null && T.push(w);
|
|
499
|
+
}
|
|
500
|
+
return T;
|
|
501
|
+
}, m = (g) => {
|
|
502
|
+
const T = on(g);
|
|
503
|
+
if (T !== null)
|
|
504
|
+
return n(T), null;
|
|
505
|
+
const l = Ot(g);
|
|
506
|
+
if (l !== null) return P(l);
|
|
507
|
+
const w = g;
|
|
508
|
+
if (o.has(w)) return P("a source returned entries that contain themselves");
|
|
509
|
+
const { name: _, path: R, type: I, mimeType: O, children: x, lazy: v } = w;
|
|
510
|
+
if (t.deniesName(_) || t.deniesPath(R)) return null;
|
|
511
|
+
if (!s(R))
|
|
512
|
+
return n({ path: R, reason: "a path outside the source's boundary" }), null;
|
|
513
|
+
const y = O === void 0 ? { name: _, path: R } : { name: _, path: R, mimeType: O };
|
|
514
|
+
if (I === "file") return { ...y, type: I };
|
|
515
|
+
if (v === !0)
|
|
516
|
+
return r ? { ...y, type: I, lazy: !0, children: [] } : P("a lazy directory came from a source without a children function");
|
|
517
|
+
if (x === void 0) return { ...y, type: I };
|
|
518
|
+
o.add(w);
|
|
519
|
+
const N = c(x);
|
|
520
|
+
return o.delete(w), { ...y, type: I, children: N };
|
|
521
|
+
};
|
|
522
|
+
return c(e);
|
|
523
|
+
}, an = /^[\x21\x23-\x7E]{1,256}$/u, lt = (e) => typeof e == "string" && an.test(e), cn = /* @__PURE__ */ new Set(["text", "image", "media", "file"]), un = (e) => typeof e == "string" && cn.has(e), ln = (e, t) => {
|
|
524
|
+
if (e !== null && typeof e == "object") {
|
|
525
|
+
const s = e, { kind: o } = s;
|
|
526
|
+
if (un(o)) {
|
|
527
|
+
if (Object.hasOwn(s, "url")) return P(`a ${o} document carries its version, not a url`);
|
|
528
|
+
if (typeof s.name != "string") return P("a document needs a name string");
|
|
529
|
+
if (!lt(s.version)) return P(`a ${o} document needs a version of 1 to 256 visible ASCII characters other than '"'`);
|
|
530
|
+
const c = t(s.version);
|
|
531
|
+
switch (o) {
|
|
532
|
+
case "image":
|
|
533
|
+
case "file":
|
|
534
|
+
return { kind: o, name: s.name, url: c };
|
|
535
|
+
case "media": {
|
|
536
|
+
const { mediaType: m } = s;
|
|
537
|
+
return St(m) ? { kind: o, name: s.name, url: c, mediaType: m } : P(`unknown media type ${typeof m == "string" ? JSON.stringify(m.slice(0, 64)) : typeof m}`);
|
|
538
|
+
}
|
|
539
|
+
case "text":
|
|
540
|
+
return typeof s.text != "string" || typeof s.truncated != "boolean" ? P("a text document needs text and truncated") : { kind: o, name: s.name, text: s.text, truncated: s.truncated, url: c };
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
const r = Tt(e);
|
|
545
|
+
if (r !== null) return P(r);
|
|
546
|
+
const n = e;
|
|
547
|
+
switch (n.kind) {
|
|
548
|
+
case "failure": {
|
|
549
|
+
const { reason: s, code: o } = n.failure;
|
|
550
|
+
return { kind: "failure", failure: Re(s, o) };
|
|
551
|
+
}
|
|
552
|
+
case "markdown":
|
|
553
|
+
return n.headings.length > xt ? { kind: "failure", failure: Re("too-large") } : { kind: "unsanitized-markdown", name: n.name, html: n.htmlContent, headings: n.headings.map(({ text: s, depth: o, id: c }) => ({ text: s, depth: o, id: c })) };
|
|
554
|
+
}
|
|
555
|
+
}, dn = (e) => {
|
|
556
|
+
if (e === null || typeof e != "object") return P("an asset file must be an object");
|
|
557
|
+
const { name: t, contentType: r, size: n, version: s, open: o } = e;
|
|
558
|
+
return typeof t != "string" || t === "" ? P("an asset file needs a non-empty name") : typeof r != "string" ? P("an asset file needs a contentType string ('' when unknown)") : n !== null && !(typeof n == "number" && Number.isSafeInteger(n) && n >= 0) ? P("an asset file's size must be null or a non-negative safe integer") : lt(s) ? typeof o != "function" ? P("an asset file needs an open function") : Object.freeze({ name: t, contentType: r, size: n, version: s, open: o }) : P(`an asset file needs a version of 1 to 256 visible ASCII characters other than '"'`);
|
|
559
|
+
}, fn = (e) => Array.isArray(e) ? e.map((t) => {
|
|
560
|
+
if (t === null || typeof t != "object") return P("a notice must be an object");
|
|
561
|
+
const { id: r, tone: n, title: s, body: o, action: c } = t;
|
|
562
|
+
if (typeof r != "string" || r === "" || typeof s != "string") return P("a notice needs an id and a title");
|
|
563
|
+
if (!yt(n)) return P(`unknown notice tone ${typeof n == "string" ? JSON.stringify(n.slice(0, 64)) : typeof n}`);
|
|
564
|
+
if (o !== void 0 && typeof o != "string") return P("a notice body must be a string");
|
|
565
|
+
const g = { id: r, tone: n, title: s, ...o === void 0 ? {} : { body: o } };
|
|
566
|
+
if (c === void 0) return g;
|
|
567
|
+
if (c === null || typeof c != "object") return P("a notice action must be an object");
|
|
568
|
+
const { label: T, href: l } = c;
|
|
569
|
+
return typeof T != "string" || !bt(l, kt) ? P("a notice action needs a label and an http(s), mailto or relative href") : { ...g, action: { label: T, href: l } };
|
|
570
|
+
}) : P("notices must be an array"), mn = 4e3, me = Object.freeze({ ttlMs: 3e5, maxEntries: 64, maxBytes: 64 * 1024 * 1024 }), Xe = 32, dt = "private, no-store", pn = Object.freeze({
|
|
571
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
572
|
+
"Cache-Control": dt,
|
|
573
|
+
"X-Content-Type-Options": "nosniff",
|
|
574
|
+
"Content-Security-Policy": "default-src 'none'; frame-ancestors 'none'; sandbox",
|
|
575
|
+
"Cross-Origin-Resource-Policy": "same-origin"
|
|
576
|
+
}), wn = 22, hn = 2147483647, ke = Object.freeze([]), Je = Object.freeze(["contains", "confirm"]), X = (e, t, r) => {
|
|
577
|
+
if (!(r && t === void 0) && typeof t != "function")
|
|
578
|
+
throw new TypeError(`[markdown-explorer] ${e} must be a function; got ${typeof t}`);
|
|
579
|
+
}, ge = (e, t, r) => {
|
|
580
|
+
if (typeof t != "number") throw new TypeError(`[markdown-explorer] ${e} must be a number; got ${typeof t}`);
|
|
581
|
+
if (!Number.isSafeInteger(t) || t <= 0 || t > r) throw new RangeError(`[markdown-explorer] ${e} must be an integer in 1..${String(r)}; got ${String(t)}`);
|
|
582
|
+
return t;
|
|
583
|
+
}, gn = (e) => {
|
|
584
|
+
if (e === null || typeof e != "object" || !Object.isFrozen(e)) throw new TypeError("[markdown-explorer] config must be the value returned by defineExplorerConfig");
|
|
585
|
+
return Lt(e);
|
|
586
|
+
}, En = (e) => {
|
|
587
|
+
if (typeof e != "string") throw new TypeError(`[markdown-explorer] scopeSecret must be a string; got ${typeof e}`);
|
|
588
|
+
if (e.length < Xe) throw new RangeError(`[markdown-explorer] scopeSecret must have at least ${String(Xe)} characters; got ${String(e.length)}`);
|
|
589
|
+
return e;
|
|
590
|
+
}, yn = (e, t) => {
|
|
591
|
+
if (!Bt(t)) throw new TypeError(`[markdown-explorer] ${e}.boundary must be a plain object { contains, confirm? }`);
|
|
592
|
+
for (const r of Object.keys(t))
|
|
593
|
+
if (!Je.includes(r)) throw new RangeError(`[markdown-explorer] ${e}.boundary.${r} is not an option; expected ${Je.join(", ")}`);
|
|
594
|
+
X(`${e}.boundary.contains`, t.contains, !1), X(`${e}.boundary.confirm`, t.confirm, !0);
|
|
595
|
+
}, bn = (e) => {
|
|
596
|
+
if (!Array.isArray(e)) throw new TypeError("[markdown-explorer] sources must be an array");
|
|
597
|
+
const t = e;
|
|
598
|
+
if (t.length === 0) throw new RangeError("[markdown-explorer] sources must hold at least one source");
|
|
599
|
+
const r = /* @__PURE__ */ new Set();
|
|
600
|
+
for (const [n, s] of t.entries()) {
|
|
601
|
+
if (s === null || typeof s != "object") throw new TypeError(`[markdown-explorer] sources[${String(n)}] must be an object`);
|
|
602
|
+
const { id: o, label: c, boundary: m, tree: g, children: T, cacheScope: l } = s;
|
|
603
|
+
if (!zt(o))
|
|
604
|
+
throw new RangeError(`[markdown-explorer] sources[${String(n)}].id must use lowercase letters, digits and "-" and start with a letter or digit; got ${typeof o == "string" ? JSON.stringify(o) : typeof o}`);
|
|
605
|
+
const w = `source ${JSON.stringify(o)}`;
|
|
606
|
+
if (r.has(o)) throw new RangeError(`[markdown-explorer] source ids must be unique; ${JSON.stringify(o)} repeats at sources[${String(n)}]`);
|
|
607
|
+
if (r.add(o), typeof c != "string" && typeof c != "function") throw new TypeError(`[markdown-explorer] ${w}.label must be a string or a function`);
|
|
608
|
+
if (typeof c == "string" && c.trim() === "") throw new RangeError(`[markdown-explorer] ${w}.label must not be blank`);
|
|
609
|
+
if (yn(w, m), X(`${w}.tree`, g, !1), X(`${w}.children`, T, !0), l !== void 0 && l !== "principal" && l !== "shared") throw new RangeError(`[markdown-explorer] ${w}.cacheScope must be "principal" or "shared"; got ${JSON.stringify(l)}`);
|
|
610
|
+
}
|
|
611
|
+
return t;
|
|
612
|
+
}, Sn = (e) => {
|
|
613
|
+
if (e === !1) return !1;
|
|
614
|
+
if (e === void 0) return me;
|
|
615
|
+
if (e === null || typeof e != "object") throw new TypeError("[markdown-explorer] treeCache must be { ttlMs, maxEntries, maxBytes } or false");
|
|
616
|
+
const { ttlMs: t, maxEntries: r, maxBytes: n } = e;
|
|
617
|
+
return {
|
|
618
|
+
ttlMs: t === void 0 ? me.ttlMs : ge("treeCache.ttlMs", t, Number.MAX_SAFE_INTEGER),
|
|
619
|
+
maxEntries: r === void 0 ? me.maxEntries : ge("treeCache.maxEntries", r, Number.MAX_SAFE_INTEGER),
|
|
620
|
+
maxBytes: n === void 0 ? me.maxBytes : ge("treeCache.maxBytes", n, Number.MAX_SAFE_INTEGER)
|
|
621
|
+
};
|
|
622
|
+
}, We = (e) => {
|
|
623
|
+
if (e === null || typeof e != "object") throw new TypeError("[markdown-explorer] authenticate must resolve to { status, ... }");
|
|
624
|
+
const { status: t } = e;
|
|
625
|
+
if (t !== "signed-in" && t !== "signed-out" && t !== "forbidden") throw new TypeError(`[markdown-explorer] authenticate resolved to an unknown status ${JSON.stringify(t)}`);
|
|
626
|
+
return e;
|
|
627
|
+
}, Ye = (e, t) => {
|
|
628
|
+
if (!(t instanceof Response)) throw new TypeError(`[markdown-explorer] ${e} must return a Response`);
|
|
629
|
+
return t;
|
|
630
|
+
}, Ke = (e, t) => {
|
|
631
|
+
if (typeof t != "string" || t === "") throw new TypeError("[markdown-explorer] principalKey must return a non-empty string");
|
|
632
|
+
return Pt("sha256", e).update(t, "utf8").digest("base64url").slice(0, wn);
|
|
633
|
+
}, Tn = (e) => e.view === "multi" ? e.docs : e.doc === null ? [] : [e.doc], pe = (e, t) => {
|
|
634
|
+
const r = e.getAll(t), [n] = r;
|
|
635
|
+
return r.length === 1 && n !== void 0 && n !== "" ? n : null;
|
|
636
|
+
}, ae = (e, t, r, n = {}) => {
|
|
637
|
+
const s = new Headers(r);
|
|
638
|
+
for (const [o, c] of Object.entries({ ...pn, ...n })) s.set(o, c);
|
|
639
|
+
return new Response(t, { status: e, headers: s });
|
|
640
|
+
}, F = (e, t, r) => ae(_t[e.reason], JSON.stringify({ failure: e }), t, r), Ve = (e) => {
|
|
641
|
+
if (e.headers.get(rt) !== Nt) return !1;
|
|
642
|
+
const t = e.headers.get("Sec-Fetch-Site");
|
|
643
|
+
return t === null || t === "same-origin";
|
|
644
|
+
}, xn = ({ parentHeaders: e, loaderHeaders: t, errorHeaders: r }) => {
|
|
645
|
+
const n = new Headers(), s = r === void 0 ? [e, t] : [e, t, r];
|
|
646
|
+
for (const o of s)
|
|
647
|
+
for (const [c, m] of o)
|
|
648
|
+
c !== "set-cookie" && n.set(c, m);
|
|
649
|
+
return n;
|
|
650
|
+
}, Yn = (e) => {
|
|
651
|
+
if (e === null || typeof e != "object") throw new TypeError("[markdown-explorer] the server configuration must be an object");
|
|
652
|
+
const t = gn(e.config), r = En(e.scopeSecret);
|
|
653
|
+
X("authenticate", e.authenticate, !1), X("principalKey", e.principalKey, !1), X("document", e.document, !1), X("asset", e.asset, !1), X("signedOutPage", e.signedOutPage, !1), X("forbiddenPage", e.forbiddenPage, !0), X("notices", e.notices, !0), X("onTreeRefreshed", e.onTreeRefreshed, !0);
|
|
654
|
+
const n = bn(e.sources), s = $t(
|
|
655
|
+
t,
|
|
656
|
+
n.map((i) => i.id)
|
|
657
|
+
), o = e.logger;
|
|
658
|
+
if (o !== void 0 && (o === null || typeof o != "object" || typeof o.error != "function"))
|
|
659
|
+
throw new TypeError("[markdown-explorer] logger must be an object with an error function");
|
|
660
|
+
const c = e.logger === void 0 ? yr : e.logger, m = tt(e.deny), g = e.deferredTimeoutMs === void 0 ? mn : ge("deferredTimeoutMs", e.deferredTimeoutMs, hn), T = Sn(e.treeCache), l = () => Date.now(), w = T === !1 ? nn(l) : rn({ ...T, now: l }), _ = Kr(e.sanitizer, t.multiView.maxPanels), R = en({ iframeHosts: t.iframeHosts, dataPath: t.dataPath, options: _, pool: Zr(Vr(), _) }), I = new Map(n.map((i) => [i.id, i])), O = jt(t), { authenticate: x, principalKey: v, signedOutPage: y, forbiddenPage: N, onTreeRefreshed: z } = e, L = (i, h) => {
|
|
661
|
+
if (typeof i.label == "string") return i.label;
|
|
662
|
+
const d = i.label(h);
|
|
663
|
+
if (typeof d != "string" || d.trim() === "") throw new TypeError(`[markdown-explorer] the label of source ${JSON.stringify(i.id)} must be a non-blank string`);
|
|
664
|
+
return d;
|
|
665
|
+
}, u = (i, h) => i.cacheScope === "shared" ? i.id : `${i.id}\0${h}`, a = (i, h) => {
|
|
666
|
+
const d = (f) => q(c, { operation: "boundary", sourceId: i.id, path: h, error: f });
|
|
667
|
+
let p;
|
|
668
|
+
try {
|
|
669
|
+
p = i.boundary.contains(h);
|
|
670
|
+
} catch (f) {
|
|
671
|
+
return d(f), !1;
|
|
672
|
+
}
|
|
673
|
+
return typeof p == "boolean" ? p : (Ce(p, d), d(new TypeError(`[markdown-explorer] boundary.contains of source ${JSON.stringify(i.id)} must return a boolean; got ${typeof p}`)), !1);
|
|
674
|
+
}, E = async (i, h, d) => {
|
|
675
|
+
const { confirm: p } = i.boundary;
|
|
676
|
+
if (p === void 0) return { status: "inside", readPath: d };
|
|
677
|
+
let f;
|
|
678
|
+
try {
|
|
679
|
+
f = await p(h, d);
|
|
680
|
+
} catch (K) {
|
|
681
|
+
return { status: "error", failure: Q(K, { operation: "boundary", sourceId: i.id, path: d }, h.signal, c) };
|
|
682
|
+
}
|
|
683
|
+
const S = (K) => (q(c, { operation: "boundary", sourceId: i.id, path: d, error: new TypeError(`[markdown-explorer] boundary.confirm of source ${JSON.stringify(i.id)} ${K}`) }), { status: "outside" });
|
|
684
|
+
if (f === null || typeof f != "object") return S(`must resolve to { inside, readPath? }; got ${f === null ? "null" : typeof f}`);
|
|
685
|
+
const { inside: j, readPath: C } = f;
|
|
686
|
+
return j === !1 ? { status: "outside" } : j !== !0 || typeof C != "string" ? S("must resolve to { inside: false } or { inside: true, readPath: string }") : !te(C) || m.deniesPath(C) ? (q(c, { operation: "boundary", sourceId: i.id, path: d, error: new RangeError(`[markdown-explorer] boundary.confirm of source ${JSON.stringify(i.id)} answered a readPath the explorer does not serve (unacceptable or denied)`) }), { status: "error", failure: { reason: "failed" } }) : { status: "inside", readPath: C };
|
|
687
|
+
}, b = async (i, h) => {
|
|
688
|
+
const d = n.filter((f) => a(f, h));
|
|
689
|
+
if (d.length === 0) return { status: "error", failure: { reason: "forbidden" } };
|
|
690
|
+
if (d.some((f) => f.boundary.confirm === void 0)) return { status: "inside", readPath: h };
|
|
691
|
+
let p = null;
|
|
692
|
+
for (const f of d) {
|
|
693
|
+
const S = await E(f, i, h);
|
|
694
|
+
if (S.status === "inside") return S;
|
|
695
|
+
S.status === "error" && p === null && (p = S.failure);
|
|
696
|
+
}
|
|
697
|
+
return { status: "error", failure: p === null ? { reason: "forbidden" } : p };
|
|
698
|
+
}, k = (i, h, d) => {
|
|
699
|
+
const p = ({ path: f, reason: S }) => Tr(c, { operation: d.operation, sourceId: d.sourceId, path: f, error: new TypeError(`[markdown-explorer] an entry with ${S} was left out`) });
|
|
700
|
+
try {
|
|
701
|
+
return { status: "ok", entries: sn(i, m, h.children !== void 0, p, (f) => a(h, f)) };
|
|
702
|
+
} catch (f) {
|
|
703
|
+
return q(c, { ...d, error: f }), { status: "error", failure: { reason: "failed" } };
|
|
704
|
+
}
|
|
705
|
+
}, A = async (i, h, d) => {
|
|
706
|
+
let p;
|
|
707
|
+
try {
|
|
708
|
+
p = await i.tree(h);
|
|
709
|
+
} catch (S) {
|
|
710
|
+
return { status: "error", failure: Q(S, { operation: "tree", sourceId: i.id }, h.signal, c) };
|
|
711
|
+
}
|
|
712
|
+
const f = k(p, i, { operation: "tree", sourceId: i.id });
|
|
713
|
+
return f.status === "ok" ? { status: "ok", entries: f.entries, loadedAt: d } : f;
|
|
714
|
+
}, D = (i, h, d, p) => (f, S) => A(i, { principal: h, request: d, signal: f, refresh: p }, S), M = (i, h, d, p, f) => w.load(u(i, d), f, D(i, h, p, !1)), U = (i, h, d, p) => w.refresh(u(i, d), p.signal, D(i, h, p, !0)), G = (i, h) => {
|
|
715
|
+
if (z === void 0) return;
|
|
716
|
+
const d = (p) => q(c, { operation: "refresh", sourceId: i.id, error: p });
|
|
717
|
+
try {
|
|
718
|
+
Ce(z(h), d);
|
|
719
|
+
} catch (p) {
|
|
720
|
+
d(p);
|
|
721
|
+
}
|
|
722
|
+
}, B = async (i, h, d, p) => {
|
|
723
|
+
if (m.deniesPath(p) || !a(i, p)) return { status: "error", failure: { reason: "forbidden" } };
|
|
724
|
+
const f = await E(i, d, p);
|
|
725
|
+
if (f.status === "outside") return { status: "error", failure: { reason: "forbidden" } };
|
|
726
|
+
if (f.status === "error") return { status: "error", failure: f.failure };
|
|
727
|
+
let S;
|
|
728
|
+
try {
|
|
729
|
+
S = await h(d, p, f.readPath);
|
|
730
|
+
} catch (j) {
|
|
731
|
+
return { status: "error", failure: Q(j, { operation: "children", sourceId: i.id, path: p }, d.signal, c) };
|
|
732
|
+
}
|
|
733
|
+
return k(S, i, { operation: "children", sourceId: i.id, path: p });
|
|
734
|
+
}, Z = async (i, h, d) => {
|
|
735
|
+
if (m.deniesPath(d)) return ee("forbidden");
|
|
736
|
+
const p = await b(i, d);
|
|
737
|
+
if (p.status === "error") return { kind: "failure", failure: p.failure };
|
|
738
|
+
let f;
|
|
739
|
+
try {
|
|
740
|
+
f = await e.document(i, d, p.readPath);
|
|
741
|
+
} catch (C) {
|
|
742
|
+
return { kind: "failure", failure: Q(C, { operation: "document", path: d }, i.signal, c) };
|
|
743
|
+
}
|
|
744
|
+
let S;
|
|
745
|
+
try {
|
|
746
|
+
S = ln(f, (C) => It(t.dataPath, d, C));
|
|
747
|
+
} catch (C) {
|
|
748
|
+
return q(c, { operation: "document", path: d, error: C }), ee("failed");
|
|
749
|
+
}
|
|
750
|
+
if (S.kind !== "unsanitized-markdown") return S;
|
|
751
|
+
const j = await R(S.html, { scope: h, signal: i.signal });
|
|
752
|
+
return j.status === "sanitized" ? { kind: "markdown", name: S.name, htmlContent: j.html, headings: S.headings } : (j.cause !== null && q(c, { operation: "document", path: d, error: j.cause }), ee(j.reason));
|
|
753
|
+
}, $ = async (i) => {
|
|
754
|
+
if (e.notices === void 0) return ke;
|
|
755
|
+
try {
|
|
756
|
+
return fn(await e.notices(i));
|
|
757
|
+
} catch (h) {
|
|
758
|
+
return Q(h, { operation: "notices" }, i.signal, c), ke;
|
|
759
|
+
}
|
|
760
|
+
}, J = async (i, h, d) => {
|
|
761
|
+
const p = pe(h, de.path);
|
|
762
|
+
if (p === null || !te(p)) return F({ reason: "invalid" }, d);
|
|
763
|
+
if (m.deniesPath(p))
|
|
764
|
+
return xr(c, { operation: "asset", path: p }), F({ reason: "forbidden" }, d);
|
|
765
|
+
const f = await b(i, p);
|
|
766
|
+
if (f.status === "error") return F(f.failure, d);
|
|
767
|
+
try {
|
|
768
|
+
const S = dn(await e.asset(i, p, f.readPath));
|
|
769
|
+
return await Er(i.request, S, d);
|
|
770
|
+
} catch (S) {
|
|
771
|
+
return F(Q(S, { operation: "asset", path: p }, i.signal, c), d);
|
|
772
|
+
}
|
|
773
|
+
}, W = async ({ request: i, url: h, params: d }) => {
|
|
774
|
+
const p = We(await x(i)), f = new Headers(p.headers);
|
|
775
|
+
if (f.set("Cache-Control", dt), p.status === "signed-out") throw Ye("signedOutPage", y(i, f));
|
|
776
|
+
if (p.status === "forbidden") throw N === void 0 ? Ie(null, { status: 403, headers: f }) : Ye("forbiddenPage", N(i, f));
|
|
777
|
+
const S = Dt(s, d["*"], h.searchParams);
|
|
778
|
+
if (S.kind === "redirect") throw vt(S.href, { headers: f });
|
|
779
|
+
const { location: j } = S, { principal: C } = p, K = Ke(r, v(C)), le = I.get(j.source);
|
|
780
|
+
if (le === void 0) throw new RangeError(`[markdown-explorer] no source for ${JSON.stringify(j.source)}`);
|
|
781
|
+
const re = { signal: i.signal, timeoutMs: g, late: null, handOverMs: g }, V = {
|
|
782
|
+
scope: K,
|
|
783
|
+
sources: n.map((H) => ({ id: H.id, label: L(H, C) })),
|
|
784
|
+
settings: Ct(Ht(i.headers.get("Cookie"), t.settingsCookie), O),
|
|
785
|
+
tree: j.view === "single" ? null : {
|
|
786
|
+
sourceId: j.source,
|
|
787
|
+
result: Te(re, (H) => M(le, C, K, i, H).then((se) => se.result))
|
|
788
|
+
},
|
|
789
|
+
documents: Tn(j).map((H) => ({ path: H, document: Te(re, (se) => Z({ principal: C, request: i, signal: se, refresh: !1 }, K, H)) })),
|
|
790
|
+
notices: Te({ signal: i.signal, timeoutMs: g, late: ke, handOverMs: 0 }, (H) => $({ principal: C, request: i, signal: H, refresh: !1 }))
|
|
791
|
+
};
|
|
792
|
+
return Ie(V, { headers: f });
|
|
793
|
+
}, Y = async (i, h, d) => {
|
|
794
|
+
let p;
|
|
795
|
+
try {
|
|
796
|
+
p = We(await x(h));
|
|
797
|
+
} catch (H) {
|
|
798
|
+
return F(Q(H, { operation: "authenticate" }, h.signal, c));
|
|
799
|
+
}
|
|
800
|
+
const f = new Headers(p.headers);
|
|
801
|
+
if (p.status === "signed-out") return F({ reason: "unauthorized" }, f);
|
|
802
|
+
if (p.status === "forbidden") return F({ reason: "forbidden" }, f);
|
|
803
|
+
const { principal: S } = p;
|
|
804
|
+
if (i === "asset") return J({ principal: S, request: h, signal: h.signal, refresh: !1 }, d, f);
|
|
805
|
+
const j = Ke(r, v(S));
|
|
806
|
+
f.set(At, j);
|
|
807
|
+
const C = { principal: S, request: h, signal: h.signal, refresh: !1 }, K = () => F({ reason: "invalid" }, f);
|
|
808
|
+
if (i === "document") {
|
|
809
|
+
const H = pe(d, de.path);
|
|
810
|
+
if (H === null || !te(H)) return K();
|
|
811
|
+
const se = h.headers.has(rt);
|
|
812
|
+
return se && !Ve(h) ? F({ reason: "forbidden" }, f) : ae(200, JSON.stringify(await Z({ ...C, refresh: se }, j, H)), f);
|
|
813
|
+
}
|
|
814
|
+
const le = pe(d, de.source), re = pe(d, de.path);
|
|
815
|
+
if (le === null || i === "children" && (re === null || !te(re))) return K();
|
|
816
|
+
const V = I.get(le);
|
|
817
|
+
if (V === void 0) return F({ reason: "not-found" }, f);
|
|
818
|
+
if (i === "tree") return ae(200, (await M(V, S, j, h, h.signal)).json, f);
|
|
819
|
+
if (i === "refresh") {
|
|
820
|
+
const H = await U(V, S, j, h);
|
|
821
|
+
return h.signal.aborted || G(V, u(V, j)), ae(200, H.json, f);
|
|
822
|
+
}
|
|
823
|
+
return V.children === void 0 || re === null ? F({ reason: "not-found" }, f) : ae(200, JSON.stringify(await B(V, V.children, C, re)), f);
|
|
824
|
+
}, ue = async (i, h, d) => {
|
|
825
|
+
if (new URL(i.url).pathname.endsWith(".data")) return F({ reason: "not-found" });
|
|
826
|
+
if (!Rt(d)) return F({ reason: "not-found" });
|
|
827
|
+
const p = i.method.toUpperCase(), f = d === "refresh";
|
|
828
|
+
if (f ? p !== "POST" : p !== "GET" && p !== "HEAD") return ae(405, JSON.stringify({ failure: { reason: "invalid" } }), void 0, { Allow: f ? "POST" : "GET, HEAD" });
|
|
829
|
+
if (f && !Ve(i)) return F({ reason: "forbidden" });
|
|
830
|
+
try {
|
|
831
|
+
return await Y(d, i, h.searchParams);
|
|
832
|
+
} catch (S) {
|
|
833
|
+
return F(Q(S, { operation: d }, i.signal, c));
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
return Object.freeze({
|
|
837
|
+
loader: W,
|
|
838
|
+
headers: xn,
|
|
839
|
+
dataLoader: ({ request: i, url: h, params: d }) => ue(i, h, d["*"]),
|
|
840
|
+
dataAction: ({ request: i, url: h, params: d }) => ue(i, h, d["*"]),
|
|
841
|
+
forgetTree: (i) => {
|
|
842
|
+
if (typeof i != "string") throw new TypeError(`[markdown-explorer] forgetTree needs the string key onTreeRefreshed received; got ${typeof i}`);
|
|
843
|
+
w.forget(i);
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
}, we = Object.freeze({ maxEntries: 2e4, maxDocumentBytes: 2 * 1024 * 1024, textPreviewBytes: 8 * 1024, walkConcurrency: 8 }), On = /* @__PURE__ */ new Set(["md", "markdown", "mdx"]), ft = new Map([
|
|
847
|
+
...[
|
|
848
|
+
["png", "image/png"],
|
|
849
|
+
["jpg", "image/jpeg"],
|
|
850
|
+
["jpeg", "image/jpeg"],
|
|
851
|
+
["gif", "image/gif"],
|
|
852
|
+
["svg", "image/svg+xml"],
|
|
853
|
+
["webp", "image/webp"],
|
|
854
|
+
["ico", "image/x-icon"],
|
|
855
|
+
["bmp", "image/bmp"],
|
|
856
|
+
["tif", "image/tiff"],
|
|
857
|
+
["tiff", "image/tiff"],
|
|
858
|
+
["avif", "image/avif"]
|
|
859
|
+
].map(([e, t]) => [e, { kind: "image", contentType: t }]),
|
|
860
|
+
...[
|
|
861
|
+
["mp4", "video/mp4"],
|
|
862
|
+
["m4v", "video/mp4"],
|
|
863
|
+
["webm", "video/webm"],
|
|
864
|
+
["mov", "video/quicktime"],
|
|
865
|
+
["avi", "video/x-msvideo"],
|
|
866
|
+
["mkv", "video/x-matroska"],
|
|
867
|
+
["ogv", "video/ogg"]
|
|
868
|
+
].map(([e, t]) => [e, { kind: "media", mediaType: "video", contentType: t }]),
|
|
869
|
+
...[
|
|
870
|
+
["mp3", "audio/mpeg"],
|
|
871
|
+
["wav", "audio/wav"],
|
|
872
|
+
["ogg", "audio/ogg"],
|
|
873
|
+
["oga", "audio/ogg"],
|
|
874
|
+
["opus", "audio/ogg"],
|
|
875
|
+
["m4a", "audio/mp4"],
|
|
876
|
+
["flac", "audio/flac"],
|
|
877
|
+
["aac", "audio/aac"],
|
|
878
|
+
["wma", "audio/x-ms-wma"]
|
|
879
|
+
].map(([e, t]) => [e, { kind: "media", mediaType: "audio", contentType: t }]),
|
|
880
|
+
["pdf", { kind: "media", mediaType: "pdf", contentType: "application/pdf" }]
|
|
881
|
+
]), kn = /* @__PURE__ */ new Set([
|
|
882
|
+
"txt",
|
|
883
|
+
"text",
|
|
884
|
+
"log",
|
|
885
|
+
"csv",
|
|
886
|
+
"tsv",
|
|
887
|
+
"json",
|
|
888
|
+
"jsonc",
|
|
889
|
+
"json5",
|
|
890
|
+
"ndjson",
|
|
891
|
+
"yaml",
|
|
892
|
+
"yml",
|
|
893
|
+
"toml",
|
|
894
|
+
"ini",
|
|
895
|
+
"cfg",
|
|
896
|
+
"conf",
|
|
897
|
+
"properties",
|
|
898
|
+
"xml",
|
|
899
|
+
"xsl",
|
|
900
|
+
"xsd",
|
|
901
|
+
"dtd",
|
|
902
|
+
"html",
|
|
903
|
+
"htm",
|
|
904
|
+
"xhtml",
|
|
905
|
+
"css",
|
|
906
|
+
"scss",
|
|
907
|
+
"sass",
|
|
908
|
+
"less",
|
|
909
|
+
"styl",
|
|
910
|
+
"js",
|
|
911
|
+
"mjs",
|
|
912
|
+
"cjs",
|
|
913
|
+
"jsx",
|
|
914
|
+
"ts",
|
|
915
|
+
"mts",
|
|
916
|
+
"cts",
|
|
917
|
+
"tsx",
|
|
918
|
+
"vue",
|
|
919
|
+
"svelte",
|
|
920
|
+
"astro",
|
|
921
|
+
"py",
|
|
922
|
+
"pyi",
|
|
923
|
+
"rb",
|
|
924
|
+
"go",
|
|
925
|
+
"rs",
|
|
926
|
+
"java",
|
|
927
|
+
"kt",
|
|
928
|
+
"kts",
|
|
929
|
+
"scala",
|
|
930
|
+
"groovy",
|
|
931
|
+
"gradle",
|
|
932
|
+
"c",
|
|
933
|
+
"h",
|
|
934
|
+
"cc",
|
|
935
|
+
"cpp",
|
|
936
|
+
"cxx",
|
|
937
|
+
"hpp",
|
|
938
|
+
"hh",
|
|
939
|
+
"hxx",
|
|
940
|
+
"cs",
|
|
941
|
+
"fs",
|
|
942
|
+
"vb",
|
|
943
|
+
"swift",
|
|
944
|
+
"m",
|
|
945
|
+
"mm",
|
|
946
|
+
"php",
|
|
947
|
+
"pl",
|
|
948
|
+
"pm",
|
|
949
|
+
"r",
|
|
950
|
+
"jl",
|
|
951
|
+
"lua",
|
|
952
|
+
"dart",
|
|
953
|
+
"ex",
|
|
954
|
+
"exs",
|
|
955
|
+
"erl",
|
|
956
|
+
"hrl",
|
|
957
|
+
"hs",
|
|
958
|
+
"ml",
|
|
959
|
+
"mli",
|
|
960
|
+
"clj",
|
|
961
|
+
"cljs",
|
|
962
|
+
"elm",
|
|
963
|
+
"nim",
|
|
964
|
+
"zig",
|
|
965
|
+
"v",
|
|
966
|
+
"sol",
|
|
967
|
+
"tf",
|
|
968
|
+
"hcl",
|
|
969
|
+
"nix",
|
|
970
|
+
"sh",
|
|
971
|
+
"bash",
|
|
972
|
+
"zsh",
|
|
973
|
+
"fish",
|
|
974
|
+
"ps1",
|
|
975
|
+
"psm1",
|
|
976
|
+
"bat",
|
|
977
|
+
"cmd",
|
|
978
|
+
"sql",
|
|
979
|
+
"graphql",
|
|
980
|
+
"gql",
|
|
981
|
+
"proto",
|
|
982
|
+
"thrift",
|
|
983
|
+
"avsc",
|
|
984
|
+
"rst",
|
|
985
|
+
"adoc",
|
|
986
|
+
"asciidoc",
|
|
987
|
+
"tex",
|
|
988
|
+
"bib",
|
|
989
|
+
"org",
|
|
990
|
+
"textile",
|
|
991
|
+
"diff",
|
|
992
|
+
"patch",
|
|
993
|
+
"srt",
|
|
994
|
+
"vtt",
|
|
995
|
+
"ics",
|
|
996
|
+
"vcf",
|
|
997
|
+
"gitignore",
|
|
998
|
+
"gitattributes",
|
|
999
|
+
"editorconfig",
|
|
1000
|
+
"dockerignore",
|
|
1001
|
+
"npmignore",
|
|
1002
|
+
"prettierrc",
|
|
1003
|
+
"eslintrc",
|
|
1004
|
+
"babelrc",
|
|
1005
|
+
"mailmap"
|
|
1006
|
+
]), Rn = /* @__PURE__ */ new Set([
|
|
1007
|
+
"readme",
|
|
1008
|
+
"license",
|
|
1009
|
+
"licence",
|
|
1010
|
+
"copying",
|
|
1011
|
+
"notice",
|
|
1012
|
+
"authors",
|
|
1013
|
+
"contributors",
|
|
1014
|
+
"changelog",
|
|
1015
|
+
"changes",
|
|
1016
|
+
"todo",
|
|
1017
|
+
"makefile",
|
|
1018
|
+
"dockerfile",
|
|
1019
|
+
"containerfile",
|
|
1020
|
+
"procfile",
|
|
1021
|
+
"gemfile",
|
|
1022
|
+
"rakefile",
|
|
1023
|
+
"vagrantfile",
|
|
1024
|
+
"jenkinsfile",
|
|
1025
|
+
"brewfile",
|
|
1026
|
+
"codeowners"
|
|
1027
|
+
]), mt = typeof ce.O_NOFOLLOW == "number" ? ce.O_NOFOLLOW : 0, pt = typeof ce.O_NONBLOCK == "number" ? ce.O_NONBLOCK : 0, Ze = (e) => `${e.size}-${e.mtimeNs}`, _n = (e) => e !== "" && e !== "." && e !== ".." && !e.includes(":") && !e.endsWith(".") && !e.endsWith(" "), Qe = (e) => te(e) && e === e.normalize("NFC") && e.split("/").every(_n), wt = (e) => {
|
|
1028
|
+
const t = e.toLowerCase(), r = t.lastIndexOf(".");
|
|
1029
|
+
return r < 0 ? "" : t.slice(r + 1);
|
|
1030
|
+
}, Nn = (e) => {
|
|
1031
|
+
const t = wt(e);
|
|
1032
|
+
if (On.has(t)) return { kind: "markdown" };
|
|
1033
|
+
const r = ft.get(t);
|
|
1034
|
+
if (r !== void 0) return r.kind === "image" ? { kind: "image" } : { kind: "media", mediaType: r.mediaType };
|
|
1035
|
+
const n = e.toLowerCase();
|
|
1036
|
+
return kn.has(t) || !n.includes(".") && Rn.has(n) ? { kind: "text" } : { kind: "file" };
|
|
1037
|
+
}, ht = (e) => {
|
|
1038
|
+
switch (typeof e == "object" && e !== null ? e.code : void 0) {
|
|
1039
|
+
case "ENOENT":
|
|
1040
|
+
case "ENOTDIR":
|
|
1041
|
+
return "not-found";
|
|
1042
|
+
case "ELOOP":
|
|
1043
|
+
case "EACCES":
|
|
1044
|
+
case "EPERM":
|
|
1045
|
+
return "forbidden";
|
|
1046
|
+
case "ENAMETOOLONG":
|
|
1047
|
+
return "invalid";
|
|
1048
|
+
case "EISDIR":
|
|
1049
|
+
return "unsupported";
|
|
1050
|
+
default:
|
|
1051
|
+
return null;
|
|
1052
|
+
}
|
|
1053
|
+
}, gt = (e) => {
|
|
1054
|
+
const t = ht(e);
|
|
1055
|
+
return t === null ? e : new Ee(t);
|
|
1056
|
+
}, An = (e) => {
|
|
1057
|
+
const t = typeof e == "object" && e !== null ? e.code : void 0;
|
|
1058
|
+
return t === "EACCES" || t === "EPERM" || t === "ENOENT" || t === "ENOTDIR";
|
|
1059
|
+
}, qe = async (e, t) => {
|
|
1060
|
+
const r = [];
|
|
1061
|
+
for await (const n of e.createReadStream({ start: 0, end: t - 1, autoClose: !1 }))
|
|
1062
|
+
r.push(n);
|
|
1063
|
+
return Buffer.concat(r);
|
|
1064
|
+
}, In = (e, t) => e !== null ? { start: e.offset, end: e.offset + e.length - 1 } : t === 0n ? {} : { start: 0, end: Number(t) - 1 }, Mn = async (e, t, r, n) => {
|
|
1065
|
+
let s;
|
|
1066
|
+
try {
|
|
1067
|
+
s = await nt(e, ce.O_RDONLY | mt | pt);
|
|
1068
|
+
const g = await s.stat({ bigint: !0 });
|
|
1069
|
+
if (!g.isFile() || g.ino !== t.ino || g.dev !== t.dev || g.size !== t.size || g.mtimeNs !== t.mtimeNs) throw new Ee("not-found");
|
|
1070
|
+
} catch (g) {
|
|
1071
|
+
throw await s?.close(), gt(g);
|
|
1072
|
+
}
|
|
1073
|
+
const o = s.createReadStream(In(r, t.size)), c = qt.toWeb(o), m = () => {
|
|
1074
|
+
o.destroy(n.reason);
|
|
1075
|
+
};
|
|
1076
|
+
return n.aborted ? m() : n.addEventListener("abort", m, { once: !0 }), c;
|
|
1077
|
+
}, Pn = (e, t) => {
|
|
1078
|
+
const r = Ft.compare(e, t);
|
|
1079
|
+
return r !== 0 ? r : e === t ? 0 : e < t ? -1 : 1;
|
|
1080
|
+
}, vn = (e) => {
|
|
1081
|
+
let t = 0;
|
|
1082
|
+
const r = [];
|
|
1083
|
+
return async (n) => {
|
|
1084
|
+
t < e ? t += 1 : await new Promise((s) => r.push(s));
|
|
1085
|
+
try {
|
|
1086
|
+
return await n();
|
|
1087
|
+
} finally {
|
|
1088
|
+
const s = r.shift();
|
|
1089
|
+
s === void 0 ? t -= 1 : s();
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
}, $n = (e, t) => {
|
|
1093
|
+
if (e.isSymbolicLink()) return "symbolic-link";
|
|
1094
|
+
if (!(e.isDirectory() || e.isFile())) return "special-file";
|
|
1095
|
+
const { name: r } = e;
|
|
1096
|
+
return r !== r.normalize("NFC") ? "not-nfc" : r.includes(":") ? "colon" : r.endsWith(".") || r.endsWith(" ") ? "trailing-dot-or-space" : te(r) ? te(t) ? null : "path-too-long" : "control-character-or-backslash";
|
|
1097
|
+
}, jn = /* @__PURE__ */ new Set(["id", "label", "root", "pathPrefix", "parse", "deny", "maxEntries", "maxDocumentBytes", "textPreviewBytes", "walkConcurrency", "cacheScope", "reportSkipped"]), he = (e, t, r) => {
|
|
1098
|
+
if (t === void 0) return r;
|
|
1099
|
+
if (typeof t != "number" || !Number.isSafeInteger(t) || t <= 0) throw new RangeError(`[markdown-explorer] ${e} must be a positive integer; got ${String(t)}`);
|
|
1100
|
+
return t;
|
|
1101
|
+
}, Kn = (e) => {
|
|
1102
|
+
if (e === null || typeof e != "object") throw new TypeError("[markdown-explorer] createFileSystemSource needs an options object");
|
|
1103
|
+
for (const a of Object.keys(e))
|
|
1104
|
+
if (!jn.has(a)) throw new TypeError(`[markdown-explorer] createFileSystemSource does not know the option ${JSON.stringify(a)}`);
|
|
1105
|
+
const { id: t, label: r, root: n, parse: s } = e;
|
|
1106
|
+
if (typeof t != "string" || t === "") throw new TypeError("[markdown-explorer] a file-system source needs a non-empty id");
|
|
1107
|
+
if (typeof r != "string" || r.trim() === "") throw new TypeError("[markdown-explorer] a file-system source needs a non-blank label");
|
|
1108
|
+
if (typeof s != "function") throw new TypeError("[markdown-explorer] a file-system source needs a parse function");
|
|
1109
|
+
if (typeof n != "string" || !Me(n)) throw new RangeError(`[markdown-explorer] root must be an absolute path; got ${JSON.stringify(n)}`);
|
|
1110
|
+
let o;
|
|
1111
|
+
try {
|
|
1112
|
+
o = Gt(n).isDirectory();
|
|
1113
|
+
} catch (a) {
|
|
1114
|
+
throw new RangeError(`[markdown-explorer] root does not exist: ${n}`, { cause: a });
|
|
1115
|
+
}
|
|
1116
|
+
if (!o) throw new RangeError(`[markdown-explorer] root is not a directory: ${n}`);
|
|
1117
|
+
const c = Xt(n), m = e.pathPrefix;
|
|
1118
|
+
if (typeof m != "string") throw new TypeError('[markdown-explorer] pathPrefix is required: "" for a source over the whole root, or relative segments ending with "/"');
|
|
1119
|
+
if (m !== "" && !(m.endsWith("/") && Qe(m.slice(0, -1))))
|
|
1120
|
+
throw new RangeError(`[markdown-explorer] pathPrefix must be empty or relative segments ending with "/"; got ${JSON.stringify(m)}`);
|
|
1121
|
+
const g = tt(e.deny), T = he("maxEntries", e.maxEntries, we.maxEntries), l = he("maxDocumentBytes", e.maxDocumentBytes, we.maxDocumentBytes), w = he("textPreviewBytes", e.textPreviewBytes, we.textPreviewBytes), _ = he("walkConcurrency", e.walkConcurrency, we.walkConcurrency), R = e.cacheScope === void 0 ? "principal" : e.cacheScope;
|
|
1122
|
+
if (R !== "principal" && R !== "shared") throw new RangeError(`[markdown-explorer] cacheScope must be "principal" or "shared"; got ${JSON.stringify(R)}`);
|
|
1123
|
+
const I = e.reportSkipped;
|
|
1124
|
+
if (I !== void 0 && typeof I != "function") throw new TypeError("[markdown-explorer] reportSkipped must be a function");
|
|
1125
|
+
const O = I === void 0 ? (a) => {
|
|
1126
|
+
console.warn(`[markdown-explorer] the file-system source ${JSON.stringify(a.sourceId)} does not list ${JSON.stringify(a.path)}: ${a.reason}`);
|
|
1127
|
+
} : I, x = /* @__PURE__ */ new Set(), v = (a) => {
|
|
1128
|
+
const E = Yt(c, ...a.split("/")), b = Pe(c, E);
|
|
1129
|
+
return b === "" || b === ".." || b.startsWith(`..${ve}`) || Me(b) ? null : E;
|
|
1130
|
+
}, y = (a, E) => {
|
|
1131
|
+
const b = `${E}\0${a}`;
|
|
1132
|
+
x.has(b) || x.size >= T || (x.add(b), O({ sourceId: t, path: a, reason: E }));
|
|
1133
|
+
}, N = async ({ signal: a }) => {
|
|
1134
|
+
a.throwIfAborted();
|
|
1135
|
+
const E = new AbortController(), b = () => {
|
|
1136
|
+
E.abort(a.reason);
|
|
1137
|
+
};
|
|
1138
|
+
a.addEventListener("abort", b, { once: !0 });
|
|
1139
|
+
const k = vn(_);
|
|
1140
|
+
let A = 0;
|
|
1141
|
+
const D = async (M, U) => {
|
|
1142
|
+
const G = await k(async () => {
|
|
1143
|
+
E.signal.throwIfAborted();
|
|
1144
|
+
try {
|
|
1145
|
+
return await Qt(M, { withFileTypes: !0 });
|
|
1146
|
+
} catch ($) {
|
|
1147
|
+
if (U === "" || !An($)) throw $;
|
|
1148
|
+
return null;
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
if (E.signal.throwIfAborted(), G === null)
|
|
1152
|
+
return y(`${m}${U}`, "unreadable-directory"), null;
|
|
1153
|
+
const B = [];
|
|
1154
|
+
for (const $ of G) {
|
|
1155
|
+
if (g.deniesName($.name)) continue;
|
|
1156
|
+
const J = U === "" ? $.name : `${U}/${$.name}`, W = `${m}${J}`, Y = $n($, W);
|
|
1157
|
+
if (Y !== null) {
|
|
1158
|
+
y(W, Y);
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
B.push({ dirent: $, relativePath: J, path: W });
|
|
1162
|
+
}
|
|
1163
|
+
if (A += B.length, A > T) throw new Ee("too-large");
|
|
1164
|
+
return B.sort(($, J) => Pn($.dirent.name, J.dirent.name)), (await Promise.all(
|
|
1165
|
+
B.map(async ({ dirent: $, relativePath: J, path: W }) => {
|
|
1166
|
+
if (!$.isDirectory()) return { name: $.name, path: W, type: "file" };
|
|
1167
|
+
const Y = await D(Wt(M, $.name), J);
|
|
1168
|
+
return Y === null ? null : { name: $.name, path: W, type: "directory", children: Y };
|
|
1169
|
+
})
|
|
1170
|
+
)).filter(($) => $ !== null);
|
|
1171
|
+
};
|
|
1172
|
+
try {
|
|
1173
|
+
const M = await D(c, "");
|
|
1174
|
+
if (M === null) throw new Error("[markdown-explorer] the source's root could not be listed");
|
|
1175
|
+
return M;
|
|
1176
|
+
} catch (M) {
|
|
1177
|
+
throw E.abort(), M;
|
|
1178
|
+
} finally {
|
|
1179
|
+
a.removeEventListener("abort", b);
|
|
1180
|
+
}
|
|
1181
|
+
}, z = async (a) => {
|
|
1182
|
+
if (!a.startsWith(m)) return { status: "refused", reason: "not-found" };
|
|
1183
|
+
const E = a.slice(m.length);
|
|
1184
|
+
if (!Qe(E)) return { status: "refused", reason: "invalid" };
|
|
1185
|
+
if (g.deniesPath(E)) return { status: "refused", reason: "forbidden" };
|
|
1186
|
+
const b = v(E);
|
|
1187
|
+
if (b === null) return { status: "refused", reason: "forbidden" };
|
|
1188
|
+
const k = await Vt(b, { bigint: !0 });
|
|
1189
|
+
if (k.isSymbolicLink()) return { status: "refused", reason: "forbidden" };
|
|
1190
|
+
if (!k.isFile()) return { status: "refused", reason: "unsupported" };
|
|
1191
|
+
const A = await Zt(b);
|
|
1192
|
+
return A !== b || g.deniesPath(Pe(c, A).split(ve).join("/")) ? { status: "refused", reason: "forbidden" } : { status: "file", target: b, name: E.slice(E.lastIndexOf("/") + 1), stats: k };
|
|
1193
|
+
}, L = async (a, E, b) => {
|
|
1194
|
+
let k;
|
|
1195
|
+
try {
|
|
1196
|
+
const A = await z(b);
|
|
1197
|
+
if (A.status === "refused") return ee(A.reason);
|
|
1198
|
+
const { target: D, name: M, stats: U } = A, G = Nn(M), B = Ze(U);
|
|
1199
|
+
if (G.kind === "image") return { kind: "image", name: M, version: B };
|
|
1200
|
+
if (G.kind === "media") return { kind: "media", name: M, mediaType: G.mediaType, version: B };
|
|
1201
|
+
if (G.kind === "file") return { kind: "file", name: M, version: B };
|
|
1202
|
+
k = await nt(D, ce.O_RDONLY | mt | pt);
|
|
1203
|
+
const Z = await k.stat({ bigint: !0 });
|
|
1204
|
+
if (!Z.isFile() || Z.ino !== U.ino || Z.dev !== U.dev) return ee("forbidden");
|
|
1205
|
+
if (G.kind === "markdown") {
|
|
1206
|
+
if (Z.size > BigInt(l)) return ee("too-large");
|
|
1207
|
+
const Y = await qe(k, l + 1);
|
|
1208
|
+
if (Y.length > l) return ee("too-large");
|
|
1209
|
+
const ue = await s(new TextDecoder("utf-8").decode(Y), E);
|
|
1210
|
+
return { kind: "markdown", name: M, htmlContent: ue.htmlContent, headings: ue.headings };
|
|
1211
|
+
}
|
|
1212
|
+
const $ = await qe(k, w);
|
|
1213
|
+
if ($.includes(0)) return { kind: "file", name: M, version: B };
|
|
1214
|
+
const J = Z.size > BigInt(w), W = new TextDecoder("utf-8").decode($, { stream: J });
|
|
1215
|
+
return { kind: "text", name: M, text: W, truncated: J, version: B };
|
|
1216
|
+
} catch (A) {
|
|
1217
|
+
const D = ht(A);
|
|
1218
|
+
if (D === null) throw A;
|
|
1219
|
+
return ee(D);
|
|
1220
|
+
} finally {
|
|
1221
|
+
await k?.close();
|
|
1222
|
+
}
|
|
1223
|
+
}, u = async (a, E, b) => {
|
|
1224
|
+
let k;
|
|
1225
|
+
try {
|
|
1226
|
+
k = await z(b);
|
|
1227
|
+
} catch (B) {
|
|
1228
|
+
throw gt(B);
|
|
1229
|
+
}
|
|
1230
|
+
if (k.status === "refused") throw new Ee(k.reason);
|
|
1231
|
+
const { target: A, name: D, stats: M } = k, U = ft.get(wt(D)), G = U === void 0 ? "" : U.contentType;
|
|
1232
|
+
return { name: D, contentType: G, size: Number(M.size), version: Ze(M), open: (B) => Mn(A, M, B, a.signal) };
|
|
1233
|
+
};
|
|
1234
|
+
return Object.freeze({
|
|
1235
|
+
id: t,
|
|
1236
|
+
label: r,
|
|
1237
|
+
cacheScope: R,
|
|
1238
|
+
boundary: Object.freeze({ contains: (a) => a.startsWith(m) }),
|
|
1239
|
+
tree: N,
|
|
1240
|
+
document: L,
|
|
1241
|
+
asset: u
|
|
1242
|
+
});
|
|
1243
|
+
};
|
|
1244
|
+
export {
|
|
1245
|
+
Qn as DEFAULT_DENIED_PATH_SEGMENTS,
|
|
1246
|
+
Ee as ExplorerError,
|
|
1247
|
+
qn as MAX_DOCUMENT_PATH_LENGTH,
|
|
1248
|
+
Wn as anonymousAccess,
|
|
1249
|
+
Kn as createFileSystemSource,
|
|
1250
|
+
Yn as createMarkdownExplorer,
|
|
1251
|
+
te as isAcceptableDocumentPath,
|
|
1252
|
+
eo as isDeniedPath
|
|
1253
|
+
};
|