zudoku 0.0.0-eff6f9b → 0.0.0-f1a6fd4

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 (268) hide show
  1. package/README.md +121 -0
  2. package/dist/app/main.js +3 -3
  3. package/dist/app/main.js.map +1 -1
  4. package/dist/cli/dev/handler.js +2 -2
  5. package/dist/cli/dev/handler.js.map +1 -1
  6. package/dist/config/config.d.ts +2 -1
  7. package/dist/config/validators/InputSidebarSchema.d.ts +1 -0
  8. package/dist/config/validators/validate.d.ts +42 -23
  9. package/dist/config/validators/validate.js +6 -2
  10. package/dist/config/validators/validate.js.map +1 -1
  11. package/dist/index.d.ts +2 -1
  12. package/dist/lib/authentication/state.d.ts +16 -0
  13. package/dist/lib/authentication/state.js +5 -0
  14. package/dist/lib/authentication/state.js.map +1 -1
  15. package/dist/lib/components/Banner.js +7 -1
  16. package/dist/lib/components/Banner.js.map +1 -1
  17. package/dist/lib/components/ErrorPage.js +1 -2
  18. package/dist/lib/components/ErrorPage.js.map +1 -1
  19. package/dist/lib/components/Header.js +1 -1
  20. package/dist/lib/components/Header.js.map +1 -1
  21. package/dist/lib/components/InlineCode.d.ts +2 -1
  22. package/dist/lib/components/InlineCode.js +9 -1
  23. package/dist/lib/components/InlineCode.js.map +1 -1
  24. package/dist/lib/components/Layout.js +1 -1
  25. package/dist/lib/components/Layout.js.map +1 -1
  26. package/dist/lib/components/MobileTopNavigation.js +2 -1
  27. package/dist/lib/components/MobileTopNavigation.js.map +1 -1
  28. package/dist/lib/components/Search.js +1 -1
  29. package/dist/lib/components/Search.js.map +1 -1
  30. package/dist/lib/components/SlotletProvider.d.ts +9 -2
  31. package/dist/lib/components/SlotletProvider.js +4 -2
  32. package/dist/lib/components/SlotletProvider.js.map +1 -1
  33. package/dist/lib/components/index.d.ts +2 -1
  34. package/dist/lib/components/index.js.map +1 -1
  35. package/dist/lib/core/DevPortalContext.d.ts +1 -1
  36. package/dist/lib/oas/graphql/index.d.ts +2 -1
  37. package/dist/lib/oas/graphql/index.js +23 -15
  38. package/dist/lib/oas/graphql/index.js.map +1 -1
  39. package/dist/lib/oas/parser/index.d.ts +1 -0
  40. package/dist/lib/oas/parser/index.js.map +1 -1
  41. package/dist/lib/plugins/custom-pages/CustomPage.d.ts +2 -0
  42. package/dist/lib/plugins/custom-pages/CustomPage.js +11 -0
  43. package/dist/lib/plugins/custom-pages/CustomPage.js.map +1 -0
  44. package/dist/lib/plugins/custom-pages/index.d.ts +10 -0
  45. package/dist/lib/plugins/custom-pages/index.js +11 -0
  46. package/dist/lib/plugins/custom-pages/index.js.map +1 -0
  47. package/dist/lib/plugins/markdown/MdxPage.js +2 -2
  48. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  49. package/dist/lib/plugins/openapi/Endpoint.d.ts +1 -3
  50. package/dist/lib/plugins/openapi/Endpoint.js +46 -8
  51. package/dist/lib/plugins/openapi/Endpoint.js.map +1 -1
  52. package/dist/lib/plugins/openapi/OperationList.js +2 -2
  53. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  54. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.d.ts +2 -1
  55. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js +2 -2
  56. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -1
  57. package/dist/lib/plugins/openapi/Route.d.ts +1 -1
  58. package/dist/lib/plugins/openapi/Route.js +1 -1
  59. package/dist/lib/plugins/openapi/Route.js.map +1 -1
  60. package/dist/lib/plugins/openapi/Sidecar.js +15 -4
  61. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  62. package/dist/lib/plugins/openapi/SimpleSelect.d.ts +2 -1
  63. package/dist/lib/plugins/openapi/SimpleSelect.js +1 -1
  64. package/dist/lib/plugins/openapi/SimpleSelect.js.map +1 -1
  65. package/dist/lib/plugins/openapi/client/createMemoryClient.js +1 -1
  66. package/dist/lib/plugins/openapi/client/createMemoryClient.js.map +1 -1
  67. package/dist/lib/plugins/openapi/client/createWorkerClient.js +1 -1
  68. package/dist/lib/plugins/openapi/client/createWorkerClient.js.map +1 -1
  69. package/dist/lib/plugins/openapi/graphql/gql.d.ts +10 -2
  70. package/dist/lib/plugins/openapi/graphql/gql.js +2 -1
  71. package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
  72. package/dist/lib/plugins/openapi/graphql/graphql.d.ts +26 -0
  73. package/dist/lib/plugins/openapi/graphql/graphql.js +87 -0
  74. package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
  75. package/dist/lib/plugins/openapi/index.js +3 -2
  76. package/dist/lib/plugins/openapi/index.js.map +1 -1
  77. package/dist/lib/plugins/openapi/playground/Playground.d.ts +2 -1
  78. package/dist/lib/plugins/openapi/playground/Playground.js +13 -5
  79. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  80. package/dist/lib/plugins/openapi/playground/ResponseTab.js +1 -1
  81. package/dist/lib/plugins/openapi/playground/ResponseTab.js.map +1 -1
  82. package/dist/lib/plugins/openapi/playground/createUrl.js +3 -1
  83. package/dist/lib/plugins/openapi/playground/createUrl.js.map +1 -1
  84. package/dist/lib/ui/Callout.d.ts +36 -35
  85. package/dist/lib/ui/Callout.js.map +1 -1
  86. package/dist/lib/ui/Drawer.d.ts +8 -10
  87. package/dist/lib/ui/Drawer.js.map +1 -1
  88. package/dist/lib/util/useExposedProps.d.ts +2 -0
  89. package/dist/lib/util/useExposedProps.js +8 -0
  90. package/dist/lib/util/useExposedProps.js.map +1 -0
  91. package/dist/vite/config.d.ts +1 -1
  92. package/dist/vite/config.js +21 -15
  93. package/dist/vite/config.js.map +1 -1
  94. package/dist/vite/dev-server.d.ts +6 -1
  95. package/dist/vite/dev-server.js +14 -4
  96. package/dist/vite/dev-server.js.map +1 -1
  97. package/dist/vite/plugin-api.js +4 -7
  98. package/dist/vite/plugin-api.js.map +1 -1
  99. package/dist/vite/plugin-component.js +0 -1
  100. package/dist/vite/plugin-component.js.map +1 -1
  101. package/dist/vite/plugin-config-reload.js +9 -4
  102. package/dist/vite/plugin-config-reload.js.map +1 -1
  103. package/dist/vite/plugin-custom-pages.d.ts +4 -0
  104. package/dist/vite/plugin-custom-pages.js +30 -0
  105. package/dist/vite/plugin-custom-pages.js.map +1 -0
  106. package/dist/vite/plugin-frontmatter.d.ts +2 -0
  107. package/dist/vite/plugin-frontmatter.js +30 -0
  108. package/dist/vite/plugin-frontmatter.js.map +1 -0
  109. package/dist/vite/plugin-mdx.js +3 -0
  110. package/dist/vite/plugin-mdx.js.map +1 -1
  111. package/dist/vite/plugin-sidebar.js +14 -1
  112. package/dist/vite/plugin-sidebar.js.map +1 -1
  113. package/dist/vite/plugin.js +4 -2
  114. package/dist/vite/plugin.js.map +1 -1
  115. package/lib/{AuthenticationPlugin-gtf8JS3V.js → AuthenticationPlugin-CbgJ5SAh.js} +3 -3
  116. package/lib/{AuthenticationPlugin-gtf8JS3V.js.map → AuthenticationPlugin-CbgJ5SAh.js.map} +1 -1
  117. package/lib/{CategoryHeading-XnFqN2lJ.js → CategoryHeading-3Qtp2yZ8.js} +2 -2
  118. package/lib/{CategoryHeading-XnFqN2lJ.js.map → CategoryHeading-3Qtp2yZ8.js.map} +1 -1
  119. package/lib/{DeveloperHint-FBb2uXJe.js → DeveloperHint-BE9DzNhv.js} +2 -2
  120. package/lib/{DeveloperHint-FBb2uXJe.js.map → DeveloperHint-BE9DzNhv.js.map} +1 -1
  121. package/lib/ErrorPage-B0COs372.js +16 -0
  122. package/lib/ErrorPage-B0COs372.js.map +1 -0
  123. package/lib/Input-CHfE_2Qk.js +2229 -0
  124. package/lib/Input-CHfE_2Qk.js.map +1 -0
  125. package/lib/{Markdown-B4aR03g6.js → Markdown-CWI6lU11.js} +1448 -1286
  126. package/lib/Markdown-CWI6lU11.js.map +1 -0
  127. package/lib/{MdxPage-BcftTg5g.js → MdxPage-C3tlrV4j.js} +24 -24
  128. package/lib/MdxPage-C3tlrV4j.js.map +1 -0
  129. package/lib/{OperationList-Da36LrGl.js → OperationList-BO-ES1C5.js} +149 -108
  130. package/lib/OperationList-BO-ES1C5.js.map +1 -0
  131. package/lib/{Route-CWj1ECzh.js → Route-Dq6zv0Pi.js} +3 -4
  132. package/lib/Route-Dq6zv0Pi.js.map +1 -0
  133. package/lib/SidebarBadge-DmI5hT04.js +503 -0
  134. package/lib/SidebarBadge-DmI5hT04.js.map +1 -0
  135. package/lib/{SlotletProvider-DJMaOUDs.js → SlotletProvider-RvaeLR6z.js} +43 -42
  136. package/lib/{SlotletProvider-DJMaOUDs.js.map → SlotletProvider-RvaeLR6z.js.map} +1 -1
  137. package/lib/ZudokuContext-BEmsYQoq.js +1173 -0
  138. package/lib/ZudokuContext-BEmsYQoq.js.map +1 -0
  139. package/lib/assets/{index-B9EWVYfo.js → index-B_Jk_Yzp.js} +968 -938
  140. package/lib/assets/index-B_Jk_Yzp.js.map +1 -0
  141. package/lib/assets/{worker-TYRbYl6N.js → worker-Bf8vjASY.js} +6768 -4410
  142. package/lib/assets/worker-Bf8vjASY.js.map +1 -0
  143. package/lib/{index-BG0g4WW0.js → index-BRCiYFaL.js} +747 -737
  144. package/lib/index-BRCiYFaL.js.map +1 -0
  145. package/lib/{index-CLd8ycZz.js → index-CkwDvuPt.js} +947 -917
  146. package/lib/index-CkwDvuPt.js.map +1 -0
  147. package/lib/index-D06ATMgg.js +2094 -0
  148. package/lib/index-D06ATMgg.js.map +1 -0
  149. package/lib/index-DJqnphbT.js +35 -0
  150. package/lib/index-DJqnphbT.js.map +1 -0
  151. package/lib/{index-DIkaYL-l.js → index-DNfiZTPV.js} +1956 -1676
  152. package/lib/index-DNfiZTPV.js.map +1 -0
  153. package/lib/{index-BoXX7LeD.js → index-Do_BBSIs.js} +597 -544
  154. package/lib/index-Do_BBSIs.js.map +1 -0
  155. package/lib/{index-B_9xr661.js → index-Dvh1BL_e.js} +3 -3
  156. package/lib/{index-B_9xr661.js.map → index-Dvh1BL_e.js.map} +1 -1
  157. package/lib/joinPath-B7kNnUX4.js +8 -0
  158. package/lib/joinPath-B7kNnUX4.js.map +1 -0
  159. package/lib/router-Oe6YmY6B.js +3024 -0
  160. package/lib/router-Oe6YmY6B.js.map +1 -0
  161. package/lib/state-CsuHT8ZO.js +183 -0
  162. package/lib/state-CsuHT8ZO.js.map +1 -0
  163. package/lib/urql-core-KJnLL26g.js +1455 -0
  164. package/lib/urql-core-KJnLL26g.js.map +1 -0
  165. package/lib/useExposedProps-Csw8oAlt.js +9 -0
  166. package/lib/useExposedProps-Csw8oAlt.js.map +1 -0
  167. package/lib/{utils-DtEHoAvg.js → utils-Chi3p5nE.js} +101 -104
  168. package/lib/utils-Chi3p5nE.js.map +1 -0
  169. package/lib/zudoku.auth-auth0.js +1 -1
  170. package/lib/zudoku.auth-clerk.js +2 -2
  171. package/lib/zudoku.auth-openid.js +363 -350
  172. package/lib/zudoku.auth-openid.js.map +1 -1
  173. package/lib/zudoku.components.js +1695 -1621
  174. package/lib/zudoku.components.js.map +1 -1
  175. package/lib/zudoku.openapi-worker.js +4671 -4383
  176. package/lib/zudoku.openapi-worker.js.map +1 -1
  177. package/lib/zudoku.plugin-api-keys.js +7 -7
  178. package/lib/zudoku.plugin-custom-pages.js +21 -0
  179. package/lib/zudoku.plugin-custom-pages.js.map +1 -0
  180. package/lib/zudoku.plugin-markdown.js +1 -1
  181. package/lib/zudoku.plugin-openapi.js +10 -9
  182. package/lib/zudoku.plugin-openapi.js.map +1 -1
  183. package/lib/zudoku.plugin-redirect.js +1 -1
  184. package/package.json +71 -69
  185. package/src/app/main.tsx +3 -3
  186. package/src/lib/authentication/state.ts +17 -0
  187. package/src/lib/components/Banner.tsx +12 -2
  188. package/src/lib/components/ErrorPage.tsx +0 -2
  189. package/src/lib/components/Header.tsx +4 -2
  190. package/src/lib/components/InlineCode.tsx +10 -0
  191. package/src/lib/components/Layout.tsx +2 -1
  192. package/src/lib/components/MobileTopNavigation.tsx +4 -0
  193. package/src/lib/components/Search.tsx +1 -1
  194. package/src/lib/components/SlotletProvider.tsx +27 -4
  195. package/src/lib/components/index.ts +1 -1
  196. package/src/lib/core/DevPortalContext.ts +1 -1
  197. package/src/lib/oas/graphql/index.ts +35 -23
  198. package/src/lib/oas/parser/index.ts +1 -0
  199. package/src/lib/plugins/custom-pages/CustomPage.tsx +18 -0
  200. package/src/lib/plugins/custom-pages/index.tsx +24 -0
  201. package/src/lib/plugins/markdown/MdxPage.tsx +2 -2
  202. package/src/lib/plugins/openapi/Endpoint.tsx +86 -22
  203. package/src/lib/plugins/openapi/OperationList.tsx +4 -2
  204. package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +3 -0
  205. package/src/lib/plugins/openapi/Route.tsx +1 -2
  206. package/src/lib/plugins/openapi/Sidecar.tsx +18 -3
  207. package/src/lib/plugins/openapi/SimpleSelect.tsx +10 -2
  208. package/src/lib/plugins/openapi/client/createMemoryClient.ts +1 -6
  209. package/src/lib/plugins/openapi/client/createWorkerClient.ts +1 -6
  210. package/src/lib/plugins/openapi/graphql/gql.ts +11 -3
  211. package/src/lib/plugins/openapi/graphql/graphql.ts +113 -1
  212. package/src/lib/plugins/openapi/index.tsx +3 -6
  213. package/src/lib/plugins/openapi/playground/Playground.tsx +39 -5
  214. package/src/lib/plugins/openapi/playground/ResponseTab.tsx +0 -1
  215. package/src/lib/plugins/openapi/playground/createUrl.ts +6 -1
  216. package/src/lib/ui/Callout.tsx +7 -6
  217. package/src/lib/ui/Drawer.tsx +38 -36
  218. package/src/lib/util/useExposedProps.tsx +10 -0
  219. package/dist/lib/plugins/custom-page/index.d.ts +0 -8
  220. package/dist/lib/plugins/custom-page/index.js +0 -12
  221. package/dist/lib/plugins/custom-page/index.js.map +0 -1
  222. package/dist/lib/plugins/openapi/playground/Editor.d.ts +0 -1
  223. package/dist/lib/plugins/openapi/playground/Editor.js +0 -5
  224. package/dist/lib/plugins/openapi/playground/Editor.js.map +0 -1
  225. package/dist/lib/plugins/openapi/util/urql.d.ts +0 -7
  226. package/dist/lib/plugins/openapi/util/urql.js +0 -8
  227. package/dist/lib/plugins/openapi/util/urql.js.map +0 -1
  228. package/dist/lib/util/slugify.d.ts +0 -2
  229. package/dist/lib/util/slugify.js +0 -3
  230. package/dist/lib/util/slugify.js.map +0 -1
  231. package/dist/vite/plugin-icons.d.ts +0 -3
  232. package/dist/vite/plugin-icons.js +0 -47
  233. package/dist/vite/plugin-icons.js.map +0 -1
  234. package/lib/ErrorPage-knunPbKI.js +0 -18
  235. package/lib/ErrorPage-knunPbKI.js.map +0 -1
  236. package/lib/Input-B1kkVL1R.js +0 -2198
  237. package/lib/Input-B1kkVL1R.js.map +0 -1
  238. package/lib/Markdown-B4aR03g6.js.map +0 -1
  239. package/lib/MdxPage-BcftTg5g.js.map +0 -1
  240. package/lib/OperationList-Da36LrGl.js.map +0 -1
  241. package/lib/Route-CWj1ECzh.js.map +0 -1
  242. package/lib/SidebarBadge-DdvT2qep.js +0 -498
  243. package/lib/SidebarBadge-DdvT2qep.js.map +0 -1
  244. package/lib/ZudokuContext-em1gHkIY.js +0 -1084
  245. package/lib/ZudokuContext-em1gHkIY.js.map +0 -1
  246. package/lib/_commonjsHelpers-BkfeUUK-.js +0 -29
  247. package/lib/_commonjsHelpers-BkfeUUK-.js.map +0 -1
  248. package/lib/assets/index-B9EWVYfo.js.map +0 -1
  249. package/lib/assets/worker-TYRbYl6N.js.map +0 -1
  250. package/lib/index-BG0g4WW0.js.map +0 -1
  251. package/lib/index-BoXX7LeD.js.map +0 -1
  252. package/lib/index-CLd8ycZz.js.map +0 -1
  253. package/lib/index-DIkaYL-l.js.map +0 -1
  254. package/lib/router-D2p7Olpn.js +0 -2971
  255. package/lib/router-D2p7Olpn.js.map +0 -1
  256. package/lib/slugify-DbLhpSPt.js +0 -28
  257. package/lib/slugify-DbLhpSPt.js.map +0 -1
  258. package/lib/state-BUM4jc0J.js +0 -288
  259. package/lib/state-BUM4jc0J.js.map +0 -1
  260. package/lib/urql-YhcsXYy8.js +0 -1591
  261. package/lib/urql-YhcsXYy8.js.map +0 -1
  262. package/lib/utils-DtEHoAvg.js.map +0 -1
  263. package/lib/zudoku.plugin-custom-page.js +0 -13
  264. package/lib/zudoku.plugin-custom-page.js.map +0 -1
  265. package/src/lib/plugins/custom-page/index.tsx +0 -22
  266. package/src/lib/plugins/openapi/playground/Editor.tsx +0 -4
  267. package/src/lib/plugins/openapi/util/urql.ts +0 -8
  268. package/src/lib/util/slugify.ts +0 -3
@@ -1,10 +1,13 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import SchemaBuilder from "@pothos/core";
3
+ import {
4
+ slugifyWithCounter,
5
+ type CountableSlugify,
6
+ } from "@sindresorhus/slugify";
3
7
  import { GraphQLJSON, GraphQLJSONObject } from "graphql-type-json";
4
8
  import { createYoga, type YogaServerOptions } from "graphql-yoga";
5
9
  import { LRUCache } from "lru-cache";
6
10
  import hashit from "object-hash";
7
- import slugify from "../../util/slugify.js";
8
11
  import {
9
12
  HttpMethods,
10
13
  validate,
@@ -15,6 +18,7 @@ import {
15
18
  type ParameterObject,
16
19
  type PathsObject,
17
20
  type SchemaObject,
21
+ type ServerObject,
18
22
  type TagObject,
19
23
  } from "../parser/index.js";
20
24
 
@@ -35,7 +39,12 @@ type OperationLike = {
35
39
  path: string;
36
40
  method: string;
37
41
  };
38
- export const slugifyOperation = (operation: OperationLike, tag?: string) => {
42
+
43
+ export const createOperationSlug = (
44
+ slugify: CountableSlugify,
45
+ operation: OperationLike,
46
+ tag?: string,
47
+ ) => {
39
48
  const summary =
40
49
  (operation.summary ?? "") +
41
50
  (operation.operationId
@@ -46,13 +55,13 @@ export const slugifyOperation = (operation: OperationLike, tag?: string) => {
46
55
  return slugify(
47
56
  (tag ? tag + "-" : "") +
48
57
  (summary || `${operation.method}-${operation.path}`),
49
- { lower: true, trim: true },
50
58
  );
51
59
  };
52
60
 
53
- const cache = new LRUCache<string, Promise<OpenAPIDocument>>({
61
+ const cache = new LRUCache<string, OpenAPIDocument>({
54
62
  ttl: 60 * 10 * 1000,
55
63
  ttlAutopurge: true,
64
+ fetchMethod: (_key, _oldValue, { context }) => validate(context as string),
56
65
  });
57
66
 
58
67
  const builder = new SchemaBuilder<{
@@ -88,6 +97,8 @@ const getAllTags = (schema: OpenAPIDocument): TagObject[] => {
88
97
  };
89
98
 
90
99
  const getAllOperations = (paths?: PathsObject, tag?: string) => {
100
+ const slugify = slugifyWithCounter();
101
+
91
102
  return Object.entries(paths ?? {}).flatMap(([path, value]) =>
92
103
  HttpMethods.flatMap((method) => {
93
104
  if (!value?.[method]) return [];
@@ -109,21 +120,20 @@ const getAllOperations = (paths?: PathsObject, tag?: string) => {
109
120
  ...operationParameters,
110
121
  ];
111
122
 
123
+ const slugData = {
124
+ summary: operation.summary,
125
+ operationId: operation.operationId,
126
+ path,
127
+ method,
128
+ };
129
+
112
130
  return {
113
131
  ...operation,
114
132
  method,
115
133
  path,
116
134
  parameters,
117
135
  tags: operation.tags ?? [],
118
- slug: slugifyOperation(
119
- {
120
- summary: operation.summary,
121
- operationId: operation.operationId,
122
- path,
123
- method,
124
- },
125
- tag,
126
- ),
136
+ slug: createOperationSlug(slugify, slugData, tag),
127
137
  };
128
138
  }),
129
139
  );
@@ -150,6 +160,13 @@ const SchemaTag = builder.objectRef<TagObject>("SchemaTag").implement({
150
160
  }),
151
161
  });
152
162
 
163
+ const ServerItem = builder.objectRef<ServerObject>("Server").implement({
164
+ fields: (t) => ({
165
+ url: t.exposeString("url"),
166
+ description: t.exposeString("description", { nullable: true }),
167
+ }),
168
+ });
169
+
153
170
  const PathItem = builder
154
171
  .objectRef<{
155
172
  path: string;
@@ -363,6 +380,10 @@ const Schema = builder.objectRef<OpenAPIDocument>("Schema").implement({
363
380
  fields: (t) => ({
364
381
  openapi: t.string({ resolve: (root) => root.openapi }),
365
382
  url: t.string({ resolve: (root) => root.servers?.at(0)?.url ?? "/" }),
383
+ servers: t.field({
384
+ type: [ServerItem],
385
+ resolve: (root) => root.servers ?? [],
386
+ }),
366
387
  title: t.string({ resolve: (root) => root.info.title }),
367
388
  version: t.string({ resolve: (root) => root.info.version }),
368
389
  description: t.string({
@@ -409,16 +430,7 @@ const Schema = builder.objectRef<OpenAPIDocument>("Schema").implement({
409
430
 
410
431
  const loadOpenAPISchema = async (input: NonNullable<unknown>) => {
411
432
  const hash = hashit(input);
412
-
413
- if (cache.has(hash)) {
414
- return cache.get(hash)!;
415
- }
416
-
417
- const schema = validate(input);
418
-
419
- cache.set(hash, schema);
420
-
421
- return schema;
433
+ return await cache.forceFetch(hash, { context: input });
422
434
  };
423
435
 
424
436
  const SchemaSource = builder.enumType("SchemaType", {
@@ -20,6 +20,7 @@ export type TagObject = DeepOmitReference<OpenAPIV3_1.TagObject>;
20
20
  export type ExampleObject = DeepOmitReference<OpenAPIV3_1.ExampleObject>;
21
21
  export type EncodingObject = DeepOmitReference<OpenAPIV3_1.EncodingObject>;
22
22
  export type SchemaObject = DeepOmitReference<OpenAPIV3_1.SchemaObject>;
23
+ export type ServerObject = DeepOmitReference<OpenAPIV3_1.ServerObject>;
23
24
 
24
25
  export const HttpMethods = Object.values(OpenAPIV3.HttpMethods);
25
26
 
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import { ProseClasses } from "../../components/Markdown.js";
3
+ import { cn } from "../../util/cn.js";
4
+ import { useExposedProps } from "../../util/useExposedProps.js";
5
+ import type { CustomPageConfig } from "./index.js";
6
+
7
+ export const CustomPage = ({
8
+ element,
9
+ render,
10
+ prose = true,
11
+ }: Omit<CustomPageConfig, "path">) => {
12
+ const slotletProps = useExposedProps();
13
+ const content = render ? React.createElement(render, slotletProps) : element;
14
+
15
+ return (
16
+ <div className={cn(prose && ProseClasses, "max-w-full")}>{content}</div>
17
+ );
18
+ };
@@ -0,0 +1,24 @@
1
+ import { type ComponentType, type ReactNode } from "react";
2
+ import type { RouteObject } from "react-router-dom";
3
+ import { type ExposedComponentProps } from "../../components/SlotletProvider.js";
4
+ import type { DevPortalPlugin, NavigationPlugin } from "../../core/plugins.js";
5
+ import { CustomPage } from "./CustomPage.js";
6
+
7
+ export type CustomPageConfig = {
8
+ path: string;
9
+ prose?: boolean;
10
+ element?: ReactNode;
11
+ render?: ComponentType<ExposedComponentProps>;
12
+ };
13
+
14
+ export const customPagesPlugin = (
15
+ config: CustomPageConfig[],
16
+ ): DevPortalPlugin & NavigationPlugin => {
17
+ return {
18
+ getRoutes: (): RouteObject[] =>
19
+ config.map(({ path, ...props }) => ({
20
+ path,
21
+ element: <CustomPage {...props} />,
22
+ })),
23
+ };
24
+ };
@@ -1,4 +1,5 @@
1
1
  import { useMDXComponents } from "@mdx-js/react";
2
+ import slugify from "@sindresorhus/slugify";
2
3
  import { Helmet } from "@zudoku/react-helmet-async";
3
4
  import { type PropsWithChildren } from "react";
4
5
  import { Link } from "react-router-dom";
@@ -11,7 +12,6 @@ import {
11
12
  } from "../../components/navigation/utils.js";
12
13
  import type { MdxComponentsType } from "../../util/MdxComponents.js";
13
14
  import { cn } from "../../util/cn.js";
14
- import slugify from "../../util/slugify.js";
15
15
  import { Toc } from "./Toc.js";
16
16
  import { MarkdownPluginDefaultOptions, MDXImport } from "./index.js";
17
17
 
@@ -71,7 +71,7 @@ export const MdxPage = ({
71
71
  <header>
72
72
  {category && <CategoryHeading>{category}</CategoryHeading>}
73
73
  {title && (
74
- <Heading level={1} id={slugify(title, { lower: true })}>
74
+ <Heading level={1} id={slugify(title)}>
75
75
  {title}
76
76
  </Heading>
77
77
  )}
@@ -1,31 +1,95 @@
1
1
  import { CheckIcon, CopyIcon } from "lucide-react";
2
- import { useState } from "react";
2
+ import { useState, useTransition } from "react";
3
+ import { useQuery } from "urql";
4
+ import { useSelectedServerStore } from "../../authentication/state.js";
3
5
  import { InlineCode } from "../../components/InlineCode.js";
6
+ import { Button } from "../../ui/Button.js";
7
+ import { useOasConfig } from "./context.js";
8
+ import { graphql } from "./graphql/index.js";
9
+ import { SimpleSelect } from "./SimpleSelect.js";
4
10
 
5
- export const Endpoint = ({ url }: { url: string }) => {
11
+ const ServersQuery = graphql(/* GraphQL */ `
12
+ query ServersQuery($input: JSON!, $type: SchemaType!) {
13
+ schema(input: $input, type: $type) {
14
+ url
15
+ servers {
16
+ url
17
+ }
18
+ }
19
+ }
20
+ `);
21
+
22
+ const CopyButton = ({ url }: { url: string }) => {
6
23
  const [isCopied, setIsCopied] = useState(false);
7
24
 
8
25
  return (
9
- <div className="my-4 flex items-center justify-end gap-2 text-sm">
10
- <span className="font-medium">Endpoint:</span>
11
- <InlineCode className="p-1.5 flex gap-2.5 items-center text-xs">
12
- {url}
13
- <button
14
- onClick={() => {
15
- void navigator.clipboard.writeText(url).then(() => {
16
- setIsCopied(true);
17
- setTimeout(() => setIsCopied(false), 2000);
18
- });
19
- }}
20
- type="button"
21
- >
22
- {isCopied ? (
23
- <CheckIcon className="text-green-600" size={14} />
24
- ) : (
25
- <CopyIcon size={14} strokeWidth={1.3} />
26
- )}
27
- </button>
28
- </InlineCode>
26
+ <Button
27
+ onClick={() => {
28
+ void navigator.clipboard.writeText(url).then(() => {
29
+ setIsCopied(true);
30
+ setTimeout(() => setIsCopied(false), 2000);
31
+ });
32
+ }}
33
+ variant="ghost"
34
+ size="icon"
35
+ >
36
+ {isCopied ? (
37
+ <CheckIcon className="text-green-600" size={14} />
38
+ ) : (
39
+ <CopyIcon size={14} strokeWidth={1.3} />
40
+ )}
41
+ </Button>
42
+ );
43
+ };
44
+
45
+ const context = { suspense: true } as const;
46
+
47
+ export const Endpoint = () => {
48
+ const [result] = useQuery({
49
+ query: ServersQuery,
50
+ variables: useOasConfig(),
51
+ context,
52
+ });
53
+ const [, startTransition] = useTransition();
54
+ const { selectedServer, setSelectedServer } = useSelectedServerStore();
55
+
56
+ if (!result.data) return null;
57
+
58
+ const { servers } = result.data.schema;
59
+
60
+ if (servers.length === 1) {
61
+ return (
62
+ <div className="flex items-center gap-2">
63
+ <span className="font-medium text-sm">Endpoint:</span>
64
+ <InlineCode className="text-xs px-2 py-1.5" selectOnClick>
65
+ {servers[0].url}
66
+ </InlineCode>
67
+ <CopyButton url={servers[0].url} />
68
+ </div>
69
+ );
70
+ }
71
+
72
+ return (
73
+ <div className="flex flex-wrap items-center gap-2">
74
+ <span className="font-medium text-sm">
75
+ {servers.length > 1 ? "Endpoints" : "Endpoint"}:
76
+ </span>
77
+
78
+ <SimpleSelect
79
+ className="font-mono text-xs bg-border/50 dark:bg-border/70 py-1.5 max-w-[450px] truncate"
80
+ onChange={(e) =>
81
+ startTransition(() => {
82
+ setSelectedServer(e.target.value);
83
+ })
84
+ }
85
+ value={selectedServer ?? result.data.schema.url}
86
+ showChevrons={servers.length > 1}
87
+ options={servers.map((server) => ({
88
+ value: server.url,
89
+ label: server.url,
90
+ }))}
91
+ />
92
+ <CopyButton url={selectedServer ?? result.data.schema.url} />
29
93
  </div>
30
94
  );
31
95
  };
@@ -1,4 +1,5 @@
1
1
  import { ResultOf } from "@graphql-typed-document-node/core";
2
+ import { useQuery } from "urql";
2
3
  import { CategoryHeading } from "../../components/CategoryHeading.js";
3
4
  import { DeveloperHint } from "../../components/DeveloperHint.js";
4
5
  import { ErrorPage } from "../../components/ErrorPage.js";
@@ -12,7 +13,6 @@ import { OperationListItem } from "./OperationListItem.js";
12
13
  import StaggeredRender from "./StaggeredRender.js";
13
14
  import { useOasConfig } from "./context.js";
14
15
  import { graphql } from "./graphql/index.js";
15
- import { useQuery } from "./util/urql.js";
16
16
 
17
17
  export const OperationsFragment = graphql(/* GraphQL */ `
18
18
  fragment OperationsFragment on OperationItem {
@@ -133,7 +133,9 @@ export const OperationList = () => {
133
133
  <Markdown content={result.data.schema.description ?? ""} />
134
134
  </div>
135
135
  <hr />
136
- <Endpoint url={result.data.schema.url} />
136
+ <div className="my-4 flex justify-end">
137
+ <Endpoint />
138
+ </div>
137
139
 
138
140
  {result.data.schema.tags
139
141
  .filter((tag) => tag.operations.length > 0)
@@ -3,9 +3,11 @@ import { PlaygroundDialog } from "./playground/PlaygroundDialog.js";
3
3
 
4
4
  export const PlaygroundDialogWrapper = ({
5
5
  server,
6
+ servers,
6
7
  operation,
7
8
  }: {
8
9
  server: string;
10
+ servers?: string[];
9
11
  operation: OperationListItemResult;
10
12
  }) => {
11
13
  const headers = operation.parameters
@@ -29,6 +31,7 @@ export const PlaygroundDialogWrapper = ({
29
31
  return (
30
32
  <PlaygroundDialog
31
33
  server={server}
34
+ servers={servers}
32
35
  method={operation.method}
33
36
  url={operation.path}
34
37
  headers={headers}
@@ -1,9 +1,8 @@
1
1
  import { Outlet } from "react-router-dom";
2
+ import { Provider, Client as UrqlClient } from "urql";
2
3
  import { OasConfigProvider } from "./context.js";
3
4
  import { OasPluginConfig } from "./interfaces.js";
4
5
 
5
- import { Provider, Client as UrqlClient } from "./util/urql.js";
6
-
7
6
  export function OpenApiRoute({
8
7
  config,
9
8
  client,
@@ -1,6 +1,8 @@
1
1
  import { HTTPSnippet } from "@zudoku/httpsnippet";
2
2
  import { Fragment, useMemo, useTransition } from "react";
3
3
  import { useSearchParams } from "react-router-dom";
4
+ import { useQuery } from "urql";
5
+ import { useSelectedServerStore } from "../../authentication/state.js";
4
6
  import { TextColorMap } from "../../components/navigation/SidebarBadge.js";
5
7
  import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
6
8
  import type { SchemaObject } from "../../oas/parser/index.js";
@@ -15,7 +17,6 @@ import { ResponsesSidecarBox } from "./ResponsesSidecarBox.js";
15
17
  import * as SidecarBox from "./SidecarBox.js";
16
18
  import { SimpleSelect } from "./SimpleSelect.js";
17
19
  import { generateSchemaExample } from "./util/generateSchemaExample.js";
18
- import { useQuery } from "./util/urql.js";
19
20
 
20
21
  const getConverted = (snippet: HTTPSnippet, option: string) => {
21
22
  let converted;
@@ -65,6 +66,9 @@ export const GetServerQuery = graphql(/* GraphQL */ `
65
66
  query getServerQuery($input: JSON!, $type: SchemaType!) {
66
67
  schema(input: $input, type: $type) {
67
68
  url
69
+ servers {
70
+ url
71
+ }
68
72
  }
69
73
  }
70
74
  `);
@@ -135,6 +139,8 @@ export const Sidecar = ({
135
139
  );
136
140
  });
137
141
 
142
+ const { selectedServer } = useSelectedServerStore();
143
+
138
144
  const code = useMemo(() => {
139
145
  const example = requestBodyContent?.[0]?.schema
140
146
  ? generateSchemaExample(requestBodyContent[0].schema as SchemaObject)
@@ -143,7 +149,7 @@ export const Sidecar = ({
143
149
  const snippet = new HTTPSnippet({
144
150
  method: operation.method.toLocaleUpperCase(),
145
151
  url:
146
- (result.data?.schema.url ?? "") +
152
+ (selectedServer ?? result.data?.schema.url ?? "") +
147
153
  operation.path.replaceAll("{", ":").replaceAll("}", ""),
148
154
  postData: example
149
155
  ? {
@@ -160,7 +166,13 @@ export const Sidecar = ({
160
166
  });
161
167
 
162
168
  return getConverted(snippet, selectedLang);
163
- }, [selectedLang, operation.method, operation.path, requestBodyContent]);
169
+ }, [
170
+ selectedServer,
171
+ selectedLang,
172
+ operation.method,
173
+ operation.path,
174
+ requestBodyContent,
175
+ ]);
164
176
 
165
177
  return (
166
178
  <aside className="flex flex-col overflow-hidden sticky top-[--scroll-padding] gap-4">
@@ -175,6 +187,9 @@ export const Sidecar = ({
175
187
  </span>
176
188
  <PlaygroundDialogWrapper
177
189
  server={result.data?.schema.url ?? ""}
190
+ servers={
191
+ result.data?.schema.servers.map((server) => server.url) ?? []
192
+ }
178
193
  operation={operation}
179
194
  />
180
195
  </SidecarBox.Head>
@@ -7,6 +7,7 @@ export const SimpleSelect = ({
7
7
  onChange,
8
8
  className,
9
9
  options,
10
+ showChevrons = true,
10
11
  }: {
11
12
  value: string;
12
13
  onChange: ChangeEventHandler<HTMLSelectElement>;
@@ -15,12 +16,14 @@ export const SimpleSelect = ({
15
16
  value: string;
16
17
  label: string;
17
18
  }[];
19
+ showChevrons?: boolean;
18
20
  }) => (
19
- <div className={cn("grid", className)}>
21
+ <div className="grid">
20
22
  <select
21
23
  className={cn(
22
24
  "row-start-1 col-start-1 border border-input text-foreground px-2 py-1 pe-6",
23
25
  "rounded-md appearance-none bg-zinc-50 hover:bg-white dark:bg-zinc-800 hover:dark:bg-zinc-800/75",
26
+ className,
24
27
  )}
25
28
  value={value}
26
29
  onChange={onChange}
@@ -31,7 +34,12 @@ export const SimpleSelect = ({
31
34
  </option>
32
35
  ))}
33
36
  </select>
34
- <div className="row-start-1 col-start-1 self-center justify-self-end relative end-2 pointer-events-none">
37
+ <div
38
+ className={cn(
39
+ !showChevrons && "hidden",
40
+ "row-start-1 col-start-1 self-center justify-self-end relative end-2 pointer-events-none",
41
+ )}
42
+ >
35
43
  <ChevronsUpDownIcon size={14} />
36
44
  </div>
37
45
  </div>
@@ -1,10 +1,5 @@
1
1
  /* eslint-disable no-console */
2
- import {
3
- cacheExchange,
4
- Client,
5
- fetchExchange,
6
- mapExchange,
7
- } from "../util/urql.js";
2
+ import { cacheExchange, Client, fetchExchange, mapExchange } from "urql";
8
3
  import { createServer } from "./createServer.js";
9
4
  import { CreateClientFunction } from "./interfaces.js";
10
5
 
@@ -1,12 +1,7 @@
1
1
  /* eslint-disable no-console */
2
2
  import { monotonicFactory } from "ulidx";
3
+ import { cacheExchange, Client, fetchExchange, mapExchange } from "urql";
3
4
  import { createWaitForNotify } from "../../../util/createWaitForNotify.js";
4
- import {
5
- cacheExchange,
6
- Client,
7
- fetchExchange,
8
- mapExchange,
9
- } from "../util/urql.js";
10
5
  import { createClient as createMemoryClient } from "./createMemoryClient.js";
11
6
  import { CreateClientFunction } from "./interfaces.js";
12
7
 
@@ -13,11 +13,13 @@ import * as types from "./graphql.js";
13
13
  * Therefore it is highly recommended to use the babel or swc plugin for production.
14
14
  */
15
15
  const documents = {
16
+ "\n query ServersQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n":
17
+ types.ServersQueryDocument,
16
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 schema\n }\n description\n required\n }\n responses {\n statusCode\n links\n description\n content {\n mediaType\n encoding {\n name\n }\n schema\n }\n }\n }\n":
17
19
  types.OperationsFragmentFragmentDoc,
18
20
  "\n query AllOperations($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n description\n title\n url\n version\n tags {\n name\n description\n operations {\n slug\n ...OperationsFragment\n }\n }\n }\n }\n":
19
21
  types.AllOperationsDocument,
20
- "\n query getServerQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n }\n }\n":
22
+ "\n query getServerQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n":
21
23
  types.GetServerQueryDocument,
22
24
  "\n query GetCategories($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n tags {\n __typename\n name\n operations {\n __typename\n slug\n deprecated\n method\n summary\n operationId\n path\n }\n }\n }\n }\n":
23
25
  types.GetCategoriesDocument,
@@ -37,6 +39,12 @@ const documents = {
37
39
  */
38
40
  export function graphql(source: string): unknown;
39
41
 
42
+ /**
43
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
44
+ */
45
+ export function graphql(
46
+ source: "\n query ServersQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n",
47
+ ): (typeof documents)["\n query ServersQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n"];
40
48
  /**
41
49
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
42
50
  */
@@ -53,8 +61,8 @@ export function graphql(
53
61
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
54
62
  */
55
63
  export function graphql(
56
- source: "\n query getServerQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n }\n }\n",
57
- ): (typeof documents)["\n query getServerQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n }\n }\n"];
64
+ source: "\n query getServerQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n",
65
+ ): (typeof documents)["\n query getServerQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n"];
58
66
  /**
59
67
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
60
68
  */