@effect-app/infra 4.0.0-beta.12 → 4.0.0-beta.121
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 +802 -0
- package/dist/CUPS.d.ts +3 -3
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +3 -3
- package/dist/Emailer/service.d.ts +3 -3
- package/dist/Emailer/service.d.ts.map +1 -1
- package/dist/Emailer/service.js +3 -3
- package/dist/MainFiberSet.d.ts +2 -2
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +3 -3
- package/dist/Model/Repository/Registry.d.ts +20 -0
- package/dist/Model/Repository/Registry.d.ts.map +1 -0
- package/dist/Model/Repository/Registry.js +17 -0
- package/dist/Model/Repository/internal/internal.d.ts +3 -3
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +22 -16
- package/dist/Model/Repository/makeRepo.d.ts +5 -4
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +4 -1
- package/dist/Model/Repository/service.d.ts +5 -0
- package/dist/Model/Repository/service.d.ts.map +1 -1
- package/dist/Model/Repository/validation.d.ts +7 -6
- package/dist/Model/Repository/validation.d.ts.map +1 -1
- package/dist/Model/Repository.d.ts +1 -0
- package/dist/Model/Repository.d.ts.map +1 -1
- package/dist/Model/Repository.js +2 -1
- package/dist/Model/query/dsl.d.ts +9 -9
- package/dist/Model/query/new-kid-interpreter.d.ts +2 -2
- package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
- package/dist/Model/query/new-kid-interpreter.js +3 -3
- package/dist/Model.d.ts +1 -0
- package/dist/Model.d.ts.map +1 -1
- package/dist/Model.js +2 -1
- package/dist/Operations.d.ts +2 -2
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Operations.js +3 -3
- package/dist/OperationsRepo.d.ts +3 -3
- package/dist/OperationsRepo.d.ts.map +1 -1
- package/dist/OperationsRepo.js +3 -3
- package/dist/QueueMaker/SQLQueue.d.ts +2 -4
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +8 -6
- package/dist/QueueMaker/errors.d.ts +1 -1
- package/dist/QueueMaker/errors.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +3 -3
- package/dist/QueueMaker/sbqueue.js +3 -3
- package/dist/RequestContext.d.ts +22 -17
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +5 -5
- package/dist/RequestFiberSet.d.ts +2 -2
- package/dist/RequestFiberSet.d.ts.map +1 -1
- package/dist/RequestFiberSet.js +5 -5
- package/dist/Store/ContextMapContainer.d.ts +19 -3
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +13 -3
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +136 -68
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +24 -21
- package/dist/Store/Memory.d.ts +2 -2
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +26 -21
- package/dist/Store/SQL/Pg.d.ts +4 -0
- package/dist/Store/SQL/Pg.d.ts.map +1 -0
- package/dist/Store/SQL/Pg.js +191 -0
- package/dist/Store/SQL/query.d.ts +38 -0
- package/dist/Store/SQL/query.d.ts.map +1 -0
- package/dist/Store/SQL/query.js +367 -0
- package/dist/Store/SQL.d.ts +20 -0
- package/dist/Store/SQL.d.ts.map +1 -0
- package/dist/Store/SQL.js +381 -0
- package/dist/Store/index.d.ts +4 -1
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +15 -3
- package/dist/Store/service.d.ts +16 -5
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +24 -6
- package/dist/adapters/ServiceBus.d.ts +6 -6
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +9 -9
- package/dist/adapters/cosmos-client.d.ts +2 -2
- package/dist/adapters/cosmos-client.d.ts.map +1 -1
- package/dist/adapters/cosmos-client.js +3 -3
- package/dist/adapters/logger.d.ts.map +1 -1
- package/dist/adapters/memQueue.d.ts +2 -2
- package/dist/adapters/memQueue.d.ts.map +1 -1
- package/dist/adapters/memQueue.js +3 -3
- package/dist/adapters/mongo-client.d.ts +2 -2
- package/dist/adapters/mongo-client.d.ts.map +1 -1
- package/dist/adapters/mongo-client.js +3 -3
- package/dist/adapters/redis-client.d.ts +3 -3
- package/dist/adapters/redis-client.d.ts.map +1 -1
- package/dist/adapters/redis-client.js +3 -3
- package/dist/api/ContextProvider.d.ts +6 -6
- package/dist/api/ContextProvider.d.ts.map +1 -1
- package/dist/api/ContextProvider.js +6 -6
- package/dist/api/internal/auth.d.ts +1 -1
- package/dist/api/internal/events.d.ts +2 -2
- package/dist/api/internal/events.d.ts.map +1 -1
- package/dist/api/internal/events.js +11 -7
- package/dist/api/layerUtils.d.ts +5 -5
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +5 -5
- package/dist/api/routing/middleware/RouterMiddleware.d.ts +3 -3
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.d.ts +35 -1
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +39 -1
- package/dist/api/routing.d.ts +1 -5
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +3 -2
- package/dist/api/setupRequest.d.ts +6 -3
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +11 -6
- package/dist/logger.d.ts.map +1 -1
- package/examples/query.ts +30 -26
- package/package.json +36 -18
- package/src/CUPS.ts +2 -2
- package/src/Emailer/service.ts +2 -2
- package/src/MainFiberSet.ts +2 -2
- package/src/Model/Repository/Registry.ts +33 -0
- package/src/Model/Repository/internal/internal.ts +76 -59
- package/src/Model/Repository/makeRepo.ts +7 -4
- package/src/Model/Repository/service.ts +6 -0
- package/src/Model/Repository.ts +1 -0
- package/src/Model/query/new-kid-interpreter.ts +2 -2
- package/src/Model.ts +1 -0
- package/src/Operations.ts +2 -2
- package/src/OperationsRepo.ts +2 -2
- package/src/QueueMaker/SQLQueue.ts +8 -7
- package/src/QueueMaker/memQueue.ts +2 -2
- package/src/QueueMaker/sbqueue.ts +2 -2
- package/src/RequestContext.ts +4 -4
- package/src/RequestFiberSet.ts +4 -4
- package/src/Store/ContextMapContainer.ts +41 -2
- package/src/Store/Cosmos.ts +350 -255
- package/src/Store/Disk.ts +37 -33
- package/src/Store/Memory.ts +29 -22
- package/src/Store/SQL/Pg.ts +321 -0
- package/src/Store/SQL/query.ts +409 -0
- package/src/Store/SQL.ts +674 -0
- package/src/Store/index.ts +17 -2
- package/src/Store/service.ts +31 -7
- package/src/adapters/ServiceBus.ts +8 -8
- package/src/adapters/cosmos-client.ts +2 -2
- package/src/adapters/memQueue.ts +2 -2
- package/src/adapters/mongo-client.ts +2 -2
- package/src/adapters/redis-client.ts +2 -2
- package/src/api/ContextProvider.ts +11 -11
- package/src/api/internal/events.ts +14 -9
- package/src/api/layerUtils.ts +8 -8
- package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
- package/src/api/routing/middleware/middleware.ts +43 -0
- package/src/api/routing.ts +3 -3
- package/src/api/setupRequest.ts +27 -7
- package/test/contextProvider.test.ts +11 -11
- package/test/controller.test.ts +12 -9
- package/test/dist/contextProvider.test.d.ts.map +1 -1
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/date-query.test.d.ts.map +1 -0
- package/test/dist/fixtures.d.ts +20 -10
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +11 -9
- package/test/dist/query.test.d.ts.map +1 -1
- package/test/dist/rawQuery.test.d.ts.map +1 -1
- package/test/dist/requires.test.d.ts.map +1 -1
- package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
- package/test/dist/sql-store.test.d.ts.map +1 -0
- package/test/fixtures.ts +10 -8
- package/test/query.test.ts +209 -33
- package/test/rawQuery.test.ts +22 -18
- package/test/requires.test.ts +6 -5
- package/test/rpc-multi-middleware.test.ts +72 -3
- package/test/sql-store.test.ts +1064 -0
- package/test/validateSample.test.ts +12 -9
- package/tsconfig.json +0 -1
package/test/query.test.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
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 { SchemaTransformation } from "effect"
|
|
5
|
+
import { Context, Effect, flow, Layer, Option, pipe, S, Struct } from "effect-app"
|
|
5
6
|
import { inspect } from "util"
|
|
6
7
|
import { expect, expectTypeOf, it } from "vitest"
|
|
7
8
|
import { setupRequestContextFromCurrent } from "../src/api/setupRequest.js"
|
|
8
9
|
import { and, count, make, one, or, order, page, project, type QueryEnd, type QueryProjection, type QueryWhere, toFilter, where } from "../src/Model/query.js"
|
|
9
10
|
import { makeRepo } from "../src/Model/Repository.js"
|
|
11
|
+
import { RepositoryRegistryLive } from "../src/Model/Repository/Registry.js"
|
|
10
12
|
import { memFilter, MemoryStoreLive } from "../src/Store/Memory.js"
|
|
11
13
|
import { SomeService } from "./fixtures.js"
|
|
12
14
|
|
|
15
|
+
const TestStoreLive = Layer.merge(MemoryStoreLive, RepositoryRegistryLive)
|
|
16
|
+
|
|
13
17
|
const str = S.Struct({ _tag: S.Literal("string"), value: S.String })
|
|
14
|
-
const num = S.Struct({ _tag: S.Literal("number"), value: S.
|
|
18
|
+
const num = S.Struct({ _tag: S.Literal("number"), value: S.Finite })
|
|
15
19
|
const someUnion = S.Union([str, num])
|
|
16
20
|
|
|
17
21
|
export class Something extends S.Class<Something>("Something")({
|
|
@@ -19,7 +23,7 @@ export class Something extends S.Class<Something>("Something")({
|
|
|
19
23
|
displayName: S.NonEmptyString255,
|
|
20
24
|
name: S.NullOr(S.NonEmptyString255).withDefault,
|
|
21
25
|
n: S.Date.withDefault,
|
|
22
|
-
union: someUnion.pipe(S.
|
|
26
|
+
union: someUnion.pipe(S.withConstructorDefault(Effect.succeed({ _tag: "string" as const, value: "hi" })))
|
|
23
27
|
}) {}
|
|
24
28
|
export declare namespace Something {
|
|
25
29
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
@@ -90,8 +94,8 @@ it("works", () => {
|
|
|
90
94
|
|
|
91
95
|
const processed = memFilter(interpreted)(items.map((_) =>
|
|
92
96
|
S.encodeUnknownSync(S.Struct({
|
|
93
|
-
...
|
|
94
|
-
displayName: S.
|
|
97
|
+
...Struct.omit(Something.fields, ["displayName"]),
|
|
98
|
+
displayName: S.Literals(["Verona", "Riley"])
|
|
95
99
|
}))(_)
|
|
96
100
|
))
|
|
97
101
|
|
|
@@ -99,7 +103,7 @@ it("works", () => {
|
|
|
99
103
|
})
|
|
100
104
|
|
|
101
105
|
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
102
|
-
class SomethingRepo extends
|
|
106
|
+
class SomethingRepo extends Context.Service<SomethingRepo>()("SomethingRepo", {
|
|
103
107
|
make: Effect.gen(function*() {
|
|
104
108
|
return yield* makeRepo("Something", Something, {})
|
|
105
109
|
})
|
|
@@ -112,7 +116,7 @@ class SomethingRepo extends ServiceMap.Service<SomethingRepo>()("SomethingRepo",
|
|
|
112
116
|
})
|
|
113
117
|
)
|
|
114
118
|
.pipe(
|
|
115
|
-
Layer.provide(
|
|
119
|
+
Layer.provide(TestStoreLive)
|
|
116
120
|
)
|
|
117
121
|
}
|
|
118
122
|
|
|
@@ -156,8 +160,6 @@ it("works with repo", () =>
|
|
|
156
160
|
|
|
157
161
|
expectTypeOf(smtArr).toEqualTypeOf<readonly Something[]>()
|
|
158
162
|
|
|
159
|
-
console.log(" $$$$$$")
|
|
160
|
-
console.log(Struct.pick(["id", "displayName"]))
|
|
161
163
|
expect(q1).toEqual(items.slice(0, 2).toReversed().map(Struct.pick(["id", "displayName"])))
|
|
162
164
|
expect(q2).toEqual(items.slice(0, 2).toReversed().map(Struct.pick(["displayName"])))
|
|
163
165
|
})
|
|
@@ -281,7 +283,7 @@ it(
|
|
|
281
283
|
expect(result).toEqual([])
|
|
282
284
|
expect(result2).toEqual([])
|
|
283
285
|
})
|
|
284
|
-
.pipe(Effect.provide(
|
|
286
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
|
|
285
287
|
)
|
|
286
288
|
|
|
287
289
|
it(
|
|
@@ -467,7 +469,7 @@ it(
|
|
|
467
469
|
|
|
468
470
|
expect([]).toEqual([])
|
|
469
471
|
})
|
|
470
|
-
.pipe(Effect.provide(
|
|
472
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
|
|
471
473
|
)
|
|
472
474
|
|
|
473
475
|
it(
|
|
@@ -510,7 +512,7 @@ it(
|
|
|
510
512
|
|
|
511
513
|
expect([]).toEqual([])
|
|
512
514
|
})
|
|
513
|
-
.pipe(Effect.provide(
|
|
515
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
|
|
514
516
|
)
|
|
515
517
|
|
|
516
518
|
it(
|
|
@@ -521,8 +523,8 @@ it(
|
|
|
521
523
|
const schema = S.Struct({
|
|
522
524
|
id: S.String,
|
|
523
525
|
createdAt: S.Date.pipe(
|
|
524
|
-
S.withDecodingDefault(() => new Date().toISOString()),
|
|
525
|
-
S.withConstructorDefault(() =>
|
|
526
|
+
S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
|
|
527
|
+
S.withConstructorDefault(Effect.sync(() => new Date()))
|
|
526
528
|
)
|
|
527
529
|
})
|
|
528
530
|
const repo = yield* makeRepo(
|
|
@@ -534,8 +536,8 @@ it(
|
|
|
534
536
|
const outputSchema = S.Struct({
|
|
535
537
|
id: S.Literal("123"),
|
|
536
538
|
createdAt: S.Date.pipe(
|
|
537
|
-
S.withDecodingDefault(() => new Date().toISOString()),
|
|
538
|
-
S.withConstructorDefault(() =>
|
|
539
|
+
S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
|
|
540
|
+
S.withConstructorDefault(Effect.sync(() => new Date()))
|
|
539
541
|
)
|
|
540
542
|
})
|
|
541
543
|
|
|
@@ -543,7 +545,34 @@ it(
|
|
|
543
545
|
|
|
544
546
|
expect(result).toEqual([])
|
|
545
547
|
})
|
|
546
|
-
.pipe(Effect.provide(
|
|
548
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
it(
|
|
552
|
+
"project with encodeKeys in projection maps encoded keys",
|
|
553
|
+
() =>
|
|
554
|
+
Effect
|
|
555
|
+
.gen(function*() {
|
|
556
|
+
const schema = S.Struct({
|
|
557
|
+
id: S.String,
|
|
558
|
+
a: S.Number
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
const repo = yield* makeRepo(
|
|
562
|
+
"test",
|
|
563
|
+
schema,
|
|
564
|
+
{
|
|
565
|
+
makeInitial: Effect.sync(() => [{ id: "1", a: 1 }])
|
|
566
|
+
}
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
const outputSchema = S.Struct({ b: S.Number }).pipe(S.encodeKeys({ b: "a" }))
|
|
570
|
+
|
|
571
|
+
const result = yield* repo.query(project(outputSchema))
|
|
572
|
+
|
|
573
|
+
expect(result).toStrictEqual([{ b: 1 }])
|
|
574
|
+
})
|
|
575
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
|
|
547
576
|
)
|
|
548
577
|
|
|
549
578
|
it(
|
|
@@ -553,7 +582,7 @@ it(
|
|
|
553
582
|
.gen(function*() {
|
|
554
583
|
const schema = S.Struct({
|
|
555
584
|
id: S.String,
|
|
556
|
-
literals: S.
|
|
585
|
+
literals: S.Literals(["a", "b", "c"])
|
|
557
586
|
})
|
|
558
587
|
|
|
559
588
|
type Schema = typeof schema.Type
|
|
@@ -573,7 +602,7 @@ it(
|
|
|
573
602
|
|
|
574
603
|
expect(result).toEqual([])
|
|
575
604
|
})
|
|
576
|
-
.pipe(Effect.provide(
|
|
605
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
|
|
577
606
|
)
|
|
578
607
|
|
|
579
608
|
it(
|
|
@@ -583,7 +612,7 @@ it(
|
|
|
583
612
|
.gen(function*() {
|
|
584
613
|
const schema = S.Struct({
|
|
585
614
|
id: S.String,
|
|
586
|
-
literals: S.Union([S.
|
|
615
|
+
literals: S.Union([S.Literals(["a", "b", "c"]), S.Null])
|
|
587
616
|
})
|
|
588
617
|
|
|
589
618
|
type Schema = typeof schema.Type
|
|
@@ -617,7 +646,7 @@ it(
|
|
|
617
646
|
|
|
618
647
|
expect(result).toEqual([])
|
|
619
648
|
})
|
|
620
|
-
.pipe(Effect.provide(
|
|
649
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
|
|
621
650
|
)
|
|
622
651
|
|
|
623
652
|
it(
|
|
@@ -661,7 +690,7 @@ it(
|
|
|
661
690
|
|
|
662
691
|
expect(result).toEqual([])
|
|
663
692
|
})
|
|
664
|
-
.pipe(Effect.provide(
|
|
693
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
|
|
665
694
|
)
|
|
666
695
|
|
|
667
696
|
it("remove null from one constituent of a tagged union", () =>
|
|
@@ -674,7 +703,7 @@ it("remove null from one constituent of a tagged union", () =>
|
|
|
674
703
|
|
|
675
704
|
class BB extends S.Class<BB>("BB")({
|
|
676
705
|
id: S.Literal("BB"),
|
|
677
|
-
b: S.NullOr(S.
|
|
706
|
+
b: S.NullOr(S.Finite)
|
|
678
707
|
}) {}
|
|
679
708
|
|
|
680
709
|
type Union = AA | BB
|
|
@@ -710,7 +739,7 @@ it("remove null from one constituent of a tagged union", () =>
|
|
|
710
739
|
})[]
|
|
711
740
|
>()
|
|
712
741
|
})
|
|
713
|
-
.pipe(Effect.provide(
|
|
742
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
714
743
|
|
|
715
744
|
it("refine 3", () =>
|
|
716
745
|
Effect
|
|
@@ -748,7 +777,7 @@ it("refine 3", () =>
|
|
|
748
777
|
const resQuer1 = yield* repo.query(where("id", "AA"))
|
|
749
778
|
expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
|
|
750
779
|
})
|
|
751
|
-
.pipe(Effect.provide(
|
|
780
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
752
781
|
|
|
753
782
|
it("my test", () =>
|
|
754
783
|
Effect
|
|
@@ -766,7 +795,7 @@ it("my test", () =>
|
|
|
766
795
|
)
|
|
767
796
|
expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
|
|
768
797
|
})
|
|
769
|
-
.pipe(Effect.provide(
|
|
798
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
770
799
|
|
|
771
800
|
it("refine inner without imposing a projection", () =>
|
|
772
801
|
Effect
|
|
@@ -810,7 +839,7 @@ it("refine inner without imposing a projection", () =>
|
|
|
810
839
|
where("union._tag", "AA"),
|
|
811
840
|
// But if I wanna the whole Data as output ignoring the inner refinement
|
|
812
841
|
// I wanna be able to do so
|
|
813
|
-
project(
|
|
842
|
+
project(Data.mapFields(Struct.pick(["union"])))
|
|
814
843
|
)
|
|
815
844
|
|
|
816
845
|
expectTypeOf(query2).toEqualTypeOf<
|
|
@@ -841,7 +870,7 @@ it("refine inner without imposing a projection", () =>
|
|
|
841
870
|
}[]
|
|
842
871
|
>()
|
|
843
872
|
})
|
|
844
|
-
.pipe(Effect.provide(
|
|
873
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
845
874
|
|
|
846
875
|
it("does not allow string queries on arrays", () =>
|
|
847
876
|
Effect
|
|
@@ -876,7 +905,7 @@ it("does not allow string queries on arrays", () =>
|
|
|
876
905
|
expectTypeOf(good3).toEqualTypeOf<QueryWhere<Some, Some>>()
|
|
877
906
|
expectTypeOf(good4).toEqualTypeOf<QueryWhere<Some, Some>>()
|
|
878
907
|
})
|
|
879
|
-
.pipe(Effect.provide(
|
|
908
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
880
909
|
|
|
881
910
|
it("test array.length", () =>
|
|
882
911
|
Effect
|
|
@@ -917,7 +946,7 @@ it("test array.length", () =>
|
|
|
917
946
|
QueryWhere<Something, Something>
|
|
918
947
|
>()
|
|
919
948
|
})
|
|
920
|
-
.pipe(Effect.provide(
|
|
949
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
921
950
|
|
|
922
951
|
it("distribution over union", () =>
|
|
923
952
|
Effect
|
|
@@ -941,7 +970,7 @@ it("distribution over union", () =>
|
|
|
941
970
|
})[]
|
|
942
971
|
>()
|
|
943
972
|
})
|
|
944
|
-
.pipe(Effect.provide(
|
|
973
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
945
974
|
|
|
946
975
|
it("refine nested union", () =>
|
|
947
976
|
Effect
|
|
@@ -982,7 +1011,154 @@ it("refine nested union", () =>
|
|
|
982
1011
|
}[]
|
|
983
1012
|
>()
|
|
984
1013
|
})
|
|
985
|
-
.pipe(Effect.provide(
|
|
1014
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
1015
|
+
|
|
1016
|
+
it("find with transformed id", () =>
|
|
1017
|
+
Effect
|
|
1018
|
+
.gen(function*() {
|
|
1019
|
+
const ConfiguratorId = S.NonEmptyString255
|
|
1020
|
+
|
|
1021
|
+
class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
|
|
1022
|
+
configuratorId: ConfiguratorId,
|
|
1023
|
+
label: S.NonEmptyString50
|
|
1024
|
+
}) {}
|
|
1025
|
+
|
|
1026
|
+
const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
|
|
1027
|
+
S.decodeTo(
|
|
1028
|
+
S.toType(PreconfigurationId),
|
|
1029
|
+
SchemaTransformation.transformOrFail({
|
|
1030
|
+
decode: Effect.fnUntraced(function*(value) {
|
|
1031
|
+
const values = value.split("_")
|
|
1032
|
+
const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
|
|
1033
|
+
const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(values.join("_"))
|
|
1034
|
+
return new PreconfigurationId({ configuratorId, label })
|
|
1035
|
+
}),
|
|
1036
|
+
encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
|
|
1037
|
+
})
|
|
1038
|
+
),
|
|
1039
|
+
S.revealCodec
|
|
1040
|
+
)
|
|
1041
|
+
|
|
1042
|
+
const Preconfiguration = S.Struct({
|
|
1043
|
+
id: PreconfigurationIdFromString,
|
|
1044
|
+
name: S.String
|
|
1045
|
+
})
|
|
1046
|
+
|
|
1047
|
+
const repo = yield* makeRepo("Preconfiguration", Preconfiguration, { idKey: "id" as const })
|
|
1048
|
+
|
|
1049
|
+
const id = new PreconfigurationId({
|
|
1050
|
+
configuratorId: S.NonEmptyString255("myConfigurator"),
|
|
1051
|
+
label: S.NonEmptyString50("myLabel")
|
|
1052
|
+
})
|
|
1053
|
+
const item = { id, name: "test preconfig" }
|
|
1054
|
+
|
|
1055
|
+
yield* repo.saveAndPublish([item])
|
|
1056
|
+
|
|
1057
|
+
const found = yield* repo.find(id)
|
|
1058
|
+
expect(Option.isSome(found)).toBe(true)
|
|
1059
|
+
expect(Option.getOrThrow(found).name).toBe("test preconfig")
|
|
1060
|
+
expect(Option.getOrThrow(found).id).toEqual(id)
|
|
1061
|
+
|
|
1062
|
+
const notFound = yield* repo.find(
|
|
1063
|
+
new PreconfigurationId({
|
|
1064
|
+
configuratorId: S.NonEmptyString255("other"),
|
|
1065
|
+
label: S.NonEmptyString50("nope")
|
|
1066
|
+
})
|
|
1067
|
+
)
|
|
1068
|
+
expect(Option.isNone(notFound)).toBe(true)
|
|
1069
|
+
})
|
|
1070
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
1071
|
+
|
|
1072
|
+
it("find with transformed id in tagged union", () =>
|
|
1073
|
+
Effect
|
|
1074
|
+
.gen(function*() {
|
|
1075
|
+
const ConfiguratorId = S.NonEmptyString255
|
|
1076
|
+
|
|
1077
|
+
class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
|
|
1078
|
+
configuratorId: ConfiguratorId,
|
|
1079
|
+
label: S.NonEmptyString50
|
|
1080
|
+
}) {}
|
|
1081
|
+
|
|
1082
|
+
const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
|
|
1083
|
+
S.decodeTo(
|
|
1084
|
+
S.toType(PreconfigurationId),
|
|
1085
|
+
SchemaTransformation.transformOrFail({
|
|
1086
|
+
decode: Effect.fnUntraced(function*(value) {
|
|
1087
|
+
const values = value.split("_")
|
|
1088
|
+
const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
|
|
1089
|
+
const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(values.join("_"))
|
|
1090
|
+
return new PreconfigurationId({ configuratorId, label })
|
|
1091
|
+
}),
|
|
1092
|
+
encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
|
|
1093
|
+
})
|
|
1094
|
+
),
|
|
1095
|
+
S.revealCodec
|
|
1096
|
+
)
|
|
1097
|
+
|
|
1098
|
+
class Draft extends S.TaggedClass<Draft>()("Draft", {
|
|
1099
|
+
id: PreconfigurationIdFromString,
|
|
1100
|
+
name: S.String
|
|
1101
|
+
}) {}
|
|
1102
|
+
|
|
1103
|
+
class Published extends S.TaggedClass<Published>()("Published", {
|
|
1104
|
+
id: PreconfigurationIdFromString,
|
|
1105
|
+
name: S.String,
|
|
1106
|
+
publishedAt: S.String
|
|
1107
|
+
}) {}
|
|
1108
|
+
|
|
1109
|
+
class Archived extends S.TaggedClass<Archived>()("Archived", {
|
|
1110
|
+
id: PreconfigurationIdFromString,
|
|
1111
|
+
name: S.String,
|
|
1112
|
+
archivedAt: S.String
|
|
1113
|
+
}) {}
|
|
1114
|
+
|
|
1115
|
+
const Preconfiguration = S.Union([Draft, Published, Archived])
|
|
1116
|
+
|
|
1117
|
+
const repo = yield* makeRepo("Preconfiguration", Preconfiguration, {})
|
|
1118
|
+
|
|
1119
|
+
const id1 = new PreconfigurationId({
|
|
1120
|
+
configuratorId: S.NonEmptyString255("conf1"),
|
|
1121
|
+
label: S.NonEmptyString50("draft1")
|
|
1122
|
+
})
|
|
1123
|
+
const id2 = new PreconfigurationId({
|
|
1124
|
+
configuratorId: S.NonEmptyString255("conf2"),
|
|
1125
|
+
label: S.NonEmptyString50("pub1")
|
|
1126
|
+
})
|
|
1127
|
+
const id3 = new PreconfigurationId({
|
|
1128
|
+
configuratorId: S.NonEmptyString255("conf3"),
|
|
1129
|
+
label: S.NonEmptyString50("arch1")
|
|
1130
|
+
})
|
|
1131
|
+
|
|
1132
|
+
const draft = new Draft({ id: id1, name: "my draft" })
|
|
1133
|
+
const published = new Published({ id: id2, name: "my published", publishedAt: "2024-01-01" })
|
|
1134
|
+
const archived = new Archived({ id: id3, name: "my archived", archivedAt: "2024-06-01" })
|
|
1135
|
+
|
|
1136
|
+
yield* repo.saveAndPublish([draft, published, archived])
|
|
1137
|
+
|
|
1138
|
+
// find each by their PreconfigurationId instance
|
|
1139
|
+
const foundDraft = yield* repo.find(id1)
|
|
1140
|
+
expect(Option.isSome(foundDraft)).toBe(true)
|
|
1141
|
+
expect(Option.getOrThrow(foundDraft)._tag).toBe("Draft")
|
|
1142
|
+
expect(Option.getOrThrow(foundDraft).name).toBe("my draft")
|
|
1143
|
+
|
|
1144
|
+
const foundPublished = yield* repo.find(id2)
|
|
1145
|
+
expect(Option.isSome(foundPublished)).toBe(true)
|
|
1146
|
+
expect(Option.getOrThrow(foundPublished)._tag).toBe("Published")
|
|
1147
|
+
|
|
1148
|
+
const foundArchived = yield* repo.find(id3)
|
|
1149
|
+
expect(Option.isSome(foundArchived)).toBe(true)
|
|
1150
|
+
expect(Option.getOrThrow(foundArchived)._tag).toBe("Archived")
|
|
1151
|
+
|
|
1152
|
+
// not found
|
|
1153
|
+
const notFound = yield* repo.find(
|
|
1154
|
+
new PreconfigurationId({
|
|
1155
|
+
configuratorId: S.NonEmptyString255("nope"),
|
|
1156
|
+
label: S.NonEmptyString50("nope")
|
|
1157
|
+
})
|
|
1158
|
+
)
|
|
1159
|
+
expect(Option.isNone(notFound)).toBe(true)
|
|
1160
|
+
})
|
|
1161
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
986
1162
|
|
|
987
1163
|
it("refine union with nested union", () =>
|
|
988
1164
|
Effect
|
|
@@ -1097,4 +1273,4 @@ it("refine union with nested union", () =>
|
|
|
1097
1273
|
})[]
|
|
1098
1274
|
>()
|
|
1099
1275
|
})
|
|
1100
|
-
.pipe(Effect.provide(
|
|
1276
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
package/test/rawQuery.test.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from "@effect/vitest"
|
|
2
|
-
import { Array, Config, Effect, flow, Layer, ManagedRuntime, Redacted, References, Result, S
|
|
2
|
+
import { Array, Config, Context, Effect, flow, Layer, ManagedRuntime, Redacted, References, Result, S } from "effect-app"
|
|
3
3
|
import { LogLevels } from "effect-app/utils"
|
|
4
4
|
import { setupRequestContextFromCurrent } from "../src/api/setupRequest.js"
|
|
5
5
|
import { and, or, project, where, whereEvery, whereSome } from "../src/Model/query.js"
|
|
6
6
|
import { makeRepo } from "../src/Model/Repository/makeRepo.js"
|
|
7
|
+
import { RepositoryRegistryLive } from "../src/Model/Repository/Registry.js"
|
|
7
8
|
import { CosmosStoreLayer } from "../src/Store/Cosmos.js"
|
|
8
9
|
import { MemoryStoreLive } from "../src/Store/Memory.js"
|
|
9
10
|
|
|
@@ -24,7 +25,7 @@ class Something extends S.Class<Something>("Something")({
|
|
|
24
25
|
id: S.String,
|
|
25
26
|
name: S.String,
|
|
26
27
|
description: S.String,
|
|
27
|
-
items: S.Array(S.Struct({ id: S.String, value: S.
|
|
28
|
+
items: S.Array(S.Struct({ id: S.String, value: S.Finite, description: S.String }))
|
|
28
29
|
}) {}
|
|
29
30
|
|
|
30
31
|
const items = [
|
|
@@ -49,7 +50,7 @@ const items = [
|
|
|
49
50
|
]
|
|
50
51
|
|
|
51
52
|
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
52
|
-
class SomethingRepo extends
|
|
53
|
+
class SomethingRepo extends Context.Service<SomethingRepo>()(
|
|
53
54
|
"SomethingRepo",
|
|
54
55
|
{
|
|
55
56
|
make: Effect.gen(function*() {
|
|
@@ -76,28 +77,31 @@ class SomethingRepo extends ServiceMap.Service<SomethingRepo>()(
|
|
|
76
77
|
static readonly Test = this
|
|
77
78
|
.layer
|
|
78
79
|
.pipe(
|
|
79
|
-
Layer.provide(MemoryStoreLive)
|
|
80
|
+
Layer.provide(Layer.merge(MemoryStoreLive, RepositoryRegistryLive))
|
|
80
81
|
)
|
|
81
82
|
|
|
82
83
|
static readonly TestCosmos = this
|
|
83
84
|
.layer
|
|
84
85
|
.pipe(
|
|
85
86
|
Layer.provide(
|
|
86
|
-
Effect
|
|
87
|
-
|
|
88
|
-
Config.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
Effect
|
|
88
|
+
.gen(function*() {
|
|
89
|
+
const url = yield* Config.redacted("STORAGE_URL").pipe(
|
|
90
|
+
Config.withDefault(
|
|
91
|
+
Redacted.make(
|
|
92
|
+
// the emulator doesn't implement array projections :/ so you need an actual cloud instance!
|
|
93
|
+
"AccountEndpoint=http://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
|
|
94
|
+
)
|
|
92
95
|
)
|
|
93
96
|
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
return CosmosStoreLayer({
|
|
98
|
+
dbName: "test",
|
|
99
|
+
prefix: "",
|
|
100
|
+
url
|
|
101
|
+
})
|
|
102
|
+
.pipe(Layer.merge(RepositoryRegistryLive))
|
|
99
103
|
})
|
|
100
|
-
|
|
104
|
+
.pipe(Layer.unwrap)
|
|
101
105
|
)
|
|
102
106
|
)
|
|
103
107
|
}
|
|
@@ -107,7 +111,7 @@ describe("select first-level array fields", () => {
|
|
|
107
111
|
.gen(function*() {
|
|
108
112
|
const repo = yield* SomethingRepo
|
|
109
113
|
|
|
110
|
-
const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.
|
|
114
|
+
const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.Finite })) })
|
|
111
115
|
|
|
112
116
|
// ok crazy lol, "value" is a reserved word in CosmosDB, so we have to use t["value"] as a field name instead of t.value
|
|
113
117
|
const items = yield* repo.queryRaw(projected, {
|
|
@@ -159,7 +163,7 @@ describe("select first-level array fields", () => {
|
|
|
159
163
|
.pipe(Effect.provide(SomethingRepo.Test), rt.runPromise))
|
|
160
164
|
})
|
|
161
165
|
|
|
162
|
-
const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.
|
|
166
|
+
const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.Finite })) })
|
|
163
167
|
|
|
164
168
|
const expected = [
|
|
165
169
|
{
|
package/test/requires.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, expectTypeOf, it } from "@effect/vitest"
|
|
2
|
-
import { Effect, Layer, Result, S
|
|
2
|
+
import { Context, Effect, Layer, Result, S } from "effect-app"
|
|
3
3
|
import { NotLoggedInError, UnauthorizedError } from "effect-app/client"
|
|
4
4
|
import { HttpHeaders } from "effect-app/http"
|
|
5
5
|
import * as RpcX from "effect-app/rpc"
|
|
@@ -63,11 +63,12 @@ const testSuite = (_mw: typeof middleware3) =>
|
|
|
63
63
|
"works",
|
|
64
64
|
Effect.fn(function*() {
|
|
65
65
|
const defaultOpts = {
|
|
66
|
+
client: null as any, // TODO?
|
|
66
67
|
headers: HttpHeaders.fromRecordUnsafe({}),
|
|
67
68
|
payload: { _tag: "Test" },
|
|
68
69
|
clientId: 0,
|
|
69
70
|
requestId: "test-id" as any,
|
|
70
|
-
rpc: { ...TestRpc, annotations:
|
|
71
|
+
rpc: { ...TestRpc, annotations: Context.make(_mw.requestContext, {}) }
|
|
71
72
|
}
|
|
72
73
|
const next = Effect.void as unknown as Effect.Effect<SuccessValue, unhandled, never>
|
|
73
74
|
const layer = _mw.layer.pipe(
|
|
@@ -89,7 +90,7 @@ const testSuite = (_mw: typeof middleware3) =>
|
|
|
89
90
|
headers: HttpHeaders.fromRecordUnsafe({ "x-user": "test-user", "x-is-manager": "true" }),
|
|
90
91
|
rpc: {
|
|
91
92
|
...defaultOpts.rpc,
|
|
92
|
-
annotations:
|
|
93
|
+
annotations: Context.make(_mw.requestContext, { requireRoles: ["manager"] })
|
|
93
94
|
}
|
|
94
95
|
})
|
|
95
96
|
)
|
|
@@ -127,7 +128,7 @@ const testSuite = (_mw: typeof middleware3) =>
|
|
|
127
128
|
Object.assign({ ...defaultOpts }, {
|
|
128
129
|
rpc: {
|
|
129
130
|
...defaultOpts.rpc,
|
|
130
|
-
annotations:
|
|
131
|
+
annotations: Context.make(_mw.requestContext, { requireRoles: ["manager"] })
|
|
131
132
|
}
|
|
132
133
|
})
|
|
133
134
|
)
|
|
@@ -153,7 +154,7 @@ const testSuite = (_mw: typeof middleware3) =>
|
|
|
153
154
|
{
|
|
154
155
|
rpc: {
|
|
155
156
|
...defaultOpts.rpc,
|
|
156
|
-
annotations:
|
|
157
|
+
annotations: Context.make(_mw.requestContext, { requireRoles: ["manager"] })
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
160
|
)
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { NodeHttpServer } from "@effect/platform-node"
|
|
2
2
|
import { expect, expectTypeOf, it } from "@effect/vitest"
|
|
3
|
-
import { Console, Effect, Layer, Result } from "effect"
|
|
4
|
-
import { S } from "effect-app"
|
|
3
|
+
import { Console, Effect, Layer, Ref, Result } from "effect"
|
|
4
|
+
import { Context, S } from "effect-app"
|
|
5
5
|
import { NotLoggedInError } from "effect-app/client"
|
|
6
6
|
import { HttpRouter } from "effect-app/http"
|
|
7
7
|
import { DefaultGenericMiddlewares } from "effect-app/middleware"
|
|
8
8
|
import { MiddlewareMaker } from "effect-app/rpc"
|
|
9
9
|
import { middlewareGroup } from "effect-app/rpc/MiddlewareMaker"
|
|
10
10
|
import { FetchHttpClient } from "effect/unstable/http"
|
|
11
|
-
import { RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "effect/unstable/rpc"
|
|
11
|
+
import { Rpc, RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "effect/unstable/rpc"
|
|
12
12
|
import { createServer } from "http"
|
|
13
13
|
import { DefaultGenericMiddlewaresLive } from "../src/api/routing.js"
|
|
14
14
|
import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElseMiddleware, SomeElseMiddlewareLive, SomeMiddleware, SomeMiddlewareLive, SomeService, Test, TestLive, UserProfile } from "./fixtures.js"
|
|
@@ -136,3 +136,72 @@ it.live(
|
|
|
136
136
|
Effect.provide(RpcTestLayer)
|
|
137
137
|
)
|
|
138
138
|
)
|
|
139
|
+
|
|
140
|
+
// Per-request service isolation test
|
|
141
|
+
|
|
142
|
+
class PerRequestCounter extends Context.Service<PerRequestCounter>()(
|
|
143
|
+
"PerRequestCounter",
|
|
144
|
+
{ make: Effect.sync(() => ({ a: 0 })) }
|
|
145
|
+
) {
|
|
146
|
+
static Default = Layer.effect(this, this.make)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
class GlobalCounter extends Context.Service<GlobalCounter, {
|
|
150
|
+
readonly ref: Ref.Ref<number>
|
|
151
|
+
}>()("GlobalCounter") {}
|
|
152
|
+
|
|
153
|
+
const CounterRpcs = RpcGroup.make(
|
|
154
|
+
Rpc.make("incrementA", {
|
|
155
|
+
success: S.Number
|
|
156
|
+
}),
|
|
157
|
+
Rpc.make("incrementB", {
|
|
158
|
+
success: S.Number
|
|
159
|
+
})
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
const counterImpl = CounterRpcs
|
|
163
|
+
.toLayer({
|
|
164
|
+
incrementA: Effect.fn(function*() {
|
|
165
|
+
const counter = yield* PerRequestCounter
|
|
166
|
+
counter.a++
|
|
167
|
+
const global = yield* GlobalCounter
|
|
168
|
+
yield* Ref.update(global.ref, (n) => n + 1)
|
|
169
|
+
return counter.a
|
|
170
|
+
}, Effect.provide(PerRequestCounter.Default)),
|
|
171
|
+
incrementB: Effect.fn(function*() {
|
|
172
|
+
const counter = yield* PerRequestCounter
|
|
173
|
+
counter.a++
|
|
174
|
+
const global = yield* GlobalCounter
|
|
175
|
+
yield* Ref.update(global.ref, (n) => n + 1)
|
|
176
|
+
return counter.a
|
|
177
|
+
}, Effect.provide(PerRequestCounter.Default))
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
const GlobalCounterLive = Layer.effect(
|
|
181
|
+
GlobalCounter,
|
|
182
|
+
Ref.make(0).pipe(Effect.map((ref) => ({ ref })))
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
const CounterTestLayer = counterImpl.pipe(Layer.provideMerge(GlobalCounterLive))
|
|
186
|
+
|
|
187
|
+
it.live(
|
|
188
|
+
"per-request service isolation with shared global counter",
|
|
189
|
+
Effect.fnUntraced(
|
|
190
|
+
function*() {
|
|
191
|
+
const client = yield* RpcTest.makeClient(CounterRpcs)
|
|
192
|
+
const global = yield* GlobalCounter
|
|
193
|
+
|
|
194
|
+
const r1 = yield* client.incrementA()
|
|
195
|
+
const r2 = yield* client.incrementB()
|
|
196
|
+
|
|
197
|
+
// per-request counter is fresh each time → both return 1
|
|
198
|
+
expect(r1).toBe(1)
|
|
199
|
+
expect(r2).toBe(1)
|
|
200
|
+
|
|
201
|
+
// global counter is shared across requests → accumulates to 2
|
|
202
|
+
const globalCount = yield* Ref.get(global.ref)
|
|
203
|
+
expect(globalCount).toBe(2)
|
|
204
|
+
},
|
|
205
|
+
Effect.provide(CounterTestLayer)
|
|
206
|
+
)
|
|
207
|
+
)
|