@effect-app/infra 1.28.3 → 1.29.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/CHANGELOG.md +19 -0
- package/_cjs/api/{routing2 → routing}/DynamicMiddleware.cjs +1 -1
- package/_cjs/api/routing/DynamicMiddleware.cjs.map +1 -0
- package/_cjs/api/routing.cjs +135 -43
- package/_cjs/api/routing.cjs.map +1 -1
- package/dist/api/routing/DynamicMiddleware.d.ts.map +1 -0
- package/dist/api/routing/DynamicMiddleware.js +33 -0
- package/dist/api/routing.d.ts +100 -4
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +116 -9
- package/dist/services/Repository/ext.d.ts +4 -4
- package/package.json +9 -79
- package/src/api/{routing2 → routing}/DynamicMiddleware.ts +1 -1
- package/src/api/routing.ts +434 -8
- package/_cjs/api/routing/base.cjs +0 -135
- package/_cjs/api/routing/base.cjs.map +0 -1
- package/_cjs/api/routing/defaultErrorHandler.cjs +0 -62
- package/_cjs/api/routing/defaultErrorHandler.cjs.map +0 -1
- package/_cjs/api/routing/makeRequestHandler.cjs +0 -130
- package/_cjs/api/routing/makeRequestHandler.cjs.map +0 -1
- package/_cjs/api/routing/match.cjs +0 -40
- package/_cjs/api/routing/match.cjs.map +0 -1
- package/_cjs/api/routing/schema/routing.cjs +0 -136
- package/_cjs/api/routing/schema/routing.cjs.map +0 -1
- package/_cjs/api/routing2/DynamicMiddleware.cjs.map +0 -1
- package/_cjs/api/routing2.cjs +0 -142
- package/_cjs/api/routing2.cjs.map +0 -1
- package/_cjs/router.cjs +0 -170
- package/_cjs/router.cjs.map +0 -1
- package/dist/api/routing/base.d.ts +0 -97
- package/dist/api/routing/base.d.ts.map +0 -1
- package/dist/api/routing/base.js +0 -129
- package/dist/api/routing/defaultErrorHandler.d.ts +0 -19
- package/dist/api/routing/defaultErrorHandler.d.ts.map +0 -1
- package/dist/api/routing/defaultErrorHandler.js +0 -68
- package/dist/api/routing/makeRequestHandler.d.ts +0 -20
- package/dist/api/routing/makeRequestHandler.d.ts.map +0 -1
- package/dist/api/routing/makeRequestHandler.js +0 -151
- package/dist/api/routing/match.d.ts +0 -12
- package/dist/api/routing/match.d.ts.map +0 -1
- package/dist/api/routing/match.js +0 -27
- package/dist/api/routing/schema/routing.d.ts +0 -31
- package/dist/api/routing/schema/routing.d.ts.map +0 -1
- package/dist/api/routing/schema/routing.js +0 -123
- package/dist/api/routing2/DynamicMiddleware.d.ts.map +0 -1
- package/dist/api/routing2/DynamicMiddleware.js +0 -33
- package/dist/api/routing2.d.ts +0 -93
- package/dist/api/routing2.d.ts.map +0 -1
- package/dist/api/routing2.js +0 -117
- package/dist/router.d.ts +0 -91
- package/dist/router.d.ts.map +0 -1
- package/dist/router.js +0 -154
- package/src/api/routing/base.ts +0 -379
- package/src/api/routing/defaultErrorHandler.ts +0 -140
- package/src/api/routing/makeRequestHandler.ts +0 -343
- package/src/api/routing/match.ts +0 -128
- package/src/api/routing/schema/routing.ts +0 -237
- package/src/api/routing2.ts +0 -425
- package/src/api/writeDocs.ts.bak +0 -31
- package/src/router.ts +0 -619
- /package/dist/api/{routing2 → routing}/DynamicMiddleware.d.ts +0 -0
package/src/api/routing/base.ts
DELETED
|
@@ -1,379 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
|
|
3
|
-
/* eslint-disable @typescript-eslint/ban-types */
|
|
4
|
-
import { ValidationError } from "@effect-app/infra/errors"
|
|
5
|
-
import type { Struct } from "@effect/schema/Schema"
|
|
6
|
-
import * as S from "@effect/schema/Schema"
|
|
7
|
-
import type { Context } from "effect-app"
|
|
8
|
-
import { Cause, Effect, Exit, Option } from "effect-app"
|
|
9
|
-
import type { REST } from "effect-app/schema"
|
|
10
|
-
import type { Simplify } from "effect/Types"
|
|
11
|
-
|
|
12
|
-
export interface ReqHandler<
|
|
13
|
-
Req,
|
|
14
|
-
R,
|
|
15
|
-
E,
|
|
16
|
-
Res,
|
|
17
|
-
ReqSchema extends S.Schema.Any,
|
|
18
|
-
ResSchema extends S.Schema.Any,
|
|
19
|
-
CTX = any,
|
|
20
|
-
Context = any
|
|
21
|
-
> {
|
|
22
|
-
h: (r: Req, ctx: CTX) => Effect<Res, E, R>
|
|
23
|
-
Request: ReqSchema
|
|
24
|
-
Response: ResSchema
|
|
25
|
-
ResponseOpenApi: any
|
|
26
|
-
name: string
|
|
27
|
-
CTX: CTX
|
|
28
|
-
Context: Context
|
|
29
|
-
rt: "raw" | "d"
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export type ReqFromSchema<ReqSchema extends S.Schema<any, any, any>> = S.Schema.Type<ReqSchema>
|
|
33
|
-
|
|
34
|
-
export type Extr<T> = T extends { Model: S.Schema<any, any, any> } ? T["Model"]
|
|
35
|
-
: T extends S.Schema<any, any, any> ? T
|
|
36
|
-
: never
|
|
37
|
-
|
|
38
|
-
export type ResFromSchema<ResSchema> = S.Schema.Type<Extr<ResSchema>>
|
|
39
|
-
|
|
40
|
-
export type _R<T extends Effect<any, any, any>> = [T] extends [
|
|
41
|
-
Effect<any, any, infer R>
|
|
42
|
-
] ? R
|
|
43
|
-
: never
|
|
44
|
-
|
|
45
|
-
export type _E<T extends Effect<any, any, any>> = [T] extends [
|
|
46
|
-
Effect<any, infer E, any>
|
|
47
|
-
] ? E
|
|
48
|
-
: never
|
|
49
|
-
|
|
50
|
-
export type Encode<A, E> = (a: A) => E
|
|
51
|
-
|
|
52
|
-
// function getErrorMessage(current: ContextEntry) {
|
|
53
|
-
// switch (current.type.name) {
|
|
54
|
-
// case "NonEmptyString":
|
|
55
|
-
// return "Must not be empty"
|
|
56
|
-
// }
|
|
57
|
-
// if (current.type.name?.startsWith("NonEmptyReadonlyArray<")) {
|
|
58
|
-
// return "Must not be empty"
|
|
59
|
-
// }
|
|
60
|
-
// return `Invalid value specified`
|
|
61
|
-
// }
|
|
62
|
-
export function decodeErrors(x: unknown) {
|
|
63
|
-
return [x]
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// const ValidationApplicative = Effect.getValidationApplicative(
|
|
67
|
-
// makeAssociative<ReadonlyArray<{ type: string; errors: ReturnType<typeof decodeErrors> }>>(
|
|
68
|
-
// (l, r) => l.concat(r)
|
|
69
|
-
// )
|
|
70
|
-
// )
|
|
71
|
-
|
|
72
|
-
// const structValidation = DSL.structF(ValidationApplicative)
|
|
73
|
-
export function parseRequestParams<
|
|
74
|
-
PathA extends Struct.Fields,
|
|
75
|
-
CookieA extends Struct.Fields,
|
|
76
|
-
QueryA extends Struct.Fields,
|
|
77
|
-
BodyA extends Struct.Fields,
|
|
78
|
-
HeaderA extends Struct.Fields
|
|
79
|
-
>(
|
|
80
|
-
parsers: RequestParsers<PathA, CookieA, QueryA, BodyA, HeaderA>
|
|
81
|
-
) {
|
|
82
|
-
const handleParse = <A, E, R>(effect: Effect<A, E, R>) =>
|
|
83
|
-
effect.pipe(
|
|
84
|
-
Effect.exit,
|
|
85
|
-
Effect
|
|
86
|
-
.flatMap((_) =>
|
|
87
|
-
Exit.isFailure(_) && !Cause.isFailure(_.cause)
|
|
88
|
-
? (Effect.failCauseSync(() => _.cause) as Effect<never, ValidationError>)
|
|
89
|
-
: Effect.sync(() =>
|
|
90
|
-
Exit.isSuccess(_)
|
|
91
|
-
? { _tag: "Success" as const, value: _.value }
|
|
92
|
-
: { _tag: "Failure", errors: Cause.failures(_.cause) }
|
|
93
|
-
)
|
|
94
|
-
)
|
|
95
|
-
)
|
|
96
|
-
return (
|
|
97
|
-
{ body, cookies, headers, params, query }: {
|
|
98
|
-
body: unknown
|
|
99
|
-
cookies: unknown
|
|
100
|
-
headers: unknown
|
|
101
|
-
params: unknown
|
|
102
|
-
query: unknown
|
|
103
|
-
}
|
|
104
|
-
) =>
|
|
105
|
-
Effect
|
|
106
|
-
.all({
|
|
107
|
-
body: parsers
|
|
108
|
-
.parseBody(body)
|
|
109
|
-
.pipe(handleParse),
|
|
110
|
-
cookie: parsers
|
|
111
|
-
.parseCookie(cookies)
|
|
112
|
-
.pipe(handleParse),
|
|
113
|
-
headers: parsers
|
|
114
|
-
.parseHeaders(headers)
|
|
115
|
-
.pipe(handleParse),
|
|
116
|
-
query: parsers
|
|
117
|
-
.parseQuery(query)
|
|
118
|
-
.pipe(handleParse),
|
|
119
|
-
path: parsers
|
|
120
|
-
.parsePath(params)
|
|
121
|
-
.pipe(handleParse)
|
|
122
|
-
})
|
|
123
|
-
.pipe(Effect
|
|
124
|
-
.flatMap(({ body, cookie, headers, path, query }) => {
|
|
125
|
-
const errors: unknown[] = []
|
|
126
|
-
if (body._tag === "Failure") {
|
|
127
|
-
errors.push(makeError("body")(body.errors))
|
|
128
|
-
}
|
|
129
|
-
if (cookie._tag === "Failure") {
|
|
130
|
-
errors.push(makeError("cookie")(cookie.errors))
|
|
131
|
-
}
|
|
132
|
-
if (headers._tag === "Failure") {
|
|
133
|
-
errors.push(makeError("headers")(headers.errors))
|
|
134
|
-
}
|
|
135
|
-
if (path._tag === "Failure") {
|
|
136
|
-
errors.push(makeError("path")(path.errors))
|
|
137
|
-
}
|
|
138
|
-
if (query._tag === "Failure") {
|
|
139
|
-
errors.push(makeError("query")(query.errors))
|
|
140
|
-
}
|
|
141
|
-
if (errors.length) {
|
|
142
|
-
return new ValidationError({ errors })
|
|
143
|
-
}
|
|
144
|
-
return Effect.sync(() => ({
|
|
145
|
-
body: body.value!,
|
|
146
|
-
cookie: cookie.value!,
|
|
147
|
-
headers: headers.value!,
|
|
148
|
-
path: path.value!,
|
|
149
|
-
query: query.value!
|
|
150
|
-
}))
|
|
151
|
-
}))
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
155
|
-
// function mapErrors_<E, NE, NER extends Record<string, Effect<any, E, any>>>(
|
|
156
|
-
// t: NER, // TODO: enforce non empty
|
|
157
|
-
// mapErrors: (k: keyof NER) => (err: E) => NE
|
|
158
|
-
// ): {
|
|
159
|
-
// [K in keyof NER]: Effect<_R<NER[K]>, NE, Effect.Success<NER[K]>>
|
|
160
|
-
// } {
|
|
161
|
-
// return typedKeysOf(t).reduce(
|
|
162
|
-
// (prev, cur) => {
|
|
163
|
-
// prev[cur] = t[cur].mapError(mapErrors(cur))
|
|
164
|
-
// return prev
|
|
165
|
-
// },
|
|
166
|
-
// {} as {
|
|
167
|
-
// [K in keyof NER]: Effect<_R<NER[K]>, NE, Effect.Success<NER[K]>>
|
|
168
|
-
// }
|
|
169
|
-
// )
|
|
170
|
-
// }
|
|
171
|
-
|
|
172
|
-
function makeError(type: string) {
|
|
173
|
-
return (e: unknown) => [{ type, errors: decodeErrors(e) }]
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export function makeRequestParsers<
|
|
177
|
-
R,
|
|
178
|
-
M,
|
|
179
|
-
PathA extends Struct.Fields,
|
|
180
|
-
CookieA extends Struct.Fields,
|
|
181
|
-
QueryA extends Struct.Fields,
|
|
182
|
-
BodyA extends Struct.Fields,
|
|
183
|
-
HeaderA extends Struct.Fields,
|
|
184
|
-
ReqA extends PathA & QueryA & BodyA,
|
|
185
|
-
ResA extends Struct.Fields,
|
|
186
|
-
Errors,
|
|
187
|
-
PPath extends `/${string}`,
|
|
188
|
-
CTX,
|
|
189
|
-
Context,
|
|
190
|
-
Config
|
|
191
|
-
>(
|
|
192
|
-
Request: RequestHandler<
|
|
193
|
-
R,
|
|
194
|
-
M,
|
|
195
|
-
PathA,
|
|
196
|
-
CookieA,
|
|
197
|
-
QueryA,
|
|
198
|
-
BodyA,
|
|
199
|
-
HeaderA,
|
|
200
|
-
ReqA,
|
|
201
|
-
ResA,
|
|
202
|
-
Errors,
|
|
203
|
-
PPath,
|
|
204
|
-
CTX,
|
|
205
|
-
Context,
|
|
206
|
-
Config
|
|
207
|
-
>["Request"]
|
|
208
|
-
): RequestParsers<PathA, CookieA, QueryA, BodyA, HeaderA> {
|
|
209
|
-
const ph = Effect.sync(() =>
|
|
210
|
-
Option
|
|
211
|
-
.fromNullable(Request.Headers)
|
|
212
|
-
.pipe(
|
|
213
|
-
Option.map((s) => s as unknown as S.Schema<any>),
|
|
214
|
-
Option.map(S.decodeUnknown)
|
|
215
|
-
)
|
|
216
|
-
)
|
|
217
|
-
const parseHeaders = (u: unknown) => Effect.flatMapOption(ph, (d) => d(u))
|
|
218
|
-
|
|
219
|
-
const pq = Effect.sync(() =>
|
|
220
|
-
Option
|
|
221
|
-
.fromNullable(Request.Query)
|
|
222
|
-
.pipe(
|
|
223
|
-
Option.map((s) => s as unknown as S.Schema<any>),
|
|
224
|
-
Option.map(S.decodeUnknown)
|
|
225
|
-
)
|
|
226
|
-
)
|
|
227
|
-
const parseQuery = (u: unknown) => Effect.flatMapOption(pq, (d) => d(u))
|
|
228
|
-
|
|
229
|
-
const pb = Effect.sync(() =>
|
|
230
|
-
Option
|
|
231
|
-
.fromNullable(Request.Body)
|
|
232
|
-
.pipe(
|
|
233
|
-
Option.map((s) => s as unknown as S.Schema<any>),
|
|
234
|
-
Option.map(S.decodeUnknown)
|
|
235
|
-
)
|
|
236
|
-
)
|
|
237
|
-
const parseBody = (u: unknown) => Effect.flatMapOption(pb, (d) => d(u))
|
|
238
|
-
|
|
239
|
-
const pp = Effect.sync(() =>
|
|
240
|
-
Option
|
|
241
|
-
.fromNullable(Request.Path)
|
|
242
|
-
.pipe(
|
|
243
|
-
Option.map((s) => s as unknown as S.Schema<any>),
|
|
244
|
-
Option.map(S.decodeUnknown)
|
|
245
|
-
)
|
|
246
|
-
)
|
|
247
|
-
const parsePath = (u: unknown) => Effect.flatMapOption(pp, (d) => d(u))
|
|
248
|
-
|
|
249
|
-
const pc = Effect.sync(() =>
|
|
250
|
-
Option
|
|
251
|
-
.fromNullable(Request.Cookie)
|
|
252
|
-
.pipe(
|
|
253
|
-
Option.map((s) => s as unknown as S.Schema<any>),
|
|
254
|
-
Option.map(S.decodeUnknown)
|
|
255
|
-
)
|
|
256
|
-
)
|
|
257
|
-
const parseCookie = (u: unknown) => Effect.flatMapOption(pc, (d) => d(u))
|
|
258
|
-
|
|
259
|
-
return {
|
|
260
|
-
parseBody,
|
|
261
|
-
parseCookie,
|
|
262
|
-
parseHeaders,
|
|
263
|
-
parsePath,
|
|
264
|
-
parseQuery
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
type Decode<A> = (u: unknown) => Effect<A, unknown>
|
|
269
|
-
|
|
270
|
-
export interface RequestParsers<
|
|
271
|
-
PathA extends Struct.Fields,
|
|
272
|
-
CookieA extends Struct.Fields,
|
|
273
|
-
QueryA extends Struct.Fields,
|
|
274
|
-
BodyA extends Struct.Fields,
|
|
275
|
-
HeaderA extends Struct.Fields
|
|
276
|
-
> {
|
|
277
|
-
parseHeaders: Decode<Option<Simplify<S.Struct.Type<HeaderA>>>>
|
|
278
|
-
parseQuery: Decode<Option<Simplify<S.Struct.Type<QueryA>>>>
|
|
279
|
-
parseBody: Decode<Option<Simplify<S.Struct.Type<BodyA>>>>
|
|
280
|
-
parsePath: Decode<Option<Simplify<S.Struct.Type<PathA>>>>
|
|
281
|
-
parseCookie: Decode<Option<Simplify<S.Struct.Type<CookieA>>>>
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
export type EffectDeps<A> = {
|
|
285
|
-
[K in keyof A as A[K] extends Effect<any, any, any> ? K : never]: A[K] extends Effect<any, any, any> ? A[K] : never
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
export type Request<
|
|
289
|
-
M,
|
|
290
|
-
PathA extends Struct.Fields,
|
|
291
|
-
CookieA extends Struct.Fields,
|
|
292
|
-
QueryA extends Struct.Fields,
|
|
293
|
-
BodyA extends Struct.Fields,
|
|
294
|
-
HeaderA extends Struct.Fields,
|
|
295
|
-
ReqA extends PathA & QueryA & BodyA,
|
|
296
|
-
PPath extends `/${string}`
|
|
297
|
-
> = REST.ReqRes<any, any, any> & {
|
|
298
|
-
method: REST.Methods.Rest
|
|
299
|
-
path: PPath
|
|
300
|
-
Cookie?: CookieA
|
|
301
|
-
Path?: PathA
|
|
302
|
-
Body?: BodyA
|
|
303
|
-
Query?: QueryA
|
|
304
|
-
Headers?: HeaderA
|
|
305
|
-
Tag: Context.Tag<M, M>
|
|
306
|
-
ReqA?: ReqA
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
export interface RequestHandlerBase<
|
|
310
|
-
R,
|
|
311
|
-
M,
|
|
312
|
-
PathA extends Struct.Fields,
|
|
313
|
-
CookieA extends Struct.Fields,
|
|
314
|
-
QueryA extends Struct.Fields,
|
|
315
|
-
BodyA extends Struct.Fields,
|
|
316
|
-
HeaderA extends Struct.Fields,
|
|
317
|
-
ReqA extends PathA & QueryA & BodyA,
|
|
318
|
-
ResA extends Struct.Fields,
|
|
319
|
-
ResE,
|
|
320
|
-
PPath extends `/${string}`,
|
|
321
|
-
Config
|
|
322
|
-
> {
|
|
323
|
-
adaptResponse?: any
|
|
324
|
-
h: (i: PathA & QueryA & BodyA & {}) => Effect<ResA, ResE, R>
|
|
325
|
-
Request: Request<M, PathA, CookieA, QueryA, BodyA, HeaderA, ReqA, PPath>
|
|
326
|
-
Response: REST.ReqRes<any, any, any>
|
|
327
|
-
ResponseOpenApi?: any
|
|
328
|
-
config: Config
|
|
329
|
-
name: string
|
|
330
|
-
rt: "raw" | "d"
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
export interface RequestHandler<
|
|
334
|
-
R,
|
|
335
|
-
M,
|
|
336
|
-
PathA extends Struct.Fields,
|
|
337
|
-
CookieA extends Struct.Fields,
|
|
338
|
-
QueryA extends Struct.Fields,
|
|
339
|
-
BodyA extends Struct.Fields,
|
|
340
|
-
HeaderA extends Struct.Fields,
|
|
341
|
-
ReqA extends PathA & QueryA & BodyA,
|
|
342
|
-
ResA extends Struct.Fields,
|
|
343
|
-
ResE,
|
|
344
|
-
PPath extends `/${string}`,
|
|
345
|
-
CTX,
|
|
346
|
-
Context,
|
|
347
|
-
Config
|
|
348
|
-
> {
|
|
349
|
-
adaptResponse?: any
|
|
350
|
-
h: (i: PathA & QueryA & BodyA & {}, ctx: any /* TODO */) => Effect<ResA, ResE, R>
|
|
351
|
-
Request: Request<M, PathA, CookieA, QueryA, BodyA, HeaderA, ReqA, PPath> & Config
|
|
352
|
-
Response: REST.ReqRes<any, any, any>
|
|
353
|
-
ResponseOpenApi?: any
|
|
354
|
-
name: string
|
|
355
|
-
CTX: CTX
|
|
356
|
-
rt: "raw" | "d"
|
|
357
|
-
Context: Context
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
export interface RequestHandlerOrig<
|
|
361
|
-
R,
|
|
362
|
-
M,
|
|
363
|
-
PathA extends Struct.Fields,
|
|
364
|
-
CookieA extends Struct.Fields,
|
|
365
|
-
QueryA extends Struct.Fields,
|
|
366
|
-
BodyA extends Struct.Fields,
|
|
367
|
-
HeaderA extends Struct.Fields,
|
|
368
|
-
ReqA extends PathA & QueryA & BodyA,
|
|
369
|
-
ResA extends Struct.Fields,
|
|
370
|
-
ResE,
|
|
371
|
-
PPath extends `/${string}`
|
|
372
|
-
> {
|
|
373
|
-
adaptResponse?: any
|
|
374
|
-
h: (i: PathA & QueryA & BodyA & {}) => Effect<ResA, ResE, R>
|
|
375
|
-
Request: Request<M, PathA, CookieA, QueryA, BodyA, HeaderA, ReqA, PPath>
|
|
376
|
-
Response: REST.ReqRes<any, any, any>
|
|
377
|
-
name: string
|
|
378
|
-
ResponseOpenApi?: any
|
|
379
|
-
}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { logError } from "@effect-app/infra/errorReporter"
|
|
2
|
-
import type { Schema } from "@effect-app/schema"
|
|
3
|
-
import { setBody, setStatus } from "@effect/platform/HttpServerResponse"
|
|
4
|
-
import { Cause, Data, Effect, S, Schedule } from "effect-app"
|
|
5
|
-
import type { SupportedErrors } from "effect-app/client/errors"
|
|
6
|
-
import {
|
|
7
|
-
InvalidStateError,
|
|
8
|
-
NotFoundError,
|
|
9
|
-
NotLoggedInError,
|
|
10
|
-
OptimisticConcurrencyException,
|
|
11
|
-
ServiceUnavailableError,
|
|
12
|
-
UnauthorizedError,
|
|
13
|
-
ValidationError
|
|
14
|
-
} from "effect-app/client/errors"
|
|
15
|
-
import { HttpBody, HttpHeaders, type HttpServerRequest, HttpServerResponse } from "effect-app/http"
|
|
16
|
-
import type {
|
|
17
|
-
InsufficientScopeError,
|
|
18
|
-
InvalidRequestError,
|
|
19
|
-
InvalidTokenError,
|
|
20
|
-
UnauthorizedError as JWTUnauthorizedError
|
|
21
|
-
} from "express-oauth2-jwt-bearer"
|
|
22
|
-
import { InfraLogger } from "../../logger.js"
|
|
23
|
-
|
|
24
|
-
export class JWTError extends Data.TaggedClass("JWTError")<{
|
|
25
|
-
error:
|
|
26
|
-
| InsufficientScopeError
|
|
27
|
-
| InvalidRequestError
|
|
28
|
-
| InvalidTokenError
|
|
29
|
-
| JWTUnauthorizedError
|
|
30
|
-
}> {}
|
|
31
|
-
|
|
32
|
-
const logRequestError = logError("Request")
|
|
33
|
-
|
|
34
|
-
export function defaultBasicErrorHandler<R>(
|
|
35
|
-
_req: HttpServerRequest.HttpServerRequest,
|
|
36
|
-
res: HttpServerResponse.HttpServerResponse,
|
|
37
|
-
r2: Effect<HttpServerResponse.HttpServerResponse, ValidationError, R>
|
|
38
|
-
) {
|
|
39
|
-
const sendError = (code: number) => (body: unknown) =>
|
|
40
|
-
Effect.sync(() => setBody(res, HttpBody.unsafeJson(body)).pipe(setStatus(code)))
|
|
41
|
-
return r2.pipe(
|
|
42
|
-
Effect.tapErrorCause((cause) => Cause.isFailure(cause) ? logRequestError(cause) : Effect.void),
|
|
43
|
-
Effect.catchTag("ValidationError", (err) => sendError(400)(err.errors)),
|
|
44
|
-
Effect
|
|
45
|
-
// final catch all; expecting never so that unhandled known errors will show up
|
|
46
|
-
.catchAll((err: never) =>
|
|
47
|
-
InfraLogger
|
|
48
|
-
.logError(
|
|
49
|
-
"Program error, compiler probably silenced, got an unsupported Error in Error Channel of Effect" + err
|
|
50
|
-
)
|
|
51
|
-
.pipe(
|
|
52
|
-
Effect.map(() => err as unknown),
|
|
53
|
-
Effect.flatMap(Effect.die)
|
|
54
|
-
)
|
|
55
|
-
)
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const optimisticConcurrencySchedule = Schedule.once
|
|
60
|
-
&& Schedule.recurWhile<{ _tag: string }>((a) => a._tag === "OptimisticConcurrencyException")
|
|
61
|
-
|
|
62
|
-
export function defaultErrorHandler<R, A extends { _tag: string } = never>(
|
|
63
|
-
req: HttpServerRequest.HttpServerRequest,
|
|
64
|
-
res: HttpServerResponse.HttpServerResponse,
|
|
65
|
-
r2: Effect<HttpServerResponse.HttpServerResponse, SupportedErrors | JWTError, R>,
|
|
66
|
-
customErrorSchema?: Schema<A, unknown>
|
|
67
|
-
) {
|
|
68
|
-
const r3 = req.method === "PATCH"
|
|
69
|
-
? Effect.retry(r2, optimisticConcurrencySchedule)
|
|
70
|
-
: r2
|
|
71
|
-
const sendError = <R, From, To>(code: number, schema: Schema<To, From, R>) => (body: To) =>
|
|
72
|
-
S
|
|
73
|
-
.encode(schema)(body)
|
|
74
|
-
.pipe(
|
|
75
|
-
Effect.orDie,
|
|
76
|
-
Effect.andThen((body) => res.pipe(setStatus(code), setBody(HttpBody.unsafeJson(body))))
|
|
77
|
-
)
|
|
78
|
-
return r3
|
|
79
|
-
.pipe(
|
|
80
|
-
Effect.tapErrorCause((cause) => Cause.isFailure(cause) ? logRequestError(cause) : Effect.void),
|
|
81
|
-
Effect.tapErrorCause((cause) =>
|
|
82
|
-
Effect.annotateCurrentSpan({
|
|
83
|
-
"exception.escaped": true,
|
|
84
|
-
"exception.message": "Request Error",
|
|
85
|
-
"exception.stacktrace": Cause.pretty(cause),
|
|
86
|
-
"exception.type": Cause.squashWith(
|
|
87
|
-
cause,
|
|
88
|
-
(_) => _._tag
|
|
89
|
-
// Predicate.hasProperty(_, "_tag")
|
|
90
|
-
// ? _._tag
|
|
91
|
-
// : Predicate.hasProperty(_, "name")
|
|
92
|
-
// ? _.name
|
|
93
|
-
// // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
94
|
-
// : `${_}`
|
|
95
|
-
),
|
|
96
|
-
"error.type": cause._tag
|
|
97
|
-
})
|
|
98
|
-
),
|
|
99
|
-
Effect
|
|
100
|
-
.catchTags({
|
|
101
|
-
"JWTError": (err) =>
|
|
102
|
-
Effect.succeed(
|
|
103
|
-
HttpServerResponse.unsafeJson({ message: err.error.message }, {
|
|
104
|
-
status: err
|
|
105
|
-
.error
|
|
106
|
-
.status,
|
|
107
|
-
headers: HttpHeaders.fromInput(err.error.headers)
|
|
108
|
-
})
|
|
109
|
-
),
|
|
110
|
-
"ValidationError": sendError(400, ValidationError),
|
|
111
|
-
"NotFoundError": sendError(404, NotFoundError),
|
|
112
|
-
"NotLoggedInError": sendError(401, NotLoggedInError),
|
|
113
|
-
"UnauthorizedError": sendError(403, UnauthorizedError),
|
|
114
|
-
"InvalidStateError": sendError(422, InvalidStateError),
|
|
115
|
-
"ServiceUnavailableError": sendError(503, ServiceUnavailableError),
|
|
116
|
-
// 412 or 409.. https://stackoverflow.com/questions/19122088/which-http-status-code-to-use-to-reject-a-put-due-to-optimistic-locking-failure
|
|
117
|
-
"OptimisticConcurrencyException": sendError(412, OptimisticConcurrencyException)
|
|
118
|
-
}),
|
|
119
|
-
customErrorSchema
|
|
120
|
-
? Effect.catchAll((x) =>
|
|
121
|
-
S.is(customErrorSchema)(x)
|
|
122
|
-
// TODO: customize error code
|
|
123
|
-
? sendError(422, customErrorSchema)(x)
|
|
124
|
-
: Effect.fail(x)
|
|
125
|
-
)
|
|
126
|
-
: (x) => x,
|
|
127
|
-
Effect
|
|
128
|
-
// final catch all; expecting never so that unhandled known errors will show up
|
|
129
|
-
.catchAll((err: never) =>
|
|
130
|
-
InfraLogger
|
|
131
|
-
.logError(
|
|
132
|
-
"Program error, compiler probably silenced, got an unsupported Error in Error Channel of Effect" + err
|
|
133
|
-
)
|
|
134
|
-
.pipe(
|
|
135
|
-
Effect.map(() => err as unknown),
|
|
136
|
-
Effect.flatMap(Effect.die)
|
|
137
|
-
)
|
|
138
|
-
)
|
|
139
|
-
)
|
|
140
|
-
}
|