effect-app 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 +960 -0
- package/dist/Array.d.ts +1 -1
- package/dist/Chunk.d.ts +1 -1
- package/dist/Chunk.d.ts.map +1 -1
- package/dist/Config/SecretURL.d.ts +1 -1
- package/dist/Config/SecretURL.d.ts.map +1 -1
- package/dist/Config/SecretURL.js +2 -2
- package/dist/Config/internal/configSecretURL.d.ts +1 -1
- package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
- package/dist/Config.d.ts +7 -0
- package/dist/Config.d.ts.map +1 -0
- package/dist/Config.js +6 -0
- package/dist/ConfigProvider.d.ts +39 -0
- package/dist/ConfigProvider.d.ts.map +1 -0
- package/dist/ConfigProvider.js +42 -0
- package/dist/Context.d.ts +40 -0
- package/dist/Context.d.ts.map +1 -0
- package/dist/Context.js +67 -0
- package/dist/Effect.d.ts +9 -10
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +3 -6
- package/dist/Function.d.ts +1 -1
- package/dist/Function.d.ts.map +1 -1
- package/dist/Inputify.type.d.ts +1 -1
- package/dist/Layer.d.ts +7 -6
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +1 -1
- package/dist/NonEmptySet.d.ts +1 -1
- package/dist/NonEmptySet.d.ts.map +1 -1
- package/dist/Option.d.ts +1 -1
- package/dist/Option.d.ts.map +1 -1
- package/dist/Pure.d.ts +5 -5
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +13 -13
- package/dist/Schema/Class.d.ts +66 -20
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +189 -22
- package/dist/Schema/FastCheck.d.ts +1 -1
- package/dist/Schema/FastCheck.d.ts.map +1 -1
- package/dist/Schema/Methods.d.ts +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 +33 -0
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
- package/dist/Schema/SpecialJsonSchema.js +122 -0
- package/dist/Schema/SpecialOpenApi.d.ts +32 -0
- package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
- package/dist/Schema/SpecialOpenApi.js +123 -0
- package/dist/Schema/brand.d.ts +14 -6
- package/dist/Schema/brand.d.ts.map +1 -1
- package/dist/Schema/brand.js +1 -1
- package/dist/Schema/email.d.ts +1 -1
- package/dist/Schema/email.d.ts.map +1 -1
- package/dist/Schema/email.js +9 -5
- package/dist/Schema/ext.d.ts +121 -48
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +134 -52
- package/dist/Schema/moreStrings.d.ts +117 -17
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +19 -18
- package/dist/Schema/numbers.d.ts +127 -15
- package/dist/Schema/numbers.d.ts.map +1 -1
- package/dist/Schema/numbers.js +10 -12
- package/dist/Schema/phoneNumber.d.ts +1 -1
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +8 -4
- package/dist/Schema/schema.d.ts +1 -1
- package/dist/Schema/strings.d.ts +37 -5
- package/dist/Schema/strings.d.ts.map +1 -1
- package/dist/Schema/strings.js +1 -5
- package/dist/Schema.d.ts +159 -58
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +136 -68
- package/dist/Set.d.ts +1 -1
- package/dist/Set.d.ts.map +1 -1
- package/dist/TypeTest.d.ts +1 -1
- package/dist/Types.d.ts +1 -1
- package/dist/Widen.type.d.ts +1 -1
- package/dist/_ext/Array.d.ts +1 -1
- package/dist/_ext/Array.d.ts.map +1 -1
- package/dist/_ext/date.d.ts +1 -1
- package/dist/_ext/misc.d.ts +1 -1
- package/dist/_ext/ord.ext.d.ts +1 -1
- package/dist/_ext/ord.ext.d.ts.map +1 -1
- package/dist/builtin.d.ts +1 -1
- package/dist/builtin.d.ts.map +1 -1
- package/dist/client/InvalidationKeys.d.ts +29 -0
- package/dist/client/InvalidationKeys.d.ts.map +1 -0
- package/dist/client/InvalidationKeys.js +33 -0
- package/dist/client/apiClientFactory.d.ts +18 -32
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +98 -36
- package/dist/client/clientFor.d.ts +51 -17
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/clientFor.js +9 -1
- package/dist/client/errors.d.ts +49 -25
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +43 -17
- package/dist/client/makeClient.d.ts +468 -32
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +61 -34
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -1
- package/dist/faker.d.ts +1 -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 +5 -5
- package/dist/http/internal/lib.d.ts +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/ids.d.ts +9 -9
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +3 -2
- package/dist/index.d.ts +5 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -9
- package/dist/logger.d.ts +1 -1
- package/dist/middleware.d.ts +16 -9
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +13 -9
- package/dist/rpc/Invalidation.d.ts +397 -0
- package/dist/rpc/Invalidation.d.ts.map +1 -0
- package/dist/rpc/Invalidation.js +150 -0
- package/dist/rpc/MiddlewareMaker.d.ts +6 -5
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +51 -28
- package/dist/rpc/RpcContextMap.d.ts +3 -3
- package/dist/rpc/RpcContextMap.d.ts.map +1 -1
- package/dist/rpc/RpcContextMap.js +4 -4
- package/dist/rpc/RpcMiddleware.d.ts +6 -5
- package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.js +1 -1
- package/dist/rpc.d.ts +2 -2
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +2 -2
- package/dist/transform.d.ts +1 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/transform.js +3 -3
- package/dist/utils/effectify.d.ts +1 -1
- package/dist/utils/extend.d.ts +1 -1
- package/dist/utils/extend.d.ts.map +1 -1
- package/dist/utils/gen.d.ts +2 -2
- 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 +3 -3
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +3 -3
- package/dist/utils.d.ts +48 -10
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +33 -8
- package/dist/validation/validators.d.ts +1 -1
- package/dist/validation/validators.d.ts.map +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/package.json +46 -28
- package/src/Config/SecretURL.ts +2 -1
- package/src/Config.ts +14 -0
- package/src/ConfigProvider.ts +48 -0
- package/src/{ServiceMap.ts → Context.ts} +58 -64
- package/src/Effect.ts +12 -14
- package/src/Layer.ts +6 -5
- package/src/Pure.ts +17 -18
- package/src/Schema/Class.ts +268 -62
- package/src/Schema/SchemaParser.ts +12 -0
- package/src/Schema/SpecialJsonSchema.ts +137 -0
- package/src/Schema/SpecialOpenApi.ts +130 -0
- package/src/Schema/brand.ts +21 -7
- package/src/Schema/email.ts +10 -3
- package/src/Schema/ext.ts +226 -86
- package/src/Schema/moreStrings.ts +37 -32
- package/src/Schema/numbers.ts +14 -16
- package/src/Schema/phoneNumber.ts +8 -2
- package/src/Schema/strings.ts +4 -8
- package/src/Schema.ts +350 -107
- package/src/client/InvalidationKeys.ts +50 -0
- package/src/client/apiClientFactory.ts +227 -136
- package/src/client/clientFor.ts +86 -29
- package/src/client/errors.ts +61 -26
- package/src/client/makeClient.ts +530 -80
- package/src/client.ts +1 -0
- package/src/http/Request.ts +7 -4
- package/src/ids.ts +3 -2
- package/src/index.ts +5 -11
- package/src/middleware.ts +12 -10
- package/src/rpc/Invalidation.ts +221 -0
- package/src/rpc/MiddlewareMaker.ts +61 -51
- package/src/rpc/README.md +2 -2
- package/src/rpc/RpcContextMap.ts +6 -5
- package/src/rpc/RpcMiddleware.ts +6 -5
- package/src/rpc.ts +1 -1
- package/src/transform.ts +2 -2
- package/src/utils/gen.ts +1 -1
- package/src/utils/logger.ts +2 -2
- package/src/utils.ts +73 -15
- package/test/dist/moreStrings.test.d.ts.map +1 -0
- package/test/dist/rpc.test.d.ts.map +1 -1
- package/test/dist/secretURL.test.d.ts.map +1 -0
- package/test/dist/special.test.d.ts.map +1 -0
- package/test/moreStrings.test.ts +17 -0
- package/test/rpc.test.ts +38 -6
- package/test/schema.test.ts +609 -4
- package/test/secretURL.test.ts +157 -0
- package/test/special.test.ts +1023 -0
- package/test/utils.test.ts +6 -6
- package/tsconfig.base.json +3 -4
- package/tsconfig.json +0 -1
- package/tsconfig.json.bak +2 -2
- package/tsconfig.src.json +29 -29
- package/tsconfig.test.json +2 -2
- package/dist/Operations.d.ts +0 -87
- package/dist/Operations.d.ts.map +0 -1
- package/dist/Operations.js +0 -29
- package/dist/ServiceMap.d.ts +0 -44
- package/dist/ServiceMap.d.ts.map +0 -1
- package/dist/ServiceMap.js +0 -91
- package/dist/Struct.d.ts +0 -44
- package/dist/Struct.d.ts.map +0 -1
- package/dist/Struct.js +0 -29
- package/eslint.config.mjs +0 -26
- package/src/Operations.ts +0 -55
- package/src/Struct.ts +0 -54
package/src/ids.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Effect } from "effect"
|
|
2
|
+
import { brandedStringId, type Codec, NonEmptyString255, StringId, type StringIdBrand, withDefaultMake } from "effect-app/Schema"
|
|
2
3
|
import type { B } from "effect-app/Schema/schema"
|
|
3
4
|
import type { Simplify } from "effect/Types"
|
|
4
5
|
import { S } from "./index.js"
|
|
@@ -17,7 +18,7 @@ export const RequestId = extendM(
|
|
|
17
18
|
const make = StringId.make as () => NonEmptyString255
|
|
18
19
|
return ({
|
|
19
20
|
make,
|
|
20
|
-
withDefault:
|
|
21
|
+
withDefault: S.withConstructorDefault(Effect.sync(make))(s as typeof s & S.WithoutConstructorDefault)
|
|
21
22
|
})
|
|
22
23
|
}
|
|
23
24
|
)
|
package/src/index.ts
CHANGED
|
@@ -1,31 +1,25 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-unassigned-import
|
|
1
2
|
import "./builtin.js"
|
|
2
3
|
|
|
3
|
-
import * as ServiceMap from "./ServiceMap.js"
|
|
4
|
-
|
|
5
4
|
export * as Fnc from "./Function.js"
|
|
6
5
|
export * as Utils from "./utils.js"
|
|
7
6
|
|
|
8
7
|
export * as Array from "./Array.js"
|
|
8
|
+
export * as Config from "./Config.js"
|
|
9
|
+
export * as ConfigProvider from "./ConfigProvider.js"
|
|
10
|
+
export * as Context from "./Context.js"
|
|
9
11
|
export * as Effect from "./Effect.js"
|
|
10
12
|
export * as Layer from "./Layer.js"
|
|
11
13
|
export * as NonEmptySet from "./NonEmptySet.js"
|
|
12
|
-
export * as ServiceMap from "./ServiceMap.js"
|
|
13
14
|
export * as Set from "./Set.js"
|
|
14
15
|
|
|
15
|
-
export {
|
|
16
|
-
/**
|
|
17
|
-
* @deprecated use ServiceMap directly instead
|
|
18
|
-
*/
|
|
19
|
-
ServiceMap as Context
|
|
20
|
-
}
|
|
21
|
-
|
|
22
16
|
export { type NonEmptyArray, type NonEmptyReadonlyArray } from "./Array.js"
|
|
23
17
|
|
|
24
18
|
export * from "effect"
|
|
25
19
|
|
|
26
|
-
export * as Struct from "./Struct.js"
|
|
27
20
|
export type * as Types from "./Types.js"
|
|
28
21
|
|
|
29
22
|
export * as SecretURL from "./Config/SecretURL.js"
|
|
23
|
+
export * as RpcX from "./rpc.js"
|
|
30
24
|
export * as S from "./Schema.js"
|
|
31
25
|
export { copy } from "./utils.js"
|
package/src/middleware.ts
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { Context } from "effect-app"
|
|
3
|
+
import { RpcMiddleware } from "./rpc.js"
|
|
4
4
|
|
|
5
|
-
export class DevMode extends
|
|
5
|
+
export class DevMode extends Context.Reference("DevMode", { defaultValue: () => false }) {}
|
|
6
6
|
|
|
7
|
-
export class RequestCacheMiddleware
|
|
8
|
-
extends RpcX.RpcMiddleware.Tag<RequestCacheMiddleware>()("RequestCacheMiddleware")
|
|
9
|
-
{}
|
|
7
|
+
export class RequestCacheMiddleware extends RpcMiddleware.Tag<RequestCacheMiddleware>()("RequestCacheMiddleware") {}
|
|
10
8
|
|
|
11
9
|
export class ConfigureInterruptibilityMiddleware
|
|
12
|
-
extends
|
|
10
|
+
extends RpcMiddleware.Tag<ConfigureInterruptibilityMiddleware>()("ConfigureInterruptibilityMiddleware")
|
|
13
11
|
{}
|
|
14
12
|
|
|
15
|
-
export class LoggerMiddleware extends
|
|
13
|
+
export class LoggerMiddleware extends RpcMiddleware.Tag<LoggerMiddleware>()("LoggerMiddleware") {}
|
|
14
|
+
|
|
15
|
+
export class DevModeMiddleware extends RpcMiddleware.Tag<DevModeMiddleware>()("DevModeMiddleware") {}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
/** RPC middleware that reads the `Invalidates` annotation and populates `InvalidationSet` before the handler runs. */
|
|
18
|
+
export class InvalidationMiddleware extends RpcMiddleware.Tag<InvalidationMiddleware>()("InvalidationMiddleware") {}
|
|
18
19
|
|
|
19
20
|
export const DefaultGenericMiddlewares = [
|
|
20
21
|
RequestCacheMiddleware,
|
|
21
22
|
ConfigureInterruptibilityMiddleware,
|
|
22
23
|
LoggerMiddleware,
|
|
23
|
-
DevModeMiddleware
|
|
24
|
+
DevModeMiddleware,
|
|
25
|
+
InvalidationMiddleware
|
|
24
26
|
] as const
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import * as Ref from "effect/Ref"
|
|
2
|
+
import { Rpc } from "effect/unstable/rpc"
|
|
3
|
+
import * as Context from "../Context.js"
|
|
4
|
+
import * as Effect from "../Effect.js"
|
|
5
|
+
import * as S from "../Schema.js"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A single segment within an `InvalidationKey` array.
|
|
9
|
+
* Accepts any JSON-compatible value: string, number, boolean, null,
|
|
10
|
+
* arrays and objects recursively — matching TanStack Query's `queryKey` element type.
|
|
11
|
+
*/
|
|
12
|
+
export const InvalidationKeySegment = S.Json
|
|
13
|
+
export type InvalidationKeySegment = S.Schema.Type<typeof InvalidationKeySegment>
|
|
14
|
+
|
|
15
|
+
/** Schema for a single invalidation key – an array of segments compatible with TanStack Query `queryKey`. */
|
|
16
|
+
export const InvalidationKey = S.Array(InvalidationKeySegment)
|
|
17
|
+
export type InvalidationKey = S.Schema.Type<typeof InvalidationKey>
|
|
18
|
+
|
|
19
|
+
/** Schema for the full set of invalidation keys – an array of `InvalidationKey`. */
|
|
20
|
+
export const InvalidationKeys = S.Array(InvalidationKey)
|
|
21
|
+
export type InvalidationKeys = S.Schema.Type<typeof InvalidationKeys>
|
|
22
|
+
|
|
23
|
+
/** Metadata included in every command response for server-driven cache invalidation. */
|
|
24
|
+
export const CommandMetaData = S.Struct({ invalidateQueries: InvalidationKeys })
|
|
25
|
+
export type CommandMetaData = S.Schema.Type<typeof CommandMetaData>
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Wraps a command's success schema so that the wire format carries both the `payload`
|
|
29
|
+
* (the handler's actual return value) and `metadata` (server-driven cache invalidation keys).
|
|
30
|
+
* Transparent to users: the server handler returns the plain payload and the client receives
|
|
31
|
+
* the plain payload — wrapping/unwrapping is handled internally by the routing layer.
|
|
32
|
+
*/
|
|
33
|
+
export const CommandResponseWithMetaData = <S extends S.Top>(success: S) =>
|
|
34
|
+
S.Struct({ payload: success, metadata: CommandMetaData })
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Wraps a command's failure schema so that the wire format carries both the `error`
|
|
38
|
+
* (the handler's actual failure value) and `metadata` (server-driven cache invalidation keys
|
|
39
|
+
* accumulated thus far before the failure occurred).
|
|
40
|
+
* Transparent to users: the server handler fails with the plain error and the client receives
|
|
41
|
+
* the plain error — wrapping/unwrapping is handled internally by the routing layer.
|
|
42
|
+
*/
|
|
43
|
+
export const CommandFailureWithMetaData = <E extends S.Top>(error: E) =>
|
|
44
|
+
S.Struct({ _tag: S.Literal("CommandFailureWithMetaData"), error, metadata: CommandMetaData })
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Stream chunk schema for stream responses with metadata.
|
|
48
|
+
* Each item is either a data value, an intermediate "metadata" signal carrying cache
|
|
49
|
+
* invalidation keys accumulated since the previous drain, or a final "done" signal.
|
|
50
|
+
* Transparent to users: stream handlers return plain values and clients receive plain values —
|
|
51
|
+
* wrapping/unwrapping is handled internally by the routing layer.
|
|
52
|
+
*
|
|
53
|
+
* The "done" chunk is always the last item in the stream and carries any remaining invalidation
|
|
54
|
+
* keys. An optional "metadata" chunk may appear after any "value" chunk and carries keys
|
|
55
|
+
* accumulated since the last drain (V3: mid-stream invalidation).
|
|
56
|
+
*/
|
|
57
|
+
export const StreamResponseChunk = <S extends S.Top>(success: S) =>
|
|
58
|
+
S.Union([
|
|
59
|
+
S.Struct({ _tag: S.Literal("value"), value: success }),
|
|
60
|
+
S.Struct({ _tag: S.Literal("metadata"), metadata: CommandMetaData }),
|
|
61
|
+
S.Struct({ _tag: S.Literal("done"), metadata: CommandMetaData })
|
|
62
|
+
])
|
|
63
|
+
|
|
64
|
+
export type StreamResponseChunk<A> =
|
|
65
|
+
| { readonly _tag: "value"; readonly value: A }
|
|
66
|
+
| { readonly _tag: "metadata"; readonly metadata: CommandMetaData }
|
|
67
|
+
| { readonly _tag: "done"; readonly metadata: CommandMetaData }
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Stream chunk schema for stream failures with metadata.
|
|
71
|
+
* Used to signal a stream failure while still carrying cache invalidation keys
|
|
72
|
+
* accumulated thus far.
|
|
73
|
+
*/
|
|
74
|
+
export const StreamFailureChunk = <E extends S.Top>(error: E) =>
|
|
75
|
+
S.Struct({ _tag: S.Literal("error"), error, metadata: CommandMetaData })
|
|
76
|
+
|
|
77
|
+
export type StreamFailureChunk<E> = { readonly _tag: "error"; readonly error: E; readonly metadata: CommandMetaData }
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Context annotation for declaring static cache invalidation keys on a low-level `Rpc` definition.
|
|
81
|
+
* These keys are always included in the command response metadata, regardless of the handler logic.
|
|
82
|
+
*
|
|
83
|
+
* Prefer using `makeQueryKey` over raw string arrays to stay in sync with the actual query
|
|
84
|
+
* definitions without manual string maintenance:
|
|
85
|
+
*
|
|
86
|
+
* ```ts
|
|
87
|
+
* import { makeQueryKey } from "effect-app/client"
|
|
88
|
+
* import { Invalidation } from "effect-app/rpc"
|
|
89
|
+
* import * as UserRsc from "../User/index.js" // separate module to avoid circular deps
|
|
90
|
+
*
|
|
91
|
+
* class UpdateProfile extends Rpc.make("UpdateProfile", { ... })
|
|
92
|
+
* .annotate(Invalidation.Invalidates, [makeQueryKey(UserRsc.GetMe), makeQueryKey(UserRsc.GetProfile)]) {}
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* **Circular dependency note:** if mutations and queries live in the same file you may hit a
|
|
96
|
+
* circular reference at evaluation time. The idiomatic fix is to move mutations into their own
|
|
97
|
+
* module (e.g. `User/mutations.ts`) that directly imports the relevant query classes rather than
|
|
98
|
+
* re-exporting them through a barrel.
|
|
99
|
+
*
|
|
100
|
+
* For the higher-level `Command`/`Query` builders from `makeRpcClient`, use the
|
|
101
|
+
* `invalidatesQueries` callback argument instead (it receives the same query keys at runtime).
|
|
102
|
+
*/
|
|
103
|
+
export const Invalidates = Context.Reference<ReadonlyArray<InvalidationKey>>(
|
|
104
|
+
"effect-app/rpc/Invalidates",
|
|
105
|
+
{ defaultValue: () => [] }
|
|
106
|
+
)
|
|
107
|
+
export type Invalidates = typeof Invalidates
|
|
108
|
+
|
|
109
|
+
/** The shape of the per-request service that accumulates invalidation keys. */
|
|
110
|
+
export interface InvalidationSetService {
|
|
111
|
+
readonly add: (key: InvalidationKey) => Effect.Effect<void>
|
|
112
|
+
readonly get: Effect.Effect<ReadonlyArray<InvalidationKey>>
|
|
113
|
+
/**
|
|
114
|
+
* V3: Reads all currently accumulated keys and resets the bucket to empty.
|
|
115
|
+
* Used by the stream routing layer to emit intermediate "metadata" chunks
|
|
116
|
+
* without re-sending keys that have already been forwarded to the client.
|
|
117
|
+
*/
|
|
118
|
+
readonly drain: Effect.Effect<ReadonlyArray<InvalidationKey>>
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Request-scoped service for accumulating invalidation keys dynamically inside a handler.
|
|
123
|
+
* Provided by `InvalidationMiddlewareLive` for every RPC call; has a no-op default so it is
|
|
124
|
+
* safe to use even when the HTTP middleware is absent (tests, workers, etc.).
|
|
125
|
+
*
|
|
126
|
+
* Use `InvalidationSet.use(_ => _.add(key))` (or `.useSync` for non-Effect callbacks) as a
|
|
127
|
+
* shorthand instead of yielding the service manually.
|
|
128
|
+
*
|
|
129
|
+
* Prefer `makeQueryKey` over raw string arrays so invalidation keys stay in sync with the
|
|
130
|
+
* actual query definitions automatically:
|
|
131
|
+
*
|
|
132
|
+
* ```ts
|
|
133
|
+
* import { makeQueryKey } from "effect-app/client"
|
|
134
|
+
* import { Effect } from "effect"
|
|
135
|
+
* import { Invalidation } from "effect-app/rpc"
|
|
136
|
+
* import * as CartRsc from "../Cart/queries.js"
|
|
137
|
+
* import * as UserRsc from "../User/queries.js"
|
|
138
|
+
*
|
|
139
|
+
* const handler = Effect.fnUntraced(function*(req: UpdateCartRequest) {
|
|
140
|
+
* const cart = yield* CartRepo.save(req.cart)
|
|
141
|
+
*
|
|
142
|
+
* // Stage 1 – unconditional: always invalidate after saving
|
|
143
|
+
* yield* Invalidation.InvalidationSet.use(_ => _.add(makeQueryKey(UserRsc.GetMe)))
|
|
144
|
+
*
|
|
145
|
+
* // Stage 2 – conditional: only if the cart changed state
|
|
146
|
+
* if (cart.isCheckedOut) {
|
|
147
|
+
* yield* Invalidation.InvalidationSet.use(_ => _.add(makeQueryKey(CartRsc.GetCartStats)))
|
|
148
|
+
* }
|
|
149
|
+
*
|
|
150
|
+
* return cart
|
|
151
|
+
* })
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* You can combine static (`Invalidates` annotation) and dynamic (`InvalidationSet.use`) keys:
|
|
155
|
+
* the annotation pre-populates the set before the handler runs; dynamic additions accumulate
|
|
156
|
+
* throughout the handler. All keys are included in the command response metadata.
|
|
157
|
+
*/
|
|
158
|
+
export const InvalidationSet = Context.Reference<InvalidationSetService>(
|
|
159
|
+
"effect-app/rpc/InvalidationSet",
|
|
160
|
+
{
|
|
161
|
+
defaultValue: () => ({
|
|
162
|
+
add: (_key: InvalidationKey) => Effect.void,
|
|
163
|
+
get: Effect.succeed([] as ReadonlyArray<InvalidationKey>),
|
|
164
|
+
drain: Effect.succeed([] as ReadonlyArray<InvalidationKey>)
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
export type InvalidationSet = typeof InvalidationSet
|
|
169
|
+
|
|
170
|
+
/** Creates a fresh `InvalidationSet` implementation backed by a `Ref`. */
|
|
171
|
+
export const makeInvalidationSet = (ref: Ref.Ref<ReadonlyArray<InvalidationKey>>): InvalidationSetService => ({
|
|
172
|
+
add: (key) => Ref.update(ref, (keys) => [...keys, key]),
|
|
173
|
+
get: Ref.get(ref),
|
|
174
|
+
drain: Ref.getAndSet(ref, [])
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* `Rpc.Custom` definition for command RPCs that wrap the success/error schemas
|
|
179
|
+
* with `CommandResponseWithMetaData` / `CommandFailureWithMetaData`.
|
|
180
|
+
*/
|
|
181
|
+
// eslint-disable-next-line import/namespace
|
|
182
|
+
export interface CommandRpc extends Rpc.Custom {
|
|
183
|
+
readonly out: Rpc.Custom.Out<
|
|
184
|
+
ReturnType<typeof CommandResponseWithMetaData<this["success"] & S.Top>>,
|
|
185
|
+
ReturnType<typeof CommandFailureWithMetaData<this["error"] & S.Top>>
|
|
186
|
+
>
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Custom Rpc constructor for command RPCs.
|
|
191
|
+
* Wraps the success schema with `CommandResponseWithMetaData` and
|
|
192
|
+
* the error schema with `CommandFailureWithMetaData`.
|
|
193
|
+
*/
|
|
194
|
+
export const makeCommandRpc = Rpc.custom<CommandRpc>(({ defect, error, success }) => ({
|
|
195
|
+
success: CommandResponseWithMetaData(success),
|
|
196
|
+
error: CommandFailureWithMetaData(error),
|
|
197
|
+
defect
|
|
198
|
+
}))
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* `Rpc.Custom` definition for stream RPCs that wrap the success/error schemas
|
|
202
|
+
* with `StreamResponseChunk` / `StreamFailureChunk`.
|
|
203
|
+
*/
|
|
204
|
+
// eslint-disable-next-line import/namespace
|
|
205
|
+
export interface StreamRpc extends Rpc.Custom {
|
|
206
|
+
readonly out: Rpc.Custom.Out<
|
|
207
|
+
ReturnType<typeof StreamResponseChunk<this["success"] & S.Top>>,
|
|
208
|
+
ReturnType<typeof StreamFailureChunk<this["error"] & S.Top>>
|
|
209
|
+
>
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Custom Rpc constructor for stream RPCs.
|
|
214
|
+
* Wraps the success schema with `StreamResponseChunk` and
|
|
215
|
+
* the error schema with `StreamFailureChunk`.
|
|
216
|
+
*/
|
|
217
|
+
export const makeStreamRpc = Rpc.custom<StreamRpc>(({ defect, error, success }) => ({
|
|
218
|
+
success: StreamResponseChunk(success),
|
|
219
|
+
error: StreamFailureChunk(error),
|
|
220
|
+
defect
|
|
221
|
+
}))
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { Effect, Layer, type Schema, Schema as S, type Scope
|
|
2
|
+
import { Effect, Layer, type Schema, Schema as S, type Scope } from "effect"
|
|
3
3
|
import { type NonEmptyArray, type NonEmptyReadonlyArray } from "effect/Array"
|
|
4
4
|
import { type Simplify } from "effect/Types"
|
|
5
5
|
import { Rpc, type RpcGroup, type RpcSchema } from "effect/unstable/rpc"
|
|
6
6
|
import { type HandlersFrom } from "effect/unstable/rpc/RpcGroup"
|
|
7
|
-
import
|
|
8
|
-
import { type HttpHeaders } from "../http.js"
|
|
7
|
+
import * as Context from "../Context.js"
|
|
9
8
|
import { PreludeLogger } from "../logger.js"
|
|
10
9
|
import { type TypeTestId } from "../TypeTest.js"
|
|
11
10
|
import { typedValuesOf } from "../utils.js"
|
|
@@ -14,7 +13,8 @@ import { type AddMiddleware, type AnyDynamic, type RpcDynamic, type RpcMiddlewar
|
|
|
14
13
|
import * as RpcMiddlewareX from "./RpcMiddleware.js"
|
|
15
14
|
|
|
16
15
|
// adapter for effect/rpc v3 middleware provides. (in effect-smol (v4), it's just a Service Identifier, no tags.)
|
|
17
|
-
|
|
16
|
+
// hm?
|
|
17
|
+
type MakeTags<A> = A
|
|
18
18
|
|
|
19
19
|
export interface MiddlewareMaker<
|
|
20
20
|
Self,
|
|
@@ -60,13 +60,13 @@ export interface MiddlewareMaker<
|
|
|
60
60
|
}
|
|
61
61
|
>
|
|
62
62
|
{
|
|
63
|
-
readonly layer: Layer.Layer<Self, never,
|
|
63
|
+
readonly layer: Layer.Layer<Self, never, Context.Service.Identifier<MiddlewareProviders[number]>>
|
|
64
64
|
readonly requestContext: RequestContextTag<RequestContextMap>
|
|
65
65
|
readonly requestContextMap: RequestContextMap
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
export interface RequestContextTag<RequestContextMap extends Record<string, RpcContextMap.Any>>
|
|
69
|
-
extends
|
|
69
|
+
extends Context.Service<"RequestContextConfig", GetContextConfig<RequestContextMap>>
|
|
70
70
|
{}
|
|
71
71
|
|
|
72
72
|
export namespace MiddlewareMaker {
|
|
@@ -257,10 +257,10 @@ export type MiddlewaresBuilder<
|
|
|
257
257
|
: { new(_: never): {} }
|
|
258
258
|
: { new(_: never): {} })
|
|
259
259
|
|
|
260
|
-
const middlewareMaker =
|
|
260
|
+
const middlewareMaker = Effect.fnUntraced(function*<
|
|
261
261
|
MiddlewareProviders extends ReadonlyArray<MiddlewareMaker.Any>
|
|
262
|
-
>(middlewares: MiddlewareProviders)
|
|
263
|
-
RpcMiddlewareV4<
|
|
262
|
+
>(middlewares: MiddlewareProviders) {
|
|
263
|
+
type Middleware = RpcMiddlewareV4<
|
|
264
264
|
MiddlewareMaker.ManyProvided<MiddlewareProviders>,
|
|
265
265
|
MiddlewareMaker.ManyErrors<MiddlewareProviders>,
|
|
266
266
|
Exclude<
|
|
@@ -269,42 +269,32 @@ const middlewareMaker = <
|
|
|
269
269
|
> extends never ? never
|
|
270
270
|
: Exclude<MiddlewareMaker.ManyRequired<MiddlewareProviders>, MiddlewareMaker.ManyProvided<MiddlewareProviders>>
|
|
271
271
|
>
|
|
272
|
-
|
|
272
|
+
type Next = Parameters<Middleware>[0]
|
|
273
|
+
type Options = Parameters<Middleware>[1]
|
|
274
|
+
|
|
273
275
|
// we want to run them in reverse order because latter middlewares will provide context to former ones
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
let handler = next
|
|
293
|
-
|
|
294
|
-
// inspired from Effect/RpcMiddleware
|
|
295
|
-
for (const tag of middlewares) {
|
|
296
|
-
// use the tag to get the middleware from context
|
|
297
|
-
const middleware = ServiceMap.getUnsafe(context, tag)
|
|
298
|
-
|
|
299
|
-
// wrap the current handler, allowing the middleware to run before and after it
|
|
300
|
-
handler = PreludeLogger.logDebug("Applying middleware wrap " + tag.key).pipe(
|
|
301
|
-
Effect.andThen(middleware(handler, options))
|
|
302
|
-
) as any
|
|
303
|
-
}
|
|
304
|
-
return handler
|
|
276
|
+
const reversed = middlewares.toReversed()
|
|
277
|
+
const context = yield* Effect.context()
|
|
278
|
+
|
|
279
|
+
// returns a Effect/RpcMiddlewareV4 with Scope.Scope in requirements
|
|
280
|
+
// v4: wrap middleware takes (effect, options) as two params instead of a single options bag
|
|
281
|
+
return (next: Next, options: Options) => {
|
|
282
|
+
// we start with the actual handler
|
|
283
|
+
let handler = next
|
|
284
|
+
|
|
285
|
+
// inspired from Effect/RpcMiddleware
|
|
286
|
+
for (const tag of reversed) {
|
|
287
|
+
// use the tag to get the middleware from context
|
|
288
|
+
const middleware = Context.getUnsafe(context, tag)
|
|
289
|
+
|
|
290
|
+
// wrap the current handler, allowing the middleware to run before and after it
|
|
291
|
+
handler = PreludeLogger.logDebug("Applying middleware wrap " + tag.key).pipe(
|
|
292
|
+
Effect.andThen(middleware(handler, options))
|
|
293
|
+
) as any
|
|
305
294
|
}
|
|
306
|
-
|
|
307
|
-
}
|
|
295
|
+
return handler
|
|
296
|
+
}
|
|
297
|
+
})
|
|
308
298
|
|
|
309
299
|
const makeMiddlewareBasic = <Self>() =>
|
|
310
300
|
// by setting RequestContextMap beforehand, execute contextual typing does not fuck up itself to anys
|
|
@@ -320,7 +310,26 @@ const makeMiddlewareBasic = <Self>() =>
|
|
|
320
310
|
// reverse middlewares and wrap one after the other
|
|
321
311
|
const middleware = middlewareMaker(make)
|
|
322
312
|
|
|
323
|
-
|
|
313
|
+
// Per-middleware error: union of the static `error` on the tag (if any) AND
|
|
314
|
+
// the rcm config entry pointed at by the middleware's `dynamic.key` (if any).
|
|
315
|
+
// Reason: middlewares declared with `dynamic: RequestContextMap.get("foo")`
|
|
316
|
+
// don't set a static `error` field — at runtime their `.error` defaults to
|
|
317
|
+
// `Schema.Never`. Without pulling from rcm, the composite middleware's
|
|
318
|
+
// `.error` collapses to `Never`, and `Rpc.exitSchema` (which walks
|
|
319
|
+
// `rpc.middlewares[*].error` to build the wire failure union) can't decode
|
|
320
|
+
// the actual middleware-thrown error type. Critical for stream rpcs whose
|
|
321
|
+
// top-level `errorSchema` is force-set to `Never` by effect-rpc.
|
|
322
|
+
const isMeaningfulError = (e: S.Top | undefined): e is S.Top => e !== undefined && e !== null && e !== S.Never
|
|
323
|
+
const rcmRecord = rcm as Record<string, RpcContextMap.Any>
|
|
324
|
+
const failures: Array<S.Top> = make.flatMap((_) => {
|
|
325
|
+
const out: Array<S.Top> = []
|
|
326
|
+
if (isMeaningfulError(_.error)) out.push(_.error)
|
|
327
|
+
const key = _.dynamic?.key as string | undefined
|
|
328
|
+
if (key && rcmRecord[key] && isMeaningfulError(rcmRecord[key].error)) {
|
|
329
|
+
out.push(rcmRecord[key].error)
|
|
330
|
+
}
|
|
331
|
+
return out
|
|
332
|
+
})
|
|
324
333
|
const provides = make.flatMap((_) => !_.provides ? [] : Array.isArray(_.provides) ? _.provides : [_.provides])
|
|
325
334
|
const requires = make
|
|
326
335
|
.flatMap((_) => !_.requires ? [] : Array.isArray(_.requires) ? _.requires : [_.requires])
|
|
@@ -356,17 +365,18 @@ const makeMiddlewareBasic = <Self>() =>
|
|
|
356
365
|
.effect(
|
|
357
366
|
MiddlewareMaker,
|
|
358
367
|
middleware as Effect.Effect<
|
|
359
|
-
any
|
|
360
|
-
Effect.Error<typeof middleware>,
|
|
361
|
-
Effect.Services<typeof middleware>
|
|
368
|
+
any
|
|
362
369
|
>
|
|
370
|
+
// todo; they dont change the type..
|
|
371
|
+
// Effect.Error<typeof middleware>,
|
|
372
|
+
// Effect.Services<typeof middleware>
|
|
363
373
|
)
|
|
364
374
|
|
|
365
375
|
// add to the tag a default implementation
|
|
366
376
|
return Object.assign(MiddlewareMaker, {
|
|
367
377
|
layer,
|
|
368
378
|
// tag to be used to retrieve the RequestContextConfig from Rpc annotations
|
|
369
|
-
requestContext:
|
|
379
|
+
requestContext: Context.Service<"RequestContextConfig", GetContextConfig<RequestContextMap>>(
|
|
370
380
|
"RequestContextConfig"
|
|
371
381
|
),
|
|
372
382
|
requestContextMap: rcm
|
|
@@ -378,8 +388,8 @@ export const Tag = <Self>() =>
|
|
|
378
388
|
const Id extends string,
|
|
379
389
|
RequestContextMap extends RequestContextMapTagAny
|
|
380
390
|
>(id: Id, rcm: RequestContextMap): MiddlewaresBuilder<Self, Id, RequestContextMap["config"]> => {
|
|
381
|
-
|
|
382
|
-
const requestContext =
|
|
391
|
+
const allMiddleware: MiddlewareMaker.Any[] = []
|
|
392
|
+
const requestContext = Context.Service<"RequestContextConfig", GetContextConfig<RequestContextMap["config"]>>(
|
|
383
393
|
"RequestContextConfig"
|
|
384
394
|
)
|
|
385
395
|
const it = {
|
|
@@ -436,7 +446,7 @@ export const Tag = <Self>() =>
|
|
|
436
446
|
middleware: (...middlewares: any[]) => {
|
|
437
447
|
for (const mw of middlewares) {
|
|
438
448
|
// recall that we run middlewares in reverse order
|
|
439
|
-
allMiddleware
|
|
449
|
+
allMiddleware.unshift(mw)
|
|
440
450
|
}
|
|
441
451
|
return allMiddleware.filter((m) => !!m.dynamic).length !== Object.keys(rcm.config).length
|
|
442
452
|
// 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,14 +2,15 @@
|
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
4
|
|
|
5
|
-
import { type Schema as S
|
|
5
|
+
import { type Schema as S } from "effect"
|
|
6
6
|
import { type AnyWithProps } from "effect/unstable/rpc/Rpc"
|
|
7
|
+
import * as Context from "../Context.js"
|
|
7
8
|
import { type RpcDynamic } from "./RpcMiddleware.js"
|
|
8
9
|
|
|
9
10
|
type Values<T extends Record<any, any>> = T[keyof T]
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
* Middleware is inactivate by default, the Key is optional in route context, and the service is optionally provided as Effect
|
|
13
|
+
* Middleware is inactivate by default, the Key is optional in route context, and the service is optionally provided as Effect Context.
|
|
13
14
|
* Unless explicitly configured as `true`.
|
|
14
15
|
*/
|
|
15
16
|
export type RpcContextMap<Service, E> = {
|
|
@@ -22,7 +23,7 @@ export type RpcContextMap<Service, E> = {
|
|
|
22
23
|
|
|
23
24
|
export declare namespace RpcContextMap {
|
|
24
25
|
/**
|
|
25
|
-
* Middleware is active by default, and provides the Service at Key in route context, and the Service is provided as Effect
|
|
26
|
+
* Middleware is active by default, and provides the Service at Key in route context, and the Service is provided as Effect Context.
|
|
26
27
|
* Unless explicitly omitted.
|
|
27
28
|
*/
|
|
28
29
|
export type Inverted<Service, E> = {
|
|
@@ -97,7 +98,7 @@ export type GetEffectError<RequestContextMap extends Record<string, RpcContextMa
|
|
|
97
98
|
}
|
|
98
99
|
>
|
|
99
100
|
|
|
100
|
-
const tag =
|
|
101
|
+
const tag = Context.Service("RequestContextConfig")
|
|
101
102
|
|
|
102
103
|
export const makeMap = <const Config extends Record<string, RpcContextMap.Any>>(config: Config) => {
|
|
103
104
|
const cls = class {
|
|
@@ -109,7 +110,7 @@ export const makeMap = <const Config extends Record<string, RpcContextMap.Any>>(
|
|
|
109
110
|
return Object.assign(cls, {
|
|
110
111
|
config, /** Retrieves RequestContextConfig out of the Rpc annotations */
|
|
111
112
|
getConfig: (rpc: AnyWithProps): GetContextConfig<Config> => {
|
|
112
|
-
return
|
|
113
|
+
return Context.getOrElse(rpc.annotations, tag as any, () => ({}))
|
|
113
114
|
},
|
|
114
115
|
/** Adapter used when setting the dynamic prop on a middleware implementation */
|
|
115
116
|
get: <
|
package/src/rpc/RpcMiddleware.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
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
|
|
4
|
+
import { type Effect, type Schema, type Schema as S, type Scope, type Stream } from "effect"
|
|
5
5
|
import { type NonEmptyReadonlyArray } from "effect/Array"
|
|
6
6
|
import { type Rpc, RpcMiddleware } from "effect/unstable/rpc"
|
|
7
7
|
import { type TypeId } from "effect/unstable/rpc/RpcMiddleware"
|
|
8
|
+
import type * as Context from "../Context.js"
|
|
8
9
|
import { type GetEffectContext, type RpcContextMap } from "./RpcContextMap.js"
|
|
9
10
|
|
|
10
11
|
export type RpcMiddlewareV4<Provides, E, Requires> = RpcMiddleware.RpcMiddleware<Provides, E, Requires>
|
|
@@ -102,8 +103,8 @@ export declare namespace TagClass {
|
|
|
102
103
|
requires?: any
|
|
103
104
|
provides?: any
|
|
104
105
|
}
|
|
105
|
-
> extends
|
|
106
|
-
new(_: never):
|
|
106
|
+
> extends Context.Service<Self, Service> {
|
|
107
|
+
new(_: never): Context.ServiceClass.Shape<Name, Service>
|
|
107
108
|
readonly [TypeId]: TypeId
|
|
108
109
|
readonly optional: Optional<Options>
|
|
109
110
|
readonly error: FailureSchema<Options>
|
|
@@ -226,8 +227,8 @@ export type ExtractProvides<R extends Rpc.Any, Tag extends string> = R extends
|
|
|
226
227
|
Rpc.Rpc<Tag, infer _Payload, infer _Success, infer _Error, infer _Middleware, infer _Requires> ? _Middleware extends {
|
|
227
228
|
readonly provides: infer _P
|
|
228
229
|
} ? [_P] extends [never] ? never
|
|
229
|
-
: _P extends
|
|
230
|
-
: never
|
|
230
|
+
: _P /*_P extends Context.Service<infer _I, infer _S> ? _I
|
|
231
|
+
: never */
|
|
231
232
|
: never
|
|
232
233
|
: never
|
|
233
234
|
|
package/src/rpc.ts
CHANGED
package/src/transform.ts
CHANGED
|
@@ -51,8 +51,8 @@ const encodeOptsAsNullable_ = (value: any, cacheMap: Map<any, any>): any => {
|
|
|
51
51
|
|
|
52
52
|
if (
|
|
53
53
|
value instanceof Date
|
|
54
|
-
|| value
|
|
55
|
-
|| value
|
|
54
|
+
|| typeof value === "function"
|
|
55
|
+
|| (typeof value === "object" && value !== null && "then" in value && typeof value.then === "function")
|
|
56
56
|
) {
|
|
57
57
|
return value
|
|
58
58
|
}
|
package/src/utils/gen.ts
CHANGED
|
@@ -15,7 +15,7 @@ export namespace EffectGenUtils {
|
|
|
15
15
|
: EG extends (..._: infer _3) => Generator<Yieldable<any, infer _, infer E, infer _R>, infer _A, infer _2> ? E
|
|
16
16
|
: never
|
|
17
17
|
|
|
18
|
-
export type
|
|
18
|
+
export type Context<EG> = EG extends Effect<infer _A, infer _E, infer R> ? R
|
|
19
19
|
// there could be a case where the generator function does not yield anything, so we need to handle that
|
|
20
20
|
: EG extends (..._: infer _3) => Generator<never, infer _A, infer _2> ? never
|
|
21
21
|
// v4: generators can yield Yieldable (Effect, Service, etc.), all have asEffect()
|
package/src/utils/logger.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
3
|
|
|
4
4
|
import { Effect, type LogLevel } from "effect"
|
|
5
|
-
import * as
|
|
5
|
+
import * as Context from "../Context.js"
|
|
6
6
|
|
|
7
7
|
type Levels = "info" | "debug" | "warn" | "error"
|
|
8
8
|
|
|
9
|
-
export class LogLevels extends
|
|
9
|
+
export class LogLevels extends Context.Reference("LogLevels", {
|
|
10
10
|
defaultValue: () => new Map<string, Levels>()
|
|
11
11
|
}) {}
|
|
12
12
|
|