@solutiondesk/help 0.0.0 → 1.0.0-1a0b98f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +282 -28
- package/dist/fumadocs.d.ts +780 -0
- package/dist/fumadocs.js +981 -0
- package/dist/index.cjs +344 -0
- package/dist/index.d.cts +479 -0
- package/dist/index.d.ts +479 -0
- package/dist/index.js +334 -0
- package/package.json +84 -7
package/dist/fumadocs.js
ADDED
|
@@ -0,0 +1,981 @@
|
|
|
1
|
+
import { directiveFromMarkdown } from 'mdast-util-directive';
|
|
2
|
+
import { fromMarkdown } from 'mdast-util-from-markdown';
|
|
3
|
+
import { gfmFromMarkdown } from 'mdast-util-gfm';
|
|
4
|
+
import { directive } from 'micromark-extension-directive';
|
|
5
|
+
import { gfm } from 'micromark-extension-gfm';
|
|
6
|
+
import { Callout } from 'fumadocs-ui/components/callout';
|
|
7
|
+
import { Card, Cards } from 'fumadocs-ui/components/card';
|
|
8
|
+
import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock';
|
|
9
|
+
import { Heading } from 'fumadocs-ui/components/heading';
|
|
10
|
+
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
|
11
|
+
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
|
12
|
+
import { Fragment as Fragment$1, createElement } from 'react';
|
|
13
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
14
|
+
|
|
15
|
+
// src/fumadocs/markdown.ts
|
|
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
|
+
var CODE_FENCE = /^[ \t]*(`{3,}|~{3,})(.*)$/;
|
|
20
|
+
var CLOSING_FENCE = /^([ \t]*):(:{2,}[ \t]*)$/;
|
|
21
|
+
var MASK = "\uE000";
|
|
22
|
+
function maskCodeFences(markdown) {
|
|
23
|
+
if (markdown.includes(MASK)) return markdown;
|
|
24
|
+
let open = null;
|
|
25
|
+
return markdown.split("\n").map((line) => {
|
|
26
|
+
const fence = CODE_FENCE.exec(line);
|
|
27
|
+
if (open) {
|
|
28
|
+
if (fence?.[1]?.[0] === open.char && fence[1].length >= open.length && !fence[2]?.trim()) {
|
|
29
|
+
open = null;
|
|
30
|
+
return line;
|
|
31
|
+
}
|
|
32
|
+
return line.replace(CLOSING_FENCE, `$1${MASK}$2`);
|
|
33
|
+
}
|
|
34
|
+
if (fence?.[1] && !(fence[1][0] === "`" && fence[2]?.includes("`"))) {
|
|
35
|
+
open = { char: fence[1][0] ?? "`", length: fence[1].length };
|
|
36
|
+
}
|
|
37
|
+
return line;
|
|
38
|
+
}).join("\n");
|
|
39
|
+
}
|
|
40
|
+
function unmask(node) {
|
|
41
|
+
if ("value" in node && typeof node.value === "string" && node.value.includes(MASK)) {
|
|
42
|
+
node.value = node.value.replaceAll(MASK, ":");
|
|
43
|
+
}
|
|
44
|
+
if ("children" in node) for (const child of node.children) unmask(child);
|
|
45
|
+
}
|
|
46
|
+
function parseHelpMarkdown(markdown) {
|
|
47
|
+
const source = markdown ?? "";
|
|
48
|
+
const masked = maskCodeFences(source);
|
|
49
|
+
const root = fromMarkdown(masked, {
|
|
50
|
+
extensions: [gfm(), directive()],
|
|
51
|
+
mdastExtensions: [gfmFromMarkdown(), directiveFromMarkdown()]
|
|
52
|
+
});
|
|
53
|
+
if (masked !== source) unmask(root);
|
|
54
|
+
return root;
|
|
55
|
+
}
|
|
56
|
+
var CONTROL = /[\u0000-\u001f\u007f]/;
|
|
57
|
+
var PROTOCOL_RELATIVE = /^[\\/]{2}/;
|
|
58
|
+
function safeHelpHref(raw) {
|
|
59
|
+
const href = (raw ?? "").trim();
|
|
60
|
+
if (CONTROL.test(href) || PROTOCOL_RELATIVE.test(href)) return null;
|
|
61
|
+
if (/^https?:\/\/[^\s]+$/i.test(href)) return href;
|
|
62
|
+
if (/^mailto:[^\s]+$/i.test(href)) return href;
|
|
63
|
+
if (/^\/[^\s]*$/.test(href)) return href;
|
|
64
|
+
if (/^#[^\s]*$/.test(href)) return href;
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
function safeHelpImageSrc(raw) {
|
|
68
|
+
const src = (raw ?? "").trim();
|
|
69
|
+
if (CONTROL.test(src) || PROTOCOL_RELATIVE.test(src)) return null;
|
|
70
|
+
if (/^https?:\/\/[^\s]+$/i.test(src)) return src;
|
|
71
|
+
if (/^\/[^\s]*$/.test(src)) return src;
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
function plainText(node, override) {
|
|
75
|
+
const own = override?.(node);
|
|
76
|
+
if (own !== void 0) return own;
|
|
77
|
+
if ("value" in node && typeof node.value === "string") return node.value;
|
|
78
|
+
if (node.type === "image") return node.alt ?? "";
|
|
79
|
+
if ("children" in node) {
|
|
80
|
+
return node.children.map((child) => plainText(child, override)).join("");
|
|
81
|
+
}
|
|
82
|
+
return "";
|
|
83
|
+
}
|
|
84
|
+
function sourceOf(node, markdown) {
|
|
85
|
+
const start = node.position?.start.offset;
|
|
86
|
+
const end = node.position?.end.offset;
|
|
87
|
+
return start === void 0 || end === void 0 ? plainText(node) : markdown.slice(start, end);
|
|
88
|
+
}
|
|
89
|
+
function labelOf(node) {
|
|
90
|
+
const first = node.children[0];
|
|
91
|
+
return first?.type === "paragraph" && first.data?.directiveLabel ? first : void 0;
|
|
92
|
+
}
|
|
93
|
+
function contentOf(node) {
|
|
94
|
+
return labelOf(node) ? node.children.slice(1) : node.children;
|
|
95
|
+
}
|
|
96
|
+
function directiveTitle(node) {
|
|
97
|
+
const attribute = node.attributes?.title?.trim();
|
|
98
|
+
if (attribute) return attribute;
|
|
99
|
+
const label = labelOf(node);
|
|
100
|
+
const text = label ? plainText(label).trim() : "";
|
|
101
|
+
return text || void 0;
|
|
102
|
+
}
|
|
103
|
+
function calloutType(node) {
|
|
104
|
+
const raw = node.attributes?.type?.trim().toLowerCase() ?? "";
|
|
105
|
+
return HELP_CALLOUT_TYPES.includes(raw) ? raw : "info";
|
|
106
|
+
}
|
|
107
|
+
function codeInfo(lang, meta) {
|
|
108
|
+
const info = [lang ?? "", meta ?? ""].join(" ").trim();
|
|
109
|
+
const first = info.match(/^\S*/)?.[0] ?? "";
|
|
110
|
+
const language = first.includes("=") ? void 0 : first || void 0;
|
|
111
|
+
const match = /(?:^|\s)title=(?:"([^"]*)"|'([^']*)'|(\S+))/.exec(info);
|
|
112
|
+
const title = (match ? match[1] ?? match[2] ?? match[3] : void 0)?.trim() || void 0;
|
|
113
|
+
return { language, title };
|
|
114
|
+
}
|
|
115
|
+
var GITHUB_SLUG_DROP = /[^\p{L}\p{M}\p{N}\p{Pc} -]/gu;
|
|
116
|
+
function headingId(text, used) {
|
|
117
|
+
const base = text.toLowerCase().replace(GITHUB_SLUG_DROP, "").replace(/ /g, "-") || "section";
|
|
118
|
+
let id = base;
|
|
119
|
+
while (used.has(id)) {
|
|
120
|
+
const count = (used.get(base) ?? 0) + 1;
|
|
121
|
+
used.set(base, count);
|
|
122
|
+
id = `${base}-${count}`;
|
|
123
|
+
}
|
|
124
|
+
used.set(id, 0);
|
|
125
|
+
return id;
|
|
126
|
+
}
|
|
127
|
+
function isTitledStep(node) {
|
|
128
|
+
return node.type === "containerDirective" && node.name === "step" && !!directiveTitle(node);
|
|
129
|
+
}
|
|
130
|
+
function collectHeadings(root) {
|
|
131
|
+
const out = [];
|
|
132
|
+
const walk = (node) => {
|
|
133
|
+
if (node.type === "heading" || isTitledStep(node)) out.push(node);
|
|
134
|
+
if ("children" in node) for (const child of node.children) walk(child);
|
|
135
|
+
};
|
|
136
|
+
walk(root);
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
function headingIds(root, known, override) {
|
|
140
|
+
const headings = collectHeadings(root);
|
|
141
|
+
const ids = /* @__PURE__ */ new Map();
|
|
142
|
+
if (known && known.length === headings.length) {
|
|
143
|
+
headings.forEach((heading, index) => {
|
|
144
|
+
ids.set(heading, known[index]?.id ?? "");
|
|
145
|
+
});
|
|
146
|
+
return ids;
|
|
147
|
+
}
|
|
148
|
+
const used = /* @__PURE__ */ new Map();
|
|
149
|
+
const withoutBlocks = override ? (node) => {
|
|
150
|
+
if (override(node) === void 0) return void 0;
|
|
151
|
+
if (node.type !== "textDirective") return "";
|
|
152
|
+
return node.children.map((child) => plainText(child)).join("").trim();
|
|
153
|
+
} : void 0;
|
|
154
|
+
for (const heading of headings) {
|
|
155
|
+
const text = heading.type === "heading" ? plainText(heading, withoutBlocks) : (directiveTitle(heading) ?? "").replace(/\s+/g, " ").trim();
|
|
156
|
+
ids.set(heading, headingId(text, used));
|
|
157
|
+
}
|
|
158
|
+
return ids;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/fumadocs/blocks.ts
|
|
162
|
+
var TEXT_VALUE_MAX = 300;
|
|
163
|
+
var LONG_VALUE_MAX = 2e3;
|
|
164
|
+
var ICON = /^[A-Za-z][A-Za-z0-9]{0,47}$/;
|
|
165
|
+
var NAMED_ENTITIES = {
|
|
166
|
+
amp: "&",
|
|
167
|
+
lt: "<",
|
|
168
|
+
gt: ">",
|
|
169
|
+
quot: '"',
|
|
170
|
+
apos: "'",
|
|
171
|
+
nbsp: "\xA0"
|
|
172
|
+
};
|
|
173
|
+
function decodeEntities(value) {
|
|
174
|
+
return value.replace(/&(#x[0-9a-f]{1,6}|#[0-9]{1,7}|[a-z]+);/gi, (match, ref) => {
|
|
175
|
+
if (ref[0] === "#") {
|
|
176
|
+
const code = ref[1] === "x" || ref[1] === "X" ? parseInt(ref.slice(2), 16) : Number(ref.slice(1));
|
|
177
|
+
return Number.isFinite(code) && code > 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
|
|
178
|
+
}
|
|
179
|
+
return NAMED_ENTITIES[ref.toLowerCase()] ?? match;
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
var ATTRIBUTE = /^(?:#([^\s"'<>=`}#.]+)|\.([^\s"'<>=`}#.]+)|([A-Za-z_:][A-Za-z0-9_.:-]*)(?:[ \t]*=[ \t]*(?:"([^"\n]*)"|'([^'\n]*)'|([^\s"'<>=`}]+)))?)/;
|
|
183
|
+
function parseAttributes(src) {
|
|
184
|
+
const attributes = {};
|
|
185
|
+
let pos = 1;
|
|
186
|
+
for (; ; ) {
|
|
187
|
+
while (src[pos] === " " || src[pos] === " ") pos++;
|
|
188
|
+
if (src[pos] === "}") return { attributes, length: pos + 1 };
|
|
189
|
+
const match = ATTRIBUTE.exec(src.slice(pos));
|
|
190
|
+
if (!match) return null;
|
|
191
|
+
const [whole, id, cls, key, dq, sq, bare] = match;
|
|
192
|
+
if (id !== void 0) attributes.id = decodeEntities(id);
|
|
193
|
+
else if (cls !== void 0)
|
|
194
|
+
attributes.class = attributes.class ? `${attributes.class} ${cls}` : cls;
|
|
195
|
+
else if (key !== void 0) attributes[key] = decodeEntities(dq ?? sq ?? bare ?? "");
|
|
196
|
+
pos += whole.length;
|
|
197
|
+
if (src[pos] !== " " && src[pos] !== " " && src[pos] !== "}") return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function parseLabel(src) {
|
|
201
|
+
let depth = 0;
|
|
202
|
+
for (let pos = 0; pos < src.length; pos++) {
|
|
203
|
+
const char = src[pos];
|
|
204
|
+
if (char === "\n") return null;
|
|
205
|
+
if (char === "\\") {
|
|
206
|
+
pos++;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (char === "[") depth++;
|
|
210
|
+
else if (char === "]") {
|
|
211
|
+
depth--;
|
|
212
|
+
if (depth === 0) return { label: src.slice(1, pos), length: pos + 1 };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
function parseDirectiveTail(src) {
|
|
218
|
+
let rest = src;
|
|
219
|
+
let label;
|
|
220
|
+
let attributes = {};
|
|
221
|
+
if (rest.startsWith("[")) {
|
|
222
|
+
const parsed = parseLabel(rest);
|
|
223
|
+
if (!parsed) return null;
|
|
224
|
+
label = parsed.label;
|
|
225
|
+
rest = rest.slice(parsed.length);
|
|
226
|
+
}
|
|
227
|
+
if (rest.startsWith("{")) {
|
|
228
|
+
const parsed = parseAttributes(rest);
|
|
229
|
+
if (!parsed) return null;
|
|
230
|
+
attributes = parsed.attributes;
|
|
231
|
+
rest = rest.slice(parsed.length);
|
|
232
|
+
}
|
|
233
|
+
return { ...label !== void 0 ? { label } : {}, attributes, rest };
|
|
234
|
+
}
|
|
235
|
+
function plainLabel(label) {
|
|
236
|
+
if (label === void 0) return void 0;
|
|
237
|
+
const text = label.replace(/\\(.)/g, "$1").replace(/[*_`~]/g, "").trim();
|
|
238
|
+
return text || void 0;
|
|
239
|
+
}
|
|
240
|
+
var isVideoAddress = (value) => safeHelpHref(value) === value && (/^https:\/\//i.test(value) || value.startsWith("/"));
|
|
241
|
+
function fieldValue(field, raw) {
|
|
242
|
+
if (field.type === "boolean") {
|
|
243
|
+
const flag = raw.trim().toLowerCase();
|
|
244
|
+
if (flag === "" || flag === "true") return true;
|
|
245
|
+
if (flag === "false") return false;
|
|
246
|
+
return void 0;
|
|
247
|
+
}
|
|
248
|
+
const value = raw.trim();
|
|
249
|
+
switch (field.type) {
|
|
250
|
+
case "text":
|
|
251
|
+
return !/[\r\n]/.test(value) && value.length <= TEXT_VALUE_MAX ? value : void 0;
|
|
252
|
+
case "longText":
|
|
253
|
+
return value.length <= LONG_VALUE_MAX ? value : void 0;
|
|
254
|
+
case "icon":
|
|
255
|
+
return ICON.test(value) ? value : void 0;
|
|
256
|
+
case "url":
|
|
257
|
+
return value.length <= LONG_VALUE_MAX && safeHelpHref(value) === value ? value : void 0;
|
|
258
|
+
case "choice":
|
|
259
|
+
return field.options?.includes(value) ? value : void 0;
|
|
260
|
+
case "image":
|
|
261
|
+
return value.length <= LONG_VALUE_MAX && safeHelpImageSrc(value) === value ? value : void 0;
|
|
262
|
+
case "video":
|
|
263
|
+
return value.length <= LONG_VALUE_MAX && isVideoAddress(value) ? value : void 0;
|
|
264
|
+
default:
|
|
265
|
+
return void 0;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function helpBlockValues(block, attributes) {
|
|
269
|
+
const values = {};
|
|
270
|
+
for (const field of block.fields) {
|
|
271
|
+
const raw = Object.hasOwn(attributes, field.key) ? attributes[field.key] : void 0;
|
|
272
|
+
if (field.type === "boolean") {
|
|
273
|
+
if (raw === void 0) {
|
|
274
|
+
values[field.key] = false;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
} else if (raw === void 0 || raw.trim() === "") {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
const value = fieldValue(field, raw);
|
|
281
|
+
if (value !== void 0) values[field.key] = value;
|
|
282
|
+
}
|
|
283
|
+
return values;
|
|
284
|
+
}
|
|
285
|
+
function isTemplate(template) {
|
|
286
|
+
for (let i = 0; i < template.length; i++) {
|
|
287
|
+
const char = template[i];
|
|
288
|
+
if ((char === "{" || char === "}") && template[i + 1] === char) {
|
|
289
|
+
i++;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (char === "}") return false;
|
|
293
|
+
if (char !== "{") continue;
|
|
294
|
+
const end = template.indexOf("}", i + 1);
|
|
295
|
+
if (end === -1) return false;
|
|
296
|
+
const key = template.slice(i + 1, end).trim();
|
|
297
|
+
if (!key || key.includes("{")) return false;
|
|
298
|
+
i = end;
|
|
299
|
+
}
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
function helpBlockText(block, values, label) {
|
|
303
|
+
const template = block.text ?? "";
|
|
304
|
+
const fields = new Map(block.fields.map((f) => [f.key, f]));
|
|
305
|
+
let text = "";
|
|
306
|
+
if (isTemplate(template)) {
|
|
307
|
+
for (let i = 0; i < template.length; i++) {
|
|
308
|
+
const char = template[i] ?? "";
|
|
309
|
+
if ((char === "{" || char === "}") && template[i + 1] === char) {
|
|
310
|
+
text += char;
|
|
311
|
+
i++;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (char !== "{") {
|
|
315
|
+
text += char;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
const end = template.indexOf("}", i + 1);
|
|
319
|
+
const key = template.slice(i + 1, end).trim();
|
|
320
|
+
const value = values[key];
|
|
321
|
+
if (value === true) text += fields.get(key)?.label ?? "";
|
|
322
|
+
else if (typeof value === "string") text += value;
|
|
323
|
+
i = end;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return `${text} ${label ?? ""}`.replace(/\s+/g, " ").trim();
|
|
327
|
+
}
|
|
328
|
+
var FORMS = {
|
|
329
|
+
textDirective: "inline",
|
|
330
|
+
leafDirective: "block",
|
|
331
|
+
containerDirective: "container"
|
|
332
|
+
};
|
|
333
|
+
function helpBlockMatcher(blocks, markdown) {
|
|
334
|
+
const defined = new Map((blocks ?? []).map((block) => [block.name, block]));
|
|
335
|
+
const cache = /* @__PURE__ */ new Map();
|
|
336
|
+
const find = (node) => {
|
|
337
|
+
if (defined.size === 0) return null;
|
|
338
|
+
if (node.type !== "textDirective" && node.type !== "leafDirective" && node.type !== "containerDirective") {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
const block = defined.get(node.name);
|
|
342
|
+
if (!block || block.kind !== FORMS[node.type]) return null;
|
|
343
|
+
const start = node.position?.start.offset;
|
|
344
|
+
const end = node.position?.end.offset;
|
|
345
|
+
if (start === void 0 || end === void 0) return null;
|
|
346
|
+
const lineEnd = markdown.indexOf("\n", start);
|
|
347
|
+
const line = markdown.slice(start, lineEnd === -1 ? void 0 : lineEnd);
|
|
348
|
+
const head = /^(:+)([A-Za-z][A-Za-z0-9_-]*)/.exec(line);
|
|
349
|
+
const colons = head?.[1]?.length ?? 0;
|
|
350
|
+
const form = colons === 1 ? "textDirective" : colons === 2 ? "leafDirective" : "containerDirective";
|
|
351
|
+
if (!head || form !== node.type || head[2] !== node.name) return null;
|
|
352
|
+
const tail = parseDirectiveTail(line.slice(head[0].length));
|
|
353
|
+
if (!tail) return null;
|
|
354
|
+
if (node.type === "textDirective") {
|
|
355
|
+
if (line.length - tail.rest.length !== end - start) return null;
|
|
356
|
+
} else if (tail.rest.trim() !== "") return null;
|
|
357
|
+
const values = helpBlockValues(block, tail.attributes);
|
|
358
|
+
const label = node.type === "containerDirective" ? void 0 : plainLabel(tail.label);
|
|
359
|
+
return { block, node, values, text: helpBlockText(block, values, label) };
|
|
360
|
+
};
|
|
361
|
+
const use = (node) => {
|
|
362
|
+
if (!cache.has(node)) cache.set(node, find(node));
|
|
363
|
+
return cache.get(node) ?? null;
|
|
364
|
+
};
|
|
365
|
+
const text = (node) => {
|
|
366
|
+
const found = use(node);
|
|
367
|
+
if (!found) return void 0;
|
|
368
|
+
if (found.node.type !== "containerDirective") return found.text;
|
|
369
|
+
const inner = contentOf(found.node).map((child) => plainText(child, text)).join(" ");
|
|
370
|
+
return `${found.text} ${inner}`.trim();
|
|
371
|
+
};
|
|
372
|
+
return { use, text };
|
|
373
|
+
}
|
|
374
|
+
var PLAIN_CODE_COLORS = {
|
|
375
|
+
"--shiki-light": "var(--color-fd-foreground)",
|
|
376
|
+
"--shiki-dark": "var(--color-fd-foreground)"
|
|
377
|
+
};
|
|
378
|
+
var helpBodyComponents = {
|
|
379
|
+
Callout: ({ type, title, children }) => /* @__PURE__ */ jsx(Callout, { type, title, children }),
|
|
380
|
+
Steps: ({ children }) => /* @__PURE__ */ jsx(Steps, { children }),
|
|
381
|
+
Step: ({ title, id, children }) => /* @__PURE__ */ jsxs(Step, { children: [
|
|
382
|
+
title ? /* @__PURE__ */ jsx(Heading, { as: "h3", id, children: title }) : null,
|
|
383
|
+
children
|
|
384
|
+
] }),
|
|
385
|
+
Tabs: ({ items, children }) => /* @__PURE__ */ jsx(Tabs, { items, children }),
|
|
386
|
+
Tab: ({ value, children }) => /* @__PURE__ */ jsx(Tab, { value, children }),
|
|
387
|
+
Cards: ({ children }) => /* @__PURE__ */ jsx(Cards, { children }),
|
|
388
|
+
Card: ({ title, href, icon, children }) => /* @__PURE__ */ jsx(Card, { title, href, icon, children }),
|
|
389
|
+
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) => (
|
|
390
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
|
|
391
|
+
/* @__PURE__ */ jsxs("span", { className: "line", children: [
|
|
392
|
+
line,
|
|
393
|
+
"\n"
|
|
394
|
+
] }, index)
|
|
395
|
+
)) }) }) }),
|
|
396
|
+
Heading: ({ level, id, children }) => {
|
|
397
|
+
const as = `h${level}`;
|
|
398
|
+
return /* @__PURE__ */ jsx(Heading, { as, id, children });
|
|
399
|
+
},
|
|
400
|
+
Link: ({ href, title, children }) => /* @__PURE__ */ jsx("a", { href, title, children }),
|
|
401
|
+
Image: ({ src, alt, title }) => /* @__PURE__ */ jsx("img", { src, alt, title, loading: "lazy" })
|
|
402
|
+
};
|
|
403
|
+
var BUILT_IN_KEYS = Object.keys(helpBodyComponents);
|
|
404
|
+
function pickBuiltIns(components) {
|
|
405
|
+
const out = {};
|
|
406
|
+
for (const key of BUILT_IN_KEYS) {
|
|
407
|
+
const component = components[key];
|
|
408
|
+
if (component) out[key] = component;
|
|
409
|
+
}
|
|
410
|
+
return out;
|
|
411
|
+
}
|
|
412
|
+
var normalizeId = (id) => id.trim().replace(/\s+/g, " ").toLowerCase();
|
|
413
|
+
function collectDefinitions(root) {
|
|
414
|
+
const out = /* @__PURE__ */ new Map();
|
|
415
|
+
const walk = (node) => {
|
|
416
|
+
if (node.type === "definition") {
|
|
417
|
+
const key = normalizeId(node.identifier);
|
|
418
|
+
if (!out.has(key)) out.set(key, node);
|
|
419
|
+
}
|
|
420
|
+
if ("children" in node) for (const child of node.children) walk(child);
|
|
421
|
+
};
|
|
422
|
+
walk(root);
|
|
423
|
+
return out;
|
|
424
|
+
}
|
|
425
|
+
function renderAll(nodes, ctx) {
|
|
426
|
+
return nodes.map((node, index) => (
|
|
427
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
|
|
428
|
+
/* @__PURE__ */ jsx(Fragment$1, { children: renderNode(node, ctx) }, index)
|
|
429
|
+
));
|
|
430
|
+
}
|
|
431
|
+
var literalBlock = (text) => /* @__PURE__ */ jsx("p", { children: text });
|
|
432
|
+
function renderImage(node, ctx) {
|
|
433
|
+
const src = safeHelpImageSrc(node.url);
|
|
434
|
+
if (!src) return node.alt ?? "";
|
|
435
|
+
return /* @__PURE__ */ jsx(ctx.c.Image, { src, alt: node.alt ?? "", title: node.title ?? void 0 });
|
|
436
|
+
}
|
|
437
|
+
function renderLink(href, title, children, ctx) {
|
|
438
|
+
const safe = safeHelpHref(href);
|
|
439
|
+
if (ctx.linkless || !safe) return children;
|
|
440
|
+
return /* @__PURE__ */ jsx(ctx.c.Link, { href: safe, title: title ?? void 0, children });
|
|
441
|
+
}
|
|
442
|
+
function renderParagraph(node, ctx) {
|
|
443
|
+
const parts = node.children.filter((child) => !(child.type === "text" && !child.value.trim()));
|
|
444
|
+
const only = parts.length === 1 ? parts[0] : void 0;
|
|
445
|
+
if (only?.type === "image" && only.title && safeHelpImageSrc(only.url)) {
|
|
446
|
+
return /* @__PURE__ */ jsxs("figure", { children: [
|
|
447
|
+
renderImage(only, ctx),
|
|
448
|
+
/* @__PURE__ */ jsx("figcaption", { children: only.title })
|
|
449
|
+
] });
|
|
450
|
+
}
|
|
451
|
+
const inner = renderAll(node.children, { ...ctx, inline: true });
|
|
452
|
+
return ctx.tight ? inner : /* @__PURE__ */ jsx("p", { children: inner });
|
|
453
|
+
}
|
|
454
|
+
function renderList(node, ctx) {
|
|
455
|
+
const tight = !node.spread && node.children.every((item) => !item.spread);
|
|
456
|
+
const items = node.children.map((item, index) => (
|
|
457
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
|
|
458
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
459
|
+
typeof item.checked === "boolean" ? /* @__PURE__ */ jsx("input", { type: "checkbox", checked: item.checked, disabled: true, readOnly: true }) : null,
|
|
460
|
+
renderAll(item.children, { ...ctx, tight })
|
|
461
|
+
] }, index)
|
|
462
|
+
));
|
|
463
|
+
if (node.ordered) {
|
|
464
|
+
return /* @__PURE__ */ jsx("ol", { start: node.start != null && node.start !== 1 ? node.start : void 0, children: items });
|
|
465
|
+
}
|
|
466
|
+
return /* @__PURE__ */ jsx("ul", { children: items });
|
|
467
|
+
}
|
|
468
|
+
function renderTable(node, ctx) {
|
|
469
|
+
const [head, ...body] = node.children;
|
|
470
|
+
const align = (index) => node.align?.[index] ?? void 0;
|
|
471
|
+
const cells = (row, Tag) => row.children.map((cell, index) => (
|
|
472
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
|
|
473
|
+
/* @__PURE__ */ jsx(Tag, { style: align(index) ? { textAlign: align(index) ?? void 0 } : void 0, children: renderAll(cell.children, { ...ctx, inline: true }) }, index)
|
|
474
|
+
));
|
|
475
|
+
return /* @__PURE__ */ jsxs("table", { children: [
|
|
476
|
+
head ? /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: cells(head, "th") }) }) : null,
|
|
477
|
+
body.length ? /* @__PURE__ */ jsx("tbody", { children: body.map((row, index) => (
|
|
478
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
|
|
479
|
+
/* @__PURE__ */ jsx("tr", { children: cells(row, "td") }, index)
|
|
480
|
+
)) }) : null
|
|
481
|
+
] });
|
|
482
|
+
}
|
|
483
|
+
function tabItems(titles) {
|
|
484
|
+
const seen = /* @__PURE__ */ new Map();
|
|
485
|
+
return titles.map((title, index) => {
|
|
486
|
+
const base = title || String(index + 1);
|
|
487
|
+
const count = (seen.get(base) ?? 0) + 1;
|
|
488
|
+
seen.set(base, count);
|
|
489
|
+
return count === 1 ? base : `${base} (${count})`;
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
var isDirective = (node, name) => node.type === "containerDirective" && node.name === name;
|
|
493
|
+
function renderCard(node, ctx) {
|
|
494
|
+
const href = node.attributes?.href ? safeHelpHref(node.attributes.href) : null;
|
|
495
|
+
const iconName = node.attributes?.icon?.trim();
|
|
496
|
+
const icon = iconName && /^[A-Za-z][A-Za-z0-9]{0,47}$/.test(iconName) && ctx.icon ? ctx.icon(iconName) : void 0;
|
|
497
|
+
return /* @__PURE__ */ jsx(ctx.c.Card, { title: directiveTitle(node) ?? "", href: href ?? void 0, icon, children: renderAll(contentOf(node), { ...ctx, linkless: ctx.linkless || !!href }) });
|
|
498
|
+
}
|
|
499
|
+
function renderTabs(children, ctx) {
|
|
500
|
+
const tabs = children.filter((child) => isDirective(child, "tab"));
|
|
501
|
+
const items = tabItems(tabs.map(directiveTitle));
|
|
502
|
+
let index = 0;
|
|
503
|
+
return /* @__PURE__ */ jsx(ctx.c.Tabs, { items, children: children.map((child, i) => {
|
|
504
|
+
if (!isDirective(child, "tab")) {
|
|
505
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(child, ctx) }, i);
|
|
506
|
+
}
|
|
507
|
+
const value = items[index++] ?? "";
|
|
508
|
+
return (
|
|
509
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
|
|
510
|
+
/* @__PURE__ */ jsx(ctx.c.Tab, { value, children: renderAll(contentOf(child), ctx) }, i)
|
|
511
|
+
);
|
|
512
|
+
}) });
|
|
513
|
+
}
|
|
514
|
+
function renderCustomBlock(use, ctx) {
|
|
515
|
+
const { block, node, values, text } = use;
|
|
516
|
+
const isContainer = node.type === "containerDirective";
|
|
517
|
+
const children = node.type === "containerDirective" ? renderAll(contentOf(node), { ...ctx, tight: false }) : node.children.length ? renderAll(node.children, { ...ctx, inline: true }) : void 0;
|
|
518
|
+
const Component = Object.hasOwn(ctx.custom, block.name) ? ctx.custom[block.name] : void 0;
|
|
519
|
+
if (Component) {
|
|
520
|
+
return createElement(Component, { ...values, block: block.name, lang: ctx.lang }, children);
|
|
521
|
+
}
|
|
522
|
+
const attributes = {
|
|
523
|
+
className: "sd-block",
|
|
524
|
+
"data-block": block.name,
|
|
525
|
+
"data-props": JSON.stringify(values)
|
|
526
|
+
};
|
|
527
|
+
if (block.kind === "inline") return /* @__PURE__ */ jsx("span", { ...attributes, children: text });
|
|
528
|
+
if (!isContainer) return /* @__PURE__ */ jsx("div", { ...attributes, children: text });
|
|
529
|
+
return /* @__PURE__ */ jsxs("div", { ...attributes, children: [
|
|
530
|
+
text ? /* @__PURE__ */ jsx("p", { className: "sd-block-text", children: text }) : null,
|
|
531
|
+
/* @__PURE__ */ jsx("div", { className: "sd-block-body", children })
|
|
532
|
+
] });
|
|
533
|
+
}
|
|
534
|
+
function renderDirective(node, ctx) {
|
|
535
|
+
const { c } = ctx;
|
|
536
|
+
const custom = ctx.blocks.use(node);
|
|
537
|
+
if (custom) return renderCustomBlock(custom, ctx);
|
|
538
|
+
if (!isHelpBlock(node.name)) {
|
|
539
|
+
const source = sourceOf(node, ctx.markdown);
|
|
540
|
+
const lines = source.split("\n");
|
|
541
|
+
const closing = lines.length > 1 && /^\s*:{3,}\s*$/.test(lines.at(-1) ?? "");
|
|
542
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
543
|
+
literalBlock(lines[0] ?? ""),
|
|
544
|
+
renderAll(contentOf(node), ctx),
|
|
545
|
+
closing ? literalBlock(lines.at(-1)?.trim() ?? "") : null
|
|
546
|
+
] });
|
|
547
|
+
}
|
|
548
|
+
const content = contentOf(node);
|
|
549
|
+
const title = directiveTitle(node);
|
|
550
|
+
switch (node.name) {
|
|
551
|
+
case "callout":
|
|
552
|
+
return /* @__PURE__ */ jsx(c.Callout, { type: calloutType(node), title, children: renderAll(content, ctx) });
|
|
553
|
+
case "steps":
|
|
554
|
+
return /* @__PURE__ */ jsx(c.Steps, { children: content.map(
|
|
555
|
+
(child, i) => isDirective(child, "step") ? (
|
|
556
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
|
|
557
|
+
/* @__PURE__ */ jsx(c.Step, { title: directiveTitle(child), id: ctx.ids.get(child), children: renderAll(contentOf(child), ctx) }, i)
|
|
558
|
+
) : (
|
|
559
|
+
// A loose block between steps becomes a step of its own.
|
|
560
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
|
|
561
|
+
/* @__PURE__ */ jsx(c.Step, { children: renderNode(child, ctx) }, i)
|
|
562
|
+
)
|
|
563
|
+
) });
|
|
564
|
+
case "step":
|
|
565
|
+
return /* @__PURE__ */ jsx(c.Steps, { children: /* @__PURE__ */ jsx(c.Step, { title, id: ctx.ids.get(node), children: renderAll(content, ctx) }) });
|
|
566
|
+
case "tabs":
|
|
567
|
+
return renderTabs(content, ctx);
|
|
568
|
+
case "tab":
|
|
569
|
+
return renderTabs([node], ctx);
|
|
570
|
+
case "cards":
|
|
571
|
+
return /* @__PURE__ */ jsx(c.Cards, { children: content.map(
|
|
572
|
+
(child, i) => isDirective(child, "card") ? (
|
|
573
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
|
|
574
|
+
/* @__PURE__ */ jsx(Fragment$1, { children: renderCard(child, ctx) }, i)
|
|
575
|
+
) : (
|
|
576
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, never reordered.
|
|
577
|
+
/* @__PURE__ */ jsx(Fragment$1, { children: renderNode(child, ctx) }, i)
|
|
578
|
+
)
|
|
579
|
+
) });
|
|
580
|
+
case "card":
|
|
581
|
+
return /* @__PURE__ */ jsx(c.Cards, { children: renderCard(node, ctx) });
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
function renderNode(node, ctx) {
|
|
585
|
+
const { c } = ctx;
|
|
586
|
+
switch (node.type) {
|
|
587
|
+
case "root":
|
|
588
|
+
return renderAll(node.children, ctx);
|
|
589
|
+
case "paragraph":
|
|
590
|
+
return renderParagraph(node, ctx);
|
|
591
|
+
case "heading":
|
|
592
|
+
return /* @__PURE__ */ jsx(c.Heading, { level: node.depth, id: ctx.ids.get(node) ?? "", children: renderAll(node.children, { ...ctx, inline: true }) });
|
|
593
|
+
case "text":
|
|
594
|
+
return node.value;
|
|
595
|
+
case "emphasis":
|
|
596
|
+
return /* @__PURE__ */ jsx("em", { children: renderAll(node.children, ctx) });
|
|
597
|
+
case "strong":
|
|
598
|
+
return /* @__PURE__ */ jsx("strong", { children: renderAll(node.children, ctx) });
|
|
599
|
+
case "delete":
|
|
600
|
+
return /* @__PURE__ */ jsx("del", { children: renderAll(node.children, ctx) });
|
|
601
|
+
case "inlineCode":
|
|
602
|
+
return /* @__PURE__ */ jsx("code", { children: node.value });
|
|
603
|
+
case "break":
|
|
604
|
+
return /* @__PURE__ */ jsx("br", {});
|
|
605
|
+
case "thematicBreak":
|
|
606
|
+
return /* @__PURE__ */ jsx("hr", {});
|
|
607
|
+
case "blockquote":
|
|
608
|
+
return /* @__PURE__ */ jsx("blockquote", { children: renderAll(node.children, { ...ctx, tight: false }) });
|
|
609
|
+
case "list":
|
|
610
|
+
return renderList(node, ctx);
|
|
611
|
+
case "listItem":
|
|
612
|
+
return /* @__PURE__ */ jsx("li", { children: renderAll(node.children, ctx) });
|
|
613
|
+
case "table":
|
|
614
|
+
return renderTable(node, ctx);
|
|
615
|
+
case "code": {
|
|
616
|
+
const { language, title } = codeInfo(node.lang, node.meta);
|
|
617
|
+
return /* @__PURE__ */ jsx(c.Code, { code: node.value, lang: language, title });
|
|
618
|
+
}
|
|
619
|
+
// Raw HTML is shown as text, never as markup.
|
|
620
|
+
case "html":
|
|
621
|
+
return ctx.inline ? node.value : literalBlock(node.value.trim());
|
|
622
|
+
case "link":
|
|
623
|
+
return renderLink(node.url, node.title, renderAll(node.children, ctx), ctx);
|
|
624
|
+
case "image":
|
|
625
|
+
return renderImage(node, ctx);
|
|
626
|
+
case "linkReference": {
|
|
627
|
+
const definition = ctx.definitions.get(normalizeId(node.identifier));
|
|
628
|
+
if (!definition) return sourceOf(node, ctx.markdown);
|
|
629
|
+
return renderLink(definition.url, definition.title, renderAll(node.children, ctx), ctx);
|
|
630
|
+
}
|
|
631
|
+
case "imageReference": {
|
|
632
|
+
const definition = ctx.definitions.get(normalizeId(node.identifier));
|
|
633
|
+
if (!definition) return sourceOf(node, ctx.markdown);
|
|
634
|
+
return renderImage({ url: definition.url, alt: node.alt, title: definition.title }, ctx);
|
|
635
|
+
}
|
|
636
|
+
case "definition":
|
|
637
|
+
return null;
|
|
638
|
+
case "containerDirective":
|
|
639
|
+
return renderDirective(node, { ...ctx, tight: false });
|
|
640
|
+
case "leafDirective":
|
|
641
|
+
case "textDirective": {
|
|
642
|
+
const custom = ctx.blocks.use(node);
|
|
643
|
+
if (custom) return renderCustomBlock(custom, ctx);
|
|
644
|
+
return node.type === "leafDirective" ? literalBlock(sourceOf(node, ctx.markdown)) : sourceOf(node, ctx.markdown);
|
|
645
|
+
}
|
|
646
|
+
// Not ours: printed back as written.
|
|
647
|
+
case "footnoteDefinition":
|
|
648
|
+
return literalBlock(sourceOf(node, ctx.markdown));
|
|
649
|
+
case "footnoteReference":
|
|
650
|
+
return sourceOf(node, ctx.markdown);
|
|
651
|
+
default:
|
|
652
|
+
return "children" in node ? renderAll(node.children, ctx) : "value" in node ? String(node.value) : plainText(node);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
function HelpBody(props) {
|
|
656
|
+
const { page, components = {}, icon } = props;
|
|
657
|
+
const source = props.markdown ?? page?.markdown ?? "";
|
|
658
|
+
const headings = props.headings ?? page?.headings;
|
|
659
|
+
const root = parseHelpMarkdown(source);
|
|
660
|
+
const blocks = helpBlockMatcher(props.blocks, source);
|
|
661
|
+
const ctx = {
|
|
662
|
+
markdown: source,
|
|
663
|
+
blocks,
|
|
664
|
+
custom: components,
|
|
665
|
+
lang: props.lang ?? page?.locale ?? "",
|
|
666
|
+
ids: headingIds(root, headings, blocks.text),
|
|
667
|
+
c: { ...helpBodyComponents, ...pickBuiltIns(components) },
|
|
668
|
+
icon,
|
|
669
|
+
definitions: collectDefinitions(root),
|
|
670
|
+
linkless: false,
|
|
671
|
+
tight: false,
|
|
672
|
+
inline: false
|
|
673
|
+
};
|
|
674
|
+
return /* @__PURE__ */ jsx(Fragment, { children: renderNode(root, ctx) });
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// ../sdk-core/src/webhook.ts
|
|
678
|
+
function encoder() {
|
|
679
|
+
const Encoder = globalThis.TextEncoder;
|
|
680
|
+
if (!Encoder) throw new Error("TextEncoder is not available in this runtime.");
|
|
681
|
+
return new Encoder();
|
|
682
|
+
}
|
|
683
|
+
function bytesOf(body) {
|
|
684
|
+
if (typeof body === "string") return encoder().encode(body);
|
|
685
|
+
return body instanceof Uint8Array ? body : new Uint8Array(body);
|
|
686
|
+
}
|
|
687
|
+
function hex(buffer) {
|
|
688
|
+
let out = "";
|
|
689
|
+
for (const byte of new Uint8Array(buffer)) out += byte.toString(16).padStart(2, "0");
|
|
690
|
+
return out;
|
|
691
|
+
}
|
|
692
|
+
function constantTimeEqual(a, b) {
|
|
693
|
+
if (a.length !== b.length) return false;
|
|
694
|
+
let diff = 0;
|
|
695
|
+
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
696
|
+
return diff === 0;
|
|
697
|
+
}
|
|
698
|
+
function parseHeader(header) {
|
|
699
|
+
let t = Number.NaN;
|
|
700
|
+
const signatures = [];
|
|
701
|
+
for (const part of header.split(",")) {
|
|
702
|
+
const index = part.indexOf("=");
|
|
703
|
+
if (index < 0) continue;
|
|
704
|
+
const key = part.slice(0, index).trim();
|
|
705
|
+
const value = part.slice(index + 1).trim();
|
|
706
|
+
if (key === "t" && /^\d{1,12}$/.test(value)) t = Number(value);
|
|
707
|
+
else if (key === "v1" && /^[0-9a-f]{64}$/i.test(value)) signatures.push(value.toLowerCase());
|
|
708
|
+
}
|
|
709
|
+
return Number.isFinite(t) && signatures.length > 0 ? { t, signatures } : null;
|
|
710
|
+
}
|
|
711
|
+
async function verifyWebhookSignature(secret, rawBody, header, options = {}) {
|
|
712
|
+
if (!secret || !header || header.length > 1e3) return false;
|
|
713
|
+
const parsed = parseHeader(header);
|
|
714
|
+
if (!parsed) return false;
|
|
715
|
+
const now = options.now instanceof Date ? options.now.getTime() : options.now ?? Date.now();
|
|
716
|
+
const tolerance = options.toleranceSeconds ?? 300;
|
|
717
|
+
if (Math.abs(now / 1e3 - parsed.t) > tolerance) return false;
|
|
718
|
+
const subtle = options.crypto?.subtle ?? globalThis.crypto?.subtle;
|
|
719
|
+
if (!subtle) {
|
|
720
|
+
throw new Error(
|
|
721
|
+
"Web Crypto is not available: pass `crypto` (on Node 18: require('node:crypto').webcrypto)."
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
const text = encoder();
|
|
725
|
+
const key = await subtle.importKey(
|
|
726
|
+
"raw",
|
|
727
|
+
text.encode(secret),
|
|
728
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
729
|
+
false,
|
|
730
|
+
["sign"]
|
|
731
|
+
);
|
|
732
|
+
const prefix = text.encode(`${parsed.t}.`);
|
|
733
|
+
const body = bytesOf(rawBody);
|
|
734
|
+
const signed = new Uint8Array(prefix.length + body.length);
|
|
735
|
+
signed.set(prefix, 0);
|
|
736
|
+
signed.set(body, prefix.length);
|
|
737
|
+
const expected = hex(await subtle.sign("HMAC", key, signed));
|
|
738
|
+
let ok = false;
|
|
739
|
+
for (const signature of parsed.signatures) ok = constantTimeEqual(expected, signature) || ok;
|
|
740
|
+
return ok;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// src/fumadocs/revalidate.ts
|
|
744
|
+
function createHelpRevalidateHandler(options) {
|
|
745
|
+
return async (request) => {
|
|
746
|
+
if (!options.secret) {
|
|
747
|
+
return new Response("Webhook secret is not configured", { status: 500 });
|
|
748
|
+
}
|
|
749
|
+
const raw = await request.text();
|
|
750
|
+
const signature = request.headers.get("x-solutiondesk-signature");
|
|
751
|
+
if (!await verifyWebhookSignature(options.secret, raw, signature, options.verifyOptions)) {
|
|
752
|
+
return new Response("Invalid signature", { status: 401 });
|
|
753
|
+
}
|
|
754
|
+
const body = parseBody(raw);
|
|
755
|
+
if (!body) return new Response("Malformed body", { status: 400 });
|
|
756
|
+
if (body.event !== "ping" || options.includePing) await options.revalidate(body);
|
|
757
|
+
return new Response(null, { status: 204 });
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
function parseBody(raw) {
|
|
761
|
+
let body;
|
|
762
|
+
try {
|
|
763
|
+
body = JSON.parse(raw);
|
|
764
|
+
} catch {
|
|
765
|
+
return null;
|
|
766
|
+
}
|
|
767
|
+
if (typeof body !== "object" || body === null) return null;
|
|
768
|
+
const { event, path, previousPaths } = body;
|
|
769
|
+
if (typeof event !== "string") return null;
|
|
770
|
+
if (path !== null && path !== void 0 && typeof path !== "string") return null;
|
|
771
|
+
if (previousPaths !== void 0 && !Array.isArray(previousPaths)) return null;
|
|
772
|
+
return body;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// src/fumadocs/structured.ts
|
|
776
|
+
function helpStructuredData(markdown, headings, blocks) {
|
|
777
|
+
const source = markdown ?? "";
|
|
778
|
+
const root = parseHelpMarkdown(source);
|
|
779
|
+
const matcher = helpBlockMatcher(blocks, source);
|
|
780
|
+
const ids = headingIds(root, headings, matcher.text);
|
|
781
|
+
const out = { headings: [], contents: [] };
|
|
782
|
+
let current;
|
|
783
|
+
const text = (node) => plainText(node, matcher.text).replace(/\s+/g, " ").trim();
|
|
784
|
+
const add = (content) => {
|
|
785
|
+
if (content) out.contents.push({ heading: current, content });
|
|
786
|
+
};
|
|
787
|
+
const walk = (node) => {
|
|
788
|
+
if (node.type === "heading") {
|
|
789
|
+
current = ids.get(node);
|
|
790
|
+
const content = text(node);
|
|
791
|
+
if (current && content) out.headings.push({ id: current, content });
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
if (isTitledStep(node)) {
|
|
795
|
+
current = ids.get(node);
|
|
796
|
+
const content = directiveTitle(node) ?? "";
|
|
797
|
+
if (current) out.headings.push({ id: current, content });
|
|
798
|
+
for (const child of contentOf(node)) walk(child);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
const use = matcher.use(node);
|
|
802
|
+
if (use) {
|
|
803
|
+
add(use.text);
|
|
804
|
+
if (use.node.type === "containerDirective")
|
|
805
|
+
for (const child of contentOf(use.node)) walk(child);
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
if (node.type === "paragraph" || node.type === "tableCell") {
|
|
809
|
+
add(text(node));
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
if (node.type === "code" || node.type === "html" || node.type === "definition") return;
|
|
813
|
+
if ("children" in node) for (const child of node.children) walk(child);
|
|
814
|
+
};
|
|
815
|
+
walk(root);
|
|
816
|
+
return out;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// src/fumadocs/source.ts
|
|
820
|
+
var linkTitle = (title) => title.replace(/[[\]]/g, "").trim() || "Link";
|
|
821
|
+
var linkUrl = (url) => url.replace(/\)/g, "%29").replace(/\s/g, "%20");
|
|
822
|
+
var lastSegment = (path) => path.split("/").at(-1) ?? path;
|
|
823
|
+
var slugsOf = (path) => path ? path.split("/") : [];
|
|
824
|
+
var pageFileName = (page) => {
|
|
825
|
+
if (!page.path) return "index";
|
|
826
|
+
const name = lastSegment(page.path);
|
|
827
|
+
return name === "index" ? "index_" : name;
|
|
828
|
+
};
|
|
829
|
+
var folderDirName = (folder) => folder.group ? `(${folder.slug})` : folder.slug;
|
|
830
|
+
function helpMetaEntry(node) {
|
|
831
|
+
switch (node.type) {
|
|
832
|
+
case "page":
|
|
833
|
+
return pageFileName(node);
|
|
834
|
+
case "folder":
|
|
835
|
+
return folderDirName(node);
|
|
836
|
+
case "separator": {
|
|
837
|
+
const title = node.title?.replace(/---/g, "\u2013").trim();
|
|
838
|
+
if (!title) return "---";
|
|
839
|
+
return `---${node.icon ? `[${node.icon}]` : ""}${title}---`;
|
|
840
|
+
}
|
|
841
|
+
case "link":
|
|
842
|
+
return `${node.external ? "external:" : ""}${node.icon ? `[${node.icon}]` : ""}[${linkTitle(node.title)}](${linkUrl(node.url)})`;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
function helpTreeToFiles(payload, options) {
|
|
846
|
+
const files = [];
|
|
847
|
+
const ext = (name, extension) => `${name}${options.suffix ? `.${options.suffix}` : ""}.${extension}`;
|
|
848
|
+
const join = (dir, name) => dir ? `${dir}/${name}` : name;
|
|
849
|
+
const seen = /* @__PURE__ */ new Set();
|
|
850
|
+
const blocks = payload.site.blocks ?? [];
|
|
851
|
+
const addPage = (page, file) => {
|
|
852
|
+
if (seen.has(file)) return;
|
|
853
|
+
seen.add(file);
|
|
854
|
+
const isPrefetched = options.prefetched?.has(page.path) ?? false;
|
|
855
|
+
const prefetched = options.prefetched?.get(page.path) ?? null;
|
|
856
|
+
const load = isPrefetched ? async () => prefetched : () => options.load(page.path);
|
|
857
|
+
const data = {
|
|
858
|
+
title: page.title,
|
|
859
|
+
...page.description ? { description: page.description } : {},
|
|
860
|
+
...page.icon ? { icon: page.icon } : {},
|
|
861
|
+
path: page.path,
|
|
862
|
+
lang: options.lang,
|
|
863
|
+
blocks,
|
|
864
|
+
load,
|
|
865
|
+
structuredData: async () => {
|
|
866
|
+
const full = await load();
|
|
867
|
+
return full ? helpStructuredData(full.markdown, full.headings, blocks) : { headings: [], contents: [] };
|
|
868
|
+
},
|
|
869
|
+
...prefetched ? { page: prefetched } : {}
|
|
870
|
+
};
|
|
871
|
+
files.push({ type: "page", path: file, slugs: slugsOf(page.path), data });
|
|
872
|
+
};
|
|
873
|
+
const folderPages = (folder) => {
|
|
874
|
+
const pages = folder.children.map(helpMetaEntry);
|
|
875
|
+
if (!folder.index || !folder.listIndex) return pages;
|
|
876
|
+
const at = folder.children[0]?.type === "separator" ? 1 : 0;
|
|
877
|
+
return [...pages.slice(0, at), "index", ...pages.slice(at)];
|
|
878
|
+
};
|
|
879
|
+
const addFolder = (folder, dir) => {
|
|
880
|
+
if (folder.index) addPage(folder.index, join(dir, ext("index", "mdx")));
|
|
881
|
+
const data = {
|
|
882
|
+
title: folder.title,
|
|
883
|
+
...folder.description ? { description: folder.description } : {},
|
|
884
|
+
...folder.icon ? { icon: folder.icon } : {},
|
|
885
|
+
...folder.root ? { root: true } : {},
|
|
886
|
+
defaultOpen: folder.defaultOpen,
|
|
887
|
+
collapsible: folder.collapsible,
|
|
888
|
+
...folder.index && !folder.listIndex ? { pagesIndex: "index" } : {},
|
|
889
|
+
pages: folderPages(folder)
|
|
890
|
+
};
|
|
891
|
+
files.push({ type: "meta", path: join(dir, ext("meta", "json")), data });
|
|
892
|
+
addChildren(folder.children, dir);
|
|
893
|
+
};
|
|
894
|
+
const addChildren = (nodes, dir) => {
|
|
895
|
+
for (const node of nodes) {
|
|
896
|
+
if (node.type === "page") {
|
|
897
|
+
addPage(node, join(dir, ext(pageFileName(node), "mdx")));
|
|
898
|
+
} else if (node.type === "folder") {
|
|
899
|
+
addFolder(node, join(dir, folderDirName(node)));
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
files.push({
|
|
904
|
+
type: "meta",
|
|
905
|
+
path: ext("meta", "json"),
|
|
906
|
+
data: { title: payload.site.name, pages: payload.tree.map(helpMetaEntry) }
|
|
907
|
+
});
|
|
908
|
+
addChildren(payload.tree, "");
|
|
909
|
+
return files;
|
|
910
|
+
}
|
|
911
|
+
function helpTreePaths(tree) {
|
|
912
|
+
const out = [];
|
|
913
|
+
const walk = (nodes) => {
|
|
914
|
+
for (const node of nodes) {
|
|
915
|
+
if (node.type === "page") out.push(node.path);
|
|
916
|
+
else if (node.type === "folder") {
|
|
917
|
+
if (node.index) out.push(node.index.path);
|
|
918
|
+
walk(node.children);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
walk(tree);
|
|
923
|
+
return out;
|
|
924
|
+
}
|
|
925
|
+
var isNotFoundError = (error) => error instanceof Error && error.name === "SolutionDeskError" && error.status === 404;
|
|
926
|
+
async function mapLimit(items, limit, fn) {
|
|
927
|
+
const out = new Array(items.length);
|
|
928
|
+
let next = 0;
|
|
929
|
+
const worker = async () => {
|
|
930
|
+
while (next < items.length) {
|
|
931
|
+
const index = next++;
|
|
932
|
+
out[index] = await fn(items[index]);
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
await Promise.all(Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, worker));
|
|
936
|
+
return out;
|
|
937
|
+
}
|
|
938
|
+
async function createHelpSource(client, options = {}) {
|
|
939
|
+
const { requestOptions } = options;
|
|
940
|
+
const languages = options.languages?.length ? [...options.languages] : [void 0];
|
|
941
|
+
const files = await Promise.all(
|
|
942
|
+
languages.map(async (lang) => {
|
|
943
|
+
const payload = await client.site(lang ? { lang } : {}, requestOptions);
|
|
944
|
+
const forLang = lang ?? payload.locale;
|
|
945
|
+
const load = async (path) => {
|
|
946
|
+
try {
|
|
947
|
+
return (await client.page(path, { lang: forLang }, requestOptions)).page;
|
|
948
|
+
} catch (error) {
|
|
949
|
+
if (isNotFoundError(error)) return null;
|
|
950
|
+
throw error;
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
let prefetched;
|
|
954
|
+
if (options.pages === "eager") {
|
|
955
|
+
const paths = helpTreePaths(payload.tree);
|
|
956
|
+
const pages = await mapLimit(paths, options.concurrency ?? 6, load);
|
|
957
|
+
prefetched = new Map(paths.map((path, index) => [path, pages[index] ?? null]));
|
|
958
|
+
}
|
|
959
|
+
return helpTreeToFiles(payload, {
|
|
960
|
+
lang: forLang,
|
|
961
|
+
load,
|
|
962
|
+
...lang ? { suffix: lang } : {},
|
|
963
|
+
...prefetched ? { prefetched } : {}
|
|
964
|
+
});
|
|
965
|
+
})
|
|
966
|
+
);
|
|
967
|
+
return { files: files.flat() };
|
|
968
|
+
}
|
|
969
|
+
function helpDynamicSource(client, options = {}) {
|
|
970
|
+
return { files: async () => (await createHelpSource(client, options)).files };
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// src/fumadocs/toc.ts
|
|
974
|
+
function getHelpToc(headings, options = {}) {
|
|
975
|
+
const list = "headings" in headings ? headings.headings : headings;
|
|
976
|
+
const min = options.minLevel ?? 1;
|
|
977
|
+
const max = options.maxLevel ?? 6;
|
|
978
|
+
return list.filter((heading) => heading.level >= min && heading.level <= max && heading.id).map((heading) => ({ title: heading.text, url: `#${heading.id}`, depth: heading.level }));
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
export { HELP_BLOCKS, HELP_CALLOUT_TYPES, HelpBody, createHelpRevalidateHandler, createHelpSource, getHelpToc, helpBlockText, helpBlockValues, helpBodyComponents, helpDynamicSource, helpMetaEntry, helpStructuredData, helpTreePaths, helpTreeToFiles, safeHelpHref };
|