zudoku 0.28.3 → 0.29.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 (133) hide show
  1. package/dist/app/entry.server.d.ts +1 -1
  2. package/dist/cli/common/output.js.map +1 -1
  3. package/dist/cli/dev/handler.js +5 -0
  4. package/dist/cli/dev/handler.js.map +1 -1
  5. package/dist/config/validators/common.d.ts +111 -0
  6. package/dist/config/validators/common.js +9 -6
  7. package/dist/config/validators/common.js.map +1 -1
  8. package/dist/config/validators/validate.d.ts +42 -0
  9. package/dist/lib/authentication/state.d.ts +9 -0
  10. package/dist/lib/authentication/state.js +11 -0
  11. package/dist/lib/authentication/state.js.map +1 -1
  12. package/dist/lib/components/Autocomplete.d.ts +4 -3
  13. package/dist/lib/components/Autocomplete.js +2 -2
  14. package/dist/lib/components/Autocomplete.js.map +1 -1
  15. package/dist/lib/components/PathRenderer.js +23 -20
  16. package/dist/lib/components/PathRenderer.js.map +1 -1
  17. package/dist/lib/components/navigation/SidebarItem.js +1 -0
  18. package/dist/lib/components/navigation/SidebarItem.js.map +1 -1
  19. package/dist/lib/oas/graphql/index.d.ts +7 -0
  20. package/dist/lib/oas/graphql/index.js +7 -6
  21. package/dist/lib/oas/graphql/index.js.map +1 -1
  22. package/dist/lib/plugins/openapi/Endpoint.js +6 -7
  23. package/dist/lib/plugins/openapi/Endpoint.js.map +1 -1
  24. package/dist/lib/plugins/openapi/OperationList.js +11 -7
  25. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  26. package/dist/lib/plugins/openapi/Sidecar.js +4 -4
  27. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  28. package/dist/lib/plugins/openapi/graphql/gql.d.ts +2 -2
  29. package/dist/lib/plugins/openapi/graphql/gql.js +2 -2
  30. package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
  31. package/dist/lib/plugins/openapi/graphql/graphql.d.ts +20 -4
  32. package/dist/lib/plugins/openapi/graphql/graphql.js +16 -2
  33. package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
  34. package/dist/lib/plugins/openapi/index.js +57 -28
  35. package/dist/lib/plugins/openapi/index.js.map +1 -1
  36. package/dist/lib/plugins/openapi/interfaces.d.ts +4 -2
  37. package/dist/lib/plugins/openapi/playground/Headers.d.ts +3 -2
  38. package/dist/lib/plugins/openapi/playground/Headers.js +42 -26
  39. package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
  40. package/dist/lib/plugins/openapi/playground/ParamsGrid.d.ts +4 -0
  41. package/dist/lib/plugins/openapi/playground/ParamsGrid.js +2 -1
  42. package/dist/lib/plugins/openapi/playground/ParamsGrid.js.map +1 -1
  43. package/dist/lib/plugins/openapi/playground/PathParams.js +3 -3
  44. package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
  45. package/dist/lib/plugins/openapi/playground/Playground.d.ts +1 -0
  46. package/dist/lib/plugins/openapi/playground/Playground.js +4 -4
  47. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  48. package/dist/lib/plugins/openapi/playground/QueryParams.js +21 -21
  49. package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
  50. package/dist/lib/ui/Input.d.ts +1 -2
  51. package/dist/lib/ui/Input.js.map +1 -1
  52. package/dist/lib/util/useScrollToAnchor.js +1 -1
  53. package/dist/lib/util/useScrollToAnchor.js.map +1 -1
  54. package/dist/lib/util/useScrollToTop.js +6 -4
  55. package/dist/lib/util/useScrollToTop.js.map +1 -1
  56. package/dist/vite/build.js +1 -1
  57. package/dist/vite/build.js.map +1 -1
  58. package/dist/vite/plugin-api.js +13 -6
  59. package/dist/vite/plugin-api.js.map +1 -1
  60. package/dist/vite/plugin-config.js +2 -1
  61. package/dist/vite/plugin-config.js.map +1 -1
  62. package/dist/vite/plugin-mdx.js +18 -12
  63. package/dist/vite/plugin-mdx.js.map +1 -1
  64. package/dist/vite/plugin.js.map +1 -1
  65. package/dist/vite/{prerender.d.ts → prerender/FileWritingResponse.d.ts} +0 -6
  66. package/dist/vite/prerender/FileWritingResponse.js +42 -0
  67. package/dist/vite/prerender/FileWritingResponse.js.map +1 -0
  68. package/dist/vite/prerender/prerender.d.ts +6 -0
  69. package/dist/vite/prerender/prerender.js +68 -0
  70. package/dist/vite/prerender/prerender.js.map +1 -0
  71. package/dist/vite/prerender/worker.d.ts +9 -0
  72. package/dist/vite/prerender/worker.js +27 -0
  73. package/dist/vite/prerender/worker.js.map +1 -0
  74. package/lib/{Markdown-LcMEZ0Sn.js → Markdown-8mv9nhGd.js} +3338 -3360
  75. package/lib/Markdown-8mv9nhGd.js.map +1 -0
  76. package/lib/{MdxPage-DkH3V4hV.js → MdxPage-BalfwlsD.js} +3 -3
  77. package/lib/{MdxPage-DkH3V4hV.js.map → MdxPage-BalfwlsD.js.map} +1 -1
  78. package/lib/{OperationList-wzZNceUl.js → OperationList-B3VX94x4.js} +500 -493
  79. package/lib/OperationList-B3VX94x4.js.map +1 -0
  80. package/lib/{Select-DJkXPPD0.js → Select-BcAbBUmk.js} +2 -2
  81. package/lib/{Select-DJkXPPD0.js.map → Select-BcAbBUmk.js.map} +1 -1
  82. package/lib/{SlotletProvider-D1t2ePCI.js → SlotletProvider-D0mFmGJu.js} +2 -2
  83. package/lib/{SlotletProvider-D1t2ePCI.js.map → SlotletProvider-D0mFmGJu.js.map} +1 -1
  84. package/lib/{createServer-DIztAu7i.js → createServer-E3cXjB0P.js} +4 -6
  85. package/lib/{createServer-DIztAu7i.js.map → createServer-E3cXjB0P.js.map} +1 -1
  86. package/lib/{hook-CiX69UZ6.js → hook-NIpDSpau.js} +2 -2
  87. package/lib/{hook-CiX69UZ6.js.map → hook-NIpDSpau.js.map} +1 -1
  88. package/lib/{index-DrR58fsJ.js → index-P0YUtHIb.js} +802 -745
  89. package/lib/index-P0YUtHIb.js.map +1 -0
  90. package/lib/state-bfQxaDxU.js +211 -0
  91. package/lib/{state-mM7uaXTW.js.map → state-bfQxaDxU.js.map} +1 -1
  92. package/lib/ui/Input.js.map +1 -1
  93. package/lib/{useScrollToAnchor-DYGn1MT9.js → useScrollToAnchor-BVCQSeKB.js} +29 -28
  94. package/lib/{useScrollToAnchor-DYGn1MT9.js.map → useScrollToAnchor-BVCQSeKB.js.map} +1 -1
  95. package/lib/zudoku.auth-auth0.js +1 -1
  96. package/lib/zudoku.auth-clerk.js +1 -1
  97. package/lib/zudoku.auth-openid.js +1 -1
  98. package/lib/zudoku.components.js +348 -347
  99. package/lib/zudoku.components.js.map +1 -1
  100. package/lib/zudoku.plugin-api-catalog.js +2 -2
  101. package/lib/zudoku.plugin-api-keys.js +3 -3
  102. package/lib/zudoku.plugin-custom-pages.js +1 -1
  103. package/lib/zudoku.plugin-markdown.js +1 -1
  104. package/lib/zudoku.plugin-openapi.js +2 -2
  105. package/package.json +3 -1
  106. package/src/app/demo-cdn.html +31 -31
  107. package/src/app/entry.server.tsx +1 -1
  108. package/src/lib/authentication/state.ts +18 -0
  109. package/src/lib/components/Autocomplete.tsx +6 -4
  110. package/src/lib/components/PathRenderer.tsx +6 -4
  111. package/src/lib/components/navigation/SidebarItem.tsx +1 -0
  112. package/src/lib/oas/graphql/index.ts +10 -9
  113. package/src/lib/plugins/openapi/Endpoint.tsx +11 -9
  114. package/src/lib/plugins/openapi/OperationList.tsx +16 -10
  115. package/src/lib/plugins/openapi/Sidecar.tsx +4 -4
  116. package/src/lib/plugins/openapi/graphql/gql.ts +4 -4
  117. package/src/lib/plugins/openapi/graphql/graphql.ts +30 -6
  118. package/src/lib/plugins/openapi/index.tsx +74 -41
  119. package/src/lib/plugins/openapi/interfaces.ts +5 -10
  120. package/src/lib/plugins/openapi/playground/Headers.tsx +125 -89
  121. package/src/lib/plugins/openapi/playground/ParamsGrid.tsx +6 -1
  122. package/src/lib/plugins/openapi/playground/PathParams.tsx +9 -9
  123. package/src/lib/plugins/openapi/playground/Playground.tsx +7 -4
  124. package/src/lib/plugins/openapi/playground/QueryParams.tsx +88 -86
  125. package/src/lib/ui/Input.tsx +1 -2
  126. package/src/lib/util/useScrollToAnchor.ts +1 -1
  127. package/src/lib/util/useScrollToTop.ts +8 -3
  128. package/dist/vite/prerender.js +0 -89
  129. package/dist/vite/prerender.js.map +0 -1
  130. package/lib/Markdown-LcMEZ0Sn.js.map +0 -1
  131. package/lib/OperationList-wzZNceUl.js.map +0 -1
  132. package/lib/index-DrR58fsJ.js.map +0 -1
  133. package/lib/state-mM7uaXTW.js +0 -202
@@ -3,8 +3,8 @@ import { s as j } from "./index-CjJS0l4l.js";
3
3
  import { u as b } from "./ZudokuContext-dUyBGMap.js";
4
4
  import { b as y } from "./chunk-SYFQ2XB5-QijJrSf0.js";
5
5
  import { Head as v, Link as N } from "./zudoku.components.js";
6
- import { u as w } from "./state-mM7uaXTW.js";
7
- import { M as C } from "./Markdown-LcMEZ0Sn.js";
6
+ import { u as w } from "./state-bfQxaDxU.js";
7
+ import { M as C } from "./Markdown-8mv9nhGd.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-D1t2ePCI.js";
3
+ import { D as k, S as m, R as N } from "./SlotletProvider-D0mFmGJu.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-DJkXPPD0.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-BcAbBUmk.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-QijJrSf0.js";
8
8
  import { a as y, e as q, u as O } from "./ZudokuContext-dUyBGMap.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-CiX69UZ6.js";
11
+ import { u as F } from "./hook-NIpDSpau.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-LcMEZ0Sn.js";
3
+ import { P as n } from "./Markdown-8mv9nhGd.js";
4
4
  import { c } from "./cn-qaFjX9_3.js";
5
5
  import { u as p } from "./useExposedProps-Bbf99zic.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-DkH3V4hV.js"), { default: p, ...g } = await a();
77
+ const { MdxPage: l } = await import("./MdxPage-BalfwlsD.js"), { default: p, ...g } = await a();
78
78
  return {
79
79
  element: /* @__PURE__ */ P.jsx(
80
80
  l,
@@ -2,10 +2,10 @@ import "./jsx-runtime-Bdg6XQ1m.js";
2
2
  import "./index-CjJS0l4l.js";
3
3
  import "lucide-react";
4
4
  import "./chunk-SYFQ2XB5-QijJrSf0.js";
5
- import "./hook-CiX69UZ6.js";
5
+ import "./hook-NIpDSpau.js";
6
6
  import "./ui/Button.js";
7
7
  import "./joinUrl-nLx9pD-Z.js";
8
- import { o as f } from "./index-DrR58fsJ.js";
8
+ import { o as f } from "./index-P0YUtHIb.js";
9
9
  export {
10
10
  f as openApiPlugin
11
11
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.28.3",
3
+ "version": "0.29.1",
4
4
  "type": "module",
5
5
  "homepage": "https://zudoku.dev",
6
6
  "repository": {
@@ -177,6 +177,7 @@
177
177
  "object-hash": "3.0.0",
178
178
  "openapi-types": "12.1.3",
179
179
  "picocolors": "1.1.1",
180
+ "piscina": "5.0.0-alpha.1",
180
181
  "postcss": "8.5.1",
181
182
  "posthog-node": "4.4.1",
182
183
  "prism-react-renderer": "2.4.1",
@@ -185,6 +186,7 @@
185
186
  "react-hook-form": "7.54.2",
186
187
  "react-is": "19.0.0",
187
188
  "react-router": "7.1.3",
189
+ "rehype-mdx-import-media": "1.2.0",
188
190
  "rehype-raw": "7.0.0",
189
191
  "rehype-slug": "6.0.0",
190
192
  "remark-comment": "1.0.0",
@@ -1,34 +1,34 @@
1
1
  <!doctype html>
2
2
  <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <link
6
- rel="icon"
7
- type="image/svg+xml"
8
- href="https://cdn.zudoku.dev/logos/favicon.svg"
9
- />
10
- <meta
11
- name="viewport"
12
- content="width=device-width, initial-scale=1.0, minimum-scale=1.0"
13
- />
14
- <title>Zudoku Demo</title>
15
- <script
16
- type="module"
17
- crossorigin
18
- src="https://cdn.zudoku.dev/latest/demo.js"
19
- ></script>
20
- <link
21
- rel="stylesheet"
22
- crossorigin
23
- href="https://cdn.zudoku.dev/latest/style.css"
24
- />
25
- <script>
26
- !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init push capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
27
- posthog.init('phc_l8rjm0vHBMwNdGeBRDrK8UIYjyVxZyBAtnYo2hS18OY',{api_host:'https://us.i.posthog.com', person_profiles: 'identified_only' // or 'always' to create profiles for anonymous users as well
28
- })
29
- </script>
30
- </head>
31
- <body>
32
- <div id="root"></div>
33
- </body>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link
6
+ rel="icon"
7
+ type="image/svg+xml"
8
+ href="https://cdn.zudoku.dev/logos/favicon.svg"
9
+ />
10
+ <meta
11
+ name="viewport"
12
+ content="width=device-width, initial-scale=1.0, minimum-scale=1.0"
13
+ />
14
+ <title>Zudoku Demo</title>
15
+ <script
16
+ type="module"
17
+ crossorigin
18
+ src="https://cdn.zudoku.dev/latest/demo.js"
19
+ ></script>
20
+ <link
21
+ rel="stylesheet"
22
+ crossorigin
23
+ href="https://cdn.zudoku.dev/latest/style.css"
24
+ />
25
+ <script>
26
+ !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init push capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
27
+ posthog.init('phc_l8rjm0vHBMwNdGeBRDrK8UIYjyVxZyBAtnYo2hS18OY',{api_host:'https://us.i.posthog.com', person_profiles: 'identified_only' // or 'always' to create profiles for anonymous users as well
28
+ })
29
+ </script>
30
+ </head>
31
+ <body>
32
+ <div id="root"></div>
33
+ </body>
34
34
  </html>
@@ -13,7 +13,7 @@ import "virtual:zudoku-theme.css";
13
13
  import "vite/modulepreload-polyfill";
14
14
  import { BootstrapStatic, ServerError } from "zudoku/components";
15
15
  import type { ZudokuConfig } from "../config/config.js";
16
- import type { FileWritingResponse } from "../vite/prerender.js";
16
+ import type { FileWritingResponse } from "../vite/prerender/FileWritingResponse.js";
17
17
  import "./main.css";
18
18
  import { getRoutesByConfig } from "./main.js";
19
19
 
@@ -1,3 +1,4 @@
1
+ import { useMemo } from "react";
1
2
  import { create, type Mutate, type StoreApi } from "zustand";
2
3
  import { createJSONStorage, persist } from "zustand/middleware";
3
4
 
@@ -77,3 +78,20 @@ export const useSelectedServerStore = create<SelectedServerState>()(
77
78
  { name: "zudoku-selected-server" },
78
79
  ),
79
80
  );
81
+
82
+ /**
83
+ * Simple wrapper for `useSelectedServerStore` to fall back to first of the provided servers
84
+ */
85
+ export const useSelectedServer = (servers: Array<{ url: string }>) => {
86
+ const { selectedServer, setSelectedServer } = useSelectedServerStore();
87
+
88
+ const finalSelectedServer = useMemo(
89
+ () =>
90
+ selectedServer && servers.some((s) => s.url === selectedServer)
91
+ ? selectedServer
92
+ : (servers.at(0)?.url ?? ""),
93
+ [selectedServer, servers],
94
+ );
95
+
96
+ return { selectedServer: finalSelectedServer, setSelectedServer };
97
+ };
@@ -1,6 +1,6 @@
1
1
  import { PopoverAnchor } from "@radix-ui/react-popover";
2
2
  import { useCommandState } from "cmdk";
3
- import { useRef, useState, type Ref } from "react";
3
+ import { useRef, useState, type KeyboardEvent, type Ref } from "react";
4
4
  import {
5
5
  Command,
6
6
  CommandInlineInput,
@@ -16,8 +16,9 @@ type AutocompleteProps = {
16
16
  onChange: (e: string) => void;
17
17
  className?: string;
18
18
  placeholder?: string;
19
- onEnterPress?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
19
+ onEnterPress?: (e: KeyboardEvent<HTMLInputElement>) => void;
20
20
  ref?: Ref<HTMLInputElement>;
21
+ shouldFilter?: boolean;
21
22
  };
22
23
 
23
24
  const AutocompletePopover = ({
@@ -33,6 +34,7 @@ const AutocompletePopover = ({
33
34
  const [dontClose, setDontClose] = useState(false);
34
35
  const count = useCommandState((state) => state.filtered.count);
35
36
  const inputRef = useRef<HTMLInputElement>(null);
37
+
36
38
  return (
37
39
  <Popover open={open}>
38
40
  <PopoverAnchor>
@@ -102,9 +104,9 @@ const AutocompletePopover = ({
102
104
  );
103
105
  };
104
106
 
105
- export const Autocomplete = (props: AutocompleteProps) => {
107
+ export const Autocomplete = ({ shouldFilter, ...props }: AutocompleteProps) => {
106
108
  return (
107
- <Command className="bg-transparent">
109
+ <Command className="bg-transparent" shouldFilter={shouldFilter}>
108
110
  <AutocompletePopover {...props} />
109
111
  </Command>
110
112
  );
@@ -12,13 +12,14 @@ export const PathRenderer = ({
12
12
  }: {
13
13
  path: string;
14
14
  renderParam: (props: PathParamProps) => ReactNode;
15
- }) =>
16
- path.split("/").map((part, i, arr) => {
15
+ }) => {
16
+ let paramIndex = 0;
17
+ return path.split("/").map((part, i, arr) => {
17
18
  const matches = Array.from(part.matchAll(/{([^}]+)}/g));
18
19
  const elements: ReactNode[] = [];
19
20
  let lastIndex = 0;
20
21
 
21
- matches.forEach((match, matchIndex) => {
22
+ matches.forEach((match) => {
22
23
  const [originalValue, name] = match;
23
24
  if (!name) return;
24
25
  const startIndex = match.index!;
@@ -33,7 +34,7 @@ export const PathRenderer = ({
33
34
 
34
35
  elements.push(
35
36
  <Fragment key={`param-${name}`}>
36
- {renderParam({ name, originalValue, index: matchIndex })}
37
+ {renderParam({ name, originalValue, index: paramIndex++ })}
37
38
  </Fragment>,
38
39
  );
39
40
 
@@ -57,3 +58,4 @@ export const PathRenderer = ({
57
58
  </Fragment>
58
59
  );
59
60
  });
61
+ };
@@ -90,6 +90,7 @@ export const SidebarItem = ({
90
90
  ) : !item.href.startsWith("http") ? (
91
91
  <NavLink
92
92
  className={navigationListItem({
93
+ isActive: item.href.split("#")[1] === activeAnchor,
93
94
  className: item.badge?.placement !== "start" && "justify-between",
94
95
  })}
95
96
  to={item.href}
@@ -45,13 +45,12 @@ export const createOperationSlug = (
45
45
  tag?: string,
46
46
  ) => {
47
47
  const summary =
48
- (operation.summary ?? "") +
49
- (operation.operationId ? "-" + operation.operationId : "");
48
+ operation.summary ||
49
+ operation.operationId ||
50
+ `${operation.method}-${operation.path}`;
51
+ const prefix = tag ? `${tag}-` : "";
50
52
 
51
- return slugify(
52
- (tag ? tag + "-" : "") +
53
- (summary || `${operation.method}-${operation.path}`),
54
- );
53
+ return slugify(prefix + summary);
55
54
  };
56
55
 
57
56
  export type SchemaImports = Record<
@@ -104,7 +103,9 @@ export const getAllTags = (schema: OpenAPIDocument): TagObject[] => {
104
103
  ];
105
104
  };
106
105
 
107
- const getAllOperations = (paths?: PathsObject) => {
106
+ export const getAllOperations = (
107
+ paths?: PathsObject,
108
+ ): GraphQLOperationObject[] => {
108
109
  const operations = Object.entries(paths ?? {}).flatMap(([path, value]) =>
109
110
  HttpMethods.flatMap((method) => {
110
111
  if (!value?.[method]) return [];
@@ -132,7 +133,7 @@ const getAllOperations = (paths?: PathsObject) => {
132
133
  path,
133
134
  parameters,
134
135
  tags: operation.tags ?? [],
135
- };
136
+ } satisfies GraphQLOperationObject;
136
137
  }),
137
138
  );
138
139
 
@@ -141,7 +142,7 @@ const getAllOperations = (paths?: PathsObject) => {
141
142
 
142
143
  const SchemaTag = builder.objectRef<TagObject>("SchemaTag").implement({
143
144
  fields: (t) => ({
144
- name: t.exposeString("name", { nullable: true }),
145
+ name: t.exposeString("name"),
145
146
  description: t.exposeString("description", { nullable: true }),
146
147
  operations: t.field({
147
148
  type: [OperationItem],
@@ -1,7 +1,7 @@
1
1
  import { useSuspenseQuery } from "@tanstack/react-query";
2
2
  import { CheckIcon, CopyIcon } from "lucide-react";
3
3
  import { useState, useTransition } from "react";
4
- import { useSelectedServerStore } from "../../authentication/state.js";
4
+ import { useSelectedServer } from "../../authentication/state.js";
5
5
  import { InlineCode } from "../../components/InlineCode.js";
6
6
  import { Button } from "../../ui/Button.js";
7
7
  import { useCreateQuery } from "./client/useCreateQuery.js";
@@ -48,20 +48,24 @@ export const Endpoint = () => {
48
48
  const query = useCreateQuery(ServersQuery, { input, type });
49
49
  const result = useSuspenseQuery(query);
50
50
  const [, startTransition] = useTransition();
51
- const { selectedServer, setSelectedServer } = useSelectedServerStore();
51
+ const { selectedServer, setSelectedServer } = useSelectedServer(
52
+ result.data.schema.servers,
53
+ );
52
54
 
53
55
  const { servers } = result.data.schema;
54
56
 
55
57
  if (servers.length === 0) return null;
56
58
 
59
+ const firstServer = servers.at(0)!;
60
+
57
61
  if (servers.length === 1) {
58
62
  return (
59
63
  <div className="flex items-center gap-2">
60
64
  <span className="font-medium text-sm">Endpoint:</span>
61
65
  <InlineCode className="text-xs px-2 py-1.5" selectOnClick>
62
- {servers[0]!.url}
66
+ {firstServer.url}
63
67
  </InlineCode>
64
- <CopyButton url={servers[0]!.url} />
68
+ <CopyButton url={firstServer.url} />
65
69
  </div>
66
70
  );
67
71
  }
@@ -73,18 +77,16 @@ export const Endpoint = () => {
73
77
  <SimpleSelect
74
78
  className="font-mono text-xs bg-border/50 dark:bg-border/70 py-1.5 max-w-[450px] truncate"
75
79
  onChange={(e) =>
76
- startTransition(() => {
77
- setSelectedServer(e.target.value);
78
- })
80
+ startTransition(() => setSelectedServer(e.target.value))
79
81
  }
80
- value={selectedServer ?? servers.at(0)!.url}
82
+ value={selectedServer}
81
83
  showChevrons={servers.length > 1}
82
84
  options={servers.map((server) => ({
83
85
  value: server.url,
84
86
  label: server.url,
85
87
  }))}
86
88
  />
87
- <CopyButton url={selectedServer ?? servers.at(0)!.url} />
89
+ <CopyButton url={selectedServer} />
88
90
  </div>
89
91
  );
90
92
  };
@@ -9,7 +9,7 @@ import {
9
9
  SelectTrigger,
10
10
  SelectValue,
11
11
  } from "zudoku/ui/Select.js";
12
- import { useSelectedServerStore } from "../../authentication/state.js";
12
+ import { useSelectedServer } from "../../authentication/state.js";
13
13
  import { CategoryHeading } from "../../components/CategoryHeading.js";
14
14
  import { Heading } from "../../components/Heading.js";
15
15
  import { Markdown, ProseClasses } from "../../components/Markdown.js";
@@ -96,6 +96,9 @@ const AllOperationsQuery = graphql(/* GraphQL */ `
96
96
  $untagged: Boolean
97
97
  ) {
98
98
  schema(input: $input, type: $type) {
99
+ servers {
100
+ url
101
+ }
99
102
  description
100
103
  summary
101
104
  title
@@ -127,13 +130,16 @@ export const OperationList = ({
127
130
  tag,
128
131
  untagged,
129
132
  });
130
- const { selectedServer } = useSelectedServerStore();
131
133
  const result = useSuspenseQuery(query);
132
- const title = result.data.schema.title;
133
- const summary = result.data.schema.summary;
134
- const description = result.data.schema.description;
134
+ const {
135
+ data: { schema },
136
+ } = result;
137
+ const { selectedServer } = useSelectedServer(schema.servers);
138
+ const title = schema.title;
139
+ const summary = schema.summary;
140
+ const description = schema.description;
135
141
  const navigate = useNavigate();
136
- const operations = result.data.schema.operations;
142
+ const operations = schema.operations;
137
143
  // Prefetch for Playground
138
144
  useApiIdentities();
139
145
 
@@ -184,7 +190,7 @@ export const OperationList = ({
184
190
  )}
185
191
  </div>
186
192
  </div>
187
- <Markdown content={result.data.schema.description ?? ""} />
193
+ <Markdown content={schema.description ?? ""} />
188
194
  </div>
189
195
  <hr />
190
196
  <div className="my-4 flex items-center justify-end gap-4">
@@ -192,12 +198,12 @@ export const OperationList = ({
192
198
  </div>
193
199
  {operations.map((fragment) => (
194
200
  <OperationListItem
195
- serverUrl={selectedServer ?? result.data.schema.url ?? ""}
201
+ serverUrl={selectedServer}
196
202
  key={fragment.slug}
197
203
  operationFragment={fragment}
198
204
  />
199
205
  ))}
200
- {/* {result.data.schema.tags
206
+ {/* {schema.tags
201
207
  .filter((tag) => tag.operations.length > 0)
202
208
  .map((tag) => (
203
209
  // px, -mx is so that `content-visibility` doesn't cut off overflown heading anchor links '#'
@@ -213,7 +219,7 @@ export const OperationList = ({
213
219
  <StaggeredRender>
214
220
  {tag.operations.map((fragment) => (
215
221
  <OperationListItem
216
- serverUrl={selectedServer ?? result.data.schema.url}
222
+ serverUrl={selectedServer ?? schema.url}
217
223
  key={fragment.slug}
218
224
  operationFragment={fragment}
219
225
  />
@@ -2,7 +2,7 @@ import { useSuspenseQuery } from "@tanstack/react-query";
2
2
  import { HTTPSnippet } from "@zudoku/httpsnippet";
3
3
  import { useMemo, useState, useTransition } from "react";
4
4
  import { useSearchParams } from "react-router";
5
- import { useSelectedServerStore } from "../../authentication/state.js";
5
+ import { useSelectedServer } from "../../authentication/state.js";
6
6
  import { PathRenderer } from "../../components/PathRenderer.js";
7
7
  import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
8
8
  import type { SchemaObject } from "../../oas/parser/index.js";
@@ -131,7 +131,7 @@ export const Sidecar = ({
131
131
  />
132
132
  );
133
133
 
134
- const { selectedServer } = useSelectedServerStore();
134
+ const { selectedServer } = useSelectedServer(result.data.schema.servers);
135
135
 
136
136
  const code = useMemo(() => {
137
137
  const example =
@@ -143,7 +143,7 @@ export const Sidecar = ({
143
143
  const snippet = new HTTPSnippet({
144
144
  method: operation.method.toLocaleUpperCase(),
145
145
  url:
146
- (selectedServer ?? result.data.schema.url ?? "") +
146
+ selectedServer +
147
147
  operation.path.replaceAll("{", ":").replaceAll("}", ""),
148
148
  postData: example
149
149
  ? {
@@ -187,7 +187,7 @@ export const Sidecar = ({
187
187
  </span>
188
188
  {isOnScreen && (
189
189
  <PlaygroundDialogWrapper
190
- server={result.data.schema.url ?? ""}
190
+ server={selectedServer}
191
191
  servers={result.data.schema.servers.map((server) => server.url)}
192
192
  operation={operation}
193
193
  examples={requestBodyContent ?? undefined}
@@ -17,13 +17,13 @@ const documents = {
17
17
  types.ServersQueryDocument,
18
18
  "\n fragment OperationsFragment on OperationItem {\n slug\n summary\n method\n description\n operationId\n contentTypes\n path\n parameters {\n name\n in\n description\n required\n schema\n style\n examples {\n name\n description\n externalValue\n value\n summary\n }\n }\n requestBody {\n content {\n mediaType\n encoding {\n name\n }\n examples {\n name\n description\n externalValue\n value\n summary\n }\n schema\n }\n description\n required\n }\n responses {\n statusCode\n links\n description\n content {\n examples {\n name\n description\n externalValue\n value\n summary\n }\n mediaType\n encoding {\n name\n }\n schema\n }\n }\n }\n":
19
19
  types.OperationsFragmentFragmentDoc,
20
- "\n query AllOperations(\n $input: JSON!\n $type: SchemaType!\n $tag: String\n $untagged: Boolean\n ) {\n schema(input: $input, type: $type) {\n description\n summary\n title\n url\n version\n tags(name: $tag) {\n name\n description\n }\n operations(tag: $tag, untagged: $untagged) {\n slug\n ...OperationsFragment\n }\n }\n }\n":
20
+ "\n query AllOperations(\n $input: JSON!\n $type: SchemaType!\n $tag: String\n $untagged: Boolean\n ) {\n schema(input: $input, type: $type) {\n servers {\n url\n }\n description\n summary\n title\n url\n version\n tags(name: $tag) {\n name\n description\n }\n operations(tag: $tag, untagged: $untagged) {\n slug\n ...OperationsFragment\n }\n }\n }\n":
21
21
  types.AllOperationsDocument,
22
22
  "\n query getServerQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n":
23
23
  types.GetServerQueryDocument,
24
24
  "\n query GetCategories($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n tags {\n name\n }\n }\n }\n":
25
25
  types.GetCategoriesDocument,
26
- "\n query GetOperations(\n $input: JSON!\n $type: SchemaType!\n $tag: String\n $untagged: Boolean\n ) {\n schema(input: $input, type: $type) {\n operations(tag: $tag, untagged: $untagged) {\n slug\n deprecated\n method\n summary\n operationId\n path\n }\n }\n }\n":
26
+ "\n query GetOperations($input: JSON!, $type: SchemaType!, $tag: String) {\n schema(input: $input, type: $type) {\n operations(tag: $tag) {\n slug\n deprecated\n method\n summary\n operationId\n path\n tags {\n name\n }\n }\n untagged: operations(untagged: true) {\n slug\n deprecated\n method\n summary\n operationId\n path\n }\n }\n }\n":
27
27
  types.GetOperationsDocument,
28
28
  };
29
29
 
@@ -43,7 +43,7 @@ export function graphql(
43
43
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
44
44
  */
45
45
  export function graphql(
46
- source: "\n query AllOperations(\n $input: JSON!\n $type: SchemaType!\n $tag: String\n $untagged: Boolean\n ) {\n schema(input: $input, type: $type) {\n description\n summary\n title\n url\n version\n tags(name: $tag) {\n name\n description\n }\n operations(tag: $tag, untagged: $untagged) {\n slug\n ...OperationsFragment\n }\n }\n }\n",
46
+ source: "\n query AllOperations(\n $input: JSON!\n $type: SchemaType!\n $tag: String\n $untagged: Boolean\n ) {\n schema(input: $input, type: $type) {\n servers {\n url\n }\n description\n summary\n title\n url\n version\n tags(name: $tag) {\n name\n description\n }\n operations(tag: $tag, untagged: $untagged) {\n slug\n ...OperationsFragment\n }\n }\n }\n",
47
47
  ): typeof import("./graphql.js").AllOperationsDocument;
48
48
  /**
49
49
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
@@ -61,7 +61,7 @@ export function graphql(
61
61
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
62
62
  */
63
63
  export function graphql(
64
- source: "\n query GetOperations(\n $input: JSON!\n $type: SchemaType!\n $tag: String\n $untagged: Boolean\n ) {\n schema(input: $input, type: $type) {\n operations(tag: $tag, untagged: $untagged) {\n slug\n deprecated\n method\n summary\n operationId\n path\n }\n }\n }\n",
64
+ source: "\n query GetOperations($input: JSON!, $type: SchemaType!, $tag: String) {\n schema(input: $input, type: $type) {\n operations(tag: $tag) {\n slug\n deprecated\n method\n summary\n operationId\n path\n tags {\n name\n }\n }\n untagged: operations(untagged: true) {\n slug\n deprecated\n method\n summary\n operationId\n path\n }\n }\n }\n",
65
65
  ): typeof import("./graphql.js").GetOperationsDocument;
66
66
 
67
67
  export function graphql(source: string) {
@@ -156,7 +156,7 @@ export type SchemaTagsArgs = {
156
156
  export type SchemaTag = {
157
157
  __typename?: "SchemaTag";
158
158
  description?: Maybe<Scalars["String"]["output"]>;
159
- name?: Maybe<Scalars["String"]["output"]>;
159
+ name: Scalars["String"]["output"];
160
160
  operations: Array<OperationItem>;
161
161
  };
162
162
 
@@ -271,9 +271,10 @@ export type AllOperationsQuery = {
271
271
  title: string;
272
272
  url?: string | null;
273
273
  version: string;
274
+ servers: Array<{ __typename?: "Server"; url: string }>;
274
275
  tags: Array<{
275
276
  __typename?: "SchemaTag";
276
- name?: string | null;
277
+ name: string;
277
278
  description?: string | null;
278
279
  }>;
279
280
  operations: Array<
@@ -310,7 +311,7 @@ export type GetCategoriesQuery = {
310
311
  schema: {
311
312
  __typename?: "Schema";
312
313
  url?: string | null;
313
- tags: Array<{ __typename?: "SchemaTag"; name?: string | null }>;
314
+ tags: Array<{ __typename?: "SchemaTag"; name: string }>;
314
315
  };
315
316
  };
316
317
 
@@ -318,7 +319,6 @@ export type GetOperationsQueryVariables = Exact<{
318
319
  input: Scalars["JSON"]["input"];
319
320
  type: SchemaType;
320
321
  tag?: InputMaybe<Scalars["String"]["input"]>;
321
- untagged?: InputMaybe<Scalars["Boolean"]["input"]>;
322
322
  }>;
323
323
 
324
324
  export type GetOperationsQuery = {
@@ -333,6 +333,16 @@ export type GetOperationsQuery = {
333
333
  summary?: string | null;
334
334
  operationId?: string | null;
335
335
  path: string;
336
+ tags?: Array<{ __typename?: "TagItem"; name: string }> | null;
337
+ }>;
338
+ untagged: Array<{
339
+ __typename?: "OperationItem";
340
+ slug: string;
341
+ deprecated?: boolean | null;
342
+ method: string;
343
+ summary?: string | null;
344
+ operationId?: string | null;
345
+ path: string;
336
346
  }>;
337
347
  };
338
348
  };
@@ -436,6 +446,9 @@ export const ServersQueryDocument = new TypedDocumentString(`
436
446
  export const AllOperationsDocument = new TypedDocumentString(`
437
447
  query AllOperations($input: JSON!, $type: SchemaType!, $tag: String, $untagged: Boolean) {
438
448
  schema(input: $input, type: $type) {
449
+ servers {
450
+ url
451
+ }
439
452
  description
440
453
  summary
441
454
  title
@@ -542,9 +555,20 @@ export const GetCategoriesDocument = new TypedDocumentString(`
542
555
  GetCategoriesQueryVariables
543
556
  >;
544
557
  export const GetOperationsDocument = new TypedDocumentString(`
545
- query GetOperations($input: JSON!, $type: SchemaType!, $tag: String, $untagged: Boolean) {
558
+ query GetOperations($input: JSON!, $type: SchemaType!, $tag: String) {
546
559
  schema(input: $input, type: $type) {
547
- operations(tag: $tag, untagged: $untagged) {
560
+ operations(tag: $tag) {
561
+ slug
562
+ deprecated
563
+ method
564
+ summary
565
+ operationId
566
+ path
567
+ tags {
568
+ name
569
+ }
570
+ }
571
+ untagged: operations(untagged: true) {
548
572
  slug
549
573
  deprecated
550
574
  method