@lukoweb/apitogo 0.1.23 → 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,729 +1,766 @@
1
- import type { Options } from "@mdx-js/rollup";
2
- import colors from "picocolors";
3
- import type { ComponentType, ReactNode } from "react";
4
- import { isValidElement } from "react";
5
- import type { BundledLanguage, BundledTheme } from "shiki";
6
- import { z } from "zod";
7
- import type { UseAuthReturn } from "../../lib/authentication/hook.js";
8
- import type { AuthState } from "../../lib/authentication/state.js";
9
- import type { SlotType } from "../../lib/components/context/SlotProvider.js";
10
- import type { ApitogoPlugin } from "../../lib/core/plugins.js";
11
- import type { ZudokuContext } from "../../lib/core/ZudokuContext.js";
12
- import type { FilterCatalogItemsFn } from "../../lib/plugins/api-catalog/index.js";
13
- import type { ApiConsumer } from "../../lib/plugins/api-keys/index.js";
14
- import type {
15
- GenerateCodeSnippetFn,
16
- TransformExamplesFn,
17
- } from "../../lib/plugins/openapi/interfaces.js";
18
- import type { PagefindSearchFragment } from "../../lib/plugins/search-pagefind/types.js";
19
- import type { MdxComponentsType } from "../../lib/util/MdxComponents.js";
20
- import type { ExposedComponentProps } from "../../lib/util/useExposedProps.js";
21
- import { GOOGLE_FONTS } from "../../vite/plugin-theme.js";
22
- import { HeaderNavigationSchema } from "./HeaderNavigationSchema.js";
23
- import {
24
- InputNavigationSchema,
25
- NavigationRulesSchema,
26
- } from "./InputNavigationSchema.js";
27
- import { ProtectedRoutesSchema } from "./ProtectedRoutesSchema.js";
28
-
29
- const ThemeSchema = z
30
- .object({
31
- background: z.string(),
32
- foreground: z.string(),
33
- card: z.string(),
34
- cardForeground: z.string(),
35
- popover: z.string(),
36
- popoverForeground: z.string(),
37
- primary: z.string(),
38
- primaryForeground: z.string(),
39
- secondary: z.string(),
40
- secondaryForeground: z.string(),
41
- muted: z.string(),
42
- mutedForeground: z.string(),
43
- accent: z.string(),
44
- accentForeground: z.string(),
45
- destructive: z.string(),
46
- destructiveForeground: z.string(),
47
- border: z.string(),
48
- input: z.string(),
49
- ring: z.string(),
50
- radius: z.string(),
51
- })
52
- .partial();
53
-
54
- const ApiCatalogCategorySchema = z.object({
55
- label: z.string(),
56
- tags: z.array(z.string()),
57
- });
58
-
59
- const LanguageOption = z.object({
60
- value: z.string().min(1),
61
- label: z.string().min(1),
62
- });
63
-
64
- const AiAssistantCustomSchema = z.object({
65
- label: z.string(),
66
- icon: z.custom<ReactNode>().optional(),
67
- url: z.union([
68
- z.string(),
69
- z.custom<
70
- (context: { pageUrl: string; type: "docs" | "openapi" }) => string
71
- >((val) => typeof val === "function"),
72
- ]),
73
- });
74
-
75
- export type AiAssistantCustom = z.infer<typeof AiAssistantCustomSchema>;
76
-
77
- const AiAssistantPresets = ["claude", "chatgpt"] as const;
78
-
79
- const AiAssistantsSchema = z
80
- .union([
81
- z.literal(false),
82
- z.array(z.union([z.enum(AiAssistantPresets), AiAssistantCustomSchema])),
83
- ])
84
- .optional();
85
-
86
- export type AiAssistantsConfig = z.infer<typeof AiAssistantsSchema>;
87
-
88
- const ApiOptionsSchema = z
89
- .object({
90
- examplesLanguage: z.string(),
91
- supportedLanguages: z.array(LanguageOption),
92
- disablePlayground: z.boolean(),
93
- disableSidecar: z.boolean(),
94
- showVersionSelect: z.enum(["always", "if-available", "hide"]),
95
- expandAllTags: z.boolean(),
96
- showInfoPage: z.boolean(),
97
- schemaDownload: z.object({ enabled: z.boolean() }).partial(),
98
- transformExamples: z.custom<TransformExamplesFn>(
99
- (val) => typeof val === "function",
100
- ),
101
- generateCodeSnippet: z.custom<GenerateCodeSnippetFn>(
102
- (val) => typeof val === "function",
103
- ),
104
- })
105
- .partial();
106
-
107
- const ApiConfigSchema = z
108
- .object({
109
- server: z.string(),
110
- path: z.string(),
111
- categories: z.array(ApiCatalogCategorySchema),
112
- options: ApiOptionsSchema,
113
- })
114
- .partial();
115
-
116
- const VersionConfigSchema = z.object({
117
- path: z.string(),
118
- input: z.string(),
119
- label: z.string().optional(),
120
- });
121
-
122
- const ApiSchema = z.discriminatedUnion("type", [
123
- z.object({
124
- type: z.literal("url"),
125
- input: z.union([z.string(), z.array(VersionConfigSchema)]),
126
- ...ApiConfigSchema.shape,
127
- }),
128
- z.object({
129
- type: z.literal("file"),
130
- input: z.union([
131
- z.string(),
132
- z.array(z.union([z.string(), VersionConfigSchema])),
133
- ]),
134
- ...ApiConfigSchema.shape,
135
- }),
136
- z.object({
137
- type: z.literal("raw"),
138
- input: z.string(),
139
- ...ApiConfigSchema.shape,
140
- }),
141
- ]);
142
-
143
- const ApiKeysSchema = z.object({
144
- enabled: z.boolean(),
145
- getConsumers: z
146
- .custom<(context: ZudokuContext) => Promise<ApiConsumer[]>>(
147
- (val) => typeof val === "function",
148
- )
149
- .optional(),
150
- rollKey: z
151
- .custom<(id: string, context: ZudokuContext) => Promise<void>>(
152
- (val) => typeof val === "function",
153
- )
154
- .optional(),
155
- deleteKey: z
156
- .custom<
157
- (
158
- consumerId: string,
159
- keyId: string,
160
- context: ZudokuContext,
161
- ) => Promise<void>
162
- >((val) => typeof val === "function")
163
- .optional(),
164
- updateKeyDescription: z
165
- .custom<
166
- (
167
- apiKey: { id: string; description: string },
168
- context: ZudokuContext,
169
- ) => Promise<void>
170
- >((val) => typeof val === "function")
171
- .optional(),
172
- createKey: z
173
- .custom<
174
- ({
175
- apiKey,
176
- context,
177
- auth,
178
- }: {
179
- apiKey: { description: string; expiresOn?: string };
180
- context: ZudokuContext;
181
- auth: UseAuthReturn;
182
- }) => Promise<void>
183
- >((val) => typeof val === "function")
184
- .optional(),
185
- });
186
-
187
- export type ApiKeysOptions = z.infer<typeof ApiKeysSchema>;
188
-
189
- const LogoSchema = z.object({
190
- src: z.object({ light: z.string(), dark: z.string() }),
191
- alt: z.string().optional(),
192
- width: z.string().or(z.number()).optional(),
193
- href: z.string().optional(),
194
- reloadDocument: z.boolean().optional(),
195
- });
196
-
197
- export const FooterSocialIcons = [
198
- "reddit",
199
- "discord",
200
- "github",
201
- "x",
202
- "linkedin",
203
- "facebook",
204
- "instagram",
205
- "youtube",
206
- "tiktok",
207
- "twitch",
208
- "pinterest",
209
- "snapchat",
210
- "whatsapp",
211
- "telegram",
212
- ] as const;
213
-
214
- export const FooterSocialSchema = z.object({
215
- label: z.string().optional(),
216
- href: z.string(),
217
- icon: z
218
- .union([
219
- z.enum(FooterSocialIcons),
220
- z.custom<ReactNode>((val) => isValidElement(val)),
221
- ])
222
- .optional(),
223
- });
224
-
225
- export const FooterSchema = z
226
- .object({
227
- columns: z
228
- .array(
229
- z.object({
230
- position: z.enum(["start", "center", "end"]).optional(),
231
- title: z.string(),
232
- links: z.array(z.object({ label: z.string(), href: z.string() })),
233
- }),
234
- )
235
- .optional(),
236
- social: z.array(FooterSocialSchema).optional(),
237
- copyright: z.string().optional(),
238
- logo: LogoSchema.optional(),
239
- position: z.enum(["start", "center", "end"]).optional(),
240
- })
241
- .optional();
242
-
243
- const SiteMapSchema = z
244
- .object({
245
- /**
246
- * Base url of your website
247
- */
248
- siteUrl: z.string(),
249
- /**
250
- * Change frequency.
251
- * @default 'daily'
252
- */
253
- changefreq: z.optional(
254
- z.enum([
255
- "always",
256
- "hourly",
257
- "daily",
258
- "weekly",
259
- "monthly",
260
- "yearly",
261
- "never",
262
- ]),
263
- ),
264
- /**
265
- * Priority
266
- * @default 0.7
267
- */
268
- priority: z.optional(z.number()),
269
- outDir: z.string().optional(),
270
- /**
271
- * Add <lastmod/> property.
272
- * @default true
273
- */
274
- autoLastmod: z.boolean().optional(),
275
- /**
276
- * Array of relative paths to exclude from listing on sitemap.xml or sitemap-*.xml.
277
- * @example ['/page-0', '/page/example']
278
- */
279
- exclude: z
280
- .union([
281
- z.custom<() => Promise<string[]>>((val) => typeof val === "function"),
282
- z.array(z.string()),
283
- ])
284
- .optional(),
285
- })
286
- .optional();
287
-
288
- const DEFAULT_DOCS_FILES = "/pages/**/*.{md,mdx}";
289
-
290
- const LlmsConfigSchema = z
291
- .object({
292
- llmsTxt: z
293
- .boolean()
294
- .default(false)
295
- .describe(
296
- "When enabled, generates an llms.txt file following the spec at https://llmstxt.org/. This file provides a summary with links to all documentation.",
297
- ),
298
- llmsTxtFull: z
299
- .boolean()
300
- .default(false)
301
- .describe(
302
- "When enabled, generates an llms-full.txt file with the complete content of all markdown documents for LLM consumption.",
303
- ),
304
- includeProtected: z
305
- .boolean()
306
- .default(false)
307
- .describe(
308
- "When enabled, includes content from protected routes in the generated .md files and llms.txt files. By default, protected routes are excluded.",
309
- ),
310
- })
311
- .partial();
312
-
313
- export const DocsConfigSchema = z.object({
314
- files: z
315
- .union([z.string(), z.array(z.string())])
316
- .transform((val) => (typeof val === "string" ? [val] : val))
317
- .default([DEFAULT_DOCS_FILES]),
318
- publishMarkdown: z
319
- .boolean()
320
- .default(false)
321
- .describe(
322
- "When enabled, generates .md files for each document during build. Access documents at their URL path with .md extension (e.g., /foo/hello.md). Markdown files are generated without frontmatter.",
323
- ),
324
- defaultOptions: z
325
- .object({
326
- toc: z.boolean(),
327
- copyPage: z.boolean().optional(),
328
- disablePager: z.boolean(),
329
- showLastModified: z.boolean(),
330
- suggestEdit: z
331
- .object({
332
- url: z.string(),
333
- text: z.string().optional(),
334
- })
335
- .optional(),
336
- })
337
- .partial()
338
- .optional(),
339
- llms: LlmsConfigSchema.optional(),
340
- });
341
-
342
- const Redirect = z.object({
343
- from: z.string(),
344
- to: z.string(),
345
- });
346
-
347
- const SearchSchema = z
348
- .discriminatedUnion("type", [
349
- // looseObject to allow additional properties so the
350
- // user can set other inkeep settings
351
- z.looseObject({
352
- type: z.literal("inkeep"),
353
- apiKey: z.string(),
354
- integrationId: z.string(),
355
- organizationId: z.string(),
356
- primaryBrandColor: z.string(),
357
- organizationDisplayName: z.string(),
358
- }),
359
- z.object({
360
- type: z.literal("pagefind"),
361
-
362
- ranking: z
363
- .object({
364
- termFrequency: z.number(),
365
- pageLength: z.number(),
366
- termSimilarity: z.number(),
367
- termSaturation: z.number(),
368
- })
369
- .optional(),
370
- maxResults: z.number().optional(),
371
- maxSubResults: z.number().optional(),
372
- transformResults: z
373
- .custom<
374
- (data: {
375
- result: PagefindSearchFragment;
376
- auth: AuthState;
377
- context: ZudokuContext;
378
- }) => PagefindSearchFragment | boolean | undefined | void
379
- >((val) => typeof val === "function")
380
- .optional(),
381
- }),
382
- ])
383
- .optional();
384
-
385
- const AuthenticationSchema = z.discriminatedUnion("type", [
386
- z.object({
387
- type: z.literal("clerk"),
388
- clerkPubKey: z
389
- .custom<`pk_test_${string}` | `pk_live_${string}`>()
390
- .refine((val) => /^pk_(test|live)_\w+$/.test(val), {
391
- message: "Clerk public key invalid, must start with pk_test or pk_live",
392
- }),
393
- jwtTemplateName: z.string().optional().default("dev-portal"),
394
- redirectToAfterSignUp: z.string().optional(),
395
- redirectToAfterSignIn: z.string().optional(),
396
- redirectToAfterSignOut: z.string().optional(),
397
- }),
398
- z.object({
399
- type: z.literal("firebase"),
400
- apiKey: z.string(),
401
- authDomain: z.string(),
402
- projectId: z.string(),
403
- storageBucket: z.string().optional(),
404
- messagingSenderId: z.string().optional(),
405
- appId: z.string(),
406
- measurementId: z.string().optional(),
407
- providers: z
408
- .array(
409
- z.enum([
410
- "google",
411
- "facebook",
412
- "twitter",
413
- "github",
414
- "microsoft",
415
- "apple",
416
- "yahoo",
417
- "password",
418
- "phone",
419
- "emailLink",
420
- ]),
421
- )
422
- .optional(),
423
- redirectToAfterSignUp: z.string().optional(),
424
- redirectToAfterSignIn: z.string().optional(),
425
- redirectToAfterSignOut: z.string().optional(),
426
- }),
427
- z.object({
428
- type: z.literal("openid"),
429
- basePath: z.string().optional(),
430
- clientId: z.string(),
431
- issuer: z.string(),
432
- audience: z.string().optional(),
433
- scopes: z.array(z.string()).optional(),
434
- redirectToAfterSignUp: z.string().optional(),
435
- redirectToAfterSignIn: z.string().optional(),
436
- redirectToAfterSignOut: z.string().optional(),
437
- }),
438
- z.object({
439
- type: z.literal("azureb2c"),
440
- basePath: z.string().optional(),
441
- clientId: z.string(),
442
- tenantName: z.string(),
443
- policyName: z.string(),
444
- scopes: z.array(z.string()).optional(),
445
- issuer: z.string(),
446
- redirectToAfterSignUp: z.string().optional(),
447
- redirectToAfterSignIn: z.string().optional(),
448
- redirectToAfterSignOut: z.string().optional(),
449
- }),
450
-
451
- z.object({
452
- type: z.literal("auth0"),
453
- clientId: z.string(),
454
- domain: z.string().refine(
455
- (val) => {
456
- if (val.startsWith("http://") || val.startsWith("https://")) {
457
- return false;
458
- }
459
- if (val.includes("/")) {
460
- return false;
461
- }
462
- return val.includes(".") && val.length > 0;
463
- },
464
- {
465
- message:
466
- "Domain must be a host only (e.g., 'example.com') without protocol or slashes",
467
- },
468
- ),
469
- audience: z.string().optional(),
470
- scopes: z.array(z.string()).optional(),
471
- redirectToAfterSignUp: z.string().optional(),
472
- redirectToAfterSignIn: z.string().optional(),
473
- redirectToAfterSignOut: z.string().optional(),
474
- options: z
475
- .object({
476
- alwaysPromptLogin: z.boolean().optional(),
477
- prompt: z.string().optional(),
478
- })
479
- .optional(),
480
- }),
481
- z.object({
482
- type: z.literal("supabase"),
483
- basePath: z.string().optional(),
484
- supabaseUrl: z.string(),
485
- supabaseKey: z.string(),
486
- provider: z.string().optional(),
487
- providers: z.array(z.string()).optional(),
488
- onlyThirdPartyProviders: z.boolean().optional(),
489
- redirectToAfterSignUp: z.string().optional(),
490
- redirectToAfterSignIn: z.string().optional(),
491
- redirectToAfterSignOut: z.string().optional(),
492
- }),
493
- ]);
494
-
495
- const MetadataSchema = z
496
- .object({
497
- title: z.string(),
498
- defaultTitle: z.string().optional(),
499
- description: z.string(),
500
- logo: z.string(),
501
- favicon: z.string(),
502
- generator: z.string(),
503
- applicationName: z.string(),
504
- referrer: z.string(),
505
- keywords: z.array(z.string()),
506
- authors: z.array(z.string()),
507
- creator: z.string(),
508
- publisher: z.string(),
509
- })
510
- .partial();
511
-
512
- const FontConfigSchema = z.union([
513
- z.enum(GOOGLE_FONTS),
514
- z.object({
515
- url: z.string(),
516
- fontFamily: z.string().optional(),
517
- }),
518
- ]);
519
-
520
- export type FontConfig = z.infer<typeof FontConfigSchema>;
521
-
522
- const FontsConfigSchema = z.object({
523
- sans: FontConfigSchema.optional(),
524
- serif: FontConfigSchema.optional(),
525
- mono: FontConfigSchema.optional(),
526
- });
527
-
528
- const CssObject = z.record(
529
- z.string(),
530
- z.lazy(() =>
531
- z.union([
532
- z.string(),
533
- z.record(
534
- z.string(),
535
- z.union([z.string(), z.record(z.string(), z.string())]),
536
- ),
537
- ]),
538
- ),
539
- );
540
-
541
- const ThemeConfigSchema = z.object({
542
- registryUrl: z.string().url().optional(),
543
- customCss: z.union([z.string(), CssObject]).optional(),
544
- light: ThemeSchema.optional(),
545
- dark: ThemeSchema.optional(),
546
- fonts: FontsConfigSchema.optional(),
547
- noDefaultTheme: z.boolean().optional(),
548
- });
549
-
550
- const SiteSchema = z
551
- .object({
552
- title: z.string(),
553
- logoUrl: z.string(),
554
- dir: z.enum(["ltr", "rtl"]).optional(),
555
- logo: LogoSchema,
556
- banner: z.object({
557
- message: z.custom<NonNullable<ReactNode>>(),
558
- color: z
559
- .custom<"note" | "tip" | "info" | "caution" | "danger" | (string & {})>(
560
- (val) => typeof val === "string",
561
- )
562
- .optional(),
563
- dismissible: z.boolean().optional(),
564
- }),
565
- footer: FooterSchema,
566
- })
567
- .partial();
568
-
569
- const PlacementPosition = z.enum(["start", "center", "end"]);
570
-
571
- const HeaderConfigSchema = z
572
- .object({
573
- navigation: HeaderNavigationSchema,
574
- placements: z
575
- .object({
576
- navigation: PlacementPosition,
577
- search: PlacementPosition,
578
- auth: z.enum(["start", "center", "end", "navigation"]),
579
- })
580
- .partial()
581
- .optional(),
582
- })
583
- .partial();
584
-
585
- const ApiCatalogSchema = z.object({
586
- path: z.string(),
587
- label: z.string(),
588
- items: z.array(z.string()).optional(),
589
- filterItems: z
590
- .custom<FilterCatalogItemsFn>((val) => typeof val === "function")
591
- .optional(),
592
- });
593
-
594
- export const CdnUrlSchema = z
595
- .union([
596
- z.string(),
597
- z.object({
598
- base: z.string().optional(),
599
- media: z.string().optional(),
600
- }),
601
- ])
602
- .transform((val) => {
603
- if (typeof val === "string") {
604
- return { base: val, media: val };
605
- }
606
- return { base: val.base, media: val.media };
607
- })
608
- .optional();
609
-
610
- const BaseConfigSchema = z.object({
611
- slots: z.record(z.string(), z.custom<SlotType>()),
612
- /**
613
- * @deprecated Use `slots` instead
614
- */
615
- UNSAFE_slotlets: z.record(z.string(), z.custom<SlotType>()),
616
- mdx: z
617
- .object({
618
- components: z.custom<MdxComponentsType>(),
619
- })
620
- .partial(),
621
- customPages: z.array(
622
- z.object({
623
- path: z.string(),
624
- element: z.custom<NonNullable<ReactNode>>().optional(),
625
- render: z.custom<ComponentType<ExposedComponentProps>>().optional(),
626
- prose: z.boolean().optional(),
627
- }),
628
- ),
629
- plugins: z.array(z.custom<ApitogoPlugin>()),
630
- build: z.custom<{
631
- remarkPlugins?: Options["remarkPlugins"];
632
- rehypePlugins?: Options["rehypePlugins"];
633
- }>(),
634
- protectedRoutes: ProtectedRoutesSchema,
635
- basePath: z.string().optional(),
636
- canonicalUrlOrigin: z.string().optional(),
637
- cdnUrl: CdnUrlSchema.optional(),
638
- port: z.number().optional(),
639
- https: z
640
- .object({
641
- key: z.string(),
642
- cert: z.string(),
643
- ca: z.string().optional(),
644
- })
645
- .optional(),
646
- site: SiteSchema,
647
- header: HeaderConfigSchema.optional(),
648
- navigation: InputNavigationSchema,
649
- navigationRules: NavigationRulesSchema.optional(),
650
- theme: ThemeConfigSchema,
651
- syntaxHighlighting: z
652
- .object({
653
- languages: z.array(z.custom<BundledLanguage>()),
654
- themes: z.object({
655
- light: z.custom<BundledTheme>(),
656
- dark: z.custom<BundledTheme>(),
657
- }),
658
- })
659
- .partial()
660
- .optional(),
661
- metadata: MetadataSchema,
662
- authentication: AuthenticationSchema,
663
- search: SearchSchema,
664
- docs: DocsConfigSchema.optional(),
665
- apis: z.union([ApiSchema, z.array(ApiSchema)]),
666
- catalogs: z.union([ApiCatalogSchema, z.array(ApiCatalogSchema)]),
667
- apiKeys: ApiKeysSchema,
668
- aiAssistants: AiAssistantsSchema,
669
- redirects: z.array(Redirect),
670
- sitemap: SiteMapSchema,
671
- enableStatusPages: z.boolean().optional(),
672
- defaults: z.object({
673
- apis: ApiOptionsSchema,
674
- /**
675
- * @deprecated Use `apis.examplesLanguage` or `defaults.apis.examplesLanguage` instead
676
- */
677
- examplesLanguage: z.string().optional(),
678
- }),
679
- // Internal: populated by plugins via `transformConfig` to track plugin directories
680
- __pluginDirs: z.array(z.string()),
681
- });
682
-
683
- export const ZudokuConfig = BaseConfigSchema.partial();
684
-
685
- export type ZudokuApiConfig = z.infer<typeof ApiSchema>;
686
- export type VersionConfig = z.infer<typeof VersionConfigSchema>;
687
- export type ZudokuSiteMapConfig = z.infer<typeof SiteMapSchema>;
688
- export type ZudokuDocsConfig = z.infer<typeof DocsConfigSchema>;
689
- export type ZudokuLlmsConfig = z.infer<typeof LlmsConfigSchema>;
690
- export type ZudokuRedirect = z.infer<typeof Redirect>;
691
- export type AuthenticationConfig = z.infer<typeof AuthenticationSchema>;
692
-
693
- // Use `z.input` type for flexibility with transforms,
694
- // but override navigation with `z.infer` for strict validation
695
- type BaseZudokuConfig = z.input<typeof ZudokuConfig>;
696
- export type ZudokuConfig = Omit<
697
- BaseZudokuConfig,
698
- "header" | "navigation" | "navigationRules"
699
- > & {
700
- header?: {
701
- navigation?: z.infer<typeof HeaderNavigationSchema>;
702
- placements?: {
703
- navigation?: "start" | "center" | "end";
704
- search?: "start" | "center" | "end";
705
- auth?: "start" | "center" | "end" | "navigation";
706
- };
707
- };
708
- navigation?: z.infer<typeof InputNavigationSchema>;
709
- navigationRules?: z.infer<typeof NavigationRulesSchema>;
710
- };
711
-
712
- export function validateConfig(config: unknown, configPath?: string) {
713
- const validationResult = ZudokuConfig.safeParse(config);
714
-
715
- if (!validationResult.success) {
716
- // In production (build mode), throw an error to fail the build
717
- if (process.env.NODE_ENV === "production") {
718
- throw new Error(
719
- `Whoops, looks like there's an issue with your ${configPath ?? "config"}:\n${z.prettifyError(validationResult.error)}`,
720
- );
721
- }
722
-
723
- // In development mode, log warnings but don't fail
724
- // biome-ignore lint/suspicious/noConsole: Logging allowed here
725
- console.log(colors.yellow("Validation errors:"));
726
- // biome-ignore lint/suspicious/noConsole: Logging allowed here
727
- console.log(colors.yellow(z.prettifyError(validationResult.error)));
728
- }
729
- }
1
+ import type { Options } from "@mdx-js/rollup";
2
+ import colors from "picocolors";
3
+ import type { ComponentType, ReactNode } from "react";
4
+ import { isValidElement } from "react";
5
+ import type { BundledLanguage, BundledTheme } from "shiki";
6
+ import { z } from "zod";
7
+ import type { UseAuthReturn } from "../../lib/authentication/hook.js";
8
+ import type { AuthState } from "../../lib/authentication/state.js";
9
+ import type { SlotType } from "../../lib/components/context/SlotProvider.js";
10
+ import type { ApitogoPlugin } from "../../lib/core/plugins.js";
11
+ import type { ZudokuContext } from "../../lib/core/ZudokuContext.js";
12
+ import type { FilterCatalogItemsFn } from "../../lib/plugins/api-catalog/index.js";
13
+ import type { ApiConsumer } from "../../lib/plugins/api-keys/index.js";
14
+ import type {
15
+ GenerateCodeSnippetFn,
16
+ TransformExamplesFn,
17
+ } from "../../lib/plugins/openapi/interfaces.js";
18
+ import type { PagefindSearchFragment } from "../../lib/plugins/search-pagefind/types.js";
19
+ import type { MdxComponentsType } from "../../lib/util/MdxComponents.js";
20
+ import type { ExposedComponentProps } from "../../lib/util/useExposedProps.js";
21
+ import { GOOGLE_FONTS } from "../../vite/plugin-theme.js";
22
+ import { HeaderNavigationSchema } from "./HeaderNavigationSchema.js";
23
+ import {
24
+ InputNavigationSchema,
25
+ NavigationRulesSchema,
26
+ } from "./InputNavigationSchema.js";
27
+ import { ModulesSchema } from "./ModulesSchema.js";
28
+ import { ProtectedRoutesSchema } from "./ProtectedRoutesSchema.js";
29
+
30
+ const ThemeSchema = z
31
+ .object({
32
+ background: z.string(),
33
+ foreground: z.string(),
34
+ card: z.string(),
35
+ cardForeground: z.string(),
36
+ popover: z.string(),
37
+ popoverForeground: z.string(),
38
+ primary: z.string(),
39
+ primaryForeground: z.string(),
40
+ secondary: z.string(),
41
+ secondaryForeground: z.string(),
42
+ muted: z.string(),
43
+ mutedForeground: z.string(),
44
+ accent: z.string(),
45
+ accentForeground: z.string(),
46
+ destructive: z.string(),
47
+ destructiveForeground: z.string(),
48
+ border: z.string(),
49
+ input: z.string(),
50
+ ring: z.string(),
51
+ radius: z.string(),
52
+ })
53
+ .partial();
54
+
55
+ const ApiCatalogCategorySchema = z.object({
56
+ label: z.string(),
57
+ tags: z.array(z.string()),
58
+ });
59
+
60
+ const LanguageOption = z.object({
61
+ value: z.string().min(1),
62
+ label: z.string().min(1),
63
+ });
64
+
65
+ const AiAssistantCustomSchema = z.object({
66
+ label: z.string(),
67
+ icon: z.custom<ReactNode>().optional(),
68
+ url: z.union([
69
+ z.string(),
70
+ z.custom<
71
+ (context: { pageUrl: string; type: "docs" | "openapi" }) => string
72
+ >((val) => typeof val === "function"),
73
+ ]),
74
+ });
75
+
76
+ export type AiAssistantCustom = z.infer<typeof AiAssistantCustomSchema>;
77
+
78
+ const AiAssistantPresets = ["claude", "chatgpt"] as const;
79
+
80
+ const AiAssistantsSchema = z
81
+ .union([
82
+ z.literal(false),
83
+ z.array(z.union([z.enum(AiAssistantPresets), AiAssistantCustomSchema])),
84
+ ])
85
+ .optional();
86
+
87
+ export type AiAssistantsConfig = z.infer<typeof AiAssistantsSchema>;
88
+
89
+ const ApiOptionsSchema = z
90
+ .object({
91
+ examplesLanguage: z.string(),
92
+ supportedLanguages: z.array(LanguageOption),
93
+ disablePlayground: z.boolean(),
94
+ disableSidecar: z.boolean(),
95
+ showVersionSelect: z.enum(["always", "if-available", "hide"]),
96
+ expandAllTags: z.boolean(),
97
+ showInfoPage: z.boolean(),
98
+ schemaDownload: z.object({ enabled: z.boolean() }).partial(),
99
+ transformExamples: z.custom<TransformExamplesFn>(
100
+ (val) => typeof val === "function",
101
+ ),
102
+ generateCodeSnippet: z.custom<GenerateCodeSnippetFn>(
103
+ (val) => typeof val === "function",
104
+ ),
105
+ })
106
+ .partial();
107
+
108
+ const ApiConfigSchema = z
109
+ .object({
110
+ server: z.string(),
111
+ path: z.string(),
112
+ categories: z.array(ApiCatalogCategorySchema),
113
+ options: ApiOptionsSchema,
114
+ })
115
+ .partial();
116
+
117
+ const VersionConfigSchema = z.object({
118
+ path: z.string(),
119
+ input: z.string(),
120
+ label: z.string().optional(),
121
+ });
122
+
123
+ const ApiSchema = z.discriminatedUnion("type", [
124
+ z.object({
125
+ type: z.literal("url"),
126
+ input: z.union([z.string(), z.array(VersionConfigSchema)]),
127
+ ...ApiConfigSchema.shape,
128
+ }),
129
+ z.object({
130
+ type: z.literal("file"),
131
+ input: z.union([
132
+ z.string(),
133
+ z.array(z.union([z.string(), VersionConfigSchema])),
134
+ ]),
135
+ ...ApiConfigSchema.shape,
136
+ }),
137
+ z.object({
138
+ type: z.literal("raw"),
139
+ input: z.string(),
140
+ ...ApiConfigSchema.shape,
141
+ }),
142
+ ]);
143
+
144
+ const ApiKeysSchema = z.object({
145
+ enabled: z.boolean(),
146
+ getConsumers: z
147
+ .custom<(context: ZudokuContext) => Promise<ApiConsumer[]>>(
148
+ (val) => typeof val === "function",
149
+ )
150
+ .optional(),
151
+ rollKey: z
152
+ .custom<(id: string, context: ZudokuContext) => Promise<void>>(
153
+ (val) => typeof val === "function",
154
+ )
155
+ .optional(),
156
+ deleteKey: z
157
+ .custom<
158
+ (
159
+ consumerId: string,
160
+ keyId: string,
161
+ context: ZudokuContext,
162
+ ) => Promise<void>
163
+ >((val) => typeof val === "function")
164
+ .optional(),
165
+ updateKeyDescription: z
166
+ .custom<
167
+ (
168
+ apiKey: { id: string; description: string },
169
+ context: ZudokuContext,
170
+ ) => Promise<void>
171
+ >((val) => typeof val === "function")
172
+ .optional(),
173
+ createKey: z
174
+ .custom<
175
+ ({
176
+ apiKey,
177
+ context,
178
+ auth,
179
+ }: {
180
+ apiKey: { description: string; expiresOn?: string };
181
+ context: ZudokuContext;
182
+ auth: UseAuthReturn;
183
+ }) => Promise<void>
184
+ >((val) => typeof val === "function")
185
+ .optional(),
186
+ });
187
+
188
+ export type ApiKeysOptions = z.infer<typeof ApiKeysSchema>;
189
+
190
+ const LogoSchema = z.object({
191
+ src: z.object({ light: z.string(), dark: z.string() }),
192
+ alt: z.string().optional(),
193
+ width: z.string().or(z.number()).optional(),
194
+ href: z.string().optional(),
195
+ reloadDocument: z.boolean().optional(),
196
+ });
197
+
198
+ export const FooterSocialIcons = [
199
+ "reddit",
200
+ "discord",
201
+ "github",
202
+ "x",
203
+ "linkedin",
204
+ "facebook",
205
+ "instagram",
206
+ "youtube",
207
+ "tiktok",
208
+ "twitch",
209
+ "pinterest",
210
+ "snapchat",
211
+ "whatsapp",
212
+ "telegram",
213
+ ] as const;
214
+
215
+ export const FooterSocialSchema = z.object({
216
+ label: z.string().optional(),
217
+ href: z.string(),
218
+ icon: z
219
+ .union([
220
+ z.enum(FooterSocialIcons),
221
+ z.custom<ReactNode>((val) => isValidElement(val)),
222
+ ])
223
+ .optional(),
224
+ });
225
+
226
+ export const FooterSchema = z
227
+ .object({
228
+ columns: z
229
+ .array(
230
+ z.object({
231
+ position: z.enum(["start", "center", "end"]).optional(),
232
+ title: z.string(),
233
+ links: z.array(z.object({ label: z.string(), href: z.string() })),
234
+ }),
235
+ )
236
+ .optional(),
237
+ social: z.array(FooterSocialSchema).optional(),
238
+ copyright: z.string().optional(),
239
+ logo: LogoSchema.optional(),
240
+ position: z.enum(["start", "center", "end"]).optional(),
241
+ })
242
+ .optional();
243
+
244
+ const SiteMapSchema = z
245
+ .object({
246
+ /**
247
+ * Base url of your website
248
+ */
249
+ siteUrl: z.string(),
250
+ /**
251
+ * Change frequency.
252
+ * @default 'daily'
253
+ */
254
+ changefreq: z.optional(
255
+ z.enum([
256
+ "always",
257
+ "hourly",
258
+ "daily",
259
+ "weekly",
260
+ "monthly",
261
+ "yearly",
262
+ "never",
263
+ ]),
264
+ ),
265
+ /**
266
+ * Priority
267
+ * @default 0.7
268
+ */
269
+ priority: z.optional(z.number()),
270
+ outDir: z.string().optional(),
271
+ /**
272
+ * Add <lastmod/> property.
273
+ * @default true
274
+ */
275
+ autoLastmod: z.boolean().optional(),
276
+ /**
277
+ * Array of relative paths to exclude from listing on sitemap.xml or sitemap-*.xml.
278
+ * @example ['/page-0', '/page/example']
279
+ */
280
+ exclude: z
281
+ .union([
282
+ z.custom<() => Promise<string[]>>((val) => typeof val === "function"),
283
+ z.array(z.string()),
284
+ ])
285
+ .optional(),
286
+ })
287
+ .optional();
288
+
289
+ const DEFAULT_DOCS_FILES = "/pages/**/*.{md,mdx}";
290
+
291
+ const LlmsConfigSchema = z
292
+ .object({
293
+ llmsTxt: z
294
+ .boolean()
295
+ .default(false)
296
+ .describe(
297
+ "When enabled, generates an llms.txt file following the spec at https://llmstxt.org/. This file provides a summary with links to all documentation.",
298
+ ),
299
+ llmsTxtFull: z
300
+ .boolean()
301
+ .default(false)
302
+ .describe(
303
+ "When enabled, generates an llms-full.txt file with the complete content of all markdown documents for LLM consumption.",
304
+ ),
305
+ includeProtected: z
306
+ .boolean()
307
+ .default(false)
308
+ .describe(
309
+ "When enabled, includes content from protected routes in the generated .md files and llms.txt files. By default, protected routes are excluded.",
310
+ ),
311
+ })
312
+ .partial();
313
+
314
+ export const DocsConfigSchema = z.object({
315
+ files: z
316
+ .union([z.string(), z.array(z.string())])
317
+ .transform((val) => (typeof val === "string" ? [val] : val))
318
+ .default([DEFAULT_DOCS_FILES]),
319
+ publishMarkdown: z
320
+ .boolean()
321
+ .default(false)
322
+ .describe(
323
+ "When enabled, generates .md files for each document during build. Access documents at their URL path with .md extension (e.g., /foo/hello.md). Markdown files are generated without frontmatter.",
324
+ ),
325
+ defaultOptions: z
326
+ .object({
327
+ toc: z.boolean(),
328
+ copyPage: z.boolean().optional(),
329
+ disablePager: z.boolean(),
330
+ showLastModified: z.boolean(),
331
+ suggestEdit: z
332
+ .object({
333
+ url: z.string(),
334
+ text: z.string().optional(),
335
+ })
336
+ .optional(),
337
+ })
338
+ .partial()
339
+ .optional(),
340
+ llms: LlmsConfigSchema.optional(),
341
+ });
342
+
343
+ const Redirect = z.object({
344
+ from: z.string(),
345
+ to: z.string(),
346
+ });
347
+
348
+ const SearchSchema = z
349
+ .discriminatedUnion("type", [
350
+ // looseObject to allow additional properties so the
351
+ // user can set other inkeep settings
352
+ z.looseObject({
353
+ type: z.literal("inkeep"),
354
+ apiKey: z.string(),
355
+ integrationId: z.string(),
356
+ organizationId: z.string(),
357
+ primaryBrandColor: z.string(),
358
+ organizationDisplayName: z.string(),
359
+ }),
360
+ z.object({
361
+ type: z.literal("pagefind"),
362
+
363
+ ranking: z
364
+ .object({
365
+ termFrequency: z.number(),
366
+ pageLength: z.number(),
367
+ termSimilarity: z.number(),
368
+ termSaturation: z.number(),
369
+ })
370
+ .optional(),
371
+ maxResults: z.number().optional(),
372
+ maxSubResults: z.number().optional(),
373
+ transformResults: z
374
+ .custom<
375
+ (data: {
376
+ result: PagefindSearchFragment;
377
+ auth: AuthState;
378
+ context: ZudokuContext;
379
+ }) => PagefindSearchFragment | boolean | undefined | void
380
+ >((val) => typeof val === "function")
381
+ .optional(),
382
+ }),
383
+ ])
384
+ .optional();
385
+
386
+ const AuthenticationSchema = z.discriminatedUnion("type", [
387
+ z.object({
388
+ type: z.literal("clerk"),
389
+ clerkPubKey: z
390
+ .custom<`pk_test_${string}` | `pk_live_${string}`>()
391
+ .refine((val) => /^pk_(test|live)_\w+$/.test(val), {
392
+ message: "Clerk public key invalid, must start with pk_test or pk_live",
393
+ }),
394
+ jwtTemplateName: z.string().optional().default("dev-portal"),
395
+ redirectToAfterSignUp: z.string().optional(),
396
+ redirectToAfterSignIn: z.string().optional(),
397
+ redirectToAfterSignOut: z.string().optional(),
398
+ }),
399
+ z.object({
400
+ type: z.literal("firebase"),
401
+ apiKey: z.string(),
402
+ authDomain: z.string(),
403
+ projectId: z.string(),
404
+ storageBucket: z.string().optional(),
405
+ messagingSenderId: z.string().optional(),
406
+ appId: z.string(),
407
+ measurementId: z.string().optional(),
408
+ providers: z
409
+ .array(
410
+ z.enum([
411
+ "google",
412
+ "facebook",
413
+ "twitter",
414
+ "github",
415
+ "microsoft",
416
+ "apple",
417
+ "yahoo",
418
+ "password",
419
+ "phone",
420
+ "emailLink",
421
+ ]),
422
+ )
423
+ .optional(),
424
+ redirectToAfterSignUp: z.string().optional(),
425
+ redirectToAfterSignIn: z.string().optional(),
426
+ redirectToAfterSignOut: z.string().optional(),
427
+ }),
428
+ z.object({
429
+ type: z.literal("openid"),
430
+ basePath: z.string().optional(),
431
+ clientId: z.string(),
432
+ issuer: z.string(),
433
+ audience: z.string().optional(),
434
+ scopes: z.array(z.string()).optional(),
435
+ redirectToAfterSignUp: z.string().optional(),
436
+ redirectToAfterSignIn: z.string().optional(),
437
+ redirectToAfterSignOut: z.string().optional(),
438
+ }),
439
+ z.object({
440
+ type: z.literal("azureb2c"),
441
+ basePath: z.string().optional(),
442
+ clientId: z.string(),
443
+ tenantName: z.string(),
444
+ policyName: z.string(),
445
+ scopes: z.array(z.string()).optional(),
446
+ issuer: z.string(),
447
+ redirectToAfterSignUp: z.string().optional(),
448
+ redirectToAfterSignIn: z.string().optional(),
449
+ redirectToAfterSignOut: z.string().optional(),
450
+ }),
451
+
452
+ z.object({
453
+ type: z.literal("auth0"),
454
+ clientId: z.string(),
455
+ domain: z.string().refine(
456
+ (val) => {
457
+ if (val.startsWith("http://") || val.startsWith("https://")) {
458
+ return false;
459
+ }
460
+ if (val.includes("/")) {
461
+ return false;
462
+ }
463
+ return val.includes(".") && val.length > 0;
464
+ },
465
+ {
466
+ message:
467
+ "Domain must be a host only (e.g., 'example.com') without protocol or slashes",
468
+ },
469
+ ),
470
+ audience: z.string().optional(),
471
+ scopes: z.array(z.string()).optional(),
472
+ redirectToAfterSignUp: z.string().optional(),
473
+ redirectToAfterSignIn: z.string().optional(),
474
+ redirectToAfterSignOut: z.string().optional(),
475
+ options: z
476
+ .object({
477
+ alwaysPromptLogin: z.boolean().optional(),
478
+ prompt: z.string().optional(),
479
+ })
480
+ .optional(),
481
+ }),
482
+ z.object({
483
+ type: z.literal("supabase"),
484
+ basePath: z.string().optional(),
485
+ supabaseUrl: z.string(),
486
+ supabaseKey: z.string(),
487
+ provider: z.string().optional(),
488
+ providers: z.array(z.string()).optional(),
489
+ onlyThirdPartyProviders: z.boolean().optional(),
490
+ redirectToAfterSignUp: z.string().optional(),
491
+ redirectToAfterSignIn: z.string().optional(),
492
+ redirectToAfterSignOut: z.string().optional(),
493
+ }),
494
+ z.object({
495
+ type: z.literal("dev-portal"),
496
+ apiBaseUrl: z.string().optional(),
497
+ redirectToAfterSignUp: z.string().optional(),
498
+ redirectToAfterSignIn: z.string().optional(),
499
+ redirectToAfterSignOut: z.string().optional(),
500
+ }),
501
+ ]);
502
+
503
+ const MetadataSchema = z
504
+ .object({
505
+ title: z.string(),
506
+ defaultTitle: z.string().optional(),
507
+ description: z.string(),
508
+ logo: z.string(),
509
+ favicon: z.string(),
510
+ generator: z.string(),
511
+ applicationName: z.string(),
512
+ referrer: z.string(),
513
+ keywords: z.array(z.string()),
514
+ authors: z.array(z.string()),
515
+ creator: z.string(),
516
+ publisher: z.string(),
517
+ })
518
+ .partial();
519
+
520
+ const FontConfigSchema = z.union([
521
+ z.enum(GOOGLE_FONTS),
522
+ z.object({
523
+ url: z.string(),
524
+ fontFamily: z.string().optional(),
525
+ }),
526
+ ]);
527
+
528
+ export type FontConfig = z.infer<typeof FontConfigSchema>;
529
+
530
+ const FontsConfigSchema = z.object({
531
+ sans: FontConfigSchema.optional(),
532
+ serif: FontConfigSchema.optional(),
533
+ mono: FontConfigSchema.optional(),
534
+ });
535
+
536
+ const CssObject = z.record(
537
+ z.string(),
538
+ z.lazy(() =>
539
+ z.union([
540
+ z.string(),
541
+ z.record(
542
+ z.string(),
543
+ z.union([z.string(), z.record(z.string(), z.string())]),
544
+ ),
545
+ ]),
546
+ ),
547
+ );
548
+
549
+ const ThemeConfigSchema = z.object({
550
+ registryUrl: z.string().url().optional(),
551
+ customCss: z.union([z.string(), CssObject]).optional(),
552
+ light: ThemeSchema.optional(),
553
+ dark: ThemeSchema.optional(),
554
+ fonts: FontsConfigSchema.optional(),
555
+ noDefaultTheme: z.boolean().optional(),
556
+ });
557
+
558
+ const SiteSchema = z
559
+ .object({
560
+ title: z.string(),
561
+ logoUrl: z.string(),
562
+ dir: z.enum(["ltr", "rtl"]).optional(),
563
+ logo: LogoSchema,
564
+ banner: z.object({
565
+ message: z.custom<NonNullable<ReactNode>>(),
566
+ color: z
567
+ .custom<"note" | "tip" | "info" | "caution" | "danger" | (string & {})>(
568
+ (val) => typeof val === "string",
569
+ )
570
+ .optional(),
571
+ dismissible: z.boolean().optional(),
572
+ }),
573
+ footer: FooterSchema,
574
+ })
575
+ .partial();
576
+
577
+ const PlacementPosition = z.enum(["start", "center", "end"]);
578
+
579
+ const HeaderConfigSchema = z
580
+ .object({
581
+ navigation: HeaderNavigationSchema,
582
+ placements: z
583
+ .object({
584
+ navigation: PlacementPosition,
585
+ search: PlacementPosition,
586
+ auth: z.enum(["start", "center", "end", "navigation"]),
587
+ })
588
+ .partial()
589
+ .optional(),
590
+ })
591
+ .partial();
592
+
593
+ const ApiCatalogSchema = z.object({
594
+ path: z.string(),
595
+ label: z.string(),
596
+ items: z.array(z.string()).optional(),
597
+ filterItems: z
598
+ .custom<FilterCatalogItemsFn>((val) => typeof val === "function")
599
+ .optional(),
600
+ });
601
+
602
+ export const CdnUrlSchema = z
603
+ .union([
604
+ z.string(),
605
+ z.object({
606
+ base: z.string().optional(),
607
+ media: z.string().optional(),
608
+ }),
609
+ ])
610
+ .transform((val) => {
611
+ if (typeof val === "string") {
612
+ return { base: val, media: val };
613
+ }
614
+ return { base: val.base, media: val.media };
615
+ })
616
+ .optional();
617
+
618
+ const BaseConfigSchema = z.object({
619
+ slots: z.record(z.string(), z.custom<SlotType>()),
620
+ /**
621
+ * @deprecated Use `slots` instead
622
+ */
623
+ UNSAFE_slotlets: z.record(z.string(), z.custom<SlotType>()),
624
+ mdx: z
625
+ .object({
626
+ components: z.custom<MdxComponentsType>(),
627
+ })
628
+ .partial(),
629
+ customPages: z.array(
630
+ z.object({
631
+ path: z.string(),
632
+ element: z.custom<NonNullable<ReactNode>>().optional(),
633
+ render: z.custom<ComponentType<ExposedComponentProps>>().optional(),
634
+ prose: z.boolean().optional(),
635
+ }),
636
+ ),
637
+ plugins: z.array(z.custom<ApitogoPlugin>()),
638
+ build: z.custom<{
639
+ remarkPlugins?: Options["remarkPlugins"];
640
+ rehypePlugins?: Options["rehypePlugins"];
641
+ }>(),
642
+ protectedRoutes: ProtectedRoutesSchema,
643
+ basePath: z.string().optional(),
644
+ canonicalUrlOrigin: z.string().optional(),
645
+ cdnUrl: CdnUrlSchema.optional(),
646
+ port: z.number().optional(),
647
+ https: z
648
+ .object({
649
+ key: z.string(),
650
+ cert: z.string(),
651
+ ca: z.string().optional(),
652
+ })
653
+ .optional(),
654
+ site: SiteSchema,
655
+ header: HeaderConfigSchema.optional(),
656
+ navigation: InputNavigationSchema,
657
+ navigationRules: NavigationRulesSchema.optional(),
658
+ theme: ThemeConfigSchema,
659
+ syntaxHighlighting: z
660
+ .object({
661
+ languages: z.array(z.custom<BundledLanguage>()),
662
+ themes: z.object({
663
+ light: z.custom<BundledTheme>(),
664
+ dark: z.custom<BundledTheme>(),
665
+ }),
666
+ })
667
+ .partial()
668
+ .optional(),
669
+ metadata: MetadataSchema,
670
+ authentication: AuthenticationSchema,
671
+ search: SearchSchema,
672
+ docs: DocsConfigSchema.optional(),
673
+ modules: ModulesSchema,
674
+ apis: z.union([ApiSchema, z.array(ApiSchema)]),
675
+ catalogs: z.union([ApiCatalogSchema, z.array(ApiCatalogSchema)]),
676
+ apiKeys: ApiKeysSchema,
677
+ aiAssistants: AiAssistantsSchema,
678
+ redirects: z.array(Redirect),
679
+ sitemap: SiteMapSchema,
680
+ enableStatusPages: z.boolean().optional(),
681
+ defaults: z.object({
682
+ apis: ApiOptionsSchema,
683
+ /**
684
+ * @deprecated Use `apis.examplesLanguage` or `defaults.apis.examplesLanguage` instead
685
+ */
686
+ examplesLanguage: z.string().optional(),
687
+ }),
688
+ // Internal: populated by plugins via `transformConfig` to track plugin directories
689
+ __pluginDirs: z.array(z.string()),
690
+ });
691
+
692
+ export const ZudokuConfig = BaseConfigSchema.partial();
693
+
694
+ export type ZudokuApiConfig = z.infer<typeof ApiSchema>;
695
+ export type VersionConfig = z.infer<typeof VersionConfigSchema>;
696
+ export type ZudokuSiteMapConfig = z.infer<typeof SiteMapSchema>;
697
+ export type ZudokuDocsConfig = z.infer<typeof DocsConfigSchema>;
698
+ export type { ModulesConfig } from "./ModulesSchema.js";
699
+ export type {
700
+ ResolvedModulesConfig,
701
+ DocsModuleConfig,
702
+ LandingModuleConfig,
703
+ LandingContentConfig,
704
+ LandingFeatureConfig,
705
+ LandingLinkConfig,
706
+ ResolvedLandingContent,
707
+ ResolvedDashboardContent,
708
+ ResolvedProfileContent,
709
+ ResolvedPlansContent,
710
+ ResolvedLandingModule,
711
+ ResolvedUserPanelModule,
712
+ ResolvedDashboardModule,
713
+ ResolvedUserManagementModule,
714
+ ResolvedApiKeysModule,
715
+ ResolvedPlansModule,
716
+ UserPanelModuleConfig,
717
+ DashboardModuleConfig,
718
+ UserManagementModuleConfig,
719
+ ApiKeysModuleConfig,
720
+ PlansModuleConfig,
721
+ DashboardContentConfig,
722
+ ProfileContentConfig,
723
+ PlansContentConfig,
724
+ PlanTierConfig,
725
+ } from "./ModulesSchema.js";
726
+ export type ZudokuLlmsConfig = z.infer<typeof LlmsConfigSchema>;
727
+ export type ZudokuRedirect = z.infer<typeof Redirect>;
728
+ export type AuthenticationConfig = z.infer<typeof AuthenticationSchema>;
729
+
730
+ // Use `z.input` type for flexibility with transforms,
731
+ // but override navigation with `z.infer` for strict validation
732
+ type BaseZudokuConfig = z.input<typeof ZudokuConfig>;
733
+ export type ZudokuConfig = Omit<
734
+ BaseZudokuConfig,
735
+ "header" | "navigation" | "navigationRules"
736
+ > & {
737
+ header?: {
738
+ navigation?: z.infer<typeof HeaderNavigationSchema>;
739
+ placements?: {
740
+ navigation?: "start" | "center" | "end";
741
+ search?: "start" | "center" | "end";
742
+ auth?: "start" | "center" | "end" | "navigation";
743
+ };
744
+ };
745
+ navigation?: z.infer<typeof InputNavigationSchema>;
746
+ navigationRules?: z.infer<typeof NavigationRulesSchema>;
747
+ };
748
+
749
+ export function validateConfig(config: unknown, configPath?: string) {
750
+ const validationResult = ZudokuConfig.safeParse(config);
751
+
752
+ if (!validationResult.success) {
753
+ // In production (build mode), throw an error to fail the build
754
+ if (process.env.NODE_ENV === "production") {
755
+ throw new Error(
756
+ `Whoops, looks like there's an issue with your ${configPath ?? "config"}:\n${z.prettifyError(validationResult.error)}`,
757
+ );
758
+ }
759
+
760
+ // In development mode, log warnings but don't fail
761
+ // biome-ignore lint/suspicious/noConsole: Logging allowed here
762
+ console.log(colors.yellow("Validation errors:"));
763
+ // biome-ignore lint/suspicious/noConsole: Logging allowed here
764
+ console.log(colors.yellow(z.prettifyError(validationResult.error)));
765
+ }
766
+ }