@effect-app/infra 2.80.0 → 2.81.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.
Files changed (30) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/api/internal/auth.d.ts +2 -2
  3. package/dist/api/internal/auth.d.ts.map +1 -1
  4. package/dist/api/internal/auth.js +5 -5
  5. package/dist/api/routing/middleware/DynamicMiddleware.d.ts +129 -6
  6. package/dist/api/routing/middleware/DynamicMiddleware.d.ts.map +1 -1
  7. package/dist/api/routing/middleware/DynamicMiddleware.js +78 -7
  8. package/dist/api/routing/middleware/dynamic-middleware.d.ts +8 -9
  9. package/dist/api/routing/middleware/dynamic-middleware.d.ts.map +1 -1
  10. package/dist/api/routing/middleware/dynamic-middleware.js +3 -3
  11. package/dist/api/routing/middleware/generic-middleware.d.ts +5 -0
  12. package/dist/api/routing/middleware/generic-middleware.d.ts.map +1 -1
  13. package/dist/api/routing/middleware/generic-middleware.js +1 -1
  14. package/dist/api/routing/middleware/middleware-api.d.ts +17 -0
  15. package/dist/api/routing/middleware/middleware-api.d.ts.map +1 -0
  16. package/dist/api/routing/middleware/middleware-api.js +19 -0
  17. package/dist/api/routing/middleware/middleware.d.ts +3 -3
  18. package/dist/api/routing/middleware.d.ts +1 -0
  19. package/dist/api/routing/middleware.d.ts.map +1 -1
  20. package/dist/api/routing/middleware.js +2 -1
  21. package/package.json +5 -1
  22. package/src/api/internal/auth.ts +4 -5
  23. package/src/api/routing/middleware/DynamicMiddleware.ts +296 -15
  24. package/src/api/routing/middleware/dynamic-middleware.ts +12 -23
  25. package/src/api/routing/middleware/generic-middleware.ts +5 -0
  26. package/src/api/routing/middleware/middleware-api.ts +44 -0
  27. package/src/api/routing/middleware.ts +1 -0
  28. package/test/controller.test.ts +35 -25
  29. package/test/dist/controller.test.d.ts.map +1 -1
  30. package/test/dist/middleware-api.test.d.ts.map +1 -0
@@ -2,21 +2,21 @@ import { Context } from "effect-app";
2
2
  declare const DevMode_base: Context.ReferenceClass<DevMode, "DevMode", boolean>;
3
3
  export declare class DevMode extends DevMode_base {
4
4
  }
5
- declare const RequestCacheMiddleware_base: import("@effect/rpc/RpcMiddleware").TagClass<RequestCacheMiddleware, "RequestCacheMiddleware", {
5
+ declare const RequestCacheMiddleware_base: import("./DynamicMiddleware.js").TagClass<RequestCacheMiddleware, "RequestCacheMiddleware", {
6
6
  readonly wrap: true;
7
7
  }> & {
8
8
  Default: import("effect/Layer").Layer<RequestCacheMiddleware, never, never>;
9
9
  };
10
10
  export declare class RequestCacheMiddleware extends RequestCacheMiddleware_base {
11
11
  }
12
- declare const ConfigureInterruptibility_base: import("@effect/rpc/RpcMiddleware").TagClass<ConfigureInterruptibility, "ConfigureInterruptibility", {
12
+ declare const ConfigureInterruptibility_base: import("./DynamicMiddleware.js").TagClass<ConfigureInterruptibility, "ConfigureInterruptibility", {
13
13
  readonly wrap: true;
14
14
  }> & {
15
15
  Default: import("effect/Layer").Layer<ConfigureInterruptibility, never, never>;
16
16
  };
17
17
  export declare class ConfigureInterruptibility extends ConfigureInterruptibility_base {
18
18
  }
19
- declare const MiddlewareLogger_base: import("@effect/rpc/RpcMiddleware").TagClass<MiddlewareLogger, "MiddlewareLogger", {
19
+ declare const MiddlewareLogger_base: import("./DynamicMiddleware.js").TagClass<MiddlewareLogger, "MiddlewareLogger", {
20
20
  readonly wrap: true;
21
21
  }> & {
22
22
  Default: import("effect/Layer").Layer<MiddlewareLogger, never, never>;
@@ -2,6 +2,7 @@ export * from "./middleware/ContextProvider.js";
2
2
  export * from "./middleware/dynamic-middleware.js";
3
3
  export * from "./middleware/DynamicMiddleware.js";
4
4
  export * from "./middleware/generic-middleware.js";
5
+ export * from "./middleware/middleware-api.js";
5
6
  export * from "./middleware/middleware.js";
6
7
  export * as Middleware from "./middleware.js";
7
8
  //# sourceMappingURL=middleware.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../src/api/routing/middleware.ts"],"names":[],"mappings":"AACA,cAAc,iCAAiC,CAAA;AAC/C,cAAc,oCAAoC,CAAA;AAClD,cAAc,mCAAmC,CAAA;AACjD,cAAc,oCAAoC,CAAA;AAClD,cAAc,4BAA4B,CAAA;AAG1C,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../src/api/routing/middleware.ts"],"names":[],"mappings":"AACA,cAAc,iCAAiC,CAAA;AAC/C,cAAc,oCAAoC,CAAA;AAClD,cAAc,mCAAmC,CAAA;AACjD,cAAc,oCAAoC,CAAA;AAClD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,4BAA4B,CAAA;AAG1C,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA"}
@@ -3,7 +3,8 @@ export * from "./middleware/ContextProvider.js";
3
3
  export * from "./middleware/dynamic-middleware.js";
4
4
  export * from "./middleware/DynamicMiddleware.js";
5
5
  export * from "./middleware/generic-middleware.js";
6
+ export * from "./middleware/middleware-api.js";
6
7
  export * from "./middleware/middleware.js";
7
8
  // codegen:end
8
9
  export * as Middleware from "./middleware.js";
9
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9hcGkvcm91dGluZy9taWRkbGV3YXJlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLDRFQUE0RTtBQUM1RSxjQUFjLGlDQUFpQyxDQUFBO0FBQy9DLGNBQWMsb0NBQW9DLENBQUE7QUFDbEQsY0FBYyxtQ0FBbUMsQ0FBQTtBQUNqRCxjQUFjLG9DQUFvQyxDQUFBO0FBQ2xELGNBQWMsNEJBQTRCLENBQUE7QUFDMUMsY0FBYztBQUVkLE9BQU8sS0FBSyxVQUFVLE1BQU0saUJBQWlCLENBQUEifQ==
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9hcGkvcm91dGluZy9taWRkbGV3YXJlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLDRFQUE0RTtBQUM1RSxjQUFjLGlDQUFpQyxDQUFBO0FBQy9DLGNBQWMsb0NBQW9DLENBQUE7QUFDbEQsY0FBYyxtQ0FBbUMsQ0FBQTtBQUNqRCxjQUFjLG9DQUFvQyxDQUFBO0FBQ2xELGNBQWMsZ0NBQWdDLENBQUE7QUFDOUMsY0FBYyw0QkFBNEIsQ0FBQTtBQUMxQyxjQUFjO0FBRWQsT0FBTyxLQUFLLFVBQVUsTUFBTSxpQkFBaUIsQ0FBQSJ9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-app/infra",
3
- "version": "2.80.0",
3
+ "version": "2.81.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -322,6 +322,10 @@
322
322
  "types": "./dist/api/routing/middleware/middleware.d.ts",
323
323
  "default": "./dist/api/routing/middleware/middleware.js"
324
324
  },
325
+ "./api/routing/middleware/middleware-api": {
326
+ "types": "./dist/api/routing/middleware/middleware-api.d.ts",
327
+ "default": "./dist/api/routing/middleware/middleware-api.js"
328
+ },
325
329
  "./api/routing/schema/jwt": {
326
330
  "types": "./dist/api/routing/schema/jwt.d.ts",
327
331
  "default": "./dist/api/routing/schema/jwt.js"
@@ -11,9 +11,7 @@ import { auth, InsufficientScopeError, InvalidRequestError, InvalidTokenError, U
11
11
  type Config = Parameters<typeof auth>[0]
12
12
  export const checkJWTI = (config: Config) => {
13
13
  const mw = auth(config)
14
- return Effect.gen(function*() {
15
- const req = yield* HttpServerRequest.HttpServerRequest
16
-
14
+ return Effect.fnUntraced(function*(headers: HttpHeaders.Headers) {
17
15
  return yield* Effect.async<
18
16
  void,
19
17
  InsufficientScopeError | InvalidRequestError | InvalidTokenError | UnauthorizedError
@@ -31,7 +29,7 @@ export const checkJWTI = (config: Config) => {
31
29
  }
32
30
  return cb(Effect.die(err))
33
31
  }
34
- const r = { headers: req.headers, query: {}, body: {}, is: () => false } // is("urlencoded")
32
+ const r = { headers, query: {}, body: {}, is: () => false } // is("urlencoded")
35
33
  try {
36
34
  mw(r as any, {} as any, next)
37
35
  } catch (e) {
@@ -46,7 +44,8 @@ export const checkJwt = (config: Config) => {
46
44
  const check = checkJWTI(config)
47
45
  return HttpMiddleware.make((app) =>
48
46
  Effect.gen(function*() {
49
- const response = yield* check.pipe(Effect.catchAll((e) =>
47
+ const req = yield* HttpServerRequest.HttpServerRequest
48
+ const response = yield* check(req.headers).pipe(Effect.catchAll((e) =>
50
49
  Effect.succeed(
51
50
  HttpServerResponse.unsafeJson({ message: e.message }, {
52
51
  status: e.status,
@@ -2,8 +2,8 @@
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-return */
3
3
  /* eslint-disable @typescript-eslint/no-explicit-any */
4
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
+ import { type SuccessValue, type TypeId } from "@effect/rpc/RpcMiddleware"
6
+ import { Console, Context, Effect, Layer, type NonEmptyReadonlyArray, type Option, type Request, type S, type Schema, type Scope, Unify } from "effect-app"
7
7
  import type { GetEffectContext, RPCContextMap } from "effect-app/client/req"
8
8
  import { type HttpHeaders } from "effect-app/http"
9
9
  import { type TagUnify, type TagUnifyIgnore } from "effect/Context"
@@ -83,7 +83,7 @@ export type RPCHandlerFactory<
83
83
  >
84
84
  >
85
85
 
86
- type RequestContextMapProvider<RequestContextMap extends Record<string, RPCContextMap.Any>> = {
86
+ export type RequestContextMapProvider<RequestContextMap extends Record<string, RPCContextMap.Any>> = {
87
87
  [K in keyof RequestContextMap]: ContextWithLayer.Base<
88
88
  { [K in keyof RequestContextMap]?: RequestContextMap[K]["contextActivation"] },
89
89
  RequestContextMap[K]["service"],
@@ -155,6 +155,13 @@ export type RequestContextMapErrors<RequestContextMap extends Record<string, RPC
155
155
  RequestContextMap[keyof RequestContextMap]["error"]
156
156
  >
157
157
 
158
+ /*:
159
+ & Context.Tag<MiddlewareMakerId, {
160
+ effect: RPCHandlerFactory<RequestContextMap, GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>>
161
+ _tag: "MiddlewareMaker"
162
+ }>
163
+ & { Default: "abc" } */
164
+
158
165
  // factory for middlewares
159
166
  export const makeMiddleware =
160
167
  // by setting RequestContextMap beforehand, execute contextual typing does not fuck up itself to anys
@@ -193,6 +200,131 @@ export const makeMiddleware =
193
200
  const dynamicMiddlewares = implementMiddleware<RequestContextMap>()(make.dynamicMiddlewares)
194
201
  const middlewares = genericMiddlewareMaker(...make.genericMiddlewares)
195
202
 
203
+ const l = Layer.scoped(
204
+ MiddlewareMaker,
205
+ Effect
206
+ .all({
207
+ dynamicMiddlewares: dynamicMiddlewares.effect,
208
+ generic: middlewares.effect,
209
+ middleware: make.execute
210
+ ? make.execute((
211
+ cb: MakeRPCHandlerFactory<
212
+ RequestContextMap,
213
+ Scope.Scope | GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
214
+ >
215
+ ) => cb)
216
+ : Effect.succeed<
217
+ MakeRPCHandlerFactory<
218
+ RequestContextMap,
219
+ Scope.Scope | GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
220
+ >
221
+ >((_schema, next) => (payload, headers) => next(payload, headers))
222
+ })
223
+ .pipe(
224
+ Effect.map(({ dynamicMiddlewares, generic, middleware }) => ({
225
+ _tag: "MiddlewareMaker" as const,
226
+ effect: makeRpcEffect<
227
+ RequestContextMap,
228
+ GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
229
+ >()(
230
+ (schema, next, moduleName) => {
231
+ const h = middleware(schema, next as any, moduleName)
232
+ return (payload, headers) => {
233
+ const basic = {
234
+ config: schema.config ?? {},
235
+ payload,
236
+ headers,
237
+ clientId: 0, // TODO: get the clientId from the request context
238
+ rpc: {
239
+ ...Rpc.fromTaggedRequest(schema as any),
240
+ // middlewares ? // todo: get from actual middleware flow?
241
+ annotations: Context.empty(), // TODO //Annotations(schema as any),
242
+ // successSchema: schema.success ?? Schema.Void,
243
+ // errorSchema: schema.failure ?? Schema.Never,
244
+ payloadSchema: schema,
245
+ _tag: `${moduleName}.${payload._tag}`,
246
+ key: `${moduleName}.${payload._tag}` /* ? */
247
+ // clientId: 0 as number /* ? */
248
+ }
249
+ }
250
+ return Effect
251
+ .gen(function*() {
252
+ const gen = generic({
253
+ ...basic,
254
+ next:
255
+ // the contextProvider is an Effect that builds the context for the request
256
+ // the dynamicMiddlewares is an Effect that builds the dynamiuc context for the request
257
+ dynamicMiddlewares(basic).pipe(
258
+ Effect.flatMap((dynamicContext) => h(payload, headers).pipe(Effect.provide(dynamicContext)))
259
+ ) as any
260
+ })
261
+ console.log({ gen })
262
+
263
+ return yield* gen
264
+ })
265
+ .pipe(Effect.onExit(Console.log)) as any // why?
266
+ }
267
+ }
268
+ )
269
+ })),
270
+ Effect.onExit(Console.log)
271
+ )
272
+ )
273
+
274
+ const dependencies = [
275
+ ...(make.dependencies ? make.dependencies : []),
276
+ ...(dynamicMiddlewares.dependencies as any),
277
+ ...middlewares.dependencies
278
+ ]
279
+ const middlewareLayer = l
280
+ .pipe(
281
+ Layer.provide(dependencies as any)
282
+ ) as Layer.Layer<
283
+ MiddlewareMakerId,
284
+ | MakeMiddlewareE // what the middleware construction can fail with
285
+ | LayerUtils.GetLayersError<typeof dynamicMiddlewares.dependencies>
286
+ | LayerUtils.GetLayersError<typeof middlewares.dependencies>, // what could go wrong when building the dynamic middleware provider
287
+ | LayerUtils.GetLayersContext<MiddlewareDependencies> // what's needed to build layers
288
+ | LayerUtils.GetLayersContext<typeof middlewares.dependencies>
289
+ | LayerUtils.GetLayersContext<typeof dynamicMiddlewares.dependencies> // what's needed to build dynamic middleware layers
290
+ | Exclude<MakeMiddlewareR, LayerUtils.GetLayersSuccess<MiddlewareDependencies>> // what layers provides
291
+ >
292
+
293
+ return Object.assign(MiddlewareMaker, { Default: middlewareLayer })
294
+ }
295
+
296
+ export const makeMiddlewareBasic =
297
+ // by setting RequestContextMap beforehand, execute contextual typing does not fuck up itself to anys
298
+ <
299
+ RequestContextMap extends Record<string, RPCContextMap.Any>,
300
+ RequestContextProviders extends RequestContextMapProvider<RequestContextMap>, // how to resolve the dynamic middleware
301
+ GenericMiddlewareProviders extends NonEmptyReadonlyArray<GenericMiddlewareMaker>
302
+ >(
303
+ make: MiddlewareMake<
304
+ RequestContextMap,
305
+ RequestContextProviders,
306
+ GenericMiddlewareProviders,
307
+ never,
308
+ never,
309
+ never
310
+ >
311
+ ) => {
312
+ const MiddlewareMaker = Context.GenericTag<
313
+ MiddlewareMakerId,
314
+ {
315
+ effect: RPCHandlerFactory<
316
+ RequestContextMap,
317
+ GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
318
+ >
319
+ _tag: "MiddlewareMaker"
320
+ }
321
+ >(
322
+ "MiddlewareMaker"
323
+ )
324
+
325
+ const dynamicMiddlewares = implementMiddleware<RequestContextMap>()(make.dynamicMiddlewares)
326
+ const middlewares = genericMiddlewareMaker(...make.genericMiddlewares)
327
+
196
328
  const l = Layer.scoped(
197
329
  MiddlewareMaker,
198
330
  Effect
@@ -225,7 +357,8 @@ export const makeMiddleware =
225
357
  return (payload, headers) =>
226
358
  Effect
227
359
  .gen(function*() {
228
- const gen = generic({
360
+ const basic = {
361
+ config: schema.config ?? {},
229
362
  payload,
230
363
  headers,
231
364
  clientId: 0, // TODO: get the clientId from the request context
@@ -239,17 +372,17 @@ export const makeMiddleware =
239
372
  _tag: `${moduleName}.${payload._tag}`,
240
373
  key: `${moduleName}.${payload._tag}` /* ? */
241
374
  // clientId: 0 as number /* ? */
242
- }, // todo: make moduleName part of the tag on S.Req creation.
375
+ }
376
+ }
377
+ return yield* generic({
378
+ ...basic,
243
379
  next:
244
380
  // the contextProvider is an Effect that builds the context for the request
245
381
  // the dynamicMiddlewares is an Effect that builds the dynamiuc context for the request
246
- dynamicMiddlewares(schema.config ?? {}, headers).pipe(
382
+ dynamicMiddlewares(basic).pipe(
247
383
  Effect.flatMap((dynamicContext) => h(payload, headers).pipe(Effect.provide(dynamicContext)))
248
384
  ) as any
249
385
  })
250
- console.log({ gen })
251
-
252
- return yield* gen
253
386
  })
254
387
  .pipe(Effect.onExit(Console.log)) as any // why?
255
388
  }
@@ -261,7 +394,7 @@ export const makeMiddleware =
261
394
 
262
395
  const dependencies = [
263
396
  ...(make.dependencies ? make.dependencies : []),
264
- ...(dynamicMiddlewares.dependencies as any),
397
+ ...(dynamicMiddlewares.dependencies),
265
398
  ...middlewares.dependencies
266
399
  ]
267
400
  const middlewareLayer = l
@@ -269,13 +402,10 @@ export const makeMiddleware =
269
402
  Layer.provide(dependencies as any)
270
403
  ) as Layer.Layer<
271
404
  MiddlewareMakerId,
272
- | MakeMiddlewareE // what the middleware construction can fail with
273
405
  | LayerUtils.GetLayersError<typeof dynamicMiddlewares.dependencies>
274
406
  | LayerUtils.GetLayersError<typeof middlewares.dependencies>, // what could go wrong when building the dynamic middleware provider
275
- | LayerUtils.GetLayersContext<MiddlewareDependencies> // what's needed to build layers
276
407
  | LayerUtils.GetLayersContext<typeof middlewares.dependencies>
277
408
  | LayerUtils.GetLayersContext<typeof dynamicMiddlewares.dependencies> // what's needed to build dynamic middleware layers
278
- | Exclude<MakeMiddlewareR, LayerUtils.GetLayersSuccess<MiddlewareDependencies>> // what layers provides
279
409
  >
280
410
 
281
411
  return Object.assign(MiddlewareMaker, { Default: middlewareLayer })
@@ -347,10 +477,154 @@ type RpcOptionsOriginal = {
347
477
  readonly requiredForClient?: boolean
348
478
  }
349
479
 
480
+ type RpcDynamic<Key extends string, A extends RPCContextMap.Any> = {
481
+ key: Key
482
+ settings: A
483
+ }
484
+
485
+ type RpcOptionsDynamic<Key extends string, A extends RPCContextMap.Any> = RpcOptionsOriginal & {
486
+ readonly dynamic: RpcDynamic<Key, A>
487
+ }
488
+
489
+ export type Dynamic<Options> = Options extends RpcOptionsDynamic<any, any> ? true : false
490
+
491
+ export interface RpcMiddlewareDynamic<A, E, Config> {
492
+ (options: {
493
+ readonly config: Config // todo
494
+ readonly clientId: number
495
+ readonly rpc: Rpc.AnyWithProps
496
+ readonly payload: unknown
497
+ readonly headers: HttpHeaders.Headers
498
+ }): Effect.Effect<Option.Option<Context.Context<A>>, E, Scope.Scope>
499
+ }
500
+
501
+ export interface TagClassDynamicAny<RequestContext extends Record<string, RPCContextMap.Any>>
502
+ extends Context.Tag<any, any>
503
+ {
504
+ readonly [RpcMiddleware.TypeId]: RpcMiddleware.TypeId
505
+ readonly optional: boolean
506
+ readonly provides?: Context.Tag<any, any> | undefined
507
+ readonly failure: Schema.Schema.All
508
+ readonly requiredForClient: boolean
509
+ readonly dynamic: RpcDynamic<any, RequestContext[keyof RequestContext]>
510
+ readonly wrap: boolean
511
+ }
512
+
513
+ export declare namespace TagClass {
514
+ /**
515
+ * @since 1.0.0
516
+ * @category models
517
+ */
518
+ export type Provides<Options> = Options extends {
519
+ readonly provides: Context.Tag<any, any>
520
+ readonly optional?: false
521
+ } ? Context.Tag.Identifier<Options["provides"]>
522
+ : never
523
+
524
+ /**
525
+ * @since 1.0.0
526
+ * @category models
527
+ */
528
+ export type Service<Options> = Options extends { readonly provides: Context.Tag<any, any> }
529
+ ? Context.Tag.Service<Options["provides"]>
530
+ : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? A["service"]
531
+ : void
532
+
533
+ /**
534
+ * @since 1.0.0
535
+ * @category models
536
+ */
537
+ export type FailureSchema<Options> = Options extends
538
+ { readonly failure: Schema.Schema.All; readonly optional?: false } ? Options["failure"]
539
+ : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? A["error"]
540
+ : typeof Schema.Never
541
+
542
+ /**
543
+ * @since 1.0.0
544
+ * @category models
545
+ */
546
+ export type Failure<Options> = Options extends
547
+ { readonly failure: Schema.Schema<infer _A, infer _I, infer _R>; readonly optional?: false } ? _A
548
+ : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? S.Schema.Type<A["error"]>
549
+ : never
550
+
551
+ /**
552
+ * @since 1.0.0
553
+ * @category models
554
+ */
555
+ export type FailureContext<Options> = Schema.Schema.Context<FailureSchema<Options>>
556
+
557
+ /**
558
+ * @since 1.0.0
559
+ * @category models
560
+ */
561
+ export type FailureService<Options> = Optional<Options> extends true ? unknown : Failure<Options>
562
+
563
+ /**
564
+ * @since 1.0.0
565
+ * @category models
566
+ */
567
+ export type Optional<Options> = Options extends { readonly optional: true } ? true : false
568
+
569
+ /**
570
+ * @since 1.0.0
571
+ * @category models
572
+ */
573
+ export type RequiredForClient<Options> = Options extends { readonly requiredForClient: true } ? true : false
574
+
575
+ /**
576
+ * @since 1.0.0
577
+ * @category models
578
+ */
579
+ export type Wrap<Options> = Options extends { readonly wrap: true } ? true : false
580
+
581
+ /**
582
+ * @since 1.0.0
583
+ * @category models
584
+ */
585
+ export interface Base<Self, Name extends string, Options, Service> extends Context.Tag<Self, Service> {
586
+ new(_: never): Context.TagClassShape<Name, Service>
587
+ readonly [TypeId]: TypeId
588
+ readonly optional: Optional<Options>
589
+ readonly failure: FailureSchema<Options>
590
+ readonly provides: Options extends { readonly provides: Context.Tag<any, any> } ? Options["provides"]
591
+ : undefined
592
+ readonly dynamic: Options extends RpcOptionsDynamic<any, any> ? Options["dynamic"]
593
+ : undefined
594
+ readonly requiredForClient: RequiredForClient<Options>
595
+ readonly wrap: Wrap<Options>
596
+ }
597
+ }
598
+
599
+ export interface TagClass<
600
+ Self,
601
+ Name extends string,
602
+ Options
603
+ > extends
604
+ TagClass.Base<
605
+ Self,
606
+ Name,
607
+ Options,
608
+ TagClass.Wrap<Options> extends true ? RpcMiddlewareWrap<
609
+ TagClass.Provides<Options>,
610
+ TagClass.Failure<Options>
611
+ >
612
+ : Options extends RpcOptionsDynamic<any, any> ? RpcMiddlewareDynamic<
613
+ TagClass.Service<Options>,
614
+ TagClass.FailureService<Options>,
615
+ { [K in Options["dynamic"]["key"]]?: Options["dynamic"]["settings"]["contextActivation"] }
616
+ >
617
+ : RpcMiddleware<
618
+ TagClass.Service<Options>,
619
+ TagClass.FailureService<Options>
620
+ >
621
+ >
622
+ {}
623
+
350
624
  export const Tag = <Self>() =>
351
625
  <
352
626
  const Name extends string,
353
- const Options extends RpcOptionsOriginal
627
+ const Options extends RpcOptionsOriginal | RpcOptionsDynamic<any, any>
354
628
  >(
355
629
  id: Name,
356
630
  options?: Options | undefined
@@ -361,6 +635,11 @@ export const Tag = <Self>() =>
361
635
  TagClass.Provides<Options>,
362
636
  TagClass.Failure<Options>
363
637
  >
638
+ : Options extends RpcOptionsDynamic<any, any> ? RpcMiddlewareDynamic<
639
+ TagClass.Service<Options>,
640
+ TagClass.FailureService<Options>,
641
+ { [K in Options["dynamic"]["key"]]?: Options["dynamic"]["settings"]["contextActivation"] }
642
+ >
364
643
  : RpcMiddleware<
365
644
  TagClass.Service<Options>,
366
645
  TagClass.FailureService<Options>
@@ -369,10 +648,12 @@ export const Tag = <Self>() =>
369
648
  R
370
649
  >
371
650
  dependencies?: L
372
- }): RpcMiddleware.TagClass<Self, Name, Options> & {
651
+ }): TagClass<Self, Name, Options> & {
373
652
  Default: Layer.Layer<Self, E | LayerUtils.GetLayersError<L>, Exclude<R, LayerUtils.GetLayersSuccess<L>>>
374
653
  } =>
375
654
  class extends RpcMiddleware.Tag<Self>()(id, options) {
655
+ // TODO: move to TagClass.
656
+ static readonly dynamic = options && "dynamic" in options ? options.dynamic : undefined
376
657
  static readonly Default = Layer.scoped(this, opts.effect as any).pipe(
377
658
  Layer.provide([Layer.empty, ...opts.dependencies ?? []])
378
659
  )
@@ -1,10 +1,12 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { Array, type Context, Effect, type Option, type S } from "effect-app"
2
+ import { Array, type Context, Effect, type S } from "effect-app"
3
3
  import { type GetEffectContext, type RPCContextMap } from "effect-app/client"
4
4
  import { type Tag } from "effect-app/Context"
5
+ import { type HttpHeaders } from "effect-app/http"
5
6
  import { typedValuesOf } from "effect-app/utils"
6
7
  import { type ContextTagWithDefault, mergeOptionContexts } from "../../layerUtils.js"
7
8
  import { sort } from "../tsort.js"
9
+ import { type RpcMiddlewareDynamic } from "./DynamicMiddleware.js"
8
10
 
9
11
  export type ContextWithLayer<
10
12
  Config,
@@ -18,25 +20,13 @@ export type ContextWithLayer<
18
20
  & (
19
21
  | ContextTagWithDefault<
20
22
  Id,
21
- {
22
- _tag: any
23
- handle: (
24
- config: Config,
25
- headers: Record<string, string>
26
- ) => Effect<Option<Context<Service>>, Error, any>
27
- },
23
+ RpcMiddlewareDynamic<Service, Error, Config>,
28
24
  LayerE,
29
25
  LayerR
30
26
  >
31
27
  | ContextTagWithDefault<
32
28
  Id,
33
- {
34
- _tag: any
35
- handle: (
36
- config: Config,
37
- headers: Record<string, string>
38
- ) => Effect<Option<Context<Service>>, Error, never>
39
- },
29
+ RpcMiddlewareDynamic<Service, Error, Config>,
40
30
  LayerE,
41
31
  LayerR
42
32
  >
@@ -76,26 +66,25 @@ export const implementMiddleware = <T extends Record<string, RPCContextMap.Any>>
76
66
 
77
67
  const makers = yield* Effect.all(sorted)
78
68
  return Effect.fnUntraced(
79
- function*(config: { [K in keyof T]?: T[K]["contextActivation"] }, headers: Record<string, string>) {
69
+ function*(options: { config: { [K in keyof T]?: T[K]["contextActivation"] }; headers: HttpHeaders.Headers }) {
80
70
  const ctx = yield* mergeOptionContexts(
81
71
  Array.map(
82
72
  makers,
83
- (_, i) => ({ maker: sorted[i], handle: (_ as any).handle(config, headers) as any }) as any
73
+ (_, i) => ({ maker: sorted[i], handle: (_ as any)(options) as any }) as any
84
74
  )
85
75
  )
86
76
  return ctx as Context.Context<
87
- GetEffectContext<T, typeof config>
77
+ GetEffectContext<T, typeof options["config"]>
88
78
  >
89
79
  }
90
80
  )
91
81
  }) as unknown as Effect<
92
82
  (
93
- config: { [K in keyof T]?: T[K]["contextActivation"] },
94
- headers: Record<string, string>
83
+ options: { config: { [K in keyof T]?: T[K]["contextActivation"] }; headers: HttpHeaders.Headers }
95
84
  ) => Effect.Effect<
96
- Context.Context<GetEffectContext<T, typeof config>>,
97
- Effect.Error<ReturnType<Tag.Service<TI[keyof TI]>["handle"]>>,
98
- Effect.Context<ReturnType<Tag.Service<TI[keyof TI]>["handle"]>>
85
+ Context.Context<GetEffectContext<T, typeof options["config"]>>,
86
+ Effect.Error<ReturnType<Tag.Service<TI[keyof TI]>>>,
87
+ Effect.Context<ReturnType<Tag.Service<TI[keyof TI]>>>
99
88
  >,
100
89
  never,
101
90
  Tag.Identifier<{ [K in keyof TI]: TI[K] }[keyof TI]>
@@ -2,8 +2,10 @@
2
2
  import { type Rpc, type RpcMiddleware } from "@effect/rpc"
3
3
  import { type SuccessValue, type TagClassAny } from "@effect/rpc/RpcMiddleware"
4
4
  import { type Array, Context, Effect, type Layer, type Scope } from "effect-app"
5
+ import { type RPCContextMap } from "effect-app/client"
5
6
  import { type HttpHeaders } from "effect-app/http"
6
7
  import { InfraLogger } from "../../../logger.js"
8
+ import { type TagClassDynamicAny } from "./DynamicMiddleware.js"
7
9
 
8
10
  export interface GenericMiddlewareOptions<E> {
9
11
  // Effect rpc middleware does not support changing payload or headers, but we do..
@@ -15,6 +17,9 @@ export interface GenericMiddlewareOptions<E> {
15
17
  }
16
18
 
17
19
  export type GenericMiddlewareMaker = TagClassAny & { Default: Layer.Layer.Any } // todo; and Layer..
20
+ export type DynamicMiddlewareMaker<RequestContext extends Record<string, RPCContextMap.Any>> =
21
+ & TagClassDynamicAny<RequestContext>
22
+ & { Default: Layer.Layer.Any } // todo; and Layer..
18
23
 
19
24
  export namespace GenericMiddlewareMaker {
20
25
  export type Provided<T> = T extends TagClassAny
@@ -0,0 +1,44 @@
1
+ import { type NonEmptyReadonlyArray } from "effect-app"
2
+ import { type RPCContextMap } from "effect-app/client"
3
+ import { type DynamicMiddlewareMaker, type GenericMiddlewareMaker, makeMiddleware, type makeMiddlewareBasic, type RequestContextMapProvider } from "../../routing.js"
4
+
5
+ export const contextMap = <RequestContextMap>() => <K extends keyof RequestContextMap>(a: K) => ({
6
+ key: a,
7
+ settings: null as any as RequestContextMap[typeof a]
8
+ })
9
+
10
+ type DynamicMiddlewareMakerrsss<
11
+ RequestContext extends Record<string, RPCContextMap.Any>,
12
+ Provided extends keyof RequestContext,
13
+ Middlewares extends NonEmptyReadonlyArray<GenericMiddlewareMaker>,
14
+ DynamicMiddlewareProviders extends RequestContextMapProvider<RequestContext>
15
+ > = keyof Omit<RequestContext, Provided> extends never
16
+ ? { make: () => ReturnType<typeof makeMiddlewareBasic<RequestContext, DynamicMiddlewareProviders, Middlewares>> }
17
+ : {
18
+ addDynamicMiddleware: <MW extends DynamicMiddlewareMaker<RequestContext>>(
19
+ a: MW
20
+ ) => DynamicMiddlewareMakerrsss<
21
+ RequestContext,
22
+ Provided | MW["dynamic"]["key"],
23
+ Middlewares,
24
+ DynamicMiddlewareProviders & { [K in MW["dynamic"]["key"]]: MW }
25
+ > // TODO: any of RequestContecxtMap, and track them, so remove the ones provided
26
+ }
27
+
28
+ export const makeNewMiddleware: <
29
+ RequestContextMap extends Record<string, RPCContextMap.Any>
30
+ >() => <Middlewares extends NonEmptyReadonlyArray<GenericMiddlewareMaker>>(
31
+ ...genericMiddlewares: Middlewares
32
+ ) => DynamicMiddlewareMakerrsss<RequestContextMap, never, Middlewares, never> = () => (...genericMiddlewares) => {
33
+ const dynamicMiddlewares: Record<keyof any, any> = {} as any
34
+ const make = makeMiddleware<any>()
35
+ const it = {
36
+ addDynamicMiddleware: (a: any) => {
37
+ console.log("Adding dynamic middleware", a, a.dynamic, Object.keys(a))
38
+ ;(dynamicMiddlewares as any)[a.dynamic.key] = a
39
+ return it
40
+ },
41
+ make: () => make({ genericMiddlewares: genericMiddlewares as any, dynamicMiddlewares })
42
+ }
43
+ return it
44
+ }
@@ -3,6 +3,7 @@ export * from "./middleware/ContextProvider.js"
3
3
  export * from "./middleware/dynamic-middleware.js"
4
4
  export * from "./middleware/DynamicMiddleware.js"
5
5
  export * from "./middleware/generic-middleware.js"
6
+ export * from "./middleware/middleware-api.js"
6
7
  export * from "./middleware/middleware.js"
7
8
  // codegen:end
8
9