@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.
Files changed (78) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/LICENSE +21 -0
  3. package/README.md +606 -2
  4. package/dist/ExplorerSplit-D2plJRRe.cjs +1 -0
  5. package/dist/ExplorerSplit-DB37U7Kh.js +1362 -0
  6. package/dist/MultiLayout-DCPDu1-l.js +909 -0
  7. package/dist/MultiLayout-UyWxs6Xy.cjs +1 -0
  8. package/dist/TreeLayout-BoyFelK2.js +22 -0
  9. package/dist/TreeLayout-CLWbWjOc.cjs +1 -0
  10. package/dist/client/MarkdownExplorer.d.cts +30 -0
  11. package/dist/client/MarkdownExplorer.d.ts +30 -0
  12. package/dist/client/storage.d.cts +11 -0
  13. package/dist/client/storage.d.ts +11 -0
  14. package/dist/core/config.d.cts +77 -0
  15. package/dist/core/config.d.ts +77 -0
  16. package/dist/core/contracts.d.cts +155 -0
  17. package/dist/core/contracts.d.ts +155 -0
  18. package/dist/core/geometry.d.cts +51 -0
  19. package/dist/core/geometry.d.ts +51 -0
  20. package/dist/core/labels.d.cts +180 -0
  21. package/dist/core/labels.d.ts +180 -0
  22. package/dist/core/location.d.cts +82 -0
  23. package/dist/core/location.d.ts +82 -0
  24. package/dist/core/paths.d.cts +17 -0
  25. package/dist/core/paths.d.ts +17 -0
  26. package/dist/core/revalidation.d.cts +2 -0
  27. package/dist/core/revalidation.d.ts +2 -0
  28. package/dist/core/theme.d.cts +6 -0
  29. package/dist/core/theme.d.ts +6 -0
  30. package/dist/core/treeSettings.d.cts +48 -0
  31. package/dist/core/treeSettings.d.ts +48 -0
  32. package/dist/core/viewSettings.d.cts +15 -0
  33. package/dist/core/viewSettings.d.ts +15 -0
  34. package/dist/dataRoute-Bkfwfp1R.js +194 -0
  35. package/dist/dataRoute-CKbdwkQ7.cjs +1 -0
  36. package/dist/index-BfuAu2GV.js +2107 -0
  37. package/dist/index-Cx3-jSna.cjs +3 -0
  38. package/dist/index.cjs +1 -0
  39. package/dist/index.d.cts +12 -0
  40. package/dist/index.d.ts +12 -0
  41. package/dist/index.js +44 -0
  42. package/dist/location-Oo21lTyV.cjs +1 -0
  43. package/dist/location-fVl2R1ee.js +395 -0
  44. package/dist/sanitizeWorker.cjs +1 -0
  45. package/dist/sanitizeWorker.js +281 -0
  46. package/dist/server/anonymous.d.cts +10 -0
  47. package/dist/server/anonymous.d.ts +10 -0
  48. package/dist/server/createMarkdownExplorer.d.cts +12 -0
  49. package/dist/server/createMarkdownExplorer.d.ts +12 -0
  50. package/dist/server/errors.d.cts +26 -0
  51. package/dist/server/errors.d.ts +26 -0
  52. package/dist/server/fileSystemSource.d.cts +38 -0
  53. package/dist/server/fileSystemSource.d.ts +38 -0
  54. package/dist/server/ports.d.cts +107 -0
  55. package/dist/server/ports.d.ts +107 -0
  56. package/dist/server/sanitizeProtocol.d.cts +17 -0
  57. package/dist/server/sanitizeProtocol.d.ts +17 -0
  58. package/dist/server/sanitizer.d.cts +51 -0
  59. package/dist/server/sanitizer.d.ts +51 -0
  60. package/dist/server/workerPool.d.cts +42 -0
  61. package/dist/server/workerPool.d.ts +42 -0
  62. package/dist/server.cjs +1 -0
  63. package/dist/server.d.cts +7 -0
  64. package/dist/server.d.ts +7 -0
  65. package/dist/server.js +1253 -0
  66. package/dist/storage-Bkb2Axdi.cjs +1 -0
  67. package/dist/storage-DA4ItHzl.js +60 -0
  68. package/dist/storage.cjs +1 -0
  69. package/dist/storage.d.cts +1 -0
  70. package/dist/storage.d.ts +1 -0
  71. package/dist/storage.js +4 -0
  72. package/dist/styles/markdown-explorer.css +3 -0
  73. package/docs/behavior/dom-hooks.md +95 -0
  74. package/docs/behavior/layout.md +215 -0
  75. package/docs/behavior/localization.md +48 -0
  76. package/docs/behavior/security.md +138 -0
  77. package/docs/behavior/url-contract.md +135 -0
  78. 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
+ };