@effect-app/infra 4.0.0-beta.7 → 4.0.0-beta.71
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 +455 -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 +4 -4
- 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/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 +10 -10
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +4 -4
- 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 +3 -3
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +3 -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 +3 -3
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +4 -4
- 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/auth.d.ts +1 -1
- 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.map +1 -1
- package/dist/api/routing.js +1 -1
- 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 +3 -3
- package/src/RequestFiberSet.ts +4 -4
- package/src/Store/ContextMapContainer.ts +2 -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 +3 -3
- 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 +5 -4
- 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 +4 -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 +9 -7
- 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 +17 -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 +157 -11
- package/test/rawQuery.test.ts +19 -17
- package/test/requires.test.ts +6 -5
- package/test/rpc-multi-middleware.test.ts +73 -4
- package/tsconfig.json +0 -1
|
@@ -7,10 +7,6 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* @since 1.0.0
|
|
9
9
|
*/
|
|
10
|
-
import * as VariantSchema from "effect/unstable/schema/VariantSchema"
|
|
11
|
-
import { SqlClient } from "effect/unstable/sql/SqlClient"
|
|
12
|
-
import * as SqlResolver from "effect/unstable/sql/SqlResolver"
|
|
13
|
-
import * as SqlSchema from "effect/unstable/sql/SqlSchema"
|
|
14
10
|
import crypto from "crypto" // TODO
|
|
15
11
|
import type { Brand } from "effect/Brand"
|
|
16
12
|
import * as DateTime from "effect/DateTime"
|
|
@@ -24,6 +20,10 @@ import * as Schema from "effect/Schema"
|
|
|
24
20
|
import * as Getter from "effect/SchemaGetter"
|
|
25
21
|
import * as Transformation from "effect/SchemaTransformation"
|
|
26
22
|
import type { Scope } from "effect/Scope"
|
|
23
|
+
import * as VariantSchema from "effect/unstable/schema/VariantSchema"
|
|
24
|
+
import { SqlClient } from "effect/unstable/sql/SqlClient"
|
|
25
|
+
import * as SqlResolver from "effect/unstable/sql/SqlResolver"
|
|
26
|
+
import * as SqlSchema from "effect/unstable/sql/SqlSchema"
|
|
27
27
|
|
|
28
28
|
const {
|
|
29
29
|
Class,
|
|
@@ -190,14 +190,13 @@ export const Generated = <S extends Schema.Top>(
|
|
|
190
190
|
* @since 1.0.0
|
|
191
191
|
* @category generated
|
|
192
192
|
*/
|
|
193
|
-
export interface GeneratedByApp<S extends Schema.Top>
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}>
|
|
193
|
+
export interface GeneratedByApp<S extends Schema.Top> extends
|
|
194
|
+
VariantSchema.Field<{
|
|
195
|
+
readonly select: S
|
|
196
|
+
readonly insert: S
|
|
197
|
+
readonly update: S
|
|
198
|
+
readonly json: S
|
|
199
|
+
}>
|
|
201
200
|
{}
|
|
202
201
|
|
|
203
202
|
/**
|
|
@@ -303,8 +302,7 @@ export const FieldOption: <Field extends VariantSchema.Field<any> | Schema.Top>(
|
|
|
303
302
|
) => Field extends Schema.Top ? FieldOption<Field>
|
|
304
303
|
: Field extends VariantSchema.Field<infer S> ? VariantSchema.Field<
|
|
305
304
|
{
|
|
306
|
-
readonly [K in keyof S]: S[K] extends Schema.Top
|
|
307
|
-
? K extends VariantsDatabase ? Schema.OptionFromNullOr<S[K]>
|
|
305
|
+
readonly [K in keyof S]: S[K] extends Schema.Top ? K extends VariantsDatabase ? Schema.OptionFromNullOr<S[K]>
|
|
308
306
|
: optionalOption<S[K]>
|
|
309
307
|
: never
|
|
310
308
|
}
|
|
@@ -545,16 +543,15 @@ export const DateTimeUpdateFromNumber: DateTimeUpdateFromNumber = Field({
|
|
|
545
543
|
* @since 1.0.0
|
|
546
544
|
* @category json
|
|
547
545
|
*/
|
|
548
|
-
export interface JsonFromString<S extends Schema.Top>
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}>
|
|
546
|
+
export interface JsonFromString<S extends Schema.Top> extends
|
|
547
|
+
VariantSchema.Field<{
|
|
548
|
+
readonly select: Schema.fromJsonString<S>
|
|
549
|
+
readonly insert: Schema.fromJsonString<S>
|
|
550
|
+
readonly update: Schema.fromJsonString<S>
|
|
551
|
+
readonly json: S
|
|
552
|
+
readonly jsonCreate: S
|
|
553
|
+
readonly jsonUpdate: S
|
|
554
|
+
}>
|
|
558
555
|
{}
|
|
559
556
|
|
|
560
557
|
/**
|
|
@@ -825,26 +822,28 @@ export const makeDataLoaders = <
|
|
|
825
822
|
const idColumn = options.idColumn as string
|
|
826
823
|
const setMaxBatchSize = options.maxBatchSize ? RequestResolver.batchN(options.maxBatchSize) : identity
|
|
827
824
|
|
|
828
|
-
const insertResolver = SqlResolver
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
825
|
+
const insertResolver = SqlResolver
|
|
826
|
+
.ordered({
|
|
827
|
+
Request: Model.insert,
|
|
828
|
+
Result: Model,
|
|
829
|
+
execute: (request: any) =>
|
|
830
|
+
sql.onDialectOrElse({
|
|
831
|
+
mysql: () =>
|
|
832
|
+
Effect.forEach(request, (request: any) =>
|
|
833
|
+
sql`insert into ${sql(options.tableName)} ${sql.insert(request)};
|
|
836
834
|
select * from ${sql(options.tableName)} where ${sql(idColumn)} = LAST_INSERT_ID();`
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
835
|
+
.unprepared
|
|
836
|
+
.pipe(
|
|
837
|
+
Effect.map(([, results]) => results![0] as any)
|
|
838
|
+
), { concurrency: 10 }),
|
|
839
|
+
orElse: () => sql`insert into ${sql(options.tableName)} ${sql.insert(request).returning("*")}`
|
|
840
|
+
})
|
|
841
|
+
})
|
|
842
|
+
.pipe(
|
|
843
|
+
RequestResolver.setDelay(options.window),
|
|
844
|
+
setMaxBatchSize,
|
|
845
|
+
RequestResolver.withSpan(`${options.spanPrefix}.insertResolver`)
|
|
846
|
+
)
|
|
848
847
|
const insertExecute = SqlResolver.request(insertResolver)
|
|
849
848
|
const insert = (
|
|
850
849
|
insert: S["insert"]["Type"]
|
|
@@ -860,14 +859,16 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = LAST_INSERT_ID(
|
|
|
860
859
|
})
|
|
861
860
|
) as any
|
|
862
861
|
|
|
863
|
-
const insertVoidResolver = SqlResolver
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
862
|
+
const insertVoidResolver = SqlResolver
|
|
863
|
+
.void({
|
|
864
|
+
Request: Model.insert,
|
|
865
|
+
execute: (request: any) => sql`insert into ${sql(options.tableName)} ${sql.insert(request)}`
|
|
866
|
+
})
|
|
867
|
+
.pipe(
|
|
868
|
+
RequestResolver.setDelay(options.window),
|
|
869
|
+
setMaxBatchSize,
|
|
870
|
+
RequestResolver.withSpan(`${options.spanPrefix}.insertVoidResolver`)
|
|
871
|
+
)
|
|
871
872
|
const insertVoidExecute = SqlResolver.request(insertVoidResolver)
|
|
872
873
|
const insertVoid = (
|
|
873
874
|
insert: S["insert"]["Type"]
|
|
@@ -878,18 +879,20 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = LAST_INSERT_ID(
|
|
|
878
879
|
})
|
|
879
880
|
) as any
|
|
880
881
|
|
|
881
|
-
const findByIdResolver = SqlResolver
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
882
|
+
const findByIdResolver = SqlResolver
|
|
883
|
+
.findById({
|
|
884
|
+
Id: idSchema,
|
|
885
|
+
Result: Model,
|
|
886
|
+
ResultId(request: any) {
|
|
887
|
+
return request[idColumn]
|
|
888
|
+
},
|
|
889
|
+
execute: (ids: any) => sql`select * from ${sql(options.tableName)} where ${sql.in(idColumn, ids)}`
|
|
890
|
+
})
|
|
891
|
+
.pipe(
|
|
892
|
+
RequestResolver.setDelay(options.window),
|
|
893
|
+
setMaxBatchSize,
|
|
894
|
+
RequestResolver.withSpan(`${options.spanPrefix}.findByIdResolver`)
|
|
895
|
+
)
|
|
893
896
|
const findByIdExecute = SqlResolver.request(findByIdResolver)
|
|
894
897
|
const findById = (
|
|
895
898
|
id: S["fields"][Id]["Type"]
|
|
@@ -904,14 +907,16 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = LAST_INSERT_ID(
|
|
|
904
907
|
})
|
|
905
908
|
) as any
|
|
906
909
|
|
|
907
|
-
const deleteResolver = SqlResolver
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
910
|
+
const deleteResolver = SqlResolver
|
|
911
|
+
.void({
|
|
912
|
+
Request: idSchema,
|
|
913
|
+
execute: (ids: any) => sql`delete from ${sql(options.tableName)} where ${sql.in(idColumn, ids)}`
|
|
914
|
+
})
|
|
915
|
+
.pipe(
|
|
916
|
+
RequestResolver.setDelay(options.window),
|
|
917
|
+
setMaxBatchSize,
|
|
918
|
+
RequestResolver.withSpan(`${options.spanPrefix}.deleteResolver`)
|
|
919
|
+
)
|
|
915
920
|
const deleteExecute = SqlResolver.request(deleteResolver)
|
|
916
921
|
const delete_ = (
|
|
917
922
|
id: S["fields"][Id]["Type"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/prefer-promise-reject-errors */
|
|
2
2
|
import { type OperationOptionsBase, type ProcessErrorArgs, ServiceBusClient, type ServiceBusMessage, type ServiceBusMessageBatch, type ServiceBusReceivedMessage, type ServiceBusReceiver } from "@azure/service-bus"
|
|
3
|
-
import { Cause, Effect, Exit, FiberSet, Layer, type Scope
|
|
3
|
+
import { Cause, Context, Effect, Exit, FiberSet, Layer, type Scope } from "effect-app"
|
|
4
4
|
import { InfraLogger } from "../logger.js"
|
|
5
5
|
|
|
6
6
|
const withSpanAndLog = (name: string) => <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
@@ -19,7 +19,7 @@ function makeClient(url: string) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export class ServiceBusClientTag
|
|
22
|
-
extends
|
|
22
|
+
extends Context.Opaque<ServiceBusClientTag, ServiceBusClient>()("@services/Client", { make: makeClient })
|
|
23
23
|
{
|
|
24
24
|
static readonly layer = (url: string) => this.toLayer(this.make(url))
|
|
25
25
|
}
|
|
@@ -50,7 +50,7 @@ const makeSender = (name: string) =>
|
|
|
50
50
|
return { name, sendMessages }
|
|
51
51
|
})
|
|
52
52
|
|
|
53
|
-
export class Sender extends
|
|
53
|
+
export class Sender extends Context.Opaque<Sender, {
|
|
54
54
|
name: string
|
|
55
55
|
sendMessages: (
|
|
56
56
|
messages: ServiceBusMessage | ServiceBusMessage[] | ServiceBusMessageBatch,
|
|
@@ -61,12 +61,12 @@ export class Sender extends ServiceMap.Opaque<Sender, {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export const SenderTag = <Id>() => <Key extends string>(queueName: Key) => {
|
|
64
|
-
const tag =
|
|
64
|
+
const tag = Context.Service<Id, Sender>(`ServiceBus.Sender.${queueName}`)
|
|
65
65
|
|
|
66
66
|
return Object.assign(tag, {
|
|
67
67
|
layer: Layer.effect(
|
|
68
68
|
tag,
|
|
69
|
-
Sender.make(queueName).pipe(Effect.map(
|
|
69
|
+
Sender.make(queueName).pipe(Effect.map(Sender.of))
|
|
70
70
|
)
|
|
71
71
|
})
|
|
72
72
|
}
|
|
@@ -163,7 +163,7 @@ const makeReceiver = (name: string) =>
|
|
|
163
163
|
}
|
|
164
164
|
})
|
|
165
165
|
|
|
166
|
-
export class Receiver extends
|
|
166
|
+
export class Receiver extends Context.Opaque<Receiver, {
|
|
167
167
|
name: string
|
|
168
168
|
make: (waitTillEmpty: Effect.Effect<void>) => Effect.Effect<ServiceBusReceiver, never, Scope.Scope>
|
|
169
169
|
makeSession: (
|
|
@@ -180,12 +180,12 @@ export class Receiver extends ServiceMap.Opaque<Receiver, {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
export const ReceiverTag = <Id>() => <Key extends string>(queueName: Key) => {
|
|
183
|
-
const tag =
|
|
183
|
+
const tag = Context.Service<Id, Receiver>(`ServiceBus.Receiver.${queueName}`)
|
|
184
184
|
|
|
185
185
|
return Object.assign(tag, {
|
|
186
186
|
layer: Layer.effect(
|
|
187
187
|
tag,
|
|
188
|
-
makeReceiver(queueName).pipe(Effect.map(
|
|
188
|
+
makeReceiver(queueName).pipe(Effect.map(Receiver.of))
|
|
189
189
|
)
|
|
190
190
|
})
|
|
191
191
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { CosmosClient as ComosClient_ } from "@azure/cosmos"
|
|
2
|
-
import { Effect, Layer
|
|
2
|
+
import { Context, Effect, Layer } from "effect-app"
|
|
3
3
|
|
|
4
4
|
const withClient = (url: string) => Effect.sync(() => new ComosClient_(url))
|
|
5
5
|
|
|
6
6
|
export const makeCosmosClient = (url: string, dbName: string) =>
|
|
7
7
|
Effect.map(withClient(url), (x) => ({ db: x.database(dbName) }))
|
|
8
8
|
|
|
9
|
-
export class CosmosClient extends
|
|
9
|
+
export class CosmosClient extends Context.Service<CosmosClient, {
|
|
10
10
|
readonly db: ReturnType<InstanceType<typeof ComosClient_>["database"]>
|
|
11
11
|
}>()("@services/CosmosClient") {}
|
|
12
12
|
|
package/src/adapters/memQueue.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect, type Queue
|
|
1
|
+
import { Context, Effect, type Queue } from "effect-app"
|
|
2
2
|
import * as Q from "effect/Queue"
|
|
3
3
|
|
|
4
4
|
const make = Effect
|
|
@@ -16,6 +16,6 @@ const make = Effect
|
|
|
16
16
|
}
|
|
17
17
|
})
|
|
18
18
|
|
|
19
|
-
export class MemQueue extends
|
|
19
|
+
export class MemQueue extends Context.Opaque<MemQueue>()("effect-app/MemQueue", { make }) {
|
|
20
20
|
static readonly Live = this.toLayer(this.make)
|
|
21
21
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect, Layer
|
|
1
|
+
import { Context, Effect, Layer } from "effect-app"
|
|
2
2
|
import { MongoClient as MongoClient_ } from "mongodb"
|
|
3
3
|
|
|
4
4
|
// TODO: we should probably share a single client...
|
|
@@ -15,7 +15,7 @@ const withClient = (url: string) =>
|
|
|
15
15
|
|
|
16
16
|
const makeMongoClient = (url: string, dbName?: string) => Effect.map(withClient(url), (x) => ({ db: x.db(dbName) }))
|
|
17
17
|
|
|
18
|
-
export class MongoClient extends
|
|
18
|
+
export class MongoClient extends Context.Service<MongoClient, {
|
|
19
19
|
readonly db: ReturnType<InstanceType<typeof MongoClient_>["db"]>
|
|
20
20
|
}>()("@services/MongoClient") {}
|
|
21
21
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Data, Effect, Layer, Option
|
|
1
|
+
import { Context, Data, Effect, Layer, Option } from "effect-app"
|
|
2
2
|
import type { RedisClient as Client } from "redis"
|
|
3
3
|
import Redlock from "redlock"
|
|
4
4
|
|
|
@@ -90,7 +90,7 @@ export const makeRedisClient = (makeClient: () => Client) =>
|
|
|
90
90
|
.pipe(Effect.uninterruptible, Effect.orDie)
|
|
91
91
|
)
|
|
92
92
|
|
|
93
|
-
export class RedisClient extends
|
|
93
|
+
export class RedisClient extends Context.Service<RedisClient, {
|
|
94
94
|
readonly client: Client
|
|
95
95
|
readonly lock: Redlock
|
|
96
96
|
readonly get: (key: string) => Effect.Effect<Option.Option<string>, ConnectionException>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { Effect, Layer, type NonEmptyReadonlyArray, pipe, type Scope
|
|
2
|
+
import { Context, Effect, Layer, type NonEmptyReadonlyArray, pipe, type Scope } from "effect-app"
|
|
3
3
|
|
|
4
4
|
import { type HttpRouter } from "effect-app/http"
|
|
5
5
|
import { type EffectGenUtils } from "effect-app/utils/gen"
|
|
@@ -23,7 +23,7 @@ type TDepsArr<TDeps extends ReadonlyArray<any>> = {
|
|
|
23
23
|
// E = never => the context provided cannot trigger errors
|
|
24
24
|
// TODO: remove HttpLayerRouter.Provided - it's not even relevant outside of Http context, while ContextProviders are for anywhere. Only support Scope.Scope?
|
|
25
25
|
// _R extends HttpLayerRouter.Provided => the context provided can only have what HttpLayerRouter.Provided provides as requirements
|
|
26
|
-
ContextTagWithDefault.Base<Effect.Effect<
|
|
26
|
+
ContextTagWithDefault.Base<Effect.Effect<Context.Context<infer _1>, never, infer _R>> // & { _tag: infer _2 }>
|
|
27
27
|
? [_R] extends [HttpRouter.Provided] ? TDeps[K]
|
|
28
28
|
: `HttpLayerRouter.Provided is the only requirement ${TDeps[K]["Service"][
|
|
29
29
|
"_tag"
|
|
@@ -58,9 +58,9 @@ export const mergeContextProviders = <
|
|
|
58
58
|
Effect.Effect<
|
|
59
59
|
// we need to merge all contexts into one
|
|
60
60
|
// v4: Service.Shape extracts the service value type (v3's Tag.Identifier)
|
|
61
|
-
|
|
61
|
+
Context.Context<GetContext<EffectGenUtils.Success<Context.Service.Shape<TDeps[number]>>>>,
|
|
62
62
|
never,
|
|
63
|
-
EffectGenUtils.
|
|
63
|
+
EffectGenUtils.Context<Context.Service.Shape<TDeps[number]>>
|
|
64
64
|
>,
|
|
65
65
|
LayerUtils.GetLayersError<{ [K in keyof TDeps]: TDeps[K]["Default"] }>,
|
|
66
66
|
LayerUtils.GetLayersSuccess<{ [K in keyof TDeps]: TDeps[K]["Default"] }>
|
|
@@ -78,14 +78,14 @@ export const mergeContextProviders = <
|
|
|
78
78
|
handle: handle[Symbol.toStringTag] === "GeneratorFunction" ? Effect.fnUntraced(handle)() : handle
|
|
79
79
|
}
|
|
80
80
|
))
|
|
81
|
-
// services are effects which return some
|
|
81
|
+
// services are effects which return some Context.Context<...>
|
|
82
82
|
const context = yield* mergeContexts(services as any)
|
|
83
83
|
return context
|
|
84
84
|
})
|
|
85
85
|
}) as any
|
|
86
86
|
})
|
|
87
87
|
|
|
88
|
-
// Effect Rpc Middleware: for single tag providing, we could use Provides, for providing
|
|
88
|
+
// Effect Rpc Middleware: for single tag providing, we could use Provides, for providing Context or Layer (bad boy) we could use Wrap..
|
|
89
89
|
export const ContextProvider = <
|
|
90
90
|
ContextProviderA,
|
|
91
91
|
MakeContextProviderE,
|
|
@@ -107,7 +107,7 @@ export const ContextProvider = <
|
|
|
107
107
|
dependencies?: Dependencies
|
|
108
108
|
}
|
|
109
109
|
) => {
|
|
110
|
-
const ctx =
|
|
110
|
+
const ctx = Context.Service<
|
|
111
111
|
ContextProviderId,
|
|
112
112
|
Effect.Effect<ContextProviderA, never, ContextProviderR>
|
|
113
113
|
>(
|
|
@@ -146,17 +146,17 @@ export const MergedContextProvider = <
|
|
|
146
146
|
Effect.Effect<
|
|
147
147
|
// we need to merge all contexts into one
|
|
148
148
|
// v4: Service.Shape extracts the service value type (v3's Tag.Identifier)
|
|
149
|
-
|
|
149
|
+
Context.Context<GetContext<EffectGenUtils.Success<Context.Service.Shape<TDeps[number]>>>>,
|
|
150
150
|
never,
|
|
151
|
-
EffectGenUtils.
|
|
151
|
+
EffectGenUtils.Context<Context.Service.Shape<TDeps[number]>>
|
|
152
152
|
>,
|
|
153
153
|
LayerUtils.GetLayersError<{ [K in keyof TDeps]: TDeps[K]["Default"] }>,
|
|
154
154
|
// v4: Identifier here is correct — it's the nominal service identity for layer provide/exclude
|
|
155
155
|
| Exclude<
|
|
156
|
-
|
|
156
|
+
Context.Service.Identifier<TDeps[number]>,
|
|
157
157
|
LayerUtils.GetLayersSuccess<{ [K in keyof TDeps]: TDeps[K]["Default"] }>
|
|
158
158
|
>
|
|
159
159
|
| LayerUtils.GetLayersContext<{ [K in keyof TDeps]: TDeps[K]["Default"] }>
|
|
160
160
|
>
|
|
161
161
|
|
|
162
|
-
export const EmptyContextProvider = ContextProvider({ effect: Effect.succeed(Effect.succeed(
|
|
162
|
+
export const EmptyContextProvider = ContextProvider({ effect: Effect.succeed(Effect.succeed(Context.empty())) })
|
|
@@ -16,7 +16,7 @@ export const makeSSE = <A extends { id: any }, SI, SR>(
|
|
|
16
16
|
Effect
|
|
17
17
|
.gen(function*() {
|
|
18
18
|
const id = connId++
|
|
19
|
-
const ctx = yield* Effect.
|
|
19
|
+
const ctx = yield* Effect.context<R | SR>()
|
|
20
20
|
const res = HttpServerResponse.stream(
|
|
21
21
|
// workaround for different scoped behaviour for streams in Bun
|
|
22
22
|
// https://discord.com/channels/795981131316985866/1098177242598756412/1389646879675125861
|
|
@@ -28,19 +28,20 @@ export const makeSSE = <A extends { id: any }, SI, SR>(
|
|
|
28
28
|
|
|
29
29
|
const enc = new TextEncoder()
|
|
30
30
|
|
|
31
|
-
const encode = S.encodeEffect(schema)
|
|
31
|
+
const encode = S.encodeEffect(S.fromJsonString(S.toCodecJson(schema)))
|
|
32
32
|
|
|
33
33
|
const eventStream = Stream.mapEffect(
|
|
34
34
|
events,
|
|
35
35
|
(_) =>
|
|
36
36
|
encode(_.evt)
|
|
37
|
-
.pipe(Effect.map((
|
|
37
|
+
.pipe(Effect.map((data) => `id: ${_.evt.id}\ndata: ${data}`))
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
const stream = pipe(
|
|
41
41
|
setRetry,
|
|
42
42
|
Stream.merge(keepAlive),
|
|
43
|
-
|
|
43
|
+
// Keep this unary so pipe receives a function, not a Stream value.
|
|
44
|
+
(self) => Stream.merge(self, eventStream, { haltStrategy: "either" }),
|
|
44
45
|
Stream.tapCause((cause) => Effect.logError("SSE error", cause)),
|
|
45
46
|
Stream.map((_) => enc.encode(_ + "\n\n"))
|
|
46
47
|
)
|
package/src/api/layerUtils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { Effect, type Layer, type NonEmptyReadonlyArray, Option
|
|
2
|
+
import { Context, Effect, type Layer, type NonEmptyReadonlyArray, Option } from "effect-app"
|
|
3
3
|
import { InfraLogger } from "../logger.js"
|
|
4
4
|
|
|
5
5
|
// TODO: These LayerUtils are flaky, like in dependencies as a readonly array, it breaks when there are two entries
|
|
@@ -27,7 +27,7 @@ export namespace LayerUtils {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export type ContextTagWithDefault<Id, A, LayerE, LayerR> =
|
|
30
|
-
&
|
|
30
|
+
& Context.Service<Id, A>
|
|
31
31
|
& {
|
|
32
32
|
Default: Layer.Layer<Id, LayerE, LayerR>
|
|
33
33
|
}
|
|
@@ -36,29 +36,29 @@ export namespace ContextTagWithDefault {
|
|
|
36
36
|
export type Base<A> = ContextTagWithDefault<any, A, any, any>
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export type GetContext<T> = T extends
|
|
39
|
+
export type GetContext<T> = T extends Context.Context<infer Y> ? Y : never
|
|
40
40
|
|
|
41
41
|
export const mergeContexts = Effect.fnUntraced(
|
|
42
42
|
function*<
|
|
43
43
|
T extends readonly {
|
|
44
44
|
maker: any
|
|
45
|
-
handle: Effect.Effect<
|
|
45
|
+
handle: Effect.Effect<Context.Context<any> | Option.Option<Context.Context<any>>>
|
|
46
46
|
}[]
|
|
47
47
|
>(
|
|
48
48
|
makers: T
|
|
49
49
|
) {
|
|
50
|
-
let context =
|
|
50
|
+
let context = Context.empty()
|
|
51
51
|
for (const mw of makers) {
|
|
52
52
|
const ctx = yield* mw.handle.pipe(Effect.provide(context))
|
|
53
|
-
const moreContext =
|
|
53
|
+
const moreContext = Context.isContext(ctx) ? Option.some(ctx) : ctx
|
|
54
54
|
yield* InfraLogger.logDebug(
|
|
55
55
|
"Built dynamic context for middleware" + (mw.maker.key ?? mw.maker),
|
|
56
56
|
Option.map(moreContext, (c) => (c as any).toJSON().services)
|
|
57
57
|
)
|
|
58
58
|
if (moreContext.value) {
|
|
59
|
-
context =
|
|
59
|
+
context = Context.merge(context, moreContext.value)
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
return context as
|
|
62
|
+
return context as Context.Context<Effect.Success<T[number]["handle"]>>
|
|
63
63
|
}
|
|
64
64
|
)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
|
-
import { type
|
|
4
|
+
import { type Context, type Layer } from "effect-app"
|
|
5
5
|
import { type GetContextConfig, type RpcContextMap } from "effect-app/rpc/RpcContextMap"
|
|
6
6
|
import { type RpcMiddlewareV4 } from "effect-app/rpc/RpcMiddleware"
|
|
7
7
|
// module:
|
|
8
8
|
//
|
|
9
9
|
|
|
10
|
-
// v4: middleware tags are
|
|
10
|
+
// v4: middleware tags are Context.Service (not Effect) — they carry the RpcMiddlewareV4 as their service Shape
|
|
11
11
|
export type RouterMiddleware<
|
|
12
12
|
Self,
|
|
13
13
|
RequestContextMap extends Record<string, RpcContextMap.Any>, // what services will the middlware provide dynamically to the next, or raise errors.
|
|
@@ -18,9 +18,9 @@ export type RouterMiddleware<
|
|
|
18
18
|
_ContextProviderR, // what the context provider requires
|
|
19
19
|
RequestContextId
|
|
20
20
|
> =
|
|
21
|
-
&
|
|
21
|
+
& Context.Service<Self, RpcMiddlewareV4<ContextProviderA, ContextProviderE, never>>
|
|
22
22
|
& {
|
|
23
23
|
readonly Default: Layer.Layer<Self, MakeMiddlewareE, MakeMiddlewareR>
|
|
24
|
-
readonly requestContext:
|
|
24
|
+
readonly requestContext: Context.Service<RequestContextId, GetContextConfig<RequestContextMap>>
|
|
25
25
|
readonly requestContextMap: RequestContextMap
|
|
26
26
|
}
|
|
@@ -15,8 +15,7 @@ export const parseJwt = <Sch extends S.Top>(
|
|
|
15
15
|
(s, _options) =>
|
|
16
16
|
Effect.try({
|
|
17
17
|
try: () => jwtDecode(s, options),
|
|
18
|
-
catch: (e: any) =>
|
|
19
|
-
new S.SchemaIssue.InvalidValue(Option.some(s), { message: e?.message })
|
|
18
|
+
catch: (e: any) => new S.SchemaIssue.InvalidValue(Option.some(s), { message: e?.message })
|
|
20
19
|
})
|
|
21
20
|
)
|
|
22
|
-
.pipe(S.decodeTo(schema) as any)
|
|
21
|
+
.pipe(S.decodeTo(schema) as any)
|
package/src/api/routing.ts
CHANGED
|
@@ -194,7 +194,8 @@ export const makeRouter = <
|
|
|
194
194
|
> = (
|
|
195
195
|
req: S.Schema.Type<Action>
|
|
196
196
|
) => Generator<
|
|
197
|
-
Yieldable<
|
|
197
|
+
Yieldable<
|
|
198
|
+
any,
|
|
198
199
|
any,
|
|
199
200
|
S.Schema.Type<GetFailure<Action>> | S.SchemaError,
|
|
200
201
|
// the actual implementation of the handler may just require the dynamic context provided by the middleware
|
|
@@ -319,7 +320,7 @@ export const makeRouter = <
|
|
|
319
320
|
? Impl[K]["raw"] extends (...args: any[]) => Effect.Effect<any, any, infer R> ? R
|
|
320
321
|
: Impl[K]["raw"] extends Effect.Effect<any, any, infer R> ? R
|
|
321
322
|
: Impl[K]["raw"] extends (...args: any[]) => Generator<
|
|
322
|
-
Yieldable<any,any, any, infer R>,
|
|
323
|
+
Yieldable<any, any, any, infer R>,
|
|
323
324
|
any,
|
|
324
325
|
any
|
|
325
326
|
> ? R
|
|
@@ -327,7 +328,7 @@ export const makeRouter = <
|
|
|
327
328
|
: Impl[K] extends (...args: any[]) => Effect.Effect<any, any, infer R> ? R
|
|
328
329
|
: Impl[K] extends Effect.Effect<any, any, infer R> ? R
|
|
329
330
|
: Impl[K] extends (...args: any[]) => Generator<
|
|
330
|
-
Yieldable<any,any, any, infer R>,
|
|
331
|
+
Yieldable<any, any, any, infer R>,
|
|
331
332
|
any,
|
|
332
333
|
any
|
|
333
334
|
> ? R
|
package/src/errorReporter.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Sentry from "@sentry/node"
|
|
2
|
-
import { Cause, Effect, LogLevel } from "effect-app"
|
|
2
|
+
import { Cause, Effect, type LogLevel } from "effect-app"
|
|
3
3
|
import { dropUndefined, LogLevelToSentry } from "effect-app/utils"
|
|
4
4
|
import { getRC } from "./api/setupRequest.js"
|
|
5
5
|
import { CauseException, tryToJson, tryToReport } from "./errors.js"
|
package/src/fileUtil.ts
CHANGED
|
@@ -119,7 +119,7 @@ export function withFileLock<A, E, R>(
|
|
|
119
119
|
// ensure lock is released
|
|
120
120
|
yield* Effect.addFinalizer(() =>
|
|
121
121
|
Effect
|
|
122
|
-
|
|
122
|
+
// we have to make sure we use a thunk, or the library will cause problems because effect passes abortsignal as first argument
|
|
123
123
|
.tryPromise(() => release())
|
|
124
124
|
.pipe(Effect.orDie)
|
|
125
125
|
)
|
package/src/rateLimit.ts
CHANGED
|
@@ -56,7 +56,7 @@ export function batchPar<R, E, A, R2, E2, A2, T>(
|
|
|
56
56
|
(_, i) =>
|
|
57
57
|
Effect
|
|
58
58
|
.forEach(_, (_, j) => forEachItem(_, j, i), { concurrency: "inherit" })
|
|
59
|
-
.pipe(Effect.flatMap((_) => forEachBatch(_
|
|
59
|
+
.pipe(Effect.flatMap((_) => forEachBatch(_, i))),
|
|
60
60
|
{ concurrency: "inherit" }
|
|
61
61
|
)
|
|
62
62
|
}
|
|
@@ -72,7 +72,7 @@ export function batch<R, E, A, R2, E2, A2, T>(
|
|
|
72
72
|
(_, i) =>
|
|
73
73
|
Effect
|
|
74
74
|
.forEach(_, (_, j) => forEachItem(_, j, i), { concurrency: "inherit" })
|
|
75
|
-
.pipe(Effect.flatMap((_) => forEachBatch(_
|
|
75
|
+
.pipe(Effect.flatMap((_) => forEachBatch(_, i)))
|
|
76
76
|
)
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
3
3
|
import { expectTypeOf, it } from "@effect/vitest"
|
|
4
|
-
import { Effect, Layer, Scope,
|
|
4
|
+
import { Effect, Layer, Scope, Context } from "effect-app"
|
|
5
5
|
import { ContextProvider, mergeContextProviders, MergedContextProvider } from "../src/api/ContextProvider.js"
|
|
6
6
|
import { CustomError1, Some, SomeElse, SomeService } from "./fixtures.js"
|
|
7
7
|
|
|
8
8
|
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
9
|
-
class MyContextProvider extends
|
|
9
|
+
class MyContextProvider extends Context.Service<MyContextProvider>()(
|
|
10
10
|
"MyContextProvider",
|
|
11
11
|
{
|
|
12
12
|
make: Effect.gen(function*() {
|
|
@@ -28,7 +28,7 @@ class MyContextProvider extends ServiceMap.Service<MyContextProvider>()(
|
|
|
28
28
|
static readonly Default = Layer.effect(this, this.make)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
class MyContextProvider2 extends
|
|
31
|
+
class MyContextProvider2 extends Context.Service<MyContextProvider2>()(
|
|
32
32
|
"MyContextProvider2",
|
|
33
33
|
{
|
|
34
34
|
make: Effect.gen(function*() {
|
|
@@ -45,7 +45,7 @@ class MyContextProvider2 extends ServiceMap.Service<MyContextProvider2>()(
|
|
|
45
45
|
static readonly Default = Layer.effect(this, this.make)
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
class MyContextProvider2Gen extends
|
|
48
|
+
class MyContextProvider2Gen extends Context.Service<MyContextProvider2Gen>()(
|
|
49
49
|
"MyContextProvider2Gen",
|
|
50
50
|
{
|
|
51
51
|
make: Effect.gen(function*() {
|
|
@@ -63,7 +63,7 @@ class MyContextProvider2Gen extends ServiceMap.Service<MyContextProvider2Gen>()(
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
66
|
-
class MyContextProviderGen extends
|
|
66
|
+
class MyContextProviderGen extends Context.Service<MyContextProviderGen>()(
|
|
67
67
|
"MyContextProviderGen",
|
|
68
68
|
{
|
|
69
69
|
make: Effect.gen(function*() {
|