@effect-app/infra 2.85.0 → 2.86.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 +6 -0
- package/dist/api/layerUtils.d.ts +3 -3
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/routing/middleware/ContextProvider.d.ts +2 -2
- package/dist/api/routing/middleware/ContextProvider.d.ts.map +1 -1
- package/dist/api/routing/middleware/DynamicMiddleware.d.ts +31 -17
- package/dist/api/routing/middleware/DynamicMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/DynamicMiddleware.js +1 -1
- package/dist/api/routing/middleware/dynamic-middleware.d.ts +2 -2
- package/dist/api/routing/middleware/dynamic-middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/dynamic-middleware.js +1 -1
- package/dist/api/routing/middleware/generic-middleware.d.ts +12 -0
- package/dist/api/routing/middleware/generic-middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/generic-middleware.js +1 -1
- package/dist/api/routing/middleware/middleware-api.d.ts +18 -7
- package/dist/api/routing/middleware/middleware-api.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware-api.js +19 -8
- package/dist/api/routing/middleware/middleware.d.ts +5 -4
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +6 -5
- package/dist/api/routing.d.ts +0 -1
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +2 -3
- package/package.json +1 -1
- package/src/api/layerUtils.ts +3 -3
- package/src/api/routing/middleware/ContextProvider.ts +5 -5
- package/src/api/routing/middleware/DynamicMiddleware.ts +39 -17
- package/src/api/routing/middleware/dynamic-middleware.ts +5 -3
- package/src/api/routing/middleware/generic-middleware.ts +11 -0
- package/src/api/routing/middleware/middleware-api.ts +109 -22
- package/src/api/routing/middleware/middleware.ts +13 -5
- package/src/api/routing.ts +1 -9
- package/test/contextProvider.test.ts +1 -2
- package/test/controller.test.ts +17 -101
- package/test/dist/contextProvider.test.d.ts.map +1 -1
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/fixtures.d.ts +144 -0
- package/test/dist/fixtures.d.ts.map +1 -0
- package/test/dist/fixtures.js +70 -0
- package/test/dist/layerUtils.test.d.ts.map +1 -0
- package/test/dist/query.test.d.ts.map +1 -1
- package/test/dist/requires.d.ts +21 -0
- package/test/dist/requires.d.ts.map +1 -0
- package/test/dist/requires.js +27 -0
- package/test/dist/requires.test.d.ts.map +1 -0
- package/test/fixtures.ts +85 -0
- package/test/layerUtils.test.ts +19 -0
- package/test/query.test.ts +2 -4
- package/test/requires.test.ts +75 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type RPCContextMap } from "effect-app/client";
|
|
2
|
+
import { type DynamicMiddlewareMaker, type GenericMiddlewareMaker, type makeMiddlewareBasic, type RequestContextMapProvider } from "../src/api/routing.js";
|
|
3
|
+
export interface MiddlewareM<RequestContext extends Record<string, RPCContextMap.Any>, Provided extends keyof RequestContext, Middlewares extends ReadonlyArray<GenericMiddlewareMaker>, DynamicMiddlewareProviders, MiddlewareR = never> {
|
|
4
|
+
middleware: <MW extends GenericMiddlewareMaker>(mw: MW) => DynamicMiddlewareMakerrsss<RequestContext, Provided, [
|
|
5
|
+
...Middlewares,
|
|
6
|
+
MW
|
|
7
|
+
], DynamicMiddlewareProviders, GenericMiddlewareMaker.ApplyServices<MW, MiddlewareR>>;
|
|
8
|
+
}
|
|
9
|
+
export interface Dynamic<RequestContext extends Record<string, RPCContextMap.Any>, Provided extends keyof RequestContext, Middlewares extends ReadonlyArray<GenericMiddlewareMaker>, DynamicMiddlewareProviders, out MiddlewareR> extends MiddlewareM<RequestContext, Provided, Middlewares, DynamicMiddlewareProviders, MiddlewareR> {
|
|
10
|
+
middleware: <MW extends DynamicMiddlewareMaker<RequestContext> | GenericMiddlewareMaker>(mw: MW) => MW extends DynamicMiddlewareMaker<RequestContext> ? DynamicMiddlewareMakerrsss<RequestContext, Provided | MW["dynamic"]["key"], Middlewares, DynamicMiddlewareProviders & {
|
|
11
|
+
[K in MW["dynamic"]["key"]]: MW;
|
|
12
|
+
}, GenericMiddlewareMaker.ApplyServices<MW, MiddlewareR>> : DynamicMiddlewareMakerrsss<RequestContext, Provided, [
|
|
13
|
+
...Middlewares,
|
|
14
|
+
MW
|
|
15
|
+
], DynamicMiddlewareProviders, GenericMiddlewareMaker.ApplyServices<MW, MiddlewareR>>;
|
|
16
|
+
}
|
|
17
|
+
type GetDynamicMiddleware<T, RequestContext extends Record<string, RPCContextMap.Any>> = T extends RequestContextMapProvider<RequestContext> ? T : never;
|
|
18
|
+
type DynamicMiddlewareMakerrsss<RequestContext extends Record<string, RPCContextMap.Any>, Provided extends keyof RequestContext = never, Middlewares extends ReadonlyArray<GenericMiddlewareMaker> = [], DynamicMiddlewareProviders = unknown, MiddlewareR = never> = keyof Omit<RequestContext, Provided> extends never ? [MiddlewareR] extends [never] ? ReturnType<typeof makeMiddlewareBasic<RequestContext, GetDynamicMiddleware<DynamicMiddlewareProviders, RequestContext>, Middlewares>> & MiddlewareM<RequestContext, Provided, Middlewares, DynamicMiddlewareProviders, MiddlewareR> : MiddlewareM<RequestContext, Provided, Middlewares, DynamicMiddlewareProviders, MiddlewareR> : Dynamic<RequestContext, Provided, Middlewares, DynamicMiddlewareProviders, MiddlewareR>;
|
|
19
|
+
export declare const makeNewMiddleware: <RequestContextMap extends Record<string, RPCContextMap.Any>>() => DynamicMiddlewareMakerrsss<RequestContextMap>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=requires.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requires.d.ts","sourceRoot":"","sources":["../requires.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Array, Either } from "effect-app";
|
|
2
|
+
import { makeMiddleware } from "../src/api/routing.js";
|
|
3
|
+
export const makeNewMiddleware = () => {
|
|
4
|
+
const make = makeMiddleware();
|
|
5
|
+
let capturedMiddlewares = [];
|
|
6
|
+
const it = {
|
|
7
|
+
middleware: (...middlewares) => {
|
|
8
|
+
for (const mw of middlewares) {
|
|
9
|
+
capturedMiddlewares = [mw, ...capturedMiddlewares];
|
|
10
|
+
if (mw.dynamic) {
|
|
11
|
+
console.log("Adding dynamic middleware", mw.key, mw.dynamic.key);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
console.log("Adding generic middleware", mw.key);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const [genericMiddlewares, dyn] = Array.partitionMap(capturedMiddlewares, (mw) => "dynamic" in mw && mw.dynamic
|
|
18
|
+
? Either.right(mw)
|
|
19
|
+
: Either.left(mw));
|
|
20
|
+
const dynamicMiddlewares = dyn.reduce((prev, cur) => ({ ...prev, [cur.dynamic.key]: cur }), {});
|
|
21
|
+
// TODO: support dynamic and generic intertwined. treat them as one
|
|
22
|
+
return Object.assign(make({ genericMiddlewares, dynamicMiddlewares }), it);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
return it;
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVxdWlyZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9yZXF1aXJlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUUxQyxPQUFPLEVBQTRELGNBQWMsRUFBNEQsTUFBTSx1QkFBdUIsQ0FBQTtBQWtIMUssTUFBTSxDQUFDLE1BQU0saUJBQWlCLEdBRXlCLEdBQUcsRUFBRTtJQUMxRCxNQUFNLElBQUksR0FBRyxjQUFjLEVBQU8sQ0FBQTtJQUNsQyxJQUFJLG1CQUFtQixHQUE2RCxFQUFFLENBQUE7SUFDdEYsTUFBTSxFQUFFLEdBQUc7UUFDVCxVQUFVLEVBQUUsQ0FBQyxHQUFHLFdBQWtCLEVBQUUsRUFBRTtZQUNwQyxLQUFLLE1BQU0sRUFBRSxJQUFJLFdBQVcsRUFBRSxDQUFDO2dCQUM3QixtQkFBbUIsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLG1CQUFtQixDQUFDLENBQUE7Z0JBQ2xELElBQUksRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDO29CQUNmLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLEVBQUUsRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFBO2dCQUNsRSxDQUFDO3FCQUFNLENBQUM7b0JBQ04sT0FBTyxDQUFDLEdBQUcsQ0FBQywyQkFBMkIsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUE7Z0JBQ2xELENBQUM7WUFDSCxDQUFDO1lBQ0QsTUFBTSxDQUFDLGtCQUFrQixFQUFFLEdBQUcsQ0FBQyxHQUFHLEtBQUssQ0FBQyxZQUFZLENBQ2xELG1CQUFtQixFQUNuQixDQUFDLEVBQUUsRUFBRSxFQUFFLENBQ0wsU0FBUyxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsT0FBTztnQkFDM0IsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBaUMsQ0FBQztnQkFDakQsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBNEIsQ0FBQyxDQUNoRCxDQUFBO1lBQ0QsTUFBTSxrQkFBa0IsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUNuQyxDQUFDLElBQUksRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxHQUFHLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFDcEQsRUFBeUIsQ0FDMUIsQ0FBQTtZQUNELG1FQUFtRTtZQUNuRSxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsa0JBQWtCLEVBQUUsa0JBQWtCLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFBO1FBQzVFLENBQUM7S0FDRixDQUFBO0lBQ0QsT0FBTyxFQUFTLENBQUE7QUFDbEIsQ0FBQyxDQUFBIn0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requires.test.d.ts","sourceRoot":"","sources":["../requires.test.ts"],"names":[],"mappings":"AACA,OAAO,EAAU,KAAK,EAAK,MAAM,YAAY,CAAA;AAC7C,OAAO,EAAqB,UAAU,EAAE,MAAM,uBAAuB,CAAA;AACrE,OAAO,EAAwD,IAAI,EAAE,QAAQ,EAAQ,MAAM,eAAe,CAAA;;;;;;;AAE1G,qBAAa,cAAe,SAAQ,mBAYlC;CACD;;;;;;;AAED,qBAAa,kBAAmB,SAAQ,uBAYtC;CACD;;;;;;;AAED,qBAAa,sBAAuB,SAAQ,2BAa1C;CACD"}
|
package/test/fixtures.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Context, Effect, Option, S, Scope } from "effect-app"
|
|
2
|
+
import { NotLoggedInError, type RPCContextMap, UnauthorizedError } from "effect-app/client"
|
|
3
|
+
import { TaggedError } from "effect-app/Schema"
|
|
4
|
+
import { contextMap, Middleware } from "../src/api/routing.js"
|
|
5
|
+
|
|
6
|
+
export class UserProfile extends Context.assignTag<UserProfile, UserProfile>("UserProfile")(
|
|
7
|
+
S.Class<UserProfile>("UserProfile")({
|
|
8
|
+
id: S.String,
|
|
9
|
+
roles: S.Array(S.String)
|
|
10
|
+
})
|
|
11
|
+
) {
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class Some extends Context.TagMakeId("Some", Effect.succeed({ a: 1 }))<Some>() {}
|
|
15
|
+
export class SomeElse extends Context.TagMakeId("SomeElse", Effect.succeed({ b: 2 }))<SomeElse>() {}
|
|
16
|
+
|
|
17
|
+
export type RequestContextMap = {
|
|
18
|
+
allowAnonymous: RPCContextMap.Inverted<UserProfile, typeof NotLoggedInError>
|
|
19
|
+
requireRoles: RPCContextMap.Custom<never, typeof UnauthorizedError, Array<string>>
|
|
20
|
+
test: RPCContextMap<never, typeof S.Never>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class AllowAnonymous extends Middleware.Tag<AllowAnonymous>()("AllowAnonymous", {
|
|
24
|
+
dynamic: contextMap<RequestContextMap>()("allowAnonymous"),
|
|
25
|
+
requires: SomeElse
|
|
26
|
+
})({
|
|
27
|
+
effect: Effect.gen(function*() {
|
|
28
|
+
return Effect.fnUntraced(
|
|
29
|
+
function*({ config, headers }) {
|
|
30
|
+
yield* SomeElse
|
|
31
|
+
yield* Scope.Scope // provided by HttpRouter.HttpRouter.Provided
|
|
32
|
+
const isLoggedIn = !!headers["x-user"]
|
|
33
|
+
if (!isLoggedIn) {
|
|
34
|
+
if (!config.allowAnonymous) {
|
|
35
|
+
return yield* new NotLoggedInError({ message: "Not logged in" })
|
|
36
|
+
}
|
|
37
|
+
return Option.none()
|
|
38
|
+
}
|
|
39
|
+
return Option.some(Context.make(
|
|
40
|
+
UserProfile,
|
|
41
|
+
{ id: "whatever", roles: ["user", "manager"] }
|
|
42
|
+
))
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
}) {
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
50
|
+
export class RequireRoles extends Middleware.Tag<RequireRoles>()("RequireRoles", {
|
|
51
|
+
dynamic: contextMap<RequestContextMap>()("requireRoles"),
|
|
52
|
+
// had to move this in here, because once you put it manually as a readonly static property on the class,
|
|
53
|
+
// there's a weird issue where the fluent api stops behaving properly after adding this middleware via `addDynamicMiddleware`
|
|
54
|
+
dependsOn: [AllowAnonymous]
|
|
55
|
+
})({
|
|
56
|
+
effect: Effect.gen(function*() {
|
|
57
|
+
yield* Some
|
|
58
|
+
return Effect.fnUntraced(
|
|
59
|
+
function*({ config }) {
|
|
60
|
+
// we don't know if the service will be provided or not, so we use option..
|
|
61
|
+
const userProfile = yield* Effect.serviceOption(UserProfile)
|
|
62
|
+
const { requireRoles } = config
|
|
63
|
+
if (requireRoles && !userProfile.value?.roles?.some((role) => requireRoles.includes(role))) {
|
|
64
|
+
return yield* new UnauthorizedError({ message: "don't have the right roles" })
|
|
65
|
+
}
|
|
66
|
+
return Option.none<Context<never>>()
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
})
|
|
70
|
+
}) {
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class Test extends Middleware.Tag<Test>()("Test", { dynamic: contextMap<RequestContextMap>()("test") })({
|
|
74
|
+
effect: Effect.gen(function*() {
|
|
75
|
+
return Effect.fn(function*() {
|
|
76
|
+
return Option.none<Context<never>>()
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
}) {}
|
|
80
|
+
|
|
81
|
+
export class CustomError1 extends TaggedError<NotLoggedInError>()("CustomError1", {}) {}
|
|
82
|
+
export class CustomError2 extends TaggedError<NotLoggedInError>()("CustomError1", {}) {}
|
|
83
|
+
|
|
84
|
+
const MakeSomeService = Effect.succeed({ a: 1 })
|
|
85
|
+
export class SomeService extends Context.TagMakeId("SomeService", MakeSomeService)<SomeService>() {}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { it } from "@effect/vitest"
|
|
2
|
+
import { type Layer } from "effect"
|
|
3
|
+
import { expectTypeOf } from "vitest/index.cjs"
|
|
4
|
+
import { type LayerUtils } from "../src/api/layerUtils.js"
|
|
5
|
+
|
|
6
|
+
it("works", () => {
|
|
7
|
+
type B = (Layer.Layer<void, "error-a", "a"> | Layer.Layer<void, "error-b", "b">)[]
|
|
8
|
+
type C = LayerUtils.GetLayersContext<B>
|
|
9
|
+
type CE = LayerUtils.GetLayersError<B>
|
|
10
|
+
|
|
11
|
+
expectTypeOf({} as C).toEqualTypeOf<"a" | "b">()
|
|
12
|
+
expectTypeOf({} as CE).toEqualTypeOf<"error-a" | "error-b">()
|
|
13
|
+
|
|
14
|
+
type B2 = [Layer.Layer<void, "error-a", "a">, Layer.Layer<void, "error-b", "b"> | Layer.Layer<void, "error-c", "c">]
|
|
15
|
+
type C2 = LayerUtils.GetLayersContext<B2>
|
|
16
|
+
type CE2 = LayerUtils.GetLayersError<B2>
|
|
17
|
+
expectTypeOf({} as C2).toEqualTypeOf<"a" | "b" | "c">()
|
|
18
|
+
expectTypeOf({} as CE2).toEqualTypeOf<"error-a" | "error-b" | "error-c">()
|
|
19
|
+
})
|
package/test/query.test.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/* eslint-disable unused-imports/no-unused-vars */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
|
-
import {
|
|
4
|
+
import { Effect, flow, Layer, Option, pipe, S, Struct } from "effect-app"
|
|
5
5
|
import { inspect } from "util"
|
|
6
6
|
import { expect, expectTypeOf, it } from "vitest"
|
|
7
7
|
import { setupRequestContextFromCurrent } from "../src/api/setupRequest.js"
|
|
8
8
|
import { and, count, make, one, or, order, page, project, type QueryEnd, type QueryProjection, type QueryWhere, toFilter, where } from "../src/Model/query.js"
|
|
9
9
|
import { makeRepo } from "../src/Model/Repository.js"
|
|
10
10
|
import { memFilter, MemoryStoreLive } from "../src/Store/Memory.js"
|
|
11
|
+
import { SomeService } from "./fixtures.js"
|
|
11
12
|
|
|
12
13
|
const str = S.Struct({ _tag: S.Literal("string"), value: S.String })
|
|
13
14
|
const num = S.Struct({ _tag: S.Literal("number"), value: S.Number })
|
|
@@ -25,9 +26,6 @@ export declare namespace Something {
|
|
|
25
26
|
export interface Encoded extends S.Schema.Encoded<typeof Something> {}
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
const MakeSomeService = Effect.succeed({ a: 1 })
|
|
29
|
-
export class SomeService extends Context.TagMakeId("SomeService", MakeSomeService)<SomeService>() {}
|
|
30
|
-
|
|
31
29
|
const q = make<Something.Encoded>()
|
|
32
30
|
.pipe( // provided automatically inside Repo.q2()
|
|
33
31
|
where("displayName", "Verona"),
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { expectTypeOf, it } from "@effect/vitest"
|
|
2
|
+
import { Effect, Layer, S } from "effect-app"
|
|
3
|
+
import { makeNewMiddleware, Middleware } from "../src/api/routing.js"
|
|
4
|
+
import { AllowAnonymous, type RequestContextMap, RequireRoles, Some, SomeElse, Test } from "./fixtures.js"
|
|
5
|
+
|
|
6
|
+
export class SomeMiddleware extends Middleware.Tag<SomeMiddleware>()("SomeMiddleware", {
|
|
7
|
+
provides: Some,
|
|
8
|
+
wrap: true
|
|
9
|
+
})({
|
|
10
|
+
effect: Effect.gen(function*() {
|
|
11
|
+
// yield* Effect.context<"test-dep">()
|
|
12
|
+
return ({ next }) =>
|
|
13
|
+
Effect.gen(function*() {
|
|
14
|
+
// yield* Effect.context<"test-dep2">()
|
|
15
|
+
return yield* next.pipe(Effect.provideService(Some, new Some({ a: 1 })))
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
}) {
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class SomeElseMiddleware extends Middleware.Tag<SomeElseMiddleware>()("SomeElseMiddleware", {
|
|
22
|
+
provides: SomeElse,
|
|
23
|
+
wrap: true
|
|
24
|
+
})({
|
|
25
|
+
effect: Effect.gen(function*() {
|
|
26
|
+
// yield* Effect.context<"test-dep">()
|
|
27
|
+
return ({ next }) =>
|
|
28
|
+
Effect.gen(function*() {
|
|
29
|
+
// yield* Effect.context<"test-dep2">()
|
|
30
|
+
return yield* next.pipe(Effect.provideService(SomeElse, new SomeElse({ b: 2 })))
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
}) {
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class RequiresSomeMiddleware extends Middleware.Tag<RequiresSomeMiddleware>()("RequiresSomeMiddleware", {
|
|
37
|
+
requires: Some,
|
|
38
|
+
wrap: true
|
|
39
|
+
})({
|
|
40
|
+
effect: Effect.gen(function*() {
|
|
41
|
+
// yield* Effect.context<"test-dep">()
|
|
42
|
+
return ({ next }) =>
|
|
43
|
+
Effect.gen(function*() {
|
|
44
|
+
yield* Some
|
|
45
|
+
// yield* Effect.context<"test-dep2">()
|
|
46
|
+
return yield* next
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
}) {
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
it("requires gets enforced", async () => {
|
|
53
|
+
const middleware3 = makeNewMiddleware<RequestContextMap>()
|
|
54
|
+
.middleware(RequiresSomeMiddleware)
|
|
55
|
+
.middleware(SomeMiddleware)
|
|
56
|
+
.middleware(AllowAnonymous, RequireRoles)
|
|
57
|
+
.middleware(SomeElseMiddleware)
|
|
58
|
+
.middleware(Test)
|
|
59
|
+
|
|
60
|
+
type LayerContext = Layer.Layer.Context<typeof middleware3["Default"]>
|
|
61
|
+
expectTypeOf({} as LayerContext).toEqualTypeOf<Some>()
|
|
62
|
+
|
|
63
|
+
await Effect
|
|
64
|
+
.gen(function*() {
|
|
65
|
+
const mw = yield* middleware3
|
|
66
|
+
const mwM = mw.effect(Object.assign({}, S.Any, { config: {} }), (_req) => Effect.void, "some-module")
|
|
67
|
+
yield* mwM({}, { "x-user": "test-user" })
|
|
68
|
+
// console.log({ v })
|
|
69
|
+
})
|
|
70
|
+
.pipe(
|
|
71
|
+
Effect.scoped,
|
|
72
|
+
Effect.provide(middleware3.Default.pipe(Layer.provide(Layer.succeed(Some, new Some({ a: 1 }))))),
|
|
73
|
+
Effect.runPromise
|
|
74
|
+
)
|
|
75
|
+
})
|