@effect-app/infra 2.35.4 → 2.37.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,197 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2
- import { makeMiddleware, makeRouter } from "@effect-app/infra/api/routing.legacy2"
3
- import type { RequestContext } from "@effect-app/infra/RequestContext"
4
- import { Rpc } from "@effect/rpc"
5
- import type { Request } from "effect-app"
6
- import { Context, Effect, FiberRef, Layer, S, Schedule } from "effect-app"
7
- import { type GetEffectContext, makeRpcClient, type RPCContextMap, UnauthorizedError } from "effect-app/client"
8
- import { HttpHeaders, HttpServerRequest } from "effect-app/http"
9
- import type * as EffectRequest from "effect/Request"
10
- import { it } from "vitest"
11
-
12
- const optimisticConcurrencySchedule = Schedule.once
13
- && Schedule.recurWhile<any>((a) => a?._tag === "OptimisticConcurrencyException")
14
-
15
- export interface CTX {
16
- context: RequestContext
17
- }
18
-
19
- export type CTXMap = {
20
- // allowAnonymous: RPCContextMap.Inverted<"userProfile", UserProfile, typeof NotLoggedInError>
21
- // TODO: not boolean but `string[]`
22
- requireRoles: RPCContextMap.Custom<"", never, typeof UnauthorizedError, Array<string>>
23
- }
24
- const middleware = makeMiddleware({
25
- contextMap: null as unknown as CTXMap,
26
- // helper to deal with nested generic lmitations
27
- context: null as any as HttpServerRequest.HttpServerRequest,
28
- execute: Effect.gen(function*() {
29
- return <T extends { config?: { [K in keyof CTXMap]?: any } }, Req extends S.TaggedRequest.All, R>(
30
- _schema: T & S.Schema<Req, any, never>,
31
- handler: (request: Req) => Effect.Effect<EffectRequest.Request.Success<Req>, EffectRequest.Request.Error<Req>, R>,
32
- moduleName?: string
33
- ) =>
34
- (
35
- req: Req
36
- ): Effect.Effect<
37
- Request.Request.Success<Req>,
38
- Request.Request.Error<Req>,
39
- | HttpServerRequest.HttpServerRequest
40
- | Exclude<R, GetEffectContext<CTXMap, T["config"]>>
41
- > =>
42
- Effect
43
- .gen(function*() {
44
- // const headers = yield* Rpc.currentHeaders
45
- const ctx = Context.empty()
46
-
47
- // const config = "config" in schema ? schema.config : undefined
48
-
49
- // Check JWT
50
- // TODO
51
- // if (!fakeLogin && !request.allowAnonymous) {
52
- // yield* Effect.catchAll(
53
- // checkJWTI({
54
- // ...authConfig,
55
- // issuer: authConfig.issuer + "/",
56
- // jwksUri: `${authConfig.issuer}/.well-known/jwks.json`
57
- // }),
58
- // (err) => Effect.fail(new JWTError({ error: err }))
59
- // )
60
- // }
61
-
62
- // const fakeLogin = true
63
- // const r = (fakeLogin
64
- // ? makeUserProfileFromUserHeader(headers["x-user"])
65
- // : makeUserProfileFromAuthorizationHeader(
66
- // headers["authorization"]
67
- // ))
68
- // .pipe(Effect.exit, basicRuntime.runSync)
69
- // if (!Exit.isSuccess(r)) {
70
- // yield* Effect.logWarning("Parsing userInfo failed").pipe(Effect.annotateLogs("r", r))
71
- // }
72
- // const userProfile = Option.fromNullable(Exit.isSuccess(r) ? r.value : undefined)
73
- // if (Option.isSome(userProfile)) {
74
- // // yield* rcc.update((_) => ({ ..._, userPorfile: userProfile.value }))
75
- // ctx = ctx.pipe(Context.add(UserProfile, userProfile.value))
76
- // } else if (!config?.allowAnonymous) {
77
- // return yield* new NotLoggedInError({ message: "no auth" })
78
- // }
79
-
80
- // if (config?.requireRoles) {
81
- // // TODO
82
- // if (
83
- // !userProfile.value
84
- // || !config.requireRoles.every((role: any) => userProfile.value!.roles.includes(role))
85
- // ) {
86
- // return yield* new UnauthorizedError()
87
- // }
88
- // }
89
-
90
- return yield* handler(req).pipe(
91
- Effect.retry(optimisticConcurrencySchedule),
92
- Effect.provide(ctx as Context.Context<GetEffectContext<CTXMap, T["config"]>>)
93
- )
94
- })
95
- .pipe(
96
- Effect.provide(
97
- Effect
98
- .gen(function*() {
99
- yield* Effect.annotateCurrentSpan("request.name", moduleName ? `${moduleName}.${req._tag}` : req._tag)
100
- // yield* RequestContextContainer.update((_) => ({
101
- // ..._,
102
- // name: NonEmptyString255(moduleName ? `${moduleName}.${req._tag}` : req._tag)
103
- // }))
104
- const httpReq = yield* HttpServerRequest.HttpServerRequest
105
- // TODO: only pass Authentication etc, or move headers to actual Rpc Headers
106
- yield* FiberRef.update(
107
- Rpc.currentHeaders,
108
- (headers) =>
109
- HttpHeaders.merge(
110
- httpReq.headers,
111
- headers
112
- )
113
- )
114
- })
115
- .pipe(Layer.effectDiscard)
116
- )
117
- )
118
- // .pipe(Effect.provide(RequestCacheLayers)) as any
119
- })
120
- })
121
-
122
- export const { matchAll, matchFor } = makeRouter(middleware, true)
123
-
124
- export type RequestConfig = {
125
- /** Disable authentication requirement */
126
- allowAnonymous?: true
127
- /** Control the roles that are required to access the resource */
128
- allowRoles?: readonly string[]
129
- }
130
- export const { TaggedRequest: Req } = makeRpcClient<RequestConfig, CTXMap>({
131
- // allowAnonymous: NotLoggedInError,
132
- requireRoles: UnauthorizedError
133
- })
134
-
135
- export class GetSomething extends Req<GetSomething>()("GetSomething", {
136
- id: S.String
137
- }, { success: S.Void }) {}
138
-
139
- export class GetSomethingElse extends Req<GetSomethingElse>()("GetSomethingElse", {
140
- id: S.String
141
- }, { success: S.String }) {}
142
-
143
- const Something = { GetSomething, GetSomethingElse, meta: { moduleName: "Something" as const } }
144
-
145
- export class SomethingService extends Effect.Service<SomethingService>()("SomethingService", {
146
- dependencies: [],
147
- effect: Effect.gen(function*() {
148
- return {}
149
- })
150
- }) {}
151
-
152
- declare const a: {
153
- (opt: { a: 1 }): void
154
- (opt: { a: 2 }): void
155
- (opt: { b: 3 }): void
156
- (opt: { b: 3 }): void
157
- }
158
-
159
- export class SomethingRepo extends Effect.Service<SomethingRepo>()("SomethingRepo", {
160
- dependencies: [SomethingService.Default],
161
- effect: Effect.gen(function*() {
162
- const smth = yield* SomethingService
163
- console.log({ smth })
164
- return {}
165
- })
166
- }) {}
167
-
168
- export class SomethingService2 extends Effect.Service<SomethingService2>()("SomethingService2", {
169
- dependencies: [],
170
- effect: Effect.gen(function*() {
171
- return {}
172
- })
173
- }) {}
174
-
175
- it("router.legacy2", () => {
176
- const routes = matchFor(Something)({
177
- dependencies: [
178
- SomethingRepo.Default,
179
- SomethingService.Default,
180
- SomethingService2.Default
181
- ],
182
- effect: Effect.gen(function*() {
183
- const repo = yield* SomethingRepo
184
- const smth = yield* SomethingService
185
- const smth2 = yield* SomethingService2
186
-
187
- console.log({ repo, smth, smth2 })
188
-
189
- const { GetSomething, GetSomethingElse } = matchFor(Something)
190
- return {
191
- GetSomething: GetSomething(Effect.void),
192
- GetSomethingElse: GetSomethingElse(Effect.succeed("12"))
193
- }
194
- })
195
- })
196
- console.log({ routes })
197
- })
@@ -1,195 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2
- import { makeMiddleware, makeRouter } from "@effect-app/infra/api/routing.legacy3"
3
- import type { RequestContext } from "@effect-app/infra/RequestContext"
4
- import { Rpc } from "@effect/rpc"
5
- import type { Request } from "effect-app"
6
- import { Context, Effect, FiberRef, Layer, S, Schedule } from "effect-app"
7
- import { type GetEffectContext, makeRpcClient, type RPCContextMap, UnauthorizedError } from "effect-app/client"
8
- import { HttpHeaders, HttpServerRequest } from "effect-app/http"
9
- import type * as EffectRequest from "effect/Request"
10
- import { it } from "vitest"
11
-
12
- const optimisticConcurrencySchedule = Schedule.once
13
- && Schedule.recurWhile<any>((a) => a?._tag === "OptimisticConcurrencyException")
14
-
15
- export interface CTX {
16
- context: RequestContext
17
- }
18
-
19
- export type CTXMap = {
20
- // allowAnonymous: RPCContextMap.Inverted<"userProfile", UserProfile, typeof NotLoggedInError>
21
- // TODO: not boolean but `string[]`
22
- requireRoles: RPCContextMap.Custom<"", never, typeof UnauthorizedError, Array<string>>
23
- }
24
- const middleware = makeMiddleware({
25
- contextMap: null as unknown as CTXMap,
26
- // helper to deal with nested generic lmitations
27
- context: null as any as HttpServerRequest.HttpServerRequest,
28
- execute: Effect.gen(function*() {
29
- return <T extends { config?: { [K in keyof CTXMap]?: any } }, Req extends S.TaggedRequest.All, R>(
30
- _schema: T & S.Schema<Req, any, never>,
31
- handler: (request: Req) => Effect.Effect<EffectRequest.Request.Success<Req>, EffectRequest.Request.Error<Req>, R>,
32
- moduleName?: string
33
- ) =>
34
- (
35
- req: Req
36
- ): Effect.Effect<
37
- Request.Request.Success<Req>,
38
- Request.Request.Error<Req>,
39
- | HttpServerRequest.HttpServerRequest
40
- | Exclude<R, GetEffectContext<CTXMap, T["config"]>>
41
- > =>
42
- Effect
43
- .gen(function*() {
44
- // const headers = yield* Rpc.currentHeaders
45
- const ctx = Context.empty()
46
-
47
- // const config = "config" in schema ? schema.config : undefined
48
-
49
- // Check JWT
50
- // TODO
51
- // if (!fakeLogin && !request.allowAnonymous) {
52
- // yield* Effect.catchAll(
53
- // checkJWTI({
54
- // ...authConfig,
55
- // issuer: authConfig.issuer + "/",
56
- // jwksUri: `${authConfig.issuer}/.well-known/jwks.json`
57
- // }),
58
- // (err) => Effect.fail(new JWTError({ error: err }))
59
- // )
60
- // }
61
-
62
- // const fakeLogin = true
63
- // const r = (fakeLogin
64
- // ? makeUserProfileFromUserHeader(headers["x-user"])
65
- // : makeUserProfileFromAuthorizationHeader(
66
- // headers["authorization"]
67
- // ))
68
- // .pipe(Effect.exit, basicRuntime.runSync)
69
- // if (!Exit.isSuccess(r)) {
70
- // yield* Effect.logWarning("Parsing userInfo failed").pipe(Effect.annotateLogs("r", r))
71
- // }
72
- // const userProfile = Option.fromNullable(Exit.isSuccess(r) ? r.value : undefined)
73
- // if (Option.isSome(userProfile)) {
74
- // // yield* rcc.update((_) => ({ ..._, userPorfile: userProfile.value }))
75
- // ctx = ctx.pipe(Context.add(UserProfile, userProfile.value))
76
- // } else if (!config?.allowAnonymous) {
77
- // return yield* new NotLoggedInError({ message: "no auth" })
78
- // }
79
-
80
- // if (config?.requireRoles) {
81
- // // TODO
82
- // if (
83
- // !userProfile.value
84
- // || !config.requireRoles.every((role: any) => userProfile.value!.roles.includes(role))
85
- // ) {
86
- // return yield* new UnauthorizedError()
87
- // }
88
- // }
89
-
90
- return yield* handler(req).pipe(
91
- Effect.retry(optimisticConcurrencySchedule),
92
- Effect.provide(ctx as Context.Context<GetEffectContext<CTXMap, T["config"]>>)
93
- )
94
- })
95
- .pipe(
96
- Effect.provide(
97
- Effect
98
- .gen(function*() {
99
- yield* Effect.annotateCurrentSpan("request.name", moduleName ? `${moduleName}.${req._tag}` : req._tag)
100
- // yield* RequestContextContainer.update((_) => ({
101
- // ..._,
102
- // name: NonEmptyString255(moduleName ? `${moduleName}.${req._tag}` : req._tag)
103
- // }))
104
- const httpReq = yield* HttpServerRequest.HttpServerRequest
105
- // TODO: only pass Authentication etc, or move headers to actual Rpc Headers
106
- yield* FiberRef.update(
107
- Rpc.currentHeaders,
108
- (headers) =>
109
- HttpHeaders.merge(
110
- httpReq.headers,
111
- headers
112
- )
113
- )
114
- })
115
- .pipe(Layer.effectDiscard)
116
- )
117
- )
118
- // .pipe(Effect.provide(RequestCacheLayers)) as any
119
- })
120
- })
121
-
122
- export const { matchAll, matchFor } = makeRouter(middleware, true)
123
-
124
- export type RequestConfig = {
125
- /** Disable authentication requirement */
126
- allowAnonymous?: true
127
- /** Control the roles that are required to access the resource */
128
- allowRoles?: readonly string[]
129
- }
130
- export const { TaggedRequest: Req } = makeRpcClient<RequestConfig, CTXMap>({
131
- // allowAnonymous: NotLoggedInError,
132
- requireRoles: UnauthorizedError
133
- })
134
-
135
- export class GetSomething extends Req<GetSomething>()("GetSomething", {
136
- id: S.String
137
- }, { success: S.Void }) {}
138
-
139
- export class GetSomethingElse extends Req<GetSomethingElse>()("GetSomethingElse", {
140
- id: S.String
141
- }, { success: S.String }) {}
142
-
143
- const Something = { GetSomething, GetSomethingElse, meta: { moduleName: "Something" as const } }
144
-
145
- export class SomethingService extends Effect.Service<SomethingService>()("SomethingService", {
146
- dependencies: [],
147
- effect: Effect.gen(function*() {
148
- return {}
149
- })
150
- }) {}
151
-
152
- declare const a: {
153
- (opt: { a: 1 }): void
154
- (opt: { a: 2 }): void
155
- (opt: { b: 3 }): void
156
- (opt: { b: 3 }): void
157
- }
158
-
159
- export class SomethingRepo extends Effect.Service<SomethingRepo>()("SomethingRepo", {
160
- dependencies: [SomethingService.Default],
161
- effect: Effect.gen(function*() {
162
- const smth = yield* SomethingService
163
- console.log({ smth })
164
- return {}
165
- })
166
- }) {}
167
-
168
- export class SomethingService2 extends Effect.Service<SomethingService2>()("SomethingService2", {
169
- dependencies: [],
170
- effect: Effect.gen(function*() {
171
- return {}
172
- })
173
- }) {}
174
-
175
- it("router.legacy3 fluent", () => {
176
- const routes = matchFor(Something)({
177
- dependencies: [
178
- SomethingRepo.Default,
179
- SomethingService.Default,
180
- SomethingService2.Default
181
- ],
182
- effect: Effect.gen(function*() {
183
- const repo = yield* SomethingRepo
184
- const smth = yield* SomethingService
185
- const smth2 = yield* SomethingService2
186
-
187
- console.log({ repo, smth, smth2 })
188
-
189
- return matchFor(Something)
190
- .GetSomething(Effect.void)
191
- .GetSomethingElse(Effect.succeed("12"))
192
- })
193
- })
194
- console.log({ routes })
195
- })