@effect/platform 0.62.5 → 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.
Files changed (123) hide show
  1. package/HttpApi/package.json +6 -0
  2. package/HttpApiBuilder/package.json +6 -0
  3. package/HttpApiClient/package.json +6 -0
  4. package/HttpApiEndpoint/package.json +6 -0
  5. package/HttpApiError/package.json +6 -0
  6. package/HttpApiGroup/package.json +6 -0
  7. package/HttpApiSchema/package.json +6 -0
  8. package/HttpApiSecurity/package.json +6 -0
  9. package/HttpApiSwagger/package.json +6 -0
  10. package/OpenApi/package.json +6 -0
  11. package/README.md +863 -302
  12. package/dist/cjs/HttpApi.js +168 -0
  13. package/dist/cjs/HttpApi.js.map +1 -0
  14. package/dist/cjs/HttpApiBuilder.js +471 -0
  15. package/dist/cjs/HttpApiBuilder.js.map +1 -0
  16. package/dist/cjs/HttpApiClient.js +134 -0
  17. package/dist/cjs/HttpApiClient.js.map +1 -0
  18. package/dist/cjs/HttpApiEndpoint.js +181 -0
  19. package/dist/cjs/HttpApiEndpoint.js.map +1 -0
  20. package/dist/cjs/HttpApiError.js +69 -0
  21. package/dist/cjs/HttpApiError.js.map +1 -0
  22. package/dist/cjs/HttpApiGroup.js +151 -0
  23. package/dist/cjs/HttpApiGroup.js.map +1 -0
  24. package/dist/cjs/HttpApiSchema.js +241 -0
  25. package/dist/cjs/HttpApiSchema.js.map +1 -0
  26. package/dist/cjs/HttpApiSecurity.js +83 -0
  27. package/dist/cjs/HttpApiSecurity.js.map +1 -0
  28. package/dist/cjs/HttpApiSwagger.js +50 -0
  29. package/dist/cjs/HttpApiSwagger.js.map +1 -0
  30. package/dist/cjs/HttpMethod.js +1 -1
  31. package/dist/cjs/HttpMethod.js.map +1 -1
  32. package/dist/cjs/HttpRouter.js +6 -1
  33. package/dist/cjs/HttpRouter.js.map +1 -1
  34. package/dist/cjs/OpenApi.js +317 -0
  35. package/dist/cjs/OpenApi.js.map +1 -0
  36. package/dist/cjs/index.js +21 -1
  37. package/dist/cjs/internal/apiSwagger.js +2 -0
  38. package/dist/cjs/internal/apiSwagger.js.map +1 -0
  39. package/dist/cjs/internal/httpRouter.js +6 -1
  40. package/dist/cjs/internal/httpRouter.js.map +1 -1
  41. package/dist/cjs/internal/multipart.js +5 -1
  42. package/dist/cjs/internal/multipart.js.map +1 -1
  43. package/dist/dts/HttpApi.d.ts +156 -0
  44. package/dist/dts/HttpApi.d.ts.map +1 -0
  45. package/dist/dts/HttpApiBuilder.d.ts +296 -0
  46. package/dist/dts/HttpApiBuilder.d.ts.map +1 -0
  47. package/dist/dts/HttpApiClient.d.ts +31 -0
  48. package/dist/dts/HttpApiClient.d.ts.map +1 -0
  49. package/dist/dts/HttpApiEndpoint.d.ts +350 -0
  50. package/dist/dts/HttpApiEndpoint.d.ts.map +1 -0
  51. package/dist/dts/HttpApiError.d.ts +70 -0
  52. package/dist/dts/HttpApiError.d.ts.map +1 -0
  53. package/dist/dts/HttpApiGroup.d.ts +196 -0
  54. package/dist/dts/HttpApiGroup.d.ts.map +1 -0
  55. package/dist/dts/HttpApiSchema.d.ts +223 -0
  56. package/dist/dts/HttpApiSchema.d.ts.map +1 -0
  57. package/dist/dts/HttpApiSecurity.d.ts +122 -0
  58. package/dist/dts/HttpApiSecurity.d.ts.map +1 -0
  59. package/dist/dts/HttpApiSwagger.d.ts +10 -0
  60. package/dist/dts/HttpApiSwagger.d.ts.map +1 -0
  61. package/dist/dts/HttpMethod.d.ts +16 -0
  62. package/dist/dts/HttpMethod.d.ts.map +1 -1
  63. package/dist/dts/HttpRouter.d.ts +8 -0
  64. package/dist/dts/HttpRouter.d.ts.map +1 -1
  65. package/dist/dts/HttpServerResponse.d.ts +3 -3
  66. package/dist/dts/HttpServerResponse.d.ts.map +1 -1
  67. package/dist/dts/OpenApi.d.ts +348 -0
  68. package/dist/dts/OpenApi.d.ts.map +1 -0
  69. package/dist/dts/index.d.ts +40 -0
  70. package/dist/dts/index.d.ts.map +1 -1
  71. package/dist/dts/internal/apiSwagger.d.ts +2 -0
  72. package/dist/dts/internal/apiSwagger.d.ts.map +1 -0
  73. package/dist/dts/internal/httpRouter.d.ts.map +1 -1
  74. package/dist/esm/HttpApi.js +157 -0
  75. package/dist/esm/HttpApi.js.map +1 -0
  76. package/dist/esm/HttpApiBuilder.js +447 -0
  77. package/dist/esm/HttpApiBuilder.js.map +1 -0
  78. package/dist/esm/HttpApiClient.js +124 -0
  79. package/dist/esm/HttpApiClient.js.map +1 -0
  80. package/dist/esm/HttpApiEndpoint.js +169 -0
  81. package/dist/esm/HttpApiEndpoint.js.map +1 -0
  82. package/dist/esm/HttpApiError.js +59 -0
  83. package/dist/esm/HttpApiError.js.map +1 -0
  84. package/dist/esm/HttpApiGroup.js +140 -0
  85. package/dist/esm/HttpApiGroup.js.map +1 -0
  86. package/dist/esm/HttpApiSchema.js +217 -0
  87. package/dist/esm/HttpApiSchema.js.map +1 -0
  88. package/dist/esm/HttpApiSecurity.js +73 -0
  89. package/dist/esm/HttpApiSecurity.js.map +1 -0
  90. package/dist/esm/HttpApiSwagger.js +40 -0
  91. package/dist/esm/HttpApiSwagger.js.map +1 -0
  92. package/dist/esm/HttpMethod.js +1 -1
  93. package/dist/esm/HttpMethod.js.map +1 -1
  94. package/dist/esm/HttpRouter.js +5 -0
  95. package/dist/esm/HttpRouter.js.map +1 -1
  96. package/dist/esm/OpenApi.js +299 -0
  97. package/dist/esm/OpenApi.js.map +1 -0
  98. package/dist/esm/index.js +40 -0
  99. package/dist/esm/index.js.map +1 -1
  100. package/dist/esm/internal/apiSwagger.js +2 -0
  101. package/dist/esm/internal/apiSwagger.js.map +1 -0
  102. package/dist/esm/internal/httpRouter.js +5 -0
  103. package/dist/esm/internal/httpRouter.js.map +1 -1
  104. package/dist/esm/internal/multipart.js +5 -1
  105. package/dist/esm/internal/multipart.js.map +1 -1
  106. package/package.json +83 -3
  107. package/src/HttpApi.ts +342 -0
  108. package/src/HttpApiBuilder.ts +869 -0
  109. package/src/HttpApiClient.ts +228 -0
  110. package/src/HttpApiEndpoint.ts +818 -0
  111. package/src/HttpApiError.ts +113 -0
  112. package/src/HttpApiGroup.ts +365 -0
  113. package/src/HttpApiSchema.ts +384 -0
  114. package/src/HttpApiSecurity.ts +169 -0
  115. package/src/HttpApiSwagger.ts +46 -0
  116. package/src/HttpMethod.ts +19 -1
  117. package/src/HttpRouter.ts +9 -0
  118. package/src/HttpServerResponse.ts +3 -3
  119. package/src/OpenApi.ts +665 -0
  120. package/src/index.ts +50 -0
  121. package/src/internal/apiSwagger.ts +7 -0
  122. package/src/internal/httpRouter.ts +9 -0
  123. 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.WithContent | undefined
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.WithContent | undefined) => HttpServerResponse =
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.WithContent | undefined) => HttpServerResponse =
151
+ export const urlParams: (body: UrlParams.Input, options?: Options.WithContentType | undefined) => HttpServerResponse =
152
152
  internal.urlParams
153
153
 
154
154
  /**