@effect-app/infra 2.70.2 → 2.71.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 +12 -0
- package/dist/Store/ContextMapContainer.d.ts +1 -1
- package/dist/api/internal/middlewares.js +1 -1
- package/dist/api/routing/DynamicMiddleware.d.ts +29 -19
- package/dist/api/routing/DynamicMiddleware.d.ts.map +1 -1
- package/dist/api/routing/DynamicMiddleware.js +37 -13
- package/dist/api/routing/utils.d.ts +7 -0
- package/dist/api/routing/utils.d.ts.map +1 -1
- package/dist/api/routing/utils.js +2 -1
- package/dist/api/routing.d.ts +47 -48
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +54 -67
- package/package.json +2 -2
- package/src/api/internal/middlewares.ts +1 -1
- package/src/api/routing/DynamicMiddleware.ts +178 -62
- package/src/api/routing/utils.ts +2 -0
- package/src/api/routing.ts +208 -169
- package/test/controller.test.ts +63 -108
- package/test/dist/controller.test.d.ts.map +1 -1
package/test/controller.test.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
3
|
-
import { type MakeContext, type MakeErrors,
|
|
3
|
+
import { type MakeContext, type MakeErrors, makeRouter } from "@effect-app/infra/api/routing"
|
|
4
4
|
import type { RequestContext } from "@effect-app/infra/RequestContext"
|
|
5
5
|
import { expectTypeOf } from "@effect/vitest"
|
|
6
|
-
import { Context, Effect, Layer,
|
|
6
|
+
import { Console, Context, Effect, Layer, S } from "effect-app"
|
|
7
7
|
import { type GetEffectContext, InvalidStateError, makeRpcClient, type RPCContextMap, UnauthorizedError } from "effect-app/client"
|
|
8
|
-
import {
|
|
8
|
+
import { HttpServerRequest } from "effect-app/http"
|
|
9
9
|
import { Class, TaggedError } from "effect-app/Schema"
|
|
10
|
-
import
|
|
10
|
+
import { makeMiddleware } from "../src/api/routing/DynamicMiddleware.js"
|
|
11
11
|
import { SomeService } from "./query.test.js"
|
|
12
12
|
|
|
13
13
|
class UserProfile extends Context.assignTag<UserProfile, UserProfile>("UserProfile")(
|
|
@@ -35,114 +35,55 @@ export class ContextMaker extends Effect.Service<ContextMaker>()("ContextMaker",
|
|
|
35
35
|
})
|
|
36
36
|
}) {}
|
|
37
37
|
|
|
38
|
-
export type
|
|
38
|
+
export type RequestContextMap = {
|
|
39
39
|
allowAnonymous: RPCContextMap.Inverted<"userProfile", UserProfile, typeof NotLoggedInError>
|
|
40
40
|
// TODO: not boolean but `string[]`
|
|
41
41
|
requireRoles: RPCContextMap.Custom<"", never, typeof UnauthorizedError, Array<string>>
|
|
42
42
|
}
|
|
43
|
-
const middleware = makeMiddleware({
|
|
44
|
-
contextMap: null as unknown as CTXMap,
|
|
45
|
-
// helper to deal with nested generic lmitations
|
|
46
|
-
context: null as any as HttpServerRequest.HttpServerRequest,
|
|
47
|
-
contextProvider: ContextMaker,
|
|
48
|
-
execute: Effect.gen(function*() {
|
|
49
|
-
return <T extends { config?: { [K in keyof CTXMap]?: any } }, Req extends S.TaggedRequest.All, HandlerR>(
|
|
50
|
-
_schema: T & S.Schema<Req, any, never>,
|
|
51
|
-
handler: (
|
|
52
|
-
request: Req,
|
|
53
|
-
headers: any
|
|
54
|
-
) => Effect.Effect<EffectRequest.Request.Success<Req>, EffectRequest.Request.Error<Req>, HandlerR>,
|
|
55
|
-
moduleName?: string
|
|
56
|
-
) =>
|
|
57
|
-
(
|
|
58
|
-
req: Req,
|
|
59
|
-
headers: any
|
|
60
|
-
): Effect.Effect<
|
|
61
|
-
Request.Request.Success<Req>,
|
|
62
|
-
Request.Request.Error<Req>,
|
|
63
|
-
| HttpServerRequest.HttpServerRequest
|
|
64
|
-
| Exclude<HandlerR, GetEffectContext<CTXMap, T["config"]>>
|
|
65
|
-
> =>
|
|
66
|
-
Effect
|
|
67
|
-
.gen(function*() {
|
|
68
|
-
// const headers = yield* Rpc.currentHeaders
|
|
69
|
-
const ctx = Context.empty().pipe(
|
|
70
|
-
Context.add(UserProfile, { id: "whatever" })
|
|
71
|
-
)
|
|
72
43
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
// }
|
|
114
|
-
// }
|
|
115
|
-
|
|
116
|
-
return yield* handler(req, headers).pipe(
|
|
117
|
-
Effect.provide(ctx as Context.Context<GetEffectContext<CTXMap, T["config"]>>),
|
|
118
|
-
Effect.provideService(Some, new Some({ a: 1 }))
|
|
119
|
-
)
|
|
120
|
-
})
|
|
121
|
-
.pipe(
|
|
122
|
-
Effect.provide(
|
|
123
|
-
Effect
|
|
124
|
-
.gen(function*() {
|
|
125
|
-
yield* Effect.annotateCurrentSpan("request.name", moduleName ? `${moduleName}.${req._tag}` : req._tag)
|
|
126
|
-
// yield* RequestContextContainer.update((_) => ({
|
|
127
|
-
// ..._,
|
|
128
|
-
// name: NonEmptyString255(moduleName ? `${moduleName}.${req._tag}` : req._tag)
|
|
129
|
-
// }))
|
|
130
|
-
// const httpReq = yield* HttpServerRequest.HttpServerRequest
|
|
131
|
-
// TODO: only pass Authentication etc, or move headers to actual Rpc Headers
|
|
132
|
-
// yield* FiberRef.update(
|
|
133
|
-
// Rpc.currentHeaders,
|
|
134
|
-
// (headers) =>
|
|
135
|
-
// HttpHeaders.merge(
|
|
136
|
-
// httpReq.headers,
|
|
137
|
-
// headers
|
|
138
|
-
// )
|
|
139
|
-
// )
|
|
140
|
-
})
|
|
141
|
-
.pipe(Layer.effectDiscard)
|
|
44
|
+
const Str = Context.GenericTag<"str", "str">("str")
|
|
45
|
+
const Str2 = Context.GenericTag<"str2", "str">("str2")
|
|
46
|
+
|
|
47
|
+
const middleware = makeMiddleware<RequestContextMap, HttpServerRequest.HttpServerRequest>()({
|
|
48
|
+
dependencies: [Layer.effect(Str2, Str)],
|
|
49
|
+
contextProvider: ContextMaker,
|
|
50
|
+
executeContextual: (maker) =>
|
|
51
|
+
Effect.gen(function*() {
|
|
52
|
+
return maker((_schema, handler, moduleName) => (req, headers) => {
|
|
53
|
+
return Effect
|
|
54
|
+
.gen(function*() {
|
|
55
|
+
// const headers = yield* Rpc.currentHeaders
|
|
56
|
+
const ctx = Context.empty().pipe(
|
|
57
|
+
Context.add(UserProfile, { id: "whatever" })
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
// you can use only HttpServerRequest.HttpServerRequest here as additional context
|
|
61
|
+
// const some = yield* Some
|
|
62
|
+
|
|
63
|
+
const httpReq = yield* HttpServerRequest.HttpServerRequest
|
|
64
|
+
yield* Console.log("HttpServerRequest", httpReq)
|
|
65
|
+
|
|
66
|
+
return yield* handler(req, headers).pipe(
|
|
67
|
+
Effect.provide(ctx as Context.Context<GetEffectContext<RequestContextMap, (typeof _schema)["config"]>>)
|
|
68
|
+
// I do expect the ContextMaker to provide this
|
|
69
|
+
// Effect.provideService(Some, new Some({ a: 1 }))
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
.pipe(
|
|
73
|
+
Effect.provide(
|
|
74
|
+
Effect
|
|
75
|
+
.gen(function*() {
|
|
76
|
+
yield* Effect.annotateCurrentSpan("request.name", moduleName ? `${moduleName}.${req._tag}` : req._tag)
|
|
77
|
+
|
|
78
|
+
// const httpReq = yield* HttpServerRequest.HttpServerRequest
|
|
79
|
+
|
|
80
|
+
//
|
|
81
|
+
})
|
|
82
|
+
.pipe(Layer.effectDiscard)
|
|
83
|
+
)
|
|
142
84
|
)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
})
|
|
85
|
+
})
|
|
86
|
+
})
|
|
146
87
|
})
|
|
147
88
|
|
|
148
89
|
export type RequestConfig = {
|
|
@@ -151,7 +92,7 @@ export type RequestConfig = {
|
|
|
151
92
|
/** Control the roles that are required to access the resource */
|
|
152
93
|
allowRoles?: readonly string[]
|
|
153
94
|
}
|
|
154
|
-
export const { TaggedRequest: Req } = makeRpcClient<RequestConfig,
|
|
95
|
+
export const { TaggedRequest: Req } = makeRpcClient<RequestConfig, RequestContextMap>({
|
|
155
96
|
allowAnonymous: NotLoggedInError,
|
|
156
97
|
requireRoles: UnauthorizedError
|
|
157
98
|
})
|
|
@@ -163,6 +104,20 @@ export class DoSomething extends Req<DoSomething>()("DoSomething", {
|
|
|
163
104
|
id: S.String
|
|
164
105
|
}, { success: S.Void }) {}
|
|
165
106
|
|
|
107
|
+
// const rpc = makeRpc(middleware).pipe(
|
|
108
|
+
// Effect.map(({ effect }) =>
|
|
109
|
+
// effect(
|
|
110
|
+
// DoSomething,
|
|
111
|
+
// Effect.fn(function*(req, headers) {
|
|
112
|
+
// const user = yield* UserProfile // dynamic context
|
|
113
|
+
// const some = yield* Some // context provided by ContextMaker
|
|
114
|
+
// const someservice = yield* SomeService // extraneous service
|
|
115
|
+
// yield* Console.log("DoSomething", req.id, some)
|
|
116
|
+
// })
|
|
117
|
+
// )
|
|
118
|
+
// )
|
|
119
|
+
// )
|
|
120
|
+
|
|
166
121
|
export class GetSomething extends Req<GetSomething>()("GetSomething", {
|
|
167
122
|
id: S.String
|
|
168
123
|
}, { success: S.String }) {}
|
|
@@ -269,7 +224,7 @@ const router = Router(Something)({
|
|
|
269
224
|
|
|
270
225
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
271
226
|
const matched = matchAll({ router })
|
|
272
|
-
expectTypeOf({} as Layer.Context<typeof matched>).toEqualTypeOf<SomeService>()
|
|
227
|
+
expectTypeOf({} as Layer.Context<typeof matched>).toEqualTypeOf<SomeService | "str">()
|
|
273
228
|
|
|
274
229
|
type makeContext = MakeContext<typeof router.make>
|
|
275
230
|
expectTypeOf({} as MakeErrors<typeof router.make>).toEqualTypeOf<InvalidStateError>()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.test.d.ts","sourceRoot":"","sources":["../controller.test.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"controller.test.d.ts","sourceRoot":"","sources":["../controller.test.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAc,MAAM,+BAA+B,CAAA;AAC7F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAEtE,OAAO,EAAW,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,YAAY,CAAA;AAC/D,OAAO,EAAE,KAAK,gBAAgB,EAAoC,KAAK,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAClI,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAGnD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;;;;;;;;AAE7C,cAAM,WAAY,SAAQ,gBAIzB;CACA;;;;;;AAED,cAAM,gBAAiB,SAAQ,qBAE7B;CAAG;AAEL,MAAM,WAAW,GAAG;IAClB,OAAO,EAAE,cAAc,CAAA;CACxB;;;;;;;;;;;;;;;;;;;;;;;;;AAED,qBAAa,IAAK,SAAQ,SAA2D;CAAG;;;;AAGxF,qBAAa,YAAa,SAAQ,iBAKhC;CAAG;AAEL,MAAM,MAAM,iBAAiB,GAAG;IAC9B,cAAc,EAAE,aAAa,CAAC,QAAQ,CAAC,aAAa,EAAE,WAAW,EAAE,OAAO,gBAAgB,CAAC,CAAA;IAE3F,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,iBAAiB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;CACvF,CAAA;AA+CD,MAAM,MAAM,aAAa,GAAG;IAC1B,yCAAyC;IACzC,cAAc,CAAC,EAAE,IAAI,CAAA;IACrB,iEAAiE;IACjE,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC/B,CAAA;AACD,eAAO,MAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAG/B,CAAA;;;;;;;;;;AAEF,qBAAa,GAAI,SAAQ,QAA0C;CAAG;;;;;;;;;;AACtE,qBAAa,GAAI,SAAQ,QAA0C;CAAG;;;;;;;;;;;;AAEtE,qBAAa,WAAY,SAAQ,gBAEV;CAAG;;;;;;;;;;;;AAgB1B,qBAAa,YAAa,SAAQ,iBAET;CAAG;;;;;;;;;;;;AAE5B,qBAAa,aAAc,SAAQ,kBAEA;CAAG;;;;;AAItC,qBAAa,gBAAiB,SAAQ,qBAKpC;CAAG;;;;;AASL,qBAAa,aAAc,SAAQ,kBAOjC;CAAG;;;;;AAEL,qBAAa,iBAAkB,SAAQ,sBAKrC;CAAG;AAEL,eAAO,MAAQ,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAAE,QAAQ;;;;;2HAAE,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qFAAiC,CAAA"}
|