@autoview/cli 0.1.0

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 (297) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +407 -0
  3. package/lib/AutoViewAgent.d.ts +109 -0
  4. package/lib/AutoViewAgent.js +123 -0
  5. package/lib/AutoViewAgent.js.map +1 -0
  6. package/lib/agent/emitMcpServer.d.ts +15 -0
  7. package/lib/agent/emitMcpServer.js +157 -0
  8. package/lib/agent/emitMcpServer.js.map +1 -0
  9. package/lib/agent/emitReport.d.ts +14 -0
  10. package/lib/agent/emitReport.js +85 -0
  11. package/lib/agent/emitReport.js.map +1 -0
  12. package/lib/agent/toolSurface.d.ts +130 -0
  13. package/lib/agent/toolSurface.js +342 -0
  14. package/lib/agent/toolSurface.js.map +1 -0
  15. package/lib/agent/verifyAgentTasks.d.ts +87 -0
  16. package/lib/agent/verifyAgentTasks.js +126 -0
  17. package/lib/agent/verifyAgentTasks.js.map +1 -0
  18. package/lib/cli/main.d.ts +2 -0
  19. package/lib/cli/main.js +295 -0
  20. package/lib/cli/main.js.map +1 -0
  21. package/lib/compiler/AutoViewInterfaceCompiler.d.ts +27 -0
  22. package/lib/compiler/AutoViewInterfaceCompiler.js +68 -0
  23. package/lib/compiler/AutoViewInterfaceCompiler.js.map +1 -0
  24. package/lib/constants/AutoViewFrontendTemplate.d.ts +1 -0
  25. package/lib/constants/AutoViewFrontendTemplate.js +46 -0
  26. package/lib/constants/AutoViewFrontendTemplate.js.map +1 -0
  27. package/lib/constants/AutoViewSystemPromptConstant.d.ts +5 -0
  28. package/lib/constants/AutoViewSystemPromptConstant.js +4 -0
  29. package/lib/constants/AutoViewSystemPromptConstant.js.map +1 -0
  30. package/lib/context/IAutoViewAgentContext.d.ts +60 -0
  31. package/lib/context/IAutoViewAgentContext.js +3 -0
  32. package/lib/context/IAutoViewAgentContext.js.map +1 -0
  33. package/lib/fromSwagger.d.ts +53 -0
  34. package/lib/fromSwagger.js +513 -0
  35. package/lib/fromSwagger.js.map +1 -0
  36. package/lib/generateDeterministic.d.ts +26 -0
  37. package/lib/generateDeterministic.js +75 -0
  38. package/lib/generateDeterministic.js.map +1 -0
  39. package/lib/index.d.ts +15 -0
  40. package/lib/index.js +41 -0
  41. package/lib/index.js.map +1 -0
  42. package/lib/orchestrate/orchestrateAutoView.d.ts +17 -0
  43. package/lib/orchestrate/orchestrateAutoView.js +491 -0
  44. package/lib/orchestrate/orchestrateAutoView.js.map +1 -0
  45. package/lib/orchestrate/orchestrateAutoViewProductPlan.d.ts +37 -0
  46. package/lib/orchestrate/orchestrateAutoViewProductPlan.js +109 -0
  47. package/lib/orchestrate/orchestrateAutoViewProductPlan.js.map +1 -0
  48. package/lib/orchestrate/orchestrateAutoViewRender.d.ts +133 -0
  49. package/lib/orchestrate/orchestrateAutoViewRender.js +943 -0
  50. package/lib/orchestrate/orchestrateAutoViewRender.js.map +1 -0
  51. package/lib/orchestrate/orchestrateAutoViewRenderDeterministic.d.ts +24 -0
  52. package/lib/orchestrate/orchestrateAutoViewRenderDeterministic.js +92 -0
  53. package/lib/orchestrate/orchestrateAutoViewRenderDeterministic.js.map +1 -0
  54. package/lib/orchestrate/orchestrateAutoViewReview.d.ts +48 -0
  55. package/lib/orchestrate/orchestrateAutoViewReview.js +328 -0
  56. package/lib/orchestrate/orchestrateAutoViewReview.js.map +1 -0
  57. package/lib/orchestrate/orchestrateAutoViewScaffold.d.ts +45 -0
  58. package/lib/orchestrate/orchestrateAutoViewScaffold.js +586 -0
  59. package/lib/orchestrate/orchestrateAutoViewScaffold.js.map +1 -0
  60. package/lib/orchestrate/orchestrateAutoViewSdkStudy.d.ts +26 -0
  61. package/lib/orchestrate/orchestrateAutoViewSdkStudy.js +85 -0
  62. package/lib/orchestrate/orchestrateAutoViewSdkStudy.js.map +1 -0
  63. package/lib/orchestrate/structures/IAutoViewProductPlan.d.ts +96 -0
  64. package/lib/orchestrate/structures/IAutoViewProductPlan.js +3 -0
  65. package/lib/orchestrate/structures/IAutoViewProductPlan.js.map +1 -0
  66. package/lib/orchestrate/structures/IAutoViewProductPlanApplication.d.ts +38 -0
  67. package/lib/orchestrate/structures/IAutoViewProductPlanApplication.js +3 -0
  68. package/lib/orchestrate/structures/IAutoViewProductPlanApplication.js.map +1 -0
  69. package/lib/orchestrate/structures/IAutoViewRenderApplication.d.ts +38 -0
  70. package/lib/orchestrate/structures/IAutoViewRenderApplication.js +3 -0
  71. package/lib/orchestrate/structures/IAutoViewRenderApplication.js.map +1 -0
  72. package/lib/orchestrate/structures/IAutoViewReviewApplication.d.ts +40 -0
  73. package/lib/orchestrate/structures/IAutoViewReviewApplication.js +3 -0
  74. package/lib/orchestrate/structures/IAutoViewReviewApplication.js.map +1 -0
  75. package/lib/orchestrate/structures/IAutoViewSdkMap.d.ts +63 -0
  76. package/lib/orchestrate/structures/IAutoViewSdkMap.js +3 -0
  77. package/lib/orchestrate/structures/IAutoViewSdkMap.js.map +1 -0
  78. package/lib/orchestrate/structures/IAutoViewSdkStudyApplication.d.ts +37 -0
  79. package/lib/orchestrate/structures/IAutoViewSdkStudyApplication.js +3 -0
  80. package/lib/orchestrate/structures/IAutoViewSdkStudyApplication.js.map +1 -0
  81. package/lib/orchestrate/utils/HistoryMessage.d.ts +10 -0
  82. package/lib/orchestrate/utils/HistoryMessage.js +25 -0
  83. package/lib/orchestrate/utils/HistoryMessage.js.map +1 -0
  84. package/lib/orchestrate/utils/auditFrontendRuntime.d.ts +53 -0
  85. package/lib/orchestrate/utils/auditFrontendRuntime.js +362 -0
  86. package/lib/orchestrate/utils/auditFrontendRuntime.js.map +1 -0
  87. package/lib/orchestrate/utils/buildDeterministicPlan.d.ts +4 -0
  88. package/lib/orchestrate/utils/buildDeterministicPlan.js +233 -0
  89. package/lib/orchestrate/utils/buildDeterministicPlan.js.map +1 -0
  90. package/lib/orchestrate/utils/buildDeterministicSdkMap.d.ts +22 -0
  91. package/lib/orchestrate/utils/buildDeterministicSdkMap.js +154 -0
  92. package/lib/orchestrate/utils/buildDeterministicSdkMap.js.map +1 -0
  93. package/lib/orchestrate/utils/cacheNodeModules.d.ts +31 -0
  94. package/lib/orchestrate/utils/cacheNodeModules.js +134 -0
  95. package/lib/orchestrate/utils/cacheNodeModules.js.map +1 -0
  96. package/lib/orchestrate/utils/describeEndpointPropsShape.d.ts +37 -0
  97. package/lib/orchestrate/utils/describeEndpointPropsShape.js +192 -0
  98. package/lib/orchestrate/utils/describeEndpointPropsShape.js.map +1 -0
  99. package/lib/orchestrate/utils/describeEndpointRequestBodyShape.d.ts +22 -0
  100. package/lib/orchestrate/utils/describeEndpointRequestBodyShape.js +29 -0
  101. package/lib/orchestrate/utils/describeEndpointRequestBodyShape.js.map +1 -0
  102. package/lib/orchestrate/utils/describeEndpointResponseShape.d.ts +19 -0
  103. package/lib/orchestrate/utils/describeEndpointResponseShape.js +30 -0
  104. package/lib/orchestrate/utils/describeEndpointResponseShape.js.map +1 -0
  105. package/lib/orchestrate/utils/executeCachedBatch.d.ts +22 -0
  106. package/lib/orchestrate/utils/executeCachedBatch.js +64 -0
  107. package/lib/orchestrate/utils/executeCachedBatch.js.map +1 -0
  108. package/lib/orchestrate/utils/loadShoppingFixture.d.ts +33 -0
  109. package/lib/orchestrate/utils/loadShoppingFixture.js +17 -0
  110. package/lib/orchestrate/utils/loadShoppingFixture.js.map +1 -0
  111. package/lib/orchestrate/utils/normalizeProductPlanPaths.d.ts +24 -0
  112. package/lib/orchestrate/utils/normalizeProductPlanPaths.js +77 -0
  113. package/lib/orchestrate/utils/normalizeProductPlanPaths.js.map +1 -0
  114. package/lib/orchestrate/utils/renderJsonSchema.d.ts +23 -0
  115. package/lib/orchestrate/utils/renderJsonSchema.js +122 -0
  116. package/lib/orchestrate/utils/renderJsonSchema.js.map +1 -0
  117. package/lib/orchestrate/utils/renderResourcePage.d.ts +36 -0
  118. package/lib/orchestrate/utils/renderResourcePage.js +1415 -0
  119. package/lib/orchestrate/utils/renderResourcePage.js.map +1 -0
  120. package/lib/orchestrate/utils/validateFrontendTypecheck.d.ts +109 -0
  121. package/lib/orchestrate/utils/validateFrontendTypecheck.js +274 -0
  122. package/lib/orchestrate/utils/validateFrontendTypecheck.js.map +1 -0
  123. package/lib/preview/renderPreview.d.ts +22 -0
  124. package/lib/preview/renderPreview.js +198 -0
  125. package/lib/preview/renderPreview.js.map +1 -0
  126. package/lib/typings/compiler.d.ts +39 -0
  127. package/lib/typings/compiler.js +3 -0
  128. package/lib/typings/compiler.js.map +1 -0
  129. package/lib/typings/events.d.ts +106 -0
  130. package/lib/typings/events.js +3 -0
  131. package/lib/typings/events.js.map +1 -0
  132. package/lib/typings/index.d.ts +10 -0
  133. package/lib/typings/index.js +27 -0
  134. package/lib/typings/index.js.map +1 -0
  135. package/lib/typings/misc.d.ts +78 -0
  136. package/lib/typings/misc.js +3 -0
  137. package/lib/typings/misc.js.map +1 -0
  138. package/lib/utils/ArrayUtil.d.ts +8 -0
  139. package/lib/utils/ArrayUtil.js +30 -0
  140. package/lib/utils/ArrayUtil.js.map +1 -0
  141. package/lib/utils/StringUtil.d.ts +11 -0
  142. package/lib/utils/StringUtil.js +28 -0
  143. package/lib/utils/StringUtil.js.map +1 -0
  144. package/lib/utils/classifyEndpoints.d.ts +62 -0
  145. package/lib/utils/classifyEndpoints.js +216 -0
  146. package/lib/utils/classifyEndpoints.js.map +1 -0
  147. package/lib/utils/endpointFilter.d.ts +26 -0
  148. package/lib/utils/endpointFilter.js +0 -0
  149. package/lib/utils/endpointFilter.js.map +1 -0
  150. package/lib/utils/extractFields.d.ts +85 -0
  151. package/lib/utils/extractFields.js +231 -0
  152. package/lib/utils/extractFields.js.map +1 -0
  153. package/lib/utils/index.d.ts +13 -0
  154. package/lib/utils/index.js +30 -0
  155. package/lib/utils/index.js.map +1 -0
  156. package/lib/utils/normalizeForNestia.d.ts +34 -0
  157. package/lib/utils/normalizeForNestia.js +133 -0
  158. package/lib/utils/normalizeForNestia.js.map +1 -0
  159. package/lib/utils/resourcePlan.d.ts +39 -0
  160. package/lib/utils/resourcePlan.js +95 -0
  161. package/lib/utils/resourcePlan.js.map +1 -0
  162. package/lib/utils/sliceDocument.d.ts +17 -0
  163. package/lib/utils/sliceDocument.js +114 -0
  164. package/lib/utils/sliceDocument.js.map +1 -0
  165. package/lib/utils/toEndpoints.d.ts +90 -0
  166. package/lib/utils/toEndpoints.js +227 -0
  167. package/lib/utils/toEndpoints.js.map +1 -0
  168. package/lib/verify/runWorkflows.d.ts +25 -0
  169. package/lib/verify/runWorkflows.js +366 -0
  170. package/lib/verify/runWorkflows.js.map +1 -0
  171. package/lib/verify/workflows.d.ts +53 -0
  172. package/lib/verify/workflows.js +107 -0
  173. package/lib/verify/workflows.js.map +1 -0
  174. package/package.json +82 -0
  175. package/prompts/AUTOVIEW_RENDER.md +398 -0
  176. package/prompts/AUTOVIEW_REVIEW.md +60 -0
  177. package/prompts/AUTOVIEW_SDK_STUDY.md +89 -0
  178. package/src/AutoViewAgent.ts +222 -0
  179. package/src/agent/emitMcpServer.integration.test.ts +168 -0
  180. package/src/agent/emitMcpServer.test.ts +51 -0
  181. package/src/agent/emitMcpServer.ts +178 -0
  182. package/src/agent/emitReport.ts +117 -0
  183. package/src/agent/toolSurface.test.ts +243 -0
  184. package/src/agent/toolSurface.ts +501 -0
  185. package/src/agent/verifyAgentTasks.test.ts +106 -0
  186. package/src/agent/verifyAgentTasks.ts +171 -0
  187. package/src/cli/main.ts +363 -0
  188. package/src/compiler/AutoViewInterfaceCompiler.ts +69 -0
  189. package/src/constants/AutoViewFrontendTemplate.ts +42 -0
  190. package/src/constants/AutoViewSystemPromptConstant.ts +6 -0
  191. package/src/context/IAutoViewAgentContext.ts +84 -0
  192. package/src/fromSwagger.test.ts +269 -0
  193. package/src/fromSwagger.ts +500 -0
  194. package/src/generateDeterministic.test.ts +39 -0
  195. package/src/generateDeterministic.ts +77 -0
  196. package/src/index.ts +30 -0
  197. package/src/orchestrate/orchestrateAutoView.ts +590 -0
  198. package/src/orchestrate/orchestrateAutoViewProductPlan.ts +121 -0
  199. package/src/orchestrate/orchestrateAutoViewRender.ts +1117 -0
  200. package/src/orchestrate/orchestrateAutoViewRenderDeterministic.ts +101 -0
  201. package/src/orchestrate/orchestrateAutoViewReview.ts +272 -0
  202. package/src/orchestrate/orchestrateAutoViewScaffold.ts +627 -0
  203. package/src/orchestrate/orchestrateAutoViewSdkStudy.ts +90 -0
  204. package/src/orchestrate/renderNavTs.test.ts +74 -0
  205. package/src/orchestrate/structures/IAutoViewProductPlan.ts +119 -0
  206. package/src/orchestrate/structures/IAutoViewProductPlanApplication.ts +41 -0
  207. package/src/orchestrate/structures/IAutoViewRenderApplication.ts +40 -0
  208. package/src/orchestrate/structures/IAutoViewReviewApplication.ts +42 -0
  209. package/src/orchestrate/structures/IAutoViewSdkMap.ts +72 -0
  210. package/src/orchestrate/structures/IAutoViewSdkStudyApplication.ts +40 -0
  211. package/src/orchestrate/utils/HistoryMessage.ts +41 -0
  212. package/src/orchestrate/utils/auditFrontendRuntime.test.ts +18 -0
  213. package/src/orchestrate/utils/auditFrontendRuntime.ts +454 -0
  214. package/src/orchestrate/utils/buildDeterministicPlan.test.ts +170 -0
  215. package/src/orchestrate/utils/buildDeterministicPlan.ts +289 -0
  216. package/src/orchestrate/utils/buildDeterministicSdkMap.test.ts +90 -0
  217. package/src/orchestrate/utils/buildDeterministicSdkMap.ts +169 -0
  218. package/src/orchestrate/utils/cacheNodeModules.ts +136 -0
  219. package/src/orchestrate/utils/describeEndpointPropsShape.test.ts +86 -0
  220. package/src/orchestrate/utils/describeEndpointPropsShape.ts +202 -0
  221. package/src/orchestrate/utils/describeEndpointRequestBodyShape.test.ts +87 -0
  222. package/src/orchestrate/utils/describeEndpointRequestBodyShape.ts +31 -0
  223. package/src/orchestrate/utils/describeEndpointResponseShape.test.ts +70 -0
  224. package/src/orchestrate/utils/describeEndpointResponseShape.ts +32 -0
  225. package/src/orchestrate/utils/executeCachedBatch.ts +59 -0
  226. package/src/orchestrate/utils/loadShoppingFixture.ts +52 -0
  227. package/src/orchestrate/utils/normalizeProductPlanPaths.ts +92 -0
  228. package/src/orchestrate/utils/renderJsonSchema.test.ts +162 -0
  229. package/src/orchestrate/utils/renderJsonSchema.ts +133 -0
  230. package/src/orchestrate/utils/renderResourcePage.test.ts +468 -0
  231. package/src/orchestrate/utils/renderResourcePage.ts +1624 -0
  232. package/src/orchestrate/utils/validateFrontendTypecheck.test.ts +32 -0
  233. package/src/orchestrate/utils/validateFrontendTypecheck.ts +335 -0
  234. package/src/preview/renderPreview.ts +273 -0
  235. package/src/typings/compiler.ts +47 -0
  236. package/src/typings/events.ts +155 -0
  237. package/src/typings/index.ts +10 -0
  238. package/src/typings/misc.ts +93 -0
  239. package/src/utils/ArrayUtil.ts +16 -0
  240. package/src/utils/StringUtil.ts +29 -0
  241. package/src/utils/classifyEndpoints.test.ts +86 -0
  242. package/src/utils/classifyEndpoints.ts +291 -0
  243. package/src/utils/endpointFilter.test.ts +50 -0
  244. package/src/utils/endpointFilter.ts +0 -0
  245. package/src/utils/extractFields.test.ts +82 -0
  246. package/src/utils/extractFields.ts +306 -0
  247. package/src/utils/index.ts +13 -0
  248. package/src/utils/normalizeForNestia.test.ts +93 -0
  249. package/src/utils/normalizeForNestia.ts +139 -0
  250. package/src/utils/resourcePlan.test.ts +104 -0
  251. package/src/utils/resourcePlan.ts +180 -0
  252. package/src/utils/sliceDocument.test.ts +85 -0
  253. package/src/utils/sliceDocument.ts +119 -0
  254. package/src/utils/toEndpoints.test.ts +251 -0
  255. package/src/utils/toEndpoints.ts +343 -0
  256. package/src/verify/runWorkflows.ts +403 -0
  257. package/src/verify/workflows.test.ts +117 -0
  258. package/src/verify/workflows.ts +154 -0
  259. package/template/CLAUDE.md +140 -0
  260. package/template/Dockerfile +31 -0
  261. package/template/PROMPT.md +80 -0
  262. package/template/SANDBOX.md +70 -0
  263. package/template/app/api/health/route.ts +10 -0
  264. package/template/app/globals.css +97 -0
  265. package/template/app/layout.tsx +30 -0
  266. package/template/app/page.tsx +19 -0
  267. package/template/components/AppShell.tsx +114 -0
  268. package/template/components/auto/CatalogGrid.tsx +159 -0
  269. package/template/components/auto/ConfirmButton.tsx +67 -0
  270. package/template/components/auto/EmbeddedCollection.tsx +144 -0
  271. package/template/components/auto/ResourceDashboard.tsx +104 -0
  272. package/template/components/auto/ResourceDetail.tsx +93 -0
  273. package/template/components/auto/ResourceForm.tsx +235 -0
  274. package/template/components/auto/ResourceIcon.tsx +88 -0
  275. package/template/components/auto/ResourceLanding.tsx +155 -0
  276. package/template/components/auto/ResourceTable.tsx +223 -0
  277. package/template/components/auto/formatValue.tsx +186 -0
  278. package/template/components/auto/types.ts +42 -0
  279. package/template/components/ui/badge.tsx +40 -0
  280. package/template/components/ui/button.tsx +57 -0
  281. package/template/components/ui/card.tsx +86 -0
  282. package/template/components/ui/dialog.tsx +119 -0
  283. package/template/components/ui/input.tsx +23 -0
  284. package/template/components/ui/label.tsx +24 -0
  285. package/template/components/ui/pagination.tsx +117 -0
  286. package/template/components/ui/select.tsx +92 -0
  287. package/template/components/ui/sheet.tsx +135 -0
  288. package/template/components/ui/skeleton.tsx +15 -0
  289. package/template/components/ui/table.tsx +120 -0
  290. package/template/components/ui/tabs.tsx +55 -0
  291. package/template/lib/utils.ts +35 -0
  292. package/template/next.config.mjs +52 -0
  293. package/template/package.json +46 -0
  294. package/template/postcss.config.js +6 -0
  295. package/template/scripts/start-shopping-backend.sh +56 -0
  296. package/template/tailwind.config.ts +96 -0
  297. package/template/tsconfig.json +29 -0
@@ -0,0 +1,92 @@
1
+ import { IAutoViewProductPlan } from "../structures/IAutoViewProductPlan";
2
+
3
+ /**
4
+ * Normalize Next.js dynamic-segment names inside a product plan so the
5
+ * generated app router does not blow up with the `"You cannot use different
6
+ * slug names for the same dynamic path"` error.
7
+ *
8
+ * Background: the Product Plan phase routinely emits sibling routes such as
9
+ * `/sales/[id]` and `/sales/[saleId]/questions`. Next.js refuses to start when
10
+ * two routes under the same parent disagree on the dynamic-segment name. Rather
11
+ * than re-running the planner just to fix the slugs, we rewrite every dynamic
12
+ * segment in place so the first name we saw at a given parent prefix wins.
13
+ *
14
+ * The rewrite touches three places that all need to stay in sync:
15
+ *
16
+ * - `screens[].path` (and the screens are returned in the same order so the
17
+ * Render phase still maps the right TSX to the right route).
18
+ * - `navigation[].primary` / `navigation[].secondary` (path arrays referencing
19
+ * the screens above).
20
+ * - That's it — `IOmission.target` is a free-form label, not a path, so the
21
+ * function leaves it alone.
22
+ *
23
+ * Pure. Does not mutate the input.
24
+ */
25
+ export function normalizeProductPlanPaths(
26
+ plan: IAutoViewProductPlan,
27
+ ): IAutoViewProductPlan {
28
+ const canonical = new Map<string, string>(); // parentPrefix -> slug name
29
+ const rewrites = new Map<string, string>(); // original → normalized
30
+ const normalize = (path: string): string => {
31
+ const cached = rewrites.get(path);
32
+ if (cached !== undefined) return cached;
33
+ const result = normalizeSinglePath(path, canonical);
34
+ rewrites.set(path, result);
35
+ return result;
36
+ };
37
+
38
+ // First pass: build the canonical map by walking screens in order.
39
+ // The first screen that introduces a dynamic segment at a given
40
+ // parent wins, which matches the operator's mental model when they
41
+ // scan the plan top-to-bottom.
42
+ const screens: IAutoViewProductPlan.IScreen[] = plan.screens.map(
43
+ (screen) => ({
44
+ ...screen,
45
+ path: normalize(screen.path),
46
+ }),
47
+ );
48
+
49
+ // Second pass: rewrite nav references through the same map.
50
+ const navigation: IAutoViewProductPlan.INavigation[] = plan.navigation.map(
51
+ (nav) => ({
52
+ ...nav,
53
+ primary: nav.primary.map(normalize),
54
+ secondary: nav.secondary.map(normalize),
55
+ }),
56
+ );
57
+
58
+ return {
59
+ screens,
60
+ navigation,
61
+ intentionalOmissions: plan.intentionalOmissions,
62
+ };
63
+ }
64
+
65
+ function normalizeSinglePath(
66
+ path: string,
67
+ canonical: Map<string, string>,
68
+ ): string {
69
+ const segments = path.replace(/^\/+/, "").split("/");
70
+ if (segments.length === 0 || segments[0] === "") return path;
71
+ let prefix = "";
72
+ const out: string[] = [];
73
+ for (const seg of segments) {
74
+ const dynamic = /^\[(.+)\]$/.exec(seg);
75
+ if (dynamic === null) {
76
+ out.push(seg);
77
+ prefix = `${prefix}/${seg}`;
78
+ continue;
79
+ }
80
+ const known = canonical.get(prefix);
81
+ if (known === undefined) {
82
+ canonical.set(prefix, dynamic[1]!);
83
+ out.push(seg);
84
+ prefix = `${prefix}/${seg}`;
85
+ } else {
86
+ const normalized = `[${known}]`;
87
+ out.push(normalized);
88
+ prefix = `${prefix}/${normalized}`;
89
+ }
90
+ }
91
+ return `/${out.join("/")}`;
92
+ }
@@ -0,0 +1,162 @@
1
+ import { OpenApi } from "@typia/interface";
2
+ import { describe, expect, it } from "vitest";
3
+
4
+ import { renderJsonSchema, renderNamedSchema } from "./renderJsonSchema";
5
+
6
+ const doc = (
7
+ schemas: Record<string, OpenApi.IJsonSchema>,
8
+ ): OpenApi.IDocument =>
9
+ ({
10
+ operations: [],
11
+ components: {
12
+ schemas,
13
+ securitySchemes: {},
14
+ },
15
+ }) as unknown as OpenApi.IDocument;
16
+
17
+ describe("renderJsonSchema", () => {
18
+ it("renders flat objects with required + optional markers", () => {
19
+ expect(
20
+ renderJsonSchema(
21
+ {
22
+ type: "object",
23
+ description: "",
24
+ properties: {
25
+ id: { type: "string", format: "uuid", description: "" },
26
+ created_at: {
27
+ type: "string",
28
+ format: "date-time",
29
+ description: "",
30
+ },
31
+ nickname: { type: "string", description: "" },
32
+ },
33
+ required: ["id", "created_at"],
34
+ },
35
+ doc({}),
36
+ ),
37
+ ).toBe(
38
+ [
39
+ "{",
40
+ " id: string /* uuid */;",
41
+ " created_at: string /* date-time */;",
42
+ " nickname?: string;",
43
+ "}",
44
+ ].join("\n"),
45
+ );
46
+ });
47
+
48
+ it("inlines nested $ref objects", () => {
49
+ expect(
50
+ renderJsonSchema(
51
+ { $ref: "#/components/schemas/IAuthorized" },
52
+ doc({
53
+ IAuthorized: {
54
+ type: "object",
55
+ description: "",
56
+ properties: {
57
+ id: { type: "string", description: "" },
58
+ token: { $ref: "#/components/schemas/IAuthorizationToken" },
59
+ },
60
+ required: ["id", "token"],
61
+ },
62
+ IAuthorizationToken: {
63
+ type: "object",
64
+ description: "",
65
+ properties: {
66
+ access: { type: "string", description: "" },
67
+ refresh: { type: "string", description: "" },
68
+ expires_in: { type: "integer", description: "" },
69
+ },
70
+ required: ["access", "refresh", "expires_in"],
71
+ },
72
+ }),
73
+ ),
74
+ ).toBe(
75
+ [
76
+ "{",
77
+ " id: string;",
78
+ " token: {",
79
+ " access: string;",
80
+ " refresh: string;",
81
+ " expires_in: number;",
82
+ " };",
83
+ "}",
84
+ ].join("\n"),
85
+ );
86
+ });
87
+
88
+ it("renders arrays of literal-string unions for sort grammars", () => {
89
+ expect(
90
+ renderJsonSchema(
91
+ {
92
+ type: "object",
93
+ description: "",
94
+ properties: {
95
+ sort: {
96
+ type: "array",
97
+ description: "",
98
+ items: {
99
+ oneOf: [
100
+ { const: "created_at.asc", description: "" },
101
+ { const: "created_at.desc", description: "" },
102
+ ],
103
+ },
104
+ },
105
+ },
106
+ required: ["sort"],
107
+ },
108
+ doc({}),
109
+ ),
110
+ ).toBe(
111
+ ["{", ' sort: Array<"created_at.asc" | "created_at.desc">;', "}"].join(
112
+ "\n",
113
+ ),
114
+ );
115
+ });
116
+
117
+ it("degrades object schemas with no properties to an index signature", () => {
118
+ expect(
119
+ renderJsonSchema({ type: "object", description: "" } as never, doc({})),
120
+ ).toBe("{ [key: string]: unknown }");
121
+ });
122
+
123
+ it("degrades array schemas with no items to unknown[]", () => {
124
+ expect(
125
+ renderJsonSchema(
126
+ {
127
+ type: "object",
128
+ description: "",
129
+ properties: {
130
+ data: { type: "array", description: "" } as never,
131
+ },
132
+ required: ["data"],
133
+ },
134
+ doc({}),
135
+ ),
136
+ ).toBe(["{", " data: unknown[];", "}"].join("\n"));
137
+ });
138
+ });
139
+
140
+ describe("renderNamedSchema", () => {
141
+ it("falls back to the bare type name when the component is missing", () => {
142
+ expect(renderNamedSchema("IMissing", doc({}))).toBe("IMissing");
143
+ });
144
+
145
+ it("renders the resolved schema when present", () => {
146
+ expect(
147
+ renderNamedSchema(
148
+ "IFlag",
149
+ doc({
150
+ IFlag: {
151
+ type: "object",
152
+ description: "",
153
+ properties: {
154
+ value: { type: "boolean", description: "" },
155
+ },
156
+ required: ["value"],
157
+ },
158
+ }),
159
+ ),
160
+ ).toBe(["{", " value: boolean;", "}"].join("\n"));
161
+ });
162
+ });
@@ -0,0 +1,133 @@
1
+ import { OpenApi } from "@typia/interface";
2
+
3
+ /**
4
+ * Render an OpenAPI schema (or a named schema reference) as a readable,
5
+ * multi-line TypeScript-shaped string. The output is intended for inclusion in
6
+ * an LLM prompt where the model has to write a TSX page against an SDK type —
7
+ * raw OpenAPI JSON forces the model to mentally translate schema indirection
8
+ * back into TypeScript and it routinely improvises instead of doing the work.
9
+ *
10
+ * Nested `$ref` targets are inlined up to a fixed depth so the LLM does not
11
+ * have to chase indirection. Beyond that depth the bare type name is emitted so
12
+ * cycles stay finite. Properties are kept in declaration order; optional fields
13
+ * are suffixed with `?`. String formats (`uuid`, `date-time`, ...) are appended
14
+ * as a trailing comment so the LLM can still see the semantic when it matters.
15
+ *
16
+ * Pure. Does not mutate the document.
17
+ */
18
+ export function renderJsonSchema(
19
+ schema: OpenApi.IJsonSchema,
20
+ doc: OpenApi.IDocument,
21
+ ): string {
22
+ return renderSchema(schema, doc, 0);
23
+ }
24
+
25
+ /**
26
+ * Look up a schema by its component name and render it. Returns the bare type
27
+ * name when the schema is missing from `(doc.components?.schemas ?? {})` so callers can
28
+ * still surface something useful when the document is incomplete.
29
+ */
30
+ export function renderNamedSchema(
31
+ typeName: string,
32
+ doc: OpenApi.IDocument,
33
+ ): string {
34
+ const schema = (doc.components?.schemas ?? {})[typeName];
35
+ if (schema === undefined) return typeName;
36
+ return renderJsonSchema(schema, doc);
37
+ }
38
+
39
+ const MAX_DEPTH = 3;
40
+ const INDENT = " ";
41
+
42
+ function renderSchema(
43
+ schema: OpenApi.IJsonSchema,
44
+ doc: OpenApi.IDocument,
45
+ depth: number,
46
+ ): string {
47
+ if ("$ref" in schema && typeof schema.$ref === "string") {
48
+ const name = schema.$ref.replace(/^#\/components\/schemas\//, "");
49
+ if (depth >= MAX_DEPTH) return name;
50
+ const target = (doc.components?.schemas ?? {})[name];
51
+ if (target === undefined) return name;
52
+ return renderSchema(target, doc, depth);
53
+ }
54
+ if ("oneOf" in schema) {
55
+ const parts = schema.oneOf.map((branch) =>
56
+ renderSchema(branch, doc, depth),
57
+ );
58
+ return parts.length > 0 ? parts.join(" | ") : "unknown";
59
+ }
60
+ if ("const" in schema) {
61
+ return JSON.stringify(
62
+ (schema as OpenApi.IJsonSchema.IConstant).const,
63
+ );
64
+ }
65
+ if (!("type" in schema)) return "unknown";
66
+ switch (schema.type) {
67
+ case "boolean":
68
+ return "boolean";
69
+ case "integer":
70
+ case "number":
71
+ return "number";
72
+ case "string": {
73
+ const tagged = formatStringTag(schema);
74
+ return tagged === null ? "string" : `string /* ${tagged} */`;
75
+ }
76
+ case "null":
77
+ return "null";
78
+ case "array": {
79
+ // OpenAPI arrays are `IArray | ITuple`; only `IArray` carries `items`.
80
+ // Real-world swaggers (Stripe) also ship array schemas with no `items`
81
+ // at all — degrade to `unknown[]` instead of recursing into `undefined`.
82
+ if (!("items" in schema) || schema.items == null) {
83
+ return "unknown[]";
84
+ }
85
+ const item = renderSchema(schema.items, doc, depth);
86
+ return needsArrayWrap(item) ? `Array<${item}>` : `${item}[]`;
87
+ }
88
+ case "object":
89
+ return renderObject(schema, doc, depth);
90
+ }
91
+ return "unknown";
92
+ }
93
+
94
+ function renderObject(
95
+ schema: OpenApi.IJsonSchema.IObject,
96
+ doc: OpenApi.IDocument,
97
+ depth: number,
98
+ ): string {
99
+ // `properties` is nominally required by the schema type, but real-world
100
+ // swaggers (Stripe) ship `type: "object"` schemas with no `properties`
101
+ // key — typically a free-form map declared only via
102
+ // `additionalProperties`. `Object.entries(undefined)` throws, so guard
103
+ // to a `Record<string, unknown>`-ish placeholder instead of crashing
104
+ // the whole Render phase.
105
+ if (schema.properties === undefined || schema.properties === null) {
106
+ return "{ [key: string]: unknown }";
107
+ }
108
+ const entries = Object.entries(schema.properties);
109
+ if (entries.length === 0) return "{}";
110
+ if (depth >= MAX_DEPTH) return "{ ... }";
111
+ const required = new Set(schema.required ?? []);
112
+ const pad = INDENT.repeat(depth + 1);
113
+ const closePad = INDENT.repeat(depth);
114
+ const lines = entries.map(([name, prop]) => {
115
+ const rendered = renderSchema(prop, doc, depth + 1);
116
+ const optional = required.has(name) ? "" : "?";
117
+ return `${pad}${name}${optional}: ${rendered};`;
118
+ });
119
+ return `{\n${lines.join("\n")}\n${closePad}}`;
120
+ }
121
+
122
+ function needsArrayWrap(rendered: string): boolean {
123
+ if (rendered.includes(" | ")) return true;
124
+ if (rendered.startsWith("{")) return true;
125
+ return false;
126
+ }
127
+
128
+ function formatStringTag(
129
+ schema: OpenApi.IJsonSchema.IString,
130
+ ): string | null {
131
+ if (typeof schema.format === "string") return schema.format;
132
+ return null;
133
+ }