@effect-app/infra 2.94.0 → 3.0.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 (54) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/Emailer/service.d.ts +1 -1
  3. package/dist/MainFiberSet.d.ts +1 -1
  4. package/dist/Operations.d.ts +1 -1
  5. package/dist/RequestFiberSet.d.ts +1 -1
  6. package/dist/Store/service.d.ts +2 -2
  7. package/dist/adapters/SQL/Model.d.ts +6 -6
  8. package/dist/adapters/ServiceBus.d.ts +2 -2
  9. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  10. package/dist/adapters/ServiceBus.js +13 -10
  11. package/dist/adapters/memQueue.d.ts +1 -1
  12. package/dist/api/routing/middleware/RouterMiddleware.d.ts +6 -6
  13. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
  14. package/dist/api/routing/middleware/RpcMiddleware.d.ts +4 -15
  15. package/dist/api/routing/middleware/RpcMiddleware.d.ts.map +1 -1
  16. package/dist/api/routing/middleware/RpcMiddleware.js +3 -4
  17. package/dist/api/routing/middleware/RpcMiddlewareX.d.ts +18 -0
  18. package/dist/api/routing/middleware/RpcMiddlewareX.d.ts.map +1 -0
  19. package/dist/api/routing/middleware/RpcMiddlewareX.js +16 -0
  20. package/dist/api/routing/middleware/generic-middleware.d.ts +22 -8
  21. package/dist/api/routing/middleware/generic-middleware.d.ts.map +1 -1
  22. package/dist/api/routing/middleware/generic-middleware.js +59 -62
  23. package/dist/api/routing/middleware/middleware-api.d.ts +51 -32
  24. package/dist/api/routing/middleware/middleware-api.d.ts.map +1 -1
  25. package/dist/api/routing/middleware/middleware-api.js +54 -24
  26. package/dist/api/routing/middleware/middleware-native.d.ts +23 -0
  27. package/dist/api/routing/middleware/middleware-native.d.ts.map +1 -0
  28. package/dist/api/routing/middleware/middleware-native.js +19 -0
  29. package/dist/api/routing/middleware/middleware.d.ts +7 -27
  30. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  31. package/dist/api/routing/middleware/middleware.js +73 -88
  32. package/dist/api/routing/middleware.d.ts +1 -0
  33. package/dist/api/routing/middleware.d.ts.map +1 -1
  34. package/dist/api/routing/middleware.js +2 -1
  35. package/package.json +11 -3
  36. package/src/adapters/ServiceBus.ts +7 -5
  37. package/src/api/routing/middleware/RouterMiddleware.ts +9 -8
  38. package/src/api/routing/middleware/RpcMiddleware.ts +4 -51
  39. package/src/api/routing/middleware/RpcMiddlewareX.ts +70 -0
  40. package/src/api/routing/middleware/generic-middleware.ts +142 -107
  41. package/src/api/routing/middleware/middleware-api.ts +239 -61
  42. package/src/api/routing/middleware/middleware-native.ts +23 -0
  43. package/src/api/routing/middleware/middleware.ts +37 -40
  44. package/src/api/routing/middleware.ts +1 -0
  45. package/test/controller.test.ts +39 -14
  46. package/test/dist/controller.test.d.ts.map +1 -1
  47. package/test/dist/fixtures.d.ts +48 -32
  48. package/test/dist/fixtures.d.ts.map +1 -1
  49. package/test/dist/fixtures.js +76 -47
  50. package/test/dist/requires.test.d.ts.map +1 -1
  51. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -0
  52. package/test/fixtures.ts +70 -16
  53. package/test/requires.test.ts +15 -50
  54. package/test/rpc-multi-middleware.test.ts +134 -0
package/test/fixtures.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { Context, Effect, Option, S, Scope } from "effect-app"
1
+ import { Context, Effect, Layer, Option, S, Scope } from "effect-app"
2
2
  import { NotLoggedInError, RPCContextMap, UnauthorizedError } from "effect-app/client"
3
3
  import { TaggedError } from "effect-app/Schema"
4
- import { contextMap, getConfig, Middleware } from "../src/api/routing.js"
4
+ import { contextMap, getConfig, Tag } from "../src/api/routing.js"
5
5
 
6
6
  export class UserProfile extends Context.assignTag<UserProfile, UserProfile>("UserProfile")(
7
7
  S.Class<UserProfile>("UserProfile")({
@@ -16,6 +16,53 @@ export class SomeElse extends Context.TagMakeId("SomeElse", Effect.succeed({ b:
16
16
  const MakeSomeService = Effect.succeed({ a: 1 })
17
17
  export class SomeService extends Context.TagMakeId("SomeService", MakeSomeService)<SomeService>() {}
18
18
 
19
+ export class SomeMiddleware extends Tag<SomeMiddleware>()("SomeMiddleware", {
20
+ provides: Some
21
+ }) {}
22
+
23
+ export const SomeMiddlewareLive = Layer.effect(
24
+ SomeMiddleware,
25
+ Effect.gen(function*() {
26
+ // yield* Effect.context<"test-dep">()
27
+ return () =>
28
+ Effect.gen(function*() {
29
+ return new Some({ a: 1 })
30
+ })
31
+ })
32
+ )
33
+
34
+ // functionally equivalent to the one above
35
+ export class SomeMiddlewareWrap extends Tag<SomeMiddlewareWrap>()("SomeMiddlewareWrap", {
36
+ provides: Some,
37
+ wrap: true
38
+ }) {
39
+ }
40
+
41
+ export const SomeMiddlewareWrapLive = Layer.effect(
42
+ SomeMiddlewareWrap,
43
+ Effect.gen(function*() {
44
+ // yield* Effect.context<"test-dep">()
45
+ return ({ next }) => next.pipe(Effect.provideService(Some, new Some({ a: 1 })))
46
+ })
47
+ )
48
+
49
+ export class SomeElseMiddleware extends Tag<SomeElseMiddleware>()("SomeElseMiddleware", {
50
+ provides: SomeElse,
51
+ wrap: true
52
+ }) {}
53
+
54
+ export const SomeElseMiddlewareLive = Layer.effect(
55
+ SomeElseMiddleware,
56
+ Effect.gen(function*() {
57
+ // yield* Effect.context<"test-dep">()
58
+ return ({ next }) =>
59
+ Effect.gen(function*() {
60
+ // yield* Effect.context<"test-dep2">()
61
+ return yield* next.pipe(Effect.provideService(SomeElse, new SomeElse({ b: 2 })))
62
+ })
63
+ })
64
+ )
65
+
19
66
  const requestConfig = getConfig<RequestContextMap>()
20
67
 
21
68
  // TODO: null as never sucks
@@ -32,11 +79,14 @@ export const RequestContextMap = {
32
79
  type _RequestContextMap = typeof RequestContextMap
33
80
  export interface RequestContextMap extends _RequestContextMap {}
34
81
 
35
- export class AllowAnonymous extends Middleware.Tag<AllowAnonymous>()("AllowAnonymous", {
82
+ export class AllowAnonymous extends Tag<AllowAnonymous>()("AllowAnonymous", {
36
83
  dynamic: contextMap(RequestContextMap, "allowAnonymous"),
37
84
  requires: SomeElse
38
- })({
39
- effect: Effect.gen(function*() {
85
+ }) {}
86
+
87
+ export const AllowAnonymousLive = Layer.effect(
88
+ AllowAnonymous,
89
+ Effect.gen(function*() {
40
90
  return Effect.fnUntraced(
41
91
  function*({ headers, rpc }) {
42
92
  yield* SomeElse
@@ -60,20 +110,22 @@ export class AllowAnonymous extends Middleware.Tag<AllowAnonymous>()("AllowAnony
60
110
  }
61
111
  )
62
112
  })
63
- }) {
64
- }
113
+ )
65
114
 
66
115
  // TODO: don't expect service when it's wrap
67
116
  // @effect-diagnostics-next-line missingEffectServiceDependency:off
68
- export class RequireRoles extends Middleware.Tag<RequireRoles>()("RequireRoles", {
117
+ export class RequireRoles extends Tag<RequireRoles>()("RequireRoles", {
69
118
  dynamic: contextMap(RequestContextMap, "requireRoles"),
70
119
  wrap: true,
71
120
  // wrap: true,
72
121
  // had to move this in here, because once you put it manually as a readonly static property on the class,
73
122
  // there's a weird issue where the fluent api stops behaving properly after adding this middleware via `addDynamicMiddleware`
74
123
  dependsOn: [AllowAnonymous]
75
- })({
76
- effect: Effect.gen(function*() {
124
+ }) {}
125
+
126
+ export const RequireRolesLive = Layer.effect(
127
+ RequireRoles,
128
+ Effect.gen(function*() {
77
129
  yield* SomeService
78
130
  return Effect.fnUntraced(
79
131
  function*({ next, rpc }) {
@@ -94,20 +146,22 @@ export class RequireRoles extends Middleware.Tag<RequireRoles>()("RequireRoles",
94
146
  }
95
147
  )
96
148
  })
97
- }) {
98
- }
149
+ )
99
150
 
100
151
  // TODO: don't expect service when it's wrap
101
- export class Test extends Middleware.Tag<Test>()("Test", {
152
+ export class Test extends Tag<Test>()("Test", {
102
153
  wrap: true,
103
154
  dynamic: contextMap(RequestContextMap, "test")
104
- })({
105
- effect: Effect.gen(function*() {
155
+ }) {}
156
+
157
+ export const TestLive = Layer.effect(
158
+ Test,
159
+ Effect.gen(function*() {
106
160
  return Effect.fn(function*({ next }) {
107
161
  return yield* next
108
162
  })
109
163
  })
110
- }) {}
164
+ )
111
165
 
112
166
  export class CustomError1 extends TaggedError<NotLoggedInError>()("CustomError1", {}) {}
113
167
  export class CustomError2 extends TaggedError<NotLoggedInError>()("CustomError1", {}) {}
@@ -5,49 +5,9 @@ import { Context, Effect, Either, Layer, S } from "effect-app"
5
5
  import { NotLoggedInError, UnauthorizedError } from "effect-app/client"
6
6
  import { HttpHeaders } from "effect-app/http"
7
7
  import { makeMiddleware, Middleware } from "../src/api/routing.js"
8
- import { AllowAnonymous, RequestContextMap, RequireRoles, Some, SomeElse, SomeService, Test } from "./fixtures.js"
8
+ import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElseMiddleware, SomeElseMiddlewareLive, SomeMiddleware, SomeMiddlewareLive, SomeMiddlewareWrap, SomeService, Test, TestLive } from "./fixtures.js"
9
9
 
10
- export class SomeMiddleware extends Middleware.Tag<SomeMiddleware>()("SomeMiddleware", {
11
- provides: Some
12
- })({
13
- effect: Effect.gen(function*() {
14
- // yield* Effect.context<"test-dep">()
15
- return () =>
16
- Effect.gen(function*() {
17
- return new Some({ a: 1 })
18
- })
19
- })
20
- }) {
21
- }
22
-
23
- // functionally equivalent to the one above
24
- export class SomeMiddlewareWrap extends Middleware.Tag<SomeMiddlewareWrap>()("SomeMiddlewareWrap", {
25
- provides: Some,
26
- wrap: true
27
- })({
28
- effect: Effect.gen(function*() {
29
- // yield* Effect.context<"test-dep">()
30
- return ({ next }) => next.pipe(Effect.provideService(Some, new Some({ a: 1 })))
31
- })
32
- }) {
33
- }
34
-
35
- export class SomeElseMiddleware extends Middleware.Tag<SomeElseMiddleware>()("SomeElseMiddleware", {
36
- provides: SomeElse,
37
- wrap: true
38
- })({
39
- effect: Effect.gen(function*() {
40
- // yield* Effect.context<"test-dep">()
41
- return ({ next }) =>
42
- Effect.gen(function*() {
43
- // yield* Effect.context<"test-dep2">()
44
- return yield* next.pipe(Effect.provideService(SomeElse, new SomeElse({ b: 2 })))
45
- })
46
- })
47
- }) {
48
- }
49
-
50
- export class RequiresSomeMiddleware extends Middleware.Tag<RequiresSomeMiddleware>()("RequiresSomeMiddleware", {
10
+ export class RequiresSomeMiddleware extends Middleware.TagService<RequiresSomeMiddleware>()("RequiresSomeMiddleware", {
51
11
  requires: [Some],
52
12
  wrap: true
53
13
  })({
@@ -79,7 +39,7 @@ const _middlewareSideways = makeMiddleware(RequestContextMap)
79
39
  const _middlewareSidewaysFully = makeMiddleware(RequestContextMap)
80
40
  .middleware(RequiresSomeMiddleware, SomeMiddleware, RequireRoles, AllowAnonymous, Test, SomeElseMiddleware)
81
41
 
82
- const _middleware3Bis = makeMiddleware(RequestContextMap)
42
+ export const _middleware3Bis = makeMiddleware(RequestContextMap)
83
43
  .middleware(RequiresSomeMiddleware)
84
44
  .middleware(SomeMiddlewareWrap)
85
45
  .middleware(RequireRoles)
@@ -88,11 +48,7 @@ const _middleware3Bis = makeMiddleware(RequestContextMap)
88
48
 
89
49
  expectTypeOf(_middlewareSideways).toEqualTypeOf<typeof middleware3>()
90
50
  expectTypeOf(_middlewareSidewaysFully).toEqualTypeOf<typeof _middlewareSideways>()
91
- // expectTypeOf(_middleware3Bis).toEqualTypeOf<typeof middleware3>() // is not the same because SomeMiddlewareWrap is not SomeMiddleware
92
-
93
- type Default = typeof middleware3["Default"]
94
- type LayerContext = Layer.Layer.Context<Default>
95
- expectTypeOf({} as LayerContext).toEqualTypeOf<SomeService>()
51
+ expectTypeOf(_middleware3Bis).not.toEqualTypeOf<typeof middleware3>() // is not the same because SomeMiddlewareWrap is not SomeMiddleware
96
52
 
97
53
  class TestRequest extends S.TaggedRequest<Test>("Test")("Test", {
98
54
  payload: {},
@@ -112,7 +68,16 @@ const testSuite = (_mw: typeof middleware3) =>
112
68
  rpc: { ...Rpc.fromTaggedRequest(TestRequest), annotations: Context.make(_mw.requestContext, {}) },
113
69
  next: Effect.void as unknown as Effect<SuccessValue, never, any>
114
70
  }
115
- const layer = _mw.Default.pipe(Layer.provide(SomeService.toLayer()))
71
+ const layer = _mw.layer.pipe(
72
+ Layer.provide([
73
+ RequiresSomeMiddleware.Default,
74
+ SomeMiddlewareLive,
75
+ RequireRolesLive.pipe(Layer.provide(SomeService.toLayer())),
76
+ AllowAnonymousLive,
77
+ TestLive,
78
+ SomeElseMiddlewareLive
79
+ ])
80
+ )
116
81
  yield* Effect
117
82
  .gen(function*() {
118
83
  const mw = yield* _mw
@@ -194,6 +159,6 @@ const testSuite = (_mw: typeof middleware3) =>
194
159
  })
195
160
 
196
161
  testSuite(middleware3)
197
- testSuite(_middleware3Bis)
162
+ // testSuite(_middleware3Bis)
198
163
  testSuite(_middlewareSideways)
199
164
  testSuite(_middlewareSidewaysFully)
@@ -0,0 +1,134 @@
1
+ import { FetchHttpClient } from "@effect/platform"
2
+ import { NodeHttpServer } from "@effect/platform-node"
3
+ import { RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "@effect/rpc"
4
+ import { expect, expectTypeOf, it } from "@effect/vitest"
5
+ import { Console, Effect, Either, Layer } from "effect"
6
+ import { S } from "effect-app"
7
+ import { NotLoggedInError } from "effect-app/client"
8
+ import { HttpLayerRouter } from "effect-app/http"
9
+ import { createServer } from "http"
10
+ import { DefaultGenericMiddlewaresLive, makeMiddleware, middlewareGroup } from "../src/api/routing.js"
11
+ import { DefaultGenericMiddlewares } from "../src/api/routing/middleware/middleware-native.js"
12
+ import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElseMiddleware, SomeElseMiddlewareLive, SomeMiddlewareWrap, SomeMiddlewareWrapLive, SomeService, Test, TestLive, UserProfile } from "./fixtures.js"
13
+
14
+ const middleware = makeMiddleware(RequestContextMap)
15
+ .middleware(RequireRoles)
16
+ .middleware(AllowAnonymous, Test)
17
+ .middleware(SomeElseMiddleware, SomeMiddlewareWrap)
18
+ .middleware(...DefaultGenericMiddlewares)
19
+
20
+ const UserRpcs = middlewareGroup(middleware)(RpcGroup
21
+ .make(
22
+ middleware.rpc("getUser", {
23
+ success: S.Literal("awesome")
24
+ }),
25
+ middleware.rpc("doSomething", {
26
+ success: S.Literal("also-awesome"),
27
+ config: { allowAnonymous: true }
28
+ })
29
+ ))
30
+
31
+ const impl = Effect
32
+ .gen(function*() {
33
+ const impl = UserRpcs
34
+ .toLayerDynamic({
35
+ getUser: Effect.fn(function*(_payload, _headers) {
36
+ yield* Some
37
+ yield* UserProfile // we only access it while protected by allowAnonymous: false
38
+ return "awesome" as const
39
+ }),
40
+ doSomething: Effect.fn(function*() {
41
+ console.log(yield* Effect.serviceOption(UserProfile)) // we access it optionally, while allowAnonymous: true
42
+ return "also-awesome" as const
43
+ })
44
+ })
45
+ return impl
46
+ })
47
+ .pipe(Layer.unwrapEffect)
48
+
49
+ expectTypeOf<Layer.Layer.Context<typeof impl>>().toEqualTypeOf<never>()
50
+
51
+ const UserRpcsBad = middlewareGroup(middleware)(RpcGroup
52
+ .make(
53
+ middleware.rpc("doSomethingElse", {
54
+ success: S.Literal("also-awesome2"),
55
+ config: { allowAnonymous: true }
56
+ })
57
+ ))
58
+ export const badImpl = Effect
59
+ .gen(function*() {
60
+ const impl = UserRpcsBad
61
+ .toLayerDynamic({
62
+ doSomethingElse: Effect.fn(function*() {
63
+ console.log(yield* UserProfile) // bad boy! allowAnonymous: false, so `UserProfile` must fall through to the Layer R.
64
+ return "also-awesome2" as const
65
+ })
66
+ })
67
+ return impl
68
+ })
69
+ .pipe(Layer.unwrapEffect)
70
+
71
+ expectTypeOf<Layer.Layer.Context<typeof badImpl>>().toEqualTypeOf<UserProfile>()
72
+
73
+ const middlwareLayer = middleware
74
+ .layer
75
+ .pipe(
76
+ Layer.provide([
77
+ DefaultGenericMiddlewaresLive,
78
+ SomeElseMiddlewareLive,
79
+ SomeMiddlewareWrapLive,
80
+ TestLive,
81
+ RequireRolesLive.pipe(Layer.provide(SomeService.toLayer())),
82
+ AllowAnonymousLive
83
+ ])
84
+ )
85
+
86
+ export const RpcTestLayer = Layer
87
+ .mergeAll(
88
+ impl,
89
+ middlwareLayer
90
+ )
91
+
92
+ export const RpcRealLayer = Layer
93
+ .mergeAll(
94
+ HttpLayerRouter
95
+ .serve(
96
+ RpcServer
97
+ .layerHttpRouter({ group: UserRpcs, path: "/rpc", protocol: "http" })
98
+ .pipe(Layer.provide(impl))
99
+ .pipe(Layer.provide(middlwareLayer))
100
+ )
101
+ .pipe(Layer.provide(NodeHttpServer.layer(() => createServer(), { port: 5918 }))),
102
+ RpcClient
103
+ .layerProtocolHttp({ url: "http://localhost:5918/rpc" })
104
+ .pipe(
105
+ Layer.provide(FetchHttpClient.layer)
106
+ )
107
+ )
108
+ .pipe(Layer.provide(RpcSerialization.layerJson))
109
+
110
+ it.scopedLive(
111
+ "require login",
112
+ Effect.fnUntraced(
113
+ function*() {
114
+ const userClient = yield* RpcTest.makeClient(UserRpcs) // RpcTest.makeClient(UserRpcs) // RpcClient.make(UserRpcs)
115
+
116
+ const user = yield* Effect.either(userClient.getUser().pipe(Effect.onExit((_) => Console.dir(_, { depth: 10 }))))
117
+ expect(user).toStrictEqual(Either.left(new NotLoggedInError("Not logged in")))
118
+ },
119
+ Effect.provide(RpcTestLayer)
120
+ )
121
+ )
122
+
123
+ it.scopedLive(
124
+ "allow anonymous, optional UserProfile",
125
+ Effect.fnUntraced(
126
+ function*() {
127
+ const userClient = yield* RpcTest.makeClient(UserRpcs) // RpcTest.makeClient(UserRpcs) // RpcClient.make(UserRpcs)
128
+
129
+ const user = yield* userClient.doSomething().pipe(Effect.onExit((_) => Console.dir(_, { depth: 10 })))
130
+ expect(user).toBe("also-awesome")
131
+ },
132
+ Effect.provide(RpcTestLayer)
133
+ )
134
+ )