@solutiondesk/help 0.0.0 → 1.0.0-69d9f69

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.
@@ -0,0 +1,664 @@
1
+ import { Callout } from 'fumadocs-ui/components/callout';
2
+ import { Card, Cards } from 'fumadocs-ui/components/card';
3
+ import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock';
4
+ import { Heading } from 'fumadocs-ui/components/heading';
5
+ import { Step, Steps } from 'fumadocs-ui/components/steps';
6
+ import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
7
+ import { Fragment as Fragment$1 } from 'react';
8
+ import { directiveFromMarkdown } from 'mdast-util-directive';
9
+ import { fromMarkdown } from 'mdast-util-from-markdown';
10
+ import { gfmFromMarkdown } from 'mdast-util-gfm';
11
+ import { directive } from 'micromark-extension-directive';
12
+ import { gfm } from 'micromark-extension-gfm';
13
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
14
+
15
+ // src/fumadocs/body.tsx
16
+ var HELP_BLOCKS = ["callout", "steps", "step", "tabs", "tab", "cards", "card"];
17
+ var HELP_CALLOUT_TYPES = ["info", "warn", "error", "success", "idea"];
18
+ var isHelpBlock = (name) => HELP_BLOCKS.includes(name);
19
+ function parseHelpMarkdown(markdown) {
20
+ return fromMarkdown(markdown ?? "", {
21
+ extensions: [gfm(), directive()],
22
+ mdastExtensions: [gfmFromMarkdown(), directiveFromMarkdown()]
23
+ });
24
+ }
25
+ var CONTROL = /[\u0000-\u001f\u007f]/;
26
+ var PROTOCOL_RELATIVE = /^[\\/]{2}/;
27
+ function safeHelpHref(raw) {
28
+ const href = (raw ?? "").trim();
29
+ if (CONTROL.test(href) || PROTOCOL_RELATIVE.test(href)) return null;
30
+ if (/^https?:\/\/[^\s]+$/i.test(href)) return href;
31
+ if (/^mailto:[^\s]+$/i.test(href)) return href;
32
+ if (/^\/[^\s]*$/.test(href)) return href;
33
+ if (/^#[^\s]*$/.test(href)) return href;
34
+ return null;
35
+ }
36
+ function safeHelpImageSrc(raw) {
37
+ const src = (raw ?? "").trim();
38
+ if (CONTROL.test(src) || PROTOCOL_RELATIVE.test(src)) return null;
39
+ if (/^https?:\/\/[^\s]+$/i.test(src)) return src;
40
+ if (/^\/[^\s]*$/.test(src)) return src;
41
+ return null;
42
+ }
43
+ function plainText(node) {
44
+ if ("value" in node && typeof node.value === "string") return node.value;
45
+ if (node.type === "image") return node.alt ?? "";
46
+ if ("children" in node) return node.children.map(plainText).join("");
47
+ return "";
48
+ }
49
+ function sourceOf(node, markdown) {
50
+ const start = node.position?.start.offset;
51
+ const end = node.position?.end.offset;
52
+ return start === void 0 || end === void 0 ? plainText(node) : markdown.slice(start, end);
53
+ }
54
+ function labelOf(node) {
55
+ const first = node.children[0];
56
+ return first?.type === "paragraph" && first.data?.directiveLabel ? first : void 0;
57
+ }
58
+ function contentOf(node) {
59
+ return labelOf(node) ? node.children.slice(1) : node.children;
60
+ }
61
+ function directiveTitle(node) {
62
+ const attribute = node.attributes?.title?.trim();
63
+ if (attribute) return attribute;
64
+ const label = labelOf(node);
65
+ const text = label ? plainText(label).trim() : "";
66
+ return text || void 0;
67
+ }
68
+ function calloutType(node) {
69
+ const raw = node.attributes?.type?.trim().toLowerCase() ?? "";
70
+ return HELP_CALLOUT_TYPES.includes(raw) ? raw : "info";
71
+ }
72
+ function codeInfo(lang, meta) {
73
+ const info = [lang ?? "", meta ?? ""].join(" ").trim();
74
+ const first = info.match(/^\S*/)?.[0] ?? "";
75
+ const language = first.includes("=") ? void 0 : first || void 0;
76
+ const match = /(?:^|\s)title=(?:"([^"]*)"|'([^']*)'|(\S+))/.exec(info);
77
+ const title = (match ? match[1] ?? match[2] ?? match[3] : void 0)?.trim() || void 0;
78
+ return { language, title };
79
+ }
80
+ var TRANSLITERATIONS = {
81
+ \u00E4: "ae",
82
+ \u00F6: "oe",
83
+ \u00FC: "ue",
84
+ \u00DF: "ss",
85
+ \u00E6: "ae",
86
+ \u0153: "oe",
87
+ \u00F8: "o",
88
+ \u00E5: "a",
89
+ \u0111: "d",
90
+ \u00F0: "d",
91
+ \u00FE: "th",
92
+ \u0142: "l",
93
+ \u0131: "i"
94
+ };
95
+ function headingSlug(text) {
96
+ return text.normalize("NFC").toLowerCase().replace(/[äöüßæœøåđðþłı]/g, (ch) => TRANSLITERATIONS[ch] ?? ch).normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 96).replace(/-+$/, "");
97
+ }
98
+ function collectHeadings(root) {
99
+ const out = [];
100
+ const walk = (node) => {
101
+ if (node.type === "heading") out.push(node);
102
+ if ("children" in node) for (const child of node.children) walk(child);
103
+ };
104
+ walk(root);
105
+ return out;
106
+ }
107
+ function headingIds(root, known) {
108
+ const headings = collectHeadings(root);
109
+ const ids = /* @__PURE__ */ new Map();
110
+ if (known && known.length === headings.length) {
111
+ headings.forEach((heading, index) => {
112
+ ids.set(heading, known[index]?.id ?? "");
113
+ });
114
+ return ids;
115
+ }
116
+ const used = /* @__PURE__ */ new Map();
117
+ for (const heading of headings) {
118
+ const text = plainText(heading).replace(/\s+/g, " ").trim();
119
+ const base = headingSlug(text) || "section";
120
+ const seen = used.get(base) ?? 0;
121
+ used.set(base, seen + 1);
122
+ ids.set(heading, seen === 0 ? base : `${base}-${seen + 1}`);
123
+ }
124
+ return ids;
125
+ }
126
+ var PLAIN_CODE_COLORS = {
127
+ "--shiki-light": "var(--color-fd-foreground)",
128
+ "--shiki-dark": "var(--color-fd-foreground)"
129
+ };
130
+ var helpBodyComponents = {
131
+ Callout: ({ type, title, children }) => /* @__PURE__ */ jsx(Callout, { type, title, children }),
132
+ Steps: ({ children }) => /* @__PURE__ */ jsx(Steps, { children }),
133
+ Step: ({ title, children }) => /* @__PURE__ */ jsxs(Step, { children: [
134
+ title ? /* @__PURE__ */ jsx("h3", { children: title }) : null,
135
+ children
136
+ ] }),
137
+ Tabs: ({ items, children }) => /* @__PURE__ */ jsx(Tabs, { items, children }),
138
+ Tab: ({ value, children }) => /* @__PURE__ */ jsx(Tab, { value, children }),
139
+ Cards: ({ children }) => /* @__PURE__ */ jsx(Cards, { children }),
140
+ Card: ({ title, href, icon, children }) => /* @__PURE__ */ jsx(Card, { title, href, icon, children }),
141
+ Code: ({ code, lang, title }) => /* @__PURE__ */ jsx(CodeBlock, { title, children: /* @__PURE__ */ jsx(Pre, { children: /* @__PURE__ */ jsx("code", { className: lang ? `language-${lang}` : void 0, style: PLAIN_CODE_COLORS, children: code.split("\n").map((line, index) => (
142
+ // biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
143
+ /* @__PURE__ */ jsxs("span", { className: "line", children: [
144
+ line,
145
+ "\n"
146
+ ] }, index)
147
+ )) }) }) }),
148
+ Heading: ({ level, id, children }) => {
149
+ const as = `h${level}`;
150
+ return /* @__PURE__ */ jsx(Heading, { as, id, children });
151
+ },
152
+ Link: ({ href, title, children }) => /* @__PURE__ */ jsx("a", { href, title, children }),
153
+ Image: ({ src, alt, title }) => /* @__PURE__ */ jsx("img", { src, alt, title, loading: "lazy" })
154
+ };
155
+ var normalizeId = (id) => id.trim().replace(/\s+/g, " ").toLowerCase();
156
+ function collectDefinitions(root) {
157
+ const out = /* @__PURE__ */ new Map();
158
+ const walk = (node) => {
159
+ if (node.type === "definition") {
160
+ const key = normalizeId(node.identifier);
161
+ if (!out.has(key)) out.set(key, node);
162
+ }
163
+ if ("children" in node) for (const child of node.children) walk(child);
164
+ };
165
+ walk(root);
166
+ return out;
167
+ }
168
+ function renderAll(nodes, ctx) {
169
+ return nodes.map((node, index) => (
170
+ // biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
171
+ /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(node, ctx) }, index)
172
+ ));
173
+ }
174
+ var literalBlock = (text) => /* @__PURE__ */ jsx("p", { children: text });
175
+ function renderImage(node, ctx) {
176
+ const src = safeHelpImageSrc(node.url);
177
+ if (!src) return node.alt ?? "";
178
+ return /* @__PURE__ */ jsx(ctx.c.Image, { src, alt: node.alt ?? "", title: node.title ?? void 0 });
179
+ }
180
+ function renderLink(href, title, children, ctx) {
181
+ const safe = safeHelpHref(href);
182
+ if (ctx.linkless || !safe) return children;
183
+ return /* @__PURE__ */ jsx(ctx.c.Link, { href: safe, title: title ?? void 0, children });
184
+ }
185
+ function renderParagraph(node, ctx) {
186
+ const parts = node.children.filter((child) => !(child.type === "text" && !child.value.trim()));
187
+ const only = parts.length === 1 ? parts[0] : void 0;
188
+ if (only?.type === "image" && only.title && safeHelpImageSrc(only.url)) {
189
+ return /* @__PURE__ */ jsxs("figure", { children: [
190
+ renderImage(only, ctx),
191
+ /* @__PURE__ */ jsx("figcaption", { children: only.title })
192
+ ] });
193
+ }
194
+ const inner = renderAll(node.children, { ...ctx, inline: true });
195
+ return ctx.tight ? inner : /* @__PURE__ */ jsx("p", { children: inner });
196
+ }
197
+ function renderList(node, ctx) {
198
+ const tight = !node.spread && node.children.every((item) => !item.spread);
199
+ const items = node.children.map((item, index) => (
200
+ // biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
201
+ /* @__PURE__ */ jsxs("li", { children: [
202
+ typeof item.checked === "boolean" ? /* @__PURE__ */ jsx("input", { type: "checkbox", checked: item.checked, disabled: true, readOnly: true }) : null,
203
+ renderAll(item.children, { ...ctx, tight })
204
+ ] }, index)
205
+ ));
206
+ if (node.ordered) {
207
+ return /* @__PURE__ */ jsx("ol", { start: node.start != null && node.start !== 1 ? node.start : void 0, children: items });
208
+ }
209
+ return /* @__PURE__ */ jsx("ul", { children: items });
210
+ }
211
+ function renderTable(node, ctx) {
212
+ const [head, ...body] = node.children;
213
+ const align = (index) => node.align?.[index] ?? void 0;
214
+ const cells = (row, Tag) => row.children.map((cell, index) => (
215
+ // biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
216
+ /* @__PURE__ */ jsx(Tag, { style: align(index) ? { textAlign: align(index) ?? void 0 } : void 0, children: renderAll(cell.children, { ...ctx, inline: true }) }, index)
217
+ ));
218
+ return /* @__PURE__ */ jsxs("table", { children: [
219
+ head ? /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: cells(head, "th") }) }) : null,
220
+ body.length ? /* @__PURE__ */ jsx("tbody", { children: body.map((row, index) => (
221
+ // biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
222
+ /* @__PURE__ */ jsx("tr", { children: cells(row, "td") }, index)
223
+ )) }) : null
224
+ ] });
225
+ }
226
+ function tabItems(titles) {
227
+ const seen = /* @__PURE__ */ new Map();
228
+ return titles.map((title, index) => {
229
+ const base = title || String(index + 1);
230
+ const count = (seen.get(base) ?? 0) + 1;
231
+ seen.set(base, count);
232
+ return count === 1 ? base : `${base} (${count})`;
233
+ });
234
+ }
235
+ var isDirective = (node, name) => node.type === "containerDirective" && node.name === name;
236
+ function renderCard(node, ctx) {
237
+ const href = node.attributes?.href ? safeHelpHref(node.attributes.href) : null;
238
+ const iconName = node.attributes?.icon?.trim();
239
+ const icon = iconName && /^[A-Za-z][A-Za-z0-9]{0,47}$/.test(iconName) && ctx.icon ? ctx.icon(iconName) : void 0;
240
+ return /* @__PURE__ */ jsx(ctx.c.Card, { title: directiveTitle(node) ?? "", href: href ?? void 0, icon, children: renderAll(contentOf(node), { ...ctx, linkless: ctx.linkless || !!href }) });
241
+ }
242
+ function renderTabs(children, ctx) {
243
+ const tabs = children.filter((child) => isDirective(child, "tab"));
244
+ const items = tabItems(tabs.map(directiveTitle));
245
+ let index = 0;
246
+ return /* @__PURE__ */ jsx(ctx.c.Tabs, { items, children: children.map((child, i) => {
247
+ if (!isDirective(child, "tab")) {
248
+ return /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(child, ctx) }, i);
249
+ }
250
+ const value = items[index++] ?? "";
251
+ return (
252
+ // biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
253
+ /* @__PURE__ */ jsx(ctx.c.Tab, { value, children: renderAll(contentOf(child), ctx) }, i)
254
+ );
255
+ }) });
256
+ }
257
+ function renderDirective(node, ctx) {
258
+ const { c } = ctx;
259
+ if (!isHelpBlock(node.name)) {
260
+ const source = sourceOf(node, ctx.markdown);
261
+ const lines = source.split("\n");
262
+ const closing = lines.length > 1 && /^\s*:{3,}\s*$/.test(lines.at(-1) ?? "");
263
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
264
+ literalBlock(lines[0] ?? ""),
265
+ renderAll(contentOf(node), ctx),
266
+ closing ? literalBlock(lines.at(-1)?.trim() ?? "") : null
267
+ ] });
268
+ }
269
+ const content = contentOf(node);
270
+ const title = directiveTitle(node);
271
+ switch (node.name) {
272
+ case "callout":
273
+ return /* @__PURE__ */ jsx(c.Callout, { type: calloutType(node), title, children: renderAll(content, ctx) });
274
+ case "steps":
275
+ return /* @__PURE__ */ jsx(c.Steps, { children: content.map(
276
+ (child, i) => isDirective(child, "step") ? (
277
+ // biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
278
+ /* @__PURE__ */ jsx(c.Step, { title: directiveTitle(child), children: renderAll(contentOf(child), ctx) }, i)
279
+ ) : (
280
+ // A loose block between steps becomes a step of its own.
281
+ // biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
282
+ /* @__PURE__ */ jsx(c.Step, { children: renderNode(child, ctx) }, i)
283
+ )
284
+ ) });
285
+ case "step":
286
+ return /* @__PURE__ */ jsx(c.Steps, { children: /* @__PURE__ */ jsx(c.Step, { title, children: renderAll(content, ctx) }) });
287
+ case "tabs":
288
+ return renderTabs(content, ctx);
289
+ case "tab":
290
+ return renderTabs([node], ctx);
291
+ case "cards":
292
+ return /* @__PURE__ */ jsx(c.Cards, { children: content.map(
293
+ (child, i) => isDirective(child, "card") ? (
294
+ // biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
295
+ /* @__PURE__ */ jsx(Fragment$1, { children: renderCard(child, ctx) }, i)
296
+ ) : (
297
+ // biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
298
+ /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(child, ctx) }, i)
299
+ )
300
+ ) });
301
+ case "card":
302
+ return /* @__PURE__ */ jsx(c.Cards, { children: renderCard(node, ctx) });
303
+ }
304
+ }
305
+ function renderNode(node, ctx) {
306
+ const { c } = ctx;
307
+ switch (node.type) {
308
+ case "root":
309
+ return renderAll(node.children, ctx);
310
+ case "paragraph":
311
+ return renderParagraph(node, ctx);
312
+ case "heading":
313
+ return /* @__PURE__ */ jsx(c.Heading, { level: node.depth, id: ctx.ids.get(node) ?? "", children: renderAll(node.children, { ...ctx, inline: true }) });
314
+ case "text":
315
+ return node.value;
316
+ case "emphasis":
317
+ return /* @__PURE__ */ jsx("em", { children: renderAll(node.children, ctx) });
318
+ case "strong":
319
+ return /* @__PURE__ */ jsx("strong", { children: renderAll(node.children, ctx) });
320
+ case "delete":
321
+ return /* @__PURE__ */ jsx("del", { children: renderAll(node.children, ctx) });
322
+ case "inlineCode":
323
+ return /* @__PURE__ */ jsx("code", { children: node.value });
324
+ case "break":
325
+ return /* @__PURE__ */ jsx("br", {});
326
+ case "thematicBreak":
327
+ return /* @__PURE__ */ jsx("hr", {});
328
+ case "blockquote":
329
+ return /* @__PURE__ */ jsx("blockquote", { children: renderAll(node.children, { ...ctx, tight: false }) });
330
+ case "list":
331
+ return renderList(node, ctx);
332
+ case "listItem":
333
+ return /* @__PURE__ */ jsx("li", { children: renderAll(node.children, ctx) });
334
+ case "table":
335
+ return renderTable(node, ctx);
336
+ case "code": {
337
+ const { language, title } = codeInfo(node.lang, node.meta);
338
+ return /* @__PURE__ */ jsx(c.Code, { code: node.value, lang: language, title });
339
+ }
340
+ // Raw HTML is shown as text, never as markup.
341
+ case "html":
342
+ return ctx.inline ? node.value : literalBlock(node.value.trim());
343
+ case "link":
344
+ return renderLink(node.url, node.title, renderAll(node.children, ctx), ctx);
345
+ case "image":
346
+ return renderImage(node, ctx);
347
+ case "linkReference": {
348
+ const definition = ctx.definitions.get(normalizeId(node.identifier));
349
+ if (!definition) return sourceOf(node, ctx.markdown);
350
+ return renderLink(definition.url, definition.title, renderAll(node.children, ctx), ctx);
351
+ }
352
+ case "imageReference": {
353
+ const definition = ctx.definitions.get(normalizeId(node.identifier));
354
+ if (!definition) return sourceOf(node, ctx.markdown);
355
+ return renderImage({ url: definition.url, alt: node.alt, title: definition.title }, ctx);
356
+ }
357
+ case "definition":
358
+ return null;
359
+ case "containerDirective":
360
+ return renderDirective(node, { ...ctx, tight: false });
361
+ // Not ours: printed back as written.
362
+ case "leafDirective":
363
+ case "footnoteDefinition":
364
+ return literalBlock(sourceOf(node, ctx.markdown));
365
+ case "textDirective":
366
+ case "footnoteReference":
367
+ return sourceOf(node, ctx.markdown);
368
+ default:
369
+ return "children" in node ? renderAll(node.children, ctx) : "value" in node ? String(node.value) : plainText(node);
370
+ }
371
+ }
372
+ function HelpBody({ markdown, headings, components, icon }) {
373
+ const source = markdown ?? "";
374
+ const root = parseHelpMarkdown(source);
375
+ const ctx = {
376
+ markdown: source,
377
+ ids: headingIds(root, headings),
378
+ c: { ...helpBodyComponents, ...components },
379
+ icon,
380
+ definitions: collectDefinitions(root),
381
+ linkless: false,
382
+ tight: false,
383
+ inline: false
384
+ };
385
+ return /* @__PURE__ */ jsx(Fragment, { children: renderNode(root, ctx) });
386
+ }
387
+
388
+ // ../sdk-core/src/webhook.ts
389
+ function encoder() {
390
+ const Encoder = globalThis.TextEncoder;
391
+ if (!Encoder) throw new Error("TextEncoder is not available in this runtime.");
392
+ return new Encoder();
393
+ }
394
+ function bytesOf(body) {
395
+ if (typeof body === "string") return encoder().encode(body);
396
+ return body instanceof Uint8Array ? body : new Uint8Array(body);
397
+ }
398
+ function hex(buffer) {
399
+ let out = "";
400
+ for (const byte of new Uint8Array(buffer)) out += byte.toString(16).padStart(2, "0");
401
+ return out;
402
+ }
403
+ function constantTimeEqual(a, b) {
404
+ if (a.length !== b.length) return false;
405
+ let diff = 0;
406
+ for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
407
+ return diff === 0;
408
+ }
409
+ function parseHeader(header) {
410
+ let t = Number.NaN;
411
+ const signatures = [];
412
+ for (const part of header.split(",")) {
413
+ const index = part.indexOf("=");
414
+ if (index < 0) continue;
415
+ const key = part.slice(0, index).trim();
416
+ const value = part.slice(index + 1).trim();
417
+ if (key === "t" && /^\d{1,12}$/.test(value)) t = Number(value);
418
+ else if (key === "v1" && /^[0-9a-f]{64}$/i.test(value)) signatures.push(value.toLowerCase());
419
+ }
420
+ return Number.isFinite(t) && signatures.length > 0 ? { t, signatures } : null;
421
+ }
422
+ async function verifyWebhookSignature(secret, rawBody, header, options = {}) {
423
+ if (!secret || !header || header.length > 1e3) return false;
424
+ const parsed = parseHeader(header);
425
+ if (!parsed) return false;
426
+ const now = options.now instanceof Date ? options.now.getTime() : options.now ?? Date.now();
427
+ const tolerance = options.toleranceSeconds ?? 300;
428
+ if (Math.abs(now / 1e3 - parsed.t) > tolerance) return false;
429
+ const subtle = options.crypto?.subtle ?? globalThis.crypto?.subtle;
430
+ if (!subtle) {
431
+ throw new Error(
432
+ "Web Crypto is not available: pass `crypto` (on Node 18: require('node:crypto').webcrypto)."
433
+ );
434
+ }
435
+ const text = encoder();
436
+ const key = await subtle.importKey(
437
+ "raw",
438
+ text.encode(secret),
439
+ { name: "HMAC", hash: "SHA-256" },
440
+ false,
441
+ ["sign"]
442
+ );
443
+ const prefix = text.encode(`${parsed.t}.`);
444
+ const body = bytesOf(rawBody);
445
+ const signed = new Uint8Array(prefix.length + body.length);
446
+ signed.set(prefix, 0);
447
+ signed.set(body, prefix.length);
448
+ const expected = hex(await subtle.sign("HMAC", key, signed));
449
+ let ok = false;
450
+ for (const signature of parsed.signatures) ok = constantTimeEqual(expected, signature) || ok;
451
+ return ok;
452
+ }
453
+
454
+ // src/fumadocs/revalidate.ts
455
+ function createHelpRevalidateHandler(options) {
456
+ return async (request) => {
457
+ if (!options.secret) {
458
+ return new Response("Webhook secret is not configured", { status: 500 });
459
+ }
460
+ const raw = await request.text();
461
+ const signature = request.headers.get("x-solutiondesk-signature");
462
+ if (!await verifyWebhookSignature(options.secret, raw, signature, options.verifyOptions)) {
463
+ return new Response("Invalid signature", { status: 401 });
464
+ }
465
+ const body = parseBody(raw);
466
+ if (!body) return new Response("Malformed body", { status: 400 });
467
+ if (body.event !== "ping" || options.includePing) await options.revalidate(body);
468
+ return new Response(null, { status: 204 });
469
+ };
470
+ }
471
+ function parseBody(raw) {
472
+ let body;
473
+ try {
474
+ body = JSON.parse(raw);
475
+ } catch {
476
+ return null;
477
+ }
478
+ if (typeof body !== "object" || body === null) return null;
479
+ const { event, path, previousPaths } = body;
480
+ if (typeof event !== "string") return null;
481
+ if (path !== null && path !== void 0 && typeof path !== "string") return null;
482
+ if (previousPaths !== void 0 && !Array.isArray(previousPaths)) return null;
483
+ return body;
484
+ }
485
+
486
+ // src/fumadocs/structured.ts
487
+ function helpStructuredData(markdown, headings) {
488
+ const root = parseHelpMarkdown(markdown);
489
+ const ids = headingIds(root, headings);
490
+ const out = { headings: [], contents: [] };
491
+ let current;
492
+ const text = (node) => plainText(node).replace(/\s+/g, " ").trim();
493
+ const walk = (node) => {
494
+ if (node.type === "heading") {
495
+ current = ids.get(node);
496
+ const content = text(node);
497
+ if (current && content) out.headings.push({ id: current, content });
498
+ return;
499
+ }
500
+ if (node.type === "paragraph" || node.type === "tableCell") {
501
+ const content = text(node);
502
+ if (content) out.contents.push({ heading: current, content });
503
+ return;
504
+ }
505
+ if (node.type === "code" || node.type === "html" || node.type === "definition") return;
506
+ if ("children" in node) for (const child of node.children) walk(child);
507
+ };
508
+ walk(root);
509
+ return out;
510
+ }
511
+
512
+ // src/fumadocs/source.ts
513
+ var linkTitle = (title) => title.replace(/[[\]]/g, "").trim() || "Link";
514
+ var linkUrl = (url) => url.replace(/\)/g, "%29").replace(/\s/g, "%20");
515
+ var lastSegment = (path) => path.split("/").at(-1) ?? path;
516
+ var slugsOf = (path) => path ? path.split("/") : [];
517
+ var pageFileName = (page) => {
518
+ if (!page.path) return "index";
519
+ const name = lastSegment(page.path);
520
+ return name === "index" ? "index_" : name;
521
+ };
522
+ function helpMetaEntry(node) {
523
+ switch (node.type) {
524
+ case "page":
525
+ return pageFileName(node);
526
+ case "folder":
527
+ return node.slug;
528
+ case "separator": {
529
+ const title = node.title?.replace(/---/g, "\u2013").trim();
530
+ if (!title) return "---";
531
+ return `---${node.icon ? `[${node.icon}]` : ""}${title}---`;
532
+ }
533
+ case "link":
534
+ return `${node.external ? "external:" : ""}${node.icon ? `[${node.icon}]` : ""}[${linkTitle(node.title)}](${linkUrl(node.url)})`;
535
+ }
536
+ }
537
+ function helpTreeToFiles(payload, options) {
538
+ const files = [];
539
+ const ext = (name, extension) => `${name}${options.suffix ? `.${options.suffix}` : ""}.${extension}`;
540
+ const join = (dir, name) => dir ? `${dir}/${name}` : name;
541
+ const seen = /* @__PURE__ */ new Set();
542
+ const addPage = (page, file) => {
543
+ if (seen.has(file)) return;
544
+ seen.add(file);
545
+ const isPrefetched = options.prefetched?.has(page.path) ?? false;
546
+ const prefetched = options.prefetched?.get(page.path) ?? null;
547
+ const load = isPrefetched ? async () => prefetched : () => options.load(page.path);
548
+ const data = {
549
+ title: page.title,
550
+ ...page.description ? { description: page.description } : {},
551
+ ...page.icon ? { icon: page.icon } : {},
552
+ path: page.path,
553
+ lang: options.lang,
554
+ load,
555
+ structuredData: async () => {
556
+ const full = await load();
557
+ return full ? helpStructuredData(full.markdown, full.headings) : { headings: [], contents: [] };
558
+ },
559
+ ...prefetched ? { page: prefetched } : {}
560
+ };
561
+ files.push({ type: "page", path: file, slugs: slugsOf(page.path), data });
562
+ };
563
+ const addFolder = (folder, dir) => {
564
+ if (folder.index) addPage(folder.index, join(dir, ext("index", "mdx")));
565
+ const data = {
566
+ title: folder.title,
567
+ ...folder.icon ? { icon: folder.icon } : {},
568
+ ...folder.root ? { root: true } : {},
569
+ defaultOpen: folder.defaultOpen,
570
+ collapsible: folder.collapsible,
571
+ ...folder.index ? { pagesIndex: "index" } : {},
572
+ pages: folder.children.map(helpMetaEntry)
573
+ };
574
+ files.push({ type: "meta", path: join(dir, ext("meta", "json")), data });
575
+ addChildren(folder.children, dir);
576
+ };
577
+ const addChildren = (nodes, dir) => {
578
+ for (const node of nodes) {
579
+ if (node.type === "page") {
580
+ addPage(node, node.path ? join(dir, ext(pageFileName(node), "mdx")) : ext("index", "mdx"));
581
+ } else if (node.type === "folder") {
582
+ addFolder(node, join(dir, node.slug));
583
+ }
584
+ }
585
+ };
586
+ files.push({
587
+ type: "meta",
588
+ path: ext("meta", "json"),
589
+ data: { title: payload.site.name, pages: payload.tree.map(helpMetaEntry) }
590
+ });
591
+ addChildren(payload.tree, "");
592
+ return files;
593
+ }
594
+ function helpTreePaths(tree) {
595
+ const out = [];
596
+ const walk = (nodes) => {
597
+ for (const node of nodes) {
598
+ if (node.type === "page") out.push(node.path);
599
+ else if (node.type === "folder") {
600
+ if (node.index) out.push(node.index.path);
601
+ walk(node.children);
602
+ }
603
+ }
604
+ };
605
+ walk(tree);
606
+ return out;
607
+ }
608
+ var isNotFoundError = (error) => error instanceof Error && error.name === "SolutionDeskError" && error.status === 404;
609
+ async function mapLimit(items, limit, fn) {
610
+ const out = new Array(items.length);
611
+ let next = 0;
612
+ const worker = async () => {
613
+ while (next < items.length) {
614
+ const index = next++;
615
+ out[index] = await fn(items[index]);
616
+ }
617
+ };
618
+ await Promise.all(Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, worker));
619
+ return out;
620
+ }
621
+ async function createHelpSource(client, options = {}) {
622
+ const { requestOptions } = options;
623
+ const languages = options.languages?.length ? [...options.languages] : [void 0];
624
+ const files = await Promise.all(
625
+ languages.map(async (lang) => {
626
+ const payload = await client.site(lang ? { lang } : {}, requestOptions);
627
+ const forLang = lang ?? payload.locale;
628
+ const load = async (path) => {
629
+ try {
630
+ return (await client.page(path, { lang: forLang }, requestOptions)).page;
631
+ } catch (error) {
632
+ if (isNotFoundError(error)) return null;
633
+ throw error;
634
+ }
635
+ };
636
+ let prefetched;
637
+ if (options.pages === "eager") {
638
+ const paths = helpTreePaths(payload.tree);
639
+ const pages = await mapLimit(paths, options.concurrency ?? 6, load);
640
+ prefetched = new Map(paths.map((path, index) => [path, pages[index] ?? null]));
641
+ }
642
+ return helpTreeToFiles(payload, {
643
+ lang: forLang,
644
+ load,
645
+ ...lang ? { suffix: lang } : {},
646
+ ...prefetched ? { prefetched } : {}
647
+ });
648
+ })
649
+ );
650
+ return { files: files.flat() };
651
+ }
652
+ function helpDynamicSource(client, options = {}) {
653
+ return { files: async () => (await createHelpSource(client, options)).files };
654
+ }
655
+
656
+ // src/fumadocs/toc.ts
657
+ function getHelpToc(headings, options = {}) {
658
+ const list = "headings" in headings ? headings.headings : headings;
659
+ const min = options.minLevel ?? 1;
660
+ const max = options.maxLevel ?? 6;
661
+ return list.filter((heading) => heading.level >= min && heading.level <= max && heading.id).map((heading) => ({ title: heading.text, url: `#${heading.id}`, depth: heading.level }));
662
+ }
663
+
664
+ export { HELP_BLOCKS, HELP_CALLOUT_TYPES, HelpBody, createHelpRevalidateHandler, createHelpSource, getHelpToc, helpBodyComponents, helpDynamicSource, helpMetaEntry, helpStructuredData, helpTreePaths, helpTreeToFiles, safeHelpHref };