@effect-app/infra 2.80.0 → 2.81.1

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 +14 -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 +79 -11
  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 +295 -19
  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.1",
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 { 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,128 @@ 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
+
262
+ return yield* gen
263
+ }) as any // why?
264
+ }
265
+ }
266
+ )
267
+ }))
268
+ )
269
+ )
270
+
271
+ const dependencies = [
272
+ ...(make.dependencies ? make.dependencies : []),
273
+ ...(dynamicMiddlewares.dependencies as any),
274
+ ...middlewares.dependencies
275
+ ]
276
+ const middlewareLayer = l
277
+ .pipe(
278
+ Layer.provide(dependencies as any)
279
+ ) as Layer.Layer<
280
+ MiddlewareMakerId,
281
+ | MakeMiddlewareE // what the middleware construction can fail with
282
+ | LayerUtils.GetLayersError<typeof dynamicMiddlewares.dependencies>
283
+ | LayerUtils.GetLayersError<typeof middlewares.dependencies>, // what could go wrong when building the dynamic middleware provider
284
+ | LayerUtils.GetLayersContext<MiddlewareDependencies> // what's needed to build layers
285
+ | LayerUtils.GetLayersContext<typeof middlewares.dependencies>
286
+ | LayerUtils.GetLayersContext<typeof dynamicMiddlewares.dependencies> // what's needed to build dynamic middleware layers
287
+ | Exclude<MakeMiddlewareR, LayerUtils.GetLayersSuccess<MiddlewareDependencies>> // what layers provides
288
+ >
289
+
290
+ return Object.assign(MiddlewareMaker, { Default: middlewareLayer })
291
+ }
292
+
293
+ export const makeMiddlewareBasic =
294
+ // by setting RequestContextMap beforehand, execute contextual typing does not fuck up itself to anys
295
+ <
296
+ RequestContextMap extends Record<string, RPCContextMap.Any>,
297
+ RequestContextProviders extends RequestContextMapProvider<RequestContextMap>, // how to resolve the dynamic middleware
298
+ GenericMiddlewareProviders extends NonEmptyReadonlyArray<GenericMiddlewareMaker>
299
+ >(
300
+ make: MiddlewareMake<
301
+ RequestContextMap,
302
+ RequestContextProviders,
303
+ GenericMiddlewareProviders,
304
+ never,
305
+ never,
306
+ never
307
+ >
308
+ ) => {
309
+ const MiddlewareMaker = Context.GenericTag<
310
+ MiddlewareMakerId,
311
+ {
312
+ effect: RPCHandlerFactory<
313
+ RequestContextMap,
314
+ GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
315
+ >
316
+ _tag: "MiddlewareMaker"
317
+ }
318
+ >(
319
+ "MiddlewareMaker"
320
+ )
321
+
322
+ const dynamicMiddlewares = implementMiddleware<RequestContextMap>()(make.dynamicMiddlewares)
323
+ const middlewares = genericMiddlewareMaker(...make.genericMiddlewares)
324
+
196
325
  const l = Layer.scoped(
197
326
  MiddlewareMaker,
198
327
  Effect
@@ -225,7 +354,8 @@ export const makeMiddleware =
225
354
  return (payload, headers) =>
226
355
  Effect
227
356
  .gen(function*() {
228
- const gen = generic({
357
+ const basic = {
358
+ config: schema.config ?? {},
229
359
  payload,
230
360
  headers,
231
361
  clientId: 0, // TODO: get the clientId from the request context
@@ -239,29 +369,27 @@ export const makeMiddleware =
239
369
  _tag: `${moduleName}.${payload._tag}`,
240
370
  key: `${moduleName}.${payload._tag}` /* ? */
241
371
  // clientId: 0 as number /* ? */
242
- }, // todo: make moduleName part of the tag on S.Req creation.
372
+ }
373
+ }
374
+ return yield* generic({
375
+ ...basic,
243
376
  next:
244
377
  // the contextProvider is an Effect that builds the context for the request
245
378
  // the dynamicMiddlewares is an Effect that builds the dynamiuc context for the request
246
- dynamicMiddlewares(schema.config ?? {}, headers).pipe(
379
+ dynamicMiddlewares(basic).pipe(
247
380
  Effect.flatMap((dynamicContext) => h(payload, headers).pipe(Effect.provide(dynamicContext)))
248
381
  ) as any
249
382
  })
250
- console.log({ gen })
251
-
252
- return yield* gen
253
- })
254
- .pipe(Effect.onExit(Console.log)) as any // why?
383
+ }) as any // why?
255
384
  }
256
385
  )
257
- })),
258
- Effect.onExit(Console.log)
386
+ }))
259
387
  )
260
388
  )
261
389
 
262
390
  const dependencies = [
263
391
  ...(make.dependencies ? make.dependencies : []),
264
- ...(dynamicMiddlewares.dependencies as any),
392
+ ...(dynamicMiddlewares.dependencies),
265
393
  ...middlewares.dependencies
266
394
  ]
267
395
  const middlewareLayer = l
@@ -269,13 +397,10 @@ export const makeMiddleware =
269
397
  Layer.provide(dependencies as any)
270
398
  ) as Layer.Layer<
271
399
  MiddlewareMakerId,
272
- | MakeMiddlewareE // what the middleware construction can fail with
273
400
  | LayerUtils.GetLayersError<typeof dynamicMiddlewares.dependencies>
274
401
  | 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
402
  | LayerUtils.GetLayersContext<typeof middlewares.dependencies>
277
403
  | LayerUtils.GetLayersContext<typeof dynamicMiddlewares.dependencies> // what's needed to build dynamic middleware layers
278
- | Exclude<MakeMiddlewareR, LayerUtils.GetLayersSuccess<MiddlewareDependencies>> // what layers provides
279
404
  >
280
405
 
281
406
  return Object.assign(MiddlewareMaker, { Default: middlewareLayer })
@@ -347,10 +472,154 @@ type RpcOptionsOriginal = {
347
472
  readonly requiredForClient?: boolean
348
473
  }
349
474
 
475
+ type RpcDynamic<Key extends string, A extends RPCContextMap.Any> = {
476
+ key: Key
477
+ settings: A
478
+ }
479
+
480
+ type RpcOptionsDynamic<Key extends string, A extends RPCContextMap.Any> = RpcOptionsOriginal & {
481
+ readonly dynamic: RpcDynamic<Key, A>
482
+ }
483
+
484
+ export type Dynamic<Options> = Options extends RpcOptionsDynamic<any, any> ? true : false
485
+
486
+ export interface RpcMiddlewareDynamic<A, E, Config> {
487
+ (options: {
488
+ readonly config: Config // todo
489
+ readonly clientId: number
490
+ readonly rpc: Rpc.AnyWithProps
491
+ readonly payload: unknown
492
+ readonly headers: HttpHeaders.Headers
493
+ }): Effect.Effect<Option.Option<Context.Context<A>>, E, Scope.Scope>
494
+ }
495
+
496
+ export interface TagClassDynamicAny<RequestContext extends Record<string, RPCContextMap.Any>>
497
+ extends Context.Tag<any, any>
498
+ {
499
+ readonly [RpcMiddleware.TypeId]: RpcMiddleware.TypeId
500
+ readonly optional: boolean
501
+ readonly provides?: Context.Tag<any, any> | undefined
502
+ readonly failure: Schema.Schema.All
503
+ readonly requiredForClient: boolean
504
+ readonly dynamic: RpcDynamic<any, RequestContext[keyof RequestContext]>
505
+ readonly wrap: boolean
506
+ }
507
+
508
+ export declare namespace TagClass {
509
+ /**
510
+ * @since 1.0.0
511
+ * @category models
512
+ */
513
+ export type Provides<Options> = Options extends {
514
+ readonly provides: Context.Tag<any, any>
515
+ readonly optional?: false
516
+ } ? Context.Tag.Identifier<Options["provides"]>
517
+ : never
518
+
519
+ /**
520
+ * @since 1.0.0
521
+ * @category models
522
+ */
523
+ export type Service<Options> = Options extends { readonly provides: Context.Tag<any, any> }
524
+ ? Context.Tag.Service<Options["provides"]>
525
+ : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? A["service"]
526
+ : void
527
+
528
+ /**
529
+ * @since 1.0.0
530
+ * @category models
531
+ */
532
+ export type FailureSchema<Options> = Options extends
533
+ { readonly failure: Schema.Schema.All; readonly optional?: false } ? Options["failure"]
534
+ : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? A["error"]
535
+ : typeof Schema.Never
536
+
537
+ /**
538
+ * @since 1.0.0
539
+ * @category models
540
+ */
541
+ export type Failure<Options> = Options extends
542
+ { readonly failure: Schema.Schema<infer _A, infer _I, infer _R>; readonly optional?: false } ? _A
543
+ : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? S.Schema.Type<A["error"]>
544
+ : never
545
+
546
+ /**
547
+ * @since 1.0.0
548
+ * @category models
549
+ */
550
+ export type FailureContext<Options> = Schema.Schema.Context<FailureSchema<Options>>
551
+
552
+ /**
553
+ * @since 1.0.0
554
+ * @category models
555
+ */
556
+ export type FailureService<Options> = Optional<Options> extends true ? unknown : Failure<Options>
557
+
558
+ /**
559
+ * @since 1.0.0
560
+ * @category models
561
+ */
562
+ export type Optional<Options> = Options extends { readonly optional: true } ? true : false
563
+
564
+ /**
565
+ * @since 1.0.0
566
+ * @category models
567
+ */
568
+ export type RequiredForClient<Options> = Options extends { readonly requiredForClient: true } ? true : false
569
+
570
+ /**
571
+ * @since 1.0.0
572
+ * @category models
573
+ */
574
+ export type Wrap<Options> = Options extends { readonly wrap: true } ? true : false
575
+
576
+ /**
577
+ * @since 1.0.0
578
+ * @category models
579
+ */
580
+ export interface Base<Self, Name extends string, Options, Service> extends Context.Tag<Self, Service> {
581
+ new(_: never): Context.TagClassShape<Name, Service>
582
+ readonly [TypeId]: TypeId
583
+ readonly optional: Optional<Options>
584
+ readonly failure: FailureSchema<Options>
585
+ readonly provides: Options extends { readonly provides: Context.Tag<any, any> } ? Options["provides"]
586
+ : undefined
587
+ readonly dynamic: Options extends RpcOptionsDynamic<any, any> ? Options["dynamic"]
588
+ : undefined
589
+ readonly requiredForClient: RequiredForClient<Options>
590
+ readonly wrap: Wrap<Options>
591
+ }
592
+ }
593
+
594
+ export interface TagClass<
595
+ Self,
596
+ Name extends string,
597
+ Options
598
+ > extends
599
+ TagClass.Base<
600
+ Self,
601
+ Name,
602
+ Options,
603
+ TagClass.Wrap<Options> extends true ? RpcMiddlewareWrap<
604
+ TagClass.Provides<Options>,
605
+ TagClass.Failure<Options>
606
+ >
607
+ : Options extends RpcOptionsDynamic<any, any> ? RpcMiddlewareDynamic<
608
+ TagClass.Service<Options>,
609
+ TagClass.FailureService<Options>,
610
+ { [K in Options["dynamic"]["key"]]?: Options["dynamic"]["settings"]["contextActivation"] }
611
+ >
612
+ : RpcMiddleware<
613
+ TagClass.Service<Options>,
614
+ TagClass.FailureService<Options>
615
+ >
616
+ >
617
+ {}
618
+
350
619
  export const Tag = <Self>() =>
351
620
  <
352
621
  const Name extends string,
353
- const Options extends RpcOptionsOriginal
622
+ const Options extends RpcOptionsOriginal | RpcOptionsDynamic<any, any>
354
623
  >(
355
624
  id: Name,
356
625
  options?: Options | undefined
@@ -361,6 +630,11 @@ export const Tag = <Self>() =>
361
630
  TagClass.Provides<Options>,
362
631
  TagClass.Failure<Options>
363
632
  >
633
+ : Options extends RpcOptionsDynamic<any, any> ? RpcMiddlewareDynamic<
634
+ TagClass.Service<Options>,
635
+ TagClass.FailureService<Options>,
636
+ { [K in Options["dynamic"]["key"]]?: Options["dynamic"]["settings"]["contextActivation"] }
637
+ >
364
638
  : RpcMiddleware<
365
639
  TagClass.Service<Options>,
366
640
  TagClass.FailureService<Options>
@@ -369,10 +643,12 @@ export const Tag = <Self>() =>
369
643
  R
370
644
  >
371
645
  dependencies?: L
372
- }): RpcMiddleware.TagClass<Self, Name, Options> & {
646
+ }): TagClass<Self, Name, Options> & {
373
647
  Default: Layer.Layer<Self, E | LayerUtils.GetLayersError<L>, Exclude<R, LayerUtils.GetLayersSuccess<L>>>
374
648
  } =>
375
649
  class extends RpcMiddleware.Tag<Self>()(id, options) {
650
+ // TODO: move to TagClass.
651
+ static readonly dynamic = options && "dynamic" in options ? options.dynamic : undefined
376
652
  static readonly Default = Layer.scoped(this, opts.effect as any).pipe(
377
653
  Layer.provide([Layer.empty, ...opts.dependencies ?? []])
378
654
  )
@@ -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