@effect-app/infra 2.78.8 → 2.80.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/api/layerUtils.d.ts +2 -4
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +4 -5
- package/dist/api/routing/middleware/ContextProvider.d.ts +1 -3
- package/dist/api/routing/middleware/ContextProvider.d.ts.map +1 -1
- package/dist/api/routing/middleware/ContextProvider.js +2 -1
- package/dist/api/routing/middleware/DynamicMiddleware.d.ts +44 -22
- package/dist/api/routing/middleware/DynamicMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/DynamicMiddleware.js +60 -20
- package/dist/api/routing/middleware/generic-middleware.d.ts +18 -11
- package/dist/api/routing/middleware/generic-middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/generic-middleware.js +34 -8
- package/dist/api/routing/middleware/middleware.d.ts +16 -10
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +41 -51
- package/dist/api/routing/middleware.d.ts +1 -0
- package/dist/api/routing/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware.js +2 -1
- package/dist/api/routing.d.ts +9 -9
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +3 -3
- package/package.json +1 -1
- package/src/api/layerUtils.ts +6 -9
- package/src/api/routing/middleware/ContextProvider.ts +9 -7
- package/src/api/routing/middleware/DynamicMiddleware.ts +164 -80
- package/src/api/routing/middleware/generic-middleware.ts +57 -21
- package/src/api/routing/middleware/middleware.ts +27 -35
- package/src/api/routing/middleware.ts +2 -0
- package/src/api/routing.ts +10 -10
- package/test/contextProvider.test.ts +152 -0
- package/test/controller.test.ts +27 -129
- package/test/dist/contextProvider.test.d.ts.map +1 -0
- package/test/dist/controller/test2.test.d.ts.map +1 -0
- package/test/dist/controller.legacy2.test.d.ts.map +1 -0
- package/test/dist/controller.legacy3.test.d.ts.map +1 -0
- package/test/dist/controller.test copy.js +129 -0
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/controller5.test.d.ts.map +1 -0
- package/test/dist/controller6.test.d.ts.map +1 -0
- package/test/dist/controller7.test.d.ts.map +1 -0
- package/test/dist/dynamicContext.test.d.ts.map +1 -0
- package/test/dist/filterApi.test.d.ts.map +1 -0
- package/vitest.config.ts.timestamp-1711656440838-19c636fe320df.mjs +0 -0
- package/vitest.config.ts.timestamp-1711724061890-6ecedb0a07fdd.mjs +0 -0
- package/vitest.config.ts.timestamp-1711743489537-da8d9e5f66c9f.mjs +0 -0
- package/vitest.config.ts.timestamp-1711744615239-dcf257a844e01.mjs +37 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
|
-
import {
|
|
4
|
+
import { Rpc, RpcMiddleware } from "@effect/rpc"
|
|
5
|
+
import { type SuccessValue, type TagClass } from "@effect/rpc/RpcMiddleware"
|
|
6
|
+
import { Console, Context, Effect, Layer, type NonEmptyReadonlyArray, type Request, type S, type Schema, type Scope, Unify } from "effect-app"
|
|
5
7
|
import type { GetEffectContext, RPCContextMap } from "effect-app/client/req"
|
|
6
|
-
import { type
|
|
8
|
+
import { type HttpHeaders } from "effect-app/http"
|
|
9
|
+
import { type TagUnify, type TagUnifyIgnore } from "effect/Context"
|
|
7
10
|
import type * as EffectRequest from "effect/Request"
|
|
8
11
|
import { type ContextTagWithDefault, type LayerUtils } from "../../layerUtils.js"
|
|
9
|
-
import { type ContextProviderId, type ContextProviderShape } from "./ContextProvider.js"
|
|
10
12
|
import { type ContextWithLayer, implementMiddleware } from "./dynamic-middleware.js"
|
|
11
13
|
import { type GenericMiddlewareMaker, genericMiddlewareMaker } from "./generic-middleware.js"
|
|
12
14
|
|
|
@@ -71,7 +73,7 @@ export type RPCHandlerFactory<
|
|
|
71
73
|
) => Effect.Effect<
|
|
72
74
|
Request.Request.Success<Req>,
|
|
73
75
|
Request.Request.Error<Req> | RequestContextMapErrors<RequestContextMap>,
|
|
74
|
-
|
|
|
76
|
+
| Scope.Scope // because of the context provider and the middleware (Middleware)
|
|
75
77
|
| Exclude<
|
|
76
78
|
// the middleware will remove from HandlerR the dynamic context
|
|
77
79
|
// & S.Schema<Req, any, never> is useless here but useful when creating the middleware
|
|
@@ -91,16 +93,8 @@ type RequestContextMapProvider<RequestContextMap extends Record<string, RPCConte
|
|
|
91
93
|
|
|
92
94
|
export interface MiddlewareMake<
|
|
93
95
|
RequestContextMap extends Record<string, RPCContextMap.Any>, // what services will the middleware provide dynamically to the next, or raise errors.
|
|
94
|
-
//
|
|
95
|
-
// ContextProvider is a service that builds additional context for each request.
|
|
96
|
-
ContextProviderA, // what the context provider provides
|
|
97
|
-
ContextProviderR extends HttpRouter.HttpRouter.Provided, // what the context provider requires
|
|
98
|
-
MakeContextProviderE, // what the context provider construction can fail with
|
|
99
|
-
MakeContextProviderR, // what the context provider construction requires
|
|
100
96
|
DynamicMiddlewareProviders extends RequestContextMapProvider<RequestContextMap>, // how to resolve the dynamic middleware
|
|
101
|
-
GenericMiddlewareProviders extends
|
|
102
|
-
ContextTagWithDefault.Base<GenericMiddlewareMaker>
|
|
103
|
-
>,
|
|
97
|
+
GenericMiddlewareProviders extends NonEmptyReadonlyArray<GenericMiddlewareMaker>,
|
|
104
98
|
MakeMiddlewareE, // what the middleware construction can fail with
|
|
105
99
|
MakeMiddlewareR, // what the middleware requires to be constructed
|
|
106
100
|
MiddlewareDependencies extends NonEmptyReadonlyArray<Layer.Layer.Any> // layers provided for the middleware to be constructed
|
|
@@ -109,25 +103,30 @@ export interface MiddlewareMake<
|
|
|
109
103
|
dynamicMiddlewares: DynamicMiddlewareProviders
|
|
110
104
|
/** generic middlewares are those which follow the (next) => (input, headers) => pattern */
|
|
111
105
|
genericMiddlewares: GenericMiddlewareProviders
|
|
112
|
-
/** static context providers */
|
|
113
|
-
contextProvider?: ContextTagWithDefault<
|
|
114
|
-
ContextProviderId,
|
|
115
|
-
ContextProviderShape<ContextProviderA, ContextProviderR>,
|
|
116
|
-
MakeContextProviderE,
|
|
117
|
-
MakeContextProviderR
|
|
118
|
-
>
|
|
119
106
|
|
|
120
107
|
/* dependencies for the main middleware running just before the next is called */
|
|
121
108
|
dependencies?: MiddlewareDependencies
|
|
122
109
|
// this actually builds "the middleware", i.e. returns the augmented next factory when yielded...
|
|
123
110
|
execute?: (
|
|
124
111
|
maker: (
|
|
125
|
-
// MiddlewareR is set to
|
|
112
|
+
// MiddlewareR is set to GenericMiddlewareProviders | Scope.Scope because that's what, at most
|
|
126
113
|
// a middleware can additionally require to get executed
|
|
127
|
-
cb: MakeRPCHandlerFactory<
|
|
128
|
-
|
|
114
|
+
cb: MakeRPCHandlerFactory<
|
|
115
|
+
RequestContextMap,
|
|
116
|
+
| GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
117
|
+
| Scope.Scope
|
|
118
|
+
>
|
|
119
|
+
) => MakeRPCHandlerFactory<
|
|
120
|
+
RequestContextMap,
|
|
121
|
+
| GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
122
|
+
| Scope.Scope
|
|
123
|
+
>
|
|
129
124
|
) => Effect<
|
|
130
|
-
MakeRPCHandlerFactory<
|
|
125
|
+
MakeRPCHandlerFactory<
|
|
126
|
+
RequestContextMap,
|
|
127
|
+
| GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
128
|
+
| Scope.Scope
|
|
129
|
+
>,
|
|
131
130
|
MakeMiddlewareE,
|
|
132
131
|
MakeMiddlewareR | Scope // ...that's why MakeMiddlewareR is here
|
|
133
132
|
>
|
|
@@ -137,7 +136,7 @@ export interface MiddlewareMakerId {
|
|
|
137
136
|
_tag: "MiddlewareMaker"
|
|
138
137
|
}
|
|
139
138
|
|
|
140
|
-
export type
|
|
139
|
+
export type RouterMiddleware<
|
|
141
140
|
RequestContextMap extends Record<string, RPCContextMap.Any>, // what services will the middlware provide dynamically to the next, or raise errors.
|
|
142
141
|
MakeMiddlewareE, // what the middleware construction can fail with
|
|
143
142
|
MakeMiddlewareR, // what the middlware requires to be constructed
|
|
@@ -145,11 +144,11 @@ export type Middleware<
|
|
|
145
144
|
> = ContextTagWithDefault<
|
|
146
145
|
MiddlewareMakerId,
|
|
147
146
|
{
|
|
147
|
+
_tag: "MiddlewareMaker"
|
|
148
148
|
effect: RPCHandlerFactory<RequestContextMap, ContextProviderA>
|
|
149
149
|
},
|
|
150
150
|
MakeMiddlewareE,
|
|
151
|
-
MakeMiddlewareR
|
|
152
|
-
"MiddlewareMaker"
|
|
151
|
+
MakeMiddlewareR
|
|
153
152
|
>
|
|
154
153
|
|
|
155
154
|
export type RequestContextMapErrors<RequestContextMap extends Record<string, RPCContextMap.Any>> = S.Schema.Type<
|
|
@@ -164,25 +163,13 @@ export const makeMiddleware =
|
|
|
164
163
|
>() =>
|
|
165
164
|
<
|
|
166
165
|
RequestContextProviders extends RequestContextMapProvider<RequestContextMap>, // how to resolve the dynamic middleware
|
|
167
|
-
GenericMiddlewareProviders extends
|
|
168
|
-
ContextTagWithDefault.Base<GenericMiddlewareMaker>
|
|
169
|
-
>,
|
|
166
|
+
GenericMiddlewareProviders extends NonEmptyReadonlyArray<GenericMiddlewareMaker>,
|
|
170
167
|
MiddlewareDependencies extends NonEmptyReadonlyArray<Layer.Layer.Any>, // layers provided for the middlware to be constructed
|
|
171
|
-
//
|
|
172
|
-
// ContextProvider is a service that builds additional context for each request.
|
|
173
|
-
ContextProviderA = never, // what the context provider provides
|
|
174
|
-
ContextProviderR extends HttpRouter.HttpRouter.Provided = never, // what the context provider requires
|
|
175
|
-
MakeContextProviderE = never, // what the context provider construction can fail with
|
|
176
|
-
MakeContextProviderR = never, // what the context provider construction requires
|
|
177
168
|
MakeMiddlewareE = never, // what the middleware construction can fail with
|
|
178
169
|
MakeMiddlewareR = never // what the middlware requires to be constructed
|
|
179
170
|
>(
|
|
180
171
|
make: MiddlewareMake<
|
|
181
172
|
RequestContextMap,
|
|
182
|
-
ContextProviderA,
|
|
183
|
-
ContextProviderR,
|
|
184
|
-
MakeContextProviderE,
|
|
185
|
-
MakeContextProviderR,
|
|
186
173
|
RequestContextProviders,
|
|
187
174
|
GenericMiddlewareProviders,
|
|
188
175
|
MakeMiddlewareE,
|
|
@@ -193,7 +180,10 @@ export const makeMiddleware =
|
|
|
193
180
|
const MiddlewareMaker = Context.GenericTag<
|
|
194
181
|
MiddlewareMakerId,
|
|
195
182
|
{
|
|
196
|
-
effect: RPCHandlerFactory<
|
|
183
|
+
effect: RPCHandlerFactory<
|
|
184
|
+
RequestContextMap,
|
|
185
|
+
GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
186
|
+
>
|
|
197
187
|
_tag: "MiddlewareMaker"
|
|
198
188
|
}
|
|
199
189
|
>(
|
|
@@ -211,58 +201,67 @@ export const makeMiddleware =
|
|
|
211
201
|
generic: middlewares.effect,
|
|
212
202
|
middleware: make.execute
|
|
213
203
|
? make.execute((
|
|
214
|
-
cb: MakeRPCHandlerFactory<
|
|
204
|
+
cb: MakeRPCHandlerFactory<
|
|
205
|
+
RequestContextMap,
|
|
206
|
+
Scope.Scope | GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
207
|
+
>
|
|
215
208
|
) => cb)
|
|
216
209
|
: Effect.succeed<
|
|
217
|
-
MakeRPCHandlerFactory<
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
210
|
+
MakeRPCHandlerFactory<
|
|
211
|
+
RequestContextMap,
|
|
212
|
+
Scope.Scope | GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
213
|
+
>
|
|
214
|
+
>((_schema, next) => (payload, headers) => next(payload, headers))
|
|
222
215
|
})
|
|
223
216
|
.pipe(
|
|
224
|
-
Effect.map(({
|
|
217
|
+
Effect.map(({ dynamicMiddlewares, generic, middleware }) => ({
|
|
225
218
|
_tag: "MiddlewareMaker" as const,
|
|
226
|
-
effect: makeRpcEffect<
|
|
219
|
+
effect: makeRpcEffect<
|
|
220
|
+
RequestContextMap,
|
|
221
|
+
GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
222
|
+
>()(
|
|
227
223
|
(schema, next, moduleName) => {
|
|
228
224
|
const h = middleware(schema, next as any, moduleName)
|
|
229
225
|
return (payload, headers) =>
|
|
230
|
-
Effect
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
226
|
+
Effect
|
|
227
|
+
.gen(function*() {
|
|
228
|
+
const gen = generic({
|
|
229
|
+
payload,
|
|
230
|
+
headers,
|
|
231
|
+
clientId: 0, // TODO: get the clientId from the request context
|
|
232
|
+
rpc: {
|
|
233
|
+
...Rpc.fromTaggedRequest(schema as any),
|
|
234
|
+
// middlewares ? // todo: get from actual middleware flow?
|
|
235
|
+
annotations: Context.empty(), // TODO //Annotations(schema as any),
|
|
236
|
+
// successSchema: schema.success ?? Schema.Void,
|
|
237
|
+
// errorSchema: schema.failure ?? Schema.Never,
|
|
238
|
+
payloadSchema: schema,
|
|
239
|
+
_tag: `${moduleName}.${payload._tag}`,
|
|
240
|
+
key: `${moduleName}.${payload._tag}` /* ? */
|
|
241
|
+
// clientId: 0 as number /* ? */
|
|
242
|
+
}, // todo: make moduleName part of the tag on S.Req creation.
|
|
243
|
+
next:
|
|
244
|
+
// the contextProvider is an Effect that builds the context for the request
|
|
245
|
+
// the dynamicMiddlewares is an Effect that builds the dynamiuc context for the request
|
|
246
|
+
dynamicMiddlewares(schema.config ?? {}, headers).pipe(
|
|
247
|
+
Effect.flatMap((dynamicContext) => h(payload, headers).pipe(Effect.provide(dynamicContext)))
|
|
248
|
+
) as any
|
|
249
|
+
})
|
|
250
|
+
console.log({ gen })
|
|
240
251
|
|
|
241
|
-
|
|
242
|
-
return yield* contextProvider.pipe(
|
|
243
|
-
Effect.flatMap((contextProviderContext) =>
|
|
244
|
-
// the dynamicMiddlewares is an Effect that builds the dynamiuc context for the request
|
|
245
|
-
dynamicMiddlewares(schema.config ?? {}, headers).pipe(
|
|
246
|
-
Effect.flatMap((dynamicContext) =>
|
|
247
|
-
h(payload, headers).pipe(Effect.provide(dynamicContext))
|
|
248
|
-
),
|
|
249
|
-
Effect.provide(Option.getOrElse(contextProviderContext, () => Context.empty()))
|
|
250
|
-
)
|
|
251
|
-
)
|
|
252
|
-
)
|
|
253
|
-
}) as any
|
|
252
|
+
return yield* gen
|
|
254
253
|
})
|
|
255
|
-
|
|
254
|
+
.pipe(Effect.onExit(Console.log)) as any // why?
|
|
256
255
|
}
|
|
257
256
|
)
|
|
258
|
-
}))
|
|
257
|
+
})),
|
|
258
|
+
Effect.onExit(Console.log)
|
|
259
259
|
)
|
|
260
260
|
)
|
|
261
261
|
|
|
262
262
|
const dependencies = [
|
|
263
263
|
...(make.dependencies ? make.dependencies : []),
|
|
264
264
|
...(dynamicMiddlewares.dependencies as any),
|
|
265
|
-
...(make.contextProvider?.Default ? [make.contextProvider.Default] : []),
|
|
266
265
|
...middlewares.dependencies
|
|
267
266
|
]
|
|
268
267
|
const middlewareLayer = l
|
|
@@ -271,14 +270,12 @@ export const makeMiddleware =
|
|
|
271
270
|
) as Layer.Layer<
|
|
272
271
|
MiddlewareMakerId,
|
|
273
272
|
| MakeMiddlewareE // what the middleware construction can fail with
|
|
274
|
-
| LayerUtils.
|
|
275
|
-
| LayerUtils.
|
|
276
|
-
| MakeContextProviderE, // what could go wrong when building the context provider
|
|
273
|
+
| LayerUtils.GetLayersError<typeof dynamicMiddlewares.dependencies>
|
|
274
|
+
| LayerUtils.GetLayersError<typeof middlewares.dependencies>, // what could go wrong when building the dynamic middleware provider
|
|
277
275
|
| LayerUtils.GetLayersContext<MiddlewareDependencies> // what's needed to build layers
|
|
278
276
|
| LayerUtils.GetLayersContext<typeof middlewares.dependencies>
|
|
279
277
|
| LayerUtils.GetLayersContext<typeof dynamicMiddlewares.dependencies> // what's needed to build dynamic middleware layers
|
|
280
278
|
| Exclude<MakeMiddlewareR, LayerUtils.GetLayersSuccess<MiddlewareDependencies>> // what layers provides
|
|
281
|
-
| MakeContextProviderR // what's needed to build the contextProvider
|
|
282
279
|
>
|
|
283
280
|
|
|
284
281
|
return Object.assign(MiddlewareMaker, { Default: middlewareLayer })
|
|
@@ -313,7 +310,7 @@ function makeRpcEffect<
|
|
|
313
310
|
) => Effect.Effect<
|
|
314
311
|
Request.Request.Success<Req>,
|
|
315
312
|
Request.Request.Error<Req> | RequestContextMapErrors<RequestContextMap>,
|
|
316
|
-
|
|
|
313
|
+
| Scope.Scope // the context provider may require Scope to run
|
|
317
314
|
| Exclude<
|
|
318
315
|
// it can also be removed from HandlerR
|
|
319
316
|
Exclude<HandlerR, GetEffectContext<RequestContextMap, (T & S.Schema<Req, any, never>)["config"]>>,
|
|
@@ -322,3 +319,90 @@ function makeRpcEffect<
|
|
|
322
319
|
>
|
|
323
320
|
) => cb
|
|
324
321
|
}
|
|
322
|
+
|
|
323
|
+
// updated to support Scope.Scope
|
|
324
|
+
export interface RpcMiddleware<Provides, E> {
|
|
325
|
+
(options: {
|
|
326
|
+
readonly clientId: number
|
|
327
|
+
readonly rpc: Rpc.AnyWithProps
|
|
328
|
+
readonly payload: unknown
|
|
329
|
+
readonly headers: HttpHeaders.Headers
|
|
330
|
+
}): Effect.Effect<Provides, E, Scope.Scope>
|
|
331
|
+
}
|
|
332
|
+
export interface RpcMiddlewareWrap<Provides, E> {
|
|
333
|
+
(options: {
|
|
334
|
+
readonly clientId: number
|
|
335
|
+
readonly rpc: Rpc.AnyWithProps
|
|
336
|
+
readonly payload: unknown
|
|
337
|
+
readonly headers: HttpHeaders.Headers
|
|
338
|
+
readonly next: Effect.Effect<SuccessValue, E, Provides | Scope.Scope>
|
|
339
|
+
}): Effect.Effect<SuccessValue, E, Scope.Scope>
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
type RpcOptionsOriginal = {
|
|
343
|
+
readonly wrap?: boolean
|
|
344
|
+
readonly optional?: boolean
|
|
345
|
+
readonly failure?: Schema.Schema.All
|
|
346
|
+
readonly provides?: Context.Tag<any, any>
|
|
347
|
+
readonly requiredForClient?: boolean
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export const Tag = <Self>() =>
|
|
351
|
+
<
|
|
352
|
+
const Name extends string,
|
|
353
|
+
const Options extends RpcOptionsOriginal
|
|
354
|
+
>(
|
|
355
|
+
id: Name,
|
|
356
|
+
options?: Options | undefined
|
|
357
|
+
) =>
|
|
358
|
+
<E, R, L extends NonEmptyReadonlyArray<Layer.Layer.Any>>(opts: {
|
|
359
|
+
effect: Effect.Effect<
|
|
360
|
+
TagClass.Wrap<Options> extends true ? RpcMiddlewareWrap<
|
|
361
|
+
TagClass.Provides<Options>,
|
|
362
|
+
TagClass.Failure<Options>
|
|
363
|
+
>
|
|
364
|
+
: RpcMiddleware<
|
|
365
|
+
TagClass.Service<Options>,
|
|
366
|
+
TagClass.FailureService<Options>
|
|
367
|
+
>,
|
|
368
|
+
E,
|
|
369
|
+
R
|
|
370
|
+
>
|
|
371
|
+
dependencies?: L
|
|
372
|
+
}): RpcMiddleware.TagClass<Self, Name, Options> & {
|
|
373
|
+
Default: Layer.Layer<Self, E | LayerUtils.GetLayersError<L>, Exclude<R, LayerUtils.GetLayersSuccess<L>>>
|
|
374
|
+
} =>
|
|
375
|
+
class extends RpcMiddleware.Tag<Self>()(id, options) {
|
|
376
|
+
static readonly Default = Layer.scoped(this, opts.effect as any).pipe(
|
|
377
|
+
Layer.provide([Layer.empty, ...opts.dependencies ?? []])
|
|
378
|
+
)
|
|
379
|
+
static override [Unify.typeSymbol]?: unknown
|
|
380
|
+
static override [Unify.unifySymbol]?: TagUnify<typeof this>
|
|
381
|
+
static override [Unify.ignoreSymbol]?: TagUnifyIgnore
|
|
382
|
+
} as any
|
|
383
|
+
|
|
384
|
+
// export const Tag = <Self>() =>
|
|
385
|
+
// <
|
|
386
|
+
// const Name extends string,
|
|
387
|
+
// const Options extends Omit<RpcOptionsOriginal, "wrap">
|
|
388
|
+
// >(
|
|
389
|
+
// id: Name,
|
|
390
|
+
// options?: Options | undefined
|
|
391
|
+
// ) =>
|
|
392
|
+
// OurTag<Self>()(id, { ...options, wrap: true } as Options & { wrap: true }) as <
|
|
393
|
+
// E,
|
|
394
|
+
// R,
|
|
395
|
+
// L extends NonEmptyReadonlyArray<Layer.Layer.Any>
|
|
396
|
+
// >(opts: {
|
|
397
|
+
// effect: Effect.Effect<
|
|
398
|
+
// RpcMiddlewareWrap<
|
|
399
|
+
// RpcMiddleware.TagClass.Provides<Options>,
|
|
400
|
+
// RpcMiddleware.TagClass.Failure<Options>
|
|
401
|
+
// >,
|
|
402
|
+
// E,
|
|
403
|
+
// R
|
|
404
|
+
// >
|
|
405
|
+
// dependencies?: L
|
|
406
|
+
// }) => RpcMiddleware.TagClass<Self, Name, Options> & {
|
|
407
|
+
// Default: Layer.Layer<Self, E | LayerUtils.GetLayersError<L>, Exclude<R, LayerUtils.GetLayersSuccess<L>>>
|
|
408
|
+
// }
|
|
@@ -1,44 +1,80 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { type
|
|
3
|
-
import { type
|
|
4
|
-
import { type
|
|
2
|
+
import { type Rpc, type RpcMiddleware } from "@effect/rpc"
|
|
3
|
+
import { type SuccessValue, type TagClassAny } from "@effect/rpc/RpcMiddleware"
|
|
4
|
+
import { type Array, Context, Effect, type Layer, type Scope } from "effect-app"
|
|
5
|
+
import { type HttpHeaders } from "effect-app/http"
|
|
6
|
+
import { InfraLogger } from "../../../logger.js"
|
|
5
7
|
|
|
6
|
-
export interface GenericMiddlewareOptions<
|
|
8
|
+
export interface GenericMiddlewareOptions<E> {
|
|
7
9
|
// Effect rpc middleware does not support changing payload or headers, but we do..
|
|
8
|
-
readonly next: Effect.Effect<
|
|
10
|
+
readonly next: Effect.Effect<SuccessValue, E, Scope.Scope>
|
|
9
11
|
readonly payload: unknown
|
|
10
12
|
readonly headers: HttpHeaders.Headers
|
|
11
|
-
|
|
12
|
-
readonly rpc:
|
|
13
|
+
readonly clientId: number
|
|
14
|
+
readonly rpc: Rpc.AnyWithProps
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
export type GenericMiddlewareMaker =
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
export type GenericMiddlewareMaker = TagClassAny & { Default: Layer.Layer.Any } // todo; and Layer..
|
|
18
|
+
|
|
19
|
+
export namespace GenericMiddlewareMaker {
|
|
20
|
+
export type Provided<T> = T extends TagClassAny
|
|
21
|
+
? T extends { provides: Context.Tag<any, any> } ? Context.Tag.Identifier<T["provides"]> : never
|
|
22
|
+
: never
|
|
23
|
+
}
|
|
18
24
|
|
|
19
25
|
export const genericMiddleware = (i: GenericMiddlewareMaker) => i
|
|
20
26
|
|
|
21
27
|
export const genericMiddlewareMaker = <
|
|
22
|
-
T extends Array<
|
|
23
|
-
ContextTagWithDefault.Base<GenericMiddlewareMaker>
|
|
24
|
-
>
|
|
28
|
+
T extends Array<GenericMiddlewareMaker>
|
|
25
29
|
>(...middlewares: T): {
|
|
26
30
|
dependencies: { [K in keyof T]: T[K]["Default"] }
|
|
27
|
-
effect: Effect.Effect<
|
|
31
|
+
effect: Effect.Effect<RpcMiddleware.RpcMiddlewareWrap<any, any>>
|
|
28
32
|
} => {
|
|
33
|
+
// we want to run them in reverse order
|
|
34
|
+
middlewares = middlewares.toReversed() as any
|
|
29
35
|
return {
|
|
30
36
|
dependencies: middlewares.map((_) => _.Default),
|
|
31
37
|
effect: Effect.gen(function*() {
|
|
32
|
-
const
|
|
38
|
+
const context = yield* Effect.context()
|
|
39
|
+
// const middlewares: readonly (RpcMiddlewareWrap<any, any> | RpcMiddleware.RpcMiddleware<any, any>)[] =
|
|
40
|
+
// (yield* Effect.all(
|
|
41
|
+
// middlewares
|
|
42
|
+
// )) as any
|
|
33
43
|
|
|
34
|
-
return <
|
|
35
|
-
options: GenericMiddlewareOptions<
|
|
44
|
+
return <E>(
|
|
45
|
+
options: GenericMiddlewareOptions<E>
|
|
36
46
|
) => {
|
|
37
|
-
let
|
|
38
|
-
|
|
39
|
-
|
|
47
|
+
let handler = options.next
|
|
48
|
+
// copied from RpcMiddleare
|
|
49
|
+
for (const tag of middlewares) {
|
|
50
|
+
if (tag.wrap) {
|
|
51
|
+
const middleware = Context.unsafeGet(context, tag)
|
|
52
|
+
handler = InfraLogger.logDebug("Applying middleware " + tag.key).pipe(
|
|
53
|
+
Effect.zipRight(middleware({ ...options, next: handler as any }))
|
|
54
|
+
) as any
|
|
55
|
+
} else if (tag.optional) {
|
|
56
|
+
const middleware = Context.unsafeGet(context, tag) as RpcMiddleware.RpcMiddleware<any, any>
|
|
57
|
+
const previous = handler
|
|
58
|
+
handler = InfraLogger.logDebug("Applying middleware " + tag.key).pipe(
|
|
59
|
+
Effect.zipRight(Effect.matchEffect(middleware(options), {
|
|
60
|
+
onFailure: () => previous,
|
|
61
|
+
onSuccess: tag.provides !== undefined
|
|
62
|
+
? (value) => Effect.provideService(previous, tag.provides as any, value)
|
|
63
|
+
: (_) => previous
|
|
64
|
+
}))
|
|
65
|
+
)
|
|
66
|
+
} else {
|
|
67
|
+
const middleware = Context.unsafeGet(context, tag) as RpcMiddleware.RpcMiddleware<any, any>
|
|
68
|
+
handler = InfraLogger.logDebug("Applying middleware " + tag.key).pipe(
|
|
69
|
+
Effect.zipRight(
|
|
70
|
+
tag.provides !== undefined
|
|
71
|
+
? Effect.provideServiceEffect(handler, tag.provides as any, middleware(options))
|
|
72
|
+
: Effect.zipRight(middleware(options), handler)
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
}
|
|
40
76
|
}
|
|
41
|
-
return
|
|
77
|
+
return handler
|
|
42
78
|
}
|
|
43
79
|
})
|
|
44
80
|
} as any
|
|
@@ -3,46 +3,38 @@ import { Cause, Context, Effect, ParseResult } from "effect-app"
|
|
|
3
3
|
import { pretty } from "effect-app/utils"
|
|
4
4
|
import { logError, reportError } from "../../../errorReporter.js"
|
|
5
5
|
import { InfraLogger } from "../../../logger.js"
|
|
6
|
-
import {
|
|
6
|
+
import { RequestCacheLayers, Tag } from "../../routing.js"
|
|
7
7
|
|
|
8
8
|
const logRequestError = logError("Request")
|
|
9
9
|
const reportRequestError = reportError("Request")
|
|
10
10
|
|
|
11
11
|
export class DevMode extends Context.Reference<DevMode>()("DevMode", { defaultValue: () => false }) {}
|
|
12
|
-
// Effect Rpc Middleware: Wrap
|
|
13
|
-
export class RequestCacheMiddleware extends Effect.Service<RequestCacheMiddleware>()("RequestCacheMiddleware", {
|
|
14
|
-
effect: Effect.gen(function*() {
|
|
15
|
-
return genericMiddleware(Effect.fnUntraced(function*(options) {
|
|
16
|
-
return yield* options.next.pipe(Effect.provide(RequestCacheLayers))
|
|
17
|
-
}))
|
|
18
|
-
})
|
|
19
|
-
}) {}
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
effect: Effect.gen(function*() {
|
|
26
|
-
return genericMiddleware(Effect.fnUntraced(function*(options) {
|
|
27
|
-
return yield* options.next.pipe(
|
|
28
|
-
// TODO: make this depend on query/command, and consider if middleware also should be affected. right now it's not.
|
|
29
|
-
Effect.uninterruptible
|
|
30
|
-
)
|
|
31
|
-
}))
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
) {}
|
|
13
|
+
export class RequestCacheMiddleware extends Tag<RequestCacheMiddleware>()("RequestCacheMiddleware", { wrap: true })({
|
|
14
|
+
effect: Effect.succeed((options) => options.next.pipe(Effect.provide(RequestCacheLayers)))
|
|
15
|
+
}) {
|
|
16
|
+
}
|
|
35
17
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
18
|
+
export class ConfigureInterruptibility
|
|
19
|
+
extends Tag<ConfigureInterruptibility>()("ConfigureInterruptibility", { wrap: true })({
|
|
20
|
+
effect: Effect.succeed((options) =>
|
|
21
|
+
options.next.pipe(
|
|
22
|
+
// TODO: make this depend on query/command, and consider if middleware also should be affected. right now it's not.
|
|
23
|
+
Effect.uninterruptible
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
})
|
|
27
|
+
{
|
|
28
|
+
}
|
|
41
29
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
30
|
+
export class MiddlewareLogger extends Tag<MiddlewareLogger>()("MiddlewareLogger", { wrap: true })({
|
|
31
|
+
effect: Effect.gen(function*() {
|
|
32
|
+
const devMode = yield* DevMode
|
|
33
|
+
return ({ headers, next, payload, rpc }) =>
|
|
34
|
+
Effect
|
|
35
|
+
.annotateCurrentSpan({
|
|
36
|
+
"request.name": rpc._tag,
|
|
37
|
+
"requestInput": typeof payload === "object" && payload !== null
|
|
46
38
|
? Object.entries(payload).reduce((prev, [key, value]: [string, unknown]) => {
|
|
47
39
|
prev[key] = key === "password"
|
|
48
40
|
? "<redacted>"
|
|
@@ -60,7 +52,7 @@ export class MiddlewareLogger extends Effect.Service<MiddlewareLogger>()("Middle
|
|
|
60
52
|
return prev
|
|
61
53
|
}, {} as Record<string, string | number | boolean>)
|
|
62
54
|
: payload
|
|
63
|
-
)
|
|
55
|
+
})
|
|
64
56
|
.pipe(
|
|
65
57
|
// can't use andThen due to some being a function and effect
|
|
66
58
|
Effect.zipRight(next),
|
|
@@ -92,9 +84,9 @@ export class MiddlewareLogger extends Effect.Service<MiddlewareLogger>()("Middle
|
|
|
92
84
|
),
|
|
93
85
|
devMode ? (_) => _ : Effect.catchAllDefect(() => Effect.die("Internal Server Error"))
|
|
94
86
|
)
|
|
95
|
-
}))
|
|
96
87
|
})
|
|
97
|
-
}) {
|
|
88
|
+
}) {
|
|
89
|
+
}
|
|
98
90
|
|
|
99
91
|
export const DefaultGenericMiddlewares = [
|
|
100
92
|
RequestCacheMiddleware,
|
package/src/api/routing.ts
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
5
|
import { determineMethod, isCommand } from "@effect-app/infra/api/routing/utils"
|
|
6
6
|
import { Rpc, RpcGroup, RpcServer } from "@effect/rpc"
|
|
7
|
-
import { type Array, Duration, Effect, Layer, type NonEmptyReadonlyArray, Predicate, Request, S, Schedule, Schema } from "effect-app"
|
|
7
|
+
import { type Array, Duration, Effect, Layer, type NonEmptyReadonlyArray, Predicate, Request, S, Schedule, Schema, type Scope } from "effect-app"
|
|
8
8
|
import type { GetEffectContext, GetEffectError, RPCContextMap } from "effect-app/client/req"
|
|
9
9
|
import { type HttpHeaders, HttpRouter } from "effect-app/http"
|
|
10
10
|
import { typedKeysOf, typedValuesOf } from "effect-app/utils"
|
|
11
11
|
import type { Contravariant } from "effect/Types"
|
|
12
12
|
import { type YieldWrap } from "effect/Utils"
|
|
13
13
|
import { type LayerUtils } from "./layerUtils.js"
|
|
14
|
-
import { DevMode, type
|
|
14
|
+
import { DevMode, type RouterMiddleware } from "./routing/middleware.js"
|
|
15
15
|
|
|
16
16
|
export * from "./routing/middleware.js"
|
|
17
17
|
|
|
@@ -123,7 +123,7 @@ type Match<
|
|
|
123
123
|
RT,
|
|
124
124
|
Exclude<
|
|
125
125
|
Exclude<R2, GetEffectContext<RequestContextMap, Resource[Key]["config"]>>,
|
|
126
|
-
|
|
126
|
+
Scope.Scope
|
|
127
127
|
>
|
|
128
128
|
>
|
|
129
129
|
|
|
@@ -134,7 +134,7 @@ type Match<
|
|
|
134
134
|
RT,
|
|
135
135
|
Exclude<
|
|
136
136
|
Exclude<R2, GetEffectContext<RequestContextMap, Resource[Key]["config"]>>,
|
|
137
|
-
|
|
137
|
+
Scope.Scope
|
|
138
138
|
>
|
|
139
139
|
>
|
|
140
140
|
}
|
|
@@ -168,7 +168,7 @@ export const makeRouter = <
|
|
|
168
168
|
MakeMiddlewareR,
|
|
169
169
|
ContextProviderA
|
|
170
170
|
>(
|
|
171
|
-
middleware:
|
|
171
|
+
middleware: RouterMiddleware<
|
|
172
172
|
RequestContextMap,
|
|
173
173
|
MakeMiddlewareE,
|
|
174
174
|
MakeMiddlewareR,
|
|
@@ -340,7 +340,7 @@ export const makeRouter = <
|
|
|
340
340
|
| GetEffectContext<RequestContextMap, Resource[K]["config"]>
|
|
341
341
|
| ContextProviderA
|
|
342
342
|
>,
|
|
343
|
-
|
|
343
|
+
Scope.Scope
|
|
344
344
|
>
|
|
345
345
|
>
|
|
346
346
|
} = (impl: Record<keyof RequestModules, any>) =>
|
|
@@ -405,8 +405,7 @@ export const makeRouter = <
|
|
|
405
405
|
handle(req, headers).pipe(
|
|
406
406
|
Effect.withSpan("Request." + meta.moduleName + "." + resource._tag, {
|
|
407
407
|
captureStackTrace: () => handler.stack
|
|
408
|
-
})
|
|
409
|
-
Effect.provideService(DevMode, devMode)
|
|
408
|
+
})
|
|
410
409
|
),
|
|
411
410
|
meta.moduleName
|
|
412
411
|
),
|
|
@@ -469,7 +468,8 @@ export const makeRouter = <
|
|
|
469
468
|
Layer.provide([
|
|
470
469
|
...dependencies ?? [],
|
|
471
470
|
middleware.Default
|
|
472
|
-
] as any) as any
|
|
471
|
+
] as any) as any,
|
|
472
|
+
Layer.provide(Layer.succeed(DevMode, devMode))
|
|
473
473
|
)
|
|
474
474
|
) as (Layer.Layer<
|
|
475
475
|
Router,
|
|
@@ -751,7 +751,7 @@ export const makeRouter = <
|
|
|
751
751
|
// RPCRouteR<
|
|
752
752
|
// { [K in keyof Filter<Resource>]: Rpc.Rpc<Resource[K], Effect.Context<ReturnType<THandlers[K]["handler"]>>> }[keyof Filter<Resource>]
|
|
753
753
|
// >,
|
|
754
|
-
//
|
|
754
|
+
// Scope.Scope
|
|
755
755
|
// >
|
|
756
756
|
routes: any
|
|
757
757
|
|