@effect-app/infra 0.133.0-next.8 → 0.133.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 +494 -0
- package/_cjs/RequestContext.cjs +8 -9
- package/_cjs/RequestContext.cjs.map +1 -1
- package/_cjs/_ext/Array.cjs +4 -3
- package/_cjs/_ext/Array.cjs.map +1 -1
- package/_cjs/_global.ext.cjs +0 -25
- package/_cjs/_global.ext.cjs.map +1 -1
- package/_cjs/api/codec.cjs +1 -1
- package/_cjs/api/codec.cjs.map +1 -1
- package/_cjs/api/http.cjs +42 -0
- package/_cjs/api/http.cjs.map +1 -0
- package/_cjs/api/routing/base.cjs +169 -0
- package/_cjs/api/routing/base.cjs.map +1 -0
- package/_cjs/api/routing/defaultErrorHandler.cjs +50 -0
- package/_cjs/api/routing/defaultErrorHandler.cjs.map +1 -0
- package/_cjs/api/routing/makeRequestHandler.cjs +134 -0
- package/_cjs/api/routing/makeRequestHandler.cjs.map +1 -0
- package/_cjs/api/routing/match.cjs +45 -0
- package/_cjs/api/routing/match.cjs.map +1 -0
- package/_cjs/api/routing.cjs +50 -0
- package/_cjs/api/routing.cjs.map +1 -0
- package/_cjs/api/setupRequest.cjs +31 -30
- package/_cjs/api/setupRequest.cjs.map +1 -1
- package/_cjs/errorReporter.cjs +4 -2
- package/_cjs/errorReporter.cjs.map +1 -1
- package/_cjs/errors.cjs +18 -66
- package/_cjs/errors.cjs.map +1 -1
- package/_cjs/services/Operations/live.cjs +11 -12
- package/_cjs/services/Operations/live.cjs.map +1 -1
- package/_cjs/services/QueueMaker/memQueue.cjs +9 -10
- package/_cjs/services/QueueMaker/memQueue.cjs.map +1 -1
- package/_cjs/services/QueueMaker/sbqueue.cjs +4 -4
- package/_cjs/services/QueueMaker/sbqueue.cjs.map +1 -1
- package/_cjs/services/Repository.cjs +3 -3
- package/_cjs/services/Repository.cjs.map +1 -1
- package/_cjs/services/RepositoryBase.cjs +14 -6
- package/_cjs/services/RepositoryBase.cjs.map +1 -1
- package/_cjs/services/RequestContextContainer.cjs +3 -3
- package/_cjs/services/RequestContextContainer.cjs.map +1 -1
- package/_cjs/services/Store/Redis.cjs.map +1 -1
- package/_cjs/test.arbs.cjs +24 -1
- package/_cjs/test.arbs.cjs.map +1 -1
- package/_src/RequestContext.ts +9 -12
- package/_src/_ext/Array.ts +4 -4
- package/_src/_global.ext.ts +0 -23
- package/_src/api/codec.ts +1 -1
- package/_src/api/http.ts +26 -0
- package/_src/api/routing/base.ts +476 -0
- package/_src/api/routing/defaultErrorHandler.ts +83 -0
- package/_src/api/routing/makeRequestHandler.ts +278 -0
- package/_src/api/routing/match.ts +119 -0
- package/_src/api/routing.ts +11 -0
- package/_src/api/setupRequest.ts +39 -34
- package/_src/errorReporter.ts +4 -2
- package/_src/errors.ts +1 -58
- package/_src/services/QueueMaker/memQueue.ts +3 -3
- package/_src/services/QueueMaker/sbqueue.ts +3 -3
- package/_src/services/Repository.ts +40 -43
- package/_src/services/RepositoryBase.ts +26 -20
- package/_src/services/Store/Redis.ts +1 -1
- package/_src/services/Store/service.ts +4 -4
- package/_src/test.arbs.ts +17 -14
- package/dist/RequestContext.d.ts +72 -24
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +10 -11
- package/dist/_ext/Array.d.ts +2 -2
- package/dist/_ext/Array.d.ts.map +1 -1
- package/dist/_ext/Array.js +5 -4
- package/dist/_global.ext.d.ts +0 -22
- package/dist/_global.ext.d.ts.map +1 -1
- package/dist/_global.ext.js +1 -18
- package/dist/api/codec.d.ts +1 -1
- package/dist/api/codec.d.ts.map +1 -1
- package/dist/api/codec.js +2 -2
- package/dist/api/http.d.ts +16 -0
- package/dist/api/http.d.ts.map +1 -0
- package/dist/api/http.js +12 -0
- package/dist/api/routing/base.d.ts +117 -0
- package/dist/api/routing/base.d.ts.map +1 -0
- package/dist/api/routing/base.js +137 -0
- package/dist/api/routing/defaultErrorHandler.d.ts +16 -0
- package/dist/api/routing/defaultErrorHandler.d.ts.map +1 -0
- package/dist/api/routing/defaultErrorHandler.js +42 -0
- package/dist/api/routing/makeRequestHandler.d.ts +20 -0
- package/dist/api/routing/makeRequestHandler.d.ts.map +1 -0
- package/dist/api/routing/makeRequestHandler.js +121 -0
- package/dist/api/routing/match.d.ts +12 -0
- package/dist/api/routing/match.d.ts.map +1 -0
- package/dist/api/routing/match.js +31 -0
- package/dist/api/routing.d.ts +5 -0
- package/dist/api/routing.d.ts.map +1 -0
- package/dist/api/routing.js +8 -0
- package/dist/api/setupRequest.d.ts +4 -4
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +31 -37
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +5 -3
- package/dist/errors.d.ts +1 -81
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -46
- package/dist/services/Operations/live.js +16 -17
- package/dist/services/QueueMaker/memQueue.d.ts +3 -3
- package/dist/services/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/services/QueueMaker/memQueue.js +9 -10
- package/dist/services/QueueMaker/sbqueue.d.ts +3 -3
- package/dist/services/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/services/QueueMaker/sbqueue.js +4 -4
- package/dist/services/QueueMaker/service.d.ts +1 -1
- package/dist/services/Repository.d.ts +37 -38
- package/dist/services/Repository.d.ts.map +1 -1
- package/dist/services/Repository.js +4 -4
- package/dist/services/RepositoryBase.d.ts +15 -15
- package/dist/services/RepositoryBase.d.ts.map +1 -1
- package/dist/services/RepositoryBase.js +12 -7
- package/dist/services/RequestContextContainer.d.ts +1 -1
- package/dist/services/RequestContextContainer.js +3 -3
- package/dist/services/Store/Redis.js +1 -1
- package/dist/services/Store/service.d.ts +4 -4
- package/dist/services/Store/service.d.ts.map +1 -1
- package/dist/services/simpledb/redisdb.d.ts +5 -1
- package/dist/services/simpledb/redisdb.d.ts.map +1 -1
- package/dist/services/simpledb/shared.d.ts +5 -1
- package/dist/services/simpledb/shared.d.ts.map +1 -1
- package/dist/test.arbs.d.ts +12 -1
- package/dist/test.arbs.d.ts.map +1 -1
- package/dist/test.arbs.js +18 -2
- package/package.json +73 -11
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
3
|
+
import type { EnforceNonEmptyRecord } from "@effect-app/core/utils"
|
|
4
|
+
import { pretty } from "@effect-app/core/utils"
|
|
5
|
+
import { snipString } from "@effect-app/infra/api/util"
|
|
6
|
+
import { reportError } from "@effect-app/infra/errorReporter"
|
|
7
|
+
import type { ValidationError } from "@effect-app/infra/errors"
|
|
8
|
+
import type { RequestContextContainer } from "@effect-app/infra/services/RequestContextContainer"
|
|
9
|
+
import type { ContextMapContainer } from "@effect-app/infra/services/Store/ContextMapContainer"
|
|
10
|
+
import type { REST, StructFields } from "@effect-app/schema"
|
|
11
|
+
import { AST } from "@effect-app/schema"
|
|
12
|
+
import type { HttpRequestError } from "../http.js"
|
|
13
|
+
import { HttpBody, HttpRouteContext, HttpServerRequest, HttpServerResponse } from "../http.js"
|
|
14
|
+
import { makeRequestParsers, parseRequestParams } from "./base.js"
|
|
15
|
+
import type { RequestHandler, RequestHandlerBase } from "./base.js"
|
|
16
|
+
|
|
17
|
+
export const RequestSettings = FiberRef.unsafeMake({
|
|
18
|
+
verbose: false
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
export type Middleware<
|
|
22
|
+
R,
|
|
23
|
+
M,
|
|
24
|
+
PathA extends StructFields,
|
|
25
|
+
CookieA extends StructFields,
|
|
26
|
+
QueryA extends StructFields,
|
|
27
|
+
BodyA extends StructFields,
|
|
28
|
+
HeaderA extends StructFields,
|
|
29
|
+
ReqA extends PathA & QueryA & BodyA,
|
|
30
|
+
ResA extends StructFields,
|
|
31
|
+
ResE,
|
|
32
|
+
MiddlewareE,
|
|
33
|
+
PPath extends `/${string}`,
|
|
34
|
+
R2,
|
|
35
|
+
PR,
|
|
36
|
+
CTX,
|
|
37
|
+
Context,
|
|
38
|
+
Config
|
|
39
|
+
> = (
|
|
40
|
+
handler: RequestHandler<R, M, PathA, CookieA, QueryA, BodyA, HeaderA, ReqA, ResA, ResE, PPath, CTX, Context, Config>
|
|
41
|
+
) => {
|
|
42
|
+
handler: RequestHandler<
|
|
43
|
+
Exclude<R2, HttpServerRequest> | PR | RequestContextContainer | ContextMapContainer,
|
|
44
|
+
M,
|
|
45
|
+
PathA,
|
|
46
|
+
CookieA,
|
|
47
|
+
QueryA,
|
|
48
|
+
BodyA,
|
|
49
|
+
HeaderA,
|
|
50
|
+
ReqA,
|
|
51
|
+
ResA,
|
|
52
|
+
ResE,
|
|
53
|
+
PPath,
|
|
54
|
+
CTX,
|
|
55
|
+
Context,
|
|
56
|
+
Config
|
|
57
|
+
>
|
|
58
|
+
makeRequestLayer: Layer<R2, MiddlewareE, PR>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function makeRequestHandler<
|
|
62
|
+
R,
|
|
63
|
+
M,
|
|
64
|
+
PathA extends StructFields,
|
|
65
|
+
CookieA extends StructFields,
|
|
66
|
+
QueryA extends StructFields,
|
|
67
|
+
BodyA extends StructFields,
|
|
68
|
+
HeaderA extends StructFields,
|
|
69
|
+
ReqA extends PathA & QueryA & BodyA,
|
|
70
|
+
ResA extends StructFields,
|
|
71
|
+
ResE,
|
|
72
|
+
MiddlewareE,
|
|
73
|
+
R2,
|
|
74
|
+
PR,
|
|
75
|
+
RErr,
|
|
76
|
+
PPath extends `/${string}`,
|
|
77
|
+
Config
|
|
78
|
+
>(
|
|
79
|
+
handler: RequestHandlerBase<
|
|
80
|
+
R,
|
|
81
|
+
M,
|
|
82
|
+
PathA,
|
|
83
|
+
CookieA,
|
|
84
|
+
QueryA,
|
|
85
|
+
BodyA,
|
|
86
|
+
HeaderA,
|
|
87
|
+
ReqA,
|
|
88
|
+
ResA,
|
|
89
|
+
ResE,
|
|
90
|
+
PPath,
|
|
91
|
+
Config
|
|
92
|
+
>,
|
|
93
|
+
errorHandler: <R>(
|
|
94
|
+
req: HttpServerRequest,
|
|
95
|
+
res: HttpServerResponse,
|
|
96
|
+
r2: Effect<R, ValidationError | ResE | MiddlewareE, HttpServerResponse>
|
|
97
|
+
) => Effect<RErr | R, never, HttpServerResponse>,
|
|
98
|
+
middlewareLayer?: Layer<R2, MiddlewareE, PR>
|
|
99
|
+
): Effect<
|
|
100
|
+
| HttpRouteContext
|
|
101
|
+
| HttpServerRequest
|
|
102
|
+
| RequestContextContainer
|
|
103
|
+
| ContextMapContainer
|
|
104
|
+
| RErr
|
|
105
|
+
| Exclude<Exclude<R, EnforceNonEmptyRecord<M>>, PR>
|
|
106
|
+
| R2,
|
|
107
|
+
HttpRequestError,
|
|
108
|
+
HttpServerResponse
|
|
109
|
+
> {
|
|
110
|
+
const { Request, Response, h: handle } = handler
|
|
111
|
+
|
|
112
|
+
const response: REST.ReqRes<any, any> = Response ? Response : Void
|
|
113
|
+
const resp = response as typeof response & { struct?: Schema<any, any> }
|
|
114
|
+
// TODO: consider if the alternative of using the struct schema is perhaps just better.
|
|
115
|
+
const encoder = "struct" in resp && resp.struct
|
|
116
|
+
? resp.struct.encodeSync
|
|
117
|
+
// ? (i: any) => {
|
|
118
|
+
// if (i instanceof (response as any)) return response.encodeSync(i)
|
|
119
|
+
// else return response.encodeSync(new (response as any)(i))
|
|
120
|
+
// }
|
|
121
|
+
: resp.encodeSync
|
|
122
|
+
// const encodeResponse = adaptResponse
|
|
123
|
+
// ? (req: ReqA) => Encoder.for(adaptResponse(req))
|
|
124
|
+
// : () => encoder
|
|
125
|
+
|
|
126
|
+
const requestParsers = makeRequestParsers(Request)
|
|
127
|
+
const parseRequest = parseRequestParams(requestParsers)
|
|
128
|
+
|
|
129
|
+
const getParams = Effect
|
|
130
|
+
.all({
|
|
131
|
+
rcx: HttpRouteContext,
|
|
132
|
+
req: HttpServerRequest.flatMap((req) => req.json.map((body) => ({ body, headers: req.headers })))
|
|
133
|
+
})
|
|
134
|
+
.map((
|
|
135
|
+
{ rcx, req }
|
|
136
|
+
) => ({
|
|
137
|
+
params: rcx.params,
|
|
138
|
+
query: rcx.searchParams,
|
|
139
|
+
body: req.body,
|
|
140
|
+
headers: req.headers,
|
|
141
|
+
cookies: {} // req.cookies
|
|
142
|
+
}))
|
|
143
|
+
|
|
144
|
+
return Effect
|
|
145
|
+
.gen(function*($) {
|
|
146
|
+
const req = yield* $(HttpServerRequest)
|
|
147
|
+
const res = HttpServerResponse
|
|
148
|
+
.empty()
|
|
149
|
+
.pipe((_) => req.method === "GET" ? _.setHeader("Cache-Control", "no-store") : _)
|
|
150
|
+
|
|
151
|
+
const pars = yield* $(getParams)
|
|
152
|
+
|
|
153
|
+
const settings = yield* $(RequestSettings.get)
|
|
154
|
+
|
|
155
|
+
const eff =
|
|
156
|
+
// TODO: we don;t have access to user id here cause context is not yet created
|
|
157
|
+
Effect
|
|
158
|
+
.logInfo("Incoming request")
|
|
159
|
+
.annotateLogs({
|
|
160
|
+
method: req.method,
|
|
161
|
+
path: req.originalUrl,
|
|
162
|
+
...settings.verbose
|
|
163
|
+
? {
|
|
164
|
+
reqPath: pars.params.$$.pretty,
|
|
165
|
+
reqQuery: pars.query.$$.pretty,
|
|
166
|
+
reqBody: pretty(pars.body),
|
|
167
|
+
reqCookies: pretty(pars.cookies),
|
|
168
|
+
reqHeaders: pretty(pars.headers)
|
|
169
|
+
}
|
|
170
|
+
: undefined
|
|
171
|
+
})
|
|
172
|
+
.andThen(
|
|
173
|
+
Effect.suspend(() => {
|
|
174
|
+
const handleRequest = parseRequest(pars)
|
|
175
|
+
.map(({ body, path, query }) => {
|
|
176
|
+
const hn = {
|
|
177
|
+
...body.value as any,
|
|
178
|
+
...query.value as any,
|
|
179
|
+
...path.value as any
|
|
180
|
+
} as unknown as ReqA
|
|
181
|
+
return hn
|
|
182
|
+
})
|
|
183
|
+
.flatMap((parsedReq) =>
|
|
184
|
+
handle(parsedReq as any)
|
|
185
|
+
.provideService(handler.Request.Tag, parsedReq as any)
|
|
186
|
+
.map(encoder)
|
|
187
|
+
.map((r) =>
|
|
188
|
+
res
|
|
189
|
+
.setBody(HttpBody.unsafeJson(r))
|
|
190
|
+
.setStatus(r === undefined ? 204 : 200)
|
|
191
|
+
)
|
|
192
|
+
) as Effect<
|
|
193
|
+
Exclude<R, EnforceNonEmptyRecord<M>>,
|
|
194
|
+
ValidationError | ResE,
|
|
195
|
+
HttpServerResponse
|
|
196
|
+
>
|
|
197
|
+
|
|
198
|
+
// Commands should not be interruptable.
|
|
199
|
+
const r = req.method !== "GET" ? handleRequest.uninterruptible : handleRequest // .instrument("Performance.RequestResponse")
|
|
200
|
+
const r2 = middlewareLayer
|
|
201
|
+
? r.provide(middlewareLayer)
|
|
202
|
+
// PR is not relevant here
|
|
203
|
+
: (r as Effect<
|
|
204
|
+
Exclude<Exclude<R, EnforceNonEmptyRecord<M>>, PR>,
|
|
205
|
+
ResE | MiddlewareE | ValidationError,
|
|
206
|
+
HttpServerResponse
|
|
207
|
+
>)
|
|
208
|
+
return errorHandler(
|
|
209
|
+
req,
|
|
210
|
+
res,
|
|
211
|
+
r2
|
|
212
|
+
)
|
|
213
|
+
})
|
|
214
|
+
)
|
|
215
|
+
.catchAllCause((cause) =>
|
|
216
|
+
Effect
|
|
217
|
+
.sync(() => res.setStatus(500))
|
|
218
|
+
.tap((res) =>
|
|
219
|
+
Effect
|
|
220
|
+
.all([
|
|
221
|
+
reportError("request")(cause, {
|
|
222
|
+
path: req.originalUrl,
|
|
223
|
+
method: req.method
|
|
224
|
+
}),
|
|
225
|
+
Effect.suspend(() => {
|
|
226
|
+
const headers = res.headers
|
|
227
|
+
return Effect
|
|
228
|
+
.logError("Finished request", cause)
|
|
229
|
+
.annotateLogs({
|
|
230
|
+
method: req.method,
|
|
231
|
+
path: req.originalUrl,
|
|
232
|
+
statusCode: res.status.toString(),
|
|
233
|
+
|
|
234
|
+
reqPath: pars.params.$$.pretty,
|
|
235
|
+
reqQuery: pars.query.$$.pretty,
|
|
236
|
+
reqBody: pretty(pars.body),
|
|
237
|
+
reqCookies: pretty(pars.cookies),
|
|
238
|
+
reqHeaders: pretty(pars.headers),
|
|
239
|
+
|
|
240
|
+
resHeaders: Object
|
|
241
|
+
.entries(headers)
|
|
242
|
+
.reduce((prev, [key, value]) => {
|
|
243
|
+
prev[key] = value && typeof value === "string" ? snipString(value) : value
|
|
244
|
+
return prev
|
|
245
|
+
}, {} as Record<string, any>)
|
|
246
|
+
.$$
|
|
247
|
+
.pretty
|
|
248
|
+
})
|
|
249
|
+
})
|
|
250
|
+
], { concurrency: "inherit" })
|
|
251
|
+
)
|
|
252
|
+
.tapErrorCause((cause) => Effect(console.error("Error occurred while reporting error", cause)))
|
|
253
|
+
)
|
|
254
|
+
.tap(
|
|
255
|
+
Effect
|
|
256
|
+
.logInfo("Finished request")
|
|
257
|
+
.annotateLogs({
|
|
258
|
+
method: req.method,
|
|
259
|
+
path: req.originalUrl,
|
|
260
|
+
statusCode: res.status.toString(),
|
|
261
|
+
...settings.verbose
|
|
262
|
+
? {
|
|
263
|
+
resHeaders: pretty(res.headers)
|
|
264
|
+
}
|
|
265
|
+
: undefined
|
|
266
|
+
})
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
return yield* $(eff)
|
|
270
|
+
})
|
|
271
|
+
.updateRequestContext((_) =>
|
|
272
|
+
_.$$.copy({
|
|
273
|
+
name: NonEmptyString255(
|
|
274
|
+
AST.getTitleAnnotation(Request.ast).value ?? "TODO"
|
|
275
|
+
)
|
|
276
|
+
})
|
|
277
|
+
)
|
|
278
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
3
|
+
import { type RouteDescriptorAny } from "@effect-app/infra/api/express/schema/routing"
|
|
4
|
+
import type { ValidationError } from "@effect-app/infra/errors"
|
|
5
|
+
import type { StructFields } from "@effect-app/schema"
|
|
6
|
+
import { HttpRouter, type HttpServerRequest, type HttpServerResponse } from "../http.js"
|
|
7
|
+
import type { RequestHandler } from "./base.js"
|
|
8
|
+
import { makeRequestHandler } from "./makeRequestHandler.js"
|
|
9
|
+
import type { Middleware } from "./makeRequestHandler.js"
|
|
10
|
+
|
|
11
|
+
export const RouteDescriptors = Tag<Ref<RouteDescriptorAny[]>>()
|
|
12
|
+
|
|
13
|
+
export function match<
|
|
14
|
+
R,
|
|
15
|
+
M,
|
|
16
|
+
PathA extends StructFields,
|
|
17
|
+
CookieA extends StructFields,
|
|
18
|
+
QueryA extends StructFields,
|
|
19
|
+
BodyA extends StructFields,
|
|
20
|
+
HeaderA extends StructFields,
|
|
21
|
+
ReqA extends PathA & QueryA & BodyA,
|
|
22
|
+
ResA extends StructFields,
|
|
23
|
+
ResE,
|
|
24
|
+
MiddlewareE,
|
|
25
|
+
PPath extends `/${string}`,
|
|
26
|
+
R2,
|
|
27
|
+
PR,
|
|
28
|
+
RErr,
|
|
29
|
+
CTX,
|
|
30
|
+
Context,
|
|
31
|
+
Config
|
|
32
|
+
>(
|
|
33
|
+
requestHandler: RequestHandler<
|
|
34
|
+
R,
|
|
35
|
+
M,
|
|
36
|
+
PathA,
|
|
37
|
+
CookieA,
|
|
38
|
+
QueryA,
|
|
39
|
+
BodyA,
|
|
40
|
+
HeaderA,
|
|
41
|
+
ReqA,
|
|
42
|
+
ResA,
|
|
43
|
+
ResE,
|
|
44
|
+
PPath,
|
|
45
|
+
CTX,
|
|
46
|
+
Context,
|
|
47
|
+
Config
|
|
48
|
+
>,
|
|
49
|
+
errorHandler: <R>(
|
|
50
|
+
req: HttpServerRequest,
|
|
51
|
+
res: HttpServerResponse,
|
|
52
|
+
r2: Effect<R, ValidationError | MiddlewareE | ResE, HttpServerResponse>
|
|
53
|
+
) => Effect<RErr | R, never, HttpServerResponse>,
|
|
54
|
+
middleware?: Middleware<
|
|
55
|
+
R,
|
|
56
|
+
M,
|
|
57
|
+
PathA,
|
|
58
|
+
CookieA,
|
|
59
|
+
QueryA,
|
|
60
|
+
BodyA,
|
|
61
|
+
HeaderA,
|
|
62
|
+
ReqA,
|
|
63
|
+
ResA,
|
|
64
|
+
ResE,
|
|
65
|
+
MiddlewareE,
|
|
66
|
+
PPath,
|
|
67
|
+
R2,
|
|
68
|
+
PR,
|
|
69
|
+
CTX,
|
|
70
|
+
Context,
|
|
71
|
+
Config
|
|
72
|
+
>
|
|
73
|
+
) {
|
|
74
|
+
let middlewareLayer: Layer<R2, MiddlewareE, PR> | undefined = undefined
|
|
75
|
+
if (middleware) {
|
|
76
|
+
const { handler, makeRequestLayer } = middleware(requestHandler)
|
|
77
|
+
requestHandler = handler as any // todo
|
|
78
|
+
middlewareLayer = makeRequestLayer
|
|
79
|
+
}
|
|
80
|
+
return Effect.gen(function*() {
|
|
81
|
+
// const rdesc = yield* $(RouteDescriptors.flatMap((_) => _.get))
|
|
82
|
+
|
|
83
|
+
const handler = makeRequestHandler<
|
|
84
|
+
R,
|
|
85
|
+
M,
|
|
86
|
+
PathA,
|
|
87
|
+
CookieA,
|
|
88
|
+
QueryA,
|
|
89
|
+
BodyA,
|
|
90
|
+
HeaderA,
|
|
91
|
+
ReqA,
|
|
92
|
+
ResA,
|
|
93
|
+
ResE,
|
|
94
|
+
MiddlewareE,
|
|
95
|
+
R2,
|
|
96
|
+
PR,
|
|
97
|
+
RErr,
|
|
98
|
+
PPath,
|
|
99
|
+
Config
|
|
100
|
+
>(
|
|
101
|
+
requestHandler as any, // one argument if no middleware, 2 if has middleware. TODO: clean this shit up
|
|
102
|
+
errorHandler,
|
|
103
|
+
middlewareLayer
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
const route = HttpRouter.makeRoute(
|
|
107
|
+
requestHandler.Request.method,
|
|
108
|
+
requestHandler.Request.path,
|
|
109
|
+
handler
|
|
110
|
+
)
|
|
111
|
+
// TODO
|
|
112
|
+
// rdesc.push(makeRouteDescriptor(
|
|
113
|
+
// requestHandler.Request.path,
|
|
114
|
+
// requestHandler.Request.method,
|
|
115
|
+
// requestHandler
|
|
116
|
+
// ))
|
|
117
|
+
return route
|
|
118
|
+
})
|
|
119
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
3
|
+
|
|
4
|
+
// codegen:start {preset: barrel, include: routing/*.ts }
|
|
5
|
+
export * from "./routing/base.js"
|
|
6
|
+
export * from "./routing/defaultErrorHandler.js"
|
|
7
|
+
export * from "./routing/makeRequestHandler.js"
|
|
8
|
+
export * from "./routing/match.js"
|
|
9
|
+
// codegen:end
|
|
10
|
+
|
|
11
|
+
// export * from "@effect-app/infra/api/routing"
|
package/_src/api/setupRequest.ts
CHANGED
|
@@ -3,13 +3,6 @@ import { RequestContext } from "../RequestContext.js"
|
|
|
3
3
|
import { RequestContextContainer } from "../services/RequestContextContainer.js"
|
|
4
4
|
import { ContextMapContainer } from "../services/Store/ContextMapContainer.js"
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* @tsplus fluent effect/io/Effect setupRequestContextFromName
|
|
8
|
-
*/
|
|
9
|
-
export function setupRequestContextFromName<R, E, A>(self: Effect<R, E, A>, name: string) {
|
|
10
|
-
return makeInternalRequestContext(name).flatMap((rc) => setupRequestContext(self, rc))
|
|
11
|
-
}
|
|
12
|
-
|
|
13
6
|
function makeInternalRequestContext(name: string) {
|
|
14
7
|
return Effect.sync(() => {
|
|
15
8
|
const id = RequestId.make()
|
|
@@ -25,40 +18,52 @@ function makeInternalRequestContext(name: string) {
|
|
|
25
18
|
const withRequestSpan = <R, E, A>(f: Effect<R, E, A>) =>
|
|
26
19
|
RequestContextContainer
|
|
27
20
|
.get
|
|
28
|
-
.
|
|
21
|
+
.andThen((ctx) =>
|
|
29
22
|
f
|
|
30
|
-
.withSpan("request", {
|
|
31
|
-
attributes: {
|
|
32
|
-
"request.id": ctx.id,
|
|
33
|
-
"request.name": ctx.name,
|
|
34
|
-
"request.locale": ctx.locale,
|
|
35
|
-
"request.namespace": ctx.namespace,
|
|
36
|
-
...ctx.userProfile?.sub
|
|
37
|
-
? {
|
|
38
|
-
"request.user.sub": ctx
|
|
39
|
-
.userProfile
|
|
40
|
-
.sub,
|
|
41
|
-
"request.user.roles": "roles" in ctx
|
|
42
|
-
.userProfile
|
|
43
|
-
? ctx.userProfile.roles
|
|
44
|
-
: undefined
|
|
45
|
-
}
|
|
46
|
-
: {}
|
|
47
|
-
}
|
|
48
|
-
})
|
|
23
|
+
.withSpan("request", { attributes: ctx.spanAttributes })
|
|
49
24
|
// request context info is picked up directly in the logger for annotations.
|
|
50
25
|
.withLogSpan("request")
|
|
51
26
|
)
|
|
52
27
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
28
|
+
const setupContextMap = ContextMapContainer.andThen((_) => _.start).toLayerDiscard
|
|
29
|
+
|
|
30
|
+
// const RequestContextLiveFromRequestContext = (requestContext: RequestContext) =>
|
|
31
|
+
// RequestContext.Tag.makeLayer(requestContext)
|
|
32
|
+
// memoization problem
|
|
33
|
+
// const RequestContextLive = RequestContextContainer.get.toLayer(RequestContext.Tag)
|
|
34
|
+
const RequestContextStartLiveFromRequestContext = (requestContext: RequestContext) =>
|
|
35
|
+
setupContextMap
|
|
36
|
+
// .provideMerge(RequestContextLiveFromRequestContext(requestContext))
|
|
37
|
+
.provideMerge(
|
|
38
|
+
RequestContextContainer
|
|
39
|
+
.andThen((_) => _.start(requestContext))
|
|
40
|
+
.toLayerDiscard
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const RequestContextStartLive = (requestContext: RequestContext | string) =>
|
|
44
|
+
typeof requestContext === "string"
|
|
45
|
+
? makeInternalRequestContext(requestContext)
|
|
46
|
+
.andThen(RequestContextStartLiveFromRequestContext)
|
|
47
|
+
.unwrapLayer
|
|
48
|
+
: RequestContextStartLiveFromRequestContext(requestContext)
|
|
57
49
|
|
|
58
50
|
/**
|
|
59
51
|
* @tsplus fluent effect/io/Effect setupRequestContext
|
|
60
52
|
*/
|
|
61
|
-
export function setupRequestContext<R, E, A>(self: Effect<R, E, A>, requestContext: RequestContext) {
|
|
62
|
-
return
|
|
63
|
-
|
|
53
|
+
export function setupRequestContext<R, E, A>(self: Effect<R, E, A>, requestContext: RequestContext | string) {
|
|
54
|
+
return self
|
|
55
|
+
.pipe(withRequestSpan)
|
|
56
|
+
.provide(RequestContextStartLive(requestContext))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const UpdateRequestContextLive = (f: (rc: RequestContext) => RequestContext) =>
|
|
60
|
+
RequestContextContainer.andThen((rcc) => rcc.update(f)).toLayerDiscard
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @tsplus fluent effect/io/Effect updateRequestContext
|
|
64
|
+
*/
|
|
65
|
+
export function updateRequestContext<R, E, A>(self: Effect<R, E, A>, f: (rc: RequestContext) => RequestContext) {
|
|
66
|
+
return self
|
|
67
|
+
// .provideServiceEffect(RequestContext.Tag, RequestContextContainer.get)
|
|
68
|
+
.provide(UpdateRequestContextLive(f))
|
|
64
69
|
}
|
package/_src/errorReporter.ts
CHANGED
|
@@ -13,12 +13,13 @@ export function reportError(
|
|
|
13
13
|
return
|
|
14
14
|
}
|
|
15
15
|
yield* $(reportSentry(cause, name, extras))
|
|
16
|
+
const error = new CauseException(cause, name)
|
|
16
17
|
yield* $(
|
|
17
18
|
Effect
|
|
18
19
|
.logError("Reporting error", cause)
|
|
19
20
|
.annotateLogs(dropUndefined({
|
|
20
21
|
extras,
|
|
21
|
-
|
|
22
|
+
__cause__: error.toJSON(),
|
|
22
23
|
__error_name__: name
|
|
23
24
|
}))
|
|
24
25
|
)
|
|
@@ -50,12 +51,13 @@ export function logError<E>(
|
|
|
50
51
|
yield* $(Effect.logDebug("Interrupted").annotateLogs(dropUndefined({ extras })))
|
|
51
52
|
return
|
|
52
53
|
}
|
|
54
|
+
const error = new CauseException(cause, name)
|
|
53
55
|
yield* $(
|
|
54
56
|
Effect
|
|
55
57
|
.logWarning("Logging error", cause)
|
|
56
58
|
.annotateLogs(dropUndefined({
|
|
57
59
|
extras,
|
|
58
|
-
|
|
60
|
+
__cause__: error.toJSON(),
|
|
59
61
|
__error_name__: name
|
|
60
62
|
}))
|
|
61
63
|
)
|
package/_src/errors.ts
CHANGED
|
@@ -1,48 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/ban-types */
|
|
2
2
|
import { makeFiberFailure } from "effect/Runtime"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
export class NotFoundError<T extends string = string>
|
|
6
|
-
extends Data.TaggedError("NotFoundError")<{ message: string; type: T; id: string }>
|
|
7
|
-
{
|
|
8
|
-
constructor(args: { readonly type: T; readonly id: string }) {
|
|
9
|
-
super({ ...args, message: `Didn't find ${args.type}#${args.id}` })
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** @tsplus type ValidationError */
|
|
14
|
-
export class ValidationError extends Data.TaggedError("ValidationError")<{ errors: ReadonlyArray<unknown> }> {}
|
|
15
|
-
|
|
16
|
-
/** @tsplus type NotLoggedInError */
|
|
17
|
-
export class NotLoggedInError extends Data.TaggedError("NotLoggedInError")<{ message?: string | undefined }> {
|
|
18
|
-
constructor(message?: string) {
|
|
19
|
-
super({ message })
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** @tsplus type UnauthorizedError */
|
|
24
|
-
export class UnauthorizedError extends Data.TaggedError("UnauthorizedError")<{ message?: string | undefined }> {
|
|
25
|
-
constructor(message?: string) {
|
|
26
|
-
super({ message })
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* The user carries a valid Userprofile, but there is a problem with the login none the less.
|
|
32
|
-
*/
|
|
33
|
-
/** @tsplus type LoginError */
|
|
34
|
-
export class LoginError extends Data.TaggedError("NotLoggeInError")<{ message: string }> {
|
|
35
|
-
constructor(message: string) {
|
|
36
|
-
super({ message })
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** @tsplus type InvalidStateError */
|
|
41
|
-
export class InvalidStateError extends Data.TaggedError("InvalidStateError")<{ message: string }> {
|
|
42
|
-
constructor(message: string) {
|
|
43
|
-
super({ message })
|
|
44
|
-
}
|
|
45
|
-
}
|
|
4
|
+
export * from "@effect-app/prelude/client/errors"
|
|
46
5
|
|
|
47
6
|
/** @tsplus type CauseException */
|
|
48
7
|
export class CauseException<E> extends Error {
|
|
@@ -75,19 +34,3 @@ export class CauseException<E> extends Error {
|
|
|
75
34
|
return `[${this._tag}] ` + Cause.pretty(this.originalCause)
|
|
76
35
|
}
|
|
77
36
|
}
|
|
78
|
-
|
|
79
|
-
/** @tsplus type OptimisticConcurrencyException */
|
|
80
|
-
export class OptimisticConcurrencyException extends Data.TaggedError("OptimisticConcurrencyException")<
|
|
81
|
-
{ type: string; id: string; current?: string | undefined; found?: string | undefined; message: string }
|
|
82
|
-
> {
|
|
83
|
-
constructor(
|
|
84
|
-
args: {
|
|
85
|
-
readonly type: string
|
|
86
|
-
readonly id: string
|
|
87
|
-
readonly current?: string | undefined
|
|
88
|
-
readonly found?: string | undefined
|
|
89
|
-
}
|
|
90
|
-
) {
|
|
91
|
-
super({ ...args, message: `Existing ${args.type} ${args.id} record changed` })
|
|
92
|
-
}
|
|
93
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MemQueue } from "@effect-app/infra-adapters/memQueue"
|
|
2
2
|
import { RequestContext } from "@effect-app/infra/RequestContext"
|
|
3
|
-
import type {
|
|
3
|
+
import type { S } from "@effect-app/prelude"
|
|
4
4
|
import { RequestId } from "@effect-app/prelude/ids"
|
|
5
5
|
import { Tracer } from "effect"
|
|
6
6
|
import { RequestContextContainer } from "../RequestContextContainer.js"
|
|
@@ -17,8 +17,8 @@ export function makeMemQueue<
|
|
|
17
17
|
>(
|
|
18
18
|
queueName: string,
|
|
19
19
|
queueDrainName: string,
|
|
20
|
-
schema:
|
|
21
|
-
drainSchema:
|
|
20
|
+
schema: S.Schema<EvtE, Evt>,
|
|
21
|
+
drainSchema: S.Schema<unknown, DrainEvt>
|
|
22
22
|
) {
|
|
23
23
|
return Effect.gen(function*($) {
|
|
24
24
|
const mem = yield* $(MemQueue)
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import type {} from "@azure/service-bus"
|
|
10
10
|
import { captureException } from "@effect-app/infra/errorReporter"
|
|
11
11
|
import { RequestContext } from "@effect-app/infra/RequestContext"
|
|
12
|
-
import type {
|
|
12
|
+
import type { S } from "@effect-app/prelude"
|
|
13
13
|
import { RequestId } from "@effect-app/prelude/ids"
|
|
14
14
|
import { struct } from "@effect-app/schema"
|
|
15
15
|
import { Tracer } from "effect"
|
|
@@ -27,8 +27,8 @@ export function makeServiceBusQueue<
|
|
|
27
27
|
>(
|
|
28
28
|
_queueName: string,
|
|
29
29
|
queueDrainName: string,
|
|
30
|
-
schema:
|
|
31
|
-
drainSchema:
|
|
30
|
+
schema: S.Schema<EvtE, Evt>,
|
|
31
|
+
drainSchema: S.Schema<unknown, DrainEvt>
|
|
32
32
|
) {
|
|
33
33
|
const wireSchema = struct({
|
|
34
34
|
body: schema,
|