@effect-app/infra 4.0.0-beta.19 → 4.0.0-beta.190
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 +1277 -0
- package/_check.sh +1 -1
- package/dist/CUPS.d.ts +15 -7
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +10 -12
- package/dist/Emailer/Sendgrid.d.ts +14 -14
- package/dist/Emailer/Sendgrid.d.ts.map +1 -1
- package/dist/Emailer/Sendgrid.js +16 -15
- package/dist/Emailer/fake.d.ts +1 -1
- package/dist/Emailer/service.d.ts +9 -3
- package/dist/Emailer/service.d.ts.map +1 -1
- package/dist/Emailer/service.js +3 -3
- package/dist/Emailer.d.ts +1 -1
- package/dist/MainFiberSet.d.ts +5 -5
- 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/ext.d.ts +33 -15
- package/dist/Model/Repository/ext.d.ts.map +1 -1
- package/dist/Model/Repository/ext.js +54 -2
- package/dist/Model/Repository/internal/internal.d.ts +6 -6
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +43 -32
- package/dist/Model/Repository/legacy.d.ts +1 -1
- package/dist/Model/Repository/makeRepo.d.ts +7 -6
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +5 -1
- package/dist/Model/Repository/service.d.ts +28 -23
- package/dist/Model/Repository/service.d.ts.map +1 -1
- package/dist/Model/Repository/validation.d.ts +142 -17
- package/dist/Model/Repository/validation.d.ts.map +1 -1
- package/dist/Model/Repository/validation.js +5 -5
- package/dist/Model/Repository.d.ts +2 -1
- package/dist/Model/Repository.d.ts.map +1 -1
- package/dist/Model/Repository.js +2 -1
- package/dist/Model/dsl.d.ts +4 -4
- package/dist/Model/dsl.d.ts.map +1 -1
- package/dist/Model/filter/filterApi.d.ts +5 -5
- package/dist/Model/filter/filterApi.d.ts.map +1 -1
- package/dist/Model/filter/types/errors.d.ts +1 -1
- package/dist/Model/filter/types/fields.d.ts +1 -1
- package/dist/Model/filter/types/path/common.d.ts +1 -1
- package/dist/Model/filter/types/path/eager.d.ts +1 -1
- package/dist/Model/filter/types/path/eager.d.ts.map +1 -1
- package/dist/Model/filter/types/path/index.d.ts +1 -1
- package/dist/Model/filter/types/utils.d.ts +1 -1
- package/dist/Model/filter/types/validator.d.ts +1 -1
- package/dist/Model/filter/types.d.ts +1 -1
- package/dist/Model/query/dsl.d.ts +1 -1
- package/dist/Model/query/dsl.d.ts.map +1 -1
- package/dist/Model/query/new-kid-interpreter.d.ts +6 -6
- 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/query.d.ts +1 -1
- package/dist/Model.d.ts +2 -1
- package/dist/Model.d.ts.map +1 -1
- package/dist/Model.js +2 -1
- package/dist/Operations.d.ts +6 -6
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Operations.js +56 -59
- package/dist/OperationsRepo.d.ts +11 -29
- package/dist/OperationsRepo.d.ts.map +1 -1
- package/dist/OperationsRepo.js +3 -3
- package/dist/QueueMaker/SQLQueue.d.ts +5 -7
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +105 -114
- package/dist/QueueMaker/errors.d.ts +2 -2
- package/dist/QueueMaker/errors.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.d.ts +7 -4
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +51 -62
- package/dist/QueueMaker/sbqueue.d.ts +6 -3
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +37 -53
- package/dist/QueueMaker/service.d.ts +1 -1
- package/dist/RequestContext.d.ts +114 -26
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +7 -7
- package/dist/RequestFiberSet.d.ts +7 -7
- 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/query.d.ts +1 -1
- package/dist/Store/Cosmos/query.d.ts.map +1 -1
- package/dist/Store/Cosmos/query.js +8 -10
- package/dist/Store/Cosmos.d.ts +1 -1
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +308 -242
- package/dist/Store/Disk.d.ts +2 -2
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +25 -22
- package/dist/Store/Memory.d.ts +4 -4
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +27 -22
- 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 +189 -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/codeFilter.d.ts +1 -1
- package/dist/Store/codeFilter.d.ts.map +1 -1
- package/dist/Store/codeFilter.js +2 -1
- package/dist/Store/index.d.ts +5 -2
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +15 -3
- package/dist/Store/service.d.ts +17 -6
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +24 -6
- package/dist/Store/utils.d.ts +1 -1
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +3 -4
- package/dist/Store.d.ts +1 -1
- package/dist/adapters/SQL/Model.d.ts +28 -42
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +2 -2
- package/dist/adapters/SQL.d.ts +1 -1
- package/dist/adapters/ServiceBus.d.ts +9 -9
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +13 -15
- package/dist/adapters/cosmos-client.d.ts +3 -3
- package/dist/adapters/cosmos-client.d.ts.map +1 -1
- package/dist/adapters/cosmos-client.js +3 -3
- package/dist/adapters/index.d.ts +8 -2
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +8 -2
- package/dist/adapters/logger.d.ts +1 -1
- package/dist/adapters/logger.d.ts.map +1 -1
- package/dist/adapters/memQueue.d.ts +3 -3
- package/dist/adapters/memQueue.d.ts.map +1 -1
- package/dist/adapters/memQueue.js +3 -3
- package/dist/adapters/mongo-client.d.ts +3 -3
- 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 +7 -7
- package/dist/api/ContextProvider.d.ts.map +1 -1
- package/dist/api/ContextProvider.js +6 -6
- package/dist/api/codec.d.ts +1 -1
- package/dist/api/internal/RequestContextMiddleware.d.ts +2 -2
- package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
- package/dist/api/internal/RequestContextMiddleware.js +2 -2
- package/dist/api/internal/auth.d.ts +44 -6
- package/dist/api/internal/auth.d.ts.map +1 -1
- package/dist/api/internal/auth.js +160 -29
- package/dist/api/internal/events.d.ts +3 -3
- package/dist/api/internal/events.d.ts.map +1 -1
- package/dist/api/internal/events.js +9 -7
- package/dist/api/internal/health.d.ts +1 -1
- package/dist/api/layerUtils.d.ts +6 -6
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +5 -5
- package/dist/api/middlewares.d.ts +1 -1
- package/dist/api/reportError.d.ts +1 -1
- package/dist/api/routing/middleware/RouterMiddleware.d.ts +4 -4
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.d.ts +50 -4
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +75 -17
- package/dist/api/routing/middleware.d.ts +1 -2
- package/dist/api/routing/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware.js +1 -2
- 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/tsort.d.ts +1 -1
- package/dist/api/routing/tsort.d.ts.map +1 -1
- package/dist/api/routing/utils.d.ts +3 -3
- package/dist/api/routing/utils.d.ts.map +1 -1
- package/dist/api/routing.d.ts +26 -18
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +70 -11
- package/dist/api/setupRequest.d.ts +8 -5
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +12 -7
- package/dist/api/util.d.ts +1 -1
- package/dist/arbs.d.ts +1 -1
- package/dist/arbs.d.ts.map +1 -1
- package/dist/arbs.js +5 -3
- package/dist/errorReporter.d.ts +4 -4
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +20 -25
- package/dist/errors.d.ts +1 -1
- package/dist/fileUtil.d.ts +1 -1
- package/dist/fileUtil.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/logger/jsonLogger.d.ts +1 -1
- package/dist/logger/logFmtLogger.d.ts +1 -1
- package/dist/logger/shared.d.ts +1 -1
- package/dist/logger/shared.js +2 -2
- package/dist/logger.d.ts +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/rateLimit.d.ts +9 -3
- package/dist/rateLimit.d.ts.map +1 -1
- package/dist/rateLimit.js +5 -11
- package/dist/test.d.ts +1 -1
- package/dist/test.d.ts.map +1 -1
- package/dist/vitest.d.ts +1 -1
- package/eslint.config.mjs +3 -3
- package/examples/query.ts +39 -35
- package/package.json +42 -36
- package/src/CUPS.ts +9 -11
- package/src/Emailer/Sendgrid.ts +17 -14
- package/src/Emailer/service.ts +8 -2
- package/src/MainFiberSet.ts +3 -3
- package/src/Model/Repository/Registry.ts +33 -0
- package/src/Model/Repository/ext.ts +93 -6
- package/src/Model/Repository/internal/internal.ts +97 -88
- package/src/Model/Repository/makeRepo.ts +12 -10
- package/src/Model/Repository/service.ts +31 -22
- package/src/Model/Repository/validation.ts +4 -4
- package/src/Model/Repository.ts +1 -0
- package/src/Model/dsl.ts +3 -3
- package/src/Model/query/new-kid-interpreter.ts +2 -2
- package/src/Model.ts +1 -0
- package/src/QueueMaker/SQLQueue.ts +121 -151
- package/src/QueueMaker/memQueue.ts +82 -103
- package/src/QueueMaker/sbqueue.ts +56 -86
- package/src/RequestContext.ts +8 -8
- package/src/RequestFiberSet.ts +4 -4
- package/src/Store/ContextMapContainer.ts +41 -2
- package/src/Store/Cosmos/query.ts +9 -11
- package/src/Store/Cosmos.ts +437 -343
- package/src/Store/Disk.ts +52 -49
- package/src/Store/Memory.ts +54 -48
- package/src/Store/SQL/Pg.ts +318 -0
- package/src/Store/SQL/query.ts +409 -0
- package/src/Store/SQL.ts +668 -0
- package/src/Store/codeFilter.ts +1 -0
- package/src/Store/index.ts +17 -2
- package/src/Store/service.ts +31 -7
- package/src/Store/utils.ts +23 -22
- package/src/adapters/SQL/Model.ts +10 -4
- package/src/adapters/ServiceBus.ts +111 -115
- package/src/adapters/cosmos-client.ts +2 -2
- package/src/adapters/index.ts +7 -0
- 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/RequestContextMiddleware.ts +1 -1
- package/src/api/internal/auth.ts +246 -44
- package/src/api/internal/events.ts +12 -8
- package/src/api/layerUtils.ts +8 -8
- package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
- package/src/api/routing/middleware/middleware.ts +109 -15
- package/src/api/routing/middleware.ts +0 -2
- package/src/api/routing.ts +154 -13
- package/src/api/setupRequest.ts +28 -8
- package/src/arbs.ts +4 -2
- package/src/errorReporter.ts +62 -74
- package/src/logger/shared.ts +1 -1
- package/src/rateLimit.ts +30 -22
- package/test/auth.test.ts +101 -0
- package/test/contextProvider.test.ts +11 -11
- package/test/controller.test.ts +18 -14
- package/test/dist/auth.test.d.ts.map +1 -0
- 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 +26 -12
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +12 -10
- package/test/dist/query.test.d.ts.map +1 -1
- package/test/dist/rawQuery.test.d.ts.map +1 -1
- package/test/dist/repository-ext.test.d.ts.map +1 -0
- package/test/dist/requires.test.d.ts.map +1 -1
- package/test/dist/router-generator.test.d.ts.map +1 -0
- package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
- package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
- package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
- package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
- package/test/dist/sql-store.test.d.ts.map +1 -0
- package/test/fixtures.ts +11 -9
- package/test/query.test.ts +216 -34
- package/test/rawQuery.test.ts +23 -19
- package/test/repository-ext.test.ts +60 -0
- package/test/requires.test.ts +6 -6
- package/test/router-generator.test.ts +180 -0
- package/test/routing-interruptibility.test.ts +63 -0
- package/test/rpc-e2e-invalidation.test.ts +507 -0
- package/test/rpc-multi-middleware.test.ts +78 -9
- package/test/rpc-stream-fullstack.test.ts +181 -0
- package/test/sql-store.test.ts +1064 -0
- package/test/validateSample.test.ts +15 -12
- package/tsconfig.examples.json +1 -1
- package/tsconfig.json +0 -1
- package/tsconfig.json.bak +2 -2
- package/tsconfig.src.json +35 -35
- package/tsconfig.test.json +2 -2
- package/src/Operations.ts +0 -235
- package/src/OperationsRepo.ts +0 -16
package/src/api/routing.ts
CHANGED
|
@@ -2,22 +2,30 @@
|
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
|
-
import { Config, Effect, Layer, type NonEmptyReadonlyArray, Predicate, S, type Scope } from "effect-app"
|
|
5
|
+
import { Config, Effect, Layer, type NonEmptyReadonlyArray, Predicate, Ref, S, type Scope, Stream } from "effect-app"
|
|
6
|
+
import { getMeta } from "effect-app/client"
|
|
6
7
|
import { type HttpHeaders } from "effect-app/http"
|
|
8
|
+
import { Invalidation } from "effect-app/rpc"
|
|
7
9
|
import { type GetEffectContext, type GetEffectError, type RpcContextMap } from "effect-app/rpc/RpcContextMap"
|
|
8
10
|
import { type TypeTestId } from "effect-app/TypeTest"
|
|
9
11
|
import { typedKeysOf, typedValuesOf } from "effect-app/utils"
|
|
10
12
|
import { type Yieldable } from "effect/Effect"
|
|
11
13
|
import { Rpc, RpcGroup, type RpcSerialization, RpcServer } from "effect/unstable/rpc"
|
|
12
14
|
import { type LayerUtils } from "./layerUtils.js"
|
|
13
|
-
import { type RouterMiddleware } from "./routing/middleware.js"
|
|
15
|
+
import { RequestType as RequestTypeAnnotation, type RouterMiddleware } from "./routing/middleware.js"
|
|
14
16
|
|
|
15
17
|
export * from "./routing/middleware.js"
|
|
16
18
|
|
|
19
|
+
export const applyRequestTypeInterruptibility = <A, E, R>(
|
|
20
|
+
requestType: "command" | "query",
|
|
21
|
+
effect: Effect.Effect<A, E, R>
|
|
22
|
+
) => requestType === "command" ? Rpc.uninterruptible(effect) : effect
|
|
23
|
+
|
|
17
24
|
// it's the result of extending S.Req setting success, config
|
|
18
25
|
// it's a schema plus some metadata
|
|
19
26
|
export type AnyRequestModule = S.Top & {
|
|
20
27
|
_tag: string // unique identifier for the request module
|
|
28
|
+
type: "command" | "query" | "stream"
|
|
21
29
|
config: any // ?
|
|
22
30
|
success: S.Top // validates the success response
|
|
23
31
|
error: S.Top // validates the failure response
|
|
@@ -65,7 +73,10 @@ interface HandlerBase<Action extends AnyRequestModule, RT extends RequestType, A
|
|
|
65
73
|
new(): {}
|
|
66
74
|
_tag: RT
|
|
67
75
|
stack: string
|
|
68
|
-
handler: (
|
|
76
|
+
handler: (
|
|
77
|
+
req: S.Schema.Type<Action>,
|
|
78
|
+
headers: HttpHeaders.Headers
|
|
79
|
+
) => Effect.Effect<A, E, R> | Stream.Stream<A, E, R>
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
export interface Handler<Action extends AnyRequestModule, RT extends RequestType, R> extends
|
|
@@ -126,6 +137,28 @@ type Match<
|
|
|
126
137
|
Scope.Scope
|
|
127
138
|
>
|
|
128
139
|
>
|
|
140
|
+
|
|
141
|
+
<A extends GetSuccessShape<Resource[Key], RT>, R2 = never, E = never>(
|
|
142
|
+
f: Stream.Stream<A, E, R2>
|
|
143
|
+
): Handler<
|
|
144
|
+
Resource[Key],
|
|
145
|
+
RT,
|
|
146
|
+
Exclude<
|
|
147
|
+
Exclude<R2, GetEffectContext<RequestContextMap, Resource[Key]["config"]>>,
|
|
148
|
+
Scope.Scope
|
|
149
|
+
>
|
|
150
|
+
>
|
|
151
|
+
|
|
152
|
+
<A extends GetSuccessShape<Resource[Key], RT>, R2 = never, E = never>(
|
|
153
|
+
f: (req: S.Schema.Type<Resource[Key]>) => Stream.Stream<A, E, R2>
|
|
154
|
+
): Handler<
|
|
155
|
+
Resource[Key],
|
|
156
|
+
RT,
|
|
157
|
+
Exclude<
|
|
158
|
+
Exclude<R2, GetEffectContext<RequestContextMap, Resource[Key]["config"]>>,
|
|
159
|
+
Scope.Scope
|
|
160
|
+
>
|
|
161
|
+
>
|
|
129
162
|
}
|
|
130
163
|
|
|
131
164
|
export type RouteMatcher<
|
|
@@ -182,10 +215,9 @@ export const makeRouter = <
|
|
|
182
215
|
* if `check` is provided, the router will only be created if the effect succeeds with true
|
|
183
216
|
*/
|
|
184
217
|
function matchFor<
|
|
185
|
-
const ModuleName extends string,
|
|
186
218
|
const Resource extends Record<string, any>
|
|
187
219
|
>(
|
|
188
|
-
rsc: Resource
|
|
220
|
+
rsc: Resource,
|
|
189
221
|
options?: { check?: Effect.Effect<boolean> }
|
|
190
222
|
) {
|
|
191
223
|
type HandlerWithInputGen<
|
|
@@ -230,10 +262,32 @@ export const makeRouter = <
|
|
|
230
262
|
GetEffectContext<RequestContextMap, Action["config"]> | ContextProviderA
|
|
231
263
|
>
|
|
232
264
|
|
|
265
|
+
type HandlerWithInputStream<
|
|
266
|
+
Action extends AnyRequestModule,
|
|
267
|
+
RT extends RequestType
|
|
268
|
+
> = (
|
|
269
|
+
req: S.Schema.Type<Action>
|
|
270
|
+
) => Stream.Stream<
|
|
271
|
+
GetSuccessShape<Action, RT>,
|
|
272
|
+
S.Schema.Type<GetFailure<Action>> | S.SchemaError,
|
|
273
|
+
GetEffectContext<RequestContextMap, Action["config"]> | ContextProviderA
|
|
274
|
+
>
|
|
275
|
+
|
|
276
|
+
type HandlerStream<
|
|
277
|
+
Action extends AnyRequestModule,
|
|
278
|
+
RT extends RequestType
|
|
279
|
+
> = Stream.Stream<
|
|
280
|
+
GetSuccessShape<Action, RT>,
|
|
281
|
+
S.Schema.Type<GetFailure<Action>> | S.SchemaError,
|
|
282
|
+
GetEffectContext<RequestContextMap, Action["config"]> | ContextProviderA
|
|
283
|
+
>
|
|
284
|
+
|
|
233
285
|
type Handlers<Action extends AnyRequestModule, RT extends RequestType> =
|
|
234
286
|
| HandlerWithInputGen<Action, RT>
|
|
235
287
|
| HandlerWithInputEff<Action, RT>
|
|
236
288
|
| HandlerEff<Action, RT>
|
|
289
|
+
| HandlerWithInputStream<Action, RT>
|
|
290
|
+
| HandlerStream<Action, RT>
|
|
237
291
|
|
|
238
292
|
type HandlersDecoded<Action extends AnyRequestModule> = Handlers<Action, RequestTypes.DECODED>
|
|
239
293
|
|
|
@@ -241,15 +295,17 @@ export const makeRouter = <
|
|
|
241
295
|
| { raw: HandlerWithInputGen<Action, RequestTypes.RAW> }
|
|
242
296
|
| { raw: HandlerWithInputEff<Action, RequestTypes.RAW> }
|
|
243
297
|
| { raw: HandlerEff<Action, RequestTypes.RAW> }
|
|
298
|
+
| { raw: HandlerWithInputStream<Action, RequestTypes.RAW> }
|
|
299
|
+
| { raw: HandlerStream<Action, RequestTypes.RAW> }
|
|
244
300
|
|
|
245
301
|
type AnyHandlers<Action extends AnyRequestModule> = HandlersRaw<Action> | HandlersDecoded<Action>
|
|
246
302
|
|
|
247
|
-
const
|
|
303
|
+
const meta = getMeta(rsc)
|
|
248
304
|
|
|
249
305
|
type RequestModules = FilterRequestModules<Resource>
|
|
250
306
|
const requestModules = typedKeysOf(rsc).reduce((acc, cur) => {
|
|
251
307
|
if (Predicate.isObjectKeyword(rsc[cur]) && rsc[cur]["success"]) {
|
|
252
|
-
acc[cur as keyof RequestModules] = rsc[cur]
|
|
308
|
+
acc[cur as keyof RequestModules] = rsc[cur]
|
|
253
309
|
}
|
|
254
310
|
return acc
|
|
255
311
|
}, {} as RequestModules)
|
|
@@ -260,13 +316,16 @@ export const makeRouter = <
|
|
|
260
316
|
// handlerImpl is the actual handler implementation
|
|
261
317
|
if (handlerImpl[Symbol.toStringTag] === "GeneratorFunction") handlerImpl = Effect.fnUntraced(handlerImpl)
|
|
262
318
|
const stack = new Error().stack?.split("\n").slice(2).join("\n")
|
|
263
|
-
|
|
319
|
+
const isValueShape = Effect.isEffect(handlerImpl) || Stream.isStream(handlerImpl)
|
|
320
|
+
return isValueShape
|
|
321
|
+
// oxlint-disable-next-line typescript/no-extraneous-class
|
|
264
322
|
? class {
|
|
265
323
|
static request = rsc[cur]
|
|
266
324
|
static stack = stack
|
|
267
325
|
static _tag = RequestTypes.DECODED
|
|
268
326
|
static handler = () => handlerImpl
|
|
269
327
|
}
|
|
328
|
+
// oxlint-disable-next-line typescript/no-extraneous-class
|
|
270
329
|
: class {
|
|
271
330
|
static request = rsc[cur]
|
|
272
331
|
static stack = stack
|
|
@@ -283,13 +342,16 @@ export const makeRouter = <
|
|
|
283
342
|
(handlerImpl: any) => {
|
|
284
343
|
if (handlerImpl[Symbol.toStringTag] === "GeneratorFunction") handlerImpl = Effect.fnUntraced(handlerImpl)
|
|
285
344
|
const stack = new Error().stack?.split("\n").slice(2).join("\n")
|
|
286
|
-
|
|
345
|
+
const isValueShape = Effect.isEffect(handlerImpl) || Stream.isStream(handlerImpl)
|
|
346
|
+
return isValueShape
|
|
347
|
+
// oxlint-disable-next-line typescript/no-extraneous-class
|
|
287
348
|
? class {
|
|
288
349
|
static request = rsc[cur]
|
|
289
350
|
static stack = stack
|
|
290
351
|
static _tag = RequestTypes.RAW
|
|
291
352
|
static handler = () => handlerImpl
|
|
292
353
|
}
|
|
354
|
+
// oxlint-disable-next-line typescript/no-extraneous-class
|
|
293
355
|
: class {
|
|
294
356
|
static request = rsc[cur]
|
|
295
357
|
static stack = stack
|
|
@@ -319,6 +381,8 @@ export const makeRouter = <
|
|
|
319
381
|
Impl[K] extends { raw: any }
|
|
320
382
|
? Impl[K]["raw"] extends (...args: any[]) => Effect.Effect<any, any, infer R> ? R
|
|
321
383
|
: Impl[K]["raw"] extends Effect.Effect<any, any, infer R> ? R
|
|
384
|
+
: Impl[K]["raw"] extends (...args: any[]) => Stream.Stream<any, any, infer R> ? R
|
|
385
|
+
: Impl[K]["raw"] extends Stream.Stream<any, any, infer R> ? R
|
|
322
386
|
: Impl[K]["raw"] extends (...args: any[]) => Generator<
|
|
323
387
|
Yieldable<any, any, any, infer R>,
|
|
324
388
|
any,
|
|
@@ -327,6 +391,8 @@ export const makeRouter = <
|
|
|
327
391
|
: never
|
|
328
392
|
: Impl[K] extends (...args: any[]) => Effect.Effect<any, any, infer R> ? R
|
|
329
393
|
: Impl[K] extends Effect.Effect<any, any, infer R> ? R
|
|
394
|
+
: Impl[K] extends (...args: any[]) => Stream.Stream<any, any, infer R> ? R
|
|
395
|
+
: Impl[K] extends Stream.Stream<any, any, infer R> ? R
|
|
330
396
|
: Impl[K] extends (...args: any[]) => Generator<
|
|
331
397
|
Yieldable<any, any, any, infer R>,
|
|
332
398
|
any,
|
|
@@ -386,12 +452,68 @@ export const makeRouter = <
|
|
|
386
452
|
static success = S.toEncoded(resource.success)
|
|
387
453
|
} as any
|
|
388
454
|
: resource,
|
|
389
|
-
(payload: any, headers: any) =>
|
|
390
|
-
|
|
455
|
+
(payload: any, headers: any) => {
|
|
456
|
+
const result = handler.handler(payload, headers)
|
|
457
|
+
if (Stream.isStream(result)) {
|
|
458
|
+
// Wrap stream items as { _tag: "value", value } and append a final
|
|
459
|
+
// { _tag: "done", metadata } chunk carrying accumulated invalidation keys.
|
|
460
|
+
// V2: on failure, convert to { _tag: "error", error, metadata } chunk so
|
|
461
|
+
// clients can invalidate queries even when the stream fails.
|
|
462
|
+
const keysRef = Ref.makeUnsafe<ReadonlyArray<Invalidation.InvalidationKey>>([])
|
|
463
|
+
const invalidationSet = Invalidation.makeInvalidationSet(keysRef)
|
|
464
|
+
return Stream.concat(
|
|
465
|
+
(result as Stream.Stream<any, any, any>).pipe(
|
|
466
|
+
Stream.map((item: any) => ({ _tag: "value" as const, value: item })),
|
|
467
|
+
Stream.provideService(Invalidation.InvalidationSet, invalidationSet),
|
|
468
|
+
// V3: after each value chunk, drain accumulated keys and emit a "metadata"
|
|
469
|
+
// chunk if any keys were collected since the last drain. This lets clients
|
|
470
|
+
// invalidate queries mid-stream without waiting for the "done" chunk.
|
|
471
|
+
Stream.flatMap((valueChunk: any) =>
|
|
472
|
+
Stream
|
|
473
|
+
.fromEffect(
|
|
474
|
+
Ref.getAndSet(keysRef, []).pipe(
|
|
475
|
+
Effect.map((keys) =>
|
|
476
|
+
keys.length > 0
|
|
477
|
+
? [
|
|
478
|
+
valueChunk,
|
|
479
|
+
{ _tag: "metadata" as const, metadata: { invalidateQueries: keys } }
|
|
480
|
+
]
|
|
481
|
+
: [valueChunk]
|
|
482
|
+
)
|
|
483
|
+
)
|
|
484
|
+
)
|
|
485
|
+
.pipe(Stream.flatMap(Stream.fromIterable))
|
|
486
|
+
),
|
|
487
|
+
// V2: catch stream failures and embed them in the stream as an error chunk
|
|
488
|
+
Stream.catch((err: any) =>
|
|
489
|
+
Stream.fromEffect(
|
|
490
|
+
Ref.get(keysRef).pipe(
|
|
491
|
+
Effect.flatMap((keys) =>
|
|
492
|
+
Effect.fail({
|
|
493
|
+
_tag: "error" as const,
|
|
494
|
+
error: err,
|
|
495
|
+
metadata: { invalidateQueries: keys }
|
|
496
|
+
})
|
|
497
|
+
)
|
|
498
|
+
)
|
|
499
|
+
)
|
|
500
|
+
)
|
|
501
|
+
),
|
|
502
|
+
Stream.fromEffect(
|
|
503
|
+
Ref.get(keysRef).pipe(
|
|
504
|
+
Effect.map((keys) => ({ _tag: "done" as const, metadata: { invalidateQueries: keys } }))
|
|
505
|
+
)
|
|
506
|
+
)
|
|
507
|
+
)
|
|
508
|
+
}
|
|
509
|
+
const effect = (result as Effect.Effect<unknown, unknown, unknown>).pipe(
|
|
391
510
|
Effect.withSpan(`Request.${meta.moduleName}.${resource._tag}`, {}, {
|
|
392
511
|
captureStackTrace: () => handler.stack // capturing the handler stack is the main reason why we are doing the span here
|
|
393
512
|
})
|
|
394
513
|
)
|
|
514
|
+
|
|
515
|
+
return applyRequestTypeInterruptibility(resource.type, effect)
|
|
516
|
+
}
|
|
395
517
|
] as const
|
|
396
518
|
return acc
|
|
397
519
|
}, {} as any) as {
|
|
@@ -415,9 +537,28 @@ export const makeRouter = <
|
|
|
415
537
|
const rpcs = RpcGroup
|
|
416
538
|
.make(
|
|
417
539
|
...typedValuesOf(mapped).map(([resource]) => {
|
|
418
|
-
|
|
419
|
-
|
|
540
|
+
const isStream = resource.type === "stream"
|
|
541
|
+
const isCommand = resource.type === "command"
|
|
542
|
+
return (isCommand
|
|
543
|
+
? Invalidation.makeCommandRpc(resource._tag, {
|
|
544
|
+
payload: resource,
|
|
545
|
+
success: resource.success,
|
|
546
|
+
error: resource.error
|
|
547
|
+
})
|
|
548
|
+
: isStream
|
|
549
|
+
? Invalidation.makeStreamRpc(resource._tag, {
|
|
550
|
+
payload: resource,
|
|
551
|
+
success: resource.success,
|
|
552
|
+
error: resource.error,
|
|
553
|
+
stream: true as const
|
|
554
|
+
})
|
|
555
|
+
: Rpc.make(resource._tag, {
|
|
556
|
+
payload: resource,
|
|
557
|
+
success: resource.success,
|
|
558
|
+
error: resource.error
|
|
559
|
+
}))
|
|
420
560
|
.annotate(middleware.requestContext, resource.config ?? {})
|
|
561
|
+
.annotate(RequestTypeAnnotation, resource.type)
|
|
421
562
|
})
|
|
422
563
|
)
|
|
423
564
|
.prefix(`${meta.moduleName}.`)
|
package/src/api/setupRequest.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
import { Effect, Layer, Tracer } from "effect-app"
|
|
1
|
+
import { Effect, Layer, Option, Tracer } from "effect-app"
|
|
2
2
|
import { NonEmptyString255 } from "effect-app/Schema"
|
|
3
|
+
import { SqlClient } from "effect/unstable/sql"
|
|
3
4
|
import { LocaleRef, RequestContext, spanAttributes } from "../RequestContext.js"
|
|
4
5
|
import { ContextMapContainer } from "../Store/ContextMapContainer.js"
|
|
5
6
|
import { storeId } from "../Store/Memory.js"
|
|
6
7
|
|
|
8
|
+
const withSqlTransaction = <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
|
|
9
|
+
Effect.serviceOption(SqlClient.SqlClient).pipe(
|
|
10
|
+
Effect.flatMap(Option.match({
|
|
11
|
+
onNone: () => self,
|
|
12
|
+
onSome: (sql) => sql.withTransaction(self).pipe(Effect.orDie)
|
|
13
|
+
}))
|
|
14
|
+
)
|
|
15
|
+
|
|
7
16
|
export const getRequestContext = Effect
|
|
8
17
|
.all({
|
|
9
18
|
span: Effect.currentSpan.pipe(Effect.orDie),
|
|
@@ -12,7 +21,7 @@ export const getRequestContext = Effect
|
|
|
12
21
|
})
|
|
13
22
|
.pipe(
|
|
14
23
|
Effect.map(({ locale, namespace, span }) =>
|
|
15
|
-
|
|
24
|
+
RequestContext.make({
|
|
16
25
|
span: Tracer.externalSpan(span),
|
|
17
26
|
locale,
|
|
18
27
|
namespace,
|
|
@@ -43,16 +52,25 @@ const withRequestSpan = (name = "request", options?: Tracer.SpanOptions) => <R,
|
|
|
43
52
|
)
|
|
44
53
|
)
|
|
45
54
|
|
|
55
|
+
export interface SetupRequestOptions {
|
|
56
|
+
readonly withTransaction?: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
46
59
|
export const setupRequestContextFromCurrent =
|
|
47
|
-
(name = "request", options?: Tracer.SpanOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) =>
|
|
60
|
+
(name = "request", options?: Tracer.SpanOptions & SetupRequestOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) =>
|
|
48
61
|
self
|
|
49
62
|
.pipe(
|
|
63
|
+
options?.withTransaction === true ? withSqlTransaction : (_) => _,
|
|
50
64
|
withRequestSpan(name, options),
|
|
51
|
-
Effect.provide(ContextMapContainer.layer)
|
|
65
|
+
Effect.provide(ContextMapContainer.layer, { local: true })
|
|
52
66
|
)
|
|
53
67
|
|
|
54
68
|
// TODO: consider integrating Effect.withParentSpan
|
|
55
|
-
export function setupRequestContext<R, E, A>(
|
|
69
|
+
export function setupRequestContext<R, E, A>(
|
|
70
|
+
self: Effect.Effect<A, E, R>,
|
|
71
|
+
requestContext: RequestContext,
|
|
72
|
+
options?: SetupRequestOptions
|
|
73
|
+
) {
|
|
56
74
|
const layer = Layer.mergeAll(
|
|
57
75
|
ContextMapContainer.layer,
|
|
58
76
|
Layer.succeed(LocaleRef, requestContext.locale),
|
|
@@ -60,8 +78,9 @@ export function setupRequestContext<R, E, A>(self: Effect.Effect<A, E, R>, reque
|
|
|
60
78
|
)
|
|
61
79
|
return self
|
|
62
80
|
.pipe(
|
|
81
|
+
options?.withTransaction === true ? withSqlTransaction : (_) => _,
|
|
63
82
|
withRequestSpan(requestContext.name),
|
|
64
|
-
Effect.provide(layer)
|
|
83
|
+
Effect.provide(layer, { local: true })
|
|
65
84
|
)
|
|
66
85
|
}
|
|
67
86
|
|
|
@@ -69,7 +88,7 @@ export function setupRequestContextWithCustomSpan<R, E, A>(
|
|
|
69
88
|
self: Effect.Effect<A, E, R>,
|
|
70
89
|
requestContext: RequestContext,
|
|
71
90
|
name: string,
|
|
72
|
-
options?: Tracer.SpanOptions
|
|
91
|
+
options?: Tracer.SpanOptions & SetupRequestOptions
|
|
73
92
|
) {
|
|
74
93
|
const layer = Layer.mergeAll(
|
|
75
94
|
ContextMapContainer.layer,
|
|
@@ -78,7 +97,8 @@ export function setupRequestContextWithCustomSpan<R, E, A>(
|
|
|
78
97
|
)
|
|
79
98
|
return self
|
|
80
99
|
.pipe(
|
|
100
|
+
options?.withTransaction === true ? withSqlTransaction : (_) => _,
|
|
81
101
|
withRequestSpan(name, options),
|
|
82
|
-
Effect.provide(layer)
|
|
102
|
+
Effect.provide(layer, { local: true })
|
|
83
103
|
)
|
|
84
104
|
}
|
package/src/arbs.ts
CHANGED
|
@@ -5,9 +5,11 @@ import { type S } from "effect-app"
|
|
|
5
5
|
import { setFaker } from "effect-app/faker"
|
|
6
6
|
import * as FastCheck from "effect/testing/FastCheck"
|
|
7
7
|
import { Random } from "fast-check"
|
|
8
|
-
import
|
|
8
|
+
import { congruential32 } from "pure-rand/generator/congruential32"
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const seed = 5
|
|
11
|
+
const rng = congruential32(seed)
|
|
12
|
+
const rnd = new Random(rng)
|
|
11
13
|
|
|
12
14
|
setFaker(faker)
|
|
13
15
|
|
package/src/errorReporter.ts
CHANGED
|
@@ -13,47 +13,41 @@ const tryCauseException = <E>(cause: Cause.Cause<E>, name: string): CauseExcepti
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export function reportError(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
const error = tryCauseException(cause, name)
|
|
31
|
-
|
|
32
|
-
yield* reportSentry(error, extras, LogLevelToSentry(level))
|
|
33
|
-
yield* InfraLogger
|
|
34
|
-
.logWithLevel(level, "Reporting error", cause)
|
|
35
|
-
.pipe(
|
|
36
|
-
Effect.annotateLogs(dropUndefined({
|
|
37
|
-
extras,
|
|
38
|
-
error: tryToReport(error),
|
|
39
|
-
cause: tryToJson(cause),
|
|
40
|
-
__error_name__: name
|
|
41
|
-
}))
|
|
42
|
-
)
|
|
43
|
-
.pipe(
|
|
44
|
-
Effect.catchCause((cause) => InfraLogger.logWarning("Failed to log error", cause)),
|
|
45
|
-
Effect.catchCause(() => InfraLogger.logFatal("Failed to log error cause"))
|
|
46
|
-
)
|
|
16
|
+
export function reportError(name: string) {
|
|
17
|
+
return Effect.fnUntraced(
|
|
18
|
+
function*(
|
|
19
|
+
cause: Cause.Cause<unknown>,
|
|
20
|
+
extras?: Record<string, unknown>,
|
|
21
|
+
level: LogLevel.Severity = "Error"
|
|
22
|
+
) {
|
|
23
|
+
if (Cause.hasInterruptsOnly(cause)) {
|
|
24
|
+
yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
const error = tryCauseException(cause, name)
|
|
47
28
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
29
|
+
yield* reportSentry(error, extras, LogLevelToSentry(level))
|
|
30
|
+
yield* InfraLogger
|
|
31
|
+
.logWithLevel(level, "Reporting error", cause)
|
|
32
|
+
.pipe(
|
|
33
|
+
Effect.annotateLogs(dropUndefined({
|
|
34
|
+
extras,
|
|
35
|
+
error: tryToReport(error),
|
|
36
|
+
cause: tryToJson(cause),
|
|
37
|
+
__error_name__: name
|
|
38
|
+
})),
|
|
39
|
+
Effect.catchCause((cause) => InfraLogger.logWarning("Failed to log error", cause)),
|
|
40
|
+
Effect.catchCause(() => InfraLogger.logFatal("Failed to log error cause"))
|
|
55
41
|
)
|
|
56
|
-
|
|
42
|
+
|
|
43
|
+
return error
|
|
44
|
+
},
|
|
45
|
+
(effect) =>
|
|
46
|
+
Effect.tapCause(effect, (cause) =>
|
|
47
|
+
InfraLogger.logError("Failed to report error", cause).pipe(
|
|
48
|
+
Effect.tapCause(() => InfraLogger.logFatal("Failed to log error cause"))
|
|
49
|
+
))
|
|
50
|
+
)
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
function reportSentry(
|
|
@@ -66,45 +60,39 @@ function reportSentry(
|
|
|
66
60
|
scope.setLevel(level)
|
|
67
61
|
if (context) scope.setContext("context", { ...context })
|
|
68
62
|
if (extras) scope.setContext("extras", extras)
|
|
69
|
-
|
|
70
|
-
scope.setContext("
|
|
63
|
+
const squashed = Cause.squash(error.originalCause)
|
|
64
|
+
scope.setContext("mainError", tryToJson(squashed))
|
|
65
|
+
scope.setContext("error", tryToReport(error))
|
|
66
|
+
scope.setContext("cause", tryToJson(error.originalCause))
|
|
71
67
|
Sentry.captureException(error, scope)
|
|
72
68
|
}))
|
|
73
69
|
}
|
|
74
70
|
|
|
75
|
-
export function logError<E>(
|
|
76
|
-
|
|
77
|
-
) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}))
|
|
93
|
-
)
|
|
94
|
-
})
|
|
95
|
-
.pipe(
|
|
96
|
-
Effect.tapCause(() => InfraLogger.logFatal("Failed to log error cause"))
|
|
97
|
-
)
|
|
71
|
+
export function logError<E>(name: string) {
|
|
72
|
+
return Effect.fnUntraced(
|
|
73
|
+
function*(cause: Cause.Cause<E>, extras?: Record<string, unknown>) {
|
|
74
|
+
if (Cause.hasInterruptsOnly(cause)) {
|
|
75
|
+
yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs(dropUndefined({ extras })))
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
yield* InfraLogger
|
|
79
|
+
.logWarning("Logging error", cause)
|
|
80
|
+
.pipe(Effect.annotateLogs(dropUndefined({
|
|
81
|
+
extras,
|
|
82
|
+
cause: tryToJson(cause),
|
|
83
|
+
__error_name__: name
|
|
84
|
+
})))
|
|
85
|
+
},
|
|
86
|
+
(effect) => Effect.tapCause(effect, () => InfraLogger.logFatal("Failed to log error cause"))
|
|
87
|
+
)
|
|
98
88
|
}
|
|
99
89
|
|
|
100
|
-
export
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Sentry.captureMessage(message, scope)
|
|
90
|
+
export const reportMessage = Effect.fnUntraced(function*(message: string, extras?: Record<string, unknown>) {
|
|
91
|
+
const context = yield* getRC
|
|
92
|
+
const scope = new Sentry.Scope()
|
|
93
|
+
if (context) scope.setContext("context", { ...context })
|
|
94
|
+
if (extras) scope.setContext("extras", extras)
|
|
95
|
+
Sentry.captureMessage(message, scope)
|
|
107
96
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
97
|
+
console.warn(message, extras)
|
|
98
|
+
})
|
package/src/logger/shared.ts
CHANGED
|
@@ -7,7 +7,7 @@ export function getRequestContextFromFiber(fiber: Fiber.Fiber<unknown, unknown>)
|
|
|
7
7
|
const span = Option.fromNullishOr(fiber.currentSpan)
|
|
8
8
|
const locale = fiber.getRef(LocaleRef)
|
|
9
9
|
const namespace = fiber.getRef(storeId)
|
|
10
|
-
return
|
|
10
|
+
return RequestContext.make({
|
|
11
11
|
span: Option.map(span, (s) => ({ spanId: s.spanId, traceId: s.traceId, sampled: s.sampled })).pipe(
|
|
12
12
|
Option.getOrElse(() => ({ spanId: "bogus", sampled: true, traceId: "bogus" }))
|
|
13
13
|
),
|
package/src/rateLimit.ts
CHANGED
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
// }
|
|
22
22
|
|
|
23
23
|
import { Array, type Duration, Effect, type NonEmptyArray } from "effect-app"
|
|
24
|
+
import { dual } from "effect-app/Function"
|
|
24
25
|
import type { Semaphore } from "effect/Semaphore"
|
|
26
|
+
import type { Concurrency } from "effect/Types"
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* Executes the specified effect, acquiring the specified number of permits
|
|
@@ -45,36 +47,42 @@ export function SEM_withPermitsDuration(permits: number, duration: Duration.Dura
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
export
|
|
49
|
-
|
|
50
|
-
forEachItem: (item: T, iWithinBatch: number, batchI: number) => Effect.Effect<A, E, R>,
|
|
51
|
-
forEachBatch: (a: NonEmptyArray<A>, i: number) => Effect.Effect<A2, E2, R2>
|
|
52
|
-
) {
|
|
53
|
-
return (items: Iterable<T>) =>
|
|
54
|
-
Effect.forEach(
|
|
55
|
-
Array.chunksOf(items, n),
|
|
56
|
-
(_, i) =>
|
|
57
|
-
Effect
|
|
58
|
-
.forEach(_, (_, j) => forEachItem(_, j, i), { concurrency: "inherit" })
|
|
59
|
-
.pipe(Effect.flatMap((_) => forEachBatch(_, i))),
|
|
60
|
-
{ concurrency: "inherit" }
|
|
61
|
-
)
|
|
50
|
+
export interface BatchOptions {
|
|
51
|
+
readonly concurrency?: Concurrency | undefined
|
|
62
52
|
}
|
|
63
53
|
|
|
64
|
-
export
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
|
|
54
|
+
export const batch: {
|
|
55
|
+
<T, A, E, R, A2, E2, R2>(
|
|
56
|
+
n: number,
|
|
57
|
+
forEachItem: (item: T, iWithinBatch: number, batchI: number) => Effect.Effect<A, E, R>,
|
|
58
|
+
forEachBatch: (a: NonEmptyArray<A>, i: number) => Effect.Effect<A2, E2, R2>,
|
|
59
|
+
options?: BatchOptions
|
|
60
|
+
): (items: Iterable<T>) => Effect.Effect<Array<A2>, E | E2, R | R2>
|
|
61
|
+
<T, A, E, R, A2, E2, R2>(
|
|
62
|
+
items: Iterable<T>,
|
|
63
|
+
n: number,
|
|
64
|
+
forEachItem: (item: T, iWithinBatch: number, batchI: number) => Effect.Effect<A, E, R>,
|
|
65
|
+
forEachBatch: (a: NonEmptyArray<A>, i: number) => Effect.Effect<A2, E2, R2>,
|
|
66
|
+
options?: BatchOptions
|
|
67
|
+
): Effect.Effect<Array<A2>, E | E2, R | R2>
|
|
68
|
+
} = dual(
|
|
69
|
+
(args) => typeof args[0] !== "number",
|
|
70
|
+
<T, A, E, R, A2, E2, R2>(
|
|
71
|
+
items: Iterable<T>,
|
|
72
|
+
n: number,
|
|
73
|
+
forEachItem: (item: T, iWithinBatch: number, batchI: number) => Effect.Effect<A, E, R>,
|
|
74
|
+
forEachBatch: (a: NonEmptyArray<A>, i: number) => Effect.Effect<A2, E2, R2>,
|
|
75
|
+
options?: BatchOptions
|
|
76
|
+
) =>
|
|
70
77
|
Effect.forEach(
|
|
71
78
|
Array.chunksOf(items, n),
|
|
72
79
|
(_, i) =>
|
|
73
80
|
Effect
|
|
74
81
|
.forEach(_, (_, j) => forEachItem(_, j, i), { concurrency: "inherit" })
|
|
75
|
-
.pipe(Effect.flatMap((_) => forEachBatch(_, i)))
|
|
82
|
+
.pipe(Effect.flatMap((_) => forEachBatch(_, i))),
|
|
83
|
+
{ concurrency: options?.concurrency }
|
|
76
84
|
)
|
|
77
|
-
|
|
85
|
+
)
|
|
78
86
|
|
|
79
87
|
// export function rateLimit(
|
|
80
88
|
// n: number,
|