@effect/platform 0.62.4 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as AST from "@effect/schema/AST"
|
|
5
|
+
import * as Schema from "@effect/schema/Schema"
|
|
6
|
+
import type { Brand } from "effect/Brand"
|
|
7
|
+
import type { LazyArg } from "effect/Function"
|
|
8
|
+
import { constVoid, dual } from "effect/Function"
|
|
9
|
+
import * as Struct from "effect/Struct"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @since 1.0.0
|
|
13
|
+
* @category annotations
|
|
14
|
+
*/
|
|
15
|
+
export const AnnotationMultipart: unique symbol = Symbol.for(
|
|
16
|
+
"@effect/platform/HttpApiSchema/AnnotationMultipart"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @since 1.0.0
|
|
21
|
+
* @category annotations
|
|
22
|
+
*/
|
|
23
|
+
export const AnnotationStatus: unique symbol = Symbol.for("@effect/platform/HttpApiSchema/AnnotationStatus")
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @since 1.0.0
|
|
27
|
+
* @category annotations
|
|
28
|
+
*/
|
|
29
|
+
export const AnnotationEmptyDecodeable: unique symbol = Symbol.for(
|
|
30
|
+
"@effect/platform/HttpApiSchema/AnnotationEmptyDecodeable"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @since 1.0.0
|
|
35
|
+
* @category annotations
|
|
36
|
+
*/
|
|
37
|
+
export const AnnotationEncoding: unique symbol = Symbol.for("@effect/platform/HttpApiSchema/AnnotationEncoding")
|
|
38
|
+
|
|
39
|
+
const mergedAnnotations = (ast: AST.AST): Record<symbol, unknown> =>
|
|
40
|
+
ast._tag === "Transformation" ?
|
|
41
|
+
{
|
|
42
|
+
...ast.to.annotations,
|
|
43
|
+
...ast.annotations
|
|
44
|
+
} :
|
|
45
|
+
ast.annotations
|
|
46
|
+
|
|
47
|
+
const getAnnotation = <A>(ast: AST.AST, key: symbol): A | undefined => mergedAnnotations(ast)[key] as A
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @since 1.0.0
|
|
51
|
+
* @category annotations
|
|
52
|
+
*/
|
|
53
|
+
export const getStatus = (ast: AST.AST, defaultStatus: number): number =>
|
|
54
|
+
getAnnotation<number>(ast, AnnotationStatus) ?? defaultStatus
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
* @category annotations
|
|
59
|
+
*/
|
|
60
|
+
export const getEmptyDecodeable = (ast: AST.AST): boolean =>
|
|
61
|
+
getAnnotation<boolean>(ast, AnnotationEmptyDecodeable) ?? false
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @since 1.0.0
|
|
65
|
+
* @category annotations
|
|
66
|
+
*/
|
|
67
|
+
export const getMultipart = (ast: AST.AST): boolean => getAnnotation<boolean>(ast, AnnotationMultipart) ?? false
|
|
68
|
+
|
|
69
|
+
const encodingJson: Encoding = {
|
|
70
|
+
kind: "Json",
|
|
71
|
+
contentType: "application/json"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @since 1.0.0
|
|
76
|
+
* @category annotations
|
|
77
|
+
*/
|
|
78
|
+
export const getEncoding = (ast: AST.AST): Encoding => getAnnotation<Encoding>(ast, AnnotationEncoding) ?? encodingJson
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @since 1.0.0
|
|
82
|
+
* @category annotations
|
|
83
|
+
*/
|
|
84
|
+
export const annotations = <A>(
|
|
85
|
+
annotations: Schema.Annotations.Schema<NoInfer<A>> & {
|
|
86
|
+
readonly status?: number | undefined
|
|
87
|
+
}
|
|
88
|
+
): Schema.Annotations.Schema<A> => {
|
|
89
|
+
const result: Record<symbol, unknown> = Struct.omit(annotations, "status")
|
|
90
|
+
if (annotations.status !== undefined) {
|
|
91
|
+
result[AnnotationStatus] = annotations.status
|
|
92
|
+
}
|
|
93
|
+
return result
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @since 1.0.0
|
|
98
|
+
* @category reflection
|
|
99
|
+
*/
|
|
100
|
+
export const isVoid = (ast: AST.AST): boolean => {
|
|
101
|
+
switch (ast._tag) {
|
|
102
|
+
case "VoidKeyword": {
|
|
103
|
+
return true
|
|
104
|
+
}
|
|
105
|
+
case "Transformation": {
|
|
106
|
+
return isVoid(ast.from)
|
|
107
|
+
}
|
|
108
|
+
case "Suspend": {
|
|
109
|
+
return isVoid(ast.f())
|
|
110
|
+
}
|
|
111
|
+
default: {
|
|
112
|
+
return false
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @since 1.0.0
|
|
119
|
+
* @category reflection
|
|
120
|
+
*/
|
|
121
|
+
export const getStatusSuccessAST = (ast: AST.AST): number => getStatus(ast, isVoid(ast) ? 204 : 200)
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @since 1.0.0
|
|
125
|
+
* @category reflection
|
|
126
|
+
*/
|
|
127
|
+
export const getStatusSuccess = <A extends Schema.Schema.Any>(self: A): number => getStatusSuccessAST(self.ast)
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @since 1.0.0
|
|
131
|
+
* @category reflection
|
|
132
|
+
*/
|
|
133
|
+
export const getStatusErrorAST = (ast: AST.AST): number => getStatus(ast, 500)
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @since 1.0.0
|
|
137
|
+
* @category reflection
|
|
138
|
+
*/
|
|
139
|
+
export const getStatusError = <A extends Schema.Schema.All>(self: A): number => getStatusErrorAST(self.ast)
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @since 1.0.0
|
|
143
|
+
*/
|
|
144
|
+
export const UnionUnify = <A extends Schema.Schema.All, B extends Schema.Schema.All>(self: A, that: B): Schema.Schema<
|
|
145
|
+
A["Type"] | B["Type"],
|
|
146
|
+
A["Encoded"] | B["Encoded"],
|
|
147
|
+
A["Context"] | B["Context"]
|
|
148
|
+
> => {
|
|
149
|
+
const selfTypes = self.ast._tag === "Union" ? self.ast.types : [self.ast]
|
|
150
|
+
const thatTypes = that.ast._tag === "Union" ? that.ast.types : [that.ast]
|
|
151
|
+
return Schema.make(AST.Union.make([
|
|
152
|
+
...selfTypes,
|
|
153
|
+
...thatTypes
|
|
154
|
+
]))
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @since 1.0.0
|
|
159
|
+
* @category params
|
|
160
|
+
*/
|
|
161
|
+
export interface PathParams extends Schema.Record$<typeof Schema.String, typeof Schema.String> {}
|
|
162
|
+
|
|
163
|
+
type Void$ = typeof Schema.Void
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @since 1.0.0
|
|
167
|
+
* @category empty response
|
|
168
|
+
*/
|
|
169
|
+
export const Empty = (status: number): typeof Schema.Void => Schema.Void.annotations(annotations({ status }))
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @since 1.0.0
|
|
173
|
+
* @category empty response
|
|
174
|
+
*/
|
|
175
|
+
export interface asEmpty<
|
|
176
|
+
S extends Schema.Schema.Any
|
|
177
|
+
> extends Schema.transform<typeof Schema.Void, S> {}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @since 1.0.0
|
|
181
|
+
* @category empty response
|
|
182
|
+
*/
|
|
183
|
+
export const asEmpty: {
|
|
184
|
+
<S extends Schema.Schema.Any>(options: {
|
|
185
|
+
readonly status: number
|
|
186
|
+
readonly decode: LazyArg<Schema.Schema.Type<S>>
|
|
187
|
+
}): (self: S) => asEmpty<S>
|
|
188
|
+
<S extends Schema.Schema.Any>(
|
|
189
|
+
self: S,
|
|
190
|
+
options: {
|
|
191
|
+
readonly status: number
|
|
192
|
+
readonly decode?: LazyArg<Schema.Schema.Type<S>>
|
|
193
|
+
}
|
|
194
|
+
): asEmpty<S>
|
|
195
|
+
} = dual(
|
|
196
|
+
2,
|
|
197
|
+
<S extends Schema.Schema.Any>(
|
|
198
|
+
self: S,
|
|
199
|
+
options: {
|
|
200
|
+
readonly status: number
|
|
201
|
+
readonly decode?: LazyArg<Schema.Schema.Type<S>>
|
|
202
|
+
}
|
|
203
|
+
): asEmpty<S> =>
|
|
204
|
+
Schema.transform(
|
|
205
|
+
Schema.Void,
|
|
206
|
+
Schema.typeSchema(self),
|
|
207
|
+
{
|
|
208
|
+
decode: options.decode as any,
|
|
209
|
+
encode: constVoid
|
|
210
|
+
}
|
|
211
|
+
).annotations(annotations({
|
|
212
|
+
status: options.status,
|
|
213
|
+
[AnnotationEmptyDecodeable]: true
|
|
214
|
+
})) as any
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @since 1.0.0
|
|
219
|
+
* @category empty response
|
|
220
|
+
*/
|
|
221
|
+
export interface Created extends Void$ {
|
|
222
|
+
readonly _: unique symbol
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* @since 1.0.0
|
|
227
|
+
* @category empty response
|
|
228
|
+
*/
|
|
229
|
+
export const Created: Created = Empty(201) as any
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @since 1.0.0
|
|
233
|
+
* @category empty response
|
|
234
|
+
*/
|
|
235
|
+
export interface Accepted extends Void$ {
|
|
236
|
+
readonly _: unique symbol
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @since 1.0.0
|
|
241
|
+
* @category empty response
|
|
242
|
+
*/
|
|
243
|
+
export const Accepted: Accepted = Empty(202) as any
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* @since 1.0.0
|
|
247
|
+
* @category empty response
|
|
248
|
+
*/
|
|
249
|
+
export interface NoContent extends Void$ {
|
|
250
|
+
readonly _: unique symbol
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @since 1.0.0
|
|
255
|
+
* @category empty response
|
|
256
|
+
*/
|
|
257
|
+
export const NoContent: NoContent = Empty(204) as any
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @since 1.0.0
|
|
261
|
+
* @category multipart
|
|
262
|
+
*/
|
|
263
|
+
export const MultipartTypeId: unique symbol = Symbol.for("@effect/platform/HttpApiSchema/Multipart")
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* @since 1.0.0
|
|
267
|
+
* @category multipart
|
|
268
|
+
*/
|
|
269
|
+
export type MultipartTypeId = typeof MultipartTypeId
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @since 1.0.0
|
|
273
|
+
* @category multipart
|
|
274
|
+
*/
|
|
275
|
+
export interface Multipart<S extends Schema.Schema.Any>
|
|
276
|
+
extends
|
|
277
|
+
Schema.Schema<Schema.Schema.Type<S> & Brand<MultipartTypeId>, Schema.Schema.Encoded<S>, Schema.Schema.Context<S>>
|
|
278
|
+
{}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* @since 1.0.0
|
|
282
|
+
* @category multipart
|
|
283
|
+
*/
|
|
284
|
+
export const Multipart = <S extends Schema.Schema.Any>(self: S): Multipart<S> =>
|
|
285
|
+
self.annotations({
|
|
286
|
+
[AnnotationMultipart]: true
|
|
287
|
+
}) as any
|
|
288
|
+
|
|
289
|
+
const defaultContentType = (encoding: Encoding["kind"]) => {
|
|
290
|
+
switch (encoding) {
|
|
291
|
+
case "Json": {
|
|
292
|
+
return "application/json"
|
|
293
|
+
}
|
|
294
|
+
case "UrlParams": {
|
|
295
|
+
return "application/x-www-form-urlencoded"
|
|
296
|
+
}
|
|
297
|
+
case "Uint8Array": {
|
|
298
|
+
return "application/octet-stream"
|
|
299
|
+
}
|
|
300
|
+
case "Text": {
|
|
301
|
+
return "text/plain"
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* @since 1.0.0
|
|
308
|
+
* @category encoding
|
|
309
|
+
*/
|
|
310
|
+
export interface Encoding {
|
|
311
|
+
readonly kind: "Json" | "UrlParams" | "Uint8Array" | "Text"
|
|
312
|
+
readonly contentType: string
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* @since 1.0.0
|
|
317
|
+
* @category encoding
|
|
318
|
+
*/
|
|
319
|
+
export declare namespace Encoding {
|
|
320
|
+
/**
|
|
321
|
+
* @since 1.0.0
|
|
322
|
+
* @category encoding
|
|
323
|
+
*/
|
|
324
|
+
export type Validate<A extends Schema.Schema.Any, Kind extends Encoding["kind"]> = Kind extends "Json" ? {}
|
|
325
|
+
: Kind extends "UrlParams" ? [A["Encoded"]] extends [Readonly<Record<string, string | undefined>>] ? {}
|
|
326
|
+
: `'UrlParams' kind can only be encoded to 'Record<string, string | undefined>'`
|
|
327
|
+
: Kind extends "Uint8Array" ?
|
|
328
|
+
[A["Encoded"]] extends [Uint8Array] ? {} : `'Uint8Array' kind can only be encoded to 'Uint8Array'`
|
|
329
|
+
: Kind extends "Text" ? [A["Encoded"]] extends [string] ? {} : `'Text' kind can only be encoded to 'string'`
|
|
330
|
+
: never
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @since 1.0.0
|
|
335
|
+
* @category encoding
|
|
336
|
+
*/
|
|
337
|
+
export const withEncoding: {
|
|
338
|
+
<A extends Schema.Schema.Any, Kind extends Encoding["kind"]>(
|
|
339
|
+
options: {
|
|
340
|
+
readonly kind: Kind
|
|
341
|
+
readonly contentType?: string | undefined
|
|
342
|
+
} & Encoding.Validate<A, Kind>
|
|
343
|
+
): (self: A) => A
|
|
344
|
+
<A extends Schema.Schema.Any, Kind extends Encoding["kind"]>(
|
|
345
|
+
self: A,
|
|
346
|
+
options: {
|
|
347
|
+
readonly kind: Kind
|
|
348
|
+
readonly contentType?: string | undefined
|
|
349
|
+
} & Encoding.Validate<A, Kind>
|
|
350
|
+
): A
|
|
351
|
+
} = dual(2, <A extends Schema.Schema.Any>(self: A, options: {
|
|
352
|
+
readonly kind: Encoding["kind"]
|
|
353
|
+
readonly contentType?: string | undefined
|
|
354
|
+
}): A =>
|
|
355
|
+
self.annotations({
|
|
356
|
+
[AnnotationEncoding]: {
|
|
357
|
+
kind: options.kind,
|
|
358
|
+
contentType: options.contentType ?? defaultContentType(options.kind)
|
|
359
|
+
},
|
|
360
|
+
...(options.kind === "Uint8Array" ?
|
|
361
|
+
{
|
|
362
|
+
jsonSchema: {
|
|
363
|
+
type: "string",
|
|
364
|
+
format: "binary"
|
|
365
|
+
}
|
|
366
|
+
} :
|
|
367
|
+
undefined)
|
|
368
|
+
}) as any)
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* @since 1.0.0
|
|
372
|
+
* @category encoding
|
|
373
|
+
*/
|
|
374
|
+
export const Text = (options?: {
|
|
375
|
+
readonly contentType?: string
|
|
376
|
+
}): typeof Schema.String => withEncoding(Schema.String, { kind: "Text", ...options })
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* @since 1.0.0
|
|
380
|
+
* @category encoding
|
|
381
|
+
*/
|
|
382
|
+
export const Uint8Array = (options?: {
|
|
383
|
+
readonly contentType?: string
|
|
384
|
+
}): typeof Schema.Uint8ArrayFromSelf => withEncoding(Schema.Uint8ArrayFromSelf, { kind: "Uint8Array", ...options })
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as Context from "effect/Context"
|
|
5
|
+
import { dual } from "effect/Function"
|
|
6
|
+
import { type Pipeable, pipeArguments } from "effect/Pipeable"
|
|
7
|
+
import type { Redacted } from "effect/Redacted"
|
|
8
|
+
import type { Covariant } from "effect/Types"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @since 1.0.0
|
|
12
|
+
* @category type ids
|
|
13
|
+
*/
|
|
14
|
+
export const TypeId: unique symbol = Symbol.for("@effect/platform/HttpApiSecurity")
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @since 1.0.0
|
|
18
|
+
* @category type ids
|
|
19
|
+
*/
|
|
20
|
+
export type TypeId = typeof TypeId
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @since 1.0.0
|
|
24
|
+
* @category models
|
|
25
|
+
*/
|
|
26
|
+
export type HttpApiSecurity = Bearer | ApiKey | Basic
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @since 1.0.0
|
|
30
|
+
* @category models
|
|
31
|
+
*/
|
|
32
|
+
export declare namespace HttpApiSecurity {
|
|
33
|
+
/**
|
|
34
|
+
* @since 1.0.0
|
|
35
|
+
* @category models
|
|
36
|
+
*/
|
|
37
|
+
export interface Proto<out A> extends Pipeable {
|
|
38
|
+
readonly [TypeId]: {
|
|
39
|
+
readonly _A: Covariant<A>
|
|
40
|
+
}
|
|
41
|
+
readonly annotations: Context.Context<never>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @since 1.0.0
|
|
46
|
+
* @category models
|
|
47
|
+
*/
|
|
48
|
+
export type Type<A extends HttpApiSecurity> = A extends Proto<infer Out> ? Out : never
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @since 1.0.0
|
|
53
|
+
* @category models
|
|
54
|
+
*/
|
|
55
|
+
export interface Bearer extends HttpApiSecurity.Proto<Redacted> {
|
|
56
|
+
readonly _tag: "Bearer"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @since 1.0.0
|
|
61
|
+
* @category models
|
|
62
|
+
*/
|
|
63
|
+
export interface ApiKey extends HttpApiSecurity.Proto<Redacted> {
|
|
64
|
+
readonly _tag: "ApiKey"
|
|
65
|
+
readonly in: "header" | "query" | "cookie"
|
|
66
|
+
readonly key: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @since 1.0.0
|
|
71
|
+
* @category models
|
|
72
|
+
*/
|
|
73
|
+
export interface Basic extends HttpApiSecurity.Proto<Credentials> {
|
|
74
|
+
readonly _tag: "Basic"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @since 1.0.0
|
|
79
|
+
* @category models
|
|
80
|
+
*/
|
|
81
|
+
export interface Credentials {
|
|
82
|
+
readonly username: string
|
|
83
|
+
readonly password: Redacted
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const Proto = {
|
|
87
|
+
[TypeId]: TypeId,
|
|
88
|
+
pipe() {
|
|
89
|
+
return pipeArguments(this, arguments)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Create an Bearer token security scheme.
|
|
95
|
+
*
|
|
96
|
+
* You can implement some api middleware for this security scheme using
|
|
97
|
+
* `HttpApiBuilder.middlewareSecurity`.
|
|
98
|
+
*
|
|
99
|
+
* @since 1.0.0
|
|
100
|
+
* @category constructors
|
|
101
|
+
*/
|
|
102
|
+
export const bearer: Bearer = Object.assign(Object.create(Proto), {
|
|
103
|
+
_tag: "Bearer",
|
|
104
|
+
annotations: Context.empty()
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Create an API key security scheme.
|
|
109
|
+
*
|
|
110
|
+
* You can implement some api middleware for this security scheme using
|
|
111
|
+
* `HttpApiBuilder.middlewareSecurity`.
|
|
112
|
+
*
|
|
113
|
+
* To set the correct cookie in a handler, you can use
|
|
114
|
+
* `HttpApiBuilder.securitySetCookie`.
|
|
115
|
+
*
|
|
116
|
+
* @since 1.0.0
|
|
117
|
+
* @category constructors
|
|
118
|
+
*/
|
|
119
|
+
export const apiKey = (options: {
|
|
120
|
+
readonly key: string
|
|
121
|
+
readonly in?: "header" | "query" | "cookie" | undefined
|
|
122
|
+
}): ApiKey =>
|
|
123
|
+
Object.assign(Object.create(Proto), {
|
|
124
|
+
_tag: "ApiKey",
|
|
125
|
+
key: options.key,
|
|
126
|
+
in: options.in ?? "header",
|
|
127
|
+
annotations: Context.empty()
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @since 1.0.0
|
|
132
|
+
* @category constructors
|
|
133
|
+
*/
|
|
134
|
+
export const basic: Basic = Object.assign(Object.create(Proto), {
|
|
135
|
+
_tag: "Basic",
|
|
136
|
+
annotations: Context.empty()
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @since 1.0.0
|
|
141
|
+
* @category annotations
|
|
142
|
+
*/
|
|
143
|
+
export const annotateMerge: {
|
|
144
|
+
<I>(context: Context.Context<I>): <A extends HttpApiSecurity>(self: A) => A
|
|
145
|
+
<A extends HttpApiSecurity, I>(self: A, context: Context.Context<I>): A
|
|
146
|
+
} = dual(
|
|
147
|
+
2,
|
|
148
|
+
<A extends HttpApiSecurity, I>(self: A, context: Context.Context<I>): A =>
|
|
149
|
+
Object.assign(Object.create(Proto), {
|
|
150
|
+
...self,
|
|
151
|
+
annotations: Context.merge(self.annotations, context)
|
|
152
|
+
})
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @since 1.0.0
|
|
157
|
+
* @category annotations
|
|
158
|
+
*/
|
|
159
|
+
export const annotate: {
|
|
160
|
+
<I, S>(tag: Context.Tag<I, S>, value: S): <A extends HttpApiSecurity>(self: A) => A
|
|
161
|
+
<A extends HttpApiSecurity, I, S>(self: A, tag: Context.Tag<I, S>, value: S): A
|
|
162
|
+
} = dual(
|
|
163
|
+
3,
|
|
164
|
+
<A extends HttpApiSecurity, I, S>(self: A, tag: Context.Tag<I, S>, value: S): A =>
|
|
165
|
+
Object.assign(Object.create(Proto), {
|
|
166
|
+
...self,
|
|
167
|
+
annotations: Context.add(self.annotations, tag, value)
|
|
168
|
+
})
|
|
169
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as Effect from "effect/Effect"
|
|
5
|
+
import type { Layer } from "effect/Layer"
|
|
6
|
+
import { HttpApi } from "./HttpApi.js"
|
|
7
|
+
import { Router } from "./HttpApiBuilder.js"
|
|
8
|
+
import * as HttpServerResponse from "./HttpServerResponse.js"
|
|
9
|
+
import * as internal from "./internal/apiSwagger.js"
|
|
10
|
+
import * as OpenApi from "./OpenApi.js"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @since 1.0.0
|
|
14
|
+
* @category layers
|
|
15
|
+
*/
|
|
16
|
+
export const layer = (options?: {
|
|
17
|
+
readonly path?: `/${string}` | undefined
|
|
18
|
+
}): Layer<never, never, HttpApi.Service> =>
|
|
19
|
+
Router.use((router) =>
|
|
20
|
+
Effect.gen(function*() {
|
|
21
|
+
const api = yield* HttpApi
|
|
22
|
+
const spec = OpenApi.fromApi(api)
|
|
23
|
+
const response = HttpServerResponse.html(`<!DOCTYPE html>
|
|
24
|
+
<html lang="en">
|
|
25
|
+
<head>
|
|
26
|
+
<meta charset="utf-8" />
|
|
27
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
28
|
+
<title>${spec.info.title} Documentation</title>
|
|
29
|
+
<style>${internal.css}</style>
|
|
30
|
+
</head>
|
|
31
|
+
<body>
|
|
32
|
+
<div id="swagger-ui"></div>
|
|
33
|
+
<script>
|
|
34
|
+
${internal.javascript}
|
|
35
|
+
window.onload = () => {
|
|
36
|
+
window.ui = SwaggerUIBundle({
|
|
37
|
+
spec: ${JSON.stringify(spec)},
|
|
38
|
+
dom_id: '#swagger-ui',
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
</script>
|
|
42
|
+
</body>
|
|
43
|
+
</html>`)
|
|
44
|
+
yield* router.get(options?.path ?? "/docs", Effect.succeed(response))
|
|
45
|
+
})
|
|
46
|
+
)
|
package/src/HttpMethod.ts
CHANGED
|
@@ -11,7 +11,25 @@ export type HttpMethod =
|
|
|
11
11
|
| "HEAD"
|
|
12
12
|
| "OPTIONS"
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @since 1.0.0
|
|
16
|
+
* @category models
|
|
17
|
+
*/
|
|
18
|
+
export declare namespace HttpMethod {
|
|
19
|
+
/**
|
|
20
|
+
* @since 1.0.0
|
|
21
|
+
* @category models
|
|
22
|
+
*/
|
|
23
|
+
export type NoBody = "GET" | "HEAD" | "OPTIONS"
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @since 1.0.0
|
|
27
|
+
* @category models
|
|
28
|
+
*/
|
|
29
|
+
export type WithBody = Exclude<HttpMethod, NoBody>
|
|
30
|
+
}
|
|
31
|
+
|
|
14
32
|
/**
|
|
15
33
|
* @since 1.0.0
|
|
16
34
|
*/
|
|
17
|
-
export const hasBody = (method: HttpMethod): boolean => method !== "GET" && method !== "HEAD"
|
|
35
|
+
export const hasBody = (method: HttpMethod): boolean => method !== "GET" && method !== "HEAD" && method !== "OPTIONS"
|
package/src/HttpRouter.ts
CHANGED
|
@@ -357,6 +357,15 @@ export const makeRoute: <E, R>(
|
|
|
357
357
|
options?: { readonly prefix?: string | undefined; readonly uninterruptible?: boolean | undefined } | undefined
|
|
358
358
|
) => Route<E, HttpRouter.ExcludeProvided<R>> = internal.makeRoute
|
|
359
359
|
|
|
360
|
+
/**
|
|
361
|
+
* @since 1.0.0
|
|
362
|
+
* @category utils
|
|
363
|
+
*/
|
|
364
|
+
export const prefixPath: {
|
|
365
|
+
(prefix: string): (self: string) => string
|
|
366
|
+
(self: string, prefix: string): string
|
|
367
|
+
} = internal.prefixPath
|
|
368
|
+
|
|
360
369
|
/**
|
|
361
370
|
* @since 1.0.0
|
|
362
371
|
* @category combinators
|
|
@@ -124,7 +124,7 @@ export const htmlStream: <A extends ReadonlyArray<Template.InterpolatedWithStrea
|
|
|
124
124
|
*/
|
|
125
125
|
export const json: (
|
|
126
126
|
body: unknown,
|
|
127
|
-
options?: Options.
|
|
127
|
+
options?: Options.WithContentType | undefined
|
|
128
128
|
) => Effect.Effect<HttpServerResponse, Body.HttpBodyError> = internal.json
|
|
129
129
|
|
|
130
130
|
/**
|
|
@@ -141,14 +141,14 @@ export const schemaJson: <A, I, R>(
|
|
|
141
141
|
* @since 1.0.0
|
|
142
142
|
* @category constructors
|
|
143
143
|
*/
|
|
144
|
-
export const unsafeJson: (body: unknown, options?: Options.
|
|
144
|
+
export const unsafeJson: (body: unknown, options?: Options.WithContentType | undefined) => HttpServerResponse =
|
|
145
145
|
internal.unsafeJson
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
148
|
* @since 1.0.0
|
|
149
149
|
* @category constructors
|
|
150
150
|
*/
|
|
151
|
-
export const urlParams: (body: UrlParams.Input, options?: Options.
|
|
151
|
+
export const urlParams: (body: UrlParams.Input, options?: Options.WithContentType | undefined) => HttpServerResponse =
|
|
152
152
|
internal.urlParams
|
|
153
153
|
|
|
154
154
|
/**
|