@effect-app/infra 4.0.0-beta.8 → 4.0.0-beta.81
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 +527 -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/Sendgrid.js +1 -1
- 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/internal/internal.d.ts +3 -3
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +11 -7
- package/dist/Model/Repository/makeRepo.d.ts +2 -2
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +1 -1
- package/dist/Model/Repository/validation.d.ts +5 -4
- package/dist/Model/Repository/validation.d.ts.map +1 -1
- package/dist/Model/query/dsl.d.ts +9 -9
- 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 +2 -2
- package/dist/OperationsRepo.d.ts.map +1 -1
- package/dist/OperationsRepo.js +3 -3
- package/dist/QueueMaker/SQLQueue.d.ts +3 -5
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +9 -7
- package/dist/QueueMaker/errors.d.ts +1 -1
- package/dist/QueueMaker/errors.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +10 -9
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +11 -9
- package/dist/RequestContext.d.ts +19 -14
- 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 +14 -3
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +64 -3
- package/dist/Store/Cosmos.js +1 -1
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +3 -4
- package/dist/Store/Memory.d.ts +2 -2
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +4 -4
- package/dist/Store/service.d.ts +6 -3
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +14 -6
- package/dist/adapters/SQL/Model.d.ts +2 -5
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +21 -13
- 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/RequestContextMiddleware.d.ts +1 -1
- 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 +7 -5
- 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/schema/jwt.d.ts +1 -1
- package/dist/api/routing/schema/jwt.d.ts.map +1 -1
- package/dist/api/routing/schema/jwt.js +1 -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.js +4 -4
- package/dist/errorReporter.d.ts +1 -1
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +1 -1
- package/dist/fileUtil.js +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/rateLimit.js +1 -1
- package/examples/query.ts +29 -25
- package/package.json +16 -16
- package/src/CUPS.ts +2 -2
- package/src/Emailer/Sendgrid.ts +1 -1
- package/src/Emailer/service.ts +2 -2
- package/src/MainFiberSet.ts +2 -2
- package/src/Model/Repository/internal/internal.ts +11 -8
- package/src/Model/Repository/makeRepo.ts +2 -2
- package/src/Operations.ts +2 -2
- package/src/OperationsRepo.ts +2 -2
- package/src/QueueMaker/SQLQueue.ts +10 -10
- package/src/QueueMaker/memQueue.ts +41 -42
- package/src/QueueMaker/sbqueue.ts +65 -62
- package/src/RequestContext.ts +4 -4
- package/src/RequestFiberSet.ts +4 -4
- package/src/Store/ContextMapContainer.ts +98 -2
- package/src/Store/Cosmos.ts +10 -10
- package/src/Store/Disk.ts +2 -3
- package/src/Store/Memory.ts +4 -6
- package/src/Store/service.ts +14 -5
- package/src/adapters/SQL/Model.ts +76 -71
- 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 +7 -6
- package/src/api/layerUtils.ts +8 -8
- package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
- package/src/api/routing/schema/jwt.ts +2 -3
- package/src/api/routing.ts +7 -6
- package/src/api/setupRequest.ts +3 -3
- package/src/errorReporter.ts +1 -1
- package/src/fileUtil.ts +1 -1
- package/src/rateLimit.ts +2 -2
- package/test/contextProvider.test.ts +5 -5
- 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/fixtures.d.ts +18 -8
- 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/fixtures.ts +10 -8
- package/test/query.test.ts +160 -14
- package/test/rawQuery.test.ts +19 -17
- package/test/requires.test.ts +6 -5
- package/test/rpc-multi-middleware.test.ts +73 -4
- package/test/validateSample.test.ts +1 -1
- package/tsconfig.json +0 -1
package/test/fixtures.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Effect, Layer, S, Scope
|
|
1
|
+
import { Context, Effect, Layer, S, Scope } from "effect-app"
|
|
2
2
|
import { NotLoggedInError, UnauthorizedError } from "effect-app/client"
|
|
3
3
|
import { RpcContextMap, RpcX } from "effect-app/rpc"
|
|
4
|
-
import {
|
|
4
|
+
import { TaggedErrorClass } from "effect-app/Schema"
|
|
5
5
|
|
|
6
|
-
export class UserProfile extends
|
|
6
|
+
export class UserProfile extends Context.assignTag<UserProfile, UserProfile>("UserProfile")(
|
|
7
7
|
S.Class<UserProfile>("UserProfile")({
|
|
8
8
|
id: S.String,
|
|
9
9
|
roles: S.Array(S.String)
|
|
@@ -11,10 +11,10 @@ export class UserProfile extends ServiceMap.assignTag<UserProfile, UserProfile>(
|
|
|
11
11
|
) {
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export class Some extends
|
|
15
|
-
export class SomeElse extends
|
|
14
|
+
export class Some extends Context.Opaque<Some>()("Some", { make: Effect.succeed({ a: 1 }) }) {}
|
|
15
|
+
export class SomeElse extends Context.Opaque<SomeElse>()("SomeElse", { make: Effect.succeed({ b: 2 }) }) {}
|
|
16
16
|
const MakeSomeService = Effect.succeed({ a: 1 })
|
|
17
|
-
export class SomeService extends
|
|
17
|
+
export class SomeService extends Context.Opaque<SomeService>()("SomeService", { make: MakeSomeService }) {
|
|
18
18
|
static readonly Default = this.toLayer(this.make)
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -128,5 +128,7 @@ export const TestLive = Layer.effect(
|
|
|
128
128
|
})
|
|
129
129
|
)
|
|
130
130
|
|
|
131
|
-
export class
|
|
132
|
-
|
|
131
|
+
export class Counter extends Context.Opaque<Counter>()("Counter", { make: Effect.succeed({ a: 0 }) }) {}
|
|
132
|
+
|
|
133
|
+
export class CustomError1 extends TaggedErrorClass<CustomError1>()("CustomError1", {}) {}
|
|
134
|
+
export class CustomError2 extends TaggedErrorClass<CustomError2>()("CustomError2", {}) {}
|
package/test/query.test.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
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"
|
|
@@ -11,7 +12,7 @@ import { memFilter, MemoryStoreLive } from "../src/Store/Memory.js"
|
|
|
11
12
|
import { SomeService } from "./fixtures.js"
|
|
12
13
|
|
|
13
14
|
const str = S.Struct({ _tag: S.Literal("string"), value: S.String })
|
|
14
|
-
const num = S.Struct({ _tag: S.Literal("number"), value: S.
|
|
15
|
+
const num = S.Struct({ _tag: S.Literal("number"), value: S.Finite })
|
|
15
16
|
const someUnion = S.Union([str, num])
|
|
16
17
|
|
|
17
18
|
export class Something extends S.Class<Something>("Something")({
|
|
@@ -19,7 +20,7 @@ export class Something extends S.Class<Something>("Something")({
|
|
|
19
20
|
displayName: S.NonEmptyString255,
|
|
20
21
|
name: S.NullOr(S.NonEmptyString255).withDefault,
|
|
21
22
|
n: S.Date.withDefault,
|
|
22
|
-
union: someUnion.pipe(S.
|
|
23
|
+
union: someUnion.pipe(S.withConstructorDefault(Effect.succeed({ _tag: "string" as const, value: "hi" })))
|
|
23
24
|
}) {}
|
|
24
25
|
export declare namespace Something {
|
|
25
26
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
@@ -91,7 +92,7 @@ it("works", () => {
|
|
|
91
92
|
const processed = memFilter(interpreted)(items.map((_) =>
|
|
92
93
|
S.encodeUnknownSync(S.Struct({
|
|
93
94
|
...Something.omit("displayName"),
|
|
94
|
-
displayName: S.
|
|
95
|
+
displayName: S.Literals(["Verona", "Riley"])
|
|
95
96
|
}))(_)
|
|
96
97
|
))
|
|
97
98
|
|
|
@@ -99,7 +100,7 @@ it("works", () => {
|
|
|
99
100
|
})
|
|
100
101
|
|
|
101
102
|
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
102
|
-
class SomethingRepo extends
|
|
103
|
+
class SomethingRepo extends Context.Service<SomethingRepo>()("SomethingRepo", {
|
|
103
104
|
make: Effect.gen(function*() {
|
|
104
105
|
return yield* makeRepo("Something", Something, {})
|
|
105
106
|
})
|
|
@@ -156,8 +157,6 @@ it("works with repo", () =>
|
|
|
156
157
|
|
|
157
158
|
expectTypeOf(smtArr).toEqualTypeOf<readonly Something[]>()
|
|
158
159
|
|
|
159
|
-
console.log(" $$$$$$")
|
|
160
|
-
console.log(Struct.pick(["id", "displayName"]))
|
|
161
160
|
expect(q1).toEqual(items.slice(0, 2).toReversed().map(Struct.pick(["id", "displayName"])))
|
|
162
161
|
expect(q2).toEqual(items.slice(0, 2).toReversed().map(Struct.pick(["displayName"])))
|
|
163
162
|
})
|
|
@@ -521,8 +520,8 @@ it(
|
|
|
521
520
|
const schema = S.Struct({
|
|
522
521
|
id: S.String,
|
|
523
522
|
createdAt: S.Date.pipe(
|
|
524
|
-
S.withDecodingDefault(() => new Date().toISOString()),
|
|
525
|
-
S.withConstructorDefault(() =>
|
|
523
|
+
S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
|
|
524
|
+
S.withConstructorDefault(Effect.sync(() => new Date()))
|
|
526
525
|
)
|
|
527
526
|
})
|
|
528
527
|
const repo = yield* makeRepo(
|
|
@@ -534,8 +533,8 @@ it(
|
|
|
534
533
|
const outputSchema = S.Struct({
|
|
535
534
|
id: S.Literal("123"),
|
|
536
535
|
createdAt: S.Date.pipe(
|
|
537
|
-
S.withDecodingDefault(() => new Date().toISOString()),
|
|
538
|
-
S.withConstructorDefault(() =>
|
|
536
|
+
S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
|
|
537
|
+
S.withConstructorDefault(Effect.sync(() => new Date()))
|
|
539
538
|
)
|
|
540
539
|
})
|
|
541
540
|
|
|
@@ -553,7 +552,7 @@ it(
|
|
|
553
552
|
.gen(function*() {
|
|
554
553
|
const schema = S.Struct({
|
|
555
554
|
id: S.String,
|
|
556
|
-
literals: S.
|
|
555
|
+
literals: S.Literals(["a", "b", "c"])
|
|
557
556
|
})
|
|
558
557
|
|
|
559
558
|
type Schema = typeof schema.Type
|
|
@@ -583,7 +582,7 @@ it(
|
|
|
583
582
|
.gen(function*() {
|
|
584
583
|
const schema = S.Struct({
|
|
585
584
|
id: S.String,
|
|
586
|
-
literals: S.Union([S.
|
|
585
|
+
literals: S.Union([S.Literals(["a", "b", "c"]), S.Null])
|
|
587
586
|
})
|
|
588
587
|
|
|
589
588
|
type Schema = typeof schema.Type
|
|
@@ -674,7 +673,7 @@ it("remove null from one constituent of a tagged union", () =>
|
|
|
674
673
|
|
|
675
674
|
class BB extends S.Class<BB>("BB")({
|
|
676
675
|
id: S.Literal("BB"),
|
|
677
|
-
b: S.NullOr(S.
|
|
676
|
+
b: S.NullOr(S.Finite)
|
|
678
677
|
}) {}
|
|
679
678
|
|
|
680
679
|
type Union = AA | BB
|
|
@@ -984,6 +983,153 @@ it("refine nested union", () =>
|
|
|
984
983
|
})
|
|
985
984
|
.pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
986
985
|
|
|
986
|
+
it("find with transformed id", () =>
|
|
987
|
+
Effect
|
|
988
|
+
.gen(function*() {
|
|
989
|
+
const ConfiguratorId = S.NonEmptyString255
|
|
990
|
+
|
|
991
|
+
class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
|
|
992
|
+
configuratorId: ConfiguratorId,
|
|
993
|
+
label: S.NonEmptyString50
|
|
994
|
+
}) {}
|
|
995
|
+
|
|
996
|
+
const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
|
|
997
|
+
S.decodeTo(
|
|
998
|
+
S.toType(PreconfigurationId),
|
|
999
|
+
SchemaTransformation.transformOrFail({
|
|
1000
|
+
decode: Effect.fnUntraced(function*(value) {
|
|
1001
|
+
const values = value.split("_")
|
|
1002
|
+
const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
|
|
1003
|
+
const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(values.join("_"))
|
|
1004
|
+
return new PreconfigurationId({ configuratorId, label })
|
|
1005
|
+
}),
|
|
1006
|
+
encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
|
|
1007
|
+
})
|
|
1008
|
+
),
|
|
1009
|
+
S.revealCodec
|
|
1010
|
+
)
|
|
1011
|
+
|
|
1012
|
+
const Preconfiguration = S.Struct({
|
|
1013
|
+
id: PreconfigurationIdFromString,
|
|
1014
|
+
name: S.String
|
|
1015
|
+
})
|
|
1016
|
+
|
|
1017
|
+
const repo = yield* makeRepo("Preconfiguration", Preconfiguration, { idKey: "id" as const })
|
|
1018
|
+
|
|
1019
|
+
const id = new PreconfigurationId({
|
|
1020
|
+
configuratorId: S.NonEmptyString255("myConfigurator"),
|
|
1021
|
+
label: S.NonEmptyString50("myLabel")
|
|
1022
|
+
})
|
|
1023
|
+
const item = { id, name: "test preconfig" }
|
|
1024
|
+
|
|
1025
|
+
yield* repo.saveAndPublish([item])
|
|
1026
|
+
|
|
1027
|
+
const found = yield* repo.find(id)
|
|
1028
|
+
expect(Option.isSome(found)).toBe(true)
|
|
1029
|
+
expect(Option.getOrThrow(found).name).toBe("test preconfig")
|
|
1030
|
+
expect(Option.getOrThrow(found).id).toEqual(id)
|
|
1031
|
+
|
|
1032
|
+
const notFound = yield* repo.find(
|
|
1033
|
+
new PreconfigurationId({
|
|
1034
|
+
configuratorId: S.NonEmptyString255("other"),
|
|
1035
|
+
label: S.NonEmptyString50("nope")
|
|
1036
|
+
})
|
|
1037
|
+
)
|
|
1038
|
+
expect(Option.isNone(notFound)).toBe(true)
|
|
1039
|
+
})
|
|
1040
|
+
.pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
1041
|
+
|
|
1042
|
+
it("find with transformed id in tagged union", () =>
|
|
1043
|
+
Effect
|
|
1044
|
+
.gen(function*() {
|
|
1045
|
+
const ConfiguratorId = S.NonEmptyString255
|
|
1046
|
+
|
|
1047
|
+
class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
|
|
1048
|
+
configuratorId: ConfiguratorId,
|
|
1049
|
+
label: S.NonEmptyString50
|
|
1050
|
+
}) {}
|
|
1051
|
+
|
|
1052
|
+
const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
|
|
1053
|
+
S.decodeTo(
|
|
1054
|
+
S.toType(PreconfigurationId),
|
|
1055
|
+
SchemaTransformation.transformOrFail({
|
|
1056
|
+
decode: Effect.fnUntraced(function*(value) {
|
|
1057
|
+
const values = value.split("_")
|
|
1058
|
+
const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
|
|
1059
|
+
const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(values.join("_"))
|
|
1060
|
+
return new PreconfigurationId({ configuratorId, label })
|
|
1061
|
+
}),
|
|
1062
|
+
encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
|
|
1063
|
+
})
|
|
1064
|
+
),
|
|
1065
|
+
S.revealCodec
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
class Draft extends S.TaggedClass<Draft>()("Draft", {
|
|
1069
|
+
id: PreconfigurationIdFromString,
|
|
1070
|
+
name: S.String
|
|
1071
|
+
}) {}
|
|
1072
|
+
|
|
1073
|
+
class Published extends S.TaggedClass<Published>()("Published", {
|
|
1074
|
+
id: PreconfigurationIdFromString,
|
|
1075
|
+
name: S.String,
|
|
1076
|
+
publishedAt: S.String
|
|
1077
|
+
}) {}
|
|
1078
|
+
|
|
1079
|
+
class Archived extends S.TaggedClass<Archived>()("Archived", {
|
|
1080
|
+
id: PreconfigurationIdFromString,
|
|
1081
|
+
name: S.String,
|
|
1082
|
+
archivedAt: S.String
|
|
1083
|
+
}) {}
|
|
1084
|
+
|
|
1085
|
+
const Preconfiguration = S.Union([Draft, Published, Archived])
|
|
1086
|
+
|
|
1087
|
+
const repo = yield* makeRepo("Preconfiguration", Preconfiguration, {})
|
|
1088
|
+
|
|
1089
|
+
const id1 = new PreconfigurationId({
|
|
1090
|
+
configuratorId: S.NonEmptyString255("conf1"),
|
|
1091
|
+
label: S.NonEmptyString50("draft1")
|
|
1092
|
+
})
|
|
1093
|
+
const id2 = new PreconfigurationId({
|
|
1094
|
+
configuratorId: S.NonEmptyString255("conf2"),
|
|
1095
|
+
label: S.NonEmptyString50("pub1")
|
|
1096
|
+
})
|
|
1097
|
+
const id3 = new PreconfigurationId({
|
|
1098
|
+
configuratorId: S.NonEmptyString255("conf3"),
|
|
1099
|
+
label: S.NonEmptyString50("arch1")
|
|
1100
|
+
})
|
|
1101
|
+
|
|
1102
|
+
const draft = new Draft({ id: id1, name: "my draft" })
|
|
1103
|
+
const published = new Published({ id: id2, name: "my published", publishedAt: "2024-01-01" })
|
|
1104
|
+
const archived = new Archived({ id: id3, name: "my archived", archivedAt: "2024-06-01" })
|
|
1105
|
+
|
|
1106
|
+
yield* repo.saveAndPublish([draft, published, archived])
|
|
1107
|
+
|
|
1108
|
+
// find each by their PreconfigurationId instance
|
|
1109
|
+
const foundDraft = yield* repo.find(id1)
|
|
1110
|
+
expect(Option.isSome(foundDraft)).toBe(true)
|
|
1111
|
+
expect(Option.getOrThrow(foundDraft)._tag).toBe("Draft")
|
|
1112
|
+
expect(Option.getOrThrow(foundDraft).name).toBe("my draft")
|
|
1113
|
+
|
|
1114
|
+
const foundPublished = yield* repo.find(id2)
|
|
1115
|
+
expect(Option.isSome(foundPublished)).toBe(true)
|
|
1116
|
+
expect(Option.getOrThrow(foundPublished)._tag).toBe("Published")
|
|
1117
|
+
|
|
1118
|
+
const foundArchived = yield* repo.find(id3)
|
|
1119
|
+
expect(Option.isSome(foundArchived)).toBe(true)
|
|
1120
|
+
expect(Option.getOrThrow(foundArchived)._tag).toBe("Archived")
|
|
1121
|
+
|
|
1122
|
+
// not found
|
|
1123
|
+
const notFound = yield* repo.find(
|
|
1124
|
+
new PreconfigurationId({
|
|
1125
|
+
configuratorId: S.NonEmptyString255("nope"),
|
|
1126
|
+
label: S.NonEmptyString50("nope")
|
|
1127
|
+
})
|
|
1128
|
+
)
|
|
1129
|
+
expect(Option.isNone(notFound)).toBe(true)
|
|
1130
|
+
})
|
|
1131
|
+
.pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
1132
|
+
|
|
987
1133
|
it("refine union with nested union", () =>
|
|
988
1134
|
Effect
|
|
989
1135
|
.gen(function*() {
|
package/test/rawQuery.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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, Effect, flow, Layer, ManagedRuntime, Redacted, References, Result, S, Context } 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"
|
|
@@ -24,7 +24,7 @@ class Something extends S.Class<Something>("Something")({
|
|
|
24
24
|
id: S.String,
|
|
25
25
|
name: S.String,
|
|
26
26
|
description: S.String,
|
|
27
|
-
items: S.Array(S.Struct({ id: S.String, value: S.
|
|
27
|
+
items: S.Array(S.Struct({ id: S.String, value: S.Finite, description: S.String }))
|
|
28
28
|
}) {}
|
|
29
29
|
|
|
30
30
|
const items = [
|
|
@@ -49,7 +49,7 @@ const items = [
|
|
|
49
49
|
]
|
|
50
50
|
|
|
51
51
|
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
52
|
-
class SomethingRepo extends
|
|
52
|
+
class SomethingRepo extends Context.Service<SomethingRepo>()(
|
|
53
53
|
"SomethingRepo",
|
|
54
54
|
{
|
|
55
55
|
make: Effect.gen(function*() {
|
|
@@ -83,21 +83,23 @@ class SomethingRepo extends ServiceMap.Service<SomethingRepo>()(
|
|
|
83
83
|
.layer
|
|
84
84
|
.pipe(
|
|
85
85
|
Layer.provide(
|
|
86
|
-
Effect
|
|
87
|
-
|
|
88
|
-
Config.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
Effect
|
|
87
|
+
.gen(function*() {
|
|
88
|
+
const url = yield* Config.redacted("STORAGE_URL").pipe(
|
|
89
|
+
Config.withDefault(
|
|
90
|
+
Redacted.make(
|
|
91
|
+
// the emulator doesn't implement array projections :/ so you need an actual cloud instance!
|
|
92
|
+
"AccountEndpoint=http://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
|
|
93
|
+
)
|
|
92
94
|
)
|
|
93
95
|
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
return CosmosStoreLayer({
|
|
97
|
+
dbName: "test",
|
|
98
|
+
prefix: "",
|
|
99
|
+
url
|
|
100
|
+
})
|
|
99
101
|
})
|
|
100
|
-
|
|
102
|
+
.pipe(Layer.unwrap)
|
|
101
103
|
)
|
|
102
104
|
)
|
|
103
105
|
}
|
|
@@ -107,7 +109,7 @@ describe("select first-level array fields", () => {
|
|
|
107
109
|
.gen(function*() {
|
|
108
110
|
const repo = yield* SomethingRepo
|
|
109
111
|
|
|
110
|
-
const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.
|
|
112
|
+
const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.Finite })) })
|
|
111
113
|
|
|
112
114
|
// 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
115
|
const items = yield* repo.queryRaw(projected, {
|
|
@@ -159,7 +161,7 @@ describe("select first-level array fields", () => {
|
|
|
159
161
|
.pipe(Effect.provide(SomethingRepo.Test), rt.runPromise))
|
|
160
162
|
})
|
|
161
163
|
|
|
162
|
-
const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.
|
|
164
|
+
const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.Finite })) })
|
|
163
165
|
|
|
164
166
|
const expected = [
|
|
165
167
|
{
|
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"
|
|
@@ -109,7 +109,7 @@ export const RpcRealLayer = Layer
|
|
|
109
109
|
Layer.provide(FetchHttpClient.layer)
|
|
110
110
|
)
|
|
111
111
|
)
|
|
112
|
-
.pipe(Layer.provide(RpcSerialization.
|
|
112
|
+
.pipe(Layer.provide(RpcSerialization.layerNdjson))
|
|
113
113
|
|
|
114
114
|
it.live(
|
|
115
115
|
"require login",
|
|
@@ -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
|
+
)
|
|
@@ -138,7 +138,7 @@ describe("validateSample", () => {
|
|
|
138
138
|
// schema that expects a 'status' field
|
|
139
139
|
class ItemWithStatus extends S.Class<ItemWithStatus>("ItemWithStatus")({
|
|
140
140
|
id: S.String,
|
|
141
|
-
status: S.
|
|
141
|
+
status: S.Literals(["active", "inactive"])
|
|
142
142
|
}) {}
|
|
143
143
|
|
|
144
144
|
// jitM that adds default status for items
|