@effect-app/infra 4.0.0-beta.2 → 4.0.0-beta.200
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 +1514 -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 +11 -5
- 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 +9 -9
- 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/eager.js +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 +25 -25
- 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/QueueMaker/SQLQueue.d.ts +6 -8
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +106 -115
- 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 +52 -62
- package/dist/QueueMaker/sbqueue.d.ts +6 -3
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +39 -53
- package/dist/QueueMaker/service.d.ts +1 -1
- package/dist/RequestContext.d.ts +117 -31
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +7 -8
- 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 +20 -4
- 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 +10 -12
- package/dist/Store/Cosmos.d.ts +1 -1
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +318 -240
- 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 +18 -7
- 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 +30 -47
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +22 -14
- package/dist/adapters/SQL.d.ts +1 -1
- package/dist/adapters/ServiceBus.d.ts +11 -11
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +15 -17
- 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 +2 -2
- 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 +4 -4
- package/dist/adapters/redis-client.d.ts.map +1 -1
- package/dist/adapters/redis-client.js +3 -3
- package/dist/api/ContextProvider.d.ts +8 -8
- 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 +45 -7
- 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 +12 -8
- 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 +79 -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 +2 -2
- package/dist/api/routing/schema/jwt.d.ts.map +1 -1
- package/dist/api/routing/schema/jwt.js +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 +32 -35
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +84 -36
- package/dist/api/setupRequest.d.ts +8 -5
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +14 -9
- 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 +5 -5
- 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/fileUtil.js +3 -2
- 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 +2 -2
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +1 -1
- package/dist/vitest.d.ts +1 -1
- package/examples/query.ts +39 -35
- package/package.json +41 -37
- package/src/CUPS.ts +9 -11
- package/src/Emailer/Sendgrid.ts +18 -15
- package/src/Emailer/service.ts +9 -3
- package/src/MainFiberSet.ts +5 -6
- package/src/Model/Repository/Registry.ts +33 -0
- package/src/Model/Repository/ext.ts +96 -10
- 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/filter/types/path/eager.ts +1 -2
- package/src/Model/query/dsl.ts +18 -18
- package/src/Model/query/new-kid-interpreter.ts +2 -2
- package/src/Model.ts +1 -0
- package/src/QueueMaker/SQLQueue.ts +123 -154
- package/src/QueueMaker/memQueue.ts +85 -107
- package/src/QueueMaker/sbqueue.ts +54 -81
- package/src/RequestContext.ts +8 -10
- package/src/RequestFiberSet.ts +4 -4
- package/src/Store/ContextMapContainer.ts +41 -2
- package/src/Store/Cosmos/query.ts +16 -20
- package/src/Store/Cosmos.ts +452 -342
- package/src/Store/Disk.ts +52 -49
- package/src/Store/Memory.ts +55 -51
- 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 +32 -8
- package/src/Store/utils.ts +23 -22
- package/src/adapters/SQL/Model.ts +83 -72
- package/src/adapters/ServiceBus.ts +114 -118
- 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 +12 -13
- package/src/api/internal/RequestContextMiddleware.ts +1 -1
- package/src/api/internal/auth.ts +246 -44
- package/src/api/internal/events.ts +15 -10
- package/src/api/layerUtils.ts +8 -8
- package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
- package/src/api/routing/middleware/middleware.ts +112 -15
- package/src/api/routing/middleware.ts +0 -2
- package/src/api/routing/schema/jwt.ts +2 -3
- package/src/api/routing.ts +153 -79
- package/src/api/setupRequest.ts +30 -10
- package/src/arbs.ts +4 -2
- package/src/errorReporter.ts +63 -75
- package/src/fileUtil.ts +2 -1
- package/src/logger/shared.ts +1 -1
- package/src/rateLimit.ts +30 -22
- package/src/test.ts +1 -1
- package/test/auth.test.ts +101 -0
- package/test/contextProvider.test.ts +11 -11
- package/test/controller.test.ts +27 -21
- 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/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 -36
- 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 +79 -10
- package/test/rpc-stream-fullstack.test.ts +325 -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/dist/Operations.d.ts +0 -55
- package/dist/Operations.d.ts.map +0 -1
- package/dist/Operations.js +0 -102
- package/dist/OperationsRepo.d.ts +0 -41
- package/dist/OperationsRepo.d.ts.map +0 -1
- package/dist/OperationsRepo.js +0 -14
- package/eslint.config.mjs +0 -24
- package/src/Operations.ts +0 -235
- package/src/OperationsRepo.ts +0 -16
package/src/api/routing.ts
CHANGED
|
@@ -2,25 +2,34 @@
|
|
|
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
|
-
export type AnyRequestModule = S.
|
|
26
|
+
export type AnyRequestModule = S.Top & {
|
|
20
27
|
_tag: string // unique identifier for the request module
|
|
28
|
+
type: "command" | "query"
|
|
29
|
+
stream: boolean
|
|
21
30
|
config: any // ?
|
|
22
|
-
success: S.
|
|
23
|
-
error: S.
|
|
31
|
+
success: S.Top // validates the success response
|
|
32
|
+
error: S.Top // validates the failure response
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
// builder pattern for adding actions to a router until all actions are added
|
|
@@ -53,10 +62,10 @@ namespace RequestTypes {
|
|
|
53
62
|
}
|
|
54
63
|
type RequestType = typeof RequestTypes[keyof typeof RequestTypes]
|
|
55
64
|
|
|
56
|
-
type GetSuccess<T> = T extends { success: S.
|
|
57
|
-
type GetFailure<T extends { error?: S.
|
|
65
|
+
type GetSuccess<T> = T extends { success: S.Top } ? T["success"] : typeof S.Void
|
|
66
|
+
type GetFailure<T extends { error?: S.Top }> = T["error"] extends never ? typeof S.Never : T["error"]
|
|
58
67
|
|
|
59
|
-
type GetSuccessShape<Action extends { success?: S.
|
|
68
|
+
type GetSuccessShape<Action extends { success?: S.Top }, RT extends RequestType> = {
|
|
60
69
|
d: S.Schema.Type<GetSuccess<Action>>
|
|
61
70
|
raw: S.Codec.Encoded<GetSuccess<Action>>
|
|
62
71
|
}[RT]
|
|
@@ -65,7 +74,10 @@ interface HandlerBase<Action extends AnyRequestModule, RT extends RequestType, A
|
|
|
65
74
|
new(): {}
|
|
66
75
|
_tag: RT
|
|
67
76
|
stack: string
|
|
68
|
-
handler: (
|
|
77
|
+
handler: (
|
|
78
|
+
req: S.Schema.Type<Action>,
|
|
79
|
+
headers: HttpHeaders.Headers
|
|
80
|
+
) => Effect.Effect<A, E, R> | Stream.Stream<A, E, R>
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
export interface Handler<Action extends AnyRequestModule, RT extends RequestType, R> extends
|
|
@@ -106,7 +118,7 @@ type Match<
|
|
|
106
118
|
> = {
|
|
107
119
|
// note: the defaults of = never prevent the whole router to error (??)
|
|
108
120
|
<A extends GetSuccessShape<Resource[Key], RT>, R2 = never, E = never>(
|
|
109
|
-
f: Effect.Effect<A, E, R2>
|
|
121
|
+
f: (req: S.Schema.Type<Resource[Key]>) => Effect.Effect<A, E, R2>
|
|
110
122
|
): Handler<
|
|
111
123
|
Resource[Key],
|
|
112
124
|
RT,
|
|
@@ -117,7 +129,7 @@ type Match<
|
|
|
117
129
|
>
|
|
118
130
|
|
|
119
131
|
<A extends GetSuccessShape<Resource[Key], RT>, R2 = never, E = never>(
|
|
120
|
-
f: (req: S.Schema.Type<Resource[Key]>) =>
|
|
132
|
+
f: (req: S.Schema.Type<Resource[Key]>) => Stream.Stream<A, E, R2>
|
|
121
133
|
): Handler<
|
|
122
134
|
Resource[Key],
|
|
123
135
|
RT,
|
|
@@ -141,7 +153,7 @@ export type RouteMatcher<
|
|
|
141
153
|
& {
|
|
142
154
|
success: Resource[Key]["success"]
|
|
143
155
|
successRaw: S.Codec<S.Codec.Encoded<Resource[Key]["success"]>>
|
|
144
|
-
error: Resource[Key]["
|
|
156
|
+
error: Resource[Key]["error"]
|
|
145
157
|
/**
|
|
146
158
|
* Requires the Encoded shape (e.g directly undecoded from DB, so that we don't do multiple Decode/Encode)
|
|
147
159
|
*/
|
|
@@ -182,10 +194,9 @@ export const makeRouter = <
|
|
|
182
194
|
* if `check` is provided, the router will only be created if the effect succeeds with true
|
|
183
195
|
*/
|
|
184
196
|
function matchFor<
|
|
185
|
-
const ModuleName extends string,
|
|
186
197
|
const Resource extends Record<string, any>
|
|
187
198
|
>(
|
|
188
|
-
rsc: Resource
|
|
199
|
+
rsc: Resource,
|
|
189
200
|
options?: { check?: Effect.Effect<boolean> }
|
|
190
201
|
) {
|
|
191
202
|
type HandlerWithInputGen<
|
|
@@ -194,7 +205,8 @@ export const makeRouter = <
|
|
|
194
205
|
> = (
|
|
195
206
|
req: S.Schema.Type<Action>
|
|
196
207
|
) => Generator<
|
|
197
|
-
|
|
208
|
+
Yieldable<
|
|
209
|
+
any,
|
|
198
210
|
any,
|
|
199
211
|
S.Schema.Type<GetFailure<Action>> | S.SchemaError,
|
|
200
212
|
// the actual implementation of the handler may just require the dynamic context provided by the middleware
|
|
@@ -218,37 +230,37 @@ export const makeRouter = <
|
|
|
218
230
|
GetEffectContext<RequestContextMap, Action["config"]> | ContextProviderA
|
|
219
231
|
>
|
|
220
232
|
|
|
221
|
-
type
|
|
233
|
+
type HandlerWithInputStream<
|
|
222
234
|
Action extends AnyRequestModule,
|
|
223
235
|
RT extends RequestType
|
|
224
|
-
> =
|
|
236
|
+
> = (
|
|
237
|
+
req: S.Schema.Type<Action>
|
|
238
|
+
) => Stream.Stream<
|
|
225
239
|
GetSuccessShape<Action, RT>,
|
|
226
240
|
S.Schema.Type<GetFailure<Action>> | S.SchemaError,
|
|
227
|
-
// the actual implementation of the handler may just require the dynamic context provided by the middleware
|
|
228
|
-
// and the per request context provided by the context provider
|
|
229
241
|
GetEffectContext<RequestContextMap, Action["config"]> | ContextProviderA
|
|
230
242
|
>
|
|
231
243
|
|
|
232
244
|
type Handlers<Action extends AnyRequestModule, RT extends RequestType> =
|
|
233
245
|
| HandlerWithInputGen<Action, RT>
|
|
234
246
|
| HandlerWithInputEff<Action, RT>
|
|
235
|
-
|
|
|
247
|
+
| HandlerWithInputStream<Action, RT>
|
|
236
248
|
|
|
237
249
|
type HandlersDecoded<Action extends AnyRequestModule> = Handlers<Action, RequestTypes.DECODED>
|
|
238
250
|
|
|
239
251
|
type HandlersRaw<Action extends AnyRequestModule> =
|
|
240
252
|
| { raw: HandlerWithInputGen<Action, RequestTypes.RAW> }
|
|
241
253
|
| { raw: HandlerWithInputEff<Action, RequestTypes.RAW> }
|
|
242
|
-
| { raw:
|
|
254
|
+
| { raw: HandlerWithInputStream<Action, RequestTypes.RAW> }
|
|
243
255
|
|
|
244
256
|
type AnyHandlers<Action extends AnyRequestModule> = HandlersRaw<Action> | HandlersDecoded<Action>
|
|
245
257
|
|
|
246
|
-
const
|
|
258
|
+
const meta = getMeta(rsc)
|
|
247
259
|
|
|
248
260
|
type RequestModules = FilterRequestModules<Resource>
|
|
249
261
|
const requestModules = typedKeysOf(rsc).reduce((acc, cur) => {
|
|
250
|
-
if (Predicate.
|
|
251
|
-
acc[cur as keyof RequestModules] = rsc[cur]
|
|
262
|
+
if (Predicate.isObjectKeyword(rsc[cur]) && rsc[cur]["success"]) {
|
|
263
|
+
acc[cur as keyof RequestModules] = rsc[cur]
|
|
252
264
|
}
|
|
253
265
|
return acc
|
|
254
266
|
}, {} as RequestModules)
|
|
@@ -259,19 +271,13 @@ export const makeRouter = <
|
|
|
259
271
|
// handlerImpl is the actual handler implementation
|
|
260
272
|
if (handlerImpl[Symbol.toStringTag] === "GeneratorFunction") handlerImpl = Effect.fnUntraced(handlerImpl)
|
|
261
273
|
const stack = new Error().stack?.split("\n").slice(2).join("\n")
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
: class {
|
|
270
|
-
static request = rsc[cur]
|
|
271
|
-
static stack = stack
|
|
272
|
-
static _tag = RequestTypes.DECODED
|
|
273
|
-
static handler = handlerImpl
|
|
274
|
-
}
|
|
274
|
+
// oxlint-disable-next-line typescript/no-extraneous-class
|
|
275
|
+
return class {
|
|
276
|
+
static request = rsc[cur]
|
|
277
|
+
static stack = stack
|
|
278
|
+
static _tag = RequestTypes.DECODED
|
|
279
|
+
static handler = handlerImpl
|
|
280
|
+
}
|
|
275
281
|
}, {
|
|
276
282
|
success: rsc[cur].success,
|
|
277
283
|
successRaw: S.toEncoded(rsc[cur].success),
|
|
@@ -282,19 +288,13 @@ export const makeRouter = <
|
|
|
282
288
|
(handlerImpl: any) => {
|
|
283
289
|
if (handlerImpl[Symbol.toStringTag] === "GeneratorFunction") handlerImpl = Effect.fnUntraced(handlerImpl)
|
|
284
290
|
const stack = new Error().stack?.split("\n").slice(2).join("\n")
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
: class {
|
|
293
|
-
static request = rsc[cur]
|
|
294
|
-
static stack = stack
|
|
295
|
-
static _tag = RequestTypes.RAW
|
|
296
|
-
static handler = handlerImpl
|
|
297
|
-
}
|
|
291
|
+
// oxlint-disable-next-line typescript/no-extraneous-class
|
|
292
|
+
return class {
|
|
293
|
+
static request = rsc[cur]
|
|
294
|
+
static stack = stack
|
|
295
|
+
static _tag = RequestTypes.RAW
|
|
296
|
+
static handler = handlerImpl
|
|
297
|
+
}
|
|
298
298
|
}
|
|
299
299
|
})
|
|
300
300
|
return prev
|
|
@@ -317,19 +317,15 @@ export const makeRouter = <
|
|
|
317
317
|
// retrieves context R from the actual implementation of the handler
|
|
318
318
|
Impl[K] extends { raw: any }
|
|
319
319
|
? Impl[K]["raw"] extends (...args: any[]) => Effect.Effect<any, any, infer R> ? R
|
|
320
|
-
: Impl[K]["raw"] extends
|
|
320
|
+
: Impl[K]["raw"] extends (...args: any[]) => Stream.Stream<any, any, infer R> ? R
|
|
321
321
|
: Impl[K]["raw"] extends (...args: any[]) => Generator<
|
|
322
|
-
|
|
323
|
-
any,
|
|
324
|
-
any
|
|
322
|
+
Yieldable<any, any, any, infer R>
|
|
325
323
|
> ? R
|
|
326
324
|
: never
|
|
327
325
|
: Impl[K] extends (...args: any[]) => Effect.Effect<any, any, infer R> ? R
|
|
328
|
-
: Impl[K] extends
|
|
326
|
+
: Impl[K] extends (...args: any[]) => Stream.Stream<any, any, infer R> ? R
|
|
329
327
|
: Impl[K] extends (...args: any[]) => Generator<
|
|
330
|
-
|
|
331
|
-
any,
|
|
332
|
-
any
|
|
328
|
+
Yieldable<any, any, any, infer R>
|
|
333
329
|
> ? R
|
|
334
330
|
: never,
|
|
335
331
|
| GetEffectContext<RequestContextMap, Resource[K]["config"]>
|
|
@@ -358,7 +354,7 @@ export const makeRouter = <
|
|
|
358
354
|
match: any
|
|
359
355
|
) =>
|
|
360
356
|
| Effect.Effect<THandlers, MakeE, MakeR>
|
|
361
|
-
| Generator<Yieldable<any, any, MakeE, MakeR>, THandlers
|
|
357
|
+
| Generator<Yieldable<any, any, MakeE, MakeR>, THandlers>
|
|
362
358
|
) => {
|
|
363
359
|
const dependenciesL = (dependencies ? Layer.mergeAll(...dependencies as any) : Layer.empty) as Layer.Layer<
|
|
364
360
|
LayerUtils.GetLayersSuccess<MakeDependencies>,
|
|
@@ -385,12 +381,78 @@ export const makeRouter = <
|
|
|
385
381
|
static success = S.toEncoded(resource.success)
|
|
386
382
|
} as any
|
|
387
383
|
: resource,
|
|
388
|
-
(payload: any, headers: any) =>
|
|
389
|
-
|
|
384
|
+
(payload: any, headers: any) => {
|
|
385
|
+
let result: any = handler.handler(payload, headers)
|
|
386
|
+
// Stream resources accept handlers returning either Stream or Effect.
|
|
387
|
+
// Lift Effect to Stream so `Effect.fail(...)` and `Effect<Stream>` (e.g.
|
|
388
|
+
// from generator handlers) work the same as a returned Stream.
|
|
389
|
+
if (resource.stream && Effect.isEffect(result)) {
|
|
390
|
+
result = Stream.unwrap(
|
|
391
|
+
(result as Effect.Effect<unknown, unknown, unknown>).pipe(
|
|
392
|
+
Effect.map((v) => Stream.isStream(v) ? v : Stream.succeed(v))
|
|
393
|
+
)
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
if (Stream.isStream(result)) {
|
|
397
|
+
// Wrap stream items as { _tag: "value", value } and append a final
|
|
398
|
+
// { _tag: "done", metadata } chunk carrying accumulated invalidation keys.
|
|
399
|
+
// V2: on failure, convert to { _tag: "error", error, metadata } chunk so
|
|
400
|
+
// clients can invalidate queries even when the stream fails.
|
|
401
|
+
const keysRef = Ref.makeUnsafe<ReadonlyArray<Invalidation.InvalidationKey>>([])
|
|
402
|
+
const invalidationSet = Invalidation.makeInvalidationSet(keysRef)
|
|
403
|
+
return Stream.concat(
|
|
404
|
+
(result as Stream.Stream<any, any, any>).pipe(
|
|
405
|
+
Stream.map((item: any) => ({ _tag: "value" as const, value: item })),
|
|
406
|
+
Stream.provideService(Invalidation.InvalidationSet, invalidationSet),
|
|
407
|
+
// V3: after each value chunk, drain accumulated keys and emit a "metadata"
|
|
408
|
+
// chunk if any keys were collected since the last drain. This lets clients
|
|
409
|
+
// invalidate queries mid-stream without waiting for the "done" chunk.
|
|
410
|
+
Stream.flatMap((valueChunk: any) =>
|
|
411
|
+
Stream
|
|
412
|
+
.fromEffect(
|
|
413
|
+
Ref.getAndSet(keysRef, []).pipe(
|
|
414
|
+
Effect.map((keys) =>
|
|
415
|
+
keys.length > 0
|
|
416
|
+
? [
|
|
417
|
+
valueChunk,
|
|
418
|
+
{ _tag: "metadata" as const, metadata: { invalidateQueries: keys } }
|
|
419
|
+
]
|
|
420
|
+
: [valueChunk]
|
|
421
|
+
)
|
|
422
|
+
)
|
|
423
|
+
)
|
|
424
|
+
.pipe(Stream.flatMap(Stream.fromIterable))
|
|
425
|
+
),
|
|
426
|
+
// V2: catch stream failures and embed them in the stream as an error chunk
|
|
427
|
+
Stream.catch((err: any) =>
|
|
428
|
+
Stream.fromEffect(
|
|
429
|
+
Ref.get(keysRef).pipe(
|
|
430
|
+
Effect.flatMap((keys) =>
|
|
431
|
+
Effect.fail({
|
|
432
|
+
_tag: "error" as const,
|
|
433
|
+
error: err,
|
|
434
|
+
metadata: { invalidateQueries: keys }
|
|
435
|
+
})
|
|
436
|
+
)
|
|
437
|
+
)
|
|
438
|
+
)
|
|
439
|
+
)
|
|
440
|
+
),
|
|
441
|
+
Stream.fromEffect(
|
|
442
|
+
Ref.get(keysRef).pipe(
|
|
443
|
+
Effect.map((keys) => ({ _tag: "done" as const, metadata: { invalidateQueries: keys } }))
|
|
444
|
+
)
|
|
445
|
+
)
|
|
446
|
+
)
|
|
447
|
+
}
|
|
448
|
+
const effect = (result as Effect.Effect<unknown, unknown, unknown>).pipe(
|
|
390
449
|
Effect.withSpan(`Request.${meta.moduleName}.${resource._tag}`, {}, {
|
|
391
450
|
captureStackTrace: () => handler.stack // capturing the handler stack is the main reason why we are doing the span here
|
|
392
451
|
})
|
|
393
452
|
)
|
|
453
|
+
|
|
454
|
+
return applyRequestTypeInterruptibility(resource.type, effect)
|
|
455
|
+
}
|
|
394
456
|
] as const
|
|
395
457
|
return acc
|
|
396
458
|
}, {} as any) as {
|
|
@@ -414,9 +476,29 @@ export const makeRouter = <
|
|
|
414
476
|
const rpcs = RpcGroup
|
|
415
477
|
.make(
|
|
416
478
|
...typedValuesOf(mapped).map(([resource]) => {
|
|
417
|
-
|
|
418
|
-
|
|
479
|
+
const isStream = resource.stream
|
|
480
|
+
const isCommand = resource.type === "command"
|
|
481
|
+
return (isCommand
|
|
482
|
+
? isStream
|
|
483
|
+
? Invalidation.makeStreamRpc(resource._tag, {
|
|
484
|
+
payload: resource,
|
|
485
|
+
success: resource.success,
|
|
486
|
+
error: resource.error,
|
|
487
|
+
stream: true as const
|
|
488
|
+
})
|
|
489
|
+
: Invalidation.makeCommandRpc(resource._tag, {
|
|
490
|
+
payload: resource,
|
|
491
|
+
success: resource.success,
|
|
492
|
+
error: resource.error
|
|
493
|
+
})
|
|
494
|
+
: Rpc.make(resource._tag, {
|
|
495
|
+
payload: resource,
|
|
496
|
+
success: resource.success,
|
|
497
|
+
error: resource.error,
|
|
498
|
+
stream: isStream
|
|
499
|
+
}))
|
|
419
500
|
.annotate(middleware.requestContext, resource.config ?? {})
|
|
501
|
+
.annotate(RequestTypeAnnotation, resource.type)
|
|
420
502
|
})
|
|
421
503
|
)
|
|
422
504
|
.prefix(`${meta.moduleName}.`)
|
|
@@ -478,8 +560,7 @@ export const makeRouter = <
|
|
|
478
560
|
any,
|
|
479
561
|
any
|
|
480
562
|
>,
|
|
481
|
-
{ [K in keyof FilterRequestModules<Resource>]: AnyHandler<Resource[K]> }
|
|
482
|
-
any
|
|
563
|
+
{ [K in keyof FilterRequestModules<Resource>]: AnyHandler<Resource[K]> }
|
|
483
564
|
>
|
|
484
565
|
/** @deprecated */
|
|
485
566
|
readonly ಠ_ಠ: never
|
|
@@ -509,15 +590,8 @@ export const makeRouter = <
|
|
|
509
590
|
dependencies?: ReadonlyArray<Layer.Any>
|
|
510
591
|
// v4: generators yield Yieldable with asEffect()
|
|
511
592
|
effect: (match: typeof router3) => Generator<
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
any,
|
|
515
|
-
any,
|
|
516
|
-
any
|
|
517
|
-
>
|
|
518
|
-
},
|
|
519
|
-
{ [K in keyof FilterRequestModules<Resource>]: AnyHandler<Resource[K]> },
|
|
520
|
-
any
|
|
593
|
+
Yieldable<any, any, any, any>,
|
|
594
|
+
{ [K in keyof FilterRequestModules<Resource>]: AnyHandler<Resource[K]> }
|
|
521
595
|
>
|
|
522
596
|
}
|
|
523
597
|
>(
|
|
@@ -577,23 +651,23 @@ export type MakeErrors<Make> = /*Make extends { readonly effect: (_: any) => Eff
|
|
|
577
651
|
: Make extends { readonly effect: (_: any) => Effect.Effect<any, never, any> } ? never
|
|
578
652
|
: */
|
|
579
653
|
// v4: generators yield Yieldable with asEffect()
|
|
580
|
-
Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, never, any
|
|
581
|
-
: Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, infer E, any
|
|
654
|
+
Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, never, any>> } ? never
|
|
655
|
+
: Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, infer E, any>> } ? E
|
|
582
656
|
: never
|
|
583
657
|
|
|
584
658
|
export type MakeContext<Make> = /*Make extends { readonly effect: (_: any) => Effect.Effect<any, any, infer R> } ? R
|
|
585
659
|
: Make extends { readonly effect: (_: any) => Effect.Effect<any, any, never> } ? never
|
|
586
660
|
: */
|
|
587
661
|
// v4: generators yield Yieldable with asEffect()
|
|
588
|
-
Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, any
|
|
589
|
-
: Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, any, infer R
|
|
662
|
+
Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, any>> } ? never
|
|
663
|
+
: Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, any, infer R>> } ? R
|
|
590
664
|
: never
|
|
591
665
|
|
|
592
666
|
export type MakeHandlers<Make, _Handlers extends Record<string, any>> = /*Make extends
|
|
593
667
|
{ readonly effect: (_: any) => Effect.Effect<{ [K in keyof Handlers]: AnyHandler<Handlers[K]> }, any, any> }
|
|
594
668
|
? Effect.Success<ReturnType<Make["effect"]>>
|
|
595
669
|
: */
|
|
596
|
-
Make extends { readonly effect: (_: any) => Generator<any, infer S
|
|
670
|
+
Make extends { readonly effect: (_: any) => Generator<any, infer S> } ? S
|
|
597
671
|
: never
|
|
598
672
|
|
|
599
673
|
export type MakeDepsE<Make> = Layer.Error<MakeDeps<Make>>
|
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,25 +52,35 @@ 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),
|
|
59
|
-
Layer.succeed(storeId,
|
|
77
|
+
Layer.succeed(storeId, requestContext.namespace)
|
|
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,16 +88,17 @@ 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,
|
|
76
95
|
Layer.succeed(LocaleRef, requestContext.locale),
|
|
77
|
-
Layer.succeed(storeId,
|
|
96
|
+
Layer.succeed(storeId, requestContext.namespace)
|
|
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
|
|