@lukoweb/apitogo 0.1.24 → 0.1.37

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 (477) hide show
  1. package/README.md +92 -0
  2. package/cli.js +13 -13
  3. package/client.d.ts +8 -8
  4. package/dist/cli/cli.js +1117 -624
  5. package/dist/declarations/config/config.d.ts +4 -0
  6. package/dist/declarations/config/loader.d.ts +2 -0
  7. package/dist/declarations/config/resolve-modules.d.ts +34 -0
  8. package/dist/declarations/config/validators/InputNavigationSchema.d.ts +7 -7
  9. package/dist/declarations/config/validators/ModulesSchema.d.ts +417 -0
  10. package/dist/declarations/config/validators/ZudokuConfig.d.ts +123 -4
  11. package/dist/declarations/index.d.ts +3 -1
  12. package/dist/declarations/lib/authentication/components/ProductionUnlockPage.d.ts +1 -0
  13. package/dist/declarations/lib/authentication/hook.d.ts +3 -19
  14. package/dist/declarations/lib/authentication/providers/dev-portal-constants.d.ts +16 -0
  15. package/dist/declarations/lib/authentication/providers/dev-portal-utils.d.ts +9 -0
  16. package/dist/declarations/lib/authentication/providers/dev-portal.d.ts +39 -0
  17. package/dist/declarations/lib/authentication/state.d.ts +12 -26
  18. package/dist/declarations/lib/components/LandingLayout.d.ts +4 -0
  19. package/dist/declarations/lib/components/index.d.ts +2 -0
  20. package/dist/declarations/lib/plugins/api-keys/index.d.ts +2 -1
  21. package/dist/flat-config.d.ts +209 -24
  22. package/docs/components/alert.mdx +130 -0
  23. package/docs/components/badge.mdx +70 -0
  24. package/docs/components/button.mdx +132 -0
  25. package/docs/components/callout.mdx +112 -0
  26. package/docs/components/card.mdx +104 -0
  27. package/docs/components/checkbox.mdx +72 -0
  28. package/docs/components/client-only.mdx +79 -0
  29. package/docs/components/code-tabs.mdx +179 -0
  30. package/docs/components/dialog.mdx +167 -0
  31. package/docs/components/head.mdx +200 -0
  32. package/docs/components/icons.mdx +27 -0
  33. package/docs/components/input.mdx +96 -0
  34. package/docs/components/label.mdx +86 -0
  35. package/docs/components/link.mdx +242 -0
  36. package/docs/components/markdown.mdx +151 -0
  37. package/docs/components/mermaid.mdx +81 -0
  38. package/docs/components/playground.mdx +87 -0
  39. package/docs/components/secret.mdx +79 -0
  40. package/docs/components/select.mdx +176 -0
  41. package/docs/components/shadcn.mdx +73 -0
  42. package/docs/components/slider.mdx +108 -0
  43. package/docs/components/slot.mdx +119 -0
  44. package/docs/components/stepper.mdx +138 -0
  45. package/docs/components/switch.mdx +96 -0
  46. package/docs/components/syntax-highlight.mdx +602 -0
  47. package/docs/components/textarea.mdx +78 -0
  48. package/docs/components/tooltip.mdx +195 -0
  49. package/docs/components/typography.mdx +61 -0
  50. package/docs/concepts/auth-provider-api-identities.md +109 -0
  51. package/docs/configuration/ai-assistants.md +65 -0
  52. package/docs/configuration/api-catalog.md +109 -0
  53. package/docs/configuration/api-reference.md +397 -0
  54. package/docs/configuration/authentication-auth0.md +174 -0
  55. package/docs/configuration/authentication-azure-ad.md +238 -0
  56. package/docs/configuration/authentication-clerk.md +110 -0
  57. package/docs/configuration/authentication-firebase.md +62 -0
  58. package/docs/configuration/authentication-pingfederate.md +136 -0
  59. package/docs/configuration/authentication-supabase.md +226 -0
  60. package/docs/configuration/authentication.md +231 -0
  61. package/docs/configuration/build-configuration.mdx +147 -0
  62. package/docs/configuration/docs.md +282 -0
  63. package/docs/configuration/footer.mdx +214 -0
  64. package/docs/configuration/llms.md +90 -0
  65. package/docs/configuration/make-config.mdx +90 -0
  66. package/docs/configuration/navigation.mdx +422 -0
  67. package/docs/configuration/overview.md +380 -0
  68. package/docs/configuration/protected-routes.md +150 -0
  69. package/docs/configuration/search.md +163 -0
  70. package/docs/configuration/sentry.mdx +44 -0
  71. package/docs/configuration/site.md +124 -0
  72. package/docs/configuration/slots.mdx +125 -0
  73. package/docs/configuration/vite-config.md +18 -0
  74. package/docs/custom-plugins.md +285 -0
  75. package/docs/customization/colors-theme.mdx +276 -0
  76. package/docs/customization/fonts.md +110 -0
  77. package/docs/deploy/apache-nginx.md +41 -0
  78. package/docs/deploy/apitogo.md +139 -0
  79. package/docs/deploy/cloudflare-pages.md +75 -0
  80. package/docs/deploy/direct-upload.md +18 -0
  81. package/docs/deploy/github-pages.md +50 -0
  82. package/docs/deploy/vercel.md +103 -0
  83. package/docs/deployment.md +21 -0
  84. package/docs/extending/events.md +124 -0
  85. package/docs/guides/custom-pages.md +106 -0
  86. package/docs/guides/environment-variables.md +99 -0
  87. package/docs/guides/managing-api-keys-and-identities.md +172 -0
  88. package/docs/guides/mermaid.mdx +70 -0
  89. package/docs/guides/navigation-migration.md +87 -0
  90. package/docs/guides/navigation-rules.mdx +203 -0
  91. package/docs/guides/processors.mdx +234 -0
  92. package/docs/guides/static-files.md +55 -0
  93. package/docs/guides/transforming-examples.md +156 -0
  94. package/docs/guides/using-multiple-apis.md +87 -0
  95. package/docs/markdown/admonitions.md +128 -0
  96. package/docs/markdown/code-blocks.md +196 -0
  97. package/docs/markdown/frontmatter.md +173 -0
  98. package/docs/markdown/mdx.md +68 -0
  99. package/docs/markdown/overview.md +275 -0
  100. package/docs/plugins.md +5 -0
  101. package/docs/quickstart.md +87 -0
  102. package/docs/writing.mdx +72 -0
  103. package/package.json +170 -116
  104. package/src/app/ZuploBuildConfig.ts +36 -36
  105. package/src/app/defaultTheme.css +77 -77
  106. package/src/app/demo-cdn.html +37 -37
  107. package/src/app/demo.html +21 -21
  108. package/src/app/demo.tsx +77 -77
  109. package/src/app/entry.client.tsx +118 -118
  110. package/src/app/entry.server.tsx +160 -160
  111. package/src/app/env.ts +40 -40
  112. package/src/app/font.geist.css +73 -73
  113. package/src/app/main.css +390 -390
  114. package/src/app/main.tsx +8 -1
  115. package/src/app/polyfills.ts +47 -47
  116. package/src/app/processRoutes.tsx +51 -25
  117. package/src/app/sentry.ts +24 -24
  118. package/src/app/standalone.html +23 -23
  119. package/src/app/standalone.tsx +60 -60
  120. package/src/app/utils/createRedirectRoutes.ts +11 -11
  121. package/src/config/config.ts +32 -0
  122. package/src/config/create-plugin.ts +71 -71
  123. package/src/config/file-exists.ts +6 -6
  124. package/src/config/loader.ts +11 -1
  125. package/src/config/resolve-modules.ts +260 -0
  126. package/src/config/validators/BuildSchema.ts +95 -95
  127. package/src/config/validators/HeaderNavigationSchema.ts +41 -41
  128. package/src/config/validators/InputNavigationSchema.test-d.ts +178 -178
  129. package/src/config/validators/InputNavigationSchema.ts +206 -206
  130. package/src/config/validators/ModulesSchema.ts +312 -0
  131. package/src/config/validators/ProtectedRoutesSchema.ts +35 -35
  132. package/src/config/validators/ZudokuConfig.ts +766 -729
  133. package/src/config/validators/auth.ts +3 -3
  134. package/src/config/validators/reason-codes.ts +7 -7
  135. package/src/env.d.ts +11 -11
  136. package/src/index.ts +31 -0
  137. package/src/lib/MissingIcon.tsx +22 -22
  138. package/src/lib/assets/language-icons/bun.tsx +50 -50
  139. package/src/lib/assets/language-icons/c.tsx +31 -31
  140. package/src/lib/assets/language-icons/commonlisp.tsx +22 -22
  141. package/src/lib/assets/language-icons/cpp.tsx +35 -35
  142. package/src/lib/assets/language-icons/csharp.tsx +35 -35
  143. package/src/lib/assets/language-icons/css.tsx +36 -36
  144. package/src/lib/assets/language-icons/dart.tsx +39 -39
  145. package/src/lib/assets/language-icons/elixir.tsx +19 -19
  146. package/src/lib/assets/language-icons/go.tsx +19 -19
  147. package/src/lib/assets/language-icons/graphql.tsx +19 -19
  148. package/src/lib/assets/language-icons/html.tsx +24 -24
  149. package/src/lib/assets/language-icons/java.tsx +35 -35
  150. package/src/lib/assets/language-icons/javascript.tsx +11 -11
  151. package/src/lib/assets/language-icons/json.tsx +19 -19
  152. package/src/lib/assets/language-icons/kotlin.tsx +30 -30
  153. package/src/lib/assets/language-icons/markdown.tsx +19 -19
  154. package/src/lib/assets/language-icons/mdx.tsx +23 -23
  155. package/src/lib/assets/language-icons/npm.tsx +15 -15
  156. package/src/lib/assets/language-icons/objectivec.tsx +23 -23
  157. package/src/lib/assets/language-icons/ocaml.tsx +34 -34
  158. package/src/lib/assets/language-icons/php.tsx +19 -19
  159. package/src/lib/assets/language-icons/pnpm.tsx +32 -32
  160. package/src/lib/assets/language-icons/powershell.tsx +27 -27
  161. package/src/lib/assets/language-icons/python.tsx +23 -23
  162. package/src/lib/assets/language-icons/react.tsx +21 -21
  163. package/src/lib/assets/language-icons/ruby.tsx +19 -19
  164. package/src/lib/assets/language-icons/rust.tsx +19 -19
  165. package/src/lib/assets/language-icons/scala.tsx +19 -19
  166. package/src/lib/assets/language-icons/shell.tsx +19 -19
  167. package/src/lib/assets/language-icons/swift.tsx +19 -19
  168. package/src/lib/assets/language-icons/toml.tsx +23 -23
  169. package/src/lib/assets/language-icons/typescript.tsx +23 -23
  170. package/src/lib/assets/language-icons/xml.tsx +19 -19
  171. package/src/lib/assets/language-icons/yaml.tsx +23 -23
  172. package/src/lib/assets/language-icons/yarn.tsx +17 -17
  173. package/src/lib/assets/language-icons/zig.tsx +32 -32
  174. package/src/lib/auth/issuer.ts +34 -31
  175. package/src/lib/authentication/AuthenticationPlugin.tsx +33 -33
  176. package/src/lib/authentication/authentication.ts +48 -48
  177. package/src/lib/authentication/components/OAuthErrorPage.tsx +197 -197
  178. package/src/lib/authentication/components/ProductionUnlockPage.tsx +27 -0
  179. package/src/lib/authentication/components/SignOut.tsx +15 -15
  180. package/src/lib/authentication/constants.ts +1 -1
  181. package/src/lib/authentication/errors.ts +34 -34
  182. package/src/lib/authentication/hook.ts +141 -139
  183. package/src/lib/authentication/providers/auth0.tsx +91 -91
  184. package/src/lib/authentication/providers/azureb2c.tsx +223 -223
  185. package/src/lib/authentication/providers/dev-portal-constants.ts +21 -0
  186. package/src/lib/authentication/providers/dev-portal-utils.ts +107 -0
  187. package/src/lib/authentication/providers/dev-portal.tsx +270 -0
  188. package/src/lib/authentication/providers/firebase.tsx +486 -486
  189. package/src/lib/authentication/providers/openid.tsx +516 -516
  190. package/src/lib/authentication/providers/supabase.tsx +412 -412
  191. package/src/lib/authentication/providers/util.ts +26 -26
  192. package/src/lib/authentication/state.ts +179 -91
  193. package/src/lib/authentication/ui/icons/Apple.tsx +10 -10
  194. package/src/lib/authentication/ui/icons/Facebook.tsx +15 -15
  195. package/src/lib/authentication/ui/icons/Github.tsx +16 -16
  196. package/src/lib/authentication/ui/icons/Google.tsx +16 -16
  197. package/src/lib/authentication/ui/icons/Microsoft.tsx +12 -12
  198. package/src/lib/authentication/ui/icons/X.tsx +10 -10
  199. package/src/lib/authentication/utils/relativeRedirectUrl.ts +16 -16
  200. package/src/lib/components/AnchorLink.tsx +24 -24
  201. package/src/lib/components/Banner.tsx +52 -52
  202. package/src/lib/components/Bootstrap.tsx +77 -77
  203. package/src/lib/components/BuildCheck.tsx +87 -87
  204. package/src/lib/components/CategoryHeading.tsx +19 -19
  205. package/src/lib/components/ClientOnly.tsx +19 -19
  206. package/src/lib/components/ErrorPage.tsx +26 -26
  207. package/src/lib/components/Footer.tsx +140 -140
  208. package/src/lib/components/Framed.tsx +51 -51
  209. package/src/lib/components/Header.tsx +11 -3
  210. package/src/lib/components/HeaderNavigation.tsx +148 -148
  211. package/src/lib/components/Heading.tsx +84 -84
  212. package/src/lib/components/InlineCode.tsx +15 -15
  213. package/src/lib/components/LandingLayout.tsx +21 -0
  214. package/src/lib/components/LanguageIcon.tsx +199 -199
  215. package/src/lib/components/Markdown.tsx +55 -55
  216. package/src/lib/components/Meta.tsx +46 -46
  217. package/src/lib/components/MobileTopNavigation.tsx +11 -3
  218. package/src/lib/components/NotFoundPage.tsx +37 -37
  219. package/src/lib/components/PageProgress.tsx +28 -28
  220. package/src/lib/components/PagefindSearchMeta.tsx +14 -14
  221. package/src/lib/components/Pagination.tsx +44 -44
  222. package/src/lib/components/PathRenderer.tsx +61 -61
  223. package/src/lib/components/PluginHeads.tsx +17 -17
  224. package/src/lib/components/Search.tsx +70 -70
  225. package/src/lib/components/Slot.tsx +64 -64
  226. package/src/lib/components/Spinner.tsx +5 -5
  227. package/src/lib/components/StatusPage.tsx +96 -96
  228. package/src/lib/components/ThemeSwitch.tsx +49 -49
  229. package/src/lib/components/Typography.tsx +12 -12
  230. package/src/lib/components/Zudoku.tsx +106 -106
  231. package/src/lib/components/cache.ts +27 -27
  232. package/src/lib/components/context/RenderContext.ts +11 -11
  233. package/src/lib/components/context/RouterEventsEmitter.tsx +19 -19
  234. package/src/lib/components/context/SlotProvider.tsx +149 -149
  235. package/src/lib/components/context/ViewportAnchorContext.tsx +137 -137
  236. package/src/lib/components/context/ZudokuContext.ts +140 -140
  237. package/src/lib/components/context/ZudokuProvider.tsx +22 -22
  238. package/src/lib/components/context/ZudokuReactContext.tsx +17 -17
  239. package/src/lib/components/index.ts +2 -0
  240. package/src/lib/components/navigation/Navigation.tsx +51 -51
  241. package/src/lib/components/navigation/NavigationBadge.tsx +48 -48
  242. package/src/lib/components/navigation/NavigationFilterContext.tsx +28 -28
  243. package/src/lib/components/navigation/NavigationWrapper.tsx +49 -49
  244. package/src/lib/components/navigation/Toc.tsx +137 -137
  245. package/src/lib/components/navigation/ZudokuLogo.tsx +25 -25
  246. package/src/lib/components/navigation/ZuploLogo.tsx +14 -14
  247. package/src/lib/components/navigation/utils.ts +234 -234
  248. package/src/lib/core/RouteGuard.tsx +11 -1
  249. package/src/lib/core/ZudokuContext.ts +271 -271
  250. package/src/lib/core/plugins.ts +138 -138
  251. package/src/lib/core/react-query.ts +1 -1
  252. package/src/lib/core/router.ts +1 -1
  253. package/src/lib/core/transform-config.ts +67 -67
  254. package/src/lib/demo/DemoAnnouncement.tsx +17 -17
  255. package/src/lib/errors/ErrorAlert.tsx +28 -28
  256. package/src/lib/errors/RouterError.tsx +18 -18
  257. package/src/lib/errors/ServerError.tsx +5 -5
  258. package/src/lib/errors/TopLevelError.tsx +6 -6
  259. package/src/lib/hooks/index.ts +13 -13
  260. package/src/lib/hooks/useEvent.ts +41 -41
  261. package/src/lib/hooks/useHighlighter.ts +15 -15
  262. package/src/lib/hooks/useHotkey.ts +70 -70
  263. package/src/lib/icons.ts +2 -2
  264. package/src/lib/navigation/applyRules.ts +127 -127
  265. package/src/lib/navigation/pathMatcher.ts +81 -81
  266. package/src/lib/oas/graphql/circular.ts +69 -69
  267. package/src/lib/oas/graphql/constants.ts +2 -2
  268. package/src/lib/oas/graphql/index.ts +791 -791
  269. package/src/lib/oas/parser/dereference/index.ts +84 -84
  270. package/src/lib/oas/parser/dereference/resolveRef.ts +32 -32
  271. package/src/lib/oas/parser/index.ts +129 -129
  272. package/src/lib/oas/parser/schemas/v3.0.json +1489 -1489
  273. package/src/lib/oas/parser/schemas/v3.1.json +1298 -1298
  274. package/src/lib/oas/parser/upgrade/index.ts +174 -174
  275. package/src/lib/plugins/api-catalog/index.tsx +119 -119
  276. package/src/lib/plugins/api-keys/ProtectedRoute.tsx +33 -33
  277. package/src/lib/plugins/api-keys/index.tsx +274 -272
  278. package/src/lib/plugins/api-keys/settings/ApiKeyList.tsx +67 -67
  279. package/src/lib/plugins/custom-pages/index.tsx +32 -32
  280. package/src/lib/plugins/markdown/assets/ChatGPTLogo.tsx +11 -11
  281. package/src/lib/plugins/markdown/assets/ClaudeLogo.tsx +19 -19
  282. package/src/lib/plugins/markdown/index.tsx +62 -62
  283. package/src/lib/plugins/openapi/ColorizedParam.tsx +115 -115
  284. package/src/lib/plugins/openapi/Endpoint.tsx +85 -85
  285. package/src/lib/plugins/openapi/MCPEndpoint.tsx +273 -273
  286. package/src/lib/plugins/openapi/OasProvider.tsx +74 -74
  287. package/src/lib/plugins/openapi/OperationList.tsx +275 -275
  288. package/src/lib/plugins/openapi/ParamInfos.tsx +96 -96
  289. package/src/lib/plugins/openapi/ParameterList.tsx +53 -53
  290. package/src/lib/plugins/openapi/ParameterListItem.tsx +161 -161
  291. package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +67 -67
  292. package/src/lib/plugins/openapi/SidecarBox.tsx +57 -57
  293. package/src/lib/plugins/openapi/SimpleSelect.tsx +46 -46
  294. package/src/lib/plugins/openapi/client/GraphQLClient.tsx +73 -73
  295. package/src/lib/plugins/openapi/client/GraphQLContext.tsx +16 -16
  296. package/src/lib/plugins/openapi/client/createServer.ts +35 -35
  297. package/src/lib/plugins/openapi/client/useCreateQuery.ts +44 -44
  298. package/src/lib/plugins/openapi/components/ConstValue.tsx +24 -24
  299. package/src/lib/plugins/openapi/components/NonHighlightedCode.tsx +22 -22
  300. package/src/lib/plugins/openapi/components/SelectOnClick.tsx +29 -29
  301. package/src/lib/plugins/openapi/context.tsx +16 -16
  302. package/src/lib/plugins/openapi/graphql/fragment-masking.ts +104 -104
  303. package/src/lib/plugins/openapi/graphql/gql.ts +95 -95
  304. package/src/lib/plugins/openapi/graphql/graphql.ts +835 -835
  305. package/src/lib/plugins/openapi/graphql/index.ts +2 -2
  306. package/src/lib/plugins/openapi/index.tsx +221 -221
  307. package/src/lib/plugins/openapi/interfaces.ts +100 -100
  308. package/src/lib/plugins/openapi/playground/Headers.tsx +6 -6
  309. package/src/lib/plugins/openapi/playground/PathParams.tsx +53 -53
  310. package/src/lib/plugins/openapi/playground/Spinner.tsx +87 -87
  311. package/src/lib/plugins/openapi/playground/createUrl.ts +25 -25
  312. package/src/lib/plugins/openapi/playground/fileUtils.ts +36 -36
  313. package/src/lib/plugins/openapi/playground/rememberedIdentity.ts +26 -26
  314. package/src/lib/plugins/openapi/playground/request-panel/UrlPath.tsx +31 -31
  315. package/src/lib/plugins/openapi/playground/request-panel/UrlQueryParams.tsx +29 -29
  316. package/src/lib/plugins/openapi/playground/request-panel/useKeyValueFieldManager.ts +340 -340
  317. package/src/lib/plugins/openapi/playground/result-panel/ResponseStatusBar.tsx +117 -117
  318. package/src/lib/plugins/openapi/playground/result-panel/convertToTypes.ts +36 -36
  319. package/src/lib/plugins/openapi/playground/serializeQueryParams.ts +125 -125
  320. package/src/lib/plugins/openapi/playground/useRememberSkipLoginDialog.tsx +23 -23
  321. package/src/lib/plugins/openapi/processors/removeExtensions.ts +29 -29
  322. package/src/lib/plugins/openapi/processors/removeParameters.ts +103 -103
  323. package/src/lib/plugins/openapi/processors/removePaths.ts +57 -57
  324. package/src/lib/plugins/openapi/processors/traverse.ts +1 -1
  325. package/src/lib/plugins/openapi/schema/SchemaExampleAndDefault.tsx +39 -39
  326. package/src/lib/plugins/openapi/schema/UnionView.tsx +132 -132
  327. package/src/lib/plugins/openapi/schema/union-helpers.ts +123 -123
  328. package/src/lib/plugins/openapi/schema/utils.ts +49 -49
  329. package/src/lib/plugins/openapi/state.ts +36 -36
  330. package/src/lib/plugins/openapi/util/buildTagCategories.ts +54 -54
  331. package/src/lib/plugins/openapi/util/createHttpSnippet.ts +135 -135
  332. package/src/lib/plugins/openapi/util/createNavigationCategory.tsx +39 -39
  333. package/src/lib/plugins/openapi/util/generateSchemaExample.ts +191 -191
  334. package/src/lib/plugins/openapi/util/getRoutes.tsx +265 -265
  335. package/src/lib/plugins/openapi/util/methodColorMap.tsx +11 -11
  336. package/src/lib/plugins/openapi/util/methodToColor.ts +27 -27
  337. package/src/lib/plugins/openapi/util/sanitizeMarkdownForMetatag.tsx +32 -32
  338. package/src/lib/plugins/openapi/util/useWarmupSchema.ts +23 -23
  339. package/src/lib/plugins/search-inkeep/index.tsx +108 -108
  340. package/src/lib/plugins/search-inkeep/inkeep.ts +24 -24
  341. package/src/lib/plugins/search-pagefind/get-results.tsx +74 -74
  342. package/src/lib/plugins/search-pagefind/index.tsx +21 -21
  343. package/src/lib/plugins/search-pagefind/types.ts +118 -118
  344. package/src/lib/shiki-constants.ts +24 -24
  345. package/src/lib/testing/index.tsx +146 -146
  346. package/src/lib/ui/Accordion.tsx +55 -55
  347. package/src/lib/ui/Alert.tsx +84 -84
  348. package/src/lib/ui/AlertDialog.tsx +195 -195
  349. package/src/lib/ui/AspectRatio.tsx +5 -5
  350. package/src/lib/ui/Badge.tsx +48 -48
  351. package/src/lib/ui/Breadcrumb.tsx +115 -115
  352. package/src/lib/ui/Button.tsx +65 -65
  353. package/src/lib/ui/ButtonGroup.tsx +80 -80
  354. package/src/lib/ui/Callout.tsx +113 -113
  355. package/src/lib/ui/Card.tsx +95 -95
  356. package/src/lib/ui/Carousel.tsx +259 -259
  357. package/src/lib/ui/Checkbox.tsx +28 -28
  358. package/src/lib/ui/CodeBlock.tsx +92 -92
  359. package/src/lib/ui/CodeTabPanel.tsx +13 -13
  360. package/src/lib/ui/CodeTabs.tsx +161 -161
  361. package/src/lib/ui/Collapsible.tsx +31 -31
  362. package/src/lib/ui/Dialog.tsx +138 -138
  363. package/src/lib/ui/DismissibleAlert.tsx +74 -74
  364. package/src/lib/ui/Drawer.tsx +121 -121
  365. package/src/lib/ui/DropdownMenu.tsx +254 -254
  366. package/src/lib/ui/EmbeddedCodeBlock.tsx +99 -99
  367. package/src/lib/ui/Form.tsx +176 -176
  368. package/src/lib/ui/Frame.tsx +81 -81
  369. package/src/lib/ui/HoverCard.tsx +26 -26
  370. package/src/lib/ui/Input.tsx +23 -23
  371. package/src/lib/ui/InputGroup.tsx +168 -168
  372. package/src/lib/ui/Item.tsx +213 -213
  373. package/src/lib/ui/Kbd.tsx +28 -28
  374. package/src/lib/ui/Label.tsx +23 -23
  375. package/src/lib/ui/NativeSelect.tsx +48 -48
  376. package/src/lib/ui/NavigationMenu.tsx +169 -169
  377. package/src/lib/ui/Pagination.tsx +115 -115
  378. package/src/lib/ui/Popover.tsx +28 -28
  379. package/src/lib/ui/Progress.tsx +25 -25
  380. package/src/lib/ui/RadioGroup.tsx +41 -41
  381. package/src/lib/ui/ReactComponentDoc.tsx +68 -68
  382. package/src/lib/ui/ScrollArea.tsx +45 -45
  383. package/src/lib/ui/Secret.tsx +133 -133
  384. package/src/lib/ui/Select.tsx +184 -184
  385. package/src/lib/ui/Separator.tsx +25 -25
  386. package/src/lib/ui/Skeleton.tsx +15 -15
  387. package/src/lib/ui/Slider.tsx +25 -25
  388. package/src/lib/ui/Stepper.tsx +8 -8
  389. package/src/lib/ui/Switch.tsx +26 -26
  390. package/src/lib/ui/SyntaxHighlight.tsx +54 -54
  391. package/src/lib/ui/Tabs.tsx +52 -52
  392. package/src/lib/ui/Textarea.tsx +22 -22
  393. package/src/lib/ui/Toggle.tsx +42 -42
  394. package/src/lib/ui/ToggleGroup.tsx +58 -58
  395. package/src/lib/ui/Tooltip.tsx +65 -65
  396. package/src/lib/ui/Value.tsx +42 -42
  397. package/src/lib/ui/util.tsx +8 -8
  398. package/src/lib/util/cn.ts +6 -6
  399. package/src/lib/util/createVariantComponent.tsx +61 -61
  400. package/src/lib/util/ensureArray.ts +3 -3
  401. package/src/lib/util/flattenAllOf.ts +72 -72
  402. package/src/lib/util/flattenAllOfProcessor.ts +74 -74
  403. package/src/lib/util/hastToJsx.tsx +67 -67
  404. package/src/lib/util/humanFileSize.ts +15 -15
  405. package/src/lib/util/invariant.ts +52 -52
  406. package/src/lib/util/joinUrl.ts +60 -60
  407. package/src/lib/util/logInit.ts +9 -9
  408. package/src/lib/util/objectEntries.ts +5 -5
  409. package/src/lib/util/os.ts +18 -18
  410. package/src/lib/util/pastellize.ts +25 -25
  411. package/src/lib/util/problemJson.ts +63 -63
  412. package/src/lib/util/readFrontmatter.ts +14 -14
  413. package/src/lib/util/renderIf.ts +4 -4
  414. package/src/lib/util/scrollIntoViewIfNeeded.ts +18 -18
  415. package/src/lib/util/slugify.ts +21 -21
  416. package/src/lib/util/syncZustandState.ts +22 -22
  417. package/src/lib/util/traverse.ts +70 -70
  418. package/src/lib/util/types.ts +7 -7
  419. package/src/lib/util/url.ts +18 -18
  420. package/src/lib/util/useCopyToClipboard.ts +17 -17
  421. package/src/lib/util/useExposedProps.tsx +27 -27
  422. package/src/lib/util/useIsomorphicLayoutEffect.ts +6 -6
  423. package/src/lib/util/useLatest.ts +18 -18
  424. package/src/lib/util/useOnScreen.ts +34 -34
  425. package/src/lib/util/useScrollToAnchor.ts +70 -70
  426. package/src/lib/util/useScrollToTop.ts +18 -18
  427. package/src/types.d.ts +57 -53
  428. package/src/vite/api/SchemaManager.ts +371 -371
  429. package/src/vite/api/schema-codegen.ts +163 -163
  430. package/src/vite/config.ts +5 -1
  431. package/src/vite/css/collect.ts +42 -42
  432. package/src/vite/css/plugin.ts +105 -105
  433. package/src/vite/debug.ts +18 -18
  434. package/src/vite/dev-portal-env.ts +93 -0
  435. package/src/vite/html.ts +54 -54
  436. package/src/vite/index.ts +2 -2
  437. package/src/vite/mdx/rehype-extract-toc-with-jsx-export.ts +19 -19
  438. package/src/vite/mdx/rehype-extract-toc-with-jsx.ts +70 -70
  439. package/src/vite/mdx/remark-code-tabs.ts +47 -47
  440. package/src/vite/mdx/remark-inject-filepath.ts +13 -13
  441. package/src/vite/mdx/remark-last-modified.ts +103 -103
  442. package/src/vite/mdx/remark-link-rewrite.ts +23 -23
  443. package/src/vite/mdx/remark-normalize-image-url.ts +15 -15
  444. package/src/vite/mdx/remark-static-generation.ts +149 -149
  445. package/src/vite/mdx/utils.ts +32 -32
  446. package/src/vite/output.ts +261 -261
  447. package/src/vite/package-root.ts +12 -12
  448. package/src/vite/pagefind-dev-index.ts +100 -100
  449. package/src/vite/plugin-api-keys.ts +6 -1
  450. package/src/vite/plugin-api.ts +4 -1
  451. package/src/vite/plugin-auth.ts +5 -1
  452. package/src/vite/plugin-component.ts +1 -1
  453. package/src/vite/plugin-config-reload.ts +49 -49
  454. package/src/vite/plugin-doc-metadata.ts +45 -45
  455. package/src/vite/plugin-docs.ts +4 -1
  456. package/src/vite/plugin-metadata.ts +30 -30
  457. package/src/vite/plugin-modules.ts +144 -0
  458. package/src/vite/plugin-shiki-register.ts +97 -97
  459. package/src/vite/plugin-theme.ts +399 -399
  460. package/src/vite/plugin.ts +2 -0
  461. package/src/vite/prerender/utils.ts +76 -76
  462. package/src/vite/prerender/worker.ts +118 -118
  463. package/src/vite/reporter.ts +34 -34
  464. package/src/vite/shadcn-registry.ts +48 -48
  465. package/src/vite/sitemap.ts +78 -78
  466. package/src/vite/ssr-templates/cloudflare.ts +19 -19
  467. package/src/vite/ssr-templates/node.ts +25 -25
  468. package/src/vite/ssr-templates/vercel.ts +22 -22
  469. package/src/vite/zuplo.ts +30 -30
  470. package/src/vite-env.d.ts +6 -6
  471. package/src/zuplo/enrich-with-zuplo-mcp.ts +168 -168
  472. package/src/zuplo/enrich-with-zuplo.ts +256 -256
  473. package/src/zuplo/policy-types.ts +46 -46
  474. package/src/zuplo/with-zuplo-processors.ts +36 -36
  475. package/src/zuplo/with-zuplo.ts +14 -14
  476. package/LICENSE.md +0 -18
  477. package/dist/declarations/lib/components/navigation/ZudokuLogo.d.ts +0 -6
@@ -1,791 +1,791 @@
1
- // biome-ignore-all lint/suspicious/noExplicitAny: Allow any type
2
- import SchemaBuilder from "@pothos/core";
3
- import { GraphQLJSON, GraphQLJSONObject } from "graphql-type-json";
4
- import { createYoga, type YogaServerOptions } from "graphql-yoga";
5
- import {
6
- type CountableSlugify,
7
- slugifyWithCounter,
8
- } from "../../util/slugify.js";
9
- import { HttpMethods, validate } from "../parser/index.js";
10
- import type {
11
- ContactObject,
12
- EncodingObject,
13
- ExampleObject,
14
- ExternalDocumentationObject,
15
- LicenseObject,
16
- OpenAPIDocument,
17
- OperationObject,
18
- ParameterObject,
19
- PathsObject,
20
- SchemaObject,
21
- ServerObject,
22
- TagObject,
23
- } from "../parser/index.js";
24
- import { GraphQLJSONSchema } from "./circular.js";
25
-
26
- export type {
27
- EncodingObject,
28
- ExampleObject,
29
- OpenAPIDocument,
30
- OperationObject,
31
- ParameterObject,
32
- PathsObject,
33
- SchemaObject,
34
- TagObject,
35
- };
36
-
37
- type OperationLike = {
38
- summary?: string | null;
39
- operationId?: string | null;
40
- path: string;
41
- method: string;
42
- };
43
-
44
- export const createOperationSlug = (
45
- slugify: CountableSlugify,
46
- operation: OperationLike,
47
- ) => {
48
- const summary =
49
- operation.summary ||
50
- operation.operationId ||
51
- `${operation.method}-${operation.path}`;
52
-
53
- return slugify(summary);
54
- };
55
-
56
- export type SchemaImport = () => Promise<{ schema: OpenAPIDocument }>;
57
-
58
- export type SchemaImports = Record<string, SchemaImport>;
59
-
60
- type Slugs = {
61
- operations: Record<string, string>;
62
- tags: Record<string, string>;
63
- };
64
-
65
- type Context = {
66
- schema: OpenAPIDocument;
67
- operations: GraphQLOperationObject[];
68
- schemaImports?: SchemaImports;
69
- tags: ReturnType<typeof getAllTags>;
70
- slugs: Slugs;
71
- };
72
-
73
- const builder = new SchemaBuilder<{
74
- DefaultFieldNullability: false;
75
- Scalars: {
76
- JSON: any;
77
- JSONObject: any;
78
- JSONSchema: any;
79
- };
80
- Context: Context;
81
- }>({
82
- defaultFieldNullability: false,
83
- });
84
-
85
- type GraphQLOperationObject = OperationObject & {
86
- path: string;
87
- method: string;
88
- slug?: string;
89
- parentTag?: string;
90
- };
91
-
92
- const JSONScalar = builder.addScalarType("JSON", GraphQLJSON);
93
- const JSONObjectScalar = builder.addScalarType("JSONObject", GraphQLJSONObject);
94
- const JSONSchemaScalar = builder.addScalarType("JSONSchema", GraphQLJSONSchema);
95
-
96
- const resolveExtensions = (obj: Record<string, any>) =>
97
- Object.fromEntries(
98
- Object.entries(obj).filter(([key]) => key.startsWith("x-")),
99
- );
100
-
101
- export const getAllTags = (
102
- schema: OpenAPIDocument,
103
- ): Array<Omit<TagObject, "name"> & { name?: string; slug?: string }> => {
104
- const rootTags = schema.tags ?? [];
105
- const operations = Object.values(schema.paths ?? {}).flatMap((path) =>
106
- HttpMethods.map((k) => path?.[k]).filter((op) => op != null),
107
- );
108
-
109
- const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
110
-
111
- const hasUntaggedOperations = operations.some(
112
- (op) => (op.tags?.length ?? 0) === 0,
113
- );
114
-
115
- const slugify = slugifyWithCounter();
116
- const slugs = Object.fromEntries(
117
- Array.from(
118
- new Set([...Array.from(operationTags), ...rootTags.map((t) => t.name)]),
119
- ).map((tag) => [tag, slugify(tag)]),
120
- );
121
-
122
- const result = [
123
- // Keep root tags that are actually used in operations
124
- ...rootTags
125
- .filter((tag) => operationTags.has(tag.name))
126
- .map((tag) => ({ ...tag, slug: slugs[tag.name] })),
127
- // Add tags found in operations but not defined in root tags
128
- ...Array.from(operationTags)
129
- .filter((tag) => !rootTags.some((rt) => rt.name === tag))
130
- .map((tag) => ({ name: tag, slug: slugs[tag] })),
131
- // Add untagged operations if there are any
132
- ...(hasUntaggedOperations ? [{ name: undefined, slug: undefined }] : []),
133
- ];
134
-
135
- // Apply x-tagGroups ordering if present
136
- const tagGroups = (schema as any)["x-tagGroups"] ?? [];
137
-
138
- if (tagGroups.length === 0) return result;
139
-
140
- const groupOrder = tagGroups.flatMap((group: any) => group.tags);
141
- return result.sort((a, b) => {
142
- if (!a.name || !b.name) return 0; // Keep untagged at end
143
- const indexA = groupOrder.indexOf(a.name);
144
- const indexB = groupOrder.indexOf(b.name);
145
- if (indexA === -1 && indexB === -1) return 0; // Keep original order for ungrouped
146
- if (indexA === -1) return 1; // Ungrouped after grouped
147
- if (indexB === -1) return -1; // Grouped before ungrouped
148
- return indexA - indexB;
149
- });
150
- };
151
-
152
- export const getAllSlugs = (
153
- ops: GraphQLOperationObject[],
154
- schemaTags: TagObject[] = [],
155
- ): Slugs => {
156
- const operationSlugify = slugifyWithCounter();
157
- const tagSlugify = slugifyWithCounter();
158
-
159
- const tags = Array.from(
160
- new Set([
161
- ...ops.flatMap((op) => op.tags ?? []),
162
- ...schemaTags.map((tag) => tag.name),
163
- ]),
164
- );
165
-
166
- return {
167
- operations: Object.fromEntries(
168
- ops.map((op) => [
169
- getOperationSlugKey(op),
170
- createOperationSlug(operationSlugify, op),
171
- ]),
172
- ),
173
- tags: Object.fromEntries(tags.map((tag) => [tag, tagSlugify(tag)])),
174
- };
175
- };
176
-
177
- const getOperationSlugKey = (op: GraphQLOperationObject) =>
178
- [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
179
-
180
- export const getAllOperations = (
181
- paths?: PathsObject,
182
- ): GraphQLOperationObject[] => {
183
- const operations = Object.entries(paths ?? {}).flatMap(([path, value]) =>
184
- HttpMethods.flatMap((method) => {
185
- if (!value?.[method]) return [];
186
-
187
- const operation = value[method] as OperationObject;
188
- const pathParameters = value.parameters ?? [];
189
- const operationParameters = operation.parameters ?? [];
190
-
191
- // parameters are inherited from the parent path object,
192
- // but can be overridden by their `name` and `in` location
193
- const parameters = [
194
- ...pathParameters.filter(
195
- // remove path parameters that are already defined in the operation
196
- (pp) =>
197
- !operationParameters.some(
198
- (op) => op.name === pp.name && op.in === pp.in,
199
- ),
200
- ),
201
- ...operationParameters,
202
- ];
203
-
204
- // servers follow the OpenAPI 3.0 resolution hierarchy:
205
- // 1. Operation-level servers (highest precedence)
206
- // 2. Path-level servers
207
- // 3. Global servers (handled at query time, lowest precedence)
208
- const pathServers = value.servers ?? [];
209
- const operationServers = operation.servers ?? [];
210
- const servers =
211
- operationServers.length > 0 ? operationServers : pathServers;
212
-
213
- return {
214
- ...operation,
215
- method,
216
- path,
217
- parameters,
218
- servers,
219
- tags: operation.tags ?? [],
220
- } satisfies GraphQLOperationObject;
221
- }),
222
- );
223
-
224
- return operations;
225
- };
226
-
227
- const SchemaContact = builder
228
- .objectRef<ContactObject>("SchemaContact")
229
- .implement({
230
- fields: (t) => ({
231
- name: t.exposeString("name", { nullable: true }),
232
- url: t.exposeString("url", { nullable: true }),
233
- email: t.exposeString("email", { nullable: true }),
234
- }),
235
- });
236
-
237
- const SchemaLicense = builder
238
- .objectRef<LicenseObject>("SchemaLicense")
239
- .implement({
240
- fields: (t) => ({
241
- name: t.exposeString("name"),
242
- url: t.exposeString("url", { nullable: true }),
243
- identifier: t.exposeString("identifier", { nullable: true }),
244
- }),
245
- });
246
-
247
- const SchemaExternalDocs = builder
248
- .objectRef<ExternalDocumentationObject>("SchemaExternalDocs")
249
- .implement({
250
- fields: (t) => ({
251
- description: t.exposeString("description", { nullable: true }),
252
- url: t.exposeString("url"),
253
- }),
254
- });
255
-
256
- const SchemaTag = builder.objectRef<
257
- Omit<TagObject, "name"> & { name?: string; slug?: string }
258
- >("SchemaTag");
259
-
260
- SchemaTag.implement({
261
- fields: (t) => ({
262
- name: t.exposeString("name", { nullable: true }),
263
- slug: t.exposeString("slug", { nullable: true }),
264
- isUntagged: t.field({ type: "Boolean", resolve: (parent) => !parent.name }),
265
- description: t.exposeString("description", { nullable: true }),
266
- operations: t.field({
267
- type: [OperationItem],
268
- resolve: (parent, _args, ctx) => {
269
- const rootTags = ctx.tags.map((tag) => tag.name);
270
-
271
- return ctx.operations
272
- .filter((item) =>
273
- parent.name
274
- ? item.tags?.includes(parent.name)
275
- : item.tags?.length === 0 ||
276
- // If none of the tags are present in the root tags, then show them here
277
- item.tags?.every((tag) => !rootTags.includes(tag)),
278
- )
279
- .map((item) => ({ ...item, parentTag: parent.name }));
280
- },
281
- }),
282
- prev: t.field({
283
- type: SchemaTag,
284
- nullable: true,
285
- resolve: (parent, _args, ctx) => {
286
- const index = ctx.tags.findIndex((tag) => tag.slug === parent.slug);
287
- return ctx.tags[index - 1];
288
- },
289
- }),
290
- next: t.field({
291
- type: SchemaTag,
292
- nullable: true,
293
- resolve: (parent, _args, ctx) => {
294
- const index = ctx.tags.findIndex((tag) => tag.slug === parent.slug);
295
- return ctx.tags[index + 1];
296
- },
297
- }),
298
- extensions: t.field({
299
- type: JSONObjectScalar,
300
- resolve: (parent) => resolveExtensions(parent),
301
- nullable: true,
302
- }),
303
- }),
304
- });
305
-
306
- const ServerItem = builder.objectRef<ServerObject>("Server").implement({
307
- fields: (t) => ({
308
- url: t.exposeString("url"),
309
- description: t.exposeString("description", { nullable: true }),
310
- }),
311
- });
312
-
313
- type WebhookData = {
314
- name: string;
315
- method: string;
316
- summary?: string;
317
- description?: string;
318
- operationId?: string;
319
- };
320
-
321
- const WebhookItem = builder.objectRef<WebhookData>("WebhookItem").implement({
322
- fields: (t) => ({
323
- name: t.exposeString("name"),
324
- method: t.exposeString("method"),
325
- summary: t.exposeString("summary", { nullable: true }),
326
- description: t.exposeString("description", { nullable: true }),
327
- operationId: t.exposeString("operationId", { nullable: true }),
328
- }),
329
- });
330
-
331
- const resolveWebhooks = (schema: OpenAPIDocument): WebhookData[] => {
332
- const webhooks = (schema as any).webhooks ?? {};
333
- return Object.entries(webhooks).flatMap(([name, pathItem]: [string, any]) =>
334
- HttpMethods.flatMap((method) => {
335
- const op = pathItem?.[method];
336
- if (!op) return [];
337
- return {
338
- name,
339
- method: method.toUpperCase(),
340
- summary: op.summary,
341
- description: op.description,
342
- operationId: op.operationId,
343
- };
344
- }),
345
- );
346
- };
347
-
348
- const PathItem = builder
349
- .objectRef<{
350
- path: string;
351
- methods: typeof HttpMethods;
352
- }>("PathItem")
353
- .implement({
354
- fields: (t) => ({
355
- path: t.exposeString("path"),
356
- methods: t.exposeStringList("methods"),
357
- }),
358
- });
359
-
360
- const TagItem = builder.objectRef<TagObject>("TagItem").implement({
361
- fields: (t) => ({
362
- name: t.exposeString("name"),
363
- description: t.exposeString("description", { nullable: true }),
364
- extensions: t.field({
365
- type: JSONObjectScalar,
366
- resolve: (parent) => resolveExtensions(parent),
367
- nullable: true,
368
- }),
369
- }),
370
- });
371
-
372
- const EncodingItem = builder
373
- .objectRef<EncodingObject & { name: string }>("EncodingItem")
374
- .implement({
375
- fields: (t) => ({
376
- name: t.exposeString("name"),
377
- contentType: t.exposeString("contentType", { nullable: true }),
378
- headers: t.expose("headers", { type: JSONObjectScalar, nullable: true }),
379
- style: t.exposeString("style", { nullable: true }),
380
- explode: t.exposeBoolean("explode", { nullable: true }),
381
- allowReserved: t.exposeBoolean("allowReserved", { nullable: true }),
382
- }),
383
- });
384
-
385
- const ExampleItem = builder
386
- .objectRef<ExampleObject & { name: string }>("ExampleItem")
387
- .implement({
388
- fields: (t) => ({
389
- name: t.exposeString("name"),
390
- summary: t.exposeString("summary", { nullable: true }),
391
- description: t.exposeString("description", { nullable: true }),
392
- value: t.expose("value", { type: JSONScalar, nullable: true }),
393
- externalValue: t.exposeString("externalValue", { nullable: true }),
394
- }),
395
- });
396
-
397
- const ParameterIn = builder.enumType("ParameterIn", {
398
- values: ["query", "header", "path", "cookie"] as const,
399
- });
400
-
401
- const ParameterItem = builder
402
- .objectRef<ParameterObject>("ParameterItem")
403
- .implement({
404
- fields: (t) => ({
405
- name: t.exposeString("name"),
406
- in: t.field({
407
- type: ParameterIn,
408
- resolve: (parent) => parent.in as typeof ParameterIn.$inferType,
409
- }),
410
- description: t.exposeString("description", { nullable: true }),
411
- required: t.exposeBoolean("required", { nullable: true }),
412
- deprecated: t.exposeBoolean("deprecated", { nullable: true }),
413
- allowEmptyValue: t.exposeBoolean("allowEmptyValue", { nullable: true }),
414
- style: t.exposeString("style", { nullable: true }),
415
- explode: t.exposeBoolean("explode", { nullable: true }),
416
- allowReserved: t.exposeBoolean("allowReserved", { nullable: true }),
417
- examples: t.field({
418
- type: [ExampleItem],
419
- resolve: (parent) =>
420
- Object.entries(parent.examples ?? {}).map(([name, value]) => ({
421
- name,
422
- ...(typeof value === "string" ? { value } : value),
423
- })),
424
- nullable: true,
425
- }),
426
- schema: t.expose("schema", { type: JSONSchemaScalar, nullable: true }),
427
- extensions: t.field({
428
- type: JSONObjectScalar,
429
- resolve: (parent) => resolveExtensions(parent),
430
- nullable: true,
431
- }),
432
- }),
433
- });
434
-
435
- const MediaTypeItem = builder
436
- .objectRef<{
437
- mediaType: string;
438
- schema: any;
439
- examples: Array<ExampleObject & { name: string }>;
440
- encoding?: Array<EncodingObject & { name: string }>;
441
- }>("MediaTypeObject")
442
- .implement({
443
- fields: (t) => ({
444
- mediaType: t.exposeString("mediaType"),
445
- schema: t.expose("schema", { type: JSONSchemaScalar, nullable: true }),
446
- examples: t.expose("examples", { type: [ExampleItem], nullable: true }),
447
- encoding: t.expose("encoding", { type: [EncodingItem], nullable: true }),
448
- }),
449
- });
450
-
451
- const RequestBodyObject = builder
452
- .objectRef<{
453
- description?: string;
454
- required?: boolean;
455
- content: Array<{
456
- mediaType: string;
457
- schema: any;
458
- examples: Array<ExampleObject & { name: string }>;
459
- encoding?: Array<EncodingObject & { name: string }>;
460
- }>;
461
- }>("RequestBodyObject")
462
- .implement({
463
- fields: (t) => ({
464
- description: t.exposeString("description", { nullable: true }),
465
- content: t.expose("content", { type: [MediaTypeItem], nullable: true }),
466
- required: t.exposeBoolean("required", { nullable: true }),
467
- }),
468
- });
469
-
470
- const ResponseItem = builder
471
- .objectRef<{
472
- statusCode: string;
473
- description?: string;
474
- content: Array<{
475
- mediaType: string;
476
- schema: any;
477
- examples: Array<ExampleObject & { name: string }>;
478
- }>;
479
- headers?: any;
480
- links?: any;
481
- }>("ResponseItem")
482
- .implement({
483
- fields: (t) => ({
484
- statusCode: t.exposeString("statusCode"),
485
- description: t.exposeString("description", { nullable: true }),
486
- content: t.expose("content", { type: [MediaTypeItem], nullable: true }),
487
- headers: t.expose("headers", { type: JSONScalar, nullable: true }),
488
- links: t.expose("links", { type: JSONScalar, nullable: true }),
489
- extensions: t.field({
490
- type: JSONObjectScalar,
491
- resolve: (parent) => resolveExtensions(parent),
492
- nullable: true,
493
- }),
494
- }),
495
- });
496
-
497
- const OperationItem = builder
498
- .objectRef<GraphQLOperationObject>("OperationItem")
499
- .implement({
500
- fields: (t) => ({
501
- slug: t.field({
502
- type: "String",
503
- resolve: (parent, _, ctx) => {
504
- const slug = ctx.slugs.operations[getOperationSlugKey(parent)];
505
-
506
- if (!slug) {
507
- throw new Error(
508
- `No slug found for operation: ${getOperationSlugKey(parent)}`,
509
- );
510
- }
511
- return slug;
512
- },
513
- }),
514
- path: t.exposeString("path"),
515
- method: t.exposeString("method"),
516
- operationId: t.exposeString("operationId", { nullable: true }),
517
- summary: t.exposeString("summary", { nullable: true }),
518
- description: t.exposeString("description", { nullable: true }),
519
- contentTypes: t.stringList({
520
- resolve: (parent) => Object.keys(parent.requestBody?.content ?? {}),
521
- }),
522
- parameters: t.expose("parameters", {
523
- type: [ParameterItem],
524
- nullable: true,
525
- }),
526
- servers: t.field({
527
- type: [ServerItem],
528
- resolve: (parent, _, ctx) => {
529
- // Return operation/path-level servers if defined, otherwise fall back to global servers
530
- return parent.servers && parent.servers.length > 0
531
- ? parent.servers
532
- : (ctx.schema.servers ?? []);
533
- },
534
- }),
535
- requestBody: t.field({
536
- type: RequestBodyObject,
537
- resolve: (parent) => ({
538
- description: parent.requestBody?.description,
539
- required: parent.requestBody?.required,
540
- content: Object.entries(parent.requestBody?.content ?? {}).map(
541
- ([mediaType, content]) => ({
542
- mediaType,
543
- schema: content.schema,
544
- examples: content.examples
545
- ? Object.entries(content.examples).map(([name, value]) => ({
546
- name,
547
- ...(typeof value === "string" ? { value } : value),
548
- }))
549
- : content.example !== undefined
550
- ? [{ name: "", value: content.example }]
551
- : [],
552
- encoding: Object.entries(content.encoding ?? {}).map(
553
- ([name, value]) => ({ name, ...value }),
554
- ),
555
- }),
556
- ),
557
- }),
558
- nullable: true,
559
- }),
560
- responses: t.field({
561
- type: [ResponseItem],
562
- resolve: (parent) => {
563
- return Object.entries(parent.responses ?? {}).map(
564
- ([statusCode, response]) => ({
565
- statusCode,
566
- description: response.description,
567
- content: Object.entries(response.content ?? {}).map(
568
- ([mediaType, { schema, examples, example }]) => ({
569
- mediaType,
570
- schema,
571
- examples: examples
572
- ? Object.entries(examples).map(([name, value]) => ({
573
- name,
574
- ...(typeof value === "string" ? { value } : value),
575
- }))
576
- : example !== undefined
577
- ? [{ name: "", value: example }]
578
- : [],
579
- }),
580
- ),
581
- headers: response.headers,
582
- links: response.links,
583
- }),
584
- );
585
- },
586
- }),
587
- tags: t.field({
588
- type: [TagItem],
589
- resolve: (parent, _, ctx) =>
590
- parent.tags?.map((tag) => ({
591
- name: tag,
592
- description: ctx.schema.tags?.find((t) => t.name === tag)
593
- ?.description,
594
- })),
595
- nullable: true,
596
- }),
597
- deprecated: t.exposeBoolean("deprecated", { nullable: true }),
598
- extensions: t.field({
599
- type: JSONObjectScalar,
600
- resolve: (parent) => resolveExtensions(parent),
601
- nullable: true,
602
- }),
603
- }),
604
- });
605
-
606
- const SchemaItem = builder
607
- .objectRef<{
608
- name: string;
609
- schema: SchemaObject;
610
- extensions?: Record<string, any>;
611
- }>("SchemaItem")
612
- .implement({
613
- fields: (t) => ({
614
- name: t.exposeString("name"),
615
- schema: t.expose("schema", { type: JSONSchemaScalar }),
616
- extensions: t.expose("extensions", {
617
- type: JSONObjectScalar,
618
- nullable: true,
619
- }),
620
- }),
621
- });
622
-
623
- const Components = builder.objectRef<{
624
- schemas?: Record<string, SchemaObject>;
625
- }>("Components");
626
-
627
- Components.implement({
628
- fields: (t) => ({
629
- schemas: t.field({
630
- type: [SchemaItem],
631
- resolve: (parent) => {
632
- return Object.entries(parent.schemas ?? {}).map(([name, schema]) => ({
633
- name,
634
- schema,
635
- extensions: resolveExtensions(schema),
636
- }));
637
- },
638
- nullable: true,
639
- }),
640
- }),
641
- });
642
-
643
- const Schema = builder.objectRef<OpenAPIDocument>("Schema").implement({
644
- fields: (t) => ({
645
- openapi: t.string({ resolve: (root) => root.openapi }),
646
- url: t.string({
647
- resolve: (root) => root.servers?.at(0)?.url,
648
- nullable: true,
649
- }),
650
- servers: t.field({
651
- type: [ServerItem],
652
- resolve: (root) => root.servers ?? [],
653
- }),
654
- title: t.string({ resolve: (root) => root.info.title }),
655
- version: t.string({ resolve: (root) => root.info.version }),
656
- description: t.string({
657
- resolve: (root) => root.info.description,
658
- nullable: true,
659
- }),
660
- summary: t.string({
661
- resolve: (root) => root.info.summary,
662
- nullable: true,
663
- }),
664
- contact: t.field({
665
- type: SchemaContact,
666
- nullable: true,
667
- resolve: (root) => root.info.contact ?? null,
668
- }),
669
- license: t.field({
670
- type: SchemaLicense,
671
- nullable: true,
672
- resolve: (root) => root.info.license ?? null,
673
- }),
674
- termsOfService: t.string({
675
- resolve: (root) => root.info.termsOfService,
676
- nullable: true,
677
- }),
678
- externalDocs: t.field({
679
- type: SchemaExternalDocs,
680
- nullable: true,
681
- resolve: (root) => root.externalDocs ?? null,
682
- }),
683
- paths: t.field({
684
- type: [PathItem],
685
- resolve: (root) =>
686
- Object.entries(root.paths ?? {}).map(([path, value]) => ({
687
- path,
688
- // biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
689
- methods: Object.keys(value!) as typeof HttpMethods,
690
- })),
691
- }),
692
- tag: t.field({
693
- type: SchemaTag,
694
- args: {
695
- slug: t.arg.string(),
696
- name: t.arg.string(),
697
- untagged: t.arg.boolean(),
698
- },
699
- nullable: true,
700
- resolve: (_, args, ctx) => {
701
- if (args.untagged) {
702
- return ctx.tags.find((tag) => tag.name === undefined);
703
- }
704
- if (args.slug) {
705
- return ctx.tags.find((tag) => tag.slug === args.slug);
706
- }
707
- if (args.name) {
708
- return ctx.tags.find((tag) => tag.name === args.name);
709
- }
710
- },
711
- }),
712
- tags: t.field({
713
- type: [SchemaTag],
714
- resolve: (_root, _args, ctx) => ctx.tags,
715
- }),
716
- operations: t.field({
717
- type: [OperationItem],
718
- args: {
719
- path: t.arg.string(),
720
- method: t.arg.string(),
721
- operationId: t.arg.string(),
722
- tag: t.arg.string(),
723
- untagged: t.arg.boolean(),
724
- },
725
- resolve: (_parent, args, ctx) =>
726
- ctx.operations.filter((op) => {
727
- return (
728
- (!args.operationId || op.operationId === args.operationId) &&
729
- (!args.path || op.path === args.path) &&
730
- (!args.method || op.method === args.method) &&
731
- (!args.tag || op.tags?.some((tag) => args.tag?.includes(tag))) &&
732
- (!args.untagged || (op.tags ?? []).length === 0)
733
- );
734
- }),
735
- }),
736
- webhooks: t.field({
737
- type: [WebhookItem],
738
- resolve: (root) => resolveWebhooks(root),
739
- }),
740
- components: t.field({
741
- type: Components,
742
- resolve: (root) => root.components,
743
- nullable: true,
744
- }),
745
- extensions: t.field({
746
- type: JSONObjectScalar,
747
- resolve: (root) => resolveExtensions(root),
748
- nullable: true,
749
- }),
750
- }),
751
- });
752
-
753
- const SchemaSource = builder.enumType("SchemaType", {
754
- values: ["url", "file", "raw"] as const,
755
- });
756
-
757
- builder.queryType({
758
- fields: (t) => ({
759
- schema: t.field({
760
- type: Schema,
761
- args: {
762
- type: t.arg({ type: SchemaSource, required: true }),
763
- input: t.arg({ type: JSONScalar, required: true }),
764
- },
765
- resolve: async (_parent, args, ctx) => {
766
- if (args.type === "file" && typeof args.input === "string") {
767
- const loadSchema = ctx.schemaImports?.[args.input];
768
-
769
- if (!loadSchema) {
770
- throw new Error(`No schema loader found for path: ${args.input}`);
771
- }
772
- ctx.schema = (await loadSchema()).schema;
773
- } else {
774
- ctx.schema = await validate(args.input as string);
775
- }
776
-
777
- ctx.operations = getAllOperations(ctx.schema.paths);
778
- ctx.slugs = getAllSlugs(ctx.operations, ctx.schema.tags);
779
- ctx.tags = getAllTags(ctx.schema);
780
-
781
- return ctx.schema;
782
- },
783
- }),
784
- }),
785
- });
786
-
787
- export const schema = builder.toSchema();
788
-
789
- export const createGraphQLServer = (
790
- options?: Omit<YogaServerOptions<any, any>, "schema">,
791
- ) => createYoga({ schema, batching: true, ...options });
1
+ // biome-ignore-all lint/suspicious/noExplicitAny: Allow any type
2
+ import SchemaBuilder from "@pothos/core";
3
+ import { GraphQLJSON, GraphQLJSONObject } from "graphql-type-json";
4
+ import { createYoga, type YogaServerOptions } from "graphql-yoga";
5
+ import {
6
+ type CountableSlugify,
7
+ slugifyWithCounter,
8
+ } from "../../util/slugify.js";
9
+ import { HttpMethods, validate } from "../parser/index.js";
10
+ import type {
11
+ ContactObject,
12
+ EncodingObject,
13
+ ExampleObject,
14
+ ExternalDocumentationObject,
15
+ LicenseObject,
16
+ OpenAPIDocument,
17
+ OperationObject,
18
+ ParameterObject,
19
+ PathsObject,
20
+ SchemaObject,
21
+ ServerObject,
22
+ TagObject,
23
+ } from "../parser/index.js";
24
+ import { GraphQLJSONSchema } from "./circular.js";
25
+
26
+ export type {
27
+ EncodingObject,
28
+ ExampleObject,
29
+ OpenAPIDocument,
30
+ OperationObject,
31
+ ParameterObject,
32
+ PathsObject,
33
+ SchemaObject,
34
+ TagObject,
35
+ };
36
+
37
+ type OperationLike = {
38
+ summary?: string | null;
39
+ operationId?: string | null;
40
+ path: string;
41
+ method: string;
42
+ };
43
+
44
+ export const createOperationSlug = (
45
+ slugify: CountableSlugify,
46
+ operation: OperationLike,
47
+ ) => {
48
+ const summary =
49
+ operation.summary ||
50
+ operation.operationId ||
51
+ `${operation.method}-${operation.path}`;
52
+
53
+ return slugify(summary);
54
+ };
55
+
56
+ export type SchemaImport = () => Promise<{ schema: OpenAPIDocument }>;
57
+
58
+ export type SchemaImports = Record<string, SchemaImport>;
59
+
60
+ type Slugs = {
61
+ operations: Record<string, string>;
62
+ tags: Record<string, string>;
63
+ };
64
+
65
+ type Context = {
66
+ schema: OpenAPIDocument;
67
+ operations: GraphQLOperationObject[];
68
+ schemaImports?: SchemaImports;
69
+ tags: ReturnType<typeof getAllTags>;
70
+ slugs: Slugs;
71
+ };
72
+
73
+ const builder = new SchemaBuilder<{
74
+ DefaultFieldNullability: false;
75
+ Scalars: {
76
+ JSON: any;
77
+ JSONObject: any;
78
+ JSONSchema: any;
79
+ };
80
+ Context: Context;
81
+ }>({
82
+ defaultFieldNullability: false,
83
+ });
84
+
85
+ type GraphQLOperationObject = OperationObject & {
86
+ path: string;
87
+ method: string;
88
+ slug?: string;
89
+ parentTag?: string;
90
+ };
91
+
92
+ const JSONScalar = builder.addScalarType("JSON", GraphQLJSON);
93
+ const JSONObjectScalar = builder.addScalarType("JSONObject", GraphQLJSONObject);
94
+ const JSONSchemaScalar = builder.addScalarType("JSONSchema", GraphQLJSONSchema);
95
+
96
+ const resolveExtensions = (obj: Record<string, any>) =>
97
+ Object.fromEntries(
98
+ Object.entries(obj).filter(([key]) => key.startsWith("x-")),
99
+ );
100
+
101
+ export const getAllTags = (
102
+ schema: OpenAPIDocument,
103
+ ): Array<Omit<TagObject, "name"> & { name?: string; slug?: string }> => {
104
+ const rootTags = schema.tags ?? [];
105
+ const operations = Object.values(schema.paths ?? {}).flatMap((path) =>
106
+ HttpMethods.map((k) => path?.[k]).filter((op) => op != null),
107
+ );
108
+
109
+ const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
110
+
111
+ const hasUntaggedOperations = operations.some(
112
+ (op) => (op.tags?.length ?? 0) === 0,
113
+ );
114
+
115
+ const slugify = slugifyWithCounter();
116
+ const slugs = Object.fromEntries(
117
+ Array.from(
118
+ new Set([...Array.from(operationTags), ...rootTags.map((t) => t.name)]),
119
+ ).map((tag) => [tag, slugify(tag)]),
120
+ );
121
+
122
+ const result = [
123
+ // Keep root tags that are actually used in operations
124
+ ...rootTags
125
+ .filter((tag) => operationTags.has(tag.name))
126
+ .map((tag) => ({ ...tag, slug: slugs[tag.name] })),
127
+ // Add tags found in operations but not defined in root tags
128
+ ...Array.from(operationTags)
129
+ .filter((tag) => !rootTags.some((rt) => rt.name === tag))
130
+ .map((tag) => ({ name: tag, slug: slugs[tag] })),
131
+ // Add untagged operations if there are any
132
+ ...(hasUntaggedOperations ? [{ name: undefined, slug: undefined }] : []),
133
+ ];
134
+
135
+ // Apply x-tagGroups ordering if present
136
+ const tagGroups = (schema as any)["x-tagGroups"] ?? [];
137
+
138
+ if (tagGroups.length === 0) return result;
139
+
140
+ const groupOrder = tagGroups.flatMap((group: any) => group.tags);
141
+ return result.sort((a, b) => {
142
+ if (!a.name || !b.name) return 0; // Keep untagged at end
143
+ const indexA = groupOrder.indexOf(a.name);
144
+ const indexB = groupOrder.indexOf(b.name);
145
+ if (indexA === -1 && indexB === -1) return 0; // Keep original order for ungrouped
146
+ if (indexA === -1) return 1; // Ungrouped after grouped
147
+ if (indexB === -1) return -1; // Grouped before ungrouped
148
+ return indexA - indexB;
149
+ });
150
+ };
151
+
152
+ export const getAllSlugs = (
153
+ ops: GraphQLOperationObject[],
154
+ schemaTags: TagObject[] = [],
155
+ ): Slugs => {
156
+ const operationSlugify = slugifyWithCounter();
157
+ const tagSlugify = slugifyWithCounter();
158
+
159
+ const tags = Array.from(
160
+ new Set([
161
+ ...ops.flatMap((op) => op.tags ?? []),
162
+ ...schemaTags.map((tag) => tag.name),
163
+ ]),
164
+ );
165
+
166
+ return {
167
+ operations: Object.fromEntries(
168
+ ops.map((op) => [
169
+ getOperationSlugKey(op),
170
+ createOperationSlug(operationSlugify, op),
171
+ ]),
172
+ ),
173
+ tags: Object.fromEntries(tags.map((tag) => [tag, tagSlugify(tag)])),
174
+ };
175
+ };
176
+
177
+ const getOperationSlugKey = (op: GraphQLOperationObject) =>
178
+ [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
179
+
180
+ export const getAllOperations = (
181
+ paths?: PathsObject,
182
+ ): GraphQLOperationObject[] => {
183
+ const operations = Object.entries(paths ?? {}).flatMap(([path, value]) =>
184
+ HttpMethods.flatMap((method) => {
185
+ if (!value?.[method]) return [];
186
+
187
+ const operation = value[method] as OperationObject;
188
+ const pathParameters = value.parameters ?? [];
189
+ const operationParameters = operation.parameters ?? [];
190
+
191
+ // parameters are inherited from the parent path object,
192
+ // but can be overridden by their `name` and `in` location
193
+ const parameters = [
194
+ ...pathParameters.filter(
195
+ // remove path parameters that are already defined in the operation
196
+ (pp) =>
197
+ !operationParameters.some(
198
+ (op) => op.name === pp.name && op.in === pp.in,
199
+ ),
200
+ ),
201
+ ...operationParameters,
202
+ ];
203
+
204
+ // servers follow the OpenAPI 3.0 resolution hierarchy:
205
+ // 1. Operation-level servers (highest precedence)
206
+ // 2. Path-level servers
207
+ // 3. Global servers (handled at query time, lowest precedence)
208
+ const pathServers = value.servers ?? [];
209
+ const operationServers = operation.servers ?? [];
210
+ const servers =
211
+ operationServers.length > 0 ? operationServers : pathServers;
212
+
213
+ return {
214
+ ...operation,
215
+ method,
216
+ path,
217
+ parameters,
218
+ servers,
219
+ tags: operation.tags ?? [],
220
+ } satisfies GraphQLOperationObject;
221
+ }),
222
+ );
223
+
224
+ return operations;
225
+ };
226
+
227
+ const SchemaContact = builder
228
+ .objectRef<ContactObject>("SchemaContact")
229
+ .implement({
230
+ fields: (t) => ({
231
+ name: t.exposeString("name", { nullable: true }),
232
+ url: t.exposeString("url", { nullable: true }),
233
+ email: t.exposeString("email", { nullable: true }),
234
+ }),
235
+ });
236
+
237
+ const SchemaLicense = builder
238
+ .objectRef<LicenseObject>("SchemaLicense")
239
+ .implement({
240
+ fields: (t) => ({
241
+ name: t.exposeString("name"),
242
+ url: t.exposeString("url", { nullable: true }),
243
+ identifier: t.exposeString("identifier", { nullable: true }),
244
+ }),
245
+ });
246
+
247
+ const SchemaExternalDocs = builder
248
+ .objectRef<ExternalDocumentationObject>("SchemaExternalDocs")
249
+ .implement({
250
+ fields: (t) => ({
251
+ description: t.exposeString("description", { nullable: true }),
252
+ url: t.exposeString("url"),
253
+ }),
254
+ });
255
+
256
+ const SchemaTag = builder.objectRef<
257
+ Omit<TagObject, "name"> & { name?: string; slug?: string }
258
+ >("SchemaTag");
259
+
260
+ SchemaTag.implement({
261
+ fields: (t) => ({
262
+ name: t.exposeString("name", { nullable: true }),
263
+ slug: t.exposeString("slug", { nullable: true }),
264
+ isUntagged: t.field({ type: "Boolean", resolve: (parent) => !parent.name }),
265
+ description: t.exposeString("description", { nullable: true }),
266
+ operations: t.field({
267
+ type: [OperationItem],
268
+ resolve: (parent, _args, ctx) => {
269
+ const rootTags = ctx.tags.map((tag) => tag.name);
270
+
271
+ return ctx.operations
272
+ .filter((item) =>
273
+ parent.name
274
+ ? item.tags?.includes(parent.name)
275
+ : item.tags?.length === 0 ||
276
+ // If none of the tags are present in the root tags, then show them here
277
+ item.tags?.every((tag) => !rootTags.includes(tag)),
278
+ )
279
+ .map((item) => ({ ...item, parentTag: parent.name }));
280
+ },
281
+ }),
282
+ prev: t.field({
283
+ type: SchemaTag,
284
+ nullable: true,
285
+ resolve: (parent, _args, ctx) => {
286
+ const index = ctx.tags.findIndex((tag) => tag.slug === parent.slug);
287
+ return ctx.tags[index - 1];
288
+ },
289
+ }),
290
+ next: t.field({
291
+ type: SchemaTag,
292
+ nullable: true,
293
+ resolve: (parent, _args, ctx) => {
294
+ const index = ctx.tags.findIndex((tag) => tag.slug === parent.slug);
295
+ return ctx.tags[index + 1];
296
+ },
297
+ }),
298
+ extensions: t.field({
299
+ type: JSONObjectScalar,
300
+ resolve: (parent) => resolveExtensions(parent),
301
+ nullable: true,
302
+ }),
303
+ }),
304
+ });
305
+
306
+ const ServerItem = builder.objectRef<ServerObject>("Server").implement({
307
+ fields: (t) => ({
308
+ url: t.exposeString("url"),
309
+ description: t.exposeString("description", { nullable: true }),
310
+ }),
311
+ });
312
+
313
+ type WebhookData = {
314
+ name: string;
315
+ method: string;
316
+ summary?: string;
317
+ description?: string;
318
+ operationId?: string;
319
+ };
320
+
321
+ const WebhookItem = builder.objectRef<WebhookData>("WebhookItem").implement({
322
+ fields: (t) => ({
323
+ name: t.exposeString("name"),
324
+ method: t.exposeString("method"),
325
+ summary: t.exposeString("summary", { nullable: true }),
326
+ description: t.exposeString("description", { nullable: true }),
327
+ operationId: t.exposeString("operationId", { nullable: true }),
328
+ }),
329
+ });
330
+
331
+ const resolveWebhooks = (schema: OpenAPIDocument): WebhookData[] => {
332
+ const webhooks = (schema as any).webhooks ?? {};
333
+ return Object.entries(webhooks).flatMap(([name, pathItem]: [string, any]) =>
334
+ HttpMethods.flatMap((method) => {
335
+ const op = pathItem?.[method];
336
+ if (!op) return [];
337
+ return {
338
+ name,
339
+ method: method.toUpperCase(),
340
+ summary: op.summary,
341
+ description: op.description,
342
+ operationId: op.operationId,
343
+ };
344
+ }),
345
+ );
346
+ };
347
+
348
+ const PathItem = builder
349
+ .objectRef<{
350
+ path: string;
351
+ methods: typeof HttpMethods;
352
+ }>("PathItem")
353
+ .implement({
354
+ fields: (t) => ({
355
+ path: t.exposeString("path"),
356
+ methods: t.exposeStringList("methods"),
357
+ }),
358
+ });
359
+
360
+ const TagItem = builder.objectRef<TagObject>("TagItem").implement({
361
+ fields: (t) => ({
362
+ name: t.exposeString("name"),
363
+ description: t.exposeString("description", { nullable: true }),
364
+ extensions: t.field({
365
+ type: JSONObjectScalar,
366
+ resolve: (parent) => resolveExtensions(parent),
367
+ nullable: true,
368
+ }),
369
+ }),
370
+ });
371
+
372
+ const EncodingItem = builder
373
+ .objectRef<EncodingObject & { name: string }>("EncodingItem")
374
+ .implement({
375
+ fields: (t) => ({
376
+ name: t.exposeString("name"),
377
+ contentType: t.exposeString("contentType", { nullable: true }),
378
+ headers: t.expose("headers", { type: JSONObjectScalar, nullable: true }),
379
+ style: t.exposeString("style", { nullable: true }),
380
+ explode: t.exposeBoolean("explode", { nullable: true }),
381
+ allowReserved: t.exposeBoolean("allowReserved", { nullable: true }),
382
+ }),
383
+ });
384
+
385
+ const ExampleItem = builder
386
+ .objectRef<ExampleObject & { name: string }>("ExampleItem")
387
+ .implement({
388
+ fields: (t) => ({
389
+ name: t.exposeString("name"),
390
+ summary: t.exposeString("summary", { nullable: true }),
391
+ description: t.exposeString("description", { nullable: true }),
392
+ value: t.expose("value", { type: JSONScalar, nullable: true }),
393
+ externalValue: t.exposeString("externalValue", { nullable: true }),
394
+ }),
395
+ });
396
+
397
+ const ParameterIn = builder.enumType("ParameterIn", {
398
+ values: ["query", "header", "path", "cookie"] as const,
399
+ });
400
+
401
+ const ParameterItem = builder
402
+ .objectRef<ParameterObject>("ParameterItem")
403
+ .implement({
404
+ fields: (t) => ({
405
+ name: t.exposeString("name"),
406
+ in: t.field({
407
+ type: ParameterIn,
408
+ resolve: (parent) => parent.in as typeof ParameterIn.$inferType,
409
+ }),
410
+ description: t.exposeString("description", { nullable: true }),
411
+ required: t.exposeBoolean("required", { nullable: true }),
412
+ deprecated: t.exposeBoolean("deprecated", { nullable: true }),
413
+ allowEmptyValue: t.exposeBoolean("allowEmptyValue", { nullable: true }),
414
+ style: t.exposeString("style", { nullable: true }),
415
+ explode: t.exposeBoolean("explode", { nullable: true }),
416
+ allowReserved: t.exposeBoolean("allowReserved", { nullable: true }),
417
+ examples: t.field({
418
+ type: [ExampleItem],
419
+ resolve: (parent) =>
420
+ Object.entries(parent.examples ?? {}).map(([name, value]) => ({
421
+ name,
422
+ ...(typeof value === "string" ? { value } : value),
423
+ })),
424
+ nullable: true,
425
+ }),
426
+ schema: t.expose("schema", { type: JSONSchemaScalar, nullable: true }),
427
+ extensions: t.field({
428
+ type: JSONObjectScalar,
429
+ resolve: (parent) => resolveExtensions(parent),
430
+ nullable: true,
431
+ }),
432
+ }),
433
+ });
434
+
435
+ const MediaTypeItem = builder
436
+ .objectRef<{
437
+ mediaType: string;
438
+ schema: any;
439
+ examples: Array<ExampleObject & { name: string }>;
440
+ encoding?: Array<EncodingObject & { name: string }>;
441
+ }>("MediaTypeObject")
442
+ .implement({
443
+ fields: (t) => ({
444
+ mediaType: t.exposeString("mediaType"),
445
+ schema: t.expose("schema", { type: JSONSchemaScalar, nullable: true }),
446
+ examples: t.expose("examples", { type: [ExampleItem], nullable: true }),
447
+ encoding: t.expose("encoding", { type: [EncodingItem], nullable: true }),
448
+ }),
449
+ });
450
+
451
+ const RequestBodyObject = builder
452
+ .objectRef<{
453
+ description?: string;
454
+ required?: boolean;
455
+ content: Array<{
456
+ mediaType: string;
457
+ schema: any;
458
+ examples: Array<ExampleObject & { name: string }>;
459
+ encoding?: Array<EncodingObject & { name: string }>;
460
+ }>;
461
+ }>("RequestBodyObject")
462
+ .implement({
463
+ fields: (t) => ({
464
+ description: t.exposeString("description", { nullable: true }),
465
+ content: t.expose("content", { type: [MediaTypeItem], nullable: true }),
466
+ required: t.exposeBoolean("required", { nullable: true }),
467
+ }),
468
+ });
469
+
470
+ const ResponseItem = builder
471
+ .objectRef<{
472
+ statusCode: string;
473
+ description?: string;
474
+ content: Array<{
475
+ mediaType: string;
476
+ schema: any;
477
+ examples: Array<ExampleObject & { name: string }>;
478
+ }>;
479
+ headers?: any;
480
+ links?: any;
481
+ }>("ResponseItem")
482
+ .implement({
483
+ fields: (t) => ({
484
+ statusCode: t.exposeString("statusCode"),
485
+ description: t.exposeString("description", { nullable: true }),
486
+ content: t.expose("content", { type: [MediaTypeItem], nullable: true }),
487
+ headers: t.expose("headers", { type: JSONScalar, nullable: true }),
488
+ links: t.expose("links", { type: JSONScalar, nullable: true }),
489
+ extensions: t.field({
490
+ type: JSONObjectScalar,
491
+ resolve: (parent) => resolveExtensions(parent),
492
+ nullable: true,
493
+ }),
494
+ }),
495
+ });
496
+
497
+ const OperationItem = builder
498
+ .objectRef<GraphQLOperationObject>("OperationItem")
499
+ .implement({
500
+ fields: (t) => ({
501
+ slug: t.field({
502
+ type: "String",
503
+ resolve: (parent, _, ctx) => {
504
+ const slug = ctx.slugs.operations[getOperationSlugKey(parent)];
505
+
506
+ if (!slug) {
507
+ throw new Error(
508
+ `No slug found for operation: ${getOperationSlugKey(parent)}`,
509
+ );
510
+ }
511
+ return slug;
512
+ },
513
+ }),
514
+ path: t.exposeString("path"),
515
+ method: t.exposeString("method"),
516
+ operationId: t.exposeString("operationId", { nullable: true }),
517
+ summary: t.exposeString("summary", { nullable: true }),
518
+ description: t.exposeString("description", { nullable: true }),
519
+ contentTypes: t.stringList({
520
+ resolve: (parent) => Object.keys(parent.requestBody?.content ?? {}),
521
+ }),
522
+ parameters: t.expose("parameters", {
523
+ type: [ParameterItem],
524
+ nullable: true,
525
+ }),
526
+ servers: t.field({
527
+ type: [ServerItem],
528
+ resolve: (parent, _, ctx) => {
529
+ // Return operation/path-level servers if defined, otherwise fall back to global servers
530
+ return parent.servers && parent.servers.length > 0
531
+ ? parent.servers
532
+ : (ctx.schema.servers ?? []);
533
+ },
534
+ }),
535
+ requestBody: t.field({
536
+ type: RequestBodyObject,
537
+ resolve: (parent) => ({
538
+ description: parent.requestBody?.description,
539
+ required: parent.requestBody?.required,
540
+ content: Object.entries(parent.requestBody?.content ?? {}).map(
541
+ ([mediaType, content]) => ({
542
+ mediaType,
543
+ schema: content.schema,
544
+ examples: content.examples
545
+ ? Object.entries(content.examples).map(([name, value]) => ({
546
+ name,
547
+ ...(typeof value === "string" ? { value } : value),
548
+ }))
549
+ : content.example !== undefined
550
+ ? [{ name: "", value: content.example }]
551
+ : [],
552
+ encoding: Object.entries(content.encoding ?? {}).map(
553
+ ([name, value]) => ({ name, ...value }),
554
+ ),
555
+ }),
556
+ ),
557
+ }),
558
+ nullable: true,
559
+ }),
560
+ responses: t.field({
561
+ type: [ResponseItem],
562
+ resolve: (parent) => {
563
+ return Object.entries(parent.responses ?? {}).map(
564
+ ([statusCode, response]) => ({
565
+ statusCode,
566
+ description: response.description,
567
+ content: Object.entries(response.content ?? {}).map(
568
+ ([mediaType, { schema, examples, example }]) => ({
569
+ mediaType,
570
+ schema,
571
+ examples: examples
572
+ ? Object.entries(examples).map(([name, value]) => ({
573
+ name,
574
+ ...(typeof value === "string" ? { value } : value),
575
+ }))
576
+ : example !== undefined
577
+ ? [{ name: "", value: example }]
578
+ : [],
579
+ }),
580
+ ),
581
+ headers: response.headers,
582
+ links: response.links,
583
+ }),
584
+ );
585
+ },
586
+ }),
587
+ tags: t.field({
588
+ type: [TagItem],
589
+ resolve: (parent, _, ctx) =>
590
+ parent.tags?.map((tag) => ({
591
+ name: tag,
592
+ description: ctx.schema.tags?.find((t) => t.name === tag)
593
+ ?.description,
594
+ })),
595
+ nullable: true,
596
+ }),
597
+ deprecated: t.exposeBoolean("deprecated", { nullable: true }),
598
+ extensions: t.field({
599
+ type: JSONObjectScalar,
600
+ resolve: (parent) => resolveExtensions(parent),
601
+ nullable: true,
602
+ }),
603
+ }),
604
+ });
605
+
606
+ const SchemaItem = builder
607
+ .objectRef<{
608
+ name: string;
609
+ schema: SchemaObject;
610
+ extensions?: Record<string, any>;
611
+ }>("SchemaItem")
612
+ .implement({
613
+ fields: (t) => ({
614
+ name: t.exposeString("name"),
615
+ schema: t.expose("schema", { type: JSONSchemaScalar }),
616
+ extensions: t.expose("extensions", {
617
+ type: JSONObjectScalar,
618
+ nullable: true,
619
+ }),
620
+ }),
621
+ });
622
+
623
+ const Components = builder.objectRef<{
624
+ schemas?: Record<string, SchemaObject>;
625
+ }>("Components");
626
+
627
+ Components.implement({
628
+ fields: (t) => ({
629
+ schemas: t.field({
630
+ type: [SchemaItem],
631
+ resolve: (parent) => {
632
+ return Object.entries(parent.schemas ?? {}).map(([name, schema]) => ({
633
+ name,
634
+ schema,
635
+ extensions: resolveExtensions(schema),
636
+ }));
637
+ },
638
+ nullable: true,
639
+ }),
640
+ }),
641
+ });
642
+
643
+ const Schema = builder.objectRef<OpenAPIDocument>("Schema").implement({
644
+ fields: (t) => ({
645
+ openapi: t.string({ resolve: (root) => root.openapi }),
646
+ url: t.string({
647
+ resolve: (root) => root.servers?.at(0)?.url,
648
+ nullable: true,
649
+ }),
650
+ servers: t.field({
651
+ type: [ServerItem],
652
+ resolve: (root) => root.servers ?? [],
653
+ }),
654
+ title: t.string({ resolve: (root) => root.info.title }),
655
+ version: t.string({ resolve: (root) => root.info.version }),
656
+ description: t.string({
657
+ resolve: (root) => root.info.description,
658
+ nullable: true,
659
+ }),
660
+ summary: t.string({
661
+ resolve: (root) => root.info.summary,
662
+ nullable: true,
663
+ }),
664
+ contact: t.field({
665
+ type: SchemaContact,
666
+ nullable: true,
667
+ resolve: (root) => root.info.contact ?? null,
668
+ }),
669
+ license: t.field({
670
+ type: SchemaLicense,
671
+ nullable: true,
672
+ resolve: (root) => root.info.license ?? null,
673
+ }),
674
+ termsOfService: t.string({
675
+ resolve: (root) => root.info.termsOfService,
676
+ nullable: true,
677
+ }),
678
+ externalDocs: t.field({
679
+ type: SchemaExternalDocs,
680
+ nullable: true,
681
+ resolve: (root) => root.externalDocs ?? null,
682
+ }),
683
+ paths: t.field({
684
+ type: [PathItem],
685
+ resolve: (root) =>
686
+ Object.entries(root.paths ?? {}).map(([path, value]) => ({
687
+ path,
688
+ // biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
689
+ methods: Object.keys(value!) as typeof HttpMethods,
690
+ })),
691
+ }),
692
+ tag: t.field({
693
+ type: SchemaTag,
694
+ args: {
695
+ slug: t.arg.string(),
696
+ name: t.arg.string(),
697
+ untagged: t.arg.boolean(),
698
+ },
699
+ nullable: true,
700
+ resolve: (_, args, ctx) => {
701
+ if (args.untagged) {
702
+ return ctx.tags.find((tag) => tag.name === undefined);
703
+ }
704
+ if (args.slug) {
705
+ return ctx.tags.find((tag) => tag.slug === args.slug);
706
+ }
707
+ if (args.name) {
708
+ return ctx.tags.find((tag) => tag.name === args.name);
709
+ }
710
+ },
711
+ }),
712
+ tags: t.field({
713
+ type: [SchemaTag],
714
+ resolve: (_root, _args, ctx) => ctx.tags,
715
+ }),
716
+ operations: t.field({
717
+ type: [OperationItem],
718
+ args: {
719
+ path: t.arg.string(),
720
+ method: t.arg.string(),
721
+ operationId: t.arg.string(),
722
+ tag: t.arg.string(),
723
+ untagged: t.arg.boolean(),
724
+ },
725
+ resolve: (_parent, args, ctx) =>
726
+ ctx.operations.filter((op) => {
727
+ return (
728
+ (!args.operationId || op.operationId === args.operationId) &&
729
+ (!args.path || op.path === args.path) &&
730
+ (!args.method || op.method === args.method) &&
731
+ (!args.tag || op.tags?.some((tag) => args.tag?.includes(tag))) &&
732
+ (!args.untagged || (op.tags ?? []).length === 0)
733
+ );
734
+ }),
735
+ }),
736
+ webhooks: t.field({
737
+ type: [WebhookItem],
738
+ resolve: (root) => resolveWebhooks(root),
739
+ }),
740
+ components: t.field({
741
+ type: Components,
742
+ resolve: (root) => root.components,
743
+ nullable: true,
744
+ }),
745
+ extensions: t.field({
746
+ type: JSONObjectScalar,
747
+ resolve: (root) => resolveExtensions(root),
748
+ nullable: true,
749
+ }),
750
+ }),
751
+ });
752
+
753
+ const SchemaSource = builder.enumType("SchemaType", {
754
+ values: ["url", "file", "raw"] as const,
755
+ });
756
+
757
+ builder.queryType({
758
+ fields: (t) => ({
759
+ schema: t.field({
760
+ type: Schema,
761
+ args: {
762
+ type: t.arg({ type: SchemaSource, required: true }),
763
+ input: t.arg({ type: JSONScalar, required: true }),
764
+ },
765
+ resolve: async (_parent, args, ctx) => {
766
+ if (args.type === "file" && typeof args.input === "string") {
767
+ const loadSchema = ctx.schemaImports?.[args.input];
768
+
769
+ if (!loadSchema) {
770
+ throw new Error(`No schema loader found for path: ${args.input}`);
771
+ }
772
+ ctx.schema = (await loadSchema()).schema;
773
+ } else {
774
+ ctx.schema = await validate(args.input as string);
775
+ }
776
+
777
+ ctx.operations = getAllOperations(ctx.schema.paths);
778
+ ctx.slugs = getAllSlugs(ctx.operations, ctx.schema.tags);
779
+ ctx.tags = getAllTags(ctx.schema);
780
+
781
+ return ctx.schema;
782
+ },
783
+ }),
784
+ }),
785
+ });
786
+
787
+ export const schema = builder.toSchema();
788
+
789
+ export const createGraphQLServer = (
790
+ options?: Omit<YogaServerOptions<any, any>, "schema">,
791
+ ) => createYoga({ schema, batching: true, ...options });