@effect/platform 0.72.1 → 0.73.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +2159 -356
  2. package/Url/package.json +6 -0
  3. package/dist/cjs/HttpApi.js +22 -18
  4. package/dist/cjs/HttpApi.js.map +1 -1
  5. package/dist/cjs/HttpApiEndpoint.js.map +1 -1
  6. package/dist/cjs/HttpApiGroup.js.map +1 -1
  7. package/dist/cjs/HttpApiSchema.js +33 -4
  8. package/dist/cjs/HttpApiSchema.js.map +1 -1
  9. package/dist/cjs/HttpApiSecurity.js +2 -0
  10. package/dist/cjs/HttpApiSecurity.js.map +1 -1
  11. package/dist/cjs/OpenApi.js +132 -142
  12. package/dist/cjs/OpenApi.js.map +1 -1
  13. package/dist/cjs/OpenApiJsonSchema.js +7 -4
  14. package/dist/cjs/OpenApiJsonSchema.js.map +1 -1
  15. package/dist/cjs/Runtime.js.map +1 -1
  16. package/dist/cjs/Url.js +259 -0
  17. package/dist/cjs/Url.js.map +1 -0
  18. package/dist/cjs/index.js +3 -1
  19. package/dist/dts/HttpApi.d.ts +4 -2
  20. package/dist/dts/HttpApi.d.ts.map +1 -1
  21. package/dist/dts/HttpApiBuilder.d.ts +1 -1
  22. package/dist/dts/HttpApiBuilder.d.ts.map +1 -1
  23. package/dist/dts/HttpApiEndpoint.d.ts +16 -8
  24. package/dist/dts/HttpApiEndpoint.d.ts.map +1 -1
  25. package/dist/dts/HttpApiGroup.d.ts +1 -2
  26. package/dist/dts/HttpApiGroup.d.ts.map +1 -1
  27. package/dist/dts/HttpApiSchema.d.ts.map +1 -1
  28. package/dist/dts/HttpApiSecurity.d.ts +2 -0
  29. package/dist/dts/HttpApiSecurity.d.ts.map +1 -1
  30. package/dist/dts/OpenApi.d.ts +102 -111
  31. package/dist/dts/OpenApi.d.ts.map +1 -1
  32. package/dist/dts/OpenApiJsonSchema.d.ts.map +1 -1
  33. package/dist/dts/Runtime.d.ts +48 -0
  34. package/dist/dts/Runtime.d.ts.map +1 -1
  35. package/dist/dts/Url.d.ts +591 -0
  36. package/dist/dts/Url.d.ts.map +1 -0
  37. package/dist/dts/index.d.ts +4 -0
  38. package/dist/dts/index.d.ts.map +1 -1
  39. package/dist/esm/HttpApi.js +22 -18
  40. package/dist/esm/HttpApi.js.map +1 -1
  41. package/dist/esm/HttpApiEndpoint.js.map +1 -1
  42. package/dist/esm/HttpApiGroup.js.map +1 -1
  43. package/dist/esm/HttpApiSchema.js +30 -3
  44. package/dist/esm/HttpApiSchema.js.map +1 -1
  45. package/dist/esm/HttpApiSecurity.js +2 -0
  46. package/dist/esm/HttpApiSecurity.js.map +1 -1
  47. package/dist/esm/OpenApi.js +132 -141
  48. package/dist/esm/OpenApi.js.map +1 -1
  49. package/dist/esm/OpenApiJsonSchema.js +4 -2
  50. package/dist/esm/OpenApiJsonSchema.js.map +1 -1
  51. package/dist/esm/Runtime.js.map +1 -1
  52. package/dist/esm/Url.js +248 -0
  53. package/dist/esm/Url.js.map +1 -0
  54. package/dist/esm/index.js +4 -0
  55. package/dist/esm/index.js.map +1 -1
  56. package/package.json +10 -2
  57. package/src/HttpApi.ts +25 -26
  58. package/src/HttpApiBuilder.ts +1 -1
  59. package/src/HttpApiEndpoint.ts +22 -13
  60. package/src/HttpApiGroup.ts +2 -3
  61. package/src/HttpApiSchema.ts +33 -8
  62. package/src/HttpApiSecurity.ts +2 -0
  63. package/src/OpenApi.ts +244 -272
  64. package/src/OpenApiJsonSchema.ts +9 -1
  65. package/src/Runtime.ts +48 -0
  66. package/src/Url.ts +632 -0
  67. package/src/index.ts +5 -0
package/src/OpenApi.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
+ import type { NonEmptyArray } from "effect/Array"
4
5
  import * as Context from "effect/Context"
5
6
  import { constFalse } from "effect/Function"
6
7
  import { globalValue } from "effect/GlobalValue"
7
8
  import * as Option from "effect/Option"
8
- import type { ReadonlyRecord } from "effect/Record"
9
- import * as Schema from "effect/Schema"
10
- import type { DeepMutable, Mutable } from "effect/Types"
9
+ import type * as Schema from "effect/Schema"
10
+ import type * as AST from "effect/SchemaAST"
11
11
  import * as HttpApi from "./HttpApi.js"
12
+ import type { HttpApiGroup } from "./HttpApiGroup.js"
12
13
  import * as HttpApiMiddleware from "./HttpApiMiddleware.js"
13
14
  import * as HttpApiSchema from "./HttpApiSchema.js"
14
15
  import type { HttpApiSecurity } from "./HttpApiSecurity.js"
@@ -132,6 +133,7 @@ export const annotations: (
132
133
  readonly description?: string | undefined
133
134
  readonly license?: OpenAPISpecLicense | undefined
134
135
  readonly summary?: string | undefined
136
+ readonly deprecated?: boolean | undefined
135
137
  readonly externalDocs?: OpenAPISpecExternalDocs | undefined
136
138
  readonly servers?: ReadonlyArray<OpenAPISpecServer> | undefined
137
139
  readonly format?: string | undefined
@@ -146,6 +148,7 @@ export const annotations: (
146
148
  description: Description,
147
149
  license: License,
148
150
  summary: Summary,
151
+ deprecated: Deprecated,
149
152
  externalDocs: ExternalDocs,
150
153
  servers: Servers,
151
154
  format: Format,
@@ -157,100 +160,152 @@ export const annotations: (
157
160
  const apiCache = globalValue("@effect/platform/OpenApi/apiCache", () => new WeakMap<HttpApi.HttpApi.Any, OpenAPISpec>())
158
161
 
159
162
  /**
163
+ * This function checks if a given tag exists within the provided context. If
164
+ * the tag is present, it retrieves the associated value and applies the given
165
+ * callback function to it. If the tag is not found, the function does nothing.
166
+ */
167
+ function processAnnotation<Services, S, I>(
168
+ ctx: Context.Context<Services>,
169
+ tag: Context.Tag<I, S>,
170
+ f: (s: S) => void
171
+ ) {
172
+ const o = Context.getOption(ctx, tag)
173
+ if (Option.isSome(o)) {
174
+ f(o.value)
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Converts an `HttpApi` instance into an OpenAPI Specification object.
180
+ *
181
+ * **Details**
182
+ *
183
+ * This function takes an `HttpApi` instance, which defines a structured API,
184
+ * and generates an OpenAPI Specification (`OpenAPISpec`). The resulting spec
185
+ * adheres to the OpenAPI 3.1.0 standard and includes detailed metadata such as
186
+ * paths, operations, security schemes, and components. The function processes
187
+ * the API's annotations, middleware, groups, and endpoints to build a complete
188
+ * and accurate representation of the API in OpenAPI format.
189
+ *
190
+ * The function also deduplicates schemas, applies transformations, and
191
+ * integrates annotations like descriptions, summaries, external documentation,
192
+ * and overrides. Cached results are used for better performance when the same
193
+ * `HttpApi` instance is processed multiple times.
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "@effect/platform"
198
+ * import { Schema } from "effect"
199
+ *
200
+ * const api = HttpApi.make("api").add(
201
+ * HttpApiGroup.make("group").add(
202
+ * HttpApiEndpoint.get("get", "/items")
203
+ * .addSuccess(Schema.Array(Schema.String))
204
+ * )
205
+ * )
206
+ *
207
+ * const spec = OpenApi.fromApi(api)
208
+ *
209
+ * // console.log(JSON.stringify(spec, null, 2))
210
+ * // Output: OpenAPI specification in JSON format
211
+ * ```
212
+ *
160
213
  * @category constructors
161
214
  * @since 1.0.0
162
215
  */
163
- export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec => {
164
- if (apiCache.has(self)) {
165
- return apiCache.get(self)!
216
+ export const fromApi = <Id extends string, Groups extends HttpApiGroup.Any, E, R>(
217
+ api: HttpApi.HttpApi<Id, Groups, E, R>
218
+ ): OpenAPISpec => {
219
+ const cached = apiCache.get(api)
220
+ if (cached !== undefined) {
221
+ return cached
166
222
  }
167
- const api = self as unknown as HttpApi.HttpApi.AnyWithProps
168
223
  const jsonSchemaDefs: Record<string, JsonSchema.JsonSchema> = {}
169
- let spec: DeepMutable<OpenAPISpec> = {
224
+ let spec: OpenAPISpec = {
170
225
  openapi: "3.1.0",
171
226
  info: {
172
227
  title: Context.getOrElse(api.annotations, Title, () => "Api"),
173
228
  version: Context.getOrElse(api.annotations, Version, () => "0.0.1")
174
229
  },
175
230
  paths: {},
176
- tags: [],
177
231
  components: {
178
232
  schemas: jsonSchemaDefs,
179
233
  securitySchemes: {}
180
234
  },
181
- security: []
235
+ security: [],
236
+ tags: []
182
237
  }
183
- function makeJsonSchemaOrRef(schema: Schema.Schema.All): JsonSchema.JsonSchema {
184
- return JsonSchema.makeWithDefs(schema as any, {
238
+
239
+ function processAST(ast: AST.AST): JsonSchema.JsonSchema {
240
+ return JsonSchema.fromAST(ast, {
185
241
  defs: jsonSchemaDefs
186
242
  })
187
243
  }
188
- function registerSecurity(
244
+
245
+ function processHttpApiSecurity(
189
246
  name: string,
190
247
  security: HttpApiSecurity
191
248
  ) {
192
- if (spec.components!.securitySchemes![name]) {
249
+ if (spec.components.securitySchemes[name] !== undefined) {
193
250
  return
194
251
  }
195
- const scheme = makeSecurityScheme(security)
196
- spec.components!.securitySchemes![name] = scheme
252
+ spec.components.securitySchemes[name] = makeSecurityScheme(security)
197
253
  }
198
- Option.map(Context.getOption(api.annotations, HttpApi.AdditionalSchemas), (componentSchemas) => {
199
- componentSchemas.forEach((componentSchema) => makeJsonSchemaOrRef(componentSchema))
254
+
255
+ processAnnotation(api.annotations, HttpApi.AdditionalSchemas, (componentSchemas) => {
256
+ componentSchemas.forEach((componentSchema) => processAST(componentSchema.ast))
200
257
  })
201
- Option.map(Context.getOption(api.annotations, Description), (description) => {
258
+ processAnnotation(api.annotations, Description, (description) => {
202
259
  spec.info.description = description
203
260
  })
204
- Option.map(Context.getOption(api.annotations, License), (license) => {
261
+ processAnnotation(api.annotations, License, (license) => {
205
262
  spec.info.license = license
206
263
  })
207
- Option.map(Context.getOption(api.annotations, Summary), (summary) => {
208
- spec.info.summary = summary as any
264
+ processAnnotation(api.annotations, Summary, (summary) => {
265
+ spec.info.summary = summary
209
266
  })
210
- Option.map(Context.getOption(api.annotations, Servers), (servers) => {
211
- spec.servers = servers as any
212
- })
213
- Option.map(Context.getOption(api.annotations, Override), (override) => {
214
- Object.assign(spec, override)
267
+ processAnnotation(api.annotations, Servers, (servers) => {
268
+ spec.servers = [...servers]
215
269
  })
270
+
216
271
  api.middlewares.forEach((middleware) => {
217
272
  if (!HttpApiMiddleware.isSecurity(middleware)) {
218
273
  return
219
274
  }
220
275
  for (const [name, security] of Object.entries(middleware.security)) {
221
- registerSecurity(name, security)
222
- spec.security!.push({ [name]: [] })
276
+ processHttpApiSecurity(name, security)
277
+ spec.security.push({ [name]: [] })
223
278
  }
224
279
  })
225
- HttpApi.reflect(api as any, {
280
+ HttpApi.reflect(api, {
226
281
  onGroup({ group }) {
227
282
  if (Context.get(group.annotations, Exclude)) {
228
283
  return
229
284
  }
230
- let tag: Mutable<OpenAPISpecTag> = {
285
+ let tag: OpenAPISpecTag = {
231
286
  name: Context.getOrElse(group.annotations, Title, () => group.identifier)
232
287
  }
233
- Option.map(Context.getOption(group.annotations, Description), (description) => {
288
+
289
+ processAnnotation(group.annotations, Description, (description) => {
234
290
  tag.description = description
235
291
  })
236
- Option.map(Context.getOption(group.annotations, ExternalDocs), (externalDocs) => {
292
+ processAnnotation(group.annotations, ExternalDocs, (externalDocs) => {
237
293
  tag.externalDocs = externalDocs
238
294
  })
239
- Option.map(Context.getOption(group.annotations, Override), (override) => {
295
+ processAnnotation(group.annotations, Override, (override) => {
240
296
  Object.assign(tag, override)
241
297
  })
242
- Option.map(Context.getOption(group.annotations, Transform), (fn) => {
243
- tag = fn(tag) as OpenAPISpecTag
298
+ processAnnotation(group.annotations, Transform, (transformFn) => {
299
+ tag = transformFn(tag) as OpenAPISpecTag
244
300
  })
245
- spec.tags!.push(tag)
301
+
302
+ spec.tags.push(tag)
246
303
  },
247
304
  onEndpoint({ endpoint, errors, group, mergedAnnotations, middleware, payloads, successes }) {
248
305
  if (Context.get(mergedAnnotations, Exclude)) {
249
306
  return
250
307
  }
251
- const path = endpoint.path.replace(/:(\w+)[^/]*/g, "{$1}")
252
- const method = endpoint.method.toLowerCase() as OpenAPISpecMethodName
253
- let op: DeepMutable<OpenAPISpecOperation> = {
308
+ let op: OpenAPISpecOperation = {
254
309
  tags: [Context.getOrElse(group.annotations, Title, () => group.identifier)],
255
310
  operationId: Context.getOrElse(
256
311
  endpoint.annotations,
@@ -261,149 +316,125 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec =>
261
316
  security: [],
262
317
  responses: {}
263
318
  }
264
- Option.map(Context.getOption(endpoint.annotations, Description), (description) => {
319
+
320
+ function processResponseMap(
321
+ map: ReadonlyMap<number, {
322
+ readonly ast: Option.Option<AST.AST>
323
+ readonly description: Option.Option<string>
324
+ }>,
325
+ defaultDescription: () => string
326
+ ) {
327
+ for (const [status, { ast, description }] of map) {
328
+ if (op.responses[status]) continue
329
+ op.responses[status] = {
330
+ description: Option.getOrElse(description, defaultDescription)
331
+ }
332
+ ast.pipe(
333
+ Option.filter((ast) => !HttpApiSchema.getEmptyDecodeable(ast)),
334
+ Option.map((ast) => {
335
+ const encoding = HttpApiSchema.getEncoding(ast)
336
+ op.responses[status].content = {
337
+ [encoding.contentType]: {
338
+ schema: processAST(ast)
339
+ }
340
+ }
341
+ })
342
+ )
343
+ }
344
+ }
345
+
346
+ function processParameters(schema: Option.Option<Schema.Schema.All>, i: OpenAPISpecParameter["in"]) {
347
+ if (Option.isSome(schema)) {
348
+ const jsonSchema = processAST(schema.value.ast)
349
+ if ("properties" in jsonSchema) {
350
+ Object.entries(jsonSchema.properties).forEach(([name, psJsonSchema]) => {
351
+ op.parameters.push({
352
+ name,
353
+ in: i,
354
+ schema: psJsonSchema,
355
+ required: jsonSchema.required.includes(name),
356
+ ...(psJsonSchema.description !== undefined ? { description: psJsonSchema.description } : undefined)
357
+ })
358
+ })
359
+ }
360
+ }
361
+ }
362
+
363
+ processAnnotation(endpoint.annotations, Description, (description) => {
265
364
  op.description = description
266
365
  })
267
- Option.map(Context.getOption(endpoint.annotations, Summary), (summary) => {
366
+ processAnnotation(endpoint.annotations, Summary, (summary) => {
268
367
  op.summary = summary
269
368
  })
270
- Option.map(Context.getOption(endpoint.annotations, Deprecated), (deprecated) => {
369
+ processAnnotation(endpoint.annotations, Deprecated, (deprecated) => {
271
370
  op.deprecated = deprecated
272
371
  })
273
- Option.map(Context.getOption(endpoint.annotations, ExternalDocs), (externalDocs) => {
372
+ processAnnotation(endpoint.annotations, ExternalDocs, (externalDocs) => {
274
373
  op.externalDocs = externalDocs
275
374
  })
375
+
276
376
  middleware.forEach((middleware) => {
277
377
  if (!HttpApiMiddleware.isSecurity(middleware)) {
278
378
  return
279
379
  }
280
380
  for (const [name, security] of Object.entries(middleware.security)) {
281
- registerSecurity(name, security)
282
- op.security!.push({ [name]: [] })
381
+ processHttpApiSecurity(name, security)
382
+ op.security.push({ [name]: [] })
283
383
  }
284
384
  })
285
- if (payloads.size > 0) {
286
- const content: Mutable<OpenApiSpecContent> = {}
385
+ const hasBody = HttpMethod.hasBody(endpoint.method)
386
+ if (hasBody && payloads.size > 0) {
387
+ const content: OpenApiSpecContent = {}
287
388
  payloads.forEach(({ ast }, contentType) => {
288
389
  content[contentType as OpenApiSpecContentType] = {
289
- schema: makeJsonSchemaOrRef(Schema.make(ast))
390
+ schema: processAST(ast)
290
391
  }
291
392
  })
292
393
  op.requestBody = { content, required: true }
293
394
  }
294
- for (const [status, { ast, description }] of successes) {
295
- if (op.responses![status]) continue
296
- op.responses![status] = {
297
- description: Option.getOrElse(description, () => "Success")
298
- }
299
- ast.pipe(
300
- Option.filter((ast) => !HttpApiSchema.getEmptyDecodeable(ast)),
301
- Option.map((ast) => {
302
- op.responses![status].content = {
303
- "application/json": {
304
- schema: makeJsonSchemaOrRef(Schema.make(ast))
305
- }
306
- }
307
- })
308
- )
309
- }
310
- if (Option.isSome(endpoint.pathSchema)) {
311
- const schema = makeJsonSchemaOrRef(endpoint.pathSchema.value) as JsonSchema.Object
312
- if ("properties" in schema) {
313
- Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
314
- op.parameters!.push({
315
- name,
316
- in: "path",
317
- schema: jsonSchema,
318
- required: schema.required.includes(name),
319
- ...(jsonSchema.description ? { description: jsonSchema.description } : {})
320
- })
321
- })
322
- }
323
- }
324
- if (!HttpMethod.hasBody(endpoint.method) && Option.isSome(endpoint.payloadSchema)) {
325
- const schema = makeJsonSchemaOrRef(endpoint.payloadSchema.value) as JsonSchema.Object
326
- if ("properties" in schema) {
327
- Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
328
- op.parameters!.push({
329
- name,
330
- in: "query",
331
- schema: jsonSchema,
332
- required: schema.required.includes(name),
333
- ...(jsonSchema.description ? { description: jsonSchema.description } : {})
334
- })
335
- })
336
- }
337
- }
338
- if (Option.isSome(endpoint.headersSchema)) {
339
- const schema = makeJsonSchemaOrRef(endpoint.headersSchema.value) as JsonSchema.Object
340
- if ("properties" in schema) {
341
- Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
342
- op.parameters!.push({
343
- name,
344
- in: "header",
345
- schema: jsonSchema,
346
- required: schema.required.includes(name),
347
- ...(jsonSchema.description ? { description: jsonSchema.description } : {})
348
- })
349
- })
350
- }
351
- }
352
- if (Option.isSome(endpoint.urlParamsSchema)) {
353
- const schema = makeJsonSchemaOrRef(endpoint.urlParamsSchema.value) as JsonSchema.Object
354
- if ("properties" in schema) {
355
- Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
356
- op.parameters!.push({
357
- name,
358
- in: "query",
359
- schema: jsonSchema,
360
- required: schema.required.includes(name),
361
- ...(jsonSchema.description ? { description: jsonSchema.description } : {})
362
- })
363
- })
364
- }
365
- }
366
- for (const [status, { ast, description }] of errors) {
367
- if (op.responses![status]) continue
368
- op.responses![status] = {
369
- description: Option.getOrElse(description, () => "Error")
370
- }
371
- ast.pipe(
372
- Option.filter((ast) => !HttpApiSchema.getEmptyDecodeable(ast)),
373
- Option.map((ast) => {
374
- op.responses![status].content = {
375
- "application/json": {
376
- schema: makeJsonSchemaOrRef(Schema.make(ast))
377
- }
378
- }
379
- })
380
- )
395
+
396
+ processParameters(endpoint.pathSchema, "path")
397
+ if (!hasBody) {
398
+ processParameters(endpoint.payloadSchema, "query")
381
399
  }
400
+ processParameters(endpoint.headersSchema, "header")
401
+ processParameters(endpoint.urlParamsSchema, "query")
402
+
403
+ processResponseMap(successes, () => "Success")
404
+ processResponseMap(errors, () => "Error")
405
+
406
+ const path = endpoint.path.replace(/:(\w+)[^/]*/g, "{$1}")
407
+ const method = endpoint.method.toLowerCase() as OpenAPISpecMethodName
382
408
  if (!spec.paths[path]) {
383
409
  spec.paths[path] = {}
384
410
  }
385
- Option.map(Context.getOption(endpoint.annotations, Override), (override) => {
411
+
412
+ processAnnotation(endpoint.annotations, Override, (override) => {
386
413
  Object.assign(op, override)
387
414
  })
388
- Option.map(Context.getOption(endpoint.annotations, Transform), (transformFn) => {
389
- op = transformFn(op)
415
+ processAnnotation(endpoint.annotations, Transform, (transformFn) => {
416
+ op = transformFn(op) as OpenAPISpecOperation
390
417
  })
418
+
391
419
  spec.paths[path][method] = op
392
420
  }
393
421
  })
394
422
 
395
- Option.map(Context.getOption(api.annotations, Transform), (transformFn) => {
423
+ processAnnotation(api.annotations, Override, (override) => {
424
+ Object.assign(spec, override)
425
+ })
426
+ processAnnotation(api.annotations, Transform, (transformFn) => {
396
427
  spec = transformFn(spec) as OpenAPISpec
397
428
  })
398
429
 
399
- apiCache.set(self, spec)
430
+ apiCache.set(api, spec)
400
431
 
401
432
  return spec
402
433
  }
403
434
 
404
435
  const makeSecurityScheme = (security: HttpApiSecurity): OpenAPISecurityScheme => {
405
- const meta: Mutable<Partial<OpenAPISecurityScheme>> = {}
406
- Option.map(Context.getOption(security.annotations, Description), (description) => {
436
+ const meta: Partial<OpenAPISecurityScheme> = {}
437
+ processAnnotation(security.annotations, Description, (description) => {
407
438
  meta.description = description
408
439
  })
409
440
  switch (security._tag) {
@@ -438,18 +469,21 @@ const makeSecurityScheme = (security: HttpApiSecurity): OpenAPISecurityScheme =>
438
469
  }
439
470
 
440
471
  /**
472
+ * This model describes the OpenAPI specification (version 3.1.0) returned by
473
+ * {@link fromApi}. It is not intended to describe the entire OpenAPI
474
+ * specification, only the output of `fromApi`.
475
+ *
441
476
  * @category models
442
477
  * @since 1.0.0
443
478
  */
444
479
  export interface OpenAPISpec {
445
- readonly openapi: "3.1.0"
446
- readonly info: OpenAPISpecInfo
447
- readonly servers?: Array<OpenAPISpecServer>
448
- readonly paths: OpenAPISpecPaths
449
- readonly components?: OpenAPIComponents
450
- readonly security?: Array<OpenAPISecurityRequirement>
451
- readonly tags?: Array<OpenAPISpecTag>
452
- readonly externalDocs?: OpenAPISpecExternalDocs
480
+ openapi: "3.1.0"
481
+ info: OpenAPISpecInfo
482
+ paths: OpenAPISpecPaths
483
+ components: OpenAPIComponents
484
+ security: Array<OpenAPISecurityRequirement>
485
+ tags: Array<OpenAPISpecTag>
486
+ servers?: Array<OpenAPISpecServer>
453
487
  }
454
488
 
455
489
  /**
@@ -457,11 +491,11 @@ export interface OpenAPISpec {
457
491
  * @since 1.0.0
458
492
  */
459
493
  export interface OpenAPISpecInfo {
460
- readonly title: string
461
- readonly version: string
462
- readonly description?: string
463
- readonly license?: OpenAPISpecLicense
464
- readonly summary?: string
494
+ title: string
495
+ version: string
496
+ description?: string
497
+ license?: OpenAPISpecLicense
498
+ summary?: string
465
499
  }
466
500
 
467
501
  /**
@@ -469,9 +503,9 @@ export interface OpenAPISpecInfo {
469
503
  * @since 1.0.0
470
504
  */
471
505
  export interface OpenAPISpecTag {
472
- readonly name: string
473
- readonly description?: string
474
- readonly externalDocs?: OpenAPISpecExternalDocs
506
+ name: string
507
+ description?: string
508
+ externalDocs?: OpenAPISpecExternalDocs
475
509
  }
476
510
 
477
511
  /**
@@ -479,8 +513,8 @@ export interface OpenAPISpecTag {
479
513
  * @since 1.0.0
480
514
  */
481
515
  export interface OpenAPISpecExternalDocs {
482
- readonly url: string
483
- readonly description?: string
516
+ url: string
517
+ description?: string
484
518
  }
485
519
 
486
520
  /**
@@ -488,8 +522,8 @@ export interface OpenAPISpecExternalDocs {
488
522
  * @since 1.0.0
489
523
  */
490
524
  export interface OpenAPISpecLicense {
491
- readonly name: string
492
- readonly url?: string
525
+ name: string
526
+ url?: string
493
527
  }
494
528
 
495
529
  /**
@@ -497,9 +531,9 @@ export interface OpenAPISpecLicense {
497
531
  * @since 1.0.0
498
532
  */
499
533
  export interface OpenAPISpecServer {
500
- readonly url: string
501
- readonly description?: string
502
- readonly variables?: Record<string, OpenAPISpecServerVariable>
534
+ url: string
535
+ description?: string
536
+ variables?: Record<string, OpenAPISpecServerVariable>
503
537
  }
504
538
 
505
539
  /**
@@ -507,19 +541,16 @@ export interface OpenAPISpecServer {
507
541
  * @since 1.0.0
508
542
  */
509
543
  export interface OpenAPISpecServerVariable {
510
- readonly default: string
511
- readonly enum?: [string, ...Array<string>]
512
- readonly description?: string
544
+ default: string
545
+ enum?: NonEmptyArray<string>
546
+ description?: string
513
547
  }
514
548
 
515
549
  /**
516
550
  * @category models
517
551
  * @since 1.0.0
518
552
  */
519
- export type OpenAPISpecPaths = ReadonlyRecord<
520
- string,
521
- OpenAPISpecPathItem
522
- >
553
+ export type OpenAPISpecPaths = Record<string, OpenAPISpecPathItem>
523
554
 
524
555
  /**
525
556
  * @category models
@@ -539,28 +570,20 @@ export type OpenAPISpecMethodName =
539
570
  * @category models
540
571
  * @since 1.0.0
541
572
  */
542
- export type OpenAPISpecPathItem =
543
- & {
544
- readonly [K in OpenAPISpecMethodName]?: OpenAPISpecOperation
545
- }
546
- & {
547
- readonly summary?: string
548
- readonly description?: string
549
- readonly parameters?: Array<OpenAPISpecParameter>
550
- }
573
+ export type OpenAPISpecPathItem = {
574
+ [K in OpenAPISpecMethodName]?: OpenAPISpecOperation
575
+ }
551
576
 
552
577
  /**
553
578
  * @category models
554
579
  * @since 1.0.0
555
580
  */
556
581
  export interface OpenAPISpecParameter {
557
- readonly name: string
558
- readonly in: "query" | "header" | "path" | "cookie"
559
- readonly schema: JsonSchema.JsonSchema
560
- readonly description?: string
561
- readonly required?: boolean
562
- readonly deprecated?: boolean
563
- readonly allowEmptyValue?: boolean
582
+ name: string
583
+ in: "query" | "header" | "path" | "cookie"
584
+ schema: JsonSchema.JsonSchema
585
+ required: boolean
586
+ description?: string
564
587
  }
565
588
 
566
589
  /**
@@ -573,42 +596,28 @@ export type OpenAPISpecResponses = Record<number, OpenApiSpecResponse>
573
596
  * @category models
574
597
  * @since 1.0.0
575
598
  */
576
- export type OpenApiSpecContentType = "application/json" | "application/xml" | "multipart/form-data" | "text/plain"
599
+ export type OpenApiSpecContentType =
600
+ | "application/json"
601
+ | "application/xml"
602
+ | "application/x-www-form-urlencoded"
603
+ | "multipart/form-data"
604
+ | "text/plain"
577
605
 
578
606
  /**
579
607
  * @category models
580
608
  * @since 1.0.0
581
609
  */
582
610
  export type OpenApiSpecContent = {
583
- readonly [K in OpenApiSpecContentType]?: OpenApiSpecMediaType
611
+ [K in OpenApiSpecContentType]?: OpenApiSpecMediaType
584
612
  }
585
613
 
586
- /**
587
- * @category models
588
- * @since 1.0.0
589
- */
590
- export interface OpenApiSpecResponseHeader {
591
- readonly description?: string
592
- readonly schema: JsonSchema.JsonSchema
593
- }
594
-
595
- /**
596
- * @category models
597
- * @since 1.0.0
598
- */
599
- export type OpenApiSpecResponseHeaders = ReadonlyRecord<
600
- string,
601
- OpenApiSpecResponseHeader
602
- >
603
-
604
614
  /**
605
615
  * @category models
606
616
  * @since 1.0.0
607
617
  */
608
618
  export interface OpenApiSpecResponse {
609
- readonly content?: OpenApiSpecContent
610
- readonly headers?: OpenApiSpecResponseHeaders
611
- readonly description: string
619
+ description: string
620
+ content?: OpenApiSpecContent
612
621
  }
613
622
 
614
623
  /**
@@ -616,9 +625,7 @@ export interface OpenApiSpecResponse {
616
625
  * @since 1.0.0
617
626
  */
618
627
  export interface OpenApiSpecMediaType {
619
- readonly schema?: JsonSchema.JsonSchema
620
- readonly example?: object
621
- readonly description?: string
628
+ schema: JsonSchema.JsonSchema
622
629
  }
623
630
 
624
631
  /**
@@ -626,9 +633,8 @@ export interface OpenApiSpecMediaType {
626
633
  * @since 1.0.0
627
634
  */
628
635
  export interface OpenAPISpecRequestBody {
629
- readonly content: OpenApiSpecContent
630
- readonly description?: string
631
- readonly required?: boolean
636
+ content: OpenApiSpecContent
637
+ required: true
632
638
  }
633
639
 
634
640
  /**
@@ -636,8 +642,8 @@ export interface OpenAPISpecRequestBody {
636
642
  * @since 1.0.0
637
643
  */
638
644
  export interface OpenAPIComponents {
639
- readonly schemas?: ReadonlyRecord<string, JsonSchema.JsonSchema>
640
- readonly securitySchemes?: ReadonlyRecord<string, OpenAPISecurityScheme>
645
+ schemas: Record<string, JsonSchema.JsonSchema>
646
+ securitySchemes: Record<string, OpenAPISecurityScheme>
641
647
  }
642
648
 
643
649
  /**
@@ -646,10 +652,10 @@ export interface OpenAPIComponents {
646
652
  */
647
653
  export interface OpenAPIHTTPSecurityScheme {
648
654
  readonly type: "http"
649
- readonly description?: string
650
- readonly scheme: "bearer" | "basic" | string
655
+ scheme: "bearer" | "basic" | string
656
+ description?: string
651
657
  /* only for scheme: 'bearer' */
652
- readonly bearerFormat?: string
658
+ bearerFormat?: string
653
659
  }
654
660
 
655
661
  /**
@@ -658,41 +664,9 @@ export interface OpenAPIHTTPSecurityScheme {
658
664
  */
659
665
  export interface OpenAPIApiKeySecurityScheme {
660
666
  readonly type: "apiKey"
661
- readonly description?: string
662
- readonly name: string
663
- readonly in: "query" | "header" | "cookie"
664
- }
665
-
666
- /**
667
- * @category models
668
- * @since 1.0.0
669
- */
670
- export interface OpenAPIMutualTLSSecurityScheme {
671
- readonly type: "mutualTLS"
672
- readonly description?: string
673
- }
674
-
675
- /**
676
- * @category models
677
- * @since 1.0.0
678
- */
679
- export interface OpenAPIOAuth2SecurityScheme {
680
- readonly type: "oauth2"
681
- readonly description?: string
682
- readonly flows: ReadonlyRecord<
683
- "implicit" | "password" | "clientCredentials" | "authorizationCode",
684
- ReadonlyRecord<string, unknown>
685
- >
686
- }
687
-
688
- /**
689
- * @category models
690
- * @since 1.0.0
691
- */
692
- export interface OpenAPIOpenIdConnectSecurityScheme {
693
- readonly type: "openIdConnect"
694
- readonly description?: string
695
- readonly openIdConnectUrl: string
667
+ name: string
668
+ in: "query" | "header" | "cookie"
669
+ description?: string
696
670
  }
697
671
 
698
672
  /**
@@ -702,29 +676,27 @@ export interface OpenAPIOpenIdConnectSecurityScheme {
702
676
  export type OpenAPISecurityScheme =
703
677
  | OpenAPIHTTPSecurityScheme
704
678
  | OpenAPIApiKeySecurityScheme
705
- | OpenAPIMutualTLSSecurityScheme
706
- | OpenAPIOAuth2SecurityScheme
707
- | OpenAPIOpenIdConnectSecurityScheme
708
679
 
709
680
  /**
710
681
  * @category models
711
682
  * @since 1.0.0
712
683
  */
713
- export type OpenAPISecurityRequirement = ReadonlyRecord<string, Array<string>>
684
+ export type OpenAPISecurityRequirement = Record<string, Array<string>>
714
685
 
715
686
  /**
716
687
  * @category models
717
688
  * @since 1.0.0
718
689
  */
719
690
  export interface OpenAPISpecOperation {
720
- readonly requestBody?: OpenAPISpecRequestBody
721
- readonly responses?: OpenAPISpecResponses
722
- readonly operationId?: string
723
- readonly description?: string
724
- readonly parameters?: Array<OpenAPISpecParameter>
725
- readonly summary?: string
726
- readonly deprecated?: boolean
727
- readonly tags?: Array<string>
728
- readonly security?: Array<OpenAPISecurityRequirement>
729
- readonly externalDocs?: OpenAPISpecExternalDocs
691
+ operationId: string
692
+ parameters: Array<OpenAPISpecParameter>
693
+ responses: OpenAPISpecResponses
694
+ /** Always contains at least the title annotation or the group identifier */
695
+ tags: NonEmptyArray<string>
696
+ security: Array<OpenAPISecurityRequirement>
697
+ requestBody?: OpenAPISpecRequestBody
698
+ description?: string
699
+ summary?: string
700
+ deprecated?: boolean
701
+ externalDocs?: OpenAPISpecExternalDocs
730
702
  }