@umami/shiso 1.10.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/bin/shiso.mjs +20 -1
  2. package/dist/chunks/App.js +363 -29
  3. package/dist/chunks/docs.js +33 -41
  4. package/dist/entry-client.js +1 -1
  5. package/dist/entry-server.js +30 -2
  6. package/docs.schema.json +90 -0
  7. package/mdx.config.ts +13 -97
  8. package/package.json +5 -4
  9. package/scripts/build-runtime.mjs +1 -0
  10. package/scripts/check-content.mjs +358 -0
  11. package/scripts/expand-navigation-globs.mjs +208 -0
  12. package/scripts/expand-openapi-navigation.mjs +94 -0
  13. package/scripts/generate-openapi.mjs +117 -0
  14. package/scripts/generate-search-index.mjs +31 -1
  15. package/scripts/lib/openapi.mjs +653 -0
  16. package/scripts/load-docs-config.mjs +52 -6
  17. package/scripts/load-shiso-config.mjs +45 -3
  18. package/scripts/prerender.mjs +83 -3
  19. package/scripts/vite-docs-config.mjs +27 -6
  20. package/src/App.tsx +0 -1
  21. package/src/components/CodeBlock.tsx +73 -9
  22. package/src/components/DocContent.tsx +24 -3
  23. package/src/components/Docs.tsx +6 -1
  24. package/src/components/OpenApiOperation.tsx +197 -0
  25. package/src/components/SideNav.tsx +8 -2
  26. package/src/components/docs/Card.tsx +1 -1
  27. package/src/components/docs/CodeGroup.tsx +6 -2
  28. package/src/components/docs/Expandable.tsx +1 -1
  29. package/src/components/docs/PropertiesTable.tsx +12 -25
  30. package/src/components/docs/styles.ts +7 -4
  31. package/src/entry-server.tsx +50 -0
  32. package/src/lib/code-blocks.ts +18 -0
  33. package/src/lib/code-meta.ts +87 -0
  34. package/src/lib/docs-config.ts +4 -1
  35. package/src/lib/openapi.generated.ts +4 -0
  36. package/src/lib/openapi.ts +63 -0
  37. package/src/lib/rehype-shiki.ts +196 -0
  38. package/src/lib/site-model.ts +2 -0
  39. package/src/lib/types.ts +116 -2
  40. package/src/styles/global.css +75 -76
  41. package/types/config.d.ts +11 -4
  42. package/vite.config.ts +49 -3
  43. package/CHANGELOG.md +0 -171
package/bin/shiso.mjs CHANGED
@@ -5,6 +5,7 @@ import fs from 'node:fs/promises';
5
5
  import path from 'node:path';
6
6
  import process from 'node:process';
7
7
  import { fileURLToPath } from 'node:url';
8
+ import { checkContent } from '../scripts/check-content.mjs';
8
9
  import { loadShisoConfig } from '../scripts/load-shiso-config.mjs';
9
10
  import { validateProject } from '../scripts/validate-config.mjs';
10
11
 
@@ -23,7 +24,7 @@ Commands:
23
24
  dev Start the development server
24
25
  build Validate and build the static site
25
26
  preview Preview the production build
26
- check Validate docs.json and shiso.config
27
+ check Validate configuration, pages, links, anchors, and local assets
27
28
 
28
29
  Options after dev, build, or preview are passed to Vite.
29
30
  `;
@@ -63,6 +64,24 @@ async function check(projectRoot) {
63
64
  if (shiso.sourcePath) {
64
65
  console.log(`${path.basename(shiso.sourcePath)} loaded.`);
65
66
  }
67
+
68
+ const content = await checkContent({ root: projectRoot, shiso: shiso.config });
69
+
70
+ for (const warning of content.warnings) {
71
+ console.warn(` warning: ${warning}`);
72
+ }
73
+
74
+ if (!content.valid) {
75
+ console.error('\nContent validation failed:\n');
76
+ for (const error of content.errors) {
77
+ console.error(` ${error}`);
78
+ }
79
+ process.exit(1);
80
+ }
81
+
82
+ console.log(
83
+ `Content is valid${content.warnings.length ? ` (${content.warnings.length} warnings)` : ''}.`,
84
+ );
66
85
  }
67
86
 
68
87
  async function main() {
@@ -1,11 +1,10 @@
1
- import { $ as POPUP_COLLISION_AVOIDANCE, $n as isHTMLElement, $t as itemPress, A as popupStoreSelectors, An as ARROW_UP, B as useSyncedFloatingRootContext, Bn as EMPTY_ARRAY, C as pressableTriggerOpenStateMapping, Cn as isTypeableCombobox, Ct as COMPOSITE_KEYS, D as useFocus, Dn as ARROW_DOWN, E as useHoverFloatingInteraction, En as getTarget, F as setPopupOpenState, Fn as useId$1, G as FloatingNode, Gn as ownerDocument, Gt as useValueAsRef, H as useDismiss, Hn as useMergedRefs, Ht as getIcon, I as useImplicitActiveTrigger, In as useTimeout, J as useFloatingParentNodeId, Jn as useIsoLayoutEffect, Jt as createChangeEventDetails, K as FloatingTree, Kn as useCompositeRootContext, Kt as useCompositeListItem, L as useOpenStateTransitions, Ln as Button, M as FOCUSABLE_POPUP_PROPS, Mn as webkit, N as PopupHandleAttachment, Nn as mac, O as usePopupHandleStore, On as ARROW_LEFT, P as attachPreventUnmountOnClose, Pn as useBaseUiId, Q as DROPDOWN_COLLISION_AVOIDANCE, Qn as getWindow, Qt as imperativeAction, R as usePopupInteractionProps, Rn as cn, Rt as Badge, S as popupTransitionStateMapping, Sn as getFloatingFocusElement, T as useHoverReferenceInteraction, Tn as contains, Tt as useCompositeItem, U as useClick, Un as useButton, Ut as useOpenChangeComplete, V as ReactStore, Vn as EMPTY_OBJECT, Vt as resolveIcon, W as FloatingFocusManager, Wn as dispatchClickWithModifiers, Wt as useAnimationsFinished, X as FloatingTreeStore, Xn as useRefWithInit, Xt as escapeKey, Y as useFloatingTree, Yn as useStableCallback, Yt as cancelOpen, Z as FloatingPortal, Zn as getParentNode, Zt as focusOut, _n as isIndexOutOfListBounds, _t as Collapsible, a as DialogTitle, an as triggerPress, b as getDisabledMountTransitionStyles, bn as isVirtualPointerEvent, c as useOpenInteractionType, cn as useDirection, cr as Route, d as useScrollLock, dn as getTabbableBeforeElement, dr as useLocation, en as listNavigation, er as isLastTraversableNode, et as enqueueFocus, f as DialogPortal$1, fn as isOutsideEvent, fr as useNavigate, g as DialogBackdrop, gn as isElementVisible, h as DialogClose, hn as getMinListIndex, ht as renderInlineMarkdown, i as DialogContent$1, in as triggerHover, ir as createLucideIcon, j as PopupTriggerMap, jn as jsdom, k as createInitialPopupStoreState, kn as ARROW_RIGHT, l as DialogTitle$1, ln as getNextTabbable, m as DialogPopup, mn as getMaxListIndex, n as ZoomableImage, nn as siblingOpen, nr as ChevronRight, nt as fastComponent, o as DialogTrigger, on as CompositeList, or as Link, p as InternalBackdrop, pn as findNonDisabledListIndex, q as useFloatingNodeId, qn as mergeProps$1, qt as useAnimationFrame, r as Dialog$1, rn as triggerFocus, rr as Check, rt as fastComponentRef, s as DialogTrigger$1, sn as addEventListener, sr as Navigate, t as docs_exports, tn as outsidePress, tr as X$1, tt as FocusGuard, u as DialogRoot, un as getTabbableAfterElement, ur as Routes, v as TooltipProvider, vn as isListIndexDisabled, vt as CollapsibleContent, w as safePolygon, wn as activeElement, wt as inertValue, x as useAnchorPositioning, xn as stopEvent, y as usePositioner, yn as isVirtualClick, yt as CollapsibleTrigger, z as useTriggerDataForwarding, zn as useRenderElement } from "./docs.js";
1
+ import { $ as POPUP_COLLISION_AVOIDANCE, $n as isHTMLElement, $t as itemPress, A as popupStoreSelectors, An as ARROW_UP, B as useSyncedFloatingRootContext, Bn as EMPTY_ARRAY, C as pressableTriggerOpenStateMapping, Cn as isTypeableCombobox, Ct as COMPOSITE_KEYS, D as useFocus, Dn as ARROW_DOWN, E as useHoverFloatingInteraction, En as getTarget, F as setPopupOpenState, Fn as useId$1, G as FloatingNode, Gn as ownerDocument, Gt as useValueAsRef, H as useDismiss, Hn as useMergedRefs, Ht as getIcon, I as useImplicitActiveTrigger, In as useTimeout, J as useFloatingParentNodeId, Jn as useIsoLayoutEffect, Jt as createChangeEventDetails, K as FloatingTree, Kn as useCompositeRootContext, Kt as useCompositeListItem, L as useOpenStateTransitions, Ln as Button, Lt as Badge, M as FOCUSABLE_POPUP_PROPS, Mn as webkit, N as PopupHandleAttachment, Nn as mac, O as usePopupHandleStore, On as ARROW_LEFT, P as attachPreventUnmountOnClose, Pn as useBaseUiId, Q as DROPDOWN_COLLISION_AVOIDANCE, Qn as getWindow, Qt as imperativeAction, R as usePopupInteractionProps, Rn as cn, Rt as Badge$1, S as popupTransitionStateMapping, Sn as getFloatingFocusElement, St as CodeGroup, T as useHoverReferenceInteraction, Tn as contains, Tt as useCompositeItem, U as useClick, Un as useButton, Ut as useOpenChangeComplete, V as ReactStore, Vn as EMPTY_OBJECT, Vt as resolveIcon, W as FloatingFocusManager, Wn as dispatchClickWithModifiers, Wt as useAnimationsFinished, X as FloatingTreeStore, Xn as useRefWithInit, Xt as escapeKey, Y as useFloatingTree, Yn as useStableCallback, Yt as cancelOpen, Z as FloatingPortal, Zn as getParentNode, Zt as focusOut, _n as isIndexOutOfListBounds, _t as Collapsible, a as DialogTitle, an as triggerPress, b as getDisabledMountTransitionStyles, bn as isVirtualPointerEvent, c as useOpenInteractionType, cn as useDirection, cr as Route, ct as ResponseField, d as useScrollLock, dn as getTabbableBeforeElement, dr as useLocation, dt as ParamField, en as listNavigation, er as isLastTraversableNode, et as enqueueFocus, f as DialogPortal$1, fn as isOutsideEvent, fr as useNavigate, g as DialogBackdrop, gn as isElementVisible, gt as Expandable, h as DialogClose, hn as getMinListIndex, ht as renderInlineMarkdown, i as DialogContent$1, in as triggerHover, ir as createLucideIcon, j as PopupTriggerMap, jn as jsdom, k as createInitialPopupStoreState, kn as ARROW_RIGHT, l as DialogTitle$1, ln as getNextTabbable, m as DialogPopup, mn as getMaxListIndex, n as ZoomableImage, nn as siblingOpen, nr as ChevronRight, nt as fastComponent, o as DialogTrigger, on as CompositeList, or as Link, p as InternalBackdrop, pn as findNonDisabledListIndex, q as useFloatingNodeId, qn as mergeProps$1, qt as useAnimationFrame, r as Dialog$1, rn as triggerFocus, rr as Check, rt as fastComponentRef, s as DialogTrigger$1, sn as addEventListener, sr as Navigate, t as docs_exports, tn as outsidePress, tr as X$1, tt as FocusGuard, u as DialogRoot, un as getTabbableAfterElement, ur as Routes, v as TooltipProvider, vn as isListIndexDisabled, vt as CollapsibleContent, w as safePolygon, wn as activeElement, wt as inertValue, x as useAnchorPositioning, xn as stopEvent, y as usePositioner, yn as isVirtualClick, yt as CollapsibleTrigger, z as useTriggerDataForwarding, zn as useRenderElement } from "./docs.js";
2
2
  import { n as highlightTerms } from "./search.js";
3
3
  import { resolveSearchProvider } from "../search.js";
4
4
  import * as React from "react";
5
5
  import { useCallback, useEffect, useRef, useState } from "react";
6
6
  import "@fontsource-variable/inter/index.css";
7
7
  import "@fontsource/jetbrains-mono/400.css";
8
- import "highlight.js/styles/github.css";
9
8
  import "@umami/shiso/styles.css";
10
9
  import { MDXProvider } from "@mdx-js/react";
11
10
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -13,6 +12,7 @@ import * as ReactDOM from "react-dom";
13
12
  import shiso from "virtual:shiso-config";
14
13
  import rawConfig from "virtual:shiso-docs-config";
15
14
  import { LAST_MODIFIED } from "@/generated/last-modified";
15
+ import { OPENAPI_OPERATIONS } from "@/lib/openapi.generated";
16
16
 
17
17
  //#region ../../node_modules/.pnpm/lucide-react@1.28.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/arrow-left.mjs
18
18
  /**
@@ -1181,36 +1181,66 @@ function ScrollBar({ className, orientation = "vertical", ...props }) {
1181
1181
 
1182
1182
  //#endregion
1183
1183
  //#region src/components/CodeBlock.tsx
1184
- function CodeBlock({ children, className }) {
1184
+ /**
1185
+ * Joins the text of each rendered line. Lines marked as removed by
1186
+ * `// [!code --]` are skipped so the clipboard holds the "after" state; in a
1187
+ * `diff` block the +/- lines are content and are copied verbatim.
1188
+ */
1189
+ function copyText(pre, language) {
1190
+ if (!pre) return "";
1191
+ const lines = [...pre.querySelectorAll(".line")];
1192
+ if (!lines.length) return pre.textContent || "";
1193
+ return lines.filter((line) => language === "diff" || line.dataset.diff !== "remove").map((line) => line.textContent || "").join("\n");
1194
+ }
1195
+ function CodeBlock({ children, className, style, ...rest }) {
1196
+ const { "data-title": title, "data-language": language, "data-line-start": lineStart, "data-line-count": lineCount, ...preProps } = rest;
1185
1197
  const textInput = useRef(null);
1186
1198
  const [copied, setCopied] = useState(false);
1199
+ const start = Number(lineStart) || 1;
1200
+ const lastLine = start + Math.max(Number(lineCount) || 1, 1) - 1;
1201
+ const gutter = `${String(lastLine).length}ch`;
1187
1202
  const handleCopy = () => {
1188
1203
  setCopied(true);
1189
- navigator?.clipboard?.writeText(textInput.current?.textContent || "");
1204
+ navigator?.clipboard?.writeText(copyText(textInput.current, language));
1190
1205
  setTimeout(() => {
1191
1206
  setCopied(false);
1192
1207
  }, 1e3);
1193
1208
  };
1194
1209
  return /* @__PURE__ */ jsxs("div", {
1195
1210
  "data-slot": "code-block",
1196
- className: "relative my-5 overflow-hidden rounded-lg bg-muted/50",
1197
- children: [/* @__PURE__ */ jsx(ScrollArea, {
1198
- scrollbars: "horizontal",
1199
- className: "w-full",
1200
- children: /* @__PURE__ */ jsx("pre", {
1201
- ref: textInput,
1202
- className: `code-block p-3 pr-12 text-sm text-foreground leading-[1.6] font-mono ${className || ""}`,
1203
- children
1211
+ className: "relative my-5 overflow-hidden rounded-lg bg-card",
1212
+ children: [
1213
+ title ? /* @__PURE__ */ jsx("div", {
1214
+ "data-slot": "code-block-header",
1215
+ className: "flex h-9 items-center border-border border-b px-3 pr-12 font-mono text-muted-foreground text-xs",
1216
+ children: title
1217
+ }) : null,
1218
+ /* @__PURE__ */ jsx(ScrollArea, {
1219
+ scrollbars: "horizontal",
1220
+ className: "w-full",
1221
+ children: /* @__PURE__ */ jsx("pre", {
1222
+ ref: textInput,
1223
+ ...preProps,
1224
+ "data-language": language,
1225
+ style: {
1226
+ ...style,
1227
+ counterReset: `line ${start - 1}`,
1228
+ "--code-gutter": gutter
1229
+ },
1230
+ className: cn("code-block w-max min-w-full py-3 font-mono text-foreground text-sm leading-[1.6]", className),
1231
+ children
1232
+ })
1233
+ }),
1234
+ /* @__PURE__ */ jsx(Button, {
1235
+ type: "button",
1236
+ variant: "ghost",
1237
+ size: "icon-sm",
1238
+ className: cn("absolute right-3 inline-flex size-7 items-center justify-center rounded-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground", title ? "top-1" : "top-2.5"),
1239
+ onClick: handleCopy,
1240
+ "aria-label": "Copy code",
1241
+ children: copied ? /* @__PURE__ */ jsx(Check, { className: "size-3.5 text-primary" }) : /* @__PURE__ */ jsx(Copy, { className: "size-3.5" })
1204
1242
  })
1205
- }), /* @__PURE__ */ jsx(Button, {
1206
- type: "button",
1207
- variant: "ghost",
1208
- size: "icon-sm",
1209
- className: "absolute top-2.5 right-3 inline-flex size-7 items-center justify-center rounded-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground",
1210
- onClick: handleCopy,
1211
- "aria-label": "Copy code",
1212
- children: copied ? /* @__PURE__ */ jsx(Check, { className: "size-3.5 text-primary" }) : /* @__PURE__ */ jsx(Copy, { className: "size-3.5" })
1213
- })]
1243
+ ]
1214
1244
  });
1215
1245
  }
1216
1246
 
@@ -3422,6 +3452,52 @@ const regex = /[\0-\x1F!-,\.\/:-@\[-\^`\{-\xA9\xAB-\xB4\xB6-\xB9\xBB-\xBF\xD7\xF
3422
3452
  //#region ../../node_modules/.pnpm/github-slugger@2.0.0/node_modules/github-slugger/index.js
3423
3453
  const own = Object.hasOwnProperty;
3424
3454
  /**
3455
+ * Slugger.
3456
+ */
3457
+ var BananaSlug = class {
3458
+ /**
3459
+ * Create a new slug class.
3460
+ */
3461
+ constructor() {
3462
+ /** @type {Record<string, number>} */
3463
+ this.occurrences;
3464
+ this.reset();
3465
+ }
3466
+ /**
3467
+ * Generate a unique slug.
3468
+ *
3469
+ * Tracks previously generated slugs: repeated calls with the same value
3470
+ * will result in different slugs.
3471
+ * Use the `slug` function to get same slugs.
3472
+ *
3473
+ * @param {string} value
3474
+ * String of text to slugify
3475
+ * @param {boolean} [maintainCase=false]
3476
+ * Keep the current case, otherwise make all lowercase
3477
+ * @return {string}
3478
+ * A unique slug string
3479
+ */
3480
+ slug(value, maintainCase) {
3481
+ const self = this;
3482
+ let result = slug(value, maintainCase === true);
3483
+ const originalSlug = result;
3484
+ while (own.call(self.occurrences, result)) {
3485
+ self.occurrences[originalSlug]++;
3486
+ result = originalSlug + "-" + self.occurrences[originalSlug];
3487
+ }
3488
+ self.occurrences[result] = 0;
3489
+ return result;
3490
+ }
3491
+ /**
3492
+ * Reset - Forget all previous slugs
3493
+ *
3494
+ * @return void
3495
+ */
3496
+ reset() {
3497
+ this.occurrences = Object.create(null);
3498
+ }
3499
+ };
3500
+ /**
3425
3501
  * Generate a slug.
3426
3502
  *
3427
3503
  * Does not track previously generated slugs: repeated calls with the same value
@@ -3444,6 +3520,22 @@ function slug(value, maintainCase) {
3444
3520
  //#endregion
3445
3521
  //#region src/lib/slug.ts
3446
3522
  /**
3523
+ * The single slug algorithm in the repo.
3524
+ *
3525
+ * Heading anchors are produced in two independent places — `remarkToc` (which
3526
+ * builds the on-page table of contents) and `rehype-slug` (which sets the `id`
3527
+ * on the rendered heading). Both must agree or every TOC link breaks, so both
3528
+ * go through github-slugger here rather than reimplementing it.
3529
+ */
3530
+ /**
3531
+ * Creates a stateful slugger. Call `.slug(text)` once per heading in document
3532
+ * order; repeated headings get `-1`, `-2`, ... suffixes exactly as rehype-slug
3533
+ * does, because it is the same implementation.
3534
+ */
3535
+ function createSlugger() {
3536
+ return new BananaSlug();
3537
+ }
3538
+ /**
3447
3539
  * Stateless slugify for one-off ids that do not need de-duplication.
3448
3540
  *
3449
3541
  * github-slugger does not trim, so padded input would otherwise produce leading
@@ -3637,7 +3729,8 @@ function addPage(pageRef, context, state, extra = {}) {
3637
3729
  order,
3638
3730
  hidden: extra.hidden || context.hidden || void 0,
3639
3731
  icon: extra.icon,
3640
- tag: extra.tag
3732
+ tag: extra.tag,
3733
+ method: extra.method
3641
3734
  });
3642
3735
  return order;
3643
3736
  }
@@ -3673,6 +3766,7 @@ function collectPages(items, context, state) {
3673
3766
  label,
3674
3767
  icon: typeof item.icon === "string" ? item.icon : void 0,
3675
3768
  tag: typeof item.tag === "string" ? item.tag : void 0,
3769
+ method: typeof item.method === "string" ? item.method : void 0,
3676
3770
  hidden: item.hidden === true
3677
3771
  })
3678
3772
  });
@@ -4054,6 +4148,24 @@ function getTextDirection(locale) {
4054
4148
  return RTL_LANGUAGES.has(primary) ? "rtl" : "ltr";
4055
4149
  }
4056
4150
 
4151
+ //#endregion
4152
+ //#region src/lib/code-blocks.ts
4153
+ const DEFAULT_CODE_THEME = {
4154
+ light: "github-light",
4155
+ dark: "github-dark"
4156
+ };
4157
+ /** Applies defaults to `styling.codeBlocks` from docs.json. */
4158
+ function resolveCodeBlockConfig(styling) {
4159
+ const codeBlocks = styling?.codeBlocks;
4160
+ return {
4161
+ lineNumbers: codeBlocks?.lineNumbers === true,
4162
+ theme: {
4163
+ light: codeBlocks?.theme?.light || DEFAULT_CODE_THEME.light,
4164
+ dark: codeBlocks?.theme?.dark || DEFAULT_CODE_THEME.dark
4165
+ }
4166
+ };
4167
+ }
4168
+
4057
4169
  //#endregion
4058
4170
  //#region src/lib/search/config.ts
4059
4171
  const DEFAULT_SEARCH_PROMPT = "Search...";
@@ -4191,7 +4303,10 @@ function resolveSiteModel(config, docs, shiso) {
4191
4303
  default: appearance.default === "light" || appearance.default === "dark" ? appearance.default : "system",
4192
4304
  strict: appearance.strict === true
4193
4305
  },
4194
- styling: { eyebrows: config.styling?.eyebrows === "breadcrumbs" ? "breadcrumbs" : "section" },
4306
+ styling: {
4307
+ eyebrows: config.styling?.eyebrows === "breadcrumbs" ? "breadcrumbs" : "section",
4308
+ codeBlocks: resolveCodeBlockConfig(config.styling)
4309
+ },
4195
4310
  search: resolveSearchConfig(config.search),
4196
4311
  contextualOptions: config.contextual?.options || [],
4197
4312
  error404: {
@@ -7953,6 +8068,195 @@ function ContextualMenu({ options, labels }) {
7953
8068
  });
7954
8069
  }
7955
8070
 
8071
+ //#endregion
8072
+ //#region src/lib/openapi.ts
8073
+ const METHOD_COLORS = {
8074
+ GET: "green",
8075
+ POST: "blue",
8076
+ PUT: "orange",
8077
+ PATCH: "purple",
8078
+ DELETE: "red"
8079
+ };
8080
+ function methodColor(method) {
8081
+ return method && METHOD_COLORS[method.toUpperCase()] || "gray";
8082
+ }
8083
+ function statusColor(status) {
8084
+ if (status.startsWith("2")) return "green";
8085
+ if (status.startsWith("3")) return "blue";
8086
+ if (status.startsWith("4") || status.startsWith("5")) return "red";
8087
+ return "gray";
8088
+ }
8089
+ /** Looks up the operation bound by an `openapi:` frontmatter value. */
8090
+ function getOperation(key) {
8091
+ if (typeof key !== "string" || !key.trim()) return;
8092
+ const [method, ...rest] = key.trim().split(/\s+/);
8093
+ return OPENAPI_OPERATIONS[`${method.toUpperCase()} ${rest.join(" ")}`];
8094
+ }
8095
+ function hasParameters(operation) {
8096
+ const { query, path, header, cookie } = operation.parameters;
8097
+ return query.length + path.length + header.length + cookie.length > 0 || operation.security.length > 0;
8098
+ }
8099
+ /**
8100
+ * Section headings for an operation, in render order. The single source of
8101
+ * truth for section ids: the component, the table of contents, the content
8102
+ * checker, and the search indexer all derive their anchors from these labels.
8103
+ */
8104
+ function operationSections(operation) {
8105
+ const slugger = createSlugger();
8106
+ return [
8107
+ hasParameters(operation) ? "Parameters" : void 0,
8108
+ operation.requestBody ? "Request body" : void 0,
8109
+ operation.responses.length ? "Responses" : void 0,
8110
+ operation.samples.length ? "Code samples" : void 0
8111
+ ].filter((name) => Boolean(name)).map((name) => ({
8112
+ name,
8113
+ id: slugger.slug(name),
8114
+ size: 2
8115
+ }));
8116
+ }
8117
+
8118
+ //#endregion
8119
+ //#region src/components/OpenApiOperation.tsx
8120
+ const PARAM_LOCATIONS = [
8121
+ "path",
8122
+ "query",
8123
+ "header",
8124
+ "cookie"
8125
+ ];
8126
+ function FieldChildren({ node }) {
8127
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
8128
+ node.description,
8129
+ node.enum && node.enum.length > 0 && /* @__PURE__ */ jsxs("div", {
8130
+ className: "mt-1",
8131
+ children: [
8132
+ "Options:",
8133
+ " ",
8134
+ node.enum.map((value, index) => /* @__PURE__ */ jsxs("span", { children: [index > 0 && ", ", /* @__PURE__ */ jsx("code", { children: value })] }, value))
8135
+ ]
8136
+ }),
8137
+ node.children && node.children.length > 0 && /* @__PURE__ */ jsx(Expandable, {
8138
+ title: "properties",
8139
+ children: node.children.map((child) => /* @__PURE__ */ jsx(SchemaField, { node: child }, child.name || child.type))
8140
+ })
8141
+ ] });
8142
+ }
8143
+ function SchemaField({ node }) {
8144
+ return /* @__PURE__ */ jsx(ResponseField, {
8145
+ name: node.name || node.type,
8146
+ type: node.name ? node.type : void 0,
8147
+ required: node.required,
8148
+ deprecated: node.deprecated,
8149
+ default: node.default,
8150
+ children: /* @__PURE__ */ jsx(FieldChildren, { node })
8151
+ });
8152
+ }
8153
+ /** Renders a schema tree: a root object's properties, or the node itself. */
8154
+ function SchemaFields({ node }) {
8155
+ if (!node.name && node.children?.length) return /* @__PURE__ */ jsx(Fragment, { children: node.children.map((child) => /* @__PURE__ */ jsx(SchemaField, { node: child }, child.name || child.type)) });
8156
+ return /* @__PURE__ */ jsx(SchemaField, { node });
8157
+ }
8158
+ function HighlightedCode({ language, title, html, source, lineCount }) {
8159
+ return /* @__PURE__ */ jsx(CodeBlock, {
8160
+ "data-language": language,
8161
+ "data-title": title,
8162
+ "data-line-count": lineCount ? String(lineCount) : void 0,
8163
+ children: html ? /* @__PURE__ */ jsx("code", {
8164
+ className: `language-${language}`,
8165
+ dangerouslySetInnerHTML: { __html: html }
8166
+ }) : /* @__PURE__ */ jsx("code", {
8167
+ className: `language-${language}`,
8168
+ children: source
8169
+ })
8170
+ });
8171
+ }
8172
+ /** The generated reference for one API operation, rendered under the page body. */
8173
+ function OpenApiOperation({ operation }) {
8174
+ const sections = new Map(operationSections(operation).map((entry) => [entry.name, entry.id]));
8175
+ const showParameters = sections.has("Parameters");
8176
+ return /* @__PURE__ */ jsxs("div", {
8177
+ className: "docs-markdown",
8178
+ children: [
8179
+ showParameters && /* @__PURE__ */ jsxs("section", { children: [
8180
+ /* @__PURE__ */ jsx("h2", {
8181
+ id: sections.get("Parameters"),
8182
+ children: "Parameters"
8183
+ }),
8184
+ operation.security.length > 0 && /* @__PURE__ */ jsxs(ParamField, {
8185
+ header: "Authorization",
8186
+ type: "string",
8187
+ required: true,
8188
+ children: [
8189
+ "Authentication credentials, e.g. ",
8190
+ /* @__PURE__ */ jsx("code", { children: "Bearer <token>" }),
8191
+ " (",
8192
+ operation.security.join(", "),
8193
+ ")."
8194
+ ]
8195
+ }),
8196
+ PARAM_LOCATIONS.map((location) => operation.parameters[location].map((parameter) => /* @__PURE__ */ jsxs(ParamField, {
8197
+ [location]: parameter.name,
8198
+ type: parameter.type,
8199
+ required: parameter.required,
8200
+ children: [/* @__PURE__ */ jsx(FieldChildren, { node: {
8201
+ ...parameter,
8202
+ name: void 0,
8203
+ description: void 0
8204
+ } }), parameter.description]
8205
+ }, `${location}-${parameter.name}`)))
8206
+ ] }),
8207
+ operation.requestBody && /* @__PURE__ */ jsxs("section", { children: [
8208
+ /* @__PURE__ */ jsx("h2", {
8209
+ id: sections.get("Request body"),
8210
+ children: "Request body"
8211
+ }),
8212
+ /* @__PURE__ */ jsx(SchemaFields, { node: operation.requestBody.schema }),
8213
+ operation.requestBody.example && /* @__PURE__ */ jsx(HighlightedCode, {
8214
+ language: "json",
8215
+ title: "Example request",
8216
+ html: operation.requestBody.exampleHtml,
8217
+ source: operation.requestBody.example
8218
+ })
8219
+ ] }),
8220
+ operation.responses.length > 0 && /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx("h2", {
8221
+ id: sections.get("Responses"),
8222
+ children: "Responses"
8223
+ }), operation.responses.map((response) => /* @__PURE__ */ jsxs("div", {
8224
+ className: "mt-6 first:mt-0",
8225
+ children: [
8226
+ /* @__PURE__ */ jsxs("div", {
8227
+ className: "flex items-center gap-2",
8228
+ children: [/* @__PURE__ */ jsx(Badge, {
8229
+ color: statusColor(response.status),
8230
+ size: "sm",
8231
+ children: response.status
8232
+ }), response.description && /* @__PURE__ */ jsx("span", {
8233
+ className: "text-muted-foreground text-sm",
8234
+ children: response.description
8235
+ })]
8236
+ }),
8237
+ response.schema && /* @__PURE__ */ jsx(SchemaFields, { node: response.schema }),
8238
+ response.example && /* @__PURE__ */ jsx(HighlightedCode, {
8239
+ language: "json",
8240
+ title: `${response.status} example`,
8241
+ html: response.exampleHtml,
8242
+ source: response.example
8243
+ })
8244
+ ]
8245
+ }, response.status))] }),
8246
+ operation.samples.length > 0 && /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx("h2", {
8247
+ id: sections.get("Code samples"),
8248
+ children: "Code samples"
8249
+ }), /* @__PURE__ */ jsx(CodeGroup, { children: operation.samples.map((sample) => /* @__PURE__ */ jsx(HighlightedCode, {
8250
+ language: sample.language,
8251
+ title: sample.label,
8252
+ html: sample.html,
8253
+ source: sample.source,
8254
+ lineCount: sample.lineCount
8255
+ }, sample.language)) })] })
8256
+ ]
8257
+ });
8258
+ }
8259
+
7956
8260
  //#endregion
7957
8261
  //#region src/components/DocContent.tsx
7958
8262
  /**
@@ -7993,6 +8297,7 @@ function DocContent({ page, doc, site }) {
7993
8297
  const eyebrow = site.styling.eyebrows === "breadcrumbs" ? [.../* @__PURE__ */ new Set([page.tabLabel, page.section])].filter(Boolean).join(" / ") : page.section;
7994
8298
  const contextualOptions = resolveContextualOptions(site.contextualOptions, page, site.labels);
7995
8299
  const related = resolveRelated(doc.frontmatter?.related);
8300
+ const operation = getOperation(doc.frontmatter?.openapi);
7996
8301
  const dateFormat = new Intl.DateTimeFormat(resolveLocale(page.language, site.locale), {
7997
8302
  dateStyle: "medium",
7998
8303
  timeZone: "UTC"
@@ -8022,6 +8327,27 @@ function DocContent({ page, doc, site }) {
8022
8327
  labels: site.labels
8023
8328
  })]
8024
8329
  }),
8330
+ operation && /* @__PURE__ */ jsxs("div", {
8331
+ className: "mt-3 flex flex-wrap items-center gap-2",
8332
+ children: [
8333
+ /* @__PURE__ */ jsx(Badge, {
8334
+ color: methodColor(operation.method),
8335
+ size: "sm",
8336
+ className: "font-mono",
8337
+ children: operation.method
8338
+ }),
8339
+ /* @__PURE__ */ jsx("code", {
8340
+ className: "font-mono text-muted-foreground text-sm",
8341
+ children: operation.path
8342
+ }),
8343
+ operation.deprecated && /* @__PURE__ */ jsx(Badge, {
8344
+ color: "red",
8345
+ size: "sm",
8346
+ stroke: true,
8347
+ children: "deprecated"
8348
+ })
8349
+ ]
8350
+ }),
8025
8351
  description && /* @__PURE__ */ jsx("p", {
8026
8352
  className: "mt-3 mb-8 text-lg text-muted-foreground leading-relaxed",
8027
8353
  children: description
@@ -8030,6 +8356,7 @@ function DocContent({ page, doc, site }) {
8030
8356
  className: "docs-markdown",
8031
8357
  children: /* @__PURE__ */ jsx(Content, {})
8032
8358
  }),
8359
+ operation && /* @__PURE__ */ jsx(OpenApiOperation, { operation }),
8033
8360
  lastModified && /* @__PURE__ */ jsxs("div", {
8034
8361
  className: "mt-8 text-sm text-muted-foreground",
8035
8362
  children: [
@@ -8085,10 +8412,10 @@ function DocContent({ page, doc, site }) {
8085
8412
  });
8086
8413
  }
8087
8414
  const NavigationButton = ({ label, url, eyebrow, isPrev }) => {
8088
- if (!url || !label) return /* @__PURE__ */ jsx("div", {});
8415
+ if (!url || !label) return /* @__PURE__ */ jsx("div", { className: "flex-1" });
8089
8416
  return /* @__PURE__ */ jsxs(Link, {
8090
8417
  to: url,
8091
- className: cn("group my-3 inline-flex items-end gap-3 text-base text-foreground", { "text-right": !isPrev }),
8418
+ className: cn("group flex flex-1 items-end gap-3 py-3 text-base text-foreground", { "justify-end text-right": !isPrev }),
8092
8419
  rel: isPrev ? "prev" : "next",
8093
8420
  children: [
8094
8421
  isPrev && /* @__PURE__ */ jsx(ArrowLeft, {
@@ -8354,7 +8681,7 @@ function NavNodes({ nodes, pathname, depth, drilldown, expandLabel, collapseLabe
8354
8681
  return;
8355
8682
  }
8356
8683
  if (node.kind === "page") {
8357
- const { url, label, icon, tag } = node.page;
8684
+ const { url, label, icon, tag, method } = node.page;
8358
8685
  const isSelected = url === pathname;
8359
8686
  rendered.push(/* @__PURE__ */ jsxs(Link, {
8360
8687
  to: url,
@@ -8366,7 +8693,12 @@ function NavNodes({ nodes, pathname, depth, drilldown, expandLabel, collapseLabe
8366
8693
  children: [
8367
8694
  resolveIcon(icon),
8368
8695
  label,
8369
- tag ? /* @__PURE__ */ jsx(Badge, {
8696
+ method ? /* @__PURE__ */ jsx(Badge, {
8697
+ color: methodColor(method),
8698
+ size: "xs",
8699
+ className: "ml-auto font-mono",
8700
+ children: method === "DELETE" ? "DEL" : method
8701
+ }) : tag ? /* @__PURE__ */ jsx(Badge$1, {
8370
8702
  variant: "secondary",
8371
8703
  className: "ml-auto h-auto rounded-sm px-[0.35rem] py-[0.05rem] text-[0.7rem] text-muted-foreground uppercase",
8372
8704
  children: tag
@@ -8506,6 +8838,8 @@ function Docs({ page, doc, site }) {
8506
8838
  children: /* @__PURE__ */ jsx(NotFound, { site })
8507
8839
  }), /* @__PURE__ */ jsx(Footer, { footer: site.footer })]
8508
8840
  });
8841
+ const operation = getOperation(doc.frontmatter?.openapi);
8842
+ const toc = operation ? [...doc.toc || [], ...operationSections(operation)] : doc.toc;
8509
8843
  return /* @__PURE__ */ jsxs("div", {
8510
8844
  className: "flex min-h-full flex-col gap-6 lg:gap-0",
8511
8845
  children: [/* @__PURE__ */ jsxs(Sheet, {
@@ -8573,7 +8907,7 @@ function Docs({ page, doc, site }) {
8573
8907
  }), /* @__PURE__ */ jsx("div", {
8574
8908
  className: "hidden min-w-0 max-w-60 basis-60 self-start lg:sticky lg:top-[calc(var(--header-height)+1.5rem)] lg:block lg:shrink-0",
8575
8909
  children: /* @__PURE__ */ jsx(PageLinks, {
8576
- items: doc.toc,
8910
+ items: toc,
8577
8911
  title: site.labels.tableOfContents,
8578
8912
  navigationLabel: site.labels.tableOfContentsNavigation
8579
8913
  })
@@ -8685,4 +9019,4 @@ function App() {
8685
9019
  }
8686
9020
 
8687
9021
  //#endregion
8688
- export { docsSite as a, getSeo as c, getDocModule as d, getLastModified as f, toAbsoluteUrl as h, docsHomeUrl as i, siteName as l, BASE_URL as m, buildHead as n, getLocaleByPathname as o, getScopeForPage as p, renderHeadToString as r, getRedirects as s, App as t, standalonePages as u };
9022
+ export { docsSite as a, getSeo as c, standalonePages as d, getDocModule as f, toAbsoluteUrl as g, BASE_URL as h, docsHomeUrl as i, siteConfig as l, getScopeForPage as m, buildHead as n, getLocaleByPathname as o, getLastModified as p, renderHeadToString as r, getRedirects as s, App as t, siteName as u };