effect-app 4.0.0-beta.99 → 4.0.0
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 +2028 -0
- package/dist/Array.d.ts +2 -1
- package/dist/Array.d.ts.map +1 -1
- package/dist/Array.js +4 -4
- package/dist/Chunk.d.ts +2 -2
- package/dist/Chunk.d.ts.map +1 -1
- package/dist/Config/SecretURL.d.ts +3 -1
- package/dist/Config/SecretURL.d.ts.map +1 -1
- package/dist/Config/SecretURL.js +3 -6
- package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
- package/dist/Config/internal/configSecretURL.js +2 -2
- package/dist/Config.d.ts +0 -5
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js +7 -5
- package/dist/ConfigProvider.d.ts +0 -37
- package/dist/ConfigProvider.d.ts.map +1 -1
- package/dist/ConfigProvider.js +5 -41
- package/dist/Context.d.ts +6 -4
- package/dist/Context.d.ts.map +1 -1
- package/dist/Context.js +4 -3
- package/dist/DataDependencies.d.ts +136 -0
- package/dist/DataDependencies.d.ts.map +1 -0
- package/dist/DataDependencies.js +112 -0
- package/dist/Effect.d.ts +3 -3
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +6 -9
- package/dist/Emailer.d.ts +51 -0
- package/dist/Emailer.d.ts.map +1 -0
- package/dist/Emailer.js +7 -0
- package/dist/Function.d.ts.map +1 -1
- package/dist/Layer.d.ts +6 -3
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +3 -2
- package/dist/Model/Repository/Registry.d.ts +22 -0
- package/dist/Model/Repository/Registry.d.ts.map +1 -0
- package/dist/Model/Repository/Registry.js +18 -0
- package/dist/Model/Repository/dependency.d.ts +6 -0
- package/dist/Model/Repository/dependency.d.ts.map +1 -0
- package/dist/Model/Repository/dependency.js +29 -0
- package/dist/Model/Repository/ext.d.ts +60 -0
- package/dist/Model/Repository/ext.d.ts.map +1 -0
- package/dist/Model/Repository/ext.js +124 -0
- package/dist/Model/Repository/internal/internal.d.ts +68 -0
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -0
- package/dist/Model/Repository/internal/internal.js +611 -0
- package/dist/Model/Repository/legacy.d.ts +21 -0
- package/dist/Model/Repository/legacy.d.ts.map +1 -0
- package/dist/Model/Repository/legacy.js +2 -0
- package/dist/Model/Repository/makeRepo.d.ts +71 -0
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -0
- package/dist/Model/Repository/makeRepo.js +30 -0
- package/dist/Model/Repository/service.d.ts +121 -0
- package/dist/Model/Repository/service.d.ts.map +1 -0
- package/dist/Model/Repository/service.js +2 -0
- package/dist/Model/Repository/validation.d.ts +72 -0
- package/dist/Model/Repository/validation.d.ts.map +1 -0
- package/dist/Model/Repository/validation.js +39 -0
- package/dist/Model/Repository.d.ts +8 -0
- package/dist/Model/Repository.d.ts.map +1 -0
- package/dist/Model/Repository.js +8 -0
- package/dist/Model/dsl.d.ts +33 -0
- package/dist/Model/dsl.d.ts.map +1 -0
- package/dist/Model/dsl.js +43 -0
- package/dist/Model/filter/filterApi.d.ts +30 -0
- package/dist/Model/filter/filterApi.d.ts.map +1 -0
- package/dist/Model/filter/filterApi.js +2 -0
- package/dist/Model/filter/types/errors.d.ts +29 -0
- package/dist/Model/filter/types/errors.d.ts.map +1 -0
- package/dist/Model/filter/types/errors.js +2 -0
- package/dist/Model/filter/types/fields.d.ts +15 -0
- package/dist/Model/filter/types/fields.d.ts.map +1 -0
- package/dist/Model/filter/types/fields.js +2 -0
- package/dist/Model/filter/types/path/common.d.ts +316 -0
- package/dist/Model/filter/types/path/common.d.ts.map +1 -0
- package/dist/Model/filter/types/path/common.js +2 -0
- package/dist/Model/filter/types/path/eager.d.ts +94 -0
- package/dist/Model/filter/types/path/eager.d.ts.map +1 -0
- package/dist/Model/filter/types/path/eager.js +36 -0
- package/dist/Model/filter/types/path/index.d.ts +4 -0
- package/dist/Model/filter/types/path/index.d.ts.map +1 -0
- package/dist/Model/filter/types/path/index.js +3 -0
- package/dist/Model/filter/types/utils.d.ts +79 -0
- package/dist/Model/filter/types/utils.d.ts.map +1 -0
- package/dist/Model/filter/types/utils.js +2 -0
- package/dist/Model/filter/types/validator.d.ts +30 -0
- package/dist/Model/filter/types/validator.d.ts.map +1 -0
- package/dist/Model/filter/types/validator.js +2 -0
- package/dist/Model/filter/types.d.ts +5 -0
- package/dist/Model/filter/types.d.ts.map +1 -0
- package/dist/Model/filter/types.js +7 -0
- package/dist/Model/query/dsl.d.ts +630 -0
- package/dist/Model/query/dsl.d.ts.map +1 -0
- package/dist/Model/query/dsl.js +376 -0
- package/dist/Model/query/new-kid-interpreter.d.ts +132 -0
- package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -0
- package/dist/Model/query/new-kid-interpreter.js +370 -0
- package/dist/Model/query.d.ts +15 -0
- package/dist/Model/query.d.ts.map +1 -0
- package/dist/Model/query.js +3 -0
- package/dist/Model.d.ts +6 -0
- package/dist/Model.d.ts.map +1 -0
- package/dist/Model.js +6 -0
- package/dist/NonEmptySet.d.ts +3 -1
- package/dist/NonEmptySet.d.ts.map +1 -1
- package/dist/NonEmptySet.js +2 -2
- package/dist/Option.d.ts +1 -0
- package/dist/Option.d.ts.map +1 -1
- package/dist/Option.js +3 -1
- package/dist/Pure.d.ts +5 -3
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +5 -2
- package/dist/QueueMaker.d.ts +11 -0
- package/dist/QueueMaker.d.ts.map +1 -0
- package/dist/QueueMaker.js +11 -0
- package/dist/RequestContext.d.ts +87 -0
- package/dist/RequestContext.d.ts.map +1 -0
- package/dist/RequestContext.js +45 -0
- package/dist/RequestScopedDependencies.d.ts +21 -0
- package/dist/RequestScopedDependencies.d.ts.map +1 -0
- package/dist/RequestScopedDependencies.js +16 -0
- package/dist/Schema/Class.d.ts +163 -16
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +193 -21
- package/dist/Schema/FastCheck.d.ts.map +1 -1
- package/dist/Schema/SchemaParser.d.ts +5 -0
- package/dist/Schema/SchemaParser.d.ts.map +1 -0
- package/dist/Schema/SchemaParser.js +6 -0
- package/dist/Schema/SpecialJsonSchema.d.ts +15 -2
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -1
- package/dist/Schema/SpecialJsonSchema.js +70 -12
- package/dist/Schema/SpecialOpenApi.d.ts +1 -1
- package/dist/Schema/SpecialOpenApi.js +3 -3
- package/dist/Schema/brand.d.ts +5 -5
- package/dist/Schema/brand.d.ts.map +1 -1
- package/dist/Schema/brand.js +6 -4
- package/dist/Schema/email.d.ts.map +1 -1
- package/dist/Schema/email.js +8 -5
- package/dist/Schema/ext.d.ts +297 -72
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +331 -67
- package/dist/Schema/moreStrings.d.ts +82 -48
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +85 -51
- package/dist/Schema/numbers.d.ts +33 -21
- package/dist/Schema/numbers.d.ts.map +1 -1
- package/dist/Schema/numbers.js +64 -12
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +6 -6
- package/dist/Schema/schema.d.ts +1 -1
- package/dist/Schema/schema.d.ts.map +1 -1
- package/dist/Schema/schema.js +2 -2
- package/dist/Schema/strings.d.ts +18 -4
- package/dist/Schema/strings.d.ts.map +1 -1
- package/dist/Schema/strings.js +1 -5
- package/dist/Schema.d.ts +223 -6
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +189 -23
- package/dist/SchemaAST.d.ts +2 -0
- package/dist/SchemaAST.d.ts.map +1 -0
- package/dist/SchemaAST.js +2 -0
- package/dist/Set.d.ts +4 -1
- package/dist/Set.d.ts.map +1 -1
- package/dist/Set.js +3 -2
- package/dist/Store.d.ts +215 -0
- package/dist/Store.d.ts.map +1 -0
- package/dist/Store.js +113 -0
- package/dist/_ext/Array.d.ts +1 -1
- package/dist/_ext/Array.d.ts.map +1 -1
- package/dist/_ext/Array.js +4 -2
- package/dist/_ext/misc.d.ts +4 -1
- package/dist/_ext/misc.d.ts.map +1 -1
- package/dist/_ext/misc.js +4 -2
- package/dist/_ext/ord.ext.d.ts +2 -1
- package/dist/_ext/ord.ext.d.ts.map +1 -1
- package/dist/_ext/ord.ext.js +2 -2
- package/dist/builtin.d.ts +2 -14
- package/dist/builtin.d.ts.map +1 -1
- package/dist/builtin.js +1 -1
- package/dist/client/InvalidationKeys.d.ts +30 -0
- package/dist/client/InvalidationKeys.d.ts.map +1 -0
- package/dist/client/InvalidationKeys.js +37 -0
- package/dist/client/apiClientFactory.d.ts +13 -8
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +148 -29
- package/dist/client/clientFor.d.ts +48 -13
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/clientFor.js +9 -1
- package/dist/client/errors.d.ts +74 -28
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +41 -10
- package/dist/client/makeClient.d.ts +487 -32
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +47 -21
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -1
- package/dist/faker.d.ts.map +1 -1
- package/dist/http/Request.d.ts +2 -2
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +2 -2
- package/dist/http/internal/lib.d.ts +12 -12
- package/dist/http/internal/lib.d.ts.map +1 -1
- package/dist/http/internal/lib.js +13 -13
- package/dist/ids.d.ts +32 -14
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +38 -7
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/middleware.d.ts +12 -6
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +13 -7
- package/dist/rpc/Invalidation.d.ts +1124 -0
- package/dist/rpc/Invalidation.d.ts.map +1 -0
- package/dist/rpc/Invalidation.js +178 -0
- package/dist/rpc/MiddlewareMaker.d.ts +10 -7
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +57 -37
- package/dist/rpc/RpcContextMap.d.ts +2 -2
- package/dist/rpc/RpcContextMap.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.d.ts +13 -10
- package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.js +2 -2
- package/dist/rpc.d.ts +1 -1
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +2 -2
- package/dist/runtime.d.ts +19 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +40 -0
- package/dist/setupRequest.d.ts +20 -0
- package/dist/setupRequest.d.ts.map +1 -0
- package/dist/setupRequest.js +72 -0
- package/dist/toast.d.ts +51 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +34 -0
- package/dist/transform.d.ts +1 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/transform.js +4 -5
- package/dist/utils/effectify.d.ts +1 -1
- package/dist/utils/effectify.d.ts.map +1 -1
- package/dist/utils/effectify.js +2 -2
- package/dist/utils/extend.d.ts +6 -1
- package/dist/utils/extend.d.ts.map +1 -1
- package/dist/utils/extend.js +24 -6
- package/dist/utils/gen.d.ts +4 -4
- package/dist/utils/gen.d.ts.map +1 -1
- package/dist/utils/logLevel.d.ts +2 -2
- package/dist/utils/logLevel.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +2 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +2 -2
- package/dist/utils.d.ts +34 -39
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +33 -37
- package/dist/validation/validators.d.ts.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/withToast.d.ts +30 -0
- package/dist/withToast.d.ts.map +1 -0
- package/dist/withToast.js +64 -0
- package/package.json +26 -266
- package/src/Array.ts +5 -5
- package/src/Chunk.ts +3 -3
- package/src/Config/SecretURL.ts +6 -3
- package/src/Config/internal/configSecretURL.ts +2 -2
- package/src/Config.ts +6 -13
- package/src/ConfigProvider.ts +4 -47
- package/src/Context.ts +9 -8
- package/src/DataDependencies.ts +171 -0
- package/src/Effect.ts +9 -12
- package/src/Emailer.ts +51 -0
- package/src/Inputify.type.ts +1 -1
- package/src/Layer.ts +8 -5
- package/src/Model/Repository/Registry.ts +35 -0
- package/src/Model/Repository/dependency.ts +32 -0
- package/src/Model/Repository/ext.ts +374 -0
- package/src/Model/Repository/internal/internal.ts +988 -0
- package/src/Model/Repository/legacy.ts +29 -0
- package/src/Model/Repository/makeRepo.ts +168 -0
- package/src/Model/Repository/service.ts +676 -0
- package/src/Model/Repository/validation.ts +38 -0
- package/src/Model/Repository.ts +7 -0
- package/src/Model/dsl.ts +129 -0
- package/src/Model/filter/filterApi.ts +78 -0
- package/src/Model/filter/types/errors.ts +47 -0
- package/src/Model/filter/types/fields.ts +50 -0
- package/src/Model/filter/types/path/common.ts +404 -0
- package/src/Model/filter/types/path/eager.ts +329 -0
- package/src/Model/filter/types/path/index.ts +4 -0
- package/src/Model/filter/types/utils.ts +128 -0
- package/src/Model/filter/types/validator.ts +46 -0
- package/src/Model/filter/types.ts +6 -0
- package/src/Model/query/dsl.ts +3092 -0
- package/src/Model/query/new-kid-interpreter.ts +532 -0
- package/src/Model/query.ts +13 -0
- package/src/Model.ts +5 -0
- package/src/NonEmptySet.ts +6 -4
- package/src/Option.ts +2 -0
- package/src/Pure.ts +6 -3
- package/src/QueueMaker.ts +11 -0
- package/src/RequestContext.ts +48 -0
- package/src/RequestScopedDependencies.ts +45 -0
- package/src/Schema/Class.ts +550 -56
- package/src/Schema/SchemaParser.ts +12 -0
- package/src/Schema/SpecialJsonSchema.ts +75 -7
- package/src/Schema/SpecialOpenApi.ts +5 -5
- package/src/Schema/brand.ts +27 -12
- package/src/Schema/email.ts +9 -7
- package/src/Schema/ext.ts +408 -91
- package/src/Schema/moreStrings.ts +213 -88
- package/src/Schema/numbers.ts +107 -29
- package/src/Schema/phoneNumber.ts +8 -9
- package/src/Schema/schema.ts +1 -1
- package/src/Schema/strings.ts +23 -14
- package/src/Schema.ts +403 -46
- package/src/SchemaAST.ts +1 -0
- package/src/Set.ts +6 -2
- package/src/Store.ts +325 -0
- package/src/_ext/Array.ts +4 -2
- package/src/_ext/misc.ts +4 -1
- package/src/_ext/ord.ext.ts +2 -1
- package/src/builtin-json.d.ts +24 -0
- package/src/builtin.ts +16 -21
- package/src/client/InvalidationKeys.ts +55 -0
- package/src/client/apiClientFactory.ts +291 -126
- package/src/client/clientFor.ts +101 -33
- package/src/client/errors.ts +66 -20
- package/src/client/makeClient.ts +519 -60
- package/src/client.ts +6 -4
- package/src/http/Request.ts +4 -4
- package/src/http/internal/lib.ts +12 -12
- package/src/http.ts +1 -1
- package/src/ids.ts +45 -8
- package/src/index.ts +20 -16
- package/src/middleware.ts +12 -8
- package/src/rpc/Invalidation.ts +278 -0
- package/src/rpc/MiddlewareMaker.ts +86 -79
- package/src/rpc/README.md +2 -2
- package/src/rpc/RpcContextMap.ts +4 -4
- package/src/rpc/RpcMiddleware.ts +18 -13
- package/src/rpc.ts +4 -4
- package/src/runtime.ts +57 -0
- package/src/setupRequest.ts +138 -0
- package/src/toast.ts +54 -0
- package/src/transform.ts +4 -4
- package/src/utils/effectify.ts +1 -1
- package/src/utils/extend.ts +24 -5
- package/src/utils/gen.ts +7 -7
- package/src/utils/logLevel.ts +1 -1
- package/src/utils/logger.ts +3 -2
- package/src/utils.ts +85 -158
- package/src/validation.ts +2 -2
- package/src/withToast.ts +133 -0
- package/test/dist/rpc.test.d.ts.map +1 -1
- package/test/dist/rpcMiddlewareContext.test.d.ts.map +1 -0
- package/test/moreStrings.test.ts +55 -10
- package/test/rpc.test.ts +28 -11
- package/test/rpcMiddlewareContext.test.ts +63 -0
- package/test/schema.test.ts +318 -55
- package/test/secretURL.test.ts +4 -1
- package/test/special.test.ts +663 -131
- package/test/utils.test.ts +5 -5
- package/tsconfig.base.json +6 -4
- package/tsconfig.json +9 -1
- package/tsconfig.src.json +29 -29
- package/tsconfig.test.json +2 -2
- package/dist/Operations.d.ts +0 -124
- package/dist/Operations.d.ts.map +0 -1
- package/dist/Operations.js +0 -29
- package/eslint.config.mjs +0 -26
- package/src/Operations.ts +0 -55
- package/tsconfig.json.bak +0 -12
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { Effect, Layer, type Schema, Schema as S, type Scope } from "effect"
|
|
3
2
|
import { type NonEmptyArray, type NonEmptyReadonlyArray } from "effect/Array"
|
|
3
|
+
import * as Effect from "effect/Effect"
|
|
4
|
+
import * as Layer from "effect/Layer"
|
|
5
|
+
import { Rpc, type RpcGroup, type RpcSchema } from "effect/rpc"
|
|
6
|
+
import { type HandlersFrom } from "effect/rpc/RpcGroup"
|
|
7
|
+
import * as S from "effect/Schema"
|
|
8
|
+
import type * as Scope from "effect/Scope"
|
|
4
9
|
import { type Simplify } from "effect/Types"
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import { type
|
|
8
|
-
import
|
|
9
|
-
import { type
|
|
10
|
-
import
|
|
11
|
-
import { type TypeTestId } from "../TypeTest.js"
|
|
12
|
-
import { typedValuesOf } from "../utils.js"
|
|
13
|
-
import { type GetContextConfig, type RequestContextMapTagAny, type RpcContextMap } from "./RpcContextMap.js"
|
|
14
|
-
import { type AddMiddleware, type AnyDynamic, type RpcDynamic, type RpcMiddlewareV4, type TagClassAny } from "./RpcMiddleware.js"
|
|
15
|
-
import * as RpcMiddlewareX from "./RpcMiddleware.js"
|
|
10
|
+
import * as Context from "../Context.ts"
|
|
11
|
+
import { PreludeLogger } from "../logger.ts"
|
|
12
|
+
import { type TypeTestId } from "../TypeTest.ts"
|
|
13
|
+
import { type GetContextConfig, type RequestContextMapTagAny, type RpcContextMap } from "./RpcContextMap.ts"
|
|
14
|
+
import { type AddMiddleware, type AnyDynamic, type RpcDynamic, type RpcMiddlewareV4, type TagClassAny } from "./RpcMiddleware.ts"
|
|
15
|
+
import * as RpcMiddlewareX from "./RpcMiddleware.ts"
|
|
16
16
|
|
|
17
17
|
// adapter for effect/rpc v3 middleware provides. (in effect-smol (v4), it's just a Service Identifier, no tags.)
|
|
18
18
|
// hm?
|
|
@@ -94,7 +94,7 @@ export namespace MiddlewareMaker {
|
|
|
94
94
|
: never
|
|
95
95
|
: never
|
|
96
96
|
|
|
97
|
-
export type Errors<T> = T extends TagClassAny ? T extends { error: S.Top } ?
|
|
97
|
+
export type Errors<T> = T extends TagClassAny ? T extends { error: S.Top } ? T["error"]["Type"]
|
|
98
98
|
: never
|
|
99
99
|
: never
|
|
100
100
|
|
|
@@ -170,9 +170,9 @@ export interface BuildingMiddleware<
|
|
|
170
170
|
> {
|
|
171
171
|
rpc: <
|
|
172
172
|
const Tag extends string,
|
|
173
|
-
Payload extends
|
|
174
|
-
Success extends
|
|
175
|
-
Error extends
|
|
173
|
+
Payload extends S.Top | S.Struct.Fields = typeof S.Void,
|
|
174
|
+
Success extends S.Top = typeof S.Void,
|
|
175
|
+
Error extends S.Top = typeof S.Never,
|
|
176
176
|
const Stream extends boolean = false,
|
|
177
177
|
Config extends GetContextConfig<RequestContextMap> = {}
|
|
178
178
|
>(tag: Tag, options?: {
|
|
@@ -181,16 +181,16 @@ export interface BuildingMiddleware<
|
|
|
181
181
|
readonly error?: Error
|
|
182
182
|
readonly stream?: Stream
|
|
183
183
|
readonly config?: Config
|
|
184
|
-
readonly primaryKey?: [Payload] extends [
|
|
185
|
-
payload: Payload extends
|
|
184
|
+
readonly primaryKey?: [Payload] extends [S.Struct.Fields] ? ((
|
|
185
|
+
payload: Payload extends S.Struct.Fields ? Simplify<S.Struct<Payload>["Type"]> : Payload["Type"]
|
|
186
186
|
) => string)
|
|
187
187
|
: never
|
|
188
188
|
}) =>
|
|
189
189
|
& Rpc.Rpc<
|
|
190
190
|
Tag,
|
|
191
|
-
Payload extends
|
|
191
|
+
Payload extends S.Struct.Fields ? S.Struct<Payload> : Payload,
|
|
192
192
|
Stream extends true ? RpcSchema.Stream<Success, Error> : Success,
|
|
193
|
-
Stream extends true ? typeof
|
|
193
|
+
Stream extends true ? typeof S.Never : Error
|
|
194
194
|
>
|
|
195
195
|
& { readonly config: Config }
|
|
196
196
|
|
|
@@ -259,10 +259,10 @@ export type MiddlewaresBuilder<
|
|
|
259
259
|
: { new(_: never): {} }
|
|
260
260
|
: { new(_: never): {} })
|
|
261
261
|
|
|
262
|
-
const middlewareMaker =
|
|
262
|
+
const middlewareMaker = Effect.fnUntraced(function*<
|
|
263
263
|
MiddlewareProviders extends ReadonlyArray<MiddlewareMaker.Any>
|
|
264
|
-
>(middlewares: MiddlewareProviders)
|
|
265
|
-
RpcMiddlewareV4<
|
|
264
|
+
>(middlewares: MiddlewareProviders) {
|
|
265
|
+
type Middleware = RpcMiddlewareV4<
|
|
266
266
|
MiddlewareMaker.ManyProvided<MiddlewareProviders>,
|
|
267
267
|
MiddlewareMaker.ManyErrors<MiddlewareProviders>,
|
|
268
268
|
Exclude<
|
|
@@ -271,42 +271,32 @@ const middlewareMaker = <
|
|
|
271
271
|
> extends never ? never
|
|
272
272
|
: Exclude<MiddlewareMaker.ManyRequired<MiddlewareProviders>, MiddlewareMaker.ManyProvided<MiddlewareProviders>>
|
|
273
273
|
>
|
|
274
|
-
|
|
274
|
+
type Next = Parameters<Middleware>[0]
|
|
275
|
+
type Options = Parameters<Middleware>[1]
|
|
276
|
+
|
|
275
277
|
// we want to run them in reverse order because latter middlewares will provide context to former ones
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
let handler = next
|
|
295
|
-
|
|
296
|
-
// inspired from Effect/RpcMiddleware
|
|
297
|
-
for (const tag of middlewares) {
|
|
298
|
-
// use the tag to get the middleware from context
|
|
299
|
-
const middleware = Context.getUnsafe(context, tag)
|
|
300
|
-
|
|
301
|
-
// wrap the current handler, allowing the middleware to run before and after it
|
|
302
|
-
handler = PreludeLogger.logDebug("Applying middleware wrap " + tag.key).pipe(
|
|
303
|
-
Effect.andThen(middleware(handler, options))
|
|
304
|
-
) as any
|
|
305
|
-
}
|
|
306
|
-
return handler
|
|
278
|
+
const reversed = middlewares.toReversed()
|
|
279
|
+
const context = yield* Effect.context()
|
|
280
|
+
|
|
281
|
+
// returns a Effect/RpcMiddlewareV4 with Scope.Scope in requirements
|
|
282
|
+
// v4: wrap middleware takes (effect, options) as two params instead of a single options bag
|
|
283
|
+
return (next: Next, options: Options) => {
|
|
284
|
+
// we start with the actual handler
|
|
285
|
+
let handler = next
|
|
286
|
+
|
|
287
|
+
// inspired from Effect/RpcMiddleware
|
|
288
|
+
for (const tag of reversed) {
|
|
289
|
+
// use the tag to get the middleware from context
|
|
290
|
+
const middleware = Context.getUnsafe(context, tag)
|
|
291
|
+
|
|
292
|
+
// wrap the current handler, allowing the middleware to run before and after it
|
|
293
|
+
handler = PreludeLogger.logDebug("Applying middleware wrap " + tag.key).pipe(
|
|
294
|
+
Effect.andThen(middleware(handler, options))
|
|
295
|
+
) as any
|
|
307
296
|
}
|
|
308
|
-
|
|
309
|
-
}
|
|
297
|
+
return handler
|
|
298
|
+
}
|
|
299
|
+
})
|
|
310
300
|
|
|
311
301
|
const makeMiddlewareBasic = <Self>() =>
|
|
312
302
|
// by setting RequestContextMap beforehand, execute contextual typing does not fuck up itself to anys
|
|
@@ -322,7 +312,26 @@ const makeMiddlewareBasic = <Self>() =>
|
|
|
322
312
|
// reverse middlewares and wrap one after the other
|
|
323
313
|
const middleware = middlewareMaker(make)
|
|
324
314
|
|
|
325
|
-
|
|
315
|
+
// Per-middleware error: union of the static `error` on the tag (if any) AND
|
|
316
|
+
// the rcm config entry pointed at by the middleware's `dynamic.key` (if any).
|
|
317
|
+
// Reason: middlewares declared with `dynamic: RequestContextMap.get("foo")`
|
|
318
|
+
// don't set a static `error` field — at runtime their `.error` defaults to
|
|
319
|
+
// `S.Never`. Without pulling from rcm, the composite middleware's
|
|
320
|
+
// `.error` collapses to `Never`, and `Rpc.exitSchema` (which walks
|
|
321
|
+
// `rpc.middlewares[*].error` to build the wire failure union) can't decode
|
|
322
|
+
// the actual middleware-thrown error type. Critical for stream rpcs whose
|
|
323
|
+
// top-level `errorSchema` is force-set to `Never` by effect-rpc.
|
|
324
|
+
const isMeaningfulError = (e: S.Top | undefined): e is S.Top => e !== undefined && e !== null && e !== S.Never
|
|
325
|
+
const rcmRecord = rcm as Record<string, RpcContextMap.Any>
|
|
326
|
+
const failures: Array<S.Top> = make.flatMap((_) => {
|
|
327
|
+
const out: Array<S.Top> = []
|
|
328
|
+
if (isMeaningfulError(_.error)) out.push(_.error)
|
|
329
|
+
const key = _.dynamic?.key as string | undefined
|
|
330
|
+
if (key && rcmRecord[key] && isMeaningfulError(rcmRecord[key].error)) {
|
|
331
|
+
out.push(rcmRecord[key].error)
|
|
332
|
+
}
|
|
333
|
+
return out
|
|
334
|
+
})
|
|
326
335
|
const provides = make.flatMap((_) => !_.provides ? [] : Array.isArray(_.provides) ? _.provides : [_.provides])
|
|
327
336
|
const requires = make
|
|
328
337
|
.flatMap((_) => !_.requires ? [] : Array.isArray(_.requires) ? _.requires : [_.requires])
|
|
@@ -358,10 +367,11 @@ const makeMiddlewareBasic = <Self>() =>
|
|
|
358
367
|
.effect(
|
|
359
368
|
MiddlewareMaker,
|
|
360
369
|
middleware as Effect.Effect<
|
|
361
|
-
any
|
|
362
|
-
Effect.Error<typeof middleware>,
|
|
363
|
-
Effect.Services<typeof middleware>
|
|
370
|
+
any
|
|
364
371
|
>
|
|
372
|
+
// todo; they dont change the type..
|
|
373
|
+
// Effect.Error<typeof middleware>,
|
|
374
|
+
// Effect.Services<typeof middleware>
|
|
365
375
|
)
|
|
366
376
|
|
|
367
377
|
// add to the tag a default implementation
|
|
@@ -380,7 +390,7 @@ export const Tag = <Self>() =>
|
|
|
380
390
|
const Id extends string,
|
|
381
391
|
RequestContextMap extends RequestContextMapTagAny
|
|
382
392
|
>(id: Id, rcm: RequestContextMap): MiddlewaresBuilder<Self, Id, RequestContextMap["config"]> => {
|
|
383
|
-
|
|
393
|
+
const allMiddleware: MiddlewareMaker.Any[] = []
|
|
384
394
|
const requestContext = Context.Service<"RequestContextConfig", GetContextConfig<RequestContextMap["config"]>>(
|
|
385
395
|
"RequestContextConfig"
|
|
386
396
|
)
|
|
@@ -389,9 +399,9 @@ export const Tag = <Self>() =>
|
|
|
389
399
|
// rpc with config
|
|
390
400
|
rpc: <
|
|
391
401
|
const Tag extends string,
|
|
392
|
-
Payload extends
|
|
393
|
-
Success extends
|
|
394
|
-
Error extends
|
|
402
|
+
Payload extends S.Top | S.Struct.Fields = typeof S.Void,
|
|
403
|
+
Success extends S.Top = typeof S.Void,
|
|
404
|
+
Error extends S.Top = typeof S.Never,
|
|
395
405
|
const Stream extends boolean = false,
|
|
396
406
|
Config extends GetContextConfig<RequestContextMap["config"]> = {}
|
|
397
407
|
>(tag: Tag, options?: {
|
|
@@ -400,35 +410,32 @@ export const Tag = <Self>() =>
|
|
|
400
410
|
readonly error?: Error
|
|
401
411
|
readonly stream?: Stream
|
|
402
412
|
readonly config?: Config
|
|
403
|
-
readonly primaryKey?: [Payload] extends [
|
|
404
|
-
payload: Payload extends
|
|
413
|
+
readonly primaryKey?: [Payload] extends [S.Struct.Fields] ? ((
|
|
414
|
+
payload: Payload extends S.Struct.Fields ? Simplify<S.Struct<Payload>["Type"]> : Payload["Type"]
|
|
405
415
|
) => string)
|
|
406
416
|
: never
|
|
407
417
|
}):
|
|
408
418
|
& Rpc.Rpc<
|
|
409
419
|
Tag,
|
|
410
|
-
Payload extends
|
|
420
|
+
Payload extends S.Struct.Fields ? S.Struct<Payload> : Payload,
|
|
411
421
|
// TODO: enhance `Error`. type based on middleware config.
|
|
412
422
|
Stream extends true ? RpcSchema.Stream<Success, Error> : Success,
|
|
413
|
-
Stream extends true ? typeof
|
|
423
|
+
Stream extends true ? typeof S.Never : Error
|
|
414
424
|
>
|
|
415
425
|
& { config: Config } =>
|
|
416
426
|
{
|
|
417
427
|
const config = options?.config ?? {} as Config
|
|
418
428
|
|
|
419
|
-
//
|
|
420
|
-
//
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const [firstError, ...restErrors] = allErrors
|
|
425
|
-
const newError = firstError ? S.Union([firstError, ...restErrors]) : S.Never
|
|
426
|
-
|
|
429
|
+
// The rpc's `error` schema carries ONLY the request's own declared errors.
|
|
430
|
+
// Middleware errors (rcm-derived) reach the wire via the middleware tag
|
|
431
|
+
// attached to the rpc group later (`RpcGroup.middleware(...)` at the
|
|
432
|
+
// routing/client level), and are unioned into the failure schema by
|
|
433
|
+
// `Rpc.exitSchema`'s `rpc.middlewares[*].error` walk.
|
|
427
434
|
// @ts-expect-error — TypeScript can't prove Simplify<T> ≡ { [K in keyof T]: T[K] } for unresolved generics (primaryKey)
|
|
428
435
|
const rpc = Rpc.make(tag, {
|
|
429
436
|
...options?.payload !== undefined ? { payload: options.payload } : {},
|
|
430
437
|
...options?.success !== undefined ? { success: options.success } : {},
|
|
431
|
-
error:
|
|
438
|
+
...options?.error !== undefined ? { error: options.error } : {},
|
|
432
439
|
...options?.stream !== undefined ? { stream: options.stream } : {},
|
|
433
440
|
...options?.primaryKey !== undefined ? { primaryKey: options.primaryKey } : {}
|
|
434
441
|
}) as any
|
|
@@ -438,7 +445,7 @@ export const Tag = <Self>() =>
|
|
|
438
445
|
middleware: (...middlewares: any[]) => {
|
|
439
446
|
for (const mw of middlewares) {
|
|
440
447
|
// recall that we run middlewares in reverse order
|
|
441
|
-
allMiddleware
|
|
448
|
+
allMiddleware.unshift(mw)
|
|
442
449
|
}
|
|
443
450
|
return allMiddleware.filter((m) => !!m.dynamic).length !== Object.keys(rcm.config).length
|
|
444
451
|
// for sure, until all the dynamic middlewares are provided it's non sensical to call makeMiddlewareBasic
|
package/src/rpc/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The extensions use V4 format of RPC middleware:
|
|
4
4
|
|
|
5
5
|
- supports `requires` besides `provides`
|
|
6
|
-
- `requires` and `provides`
|
|
6
|
+
- `requires` and `provides` should be set as second generic argument: `Tag<Self, Config>`.
|
|
7
7
|
- `wrap: true` is the default, there is no classic `provides: Tag`
|
|
8
8
|
|
|
9
9
|
## Features
|
|
@@ -34,7 +34,7 @@ NOTE: perhaps not as useful anymore if support for dynamic middleware gets integ
|
|
|
34
34
|
|
|
35
35
|
## Examples
|
|
36
36
|
|
|
37
|
-
See
|
|
37
|
+
See [tests](../../../infra/test/rpc-multi-middleware.test.ts)
|
|
38
38
|
|
|
39
39
|
## Future
|
|
40
40
|
|
package/src/rpc/RpcContextMap.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
4
|
|
|
5
|
-
import { type
|
|
6
|
-
import
|
|
7
|
-
import * as Context from "../Context.
|
|
8
|
-
import { type RpcDynamic } from "./RpcMiddleware.
|
|
5
|
+
import { type AnyWithProps } from "effect/rpc/Rpc"
|
|
6
|
+
import type * as S from "effect/Schema"
|
|
7
|
+
import * as Context from "../Context.ts"
|
|
8
|
+
import { type RpcDynamic } from "./RpcMiddleware.ts"
|
|
9
9
|
|
|
10
10
|
type Values<T extends Record<any, any>> = T[keyof T]
|
|
11
11
|
|
package/src/rpc/RpcMiddleware.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
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 Effect, type Schema, type Schema as S, type Scope, type Stream } from "effect"
|
|
5
4
|
import { type NonEmptyReadonlyArray } from "effect/Array"
|
|
6
|
-
import
|
|
7
|
-
import { type
|
|
8
|
-
import type
|
|
9
|
-
import
|
|
5
|
+
import type * as Effect from "effect/Effect"
|
|
6
|
+
import { type Rpc, RpcMiddleware } from "effect/rpc"
|
|
7
|
+
import { type TypeId } from "effect/rpc/RpcMiddleware"
|
|
8
|
+
import type * as S from "effect/Schema"
|
|
9
|
+
import type * as Scope from "effect/Scope"
|
|
10
|
+
import type * as Stream from "effect/Stream"
|
|
11
|
+
import type * as Context from "../Context.ts"
|
|
12
|
+
import { type GetEffectContext, type RpcContextMap } from "./RpcContextMap.ts"
|
|
10
13
|
|
|
11
14
|
export type RpcMiddlewareV4<Provides, E, Requires> = RpcMiddleware.RpcMiddleware<Provides, E, Requires>
|
|
12
15
|
|
|
13
16
|
export type RpcOptionsOriginal = {
|
|
14
17
|
readonly optional?: boolean
|
|
15
|
-
readonly error?:
|
|
18
|
+
readonly error?: S.Top
|
|
16
19
|
readonly requiredForClient?: boolean
|
|
17
20
|
}
|
|
18
21
|
|
|
@@ -40,7 +43,7 @@ export interface TagClassAny extends RpcMiddleware.AnyService {
|
|
|
40
43
|
readonly optional: boolean
|
|
41
44
|
readonly provides: any
|
|
42
45
|
readonly requires: any
|
|
43
|
-
readonly error:
|
|
46
|
+
readonly error: S.Top
|
|
44
47
|
readonly dynamic?: RpcDynamic<any, any> | undefined
|
|
45
48
|
readonly dependsOn?: NonEmptyReadonlyArray<AnyDynamic> | undefined
|
|
46
49
|
}
|
|
@@ -50,27 +53,29 @@ export declare namespace TagClass {
|
|
|
50
53
|
* @since 1.0.0
|
|
51
54
|
* @category models
|
|
52
55
|
*/
|
|
53
|
-
export type FailureSchema<Options> = Options extends { readonly error:
|
|
56
|
+
export type FailureSchema<Options> = Options extends { readonly error: S.Top; readonly optional?: false }
|
|
54
57
|
? Options["error"]
|
|
55
58
|
// actually not, the Failure depends on Dynamic Middleware Configuration!
|
|
56
59
|
// : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? A["error"]
|
|
57
|
-
: typeof
|
|
60
|
+
: typeof S.Never
|
|
58
61
|
|
|
59
62
|
/**
|
|
60
63
|
* @since 1.0.0
|
|
61
64
|
* @category models
|
|
62
65
|
*/
|
|
63
|
-
export type Failure<Options> = Options extends { readonly error:
|
|
64
|
-
? _A
|
|
66
|
+
export type Failure<Options> = Options extends { readonly error: S.Codec<infer _A>; readonly optional?: false } ? _A
|
|
65
67
|
// actually not, the Failure depends on Dynamic Middleware Configuration!
|
|
66
|
-
: Options extends { readonly dynamic: RpcDynamic<any, infer A> } ?
|
|
68
|
+
: Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? A["error"]["Type"]
|
|
67
69
|
: never
|
|
68
70
|
|
|
69
71
|
/**
|
|
70
72
|
* @since 1.0.0
|
|
71
73
|
* @category models
|
|
72
74
|
*/
|
|
73
|
-
|
|
75
|
+
// Avoid `S.Codec.DecodingServices<X> = X extends Top ? X["DecodingServices"] : never`:
|
|
76
|
+
// tsgo fails to reduce it here and leaves `unknown`. `FailureSchema<Options>` is
|
|
77
|
+
// always `S.Top` (either `Options["error"]` or `typeof S.Never`), so read directly.
|
|
78
|
+
export type FailureContext<Options> = FailureSchema<Options>["DecodingServices"]
|
|
74
79
|
|
|
75
80
|
/**
|
|
76
81
|
* @since 1.0.0
|
package/src/rpc.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * as
|
|
2
|
-
export * as MiddlewareMaker from "./rpc/MiddlewareMaker.
|
|
3
|
-
export * as RpcContextMap from "./rpc/RpcContextMap.
|
|
4
|
-
export * as RpcMiddleware from "./rpc/RpcMiddleware.
|
|
1
|
+
export * as Invalidation from "./rpc/Invalidation.ts"
|
|
2
|
+
export * as MiddlewareMaker from "./rpc/MiddlewareMaker.ts"
|
|
3
|
+
export * as RpcContextMap from "./rpc/RpcContextMap.ts"
|
|
4
|
+
export * as RpcMiddleware from "./rpc/RpcMiddleware.ts"
|
package/src/runtime.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as Exit from "effect/Exit"
|
|
2
|
+
import { flow } from "effect/Function"
|
|
3
|
+
import * as Logger from "effect/Logger"
|
|
4
|
+
import * as ManagedRuntime from "effect/ManagedRuntime"
|
|
5
|
+
import { CauseException } from "./client/errors.ts"
|
|
6
|
+
import { type Context } from "./Context.ts"
|
|
7
|
+
import * as Effect from "./Effect.ts"
|
|
8
|
+
import * as Layer from "./Layer.ts"
|
|
9
|
+
|
|
10
|
+
export const makeAppRuntime = Effect.fnUntraced(function*<A, E>(layer: Layer.Layer<A, E>) {
|
|
11
|
+
const l = layer.pipe(
|
|
12
|
+
Layer.provide(Logger.layer([Logger.consolePretty()])),
|
|
13
|
+
Layer.orDie
|
|
14
|
+
)
|
|
15
|
+
const mrt = ManagedRuntime.make(l)
|
|
16
|
+
yield* mrt.contextEffect
|
|
17
|
+
return Object.assign(mrt, {
|
|
18
|
+
[Symbol.dispose]() {
|
|
19
|
+
return Effect.runSync(mrt.disposeEffect)
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
[Symbol.asyncDispose]() {
|
|
23
|
+
return mrt.dispose()
|
|
24
|
+
}
|
|
25
|
+
}) // as we initialise here, there is no more error left.
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
export function initializeSync<A, E>(layer: Layer.Layer<A, E>) {
|
|
29
|
+
const runtime = Effect.runSync(makeAppRuntime(layer))
|
|
30
|
+
return runtime
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function initializeAsync<A, E>(layer: Layer.Layer<A, E>) {
|
|
34
|
+
return Effect
|
|
35
|
+
.runPromise(makeAppRuntime(layer))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// we wrap into CauseException because we want to keep the full cause of the failure.
|
|
39
|
+
export const makeRunPromise = <T>(services: Context<T>) =>
|
|
40
|
+
flow(Effect.runPromiseExitWith(services), (_) =>
|
|
41
|
+
_.then(
|
|
42
|
+
Exit.match({
|
|
43
|
+
onFailure: (cause) => Promise.reject(new CauseException(cause, "runPromise")),
|
|
44
|
+
onSuccess: (value) => Promise.resolve(value)
|
|
45
|
+
})
|
|
46
|
+
))
|
|
47
|
+
|
|
48
|
+
export const makeRunSync = <T>(services: Context<T>) =>
|
|
49
|
+
flow(
|
|
50
|
+
Effect.runSyncExitWith(services),
|
|
51
|
+
Exit.match({
|
|
52
|
+
onFailure: (cause) => {
|
|
53
|
+
throw new CauseException(cause, "runSync")
|
|
54
|
+
},
|
|
55
|
+
onSuccess: (value) => value
|
|
56
|
+
})
|
|
57
|
+
)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { SqlClient } from "effect/sql"
|
|
2
|
+
import * as Tracer from "effect/Tracer"
|
|
3
|
+
import { DataDependencyRecorder } from "./DataDependencies.ts"
|
|
4
|
+
import * as Effect from "./Effect.ts"
|
|
5
|
+
import * as Layer from "./Layer.ts"
|
|
6
|
+
import * as Option from "./Option.ts"
|
|
7
|
+
import { LocaleRef, RequestContext, spanAttributes } from "./RequestContext.ts"
|
|
8
|
+
import * as RequestScopedDependencies from "./RequestScopedDependencies.ts"
|
|
9
|
+
import { NonEmptyString255 } from "./Schema.ts"
|
|
10
|
+
import { ContextMapContainer, storeId } from "./Store.ts"
|
|
11
|
+
|
|
12
|
+
const withSqlTransaction = <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
|
|
13
|
+
Effect.serviceOption(SqlClient.SqlClient).pipe(
|
|
14
|
+
Effect.flatMap(Option.match({
|
|
15
|
+
onNone: () => self,
|
|
16
|
+
onSome: (sql) => sql.withTransaction(self).pipe(Effect.orDie)
|
|
17
|
+
}))
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
export const getRequestContext = Effect
|
|
21
|
+
.all({
|
|
22
|
+
span: Effect.currentSpan.pipe(Effect.orDie),
|
|
23
|
+
locale: LocaleRef,
|
|
24
|
+
namespace: storeId
|
|
25
|
+
})
|
|
26
|
+
.pipe(
|
|
27
|
+
Effect.map(({ locale, namespace, span }) =>
|
|
28
|
+
RequestContext.make({
|
|
29
|
+
span: Tracer.externalSpan(span),
|
|
30
|
+
locale,
|
|
31
|
+
namespace,
|
|
32
|
+
name: NonEmptyString255(span.name)
|
|
33
|
+
})
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
export const getRC = Effect.all({
|
|
38
|
+
locale: LocaleRef,
|
|
39
|
+
namespace: storeId
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const withRequestSpan = (name = "request", options?: Tracer.SpanOptions) => <R, E, A>(f: Effect.Effect<A, E, R>) =>
|
|
43
|
+
Effect.andThen(
|
|
44
|
+
getRC,
|
|
45
|
+
(ctx) =>
|
|
46
|
+
f.pipe(
|
|
47
|
+
Effect.withSpan(name, {
|
|
48
|
+
...options,
|
|
49
|
+
attributes: { ...spanAttributes({ ...ctx, name: NonEmptyString255(name) }), ...options?.attributes }
|
|
50
|
+
}, {
|
|
51
|
+
captureStackTrace: options?.captureStackTrace ?? false
|
|
52
|
+
}),
|
|
53
|
+
// TODO: false
|
|
54
|
+
// request context info is picked up directly in the logger for annotations.
|
|
55
|
+
Effect.withLogSpan(name)
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
export interface SetupRequestOptions {
|
|
60
|
+
readonly withTransaction?: boolean
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const requestStateLayer = RequestScopedDependencies.layer(ContextMapContainer, DataDependencyRecorder)
|
|
64
|
+
|
|
65
|
+
// Build `layer` against the ambient (request) scope rather than a sub-scope of the
|
|
66
|
+
// returned Effect. Required when the returned value is a streaming HttpServerResponse:
|
|
67
|
+
// the response body keeps producing chunks (and using layer-provided state) after the
|
|
68
|
+
// Effect returns, so a sub-scope would close too early and run finalizers mid-stream.
|
|
69
|
+
export const provideOnRequestScope =
|
|
70
|
+
<ROut, E2, RIn>(layer: Layer.Layer<ROut, E2, RIn>) => <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
71
|
+
Effect.gen(function*() {
|
|
72
|
+
const requestScope = yield* Effect.scope
|
|
73
|
+
// Fresh MemoMap per request: `Layer.buildWithScope` would otherwise reuse
|
|
74
|
+
// the ambient MemoMap living on the HTTP server fiber, sharing the built
|
|
75
|
+
// value (e.g. ContextMap) across every request handled by that server.
|
|
76
|
+
const memoMap = yield* Layer.makeMemoMap
|
|
77
|
+
const ctx = yield* Layer.buildWithMemoMap(layer, memoMap, requestScope)
|
|
78
|
+
return yield* Effect.provide(self, ctx)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
export const setupRequestContextFromCurrent =
|
|
82
|
+
(name = "request", options?: Tracer.SpanOptions & SetupRequestOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) =>
|
|
83
|
+
self
|
|
84
|
+
.pipe(
|
|
85
|
+
options?.withTransaction === true ? withSqlTransaction : (_) => _,
|
|
86
|
+
withRequestSpan(name, options),
|
|
87
|
+
Effect.provide(requestStateLayer, { local: true })
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
// Streaming variant: binds ContextMapContainer to the ambient (request) scope so its
|
|
91
|
+
// finalizer (clear()) runs only after the response body is fully drained, not when the
|
|
92
|
+
// outer Effect returns its HttpServerResponse value. Use for handlers that return a
|
|
93
|
+
// streaming HttpServerResponse (e.g. SSE) — see RequestContextMiddleware for context.
|
|
94
|
+
export const setupStreamingRequestContextFromCurrent =
|
|
95
|
+
(name = "request", options?: Tracer.SpanOptions & SetupRequestOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) =>
|
|
96
|
+
self.pipe(
|
|
97
|
+
options?.withTransaction === true ? withSqlTransaction : (_) => _,
|
|
98
|
+
withRequestSpan(name, options),
|
|
99
|
+
provideOnRequestScope(requestStateLayer)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
// TODO: consider integrating Effect.withParentSpan
|
|
103
|
+
export function setupRequestContext<R, E, A>(
|
|
104
|
+
self: Effect.Effect<A, E, R>,
|
|
105
|
+
requestContext: RequestContext,
|
|
106
|
+
options?: SetupRequestOptions
|
|
107
|
+
) {
|
|
108
|
+
const layer = Layer.mergeAll(
|
|
109
|
+
requestStateLayer,
|
|
110
|
+
Layer.succeed(LocaleRef, requestContext.locale),
|
|
111
|
+
Layer.succeed(storeId, requestContext.namespace)
|
|
112
|
+
)
|
|
113
|
+
return self
|
|
114
|
+
.pipe(
|
|
115
|
+
options?.withTransaction === true ? withSqlTransaction : (_) => _,
|
|
116
|
+
withRequestSpan(requestContext.name),
|
|
117
|
+
Effect.provide(layer, { local: true })
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function setupRequestContextWithCustomSpan<R, E, A>(
|
|
122
|
+
self: Effect.Effect<A, E, R>,
|
|
123
|
+
requestContext: RequestContext,
|
|
124
|
+
name: string,
|
|
125
|
+
options?: Tracer.SpanOptions & SetupRequestOptions
|
|
126
|
+
) {
|
|
127
|
+
const layer = Layer.mergeAll(
|
|
128
|
+
requestStateLayer,
|
|
129
|
+
Layer.succeed(LocaleRef, requestContext.locale),
|
|
130
|
+
Layer.succeed(storeId, requestContext.namespace)
|
|
131
|
+
)
|
|
132
|
+
return self
|
|
133
|
+
.pipe(
|
|
134
|
+
options?.withTransaction === true ? withSqlTransaction : (_) => _,
|
|
135
|
+
withRequestSpan(name, options),
|
|
136
|
+
Effect.provide(layer, { local: true })
|
|
137
|
+
)
|
|
138
|
+
}
|
package/src/toast.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as Context from "./Context.ts"
|
|
2
|
+
import { accessEffectFn } from "./Context.ts"
|
|
3
|
+
import * as Effect from "./Effect.ts"
|
|
4
|
+
import * as Option from "./Option.ts"
|
|
5
|
+
|
|
6
|
+
export type ToastId = string | number
|
|
7
|
+
export type ToastOpts = { id?: ToastId; timeout?: number; groupId?: string; requestId?: string }
|
|
8
|
+
export type ToastOptsInternal = { id?: ToastId | null; timeout?: number; groupId?: string; requestId?: string }
|
|
9
|
+
|
|
10
|
+
export type UseToast = () => {
|
|
11
|
+
error: (this: void, message: string, options?: ToastOpts) => ToastId
|
|
12
|
+
warning: (this: void, message: string, options?: ToastOpts) => ToastId
|
|
13
|
+
success: (this: void, message: string, options?: ToastOpts) => ToastId
|
|
14
|
+
info: (this: void, message: string, options?: ToastOpts) => ToastId
|
|
15
|
+
dismiss: (this: void, id: ToastId) => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class CurrentToastId extends Context.Opaque<CurrentToastId, { toastId: ToastId }>()("CurrentToastId") {}
|
|
19
|
+
|
|
20
|
+
/** fallback to CurrentToastId when available unless id is explicitly set to a value or null */
|
|
21
|
+
export const wrap = (toast: ReturnType<UseToast>) => {
|
|
22
|
+
const wrap = (toastHandler: (message: string, options?: ToastOpts) => ToastId) => {
|
|
23
|
+
return (message: string, options?: ToastOptsInternal) =>
|
|
24
|
+
Effect.serviceOption(CurrentToastId).pipe(
|
|
25
|
+
Effect.flatMap((currentToast) =>
|
|
26
|
+
Effect.sync(() => {
|
|
27
|
+
const { id: _id, ...rest } = options ?? {}
|
|
28
|
+
const id = _id !== undefined
|
|
29
|
+
? _id ?? undefined
|
|
30
|
+
: Option.getOrUndefined(Option.map(currentToast, (_) => _.toastId))
|
|
31
|
+
// when id is undefined, we may end up with no toast at all..
|
|
32
|
+
return toastHandler(message, id !== undefined ? { ...rest, id } : rest)
|
|
33
|
+
})
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
error: wrap(toast.error),
|
|
39
|
+
info: wrap(toast.info),
|
|
40
|
+
success: wrap(toast.success),
|
|
41
|
+
warning: wrap(toast.warning),
|
|
42
|
+
dismiss: (toastId: ToastId) => Effect.sync(() => toast.dismiss(toastId))
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type ToastShape = ReturnType<typeof wrap>
|
|
47
|
+
|
|
48
|
+
export class Toast extends Context.Opaque<Toast, ToastShape>()("Toast") {
|
|
49
|
+
static readonly error = accessEffectFn(this, "error")
|
|
50
|
+
static readonly info = accessEffectFn(this, "info")
|
|
51
|
+
static readonly success = accessEffectFn(this, "success")
|
|
52
|
+
static readonly warning = accessEffectFn(this, "warning")
|
|
53
|
+
static readonly dismiss = accessEffectFn(this, "dismiss")
|
|
54
|
+
}
|