@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,500 @@
1
+ import { OpenApi } from "@typia/interface";
2
+ import { OpenApiConverter } from "@typia/utils";
3
+ import fs from "fs/promises";
4
+
5
+ /**
6
+ * Normalize a raw OpenAPI / Swagger payload into a standard
7
+ * {@link OpenApi.IDocument} — the swagger-native shape the AutoView
8
+ * orchestrators now consume directly (via {@link toEndpoints}).
9
+ *
10
+ * Accepts both an in-memory JSON object and a file path; the latter is
11
+ * convenient for the CLI binary. `OpenApiConverter.upgradeDocument` upgrades
12
+ * Swagger 2.0 / OpenAPI 3.0 / 3.1 to a single normalized 3.1 document, so
13
+ * every endpoint — query-string endpoints included — is preserved. (The old
14
+ * `invertOpenApiDocument` route through the AutoBE AST silently dropped
15
+ * query-string endpoints.)
16
+ */
17
+ export function fromSwagger(
18
+ swagger: Parameters<typeof OpenApiConverter.upgradeDocument>[0],
19
+ ): OpenApi.IDocument {
20
+ resolveParameterRefs(swagger);
21
+ resolvePathRefs(swagger);
22
+ stripPathRefAllOf(swagger, 0);
23
+ flattenAllOf(swagger);
24
+ promoteInlineBodies(swagger);
25
+ dropUnroutablePaths(swagger);
26
+ return OpenApiConverter.upgradeDocument(swagger);
27
+ }
28
+
29
+ /**
30
+ * Resolve `$ref`s that are a JSON Pointer INTO another operation's inline
31
+ * schema (`#/paths/~1v2~1bandwidth/get/responses/...`) by replacing the `$ref`
32
+ * node with a deep clone of the pointer's target. DigitalOcean DRYs whole
33
+ * response schemas this way — at the schema root (`/v2/monitoring/metrics/*`
34
+ * all alias one bandwidth schema) and inside properties
35
+ * (`POST /v2/apps` → `{ app: $ref → GET /v2/apps …items }`). Standard tooling
36
+ * only chases `#/components/...` refs; nestia emits `Response = any` for these,
37
+ * which turns every simulate-mode screen of the resource into a silent empty
38
+ * state.
39
+ *
40
+ * Cycle-safe: a pointer actively being expanded is left verbatim (the later
41
+ * allOf-stripping pass treats the leftover as noise). Depth-guarded against
42
+ * pathological nesting. Mutates `raw` in place.
43
+ */
44
+ function resolvePathRefs(raw: unknown): void {
45
+ /**
46
+ * Follow an escaped JSON Pointer from the root. Being a URI fragment, parts
47
+ * are percent-decoded first (`%7B` → `{` — DO encodes `{app_id}` segments),
48
+ * then pointer-unescaped (`~1` → `/`, `~0` → `~`), per RFC 6901.
49
+ */
50
+ const lookup = (pointer: string): unknown => {
51
+ let node: unknown = raw;
52
+ for (const part of pointer.slice(2).split("/")) {
53
+ let key = part;
54
+ try {
55
+ key = decodeURIComponent(key);
56
+ } catch {
57
+ // not percent-encoded — use the raw part
58
+ }
59
+ key = key.replace(/~1/g, "/").replace(/~0/g, "~");
60
+ if (typeof node !== "object" || node === null) return undefined;
61
+ node = (node as Record<string, unknown>)[key];
62
+ }
63
+ return node;
64
+ };
65
+
66
+ const active = new Set<string>();
67
+ const walk = (node: unknown, depth: number): void => {
68
+ if (depth > 64 || typeof node !== "object" || node === null) return;
69
+ if (Array.isArray(node)) {
70
+ for (const v of node) walk(v, depth + 1);
71
+ return;
72
+ }
73
+ const obj = node as Record<string, unknown>;
74
+ const ref = obj.$ref;
75
+ if (typeof ref === "string" && ref.startsWith("#/paths/")) {
76
+ if (!active.has(ref)) {
77
+ const target = lookup(ref);
78
+ if (typeof target === "object" && target !== null) {
79
+ active.add(ref);
80
+ // The target may itself hold path-pointers — resolve before cloning.
81
+ walk(target, depth + 1);
82
+ active.delete(ref);
83
+ delete obj.$ref;
84
+ Object.assign(obj, structuredClone(target));
85
+ }
86
+ }
87
+ return; // a (possibly unresolved) ref node has nothing else to walk
88
+ }
89
+ for (const v of Object.values(obj)) walk(v, depth + 1);
90
+ };
91
+ walk(raw, 0);
92
+ }
93
+
94
+ /**
95
+ * Promote every INLINE OBJECT request/response body into a uniquely named
96
+ * component schema (replacing it with a `$ref`) BEFORE nestia sees the document.
97
+ *
98
+ * nestia promotes inline bodies itself, but derives the component name from the
99
+ * path with `{param}` segments DROPPED — so a parent list and its child detail
100
+ * (`/v2/droplets` vs `/v2/droplets/{droplet_id}`) both claim
101
+ * `IApiV2Droplets.GetResponse`. The loser's SDK function is emitted as
102
+ * `Response = any`; `typia.random<any>()` then yields nothing, and the
103
+ * simulate-mode screen for that resource renders empty forever — silently. On
104
+ * DigitalOcean (every body inline) this gutted 38 of 148 SDK files.
105
+ *
106
+ * Naming here keeps the param segments (`ApiV2DropletsDropletIdGetResponse`), so
107
+ * names cannot collide across sibling paths; an unexpected clash falls back to a
108
+ * numeric suffix. Only plain OBJECT schemas are hoisted: an inline ARRAY must
109
+ * stay inline so the READ layer still sees `Array<$ref>` and derives the list's
110
+ * element columns ({@link toEndpoints}'s `resolveBody`), and primitives gain
111
+ * nothing. Swagger 2.0 inputs are skipped — they have no `components` section
112
+ * for the refs to land in (the upgrade builds it later). Mutates `raw` in place.
113
+ */
114
+ function promoteInlineBodies(raw: unknown): void {
115
+ if (typeof raw !== "object" || raw === null) return;
116
+ const doc = raw as {
117
+ openapi?: unknown;
118
+ paths?: Record<string, Record<string, unknown>>;
119
+ components?: { schemas?: Record<string, unknown> };
120
+ };
121
+ if (typeof doc.openapi !== "string" || doc.paths === undefined) return;
122
+ doc.components ??= {};
123
+ doc.components.schemas ??= {};
124
+ const table = doc.components.schemas;
125
+
126
+ const pascal = (s: string): string =>
127
+ s
128
+ .replace(/[{}]/g, "")
129
+ .split(/[^A-Za-z0-9]+/)
130
+ .filter(Boolean)
131
+ .map((w) => w[0]!.toUpperCase() + w.slice(1))
132
+ .join("");
133
+ const claim = (base: string): string => {
134
+ let name = base;
135
+ for (let i = 2; table[name] !== undefined; i++) name = `${base}${i}`;
136
+ return name;
137
+ };
138
+ const isInlineObject = (schema: unknown): schema is Record<string, unknown> =>
139
+ typeof schema === "object" &&
140
+ schema !== null &&
141
+ (schema as { $ref?: unknown }).$ref === undefined &&
142
+ ((schema as { type?: unknown }).type === "object" ||
143
+ typeof (schema as { properties?: unknown }).properties === "object") &&
144
+ Object.keys(
145
+ ((schema as { properties?: unknown }).properties ?? {}) as object,
146
+ ).length > 0;
147
+ const hoist = (holder: unknown, baseName: string): void => {
148
+ const h = holder as { schema?: unknown } | undefined;
149
+ if (h === undefined || !isInlineObject(h.schema)) return;
150
+ const name = claim(baseName);
151
+ table[name] = h.schema;
152
+ h.schema = { $ref: `#/components/schemas/${name}` };
153
+ };
154
+
155
+ for (const [path, item] of Object.entries(doc.paths)) {
156
+ if (typeof item !== "object" || item === null) continue;
157
+ const pathName = path.split("/").filter(Boolean).map(pascal).join("");
158
+ for (const method of ["get", "post", "put", "patch", "delete", "head"]) {
159
+ const op = item[method] as
160
+ | {
161
+ requestBody?: { content?: Record<string, unknown> };
162
+ responses?: Record<string, { content?: Record<string, unknown> }>;
163
+ }
164
+ | undefined;
165
+ if (op === undefined || typeof op !== "object") continue;
166
+ const opName = `Api${pathName}${pascal(method)}`;
167
+ hoist(op.requestBody?.content?.["application/json"], `${opName}Body`);
168
+ for (const [status, response] of Object.entries(op.responses ?? {})) {
169
+ if (typeof response !== "object" || response === null) continue;
170
+ hoist(
171
+ response.content?.["application/json"],
172
+ `${opName}${pascal(status)}Response`,
173
+ );
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Strip `allOf` members that are an unresolvable JSON-Pointer `$ref` INTO another
181
+ * operation's inline schema (`#/paths/~1v2~1account~1keys/.../allOf/1`) — the way
182
+ * DigitalOcean DRYs up shared pagination/meta wrappers. `upgradeDocument` cannot
183
+ * chase a `#/paths/...` pointer, and when one sits alongside the real data member
184
+ * (`allOf: [{ properties: { droplets } }, <links-ref>, <meta-ref>]`) it discards
185
+ * the WHOLE `allOf` — so the list response comes out an empty schema, its table
186
+ * renders no columns, and its id producer is lost. Drop only the path-pointer
187
+ * members (pagination noise, not domain data); when a single data member with
188
+ * `properties` remains, hoist it to a plain object so the upgrade keeps it.
189
+ * Document-wide, depth-guarded, mutates in place.
190
+ */
191
+ function stripPathRefAllOf(node: unknown, depth: number): void {
192
+ if (node === null || typeof node !== "object" || depth > 64) return;
193
+ if (Array.isArray(node)) {
194
+ for (const v of node) stripPathRefAllOf(v, depth + 1);
195
+ return;
196
+ }
197
+ const obj = node as Record<string, unknown>;
198
+ if (Array.isArray(obj.allOf)) {
199
+ const kept = obj.allOf.filter(
200
+ (m) =>
201
+ !(
202
+ m !== null &&
203
+ typeof m === "object" &&
204
+ typeof (m as { $ref?: unknown }).$ref === "string" &&
205
+ ((m as { $ref: string }).$ref).startsWith("#/paths/")
206
+ ),
207
+ );
208
+ if (kept.length !== obj.allOf.length) {
209
+ const only = kept[0] as Record<string, unknown> | undefined;
210
+ if (kept.length === 1 && only?.properties !== undefined && only.$ref === undefined) {
211
+ obj.type = "object";
212
+ obj.properties = { ...(obj.properties as object), ...(only.properties as object) };
213
+ if (only.required !== undefined) obj.required = only.required;
214
+ delete obj.allOf;
215
+ } else if (kept.length === 0) {
216
+ delete obj.allOf;
217
+ } else {
218
+ obj.allOf = kept;
219
+ }
220
+ }
221
+ }
222
+ for (const v of Object.values(obj)) stripPathRefAllOf(v, depth + 1);
223
+ }
224
+
225
+ /**
226
+ * Flatten `allOf` composition into plain `properties` BEFORE upgrading —
227
+ * document-wide, not just component schemas. Two real-world failure shapes:
228
+ *
229
+ * - `OpenApiConverter.upgradeDocument` drops `allOf` for some component shapes
230
+ * (Box's `CollaborationAllowlistEntries = allOf[Pagination, { entries }]`
231
+ * comes out an empty object), so the type would render zero fields.
232
+ * - An `allOf` INLINE in an operation response (DigitalOcean's
233
+ * `GET /v2/droplets` → `allOf:[{properties:{droplets}}, {$ref:Pagination}]`)
234
+ * defeats nestia's type emitter: the route's SDK function comes out
235
+ * `Response = any`, `typia.random<any>()` yields nothing, and every
236
+ * simulate-mode screen for that resource renders empty forever — silently
237
+ * (38 of DigitalOcean's 148 SDK files before this pass).
238
+ *
239
+ * Merge each member's properties (resolving `$ref` members recursively,
240
+ * depth-guarded) into the schema's own `properties` and remove the `allOf`, so
241
+ * the composed fields survive both the upgrade and SDK generation. Component
242
+ * schemas are flattened first (so `$ref` members resolve to already-flat
243
+ * shapes), then every remaining `allOf` anywhere in the document. Mutates `raw`
244
+ * in place.
245
+ */
246
+ function flattenAllOf(raw: unknown): void {
247
+ if (typeof raw !== "object" || raw === null) return;
248
+ const schemas = (raw as { components?: { schemas?: unknown } }).components
249
+ ?.schemas;
250
+ const table = (typeof schemas === "object" && schemas !== null
251
+ ? schemas
252
+ : {}) as Record<string, Record<string, unknown>>;
253
+
254
+ const merge = (
255
+ schema: unknown,
256
+ depth: number,
257
+ seen: ReadonlySet<string>,
258
+ ): { properties: Record<string, unknown>; required: string[] } => {
259
+ if (depth > 8 || typeof schema !== "object" || schema === null) {
260
+ return { properties: {}, required: [] };
261
+ }
262
+ const node = schema as Record<string, unknown>;
263
+ if (typeof node.$ref === "string") {
264
+ const name = node.$ref.split("/").pop()!;
265
+ if (seen.has(name) || table[name] === undefined) {
266
+ return { properties: {}, required: [] };
267
+ }
268
+ return merge(table[name], depth + 1, new Set([...seen, name]));
269
+ }
270
+ let properties: Record<string, unknown> = {};
271
+ let required: string[] = [];
272
+ if (Array.isArray(node.allOf)) {
273
+ for (const member of node.allOf) {
274
+ const sub = merge(member, depth + 1, seen);
275
+ properties = { ...properties, ...sub.properties };
276
+ required = [...required, ...sub.required];
277
+ }
278
+ }
279
+ if (typeof node.properties === "object" && node.properties !== null) {
280
+ properties = { ...properties, ...(node.properties as object) };
281
+ }
282
+ if (Array.isArray(node.required)) required = [...required, ...node.required];
283
+ return { properties, required: [...new Set(required)] };
284
+ };
285
+
286
+ const flattenInPlace = (
287
+ schema: Record<string, unknown>,
288
+ seen: ReadonlySet<string>,
289
+ ): void => {
290
+ const merged = merge(schema, 0, seen);
291
+ if (Object.keys(merged.properties).length > 0) {
292
+ schema.properties = merged.properties;
293
+ schema.required = merged.required;
294
+ schema.type = "object";
295
+ delete schema.allOf;
296
+ }
297
+ };
298
+
299
+ // Pass 1 — component schemas, so a `$ref` member met later resolves flat.
300
+ for (const name of Object.keys(table)) {
301
+ const schema = table[name];
302
+ if (schema === undefined || !Array.isArray(schema.allOf)) continue;
303
+ flattenInPlace(schema, new Set([name]));
304
+ }
305
+
306
+ // Pass 2 — every remaining `allOf` anywhere in the document (inline response
307
+ // and request schemas, nested properties, array items …).
308
+ const walk = (node: unknown, depth: number): void => {
309
+ if (depth > 64 || typeof node !== "object" || node === null) return;
310
+ if (Array.isArray(node)) {
311
+ for (const v of node) walk(v, depth + 1);
312
+ return;
313
+ }
314
+ const obj = node as Record<string, unknown>;
315
+ // Children first, so a nested allOf is flat before its parent merges it.
316
+ for (const v of Object.values(obj)) walk(v, depth + 1);
317
+ if (Array.isArray(obj.allOf)) flattenInPlace(obj, new Set());
318
+ };
319
+ walk(raw, 0);
320
+ }
321
+
322
+ /**
323
+ * Remove path keys a typed SDK cannot be generated from:
324
+ *
325
+ * 1. `#` fragments some swaggers (Box) use to disambiguate overloaded paths
326
+ * (`/shared_items#folders`) — nestia turns the `#` into an illegal JS
327
+ * accessor segment, and the generated SDK file fails to parse.
328
+ * 2. A path-template parameter whose NAME is not a valid identifier
329
+ * (`/teams/{enterprise-team}/...`, GitHub). nestia derives the SDK accessor
330
+ * from the param name and crashes (`Cannot read properties of undefined`)
331
+ * when the hyphenated name does not round-trip — taking the WHOLE generation
332
+ * down, not just that route.
333
+ *
334
+ * Dropping the handful of offending paths up front lets the rest of a large real
335
+ * swagger (GitHub's 1190 operations) generate. Mutates `raw` in place.
336
+ */
337
+ function dropUnroutablePaths(raw: unknown): void {
338
+ if (typeof raw !== "object" || raw === null) return;
339
+ const paths = (raw as Record<string, unknown>).paths;
340
+ if (typeof paths !== "object" || paths === null) return;
341
+ // Standard URL path characters only — letters, digits, `_-./`, and `{param}`.
342
+ const SAFE = /^[A-Za-z0-9_\-./{}]+$/;
343
+ // A `{param}` template name must be a valid JS identifier (no hyphens).
344
+ const IDENT_PARAM = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
345
+ for (const key of Object.keys(paths as object)) {
346
+ const paramNames = [...key.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]!);
347
+ if (!SAFE.test(key) || paramNames.some((p) => !IDENT_PARAM.test(p))) {
348
+ delete (paths as Record<string, unknown>)[key];
349
+ }
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Resolve `$ref` parameters before upgrading. Real-world swaggers (DigitalOcean,
355
+ * Box, …) DRY up shared query/path parameters with a `$ref` — sometimes a
356
+ * standard `#/components/parameters/X`, sometimes a raw JSON Pointer into another
357
+ * operation (`#/paths/~1v2~1account~1keys/get/parameters/0`). `upgradeDocument`
358
+ * does not chase those pointers; it leaves the slot `null`, which then crashes
359
+ * `HttpMigration` with "Cannot read properties of undefined (reading 'in')".
360
+ *
361
+ * Walk every operation's (and path item's) `parameters`, dereference each `$ref`
362
+ * by generic JSON Pointer against the raw document — transitively, with a depth
363
+ * guard — and drop anything that does not resolve to a real `{ in, name }`
364
+ * parameter. Mutates the freshly-parsed payload in place (we own it; cloning a
365
+ * multi-MB document would be wasteful).
366
+ */
367
+ function resolveParameterRefs(raw: unknown): unknown {
368
+ if (typeof raw !== "object" || raw === null) return raw;
369
+ const doc = raw as Record<string, unknown>;
370
+ const paths = doc.paths;
371
+ if (typeof paths !== "object" || paths === null) return raw;
372
+
373
+ const resolve = (node: unknown, depth: number): unknown => {
374
+ if (depth > 16 || node === null || typeof node !== "object") return undefined;
375
+ const ref = (node as { $ref?: unknown }).$ref;
376
+ if (typeof ref === "string") return resolve(jsonPointer(doc, ref), depth + 1);
377
+ return "in" in (node as object) ? node : undefined;
378
+ };
379
+ const clean = (list: unknown): unknown[] | undefined => {
380
+ if (!Array.isArray(list)) return undefined;
381
+ return list.map((p) => resolve(p, 0)).filter((p) => p !== undefined);
382
+ };
383
+
384
+ const METHODS = ["get", "post", "put", "patch", "delete", "options", "head"];
385
+ for (const key of Object.keys(paths as object)) {
386
+ const item = (paths as Record<string, unknown>)[key];
387
+ if (typeof item !== "object" || item === null) continue;
388
+ const pathItem = item as Record<string, unknown>;
389
+ const shared = clean(pathItem.parameters);
390
+ if (shared !== undefined) pathItem.parameters = shared;
391
+ for (const method of METHODS) {
392
+ const op = pathItem[method];
393
+ if (typeof op !== "object" || op === null) continue;
394
+ const params = clean((op as Record<string, unknown>).parameters);
395
+ if (params !== undefined) {
396
+ (op as Record<string, unknown>).parameters = params;
397
+ }
398
+ }
399
+ }
400
+ return raw;
401
+ }
402
+
403
+ /** Resolve a `#/...` JSON Pointer against a document. Returns `undefined` on miss. */
404
+ function jsonPointer(doc: unknown, pointer: string): unknown {
405
+ if (!pointer.startsWith("#/")) return undefined;
406
+ let current: unknown = doc;
407
+ for (const rawPart of pointer.slice(2).split("/")) {
408
+ const part = rawPart.replace(/~1/g, "/").replace(/~0/g, "~");
409
+ if (current === null || typeof current !== "object") return undefined;
410
+ current = (current as Record<string, unknown>)[part];
411
+ }
412
+ return current;
413
+ }
414
+
415
+ /**
416
+ * File-path convenience: read `path` as UTF-8 JSON, parse, and run it through
417
+ * {@link fromSwagger}. Rejects with a helpful error message when the file is
418
+ * missing or not valid JSON.
419
+ */
420
+ export async function fromSwaggerFile(
421
+ path: string,
422
+ ): Promise<OpenApi.IDocument> {
423
+ let raw: string;
424
+ try {
425
+ raw = await fs.readFile(path, "utf-8");
426
+ } catch (err) {
427
+ const message = err instanceof Error ? err.message : String(err);
428
+ throw new Error(`Failed to read swagger file at "${path}": ${message}`);
429
+ }
430
+ let parsed: unknown;
431
+ try {
432
+ parsed = JSON.parse(raw);
433
+ } catch (err) {
434
+ const message = err instanceof Error ? err.message : String(err);
435
+ throw new Error(`Swagger file at "${path}" is not valid JSON: ${message}`);
436
+ }
437
+ return fromSwagger(
438
+ parsed as Parameters<typeof OpenApiConverter.upgradeDocument>[0],
439
+ );
440
+ }
441
+
442
+ /**
443
+ * Pull the first non-empty `servers[].url` out of a raw OpenAPI payload, or
444
+ * `null` when none is present.
445
+ *
446
+ * `OpenApi.IDocument` (the shape AutoView's orchestrators consume) does
447
+ * not carry the OpenAPI `servers[]` field — it is dropped by
448
+ * `invertOpenApiDocument` because the AutoBE backend phases do not need it. The
449
+ * standalone CLI does need it, though: it is the difference between the
450
+ * generated frontend booting with `simulate: false` against the user's real
451
+ * backend (real data on first open) and the simulator default (typia-random
452
+ * gibberish). Read it directly off the raw swagger here so the CLI can pass the
453
+ * host into {@link AutoViewAgent} without parsing the swagger twice.
454
+ *
455
+ * Returns `null` when:
456
+ *
457
+ * - The payload is not an object, or
458
+ * - `servers` is missing / empty / not an array, or
459
+ * - No `servers[i].url` is a non-empty string.
460
+ */
461
+ export function extractBackendFromSwagger(payload: unknown): {
462
+ host: string;
463
+ } | null {
464
+ if (typeof payload !== "object" || payload === null) return null;
465
+ const servers = (payload as { servers?: unknown }).servers;
466
+ if (!Array.isArray(servers)) return null;
467
+ for (const server of servers) {
468
+ if (typeof server !== "object" || server === null) continue;
469
+ const url = (server as { url?: unknown }).url;
470
+ if (typeof url === "string" && url.length > 0) {
471
+ return { host: url };
472
+ }
473
+ }
474
+ return null;
475
+ }
476
+
477
+ /**
478
+ * File-path convenience for {@link extractBackendFromSwagger}: read `path` as
479
+ * UTF-8 JSON, parse, and run it through the extractor. Returns `null` on read
480
+ * or parse failure rather than throwing — the caller usually has a `--backend
481
+ * <url>` flag and a simulator fallback to lean on, and a missing file would
482
+ * already have failed louder via {@link fromSwaggerFile}.
483
+ */
484
+ export async function extractBackendFromSwaggerFile(
485
+ path: string,
486
+ ): Promise<{ host: string } | null> {
487
+ let raw: string;
488
+ try {
489
+ raw = await fs.readFile(path, "utf-8");
490
+ } catch {
491
+ return null;
492
+ }
493
+ let parsed: unknown;
494
+ try {
495
+ parsed = JSON.parse(raw);
496
+ } catch {
497
+ return null;
498
+ }
499
+ return extractBackendFromSwagger(parsed);
500
+ }
@@ -0,0 +1,39 @@
1
+ import { OpenApiConverter } from "@typia/utils";
2
+ import { describe, expect, it } from "vitest";
3
+
4
+ import { generateDeterministic } from "./generateDeterministic";
5
+
6
+ function doc(paths: Record<string, unknown>, schemas: Record<string, unknown> = {}) {
7
+ return OpenApiConverter.upgradeDocument({
8
+ openapi: "3.0.0", info: { title: "Shop", version: "1.0.0" }, paths, components: { schemas },
9
+ } as never);
10
+ }
11
+
12
+ const fixture = doc(
13
+ {
14
+ "/sales": { get: { operationId: "index", responses: { 200: { description: "ok", content: { "application/json": { schema: { type: "array", items: { $ref: "#/components/schemas/Sale" } } } } } } } },
15
+ "/sales/{saleId}": { get: { operationId: "at", parameters: [{ name: "saleId", in: "path", required: true, schema: { type: "string" } }], responses: { 200: { description: "ok", content: { "application/json": { schema: { $ref: "#/components/schemas/Sale" } } } } } } },
16
+ },
17
+ { Sale: { type: "object", properties: { id: { type: "string" }, title: { type: "string" } }, required: ["id"] } },
18
+ );
19
+
20
+ describe("generateDeterministic", () => {
21
+ it("produces a runnable project with NO LLM (scaffold + pages + connection)", async () => {
22
+ const files = await generateDeterministic(fixture, { backend: { host: null } });
23
+ // the project shell, the connection module, and at least the list page exist
24
+ expect(files["app/page.tsx"]).toBeDefined();
25
+ expect(files["src/lib/connection.ts"]).toBeDefined();
26
+ expect(files["app/sales/page.tsx"]).toBeDefined();
27
+ // the list page wires the typed SDK call — proof the deterministic render ran
28
+ expect(files["app/sales/page.tsx"]).toContain("api.functional.sales");
29
+ // no LLM path was taken (it would have thrown); reaching here proves it
30
+ expect(Object.keys(files).length).toBeGreaterThan(20);
31
+ }, 30000);
32
+
33
+ it("is deterministic — same document, same files", async () => {
34
+ const a = await generateDeterministic(fixture, { backend: { host: null } });
35
+ const b = await generateDeterministic(fixture, { backend: { host: null } });
36
+ expect(Object.keys(a).sort()).toEqual(Object.keys(b).sort());
37
+ expect(a["app/sales/page.tsx"]).toBe(b["app/sales/page.tsx"]);
38
+ }, 30000);
39
+ });
@@ -0,0 +1,77 @@
1
+ import { OpenApi } from "@typia/interface";
2
+
3
+ import { AutoViewInterfaceCompiler } from "./compiler/AutoViewInterfaceCompiler";
4
+ import { orchestrateAutoViewRenderDeterministic } from "./orchestrate/orchestrateAutoViewRenderDeterministic";
5
+ import { orchestrateAutoViewScaffold } from "./orchestrate/orchestrateAutoViewScaffold";
6
+ import { buildDeterministicPlan } from "./orchestrate/utils/buildDeterministicPlan";
7
+ import { buildDeterministicSdkMap } from "./orchestrate/utils/buildDeterministicSdkMap";
8
+ import { normalizeProductPlanPaths } from "./orchestrate/utils/normalizeProductPlanPaths";
9
+ import { IEndpointFilter } from "./utils/endpointFilter";
10
+ import { sliceDocument } from "./utils/sliceDocument";
11
+
12
+ /**
13
+ * Generate the frontend with NO LLM and NO API key — the whole READ → MAP →
14
+ * RENDER pipeline is deterministic, so for the common case (a clean swagger that
15
+ * compiles) you do not need a model at all. This is the path the `--no-llm` CLI
16
+ * flag and any programmatic caller take.
17
+ *
18
+ * It runs exactly the deterministic phases of {@link orchestrateAutoView}:
19
+ *
20
+ * 1. slice the document to `include`/`exclude` (so a large ERP spec still fits),
21
+ * 2. derive the SDK map + product plan structurally,
22
+ * 3. scaffold the project + render every page deterministically.
23
+ *
24
+ * It deliberately skips the LLM phases the full agent runs — the typecheck-
25
+ * recovery rerender, the runtime audit, the UI review. The render is already
26
+ * typecheck-clean on real swaggers; without a key those polish passes simply do
27
+ * not run. Returns the flat `path → content` project map.
28
+ */
29
+ export async function generateDeterministic(
30
+ rawDocument: OpenApi.IDocument,
31
+ options: {
32
+ backend: { host: string | null };
33
+ filter?: IEndpointFilter;
34
+ semaphore?: number;
35
+ },
36
+ ): Promise<Record<string, string>> {
37
+ const document = sliceDocument(rawDocument, options.filter ?? {});
38
+ const sdkMap = buildDeterministicSdkMap(document);
39
+ const actor = sdkMap.actors[0]?.name ?? "user";
40
+ const productPlan = normalizeProductPlanPaths(
41
+ buildDeterministicPlan(document, actor, options.filter ?? {}),
42
+ );
43
+
44
+ // A minimal context: the deterministic phases need only the interface compiler
45
+ // and a semaphore. `conversate` must never be reached on this path — if it is,
46
+ // that is a bug (an LLM call leaked into the deterministic pipeline).
47
+ const compiler = { interface: new AutoViewInterfaceCompiler() };
48
+ const ctx = {
49
+ state: () => ({ interface: { document, step: 0 } }),
50
+ compiler: async () => compiler,
51
+ vendor: { semaphore: options.semaphore ?? 8 },
52
+ dispatch: () => undefined,
53
+ conversate: async () => {
54
+ throw new Error(
55
+ "generateDeterministic reached an LLM call — the deterministic path must not.",
56
+ );
57
+ },
58
+ // biome-ignore lint: a structural stub of IAutoViewAgentContext's deterministic surface
59
+ } as never;
60
+
61
+ const scaffolded = await orchestrateAutoViewScaffold(ctx, {
62
+ document,
63
+ sdkMap,
64
+ sdkMapMarkdown: "# SDK Map\n\n_(deterministic)_\n",
65
+ productPlan,
66
+ productPlanMarkdown: "# Product Plan\n\n_(deterministic)_\n",
67
+ step: 0,
68
+ backend: options.backend,
69
+ });
70
+ const render = await orchestrateAutoViewRenderDeterministic(ctx, {
71
+ document,
72
+ productPlan,
73
+ step: 0,
74
+ });
75
+
76
+ return { ...scaffolded, ...render.files };
77
+ }
package/src/index.ts ADDED
@@ -0,0 +1,30 @@
1
+ export * from "./AutoViewAgent";
2
+ export * from "./context/IAutoViewAgentContext";
3
+ export * from "./fromSwagger";
4
+ export * from "./orchestrate/orchestrateAutoView";
5
+ export { AutoViewFrontendTemplate } from "./constants/AutoViewFrontendTemplate";
6
+ export {
7
+ describeEndpointPropsShape,
8
+ describeRequestBodyHint,
9
+ } from "./orchestrate/utils/describeEndpointPropsShape";
10
+ export { describeEndpointRequestBodyShape } from "./orchestrate/utils/describeEndpointRequestBodyShape";
11
+ export { describeEndpointResponseShape } from "./orchestrate/utils/describeEndpointResponseShape";
12
+ export {
13
+ renderJsonSchema,
14
+ renderNamedSchema,
15
+ } from "./orchestrate/utils/renderJsonSchema";
16
+ export {
17
+ hashFrontendDependencies,
18
+ validateFrontendTypecheck,
19
+ } from "./orchestrate/utils/validateFrontendTypecheck";
20
+ export type {
21
+ ITypecheckDiagnostic,
22
+ IValidateFrontendTypecheckResult,
23
+ } from "./orchestrate/utils/validateFrontendTypecheck";
24
+ export { auditFrontendRuntime } from "./orchestrate/utils/auditFrontendRuntime";
25
+ export type {
26
+ IAuditFrontendRuntimeResult,
27
+ IRuntimeAuditDiagnostic,
28
+ } from "./orchestrate/utils/auditFrontendRuntime";
29
+ export { loadShoppingFixture } from "./orchestrate/utils/loadShoppingFixture";
30
+ export type { IShoppingFixture } from "./orchestrate/utils/loadShoppingFixture";