zudoku 0.0.0-z6bcd96da → 0.0.0-z6e949a6d

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 (212) hide show
  1. package/dist/config/loader.js +3 -1
  2. package/dist/config/loader.js.map +1 -1
  3. package/dist/config/validators/InputNavigationSchema.d.ts +170 -120
  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 +6 -0
  8. package/dist/config/validators/NavigationSchema.js.map +1 -1
  9. package/dist/config/validators/ProtectedRoutesSchema.d.ts +1 -1
  10. package/dist/config/validators/validate.d.ts +7 -7
  11. package/dist/flat-config.d.ts +35 -24
  12. package/dist/lib/components/Heading.d.ts +1 -1
  13. package/dist/lib/components/MobileTopNavigation.js +2 -1
  14. package/dist/lib/components/MobileTopNavigation.js.map +1 -1
  15. package/dist/lib/components/TopNavigation.d.ts +7 -1
  16. package/dist/lib/components/TopNavigation.js +7 -2
  17. package/dist/lib/components/TopNavigation.js.map +1 -1
  18. package/dist/lib/components/context/ZudokuContext.d.ts +8 -1
  19. package/dist/lib/components/context/ZudokuContext.js +2 -0
  20. package/dist/lib/components/context/ZudokuContext.js.map +1 -1
  21. package/dist/lib/components/index.d.ts +18 -74
  22. package/dist/lib/components/index.js +19 -36
  23. package/dist/lib/components/index.js.map +1 -1
  24. package/dist/lib/components/navigation/Navigation.js +4 -3
  25. package/dist/lib/components/navigation/Navigation.js.map +1 -1
  26. package/dist/lib/components/navigation/NavigationCategory.js +8 -0
  27. package/dist/lib/components/navigation/NavigationCategory.js.map +1 -1
  28. package/dist/lib/components/navigation/NavigationFilterContext.d.ts +8 -0
  29. package/dist/lib/components/navigation/NavigationFilterContext.js +12 -0
  30. package/dist/lib/components/navigation/NavigationFilterContext.js.map +1 -0
  31. package/dist/lib/components/navigation/NavigationFilterInput.d.ts +3 -0
  32. package/dist/lib/components/navigation/NavigationFilterInput.js +9 -0
  33. package/dist/lib/components/navigation/NavigationFilterInput.js.map +1 -0
  34. package/dist/lib/components/navigation/NavigationItem.js +11 -1
  35. package/dist/lib/components/navigation/NavigationItem.js.map +1 -1
  36. package/dist/lib/components/navigation/utils.d.ts +2 -1
  37. package/dist/lib/components/navigation/utils.js +22 -1
  38. package/dist/lib/components/navigation/utils.js.map +1 -1
  39. package/dist/lib/core/plugins.d.ts +11 -1
  40. package/dist/lib/core/plugins.js +1 -0
  41. package/dist/lib/core/plugins.js.map +1 -1
  42. package/dist/lib/core/transform-config.d.ts +2 -0
  43. package/dist/lib/core/transform-config.js +22 -0
  44. package/dist/lib/core/transform-config.js.map +1 -0
  45. package/dist/lib/hooks/index.d.ts +7 -30
  46. package/dist/lib/hooks/index.js +7 -15
  47. package/dist/lib/hooks/index.js.map +1 -1
  48. package/dist/lib/oas/graphql/circular.d.ts +1 -1
  49. package/dist/lib/oas/graphql/circular.js +18 -35
  50. package/dist/lib/oas/graphql/circular.js.map +1 -1
  51. package/dist/lib/oas/graphql/circular.test.js +33 -2
  52. package/dist/lib/oas/graphql/circular.test.js.map +1 -1
  53. package/dist/lib/oas/parser/index.js +14 -5
  54. package/dist/lib/oas/parser/index.js.map +1 -1
  55. package/dist/lib/plugins/openapi/playground/fileUtils.d.ts +1 -0
  56. package/dist/lib/plugins/openapi/playground/fileUtils.js +3 -0
  57. package/dist/lib/plugins/openapi/playground/fileUtils.js.map +1 -1
  58. package/dist/lib/plugins/openapi/playground/result-panel/AudioPlayer.d.ts +6 -0
  59. package/dist/lib/plugins/openapi/playground/result-panel/AudioPlayer.js +20 -0
  60. package/dist/lib/plugins/openapi/playground/result-panel/AudioPlayer.js.map +1 -0
  61. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js +7 -2
  62. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js.map +1 -1
  63. package/dist/lib/ui/Command.d.ts +3 -3
  64. package/dist/lib/ui/InputGroup.d.ts +16 -0
  65. package/dist/lib/ui/InputGroup.js +65 -0
  66. package/dist/lib/ui/InputGroup.js.map +1 -0
  67. package/dist/lib/util/flattenAllOf.d.ts +0 -2
  68. package/dist/lib/util/flattenAllOf.js +0 -46
  69. package/dist/lib/util/flattenAllOf.js.map +1 -1
  70. package/dist/lib/util/flattenAllOf.test.js +2 -1
  71. package/dist/lib/util/flattenAllOf.test.js.map +1 -1
  72. package/dist/lib/util/flattenAllOfProcessor.d.ts +2 -0
  73. package/dist/lib/util/flattenAllOfProcessor.js +48 -0
  74. package/dist/lib/util/flattenAllOfProcessor.js.map +1 -0
  75. package/dist/lib/util/readFrontmatter.js +2 -1
  76. package/dist/lib/util/readFrontmatter.js.map +1 -1
  77. package/dist/vite/api/SchemaManager.js +1 -1
  78. package/dist/vite/api/SchemaManager.js.map +1 -1
  79. package/dist/vite/api/SchemaManager.test.js +1 -1
  80. package/dist/vite/api/SchemaManager.test.js.map +1 -1
  81. package/dist/vite/build.js +91 -73
  82. package/dist/vite/build.js.map +1 -1
  83. package/dist/vite/mdx/remark-inject-filepath.js +5 -1
  84. package/dist/vite/mdx/remark-inject-filepath.js.map +1 -1
  85. package/dist/vite/mdx/remark-link-rewrite.js +3 -2
  86. package/dist/vite/mdx/remark-link-rewrite.js.map +1 -1
  87. package/dist/vite/plugin-docs.js +9 -7
  88. package/dist/vite/plugin-docs.js.map +1 -1
  89. package/dist/vite/plugin-markdown-export.js +4 -2
  90. package/dist/vite/plugin-markdown-export.js.map +1 -1
  91. package/lib/{ClaudeLogo-DJ9bU-sO.js → ClaudeLogo-OpUSMQJe.js} +26 -22
  92. package/lib/{ClaudeLogo-DJ9bU-sO.js.map → ClaudeLogo-OpUSMQJe.js.map} +1 -1
  93. package/lib/{MdxPage-stpAoBtx.js → MdxPage-dzCPGdvD.js} +8 -8
  94. package/lib/{MdxPage-stpAoBtx.js.map → MdxPage-dzCPGdvD.js.map} +1 -1
  95. package/lib/{Mermaid-Koc3z8mU.js → Mermaid-JEnWyK0s.js} +3 -2
  96. package/lib/{Mermaid-Koc3z8mU.js.map → Mermaid-JEnWyK0s.js.map} +1 -1
  97. package/lib/{OAuthErrorPage-DJ811Bn_.js → OAuthErrorPage-D7n-_cqN.js} +20 -18
  98. package/lib/{OAuthErrorPage-DJ811Bn_.js.map → OAuthErrorPage-D7n-_cqN.js.map} +1 -1
  99. package/lib/{OasProvider-CS_ASmBB.js → OasProvider-DPPdikt_.js} +3 -3
  100. package/lib/{OasProvider-CS_ASmBB.js.map → OasProvider-DPPdikt_.js.map} +1 -1
  101. package/lib/{OperationList-Dq_AB4W9.js → OperationList-cEveQ_l5.js} +948 -946
  102. package/lib/OperationList-cEveQ_l5.js.map +1 -0
  103. package/lib/{RouteGuard--A04ESy8.js → RouteGuard-BMbu_Yb7.js} +5 -5
  104. package/lib/{RouteGuard--A04ESy8.js.map → RouteGuard-BMbu_Yb7.js.map} +1 -1
  105. package/lib/{SchemaList-BJZJv1gD.js → SchemaList-CRC8n5co.js} +7 -7
  106. package/lib/{SchemaList-BJZJv1gD.js.map → SchemaList-CRC8n5co.js.map} +1 -1
  107. package/lib/{SchemaView-U4JMYB3N.js → SchemaView-BR6dtnPg.js} +3 -3
  108. package/lib/{SchemaView-U4JMYB3N.js.map → SchemaView-BR6dtnPg.js.map} +1 -1
  109. package/lib/{SignUp-DCBViNUi.js → SignUp-ChqXj9vd.js} +31 -26
  110. package/lib/{SignUp-DCBViNUi.js.map → SignUp-ChqXj9vd.js.map} +1 -1
  111. package/lib/{SyntaxHighlight-Dshjn3Zf.js → SyntaxHighlight-O-IZOPLg.js} +3 -3
  112. package/lib/{SyntaxHighlight-Dshjn3Zf.js.map → SyntaxHighlight-O-IZOPLg.js.map} +1 -1
  113. package/lib/{Toc-Cgz6CPiE.js → Toc-DQF7trHT.js} +2 -2
  114. package/lib/{Toc-Cgz6CPiE.js.map → Toc-DQF7trHT.js.map} +1 -1
  115. package/lib/{index-CL8eDnQW.js → Zudoku-DA1yA-te.js} +2609 -2452
  116. package/lib/Zudoku-DA1yA-te.js.map +1 -0
  117. package/lib/{ZudokuContext-BZB1TWdT.js → ZudokuContext-C6wlLMUH.js} +137 -135
  118. package/lib/{ZudokuContext-BZB1TWdT.js.map → ZudokuContext-C6wlLMUH.js.map} +1 -1
  119. package/lib/{circular-BmMJjG1v.js → circular-C4l1Kj1N.js} +1327 -1346
  120. package/lib/{circular-BmMJjG1v.js.map → circular-C4l1Kj1N.js.map} +1 -1
  121. package/lib/createServer-DoRZ6tMa.js +13036 -0
  122. package/lib/createServer-DoRZ6tMa.js.map +1 -0
  123. package/lib/{errors-b9I-fAOY.js → errors-CYLN8SNc.js} +3 -3
  124. package/lib/{errors-b9I-fAOY.js.map → errors-CYLN8SNc.js.map} +1 -1
  125. package/lib/{firebase-BCXX7Qv5.js → firebase-DF-VVKB7.js} +14 -14
  126. package/lib/firebase-DF-VVKB7.js.map +1 -0
  127. package/lib/{hook-BGlHBdET.js → hook-C35h0YhF.js} +2 -2
  128. package/lib/{hook-BGlHBdET.js.map → hook-C35h0YhF.js.map} +1 -1
  129. package/lib/{index-O9RHI87z.js → index-Ck4TmzTO.js} +574 -538
  130. package/lib/index-Ck4TmzTO.js.map +1 -0
  131. package/lib/index-DAWHN3cH.js +86 -0
  132. package/lib/index-DAWHN3cH.js.map +1 -0
  133. package/lib/{index-UOLtazB8.js → index-DrAVvbXa.js} +2 -2
  134. package/lib/{index-UOLtazB8.js.map → index-DrAVvbXa.js.map} +1 -1
  135. package/lib/{index.esm-C5CBsVzN.js → index.esm-B2cLXwjS.js} +2 -2
  136. package/lib/index.esm-B2cLXwjS.js.map +1 -0
  137. package/lib/{index.esm-B_0dvNjB.js → index.esm-Ca5zvoff.js} +20 -20
  138. package/lib/{index.esm-B_0dvNjB.js.map → index.esm-Ca5zvoff.js.map} +1 -1
  139. package/lib/{invariant-BJAl77rw.js → invariant-B_t_F2s_.js} +3 -3
  140. package/lib/{invariant-BJAl77rw.js.map → invariant-B_t_F2s_.js.map} +1 -1
  141. package/lib/ui/InputGroup.js +155 -0
  142. package/lib/ui/InputGroup.js.map +1 -0
  143. package/lib/ui/SyntaxHighlight.js +3 -3
  144. package/lib/useExposedProps-CzTDfXfq.js +30 -0
  145. package/lib/useExposedProps-CzTDfXfq.js.map +1 -0
  146. package/lib/zudoku.__internal.js +1493 -1031
  147. package/lib/zudoku.__internal.js.map +1 -1
  148. package/lib/zudoku.auth-auth0.js +1 -1
  149. package/lib/zudoku.auth-azureb2c.js +4 -4
  150. package/lib/zudoku.auth-clerk.js +2 -2
  151. package/lib/zudoku.auth-firebase.js +5 -5
  152. package/lib/zudoku.auth-openid.js +5 -5
  153. package/lib/zudoku.auth-supabase.js +4 -4
  154. package/lib/zudoku.components.js +31 -29
  155. package/lib/zudoku.components.js.map +1 -1
  156. package/lib/zudoku.hooks.js +24 -11
  157. package/lib/zudoku.hooks.js.map +1 -1
  158. package/lib/zudoku.mermaid.js +4 -3
  159. package/lib/zudoku.mermaid.js.map +1 -1
  160. package/lib/zudoku.plugin-api-catalog.js +36 -32
  161. package/lib/zudoku.plugin-api-catalog.js.map +1 -1
  162. package/lib/zudoku.plugin-api-keys.js +131 -130
  163. package/lib/zudoku.plugin-api-keys.js.map +1 -1
  164. package/lib/zudoku.plugin-custom-pages.js +1 -1
  165. package/lib/zudoku.plugin-markdown.js +1 -1
  166. package/lib/zudoku.plugin-openapi.js +2 -2
  167. package/lib/zudoku.plugin-search-pagefind.js +2 -2
  168. package/lib/zudoku.plugins.js +9 -8
  169. package/lib/zudoku.plugins.js.map +1 -1
  170. package/package.json +6 -4
  171. package/src/lib/components/MobileTopNavigation.tsx +13 -8
  172. package/src/lib/components/TopNavigation.tsx +25 -7
  173. package/src/lib/components/context/ZudokuContext.ts +1 -0
  174. package/src/lib/components/index.ts +19 -39
  175. package/src/lib/components/navigation/Navigation.tsx +4 -3
  176. package/src/lib/components/navigation/NavigationCategory.tsx +9 -0
  177. package/src/lib/components/navigation/NavigationFilterContext.tsx +28 -0
  178. package/src/lib/components/navigation/NavigationFilterInput.tsx +35 -0
  179. package/src/lib/components/navigation/NavigationItem.tsx +17 -1
  180. package/src/lib/components/navigation/utils.ts +32 -1
  181. package/src/lib/core/plugins.ts +21 -1
  182. package/src/lib/core/transform-config.ts +29 -0
  183. package/src/lib/hooks/index.ts +7 -16
  184. package/src/lib/oas/graphql/circular.test.ts +37 -2
  185. package/src/lib/oas/graphql/circular.ts +25 -51
  186. package/src/lib/oas/parser/index.ts +17 -6
  187. package/src/lib/plugins/openapi/playground/fileUtils.ts +4 -0
  188. package/src/lib/plugins/openapi/playground/result-panel/AudioPlayer.tsx +50 -0
  189. package/src/lib/plugins/openapi/playground/result-panel/ResponseTab.tsx +33 -17
  190. package/src/lib/ui/InputGroup.tsx +168 -0
  191. package/src/lib/util/flattenAllOf.test.ts +2 -1
  192. package/src/lib/util/flattenAllOf.ts +0 -57
  193. package/src/lib/util/flattenAllOfProcessor.ts +58 -0
  194. package/src/lib/util/readFrontmatter.ts +2 -1
  195. package/src/zuplo/enrich-with-zuplo-mcp.ts +168 -0
  196. package/src/zuplo/enrich-with-zuplo.ts +254 -0
  197. package/src/zuplo/policy-types.ts +46 -0
  198. package/src/zuplo/with-zuplo-processors.ts +35 -0
  199. package/src/zuplo/with-zuplo.ts +14 -0
  200. package/lib/OperationList-Dq_AB4W9.js.map +0 -1
  201. package/lib/Separator-BXt1LYnm.js +0 -27
  202. package/lib/Separator-BXt1LYnm.js.map +0 -1
  203. package/lib/___vite-browser-external_commonjs-proxy-BttVsNON.js +0 -9
  204. package/lib/___vite-browser-external_commonjs-proxy-BttVsNON.js.map +0 -1
  205. package/lib/createServer-CLSZ7hWJ.js +0 -16693
  206. package/lib/createServer-CLSZ7hWJ.js.map +0 -1
  207. package/lib/firebase-BCXX7Qv5.js.map +0 -1
  208. package/lib/index-CL8eDnQW.js.map +0 -1
  209. package/lib/index-DBjOT2H1.js +0 -133
  210. package/lib/index-DBjOT2H1.js.map +0 -1
  211. package/lib/index-O9RHI87z.js.map +0 -1
  212. package/lib/index.esm-C5CBsVzN.js.map +0 -1
@@ -1,6 +1,5 @@
1
1
  import { GraphQLScalarType } from "graphql/index.js";
2
2
  import { GraphQLJSON } from "graphql-type-json";
3
- import type { RecordAny } from "../../util/traverse.js";
4
3
 
5
4
  export const CIRCULAR_REF = "$[Circular Reference]";
6
5
  export const SCHEMA_REF_PREFIX = "$ref:";
@@ -17,73 +16,48 @@ const OPENAPI_PROPS = new Set([
17
16
  export const handleCircularRefs = (
18
17
  // biome-ignore lint/suspicious/noExplicitAny: Allow any type
19
18
  obj: any,
20
- visited = new WeakSet(),
19
+ currentPath = new WeakSet(),
21
20
  refs = new WeakMap(),
22
21
  path: string[] = [],
23
- seenRefPaths = new Set<string>(),
22
+ currentRefPaths = new Set<string>(),
24
23
  // biome-ignore lint/suspicious/noExplicitAny: Allow any type
25
24
  ): any => {
26
25
  if (obj === null || typeof obj !== "object") return obj;
27
26
 
28
27
  const refPath = obj.__$ref;
28
+ const isCircular =
29
+ currentPath.has(obj) ||
30
+ (typeof refPath === "string" && currentRefPaths.has(refPath));
29
31
 
30
- // Check if this object has a __$ref marker (set during schema code generation)
31
- // If we've already fully processed this ref path, return a reference marker
32
- // instead of the full data to avoid JSON.stringify serializing duplicates
33
- if (typeof refPath === "string" && seenRefPaths.has(refPath)) {
34
- return SCHEMA_REF_PREFIX + refPath;
35
- }
36
-
37
- if (visited.has(obj)) {
38
- const cached = refs.get(obj);
39
- if (cached) {
40
- return typeof refPath === "string"
41
- ? // If already processed, return ref marker to avoid duplicate serialization
42
- SCHEMA_REF_PREFIX + refPath
43
- : cached;
44
- }
32
+ if (isCircular) {
33
+ if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
45
34
  const circularProp = path.find((p) => !OPENAPI_PROPS.has(p)) || path[0];
46
-
47
35
  return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
48
36
  }
49
37
 
50
- visited.add(obj);
38
+ if (refs.has(obj)) return refs.get(obj);
51
39
 
52
- // Add refPath BEFORE recursing to detect cycles within this branch
53
- // This will be removed after processing to allow siblings with the same ref
54
- if (typeof refPath === "string") {
55
- seenRefPaths.add(refPath);
56
- }
40
+ currentPath.add(obj);
41
+ if (typeof refPath === "string") currentRefPaths.add(refPath);
57
42
 
58
- let result: RecordAny | RecordAny[];
59
- if (Array.isArray(obj)) {
60
- result = obj.map((item, index) =>
61
- handleCircularRefs(
62
- item,
63
- visited,
64
- refs,
65
- [...path, index.toString()],
66
- seenRefPaths,
67
- ),
43
+ const recurse = (value: unknown, key: string) =>
44
+ handleCircularRefs(
45
+ value,
46
+ currentPath,
47
+ refs,
48
+ [...path, key],
49
+ currentRefPaths,
68
50
  );
69
- } else {
70
- result = {};
71
- for (const [key, value] of Object.entries(obj)) {
72
- result[key] = handleCircularRefs(
73
- value,
74
- visited,
75
- refs,
76
- [...path, key],
77
- seenRefPaths,
51
+
52
+ const result = Array.isArray(obj)
53
+ ? obj.map((item, i) => recurse(item, i.toString()))
54
+ : Object.fromEntries(
55
+ Object.entries(obj).map(([k, v]) => [k, recurse(v, k)]),
78
56
  );
79
- }
80
- }
81
- refs.set(obj, result);
82
57
 
83
- // Remove refPath after processing so sibling refs aren't incorrectly marked
84
- if (typeof refPath === "string") {
85
- seenRefPaths.delete(refPath);
86
- }
58
+ refs.set(obj, result);
59
+ currentPath.delete(obj);
60
+ if (typeof refPath === "string") currentRefPaths.delete(refPath);
87
61
 
88
62
  return result;
89
63
  };
@@ -1,6 +1,7 @@
1
1
  import { GraphQLError } from "graphql/error/index.js";
2
2
  import { OpenAPIV3, type OpenAPIV3_1 } from "openapi-types";
3
- import { flattenAllOfProcessor } from "../../util/flattenAllOf.js";
3
+ import { flattenAllOf } from "../../util/flattenAllOf.js";
4
+ import { traverse } from "../../util/traverse.js";
4
5
  import { dereference, type JSONSchema } from "./dereference/index.js";
5
6
  import { upgradeSchema } from "./upgrade/index.js";
6
7
 
@@ -104,11 +105,21 @@ export const validate = async (schemaInput: unknown) => {
104
105
  const dereferenced = await dereference(schema);
105
106
  const upgraded = upgradeSchema(dereferenced);
106
107
 
107
- const flattened = await flattenAllOfProcessor({
108
- schema: upgraded,
109
- file: "schema.json",
110
- dereference: async (schema) => schema,
111
- });
108
+ const flattened = traverse(upgraded, (spec) => {
109
+ if (!spec || typeof spec !== "object" || Array.isArray(spec)) {
110
+ return spec;
111
+ }
112
+ const isSchemaObject =
113
+ "type" in spec ||
114
+ "properties" in spec ||
115
+ "allOf" in spec ||
116
+ "anyOf" in spec ||
117
+ "oneOf" in spec;
118
+
119
+ if (!isSchemaObject) return spec;
120
+
121
+ return flattenAllOf(spec) as typeof spec;
122
+ }) as OpenAPIDocument;
112
123
 
113
124
  return flattened;
114
125
  };
@@ -4,6 +4,10 @@ export function isBinaryContentType(contentType: string) {
4
4
  );
5
5
  }
6
6
 
7
+ export function isAudioContentType(contentType: string) {
8
+ return /^audio\//i.test(contentType);
9
+ }
10
+
7
11
  export const extractFileName = (
8
12
  headers: Array<[string, string]>,
9
13
  url: string,
@@ -0,0 +1,50 @@
1
+ import { DownloadIcon } from "lucide-react";
2
+ import { useEffect, useState } from "react";
3
+ import { Button } from "zudoku/ui/Button.js";
4
+ import { humanFileSize } from "../../../../util/humanFileSize.js";
5
+
6
+ export const AudioPlayer = ({
7
+ blob,
8
+ fileName,
9
+ size,
10
+ onDownload,
11
+ }: {
12
+ blob: Blob;
13
+ fileName: string;
14
+ size: number;
15
+ onDownload: () => void;
16
+ }) => {
17
+ const [audioUrl, setAudioUrl] = useState<string | null>(null);
18
+
19
+ useEffect(() => {
20
+ const url = URL.createObjectURL(blob);
21
+ setAudioUrl(url);
22
+
23
+ return () => {
24
+ URL.revokeObjectURL(url);
25
+ };
26
+ }, [blob]);
27
+
28
+ if (!audioUrl) {
29
+ return (
30
+ <div className="p-4 text-center">
31
+ <div className="text-sm text-muted-foreground">Loading audio...</div>
32
+ </div>
33
+ );
34
+ }
35
+
36
+ return (
37
+ <div className="p-4 text-center">
38
+ <div className="flex flex-col items-center gap-4">
39
+ {/* biome-ignore lint/a11y/useMediaCaption: API response audio cannot have predefined captions */}
40
+ <audio controls src={audioUrl} className="w-full max-w-md">
41
+ Your browser does not support the audio element.
42
+ </audio>
43
+ <Button onClick={onDownload} className="flex items-center gap-2">
44
+ <DownloadIcon className="h-4 w-4" />
45
+ Download {fileName} ({humanFileSize(size)})
46
+ </Button>
47
+ </div>
48
+ </div>
49
+ );
50
+ };
@@ -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"
@@ -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
  };