@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.
@@ -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, makeMiddleware, makeRouter } from "@effect-app/infra/api/routing"
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, type Request, S } from "effect-app"
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 { type HttpServerRequest } from "effect-app/http"
8
+ import { HttpServerRequest } from "effect-app/http"
9
9
  import { Class, TaggedError } from "effect-app/Schema"
10
- import type * as EffectRequest from "effect/Request"
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 CTXMap = {
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
- // const config = "config" in schema ? schema.config : undefined
74
-
75
- // Check JWT
76
- // TODO
77
- // if (!fakeLogin && !request.allowAnonymous) {
78
- // yield* Effect.catchAll(
79
- // checkJWTI({
80
- // ...authConfig,
81
- // issuer: authConfig.issuer + "/",
82
- // jwksUri: `${authConfig.issuer}/.well-known/jwks.json`
83
- // }),
84
- // (err) => Effect.fail(new JWTError({ error: err }))
85
- // )
86
- // }
87
-
88
- // const fakeLogin = true
89
- // const r = (fakeLogin
90
- // ? makeUserProfileFromUserHeader(headers["x-user"])
91
- // : makeUserProfileFromAuthorizationHeader(
92
- // headers["authorization"]
93
- // ))
94
- // .pipe(Effect.exit, basicRuntime.runSync)
95
- // if (!Exit.isSuccess(r)) {
96
- // yield* Effect.logWarning("Parsing userInfo failed").pipe(Effect.annotateLogs("r", r))
97
- // }
98
- // const userProfile = Option.fromNullable(Exit.isSuccess(r) ? r.value : undefined)
99
- // if (Option.isSome(userProfile)) {
100
- // // yield* rcc.update((_) => ({ ..._, userPorfile: userProfile.value }))
101
- // ctx = ctx.pipe(Context.add(UserProfile, userProfile.value))
102
- // } else if (!config?.allowAnonymous) {
103
- // return yield* new NotLoggedInError({ message: "no auth" })
104
- // }
105
-
106
- // if (config?.requireRoles) {
107
- // // TODO
108
- // if (
109
- // !userProfile.value
110
- // || !config.requireRoles.every((role: any) => userProfile.value!.roles.includes(role))
111
- // ) {
112
- // return yield* new UnauthorizedError()
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
- // .pipe(Effect.provide(RequestCacheLayers)) as any
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, CTXMap>({
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,EAA8B,MAAM,+BAA+B,CAAA;AAC7G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAEtE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAgB,CAAC,EAAE,MAAM,YAAY,CAAA;AACpE,OAAO,EAAE,KAAK,gBAAgB,EAAoC,KAAK,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAClI,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAGxD,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,MAAM,GAAG;IACnB,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;AA0GD,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;;;;;;;;;;;;AAE1B,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"}
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"}