effect-app 3.16.0 → 4.0.0-beta.1
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 +12 -0
- package/dist/Array.js +1 -1
- package/dist/Chunk.d.ts +2 -4
- package/dist/Chunk.d.ts.map +1 -1
- package/dist/Chunk.js +2 -2
- package/dist/Config/SecretURL.d.ts +2 -12
- package/dist/Config/SecretURL.d.ts.map +1 -1
- package/dist/Config/SecretURL.js +2 -4
- package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
- package/dist/Config/internal/configSecretURL.js +3 -4
- package/dist/Effect.d.ts +12 -10
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +6 -15
- package/dist/Layer.d.ts +15 -9
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +2 -2
- package/dist/Operations.d.ts +37 -47
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Option.js +3 -3
- package/dist/Pure.d.ts +17 -6
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +35 -17
- package/dist/Schema/Class.d.ts +13 -16
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +5 -27
- package/dist/Schema/brand.d.ts +7 -10
- package/dist/Schema/brand.d.ts.map +1 -1
- package/dist/Schema/brand.js +3 -2
- package/dist/Schema/email.d.ts +1 -1
- package/dist/Schema/email.d.ts.map +1 -1
- package/dist/Schema/email.js +2 -2
- package/dist/Schema/ext.d.ts +42 -45
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +49 -63
- package/dist/Schema/moreStrings.d.ts +17 -17
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +10 -10
- package/dist/Schema/numbers.d.ts +14 -14
- package/dist/Schema/numbers.js +5 -5
- package/dist/Schema/phoneNumber.d.ts +1 -1
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +2 -2
- package/dist/Schema/schema.d.ts +2 -3
- package/dist/Schema/schema.d.ts.map +1 -1
- package/dist/Schema/schema.js +3 -4
- package/dist/Schema/strings.d.ts +4 -4
- package/dist/Schema/strings.d.ts.map +1 -1
- package/dist/Schema/strings.js +4 -4
- package/dist/Schema.d.ts +27 -25
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +22 -21
- package/dist/ServiceMap.d.ts +44 -0
- package/dist/ServiceMap.d.ts.map +1 -0
- package/dist/ServiceMap.js +91 -0
- package/dist/Set.d.ts +4 -4
- package/dist/Set.d.ts.map +1 -1
- package/dist/Set.js +14 -14
- package/dist/Struct.d.ts +4 -4
- package/dist/Struct.d.ts.map +1 -1
- package/dist/_ext/Array.d.ts.map +1 -1
- package/dist/_ext/Array.js +4 -4
- package/dist/_ext/misc.d.ts +2 -2
- package/dist/_ext/misc.js +4 -4
- package/dist/_ext/ord.ext.js +2 -2
- package/dist/builtin.d.ts +0 -8
- package/dist/builtin.d.ts.map +1 -1
- package/dist/builtin.js +3 -1
- package/dist/client/apiClientFactory.d.ts +14 -16
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +38 -23
- package/dist/client/clientFor.d.ts +7 -4
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/errors.d.ts +36 -48
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +19 -9
- package/dist/client/makeClient.d.ts +34 -50
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +28 -18
- package/dist/http/Request.d.ts +3 -3
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +5 -8
- package/dist/http/internal/lib.d.ts +12 -13
- package/dist/http/internal/lib.d.ts.map +1 -1
- package/dist/http/internal/lib.js +14 -14
- package/dist/ids.d.ts +9 -9
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/logger.d.ts +1 -1
- package/dist/middleware.d.ts +2 -2
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +3 -3
- package/dist/rpc/MiddlewareMaker.d.ts +17 -16
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +27 -18
- package/dist/rpc/RpcContextMap.d.ts +4 -4
- package/dist/rpc/RpcContextMap.d.ts.map +1 -1
- package/dist/rpc/RpcContextMap.js +4 -4
- package/dist/rpc/RpcMiddleware.d.ts +24 -40
- package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.js +3 -10
- package/dist/utils/effectify.js +2 -2
- package/dist/utils/gen.d.ts +4 -5
- package/dist/utils/gen.d.ts.map +1 -1
- package/dist/utils/logLevel.d.ts +1 -1
- package/dist/utils/logLevel.d.ts.map +1 -1
- package/dist/utils/logLevel.js +6 -7
- package/dist/utils/logger.d.ts +4 -3
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +10 -9
- package/dist/utils.d.ts +4 -5
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +10 -9
- package/package.json +12 -21
- package/src/Array.ts +1 -1
- package/src/Chunk.ts +2 -2
- package/src/Config/SecretURL.ts +3 -18
- package/src/Config/internal/configSecretURL.ts +2 -3
- package/src/Effect.ts +17 -37
- package/src/Layer.ts +16 -11
- package/src/Option.ts +2 -2
- package/src/Pure.ts +60 -26
- package/src/Schema/Class.ts +17 -73
- package/src/Schema/brand.ts +11 -12
- package/src/Schema/email.ts +2 -2
- package/src/Schema/ext.ts +114 -167
- package/src/Schema/moreStrings.ts +20 -23
- package/src/Schema/numbers.ts +4 -4
- package/src/Schema/phoneNumber.ts +2 -2
- package/src/Schema/schema.ts +2 -3
- package/src/Schema/strings.ts +3 -3
- package/src/Schema.ts +49 -47
- package/src/ServiceMap.ts +187 -0
- package/src/Set.ts +19 -19
- package/src/Struct.ts +4 -4
- package/src/_ext/Array.ts +4 -5
- package/src/_ext/misc.ts +4 -4
- package/src/_ext/ord.ext.ts +2 -2
- package/src/builtin.ts +2 -8
- package/src/client/apiClientFactory.ts +74 -59
- package/src/client/clientFor.ts +10 -7
- package/src/client/errors.ts +28 -22
- package/src/client/makeClient.ts +75 -100
- package/src/http/Request.ts +5 -8
- package/src/http/internal/lib.ts +13 -13
- package/src/ids.ts +1 -1
- package/src/index.ts +10 -1
- package/src/middleware.ts +2 -2
- package/src/rpc/MiddlewareMaker.ts +76 -47
- package/src/rpc/RpcContextMap.ts +7 -7
- package/src/rpc/RpcMiddleware.ts +28 -54
- package/src/utils/effectify.ts +1 -1
- package/src/utils/gen.ts +8 -6
- package/src/utils/logLevel.ts +6 -6
- package/src/utils/logger.ts +15 -20
- package/src/utils.ts +12 -12
- package/test/dist/rpc.test.d.ts.map +1 -1
- package/test/schema.test.ts +8 -8
- package/test/utils.test.ts +2 -2
- package/tsconfig.json +1 -27
- package/dist/Context.d.ts +0 -67
- package/dist/Context.d.ts.map +0 -1
- package/dist/Context.js +0 -207
- package/dist/Tag.d.ts +0 -6
- package/dist/Tag.d.ts.map +0 -1
- package/dist/Tag.js +0 -9
- package/dist/Unify.d.ts +0 -27
- package/dist/Unify.d.ts.map +0 -1
- package/dist/Unify.js +0 -15
- package/src/Context.ts +0 -351
- package/src/Tag.ts +0 -11
- package/src/Unify.ts +0 -40
|
@@ -1,45 +1,46 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { Rpc, RpcClient, RpcGroup, RpcSerialization } from "@effect/rpc"
|
|
3
2
|
import * as Config from "effect/Config"
|
|
4
3
|
import { flow } from "effect/Function"
|
|
5
|
-
import * as HashMap from "effect/HashMap"
|
|
6
4
|
import * as Layer from "effect/Layer"
|
|
7
5
|
import * as ManagedRuntime from "effect/ManagedRuntime"
|
|
8
6
|
import * as Predicate from "effect/Predicate"
|
|
7
|
+
import * as Schema from "effect/Schema"
|
|
9
8
|
import * as Struct from "effect/Struct"
|
|
10
|
-
import
|
|
9
|
+
import { Rpc, RpcClient, RpcGroup, RpcSerialization } from "effect/unstable/rpc"
|
|
11
10
|
import * as Effect from "../Effect.js"
|
|
12
11
|
import { HttpClient, HttpClientRequest } from "../http.js"
|
|
13
12
|
import * as Option from "../Option.js"
|
|
14
13
|
import type * as S from "../Schema.js"
|
|
14
|
+
import * as ServiceMap from "../ServiceMap.js"
|
|
15
15
|
import { typedKeysOf, typedValuesOf } from "../utils.js"
|
|
16
16
|
import type { Client, ClientForOptions, Requests, RequestsAny } from "./clientFor.js"
|
|
17
17
|
|
|
18
18
|
export interface ApiConfig {
|
|
19
19
|
url: string
|
|
20
|
-
headers: Option.Option<
|
|
20
|
+
headers: Option.Option<Record<string, string>>
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export const DefaultApiConfig = Config.all({
|
|
24
24
|
url: Config.string("apiUrl").pipe(Config.withDefault("/api")),
|
|
25
25
|
headers: Config
|
|
26
|
-
.
|
|
27
|
-
Config.
|
|
26
|
+
.schema(
|
|
27
|
+
Config.Record(Schema.String, Schema.String),
|
|
28
28
|
"headers"
|
|
29
29
|
)
|
|
30
30
|
.pipe(Config.option)
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
export type Req = S.
|
|
33
|
+
export type Req = S.Top & {
|
|
34
34
|
new(...args: any[]): any
|
|
35
35
|
_tag: string
|
|
36
36
|
fields: S.Struct.Fields
|
|
37
|
-
success: S.
|
|
38
|
-
|
|
37
|
+
success: S.Top
|
|
38
|
+
error: S.Top
|
|
39
39
|
config?: Record<string, any>
|
|
40
|
+
readonly "~decodingServices"?: unknown
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
class RequestName extends
|
|
43
|
+
class RequestName extends ServiceMap.Reference("RequestName", {
|
|
43
44
|
defaultValue: () => ({ requestName: "Unspecified", moduleName: "Error" })
|
|
44
45
|
}) {}
|
|
45
46
|
|
|
@@ -49,29 +50,28 @@ export const HttpClientLayer = (config: ApiConfig) =>
|
|
|
49
50
|
Effect
|
|
50
51
|
.gen(function*() {
|
|
51
52
|
const baseClient = yield* HttpClient.HttpClient
|
|
53
|
+
const ctx = yield* RequestName
|
|
52
54
|
const client = baseClient.pipe(
|
|
53
55
|
HttpClient.mapRequest(HttpClientRequest.prependUrl(config.url + "/rpc")),
|
|
54
56
|
HttpClient.mapRequest(
|
|
55
|
-
HttpClientRequest.setHeaders(config.headers.pipe(Option.getOrElse(() =>
|
|
57
|
+
HttpClientRequest.setHeaders(config.headers.pipe(Option.getOrElse(() => ({}))))
|
|
56
58
|
),
|
|
57
|
-
HttpClient.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
HttpClientRequest.appendUrl("/" + ctx.moduleName)
|
|
63
|
-
)(req)
|
|
64
|
-
)
|
|
65
|
-
)
|
|
59
|
+
HttpClient.mapRequest((req) =>
|
|
60
|
+
flow(
|
|
61
|
+
HttpClientRequest.appendUrlParam("action", ctx.requestName),
|
|
62
|
+
HttpClientRequest.appendUrl("/" + ctx.moduleName)
|
|
63
|
+
)(req)
|
|
66
64
|
)
|
|
67
65
|
)
|
|
68
66
|
return client
|
|
69
67
|
})
|
|
70
68
|
)
|
|
71
69
|
|
|
72
|
-
export const HttpClientFromConfigLayer =
|
|
73
|
-
Effect.
|
|
74
|
-
|
|
70
|
+
export const HttpClientFromConfigLayer = Layer.unwrap(
|
|
71
|
+
Effect.gen(function*() {
|
|
72
|
+
const config = yield* DefaultApiConfig
|
|
73
|
+
return HttpClientLayer(config)
|
|
74
|
+
})
|
|
75
75
|
)
|
|
76
76
|
|
|
77
77
|
export const RpcSerializationLayer = (config: ApiConfig) =>
|
|
@@ -81,7 +81,7 @@ export const RpcSerializationLayer = (config: ApiConfig) =>
|
|
|
81
81
|
)
|
|
82
82
|
|
|
83
83
|
type RpcHandlers<M extends RequestsAny> = {
|
|
84
|
-
[K in keyof M]: Rpc.Rpc<M[K]["_tag"], M[K], M[K]["success"], M[K]["
|
|
84
|
+
[K in keyof M]: Rpc.Rpc<M[K]["_tag"], M[K], M[K]["success"], M[K]["error"]>
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
const getFiltered = <M extends Requests>(resource: M) => {
|
|
@@ -94,7 +94,7 @@ const getFiltered = <M extends Requests>(resource: M) => {
|
|
|
94
94
|
Predicate.isObject(resource[cur])
|
|
95
95
|
&& (resource[cur].success)
|
|
96
96
|
) {
|
|
97
|
-
acc[cur as keyof Filtered] = resource[cur]
|
|
97
|
+
acc[cur as keyof Filtered] = resource[cur] as any
|
|
98
98
|
}
|
|
99
99
|
return acc
|
|
100
100
|
}, {} as Record<keyof Filtered, Req>)
|
|
@@ -117,7 +117,7 @@ export const makeRpcGroupFromRequestsAndModuleName = <M extends Requests, const
|
|
|
117
117
|
const rpcs = RpcGroup
|
|
118
118
|
.make(
|
|
119
119
|
...typedValuesOf(filtered).map((_) => {
|
|
120
|
-
return Rpc.
|
|
120
|
+
return Rpc.make((_ as any)._tag, { payload: _ as any, success: (_ as any).success, error: (_ as any).error })
|
|
121
121
|
})
|
|
122
122
|
)
|
|
123
123
|
.prefix(`${moduleName}.`) as unknown as RpcGroup.RpcGroup<
|
|
@@ -137,23 +137,26 @@ const makeRpcTag = <M extends Requests>(resource: M) => {
|
|
|
137
137
|
const meta = getMeta(resource)
|
|
138
138
|
const rpcs = makeRpcGroupFromRequestsAndModuleName(resource, meta.moduleName)
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
// Use Object.assign instead of class extension to avoid TS2509 with complex generic return types.
|
|
141
|
+
// The first type arg is `any` because this is a dynamically created tag — its identity is the string key.
|
|
142
|
+
const TheClient = ServiceMap.Opaque<
|
|
143
|
+
any,
|
|
142
144
|
RpcClient.RpcClient<RpcGroup.Rpcs<typeof rpcs>>
|
|
143
|
-
>()
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
)
|
|
145
|
+
>()(`RpcClient.${meta.moduleName}`)
|
|
146
|
+
// Use Layer.effect directly (not TheClient.toLayer) so TypeScript properly excludes Scope
|
|
147
|
+
const layer = Layer.effect(
|
|
148
|
+
TheClient,
|
|
149
|
+
Effect.map(
|
|
150
|
+
RpcClient.make(rpcs, { spanPrefix: "RpcClient." + meta.moduleName }),
|
|
151
|
+
(cl) => (cl as any)[meta.moduleName]
|
|
150
152
|
)
|
|
151
|
-
|
|
153
|
+
)
|
|
154
|
+
return Object.assign(TheClient, { layer })
|
|
152
155
|
}
|
|
153
156
|
|
|
154
157
|
const makeApiClientFactory = Effect
|
|
155
158
|
.gen(function*() {
|
|
156
|
-
const ctx = yield* Effect.
|
|
159
|
+
const ctx = yield* Effect.services<RpcSerialization.RpcSerialization | HttpClient.HttpClient>()
|
|
157
160
|
const makeClientFor = <M extends Requests>(
|
|
158
161
|
resource: M,
|
|
159
162
|
requestLevelLayers = Layer.empty,
|
|
@@ -176,7 +179,7 @@ const makeApiClientFactory = Effect
|
|
|
176
179
|
url: "" // why not here set meta.moduleName as root?
|
|
177
180
|
})
|
|
178
181
|
.pipe(
|
|
179
|
-
Layer.provideMerge(Layer.
|
|
182
|
+
Layer.provideMerge(Layer.succeedServices(ctx))
|
|
180
183
|
)
|
|
181
184
|
)
|
|
182
185
|
)
|
|
@@ -207,28 +210,36 @@ const makeApiClientFactory = Effect
|
|
|
207
210
|
|
|
208
211
|
const layers = requestLevelLayers.pipe(Layer.provideMerge(requestNameLayer))
|
|
209
212
|
|
|
210
|
-
const fields = Struct.omit(Request.fields, "_tag")
|
|
213
|
+
const fields = Struct.omit(Request.fields, ["_tag"] as const)
|
|
211
214
|
const requestAttr = h._tag
|
|
212
215
|
// @ts-expect-error doc
|
|
213
216
|
prev[cur] = Object.keys(fields).length === 0
|
|
214
217
|
? {
|
|
215
|
-
handler:
|
|
216
|
-
Effect.flatMap((
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
handler: mr.servicesEffect.pipe(
|
|
219
|
+
Effect.flatMap((svcs) =>
|
|
220
|
+
TheClient
|
|
221
|
+
.use((client) => (client as any)[requestAttr]!(new Request()) as Effect.Effect<any, any, never>)
|
|
222
|
+
.pipe(
|
|
223
|
+
Effect.provide(layers),
|
|
224
|
+
Effect.provide(svcs)
|
|
225
|
+
)
|
|
226
|
+
)
|
|
221
227
|
),
|
|
222
228
|
...requestMeta
|
|
223
229
|
}
|
|
224
230
|
: {
|
|
225
231
|
handler: (req: any) =>
|
|
226
|
-
|
|
227
|
-
Effect.flatMap((
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
+
mr.servicesEffect.pipe(
|
|
233
|
+
Effect.flatMap((svcs) =>
|
|
234
|
+
TheClient
|
|
235
|
+
.use((client) =>
|
|
236
|
+
(client as any)[requestAttr]!(new Request(req)) as Effect.Effect<any, any, never>
|
|
237
|
+
)
|
|
238
|
+
.pipe(
|
|
239
|
+
Effect.provide(layers),
|
|
240
|
+
Effect.provide(svcs)
|
|
241
|
+
)
|
|
242
|
+
)
|
|
232
243
|
),
|
|
233
244
|
|
|
234
245
|
...requestMeta
|
|
@@ -273,20 +284,24 @@ const makeApiClientFactory = Effect
|
|
|
273
284
|
* Used to create clients for resource modules.
|
|
274
285
|
*/
|
|
275
286
|
export class ApiClientFactory
|
|
276
|
-
extends
|
|
287
|
+
extends ServiceMap.Opaque<ApiClientFactory, Effect.Success<typeof makeApiClientFactory>>()("ApiClientFactory")
|
|
277
288
|
{
|
|
278
289
|
static readonly layer = (config: ApiConfig) =>
|
|
279
|
-
|
|
280
|
-
static readonly layerFromConfig =
|
|
290
|
+
ApiClientFactory.toLayer(makeApiClientFactory).pipe(Layer.provide(RpcSerializationLayer(config)))
|
|
291
|
+
static readonly layerFromConfig = Layer.unwrap(
|
|
292
|
+
Effect.gen(function*() {
|
|
293
|
+
const config = yield* DefaultApiConfig
|
|
294
|
+
return ApiClientFactory.layer(config)
|
|
295
|
+
})
|
|
296
|
+
)
|
|
281
297
|
|
|
282
298
|
static readonly makeFor =
|
|
283
299
|
(requestLevelLayers: Layer.Layer<never, never, never>, options?: ClientForOptions) =>
|
|
284
300
|
<M extends Requests>(
|
|
285
301
|
resource: M
|
|
286
302
|
) =>
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
) // don't rename f to clientFor or integration in vue project linked fucks up
|
|
303
|
+
ApiClientFactory.use((apiClientFactory) => {
|
|
304
|
+
const f = apiClientFactory(requestLevelLayers, options)
|
|
305
|
+
return f(resource)
|
|
306
|
+
})
|
|
292
307
|
}
|
package/src/client/clientFor.ts
CHANGED
|
@@ -58,18 +58,19 @@ export type Client<M extends RequestsAny, ModuleName extends string> = RequestHa
|
|
|
58
58
|
ModuleName
|
|
59
59
|
>
|
|
60
60
|
|
|
61
|
-
export type ExtractResponse<T> = T extends S.Schema<any
|
|
61
|
+
export type ExtractResponse<T> = T extends S.Schema<any> ? S.Schema.Type<T>
|
|
62
62
|
: T extends unknown ? void
|
|
63
63
|
: never
|
|
64
64
|
|
|
65
|
-
export type ExtractEResponse<T> = T extends S.Schema<any
|
|
65
|
+
export type ExtractEResponse<T> = T extends S.Schema<any> ? S.Codec.Encoded<T>
|
|
66
66
|
: T extends unknown ? void
|
|
67
67
|
: never
|
|
68
68
|
|
|
69
69
|
type IsEmpty<T> = keyof T extends never ? true
|
|
70
70
|
: false
|
|
71
71
|
|
|
72
|
-
|
|
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>
|
|
73
74
|
|
|
74
75
|
export interface ClientForOptions {
|
|
75
76
|
readonly skipQueryKey?: readonly string[]
|
|
@@ -90,20 +91,22 @@ export interface RequestHandlerWithInput<I, A, E, R, Request extends Req, Id ext
|
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
// make sure this is exported or d.ts of apiClientFactory breaks?!
|
|
94
|
+
type ReqDecodingServices<M> = M extends { readonly "~decodingServices": infer DS } ? DS : never
|
|
95
|
+
|
|
93
96
|
export type RequestHandlers<R, E, M extends RequestsAny, ModuleName extends string> = {
|
|
94
97
|
[K in keyof M as M[K] extends Req ? K : never]: IsEmpty<Omit<S.Schema.Type<M[K]>, Cruft>> extends true
|
|
95
98
|
? RequestHandler<
|
|
96
99
|
S.Schema.Type<M[K]["success"]>,
|
|
97
|
-
S.Schema.Type<M[K]["
|
|
98
|
-
R |
|
|
100
|
+
S.Schema.Type<M[K]["error"]> | E,
|
|
101
|
+
R | ReqDecodingServices<M[K]>,
|
|
99
102
|
M[K],
|
|
100
103
|
`${ModuleName}.${K & string}`
|
|
101
104
|
>
|
|
102
105
|
: RequestHandlerWithInput<
|
|
103
106
|
Omit<S.Schema.Type<M[K]>, Cruft>,
|
|
104
107
|
S.Schema.Type<M[K]["success"]>,
|
|
105
|
-
S.Schema.Type<M[K]["
|
|
106
|
-
R |
|
|
108
|
+
S.Schema.Type<M[K]["error"]> | E,
|
|
109
|
+
R | ReqDecodingServices<M[K]>,
|
|
107
110
|
M[K],
|
|
108
111
|
`${ModuleName}.${K & string}`
|
|
109
112
|
>
|
package/src/client/errors.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/** @effect-diagnostics overriddenSchemaConstructor:skip-file */
|
|
2
2
|
import { TaggedError } from "effect-app/Schema"
|
|
3
3
|
import * as Cause from "effect/Cause"
|
|
4
|
-
import { makeFiberFailure } from "effect/Runtime"
|
|
5
4
|
import * as S from "../Schema.js"
|
|
6
5
|
|
|
7
6
|
export const tryToJson = (error: { toJSON(): unknown; toString(): string }) => {
|
|
@@ -27,13 +26,13 @@ export class NotFoundError<ItemType = string> extends TaggedError<NotFoundError<
|
|
|
27
26
|
id: S.Unknown
|
|
28
27
|
}) {
|
|
29
28
|
constructor(
|
|
30
|
-
props:
|
|
29
|
+
props: { type: string; id: unknown; cause?: unknown },
|
|
31
30
|
disableValidation?: boolean
|
|
32
31
|
) {
|
|
33
|
-
super(props, disableValidation)
|
|
32
|
+
super(props as any, disableValidation as any)
|
|
34
33
|
}
|
|
35
34
|
override get message() {
|
|
36
|
-
return `Didn't find ${this.type}#${JSON.stringify(this.id)}`
|
|
35
|
+
return `Didn't find ${(this as any).type}#${JSON.stringify((this as any).id)}`
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
|
|
@@ -44,7 +43,7 @@ export class InvalidStateError extends TaggedError<InvalidStateError>()("Invalid
|
|
|
44
43
|
message: S.String
|
|
45
44
|
}) {
|
|
46
45
|
constructor(messageOrObject: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
|
|
47
|
-
super(typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject }, disableValidation)
|
|
46
|
+
super(typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject } as any, disableValidation as any)
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
49
|
|
|
@@ -52,7 +51,7 @@ export class ServiceUnavailableError extends TaggedError<ServiceUnavailableError
|
|
|
52
51
|
message: S.String
|
|
53
52
|
}) {
|
|
54
53
|
constructor(messageOrObject: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
|
|
55
|
-
super(typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject }, disableValidation)
|
|
54
|
+
super(typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject } as any, disableValidation as any)
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
|
|
@@ -60,13 +59,13 @@ export class ValidationError extends TaggedError<ValidationError>()("ValidationE
|
|
|
60
59
|
errors: S.Array(S.Unknown)
|
|
61
60
|
}) {
|
|
62
61
|
constructor(
|
|
63
|
-
props:
|
|
62
|
+
props: { errors: ReadonlyArray<unknown>; cause?: unknown },
|
|
64
63
|
disableValidation?: boolean
|
|
65
64
|
) {
|
|
66
|
-
super(props, disableValidation)
|
|
65
|
+
super(props as any, disableValidation as any)
|
|
67
66
|
}
|
|
68
67
|
override get message() {
|
|
69
|
-
return `Validation failed: ${this.errors.map((e) => JSON.stringify(e, undefined, 2)).join(",\n")}`
|
|
68
|
+
return `Validation failed: ${(this as any).errors.map((e: any) => JSON.stringify(e, undefined, 2)).join(",\n")}`
|
|
70
69
|
}
|
|
71
70
|
}
|
|
72
71
|
|
|
@@ -74,7 +73,7 @@ export class NotLoggedInError extends TaggedError<NotLoggedInError>()("NotLogged
|
|
|
74
73
|
message: S.String
|
|
75
74
|
}) {
|
|
76
75
|
constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
|
|
77
|
-
super(messageFallback(messageOrObject), disableValidation)
|
|
76
|
+
super(messageFallback(messageOrObject) as any, disableValidation as any)
|
|
78
77
|
}
|
|
79
78
|
}
|
|
80
79
|
|
|
@@ -85,7 +84,7 @@ export class LoginError extends TaggedError<LoginError>()("NotLoggedInError", {
|
|
|
85
84
|
message: S.String
|
|
86
85
|
}) {
|
|
87
86
|
constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
|
|
88
|
-
super(messageFallback(messageOrObject), disableValidation)
|
|
87
|
+
super(messageFallback(messageOrObject) as any, disableValidation as any)
|
|
89
88
|
}
|
|
90
89
|
}
|
|
91
90
|
|
|
@@ -93,7 +92,7 @@ export class UnauthorizedError extends TaggedError<UnauthorizedError>()("Unautho
|
|
|
93
92
|
message: S.String
|
|
94
93
|
}) {
|
|
95
94
|
constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
|
|
96
|
-
super(messageFallback(messageOrObject), disableValidation)
|
|
95
|
+
super(messageFallback(messageOrObject) as any, disableValidation as any)
|
|
97
96
|
}
|
|
98
97
|
}
|
|
99
98
|
|
|
@@ -114,10 +113,10 @@ export class OptimisticConcurrencyException extends TaggedError<OptimisticConcur
|
|
|
114
113
|
constructor(
|
|
115
114
|
args:
|
|
116
115
|
| OptimisticConcurrencyDetails
|
|
117
|
-
| (
|
|
116
|
+
| ({ message: string; cause?: unknown; raw?: unknown }),
|
|
118
117
|
disableValidation?: boolean
|
|
119
118
|
) {
|
|
120
|
-
super("message" in args ? args : { message: `Existing ${args.type} ${args.id} record changed` }, disableValidation)
|
|
119
|
+
super("message" in args ? args : { message: `Existing ${args.type} ${args.id} record changed` } as any, disableValidation as any)
|
|
121
120
|
if (!("message" in args)) {
|
|
122
121
|
this.details = args
|
|
123
122
|
}
|
|
@@ -138,10 +137,10 @@ const GeneralErrors = [
|
|
|
138
137
|
ServiceUnavailableError
|
|
139
138
|
] as const
|
|
140
139
|
|
|
141
|
-
export const SupportedErrors = S.Union(
|
|
140
|
+
export const SupportedErrors = S.Union([
|
|
142
141
|
...MutationOnlyErrors,
|
|
143
142
|
...GeneralErrors
|
|
144
|
-
)
|
|
143
|
+
])
|
|
145
144
|
// .pipe(named("SupportedErrors"))
|
|
146
145
|
// .pipe(withDefaultMake)
|
|
147
146
|
export type SupportedErrors = S.Schema.Type<typeof SupportedErrors>
|
|
@@ -175,11 +174,18 @@ export class CauseException<E> extends Error {
|
|
|
175
174
|
Error.stackTraceLimit = 0
|
|
176
175
|
super()
|
|
177
176
|
Error.stackTraceLimit = limit
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
this.
|
|
177
|
+
// v4: makeFiberFailure removed — use Cause.prettyErrors instead
|
|
178
|
+
const errors = Cause.prettyErrors(originalCause)
|
|
179
|
+
const first = errors[0]
|
|
180
|
+
if (first) {
|
|
181
|
+
this.name = first.name
|
|
182
|
+
this.message = first.message
|
|
183
|
+
if (first.stack) {
|
|
184
|
+
this.stack = first.stack
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
this.name = "CauseException"
|
|
188
|
+
this.message = Cause.pretty(originalCause)
|
|
183
189
|
}
|
|
184
190
|
}
|
|
185
191
|
toReport() {
|
|
@@ -203,7 +209,7 @@ export class CauseException<E> extends Error {
|
|
|
203
209
|
return this.toJSON()
|
|
204
210
|
}
|
|
205
211
|
override toString() {
|
|
206
|
-
return `[${this._tag}] ` + Cause.pretty(this.originalCause
|
|
212
|
+
return `[${this._tag}] ` + Cause.pretty(this.originalCause)
|
|
207
213
|
}
|
|
208
214
|
}
|
|
209
215
|
|
package/src/client/makeClient.ts
CHANGED
|
@@ -1,144 +1,119 @@
|
|
|
1
|
-
import { type GetContextConfig, type
|
|
1
|
+
import { type GetContextConfig, type RequestContextMapTagAny } from "../rpc/RpcContextMap.js"
|
|
2
2
|
import * as S from "../Schema.js"
|
|
3
3
|
import { AST } from "../Schema.js"
|
|
4
4
|
|
|
5
|
-
// TODO: Fix error types... (?)
|
|
6
|
-
type JoinSchema<T> = T extends ReadonlyArray<S.Schema.All> ? S.Union<T> : typeof S.Never
|
|
7
|
-
|
|
8
5
|
const merge = (a: any, b: Array<any>) =>
|
|
9
|
-
a !== undefined && b.length ? S.Union(a, ...b) : a !== undefined ? a : b.length ? S.Union(
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Converts struct fields to TypeLiteral schema, or returns existing schema.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```typescript
|
|
16
|
-
* type Fields = { name: S.String; age: S.Number }
|
|
17
|
-
* type Schema = SchemaOrFields<Fields>
|
|
18
|
-
* // Result: S.TypeLiteral<Fields, []>
|
|
19
|
-
*
|
|
20
|
-
* type Existing = S.String
|
|
21
|
-
* type Same = SchemaOrFields<Existing>
|
|
22
|
-
* // Result: S.String
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
type SchemaOrFields<T> = T extends S.Struct.Fields ? S.TypeLiteral<T, []> : T extends S.Schema.Any ? T : never
|
|
6
|
+
a !== undefined && b.length ? S.Union([a, ...b]) : a !== undefined ? a : b.length ? S.Union(b) : S.Never
|
|
26
7
|
|
|
27
8
|
/**
|
|
28
9
|
* Whatever the input, we will only decode or encode to void
|
|
29
10
|
*/
|
|
30
|
-
const ForceVoid: S.Schema<void> = S.
|
|
11
|
+
const ForceVoid: S.Schema<void> = S.Void as any
|
|
12
|
+
|
|
13
|
+
type SchemaOrFields<T> = T extends S.Top ? T : T extends S.Struct.Fields ? S.Struct<T> : S.Void
|
|
14
|
+
|
|
15
|
+
type TaggedRequestResult<
|
|
16
|
+
Tag extends string,
|
|
17
|
+
Payload extends S.Struct.Fields,
|
|
18
|
+
Success extends S.Top,
|
|
19
|
+
Error extends S.Top,
|
|
20
|
+
Config = Record<string, never>
|
|
21
|
+
> =
|
|
22
|
+
& S.TaggedStruct<Tag, Payload>
|
|
23
|
+
& {
|
|
24
|
+
new(...args: any[]): any
|
|
25
|
+
readonly _tag: Tag
|
|
26
|
+
readonly fields: { readonly _tag: S.tag<Tag> } & Payload
|
|
27
|
+
readonly success: Success
|
|
28
|
+
readonly error: Error
|
|
29
|
+
readonly config: Config
|
|
30
|
+
readonly "~decodingServices": S.Codec.DecodingServices<Success> | S.Codec.DecodingServices<Error>
|
|
31
|
+
}
|
|
31
32
|
|
|
32
33
|
export const makeRpcClient = <
|
|
33
34
|
RequestContextMap extends RequestContextMapTagAny,
|
|
34
|
-
GeneralErrors extends S.
|
|
35
|
+
GeneralErrors extends S.Top = never
|
|
35
36
|
>(rcs: RequestContextMap, generalErrors?: GeneralErrors) => {
|
|
36
|
-
// Long way around
|
|
37
|
-
type
|
|
38
|
-
success: S.
|
|
39
|
-
|
|
37
|
+
// Long way around ServiceMap/C extends etc to support actual jsdoc from passed in RequestConfig etc... (??)
|
|
38
|
+
type ServiceMap = {
|
|
39
|
+
success: S.Top | S.Struct.Fields // SchemaOrFields will make a Schema type out of Struct.Fields
|
|
40
|
+
error: S.Top | S.Struct.Fields // SchemaOrFields will make a Schema type out of Struct.Fields
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
type RequestConfig = GetContextConfig<RequestContextMap["config"]>
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
type MergeError<E> = [GeneralErrors] extends [never] ? SchemaOrFields<E> : S.Union<[SchemaOrFields<E>, GeneralErrors]>
|
|
46
|
+
type ErrorResult<C> = C extends { error: infer E } ? MergeError<E>
|
|
47
|
+
: [GeneralErrors] extends [never] ? S.Void
|
|
48
|
+
: GeneralErrors
|
|
49
|
+
|
|
50
|
+
function TaggedRequest<_Self>(): {
|
|
51
|
+
<Tag extends string, Payload extends S.Struct.Fields, C extends ServiceMap>(
|
|
46
52
|
tag: Tag,
|
|
47
53
|
fields: Payload,
|
|
48
54
|
config: RequestConfig & C
|
|
49
|
-
):
|
|
50
|
-
|
|
51
|
-
Self,
|
|
52
|
-
Tag,
|
|
53
|
-
{ readonly _tag: S.tag<Tag> } & Payload,
|
|
54
|
-
SchemaOrFields<typeof config["success"]>,
|
|
55
|
-
JoinSchema<
|
|
56
|
-
[SchemaOrFields<typeof config["failure"]> | GetEffectError<RequestContextMap["config"], C> | GeneralErrors]
|
|
57
|
-
>
|
|
58
|
-
>
|
|
59
|
-
& { config: Omit<C, "success" | "failure"> }
|
|
60
|
-
<Tag extends string, Payload extends S.Struct.Fields, C extends Pick<Context, "success">>(
|
|
55
|
+
): TaggedRequestResult<Tag, Payload, SchemaOrFields<C["success"]>, ErrorResult<C>, Omit<C, "success" | "error">>
|
|
56
|
+
<Tag extends string, Payload extends S.Struct.Fields, C extends Pick<ServiceMap, "success">>(
|
|
61
57
|
tag: Tag,
|
|
62
58
|
fields: Payload,
|
|
63
59
|
config: RequestConfig & C
|
|
64
|
-
):
|
|
65
|
-
|
|
66
|
-
Self,
|
|
67
|
-
Tag,
|
|
68
|
-
{ readonly _tag: S.tag<Tag> } & Payload,
|
|
69
|
-
SchemaOrFields<typeof config["success"]>,
|
|
70
|
-
JoinSchema<[GetEffectError<RequestContextMap["config"], C> | GeneralErrors]>
|
|
71
|
-
>
|
|
72
|
-
& { config: Omit<C, "success" | "failure"> }
|
|
73
|
-
<Tag extends string, Payload extends S.Struct.Fields, C extends Pick<Context, "failure">>(
|
|
60
|
+
): TaggedRequestResult<Tag, Payload, SchemaOrFields<C["success"]>, ErrorResult<C>, Omit<C, "success" | "error">>
|
|
61
|
+
<Tag extends string, Payload extends S.Struct.Fields, C extends Pick<ServiceMap, "error">>(
|
|
74
62
|
tag: Tag,
|
|
75
63
|
fields: Payload,
|
|
76
64
|
config: RequestConfig & C
|
|
77
|
-
):
|
|
78
|
-
& S.TaggedRequestClass<
|
|
79
|
-
Self,
|
|
80
|
-
Tag,
|
|
81
|
-
{ readonly _tag: S.tag<Tag> } & Payload,
|
|
82
|
-
typeof S.Void,
|
|
83
|
-
JoinSchema<
|
|
84
|
-
[SchemaOrFields<typeof config["failure"]> | GetEffectError<RequestContextMap["config"], C> | GeneralErrors]
|
|
85
|
-
>
|
|
86
|
-
>
|
|
87
|
-
& { config: Omit<C, "success" | "failure"> }
|
|
65
|
+
): TaggedRequestResult<Tag, Payload, S.Schema<void>, ErrorResult<C>, Omit<C, "success" | "error">>
|
|
88
66
|
<Tag extends string, Payload extends S.Struct.Fields, C extends Record<string, any>>(
|
|
89
67
|
tag: Tag,
|
|
90
68
|
fields: Payload,
|
|
91
69
|
config: C & RequestConfig
|
|
92
|
-
):
|
|
93
|
-
& S.TaggedRequestClass<
|
|
94
|
-
Self,
|
|
95
|
-
Tag,
|
|
96
|
-
{ readonly _tag: S.tag<Tag> } & Payload,
|
|
97
|
-
typeof S.Void,
|
|
98
|
-
JoinSchema<[GetEffectError<RequestContextMap["config"], C> | GeneralErrors]>
|
|
99
|
-
>
|
|
100
|
-
& { config: Omit<C, "success" | "failure"> }
|
|
70
|
+
): TaggedRequestResult<Tag, Payload, S.Schema<void>, ErrorResult<C>, Omit<C, "success" | "error">>
|
|
101
71
|
<Tag extends string, Payload extends S.Struct.Fields>(
|
|
102
72
|
tag: Tag,
|
|
103
73
|
fields: Payload
|
|
104
|
-
):
|
|
105
|
-
& S.TaggedRequestClass<
|
|
106
|
-
Self,
|
|
107
|
-
Tag,
|
|
108
|
-
{ readonly _tag: S.tag<Tag> } & Payload,
|
|
109
|
-
typeof S.Void,
|
|
110
|
-
GeneralErrors extends never ? typeof S.Never : GeneralErrors
|
|
111
|
-
>
|
|
112
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
113
|
-
& { config: {} }
|
|
74
|
+
): TaggedRequestResult<Tag, Payload, S.Schema<void>, ErrorResult<never>, Record<string, never>>
|
|
114
75
|
} {
|
|
115
76
|
// TODO: filter errors based on config + take care of inversion
|
|
116
77
|
const errorSchemas = Object.values(rcs.config).map((_) => _.error)
|
|
117
|
-
return (<Tag extends string, Fields extends S.Struct.Fields, C extends
|
|
78
|
+
return (<Tag extends string, Fields extends S.Struct.Fields, C extends ServiceMap>(
|
|
118
79
|
tag: Tag,
|
|
119
80
|
fields: Fields,
|
|
120
81
|
config?: C
|
|
121
82
|
) => {
|
|
122
|
-
// S.TaggedRequest
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
83
|
+
// TODO: S.TaggedRequest removed in v4 — needs rework to use Rpc.make or Request.TaggedClass
|
|
84
|
+
// For now, creating a simple tagged struct class with success/failure properties
|
|
85
|
+
const failureSchema = merge(
|
|
86
|
+
config?.error ? S.isSchema(config.error) ? config.error : S.Struct(config.error) : undefined,
|
|
87
|
+
[...errorSchemas, generalErrors].filter(Boolean)
|
|
88
|
+
)
|
|
89
|
+
const successSchema = config?.success
|
|
90
|
+
? S.isSchema(config.success)
|
|
91
|
+
? AST.isVoid(config.success.ast) ? ForceVoid : config.success
|
|
92
|
+
: S.Struct(config.success)
|
|
93
|
+
: ForceVoid
|
|
94
|
+
|
|
95
|
+
const payloadSchema = S.Struct({ _tag: S.tag(tag), ...fields })
|
|
96
|
+
|
|
97
|
+
const taggedFields = { _tag: S.tag(tag), ...fields }
|
|
98
|
+
|
|
99
|
+
const RequestClass = class {
|
|
100
|
+
constructor(payload?: any) {
|
|
101
|
+
if (payload) {
|
|
102
|
+
Object.assign(this, payload)
|
|
103
|
+
}
|
|
104
|
+
;(this as any)._tag = tag
|
|
140
105
|
}
|
|
141
106
|
}
|
|
107
|
+
|
|
108
|
+
Object.assign(RequestClass, payloadSchema, {
|
|
109
|
+
_tag: tag,
|
|
110
|
+
fields: taggedFields,
|
|
111
|
+
success: successSchema,
|
|
112
|
+
error: failureSchema,
|
|
113
|
+
config
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
return RequestClass
|
|
142
117
|
}) as any
|
|
143
118
|
}
|
|
144
119
|
|