zudoku 0.3.0-dev.30 → 0.3.0-dev.32

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 (224) hide show
  1. package/dist/app/App.d.ts +1 -0
  2. package/dist/app/App.js +2 -0
  3. package/dist/app/App.js.map +1 -0
  4. package/dist/app/demo.js +15 -11
  5. package/dist/app/demo.js.map +1 -1
  6. package/dist/app/entry.client.d.ts +5 -0
  7. package/dist/app/entry.client.js +28 -0
  8. package/dist/app/entry.client.js.map +1 -0
  9. package/dist/app/entry.server.d.ts +10 -0
  10. package/dist/app/entry.server.js +107 -0
  11. package/dist/app/entry.server.js.map +1 -0
  12. package/dist/app/main.d.ts +26 -2
  13. package/dist/app/main.js +39 -15
  14. package/dist/app/main.js.map +1 -1
  15. package/dist/app/standalone.js +14 -10
  16. package/dist/app/standalone.js.map +1 -1
  17. package/dist/app/tailwind.d.ts +1 -1
  18. package/dist/app/tailwind.js +0 -4
  19. package/dist/app/tailwind.js.map +1 -1
  20. package/dist/app/zudoku-manifest.d.ts +1 -0
  21. package/dist/app/zudoku-manifest.js +20 -0
  22. package/dist/app/zudoku-manifest.js.map +1 -0
  23. package/dist/cli/cmds/dev.js +5 -0
  24. package/dist/cli/cmds/dev.js.map +1 -1
  25. package/dist/cli/dev/handler.d.ts +1 -0
  26. package/dist/cli/dev/handler.js +3 -1
  27. package/dist/cli/dev/handler.js.map +1 -1
  28. package/dist/config/config.d.ts +5 -0
  29. package/dist/lib/authentication/providers/clerk.js +2 -0
  30. package/dist/lib/authentication/providers/clerk.js.map +1 -1
  31. package/dist/lib/components/DevPortal.d.ts +5 -2
  32. package/dist/lib/components/DevPortal.js +11 -10
  33. package/dist/lib/components/DevPortal.js.map +1 -1
  34. package/dist/lib/components/ErrorPage.d.ts +6 -0
  35. package/dist/lib/components/ErrorPage.js +9 -0
  36. package/dist/lib/components/ErrorPage.js.map +1 -0
  37. package/dist/lib/components/InlineCode.d.ts +5 -0
  38. package/dist/lib/components/InlineCode.js +4 -0
  39. package/dist/lib/components/InlineCode.js.map +1 -0
  40. package/dist/lib/components/Layout.js +2 -1
  41. package/dist/lib/components/Layout.js.map +1 -1
  42. package/dist/lib/components/NotFoundPage.d.ts +1 -0
  43. package/dist/lib/components/NotFoundPage.js +12 -0
  44. package/dist/lib/components/NotFoundPage.js.map +1 -0
  45. package/dist/lib/components/SyntaxHighlight.d.ts +3 -2
  46. package/dist/lib/components/SyntaxHighlight.js +20 -22
  47. package/dist/lib/components/SyntaxHighlight.js.map +1 -1
  48. package/dist/lib/components/index.d.ts +4 -1
  49. package/dist/lib/errors/ErrorAlert.d.ts +3 -0
  50. package/dist/lib/errors/ErrorAlert.js +8 -0
  51. package/dist/lib/errors/ErrorAlert.js.map +1 -0
  52. package/dist/lib/errors/RouterError.d.ts +1 -0
  53. package/dist/lib/errors/RouterError.js +12 -0
  54. package/dist/lib/errors/RouterError.js.map +1 -0
  55. package/dist/lib/errors/ServerError.d.ts +3 -0
  56. package/dist/lib/errors/ServerError.js +6 -0
  57. package/dist/lib/errors/ServerError.js.map +1 -0
  58. package/dist/lib/errors/TopLevelError.d.ts +2 -0
  59. package/dist/lib/errors/TopLevelError.js +7 -0
  60. package/dist/lib/errors/TopLevelError.js.map +1 -0
  61. package/dist/lib/oas/parser/index.d.ts +1 -1
  62. package/dist/lib/oas/parser/index.js +38 -14
  63. package/dist/lib/oas/parser/index.js.map +1 -1
  64. package/dist/lib/plugins/api-keys/index.js +3 -8
  65. package/dist/lib/plugins/api-keys/index.js.map +1 -1
  66. package/dist/lib/plugins/markdown/generateRoutes.js.map +1 -1
  67. package/dist/lib/plugins/markdown/index.js +3 -7
  68. package/dist/lib/plugins/markdown/index.js.map +1 -1
  69. package/dist/lib/plugins/openapi/OperationList.js +11 -1
  70. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  71. package/dist/lib/plugins/openapi/OperationListItem.js +2 -1
  72. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  73. package/dist/lib/plugins/openapi/SchemaListView.js +2 -1
  74. package/dist/lib/plugins/openapi/SchemaListView.js.map +1 -1
  75. package/dist/lib/plugins/openapi/client/createMemoryClient.js +1 -1
  76. package/dist/lib/plugins/openapi/client/createMemoryClient.js.map +1 -1
  77. package/dist/lib/plugins/openapi/index.js +9 -1
  78. package/dist/lib/plugins/openapi/index.js.map +1 -1
  79. package/dist/lib/plugins/redirect/index.js +2 -3
  80. package/dist/lib/plugins/redirect/index.js.map +1 -1
  81. package/dist/lib/ui/Callout.js +1 -1
  82. package/dist/lib/ui/Callout.js.map +1 -1
  83. package/dist/lib/ui/button-variants.d.ts +1 -1
  84. package/dist/lib/util/MdxComponents.js +2 -2
  85. package/dist/lib/util/MdxComponents.js.map +1 -1
  86. package/dist/lib/util/groupBy.d.ts +1 -6
  87. package/dist/lib/util/groupBy.js +10 -8
  88. package/dist/lib/util/groupBy.js.map +1 -1
  89. package/dist/vite/build.js +23 -10
  90. package/dist/vite/build.js.map +1 -1
  91. package/dist/vite/config.d.ts +2 -2
  92. package/dist/vite/config.js +36 -8
  93. package/dist/vite/config.js.map +1 -1
  94. package/dist/vite/dev-server.d.ts +1 -1
  95. package/dist/vite/dev-server.js +15 -9
  96. package/dist/vite/dev-server.js.map +1 -1
  97. package/dist/vite/html.js +5 -4
  98. package/dist/vite/html.js.map +1 -1
  99. package/dist/vite/plugin-custom-css.d.ts +6 -0
  100. package/dist/vite/plugin-custom-css.js +55 -0
  101. package/dist/vite/plugin-custom-css.js.map +1 -0
  102. package/dist/vite/plugin-docs.js +14 -5
  103. package/dist/vite/plugin-docs.js.map +1 -1
  104. package/dist/vite/plugin-openapi-worker.js +4 -1
  105. package/dist/vite/plugin-openapi-worker.js.map +1 -1
  106. package/dist/vite/plugin.js +2 -0
  107. package/dist/vite/plugin.js.map +1 -1
  108. package/dist/vite/prerender.d.ts +1 -0
  109. package/dist/vite/prerender.js +57 -0
  110. package/dist/vite/prerender.js.map +1 -0
  111. package/lib/{DevPortalProvider--xZTs0RJ.js → DevPortalProvider-BlxLX6GG.js} +230 -250
  112. package/lib/DevPortalProvider-BlxLX6GG.js.map +1 -0
  113. package/lib/{Markdown-oJFqm0uk.js → Markdown-CL8KPvJN.js} +8 -9
  114. package/lib/{Markdown-oJFqm0uk.js.map → Markdown-CL8KPvJN.js.map} +1 -1
  115. package/lib/MdxComponents-Ev_hBHb2.js +5885 -0
  116. package/lib/MdxComponents-Ev_hBHb2.js.map +1 -0
  117. package/lib/{MdxPage-BV_9ncEk.js → MdxPage-Z3HKNTrj.js} +92 -89
  118. package/lib/MdxPage-Z3HKNTrj.js.map +1 -0
  119. package/lib/{OperationList-DfG_E0Xa.js → OperationList-KoITgfDT.js} +1967 -1785
  120. package/lib/OperationList-KoITgfDT.js.map +1 -0
  121. package/lib/Route-Bf1_D_vC.js +13 -0
  122. package/lib/{Route-CHqr53jb.js.map → Route-Bf1_D_vC.js.map} +1 -1
  123. package/lib/Select-DSa3bN4t.js +4770 -0
  124. package/lib/Select-DSa3bN4t.js.map +1 -0
  125. package/lib/assets/{worker-BXS8hiSM.js → worker-BjPv-hjP.js} +3100 -2720
  126. package/lib/assets/worker-BjPv-hjP.js.map +1 -0
  127. package/lib/hook-CTmJ6CWq.js +35 -0
  128. package/lib/hook-CTmJ6CWq.js.map +1 -0
  129. package/lib/index-BdWBDosx.js +74 -0
  130. package/lib/index-BdWBDosx.js.map +1 -0
  131. package/lib/{index-B2qLeglF.js → index-BoWzKb_9.js} +57 -41
  132. package/lib/index-BoWzKb_9.js.map +1 -0
  133. package/lib/{AnchorLink-BtVKbEwm.js → index.esm-CPEExBJE.js} +156 -168
  134. package/lib/index.esm-CPEExBJE.js.map +1 -0
  135. package/lib/jsx-runtime-CM0TzjGp.js +866 -0
  136. package/lib/jsx-runtime-CM0TzjGp.js.map +1 -0
  137. package/lib/mutation-91kw0lHb.js +208 -0
  138. package/lib/mutation-91kw0lHb.js.map +1 -0
  139. package/lib/router-CcYTwKjf.js +183 -0
  140. package/lib/router-CcYTwKjf.js.map +1 -0
  141. package/lib/zudoku.auth-clerk.js.map +1 -1
  142. package/lib/zudoku.auth-openid.js +588 -441
  143. package/lib/zudoku.auth-openid.js.map +1 -1
  144. package/lib/zudoku.components.js +330 -543
  145. package/lib/zudoku.components.js.map +1 -1
  146. package/lib/zudoku.openapi-worker.js +18 -18
  147. package/lib/zudoku.plugin-api-keys.js +143 -98
  148. package/lib/zudoku.plugin-api-keys.js.map +1 -1
  149. package/lib/zudoku.plugin-markdown.js +2 -49
  150. package/lib/zudoku.plugin-markdown.js.map +1 -1
  151. package/lib/zudoku.plugin-openapi.js +5 -3
  152. package/lib/zudoku.plugin-openapi.js.map +1 -1
  153. package/lib/zudoku.plugin-redirect.js +6 -7
  154. package/lib/zudoku.plugin-redirect.js.map +1 -1
  155. package/package.json +3 -1
  156. package/src/app/App.tsx +0 -0
  157. package/src/app/demo.tsx +18 -13
  158. package/src/app/entry.client.tsx +51 -0
  159. package/src/app/entry.server.tsx +158 -0
  160. package/src/app/main.tsx +65 -41
  161. package/src/app/standalone.tsx +15 -11
  162. package/src/app/tailwind.ts +2 -6
  163. package/src/app/zudoku-manifest.ts +22 -0
  164. package/src/lib/authentication/providers/clerk.tsx +1 -0
  165. package/src/lib/components/DevPortal.tsx +34 -33
  166. package/src/lib/components/ErrorPage.tsx +28 -0
  167. package/src/lib/components/InlineCode.tsx +19 -0
  168. package/src/lib/components/Layout.tsx +7 -4
  169. package/src/lib/components/NotFoundPage.tsx +39 -0
  170. package/src/lib/components/SyntaxHighlight.tsx +26 -22
  171. package/src/lib/errors/ErrorAlert.tsx +21 -0
  172. package/src/lib/errors/RouterError.tsx +13 -0
  173. package/src/lib/errors/ServerError.tsx +5 -0
  174. package/src/lib/errors/TopLevelError.tsx +8 -0
  175. package/src/lib/oas/parser/index.ts +41 -22
  176. package/src/lib/plugins/api-keys/index.tsx +4 -16
  177. package/src/lib/plugins/markdown/generateRoutes.tsx +1 -1
  178. package/src/lib/plugins/markdown/index.tsx +3 -7
  179. package/src/lib/plugins/openapi/OperationList.tsx +30 -0
  180. package/src/lib/plugins/openapi/OperationListItem.tsx +3 -1
  181. package/src/lib/plugins/openapi/SchemaListView.tsx +8 -10
  182. package/src/lib/plugins/openapi/client/createMemoryClient.ts +1 -1
  183. package/src/lib/plugins/openapi/index.tsx +18 -1
  184. package/src/lib/plugins/redirect/index.tsx +2 -2
  185. package/src/lib/ui/Callout.tsx +2 -2
  186. package/src/lib/util/MdxComponents.tsx +2 -11
  187. package/src/lib/util/groupBy.ts +7 -12
  188. package/dist/lib/components/Error.d.ts +0 -1
  189. package/dist/lib/components/Error.js +0 -10
  190. package/dist/lib/components/Error.js.map +0 -1
  191. package/dist/lib/components/Router.d.ts +0 -4
  192. package/dist/lib/components/Router.js +0 -21
  193. package/dist/lib/components/Router.js.map +0 -1
  194. package/lib/AnchorLink-BtVKbEwm.js.map +0 -1
  195. package/lib/DevPortalProvider--xZTs0RJ.js.map +0 -1
  196. package/lib/MdxComponents-CsU8yR42.js +0 -3019
  197. package/lib/MdxComponents-CsU8yR42.js.map +0 -1
  198. package/lib/MdxPage-BV_9ncEk.js.map +0 -1
  199. package/lib/OperationList-DfG_E0Xa.js.map +0 -1
  200. package/lib/Route-CHqr53jb.js +0 -14
  201. package/lib/Select-CNmXi4JU.js +0 -4572
  202. package/lib/Select-CNmXi4JU.js.map +0 -1
  203. package/lib/Spinner-By5opWs5.js +0 -182
  204. package/lib/Spinner-By5opWs5.js.map +0 -1
  205. package/lib/assets/worker-BXS8hiSM.js.map +0 -1
  206. package/lib/cn-DpqTslo9.js +0 -2342
  207. package/lib/cn-DpqTslo9.js.map +0 -1
  208. package/lib/hook-kVJ4gpk5.js +0 -25
  209. package/lib/hook-kVJ4gpk5.js.map +0 -1
  210. package/lib/index-B2qLeglF.js.map +0 -1
  211. package/lib/index-CUIxJAeE.js +0 -713
  212. package/lib/index-CUIxJAeE.js.map +0 -1
  213. package/lib/index-Cr3hgaqt.js +0 -412
  214. package/lib/index-Cr3hgaqt.js.map +0 -1
  215. package/lib/index-fXFJf9Ua.js +0 -464
  216. package/lib/index-fXFJf9Ua.js.map +0 -1
  217. package/lib/jsx-runtime-D7DwziLW.js +0 -3009
  218. package/lib/jsx-runtime-D7DwziLW.js.map +0 -1
  219. package/lib/loglevel-CA34MiFn.js +0 -153
  220. package/lib/loglevel-CA34MiFn.js.map +0 -1
  221. package/lib/util-DnDPBx_j.js +0 -41
  222. package/lib/util-DnDPBx_j.js.map +0 -1
  223. package/src/lib/components/Error.tsx +0 -15
  224. package/src/lib/components/Router.tsx +0 -23
@@ -0,0 +1,28 @@
1
+ import type { ReactNode } from "react";
2
+ import { Link } from "react-router-dom";
3
+ import { CategoryHeading } from "./CategoryHeading.js";
4
+ import { Heading } from "./Heading.js";
5
+ import { ProseClasses } from "./Markdown.js";
6
+
7
+ export const ErrorPage = ({
8
+ title = "An error occurred",
9
+ message,
10
+ category,
11
+ }: {
12
+ title?: ReactNode;
13
+ message?: ReactNode;
14
+ category?: ReactNode;
15
+ }) => {
16
+ return (
17
+ <div className={ProseClasses + " h-full pt-[--padding-content-top]"}>
18
+ {category && <CategoryHeading>{category}</CategoryHeading>}
19
+ {title && (
20
+ <Heading level={1} className="flex gap-3.5 items-center">
21
+ {title}
22
+ </Heading>
23
+ )}
24
+ <p>{message}</p>
25
+ <Link to="/">Go back home</Link>
26
+ </div>
27
+ );
28
+ };
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from "react";
2
+ import { cn } from "../util/cn.js";
3
+
4
+ export const InlineCode = ({
5
+ className,
6
+ children,
7
+ }: {
8
+ className?: string;
9
+ children: ReactNode;
10
+ }) => (
11
+ <code
12
+ className={cn(
13
+ className,
14
+ "font-mono border border-border p-1 py-0.5 rounded bg-border/50 dark:bg-border/70 whitespace-nowrap",
15
+ )}
16
+ >
17
+ {children}
18
+ </code>
19
+ );
@@ -1,6 +1,7 @@
1
1
  import { Suspense, useEffect, useRef, type ReactNode } from "react";
2
2
  import { Helmet } from "react-helmet-async";
3
3
  import { Outlet, useLocation } from "react-router-dom";
4
+ import { cn } from "../util/cn.js";
4
5
  import { useScrollToAnchor } from "../util/useScrollToAnchor.js";
5
6
  import { useScrollToTop } from "../util/useScrollToTop.js";
6
7
  import { useDevPortal } from "./context/DevPortalProvider.js";
@@ -47,10 +48,12 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
47
48
  >
48
49
  <SideNavigation />
49
50
  <main
50
- className="dark:border-white/10 translate-x-0 h-full
51
- lg:overflow-visible
52
- lg:peer-data-[navigation=true]:w-[calc(100%-var(--side-nav-width))]
53
- lg:peer-data-[navigation=true]:translate-x-[--side-nav-width] peer-data-[navigation=true]:pl-12"
51
+ className={cn(
52
+ "dark:border-white/10 translate-x-0 h-full",
53
+ "lg:overflow-visible",
54
+ "lg:peer-data-[navigation=true]:w-[calc(100%-var(--side-nav-width))]",
55
+ "lg:peer-data-[navigation=true]:translate-x-[--side-nav-width] peer-data-[navigation=true]:pl-12",
56
+ )}
54
57
  >
55
58
  {children ?? <Outlet />}
56
59
  </main>
@@ -0,0 +1,39 @@
1
+ import { UnlinkIcon } from "lucide-react";
2
+ import { Link, useParams } from "react-router-dom";
3
+ import { Callout } from "../ui/Callout.js";
4
+ import { CategoryHeading } from "./CategoryHeading.js";
5
+ import { Heading } from "./Heading.js";
6
+ import { ProseClasses } from "./Markdown.js";
7
+
8
+ export const NotFoundPage = () => {
9
+ const params = useParams();
10
+
11
+ return (
12
+ <div className={ProseClasses + " h-full pt-[--padding-content-top]"}>
13
+ <CategoryHeading>404</CategoryHeading>
14
+ <Heading level={1} className="flex gap-3.5 items-center">
15
+ Page not found
16
+ <UnlinkIcon size={24} />
17
+ </Heading>
18
+ {import.meta.env.DEV && (
19
+ <Callout type="caution" title="Developer hint">
20
+ Start by adding a file at{" "}
21
+ <code>
22
+ {"{PROJECT_ROOT}"}/{params["*"]}.mdx
23
+ </code>{" "}
24
+ and add some content to make this error go away.
25
+ <br />
26
+ <small className="italic">
27
+ Note: This hint is only shown in development mode.
28
+ </small>
29
+ </Callout>
30
+ )}
31
+ <p>
32
+ It seems that the page you are looking for does not exist or may have
33
+ been moved. Please check the URL for any typos or use the navigation
34
+ menu to find the correct page.
35
+ </p>
36
+ <Link to="/">Go back home</Link>
37
+ </div>
38
+ );
39
+ };
@@ -7,25 +7,23 @@ import {
7
7
 
8
8
  import { CheckIcon, CopyIcon } from "lucide-react";
9
9
 
10
- if (!import.meta.env.SSR) {
11
- globalThis.Prism = Prism;
12
- // @ts-expect-error This is untyped
13
- import("prismjs/components/prism-bash.min.js");
14
- // @ts-expect-error This is untyped
15
- import("prismjs/components/prism-ruby.min.js");
16
- // @ts-expect-error This is untyped
17
- import("prismjs/components/prism-markup-templating.js");
18
- // @ts-expect-error This is untyped
19
- import("prismjs/components/prism-php.min.js");
20
- // @ts-expect-error This is untyped
21
- import("prismjs/components/prism-json.min.js");
22
- // @ts-expect-error This is untyped
23
- import("prismjs/components/prism-java.min.js");
24
- // @ts-expect-error This is untyped
25
- import("prismjs/components/prism-csharp.min.js");
26
- // @ts-expect-error This is untyped
27
- import("prismjs/components/prism-objectivec.min.js");
28
- }
10
+ globalThis.Prism = Prism;
11
+ // @ts-expect-error This is untyped
12
+ import("prismjs/components/prism-bash.min.js");
13
+ // @ts-expect-error This is untyped
14
+ import("prismjs/components/prism-ruby.min.js");
15
+ // @ts-expect-error This is untyped
16
+ import("prismjs/components/prism-markup-templating.js");
17
+ // @ts-expect-error This is untyped
18
+ import("prismjs/components/prism-php.min.js");
19
+ // @ts-expect-error This is untyped
20
+ import("prismjs/components/prism-json.min.js");
21
+ // @ts-expect-error This is untyped
22
+ import("prismjs/components/prism-java.min.js");
23
+ // @ts-expect-error This is untyped
24
+ import("prismjs/components/prism-csharp.min.js");
25
+ // @ts-expect-error This is untyped
26
+ import("prismjs/components/prism-objectivec.min.js");
29
27
 
30
28
  import { useState } from "react";
31
29
  import { cn } from "../util/cn.js";
@@ -37,10 +35,12 @@ type SyntaxHighlightProps = {
37
35
  wrapLines?: boolean;
38
36
  copyable?: boolean;
39
37
  showLanguageIndicator?: boolean;
40
- } & Omit<HighlightProps, "children">;
38
+ language?: string;
39
+ } & Omit<HighlightProps, "children" | "language">;
41
40
 
42
41
  export const SyntaxHighlight = ({
43
42
  copyable = true,
43
+ language = "plain",
44
44
  ...props
45
45
  }: SyntaxHighlightProps) => {
46
46
  const [isDark] = useTheme();
@@ -51,7 +51,11 @@ export const SyntaxHighlight = ({
51
51
  }
52
52
 
53
53
  return (
54
- <Highlight theme={isDark ? themes.vsDark : themes.github} {...props}>
54
+ <Highlight
55
+ theme={isDark ? themes.vsDark : themes.github}
56
+ language={language}
57
+ {...props}
58
+ >
55
59
  {({ className, style, tokens, getLineProps, getTokenProps }) => (
56
60
  <pre
57
61
  className={cn(
@@ -88,7 +92,7 @@ export const SyntaxHighlight = ({
88
92
  )}
89
93
  {props.showLanguageIndicator && (
90
94
  <span className="absolute top-1.5 right-3 text-[11px] font-mono text-muted-foreground transition group-hover:opacity-0">
91
- {props.language}
95
+ {language}
92
96
  </span>
93
97
  )}
94
98
  {tokens.map((line, i) => (
@@ -0,0 +1,21 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
+ export function ErrorAlert({ error }: { error: any }) {
3
+ const message = error?.message ?? "Something went wrong";
4
+ const stack = error?.stack;
5
+
6
+ return (
7
+ <div className="flex h-screen max-h-screen min-h-full items-center justify-center bg-primary-background px-4 py-16 lg:px-8">
8
+ <div className="mx-auto max-w-[85%] sm:max-w-[50%]">
9
+ <h1 className="text-4xl font-bold tracking-tight text-h1-text sm:text-5xl">
10
+ Something went wrong
11
+ </h1>
12
+ <p className="mt-5 text-h1-text">{message}</p>
13
+ {stack ? (
14
+ <pre className="mt-5 max-h-[400px] w-full overflow-scroll rounded-md border border-input-border bg-input-background p-3 text-property-name-text text-red-700">
15
+ {stack}
16
+ </pre>
17
+ ) : null}
18
+ </div>
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,13 @@
1
+ import { isRouteErrorResponse, useRouteError } from "react-router-dom";
2
+ import { NotFoundPage } from "../components/NotFoundPage.js";
3
+ import { ErrorAlert } from "./ErrorAlert.js";
4
+
5
+ export function RouterError() {
6
+ const error = useRouteError();
7
+
8
+ if (isRouteErrorResponse(error) && error.status === 404) {
9
+ return <NotFoundPage />;
10
+ }
11
+
12
+ return <ErrorAlert error={error} />;
13
+ }
@@ -0,0 +1,5 @@
1
+ import { ErrorAlert } from "./ErrorAlert.js";
2
+
3
+ export function ServerError({ error }: { error: unknown }) {
4
+ return <ErrorAlert error={error} />;
5
+ }
@@ -0,0 +1,8 @@
1
+ import { FallbackProps } from "react-error-boundary";
2
+ import { ErrorAlert } from "./ErrorAlert.js";
3
+
4
+ export function TopLevelError({ error, resetErrorBoundary }: FallbackProps) {
5
+ // Call resetErrorBoundary() to reset the error boundary and retry the render.
6
+
7
+ return <ErrorAlert error={error} />;
8
+ }
@@ -1,3 +1,4 @@
1
+ import { GraphQLError } from "graphql/error/index.js";
1
2
  import { OpenAPIV3, type OpenAPIV3_1 } from "openapi-types";
2
3
  import { dereference, type JSONSchema } from "./dereference/index.js";
3
4
  import { upgradeSchema } from "./upgrade/index.js";
@@ -9,10 +10,6 @@ type DeepOmitReference<T> = T extends ReferenceObject
9
10
  ? { [K in keyof T]: DeepOmitReference<T[K]> }
10
11
  : T;
11
12
 
12
- // type Prettify<T> = {
13
- // [K in keyof T]: T[K];
14
- // } & {};
15
-
16
13
  export type OpenAPIDocument = DeepOmitReference<OpenAPIV3_1.Document>;
17
14
  export type ResponseObject = DeepOmitReference<OpenAPIV3_1.ResponseObject>;
18
15
  export type OperationObject = DeepOmitReference<OpenAPIV3_1.OperationObject>;
@@ -46,37 +43,63 @@ export const HttpMethods = Object.values(OpenAPIV3.HttpMethods);
46
43
  // };
47
44
 
48
45
  const parseSchemaInput = async (
49
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
- schemaInput: any,
46
+ schemaInput: unknown,
51
47
  ): Promise<JSONSchema & { openapi?: string }> => {
52
48
  if (typeof schemaInput === "string") {
53
49
  if (schemaInput.trim().startsWith("{")) {
54
- return JSON.parse(schemaInput);
50
+ try {
51
+ return JSON.parse(schemaInput);
52
+ } catch (err) {
53
+ throw new GraphQLError("Invalid JSON schema", {
54
+ originalError: err,
55
+ });
56
+ }
55
57
  }
56
58
  if (schemaInput.includes("://")) {
57
- const response = await fetch(schemaInput, {
58
- cache: "force-cache",
59
- });
60
- return (await response.json()) as JSONSchema;
59
+ let response;
60
+ try {
61
+ response = await fetch(schemaInput, {
62
+ cache: "force-cache",
63
+ });
64
+ } catch (err) {
65
+ throw new GraphQLError("Failed to fetch schema", {
66
+ originalError: err,
67
+ });
68
+ }
69
+
70
+ if (!response.ok) {
71
+ throw new GraphQLError(
72
+ `Failed to fetch schema: ${response.statusText}`,
73
+ );
74
+ }
75
+
76
+ try {
77
+ return (await response.json()) as JSONSchema;
78
+ } catch (err) {
79
+ throw new GraphQLError("Fetched invalid JSON schema", {
80
+ originalError: err,
81
+ });
82
+ }
61
83
  }
62
84
  const yaml = await import("yaml");
63
- return yaml.parse(schemaInput);
85
+ const parsed = yaml.parse(schemaInput);
86
+
87
+ if (typeof parsed === "object") return parsed;
64
88
  }
65
89
 
66
- if (typeof schemaInput === "object") return schemaInput;
90
+ if (typeof schemaInput === "object") return schemaInput as JSONSchema;
67
91
 
68
- throw new Error("Unsupported schema input");
92
+ throw new GraphQLError("Unsupported schema input: " + schemaInput);
69
93
  };
70
94
 
71
95
  /**
72
96
  * Validates, dereferences and upgrades the OpenAPI schema (to v3.1) if necessary.
73
97
  */
74
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
- export const validate = async (schemaInput: any) => {
98
+ export const validate = async (schemaInput: unknown) => {
76
99
  const schema = await parseSchemaInput(schemaInput);
77
100
 
78
101
  if (!schema.openapi) {
79
- throw new Error("OpenAPI version is not defined");
102
+ throw new GraphQLError("OpenAPI version is not defined");
80
103
  }
81
104
 
82
105
  // const validator = await getValidator(schema.openapi);
@@ -88,9 +111,5 @@ export const validate = async (schemaInput: any) => {
88
111
 
89
112
  const dereferenced = await dereference(schema);
90
113
 
91
- const upgraded = upgradeSchema(
92
- dereferenced as OpenAPIV3_1.Document | OpenAPIV3.Document,
93
- );
94
-
95
- return upgraded;
114
+ return upgradeSchema(dereferenced);
96
115
  };
@@ -1,5 +1,4 @@
1
- import logger from "loglevel";
2
- import { Outlet, useRouteError } from "react-router-dom";
1
+ import { Outlet, type RouteObject } from "react-router-dom";
3
2
  import invariant from "tiny-invariant";
4
3
  import { useAuth } from "../../authentication/hook.js";
5
4
  import { DevPortalContext } from "../../core/DevPortalContext.js";
@@ -7,6 +6,7 @@ import {
7
6
  type ApiIdentityPlugin,
8
7
  type DevPortalPlugin,
9
8
  } from "../../core/plugins.js";
9
+ import { RouterError } from "../../errors/RouterError.js";
10
10
  import { Button } from "../../ui/Button.js";
11
11
  import { CreateApiKey } from "./CreateApiKey.js";
12
12
  import { SettingsApiKeys } from "./SettingsApiKeys.js";
@@ -100,18 +100,6 @@ const ProtectedRoute = () => {
100
100
  );
101
101
  };
102
102
 
103
- const SettingsErrorBoundary = () => {
104
- const error = useRouteError();
105
- logger.error(String(error));
106
-
107
- return (
108
- <div className="flex flex-col justify-center gap-2 items-center h-1/2 my-12">
109
- <h1>Something went wrong</h1>
110
- {error instanceof Error && <p>{error.message}</p>}
111
- </div>
112
- );
113
- };
114
-
115
103
  export const apiKeyPlugin = (
116
104
  options: ApiKeyPluginOptions,
117
105
  ): DevPortalPlugin & ApiIdentityPlugin => {
@@ -138,12 +126,12 @@ export const apiKeyPlugin = (
138
126
  return [];
139
127
  }
140
128
  },
141
- getRoutes: () => {
129
+ getRoutes: (): RouteObject[] => {
142
130
  // TODO: Make lazy
143
131
  return [
144
132
  {
145
133
  element: <ProtectedRoute />,
146
- errorElement: <SettingsErrorBoundary />,
134
+ errorElement: <RouterError />,
147
135
  children: [
148
136
  {
149
137
  path: "/settings/api-keys",
@@ -45,7 +45,7 @@ export const generateRoutes = (
45
45
  },
46
46
  );
47
47
 
48
- const rootRoutes = Array.from(
48
+ const rootRoutes: RouteObject[] = Array.from(
49
49
  new Set(routes.map((route) => route.path.split("/").at(0))),
50
50
  ).map((dir) => ({
51
51
  path: `/${dir}`,
@@ -29,10 +29,6 @@ export type MDXImport = {
29
29
  export const markdownPlugin = ({
30
30
  markdownFiles,
31
31
  defaultOptions,
32
- }: MarkdownPluginOptions): DevPortalPlugin => {
33
- return {
34
- getRoutes() {
35
- return generateRoutes(markdownFiles, defaultOptions);
36
- },
37
- };
38
- };
32
+ }: MarkdownPluginOptions): DevPortalPlugin => ({
33
+ getRoutes: () => generateRoutes(markdownFiles, defaultOptions),
34
+ });
@@ -1,7 +1,11 @@
1
1
  import { ResultOf } from "@graphql-typed-document-node/core";
2
2
  import { CategoryHeading } from "../../components/CategoryHeading.js";
3
+ import { ErrorPage } from "../../components/ErrorPage.js";
3
4
  import { Heading } from "../../components/Heading.js";
5
+ import { InlineCode } from "../../components/InlineCode.js";
4
6
  import { Markdown } from "../../components/Markdown.js";
7
+ import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
8
+ import { Callout } from "../../ui/Callout.js";
5
9
  import { cn } from "../../util/cn.js";
6
10
  import { OperationListItem } from "./OperationListItem.js";
7
11
  import { useOasConfig } from "./context.js";
@@ -91,6 +95,32 @@ export const OperationList = () => {
91
95
  context: suspenseContext,
92
96
  });
93
97
 
98
+ const error = result.error?.graphQLErrors.at(0);
99
+
100
+ // Looks like there is no Suspense level error handling (yet)?
101
+ // So we handle the error case in the component directly
102
+ if (error) {
103
+ return (
104
+ <ErrorPage
105
+ category="Error"
106
+ title="Schema cannot be displayed"
107
+ message={
108
+ <>
109
+ {import.meta.env.DEV && (
110
+ <Callout type="danger" title="Developer hint" className="mb-4">
111
+ Check your configuration value{" "}
112
+ <InlineCode>apis.type</InlineCode> and{" "}
113
+ <InlineCode>apis.input</InlineCode> in the Zudoku config.
114
+ </Callout>
115
+ )}
116
+ An error occurred while trying to fetch the API reference:
117
+ <SyntaxHighlight code={error.toString()} language="plain" />
118
+ </>
119
+ }
120
+ />
121
+ );
122
+ }
123
+
94
124
  if (!result.data) return null;
95
125
 
96
126
  return (
@@ -1,6 +1,7 @@
1
1
  import { Heading } from "../../components/Heading.js";
2
2
  import { Markdown } from "../../components/Markdown.js";
3
3
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../ui/Tabs.js";
4
+ import { groupBy } from "../../util/groupBy.js";
4
5
  import { renderIf } from "../../util/renderIf.js";
5
6
  import { OperationsFragment } from "./OperationList.js";
6
7
  import { ParameterList } from "./ParameterList.js";
@@ -18,10 +19,11 @@ export const OperationListItem = ({
18
19
  operationFragment: FragmentType<typeof OperationsFragment>;
19
20
  }) => {
20
21
  const operation = useFragment(OperationsFragment, operationFragment);
21
- const groupedParameters = Object.groupBy(
22
+ const groupedParameters = groupBy(
22
23
  operation.parameters ?? [],
23
24
  (param) => param.in,
24
25
  );
26
+
25
27
  const first = operation.responses.at(0);
26
28
  return (
27
29
  <div
@@ -1,5 +1,6 @@
1
1
  import { Markdown } from "../../components/Markdown.js";
2
2
  import { SchemaObject } from "../../oas/parser/index.js";
3
+ import { groupBy } from "../../util/groupBy.js";
3
4
  import { objectEntries } from "../../util/objectEntries.js";
4
5
  import { SchemaListViewItemGroup } from "./SchemaListViewItemGroup.js";
5
6
  import { SchemaProseClasses } from "./util/prose.js";
@@ -25,16 +26,13 @@ export const SchemaListView = ({
25
26
  Array.isArray(additionalProperties) ? additionalProperties : [],
26
27
  );
27
28
 
28
- const groups = Object.groupBy(
29
- combinedProperties,
30
- ([propertyName, property]) => {
31
- return property.deprecated
32
- ? "deprecated"
33
- : schema.required?.includes(propertyName)
34
- ? "required"
35
- : "optional";
36
- },
37
- );
29
+ const groups = groupBy(combinedProperties, ([propertyName, property]) => {
30
+ return property.deprecated
31
+ ? "deprecated"
32
+ : schema.required?.includes(propertyName)
33
+ ? "required"
34
+ : "optional";
35
+ });
38
36
 
39
37
  return (
40
38
  <div className="flex flex-col gap-2.5">
@@ -25,7 +25,7 @@ export const createClient: CreateClientFunction = () => {
25
25
  fetch: async (req, init) => {
26
26
  if (!init?.body) throw new Error("No body");
27
27
  const response = await localServer.fetch(
28
- new Request("/__z/graphql", {
28
+ new Request("http://localhost/__z/graphql", {
29
29
  method: "POST",
30
30
  body: init.body,
31
31
  headers: {
@@ -1,4 +1,4 @@
1
- import { matchPath, type RouteObject } from "react-router-dom";
1
+ import { matchPath, useRouteError, type RouteObject } from "react-router-dom";
2
2
  import {
3
3
  type DevPortalPlugin,
4
4
  type PluginNavigationCategory,
@@ -12,6 +12,8 @@ import {
12
12
  } from "./util/urql.js";
13
13
 
14
14
  import { createClient } from "virtual:zudoku-openapi-worker";
15
+ import { ErrorPage } from "../../components/ErrorPage.js";
16
+ import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
15
17
  import { OasPluginConfig } from "./interfaces.js";
16
18
 
17
19
  const GetCategoriesQuery = graphql(`
@@ -34,6 +36,20 @@ const GetCategoriesQuery = graphql(`
34
36
  }
35
37
  `);
36
38
 
39
+ const OpenApiErrorPage = () => {
40
+ const error = useRouteError();
41
+ const message =
42
+ error instanceof Error ? (
43
+ <SyntaxHighlight code={error.message} />
44
+ ) : (
45
+ "An unknown error occurred"
46
+ );
47
+
48
+ return (
49
+ <ErrorPage category="Error" title="An error occurred" message={message} />
50
+ );
51
+ };
52
+
37
53
  export const openApiPlugin = (config: OasPluginConfig): DevPortalPlugin => {
38
54
  const basePath = config.path ?? "/reference";
39
55
 
@@ -112,6 +128,7 @@ export const openApiPlugin = (config: OasPluginConfig): DevPortalPlugin => {
112
128
  element: <OpenApiRoute client={client} config={config} />,
113
129
  };
114
130
  },
131
+ errorElement: <OpenApiErrorPage />,
115
132
  children: [
116
133
  {
117
134
  path: basePath,
@@ -1,4 +1,4 @@
1
- import { Navigate } from "react-router-dom";
1
+ import { redirect } from "react-router-dom";
2
2
  import type { DevPortalPlugin } from "../../core/plugins.js";
3
3
 
4
4
  export type Redirect = {
@@ -14,7 +14,7 @@ export const redirectPlugin = (options: {
14
14
  getRoutes: () =>
15
15
  options.redirects.map(({ from, to, replace }) => ({
16
16
  path: from,
17
- element: <Navigate to={to} replace={replace} />,
17
+ loader: () => redirect(to),
18
18
  })),
19
19
  };
20
20
  };
@@ -64,9 +64,9 @@ export const Callout = ({ type, children, title, className }: CalloutProps) => {
64
64
  return (
65
65
  <div
66
66
  className={cn(
67
- "not-prose grid grid-cols-[fit-content_1fr] grid-rows-[fit-content_1fr] gap-x-4 gap-y-2 text-md rounded-md border p-4",
67
+ "not-prose grid grid-cols-[min-content_1fr] grid-rows-[fit-content_1fr] gap-x-4 gap-y-2 text-md rounded-md border p-4",
68
68
  "[&_a]:underline [&_a]:decoration-current [&_a]:decoration-from-font [&_a]:underline-offset-4 hover:[&_a]:decoration-1",
69
- "[&_code]:!bg-transparent [&_code]:!border-none",
69
+ "[&_code]:!bg-gray-50 [&_code]:dark:!bg-gray-800 [&_code]:!border-none",
70
70
  title && "items-center",
71
71
  border,
72
72
  bg,
@@ -2,9 +2,9 @@ import { MDXProvider } from "@mdx-js/react";
2
2
  import type { ComponentProps } from "react";
3
3
  import { Link } from "react-router-dom";
4
4
  import { Heading } from "../components/Heading.js";
5
+ import { InlineCode } from "../components/InlineCode.js";
5
6
  import { SyntaxHighlight } from "../components/SyntaxHighlight.js";
6
7
  import { Callout } from "../ui/Callout.js";
7
- import { cn } from "./cn.js";
8
8
 
9
9
  export type MdxComponentsType = ComponentProps<
10
10
  typeof MDXProvider
@@ -80,15 +80,6 @@ export const MdxComponents = {
80
80
  );
81
81
  }
82
82
 
83
- return (
84
- <code
85
- className={cn(
86
- className,
87
- "font-mono border border-border p-1 py-0.5 rounded bg-border/50 dark:bg-border/70 whitespace-nowrap",
88
- )}
89
- >
90
- {children}
91
- </code>
92
- );
83
+ return <InlineCode className={className}>{children}</InlineCode>;
93
84
  },
94
85
  } satisfies MdxComponentsType;