effect-app 4.0.0-beta.18 → 4.0.0-beta.180
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 +760 -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 +6 -5
- 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/Operations.d.ts +369 -47
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Operations.js +10 -10
- 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 +69 -20
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +190 -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 +7 -2
- 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 +7 -4
- package/dist/Schema/ext.d.ts +118 -45
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +129 -53
- package/dist/Schema/moreStrings.d.ts +111 -11
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +14 -15
- 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 +6 -3
- 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 +102 -56
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +128 -64
- 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 +17 -31
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +81 -26
- package/dist/client/clientFor.d.ts +63 -10
- 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 +360 -30
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +63 -23
- 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 +3 -3
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +3 -2
- package/dist/index.d.ts +5 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -8
- 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 +490 -0
- package/dist/rpc/Invalidation.d.ts.map +1 -0
- package/dist/rpc/Invalidation.js +129 -0
- package/dist/rpc/MiddlewareMaker.d.ts +5 -4
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +26 -27
- 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 +5 -4
- 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 +30 -10
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +10 -4
- 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/eslint.config.mjs +2 -2
- package/package.json +47 -19
- 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} +52 -59
- package/src/Effect.ts +12 -14
- package/src/Layer.ts +5 -4
- package/src/Operations.ts +14 -14
- package/src/Pure.ts +17 -18
- package/src/Schema/Class.ts +279 -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 +9 -1
- package/src/Schema/email.ts +7 -2
- package/src/Schema/ext.ts +210 -80
- package/src/Schema/moreStrings.ts +22 -20
- package/src/Schema/numbers.ts +14 -16
- package/src/Schema/phoneNumber.ts +5 -1
- package/src/Schema/strings.ts +4 -8
- package/src/Schema.ts +265 -105
- package/src/client/InvalidationKeys.ts +50 -0
- package/src/client/apiClientFactory.ts +203 -119
- package/src/client/clientFor.ts +112 -23
- package/src/client/errors.ts +52 -26
- package/src/client/makeClient.ts +339 -63
- package/src/client.ts +1 -0
- package/src/http/Request.ts +7 -4
- package/src/ids.ts +2 -1
- package/src/index.ts +5 -10
- package/src/middleware.ts +12 -10
- package/src/rpc/Invalidation.ts +174 -0
- package/src/rpc/MiddlewareMaker.ts +36 -47
- package/src/rpc/README.md +2 -2
- package/src/rpc/RpcContextMap.ts +6 -5
- package/src/rpc/RpcMiddleware.ts +5 -4
- 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 +47 -11
- 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/rpc.test.ts +38 -6
- package/test/schema.test.ts +591 -17
- 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/ServiceMap.d.ts +0 -44
- package/dist/ServiceMap.d.ts.map +0 -1
- package/dist/ServiceMap.js +0 -91
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import * as Config from "effect/Config"
|
|
3
2
|
import { flow } from "effect/Function"
|
|
4
3
|
import * as Layer from "effect/Layer"
|
|
5
4
|
import * as ManagedRuntime from "effect/ManagedRuntime"
|
|
5
|
+
import * as Option from "effect/Option"
|
|
6
6
|
import * as Predicate from "effect/Predicate"
|
|
7
7
|
import * as Schema from "effect/Schema"
|
|
8
|
+
import * as Stream from "effect/Stream"
|
|
8
9
|
import * as Struct from "effect/Struct"
|
|
9
10
|
import { Rpc, RpcClient, RpcGroup, RpcSerialization } from "effect/unstable/rpc"
|
|
11
|
+
import * as Config from "../Config.js"
|
|
12
|
+
import * as Context from "../Context.js"
|
|
10
13
|
import * as Effect from "../Effect.js"
|
|
11
14
|
import { HttpClient, HttpClientRequest } from "../http.js"
|
|
12
|
-
import
|
|
15
|
+
import { Invalidation } from "../rpc.js"
|
|
13
16
|
import type * as S from "../Schema.js"
|
|
14
|
-
import * as ServiceMap from "../ServiceMap.js"
|
|
15
17
|
import { typedKeysOf, typedValuesOf } from "../utils.js"
|
|
16
|
-
import type { Client, ClientForOptions,
|
|
18
|
+
import type { Client, ClientForOptions, ExtractModuleName, RequestsAny } from "./clientFor.js"
|
|
19
|
+
import { InvalidationKeysFromServer } from "./InvalidationKeys.js"
|
|
17
20
|
|
|
18
21
|
export interface ApiConfig {
|
|
19
22
|
url: string
|
|
@@ -31,16 +34,21 @@ export const DefaultApiConfig = Config.all({
|
|
|
31
34
|
})
|
|
32
35
|
|
|
33
36
|
export type Req = S.Top & {
|
|
34
|
-
|
|
37
|
+
readonly make: (...args: any[]) => any
|
|
35
38
|
_tag: string
|
|
36
39
|
fields: S.Struct.Fields
|
|
37
40
|
success: S.Top
|
|
38
41
|
error: S.Top
|
|
42
|
+
/** Optional final-value schema for stream requests. When set, the execute effect resolves with the last stream value decoded to this type. */
|
|
43
|
+
final?: S.Top
|
|
39
44
|
config?: Record<string, any>
|
|
45
|
+
readonly id: string
|
|
46
|
+
readonly moduleName: string
|
|
47
|
+
readonly type: "command" | "query" | "stream"
|
|
40
48
|
readonly "~decodingServices"?: unknown
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
class RequestName extends
|
|
51
|
+
class RequestName extends Context.Reference("RequestName", {
|
|
44
52
|
defaultValue: () => ({ requestName: "Unspecified", moduleName: "Error" })
|
|
45
53
|
}) {}
|
|
46
54
|
|
|
@@ -84,7 +92,7 @@ type RpcHandlers<M extends RequestsAny> = {
|
|
|
84
92
|
[K in keyof M]: Rpc.Rpc<M[K]["_tag"], M[K], M[K]["success"], M[K]["error"]>
|
|
85
93
|
}
|
|
86
94
|
|
|
87
|
-
const getFiltered = <M extends
|
|
95
|
+
const getFiltered = <M extends RequestsAny>(resource: M) => {
|
|
88
96
|
type Filtered = {
|
|
89
97
|
[K in keyof M as M[K] extends Req ? K : never]: M[K] extends Req ? M[K] : never
|
|
90
98
|
}
|
|
@@ -102,13 +110,13 @@ const getFiltered = <M extends Requests>(resource: M) => {
|
|
|
102
110
|
return filtered as unknown as Filtered
|
|
103
111
|
}
|
|
104
112
|
|
|
105
|
-
export const getMeta = <M extends
|
|
106
|
-
const
|
|
107
|
-
if (
|
|
108
|
-
|
|
113
|
+
export const getMeta = <M extends RequestsAny>(resource: M): { moduleName: ExtractModuleName<M> } => {
|
|
114
|
+
const first = typedValuesOf(getFiltered(resource))[0]
|
|
115
|
+
if (first && "moduleName" in first) return { moduleName: first.moduleName }
|
|
116
|
+
throw new Error("No moduleName on requests!")
|
|
109
117
|
}
|
|
110
118
|
|
|
111
|
-
export const makeRpcGroupFromRequestsAndModuleName = <M extends
|
|
119
|
+
export const makeRpcGroupFromRequestsAndModuleName = <M extends RequestsAny, const ModuleName extends string>(
|
|
112
120
|
resource: M,
|
|
113
121
|
moduleName: ModuleName
|
|
114
122
|
) => {
|
|
@@ -117,7 +125,22 @@ export const makeRpcGroupFromRequestsAndModuleName = <M extends Requests, const
|
|
|
117
125
|
const rpcs = RpcGroup
|
|
118
126
|
.make(
|
|
119
127
|
...typedValuesOf(filtered).map((_) => {
|
|
120
|
-
|
|
128
|
+
const r = _ as any
|
|
129
|
+
const isStream = r.type === "stream"
|
|
130
|
+
return Rpc.make(r._tag, {
|
|
131
|
+
payload: r,
|
|
132
|
+
success: r.type === "command"
|
|
133
|
+
? Invalidation.CommandResponseWithMetaData(r.success)
|
|
134
|
+
: isStream
|
|
135
|
+
? Invalidation.StreamResponseChunk(r.success)
|
|
136
|
+
: r.success,
|
|
137
|
+
error: r.type === "command"
|
|
138
|
+
? Invalidation.CommandFailureWithMetaData(r.error)
|
|
139
|
+
: isStream
|
|
140
|
+
? Invalidation.StreamFailureChunk(r.error)
|
|
141
|
+
: r.error,
|
|
142
|
+
...isStream ? { stream: true as const } : {}
|
|
143
|
+
})
|
|
121
144
|
})
|
|
122
145
|
)
|
|
123
146
|
.prefix(`${moduleName}.`) as unknown as RpcGroup.RpcGroup<
|
|
@@ -126,20 +149,13 @@ export const makeRpcGroupFromRequestsAndModuleName = <M extends Requests, const
|
|
|
126
149
|
return rpcs
|
|
127
150
|
}
|
|
128
151
|
|
|
129
|
-
|
|
130
|
-
M extends Requests,
|
|
131
|
-
const ModuleName extends string
|
|
132
|
-
>(
|
|
133
|
-
resource: M & { meta: { moduleName: ModuleName } }
|
|
134
|
-
) => makeRpcGroupFromRequestsAndModuleName(resource, resource.meta.moduleName)
|
|
135
|
-
|
|
136
|
-
const makeRpcTag = <M extends Requests>(resource: M) => {
|
|
152
|
+
const makeRpcTag = <M extends RequestsAny>(resource: M) => {
|
|
137
153
|
const meta = getMeta(resource)
|
|
138
154
|
const rpcs = makeRpcGroupFromRequestsAndModuleName(resource, meta.moduleName)
|
|
139
155
|
|
|
140
156
|
// Use Object.assign instead of class extension to avoid TS2509 with complex generic return types.
|
|
141
157
|
// The first type arg is `any` because this is a dynamically created tag — its identity is the string key.
|
|
142
|
-
const TheClient =
|
|
158
|
+
const TheClient = Context.Opaque<
|
|
143
159
|
any,
|
|
144
160
|
RpcClient.RpcClient<RpcGroup.Rpcs<typeof rpcs>>
|
|
145
161
|
>()(`RpcClient.${meta.moduleName}`)
|
|
@@ -153,99 +169,170 @@ const makeRpcTag = <M extends Requests>(resource: M) => {
|
|
|
153
169
|
|
|
154
170
|
const makeApiClientFactory = Effect
|
|
155
171
|
.gen(function*() {
|
|
156
|
-
const ctx = yield* Effect.
|
|
157
|
-
const makeClientFor =
|
|
172
|
+
const ctx = yield* Effect.context<RpcSerialization.RpcSerialization | HttpClient.HttpClient>()
|
|
173
|
+
const makeClientFor = Effect.fnUntraced(function*<M extends RequestsAny>(
|
|
158
174
|
resource: M,
|
|
159
175
|
requestLevelLayers = Layer.empty,
|
|
160
176
|
options?: ClientForOptions
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
) {
|
|
178
|
+
const TheClient = makeRpcTag(resource)
|
|
179
|
+
|
|
180
|
+
const meta = getMeta(resource)
|
|
181
|
+
|
|
182
|
+
// TODO: somehow we need a protocol per REQUEST kind of it seems ...
|
|
183
|
+
// otherwise it locks up on the client, navigation remains empty...
|
|
184
|
+
const clientLayer = TheClient.layer.pipe(
|
|
185
|
+
// add ApiClientFactory for nested schemas
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
187
|
+
Layer.provide(Layer.succeed(ApiClientFactory, makeClientForCached as any)),
|
|
188
|
+
Layer.provide(
|
|
189
|
+
RpcClient
|
|
190
|
+
.layerProtocolHttp({ url: "" }) // why not here set meta.moduleName as root?
|
|
191
|
+
.pipe(
|
|
192
|
+
Layer.provideMerge(Layer.succeedContext(ctx))
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
const mr = ManagedRuntime.make(clientLayer)
|
|
197
|
+
|
|
198
|
+
const filtered = getFiltered(resource)
|
|
199
|
+
|
|
200
|
+
const unwrapCommand = (eff: Effect.Effect<any, any, any>): Effect.Effect<any, any, any> =>
|
|
201
|
+
eff.pipe(
|
|
202
|
+
Effect.flatMap((result: any) =>
|
|
203
|
+
Effect.gen(function*() {
|
|
204
|
+
const keys: ReadonlyArray<Invalidation.InvalidationKey> = result?.metadata?.invalidateQueries ?? []
|
|
205
|
+
const invalidationKeys = yield* InvalidationKeysFromServer
|
|
206
|
+
yield* Effect.forEach(keys, (key) => invalidationKeys.add(key), { discard: true })
|
|
207
|
+
return result.payload
|
|
208
|
+
})
|
|
209
|
+
),
|
|
210
|
+
// V2: unwrap CommandFailureWithMetaData failures — forward keys, re-fail with the
|
|
211
|
+
// original error so callers see the unmodified error type.
|
|
212
|
+
Effect.catch((result: any) =>
|
|
213
|
+
result?._tag === "CommandFailureWithMetaData"
|
|
214
|
+
? Effect.gen(function*() {
|
|
215
|
+
const keys: ReadonlyArray<Invalidation.InvalidationKey> = result.metadata?.invalidateQueries ?? []
|
|
216
|
+
const invalidationKeys = yield* InvalidationKeysFromServer
|
|
217
|
+
yield* Effect.forEach(keys, (key) => invalidationKeys.add(key), { discard: true })
|
|
218
|
+
return yield* Effect.fail(result.error)
|
|
177
219
|
})
|
|
178
|
-
.
|
|
179
|
-
Layer.provideMerge(Layer.succeedServices(ctx))
|
|
180
|
-
)
|
|
220
|
+
: Effect.fail(result)
|
|
181
221
|
)
|
|
182
222
|
)
|
|
183
|
-
const mr = ManagedRuntime.make(clientLayer)
|
|
184
223
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
224
|
+
return {
|
|
225
|
+
mr,
|
|
226
|
+
client: typedKeysOf(filtered)
|
|
227
|
+
.reduce((prev, cur) => {
|
|
228
|
+
const h = filtered[cur]!
|
|
229
|
+
|
|
230
|
+
const Request = h
|
|
231
|
+
|
|
232
|
+
const id = `${meta.moduleName}.${cur as string}`
|
|
233
|
+
.replaceAll(".js", "")
|
|
234
|
+
|
|
235
|
+
const requestMeta = {
|
|
236
|
+
Request,
|
|
237
|
+
id,
|
|
238
|
+
options
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const requestNameLayer = Layer.succeed(RequestName, {
|
|
242
|
+
requestName: cur as string,
|
|
243
|
+
moduleName: meta.moduleName
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
const layers = requestLevelLayers.pipe(Layer.provideMerge(requestNameLayer))
|
|
247
|
+
|
|
248
|
+
const fields = Struct.omit(Request.fields, ["_tag"] as const)
|
|
249
|
+
const requestAttr = `${meta.moduleName}.${h._tag}`
|
|
250
|
+
const isCommand = h.type === "command"
|
|
251
|
+
const isStream = h.type === "stream"
|
|
252
|
+
|
|
253
|
+
const buildEffect = (input: any) =>
|
|
254
|
+
mr.contextEffect.pipe(
|
|
255
|
+
Effect.flatMap((svcs) => {
|
|
256
|
+
const rpcEffect = TheClient
|
|
257
|
+
.use((client) =>
|
|
258
|
+
(client as any)[requestAttr]!(Request.make(input)) as Effect.Effect<any, any, never>
|
|
259
|
+
)
|
|
260
|
+
.pipe(
|
|
261
|
+
Effect.provide(layers),
|
|
262
|
+
Effect.provide(svcs)
|
|
263
|
+
)
|
|
264
|
+
return isCommand ? unwrapCommand(rpcEffect) : rpcEffect
|
|
265
|
+
})
|
|
266
|
+
)
|
|
193
267
|
|
|
194
|
-
|
|
195
|
-
|
|
268
|
+
const buildStream = (input: any) =>
|
|
269
|
+
Stream.unwrap(
|
|
270
|
+
mr.contextEffect.pipe(
|
|
271
|
+
Effect.flatMap((svcs) =>
|
|
272
|
+
TheClient
|
|
273
|
+
.useSync((client) => {
|
|
274
|
+
const rpcStream = (client as any)[requestAttr]!(
|
|
275
|
+
Request.make(input)
|
|
276
|
+
) as Stream.Stream<any, any, any>
|
|
277
|
+
return rpcStream.pipe(
|
|
278
|
+
// Collect server invalidation keys from the "done" chunk, then discard it.
|
|
279
|
+
Stream.tap((item: any) =>
|
|
280
|
+
item._tag === "done" || item._tag === "metadata"
|
|
281
|
+
? InvalidationKeysFromServer.use((svc) =>
|
|
282
|
+
Effect.forEach(
|
|
283
|
+
(item.metadata as Invalidation.CommandMetaData).invalidateQueries,
|
|
284
|
+
svc.add,
|
|
285
|
+
{ discard: true }
|
|
286
|
+
)
|
|
287
|
+
)
|
|
288
|
+
: Effect.void
|
|
289
|
+
),
|
|
290
|
+
Stream.filter((item: any) => item._tag === "value"),
|
|
291
|
+
Stream.map((item: any) => item.value),
|
|
292
|
+
// V2: unwrap StreamFailureChunk — forward keys from failures too,
|
|
293
|
+
// then re-fail with the original error so callers see the unmodified
|
|
294
|
+
// error type.
|
|
295
|
+
Stream.catch((err: any) =>
|
|
296
|
+
err?._tag === "error" && err?.metadata
|
|
297
|
+
? Stream.fromEffect(
|
|
298
|
+
InvalidationKeysFromServer.use((svc) =>
|
|
299
|
+
Effect
|
|
300
|
+
.forEach(
|
|
301
|
+
(err.metadata as Invalidation.CommandMetaData).invalidateQueries,
|
|
302
|
+
svc.add,
|
|
303
|
+
{ discard: true }
|
|
304
|
+
)
|
|
305
|
+
.pipe(Effect.flatMap(() => Effect.fail(err.error)))
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
: Stream.fail(err)
|
|
309
|
+
),
|
|
310
|
+
Stream.provide(layers),
|
|
311
|
+
Stream.provide(svcs)
|
|
312
|
+
)
|
|
313
|
+
})
|
|
314
|
+
.pipe(Effect.provide(svcs))
|
|
315
|
+
)
|
|
316
|
+
)
|
|
317
|
+
)
|
|
196
318
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
319
|
+
// @ts-expect-error doc
|
|
320
|
+
prev[cur] = Object.keys(fields).length === 0
|
|
321
|
+
? {
|
|
322
|
+
handler: isStream ? buildStream({}) : buildEffect({}),
|
|
323
|
+
...requestMeta
|
|
324
|
+
}
|
|
325
|
+
: {
|
|
326
|
+
handler: isStream
|
|
327
|
+
? (req: any) => buildStream(req)
|
|
328
|
+
: (req: any) => buildEffect(req),
|
|
329
|
+
...requestMeta
|
|
201
330
|
}
|
|
202
331
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const layers = requestLevelLayers.pipe(Layer.provideMerge(requestNameLayer))
|
|
209
|
-
|
|
210
|
-
const fields = Struct.omit(Request.fields, ["_tag"] as const)
|
|
211
|
-
const requestAttr = `${meta.moduleName}.${h._tag}`
|
|
212
|
-
// @ts-expect-error doc
|
|
213
|
-
prev[cur] = Object.keys(fields).length === 0
|
|
214
|
-
? {
|
|
215
|
-
handler: mr.servicesEffect.pipe(
|
|
216
|
-
Effect.flatMap((svcs) =>
|
|
217
|
-
TheClient
|
|
218
|
-
.use((client) => (client as any)[requestAttr]!(new Request()) as Effect.Effect<any, any, never>)
|
|
219
|
-
.pipe(
|
|
220
|
-
Effect.provide(layers),
|
|
221
|
-
Effect.provide(svcs)
|
|
222
|
-
)
|
|
223
|
-
)
|
|
224
|
-
),
|
|
225
|
-
...requestMeta
|
|
226
|
-
}
|
|
227
|
-
: {
|
|
228
|
-
handler: (req: any) =>
|
|
229
|
-
mr.servicesEffect.pipe(
|
|
230
|
-
Effect.flatMap((svcs) =>
|
|
231
|
-
TheClient
|
|
232
|
-
.use((client) =>
|
|
233
|
-
(client as any)[requestAttr]!(new Request(req)) as Effect.Effect<any, any, never>
|
|
234
|
-
)
|
|
235
|
-
.pipe(
|
|
236
|
-
Effect.provide(layers),
|
|
237
|
-
Effect.provide(svcs)
|
|
238
|
-
)
|
|
239
|
-
)
|
|
240
|
-
),
|
|
241
|
-
|
|
242
|
-
...requestMeta
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return prev
|
|
246
|
-
}, {} as Client<M, M["meta"]["moduleName"]>)
|
|
247
|
-
}
|
|
248
|
-
})
|
|
332
|
+
return prev
|
|
333
|
+
}, {} as Client<M, ExtractModuleName<M>>)
|
|
334
|
+
}
|
|
335
|
+
})
|
|
249
336
|
|
|
250
337
|
const register: ManagedRuntime.ManagedRuntime<any, any>[] = []
|
|
251
338
|
yield* Effect.addFinalizer(() => Effect.forEach(register, (mr) => mr.disposeEffect))
|
|
@@ -259,19 +346,16 @@ const makeApiClientFactory = Effect
|
|
|
259
346
|
cacheL.set(requestLevelLayers, cache)
|
|
260
347
|
}
|
|
261
348
|
|
|
262
|
-
return
|
|
263
|
-
models
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
register.push(m.mr)
|
|
273
|
-
return m.client
|
|
274
|
-
})
|
|
349
|
+
return Effect.fnUntraced(function*<M extends RequestsAny>(models: M) {
|
|
350
|
+
const found = cache.get(models) as Client<M, ExtractModuleName<M>> | undefined
|
|
351
|
+
if (found) {
|
|
352
|
+
return found
|
|
353
|
+
}
|
|
354
|
+
const m = yield* makeClientFor(models, requestLevelLayers, options)
|
|
355
|
+
cache.set(models, m.client)
|
|
356
|
+
register.push(m.mr)
|
|
357
|
+
return m.client
|
|
358
|
+
})
|
|
275
359
|
}
|
|
276
360
|
|
|
277
361
|
return makeClientForCached
|
|
@@ -281,7 +365,7 @@ const makeApiClientFactory = Effect
|
|
|
281
365
|
* Used to create clients for resource modules.
|
|
282
366
|
*/
|
|
283
367
|
export class ApiClientFactory
|
|
284
|
-
extends
|
|
368
|
+
extends Context.Opaque<ApiClientFactory, Effect.Success<typeof makeApiClientFactory>>()("ApiClientFactory")
|
|
285
369
|
{
|
|
286
370
|
static readonly layer = (config: ApiConfig) =>
|
|
287
371
|
ApiClientFactory.toLayer(makeApiClientFactory).pipe(Layer.provide(RpcSerializationLayer(config)))
|
|
@@ -294,7 +378,7 @@ export class ApiClientFactory
|
|
|
294
378
|
|
|
295
379
|
static readonly makeFor =
|
|
296
380
|
(requestLevelLayers: Layer.Layer<never, never, never>, options?: ClientForOptions) =>
|
|
297
|
-
<M extends
|
|
381
|
+
<M extends RequestsAny>(
|
|
298
382
|
resource: M
|
|
299
383
|
) =>
|
|
300
384
|
ApiClientFactory.use((apiClientFactory) => {
|
package/src/client/clientFor.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
3
|
|
|
4
4
|
import * as Record from "effect/Record"
|
|
5
|
-
import type * as
|
|
5
|
+
import type * as Stream from "effect/Stream"
|
|
6
6
|
import type { Path } from "path-parser"
|
|
7
7
|
import qs from "query-string"
|
|
8
8
|
import type * as Effect from "../Effect.js"
|
|
@@ -48,9 +48,14 @@ export function makePathWithBody(
|
|
|
48
48
|
return path.build(pars, { ignoreSearch: true, ignoreConstraints: true })
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
export type Requests
|
|
51
|
+
export type Requests = RequestsAny
|
|
52
52
|
export type RequestsAny = Record<string, any>
|
|
53
53
|
|
|
54
|
+
export type ExtractModuleName<M extends RequestsAny> =
|
|
55
|
+
{ [K in keyof M]: M[K] extends { moduleName: infer N extends string } ? N : never }[keyof M] extends
|
|
56
|
+
infer R extends string ? R
|
|
57
|
+
: string
|
|
58
|
+
|
|
54
59
|
export type Client<M extends RequestsAny, ModuleName extends string> = RequestHandlers<
|
|
55
60
|
never,
|
|
56
61
|
never,
|
|
@@ -66,16 +71,20 @@ export type ExtractEResponse<T> = T extends S.Codec<any> ? S.Codec.Encoded<T>
|
|
|
66
71
|
: T extends unknown ? void
|
|
67
72
|
: never
|
|
68
73
|
|
|
69
|
-
type IsEmpty<T> = keyof T extends never ? true
|
|
70
|
-
: false
|
|
71
|
-
|
|
72
|
-
// v4: Request.RequestTypeId, S.symbolSerializable, S.symbolWithResult removed — use keyof Request to filter internal props
|
|
73
|
-
type Cruft = "_tag" | keyof Request.Request<any, any, any>
|
|
74
|
-
|
|
75
74
|
export interface ClientForOptions {
|
|
76
75
|
readonly skipQueryKey?: readonly string[]
|
|
77
76
|
}
|
|
78
77
|
|
|
78
|
+
// $Project/$Configuration.Index
|
|
79
|
+
// -> "$Project", "$Configuration", "Index"
|
|
80
|
+
export const makeQueryKey = ({ id, options }: { id: string; options?: ClientForOptions }) =>
|
|
81
|
+
id
|
|
82
|
+
.split("/")
|
|
83
|
+
.filter((segment: string) => !options || !options.skipQueryKey?.includes(segment))
|
|
84
|
+
.map((segment: string) => "$" + segment)
|
|
85
|
+
.join(".")
|
|
86
|
+
.split(".")
|
|
87
|
+
|
|
79
88
|
export interface RequestHandler<A, E, R, Request extends Req, Id extends string> {
|
|
80
89
|
handler: Effect.Effect<A, E, R>
|
|
81
90
|
id: Id
|
|
@@ -90,24 +99,104 @@ export interface RequestHandlerWithInput<I, A, E, R, Request extends Req, Id ext
|
|
|
90
99
|
Request: Request
|
|
91
100
|
}
|
|
92
101
|
|
|
102
|
+
export interface RequestStreamHandler<A, E, R, Request extends Req, Id extends string, Final = void> {
|
|
103
|
+
handler: Stream.Stream<A, E, R>
|
|
104
|
+
id: Id
|
|
105
|
+
options?: ClientForOptions
|
|
106
|
+
Request: Request
|
|
107
|
+
/**
|
|
108
|
+
* Phantom type property (never set at runtime) that carries the `Final` type to
|
|
109
|
+
* `StreamMutationWithExtensions`. The tilde prefix follows the Effect convention for
|
|
110
|
+
* phantom/virtual properties and prevents accidental runtime access.
|
|
111
|
+
* When the stream fails, the execute effect still resolves (with `undefined`);
|
|
112
|
+
* check the reactive `AsyncResult` ref to distinguish success from failure.
|
|
113
|
+
*/
|
|
114
|
+
readonly "~final"?: Final
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface RequestStreamHandlerWithInput<I, A, E, R, Request extends Req, Id extends string, Final = void> {
|
|
118
|
+
handler: (i: I) => Stream.Stream<A, E, R>
|
|
119
|
+
id: Id
|
|
120
|
+
options?: ClientForOptions
|
|
121
|
+
Request: Request
|
|
122
|
+
/**
|
|
123
|
+
* Phantom type property (never set at runtime) that carries the `Final` type to
|
|
124
|
+
* `StreamMutationWithExtensions`. The tilde prefix follows the Effect convention for
|
|
125
|
+
* phantom/virtual properties and prevents accidental runtime access.
|
|
126
|
+
* When the stream fails, the execute effect still resolves (with `undefined`);
|
|
127
|
+
* check the reactive `AsyncResult` ref to distinguish success from failure.
|
|
128
|
+
*/
|
|
129
|
+
readonly "~final"?: Final
|
|
130
|
+
}
|
|
131
|
+
|
|
93
132
|
// make sure this is exported or d.ts of apiClientFactory breaks?!
|
|
94
133
|
type ReqDecodingServices<M> = M extends { readonly "~decodingServices": infer DS } ? DS : never
|
|
95
134
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
135
|
+
type RequestFields<I> = I extends { readonly fields: infer F extends S.Struct.Fields } ? F : never
|
|
136
|
+
|
|
137
|
+
type RequestInputFromFields<I> = [RequestFields<I>] extends [never] ? never
|
|
138
|
+
: keyof RequestFields<I> extends never ? void
|
|
139
|
+
: S.Schema.Type<S.Struct<RequestFields<I>>>
|
|
140
|
+
|
|
141
|
+
type RequestInputFromOverloadedMake<I extends { readonly make: (...args: any[]) => any }> =
|
|
142
|
+
Parameters<I["make"]> extends [] ? void
|
|
143
|
+
: Parameters<I["make"]>[0]
|
|
144
|
+
|
|
145
|
+
export type RequestInputFromMake<I extends { readonly make: (...args: any[]) => any }> =
|
|
146
|
+
[RequestInputFromFields<I>] extends [never] ? RequestInputFromOverloadedMake<I>
|
|
147
|
+
: RequestInputFromFields<I>
|
|
148
|
+
|
|
149
|
+
type NormalizedRequestInput<T> = Omit<Exclude<T, undefined>, "_tag">
|
|
150
|
+
|
|
151
|
+
// If make's first param has only an optional _tag property, treat as no-input handler.
|
|
152
|
+
type IsTagOnly<T> = [Exclude<T, undefined>] extends [never] ? true
|
|
153
|
+
: [keyof NormalizedRequestInput<T>] extends [never] ? true
|
|
154
|
+
: false
|
|
155
|
+
|
|
156
|
+
type RequestInput<I extends { readonly make: (...args: any[]) => any }> = NormalizedRequestInput<
|
|
157
|
+
RequestInputFromMake<I>
|
|
158
|
+
>
|
|
159
|
+
|
|
160
|
+
/** Extracts the final-value type from a stream request. Defaults to `void` when no `final` schema is set. */
|
|
161
|
+
type FinalTypeOf<T extends Req> = T extends { readonly final: infer F extends S.Top } ? S.Schema.Type<F>
|
|
162
|
+
: void
|
|
163
|
+
|
|
164
|
+
type RequestHandlerFor<R, E, T extends Req, Id extends string> = T["type"] extends "stream"
|
|
165
|
+
? IsTagOnly<RequestInputFromMake<T>> extends true ? RequestStreamHandler<
|
|
166
|
+
S.Schema.Type<T["success"]>,
|
|
167
|
+
S.Schema.Type<T["error"]> | E,
|
|
168
|
+
R | ReqDecodingServices<T>,
|
|
169
|
+
T,
|
|
170
|
+
Id,
|
|
171
|
+
FinalTypeOf<T>
|
|
104
172
|
>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
173
|
+
: RequestStreamHandlerWithInput<
|
|
174
|
+
RequestInput<T>,
|
|
175
|
+
S.Schema.Type<T["success"]>,
|
|
176
|
+
S.Schema.Type<T["error"]> | E,
|
|
177
|
+
R | ReqDecodingServices<T>,
|
|
178
|
+
T,
|
|
179
|
+
Id,
|
|
180
|
+
FinalTypeOf<T>
|
|
181
|
+
>
|
|
182
|
+
: IsTagOnly<RequestInputFromMake<T>> extends true ? RequestHandler<
|
|
183
|
+
S.Schema.Type<T["success"]>,
|
|
184
|
+
S.Schema.Type<T["error"]> | E,
|
|
185
|
+
R | ReqDecodingServices<T>,
|
|
186
|
+
T,
|
|
187
|
+
Id
|
|
112
188
|
>
|
|
189
|
+
: RequestHandlerWithInput<
|
|
190
|
+
RequestInput<T>,
|
|
191
|
+
S.Schema.Type<T["success"]>,
|
|
192
|
+
S.Schema.Type<T["error"]> | E,
|
|
193
|
+
R | ReqDecodingServices<T>,
|
|
194
|
+
T,
|
|
195
|
+
Id
|
|
196
|
+
>
|
|
197
|
+
|
|
198
|
+
export type RequestHandlers<R, E, M extends RequestsAny, ModuleName extends string> = {
|
|
199
|
+
[K in keyof M as M[K] extends Req ? K : never]: Extract<M[K], Req> extends infer T extends Req
|
|
200
|
+
? RequestHandlerFor<R, E, T, `${ModuleName}.${K & string}`>
|
|
201
|
+
: never
|
|
113
202
|
}
|