@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.
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
package/src/HttpApi.ts ADDED
@@ -0,0 +1,342 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ import * as AST from "@effect/schema/AST"
5
+ import type * as Schema from "@effect/schema/Schema"
6
+ import * as Chunk from "effect/Chunk"
7
+ import * as Context from "effect/Context"
8
+ import { dual } from "effect/Function"
9
+ import * as Option from "effect/Option"
10
+ import type { Pipeable } from "effect/Pipeable"
11
+ import { pipeArguments } from "effect/Pipeable"
12
+ import * as Predicate from "effect/Predicate"
13
+ import * as HttpApiEndpoint from "./HttpApiEndpoint.js"
14
+ import { HttpApiDecodeError } from "./HttpApiError.js"
15
+ import * as HttpApiGroup from "./HttpApiGroup.js"
16
+ import * as HttpApiSchema from "./HttpApiSchema.js"
17
+ import type { HttpMethod } from "./HttpMethod.js"
18
+ import type * as HttpRouter from "./HttpRouter.js"
19
+
20
+ /**
21
+ * @since 1.0.0
22
+ * @category type ids
23
+ */
24
+ export const TypeId: unique symbol = Symbol.for("@effect/platform/HttpApi")
25
+
26
+ /**
27
+ * @since 1.0.0
28
+ * @category type ids
29
+ */
30
+ export type TypeId = typeof TypeId
31
+
32
+ /**
33
+ * @since 1.0.0
34
+ * @category guards
35
+ */
36
+ export const isHttpApi = (u: unknown): u is HttpApi<any, any> => Predicate.hasProperty(u, TypeId)
37
+
38
+ /**
39
+ * An `HttpApi` represents a collection of `HttpApiGroup`s. You can use an `HttpApi` to
40
+ * represent your entire domain.
41
+ *
42
+ * @since 1.0.0
43
+ * @category models
44
+ */
45
+ export interface HttpApi<
46
+ out Groups extends HttpApiGroup.HttpApiGroup.Any = never,
47
+ in out Error = never,
48
+ out ErrorR = never
49
+ > extends Pipeable {
50
+ new(_: never): {}
51
+ readonly [TypeId]: TypeId
52
+ readonly groups: Chunk.Chunk<Groups>
53
+ readonly errorSchema: Schema.Schema<Error, unknown, ErrorR>
54
+ readonly annotations: Context.Context<never>
55
+ }
56
+
57
+ /**
58
+ * @since 1.0.0
59
+ * @category tags
60
+ */
61
+ export const HttpApi: Context.Tag<HttpApi.Service, HttpApi.Any> = Context.GenericTag<HttpApi.Service, HttpApi.Any>(
62
+ "@effect/platform/HttpApi"
63
+ )
64
+
65
+ /**
66
+ * @since 1.0.0
67
+ * @category models
68
+ */
69
+ export declare namespace HttpApi {
70
+ /**
71
+ * @since 1.0.0
72
+ * @category models
73
+ */
74
+ export interface Service {
75
+ readonly _: unique symbol
76
+ }
77
+
78
+ /**
79
+ * @since 1.0.0
80
+ * @category models
81
+ */
82
+ export interface Any extends Pipeable {
83
+ new(_: never): {}
84
+ readonly [TypeId]: TypeId
85
+ readonly groups: Chunk.Chunk<HttpApiGroup.HttpApiGroup.Any>
86
+ readonly errorSchema: Schema.Schema.All
87
+ readonly annotations: Context.Context<never>
88
+ }
89
+
90
+ /**
91
+ * @since 1.0.0
92
+ * @category models
93
+ */
94
+ export type Context<A> = A extends HttpApi<infer _Groups, infer _ApiError, infer _ApiErrorR>
95
+ ? _ApiErrorR | HttpApiGroup.HttpApiGroup.Context<_Groups>
96
+ : never
97
+ }
98
+
99
+ const Proto = {
100
+ [TypeId]: TypeId,
101
+ pipe() {
102
+ return pipeArguments(this, arguments)
103
+ }
104
+ }
105
+
106
+ const makeProto = <Groups extends HttpApiGroup.HttpApiGroup.Any, Error, ErrorR>(options: {
107
+ readonly groups: Chunk.Chunk<Groups>
108
+ readonly errorSchema: Schema.Schema<Error, unknown, ErrorR>
109
+ readonly annotations: Context.Context<never>
110
+ }): HttpApi<Groups, Error, ErrorR> => {
111
+ function HttpApi() {}
112
+ Object.setPrototypeOf(HttpApi, Proto)
113
+ return Object.assign(HttpApi, options) as any
114
+ }
115
+
116
+ /**
117
+ * An empty `HttpApi`. You can use this to start building your `HttpApi`.
118
+ *
119
+ * You can add groups to this `HttpApi` using the `addGroup` function.
120
+ *
121
+ * @since 1.0.0
122
+ * @category constructors
123
+ */
124
+ export const empty: HttpApi = makeProto({
125
+ groups: Chunk.empty(),
126
+ errorSchema: HttpApiDecodeError as any,
127
+ annotations: Context.empty()
128
+ })
129
+
130
+ /**
131
+ * Add a `HttpApiGroup` to an `HttpApi`.
132
+ *
133
+ * @since 1.0.0
134
+ * @category constructors
135
+ */
136
+ export const addGroup: {
137
+ <Group extends HttpApiGroup.HttpApiGroup.Any>(
138
+ group: Group
139
+ ): <Groups extends HttpApiGroup.HttpApiGroup.Any, Error, ErrorR>(
140
+ self: HttpApi<Groups, Error, ErrorR>
141
+ ) => HttpApi<Groups | Group, Error, ErrorR>
142
+ <Group extends HttpApiGroup.HttpApiGroup.Any>(
143
+ path: HttpRouter.PathInput,
144
+ group: Group
145
+ ): <Groups extends HttpApiGroup.HttpApiGroup.Any, Error, ErrorR>(
146
+ self: HttpApi<Groups, Error, ErrorR>
147
+ ) => HttpApi<Groups | Group, Error, ErrorR>
148
+ <Groups extends HttpApiGroup.HttpApiGroup.Any, Error, ErrorR, Group extends HttpApiGroup.HttpApiGroup.Any>(
149
+ self: HttpApi<Groups, Error, ErrorR>,
150
+ group: Group
151
+ ): HttpApi<Groups | Group, Error, ErrorR>
152
+ <Groups extends HttpApiGroup.HttpApiGroup.Any, Error, ErrorR, Group extends HttpApiGroup.HttpApiGroup.Any>(
153
+ self: HttpApi<Groups, Error, ErrorR>,
154
+ path: HttpRouter.PathInput,
155
+ group: Group
156
+ ): HttpApi<Groups | Group, Error, ErrorR>
157
+ } = dual(
158
+ (args) => isHttpApi(args[0]),
159
+ (
160
+ self: HttpApi.Any,
161
+ ...args: [group: HttpApiGroup.HttpApiGroup.Any] | [path: HttpRouter.PathInput, group: HttpApiGroup.HttpApiGroup.Any]
162
+ ): HttpApi.Any => {
163
+ const group = args.length === 1 ? args[0] : HttpApiGroup.prefix(args[1] as any, args[0])
164
+ return makeProto({
165
+ errorSchema: self.errorSchema as any,
166
+ annotations: self.annotations,
167
+ groups: Chunk.append(self.groups, group)
168
+ })
169
+ }
170
+ )
171
+ /**
172
+ * Add an error schema to an `HttpApi`, which is shared by all endpoints in the
173
+ * `HttpApi`.
174
+ *
175
+ * Useful for adding error types from middleware or other shared error types.
176
+ *
177
+ * @since 1.0.0
178
+ * @category errors
179
+ */
180
+ export const addError: {
181
+ <A, I, R>(
182
+ schema: Schema.Schema<A, I, R>,
183
+ annotations?: {
184
+ readonly status?: number | undefined
185
+ }
186
+ ): <Groups extends HttpApiGroup.HttpApiGroup.Any, Error, ErrorR>(
187
+ self: HttpApi<Groups, Error, ErrorR>
188
+ ) => HttpApi<Groups, Error | A, ErrorR | R>
189
+ <Groups extends HttpApiGroup.HttpApiGroup.Any, Error, ErrorR, A, I, R>(
190
+ self: HttpApi<Groups, Error, ErrorR>,
191
+ schema: Schema.Schema<A, I, R>,
192
+ annotations?: {
193
+ readonly status?: number | undefined
194
+ }
195
+ ): HttpApi<Groups, Error | A, ErrorR | R>
196
+ } = dual(
197
+ (args) => isHttpApi(args[0]),
198
+ <Groups extends HttpApiGroup.HttpApiGroup.Any, Error, ErrorR, A, I, R>(
199
+ self: HttpApi<Groups, Error, ErrorR>,
200
+ schema: Schema.Schema<A, I, R>,
201
+ annotations?: {
202
+ readonly status?: number | undefined
203
+ }
204
+ ): HttpApi<Groups, Error | A, ErrorR | R> =>
205
+ makeProto({
206
+ groups: self.groups,
207
+ annotations: self.annotations,
208
+ errorSchema: HttpApiSchema.UnionUnify(
209
+ self.errorSchema,
210
+ schema.annotations(HttpApiSchema.annotations({
211
+ status: annotations?.status ?? HttpApiSchema.getStatusError(schema)
212
+ }))
213
+ )
214
+ })
215
+ )
216
+
217
+ /**
218
+ * @since 1.0.0
219
+ * @category annotations
220
+ */
221
+ export const annotateMerge: {
222
+ <I>(context: Context.Context<I>): <A extends HttpApi.Any>(self: A) => A
223
+ <A extends HttpApi.Any, I>(self: A, context: Context.Context<I>): A
224
+ } = dual(
225
+ 2,
226
+ <A extends HttpApi.Any, I>(self: A, context: Context.Context<I>): A =>
227
+ makeProto({
228
+ groups: self.groups,
229
+ errorSchema: self.errorSchema as any,
230
+ annotations: Context.merge(self.annotations, context)
231
+ }) as A
232
+ )
233
+
234
+ /**
235
+ * @since 1.0.0
236
+ * @category annotations
237
+ */
238
+ export const annotate: {
239
+ <I, S>(tag: Context.Tag<I, S>, value: S): <A extends HttpApi.Any>(self: A) => A
240
+ <A extends HttpApi.Any, I, S>(self: A, tag: Context.Tag<I, S>, value: S): A
241
+ } = dual(
242
+ 3,
243
+ <A extends HttpApi.Any, I, S>(self: A, tag: Context.Tag<I, S>, value: S): A =>
244
+ makeProto({
245
+ groups: self.groups,
246
+ errorSchema: self.errorSchema as any,
247
+ annotations: Context.add(self.annotations, tag, value)
248
+ }) as A
249
+ )
250
+
251
+ /**
252
+ * Extract metadata from an `HttpApi`, which can be used to generate documentation
253
+ * or other tooling.
254
+ *
255
+ * See the `OpenApi` & `HttpApiClient` modules for examples of how to use this function.
256
+ *
257
+ * @since 1.0.0
258
+ * @category reflection
259
+ */
260
+ export const reflect = <Groups extends HttpApiGroup.HttpApiGroup.Any, Error, ErrorR>(
261
+ self: HttpApi<Groups, Error, ErrorR>,
262
+ options: {
263
+ readonly onGroup: (options: {
264
+ readonly group: HttpApiGroup.HttpApiGroup<string, any>
265
+ readonly mergedAnnotations: Context.Context<never>
266
+ }) => void
267
+ readonly onEndpoint: (options: {
268
+ readonly group: HttpApiGroup.HttpApiGroup<string, any>
269
+ readonly endpoint: HttpApiEndpoint.HttpApiEndpoint<string, HttpMethod>
270
+ readonly mergedAnnotations: Context.Context<never>
271
+ readonly successAST: Option.Option<AST.AST>
272
+ readonly successStatus: number
273
+ readonly successEncoding: HttpApiSchema.Encoding
274
+ readonly errors: ReadonlyMap<number, Option.Option<AST.AST>>
275
+ }) => void
276
+ }
277
+ ) => {
278
+ const apiErrors = extractErrors(self.errorSchema.ast, new Map())
279
+
280
+ const groups = self.groups as Iterable<HttpApiGroup.HttpApiGroup<string, any>>
281
+ for (const group of groups) {
282
+ const groupErrors = extractErrors(group.errorSchema.ast, apiErrors)
283
+ const groupAnnotations = Context.merge(self.annotations, group.annotations)
284
+ options.onGroup({
285
+ group,
286
+ mergedAnnotations: groupAnnotations
287
+ })
288
+ const endpoints = group.endpoints as Iterable<HttpApiEndpoint.HttpApiEndpoint<string, HttpMethod>>
289
+ for (const endpoint of endpoints) {
290
+ options.onEndpoint({
291
+ group,
292
+ endpoint,
293
+ mergedAnnotations: Context.merge(groupAnnotations, endpoint.annotations),
294
+ successAST: HttpApiEndpoint.schemaSuccess(endpoint).pipe(
295
+ Option.map((schema) => schema.ast)
296
+ ),
297
+ successStatus: HttpApiSchema.getStatusSuccess(endpoint.successSchema),
298
+ successEncoding: HttpApiSchema.getEncoding(endpoint.successSchema.ast),
299
+ errors: extractErrors(endpoint.errorSchema.ast, groupErrors)
300
+ })
301
+ }
302
+ }
303
+ }
304
+
305
+ // -------------------------------------------------------------------------------------
306
+
307
+ const extractErrors = (
308
+ ast: AST.AST,
309
+ inherited: ReadonlyMap<number, Option.Option<AST.AST>>
310
+ ): ReadonlyMap<number, Option.Option<AST.AST>> => {
311
+ const topStatus = HttpApiSchema.getStatusErrorAST(ast)
312
+ const errors = new Map(inherited)
313
+ function process(ast: AST.AST) {
314
+ if (ast._tag === "NeverKeyword") {
315
+ return
316
+ }
317
+ const status = HttpApiSchema.getStatus(ast, topStatus)
318
+ const emptyDecodeable = HttpApiSchema.getEmptyDecodeable(ast)
319
+ const current = errors.get(status) ?? Option.none()
320
+ errors.set(
321
+ status,
322
+ current.pipe(
323
+ Option.map((current) =>
324
+ AST.Union.make(
325
+ current._tag === "Union" ? [...current.types, ast] : [current, ast]
326
+ )
327
+ ),
328
+ Option.orElse(() =>
329
+ !emptyDecodeable && AST.encodedAST(ast)._tag === "VoidKeyword" ? Option.none() : Option.some(ast)
330
+ )
331
+ )
332
+ )
333
+ }
334
+ if (ast._tag === "Union") {
335
+ for (const type of ast.types) {
336
+ process(type)
337
+ }
338
+ } else {
339
+ process(ast)
340
+ }
341
+ return errors
342
+ }