@effect/platform 0.62.5 → 0.63.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HttpApi/package.json +6 -0
- package/HttpApiBuilder/package.json +6 -0
- package/HttpApiClient/package.json +6 -0
- package/HttpApiEndpoint/package.json +6 -0
- package/HttpApiError/package.json +6 -0
- package/HttpApiGroup/package.json +6 -0
- package/HttpApiSchema/package.json +6 -0
- package/HttpApiSecurity/package.json +6 -0
- package/HttpApiSwagger/package.json +6 -0
- package/OpenApi/package.json +6 -0
- package/README.md +863 -302
- package/dist/cjs/HttpApi.js +168 -0
- package/dist/cjs/HttpApi.js.map +1 -0
- package/dist/cjs/HttpApiBuilder.js +471 -0
- package/dist/cjs/HttpApiBuilder.js.map +1 -0
- package/dist/cjs/HttpApiClient.js +134 -0
- package/dist/cjs/HttpApiClient.js.map +1 -0
- package/dist/cjs/HttpApiEndpoint.js +181 -0
- package/dist/cjs/HttpApiEndpoint.js.map +1 -0
- package/dist/cjs/HttpApiError.js +69 -0
- package/dist/cjs/HttpApiError.js.map +1 -0
- package/dist/cjs/HttpApiGroup.js +151 -0
- package/dist/cjs/HttpApiGroup.js.map +1 -0
- package/dist/cjs/HttpApiSchema.js +241 -0
- package/dist/cjs/HttpApiSchema.js.map +1 -0
- package/dist/cjs/HttpApiSecurity.js +83 -0
- package/dist/cjs/HttpApiSecurity.js.map +1 -0
- package/dist/cjs/HttpApiSwagger.js +50 -0
- package/dist/cjs/HttpApiSwagger.js.map +1 -0
- package/dist/cjs/HttpMethod.js +1 -1
- package/dist/cjs/HttpMethod.js.map +1 -1
- package/dist/cjs/HttpRouter.js +6 -1
- package/dist/cjs/HttpRouter.js.map +1 -1
- package/dist/cjs/OpenApi.js +317 -0
- package/dist/cjs/OpenApi.js.map +1 -0
- package/dist/cjs/index.js +21 -1
- package/dist/cjs/internal/apiSwagger.js +2 -0
- package/dist/cjs/internal/apiSwagger.js.map +1 -0
- package/dist/cjs/internal/httpRouter.js +6 -1
- package/dist/cjs/internal/httpRouter.js.map +1 -1
- package/dist/cjs/internal/multipart.js +5 -1
- package/dist/cjs/internal/multipart.js.map +1 -1
- package/dist/dts/HttpApi.d.ts +156 -0
- package/dist/dts/HttpApi.d.ts.map +1 -0
- package/dist/dts/HttpApiBuilder.d.ts +296 -0
- package/dist/dts/HttpApiBuilder.d.ts.map +1 -0
- package/dist/dts/HttpApiClient.d.ts +31 -0
- package/dist/dts/HttpApiClient.d.ts.map +1 -0
- package/dist/dts/HttpApiEndpoint.d.ts +350 -0
- package/dist/dts/HttpApiEndpoint.d.ts.map +1 -0
- package/dist/dts/HttpApiError.d.ts +70 -0
- package/dist/dts/HttpApiError.d.ts.map +1 -0
- package/dist/dts/HttpApiGroup.d.ts +196 -0
- package/dist/dts/HttpApiGroup.d.ts.map +1 -0
- package/dist/dts/HttpApiSchema.d.ts +223 -0
- package/dist/dts/HttpApiSchema.d.ts.map +1 -0
- package/dist/dts/HttpApiSecurity.d.ts +122 -0
- package/dist/dts/HttpApiSecurity.d.ts.map +1 -0
- package/dist/dts/HttpApiSwagger.d.ts +10 -0
- package/dist/dts/HttpApiSwagger.d.ts.map +1 -0
- package/dist/dts/HttpMethod.d.ts +16 -0
- package/dist/dts/HttpMethod.d.ts.map +1 -1
- package/dist/dts/HttpRouter.d.ts +8 -0
- package/dist/dts/HttpRouter.d.ts.map +1 -1
- package/dist/dts/HttpServerResponse.d.ts +3 -3
- package/dist/dts/HttpServerResponse.d.ts.map +1 -1
- package/dist/dts/OpenApi.d.ts +348 -0
- package/dist/dts/OpenApi.d.ts.map +1 -0
- package/dist/dts/index.d.ts +40 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/internal/apiSwagger.d.ts +2 -0
- package/dist/dts/internal/apiSwagger.d.ts.map +1 -0
- package/dist/dts/internal/httpRouter.d.ts.map +1 -1
- package/dist/esm/HttpApi.js +157 -0
- package/dist/esm/HttpApi.js.map +1 -0
- package/dist/esm/HttpApiBuilder.js +447 -0
- package/dist/esm/HttpApiBuilder.js.map +1 -0
- package/dist/esm/HttpApiClient.js +124 -0
- package/dist/esm/HttpApiClient.js.map +1 -0
- package/dist/esm/HttpApiEndpoint.js +169 -0
- package/dist/esm/HttpApiEndpoint.js.map +1 -0
- package/dist/esm/HttpApiError.js +59 -0
- package/dist/esm/HttpApiError.js.map +1 -0
- package/dist/esm/HttpApiGroup.js +140 -0
- package/dist/esm/HttpApiGroup.js.map +1 -0
- package/dist/esm/HttpApiSchema.js +217 -0
- package/dist/esm/HttpApiSchema.js.map +1 -0
- package/dist/esm/HttpApiSecurity.js +73 -0
- package/dist/esm/HttpApiSecurity.js.map +1 -0
- package/dist/esm/HttpApiSwagger.js +40 -0
- package/dist/esm/HttpApiSwagger.js.map +1 -0
- package/dist/esm/HttpMethod.js +1 -1
- package/dist/esm/HttpMethod.js.map +1 -1
- package/dist/esm/HttpRouter.js +5 -0
- package/dist/esm/HttpRouter.js.map +1 -1
- package/dist/esm/OpenApi.js +299 -0
- package/dist/esm/OpenApi.js.map +1 -0
- package/dist/esm/index.js +40 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/apiSwagger.js +2 -0
- package/dist/esm/internal/apiSwagger.js.map +1 -0
- package/dist/esm/internal/httpRouter.js +5 -0
- package/dist/esm/internal/httpRouter.js.map +1 -1
- package/dist/esm/internal/multipart.js +5 -1
- package/dist/esm/internal/multipart.js.map +1 -1
- package/package.json +83 -3
- package/src/HttpApi.ts +342 -0
- package/src/HttpApiBuilder.ts +869 -0
- package/src/HttpApiClient.ts +228 -0
- package/src/HttpApiEndpoint.ts +818 -0
- package/src/HttpApiError.ts +113 -0
- package/src/HttpApiGroup.ts +365 -0
- package/src/HttpApiSchema.ts +384 -0
- package/src/HttpApiSecurity.ts +169 -0
- package/src/HttpApiSwagger.ts +46 -0
- package/src/HttpMethod.ts +19 -1
- package/src/HttpRouter.ts +9 -0
- package/src/HttpServerResponse.ts +3 -3
- package/src/OpenApi.ts +665 -0
- package/src/index.ts +50 -0
- package/src/internal/apiSwagger.ts +7 -0
- package/src/internal/httpRouter.ts +9 -0
- package/src/internal/multipart.ts +5 -1
package/src/OpenApi.ts
ADDED
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as AST from "@effect/schema/AST"
|
|
5
|
+
import * as JSONSchema from "@effect/schema/JSONSchema"
|
|
6
|
+
import * as Schema from "@effect/schema/Schema"
|
|
7
|
+
import * as Context from "effect/Context"
|
|
8
|
+
import { dual } from "effect/Function"
|
|
9
|
+
import * as Option from "effect/Option"
|
|
10
|
+
import type { ReadonlyRecord } from "effect/Record"
|
|
11
|
+
import type { DeepMutable, Mutable } from "effect/Types"
|
|
12
|
+
import * as HttpApi from "./HttpApi.js"
|
|
13
|
+
import * as HttpApiSchema from "./HttpApiSchema.js"
|
|
14
|
+
import type { HttpApiSecurity } from "./HttpApiSecurity.js"
|
|
15
|
+
import * as HttpMethod from "./HttpMethod.js"
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @since 1.0.0
|
|
19
|
+
* @category annotations
|
|
20
|
+
*/
|
|
21
|
+
export class Identifier extends Context.Tag("@effect/platform/OpenApi/Identifier")<Identifier, string>() {}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @since 1.0.0
|
|
25
|
+
* @category annotations
|
|
26
|
+
*/
|
|
27
|
+
export class Title extends Context.Tag("@effect/platform/OpenApi/Title")<Title, string>() {}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @since 1.0.0
|
|
31
|
+
* @category annotations
|
|
32
|
+
*/
|
|
33
|
+
export class Version extends Context.Tag("@effect/platform/OpenApi/Version")<Version, string>() {}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @since 1.0.0
|
|
37
|
+
* @category annotations
|
|
38
|
+
*/
|
|
39
|
+
export class Description extends Context.Tag("@effect/platform/OpenApi/Description")<Description, string>() {}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @since 1.0.0
|
|
43
|
+
* @category annotations
|
|
44
|
+
*/
|
|
45
|
+
export class License extends Context.Tag("@effect/platform/OpenApi/License")<License, OpenAPISpecLicense>() {}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @since 1.0.0
|
|
49
|
+
* @category annotations
|
|
50
|
+
*/
|
|
51
|
+
export class Security extends Context.Tag("@effect/platform/OpenApi/Security")<Security, HttpApiSecurity>() {}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @since 1.0.0
|
|
55
|
+
* @category annotations
|
|
56
|
+
*/
|
|
57
|
+
export class ExternalDocs
|
|
58
|
+
extends Context.Tag("@effect/platform/OpenApi/ExternalDocs")<ExternalDocs, OpenAPISpecExternalDocs>()
|
|
59
|
+
{}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @since 1.0.0
|
|
63
|
+
* @category annotations
|
|
64
|
+
*/
|
|
65
|
+
export const annotations = (annotations: {
|
|
66
|
+
readonly identifier?: string | undefined
|
|
67
|
+
readonly title?: string | undefined
|
|
68
|
+
readonly description?: string | undefined
|
|
69
|
+
readonly version?: string | undefined
|
|
70
|
+
readonly license?: OpenAPISpecLicense | undefined
|
|
71
|
+
readonly security?: HttpApiSecurity | undefined
|
|
72
|
+
readonly externalDocs?: OpenAPISpecExternalDocs | undefined
|
|
73
|
+
}): Context.Context<never> => {
|
|
74
|
+
let context = Context.empty()
|
|
75
|
+
if (annotations.identifier !== undefined) {
|
|
76
|
+
context = Context.add(context, Identifier, annotations.identifier)
|
|
77
|
+
}
|
|
78
|
+
if (annotations.title !== undefined) {
|
|
79
|
+
context = Context.add(context, Title, annotations.title)
|
|
80
|
+
}
|
|
81
|
+
if (annotations.description !== undefined) {
|
|
82
|
+
context = Context.add(context, Description, annotations.description)
|
|
83
|
+
}
|
|
84
|
+
if (annotations.version !== undefined) {
|
|
85
|
+
context = Context.add(context, Version, annotations.version)
|
|
86
|
+
}
|
|
87
|
+
if (annotations.license !== undefined) {
|
|
88
|
+
context = Context.add(context, License, annotations.license)
|
|
89
|
+
}
|
|
90
|
+
if (annotations.security !== undefined) {
|
|
91
|
+
context = Context.add(context, Security, annotations.security)
|
|
92
|
+
}
|
|
93
|
+
if (annotations.externalDocs !== undefined) {
|
|
94
|
+
context = Context.add(context, ExternalDocs, annotations.externalDocs)
|
|
95
|
+
}
|
|
96
|
+
return context
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @since 1.0.0
|
|
101
|
+
* @category annotations
|
|
102
|
+
*/
|
|
103
|
+
export interface Annotatable {
|
|
104
|
+
readonly annotations: Context.Context<never>
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @since 1.0.0
|
|
109
|
+
* @category annotations
|
|
110
|
+
*/
|
|
111
|
+
export const annotate: {
|
|
112
|
+
(annotations: {
|
|
113
|
+
readonly identifier?: string | undefined
|
|
114
|
+
readonly title?: string | undefined
|
|
115
|
+
readonly description?: string | undefined
|
|
116
|
+
readonly version?: string | undefined
|
|
117
|
+
readonly license?: OpenAPISpecLicense | undefined
|
|
118
|
+
readonly security?: HttpApiSecurity | undefined
|
|
119
|
+
readonly externalDocs?: OpenAPISpecExternalDocs | undefined
|
|
120
|
+
}): <A extends Annotatable>(self: A) => A
|
|
121
|
+
<A extends Annotatable>(self: A, annotations: {
|
|
122
|
+
readonly identifier?: string | undefined
|
|
123
|
+
readonly title?: string | undefined
|
|
124
|
+
readonly description?: string | undefined
|
|
125
|
+
readonly version?: string | undefined
|
|
126
|
+
readonly license?: OpenAPISpecLicense | undefined
|
|
127
|
+
readonly security?: HttpApiSecurity | undefined
|
|
128
|
+
readonly externalDocs?: OpenAPISpecExternalDocs | undefined
|
|
129
|
+
}): A
|
|
130
|
+
} = dual(2, <A extends Annotatable>(self: A, annotations_: {
|
|
131
|
+
readonly identifier?: string | undefined
|
|
132
|
+
readonly title?: string | undefined
|
|
133
|
+
readonly description?: string | undefined
|
|
134
|
+
readonly version?: string | undefined
|
|
135
|
+
readonly license?: OpenAPISpecLicense | undefined
|
|
136
|
+
readonly security?: HttpApiSecurity | undefined
|
|
137
|
+
readonly externalDocs?: OpenAPISpecExternalDocs | undefined
|
|
138
|
+
}): A => {
|
|
139
|
+
const base = typeof self === "function" ? function() {} : self
|
|
140
|
+
Object.setPrototypeOf(base, Object.getPrototypeOf(self))
|
|
141
|
+
const context = Context.merge(
|
|
142
|
+
self.annotations,
|
|
143
|
+
annotations(annotations_)
|
|
144
|
+
)
|
|
145
|
+
return Object.assign(base, self, {
|
|
146
|
+
annotations: context
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @category constructors
|
|
152
|
+
* @since 1.0.0
|
|
153
|
+
*/
|
|
154
|
+
export const fromApi = <A extends HttpApi.HttpApi.Any>(api: A): OpenAPISpec => {
|
|
155
|
+
const spec: DeepMutable<OpenAPISpec> = {
|
|
156
|
+
openapi: "3.0.3",
|
|
157
|
+
info: {
|
|
158
|
+
title: Context.getOrElse(api.annotations, Title, () => "Api"),
|
|
159
|
+
version: Context.getOrElse(api.annotations, Version, () => "0.0.1")
|
|
160
|
+
},
|
|
161
|
+
paths: {},
|
|
162
|
+
tags: [],
|
|
163
|
+
components: {
|
|
164
|
+
schemas: {},
|
|
165
|
+
securitySchemes: {}
|
|
166
|
+
},
|
|
167
|
+
security: []
|
|
168
|
+
}
|
|
169
|
+
const securityMap = new Map<HttpApiSecurity, string>()
|
|
170
|
+
let securityCount = 0
|
|
171
|
+
function registerSecurity(security: HttpApiSecurity): string {
|
|
172
|
+
if (securityMap.has(security)) {
|
|
173
|
+
return securityMap.get(security)!
|
|
174
|
+
}
|
|
175
|
+
const count = securityCount++
|
|
176
|
+
const id = `${security._tag}${count === 0 ? "" : count}`
|
|
177
|
+
const scheme = makeSecurityScheme(security)
|
|
178
|
+
spec.components!.securitySchemes![id] = scheme
|
|
179
|
+
securityMap.set(security, id)
|
|
180
|
+
return id
|
|
181
|
+
}
|
|
182
|
+
Option.map(Context.getOption(api.annotations, Description), (description) => {
|
|
183
|
+
spec.info.description = description
|
|
184
|
+
})
|
|
185
|
+
Option.map(Context.getOption(api.annotations, License), (license) => {
|
|
186
|
+
spec.info.license = license
|
|
187
|
+
})
|
|
188
|
+
Option.map(Context.getOption(api.annotations, Security), (apiSecurity) => {
|
|
189
|
+
spec.security!.push({
|
|
190
|
+
[registerSecurity(apiSecurity)]: []
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
HttpApi.reflect(api as any, {
|
|
194
|
+
onGroup({ group }) {
|
|
195
|
+
const tag: Mutable<OpenAPISpecTag> = {
|
|
196
|
+
name: Context.getOrElse(group.annotations, Title, () => group.identifier)
|
|
197
|
+
}
|
|
198
|
+
Option.map(Context.getOption(group.annotations, Description), (description) => {
|
|
199
|
+
tag.description = description
|
|
200
|
+
})
|
|
201
|
+
Option.map(Context.getOption(group.annotations, ExternalDocs), (externalDocs) => {
|
|
202
|
+
tag.externalDocs = externalDocs
|
|
203
|
+
})
|
|
204
|
+
spec.tags!.push(tag)
|
|
205
|
+
},
|
|
206
|
+
onEndpoint({ endpoint, errors, group, mergedAnnotations, successAST, successEncoding, successStatus }) {
|
|
207
|
+
const path = endpoint.path.replace(/:(\w+)[^/]*/g, "{$1}")
|
|
208
|
+
const method = endpoint.method.toLowerCase() as OpenAPISpecMethodName
|
|
209
|
+
const op: DeepMutable<OpenAPISpecOperation> = {
|
|
210
|
+
tags: [Context.getOrElse(group.annotations, Title, () => group.identifier)],
|
|
211
|
+
operationId: Context.getOrElse(endpoint.annotations, Identifier, () => `${group.identifier}.${endpoint.name}`),
|
|
212
|
+
parameters: [],
|
|
213
|
+
security: [],
|
|
214
|
+
responses: {
|
|
215
|
+
[successStatus]: {
|
|
216
|
+
description: Option.getOrElse(getDescriptionOrIdentifier(successAST), () => "Success")
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
Option.map(Context.getOption(endpoint.annotations, Description), (description) => {
|
|
221
|
+
op.description = description
|
|
222
|
+
})
|
|
223
|
+
Option.map(Context.getOption(endpoint.annotations, ExternalDocs), (externalDocs) => {
|
|
224
|
+
op.externalDocs = externalDocs
|
|
225
|
+
})
|
|
226
|
+
Option.map(Context.getOption(mergedAnnotations, Security), (apiSecurity) => {
|
|
227
|
+
op.security!.push({
|
|
228
|
+
[registerSecurity(apiSecurity)]: []
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
endpoint.payloadSchema.pipe(
|
|
232
|
+
Option.filter(() => HttpMethod.hasBody(endpoint.method)),
|
|
233
|
+
Option.map((schema) => {
|
|
234
|
+
op.requestBody = {
|
|
235
|
+
content: {
|
|
236
|
+
[HttpApiSchema.getMultipart(schema.ast) ? "multipart/form-data" : "application/json"]: {
|
|
237
|
+
schema: makeJsonSchema(schema)
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
required: true
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
)
|
|
244
|
+
successAST.pipe(
|
|
245
|
+
Option.map((ast) => {
|
|
246
|
+
op.responses![successStatus].content = {
|
|
247
|
+
[successEncoding.contentType]: {
|
|
248
|
+
schema: makeJsonSchema(Schema.make(ast))
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
)
|
|
253
|
+
if (Option.isSome(endpoint.pathSchema)) {
|
|
254
|
+
const schema = makeJsonSchema(endpoint.pathSchema.value) as JSONSchema.JsonSchema7Object
|
|
255
|
+
if ("properties" in schema) {
|
|
256
|
+
Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
|
|
257
|
+
op.parameters!.push({
|
|
258
|
+
name,
|
|
259
|
+
in: "path",
|
|
260
|
+
schema: jsonSchema,
|
|
261
|
+
required: schema.required.includes(name)
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (!HttpMethod.hasBody(endpoint.method) && Option.isSome(endpoint.payloadSchema)) {
|
|
267
|
+
const schema = makeJsonSchema(endpoint.payloadSchema.value) as JSONSchema.JsonSchema7Object
|
|
268
|
+
if ("properties" in schema) {
|
|
269
|
+
Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
|
|
270
|
+
op.parameters!.push({
|
|
271
|
+
name,
|
|
272
|
+
in: "query",
|
|
273
|
+
schema: jsonSchema,
|
|
274
|
+
required: schema.required.includes(name)
|
|
275
|
+
})
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (Option.isSome(endpoint.headersSchema)) {
|
|
280
|
+
const schema = makeJsonSchema(endpoint.headersSchema.value) as JSONSchema.JsonSchema7Object
|
|
281
|
+
if ("properties" in schema) {
|
|
282
|
+
Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
|
|
283
|
+
op.parameters!.push({
|
|
284
|
+
name,
|
|
285
|
+
in: "header",
|
|
286
|
+
schema: jsonSchema,
|
|
287
|
+
required: schema.required.includes(name)
|
|
288
|
+
})
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
for (const [status, ast] of errors) {
|
|
293
|
+
if (op.responses![status]) continue
|
|
294
|
+
op.responses![status] = {
|
|
295
|
+
description: Option.getOrElse(getDescriptionOrIdentifier(ast), () => "Error")
|
|
296
|
+
}
|
|
297
|
+
ast.pipe(
|
|
298
|
+
Option.filter((ast) => !HttpApiSchema.getEmptyDecodeable(ast)),
|
|
299
|
+
Option.map((ast) => {
|
|
300
|
+
op.responses![status].content = {
|
|
301
|
+
"application/json": {
|
|
302
|
+
schema: makeJsonSchema(Schema.make(ast))
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
})
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
if (!spec.paths[path]) {
|
|
309
|
+
spec.paths[path] = {}
|
|
310
|
+
}
|
|
311
|
+
spec.paths[path][method] = op
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
return spec
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const makeSecurityScheme = (security: HttpApiSecurity): OpenAPISecurityScheme => {
|
|
319
|
+
const meta: Mutable<Partial<OpenAPISecurityScheme>> = {}
|
|
320
|
+
Option.map(Context.getOption(security.annotations, Description), (description) => {
|
|
321
|
+
meta.description = description
|
|
322
|
+
})
|
|
323
|
+
switch (security._tag) {
|
|
324
|
+
case "Basic": {
|
|
325
|
+
return {
|
|
326
|
+
...meta,
|
|
327
|
+
type: "http",
|
|
328
|
+
scheme: "basic"
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
case "Bearer": {
|
|
332
|
+
return {
|
|
333
|
+
...meta,
|
|
334
|
+
type: "http",
|
|
335
|
+
scheme: "bearer"
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
case "ApiKey": {
|
|
339
|
+
return {
|
|
340
|
+
...meta,
|
|
341
|
+
type: "apiKey",
|
|
342
|
+
name: security.key,
|
|
343
|
+
in: security.in
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const getDescriptionOrIdentifier = (ast: Option.Option<AST.PropertySignature | AST.AST>): Option.Option<string> =>
|
|
350
|
+
ast.pipe(
|
|
351
|
+
Option.map((ast) =>
|
|
352
|
+
"to" in ast ?
|
|
353
|
+
{
|
|
354
|
+
...ast.to.annotations,
|
|
355
|
+
...ast.annotations
|
|
356
|
+
} :
|
|
357
|
+
ast.annotations
|
|
358
|
+
),
|
|
359
|
+
Option.flatMapNullable((annotations) =>
|
|
360
|
+
annotations[AST.DescriptionAnnotationId] ?? annotations[AST.IdentifierAnnotationId] as any
|
|
361
|
+
)
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
const makeJsonSchema = (schema: Schema.Schema.All): OpenAPIJSONSchema => {
|
|
365
|
+
const jsonSchema = JSONSchema.make(schema as any)
|
|
366
|
+
delete jsonSchema.$schema
|
|
367
|
+
return jsonSchema
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* @category models
|
|
372
|
+
* @since 1.0.0
|
|
373
|
+
*/
|
|
374
|
+
export interface OpenAPISpec {
|
|
375
|
+
readonly openapi: "3.0.3"
|
|
376
|
+
readonly info: OpenAPISpecInfo
|
|
377
|
+
readonly servers?: Array<OpenAPISpecServer>
|
|
378
|
+
readonly paths: OpenAPISpecPaths
|
|
379
|
+
readonly components?: OpenAPIComponents
|
|
380
|
+
readonly security?: Array<OpenAPISecurityRequirement>
|
|
381
|
+
readonly tags?: Array<OpenAPISpecTag>
|
|
382
|
+
readonly externalDocs?: OpenAPISpecExternalDocs
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* @category models
|
|
387
|
+
* @since 1.0.0
|
|
388
|
+
*/
|
|
389
|
+
export interface OpenAPISpecInfo {
|
|
390
|
+
readonly title: string
|
|
391
|
+
readonly version: string
|
|
392
|
+
readonly description?: string
|
|
393
|
+
readonly license?: OpenAPISpecLicense
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* @category models
|
|
398
|
+
* @since 1.0.0
|
|
399
|
+
*/
|
|
400
|
+
export interface OpenAPISpecTag {
|
|
401
|
+
readonly name: string
|
|
402
|
+
readonly description?: string
|
|
403
|
+
readonly externalDocs?: OpenAPISpecExternalDocs
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* @category models
|
|
408
|
+
* @since 1.0.0
|
|
409
|
+
*/
|
|
410
|
+
export interface OpenAPISpecExternalDocs {
|
|
411
|
+
readonly url: string
|
|
412
|
+
readonly description?: string
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* @category models
|
|
417
|
+
* @since 1.0.0
|
|
418
|
+
*/
|
|
419
|
+
export interface OpenAPISpecLicense {
|
|
420
|
+
readonly name: string
|
|
421
|
+
readonly url?: string
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* @category models
|
|
426
|
+
* @since 1.0.0
|
|
427
|
+
*/
|
|
428
|
+
export interface OpenAPISpecServer {
|
|
429
|
+
readonly url: string
|
|
430
|
+
readonly description?: string
|
|
431
|
+
readonly variables?: Record<string, OpenAPISpecServerVariable>
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* @category models
|
|
436
|
+
* @since 1.0.0
|
|
437
|
+
*/
|
|
438
|
+
export interface OpenAPISpecServerVariable {
|
|
439
|
+
readonly default: string
|
|
440
|
+
readonly enum?: [string, ...Array<string>]
|
|
441
|
+
readonly description?: string
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* @category models
|
|
446
|
+
* @since 1.0.0
|
|
447
|
+
*/
|
|
448
|
+
export type OpenAPISpecPaths = ReadonlyRecord<
|
|
449
|
+
string,
|
|
450
|
+
OpenAPISpecPathItem
|
|
451
|
+
>
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* @category models
|
|
455
|
+
* @since 1.0.0
|
|
456
|
+
*/
|
|
457
|
+
export type OpenAPISpecMethodName =
|
|
458
|
+
| "get"
|
|
459
|
+
| "put"
|
|
460
|
+
| "post"
|
|
461
|
+
| "delete"
|
|
462
|
+
| "options"
|
|
463
|
+
| "head"
|
|
464
|
+
| "patch"
|
|
465
|
+
| "trace"
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* @category models
|
|
469
|
+
* @since 1.0.0
|
|
470
|
+
*/
|
|
471
|
+
export type OpenAPISpecPathItem =
|
|
472
|
+
& {
|
|
473
|
+
readonly [K in OpenAPISpecMethodName]?: OpenAPISpecOperation
|
|
474
|
+
}
|
|
475
|
+
& {
|
|
476
|
+
readonly summary?: string
|
|
477
|
+
readonly description?: string
|
|
478
|
+
readonly parameters?: Array<OpenAPISpecParameter>
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* @category models
|
|
483
|
+
* @since 1.0.0
|
|
484
|
+
*/
|
|
485
|
+
export type OpenAPIJSONSchema = JSONSchema.JsonSchema7
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* @category models
|
|
489
|
+
* @since 1.0.0
|
|
490
|
+
*/
|
|
491
|
+
export interface OpenAPISpecParameter {
|
|
492
|
+
readonly name: string
|
|
493
|
+
readonly in: "query" | "header" | "path" | "cookie"
|
|
494
|
+
readonly schema: OpenAPIJSONSchema
|
|
495
|
+
readonly description?: string
|
|
496
|
+
readonly required?: boolean
|
|
497
|
+
readonly deprecated?: boolean
|
|
498
|
+
readonly allowEmptyValue?: boolean
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* @category models
|
|
503
|
+
* @since 1.0.0
|
|
504
|
+
*/
|
|
505
|
+
export type OpenAPISpecResponses = Record<number, OpenApiSpecResponse>
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* @category models
|
|
509
|
+
* @since 1.0.0
|
|
510
|
+
*/
|
|
511
|
+
export type OpenApiSpecContentType = "application/json" | "application/xml" | "multipart/form-data" | "text/plain"
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* @category models
|
|
515
|
+
* @since 1.0.0
|
|
516
|
+
*/
|
|
517
|
+
export type OpenApiSpecContent = {
|
|
518
|
+
readonly [K in OpenApiSpecContentType]?: OpenApiSpecMediaType
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* @category models
|
|
523
|
+
* @since 1.0.0
|
|
524
|
+
*/
|
|
525
|
+
export interface OpenApiSpecResponseHeader {
|
|
526
|
+
readonly description?: string
|
|
527
|
+
readonly schema: OpenAPIJSONSchema
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* @category models
|
|
532
|
+
* @since 1.0.0
|
|
533
|
+
*/
|
|
534
|
+
export type OpenApiSpecResponseHeaders = ReadonlyRecord<
|
|
535
|
+
string,
|
|
536
|
+
OpenApiSpecResponseHeader
|
|
537
|
+
>
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* @category models
|
|
541
|
+
* @since 1.0.0
|
|
542
|
+
*/
|
|
543
|
+
export interface OpenApiSpecResponse {
|
|
544
|
+
readonly content?: OpenApiSpecContent
|
|
545
|
+
readonly headers?: OpenApiSpecResponseHeaders
|
|
546
|
+
readonly description: string
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* @category models
|
|
551
|
+
* @since 1.0.0
|
|
552
|
+
*/
|
|
553
|
+
export interface OpenApiSpecMediaType {
|
|
554
|
+
readonly schema?: OpenAPIJSONSchema
|
|
555
|
+
readonly example?: object
|
|
556
|
+
readonly description?: string
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* @category models
|
|
561
|
+
* @since 1.0.0
|
|
562
|
+
*/
|
|
563
|
+
export interface OpenAPISpecRequestBody {
|
|
564
|
+
readonly content: OpenApiSpecContent
|
|
565
|
+
readonly description?: string
|
|
566
|
+
readonly required?: boolean
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* @category models
|
|
571
|
+
* @since 1.0.0
|
|
572
|
+
*/
|
|
573
|
+
export interface OpenAPIComponents {
|
|
574
|
+
readonly schemas?: ReadonlyRecord<string, OpenAPIJSONSchema>
|
|
575
|
+
readonly securitySchemes?: ReadonlyRecord<string, OpenAPISecurityScheme>
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* @category models
|
|
580
|
+
* @since 1.0.0
|
|
581
|
+
*/
|
|
582
|
+
export interface OpenAPIHTTPSecurityScheme {
|
|
583
|
+
readonly type: "http"
|
|
584
|
+
readonly description?: string
|
|
585
|
+
readonly scheme: "bearer" | "basic" | string
|
|
586
|
+
/* only for scheme: 'bearer' */
|
|
587
|
+
readonly bearerFormat?: string
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* @category models
|
|
592
|
+
* @since 1.0.0
|
|
593
|
+
*/
|
|
594
|
+
export interface OpenAPIApiKeySecurityScheme {
|
|
595
|
+
readonly type: "apiKey"
|
|
596
|
+
readonly description?: string
|
|
597
|
+
readonly name: string
|
|
598
|
+
readonly in: "query" | "header" | "cookie"
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* @category models
|
|
603
|
+
* @since 1.0.0
|
|
604
|
+
*/
|
|
605
|
+
export interface OpenAPIMutualTLSSecurityScheme {
|
|
606
|
+
readonly type: "mutualTLS"
|
|
607
|
+
readonly description?: string
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* @category models
|
|
612
|
+
* @since 1.0.0
|
|
613
|
+
*/
|
|
614
|
+
export interface OpenAPIOAuth2SecurityScheme {
|
|
615
|
+
readonly type: "oauth2"
|
|
616
|
+
readonly description?: string
|
|
617
|
+
readonly flows: ReadonlyRecord<
|
|
618
|
+
"implicit" | "password" | "clientCredentials" | "authorizationCode",
|
|
619
|
+
ReadonlyRecord<string, unknown>
|
|
620
|
+
>
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* @category models
|
|
625
|
+
* @since 1.0.0
|
|
626
|
+
*/
|
|
627
|
+
export interface OpenAPIOpenIdConnectSecurityScheme {
|
|
628
|
+
readonly type: "openIdConnect"
|
|
629
|
+
readonly description?: string
|
|
630
|
+
readonly openIdConnectUrl: string
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* @category models
|
|
635
|
+
* @since 1.0.0
|
|
636
|
+
*/
|
|
637
|
+
export type OpenAPISecurityScheme =
|
|
638
|
+
| OpenAPIHTTPSecurityScheme
|
|
639
|
+
| OpenAPIApiKeySecurityScheme
|
|
640
|
+
| OpenAPIMutualTLSSecurityScheme
|
|
641
|
+
| OpenAPIOAuth2SecurityScheme
|
|
642
|
+
| OpenAPIOpenIdConnectSecurityScheme
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* @category models
|
|
646
|
+
* @since 1.0.0
|
|
647
|
+
*/
|
|
648
|
+
export type OpenAPISecurityRequirement = ReadonlyRecord<string, Array<string>>
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* @category models
|
|
652
|
+
* @since 1.0.0
|
|
653
|
+
*/
|
|
654
|
+
export interface OpenAPISpecOperation {
|
|
655
|
+
readonly requestBody?: OpenAPISpecRequestBody
|
|
656
|
+
readonly responses?: OpenAPISpecResponses
|
|
657
|
+
readonly operationId?: string
|
|
658
|
+
readonly description?: string
|
|
659
|
+
readonly parameters?: Array<OpenAPISpecParameter>
|
|
660
|
+
readonly summary?: string
|
|
661
|
+
readonly deprecated?: boolean
|
|
662
|
+
readonly tags?: Array<string>
|
|
663
|
+
readonly security?: Array<OpenAPISecurityRequirement>
|
|
664
|
+
readonly externalDocs?: OpenAPISpecExternalDocs
|
|
665
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -38,6 +38,51 @@ export * as FileSystem from "./FileSystem.js"
|
|
|
38
38
|
*/
|
|
39
39
|
export * as Headers from "./Headers.js"
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* @since 1.0.0
|
|
43
|
+
*/
|
|
44
|
+
export * as HttpApi from "./HttpApi.js"
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @since 1.0.0
|
|
48
|
+
*/
|
|
49
|
+
export * as HttpApiBuilder from "./HttpApiBuilder.js"
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @since 1.0.0
|
|
53
|
+
*/
|
|
54
|
+
export * as HttpApiClient from "./HttpApiClient.js"
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
*/
|
|
59
|
+
export * as HttpApiEndpoint from "./HttpApiEndpoint.js"
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @since 1.0.0
|
|
63
|
+
*/
|
|
64
|
+
export * as HttpApiError from "./HttpApiError.js"
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @since 1.0.0
|
|
68
|
+
*/
|
|
69
|
+
export * as HttpApiGroup from "./HttpApiGroup.js"
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @since 1.0.0
|
|
73
|
+
*/
|
|
74
|
+
export * as HttpApiSchema from "./HttpApiSchema.js"
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @since 1.0.0
|
|
78
|
+
*/
|
|
79
|
+
export * as HttpApiSecurity from "./HttpApiSecurity.js"
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @since 1.0.0
|
|
83
|
+
*/
|
|
84
|
+
export * as HttpApiSwagger from "./HttpApiSwagger.js"
|
|
85
|
+
|
|
41
86
|
/**
|
|
42
87
|
* @since 1.0.0
|
|
43
88
|
*/
|
|
@@ -139,6 +184,11 @@ export * as KeyValueStore from "./KeyValueStore.js"
|
|
|
139
184
|
*/
|
|
140
185
|
export * as Multipart from "./Multipart.js"
|
|
141
186
|
|
|
187
|
+
/**
|
|
188
|
+
* @since 1.0.0
|
|
189
|
+
*/
|
|
190
|
+
export * as OpenApi from "./OpenApi.js"
|
|
191
|
+
|
|
142
192
|
/**
|
|
143
193
|
* @since 1.0.0
|
|
144
194
|
*/
|