@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.
- package/CHANGELOG.md +23 -0
- package/dist/api/routing.d.ts +4 -216
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +2 -2
- package/package.json +10 -22
- package/src/api/routing.ts +14 -58
- package/test/controller.test.ts +48 -4
- package/test/dist/controller.test.d.ts.map +1 -1
- package/dist/api/routing.legacy.d.ts +0 -104
- package/dist/api/routing.legacy.d.ts.map +0 -1
- package/dist/api/routing.legacy.js +0 -209
- package/dist/api/routing.legacy2.d.ts +0 -192
- package/dist/api/routing.legacy2.d.ts.map +0 -1
- package/dist/api/routing.legacy2.js +0 -226
- package/dist/api/routing.legacy3.d.ts +0 -263
- package/dist/api/routing.legacy3.d.ts.map +0 -1
- package/dist/api/routing.legacy3.js +0 -233
- package/src/api/routing.legacy.ts +0 -530
- package/src/api/routing.legacy2.ts +0 -802
- package/src/api/routing.legacy3.ts +0 -989
- package/test/controller.legacy2.test.ts +0 -197
- package/test/controller.legacy3.test.ts +0 -195
|
@@ -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
|
-
})
|