zudoku 0.0.0-monetization-standalone.zada7f04c → 0.0.0-money-plugin.z605c614d

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 (241) hide show
  1. package/dist/config/create-plugin.d.ts +2 -2
  2. package/dist/config/create-plugin.js.map +1 -1
  3. package/dist/config/validators/InputNavigationSchema.d.ts +174 -124
  4. package/dist/config/validators/InputNavigationSchema.js +17 -0
  5. package/dist/config/validators/InputNavigationSchema.js.map +1 -1
  6. package/dist/config/validators/NavigationSchema.d.ts +10 -2
  7. package/dist/config/validators/NavigationSchema.js +7 -0
  8. package/dist/config/validators/NavigationSchema.js.map +1 -1
  9. package/dist/config/validators/validate.d.ts +4 -4
  10. package/dist/flat-config.d.ts +35 -24
  11. package/dist/lib/components/Heading.d.ts +1 -1
  12. package/dist/lib/components/MobileTopNavigation.js +2 -1
  13. package/dist/lib/components/MobileTopNavigation.js.map +1 -1
  14. package/dist/lib/components/TopNavigation.d.ts +7 -1
  15. package/dist/lib/components/TopNavigation.js +7 -2
  16. package/dist/lib/components/TopNavigation.js.map +1 -1
  17. package/dist/lib/components/context/ZudokuContext.d.ts +8 -1
  18. package/dist/lib/components/context/ZudokuContext.js +2 -0
  19. package/dist/lib/components/context/ZudokuContext.js.map +1 -1
  20. package/dist/lib/components/index.d.ts +18 -76
  21. package/dist/lib/components/index.js +19 -36
  22. package/dist/lib/components/index.js.map +1 -1
  23. package/dist/lib/components/navigation/Navigation.js +4 -3
  24. package/dist/lib/components/navigation/Navigation.js.map +1 -1
  25. package/dist/lib/components/navigation/NavigationCategory.js +8 -0
  26. package/dist/lib/components/navigation/NavigationCategory.js.map +1 -1
  27. package/dist/lib/components/navigation/NavigationFilterContext.d.ts +8 -0
  28. package/dist/lib/components/navigation/NavigationFilterContext.js +12 -0
  29. package/dist/lib/components/navigation/NavigationFilterContext.js.map +1 -0
  30. package/dist/lib/components/navigation/NavigationFilterInput.d.ts +3 -0
  31. package/dist/lib/components/navigation/NavigationFilterInput.js +9 -0
  32. package/dist/lib/components/navigation/NavigationFilterInput.js.map +1 -0
  33. package/dist/lib/components/navigation/NavigationItem.js +11 -1
  34. package/dist/lib/components/navigation/NavigationItem.js.map +1 -1
  35. package/dist/lib/components/navigation/utils.d.ts +2 -1
  36. package/dist/lib/components/navigation/utils.js +22 -1
  37. package/dist/lib/components/navigation/utils.js.map +1 -1
  38. package/dist/lib/core/__internal.d.ts +1 -1
  39. package/dist/lib/core/transform-config.d.ts +2 -2
  40. package/dist/lib/core/transform-config.js.map +1 -1
  41. package/dist/lib/hooks/index.d.ts +7 -30
  42. package/dist/lib/hooks/index.js +7 -15
  43. package/dist/lib/hooks/index.js.map +1 -1
  44. package/dist/lib/oas/graphql/circular.d.ts +1 -1
  45. package/dist/lib/oas/graphql/circular.js +18 -35
  46. package/dist/lib/oas/graphql/circular.js.map +1 -1
  47. package/dist/lib/oas/graphql/circular.test.js +33 -2
  48. package/dist/lib/oas/graphql/circular.test.js.map +1 -1
  49. package/dist/lib/oas/parser/index.js +14 -5
  50. package/dist/lib/oas/parser/index.js.map +1 -1
  51. package/dist/lib/plugins/openapi/playground/fileUtils.d.ts +1 -0
  52. package/dist/lib/plugins/openapi/playground/fileUtils.js +3 -0
  53. package/dist/lib/plugins/openapi/playground/fileUtils.js.map +1 -1
  54. package/dist/lib/plugins/openapi/playground/result-panel/AudioPlayer.d.ts +6 -0
  55. package/dist/lib/plugins/openapi/playground/result-panel/AudioPlayer.js +20 -0
  56. package/dist/lib/plugins/openapi/playground/result-panel/AudioPlayer.js.map +1 -0
  57. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js +7 -2
  58. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js.map +1 -1
  59. package/dist/lib/ui/Alert.d.ts +3 -2
  60. package/dist/lib/ui/Alert.js +9 -5
  61. package/dist/lib/ui/Alert.js.map +1 -1
  62. package/dist/lib/ui/InputGroup.d.ts +16 -0
  63. package/dist/lib/ui/InputGroup.js +65 -0
  64. package/dist/lib/ui/InputGroup.js.map +1 -0
  65. package/dist/lib/util/flattenAllOf.d.ts +0 -2
  66. package/dist/lib/util/flattenAllOf.js +0 -46
  67. package/dist/lib/util/flattenAllOf.js.map +1 -1
  68. package/dist/lib/util/flattenAllOf.test.js +2 -1
  69. package/dist/lib/util/flattenAllOf.test.js.map +1 -1
  70. package/dist/lib/util/flattenAllOfProcessor.d.ts +2 -0
  71. package/dist/lib/util/flattenAllOfProcessor.js +48 -0
  72. package/dist/lib/util/flattenAllOfProcessor.js.map +1 -0
  73. package/dist/lib/util/readFrontmatter.js +2 -1
  74. package/dist/lib/util/readFrontmatter.js.map +1 -1
  75. package/dist/vite/api/SchemaManager.js +1 -1
  76. package/dist/vite/api/SchemaManager.js.map +1 -1
  77. package/dist/vite/api/SchemaManager.test.js +1 -1
  78. package/dist/vite/api/SchemaManager.test.js.map +1 -1
  79. package/dist/vite/build.js +91 -73
  80. package/dist/vite/build.js.map +1 -1
  81. package/dist/vite/mdx/remark-inject-filepath.js +5 -1
  82. package/dist/vite/mdx/remark-inject-filepath.js.map +1 -1
  83. package/dist/vite/mdx/remark-link-rewrite.js +3 -2
  84. package/dist/vite/mdx/remark-link-rewrite.js.map +1 -1
  85. package/dist/vite/plugin-docs.js +9 -7
  86. package/dist/vite/plugin-docs.js.map +1 -1
  87. package/dist/vite/plugin-markdown-export.js +4 -2
  88. package/dist/vite/plugin-markdown-export.js.map +1 -1
  89. package/dist/vite/prerender/prerender.js +3 -1
  90. package/dist/vite/prerender/prerender.js.map +1 -1
  91. package/dist/vite/prerender/worker.js +3 -1
  92. package/dist/vite/prerender/worker.js.map +1 -1
  93. package/lib/{ClaudeLogo-K64Qm6gS.js → ClaudeLogo-DP6JOmz1.js} +26 -22
  94. package/lib/{ClaudeLogo-K64Qm6gS.js.map → ClaudeLogo-DP6JOmz1.js.map} +1 -1
  95. package/lib/{HydrationBoundary-CNF2ZV3E.js → HydrationBoundary-CJu4vUlG.js} +6 -6
  96. package/lib/{HydrationBoundary-CNF2ZV3E.js.map → HydrationBoundary-CJu4vUlG.js.map} +1 -1
  97. package/lib/{MdxPage-Cr99RARi.js → MdxPage-BR_7-0cT.js} +8 -8
  98. package/lib/{MdxPage-Cr99RARi.js.map → MdxPage-BR_7-0cT.js.map} +1 -1
  99. package/lib/Mermaid-DAWm9rzb.js +104 -0
  100. package/lib/Mermaid-DAWm9rzb.js.map +1 -0
  101. package/lib/{OAuthErrorPage-BRXS5AMu.js → OAuthErrorPage-l0orKh-A.js} +22 -19
  102. package/lib/{OAuthErrorPage-BRXS5AMu.js.map → OAuthErrorPage-l0orKh-A.js.map} +1 -1
  103. package/lib/{OasProvider-sP_SqSM7.js → OasProvider-DPW0x8dk.js} +3 -3
  104. package/lib/{OasProvider-sP_SqSM7.js.map → OasProvider-DPW0x8dk.js.map} +1 -1
  105. package/lib/{OperationList-DR0APPhk.js → OperationList-Dcm9TZG7.js} +952 -948
  106. package/lib/OperationList-Dcm9TZG7.js.map +1 -0
  107. package/lib/{RouteGuard-CZuPjknT.js → RouteGuard-CVs3yvEs.js} +5 -5
  108. package/lib/{RouteGuard-CZuPjknT.js.map → RouteGuard-CVs3yvEs.js.map} +1 -1
  109. package/lib/{SchemaList-DgqAAHSI.js → SchemaList-B7RzviG5.js} +8 -8
  110. package/lib/{SchemaList-DgqAAHSI.js.map → SchemaList-B7RzviG5.js.map} +1 -1
  111. package/lib/{SchemaView-7IyQYEqk.js → SchemaView-Dt5-T06r.js} +3 -3
  112. package/lib/{SchemaView-7IyQYEqk.js.map → SchemaView-Dt5-T06r.js.map} +1 -1
  113. package/lib/{Secret-BE7V0Vb5.js → Secret-DUpgv4V3.js} +4 -4
  114. package/lib/{Secret-BE7V0Vb5.js.map → Secret-DUpgv4V3.js.map} +1 -1
  115. package/lib/{SignUp-D3zhMk7q.js → SignUp-CifBTCq4.js} +31 -26
  116. package/lib/{SignUp-D3zhMk7q.js.map → SignUp-CifBTCq4.js.map} +1 -1
  117. package/lib/{SyntaxHighlight-paLQ8NP8.js → SyntaxHighlight-BMu0b_hF.js} +3 -3
  118. package/lib/{SyntaxHighlight-paLQ8NP8.js.map → SyntaxHighlight-BMu0b_hF.js.map} +1 -1
  119. package/lib/{Toc-GluXkcIk.js → Toc-CeMXYWgp.js} +2 -2
  120. package/lib/{Toc-GluXkcIk.js.map → Toc-CeMXYWgp.js.map} +1 -1
  121. package/lib/{index-DYfX9H7i.js → Zudoku-B5YsolG1.js} +2869 -2710
  122. package/lib/Zudoku-B5YsolG1.js.map +1 -0
  123. package/lib/ZudokuContext-CYyb_PB_.js +175 -0
  124. package/lib/ZudokuContext-CYyb_PB_.js.map +1 -0
  125. package/lib/ZudokuReactContext-DGJAP1sN.js +222 -0
  126. package/lib/ZudokuReactContext-DGJAP1sN.js.map +1 -0
  127. package/lib/{circular-Bunzbpsy.js → circular-BV9K7T3F.js} +1327 -1346
  128. package/lib/{circular-Bunzbpsy.js.map → circular-BV9K7T3F.js.map} +1 -1
  129. package/lib/createServer-DYUdwZG4.js +13036 -0
  130. package/lib/createServer-DYUdwZG4.js.map +1 -0
  131. package/lib/{errors-D2NINcVk.js → errors-C3XsqYso.js} +3 -3
  132. package/lib/{errors-D2NINcVk.js.map → errors-C3XsqYso.js.map} +1 -1
  133. package/lib/{firebase-BVAmGV_v.js → firebase-CL9aCRn0.js} +26 -25
  134. package/lib/firebase-CL9aCRn0.js.map +1 -0
  135. package/lib/{hook-Da8QTnPd.js → hook-Dz_n9SoE.js} +16 -15
  136. package/lib/{hook-Da8QTnPd.js.map → hook-Dz_n9SoE.js.map} +1 -1
  137. package/lib/{index-DTjrb36R.js → index-CFW_9FdI.js} +2 -2
  138. package/lib/{index-DTjrb36R.js.map → index-CFW_9FdI.js.map} +1 -1
  139. package/lib/index-DAWHN3cH.js +86 -0
  140. package/lib/index-DAWHN3cH.js.map +1 -0
  141. package/lib/{index-BfTLawvZ.js → index-DywuWe2u.js} +589 -551
  142. package/lib/index-DywuWe2u.js.map +1 -0
  143. package/lib/{index.esm-B_0dvNjB.js → index.esm-Ca5zvoff.js} +20 -20
  144. package/lib/{index.esm-B_0dvNjB.js.map → index.esm-Ca5zvoff.js.map} +1 -1
  145. package/lib/{index.esm-GMDd_9gw.js → index.esm-ld_S_9qs.js} +2 -2
  146. package/lib/index.esm-ld_S_9qs.js.map +1 -0
  147. package/lib/{invariant-BJAl77rw.js → invariant-B_t_F2s_.js} +3 -3
  148. package/lib/{invariant-BJAl77rw.js.map → invariant-B_t_F2s_.js.map} +1 -1
  149. package/lib/{mutation-BISOc7OM.js → mutation-B7eFBLZY.js} +2 -2
  150. package/lib/{mutation-BISOc7OM.js.map → mutation-B7eFBLZY.js.map} +1 -1
  151. package/lib/ui/Alert.js +36 -24
  152. package/lib/ui/Alert.js.map +1 -1
  153. package/lib/ui/InputGroup.js +155 -0
  154. package/lib/ui/InputGroup.js.map +1 -0
  155. package/lib/ui/SyntaxHighlight.js +3 -3
  156. package/lib/useExposedProps-CzTDfXfq.js +30 -0
  157. package/lib/useExposedProps-CzTDfXfq.js.map +1 -0
  158. package/lib/{useMutation-CFMGlAMW.js → useMutation-CErliDZ9.js} +5 -5
  159. package/lib/{useMutation-CFMGlAMW.js.map → useMutation-CErliDZ9.js.map} +1 -1
  160. package/lib/{useSuspenseQuery-CSB_rVek.js → useQuery-ht7aWJ3S.js} +432 -446
  161. package/lib/useQuery-ht7aWJ3S.js.map +1 -0
  162. package/lib/useSuspenseQuery-DQH4Bmc2.js +18 -0
  163. package/lib/useSuspenseQuery-DQH4Bmc2.js.map +1 -0
  164. package/lib/zudoku.__internal.js +1504 -1041
  165. package/lib/zudoku.__internal.js.map +1 -1
  166. package/lib/zudoku.auth-auth0.js +6 -5
  167. package/lib/zudoku.auth-auth0.js.map +1 -1
  168. package/lib/zudoku.auth-azureb2c.js +14 -13
  169. package/lib/zudoku.auth-azureb2c.js.map +1 -1
  170. package/lib/zudoku.auth-clerk.js +2 -2
  171. package/lib/zudoku.auth-firebase.js +5 -5
  172. package/lib/zudoku.auth-openid.js +8 -7
  173. package/lib/zudoku.auth-openid.js.map +1 -1
  174. package/lib/zudoku.auth-supabase.js +4 -4
  175. package/lib/zudoku.components.js +31 -29
  176. package/lib/zudoku.components.js.map +1 -1
  177. package/lib/zudoku.hooks.js +24 -11
  178. package/lib/zudoku.hooks.js.map +1 -1
  179. package/lib/zudoku.mermaid.js +5 -4
  180. package/lib/zudoku.mermaid.js.map +1 -1
  181. package/lib/zudoku.plugin-api-catalog.js +41 -36
  182. package/lib/zudoku.plugin-api-catalog.js.map +1 -1
  183. package/lib/zudoku.plugin-api-keys.js +156 -153
  184. package/lib/zudoku.plugin-api-keys.js.map +1 -1
  185. package/lib/zudoku.plugin-custom-pages.js +1 -1
  186. package/lib/zudoku.plugin-markdown.js +1 -1
  187. package/lib/zudoku.plugin-openapi.js +2 -2
  188. package/lib/zudoku.plugin-search-pagefind.js +19 -18
  189. package/lib/zudoku.plugin-search-pagefind.js.map +1 -1
  190. package/lib/zudoku.react-query.js +26 -25
  191. package/lib/zudoku.react-query.js.map +1 -1
  192. package/package.json +5 -4
  193. package/src/app/defaultTheme.css +4 -0
  194. package/src/app/main.css +2 -0
  195. package/src/lib/components/MobileTopNavigation.tsx +13 -8
  196. package/src/lib/components/TopNavigation.tsx +25 -7
  197. package/src/lib/components/context/ZudokuContext.ts +1 -0
  198. package/src/lib/components/index.ts +19 -39
  199. package/src/lib/components/navigation/Navigation.tsx +4 -3
  200. package/src/lib/components/navigation/NavigationCategory.tsx +9 -0
  201. package/src/lib/components/navigation/NavigationFilterContext.tsx +28 -0
  202. package/src/lib/components/navigation/NavigationFilterInput.tsx +35 -0
  203. package/src/lib/components/navigation/NavigationItem.tsx +17 -1
  204. package/src/lib/components/navigation/utils.ts +32 -1
  205. package/src/lib/core/transform-config.ts +5 -5
  206. package/src/lib/hooks/index.ts +7 -16
  207. package/src/lib/oas/graphql/circular.test.ts +37 -2
  208. package/src/lib/oas/graphql/circular.ts +25 -51
  209. package/src/lib/oas/parser/index.ts +17 -6
  210. package/src/lib/plugins/openapi/playground/fileUtils.ts +4 -0
  211. package/src/lib/plugins/openapi/playground/result-panel/AudioPlayer.tsx +50 -0
  212. package/src/lib/plugins/openapi/playground/result-panel/ResponseTab.tsx +33 -17
  213. package/src/lib/ui/Alert.tsx +17 -5
  214. package/src/lib/ui/InputGroup.tsx +168 -0
  215. package/src/lib/util/flattenAllOf.test.ts +2 -1
  216. package/src/lib/util/flattenAllOf.ts +0 -57
  217. package/src/lib/util/flattenAllOfProcessor.ts +58 -0
  218. package/src/lib/util/readFrontmatter.ts +2 -1
  219. package/src/zuplo/enrich-with-zuplo-mcp.ts +168 -0
  220. package/src/zuplo/enrich-with-zuplo.ts +254 -0
  221. package/src/zuplo/policy-types.ts +46 -0
  222. package/src/zuplo/with-zuplo-processors.ts +35 -0
  223. package/src/zuplo/with-zuplo.ts +14 -0
  224. package/lib/Mermaid-DEztDKFw.js +0 -102
  225. package/lib/Mermaid-DEztDKFw.js.map +0 -1
  226. package/lib/OperationList-DR0APPhk.js.map +0 -1
  227. package/lib/Separator-BXt1LYnm.js +0 -27
  228. package/lib/Separator-BXt1LYnm.js.map +0 -1
  229. package/lib/ZudokuContext-CnEI8jPU.js +0 -389
  230. package/lib/ZudokuContext-CnEI8jPU.js.map +0 -1
  231. package/lib/___vite-browser-external_commonjs-proxy-BttVsNON.js +0 -9
  232. package/lib/___vite-browser-external_commonjs-proxy-BttVsNON.js.map +0 -1
  233. package/lib/createServer-Dl4Xwsm4.js +0 -16693
  234. package/lib/createServer-Dl4Xwsm4.js.map +0 -1
  235. package/lib/firebase-BVAmGV_v.js.map +0 -1
  236. package/lib/index-Ba6RP577.js +0 -133
  237. package/lib/index-Ba6RP577.js.map +0 -1
  238. package/lib/index-BfTLawvZ.js.map +0 -1
  239. package/lib/index-DYfX9H7i.js.map +0 -1
  240. package/lib/index.esm-GMDd_9gw.js.map +0 -1
  241. package/lib/useSuspenseQuery-CSB_rVek.js.map +0 -1
@@ -32,6 +32,8 @@ import {
32
32
  CollapsibleHeader,
33
33
  CollapsibleHeaderTrigger,
34
34
  } from "../CollapsibleHeader.js";
35
+ import { isAudioContentType } from "../fileUtils.js";
36
+ import { AudioPlayer } from "./AudioPlayer.js";
35
37
  import { convertToTypes } from "./convertToTypes.js";
36
38
 
37
39
  const mimeTypeToLanguage = (mimeType: string) => {
@@ -50,9 +52,14 @@ const mimeTypeToLanguage = (mimeType: string) => {
50
52
  )?.[1];
51
53
  };
52
54
 
55
+ const getContentType = (headers: Array<[string, string]>) => {
56
+ return (
57
+ headers.find(([key]) => key.toLowerCase() === "content-type")?.[1] || ""
58
+ );
59
+ };
60
+
53
61
  const detectLanguage = (headers: Array<[string, string]>) => {
54
- const contentType =
55
- headers.find(([key]) => key.toLowerCase() === "content-type")?.[1] || "";
62
+ const contentType = getContentType(headers);
56
63
  return mimeTypeToLanguage(contentType);
57
64
  };
58
65
 
@@ -293,23 +300,32 @@ export const ResponseTab = ({
293
300
  </div>
294
301
  <div className="flex-1">
295
302
  {isBinary ? (
296
- <div className="p-4 text-center">
297
- <div className="flex flex-col items-center gap-4">
298
- <div className="text-lg font-semibold">Binary Content</div>
299
- <div className="text-sm text-muted-foreground">
300
- This response contains binary data that cannot be displayed as
301
- text.
303
+ blob && isAudioContentType(getContentType(headers)) ? (
304
+ <AudioPlayer
305
+ blob={blob}
306
+ fileName={fileName ?? "audio"}
307
+ size={size}
308
+ onDownload={handleDownload}
309
+ />
310
+ ) : (
311
+ <div className="p-4 text-center">
312
+ <div className="flex flex-col items-center gap-4">
313
+ <div className="text-lg font-semibold">Binary Content</div>
314
+ <div className="text-sm text-muted-foreground">
315
+ This response contains binary data that cannot be displayed as
316
+ text.
317
+ </div>
318
+ <Button
319
+ onClick={handleDownload}
320
+ className="flex items-center gap-2"
321
+ disabled={!blob}
322
+ >
323
+ <DownloadIcon className="h-4 w-4" />
324
+ Download {fileName || "file"} ({humanFileSize(size)})
325
+ </Button>
302
326
  </div>
303
- <Button
304
- onClick={handleDownload}
305
- className="flex items-center gap-2"
306
- disabled={!blob}
307
- >
308
- <DownloadIcon className="h-4 w-4" />
309
- Download {fileName || "file"} ({humanFileSize(size)})
310
- </Button>
311
327
  </div>
312
- </div>
328
+ )
313
329
  ) : (
314
330
  <SyntaxHighlight
315
331
  className="text-xs flex-1"
@@ -3,13 +3,15 @@ import type * as React from "react";
3
3
  import { cn } from "../util/cn.js";
4
4
 
5
5
  const alertVariants = cva(
6
- "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
6
+ "grid gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 w-full relative group/alert",
7
7
  {
8
8
  variants: {
9
9
  variant: {
10
10
  default: "bg-card text-card-foreground",
11
11
  destructive:
12
- "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
12
+ "text-destructive bg-card bg-destructive/5 border-destructive/20 *:data-[slot=alert-description]:text-destructive *:[svg]:text-current",
13
+ warning:
14
+ "text-warning-foreground bg-card bg-warning/5 border-warning/50 *:data-[slot=alert-description]:text-warning-foreground *:[svg]:text-current",
13
15
  },
14
16
  },
15
17
  defaultVariants: {
@@ -38,7 +40,7 @@ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
38
40
  <div
39
41
  data-slot="alert-title"
40
42
  className={cn(
41
- "col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
43
+ "font-medium group-has-[>svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
42
44
  className,
43
45
  )}
44
46
  {...props}
@@ -54,7 +56,7 @@ function AlertDescription({
54
56
  <div
55
57
  data-slot="alert-description"
56
58
  className={cn(
57
- "text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
59
+ "text-muted-foreground text-sm text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
58
60
  className,
59
61
  )}
60
62
  {...props}
@@ -62,4 +64,14 @@ function AlertDescription({
62
64
  );
63
65
  }
64
66
 
65
- export { Alert, AlertDescription, AlertTitle };
67
+ function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
68
+ return (
69
+ <div
70
+ data-slot="alert-action"
71
+ className={cn("absolute top-2 right-2", className)}
72
+ {...props}
73
+ />
74
+ );
75
+ }
76
+
77
+ export { Alert, AlertTitle, AlertDescription, AlertAction };
@@ -0,0 +1,168 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import type * as React from "react";
3
+ import { cn } from "../util/cn.js";
4
+ import { Button } from "./Button.js";
5
+ import { Input } from "./Input.js";
6
+ import { Textarea } from "./Textarea.js";
7
+
8
+ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
9
+ return (
10
+ <div
11
+ data-slot="input-group"
12
+ role="group"
13
+ className={cn(
14
+ "group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none",
15
+ "h-9 min-w-0 has-[>textarea]:h-auto",
16
+
17
+ // Variants based on alignment.
18
+ "has-[>[data-align=inline-start]]:[&>input]:pl-2",
19
+ "has-[>[data-align=inline-end]]:[&>input]:pr-2",
20
+ "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
21
+ "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
22
+
23
+ // Focus state.
24
+ "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]",
25
+
26
+ // Error state.
27
+ "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
28
+
29
+ className,
30
+ )}
31
+ {...props}
32
+ />
33
+ );
34
+ }
35
+
36
+ const inputGroupAddonVariants = cva(
37
+ "text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
38
+ {
39
+ variants: {
40
+ align: {
41
+ "inline-start":
42
+ "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
43
+ "inline-end":
44
+ "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
45
+ "block-start":
46
+ "order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
47
+ "block-end":
48
+ "order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5",
49
+ },
50
+ },
51
+ defaultVariants: {
52
+ align: "inline-start",
53
+ },
54
+ },
55
+ );
56
+
57
+ function InputGroupAddon({
58
+ className,
59
+ align = "inline-start",
60
+ ...props
61
+ }: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
62
+ return (
63
+ // biome-ignore lint/a11y/useKeyWithClickEvents: Focus management
64
+ <div
65
+ role="group"
66
+ data-slot="input-group-addon"
67
+ data-align={align}
68
+ className={cn(inputGroupAddonVariants({ align }), className)}
69
+ onClick={(e) => {
70
+ if ((e.target as HTMLElement).closest("button")) {
71
+ return;
72
+ }
73
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
74
+ }}
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+
80
+ const inputGroupButtonVariants = cva(
81
+ "text-sm shadow-none flex gap-2 items-center",
82
+ {
83
+ variants: {
84
+ size: {
85
+ xs: "h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2",
86
+ sm: "h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5",
87
+ "icon-xs":
88
+ "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
89
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0",
90
+ },
91
+ },
92
+ defaultVariants: {
93
+ size: "xs",
94
+ },
95
+ },
96
+ );
97
+
98
+ function InputGroupButton({
99
+ className,
100
+ type = "button",
101
+ variant = "ghost",
102
+ size = "xs",
103
+ ...props
104
+ }: Omit<React.ComponentProps<typeof Button>, "size"> &
105
+ VariantProps<typeof inputGroupButtonVariants>) {
106
+ return (
107
+ <Button
108
+ type={type}
109
+ data-size={size}
110
+ variant={variant}
111
+ className={cn(inputGroupButtonVariants({ size }), className)}
112
+ {...props}
113
+ />
114
+ );
115
+ }
116
+
117
+ function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
118
+ return (
119
+ <span
120
+ className={cn(
121
+ "text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
122
+ className,
123
+ )}
124
+ {...props}
125
+ />
126
+ );
127
+ }
128
+
129
+ function InputGroupInput({
130
+ className,
131
+ ...props
132
+ }: React.ComponentProps<"input">) {
133
+ return (
134
+ <Input
135
+ data-slot="input-group-control"
136
+ className={cn(
137
+ "flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
138
+ className,
139
+ )}
140
+ {...props}
141
+ />
142
+ );
143
+ }
144
+
145
+ function InputGroupTextarea({
146
+ className,
147
+ ...props
148
+ }: React.ComponentProps<"textarea">) {
149
+ return (
150
+ <Textarea
151
+ data-slot="input-group-control"
152
+ className={cn(
153
+ "flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
154
+ className,
155
+ )}
156
+ {...props}
157
+ />
158
+ );
159
+ }
160
+
161
+ export {
162
+ InputGroup,
163
+ InputGroupAddon,
164
+ InputGroupButton,
165
+ InputGroupText,
166
+ InputGroupInput,
167
+ InputGroupTextarea,
168
+ };
@@ -1,7 +1,8 @@
1
1
  import type { JSONSchema7 } from "json-schema";
2
2
  import { describe, expect, it, vi } from "vitest";
3
3
  import type { OpenAPIDocument } from "../oas/parser/index.js";
4
- import { flattenAllOf, flattenAllOfProcessor } from "./flattenAllOf.js";
4
+ import { flattenAllOf } from "./flattenAllOf.js";
5
+ import { flattenAllOfProcessor } from "./flattenAllOfProcessor.js";
5
6
  import invariant from "./invariant.js";
6
7
 
7
8
  describe("flattenAllOf", () => {
@@ -1,4 +1,3 @@
1
- import { $RefParser } from "@apidevtools/json-schema-ref-parser";
2
1
  import {
3
2
  createComparator,
4
3
  createMerger,
@@ -9,62 +8,6 @@ import {
9
8
  createIntersector,
10
9
  } from "@x0k/json-schema-merge/lib/array";
11
10
  import type { JSONSchema7Definition } from "json-schema";
12
- import type { Processor } from "../../config/validators/BuildSchema.js";
13
- import type { OpenAPIDocument } from "../oas/parser/index.js";
14
- import { type RecordAny, traverse } from "./traverse.js";
15
-
16
- export const flattenAllOfProcessor: Processor = async ({ schema, file }) => {
17
- try {
18
- // Resolve refs once - creates a lookup table without modifying the schema
19
- const parser = new $RefParser();
20
- await parser.resolve(schema);
21
- const $refs = parser.$refs;
22
-
23
- const flattened = traverse(schema, (spec) => {
24
- if (!spec || typeof spec !== "object" || Array.isArray(spec)) {
25
- return spec;
26
- }
27
-
28
- const isSchemaObject =
29
- "type" in spec ||
30
- "properties" in spec ||
31
- "allOf" in spec ||
32
- "anyOf" in spec ||
33
- "oneOf" in spec;
34
-
35
- if (!isSchemaObject) return spec;
36
-
37
- if ("allOf" in spec && Array.isArray(spec.allOf)) {
38
- const resolvedAllOf = spec.allOf.map((item) => {
39
- if (
40
- item &&
41
- typeof item === "object" &&
42
- "$ref" in item &&
43
- typeof item.$ref === "string"
44
- ) {
45
- try {
46
- return $refs.get(item.$ref) ?? item;
47
- } catch {
48
- return item;
49
- }
50
- }
51
- return item;
52
- });
53
- return flattenAllOf({ ...spec, allOf: resolvedAllOf }) as RecordAny;
54
- }
55
-
56
- return flattenAllOf(spec) as RecordAny;
57
- }) as OpenAPIDocument;
58
-
59
- return flattened;
60
- } catch (error) {
61
- // biome-ignore lint/suspicious/noConsole: Logging allowed here
62
- console.warn(
63
- `Failed to flatten \`allOf\` in ${file}: ${error instanceof Error ? error.message : error}`,
64
- );
65
- return schema;
66
- }
67
- };
68
11
 
69
12
  const { compareSchemaDefinitions, compareSchemaValues } = createComparator();
70
13
  const { mergeArrayOfSchemaDefinitions } = createMerger({
@@ -0,0 +1,58 @@
1
+ import { $RefParser } from "@apidevtools/json-schema-ref-parser";
2
+ import type { Processor } from "../../config/validators/BuildSchema.js";
3
+ import type { OpenAPIDocument } from "../oas/parser/index.js";
4
+ import { flattenAllOf } from "./flattenAllOf.js";
5
+ import { type RecordAny, traverse } from "./traverse.js";
6
+
7
+ export const flattenAllOfProcessor: Processor = async ({ schema, file }) => {
8
+ try {
9
+ // Resolve refs once - creates a lookup table without modifying the schema
10
+ const parser = new $RefParser();
11
+ await parser.resolve(schema);
12
+ const $refs = parser.$refs;
13
+
14
+ const flattened = traverse(schema, (spec) => {
15
+ if (!spec || typeof spec !== "object" || Array.isArray(spec)) {
16
+ return spec;
17
+ }
18
+
19
+ const isSchemaObject =
20
+ "type" in spec ||
21
+ "properties" in spec ||
22
+ "allOf" in spec ||
23
+ "anyOf" in spec ||
24
+ "oneOf" in spec;
25
+
26
+ if (!isSchemaObject) return spec;
27
+
28
+ if ("allOf" in spec && Array.isArray(spec.allOf)) {
29
+ const resolvedAllOf = spec.allOf.map((item) => {
30
+ if (
31
+ item &&
32
+ typeof item === "object" &&
33
+ "$ref" in item &&
34
+ typeof item.$ref === "string"
35
+ ) {
36
+ try {
37
+ return $refs.get(item.$ref) ?? item;
38
+ } catch {
39
+ return item;
40
+ }
41
+ }
42
+ return item;
43
+ });
44
+ return flattenAllOf({ ...spec, allOf: resolvedAllOf }) as RecordAny;
45
+ }
46
+
47
+ return flattenAllOf(spec) as RecordAny;
48
+ }) as OpenAPIDocument;
49
+
50
+ return flattened;
51
+ } catch (error) {
52
+ // biome-ignore lint/suspicious/noConsole: Logging allowed here
53
+ console.warn(
54
+ `Failed to flatten \`allOf\` in ${file}: ${error instanceof Error ? error.message : error}`,
55
+ );
56
+ return schema;
57
+ }
58
+ };
@@ -9,5 +9,6 @@ export const yaml = {
9
9
 
10
10
  export const readFrontmatter = async (filePath: string) => {
11
11
  const content = await readFile(filePath, "utf-8");
12
- return matter(content, { engines: { yaml } });
12
+ const normalizedContent = content.replace(/\r\n/g, "\n");
13
+ return matter(normalizedContent, { engines: { yaml } });
13
14
  };
@@ -0,0 +1,168 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import type {
4
+ ExtensionMcpServer,
5
+ ExtensionMcpServerTool,
6
+ } from "@zuplo/mcp/openapi/types";
7
+ import {
8
+ DEFAULT_MCP_SERVER_NAME,
9
+ DEFAULT_MCP_SERVER_VERSION,
10
+ } from "@zuplo/mcp/server";
11
+ import type { OpenAPIV3_1 } from "openapi-types";
12
+ import type { ProcessorArg } from "../config/validators/BuildSchema.js";
13
+ import { traverse, traverseAsync } from "../lib/util/traverse.js";
14
+ import type { RecordAny } from "../lib/util/types.js";
15
+ import { operations } from "./enrich-with-zuplo.js";
16
+
17
+ // extracts x-mcp-server metadata from the operation using x-zuplo-mcp-tool
18
+ // as a first class citizen.
19
+ const extractOperationSchema = (
20
+ operation: OpenAPIV3_1.OperationObject & RecordAny,
21
+ ): ExtensionMcpServerTool | null => {
22
+ if (!operation.operationId) return null;
23
+
24
+ // Check if tool is explicitly disabled
25
+ const mcpToolConfig = operation["x-zuplo-mcp-tool"];
26
+ if (mcpToolConfig?.enabled === false) {
27
+ return null;
28
+ }
29
+
30
+ const tool: ExtensionMcpServerTool = {
31
+ // Use custom name from x-zuplo-mcp-tool or fallback to operationId
32
+ name: mcpToolConfig?.name || operation.operationId,
33
+
34
+ // Use custom description from x-zuplo-mcp-tool or fallback
35
+ // to operation description
36
+ description:
37
+ mcpToolConfig?.description ||
38
+ operation.summary ||
39
+ operation.description ||
40
+ `Operation ${operation.operationId}`,
41
+ };
42
+
43
+ // Grab valid request body JSON schema for the tool
44
+ const requestBody = operation.requestBody as
45
+ | OpenAPIV3_1.RequestBodyObject
46
+ | undefined;
47
+
48
+ const schema = requestBody?.content?.["application/json"]?.schema;
49
+ if (schema && typeof schema === "object") {
50
+ // TODO: @jpmcb - Zuplo also supports in-path parameters and query parameters
51
+ // as MCP "inputSchema" arguments. In order to document full argument params,
52
+ // Zudoku will need to more intelligently parse these elements of an operation.
53
+ tool.inputSchema = { body: schema };
54
+ }
55
+
56
+ return tool;
57
+ };
58
+
59
+ // Builds a lookup map of operationId -> operation for efficient access
60
+ const buildOperationLookup = (
61
+ document: OpenAPIV3_1.Document,
62
+ ): Map<string, OpenAPIV3_1.OperationObject> => {
63
+ const operationMap = new Map<string, OpenAPIV3_1.OperationObject>();
64
+
65
+ traverse(document, (node, path) => {
66
+ // Check if we're at a path item level (paths -> /some/path -> method)
67
+ // and validate it's in allowed operations
68
+ if (
69
+ !path ||
70
+ path.length < 2 ||
71
+ path[0] !== "paths" ||
72
+ !operations.includes(path[path.length - 1] as string)
73
+ ) {
74
+ return node;
75
+ }
76
+
77
+ if (node.operationId) {
78
+ operationMap.set(node.operationId, node);
79
+ }
80
+
81
+ return node;
82
+ });
83
+
84
+ return operationMap;
85
+ };
86
+
87
+ // Takes an OpenAPI document and returns the x-mcp-server tools list defined
88
+ // by an MCP server's options.files[x].operationIds array.
89
+ const findOperationsInDocument = (
90
+ document: OpenAPIV3_1.Document,
91
+ operationIds: string[],
92
+ ): ExtensionMcpServerTool[] => {
93
+ const tools: ExtensionMcpServerTool[] = [];
94
+ const operationLookup = buildOperationLookup(document);
95
+
96
+ operationIds.forEach((operationId) => {
97
+ const operation = operationLookup.get(operationId);
98
+ if (operation) {
99
+ const tool = extractOperationSchema(operation);
100
+ if (tool) {
101
+ tools.push(tool);
102
+ }
103
+ }
104
+ });
105
+
106
+ return tools;
107
+ };
108
+
109
+ // Enriches an OpenAPI schema with x-mcp-server data based on the Zuplo MCP server handler
110
+ export const enrichWithZuploMcpServerData = ({
111
+ rootDir,
112
+ }: {
113
+ rootDir: string;
114
+ }) => {
115
+ return async ({ schema }: ProcessorArg) => {
116
+ if (!schema.paths) return schema;
117
+ const modifiedSchema = { ...schema };
118
+ if (!modifiedSchema?.paths) return modifiedSchema;
119
+
120
+ await traverseAsync(modifiedSchema, async (node, nodePath) => {
121
+ // Check if we're at a "post" operation (paths -> /some/path -> "post").
122
+ // HTTP MCP servers are only allow post operations.
123
+ if (!nodePath || nodePath.length !== 3 || nodePath[2] !== "post") {
124
+ return node;
125
+ }
126
+
127
+ const operation = node as RecordAny;
128
+ if (!operation?.["x-zuplo-route"]) return node;
129
+
130
+ const handler = operation["x-zuplo-route"]?.handler;
131
+ if (handler?.export !== "mcpServerHandler" || !handler.options?.files)
132
+ return node;
133
+
134
+ const tools: ExtensionMcpServerTool[] = [];
135
+
136
+ for (const fileConfig of handler.options.files) {
137
+ if (!fileConfig.path || !fileConfig.operationIds) continue;
138
+
139
+ const resolvedPath = path.resolve(rootDir, "../", fileConfig.path);
140
+ const fileContent = await fs.readFile(resolvedPath, "utf-8");
141
+ const document = JSON.parse(fileContent);
142
+
143
+ if (document) {
144
+ const fileTools = findOperationsInDocument(
145
+ document,
146
+ fileConfig.operationIds,
147
+ );
148
+
149
+ tools.push(...fileTools);
150
+ }
151
+ }
152
+
153
+ const mcpExtension: ExtensionMcpServer = {
154
+ name: DEFAULT_MCP_SERVER_NAME,
155
+ version: DEFAULT_MCP_SERVER_VERSION,
156
+ };
157
+
158
+ if (tools.length > 0) {
159
+ mcpExtension.tools = tools;
160
+ }
161
+
162
+ node["x-mcp-server"] = mcpExtension;
163
+ return node;
164
+ });
165
+
166
+ return modifiedSchema;
167
+ };
168
+ };