effect-app 4.0.0-beta.5 → 4.0.0-beta.51
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 +237 -0
- 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/Effect.d.ts.map +1 -1
- package/dist/Effect.js +3 -2
- package/dist/Operations.d.ts +48 -12
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +11 -11
- package/dist/Schema/Class.d.ts +39 -1
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +89 -12
- package/dist/Schema/SpecialJsonSchema.d.ts +40 -0
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
- package/dist/Schema/SpecialJsonSchema.js +199 -0
- package/dist/Schema/SpecialOpenApi.d.ts +30 -0
- package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
- package/dist/Schema/SpecialOpenApi.js +120 -0
- package/dist/Schema/brand.d.ts +8 -5
- package/dist/Schema/brand.d.ts.map +1 -1
- package/dist/Schema/brand.js +1 -1
- package/dist/Schema/email.d.ts.map +1 -1
- package/dist/Schema/email.js +4 -3
- package/dist/Schema/ext.d.ts +43 -27
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +26 -21
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +6 -4
- package/dist/Schema/numbers.d.ts +8 -8
- package/dist/Schema/numbers.js +2 -2
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +3 -2
- package/dist/Schema.d.ts +21 -54
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +43 -64
- package/dist/ServiceMap.d.ts +3 -3
- package/dist/ServiceMap.d.ts.map +1 -1
- package/dist/ServiceMap.js +1 -1
- package/dist/client/apiClientFactory.d.ts +1 -1
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +8 -9
- package/dist/client/errors.d.ts +8 -0
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +34 -10
- package/dist/client/makeClient.d.ts +13 -12
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +7 -15
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +5 -5
- package/dist/ids.d.ts +1 -1
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/utils.d.ts +18 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +24 -5
- package/package.json +24 -12
- package/src/Config.ts +14 -0
- package/src/ConfigProvider.ts +48 -0
- package/src/Effect.ts +3 -2
- package/src/Pure.ts +12 -13
- package/src/Schema/Class.ts +114 -16
- package/src/Schema/SpecialJsonSchema.ts +216 -0
- package/src/Schema/SpecialOpenApi.ts +126 -0
- package/src/Schema/brand.ts +13 -7
- package/src/Schema/email.ts +4 -2
- package/src/Schema/ext.ts +61 -39
- package/src/Schema/moreStrings.ts +10 -6
- package/src/Schema/numbers.ts +2 -2
- package/src/Schema/phoneNumber.ts +3 -1
- package/src/Schema.ts +79 -103
- package/src/ServiceMap.ts +7 -6
- package/src/client/apiClientFactory.ts +12 -15
- package/src/client/errors.ts +45 -12
- package/src/client/makeClient.ts +33 -26
- package/src/http/Request.ts +7 -4
- package/src/ids.ts +1 -1
- package/src/index.ts +2 -1
- package/src/utils.ts +26 -4
- package/test/dist/moreStrings.test.d.ts.map +1 -0
- package/test/dist/rpc.test.d.ts.map +1 -1
- package/test/dist/special.test.d.ts.map +1 -0
- package/test/moreStrings.test.ts +17 -0
- package/test/rpc.test.ts +26 -5
- package/test/schema.test.ts +178 -1
- package/test/special.test.ts +525 -0
- package/test/utils.test.ts +1 -1
- package/tsconfig.base.json +0 -1
- package/tsconfig.json +0 -1
- package/dist/Struct.d.ts +0 -44
- package/dist/Struct.d.ts.map +0 -1
- package/dist/Struct.js +0 -29
- package/src/Struct.ts +0 -54
|
@@ -1,5 +1,4 @@
|
|
|
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"
|
|
@@ -7,6 +6,7 @@ import * as Predicate from "effect/Predicate"
|
|
|
7
6
|
import * as Schema from "effect/Schema"
|
|
8
7
|
import * as Struct from "effect/Struct"
|
|
9
8
|
import { Rpc, RpcClient, RpcGroup, RpcSerialization } from "effect/unstable/rpc"
|
|
9
|
+
import * as Config from "../Config.js"
|
|
10
10
|
import * as Effect from "../Effect.js"
|
|
11
11
|
import { HttpClient, HttpClientRequest } from "../http.js"
|
|
12
12
|
import * as Option from "../Option.js"
|
|
@@ -50,17 +50,17 @@ export const HttpClientLayer = (config: ApiConfig) =>
|
|
|
50
50
|
Effect
|
|
51
51
|
.gen(function*() {
|
|
52
52
|
const baseClient = yield* HttpClient.HttpClient
|
|
53
|
-
const ctx = yield* RequestName
|
|
54
53
|
const client = baseClient.pipe(
|
|
55
54
|
HttpClient.mapRequest(HttpClientRequest.prependUrl(config.url + "/rpc")),
|
|
56
55
|
HttpClient.mapRequest(
|
|
57
56
|
HttpClientRequest.setHeaders(config.headers.pipe(Option.getOrElse(() => ({}))))
|
|
58
57
|
),
|
|
59
|
-
HttpClient.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
HttpClient.mapRequestEffect((req) =>
|
|
59
|
+
Effect.map(RequestName.asEffect(), (ctx) =>
|
|
60
|
+
flow(
|
|
61
|
+
HttpClientRequest.appendUrlParam("action", ctx.requestName),
|
|
62
|
+
HttpClientRequest.appendUrl("/" + ctx.moduleName)
|
|
63
|
+
)(req))
|
|
64
64
|
)
|
|
65
65
|
)
|
|
66
66
|
return client
|
|
@@ -76,7 +76,7 @@ export const HttpClientFromConfigLayer = Layer.unwrap(
|
|
|
76
76
|
|
|
77
77
|
export const RpcSerializationLayer = (config: ApiConfig) =>
|
|
78
78
|
Layer.mergeAll(
|
|
79
|
-
RpcSerialization.
|
|
79
|
+
RpcSerialization.layerNdjson,
|
|
80
80
|
HttpClientLayer(config)
|
|
81
81
|
)
|
|
82
82
|
|
|
@@ -146,10 +146,7 @@ const makeRpcTag = <M extends Requests>(resource: M) => {
|
|
|
146
146
|
// Use Layer.effect directly (not TheClient.toLayer) so TypeScript properly excludes Scope
|
|
147
147
|
const layer = Layer.effect(
|
|
148
148
|
TheClient,
|
|
149
|
-
|
|
150
|
-
RpcClient.make(rpcs, { spanPrefix: "RpcClient." + meta.moduleName }),
|
|
151
|
-
(cl) => (cl as any)[meta.moduleName]
|
|
152
|
-
)
|
|
149
|
+
RpcClient.make(rpcs, { spanPrefix: "RpcClient." + meta.moduleName })
|
|
153
150
|
)
|
|
154
151
|
return Object.assign(TheClient, { layer })
|
|
155
152
|
}
|
|
@@ -188,7 +185,7 @@ const makeApiClientFactory = Effect
|
|
|
188
185
|
const filtered = getFiltered(resource)
|
|
189
186
|
return {
|
|
190
187
|
mr,
|
|
191
|
-
client:
|
|
188
|
+
client: typedKeysOf(filtered)
|
|
192
189
|
.reduce((prev, cur) => {
|
|
193
190
|
const h = filtered[cur]!
|
|
194
191
|
|
|
@@ -211,7 +208,7 @@ const makeApiClientFactory = Effect
|
|
|
211
208
|
const layers = requestLevelLayers.pipe(Layer.provideMerge(requestNameLayer))
|
|
212
209
|
|
|
213
210
|
const fields = Struct.omit(Request.fields, ["_tag"] as const)
|
|
214
|
-
const requestAttr = h._tag
|
|
211
|
+
const requestAttr = `${meta.moduleName}.${h._tag}`
|
|
215
212
|
// @ts-expect-error doc
|
|
216
213
|
prev[cur] = Object.keys(fields).length === 0
|
|
217
214
|
? {
|
|
@@ -246,7 +243,7 @@ const makeApiClientFactory = Effect
|
|
|
246
243
|
}
|
|
247
244
|
|
|
248
245
|
return prev
|
|
249
|
-
}, {} as Client<M, M["meta"]["moduleName"]>)
|
|
246
|
+
}, {} as Client<M, M["meta"]["moduleName"]>)
|
|
250
247
|
}
|
|
251
248
|
})
|
|
252
249
|
|
package/src/client/errors.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @effect-diagnostics overriddenSchemaConstructor:skip-file */
|
|
2
|
-
import {
|
|
2
|
+
import { TaggedErrorClass } from "effect-app/Schema"
|
|
3
3
|
import * as Cause from "effect/Cause"
|
|
4
4
|
import * as S from "../Schema.js"
|
|
5
5
|
|
|
@@ -21,7 +21,7 @@ export const tryToJson = (error: { toJSON(): unknown; toString(): string }) => {
|
|
|
21
21
|
|
|
22
22
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
23
23
|
// @ts-expect-error type not used
|
|
24
|
-
export class NotFoundError<ItemType = string> extends
|
|
24
|
+
export class NotFoundError<ItemType = string> extends TaggedErrorClass<NotFoundError<ItemType>>()("NotFoundError", {
|
|
25
25
|
type: S.String,
|
|
26
26
|
id: S.Unknown
|
|
27
27
|
}) {
|
|
@@ -34,28 +34,43 @@ export class NotFoundError<ItemType = string> extends TaggedError<NotFoundError<
|
|
|
34
34
|
override get message() {
|
|
35
35
|
return `Didn't find ${(this as any).type}#${JSON.stringify((this as any).id)}`
|
|
36
36
|
}
|
|
37
|
+
override toString() {
|
|
38
|
+
return `NotFoundError: ${this.message}`
|
|
39
|
+
}
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
const messageFallback = (messageOrObject?: string | { message: string }) =>
|
|
40
43
|
typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject ?? "" }
|
|
41
44
|
|
|
42
|
-
export class InvalidStateError extends
|
|
45
|
+
export class InvalidStateError extends TaggedErrorClass<InvalidStateError>()("InvalidStateError", {
|
|
43
46
|
message: S.String
|
|
44
47
|
}) {
|
|
45
48
|
constructor(messageOrObject: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
|
|
46
|
-
super(
|
|
49
|
+
super(
|
|
50
|
+
typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject } as any,
|
|
51
|
+
disableValidation as any
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
override toString() {
|
|
55
|
+
return `InvalidStateError: ${this.message}`
|
|
47
56
|
}
|
|
48
57
|
}
|
|
49
58
|
|
|
50
|
-
export class ServiceUnavailableError extends
|
|
59
|
+
export class ServiceUnavailableError extends TaggedErrorClass<ServiceUnavailableError>()("ServiceUnavailableError", {
|
|
51
60
|
message: S.String
|
|
52
61
|
}) {
|
|
53
62
|
constructor(messageOrObject: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
|
|
54
|
-
super(
|
|
63
|
+
super(
|
|
64
|
+
typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject } as any,
|
|
65
|
+
disableValidation as any
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
override toString() {
|
|
69
|
+
return `ServiceUnavailableError: ${this.message}`
|
|
55
70
|
}
|
|
56
71
|
}
|
|
57
72
|
|
|
58
|
-
export class ValidationError extends
|
|
73
|
+
export class ValidationError extends TaggedErrorClass<ValidationError>()("ValidationError", {
|
|
59
74
|
errors: S.Array(S.Unknown)
|
|
60
75
|
}) {
|
|
61
76
|
constructor(
|
|
@@ -67,33 +82,45 @@ export class ValidationError extends TaggedError<ValidationError>()("ValidationE
|
|
|
67
82
|
override get message() {
|
|
68
83
|
return `Validation failed: ${(this as any).errors.map((e: any) => JSON.stringify(e, undefined, 2)).join(",\n")}`
|
|
69
84
|
}
|
|
85
|
+
override toString() {
|
|
86
|
+
return `ValidationError: ${this.message}`
|
|
87
|
+
}
|
|
70
88
|
}
|
|
71
89
|
|
|
72
|
-
export class NotLoggedInError extends
|
|
90
|
+
export class NotLoggedInError extends TaggedErrorClass<NotLoggedInError>()("NotLoggedInError", {
|
|
73
91
|
message: S.String
|
|
74
92
|
}) {
|
|
75
93
|
constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
|
|
76
94
|
super(messageFallback(messageOrObject) as any, disableValidation as any)
|
|
77
95
|
}
|
|
96
|
+
override toString() {
|
|
97
|
+
return `NotLoggedInError: ${this.message}`
|
|
98
|
+
}
|
|
78
99
|
}
|
|
79
100
|
|
|
80
101
|
/**
|
|
81
102
|
* The user carries a valid Userprofile, but there is a problem with the login none the less.
|
|
82
103
|
*/
|
|
83
|
-
export class LoginError extends
|
|
104
|
+
export class LoginError extends TaggedErrorClass<LoginError>()("NotLoggedInError", {
|
|
84
105
|
message: S.String
|
|
85
106
|
}) {
|
|
86
107
|
constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
|
|
87
108
|
super(messageFallback(messageOrObject) as any, disableValidation as any)
|
|
88
109
|
}
|
|
110
|
+
override toString() {
|
|
111
|
+
return `LoginError: ${this.message}`
|
|
112
|
+
}
|
|
89
113
|
}
|
|
90
114
|
|
|
91
|
-
export class UnauthorizedError extends
|
|
115
|
+
export class UnauthorizedError extends TaggedErrorClass<UnauthorizedError>()("UnauthorizedError", {
|
|
92
116
|
message: S.String
|
|
93
117
|
}) {
|
|
94
118
|
constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
|
|
95
119
|
super(messageFallback(messageOrObject) as any, disableValidation as any)
|
|
96
120
|
}
|
|
121
|
+
override toString() {
|
|
122
|
+
return `UnauthorizedError: ${this.message}`
|
|
123
|
+
}
|
|
97
124
|
}
|
|
98
125
|
|
|
99
126
|
type OptimisticConcurrencyDetails = {
|
|
@@ -104,7 +131,7 @@ type OptimisticConcurrencyDetails = {
|
|
|
104
131
|
readonly found?: string | undefined
|
|
105
132
|
}
|
|
106
133
|
|
|
107
|
-
export class OptimisticConcurrencyException extends
|
|
134
|
+
export class OptimisticConcurrencyException extends TaggedErrorClass<OptimisticConcurrencyException>()(
|
|
108
135
|
"OptimisticConcurrencyException",
|
|
109
136
|
{ message: S.String }
|
|
110
137
|
) {
|
|
@@ -116,11 +143,17 @@ export class OptimisticConcurrencyException extends TaggedError<OptimisticConcur
|
|
|
116
143
|
| ({ message: string; cause?: unknown; raw?: unknown }),
|
|
117
144
|
disableValidation?: boolean
|
|
118
145
|
) {
|
|
119
|
-
super(
|
|
146
|
+
super(
|
|
147
|
+
"message" in args ? args : { message: `Existing ${args.type} ${args.id} record changed` } as any,
|
|
148
|
+
disableValidation as any
|
|
149
|
+
)
|
|
120
150
|
if (!("message" in args)) {
|
|
121
151
|
this.details = args
|
|
122
152
|
}
|
|
123
153
|
}
|
|
154
|
+
override toString() {
|
|
155
|
+
return `OptimisticConcurrencyException: ${this.message}`
|
|
156
|
+
}
|
|
124
157
|
}
|
|
125
158
|
|
|
126
159
|
const MutationOnlyErrors = [
|
package/src/client/makeClient.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SchemaTransformation } from "effect"
|
|
2
|
+
import { type GetContextConfig, type GetEffectError, type RequestContextMapTagAny } from "../rpc/RpcContextMap.js"
|
|
2
3
|
import * as S from "../Schema.js"
|
|
3
4
|
import { AST } from "../Schema.js"
|
|
4
5
|
|
|
@@ -8,26 +9,31 @@ const merge = (a: any, b: Array<any>) =>
|
|
|
8
9
|
/**
|
|
9
10
|
* Whatever the input, we will only decode or encode to void
|
|
10
11
|
*/
|
|
11
|
-
const ForceVoid
|
|
12
|
+
export const ForceVoid = S
|
|
13
|
+
.declare((_: unknown): _ is unknown => true)
|
|
14
|
+
.pipe(
|
|
15
|
+
S.decodeTo(S.Any, SchemaTransformation.transform<unknown, unknown>({ decode: () => void 0, encode: () => void 0 }))
|
|
16
|
+
)
|
|
12
17
|
|
|
13
18
|
type SchemaOrFields<T> = T extends S.Top ? T : T extends S.Struct.Fields ? S.Struct<T> : S.Void
|
|
14
19
|
|
|
15
20
|
type TaggedRequestResult<
|
|
21
|
+
Self,
|
|
16
22
|
Tag extends string,
|
|
17
23
|
Payload extends S.Struct.Fields,
|
|
18
24
|
Success extends S.Top,
|
|
19
25
|
Error extends S.Top,
|
|
20
26
|
Config = Record<string, never>
|
|
21
27
|
> =
|
|
22
|
-
& S.TaggedStruct<Tag, Payload>
|
|
28
|
+
& S.EnhancedClass<Self, S.TaggedStruct<Tag, Payload>, {}>
|
|
23
29
|
& {
|
|
24
|
-
new(...args: any[]): any
|
|
25
30
|
readonly _tag: Tag
|
|
26
|
-
readonly fields: { readonly _tag: S.tag<Tag> } & Payload
|
|
27
31
|
readonly success: Success
|
|
28
32
|
readonly error: Error
|
|
29
33
|
readonly config: Config
|
|
34
|
+
// TODO: these two are wrong. Anything using this request's success/error, should however derive the Decoding/Encoding services from them..
|
|
30
35
|
readonly "~decodingServices": S.Codec.DecodingServices<Success> | S.Codec.DecodingServices<Error>
|
|
36
|
+
readonly "~encodingServices": S.Codec.EncodingServices<Success> | S.Codec.EncodingServices<Error>
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
export const makeRpcClient = <
|
|
@@ -47,31 +53,45 @@ export const makeRpcClient = <
|
|
|
47
53
|
: [GeneralErrors] extends [never] ? GetEffectError<RequestContextMap["config"], C>
|
|
48
54
|
: MergeError<GetEffectError<RequestContextMap["config"], C>>
|
|
49
55
|
|
|
50
|
-
function TaggedRequest<
|
|
56
|
+
function TaggedRequest<Self>(): {
|
|
51
57
|
<Tag extends string, Payload extends S.Struct.Fields, C extends ServiceMap>(
|
|
52
58
|
tag: Tag,
|
|
53
59
|
fields: Payload,
|
|
54
60
|
config: RequestConfig & C
|
|
55
|
-
): TaggedRequestResult<
|
|
61
|
+
): TaggedRequestResult<
|
|
62
|
+
Self,
|
|
63
|
+
Tag,
|
|
64
|
+
Payload,
|
|
65
|
+
SchemaOrFields<C["success"]>,
|
|
66
|
+
ErrorResult<C>,
|
|
67
|
+
Omit<C, "success" | "error">
|
|
68
|
+
>
|
|
56
69
|
<Tag extends string, Payload extends S.Struct.Fields, C extends Pick<ServiceMap, "success">>(
|
|
57
70
|
tag: Tag,
|
|
58
71
|
fields: Payload,
|
|
59
72
|
config: RequestConfig & C
|
|
60
|
-
): TaggedRequestResult<
|
|
73
|
+
): TaggedRequestResult<
|
|
74
|
+
Self,
|
|
75
|
+
Tag,
|
|
76
|
+
Payload,
|
|
77
|
+
SchemaOrFields<C["success"]>,
|
|
78
|
+
ErrorResult<C>,
|
|
79
|
+
Omit<C, "success" | "error">
|
|
80
|
+
>
|
|
61
81
|
<Tag extends string, Payload extends S.Struct.Fields, C extends Pick<ServiceMap, "error">>(
|
|
62
82
|
tag: Tag,
|
|
63
83
|
fields: Payload,
|
|
64
84
|
config: RequestConfig & C
|
|
65
|
-
): TaggedRequestResult<Tag, Payload,
|
|
85
|
+
): TaggedRequestResult<Self, Tag, Payload, typeof ForceVoid, ErrorResult<C>, Omit<C, "success" | "error">>
|
|
66
86
|
<Tag extends string, Payload extends S.Struct.Fields, C extends Record<string, any>>(
|
|
67
87
|
tag: Tag,
|
|
68
88
|
fields: Payload,
|
|
69
89
|
config: C & RequestConfig
|
|
70
|
-
): TaggedRequestResult<Tag, Payload,
|
|
90
|
+
): TaggedRequestResult<Self, Tag, Payload, typeof ForceVoid, ErrorResult<C>, Omit<C, "success" | "error">>
|
|
71
91
|
<Tag extends string, Payload extends S.Struct.Fields>(
|
|
72
92
|
tag: Tag,
|
|
73
93
|
fields: Payload
|
|
74
|
-
): TaggedRequestResult<Tag, Payload,
|
|
94
|
+
): TaggedRequestResult<Self, Tag, Payload, typeof ForceVoid, ErrorResult<{}>, Record<string, never>>
|
|
75
95
|
} {
|
|
76
96
|
// TODO: filter errors based on config + take care of inversion
|
|
77
97
|
const errorSchemas = Object.values(rcs.config).map((_) => _.error)
|
|
@@ -92,22 +112,9 @@ export const makeRpcClient = <
|
|
|
92
112
|
: S.Struct(config.success)
|
|
93
113
|
: ForceVoid
|
|
94
114
|
|
|
95
|
-
const
|
|
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
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
Object.assign(RequestClass, payloadSchema, {
|
|
115
|
+
const RequestClass = S.TaggedClass<any>()(tag, fields)
|
|
116
|
+
Object.assign(RequestClass, {
|
|
109
117
|
_tag: tag,
|
|
110
|
-
fields: taggedFields,
|
|
111
118
|
success: successSchema,
|
|
112
119
|
error: failureSchema,
|
|
113
120
|
config
|
package/src/http/Request.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Option } from "effect"
|
|
1
2
|
import type { HttpClientResponse } from "effect/unstable/http/HttpClientResponse"
|
|
2
3
|
import * as Effect from "../Effect.js"
|
|
3
4
|
import { HttpClient, HttpClientError, HttpClientRequest, HttpHeaders } from "./internal/lib.js"
|
|
@@ -24,16 +25,18 @@ export const demandJson = (client: HttpClient.HttpClient) =>
|
|
|
24
25
|
.mapRequest(client, (_) => HttpClientRequest.acceptJson(_))
|
|
25
26
|
.pipe(HttpClient.transform((r, request) =>
|
|
26
27
|
Effect.tap(r, (response) =>
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
|
|
28
|
+
Option
|
|
29
|
+
.exists(
|
|
30
|
+
HttpHeaders.get(response.headers, "Content-Type"),
|
|
31
|
+
(_) => _.startsWith("application/json")
|
|
32
|
+
)
|
|
30
33
|
? Effect.void
|
|
31
34
|
: Effect.fail(
|
|
32
35
|
new HttpClientError.DecodeError({
|
|
33
36
|
request,
|
|
34
37
|
response,
|
|
35
38
|
description: "not json response: "
|
|
36
|
-
+ HttpHeaders.get(response.headers, "Content-Type")
|
|
39
|
+
+ Option.getOrElse(HttpHeaders.get(response.headers, "Content-Type"), () => "<missing>")
|
|
37
40
|
})
|
|
38
41
|
))
|
|
39
42
|
))
|
package/src/ids.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { brandedStringId, NonEmptyString255, StringId, type StringIdBrand, withDefaultMake
|
|
1
|
+
import { brandedStringId, type Codec, NonEmptyString255, StringId, type StringIdBrand, withDefaultMake } from "effect-app/Schema"
|
|
2
2
|
import type { B } from "effect-app/Schema/schema"
|
|
3
3
|
import type { Simplify } from "effect/Types"
|
|
4
4
|
import { S } from "./index.js"
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,8 @@ export * as Fnc from "./Function.js"
|
|
|
6
6
|
export * as Utils from "./utils.js"
|
|
7
7
|
|
|
8
8
|
export * as Array from "./Array.js"
|
|
9
|
+
export * as Config from "./Config.js"
|
|
10
|
+
export * as ConfigProvider from "./ConfigProvider.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"
|
|
@@ -23,7 +25,6 @@ export { type NonEmptyArray, type NonEmptyReadonlyArray } from "./Array.js"
|
|
|
23
25
|
|
|
24
26
|
export * from "effect"
|
|
25
27
|
|
|
26
|
-
export * as Struct from "./Struct.js"
|
|
27
28
|
export type * as Types from "./Types.js"
|
|
28
29
|
|
|
29
30
|
export * as SecretURL from "./Config/SecretURL.js"
|
package/src/utils.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-redundant-type-constituents */
|
|
4
|
-
import { Effect, Exit, Fiber, Option, Record } from "effect"
|
|
4
|
+
import { Cause, Effect, Exit, Fiber, Option, Record } from "effect"
|
|
5
5
|
import { dual } from "effect/Function"
|
|
6
|
-
import { isFunction } from "effect/Predicate"
|
|
6
|
+
import { isFunction, isObject } from "effect/Predicate"
|
|
7
7
|
import * as Result from "effect/Result"
|
|
8
8
|
import type { GetFieldType, NumericDictionary, PropertyPath } from "lodash"
|
|
9
9
|
import { identity, pipe } from "./Function.js"
|
|
@@ -924,8 +924,8 @@ export const runtimeFiberAsPromise = <A, E>(fiber: Fiber.Fiber<A, E>, signal?: A
|
|
|
924
924
|
if (Exit.isSuccess(exit)) {
|
|
925
925
|
resolve(exit.value)
|
|
926
926
|
} else {
|
|
927
|
-
//
|
|
928
|
-
reject(exit.cause)
|
|
927
|
+
// eslint-disable-next-line
|
|
928
|
+
reject(Cause.squash(exit.cause))
|
|
929
929
|
}
|
|
930
930
|
})
|
|
931
931
|
)
|
|
@@ -950,3 +950,25 @@ export type UnionToTuples<T, U = T> = [T] extends [never] ? []
|
|
|
950
950
|
| [T, ...UnionToTuples<Exclude<U, T>>]
|
|
951
951
|
| UnionToTuples<Exclude<U, T>>
|
|
952
952
|
: []
|
|
953
|
+
|
|
954
|
+
const genConstructor = (function*() {}).constructor
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* @example
|
|
958
|
+
* ```ts
|
|
959
|
+
* import { Utils } from "effect"
|
|
960
|
+
*
|
|
961
|
+
* function* generatorFn() {
|
|
962
|
+
* yield 1
|
|
963
|
+
* yield 2
|
|
964
|
+
* }
|
|
965
|
+
*
|
|
966
|
+
* console.log(Utils.isGeneratorFunction(generatorFn)) // true
|
|
967
|
+
* console.log(Utils.isGeneratorFunction(() => {})) // false
|
|
968
|
+
* ```
|
|
969
|
+
*
|
|
970
|
+
* @category predicates
|
|
971
|
+
* @since 3.11.0
|
|
972
|
+
*/
|
|
973
|
+
export const isGeneratorFunction = (u: unknown): u is (...args: Array<any>) => Generator<any, any, any> =>
|
|
974
|
+
isObject(u) && u.constructor === genConstructor
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moreStrings.test.d.ts","sourceRoot":"","sources":["../moreStrings.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc.test.d.ts","sourceRoot":"","sources":["../rpc.test.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rpc.test.d.ts","sourceRoot":"","sources":["../rpc.test.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAErF,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;;;;;;;;;;;;;;;;;;;;;;;;AAE7C,qBAAa,iBAAkB,SAAQ,sBAIrC;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIL,qBAAa,KAAM,SAAQ,UAQzB;CAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"special.test.d.ts","sourceRoot":"","sources":["../special.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { S } from "effect-app"
|
|
2
|
+
import * as fc from "fast-check"
|
|
3
|
+
import { urlAlphabet } from "nanoid"
|
|
4
|
+
import { test } from "vitest"
|
|
5
|
+
|
|
6
|
+
const nanoidAlphabet = new Set(urlAlphabet)
|
|
7
|
+
|
|
8
|
+
const isNanoId = (value: string) => value.length === 21 && Array.from(value).every((char) => nanoidAlphabet.has(char))
|
|
9
|
+
|
|
10
|
+
test("StringId arbitrary generates nanoid-shaped values", () => {
|
|
11
|
+
fc.assert(
|
|
12
|
+
fc.property(S.toArbitrary(S.StringId), (value) => {
|
|
13
|
+
expect(isNanoId(value)).toBe(true)
|
|
14
|
+
expect(S.is(S.StringId)(value)).toBe(true)
|
|
15
|
+
})
|
|
16
|
+
)
|
|
17
|
+
})
|
package/test/rpc.test.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
1
2
|
import { makeRpcClient, NotLoggedInError, UnauthorizedError } from "../src/client.js"
|
|
3
|
+
import { ForceVoid } from "../src/client/makeClient.js"
|
|
2
4
|
import { S } from "../src/index.js"
|
|
3
5
|
import { RpcContextMap } from "../src/rpc.js"
|
|
4
6
|
|
|
@@ -13,11 +15,30 @@ const { TaggedRequest } = makeRpcClient(RequestContextMap)
|
|
|
13
15
|
export class Stats extends TaggedRequest<Stats>()("Stats", {}, {
|
|
14
16
|
allowedRoles: ["manager"],
|
|
15
17
|
success: {
|
|
16
|
-
usersActive24Hours: S.
|
|
17
|
-
usersActiveLastWeek: S.
|
|
18
|
-
newUsersLast24Hours: S.
|
|
19
|
-
newUsersLastWeek: S.
|
|
18
|
+
usersActive24Hours: S.Finite,
|
|
19
|
+
usersActiveLastWeek: S.Finite,
|
|
20
|
+
newUsersLast24Hours: S.Finite,
|
|
21
|
+
newUsersLastWeek: S.Finite
|
|
20
22
|
}
|
|
21
23
|
}) {}
|
|
22
24
|
|
|
23
|
-
declare const _stats: typeof Stats.
|
|
25
|
+
declare const _stats: typeof Stats.Type
|
|
26
|
+
declare const _statsSuccess: typeof Stats.success.Type
|
|
27
|
+
declare const _statsError: typeof Stats.error.Type
|
|
28
|
+
|
|
29
|
+
test("ForceVoid decodes and encodes as void", () => {
|
|
30
|
+
expect(S.decodeUnknownSync(ForceVoid)(undefined)).toBe(undefined)
|
|
31
|
+
expect(S.is(ForceVoid)(undefined)).toBe(true)
|
|
32
|
+
expect(S.decodeUnknownSync(ForceVoid)("test")).toBe(undefined)
|
|
33
|
+
expect(S.is(ForceVoid)("test")).toBe(true)
|
|
34
|
+
expect(S.encodeUnknownSync(ForceVoid)("test")).toBe(undefined)
|
|
35
|
+
expect(S.encodeUnknownSync(S.toCodecJson(ForceVoid))("test")).toBe(null)
|
|
36
|
+
expectTypeOf<typeof _stats>().toEqualTypeOf<Stats>()
|
|
37
|
+
expectTypeOf<typeof _statsSuccess>().toEqualTypeOf<{
|
|
38
|
+
readonly usersActive24Hours: number
|
|
39
|
+
readonly usersActiveLastWeek: number
|
|
40
|
+
readonly newUsersLast24Hours: number
|
|
41
|
+
readonly newUsersLastWeek: number
|
|
42
|
+
}>()
|
|
43
|
+
expectTypeOf<typeof _statsError>().toEqualTypeOf<NotLoggedInError | UnauthorizedError>()
|
|
44
|
+
})
|