@tenphi/docs 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/schema.json +976 -80
- package/dist/config-CD77r6rT.js +2005 -0
- package/dist/config-CD77r6rT.js.map +1 -0
- package/dist/content/index.d.ts +1 -1
- package/dist/content/index.js +1 -1
- package/dist/{content-XLQlJDnf.js → content-6Xp1J4Jh.js} +128 -26
- package/dist/content-6Xp1J4Jh.js.map +1 -0
- package/dist/index.d.ts +88 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +324 -5
- package/dist/index.js.map +1 -1
- package/dist/markdown/index.d.ts +1 -1
- package/dist/{types-C-AgUcAb.d.ts → types-CoFJUBNa.d.ts} +131 -21
- package/dist/types-CoFJUBNa.d.ts.map +1 -0
- package/package.json +2 -2
- package/dist/config-Ci9Nv0wQ.js +0 -927
- package/dist/config-Ci9Nv0wQ.js.map +0 -1
- package/dist/content-XLQlJDnf.js.map +0 -1
- package/dist/types-C-AgUcAb.d.ts.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as normalizeDocsConfig } from "./config-
|
|
1
|
+
import { i as normalizeDocsConfig, m as validateTableOfContents, p as validateSeo, u as pagePublishing } from "./config-CD77r6rT.js";
|
|
2
2
|
import { cloneAst, parseMarkdown, removeRenderedTitle, serializeMarkdown, stripLeadingBadgeBlock } from "./markdown/index.js";
|
|
3
3
|
import { lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
@@ -150,17 +150,20 @@ function openApiPages(source, routeBase, text) {
|
|
|
150
150
|
];
|
|
151
151
|
if (string(operation.description)) body.push(operation.description, "");
|
|
152
152
|
if (Array.isArray(operation.tags) && operation.tags.length) body.push(`**Tags:** ${operation.tags.filter((tag) => typeof tag === "string").map(escapeText).join(", ")}`, "");
|
|
153
|
-
const
|
|
154
|
-
|
|
153
|
+
const parameters = mergeParameters(resolveReference(document.paths[path], document).parameters, operation.parameters, document);
|
|
154
|
+
renderSecurity(body, operation.security !== void 0 ? operation.security : document.security, document);
|
|
155
155
|
if (parameters.length) {
|
|
156
156
|
body.push("## Parameters", "", "| Name | In | Required | Description |", "| --- | --- | --- | --- |");
|
|
157
|
-
for (const
|
|
158
|
-
const item = resolveReference(parameter, document);
|
|
159
|
-
if (!record(item)) continue;
|
|
157
|
+
for (const item of parameters) {
|
|
160
158
|
const schema = record(item.schema) ? ` (${schemaLabel(item.schema)})` : "";
|
|
161
159
|
body.push(`| ${tableCell(string(item.name) ?? "—")} | ${tableCell(string(item.in) ?? "—")} | ${item.required === true ? "Yes" : "No"} | ${tableCell(`${string(item.description) ?? ""}${schema}`)} |`);
|
|
162
160
|
}
|
|
163
161
|
body.push("");
|
|
162
|
+
for (const item of parameters) if ("example" in item || "examples" in item || "content" in item) {
|
|
163
|
+
body.push(`### ${escapeText(String(item.name))} (${escapeText(String(item.in))})`, "");
|
|
164
|
+
renderExamples(body, item, document);
|
|
165
|
+
renderContent(body, item.content, document);
|
|
166
|
+
}
|
|
164
167
|
}
|
|
165
168
|
if (operation.requestBody !== void 0) {
|
|
166
169
|
const request = resolveReference(operation.requestBody, document);
|
|
@@ -168,7 +171,7 @@ function openApiPages(source, routeBase, text) {
|
|
|
168
171
|
body.push("## Request body", "");
|
|
169
172
|
if (string(request.description)) body.push(request.description, "");
|
|
170
173
|
body.push(`**Required:** ${request.required === true ? "Yes" : "No"}`, "");
|
|
171
|
-
renderContent(body, request.content);
|
|
174
|
+
renderContent(body, request.content, document);
|
|
172
175
|
}
|
|
173
176
|
}
|
|
174
177
|
if (record(operation.responses)) {
|
|
@@ -177,12 +180,12 @@ function openApiPages(source, routeBase, text) {
|
|
|
177
180
|
const response = resolveReference(value, document);
|
|
178
181
|
if (!record(response)) continue;
|
|
179
182
|
body.push(`### ${escapeText(status)}${string(response.description) ? ` — ${escapeText(response.description)}` : ""}`, "");
|
|
180
|
-
renderContent(body, response.content);
|
|
183
|
+
renderContent(body, response.content, document);
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
pages.push({
|
|
184
187
|
route,
|
|
185
|
-
sourcePath: `${source}#${route.
|
|
188
|
+
sourcePath: `${source}#${route.split("/").at(-1)}.md`,
|
|
186
189
|
title: operationTitle,
|
|
187
190
|
...string(operation.description) ? { description: operation.description } : {},
|
|
188
191
|
body: body.join("\n")
|
|
@@ -190,15 +193,85 @@ function openApiPages(source, routeBase, text) {
|
|
|
190
193
|
}
|
|
191
194
|
return pages;
|
|
192
195
|
}
|
|
193
|
-
function
|
|
196
|
+
function mergeParameters(path, operation, document) {
|
|
197
|
+
const merged = /* @__PURE__ */ new Map();
|
|
198
|
+
for (const list of [path, operation]) {
|
|
199
|
+
if (list === void 0) continue;
|
|
200
|
+
if (!Array.isArray(list)) throw new Error("OpenAPI parameters must be an array.");
|
|
201
|
+
const seen = /* @__PURE__ */ new Set();
|
|
202
|
+
for (const value of list) {
|
|
203
|
+
const item = resolveReference(value, document);
|
|
204
|
+
if (!record(item) || !string(item.name) || ![
|
|
205
|
+
"query",
|
|
206
|
+
"header",
|
|
207
|
+
"path",
|
|
208
|
+
"cookie"
|
|
209
|
+
].includes(String(item.in))) throw new Error("OpenAPI parameters need a name and a valid in location.");
|
|
210
|
+
const identity = JSON.stringify([item.name, item.in]);
|
|
211
|
+
if (seen.has(identity)) throw new Error(`Duplicate OpenAPI parameter ${item.name} in ${item.in}.`);
|
|
212
|
+
seen.add(identity);
|
|
213
|
+
merged.set(identity, item);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return [...merged.values()];
|
|
217
|
+
}
|
|
218
|
+
function renderContent(lines, content, document) {
|
|
194
219
|
if (!record(content)) return;
|
|
195
220
|
for (const [mediaType, media] of Object.entries(content)) {
|
|
196
221
|
lines.push(`#### ${escapeText(mediaType)}`, "");
|
|
197
222
|
if (record(media) && media.schema !== void 0) lines.push(jsonBlock(media.schema), "");
|
|
198
|
-
if (record(media)
|
|
199
|
-
|
|
200
|
-
|
|
223
|
+
if (record(media)) renderExamples(lines, media, document);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function renderExamples(lines, owner, document) {
|
|
227
|
+
if ("example" in owner) lines.push("**Example**", "", jsonBlock(owner.example), "");
|
|
228
|
+
if (!record(owner.examples)) return;
|
|
229
|
+
for (const [name, raw] of Object.entries(owner.examples)) {
|
|
230
|
+
const example = resolveReference(raw, document);
|
|
231
|
+
if (!record(example)) throw new Error(`OpenAPI example ${name} must be an Example Object.`);
|
|
232
|
+
lines.push(`**Example: ${escapeText(name)}**`, "");
|
|
233
|
+
if (string(example.summary)) lines.push(escapeText(example.summary), "");
|
|
234
|
+
if (string(example.description)) lines.push(example.description, "");
|
|
235
|
+
if ("value" in example) lines.push(jsonBlock(example.value), "");
|
|
236
|
+
if (string(example.externalValue)) {
|
|
237
|
+
const url = example.externalValue;
|
|
238
|
+
lines.push(/^https?:\/\/[^\s<>]+$/i.test(url) ? `[External example](<${url}>)` : `External example: ${escapeText(url)}`, "");
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function renderSecurity(lines, security, document) {
|
|
243
|
+
lines.push("## Authentication", "");
|
|
244
|
+
if (security === void 0 || Array.isArray(security) && !security.length) {
|
|
245
|
+
lines.push("No authentication required.", "");
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (!Array.isArray(security)) throw new Error("OpenAPI security must be an array.");
|
|
249
|
+
const schemes = record(document.components) && record(document.components.securitySchemes) ? document.components.securitySchemes : {};
|
|
250
|
+
if (security.length > 1) lines.push("Use any one of these alternatives:", "");
|
|
251
|
+
for (const [index, requirement] of security.entries()) {
|
|
252
|
+
if (!record(requirement)) throw new Error("OpenAPI security requirements must be objects.");
|
|
253
|
+
if (security.length > 1) lines.push(`### Alternative ${index + 1}`, "");
|
|
254
|
+
const names = Object.keys(requirement);
|
|
255
|
+
if (!names.length) {
|
|
256
|
+
lines.push("No authentication required (anonymous access).", "");
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (names.length > 1) lines.push("All of the following are required together:", "");
|
|
260
|
+
for (const name of names) {
|
|
261
|
+
const scheme = resolveReference(schemes[name], document);
|
|
262
|
+
if (!record(scheme)) throw new Error(`Unknown OpenAPI security scheme: ${name}.`);
|
|
263
|
+
const scopes = requirement[name];
|
|
264
|
+
if (!Array.isArray(scopes) || scopes.some((scope) => typeof scope !== "string")) throw new Error(`OpenAPI security scopes for ${name} must be strings.`);
|
|
265
|
+
let detail = string(scheme.type) ?? "security scheme";
|
|
266
|
+
if (scheme.type === "http") detail = `HTTP ${string(scheme.scheme) ?? "authentication"}${string(scheme.bearerFormat) ? ` (${scheme.bearerFormat})` : ""}`;
|
|
267
|
+
else if (scheme.type === "apiKey") detail = `API key ${string(scheme.name) ?? name} in ${string(scheme.in) ?? "request"}`;
|
|
268
|
+
else if (scheme.type === "oauth2") detail = "OAuth 2.0";
|
|
269
|
+
else if (scheme.type === "openIdConnect") detail = "OpenID Connect";
|
|
270
|
+
else if (scheme.type === "mutualTLS") detail = "Mutual TLS";
|
|
271
|
+
lines.push(`- **${escapeText(name)}:** ${escapeText(detail)}${scopes.length ? `. Required scopes: ${scopes.map((scope) => escapeText(String(scope))).join(", ")}` : ""}`);
|
|
272
|
+
if (string(scheme.description)) lines.push("", scheme.description, "");
|
|
201
273
|
}
|
|
274
|
+
lines.push("");
|
|
202
275
|
}
|
|
203
276
|
}
|
|
204
277
|
function assertLocalReferences(value, document, source, seen = /* @__PURE__ */ new Set()) {
|
|
@@ -208,22 +281,46 @@ function assertLocalReferences(value, document, source, seen = /* @__PURE__ */ n
|
|
|
208
281
|
else for (const [key, item] of Object.entries(value)) {
|
|
209
282
|
if (key === "$ref" && (typeof item !== "string" || !item.startsWith("#/"))) throw new Error(`OpenAPI source ${source} has an external or invalid $ref; bundle references into the document first.`);
|
|
210
283
|
if (key === "$ref" && typeof item === "string" && lookupReference(item, document) === void 0) throw new Error(`OpenAPI source ${source} has an unresolved reference: ${item}.`);
|
|
284
|
+
if ([
|
|
285
|
+
"example",
|
|
286
|
+
"default",
|
|
287
|
+
"const",
|
|
288
|
+
"enum"
|
|
289
|
+
].includes(key)) continue;
|
|
290
|
+
if (key === "examples") {
|
|
291
|
+
if (record(item)) {
|
|
292
|
+
for (const example of Object.values(item)) if (record(example)) {
|
|
293
|
+
const { value: _payload, ...metadata } = example;
|
|
294
|
+
assertLocalReferences(metadata, document, source, seen);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (key === "properties" && record(item)) {
|
|
300
|
+
for (const schema of Object.values(item)) assertLocalReferences(schema, document, source, seen);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
211
303
|
assertLocalReferences(item, document, source, seen);
|
|
212
304
|
}
|
|
213
305
|
}
|
|
214
306
|
function resolveReference(value, document) {
|
|
215
307
|
const visited = /* @__PURE__ */ new Set();
|
|
308
|
+
const overrides = {};
|
|
216
309
|
while (record(value) && typeof value.$ref === "string") {
|
|
310
|
+
for (const key of ["summary", "description"]) if (!(key in overrides) && typeof value[key] === "string") overrides[key] = value[key];
|
|
217
311
|
const ref = value.$ref;
|
|
218
312
|
if (visited.has(ref)) throw new Error(`Circular OpenAPI reference: ${ref}.`);
|
|
219
313
|
visited.add(ref);
|
|
220
314
|
value = lookupReference(ref, document);
|
|
221
315
|
if (value === void 0) throw new Error(`Unresolved OpenAPI reference: ${ref}.`);
|
|
222
316
|
}
|
|
223
|
-
return value
|
|
317
|
+
return record(value) ? {
|
|
318
|
+
...value,
|
|
319
|
+
...overrides
|
|
320
|
+
} : value;
|
|
224
321
|
}
|
|
225
322
|
function lookupReference(ref, document) {
|
|
226
|
-
return ref.slice(2).split("/").reduce((current, part) => record(current) ? current[part.replace(/~1/g, "/").replace(/~0/g, "~")] : void 0, document);
|
|
323
|
+
return decodeURIComponent(ref.slice(2)).split("/").reduce((current, part) => record(current) || Array.isArray(current) ? current[part.replace(/~1/g, "/").replace(/~0/g, "~")] : void 0, document);
|
|
227
324
|
}
|
|
228
325
|
function record(value) {
|
|
229
326
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -247,7 +344,7 @@ function schemaLabel(schema) {
|
|
|
247
344
|
return typeof schema.$ref === "string" ? schema.$ref.split("/").at(-1) ?? "schema" : string(schema.type) ?? "schema";
|
|
248
345
|
}
|
|
249
346
|
function jsonBlock(value) {
|
|
250
|
-
const body = JSON.stringify(value, null, 2);
|
|
347
|
+
const body = JSON.stringify(value, null, 2) ?? "null";
|
|
251
348
|
let longest = 0;
|
|
252
349
|
for (const [run] of body.matchAll(/`+/g)) longest = Math.max(longest, run.length);
|
|
253
350
|
const fence = "`".repeat(Math.max(3, longest + 1));
|
|
@@ -495,7 +592,8 @@ function inside$1(root, path) {
|
|
|
495
592
|
//#region src/graph/index.ts
|
|
496
593
|
const MARKDOWN_EXTENSIONS = [".md", ".mdx"];
|
|
497
594
|
const execFileAsync = promisify(execFile);
|
|
498
|
-
const
|
|
595
|
+
const DOCS_FRONTMATTER_KEYS = /* @__PURE__ */ new Set([
|
|
596
|
+
"seo",
|
|
499
597
|
"aliases",
|
|
500
598
|
"title",
|
|
501
599
|
"description",
|
|
@@ -576,10 +674,10 @@ async function createDocsGraph(options = {}) {
|
|
|
576
674
|
}], routeMap, diagnostics);
|
|
577
675
|
validateNavigation(tab.items ?? [], routeMap, diagnostics);
|
|
578
676
|
}
|
|
579
|
-
for (const version of config.site.versions ?? []) if (!
|
|
677
|
+
for (const version of config.site.versions ?? []) if (![version.routeBase, ...Object.keys(config.locales ?? {}).filter((locale) => locale !== "root").map((locale) => `/${locale}${version.routeBase === "/" ? "" : version.routeBase}`)].some((route) => routeMap.has(route) && !routeMap.get(route).frontmatter.draft)) diagnostics.push({
|
|
580
678
|
code: "DOCS_VERSION_ROOT_MISSING",
|
|
581
679
|
severity: "error",
|
|
582
|
-
message: `Version ${version.label} needs a page at ${version.routeBase}.`
|
|
680
|
+
message: `Version ${version.label} needs a non-draft page at ${version.routeBase} or a localized equivalent.`
|
|
583
681
|
});
|
|
584
682
|
entries.sort((left, right) => left.route.localeCompare(right.route));
|
|
585
683
|
return {
|
|
@@ -592,6 +690,10 @@ async function createDocsGraph(options = {}) {
|
|
|
592
690
|
entryId: entry.id,
|
|
593
691
|
sourcePath: entry.sourcePath,
|
|
594
692
|
title: entry.title,
|
|
693
|
+
discoverable: !entry.frontmatter.draft,
|
|
694
|
+
indexable: pagePublishing(entry, config).index,
|
|
695
|
+
sitemap: pagePublishing(entry, config).sitemap,
|
|
696
|
+
canonical: pagePublishing(entry, config).canonical,
|
|
595
697
|
...entry.frontmatter.sidebar !== void 0 ? { sidebar: entry.frontmatter.sidebar } : {}
|
|
596
698
|
})),
|
|
597
699
|
assets: Array.from(new Map(entries.flatMap((entry) => entry.assets).map((asset) => [asset.publicPath ?? `${asset.original}:${asset.line ?? ""}`, asset])).values()),
|
|
@@ -817,7 +919,7 @@ async function readEntry(source, config, diagnostics) {
|
|
|
817
919
|
if (frontmatter.sidebar === void 0 && source.navigation !== void 0) frontmatter.sidebar = source.navigation;
|
|
818
920
|
const diagnosticCount = diagnostics.length;
|
|
819
921
|
const metadata = {};
|
|
820
|
-
for (const key of Object.keys(frontmatter)) if (!
|
|
922
|
+
for (const key of Object.keys(frontmatter)) if (!DOCS_FRONTMATTER_KEYS.has(key)) {
|
|
821
923
|
if (config.content.frontmatter !== "reject") {
|
|
822
924
|
metadata[key] = frontmatter[key];
|
|
823
925
|
delete frontmatter[key];
|
|
@@ -1381,6 +1483,7 @@ function validateFrontmatter(value, file, diagnostics) {
|
|
|
1381
1483
|
message,
|
|
1382
1484
|
file
|
|
1383
1485
|
});
|
|
1486
|
+
if (value.seo !== void 0) for (const message of validateSeo(value.seo, "page")) error(message);
|
|
1384
1487
|
if (value.aliases !== void 0 && (!Array.isArray(value.aliases) || value.aliases.some((alias) => typeof alias !== "string"))) error("aliases must be an array of route strings.");
|
|
1385
1488
|
for (const key of [
|
|
1386
1489
|
"title",
|
|
@@ -1397,10 +1500,7 @@ function validateFrontmatter(value, file, diagnostics) {
|
|
|
1397
1500
|
validateOptionalStrings(record.sidebar, ["label", "group"], "sidebar", error);
|
|
1398
1501
|
if (record.sidebar.order !== void 0 && typeof record.sidebar.order !== "number") error("sidebar.order must be a number.");
|
|
1399
1502
|
}
|
|
1400
|
-
if (
|
|
1401
|
-
const level = record.tableOfContents[key];
|
|
1402
|
-
if (level !== void 0 && (!Number.isInteger(level) || Number(level) < 1 || Number(level) > 6)) error(`tableOfContents.${key} must be an integer from 1 to 6.`);
|
|
1403
|
-
}
|
|
1503
|
+
if (record.tableOfContents !== void 0) for (const message of validateTableOfContents(record.tableOfContents)) error(message);
|
|
1404
1504
|
if (record.editUrl !== void 0 && record.editUrl !== false && typeof record.editUrl !== "string") error("editUrl must be false or a string.");
|
|
1405
1505
|
if (record.lastUpdated !== void 0 && typeof record.lastUpdated !== "boolean" && !(record.lastUpdated instanceof Date)) error("lastUpdated must be a date or boolean.");
|
|
1406
1506
|
if (record.hero !== void 0) {
|
|
@@ -1424,6 +1524,8 @@ function validateHero(hero, error) {
|
|
|
1424
1524
|
if (!isPlainRecord(hero.image)) error("hero.image must be an object.");
|
|
1425
1525
|
else {
|
|
1426
1526
|
const image = hero.image;
|
|
1527
|
+
for (const key of ["width", "height"]) if (image[key] !== void 0 && (!Number.isInteger(image[key]) || Number(image[key]) <= 0)) error(`hero.image.${key} must be a positive integer.`);
|
|
1528
|
+
if (image.width === void 0 !== (image.height === void 0)) error("hero.image needs both width and height when dimensions are specified.");
|
|
1427
1529
|
if ([
|
|
1428
1530
|
"html",
|
|
1429
1531
|
"file",
|
|
@@ -1549,6 +1651,6 @@ function toLoaderEntry(entry) {
|
|
|
1549
1651
|
};
|
|
1550
1652
|
}
|
|
1551
1653
|
//#endregion
|
|
1552
|
-
export {
|
|
1654
|
+
export { reconcileDocsLock as _, validateDocs as a, writeDocsLock as b, normalizeRoute as c, defaultLock as d, discoverPackage as f, readDocsLock as g, packageNameFromSpecifier as h, formatDiagnostics as i, routeForPath as l, materializePackage as m, DocsValidationError as n, DOCS_FRONTMATTER_KEYS as o, lockForSource as p, assertValidDocs as r, createDocsGraph as s, createDocsLoader as t, assertSafePackagePath as u, resolvePackageLock as v, validateLock as y };
|
|
1553
1655
|
|
|
1554
|
-
//# sourceMappingURL=content-
|
|
1656
|
+
//# sourceMappingURL=content-6Xp1J4Jh.js.map
|