@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.
- package/LICENSE +661 -0
- package/README.md +407 -0
- package/lib/AutoViewAgent.d.ts +109 -0
- package/lib/AutoViewAgent.js +123 -0
- package/lib/AutoViewAgent.js.map +1 -0
- package/lib/agent/emitMcpServer.d.ts +15 -0
- package/lib/agent/emitMcpServer.js +157 -0
- package/lib/agent/emitMcpServer.js.map +1 -0
- package/lib/agent/emitReport.d.ts +14 -0
- package/lib/agent/emitReport.js +85 -0
- package/lib/agent/emitReport.js.map +1 -0
- package/lib/agent/toolSurface.d.ts +130 -0
- package/lib/agent/toolSurface.js +342 -0
- package/lib/agent/toolSurface.js.map +1 -0
- package/lib/agent/verifyAgentTasks.d.ts +87 -0
- package/lib/agent/verifyAgentTasks.js +126 -0
- package/lib/agent/verifyAgentTasks.js.map +1 -0
- package/lib/cli/main.d.ts +2 -0
- package/lib/cli/main.js +295 -0
- package/lib/cli/main.js.map +1 -0
- package/lib/compiler/AutoViewInterfaceCompiler.d.ts +27 -0
- package/lib/compiler/AutoViewInterfaceCompiler.js +68 -0
- package/lib/compiler/AutoViewInterfaceCompiler.js.map +1 -0
- package/lib/constants/AutoViewFrontendTemplate.d.ts +1 -0
- package/lib/constants/AutoViewFrontendTemplate.js +46 -0
- package/lib/constants/AutoViewFrontendTemplate.js.map +1 -0
- package/lib/constants/AutoViewSystemPromptConstant.d.ts +5 -0
- package/lib/constants/AutoViewSystemPromptConstant.js +4 -0
- package/lib/constants/AutoViewSystemPromptConstant.js.map +1 -0
- package/lib/context/IAutoViewAgentContext.d.ts +60 -0
- package/lib/context/IAutoViewAgentContext.js +3 -0
- package/lib/context/IAutoViewAgentContext.js.map +1 -0
- package/lib/fromSwagger.d.ts +53 -0
- package/lib/fromSwagger.js +513 -0
- package/lib/fromSwagger.js.map +1 -0
- package/lib/generateDeterministic.d.ts +26 -0
- package/lib/generateDeterministic.js +75 -0
- package/lib/generateDeterministic.js.map +1 -0
- package/lib/index.d.ts +15 -0
- package/lib/index.js +41 -0
- package/lib/index.js.map +1 -0
- package/lib/orchestrate/orchestrateAutoView.d.ts +17 -0
- package/lib/orchestrate/orchestrateAutoView.js +491 -0
- package/lib/orchestrate/orchestrateAutoView.js.map +1 -0
- package/lib/orchestrate/orchestrateAutoViewProductPlan.d.ts +37 -0
- package/lib/orchestrate/orchestrateAutoViewProductPlan.js +109 -0
- package/lib/orchestrate/orchestrateAutoViewProductPlan.js.map +1 -0
- package/lib/orchestrate/orchestrateAutoViewRender.d.ts +133 -0
- package/lib/orchestrate/orchestrateAutoViewRender.js +943 -0
- package/lib/orchestrate/orchestrateAutoViewRender.js.map +1 -0
- package/lib/orchestrate/orchestrateAutoViewRenderDeterministic.d.ts +24 -0
- package/lib/orchestrate/orchestrateAutoViewRenderDeterministic.js +92 -0
- package/lib/orchestrate/orchestrateAutoViewRenderDeterministic.js.map +1 -0
- package/lib/orchestrate/orchestrateAutoViewReview.d.ts +48 -0
- package/lib/orchestrate/orchestrateAutoViewReview.js +328 -0
- package/lib/orchestrate/orchestrateAutoViewReview.js.map +1 -0
- package/lib/orchestrate/orchestrateAutoViewScaffold.d.ts +45 -0
- package/lib/orchestrate/orchestrateAutoViewScaffold.js +586 -0
- package/lib/orchestrate/orchestrateAutoViewScaffold.js.map +1 -0
- package/lib/orchestrate/orchestrateAutoViewSdkStudy.d.ts +26 -0
- package/lib/orchestrate/orchestrateAutoViewSdkStudy.js +85 -0
- package/lib/orchestrate/orchestrateAutoViewSdkStudy.js.map +1 -0
- package/lib/orchestrate/structures/IAutoViewProductPlan.d.ts +96 -0
- package/lib/orchestrate/structures/IAutoViewProductPlan.js +3 -0
- package/lib/orchestrate/structures/IAutoViewProductPlan.js.map +1 -0
- package/lib/orchestrate/structures/IAutoViewProductPlanApplication.d.ts +38 -0
- package/lib/orchestrate/structures/IAutoViewProductPlanApplication.js +3 -0
- package/lib/orchestrate/structures/IAutoViewProductPlanApplication.js.map +1 -0
- package/lib/orchestrate/structures/IAutoViewRenderApplication.d.ts +38 -0
- package/lib/orchestrate/structures/IAutoViewRenderApplication.js +3 -0
- package/lib/orchestrate/structures/IAutoViewRenderApplication.js.map +1 -0
- package/lib/orchestrate/structures/IAutoViewReviewApplication.d.ts +40 -0
- package/lib/orchestrate/structures/IAutoViewReviewApplication.js +3 -0
- package/lib/orchestrate/structures/IAutoViewReviewApplication.js.map +1 -0
- package/lib/orchestrate/structures/IAutoViewSdkMap.d.ts +63 -0
- package/lib/orchestrate/structures/IAutoViewSdkMap.js +3 -0
- package/lib/orchestrate/structures/IAutoViewSdkMap.js.map +1 -0
- package/lib/orchestrate/structures/IAutoViewSdkStudyApplication.d.ts +37 -0
- package/lib/orchestrate/structures/IAutoViewSdkStudyApplication.js +3 -0
- package/lib/orchestrate/structures/IAutoViewSdkStudyApplication.js.map +1 -0
- package/lib/orchestrate/utils/HistoryMessage.d.ts +10 -0
- package/lib/orchestrate/utils/HistoryMessage.js +25 -0
- package/lib/orchestrate/utils/HistoryMessage.js.map +1 -0
- package/lib/orchestrate/utils/auditFrontendRuntime.d.ts +53 -0
- package/lib/orchestrate/utils/auditFrontendRuntime.js +362 -0
- package/lib/orchestrate/utils/auditFrontendRuntime.js.map +1 -0
- package/lib/orchestrate/utils/buildDeterministicPlan.d.ts +4 -0
- package/lib/orchestrate/utils/buildDeterministicPlan.js +233 -0
- package/lib/orchestrate/utils/buildDeterministicPlan.js.map +1 -0
- package/lib/orchestrate/utils/buildDeterministicSdkMap.d.ts +22 -0
- package/lib/orchestrate/utils/buildDeterministicSdkMap.js +154 -0
- package/lib/orchestrate/utils/buildDeterministicSdkMap.js.map +1 -0
- package/lib/orchestrate/utils/cacheNodeModules.d.ts +31 -0
- package/lib/orchestrate/utils/cacheNodeModules.js +134 -0
- package/lib/orchestrate/utils/cacheNodeModules.js.map +1 -0
- package/lib/orchestrate/utils/describeEndpointPropsShape.d.ts +37 -0
- package/lib/orchestrate/utils/describeEndpointPropsShape.js +192 -0
- package/lib/orchestrate/utils/describeEndpointPropsShape.js.map +1 -0
- package/lib/orchestrate/utils/describeEndpointRequestBodyShape.d.ts +22 -0
- package/lib/orchestrate/utils/describeEndpointRequestBodyShape.js +29 -0
- package/lib/orchestrate/utils/describeEndpointRequestBodyShape.js.map +1 -0
- package/lib/orchestrate/utils/describeEndpointResponseShape.d.ts +19 -0
- package/lib/orchestrate/utils/describeEndpointResponseShape.js +30 -0
- package/lib/orchestrate/utils/describeEndpointResponseShape.js.map +1 -0
- package/lib/orchestrate/utils/executeCachedBatch.d.ts +22 -0
- package/lib/orchestrate/utils/executeCachedBatch.js +64 -0
- package/lib/orchestrate/utils/executeCachedBatch.js.map +1 -0
- package/lib/orchestrate/utils/loadShoppingFixture.d.ts +33 -0
- package/lib/orchestrate/utils/loadShoppingFixture.js +17 -0
- package/lib/orchestrate/utils/loadShoppingFixture.js.map +1 -0
- package/lib/orchestrate/utils/normalizeProductPlanPaths.d.ts +24 -0
- package/lib/orchestrate/utils/normalizeProductPlanPaths.js +77 -0
- package/lib/orchestrate/utils/normalizeProductPlanPaths.js.map +1 -0
- package/lib/orchestrate/utils/renderJsonSchema.d.ts +23 -0
- package/lib/orchestrate/utils/renderJsonSchema.js +122 -0
- package/lib/orchestrate/utils/renderJsonSchema.js.map +1 -0
- package/lib/orchestrate/utils/renderResourcePage.d.ts +36 -0
- package/lib/orchestrate/utils/renderResourcePage.js +1415 -0
- package/lib/orchestrate/utils/renderResourcePage.js.map +1 -0
- package/lib/orchestrate/utils/validateFrontendTypecheck.d.ts +109 -0
- package/lib/orchestrate/utils/validateFrontendTypecheck.js +274 -0
- package/lib/orchestrate/utils/validateFrontendTypecheck.js.map +1 -0
- package/lib/preview/renderPreview.d.ts +22 -0
- package/lib/preview/renderPreview.js +198 -0
- package/lib/preview/renderPreview.js.map +1 -0
- package/lib/typings/compiler.d.ts +39 -0
- package/lib/typings/compiler.js +3 -0
- package/lib/typings/compiler.js.map +1 -0
- package/lib/typings/events.d.ts +106 -0
- package/lib/typings/events.js +3 -0
- package/lib/typings/events.js.map +1 -0
- package/lib/typings/index.d.ts +10 -0
- package/lib/typings/index.js +27 -0
- package/lib/typings/index.js.map +1 -0
- package/lib/typings/misc.d.ts +78 -0
- package/lib/typings/misc.js +3 -0
- package/lib/typings/misc.js.map +1 -0
- package/lib/utils/ArrayUtil.d.ts +8 -0
- package/lib/utils/ArrayUtil.js +30 -0
- package/lib/utils/ArrayUtil.js.map +1 -0
- package/lib/utils/StringUtil.d.ts +11 -0
- package/lib/utils/StringUtil.js +28 -0
- package/lib/utils/StringUtil.js.map +1 -0
- package/lib/utils/classifyEndpoints.d.ts +62 -0
- package/lib/utils/classifyEndpoints.js +216 -0
- package/lib/utils/classifyEndpoints.js.map +1 -0
- package/lib/utils/endpointFilter.d.ts +26 -0
- package/lib/utils/endpointFilter.js +0 -0
- package/lib/utils/endpointFilter.js.map +1 -0
- package/lib/utils/extractFields.d.ts +85 -0
- package/lib/utils/extractFields.js +231 -0
- package/lib/utils/extractFields.js.map +1 -0
- package/lib/utils/index.d.ts +13 -0
- package/lib/utils/index.js +30 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/normalizeForNestia.d.ts +34 -0
- package/lib/utils/normalizeForNestia.js +133 -0
- package/lib/utils/normalizeForNestia.js.map +1 -0
- package/lib/utils/resourcePlan.d.ts +39 -0
- package/lib/utils/resourcePlan.js +95 -0
- package/lib/utils/resourcePlan.js.map +1 -0
- package/lib/utils/sliceDocument.d.ts +17 -0
- package/lib/utils/sliceDocument.js +114 -0
- package/lib/utils/sliceDocument.js.map +1 -0
- package/lib/utils/toEndpoints.d.ts +90 -0
- package/lib/utils/toEndpoints.js +227 -0
- package/lib/utils/toEndpoints.js.map +1 -0
- package/lib/verify/runWorkflows.d.ts +25 -0
- package/lib/verify/runWorkflows.js +366 -0
- package/lib/verify/runWorkflows.js.map +1 -0
- package/lib/verify/workflows.d.ts +53 -0
- package/lib/verify/workflows.js +107 -0
- package/lib/verify/workflows.js.map +1 -0
- package/package.json +82 -0
- package/prompts/AUTOVIEW_RENDER.md +398 -0
- package/prompts/AUTOVIEW_REVIEW.md +60 -0
- package/prompts/AUTOVIEW_SDK_STUDY.md +89 -0
- package/src/AutoViewAgent.ts +222 -0
- package/src/agent/emitMcpServer.integration.test.ts +168 -0
- package/src/agent/emitMcpServer.test.ts +51 -0
- package/src/agent/emitMcpServer.ts +178 -0
- package/src/agent/emitReport.ts +117 -0
- package/src/agent/toolSurface.test.ts +243 -0
- package/src/agent/toolSurface.ts +501 -0
- package/src/agent/verifyAgentTasks.test.ts +106 -0
- package/src/agent/verifyAgentTasks.ts +171 -0
- package/src/cli/main.ts +363 -0
- package/src/compiler/AutoViewInterfaceCompiler.ts +69 -0
- package/src/constants/AutoViewFrontendTemplate.ts +42 -0
- package/src/constants/AutoViewSystemPromptConstant.ts +6 -0
- package/src/context/IAutoViewAgentContext.ts +84 -0
- package/src/fromSwagger.test.ts +269 -0
- package/src/fromSwagger.ts +500 -0
- package/src/generateDeterministic.test.ts +39 -0
- package/src/generateDeterministic.ts +77 -0
- package/src/index.ts +30 -0
- package/src/orchestrate/orchestrateAutoView.ts +590 -0
- package/src/orchestrate/orchestrateAutoViewProductPlan.ts +121 -0
- package/src/orchestrate/orchestrateAutoViewRender.ts +1117 -0
- package/src/orchestrate/orchestrateAutoViewRenderDeterministic.ts +101 -0
- package/src/orchestrate/orchestrateAutoViewReview.ts +272 -0
- package/src/orchestrate/orchestrateAutoViewScaffold.ts +627 -0
- package/src/orchestrate/orchestrateAutoViewSdkStudy.ts +90 -0
- package/src/orchestrate/renderNavTs.test.ts +74 -0
- package/src/orchestrate/structures/IAutoViewProductPlan.ts +119 -0
- package/src/orchestrate/structures/IAutoViewProductPlanApplication.ts +41 -0
- package/src/orchestrate/structures/IAutoViewRenderApplication.ts +40 -0
- package/src/orchestrate/structures/IAutoViewReviewApplication.ts +42 -0
- package/src/orchestrate/structures/IAutoViewSdkMap.ts +72 -0
- package/src/orchestrate/structures/IAutoViewSdkStudyApplication.ts +40 -0
- package/src/orchestrate/utils/HistoryMessage.ts +41 -0
- package/src/orchestrate/utils/auditFrontendRuntime.test.ts +18 -0
- package/src/orchestrate/utils/auditFrontendRuntime.ts +454 -0
- package/src/orchestrate/utils/buildDeterministicPlan.test.ts +170 -0
- package/src/orchestrate/utils/buildDeterministicPlan.ts +289 -0
- package/src/orchestrate/utils/buildDeterministicSdkMap.test.ts +90 -0
- package/src/orchestrate/utils/buildDeterministicSdkMap.ts +169 -0
- package/src/orchestrate/utils/cacheNodeModules.ts +136 -0
- package/src/orchestrate/utils/describeEndpointPropsShape.test.ts +86 -0
- package/src/orchestrate/utils/describeEndpointPropsShape.ts +202 -0
- package/src/orchestrate/utils/describeEndpointRequestBodyShape.test.ts +87 -0
- package/src/orchestrate/utils/describeEndpointRequestBodyShape.ts +31 -0
- package/src/orchestrate/utils/describeEndpointResponseShape.test.ts +70 -0
- package/src/orchestrate/utils/describeEndpointResponseShape.ts +32 -0
- package/src/orchestrate/utils/executeCachedBatch.ts +59 -0
- package/src/orchestrate/utils/loadShoppingFixture.ts +52 -0
- package/src/orchestrate/utils/normalizeProductPlanPaths.ts +92 -0
- package/src/orchestrate/utils/renderJsonSchema.test.ts +162 -0
- package/src/orchestrate/utils/renderJsonSchema.ts +133 -0
- package/src/orchestrate/utils/renderResourcePage.test.ts +468 -0
- package/src/orchestrate/utils/renderResourcePage.ts +1624 -0
- package/src/orchestrate/utils/validateFrontendTypecheck.test.ts +32 -0
- package/src/orchestrate/utils/validateFrontendTypecheck.ts +335 -0
- package/src/preview/renderPreview.ts +273 -0
- package/src/typings/compiler.ts +47 -0
- package/src/typings/events.ts +155 -0
- package/src/typings/index.ts +10 -0
- package/src/typings/misc.ts +93 -0
- package/src/utils/ArrayUtil.ts +16 -0
- package/src/utils/StringUtil.ts +29 -0
- package/src/utils/classifyEndpoints.test.ts +86 -0
- package/src/utils/classifyEndpoints.ts +291 -0
- package/src/utils/endpointFilter.test.ts +50 -0
- package/src/utils/endpointFilter.ts +0 -0
- package/src/utils/extractFields.test.ts +82 -0
- package/src/utils/extractFields.ts +306 -0
- package/src/utils/index.ts +13 -0
- package/src/utils/normalizeForNestia.test.ts +93 -0
- package/src/utils/normalizeForNestia.ts +139 -0
- package/src/utils/resourcePlan.test.ts +104 -0
- package/src/utils/resourcePlan.ts +180 -0
- package/src/utils/sliceDocument.test.ts +85 -0
- package/src/utils/sliceDocument.ts +119 -0
- package/src/utils/toEndpoints.test.ts +251 -0
- package/src/utils/toEndpoints.ts +343 -0
- package/src/verify/runWorkflows.ts +403 -0
- package/src/verify/workflows.test.ts +117 -0
- package/src/verify/workflows.ts +154 -0
- package/template/CLAUDE.md +140 -0
- package/template/Dockerfile +31 -0
- package/template/PROMPT.md +80 -0
- package/template/SANDBOX.md +70 -0
- package/template/app/api/health/route.ts +10 -0
- package/template/app/globals.css +97 -0
- package/template/app/layout.tsx +30 -0
- package/template/app/page.tsx +19 -0
- package/template/components/AppShell.tsx +114 -0
- package/template/components/auto/CatalogGrid.tsx +159 -0
- package/template/components/auto/ConfirmButton.tsx +67 -0
- package/template/components/auto/EmbeddedCollection.tsx +144 -0
- package/template/components/auto/ResourceDashboard.tsx +104 -0
- package/template/components/auto/ResourceDetail.tsx +93 -0
- package/template/components/auto/ResourceForm.tsx +235 -0
- package/template/components/auto/ResourceIcon.tsx +88 -0
- package/template/components/auto/ResourceLanding.tsx +155 -0
- package/template/components/auto/ResourceTable.tsx +223 -0
- package/template/components/auto/formatValue.tsx +186 -0
- package/template/components/auto/types.ts +42 -0
- package/template/components/ui/badge.tsx +40 -0
- package/template/components/ui/button.tsx +57 -0
- package/template/components/ui/card.tsx +86 -0
- package/template/components/ui/dialog.tsx +119 -0
- package/template/components/ui/input.tsx +23 -0
- package/template/components/ui/label.tsx +24 -0
- package/template/components/ui/pagination.tsx +117 -0
- package/template/components/ui/select.tsx +92 -0
- package/template/components/ui/sheet.tsx +135 -0
- package/template/components/ui/skeleton.tsx +15 -0
- package/template/components/ui/table.tsx +120 -0
- package/template/components/ui/tabs.tsx +55 -0
- package/template/lib/utils.ts +35 -0
- package/template/next.config.mjs +52 -0
- package/template/package.json +46 -0
- package/template/postcss.config.js +6 -0
- package/template/scripts/start-shopping-backend.sh +56 -0
- package/template/tailwind.config.ts +96 -0
- package/template/tsconfig.json +29 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { OpenApi } from "@typia/interface";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { extractBackendFromSwagger, fromSwagger } from "./fromSwagger";
|
|
5
|
+
import { toEndpoints } from "./utils/toEndpoints";
|
|
6
|
+
|
|
7
|
+
/** Resolve the (possibly `$ref`-promoted) application/json schema of a response. */
|
|
8
|
+
function jsonSchemaOf(
|
|
9
|
+
doc: OpenApi.IDocument,
|
|
10
|
+
path: string,
|
|
11
|
+
method: "get" | "post",
|
|
12
|
+
status: string,
|
|
13
|
+
): Record<string, unknown> {
|
|
14
|
+
const op = (doc.paths?.[path] as Record<string, unknown> | undefined)?.[
|
|
15
|
+
method
|
|
16
|
+
] as { responses?: Record<string, { content?: Record<string, { schema?: unknown }> }> } | undefined;
|
|
17
|
+
const schema = op?.responses?.[status]?.content?.["application/json"]?.schema as
|
|
18
|
+
| Record<string, unknown>
|
|
19
|
+
| undefined;
|
|
20
|
+
if (schema === undefined) return {};
|
|
21
|
+
if (typeof schema.$ref === "string") {
|
|
22
|
+
const name = schema.$ref.split("/").pop()!;
|
|
23
|
+
return ((doc.components?.schemas ?? {})[name] ?? {}) as Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
return schema;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("fromSwagger — strips #/paths/ JSON-pointer allOf members (DigitalOcean)", () => {
|
|
29
|
+
it("keeps the data member when an allOf mixes it with unresolvable path-pointer refs", () => {
|
|
30
|
+
// DO's list responses are allOf:[{properties:{droplets}}, <links-ref>, <meta-ref>]
|
|
31
|
+
// where the refs point into another operation's inline schema. upgradeDocument
|
|
32
|
+
// discards the whole allOf, losing the data — stripping the path refs recovers it.
|
|
33
|
+
const responseSchema = {
|
|
34
|
+
allOf: [
|
|
35
|
+
{ properties: { droplets: { type: "array", items: { $ref: "#/components/schemas/Droplet" } } } },
|
|
36
|
+
{ $ref: "#/paths/~1other/get/responses/200/content/application~1json/schema/allOf/1" },
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
const raw = {
|
|
40
|
+
openapi: "3.0.0",
|
|
41
|
+
info: { title: "t", version: "1" },
|
|
42
|
+
paths: {
|
|
43
|
+
"/droplets": {
|
|
44
|
+
get: {
|
|
45
|
+
operationId: "i",
|
|
46
|
+
responses: { 200: { description: "ok", content: { "application/json": { schema: responseSchema } } } },
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
components: {
|
|
51
|
+
schemas: { Droplet: { type: "object", properties: { id: { type: "integer" }, name: { type: "string" } }, required: ["id"] } },
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const doc = fromSwagger(raw as never);
|
|
55
|
+
const e = toEndpoints(doc).find((x) => x.path === "/droplets");
|
|
56
|
+
expect(e).toBeDefined();
|
|
57
|
+
// the response survived the upgrade with its droplets array (not an empty schema)
|
|
58
|
+
expect(JSON.stringify(jsonSchemaOf(doc, "/droplets", "get", "200"))).toContain("droplets");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("flattens an INLINE allOf in an operation response (DigitalOcean droplets)", () => {
|
|
62
|
+
// DO's `GET /v2/droplets` response is `allOf:[{properties:{droplets}}, {$ref:Pagination}]`
|
|
63
|
+
// INLINE in the operation — not a component schema, so the components-only
|
|
64
|
+
// flatten pass missed it. nestia then emits `Response = any` for the route,
|
|
65
|
+
// `typia.random<any>()` yields nothing, and the simulate-mode list renders
|
|
66
|
+
// empty forever. The flatten must walk the whole document.
|
|
67
|
+
const raw = {
|
|
68
|
+
openapi: "3.0.0",
|
|
69
|
+
info: { title: "t", version: "1" },
|
|
70
|
+
paths: {
|
|
71
|
+
"/droplets": {
|
|
72
|
+
get: {
|
|
73
|
+
operationId: "list",
|
|
74
|
+
responses: {
|
|
75
|
+
200: {
|
|
76
|
+
description: "ok",
|
|
77
|
+
content: {
|
|
78
|
+
"application/json": {
|
|
79
|
+
schema: {
|
|
80
|
+
allOf: [
|
|
81
|
+
{ properties: { droplets: { type: "array", items: { type: "object", properties: { id: { type: "integer" } } } } } },
|
|
82
|
+
{ $ref: "#/components/schemas/Pagination" },
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
components: {
|
|
93
|
+
schemas: { Pagination: { type: "object", properties: { total: { type: "integer" } } } },
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
const doc = fromSwagger(raw as never);
|
|
97
|
+
const flat = jsonSchemaOf(doc, "/droplets", "get", "200");
|
|
98
|
+
// the inline allOf was merged into plain properties — both members survive
|
|
99
|
+
expect(flat.allOf).toBeUndefined();
|
|
100
|
+
const props = (flat.properties ?? {}) as Record<string, unknown>;
|
|
101
|
+
expect(Object.keys(props).sort()).toEqual(["droplets", "total"]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("flattens an allOf nested INSIDE a property (DigitalOcean databases.connection)", () => {
|
|
105
|
+
const raw = {
|
|
106
|
+
openapi: "3.0.0",
|
|
107
|
+
info: { title: "t", version: "1" },
|
|
108
|
+
paths: {
|
|
109
|
+
"/databases": {
|
|
110
|
+
get: {
|
|
111
|
+
operationId: "list",
|
|
112
|
+
responses: {
|
|
113
|
+
200: {
|
|
114
|
+
description: "ok",
|
|
115
|
+
content: {
|
|
116
|
+
"application/json": {
|
|
117
|
+
schema: {
|
|
118
|
+
type: "object",
|
|
119
|
+
properties: {
|
|
120
|
+
connection: { allOf: [{ type: "object", properties: { host: { type: "string" } } }, { type: "object", properties: { port: { type: "integer" } } }] },
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
components: { schemas: {} },
|
|
131
|
+
};
|
|
132
|
+
const doc = fromSwagger(raw as never);
|
|
133
|
+
const flat = jsonSchemaOf(doc, "/databases", "get", "200");
|
|
134
|
+
const connection = ((flat.properties ?? {}) as Record<string, Record<string, unknown>>)
|
|
135
|
+
.connection ?? {};
|
|
136
|
+
expect(connection.allOf).toBeUndefined();
|
|
137
|
+
expect(Object.keys((connection.properties ?? {}) as object).sort()).toEqual(["host", "port"]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("resolves a #/paths/ JSON-pointer $ref OUTSIDE an allOf (DigitalOcean metrics/apps)", () => {
|
|
141
|
+
// DO also DRYs whole schemas as `$ref: "#/paths/~1v2~1bandwidth/get/..."` at
|
|
142
|
+
// the schema ROOT or inside a property — not as an allOf member, so the
|
|
143
|
+
// allOf-stripping pass never sees it. nestia cannot chase the pointer and
|
|
144
|
+
// emits `Response = any`. Resolve the pointer against the document itself.
|
|
145
|
+
const raw = {
|
|
146
|
+
openapi: "3.0.0",
|
|
147
|
+
info: { title: "t", version: "1" },
|
|
148
|
+
paths: {
|
|
149
|
+
"/v2/bandwidth": {
|
|
150
|
+
get: { operationId: "bw", responses: { 200: { description: "ok", content: { "application/json": { schema: { type: "object", properties: { metrics: { type: "array", items: { type: "object", properties: { value: { type: "number" } } } } } } } } } } },
|
|
151
|
+
},
|
|
152
|
+
"/v2/cpu": {
|
|
153
|
+
get: { operationId: "cpu", responses: { 200: { description: "ok", content: { "application/json": { schema: { $ref: "#/paths/~1v2~1bandwidth/get/responses/200/content/application~1json/schema" } } } } } },
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
components: { schemas: {} },
|
|
157
|
+
};
|
|
158
|
+
const doc = fromSwagger(raw as never);
|
|
159
|
+
const cpu = jsonSchemaOf(doc, "/v2/cpu", "get", "200");
|
|
160
|
+
// the pointer was resolved to the bandwidth schema's real shape
|
|
161
|
+
expect(JSON.stringify(cpu)).toContain("metrics");
|
|
162
|
+
const e = toEndpoints(doc).find((x) => x.path === "/v2/cpu");
|
|
163
|
+
expect(e?.responseBody ?? e?.responseSchema).not.toBeNull();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("promotes colliding inline responses so a parent list and child detail both stay typed", () => {
|
|
167
|
+
// nestia derives the promoted component name from the path with `{param}`
|
|
168
|
+
// segments dropped, so `/v2/droplets` and `/v2/droplets/{droplet_id}` both
|
|
169
|
+
// claim `IApiV2Droplets.GetResponse` — the loser's SDK function comes out
|
|
170
|
+
// `Response = any` and its simulate screen renders empty forever (38 of
|
|
171
|
+
// DigitalOcean's 148 SDK files). Promoting each inline OBJECT body to a
|
|
172
|
+
// unique named component before nestia sees it removes the collision.
|
|
173
|
+
const inlineObj = (props: Record<string, unknown>) => ({
|
|
174
|
+
type: "object",
|
|
175
|
+
properties: props,
|
|
176
|
+
});
|
|
177
|
+
const raw = {
|
|
178
|
+
openapi: "3.0.0",
|
|
179
|
+
info: { title: "t", version: "1" },
|
|
180
|
+
paths: {
|
|
181
|
+
"/v2/droplets": {
|
|
182
|
+
get: { operationId: "list", responses: { 200: { description: "ok", content: { "application/json": { schema: inlineObj({ droplets: { type: "array", items: inlineObj({ id: { type: "integer" } }) } }) } } } } },
|
|
183
|
+
},
|
|
184
|
+
"/v2/droplets/{droplet_id}": {
|
|
185
|
+
get: { operationId: "at", parameters: [{ name: "droplet_id", in: "path", required: true, schema: { type: "integer" } }], responses: { 200: { description: "ok", content: { "application/json": { schema: inlineObj({ droplet: inlineObj({ id: { type: "integer" } }) }) } } } } },
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
components: { schemas: {} },
|
|
189
|
+
};
|
|
190
|
+
const doc = fromSwagger(raw as never);
|
|
191
|
+
const eps = toEndpoints(doc);
|
|
192
|
+
const list = eps.find((e) => e.path === "/v2/droplets");
|
|
193
|
+
const detail = eps.find((e) => e.path === "/v2/droplets/{droplet_id}");
|
|
194
|
+
// BOTH carry a named response type that resolves in the document — neither
|
|
195
|
+
// collapsed to an untyped (any) body.
|
|
196
|
+
for (const e of [list, detail]) {
|
|
197
|
+
expect(e?.responseBody).not.toBeNull();
|
|
198
|
+
expect((doc.components?.schemas ?? {})[e!.responseBody!.typeName]).toBeDefined();
|
|
199
|
+
}
|
|
200
|
+
expect(list!.responseBody!.typeName).not.toBe(detail!.responseBody!.typeName);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("drops a path whose {param} name is not a valid identifier (GitHub's {enterprise-team})", () => {
|
|
204
|
+
// nestia derives the SDK accessor from the param name and crashes on a
|
|
205
|
+
// hyphenated one — taking the WHOLE generation down. Drop just that path.
|
|
206
|
+
const raw = {
|
|
207
|
+
openapi: "3.0.0",
|
|
208
|
+
info: { title: "t", version: "1" },
|
|
209
|
+
paths: {
|
|
210
|
+
"/teams/{teamId}": { get: { operationId: "ok", parameters: [{ name: "teamId", in: "path", required: true, schema: { type: "string" } }], responses: { 200: { description: "ok" } } } },
|
|
211
|
+
"/teams/{enterprise-team}/members": { get: { operationId: "bad", parameters: [{ name: "enterprise-team", in: "path", required: true, schema: { type: "string" } }], responses: { 200: { description: "ok" } } } },
|
|
212
|
+
},
|
|
213
|
+
components: { schemas: {} },
|
|
214
|
+
};
|
|
215
|
+
const doc = fromSwagger(raw as never);
|
|
216
|
+
expect(doc.paths["/teams/{teamId}"]).toBeDefined(); // the valid one survives
|
|
217
|
+
expect(doc.paths["/teams/{enterprise-team}/members"]).toBeUndefined(); // the hyphen one is dropped
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe("extractBackendFromSwagger", () => {
|
|
222
|
+
it("returns null for non-object payloads", () => {
|
|
223
|
+
expect(extractBackendFromSwagger(null)).toBeNull();
|
|
224
|
+
expect(extractBackendFromSwagger(undefined)).toBeNull();
|
|
225
|
+
expect(extractBackendFromSwagger("")).toBeNull();
|
|
226
|
+
expect(extractBackendFromSwagger(42)).toBeNull();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("returns null when servers is missing", () => {
|
|
230
|
+
expect(extractBackendFromSwagger({})).toBeNull();
|
|
231
|
+
expect(extractBackendFromSwagger({ openapi: "3.0.0" })).toBeNull();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("returns null when servers is empty or not an array", () => {
|
|
235
|
+
expect(extractBackendFromSwagger({ servers: [] })).toBeNull();
|
|
236
|
+
expect(
|
|
237
|
+
extractBackendFromSwagger({ servers: "http://x" } as unknown),
|
|
238
|
+
).toBeNull();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("returns the first server with a non-empty url", () => {
|
|
242
|
+
expect(
|
|
243
|
+
extractBackendFromSwagger({
|
|
244
|
+
servers: [{ url: "https://api.example.com" }],
|
|
245
|
+
}),
|
|
246
|
+
).toEqual({ host: "https://api.example.com" });
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("skips entries with missing or empty url and picks the next valid one", () => {
|
|
250
|
+
expect(
|
|
251
|
+
extractBackendFromSwagger({
|
|
252
|
+
servers: [
|
|
253
|
+
{},
|
|
254
|
+
{ url: "" },
|
|
255
|
+
{ url: 42 },
|
|
256
|
+
{ url: "https://api.example.com" },
|
|
257
|
+
],
|
|
258
|
+
}),
|
|
259
|
+
).toEqual({ host: "https://api.example.com" });
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("returns null when no server has a usable url", () => {
|
|
263
|
+
expect(
|
|
264
|
+
extractBackendFromSwagger({
|
|
265
|
+
servers: [{}, { url: "" }, { url: null }],
|
|
266
|
+
}),
|
|
267
|
+
).toBeNull();
|
|
268
|
+
});
|
|
269
|
+
});
|