zudoku 0.27.0 → 0.28.1

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 (152) hide show
  1. package/dist/app/main.js +1 -2
  2. package/dist/app/main.js.map +1 -1
  3. package/dist/config/validators/InputSidebarSchema.d.ts +2 -2
  4. package/dist/lib/components/PathRenderer.d.ts +11 -0
  5. package/dist/lib/components/PathRenderer.js +25 -0
  6. package/dist/lib/components/PathRenderer.js.map +1 -0
  7. package/dist/lib/components/ThemeSwitch.js +4 -4
  8. package/dist/lib/components/ThemeSwitch.js.map +1 -1
  9. package/dist/lib/components/index.d.ts +1 -0
  10. package/dist/lib/components/index.js +4 -2
  11. package/dist/lib/components/index.js.map +1 -1
  12. package/dist/lib/components/navigation/SidebarCategory.js +17 -15
  13. package/dist/lib/components/navigation/SidebarCategory.js.map +1 -1
  14. package/dist/lib/oas/graphql/circular.js +17 -6
  15. package/dist/lib/oas/graphql/circular.js.map +1 -1
  16. package/dist/lib/oas/graphql/index.d.ts +1 -0
  17. package/dist/lib/oas/graphql/index.js +41 -23
  18. package/dist/lib/oas/graphql/index.js.map +1 -1
  19. package/dist/lib/plugins/openapi/ColorizedParam.js +3 -1
  20. package/dist/lib/plugins/openapi/ColorizedParam.js.map +1 -1
  21. package/dist/lib/plugins/openapi/Endpoint.js +2 -2
  22. package/dist/lib/plugins/openapi/Endpoint.js.map +1 -1
  23. package/dist/lib/plugins/openapi/{Route.d.ts → OpenApiRoute.d.ts} +2 -1
  24. package/dist/lib/plugins/openapi/{Route.js → OpenApiRoute.js} +3 -4
  25. package/dist/lib/plugins/openapi/OpenApiRoute.js.map +1 -0
  26. package/dist/lib/plugins/openapi/OperationList.d.ts +4 -1
  27. package/dist/lib/plugins/openapi/OperationList.js +20 -14
  28. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  29. package/dist/lib/plugins/openapi/OperationListItem.js +1 -1
  30. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  31. package/dist/lib/plugins/openapi/ParameterListItem.js +1 -1
  32. package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
  33. package/dist/lib/plugins/openapi/RequestBodySidecarBox.d.ts +1 -1
  34. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js +2 -0
  35. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js.map +1 -1
  36. package/dist/lib/plugins/openapi/Sidecar.js +6 -11
  37. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  38. package/dist/lib/plugins/openapi/SidecarExamples.js +17 -14
  39. package/dist/lib/plugins/openapi/SidecarExamples.js.map +1 -1
  40. package/dist/lib/plugins/openapi/graphql/gql.d.ts +6 -2
  41. package/dist/lib/plugins/openapi/graphql/gql.js +3 -2
  42. package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
  43. package/dist/lib/plugins/openapi/graphql/graphql.d.ts +47 -26
  44. package/dist/lib/plugins/openapi/graphql/graphql.js +20 -16
  45. package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
  46. package/dist/lib/plugins/openapi/index.js +97 -54
  47. package/dist/lib/plugins/openapi/index.js.map +1 -1
  48. package/dist/lib/plugins/openapi/interfaces.d.ts +1 -0
  49. package/dist/lib/plugins/openapi/playground/PathParams.js +1 -1
  50. package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
  51. package/dist/lib/plugins/openapi/playground/Playground.js +7 -15
  52. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  53. package/dist/lib/plugins/openapi/schema/{SchemaComponents.js → SchemaPropertyItem.js} +10 -8
  54. package/dist/lib/plugins/openapi/schema/SchemaPropertyItem.js.map +1 -0
  55. package/dist/lib/plugins/openapi/schema/SchemaView.js +1 -1
  56. package/dist/lib/plugins/openapi/schema/SchemaView.js.map +1 -1
  57. package/dist/lib/plugins/openapi/schema/utils.d.ts +1 -0
  58. package/dist/lib/plugins/openapi/schema/utils.js +2 -0
  59. package/dist/lib/plugins/openapi/schema/utils.js.map +1 -1
  60. package/dist/lib/util/joinUrl.js +1 -1
  61. package/dist/lib/util/joinUrl.js.map +1 -1
  62. package/dist/lib/util/joinUrl.test.d.ts +1 -0
  63. package/dist/lib/util/joinUrl.test.js +43 -0
  64. package/dist/lib/util/joinUrl.test.js.map +1 -0
  65. package/dist/vite/plugin-api.js +9 -1
  66. package/dist/vite/plugin-api.js.map +1 -1
  67. package/dist/vite/prerender.js +0 -1
  68. package/dist/vite/prerender.js.map +1 -1
  69. package/lib/{AuthenticationPlugin-CO_YCd2x.js → AuthenticationPlugin-Du8cLBSr.js} +2 -2
  70. package/lib/{AuthenticationPlugin-CO_YCd2x.js.map → AuthenticationPlugin-Du8cLBSr.js.map} +1 -1
  71. package/lib/{Markdown-B8o9Qz4q.js → Markdown-Cyrx_JrO.js} +8 -9
  72. package/lib/{Markdown-B8o9Qz4q.js.map → Markdown-Cyrx_JrO.js.map} +1 -1
  73. package/lib/{MdxPage-BxRt3Ly7.js → MdxPage-BuG8Tuwc.js} +5 -5
  74. package/lib/{MdxPage-BxRt3Ly7.js.map → MdxPage-BuG8Tuwc.js.map} +1 -1
  75. package/lib/OpenApiRoute-UrC_t0e5.js +36 -0
  76. package/lib/OpenApiRoute-UrC_t0e5.js.map +1 -0
  77. package/lib/{OperationList-DH-zIgtq.js → OperationList-CDt1xdc4.js} +1312 -1303
  78. package/lib/OperationList-CDt1xdc4.js.map +1 -0
  79. package/lib/{Select-B7UXR0SB.js → Select-CnCZ4WhS.js} +3 -3
  80. package/lib/{Select-B7UXR0SB.js.map → Select-CnCZ4WhS.js.map} +1 -1
  81. package/lib/{SlotletProvider-CtIp8rP3.js → SlotletProvider-mQiPDQIH.js} +2 -2
  82. package/lib/{SlotletProvider-CtIp8rP3.js.map → SlotletProvider-mQiPDQIH.js.map} +1 -1
  83. package/lib/{SyntaxHighlight-C1w1QPdY.js → SyntaxHighlight-B0L4SC_N.js} +11 -5
  84. package/lib/SyntaxHighlight-B0L4SC_N.js.map +1 -0
  85. package/lib/{ZudokuContext-8jts0fF3.js → ZudokuContext-BTUJPpQl.js} +21 -21
  86. package/lib/{ZudokuContext-8jts0fF3.js.map → ZudokuContext-BTUJPpQl.js.map} +1 -1
  87. package/lib/{circular-Dgpd6AN-.js → circular-DxaIIlWD.js} +251 -239
  88. package/lib/{circular-Dgpd6AN-.js.map → circular-DxaIIlWD.js.map} +1 -1
  89. package/lib/{createServer-BV0tHzLK.js → createServer-CjNktZzL.js} +821 -808
  90. package/lib/{createServer-BV0tHzLK.js.map → createServer-CjNktZzL.js.map} +1 -1
  91. package/lib/{hook-BG02esyv.js → hook-FT3SJLe_.js} +2 -2
  92. package/lib/{hook-BG02esyv.js.map → hook-FT3SJLe_.js.map} +1 -1
  93. package/lib/{index-LNp6rxyU.js → index-CjJS0l4l.js} +2 -2
  94. package/lib/{index-LNp6rxyU.js.map → index-CjJS0l4l.js.map} +1 -1
  95. package/lib/{index-DmqsUPcm.js → index-Eb1oiHbM.js} +881 -799
  96. package/lib/index-Eb1oiHbM.js.map +1 -0
  97. package/lib/{joinUrl-BTy9bvoK.js → joinUrl-nLx9pD-Z.js} +2 -2
  98. package/lib/joinUrl-nLx9pD-Z.js.map +1 -0
  99. package/lib/{useScrollToAnchor-Bl6mz9_x.js → useScrollToAnchor-BZsGmBng.js} +86 -90
  100. package/lib/useScrollToAnchor-BZsGmBng.js.map +1 -0
  101. package/lib/zudoku.auth-clerk.js +1 -1
  102. package/lib/zudoku.auth-openid.js +3 -3
  103. package/lib/zudoku.components.js +364 -348
  104. package/lib/zudoku.components.js.map +1 -1
  105. package/lib/zudoku.plugin-api-catalog.js +3 -3
  106. package/lib/zudoku.plugin-api-keys.js +4 -4
  107. package/lib/zudoku.plugin-custom-pages.js +1 -1
  108. package/lib/zudoku.plugin-markdown.js +1 -1
  109. package/lib/zudoku.plugin-openapi.js +6 -5
  110. package/lib/zudoku.plugin-openapi.js.map +1 -1
  111. package/package.json +1 -1
  112. package/src/app/main.tsx +1 -2
  113. package/src/lib/components/PathRenderer.tsx +59 -0
  114. package/src/lib/components/ThemeSwitch.tsx +15 -14
  115. package/src/lib/components/index.ts +7 -5
  116. package/src/lib/components/navigation/SidebarCategory.tsx +44 -41
  117. package/src/lib/oas/graphql/circular.ts +27 -6
  118. package/src/lib/oas/graphql/index.ts +63 -35
  119. package/src/lib/plugins/openapi/ColorizedParam.tsx +3 -3
  120. package/src/lib/plugins/openapi/Endpoint.tsx +2 -2
  121. package/src/lib/plugins/openapi/{Route.tsx → OpenApiRoute.tsx} +3 -3
  122. package/src/lib/plugins/openapi/OperationList.tsx +34 -12
  123. package/src/lib/plugins/openapi/OperationListItem.tsx +6 -1
  124. package/src/lib/plugins/openapi/ParameterListItem.tsx +1 -1
  125. package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +2 -0
  126. package/src/lib/plugins/openapi/Sidecar.tsx +18 -27
  127. package/src/lib/plugins/openapi/SidecarExamples.tsx +24 -24
  128. package/src/lib/plugins/openapi/graphql/gql.ts +12 -4
  129. package/src/lib/plugins/openapi/graphql/graphql.ts +66 -43
  130. package/src/lib/plugins/openapi/index.tsx +125 -67
  131. package/src/lib/plugins/openapi/interfaces.ts +1 -0
  132. package/src/lib/plugins/openapi/playground/PathParams.tsx +1 -1
  133. package/src/lib/plugins/openapi/playground/Playground.tsx +23 -33
  134. package/src/lib/plugins/openapi/schema/{SchemaComponents.tsx → SchemaPropertyItem.tsx} +10 -6
  135. package/src/lib/plugins/openapi/schema/SchemaView.tsx +4 -1
  136. package/src/lib/plugins/openapi/schema/utils.ts +4 -0
  137. package/src/lib/util/joinUrl.test.ts +62 -0
  138. package/src/lib/util/joinUrl.ts +1 -1
  139. package/dist/lib/plugins/openapi/Route.js.map +0 -1
  140. package/dist/lib/plugins/openapi/schema/SchemaComponents.js.map +0 -1
  141. package/lib/OperationList-DH-zIgtq.js.map +0 -1
  142. package/lib/Route-DJ0ZlVq1.js +0 -35
  143. package/lib/Route-DJ0ZlVq1.js.map +0 -1
  144. package/lib/StaggeredRender-DgsamH_G.js +0 -17
  145. package/lib/StaggeredRender-DgsamH_G.js.map +0 -1
  146. package/lib/SyntaxHighlight-C1w1QPdY.js.map +0 -1
  147. package/lib/index-Bn6Lc9tq.js +0 -9
  148. package/lib/index-Bn6Lc9tq.js.map +0 -1
  149. package/lib/index-DmqsUPcm.js.map +0 -1
  150. package/lib/joinUrl-BTy9bvoK.js.map +0 -1
  151. package/lib/useScrollToAnchor-Bl6mz9_x.js.map +0 -1
  152. /package/dist/lib/plugins/openapi/schema/{SchemaComponents.d.ts → SchemaPropertyItem.d.ts} +0 -0
@@ -1,10 +1,10 @@
1
1
  import { j as e } from "./jsx-runtime-Bdg6XQ1m.js";
2
- import { s as j } from "./index-LNp6rxyU.js";
3
- import { u as b } from "./ZudokuContext-8jts0fF3.js";
2
+ import { s as j } from "./index-CjJS0l4l.js";
3
+ import { u as b } from "./ZudokuContext-BTUJPpQl.js";
4
4
  import { b as y } from "./chunk-SYFQ2XB5-BPvC-soB.js";
5
5
  import { Head as v, Link as N } from "./zudoku.components.js";
6
6
  import { u as w } from "./state-mM7uaXTW.js";
7
- import { M as C } from "./Markdown-B8o9Qz4q.js";
7
+ import { M as C } from "./Markdown-Cyrx_JrO.js";
8
8
  import { c as h } from "./cn-qaFjX9_3.js";
9
9
  const f = (r, n) => j(`${r}-${n}`), k = ({
10
10
  items: r,
@@ -1,14 +1,14 @@
1
1
  import { j as e } from "./jsx-runtime-Bdg6XQ1m.js";
2
2
  import { RotateCwIcon as g, TrashIcon as f, EyeOffIcon as j, EyeIcon as v, CheckIcon as w, CopyIcon as b, FileKey2Icon as K } from "lucide-react";
3
- import { D as k, S as m, R as N } from "./SlotletProvider-CtIp8rP3.js";
3
+ import { D as k, S as m, R as N } from "./SlotletProvider-mQiPDQIH.js";
4
4
  import { i as c } from "./invariant-Caa8-XvF.js";
5
- import { u as d, S as I, a as S, b as A, c as C, d as E, e as x } from "./Select-B7UXR0SB.js";
5
+ import { u as d, S as I, a as S, b as A, c as C, d as E, e as x } from "./Select-CnCZ4WhS.js";
6
6
  import { a as P } from "./index.esm-CrSoEshU.js";
7
7
  import { a as D, L as u, O as R } from "./chunk-SYFQ2XB5-BPvC-soB.js";
8
- import { a as y, e as q, u as O } from "./ZudokuContext-8jts0fF3.js";
8
+ import { a as y, e as q, u as O } from "./ZudokuContext-BTUJPpQl.js";
9
9
  import { Button as l } from "./ui/Button.js";
10
10
  import { Input as z } from "./ui/Input.js";
11
- import { u as F } from "./hook-BG02esyv.js";
11
+ import { u as F } from "./hook-FT3SJLe_.js";
12
12
  import { useState as p } from "react";
13
13
  import { c as T } from "./cn-qaFjX9_3.js";
14
14
  const L = ({ service: t }) => {
@@ -1,6 +1,6 @@
1
1
  import { j as o } from "./jsx-runtime-Bdg6XQ1m.js";
2
2
  import a from "react";
3
- import { P as n } from "./Markdown-B8o9Qz4q.js";
3
+ import { P as n } from "./Markdown-Cyrx_JrO.js";
4
4
  import { c } from "./cn-qaFjX9_3.js";
5
5
  import { u as p } from "./useExposedProps-BLKFBylA.js";
6
6
  const u = ({
@@ -74,7 +74,7 @@ const C = (n) => ({
74
74
  const h = {
75
75
  path: r,
76
76
  lazy: async () => {
77
- const { MdxPage: l } = await import("./MdxPage-BxRt3Ly7.js"), { default: p, ...g } = await a();
77
+ const { MdxPage: l } = await import("./MdxPage-BuG8Tuwc.js"), { default: p, ...g } = await a();
78
78
  return {
79
79
  element: /* @__PURE__ */ P.jsx(
80
80
  l,
@@ -1,11 +1,12 @@
1
1
  import "./jsx-runtime-Bdg6XQ1m.js";
2
- import "./chunk-SYFQ2XB5-BPvC-soB.js";
3
- import { o as a } from "./index-DmqsUPcm.js";
2
+ import "./index-CjJS0l4l.js";
4
3
  import "lucide-react";
5
- import "./hook-BG02esyv.js";
4
+ import "./chunk-SYFQ2XB5-BPvC-soB.js";
5
+ import "./hook-FT3SJLe_.js";
6
6
  import "./ui/Button.js";
7
- import "./ZudokuContext-8jts0fF3.js";
7
+ import "./joinUrl-nLx9pD-Z.js";
8
+ import { o as f } from "./index-Eb1oiHbM.js";
8
9
  export {
9
- a as openApiPlugin
10
+ f as openApiPlugin
10
11
  };
11
12
  //# sourceMappingURL=zudoku.plugin-openapi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"zudoku.plugin-openapi.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
1
+ {"version":3,"file":"zudoku.plugin-openapi.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.27.0",
3
+ "version": "0.28.1",
4
4
  "type": "module",
5
5
  "homepage": "https://zudoku.dev",
6
6
  "repository": {
package/src/app/main.tsx CHANGED
@@ -11,12 +11,11 @@ import { configuredRedirectPlugin } from "virtual:zudoku-redirect-plugin";
11
11
  import { configuredSearchPlugin } from "virtual:zudoku-search-plugin";
12
12
  import { configuredSidebar } from "virtual:zudoku-sidebar";
13
13
  import "virtual:zudoku-theme.css";
14
- import { Layout, RouterError, Zudoku } from "zudoku/components";
14
+ import { Layout, RouteGuard, RouterError, Zudoku } from "zudoku/components";
15
15
  import type { ZudokuConfig } from "../config/config.js";
16
16
  import { StatusPage } from "../lib/components/StatusPage.js";
17
17
  import type { ZudokuContextOptions } from "../lib/core/ZudokuContext.js";
18
18
  import { isNavigationPlugin } from "../lib/core/plugins.js";
19
- import { RouteGuard } from "../lib/core/RouteGuard.js";
20
19
 
21
20
  export const convertZudokuConfigToOptions = (
22
21
  config: ZudokuConfig,
@@ -0,0 +1,59 @@
1
+ import { Fragment, type ReactNode } from "react";
2
+
3
+ type PathParamProps = {
4
+ name: string;
5
+ index: number;
6
+ originalValue?: string;
7
+ };
8
+
9
+ export const PathRenderer = ({
10
+ path,
11
+ renderParam,
12
+ }: {
13
+ path: string;
14
+ renderParam: (props: PathParamProps) => ReactNode;
15
+ }) =>
16
+ path.split("/").map((part, i, arr) => {
17
+ const matches = Array.from(part.matchAll(/{([^}]+)}/g));
18
+ const elements: ReactNode[] = [];
19
+ let lastIndex = 0;
20
+
21
+ matches.forEach((match, matchIndex) => {
22
+ const [originalValue, name] = match;
23
+ if (!name) return;
24
+ const startIndex = match.index!;
25
+
26
+ if (startIndex > lastIndex) {
27
+ elements.push(
28
+ <Fragment key={`text-${lastIndex}-${startIndex}`}>
29
+ {part.slice(lastIndex, startIndex)}
30
+ </Fragment>,
31
+ );
32
+ }
33
+
34
+ elements.push(
35
+ <Fragment key={`param-${name}`}>
36
+ {renderParam({ name, originalValue, index: matchIndex })}
37
+ </Fragment>,
38
+ );
39
+
40
+ lastIndex = startIndex + originalValue.length;
41
+ });
42
+
43
+ if (lastIndex < part.length) {
44
+ elements.push(
45
+ <Fragment key={`text-${lastIndex}-${part.length}`}>
46
+ {part.slice(lastIndex)}
47
+ </Fragment>,
48
+ );
49
+ }
50
+
51
+ return (
52
+ // eslint-disable-next-line react/no-array-index-key
53
+ <Fragment key={`${part}-${i}`}>
54
+ {elements}
55
+ {i < arr.length - 1 && "/"}
56
+ <wbr />
57
+ </Fragment>
58
+ );
59
+ });
@@ -1,25 +1,26 @@
1
1
  import { MoonStarIcon, SunIcon } from "lucide-react";
2
2
  import { useTheme } from "next-themes";
3
3
  import { Button } from "zudoku/ui/Button.js";
4
- import { cn } from "../util/cn.js";
4
+ import { ClientOnly } from "./ClientOnly.js";
5
5
 
6
6
  export const ThemeSwitch = () => {
7
7
  const { resolvedTheme, setTheme } = useTheme();
8
8
  const ThemeIcon = resolvedTheme === "dark" ? MoonStarIcon : SunIcon;
9
9
 
10
10
  return (
11
- <Button
12
- variant="ghost"
13
- size="icon"
14
- aria-label={
15
- resolvedTheme === "dark"
16
- ? "Switch to light mode"
17
- : "Switch to dark mode"
18
- }
19
- className={cn(resolvedTheme ? "opacity-100" : "opacity-0")}
20
- onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
21
- >
22
- <ThemeIcon size={18} />
23
- </Button>
11
+ <ClientOnly fallback={<Button variant="ghost" size="icon" />}>
12
+ <Button
13
+ variant="ghost"
14
+ size="icon"
15
+ aria-label={
16
+ resolvedTheme === "dark"
17
+ ? "Switch to light mode"
18
+ : "Switch to dark mode"
19
+ }
20
+ onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
21
+ >
22
+ <ThemeIcon size={18} />
23
+ </Button>
24
+ </ClientOnly>
24
25
  );
25
26
  };
@@ -2,6 +2,7 @@ import { useMDXComponents as useMDXComponentsImport } from "@mdx-js/react";
2
2
  import { Helmet } from "@zudoku/react-helmet-async";
3
3
  import { Link as LinkImport } from "react-router";
4
4
  import { useAuth as useAuthImport } from "../authentication/hook.js";
5
+ import { RouteGuard as RouteGuardImport } from "../core/RouteGuard.js";
5
6
  import { RouterError as RouterErrorImport } from "../errors/RouterError.js";
6
7
  import { ServerError as ServerErrorImport } from "../errors/ServerError.js";
7
8
  import { Button as ButtonImport } from "../ui/Button.js";
@@ -10,16 +11,16 @@ import {
10
11
  Bootstrap as BootstrapImport,
11
12
  BootstrapStatic as BootstrapStaticImport,
12
13
  } from "./Bootstrap.js";
13
- import { ClientOnly as ClientOnlyImport } from "./ClientOnly.js";
14
- import { Layout as LayoutImport } from "./Layout.js";
15
- import { Markdown as MarkdownImport } from "./Markdown.js";
16
- import { Spinner as SpinnerImport } from "./Spinner.js";
17
- import { Zudoku as ZudokuImport } from "./Zudoku.js";
18
14
  import {
19
15
  CACHE_KEYS as CACHE_KEYS_IMPORT,
20
16
  useCache as useCacheImport,
21
17
  } from "./cache.js";
18
+ import { ClientOnly as ClientOnlyImport } from "./ClientOnly.js";
22
19
  import { useZudoku as useZudokuImport } from "./context/ZudokuContext.js";
20
+ import { Layout as LayoutImport } from "./Layout.js";
21
+ import { Markdown as MarkdownImport } from "./Markdown.js";
22
+ import { Spinner as SpinnerImport } from "./Spinner.js";
23
+ import { Zudoku as ZudokuImport } from "./Zudoku.js";
23
24
 
24
25
  export const useMDXComponents = /*@__PURE__*/ useMDXComponentsImport;
25
26
  export const Layout = /*@__PURE__*/ LayoutImport;
@@ -27,6 +28,7 @@ export const RouterError = /*@__PURE__*/ RouterErrorImport;
27
28
  export const ServerError = /*@__PURE__*/ ServerErrorImport;
28
29
  export const Bootstrap = /*@__PURE__*/ BootstrapImport;
29
30
  export const BootstrapStatic = /*@__PURE__*/ BootstrapStaticImport;
31
+ export const RouteGuard = /*@__PURE__*/ RouteGuardImport;
30
32
 
31
33
  export const Head = /*@__PURE__*/ Helmet;
32
34
 
@@ -53,6 +53,22 @@ export const SidebarCategory = ({
53
53
  </button>
54
54
  );
55
55
 
56
+ const icon = category.icon && (
57
+ <category.icon
58
+ size={16}
59
+ className={cn("align-[-0.125em] ", isActive && "text-primary")}
60
+ />
61
+ );
62
+
63
+ const styles = navigationListItem({
64
+ className: [
65
+ "text-start font-medium",
66
+ isCollapsible || typeof category.link !== "undefined"
67
+ ? "cursor-pointer"
68
+ : "cursor-default hover:bg-transparent",
69
+ ],
70
+ });
71
+
56
72
  return (
57
73
  <Collapsible.Root
58
74
  className="flex flex-col"
@@ -61,57 +77,44 @@ export const SidebarCategory = ({
61
77
  onOpenChange={() => setOpen(true)}
62
78
  >
63
79
  <Collapsible.Trigger className="group" asChild disabled={!isCollapsible}>
64
- <div
65
- onClick={() => setHasInteracted(true)}
66
- className={navigationListItem({
67
- isActive: false,
68
- className: [
69
- "text-start font-medium",
70
- isCollapsible
71
- ? "cursor-pointer"
72
- : "cursor-default hover:bg-transparent",
73
- ],
74
- })}
75
- >
76
- {category.icon && (
77
- <category.icon
78
- size={16}
79
- className={cn("align-[-0.125em] ", isActive && "text-primary")}
80
- />
81
- )}
82
- {category.link?.type === "doc" ? (
83
- <NavLink
84
- to={joinPath(category.link.id)}
85
- className="flex-1"
86
- onClick={() => {
87
- // if it is the current path and closed then open it because there's no path change to trigger the open
88
- if (isActive && !open) {
89
- setOpen(true);
90
- }
91
- }}
80
+ {category.link?.type === "doc" ? (
81
+ <NavLink
82
+ to={joinPath(category.link.id)}
83
+ className={styles}
84
+ onClick={() => {
85
+ setHasInteracted(true);
86
+ // if it is the current path and closed then open it because there's no path change to trigger the open
87
+ if (isActive && !open) {
88
+ setOpen(true);
89
+ }
90
+ }}
91
+ >
92
+ {icon}
93
+ <div
94
+ className={cn(
95
+ "flex items-center gap-2 justify-between w-full",
96
+ isActive ? "text-primary" : "text-foreground/80",
97
+ )}
92
98
  >
93
- <div
94
- className={cn(
95
- "flex items-center gap-2 justify-between w-full",
96
- isActive ? "text-primary" : "text-foreground/80",
97
- )}
98
- >
99
- <div className="truncate">{category.label}</div>
100
- {ToggleButton}
101
- </div>
102
- </NavLink>
103
- ) : (
99
+ <div className="truncate">{category.label}</div>
100
+ {ToggleButton}
101
+ </div>
102
+ </NavLink>
103
+ ) : (
104
+ <div onClick={() => setHasInteracted(true)} className={styles}>
105
+ {icon}
104
106
  <div className="flex items-center justify-between w-full">
105
107
  <div className="flex gap-2 truncate w-full">{category.label}</div>
106
108
  {ToggleButton}
107
109
  </div>
108
- )}
109
- </div>
110
+ </div>
111
+ )}
110
112
  </Collapsible.Trigger>
111
113
  <Collapsible.Content
112
114
  className={cn(
113
115
  // CollapsibleContent class is used to animate and it should only be applied when the user has triggered the toggle
114
116
  hasInteracted && "CollapsibleContent",
117
+ category.items.length === 0 && "hidden",
115
118
  "ms-6 my-1",
116
119
  )}
117
120
  >
@@ -1,25 +1,46 @@
1
1
  import { GraphQLJSON } from "graphql-type-json";
2
2
  import { GraphQLScalarType } from "graphql/index.js";
3
+ import { RecordAny } from "../../util/traverse.js";
3
4
 
4
5
  export const CIRCULAR_REF = "$[Circular Reference]";
5
- const handleCircularRefs = (obj: any, visited = new WeakSet()): any => {
6
+
7
+ const handleCircularRefs = (
8
+ obj: any,
9
+ visited = new Map<string, string[]>(),
10
+ path: string[] = [],
11
+ ): any => {
6
12
  if (obj === CIRCULAR_REF) return CIRCULAR_REF;
7
13
  if (obj === null || typeof obj !== "object") return obj;
8
14
 
9
- if (visited.has(obj)) return CIRCULAR_REF;
15
+ const currentPath = path.join(".");
16
+
17
+ if (obj.type === "object" && obj.properties) {
18
+ const schemaKey = Object.keys(obj.properties).sort().join("-");
10
19
 
11
- visited.add(obj);
20
+ if (visited.has(schemaKey)) {
21
+ const prevPaths = visited.get(schemaKey)!;
22
+ if (prevPaths.some((prev) => currentPath.startsWith(prev))) {
23
+ return CIRCULAR_REF;
24
+ }
25
+ visited.set(schemaKey, [...prevPaths, currentPath]);
26
+ } else {
27
+ visited.set(schemaKey, [currentPath]);
28
+ }
29
+ }
12
30
 
13
31
  if (Array.isArray(obj)) {
14
- return obj.map((item) => handleCircularRefs(item, visited));
32
+ return obj.map((item, index) =>
33
+ handleCircularRefs(item, visited, [...path, `${index}`]),
34
+ );
15
35
  }
16
36
 
17
- const result: Record<string, any> = {};
37
+ const result: RecordAny = {};
18
38
  for (const [key, value] of Object.entries(obj)) {
19
- result[key] = handleCircularRefs(value, visited);
39
+ result[key] = handleCircularRefs(value, visited, [...path, key]);
20
40
  }
21
41
  return result;
22
42
  };
43
+
23
44
  export const GraphQLJSONSchema = new GraphQLScalarType({
24
45
  ...GraphQLJSON,
25
46
  name: "JSONSchema",
@@ -67,15 +67,26 @@ const builder = new SchemaBuilder<{
67
67
  };
68
68
  Context: {
69
69
  schema: OpenAPIDocument;
70
+ operations: GraphQLOperationObject[];
71
+ tags: TagObject[];
70
72
  schemaImports?: SchemaImports;
73
+ currentTag?: string;
74
+ slugify: CountableSlugify;
71
75
  };
72
76
  }>({});
73
77
 
78
+ type GraphQLOperationObject = OperationObject & {
79
+ path: string;
80
+ method: string;
81
+ slug?: string;
82
+ parentTag?: string;
83
+ };
84
+
74
85
  const JSONScalar = builder.addScalarType("JSON", GraphQLJSON);
75
86
  const JSONObjectScalar = builder.addScalarType("JSONObject", GraphQLJSONObject);
76
87
  const JSONSchemaScalar = builder.addScalarType("JSONSchema", GraphQLJSONSchema);
77
88
 
78
- const getAllTags = (schema: OpenAPIDocument): TagObject[] => {
89
+ export const getAllTags = (schema: OpenAPIDocument): TagObject[] => {
79
90
  const tags = schema.tags ?? [];
80
91
 
81
92
  // Extract tags from operations
@@ -94,10 +105,10 @@ const getAllTags = (schema: OpenAPIDocument): TagObject[] => {
94
105
  return [...tags, ...uniqueOperationTags.map((tag) => ({ name: tag }))];
95
106
  };
96
107
 
97
- const getAllOperations = (paths?: PathsObject, tag?: string) => {
98
- const slugify = slugifyWithCounter();
108
+ const getAllOperations = (paths?: PathsObject) => {
109
+ const start = performance.now();
99
110
 
100
- return Object.entries(paths ?? {}).flatMap(([path, value]) =>
111
+ const operations = Object.entries(paths ?? {}).flatMap(([path, value]) =>
101
112
  HttpMethods.flatMap((method) => {
102
113
  if (!value?.[method]) return [];
103
114
 
@@ -118,23 +129,17 @@ const getAllOperations = (paths?: PathsObject, tag?: string) => {
118
129
  ...operationParameters,
119
130
  ];
120
131
 
121
- const slugData = {
122
- summary: operation.summary,
123
- operationId: operation.operationId,
124
- path,
125
- method,
126
- };
127
-
128
132
  return {
129
133
  ...operation,
130
134
  method,
131
135
  path,
132
136
  parameters,
133
137
  tags: operation.tags ?? [],
134
- slug: createOperationSlug(slugify, slugData, tag),
135
138
  };
136
139
  }),
137
140
  );
141
+
142
+ return operations;
138
143
  };
139
144
 
140
145
  const SchemaTag = builder.objectRef<TagObject>("SchemaTag").implement({
@@ -144,15 +149,19 @@ const SchemaTag = builder.objectRef<TagObject>("SchemaTag").implement({
144
149
  operations: t.field({
145
150
  type: [OperationItem],
146
151
  resolve: (parent, _args, ctx) => {
147
- const rootTags = getAllTags(ctx.schema).map((tag) => tag.name);
148
-
149
- return getAllOperations(ctx.schema.paths, parent.name).filter((item) =>
150
- parent.name
151
- ? item.tags.includes(parent.name)
152
- : item.tags.length === 0 ||
153
- // If none of the tags are present in the root tags, then show them here
154
- item.tags.every((tag) => !rootTags.includes(tag)),
155
- );
152
+ const rootTags = ctx.tags.map((tag) => tag.name);
153
+ return ctx.operations
154
+ .filter((item) =>
155
+ parent.name
156
+ ? item.tags?.includes(parent.name)
157
+ : item.tags?.length === 0 ||
158
+ // If none of the tags are present in the root tags, then show them here
159
+ item.tags?.every((tag) => !rootTags.includes(tag)),
160
+ )
161
+ .map((item) => ({
162
+ ...item,
163
+ parentTag: parent.name,
164
+ }));
156
165
  },
157
166
  }),
158
167
  }),
@@ -300,12 +309,24 @@ const ResponseItem = builder
300
309
  });
301
310
 
302
311
  const OperationItem = builder
303
- .objectRef<
304
- OperationObject & { path: string; method: string; slug: string }
305
- >("OperationItem")
312
+ .objectRef<GraphQLOperationObject>("OperationItem")
306
313
  .implement({
307
314
  fields: (t) => ({
308
- slug: t.exposeString("slug"),
315
+ slug: t.field({
316
+ type: "String",
317
+ resolve: (parent, _, ctx) => {
318
+ const slugData = {
319
+ summary: parent.summary,
320
+ operationId: parent.operationId,
321
+ path: parent.path,
322
+ method: parent.method,
323
+ };
324
+
325
+ //TODO: fix parent tag parent.tags
326
+ return createOperationSlug(ctx.slugify, slugData, parent.parentTag);
327
+ },
328
+ }),
329
+
309
330
  path: t.exposeString("path"),
310
331
  method: t.exposeString("method"),
311
332
  operationId: t.exposeString("operationId", { nullable: true }),
@@ -414,8 +435,8 @@ const Schema = builder.objectRef<OpenAPIDocument>("Schema").implement({
414
435
  name: t.arg.string(),
415
436
  },
416
437
  type: [SchemaTag],
417
- resolve: (root, args) => {
418
- const tags = [...getAllTags(root), { name: "" }];
438
+ resolve: (root, args, ctx) => {
439
+ const tags = [...ctx.tags, { name: "" }];
419
440
  return args.name ? tags.filter((tag) => tag.name === args.name) : tags;
420
441
  },
421
442
  }),
@@ -426,15 +447,18 @@ const Schema = builder.objectRef<OpenAPIDocument>("Schema").implement({
426
447
  method: t.arg.string(),
427
448
  operationId: t.arg.string(),
428
449
  tag: t.arg.string(),
450
+ untagged: t.arg.boolean(),
429
451
  },
430
- resolve: (parent, args) =>
431
- getAllOperations(parent.paths).filter(
432
- (item) =>
433
- (!args.operationId || item.operationId === args.operationId) &&
434
- (!args.path || item.path === args.path) &&
435
- (!args.method || item.method === args.method) &&
436
- (!args.tag || item.tags.includes(args.tag)),
437
- ),
452
+ resolve: (parent, args, ctx) =>
453
+ ctx.operations.filter((op) => {
454
+ return (
455
+ (!args.operationId || op.operationId === args.operationId) &&
456
+ (!args.path || op.path === args.path) &&
457
+ (!args.method || op.method === args.method) &&
458
+ (!args.tag || op.tags?.some((tag) => args.tag?.includes(tag))) &&
459
+ (!args.untagged || (op.tags ?? []).length === 0)
460
+ );
461
+ }),
438
462
  }),
439
463
  }),
440
464
  });
@@ -467,6 +491,10 @@ builder.queryType({
467
491
  }
468
492
 
469
493
  ctx.schema = schema;
494
+ ctx.operations = getAllOperations(schema.paths);
495
+ ctx.slugify = slugifyWithCounter();
496
+ ctx.tags = getAllTags(schema);
497
+
470
498
  return schema;
471
499
  },
472
500
  }),
@@ -89,10 +89,10 @@ export const ColorizedParam = ({
89
89
  <span
90
90
  {...{ [DATA_ATTR]: normalizedSlug }}
91
91
  className={cn(
92
- "relative inline-block rounded transition-all duration-100",
93
- "rounded-lg",
92
+ // This may not contain (inline-)flex or (inline-)block otherwise it breaks the browser's full text search
93
+ "relative rounded transition-all duration-100 rounded-lg",
94
94
  "border border-[--border-color] p-0.5 text-[--param-color] bg-[--background-color]",
95
- "data-[active=true]:border-[--param-color] data-[active=true]:shadow data-[active=true]:-translate-y-px",
95
+ "data-[active=true]:border-[--param-color] data-[active=true]:shadow data-[active=true]:bottom-px",
96
96
  className,
97
97
  )}
98
98
  title={title}
@@ -77,14 +77,14 @@ export const Endpoint = () => {
77
77
  setSelectedServer(e.target.value);
78
78
  })
79
79
  }
80
- value={selectedServer ?? result.data.schema.url}
80
+ value={selectedServer ?? servers.at(0)!.url}
81
81
  showChevrons={servers.length > 1}
82
82
  options={servers.map((server) => ({
83
83
  value: server.url,
84
84
  label: server.url,
85
85
  }))}
86
86
  />
87
- <CopyButton url={selectedServer ?? result.data.schema.url} />
87
+ <CopyButton url={selectedServer ?? servers.at(0)!.url} />
88
88
  </div>
89
89
  );
90
90
  };
@@ -1,4 +1,4 @@
1
- import { Outlet, useParams } from "react-router";
1
+ import { Outlet } from "react-router";
2
2
  import { joinPath } from "../../util/joinPath.js";
3
3
  import type { GraphQLClient } from "./client/GraphQLClient.js";
4
4
  import { GraphQLProvider } from "./client/GraphQLContext.js";
@@ -8,16 +8,16 @@ import { type OasPluginConfig } from "./interfaces.js";
8
8
  export const OpenApiRoute = ({
9
9
  basePath,
10
10
  versions,
11
+ version,
11
12
  config,
12
13
  client,
13
14
  }: {
14
15
  basePath: string;
16
+ version?: string;
15
17
  versions: string[];
16
18
  config: OasPluginConfig;
17
19
  client: GraphQLClient;
18
20
  }) => {
19
- const { version } = useParams<"version">();
20
-
21
21
  const input =
22
22
  config.type === "file"
23
23
  ? {