@distilled.cloud/core 0.30.3 → 1.0.0-rc.10

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 (216) hide show
  1. package/LICENSE +201 -0
  2. package/lib/api.d.ts +165 -0
  3. package/lib/api.d.ts.map +1 -0
  4. package/lib/api.js +190 -0
  5. package/lib/api.js.map +1 -0
  6. package/lib/category.d.ts +4 -4
  7. package/lib/category.js +4 -4
  8. package/lib/codegen/boolean-string-enums.d.ts +36 -0
  9. package/lib/codegen/boolean-string-enums.d.ts.map +1 -0
  10. package/lib/codegen/boolean-string-enums.js +94 -0
  11. package/lib/codegen/boolean-string-enums.js.map +1 -0
  12. package/lib/codegen/boolean-string-enums.test.d.ts +2 -0
  13. package/lib/codegen/boolean-string-enums.test.d.ts.map +1 -0
  14. package/lib/codegen/boolean-string-enums.test.js +147 -0
  15. package/lib/codegen/boolean-string-enums.test.js.map +1 -0
  16. package/lib/codegen/cli.d.ts +79 -0
  17. package/lib/codegen/cli.d.ts.map +1 -0
  18. package/lib/codegen/cli.js +149 -0
  19. package/lib/codegen/cli.js.map +1 -0
  20. package/lib/codegen/emit.d.ts +125 -0
  21. package/lib/codegen/emit.d.ts.map +1 -0
  22. package/lib/codegen/emit.js +101 -0
  23. package/lib/codegen/emit.js.map +1 -0
  24. package/lib/codegen/format.d.ts +23 -0
  25. package/lib/codegen/format.d.ts.map +1 -0
  26. package/lib/codegen/format.js +28 -0
  27. package/lib/codegen/format.js.map +1 -0
  28. package/lib/codegen/generator.d.ts +334 -0
  29. package/lib/codegen/generator.d.ts.map +1 -0
  30. package/lib/codegen/generator.js +813 -0
  31. package/lib/codegen/generator.js.map +1 -0
  32. package/lib/codegen/graph.d.ts +36 -0
  33. package/lib/codegen/graph.d.ts.map +1 -0
  34. package/lib/codegen/graph.js +136 -0
  35. package/lib/codegen/graph.js.map +1 -0
  36. package/lib/codegen/graphql-client.d.ts +62 -0
  37. package/lib/codegen/graphql-client.d.ts.map +1 -0
  38. package/lib/codegen/graphql-client.js +294 -0
  39. package/lib/codegen/graphql-client.js.map +1 -0
  40. package/lib/codegen/graphql-client.test.d.ts +2 -0
  41. package/lib/codegen/graphql-client.test.d.ts.map +1 -0
  42. package/lib/codegen/graphql-client.test.js +311 -0
  43. package/lib/codegen/graphql-client.test.js.map +1 -0
  44. package/lib/codegen/graphql.d.ts +207 -0
  45. package/lib/codegen/graphql.d.ts.map +1 -0
  46. package/lib/codegen/graphql.js +799 -0
  47. package/lib/codegen/graphql.js.map +1 -0
  48. package/lib/codegen/members.d.ts +25 -0
  49. package/lib/codegen/members.d.ts.map +1 -0
  50. package/lib/codegen/members.js +55 -0
  51. package/lib/codegen/members.js.map +1 -0
  52. package/lib/codegen/naming.d.ts +29 -0
  53. package/lib/codegen/naming.d.ts.map +1 -0
  54. package/lib/codegen/naming.js +74 -0
  55. package/lib/codegen/naming.js.map +1 -0
  56. package/lib/codegen/openapi-cli.d.ts +52 -0
  57. package/lib/codegen/openapi-cli.d.ts.map +1 -0
  58. package/lib/codegen/openapi-cli.js +109 -0
  59. package/lib/codegen/openapi-cli.js.map +1 -0
  60. package/lib/codegen/openapi.d.ts +178 -0
  61. package/lib/codegen/openapi.d.ts.map +1 -0
  62. package/lib/codegen/openapi.js +1377 -0
  63. package/lib/codegen/openapi.js.map +1 -0
  64. package/lib/codegen/operations.d.ts +24 -0
  65. package/lib/codegen/operations.d.ts.map +1 -0
  66. package/lib/codegen/operations.js +56 -0
  67. package/lib/codegen/operations.js.map +1 -0
  68. package/lib/codegen/pagination.d.ts +39 -0
  69. package/lib/codegen/pagination.d.ts.map +1 -0
  70. package/lib/codegen/pagination.js +33 -0
  71. package/lib/codegen/pagination.js.map +1 -0
  72. package/lib/codegen/patches.d.ts +65 -0
  73. package/lib/codegen/patches.d.ts.map +1 -0
  74. package/lib/codegen/patches.js +236 -0
  75. package/lib/codegen/patches.js.map +1 -0
  76. package/lib/codegen/patches.test.d.ts +2 -0
  77. package/lib/codegen/patches.test.d.ts.map +1 -0
  78. package/lib/codegen/patches.test.js +105 -0
  79. package/lib/codegen/patches.test.js.map +1 -0
  80. package/lib/codegen/prelude.d.ts +15 -0
  81. package/lib/codegen/prelude.d.ts.map +1 -0
  82. package/lib/codegen/prelude.js +60 -0
  83. package/lib/codegen/prelude.js.map +1 -0
  84. package/lib/codegen/proto.d.ts +121 -0
  85. package/lib/codegen/proto.d.ts.map +1 -0
  86. package/lib/codegen/proto.js +962 -0
  87. package/lib/codegen/proto.js.map +1 -0
  88. package/lib/codegen/rewrite-operation-ids.d.ts +131 -0
  89. package/lib/codegen/rewrite-operation-ids.d.ts.map +1 -0
  90. package/lib/codegen/rewrite-operation-ids.js +1079 -0
  91. package/lib/codegen/rewrite-operation-ids.js.map +1 -0
  92. package/lib/codegen/rewrite-operation-ids.test.d.ts +2 -0
  93. package/lib/codegen/rewrite-operation-ids.test.d.ts.map +1 -0
  94. package/lib/codegen/rewrite-operation-ids.test.js +533 -0
  95. package/lib/codegen/rewrite-operation-ids.test.js.map +1 -0
  96. package/lib/codegen/spec-path.d.ts +16 -0
  97. package/lib/codegen/spec-path.d.ts.map +1 -0
  98. package/lib/codegen/spec-path.js +101 -0
  99. package/lib/codegen/spec-path.js.map +1 -0
  100. package/lib/error-category.d.ts +28 -0
  101. package/lib/error-category.d.ts.map +1 -0
  102. package/lib/error-category.js +46 -0
  103. package/lib/error-category.js.map +1 -0
  104. package/lib/errors.d.ts +1 -0
  105. package/lib/errors.d.ts.map +1 -1
  106. package/lib/errors.js +18 -13
  107. package/lib/errors.js.map +1 -1
  108. package/lib/graphql.d.ts +284 -0
  109. package/lib/graphql.d.ts.map +1 -0
  110. package/lib/graphql.fixture.d.ts +249 -0
  111. package/lib/graphql.fixture.d.ts.map +1 -0
  112. package/lib/graphql.fixture.js +240 -0
  113. package/lib/graphql.fixture.js.map +1 -0
  114. package/lib/graphql.js +718 -0
  115. package/lib/graphql.js.map +1 -0
  116. package/lib/graphql.test.d.ts +2 -0
  117. package/lib/graphql.test.d.ts.map +1 -0
  118. package/lib/graphql.test.js +780 -0
  119. package/lib/graphql.test.js.map +1 -0
  120. package/lib/graphql.types.d.ts +2 -0
  121. package/lib/graphql.types.d.ts.map +1 -0
  122. package/lib/graphql.types.js +45 -0
  123. package/lib/graphql.types.js.map +1 -0
  124. package/lib/json-patch.d.ts +30 -30
  125. package/lib/json-patch.d.ts.map +1 -1
  126. package/lib/json-patch.js +73 -107
  127. package/lib/json-patch.js.map +1 -1
  128. package/lib/pagination.d.ts +77 -51
  129. package/lib/pagination.d.ts.map +1 -1
  130. package/lib/pagination.js +162 -94
  131. package/lib/pagination.js.map +1 -1
  132. package/lib/protocol-http.d.ts +74 -0
  133. package/lib/protocol-http.d.ts.map +1 -0
  134. package/lib/protocol-http.js +590 -0
  135. package/lib/protocol-http.js.map +1 -0
  136. package/lib/protocol-http.test.d.ts +2 -0
  137. package/lib/protocol-http.test.d.ts.map +1 -0
  138. package/lib/protocol-http.test.js +88 -0
  139. package/lib/protocol-http.test.js.map +1 -0
  140. package/lib/protocol-rest.d.ts +134 -0
  141. package/lib/protocol-rest.d.ts.map +1 -0
  142. package/lib/protocol-rest.js +256 -0
  143. package/lib/protocol-rest.js.map +1 -0
  144. package/lib/retry.d.ts +8 -2
  145. package/lib/retry.d.ts.map +1 -1
  146. package/lib/retry.js +22 -16
  147. package/lib/retry.js.map +1 -1
  148. package/lib/schema.d.ts +8 -9
  149. package/lib/schema.d.ts.map +1 -1
  150. package/lib/schema.js +8 -9
  151. package/lib/schema.js.map +1 -1
  152. package/lib/trait.d.ts +174 -0
  153. package/lib/trait.d.ts.map +1 -0
  154. package/lib/trait.js +123 -0
  155. package/lib/trait.js.map +1 -0
  156. package/package.json +24 -78
  157. package/src/api.ts +460 -0
  158. package/src/category.ts +4 -4
  159. package/src/codegen/boolean-string-enums.test.ts +168 -0
  160. package/src/codegen/boolean-string-enums.ts +106 -0
  161. package/src/codegen/cli.ts +285 -0
  162. package/src/codegen/emit.ts +203 -0
  163. package/src/codegen/format.ts +47 -0
  164. package/src/codegen/generator.ts +1283 -0
  165. package/src/codegen/graph.ts +151 -0
  166. package/src/codegen/graphql-client.test.ts +386 -0
  167. package/src/codegen/graphql-client.ts +419 -0
  168. package/src/codegen/graphql.ts +1217 -0
  169. package/src/codegen/members.ts +71 -0
  170. package/src/codegen/naming.ts +86 -0
  171. package/src/codegen/openapi-cli.ts +182 -0
  172. package/src/codegen/openapi.ts +1689 -0
  173. package/src/codegen/operations.ts +76 -0
  174. package/src/codegen/pagination.ts +71 -0
  175. package/src/codegen/patches.test.ts +130 -0
  176. package/src/codegen/patches.ts +291 -0
  177. package/src/codegen/prelude.ts +70 -0
  178. package/src/codegen/proto.ts +1128 -0
  179. package/src/codegen/rewrite-operation-ids.test.ts +563 -0
  180. package/src/codegen/rewrite-operation-ids.ts +1206 -0
  181. package/src/codegen/spec-path.ts +115 -0
  182. package/src/error-category.ts +84 -0
  183. package/src/errors.ts +22 -25
  184. package/src/graphql.fixture.ts +371 -0
  185. package/src/graphql.test.ts +974 -0
  186. package/src/graphql.ts +1321 -0
  187. package/src/graphql.types.ts +185 -0
  188. package/src/json-patch.ts +95 -122
  189. package/src/pagination.ts +217 -146
  190. package/src/protocol-http.test.ts +107 -0
  191. package/src/protocol-http.ts +735 -0
  192. package/src/protocol-rest.ts +391 -0
  193. package/src/retry.ts +21 -22
  194. package/src/schema.ts +9 -10
  195. package/src/trait.ts +274 -0
  196. package/README.md +0 -30
  197. package/lib/client.d.ts +0 -167
  198. package/lib/client.d.ts.map +0 -1
  199. package/lib/client.js +0 -659
  200. package/lib/client.js.map +0 -1
  201. package/lib/schemas.d.ts +0 -60
  202. package/lib/schemas.d.ts.map +0 -1
  203. package/lib/schemas.js +0 -79
  204. package/lib/schemas.js.map +0 -1
  205. package/lib/sensitive.d.ts +0 -71
  206. package/lib/sensitive.d.ts.map +0 -1
  207. package/lib/sensitive.js +0 -96
  208. package/lib/sensitive.js.map +0 -1
  209. package/lib/traits.d.ts +0 -421
  210. package/lib/traits.d.ts.map +0 -1
  211. package/lib/traits.js +0 -737
  212. package/lib/traits.js.map +0 -1
  213. package/src/client.ts +0 -1177
  214. package/src/schemas.ts +0 -128
  215. package/src/sensitive.ts +0 -119
  216. package/src/traits.ts +0 -996
@@ -0,0 +1,1377 @@
1
+ /**
2
+ * OpenAPI → Smithy 2.0 JSON converter (dev-time only, provider-agnostic).
3
+ *
4
+ * Turns a Swagger 2.0 / OAS 3.0 / OAS 3.1 document into the same Smithy JSON
5
+ * model shape the other spec converters produce (`{ smithy: "2.0", metadata,
6
+ * shapes }`), so every OpenAPI-sourced provider flows through the one shared
7
+ * `generateService` compiler. Conversion fidelity follows distilled v0's
8
+ * `generate-openapi.ts` feature matrix:
9
+ *
10
+ * • one operation per (path × get/post/put/patch/delete — plus head/options
11
+ * when a provider opts in via `extraHttpMethods`), deprecated skipped by
12
+ * default; op shape name = PascalCase(operationId), or verbNoun
13
+ * (`Apps_list` → `ListApps`) when {@link OpenApiConvertOptions.operationNaming}
14
+ * is `"verbNoun"`
15
+ * • input `<Op>Request`: path params → `smithy.api#httpLabel` (+required),
16
+ * query params → `smithy.api#httpQuery`, header params →
17
+ * `smithy.api#httpHeader` when `headerParams` is on (dropped otherwise,
18
+ * as cookie params always are),
19
+ * body properties flattened alongside
20
+ * (labels win, then query, then headers, then body);
21
+ * non-object bodies become a sole `body` member with `smithy.api#httpPayload`
22
+ * • `$ref`s become NAMED shapes (`components/schemas/X` → `<ns>#X`), reused
23
+ * across operations; anonymous nested objects synthesize names from the
24
+ * parent + member path
25
+ * • responses: 200 → 201 → 204 precedence (`successStatuses` overrides),
26
+ * `application/json` only; object
27
+ * results become `<Op>Response` structures (a sole `$ref` reuses the named
28
+ * shape); bare array/scalar results wrap in a structure whose single
29
+ * member carries `com.distilled.openapi#rawResponse` (the SdkSpec maps it
30
+ * to a root pipe); no schema → `smithy.api#Unit`; `head` is always
31
+ * `smithy.api#Unit`, whatever content the spec declares
32
+ * • nullability (3.0 `nullable`, 3.1 `type: [..., "null"]`, 2.0
33
+ * `x-nullable`, `oneOf`/`anyOf` null branches) → member-level
34
+ * `com.distilled.openapi#nullable` trait
35
+ * • sensitive string members (x-sensitive or name-pattern match) →
36
+ * `smithy.api#sensitive` on the member (direction-agnostic; the SdkSpec
37
+ * decides the input/output treatment)
38
+ * • per-op non-2xx statuses (outside `defaultErrorStatuses`) → typed error
39
+ * shapes with `smithy.api#error`/`smithy.api#httpError` and
40
+ * `com.distilled.openapi#errorMatchers` status matchers
41
+ * • v0 pagination detection (`pagination.cursor` / `.next` / `.next_page`,
42
+ * `next_token`/`NextToken`/`nextToken`, `next_page` + input alias lists +
43
+ * first top-level array property) → `smithy.api#paginated` on the op
44
+ * (with the non-standard `mode` member the core runtime dispatches on)
45
+ */
46
+ import { isMechanicalOperationId, isVerbatimRouteId, pathToVerbNoun, resolveOperationName, toVerbNoun, } from "./rewrite-operation-ids.js";
47
+ // ============================================================================
48
+ // Trait ids (the `com.distilled.openapi` vocabulary)
49
+ // ============================================================================
50
+ /** Member-level nullability (`S.NullOr` + `| null` via `SdkSpec.nullableTrait`). */
51
+ export const NULLABLE_TRAIT = "com.distilled.openapi#nullable";
52
+ /**
53
+ * Marks the sole member of a synthesized response wrapper for bare
54
+ * array/scalar response bodies. SdkSpecs bind it with a `rootPipe`
55
+ * (`T.RawResponseRoot()` from `core/protocol-rest`), so the emitted response
56
+ * type IS the payload type.
57
+ */
58
+ export const RAW_RESPONSE_TRAIT = "com.distilled.openapi#rawResponse";
59
+ /**
60
+ * Wire-matching rules for a generated error class (see
61
+ * `SdkSpec.errorMatchersTrait` + `applyErrorMatchers`). The converter stamps
62
+ * `[{ status }]` from the response status the class was mapped from.
63
+ */
64
+ export const ERROR_MATCHERS_TRAIT = "com.distilled.openapi#errorMatchers";
65
+ /**
66
+ * Non-JSON request body encoding chosen by the v0 content-type precedence
67
+ * (json > form-urlencoded > multipart). Value: `"form-urlencoded"` |
68
+ * `"multipart"`. `"multipart"` is also merged into the `smithy.api#http`
69
+ * trait's `contentType` (which core's `buildRequest` understands);
70
+ * form-urlencoded is left to provider SdkSpecs/protocols.
71
+ */
72
+ export const CONTENT_TYPE_TRAIT = "com.distilled.openapi#contentType";
73
+ /**
74
+ * The `apiVersion` recorded on every operation when
75
+ * {@link OpenApiConvertOptions.apiVersion} is set (Azure ARM style). The
76
+ * matching `api-version` query parameter is dropped from inputs; the
77
+ * provider's protocol injects it at request time.
78
+ */
79
+ export const API_VERSION_TRAIT = "com.distilled.openapi#apiVersion";
80
+ // ============================================================================
81
+ // Sensitive-field name patterns (distilled v0's SENSITIVE_FIELD_PATTERNS)
82
+ // ============================================================================
83
+ export const SENSITIVE_FIELD_PATTERNS = [
84
+ /password/i,
85
+ /^secret$/i,
86
+ /secret[-_]?key/i,
87
+ /[-_]secret$/i,
88
+ /^client[-_]?secret$/i,
89
+ /^access[-_]?token$/i,
90
+ /^refresh[-_]?token$/i,
91
+ /^api[-_]?key$/i,
92
+ /^api[-_]?key[-_]?secret$/i,
93
+ /^api[-_]?token$/i,
94
+ /^private[-_]?key$/i,
95
+ /^secret[-_]?access[-_]?key$/i,
96
+ /^session[-_]?token$/i,
97
+ /^access[-_]?key[-_]?id$/i,
98
+ /^one[-_]?time[-_]?password$/i,
99
+ /^connection[-_]?string$/i,
100
+ /^connection[-_]?uri$/i,
101
+ /^plain[-_]?text$/i,
102
+ /^plain[-_]?text[-_]?refresh[-_]?token$/i,
103
+ ];
104
+ // ============================================================================
105
+ // String helpers (same conventions as cloudflare's spec-to-smithy)
106
+ // ============================================================================
107
+ const PRELUDE = {
108
+ Unit: "smithy.api#Unit",
109
+ String: "smithy.api#String",
110
+ Boolean: "smithy.api#Boolean",
111
+ Double: "smithy.api#Double",
112
+ Integer: "smithy.api#Integer",
113
+ Document: "smithy.api#Document",
114
+ };
115
+ /** snake/kebab/space/camel → PascalCase identifier (inner caps preserved). */
116
+ const pascal = (s) => {
117
+ const parts = s.split(/[^A-Za-z0-9]+/).filter(Boolean);
118
+ let out = parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
119
+ if (out === "")
120
+ out = "Shape";
121
+ if (/^[0-9]/.test(out))
122
+ out = `_${out}`;
123
+ return out;
124
+ };
125
+ /** Keep a JSON field name if it's a valid Smithy member identifier, else sanitize. */
126
+ const memberIdent = (name) => {
127
+ let out = name.replace(/[^A-Za-z0-9_]/g, "_");
128
+ if (/^[0-9]/.test(out))
129
+ out = `_${out}`;
130
+ return out || "_";
131
+ };
132
+ /**
133
+ * Header name → member identifier (`x-Artifact-Tag` → `xArtifactTag`). The
134
+ * wire name is carried by the `smithy.api#httpHeader` trait, so the member
135
+ * can read like the rest of the input rather than like a header.
136
+ */
137
+ const headerMemberName = (name) => {
138
+ const parts = name.split(/[^A-Za-z0-9]+/).filter(Boolean);
139
+ if (parts.length === 0)
140
+ return "_";
141
+ const out = parts
142
+ .map((p, i) => i === 0
143
+ ? p.charAt(0).toLowerCase() + p.slice(1)
144
+ : p.charAt(0).toUpperCase() + p.slice(1))
145
+ .join("");
146
+ return /^[0-9]/.test(out) ? `_${out}` : out;
147
+ };
148
+ const enumMemberName = (value) => {
149
+ let out = value
150
+ .toUpperCase()
151
+ .replace(/[^A-Z0-9]+/g, "_")
152
+ .replace(/^_+|_+$/g, "");
153
+ if (out === "")
154
+ out = "VALUE";
155
+ if (/^[0-9]/.test(out))
156
+ out = `_${out}`;
157
+ return out;
158
+ };
159
+ // ============================================================================
160
+ // Conversion context
161
+ // ============================================================================
162
+ const MAX_SCHEMA_DEPTH = 40;
163
+ const uniqueName = (ctx, base) => {
164
+ const want = pascal(base);
165
+ let name = want;
166
+ let n = 2;
167
+ while (ctx.names.has(name))
168
+ name = `${want}${n++}`;
169
+ ctx.names.add(name);
170
+ return name;
171
+ };
172
+ const addShape = (ctx, base, def) => {
173
+ const id = `${ctx.ns}#${uniqueName(ctx, base)}`;
174
+ ctx.shapes[id] = def;
175
+ return id;
176
+ };
177
+ const detectVersion = (spec) => {
178
+ if (spec?.swagger === "2.0")
179
+ return "2.0";
180
+ const v = spec?.openapi;
181
+ if (typeof v === "string" && v.startsWith("3.1"))
182
+ return "3.1";
183
+ if (typeof v === "string" && v.startsWith("3.0"))
184
+ return "3.0";
185
+ throw new Error(`Unsupported OpenAPI version (swagger=${spec?.swagger}, openapi=${spec?.openapi})`);
186
+ };
187
+ /** Resolve a local `#/...` JSON pointer against the spec document. */
188
+ const resolvePointer = (spec, ref) => {
189
+ if (typeof ref !== "string" || !ref.startsWith("#/"))
190
+ return undefined;
191
+ let cur = spec;
192
+ for (const raw of ref.slice(2).split("/")) {
193
+ const seg = raw.replace(/~1/g, "/").replace(/~0/g, "~");
194
+ if (cur === null || typeof cur !== "object")
195
+ return undefined;
196
+ cur = cur[seg];
197
+ }
198
+ return cur;
199
+ };
200
+ /** Follow `$ref` chains (bounded) to the underlying schema object. */
201
+ const deref = (ctx, def) => {
202
+ let cur = def;
203
+ for (let i = 0; i < 16 && cur && typeof cur === "object" && cur.$ref; i++) {
204
+ cur = resolvePointer(ctx.spec, cur.$ref);
205
+ }
206
+ return cur;
207
+ };
208
+ /**
209
+ * Flatten a schema into a property bag: `$ref`s resolved, `allOf` merged
210
+ * (properties + required unioned across resolved subschemas). A
211
+ * `oneOf`/`anyOf` encountered INSIDE an `allOf` merges its branches'
212
+ * properties as OPTIONAL members — the loosest satisfiable reading of the
213
+ * intersection — rather than dropping them; a top-level union is left to
214
+ * the union conversion path.
215
+ */
216
+ const flattenObject = (ctx, def, depth = 0) => {
217
+ const out = {
218
+ properties: {},
219
+ required: new Set(),
220
+ isObject: false,
221
+ };
222
+ // Revisiting a `$ref` under the same flags merges nothing new (property
223
+ // merge is first-wins) — dedupe so shared/cyclic allOf bases are walked
224
+ // once per flag combination instead of exponentially (MongoDB Atlas's
225
+ // polymorphic allOf/oneOf graph never finished under the bare depth cap).
226
+ const seen = new Set();
227
+ const visit = (d, dep, inAllOf, inUnion) => {
228
+ if (dep > MAX_SCHEMA_DEPTH)
229
+ return;
230
+ if (d && typeof d === "object" && typeof d.$ref === "string") {
231
+ const key = `${d.$ref}|${inAllOf}|${inUnion}`;
232
+ if (seen.has(key))
233
+ return;
234
+ seen.add(key);
235
+ }
236
+ const r = deref(ctx, d);
237
+ if (!r || typeof r !== "object")
238
+ return;
239
+ if (Array.isArray(r.allOf)) {
240
+ for (const sub of r.allOf)
241
+ visit(sub, dep + 1, true, inUnion);
242
+ }
243
+ if (inAllOf) {
244
+ const branches = r.oneOf ?? r.anyOf;
245
+ if (Array.isArray(branches)) {
246
+ for (const b of branches) {
247
+ if (!isNullBranch(ctx, b))
248
+ visit(b, dep + 1, inAllOf, true);
249
+ }
250
+ }
251
+ }
252
+ if (r.properties && typeof r.properties === "object") {
253
+ out.isObject = true;
254
+ for (const [k, v] of Object.entries(r.properties)) {
255
+ if (!(k in out.properties))
256
+ out.properties[k] = v;
257
+ }
258
+ }
259
+ if (r.type === "object")
260
+ out.isObject = true;
261
+ if (out.additionalProperties === undefined &&
262
+ r.additionalProperties !== undefined &&
263
+ r.additionalProperties !== false) {
264
+ out.additionalProperties = r.additionalProperties;
265
+ }
266
+ if (!inUnion && Array.isArray(r.required)) {
267
+ for (const k of r.required)
268
+ out.required.add(k);
269
+ }
270
+ };
271
+ visit(def, depth, false, false);
272
+ return out;
273
+ };
274
+ /** Whether a `oneOf`/`anyOf` branch represents JSON null. */
275
+ const isNullBranch = (ctx, branch) => {
276
+ const r = deref(ctx, branch);
277
+ if (!r || typeof r !== "object")
278
+ return false;
279
+ if (r.type === "null")
280
+ return true;
281
+ if (Array.isArray(r.type) && r.type.every((t) => t === "null")) {
282
+ return true;
283
+ }
284
+ if (Array.isArray(r.enum) &&
285
+ r.enum.length > 0 &&
286
+ r.enum.every((v) => v === null)) {
287
+ return true;
288
+ }
289
+ return false;
290
+ };
291
+ /** Intrinsic nullability flags on a schema node (not union null branches). */
292
+ const ownNullable = (ctx, def) => {
293
+ if (!def || typeof def !== "object")
294
+ return false;
295
+ // `nullable: true` is 3.0 vocabulary but appears in real 3.1 (and even
296
+ // 2.0) documents — honor it everywhere rather than silently dropping the
297
+ // null arm.
298
+ if (def.nullable === true)
299
+ return true;
300
+ if (def["x-nullable"] === true)
301
+ return true;
302
+ if (Array.isArray(def.type) && def.type.includes("null"))
303
+ return true;
304
+ return false;
305
+ };
306
+ /**
307
+ * Whether a property schema is excluded from the given direction:
308
+ * `readOnly: true` properties never appear in requests, `writeOnly: true`
309
+ * properties never appear in responses. Checks the property node itself and
310
+ * its `$ref` resolution.
311
+ */
312
+ const dirExcluded = (ctx, prop, dir) => {
313
+ const flag = dir === "in" ? "readOnly" : "writeOnly";
314
+ if (prop && typeof prop === "object" && prop[flag] === true)
315
+ return true;
316
+ const r = deref(ctx, prop);
317
+ return !!r && typeof r === "object" && r[flag] === true;
318
+ };
319
+ /** Child schema slots the direction-sensitivity walk descends through. */
320
+ const schemaChildren = (d) => [
321
+ ...(Array.isArray(d.allOf) ? d.allOf : []),
322
+ ...(Array.isArray(d.oneOf) ? d.oneOf : []),
323
+ ...(Array.isArray(d.anyOf) ? d.anyOf : []),
324
+ ...(d.properties && typeof d.properties === "object"
325
+ ? Object.values(d.properties)
326
+ : []),
327
+ ...(d.items ? [d.items] : []),
328
+ ...(d.additionalProperties && typeof d.additionalProperties === "object"
329
+ ? [d.additionalProperties]
330
+ : []),
331
+ ];
332
+ /**
333
+ * Walk an INLINE schema subtree (stopping at `$ref` nodes, which are
334
+ * appended to `refs` instead of followed). Returns whether the flag
335
+ * appears inline. Inline subtrees are JSON trees — no cycles.
336
+ */
337
+ const scanInlineForFlag = (d, flag, refs) => {
338
+ if (!d || typeof d !== "object")
339
+ return false;
340
+ if (typeof d.$ref === "string") {
341
+ refs.push(d.$ref);
342
+ return false;
343
+ }
344
+ if (d[flag] === true)
345
+ return true;
346
+ for (const sub of schemaChildren(d)) {
347
+ if (scanInlineForFlag(sub, flag, refs))
348
+ return true;
349
+ }
350
+ return false;
351
+ };
352
+ /**
353
+ * Compute, for the WHOLE document at once, the set of `$ref` pointers that
354
+ * are direction-sensitive: the flag appears somewhere in the schema graph
355
+ * reachable from the ref's target. One inline scan per distinct ref target
356
+ * (local flag + outgoing ref edges), then reverse propagation from the
357
+ * locally-flagged nodes — linear in spec size, and cycles / diamond-shared
358
+ * refs cost nothing. (This replaces a per-call DFS whose memoization was
359
+ * disabled by ANY re-encountered ref; on large specs where every schema
360
+ * shares refs — e.g. MongoDB Atlas's ubiquitous `links` — that re-walked
361
+ * the graph for each of thousands of `$ref` sites and never finished.)
362
+ */
363
+ const buildDirSensitiveRefs = (ctx, dir) => {
364
+ const flag = dir === "in" ? "readOnly" : "writeOnly";
365
+ // Every distinct `$ref` pointer in the document.
366
+ const allRefs = new Set();
367
+ const collect = (d) => {
368
+ if (Array.isArray(d)) {
369
+ for (const v of d)
370
+ collect(v);
371
+ return;
372
+ }
373
+ if (!d || typeof d !== "object")
374
+ return;
375
+ if (typeof d.$ref === "string")
376
+ allRefs.add(d.$ref);
377
+ for (const v of Object.values(d))
378
+ collect(v);
379
+ };
380
+ collect(ctx.spec);
381
+ // ref → refs reachable in one hop; refs whose target carries the flag inline.
382
+ const reverse = new Map();
383
+ const flagged = [];
384
+ for (const ref of allRefs) {
385
+ const outgoing = [];
386
+ if (scanInlineForFlag(resolvePointer(ctx.spec, ref), flag, outgoing)) {
387
+ flagged.push(ref);
388
+ continue; // already sensitive; its edges can't add anything
389
+ }
390
+ for (const to of outgoing) {
391
+ let from = reverse.get(to);
392
+ if (!from)
393
+ reverse.set(to, (from = []));
394
+ from.push(ref);
395
+ }
396
+ }
397
+ // Sensitivity propagates from flagged targets to every ref that reaches them.
398
+ const sensitive = new Set(flagged);
399
+ const queue = [...flagged];
400
+ while (queue.length > 0) {
401
+ const next = queue.pop();
402
+ for (const from of reverse.get(next) ?? []) {
403
+ if (!sensitive.has(from)) {
404
+ sensitive.add(from);
405
+ queue.push(from);
406
+ }
407
+ }
408
+ }
409
+ return sensitive;
410
+ };
411
+ /**
412
+ * Whether a schema subtree is direction-sensitive — contains
413
+ * `readOnly: true` (request direction) or `writeOnly: true` (response
414
+ * direction) anywhere, following local `$ref`s. Direction-sensitive named
415
+ * components convert to separate request/response shape variants
416
+ * (`<Name>Input`/`<Name>Output`); everything else is shared.
417
+ */
418
+ const dirSensitive = (ctx, def, dir) => {
419
+ let sensitiveRefs = ctx.dirSensitiveRefs.get(dir);
420
+ if (sensitiveRefs === undefined) {
421
+ sensitiveRefs = buildDirSensitiveRefs(ctx, dir);
422
+ ctx.dirSensitiveRefs.set(dir, sensitiveRefs);
423
+ }
424
+ const refs = [];
425
+ if (scanInlineForFlag(def, dir === "in" ? "readOnly" : "writeOnly", refs)) {
426
+ return true;
427
+ }
428
+ return refs.some((ref) => sensitiveRefs.has(ref));
429
+ };
430
+ /** Single non-array type from a possibly 3.1-style `type` value. */
431
+ const typeOf = (def) => {
432
+ const t = def?.type;
433
+ if (typeof t === "string")
434
+ return t;
435
+ if (Array.isArray(t)) {
436
+ const real = t.filter((x) => x !== "null");
437
+ if (real.length === 1 && typeof real[0] === "string")
438
+ return real[0];
439
+ }
440
+ return undefined;
441
+ };
442
+ // ============================================================================
443
+ // Schema conversion
444
+ // ============================================================================
445
+ /**
446
+ * Whether a schema definition warrants a NAMED shape (vs an inline prelude
447
+ * target). Named shapes can participate in reference cycles, so `$ref`s to
448
+ * nameable schemas reserve their name before converting.
449
+ */
450
+ const isNameable = (ctx, def) => {
451
+ if (!def || typeof def !== "object")
452
+ return false;
453
+ if (def.$ref)
454
+ return isNameable(ctx, deref(ctx, def));
455
+ if (Array.isArray(def.enum)) {
456
+ const values = def.enum.filter((v) => v !== null);
457
+ return (values.length > 0 &&
458
+ (values.every((v) => typeof v === "string") ||
459
+ values.every((v) => typeof v === "number")));
460
+ }
461
+ if (Array.isArray(def.allOf))
462
+ return true;
463
+ const branches = def.oneOf ?? def.anyOf;
464
+ if (Array.isArray(branches)) {
465
+ return branches.filter((b) => !isNullBranch(ctx, b)).length >= 2;
466
+ }
467
+ const t = typeOf(def);
468
+ if (t === "object" || def.properties || def.additionalProperties)
469
+ return true;
470
+ if (t === "array")
471
+ return true;
472
+ return false;
473
+ };
474
+ /**
475
+ * Convert one schema to a shape target. `dir` is the conversion direction
476
+ * (request vs response position — readOnly/writeOnly members are dropped
477
+ * accordingly). `reservedId` names the top-level shape when the caller
478
+ * pre-registered it (named `$ref` targets); otherwise anonymous shapes
479
+ * synthesize names from `hint`.
480
+ */
481
+ const convertSchema = (ctx, def, hint, depth, dir, reservedId) => {
482
+ const emit = (shapeDef, base) => {
483
+ if (reservedId !== undefined) {
484
+ ctx.shapes[reservedId] = shapeDef;
485
+ return reservedId;
486
+ }
487
+ return addShape(ctx, base, shapeDef);
488
+ };
489
+ const inline = (target, nullable) => {
490
+ // A reserved placeholder that turned out to be inline (scalar/alias):
491
+ // drop the placeholder; the burned name is harmless.
492
+ if (reservedId !== undefined)
493
+ delete ctx.shapes[reservedId];
494
+ return { target, nullable };
495
+ };
496
+ if (depth > MAX_SCHEMA_DEPTH)
497
+ return inline(PRELUDE.Document, false);
498
+ if (def === true || def === undefined || def === null) {
499
+ return inline(PRELUDE.Document, false);
500
+ }
501
+ if (typeof def !== "object")
502
+ return inline(PRELUDE.Document, false);
503
+ // --- $ref → named (or cached inline) shape --------------------------------
504
+ if (def.$ref) {
505
+ // Sibling nullability next to the $ref (common tool output even where
506
+ // the spec says siblings are ignored) survives onto the member.
507
+ const siteNullable = ownNullable(ctx, def);
508
+ // Direction-sensitive components (readOnly members in request position,
509
+ // writeOnly in response position) get per-direction variants; the rest
510
+ // share one shape across both directions.
511
+ const sensitive = dirSensitive(ctx, def, dir);
512
+ const cacheKey = sensitive ? `${dir}:${def.$ref}` : `*:${def.$ref}`;
513
+ const cached = ctx.refs.get(cacheKey);
514
+ if (cached)
515
+ return inline(cached.target, cached.nullable || siteNullable);
516
+ const resolved = resolvePointer(ctx.spec, def.$ref);
517
+ if (resolved === undefined)
518
+ return inline(PRELUDE.Document, siteNullable);
519
+ const refName = (String(def.$ref).split("/").pop() ?? "Shape") +
520
+ (sensitive ? (dir === "in" ? "Input" : "Output") : "");
521
+ if (!isNameable(ctx, resolved)) {
522
+ // Scalars and aliases can't cycle — convert inline and cache.
523
+ const entry = { target: PRELUDE.Document, nullable: false };
524
+ ctx.refs.set(cacheKey, entry);
525
+ const r = convertSchema(ctx, resolved, pascal(refName), depth + 1, dir);
526
+ entry.target = r.target;
527
+ entry.nullable = r.nullable;
528
+ return inline(r.target, r.nullable || siteNullable);
529
+ }
530
+ // Reserve the component name before converting so cycles resolve.
531
+ const id = `${ctx.ns}#${uniqueName(ctx, refName)}`;
532
+ const entry = { target: id, nullable: false };
533
+ ctx.refs.set(cacheKey, entry);
534
+ ctx.shapes[id] = { type: "structure", members: {} }; // placeholder
535
+ const r = convertSchema(ctx, resolved, pascal(refName), depth + 1, dir, id);
536
+ entry.target = r.target;
537
+ entry.nullable = r.nullable;
538
+ return inline(r.target, r.nullable || siteNullable);
539
+ }
540
+ let nullable = ownNullable(ctx, def);
541
+ const doc = typeof def.description === "string" ? def.description : undefined;
542
+ const docTraits = doc ? { "smithy.api#documentation": doc } : {};
543
+ // --- oneOf / anyOf --------------------------------------------------------
544
+ const branches = def.oneOf ?? def.anyOf;
545
+ if (Array.isArray(branches)) {
546
+ const real = [];
547
+ for (const b of branches) {
548
+ if (isNullBranch(ctx, b))
549
+ nullable = true;
550
+ else
551
+ real.push(b);
552
+ }
553
+ if (real.length === 0)
554
+ return inline(PRELUDE.Document, nullable);
555
+ if (real.length === 1) {
556
+ const r = convertSchema(ctx, real[0], hint, depth + 1, dir, reservedId);
557
+ return { target: r.target, nullable: nullable || r.nullable };
558
+ }
559
+ // Distinct branches → a union shape. Members are synthesized case names;
560
+ // duplicate targets are deduped.
561
+ const members = {};
562
+ const seenTargets = new Set();
563
+ real.forEach((b, i) => {
564
+ const branchName = typeof b?.$ref === "string"
565
+ ? pascal(String(b.$ref).split("/").pop() ?? `Case${i}`)
566
+ : `Case${i}`;
567
+ const r = convertSchema(ctx, b, `${hint}${branchName}`, depth + 1, dir);
568
+ if (seenTargets.has(r.target))
569
+ return;
570
+ seenTargets.add(r.target);
571
+ let mn = memberIdent(branchName);
572
+ let k = 2;
573
+ while (mn in members)
574
+ mn = `${memberIdent(branchName)}_${k++}`;
575
+ members[mn] = { target: r.target };
576
+ });
577
+ const targets = Object.values(members);
578
+ if (targets.length === 1) {
579
+ return inline(targets[0].target, nullable);
580
+ }
581
+ return {
582
+ target: emit({ type: "union", members, traits: docTraits }, hint),
583
+ nullable,
584
+ };
585
+ }
586
+ // --- allOf ----------------------------------------------------------------
587
+ if (Array.isArray(def.allOf)) {
588
+ // Single-entry allOf over a $ref: passthrough (v0 semantics), carrying
589
+ // the parent's nullability/description.
590
+ if (def.allOf.length === 1 && !def.properties) {
591
+ const r = convertSchema(ctx, def.allOf[0], hint, depth + 1, dir, reservedId);
592
+ return { target: r.target, nullable: nullable || r.nullable };
593
+ }
594
+ const flat = flattenObject(ctx, def, depth);
595
+ if (Object.keys(flat.properties).length === 0) {
596
+ // A property-less intersection of map schemas is a map, not an empty
597
+ // struct.
598
+ if (flat.additionalProperties !== undefined) {
599
+ const r = convertSchema(ctx, { type: "object", additionalProperties: flat.additionalProperties }, hint, depth + 1, dir, reservedId);
600
+ return { target: r.target, nullable: nullable || r.nullable };
601
+ }
602
+ if (!flat.isObject)
603
+ return inline(PRELUDE.Document, nullable);
604
+ }
605
+ const members = buildMembers(ctx, flat.properties, flat.required, hint, depth + 1, dir);
606
+ return {
607
+ target: emit({ type: "structure", members, traits: docTraits }, hint),
608
+ nullable,
609
+ };
610
+ }
611
+ // --- enum -----------------------------------------------------------------
612
+ if (Array.isArray(def.enum) && def.enum.length > 0) {
613
+ const values = def.enum.filter((v) => v !== null);
614
+ if (values.some((v) => v === null))
615
+ nullable = true;
616
+ if (values.length === 0)
617
+ return inline(PRELUDE.Document, true);
618
+ if (values.every((v) => typeof v === "string")) {
619
+ const members = {};
620
+ const used = new Set();
621
+ for (const lit of values) {
622
+ let mn = enumMemberName(lit);
623
+ let k = 2;
624
+ while (used.has(mn))
625
+ mn = `${enumMemberName(lit)}_${k++}`;
626
+ used.add(mn);
627
+ members[mn] = {
628
+ target: PRELUDE.Unit,
629
+ traits: { "smithy.api#enumValue": lit },
630
+ };
631
+ }
632
+ return {
633
+ target: emit({ type: "enum", members, traits: docTraits }, hint),
634
+ nullable,
635
+ };
636
+ }
637
+ // Numeric enums → intEnum shapes (closed numeric literal unions in the
638
+ // generated TS; the schema stays a plain number).
639
+ if (values.every((v) => typeof v === "number")) {
640
+ const members = {};
641
+ const used = new Set();
642
+ for (const lit of values) {
643
+ let mn = enumMemberName(String(lit));
644
+ let k = 2;
645
+ while (used.has(mn))
646
+ mn = `${enumMemberName(String(lit))}_${k++}`;
647
+ used.add(mn);
648
+ members[mn] = {
649
+ target: PRELUDE.Unit,
650
+ traits: { "smithy.api#enumValue": lit },
651
+ };
652
+ }
653
+ return {
654
+ target: emit({ type: "intEnum", members, traits: docTraits }, hint),
655
+ nullable,
656
+ };
657
+ }
658
+ if (values.every((v) => typeof v === "boolean")) {
659
+ return inline(PRELUDE.Boolean, nullable);
660
+ }
661
+ return inline(PRELUDE.Document, nullable);
662
+ }
663
+ const t = typeOf(def);
664
+ // --- array ----------------------------------------------------------------
665
+ if (t === "array") {
666
+ const item = convertSchema(ctx, def.items, `${hint}Item`, depth + 1, dir);
667
+ return {
668
+ target: emit({
669
+ type: "list",
670
+ member: {
671
+ target: item.target,
672
+ ...(item.nullable ? { traits: { [NULLABLE_TRAIT]: {} } } : {}),
673
+ },
674
+ traits: docTraits,
675
+ }, reservedId !== undefined ? hint : `${hint}List`),
676
+ nullable,
677
+ };
678
+ }
679
+ // --- object ---------------------------------------------------------------
680
+ if (t === "object" || def.properties || def.additionalProperties) {
681
+ if (def.properties && Object.keys(def.properties).length > 0) {
682
+ const required = new Set(Array.isArray(def.required) ? def.required : []);
683
+ const members = buildMembers(ctx, def.properties, required, hint, depth + 1, dir);
684
+ return {
685
+ target: emit({ type: "structure", members, traits: docTraits }, hint),
686
+ nullable,
687
+ };
688
+ }
689
+ const ap = def.additionalProperties;
690
+ if (ap !== undefined && ap !== false) {
691
+ const value = ap === true || (typeof ap === "object" && Object.keys(ap).length === 0)
692
+ ? { target: PRELUDE.Document, nullable: false }
693
+ : convertSchema(ctx, ap, `${hint}Value`, depth + 1, dir);
694
+ return {
695
+ target: emit({
696
+ type: "map",
697
+ key: { target: PRELUDE.String },
698
+ value: {
699
+ target: value.target,
700
+ ...(value.nullable ? { traits: { [NULLABLE_TRAIT]: {} } } : {}),
701
+ },
702
+ traits: docTraits,
703
+ }, reservedId !== undefined ? hint : `${hint}Map`),
704
+ nullable,
705
+ };
706
+ }
707
+ return inline(PRELUDE.Document, nullable);
708
+ }
709
+ // --- scalars --------------------------------------------------------------
710
+ switch (t) {
711
+ case "string":
712
+ return inline(PRELUDE.String, nullable);
713
+ case "boolean":
714
+ return inline(PRELUDE.Boolean, nullable);
715
+ case "integer":
716
+ return inline(PRELUDE.Integer, nullable);
717
+ case "number":
718
+ return inline(PRELUDE.Double, nullable);
719
+ case "null":
720
+ return inline(PRELUDE.Document, true);
721
+ default:
722
+ return inline(PRELUDE.Document, nullable);
723
+ }
724
+ };
725
+ /** Whether a property is a sensitive string (x-sensitive or name pattern). */
726
+ const isSensitiveProperty = (ctx, name, def) => {
727
+ const r = deref(ctx, def);
728
+ if (!r || typeof r !== "object")
729
+ return false;
730
+ const isString = typeOf(r) === "string" && !Array.isArray(r.enum);
731
+ if (!isString)
732
+ return false;
733
+ if (r["x-sensitive"] === true || def?.["x-sensitive"] === true)
734
+ return true;
735
+ return ctx.sensitivePatterns.some((p) => p.test(name));
736
+ };
737
+ /**
738
+ * Build a structure's members from an OpenAPI property map. Direction-
739
+ * excluded properties (`readOnly` in requests, `writeOnly` in responses)
740
+ * are dropped — along with any response-side `required` they carried.
741
+ */
742
+ const buildMembers = (ctx, properties, required, hint, depth, dir) => {
743
+ const members = {};
744
+ for (const [name, prop] of Object.entries(properties)) {
745
+ if (dirExcluded(ctx, prop, dir))
746
+ continue;
747
+ let mn = memberIdent(name);
748
+ let k = 2;
749
+ while (mn in members)
750
+ mn = `${memberIdent(name)}_${k++}`;
751
+ const conv = convertSchema(ctx, prop, `${hint}${pascal(name)}`, depth, dir);
752
+ const traits = {};
753
+ const doc = prop && typeof prop === "object" && typeof prop.description === "string"
754
+ ? prop.description
755
+ : undefined;
756
+ if (doc)
757
+ traits["smithy.api#documentation"] = doc;
758
+ if (required.has(name))
759
+ traits["smithy.api#required"] = {};
760
+ if (mn !== name)
761
+ traits["smithy.api#jsonName"] = name;
762
+ if (conv.nullable)
763
+ traits[NULLABLE_TRAIT] = {};
764
+ if (isSensitiveProperty(ctx, name, prop)) {
765
+ traits["smithy.api#sensitive"] = {};
766
+ }
767
+ members[mn] = {
768
+ target: conv.target,
769
+ ...(Object.keys(traits).length ? { traits } : {}),
770
+ };
771
+ }
772
+ return members;
773
+ };
774
+ /** Normalize a (possibly `$ref`'d) parameter; Swagger 2.0 carries the schema inline. */
775
+ const normalizeParam = (ctx, raw) => {
776
+ const p = raw?.$ref ? resolvePointer(ctx.spec, raw.$ref) : raw;
777
+ if (!p || typeof p !== "object" || typeof p.name !== "string") {
778
+ return undefined;
779
+ }
780
+ // Swagger 2.0: `in: body` parameters carry their (usually `$ref`'d)
781
+ // schema under `schema`; all other locations describe the type inline on
782
+ // the parameter itself.
783
+ const schema = ctx.version === "2.0"
784
+ ? p.in === "body"
785
+ ? (p.schema ?? {})
786
+ : {
787
+ type: p.type,
788
+ enum: p.enum,
789
+ items: p.items,
790
+ format: p.format,
791
+ "x-nullable": p["x-nullable"],
792
+ }
793
+ : (p.schema ?? { type: "string" });
794
+ return {
795
+ in: p.in,
796
+ name: p.name,
797
+ required: p.required === true || p.in === "path",
798
+ description: typeof p.description === "string" ? p.description : undefined,
799
+ schema,
800
+ };
801
+ };
802
+ /**
803
+ * Path-level parameters merged before operation-level ones; an op-level
804
+ * parameter overrides a path-level one with the same (in, name). Header and
805
+ * cookie parameters are dropped (v0 parity); `in: body` (2.0) is handled by
806
+ * the request-body path.
807
+ */
808
+ const collectParams = (ctx, pathItem, op) => {
809
+ const byKey = new Map();
810
+ for (const raw of [
811
+ ...(Array.isArray(pathItem?.parameters) ? pathItem.parameters : []),
812
+ ...(Array.isArray(op?.parameters) ? op.parameters : []),
813
+ ]) {
814
+ const p = normalizeParam(ctx, raw);
815
+ if (!p)
816
+ continue;
817
+ byKey.set(`${p.in}${p.name}`, p);
818
+ }
819
+ return [...byKey.values()].filter((p) => p.in === "path" ||
820
+ p.in === "query" ||
821
+ p.in === "body" ||
822
+ p.in === "header");
823
+ };
824
+ /**
825
+ * Simple-type coercion for path labels (Smithy restricts label targets):
826
+ * enum → enum shape, integer → Integer, number → Double, boolean → Boolean,
827
+ * everything else → String.
828
+ */
829
+ const labelTarget = (ctx, schema, hint) => {
830
+ const r = deref(ctx, schema);
831
+ if (Array.isArray(r?.enum) &&
832
+ r.enum.length > 0 &&
833
+ r.enum.every((v) => typeof v === "string")) {
834
+ return convertSchema(ctx, r, hint, 0, "in").target;
835
+ }
836
+ switch (typeOf(r)) {
837
+ case "integer":
838
+ return PRELUDE.Integer;
839
+ case "number":
840
+ return PRELUDE.Double;
841
+ case "boolean":
842
+ return PRELUDE.Boolean;
843
+ default:
844
+ return PRELUDE.String;
845
+ }
846
+ };
847
+ // ============================================================================
848
+ // Docs
849
+ // ============================================================================
850
+ /**
851
+ * v0 trimming rules: stop at a `### Authorization` heading, strip markdown
852
+ * table rows (lines starting with `|`).
853
+ */
854
+ const trimDescription = (desc) => {
855
+ if (typeof desc !== "string" || !desc)
856
+ return undefined;
857
+ const lines = [];
858
+ for (const line of desc.split(/\r?\n/)) {
859
+ if (line.trim().startsWith("### Authorization"))
860
+ break;
861
+ if (line.trim().startsWith("|"))
862
+ continue;
863
+ lines.push(line);
864
+ }
865
+ const out = lines.join("\n").trim();
866
+ return out || undefined;
867
+ };
868
+ const opDoc = (op) => {
869
+ const summary = typeof op.summary === "string" ? op.summary.trim() : undefined;
870
+ const desc = trimDescription(op.description);
871
+ const parts = [summary, desc].filter((s) => s !== undefined && s !== "");
872
+ return parts.length ? parts.join("\n\n") : undefined;
873
+ };
874
+ // ============================================================================
875
+ // Responses
876
+ // ============================================================================
877
+ /** First declared status in `order` wins; response-level `$ref` resolved; JSON only. */
878
+ const successSchema = (ctx, responses, order) => {
879
+ for (const code of order) {
880
+ const raw = responses?.[code];
881
+ if (!raw)
882
+ continue;
883
+ const resp = raw.$ref ? resolvePointer(ctx.spec, raw.$ref) : raw;
884
+ if (!resp || typeof resp !== "object")
885
+ return { schema: undefined };
886
+ if (ctx.version === "2.0") {
887
+ return { schema: resp.schema };
888
+ }
889
+ return { schema: resp.content?.["application/json"]?.schema };
890
+ }
891
+ return { schema: undefined };
892
+ };
893
+ /**
894
+ * Whether the success body is a collection: an array, or an object whose
895
+ * array members outnumber its scalar ones (envelopes like `{ data: [] }`,
896
+ * `{ items: [], total }`). `undefined` when there is no body to judge.
897
+ */
898
+ const responseIsCollection = (ctx, responses, order) => {
899
+ const { schema } = successSchema(ctx, responses, order);
900
+ if (!schema)
901
+ return undefined;
902
+ const s = deref(ctx, schema);
903
+ if (!s || typeof s !== "object")
904
+ return undefined;
905
+ if (s.type === "array" || s.items)
906
+ return true;
907
+ const props = s.properties;
908
+ if (!props || typeof props !== "object") {
909
+ return s.allOf || s.oneOf || s.anyOf ? undefined : false;
910
+ }
911
+ let arrays = 0;
912
+ let others = 0;
913
+ for (const prop of Object.values(props)) {
914
+ const p = deref(ctx, prop);
915
+ if (p?.type === "array" || p?.items)
916
+ arrays++;
917
+ else
918
+ others++;
919
+ }
920
+ if (arrays === 0)
921
+ return false;
922
+ // `{ data: [...] }`, `{ items, next_cursor }`, `{ results, count, page }`
923
+ return arrays >= 1 && others <= 3;
924
+ };
925
+ const PAGINATION_INPUT_ALIASES = {
926
+ cursor: ["cursor", "page_token", "pageToken"],
927
+ token: ["next_token", "NextToken", "nextToken"],
928
+ page: ["page"],
929
+ };
930
+ const detectPagination = (ctx, params, responseSchema) => {
931
+ if (!responseSchema)
932
+ return undefined;
933
+ const bag = flattenObject(ctx, responseSchema).properties;
934
+ if (Object.keys(bag).length === 0)
935
+ return undefined;
936
+ let mode;
937
+ let outputToken;
938
+ const pag = bag["pagination"]
939
+ ? flattenObject(ctx, bag["pagination"]).properties
940
+ : undefined;
941
+ if (pag?.["cursor"]) {
942
+ mode = "cursor";
943
+ outputToken = "pagination.cursor";
944
+ }
945
+ else if (pag?.["next"]) {
946
+ mode = "cursor";
947
+ outputToken = "pagination.next";
948
+ }
949
+ else if (pag?.["next_page"]) {
950
+ mode = "page";
951
+ outputToken = "pagination.next_page";
952
+ }
953
+ else if (bag["next_token"]) {
954
+ mode = "token";
955
+ outputToken = "next_token";
956
+ }
957
+ else if (bag["NextToken"]) {
958
+ mode = "token";
959
+ outputToken = "NextToken";
960
+ }
961
+ else if (bag["nextToken"]) {
962
+ mode = "token";
963
+ outputToken = "nextToken";
964
+ }
965
+ else if (bag["next_page"]) {
966
+ mode = "page";
967
+ outputToken = "next_page";
968
+ }
969
+ if (!mode || !outputToken)
970
+ return undefined;
971
+ const aliases = PAGINATION_INPUT_ALIASES[mode];
972
+ const inputToken = params.find((p) => p.in === "query" && aliases.includes(p.name))?.name;
973
+ if (!inputToken)
974
+ return undefined;
975
+ let items;
976
+ for (const [k, v] of Object.entries(bag)) {
977
+ if (k === "pagination" || k === "next_token" || k === "NextToken")
978
+ continue;
979
+ if (typeOf(deref(ctx, v)) === "array") {
980
+ items = k;
981
+ break;
982
+ }
983
+ }
984
+ if (!items)
985
+ return undefined;
986
+ return { mode, inputToken, outputToken, items };
987
+ };
988
+ // ============================================================================
989
+ // Main conversion
990
+ // ============================================================================
991
+ const HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
992
+ /**
993
+ * Methods a spec may declare beyond {@link HTTP_METHODS}, opt-in via
994
+ * {@link OpenApiConvertOptions.extraHttpMethods} — a provider that models
995
+ * `head` (Vercel's cache-artifact and file existence probes) asks for it
996
+ * rather than every provider silently gaining operations on regeneration.
997
+ */
998
+ const OPTIONAL_HTTP_METHODS = ["head", "options"];
999
+ const DEFAULT_STATUS_TO_ERROR_CLASS = {
1000
+ "400": "BadRequest",
1001
+ "403": "Forbidden",
1002
+ "404": "NotFound",
1003
+ "409": "Conflict",
1004
+ "422": "UnprocessableEntity",
1005
+ };
1006
+ const DEFAULT_ERROR_STATUSES = ["401", "429", "500", "503"];
1007
+ const DEFAULT_SUCCESS_STATUSES = ["200", "201", "204"];
1008
+ export const convertOpenApiToSmithy = (spec, options) => {
1009
+ const doc = spec;
1010
+ const version = detectVersion(doc);
1011
+ const ctx = {
1012
+ spec: doc,
1013
+ version,
1014
+ ns: options.namespace,
1015
+ shapes: {},
1016
+ names: new Set(),
1017
+ refs: new Map(),
1018
+ dirSensitiveRefs: new Map(),
1019
+ sensitivePatterns: options.sensitivePatterns ?? SENSITIVE_FIELD_PATTERNS,
1020
+ };
1021
+ const statusToErrorClass = options.statusToErrorClass ?? DEFAULT_STATUS_TO_ERROR_CLASS;
1022
+ const defaultErrorStatuses = new Set(options.defaultErrorStatuses ?? DEFAULT_ERROR_STATUSES);
1023
+ const skipDeprecated = options.skipDeprecated ?? true;
1024
+ const successStatuses = options.successStatuses ?? DEFAULT_SUCCESS_STATUSES;
1025
+ const httpMethods = [
1026
+ ...HTTP_METHODS,
1027
+ ...OPTIONAL_HTTP_METHODS.filter((m) => options.extraHttpMethods?.includes(m)),
1028
+ ];
1029
+ // Error class names and the service name are reserved up front so schema
1030
+ // components can never steal them.
1031
+ const errorIds = new Map(); // class name → shape id
1032
+ for (const cls of new Set(Object.values(statusToErrorClass))) {
1033
+ errorIds.set(cls, `${ctx.ns}#${uniqueName(ctx, cls)}`);
1034
+ }
1035
+ const serviceName = uniqueName(ctx, options.serviceName);
1036
+ const usedErrors = new Map(); // class name → first status
1037
+ const serviceOps = [];
1038
+ for (const [rawPath, pathItem] of Object.entries(doc.paths ?? {})) {
1039
+ if (!pathItem || typeof pathItem !== "object")
1040
+ continue;
1041
+ for (const method of httpMethods) {
1042
+ const op = pathItem[method];
1043
+ if (!op || typeof op !== "object")
1044
+ continue;
1045
+ if (skipDeprecated && op.deprecated === true)
1046
+ continue;
1047
+ const rawOperationId = typeof op.operationId === "string" && op.operationId
1048
+ ? op.operationId
1049
+ : `${method}_${rawPath}`;
1050
+ const idCtx = { path: rawPath, method };
1051
+ const naming = options.operationNaming ?? "verbNoun";
1052
+ let named = options.operationNames !== undefined
1053
+ ? resolveOperationName(options.operationNames, rawOperationId, idCtx)
1054
+ : undefined;
1055
+ if (named === undefined && naming === "verbNoun") {
1056
+ // A mechanical id (`get-api-card`, or none at all) is named from
1057
+ // the route. A hand-chosen one that merely starts with the method
1058
+ // and reads the route in order (`get-feeds` on /feeds) keeps its
1059
+ // own nouns; only the HTTP-method verb is normalised.
1060
+ if (typeof op.operationId !== "string" || !op.operationId) {
1061
+ // No id: everything comes from the route, including whether a
1062
+ // GET on a collection route is `list` or `get`.
1063
+ named = pathToVerbNoun(idCtx, {
1064
+ returnsCollection: responseIsCollection(ctx, op.responses, successStatuses),
1065
+ });
1066
+ }
1067
+ else if (isMechanicalOperationId(op.operationId, idCtx)) {
1068
+ // Method-prefixed and reading the route (`get-feeds`,
1069
+ // `postV1AppsByAppIdPromote`, `deleteProjectJWKS`): keep the
1070
+ // author's tokens and casing; normalise `post`/`patch`, drop
1071
+ // parameter clauses (`ByAppId`, `ById`) and api/version roots.
1072
+ named = pathToVerbNoun(idCtx, {
1073
+ nouns: op.operationId,
1074
+ verbatim: isVerbatimRouteId(op.operationId, idCtx),
1075
+ });
1076
+ }
1077
+ else {
1078
+ named = toVerbNoun(rawOperationId);
1079
+ }
1080
+ }
1081
+ let resolved = named ?? rawOperationId;
1082
+ // Two routes can derive the same name (`/collections/{slug}` and
1083
+ // `/collections/{slug}-{id}`); suffix the trailing parameter rather
1084
+ // than a counter.
1085
+ if (naming === "verbNoun" &&
1086
+ ctx.names.has(pascal(resolved)) &&
1087
+ isMechanicalOperationId(typeof op.operationId === "string" ? op.operationId : undefined, idCtx)) {
1088
+ const lastParam = /\{([^}]+)\}[^/]*$/.exec(rawPath)?.[1];
1089
+ if (lastParam)
1090
+ resolved = `${resolved}By${pascal(lastParam)}`;
1091
+ }
1092
+ const opName = pascal(resolved);
1093
+ const params = collectParams(ctx, pathItem, op);
1094
+ // ---- URI + labels (sanitize placeholder names to member idents) ----
1095
+ let uri = rawPath.split(/[?#]/)[0];
1096
+ const rawLabels = Array.from(uri.matchAll(/\{([^}]+)\}/g)).map((m) => m[1]);
1097
+ const members = {};
1098
+ const addMember = (name, member) => {
1099
+ if (name in members)
1100
+ return false;
1101
+ members[name] = member;
1102
+ return true;
1103
+ };
1104
+ for (const raw of rawLabels) {
1105
+ const san = memberIdent(raw);
1106
+ if (san !== raw)
1107
+ uri = uri.split(`{${raw}}`).join(`{${san}}`);
1108
+ const p = params.find((x) => x.in === "path" && x.name === raw);
1109
+ addMember(san, {
1110
+ target: labelTarget(ctx, p?.schema, `${opName}Request${pascal(san)}`),
1111
+ traits: {
1112
+ "smithy.api#httpLabel": {},
1113
+ "smithy.api#required": {},
1114
+ ...(p?.description
1115
+ ? { "smithy.api#documentation": p.description }
1116
+ : {}),
1117
+ },
1118
+ });
1119
+ }
1120
+ // ---- Query params ----
1121
+ for (const p of params) {
1122
+ if (p.in !== "query")
1123
+ continue;
1124
+ if (options.apiVersion !== undefined && p.name === "api-version") {
1125
+ continue;
1126
+ }
1127
+ const san = memberIdent(p.name);
1128
+ const conv = convertSchema(ctx, p.schema, `${opName}Request${pascal(p.name)}`, 0, "in");
1129
+ addMember(san, {
1130
+ target: conv.target,
1131
+ traits: {
1132
+ "smithy.api#httpQuery": p.name,
1133
+ ...(p.required ? { "smithy.api#required": {} } : {}),
1134
+ ...(p.description
1135
+ ? { "smithy.api#documentation": p.description }
1136
+ : {}),
1137
+ },
1138
+ });
1139
+ }
1140
+ // ---- Header params (opt-in) ----
1141
+ if (options.headerParams) {
1142
+ for (const p of params) {
1143
+ if (p.in !== "header")
1144
+ continue;
1145
+ const conv = convertSchema(ctx, p.schema, `${opName}Request${pascal(p.name)}`, 0, "in");
1146
+ addMember(headerMemberName(p.name), {
1147
+ target: conv.target,
1148
+ traits: {
1149
+ // The wire name rides on the trait, so the member is free to be
1150
+ // a normal identifier.
1151
+ "smithy.api#httpHeader": p.name,
1152
+ ...(p.required ? { "smithy.api#required": {} } : {}),
1153
+ ...(p.description
1154
+ ? { "smithy.api#documentation": p.description }
1155
+ : {}),
1156
+ },
1157
+ });
1158
+ }
1159
+ }
1160
+ // ---- Request body (json > form-urlencoded > multipart) ----
1161
+ let contentType;
1162
+ let bodySchema;
1163
+ let bodyRequired = false;
1164
+ if (version === "2.0") {
1165
+ const bodyParam = params.find((p) => p.in === "body");
1166
+ bodySchema = bodyParam?.schema;
1167
+ bodyRequired = bodyParam?.required === true;
1168
+ }
1169
+ else if (op.requestBody) {
1170
+ const rb = op.requestBody.$ref
1171
+ ? resolvePointer(doc, op.requestBody.$ref)
1172
+ : op.requestBody;
1173
+ bodyRequired = rb?.required === true;
1174
+ const content = rb?.content ?? {};
1175
+ if (content["application/json"]) {
1176
+ bodySchema = content["application/json"].schema;
1177
+ }
1178
+ else if (content["application/x-www-form-urlencoded"]) {
1179
+ bodySchema = content["application/x-www-form-urlencoded"].schema;
1180
+ contentType = "form-urlencoded";
1181
+ }
1182
+ else if (content["multipart/form-data"]) {
1183
+ bodySchema = content["multipart/form-data"].schema;
1184
+ contentType = "multipart";
1185
+ }
1186
+ }
1187
+ if (bodySchema !== undefined) {
1188
+ const flat = flattenObject(ctx, bodySchema);
1189
+ if (Object.keys(flat.properties).length > 0) {
1190
+ const bodyMembers = buildMembers(ctx, flat.properties, flat.required, `${opName}Request`, 0, "in");
1191
+ for (const [mn, m] of Object.entries(bodyMembers)) {
1192
+ addMember(mn, m); // labels win, then query, then body
1193
+ }
1194
+ }
1195
+ else {
1196
+ // Non-flattenable body (bare array/scalar/map/union) → sole TYPED
1197
+ // `body` member sent as the entire request body
1198
+ // (`smithy.api#httpPayload`). A schema-less JSON body (empty or
1199
+ // bare-object schema → Document) gets NO body member at all — the
1200
+ // runtime's unknown-key passthrough is the escape hatch, and an
1201
+ // opaque `body: unknown` payload member would swallow the typed
1202
+ // surface.
1203
+ const conv = convertSchema(ctx, bodySchema, `${opName}RequestBody`, 0, "in");
1204
+ if (conv.target !== PRELUDE.Document) {
1205
+ addMember("body", {
1206
+ target: conv.target,
1207
+ traits: {
1208
+ "smithy.api#httpPayload": {},
1209
+ ...(bodyRequired ? { "smithy.api#required": {} } : {}),
1210
+ ...(conv.nullable ? { [NULLABLE_TRAIT]: {} } : {}),
1211
+ },
1212
+ });
1213
+ }
1214
+ }
1215
+ }
1216
+ // ---- Input shape ----
1217
+ const inputTarget = Object.keys(members).length > 0
1218
+ ? addShape(ctx, `${opName}Request`, {
1219
+ type: "structure",
1220
+ members,
1221
+ traits: { "smithy.api#input": {} },
1222
+ })
1223
+ : PRELUDE.Unit;
1224
+ // ---- Output shape ----
1225
+ // A HEAD response carries no content, and not merely by convention:
1226
+ // HTTP/1.1 framing terminates it at the end of the header section
1227
+ // "regardless of the header fields present in the message" (RFC 9112
1228
+ // §6.3), so a declared `Content-Length` describes what a GET *would*
1229
+ // return and no body can reach the client. Specs declare one anyway —
1230
+ // Vercel mirrors the GET's schema onto two of its HEAD probes and
1231
+ // gives the other two a bare `{"nullable": true}` — and honouring it
1232
+ // generates an output struct with required members that then fails to
1233
+ // decode against the empty body on every successful call.
1234
+ const { schema: respSchema } = method === "head"
1235
+ ? { schema: undefined }
1236
+ : successSchema(ctx, op.responses, successStatuses);
1237
+ let outputTarget = PRELUDE.Unit;
1238
+ if (respSchema !== undefined) {
1239
+ const flat = flattenObject(ctx, respSchema);
1240
+ if (Object.keys(flat.properties).length > 0) {
1241
+ const resolved = deref(ctx, respSchema);
1242
+ const isPlainRef = typeof respSchema.$ref === "string" &&
1243
+ !Array.isArray(resolved?.allOf) &&
1244
+ isNameable(ctx, resolved);
1245
+ if (isPlainRef) {
1246
+ // Reuse the named component shape as the output directly.
1247
+ outputTarget = convertSchema(ctx, respSchema, opName, 0, "out").target;
1248
+ }
1249
+ else {
1250
+ outputTarget = addShape(ctx, `${opName}Response`, {
1251
+ type: "structure",
1252
+ members: buildMembers(ctx, flat.properties, flat.required, `${opName}Response`, 0, "out"),
1253
+ traits: { "smithy.api#output": {} },
1254
+ });
1255
+ }
1256
+ }
1257
+ else {
1258
+ // Non-flattenable response (bare array/scalar/map/union, or an
1259
+ // opaque object) → wrapper whose sole TYPED member IS the payload;
1260
+ // the SdkSpec's rootPipe collapses the wrapper.
1261
+ const conv = convertSchema(ctx, respSchema, `${opName}ResponseBody`, 0, "out");
1262
+ if (conv.target !== PRELUDE.Document || deref(ctx, respSchema)) {
1263
+ outputTarget = addShape(ctx, `${opName}Response`, {
1264
+ type: "structure",
1265
+ members: {
1266
+ body: {
1267
+ target: conv.target,
1268
+ traits: {
1269
+ [RAW_RESPONSE_TRAIT]: {},
1270
+ "smithy.api#required": {},
1271
+ ...(conv.nullable ? { [NULLABLE_TRAIT]: {} } : {}),
1272
+ },
1273
+ },
1274
+ },
1275
+ traits: { "smithy.api#output": {} },
1276
+ });
1277
+ }
1278
+ }
1279
+ }
1280
+ // ---- Errors ----
1281
+ const errors = [];
1282
+ for (const status of Object.keys(op.responses ?? {})) {
1283
+ if (!/^[45]\d\d$/.test(status))
1284
+ continue;
1285
+ if (defaultErrorStatuses.has(status))
1286
+ continue;
1287
+ const cls = statusToErrorClass[status];
1288
+ if (!cls)
1289
+ continue;
1290
+ const id = errorIds.get(cls);
1291
+ if (!usedErrors.has(cls))
1292
+ usedErrors.set(cls, Number(status));
1293
+ if (!errors.some((e) => e.target === id))
1294
+ errors.push({ target: id });
1295
+ }
1296
+ // ---- Pagination ----
1297
+ const pagination = detectPagination(ctx, params, respSchema);
1298
+ // ---- Operation shape ----
1299
+ const httpTrait = {
1300
+ method: method.toUpperCase(),
1301
+ uri,
1302
+ code: 200,
1303
+ };
1304
+ if (contentType === "multipart")
1305
+ httpTrait.contentType = "multipart";
1306
+ const traits = { "smithy.api#http": httpTrait };
1307
+ const documentation = opDoc(op);
1308
+ if (documentation)
1309
+ traits["smithy.api#documentation"] = documentation;
1310
+ if (pagination)
1311
+ traits["smithy.api#paginated"] = pagination;
1312
+ if (contentType)
1313
+ traits[CONTENT_TYPE_TRAIT] = contentType;
1314
+ if (options.apiVersion !== undefined) {
1315
+ traits[API_VERSION_TRAIT] = options.apiVersion;
1316
+ }
1317
+ const opId = addShape(ctx, opName, {
1318
+ type: "operation",
1319
+ input: { target: inputTarget },
1320
+ output: { target: outputTarget },
1321
+ ...(errors.length ? { errors } : {}),
1322
+ traits,
1323
+ });
1324
+ serviceOps.push({ target: opId });
1325
+ }
1326
+ }
1327
+ // ---- Error shapes (one per used class) ----
1328
+ for (const [cls, status] of usedErrors) {
1329
+ const id = errorIds.get(cls);
1330
+ const base = {
1331
+ type: "structure",
1332
+ members: {},
1333
+ traits: {
1334
+ "smithy.api#error": status < 500 ? "client" : "server",
1335
+ "smithy.api#httpError": status,
1336
+ [ERROR_MATCHERS_TRAIT]: [{ status }],
1337
+ },
1338
+ };
1339
+ const override = options.errorShapes?.[cls];
1340
+ ctx.shapes[id] = override
1341
+ ? {
1342
+ ...base,
1343
+ ...override,
1344
+ traits: { ...base.traits, ...(override.traits ?? {}) },
1345
+ }
1346
+ : base;
1347
+ }
1348
+ // ---- Service shape ----
1349
+ ctx.shapes[`${ctx.ns}#${serviceName}`] = {
1350
+ type: "service",
1351
+ version: options.serviceVersion ??
1352
+ (typeof doc.info?.version === "string" ? doc.info.version : "1.0"),
1353
+ operations: serviceOps,
1354
+ traits: {
1355
+ "smithy.api#title": typeof doc.info?.title === "string"
1356
+ ? doc.info.title
1357
+ : options.serviceName,
1358
+ ...(typeof doc.info?.description === "string"
1359
+ ? {
1360
+ "smithy.api#documentation": trimDescription(doc.info.description),
1361
+ }
1362
+ : {}),
1363
+ },
1364
+ };
1365
+ return {
1366
+ smithy: "2.0",
1367
+ metadata: {
1368
+ suppressions: [
1369
+ { id: "HttpUriConflict", namespace: "*" },
1370
+ { id: "HttpMethodSemantics", namespace: "*" },
1371
+ { id: "UnreferencedShape", namespace: "*" },
1372
+ ],
1373
+ },
1374
+ shapes: ctx.shapes,
1375
+ };
1376
+ };
1377
+ //# sourceMappingURL=openapi.js.map