effect-app 4.0.0-beta.9 → 4.0.0-beta.91
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 +370 -0
- package/dist/Config/SecretURL.js +2 -2
- 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 +66 -0
- package/dist/Effect.d.ts +8 -7
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +3 -2
- package/dist/Layer.d.ts +5 -4
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +1 -1
- package/dist/Operations.d.ts +68 -31
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Pure.d.ts +2 -2
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +13 -13
- package/dist/Schema/Class.d.ts +60 -5
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +103 -12
- package/dist/Schema/SpecialJsonSchema.d.ts +21 -0
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
- package/dist/Schema/SpecialJsonSchema.js +59 -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 +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 +9 -4
- package/dist/Schema/ext.d.ts +103 -46
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +110 -51
- package/dist/Schema/moreStrings.d.ts +19 -7
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +14 -9
- package/dist/Schema/numbers.d.ts +11 -11
- package/dist/Schema/numbers.d.ts.map +1 -1
- package/dist/Schema/numbers.js +10 -9
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +8 -3
- package/dist/Schema/strings.d.ts +4 -4
- package/dist/Schema/strings.d.ts.map +1 -1
- package/dist/Schema.d.ts +23 -56
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +44 -65
- package/dist/client/apiClientFactory.d.ts +11 -28
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +17 -18
- package/dist/client/clientFor.d.ts +6 -5
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/errors.d.ts +18 -9
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +35 -10
- package/dist/client/makeClient.d.ts +21 -16
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +32 -23
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +5 -5
- 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 +3 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -9
- 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 +4 -3
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +7 -6
- package/dist/rpc/RpcContextMap.d.ts +2 -2
- package/dist/rpc/RpcContextMap.d.ts.map +1 -1
- package/dist/rpc/RpcContextMap.js +4 -4
- package/dist/rpc/RpcMiddleware.d.ts +4 -3
- package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.js +1 -1
- package/dist/utils/gen.d.ts +1 -1
- package/dist/utils/gen.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +2 -2
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +3 -3
- package/dist/utils.d.ts +18 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +24 -5
- package/package.json +29 -17
- package/src/Config/SecretURL.ts +1 -1
- package/src/Config.ts +14 -0
- package/src/ConfigProvider.ts +48 -0
- package/src/{ServiceMap.ts → Context.ts} +57 -64
- package/src/Effect.ts +11 -9
- package/src/Layer.ts +5 -4
- package/src/Pure.ts +17 -18
- package/src/Schema/Class.ts +168 -20
- package/src/Schema/SpecialJsonSchema.ts +69 -0
- package/src/Schema/SpecialOpenApi.ts +130 -0
- package/src/Schema/brand.ts +13 -7
- package/src/Schema/email.ts +10 -2
- package/src/Schema/ext.ts +185 -82
- package/src/Schema/moreStrings.ts +21 -11
- package/src/Schema/numbers.ts +9 -8
- package/src/Schema/phoneNumber.ts +8 -1
- package/src/Schema.ts +80 -104
- package/src/client/apiClientFactory.ts +30 -34
- package/src/client/clientFor.ts +6 -1
- package/src/client/errors.ts +46 -12
- package/src/client/makeClient.ts +122 -62
- package/src/http/Request.ts +7 -4
- package/src/ids.ts +3 -2
- package/src/index.ts +3 -11
- package/src/middleware.ts +2 -2
- package/src/rpc/MiddlewareMaker.ts +8 -7
- package/src/rpc/RpcContextMap.ts +6 -5
- package/src/rpc/RpcMiddleware.ts +5 -4
- package/src/utils/gen.ts +1 -1
- package/src/utils/logger.ts +2 -2
- 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/secretURL.test.d.ts.map +1 -0
- package/test/dist/special.test.d.ts.map +1 -0
- package/test/moreStrings.test.ts +17 -0
- package/test/rpc.test.ts +28 -6
- package/test/schema.test.ts +397 -4
- package/test/secretURL.test.ts +157 -0
- package/test/special.test.ts +732 -0
- package/test/utils.test.ts +2 -2
- package/tsconfig.base.json +0 -1
- package/tsconfig.json +0 -1
- package/dist/ServiceMap.d.ts +0 -44
- package/dist/ServiceMap.d.ts.map +0 -1
- package/dist/ServiceMap.js +0 -91
- package/dist/Struct.d.ts +0 -44
- package/dist/Struct.d.ts.map +0 -1
- package/dist/Struct.js +0 -29
- package/src/Struct.ts +0 -54
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 = [
|
|
@@ -174,6 +207,7 @@ export class CauseException<E> extends Error {
|
|
|
174
207
|
Error.stackTraceLimit = 0
|
|
175
208
|
super()
|
|
176
209
|
Error.stackTraceLimit = limit
|
|
210
|
+
this.cause = Cause.squash(originalCause)
|
|
177
211
|
// v4: makeFiberFailure removed — use Cause.prettyErrors instead
|
|
178
212
|
const errors = Cause.prettyErrors(originalCause)
|
|
179
213
|
const first = errors[0]
|
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,33 +9,40 @@ 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
|
-
type
|
|
20
|
+
type TaggedRequestForResult<
|
|
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
|
-
Config
|
|
26
|
+
Config,
|
|
27
|
+
ModuleName extends string
|
|
21
28
|
> =
|
|
22
|
-
& S.TaggedStruct<Tag, Payload>
|
|
29
|
+
& S.EnhancedClass<Self, S.TaggedStruct<Tag, Payload>, {}>
|
|
23
30
|
& {
|
|
24
|
-
new(...args: any[]): any
|
|
25
31
|
readonly _tag: Tag
|
|
26
|
-
readonly fields: { readonly _tag: S.tag<Tag> } & Payload
|
|
27
32
|
readonly success: Success
|
|
28
33
|
readonly error: Error
|
|
29
34
|
readonly config: Config
|
|
30
35
|
readonly "~decodingServices": S.Codec.DecodingServices<Success> | S.Codec.DecodingServices<Error>
|
|
36
|
+
readonly "~encodingServices": S.Codec.EncodingServices<Success> | S.Codec.EncodingServices<Error>
|
|
37
|
+
readonly id: `${ModuleName}.${Tag}`
|
|
38
|
+
readonly moduleName: ModuleName
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
export const makeRpcClient = <
|
|
34
42
|
RequestContextMap extends RequestContextMapTagAny,
|
|
35
43
|
GeneralErrors extends S.Top = never
|
|
36
44
|
>(rcs: RequestContextMap, generalErrors?: GeneralErrors) => {
|
|
37
|
-
// Long way around
|
|
45
|
+
// Long way around Context/C extends etc to support actual jsdoc from passed in RequestConfig etc... (??)
|
|
38
46
|
type ServiceMap = {
|
|
39
47
|
success: S.Top | S.Struct.Fields // SchemaOrFields will make a Schema type out of Struct.Fields
|
|
40
48
|
error: S.Top | S.Struct.Fields // SchemaOrFields will make a Schema type out of Struct.Fields
|
|
@@ -47,64 +55,116 @@ export const makeRpcClient = <
|
|
|
47
55
|
: [GeneralErrors] extends [never] ? GetEffectError<RequestContextMap["config"], C>
|
|
48
56
|
: MergeError<GetEffectError<RequestContextMap["config"], C>>
|
|
49
57
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
tag: Tag,
|
|
53
|
-
fields: Payload,
|
|
54
|
-
config: RequestConfig & C
|
|
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">>(
|
|
57
|
-
tag: Tag,
|
|
58
|
-
fields: Payload,
|
|
59
|
-
config: RequestConfig & C
|
|
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">>(
|
|
62
|
-
tag: Tag,
|
|
63
|
-
fields: Payload,
|
|
64
|
-
config: RequestConfig & C
|
|
65
|
-
): TaggedRequestResult<Tag, Payload, S.Codec<void>, ErrorResult<C>, Omit<C, "success" | "error">>
|
|
66
|
-
<Tag extends string, Payload extends S.Struct.Fields, C extends Record<string, any>>(
|
|
67
|
-
tag: Tag,
|
|
68
|
-
fields: Payload,
|
|
69
|
-
config: C & RequestConfig
|
|
70
|
-
): TaggedRequestResult<Tag, Payload, S.Codec<void>, ErrorResult<C>, Omit<C, "success" | "error">>
|
|
71
|
-
<Tag extends string, Payload extends S.Struct.Fields>(
|
|
72
|
-
tag: Tag,
|
|
73
|
-
fields: Payload
|
|
74
|
-
): TaggedRequestResult<Tag, Payload, S.Codec<void>, ErrorResult<never>, Record<string, never>>
|
|
75
|
-
} {
|
|
76
|
-
// TODO: filter errors based on config + take care of inversion
|
|
77
|
-
const errorSchemas = Object.values(rcs.config).map((_) => _.error)
|
|
78
|
-
return (<Tag extends string, Fields extends S.Struct.Fields, C extends ServiceMap>(
|
|
79
|
-
tag: Tag,
|
|
80
|
-
fields: Fields,
|
|
81
|
-
config?: C
|
|
82
|
-
) => {
|
|
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
|
|
58
|
+
// TODO: filter errors based on config + take care of inversion
|
|
59
|
+
const errorSchemas = Object.values(rcs.config).map((_) => _.error)
|
|
94
60
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
61
|
+
function makeRequestClass<Tag extends string, Fields extends S.Struct.Fields, C extends ServiceMap>(
|
|
62
|
+
tag: Tag,
|
|
63
|
+
fields: Fields,
|
|
64
|
+
config?: C
|
|
65
|
+
) {
|
|
66
|
+
const failureSchema = merge(
|
|
67
|
+
config?.error ? S.isSchema(config.error) ? config.error : S.Struct(config.error) : undefined,
|
|
68
|
+
[...errorSchemas, generalErrors].filter(Boolean)
|
|
69
|
+
)
|
|
70
|
+
const successSchema = config?.success
|
|
71
|
+
? S.isSchema(config.success)
|
|
72
|
+
? AST.isVoid(config.success.ast) ? ForceVoid : config.success
|
|
73
|
+
: S.Struct(config.success)
|
|
74
|
+
: ForceVoid
|
|
102
75
|
|
|
103
|
-
|
|
104
|
-
|
|
76
|
+
const RequestClass = S.TaggedClass<any>()(tag, fields)
|
|
77
|
+
Object.assign(RequestClass, {
|
|
78
|
+
_tag: tag,
|
|
79
|
+
success: successSchema,
|
|
80
|
+
error: failureSchema,
|
|
81
|
+
config
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
return RequestClass
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function TaggedRequestFor<ModuleName extends string>(moduleName: ModuleName) {
|
|
88
|
+
function TaggedRequestWithMeta<Self>(): {
|
|
89
|
+
<Tag extends string, Payload extends S.Struct.Fields, C extends ServiceMap>(
|
|
90
|
+
tag: Tag,
|
|
91
|
+
fields: Payload,
|
|
92
|
+
config: RequestConfig & C
|
|
93
|
+
): TaggedRequestForResult<
|
|
94
|
+
Self,
|
|
95
|
+
Tag,
|
|
96
|
+
Payload,
|
|
97
|
+
SchemaOrFields<C["success"]>,
|
|
98
|
+
ErrorResult<C>,
|
|
99
|
+
Omit<C, "success" | "error">,
|
|
100
|
+
ModuleName
|
|
101
|
+
>
|
|
102
|
+
<Tag extends string, Payload extends S.Struct.Fields, C extends Pick<ServiceMap, "success">>(
|
|
103
|
+
tag: Tag,
|
|
104
|
+
fields: Payload,
|
|
105
|
+
config: RequestConfig & C
|
|
106
|
+
): TaggedRequestForResult<
|
|
107
|
+
Self,
|
|
108
|
+
Tag,
|
|
109
|
+
Payload,
|
|
110
|
+
SchemaOrFields<C["success"]>,
|
|
111
|
+
ErrorResult<C>,
|
|
112
|
+
Omit<C, "success" | "error">,
|
|
113
|
+
ModuleName
|
|
114
|
+
>
|
|
115
|
+
<Tag extends string, Payload extends S.Struct.Fields, C extends Pick<ServiceMap, "error">>(
|
|
116
|
+
tag: Tag,
|
|
117
|
+
fields: Payload,
|
|
118
|
+
config: RequestConfig & C
|
|
119
|
+
): TaggedRequestForResult<
|
|
120
|
+
Self,
|
|
121
|
+
Tag,
|
|
122
|
+
Payload,
|
|
123
|
+
typeof ForceVoid,
|
|
124
|
+
ErrorResult<C>,
|
|
125
|
+
Omit<C, "success" | "error">,
|
|
126
|
+
ModuleName
|
|
127
|
+
>
|
|
128
|
+
<Tag extends string, Payload extends S.Struct.Fields, C extends Record<string, any>>(
|
|
129
|
+
tag: Tag,
|
|
130
|
+
fields: Payload,
|
|
131
|
+
config: C & RequestConfig
|
|
132
|
+
): TaggedRequestForResult<
|
|
133
|
+
Self,
|
|
134
|
+
Tag,
|
|
135
|
+
Payload,
|
|
136
|
+
typeof ForceVoid,
|
|
137
|
+
ErrorResult<C>,
|
|
138
|
+
Omit<C, "success" | "error">,
|
|
139
|
+
ModuleName
|
|
140
|
+
>
|
|
141
|
+
<Tag extends string, Payload extends S.Struct.Fields>(
|
|
142
|
+
tag: Tag,
|
|
143
|
+
fields: Payload
|
|
144
|
+
): TaggedRequestForResult<
|
|
145
|
+
Self,
|
|
146
|
+
Tag,
|
|
147
|
+
Payload,
|
|
148
|
+
typeof ForceVoid,
|
|
149
|
+
ErrorResult<{}>,
|
|
150
|
+
Record<string, never>,
|
|
151
|
+
ModuleName
|
|
152
|
+
>
|
|
153
|
+
} {
|
|
154
|
+
return (<Tag extends string, Fields extends S.Struct.Fields, C extends ServiceMap>(
|
|
155
|
+
tag: Tag,
|
|
156
|
+
fields: Fields,
|
|
157
|
+
config?: C
|
|
158
|
+
) => {
|
|
159
|
+
const cls = makeRequestClass(tag, fields, config)
|
|
160
|
+
Object.assign(cls, { id: `${moduleName}.${tag}`, moduleName })
|
|
161
|
+
return cls
|
|
162
|
+
}) as any
|
|
163
|
+
}
|
|
164
|
+
return Object.assign(TaggedRequestWithMeta, { moduleName } as const)
|
|
105
165
|
}
|
|
106
166
|
|
|
107
167
|
return {
|
|
108
|
-
|
|
168
|
+
TaggedRequestFor
|
|
109
169
|
}
|
|
110
170
|
}
|
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,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Effect } from "effect"
|
|
2
|
+
import { brandedStringId, type Codec, NonEmptyString255, StringId, type StringIdBrand, withDefaultMake } from "effect-app/Schema"
|
|
2
3
|
import type { B } from "effect-app/Schema/schema"
|
|
3
4
|
import type { Simplify } from "effect/Types"
|
|
4
5
|
import { S } from "./index.js"
|
|
@@ -17,7 +18,7 @@ export const RequestId = extendM(
|
|
|
17
18
|
const make = StringId.make as () => NonEmptyString255
|
|
18
19
|
return ({
|
|
19
20
|
make,
|
|
20
|
-
withDefault:
|
|
21
|
+
withDefault: S.withConstructorDefault(Effect.sync(make))(s as typeof s & S.WithoutConstructorDefault)
|
|
21
22
|
})
|
|
22
23
|
}
|
|
23
24
|
)
|
package/src/index.ts
CHANGED
|
@@ -1,29 +1,21 @@
|
|
|
1
1
|
import "./builtin.js"
|
|
2
2
|
|
|
3
|
-
import * as ServiceMap from "./ServiceMap.js"
|
|
4
|
-
|
|
5
3
|
export * as Fnc from "./Function.js"
|
|
6
4
|
export * as Utils from "./utils.js"
|
|
7
5
|
|
|
8
6
|
export * as Array from "./Array.js"
|
|
7
|
+
export * as Config from "./Config.js"
|
|
8
|
+
export * as ConfigProvider from "./ConfigProvider.js"
|
|
9
|
+
export * as Context from "./Context.js"
|
|
9
10
|
export * as Effect from "./Effect.js"
|
|
10
11
|
export * as Layer from "./Layer.js"
|
|
11
12
|
export * as NonEmptySet from "./NonEmptySet.js"
|
|
12
|
-
export * as ServiceMap from "./ServiceMap.js"
|
|
13
13
|
export * as Set from "./Set.js"
|
|
14
14
|
|
|
15
|
-
export {
|
|
16
|
-
/**
|
|
17
|
-
* @deprecated use ServiceMap directly instead
|
|
18
|
-
*/
|
|
19
|
-
ServiceMap as Context
|
|
20
|
-
}
|
|
21
|
-
|
|
22
15
|
export { type NonEmptyArray, type NonEmptyReadonlyArray } from "./Array.js"
|
|
23
16
|
|
|
24
17
|
export * from "effect"
|
|
25
18
|
|
|
26
|
-
export * as Struct from "./Struct.js"
|
|
27
19
|
export type * as Types from "./Types.js"
|
|
28
20
|
|
|
29
21
|
export * as SecretURL from "./Config/SecretURL.js"
|
package/src/middleware.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import {
|
|
2
|
+
import { Context } from "effect-app"
|
|
3
3
|
import { RpcX } from "./rpc.js"
|
|
4
4
|
|
|
5
|
-
export class DevMode extends
|
|
5
|
+
export class DevMode extends Context.Reference("DevMode", { defaultValue: () => false }) {}
|
|
6
6
|
|
|
7
7
|
export class RequestCacheMiddleware
|
|
8
8
|
extends RpcX.RpcMiddleware.Tag<RequestCacheMiddleware>()("RequestCacheMiddleware")
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { Effect, Layer, type Schema, Schema as S, type Scope
|
|
2
|
+
import { Effect, Layer, type Schema, Schema as S, type Scope } from "effect"
|
|
3
3
|
import { type NonEmptyArray, type NonEmptyReadonlyArray } from "effect/Array"
|
|
4
4
|
import { type Simplify } from "effect/Types"
|
|
5
5
|
import { Rpc, type RpcGroup, type RpcSchema } from "effect/unstable/rpc"
|
|
6
6
|
import { type HandlersFrom } from "effect/unstable/rpc/RpcGroup"
|
|
7
7
|
import { type RequestId } from "effect/unstable/rpc/RpcMessage"
|
|
8
|
+
import * as Context from "../Context.js"
|
|
8
9
|
import { type HttpHeaders } from "../http.js"
|
|
9
10
|
import { PreludeLogger } from "../logger.js"
|
|
10
11
|
import { type TypeTestId } from "../TypeTest.js"
|
|
@@ -61,13 +62,13 @@ export interface MiddlewareMaker<
|
|
|
61
62
|
}
|
|
62
63
|
>
|
|
63
64
|
{
|
|
64
|
-
readonly layer: Layer.Layer<Self, never,
|
|
65
|
+
readonly layer: Layer.Layer<Self, never, Context.Service.Identifier<MiddlewareProviders[number]>>
|
|
65
66
|
readonly requestContext: RequestContextTag<RequestContextMap>
|
|
66
67
|
readonly requestContextMap: RequestContextMap
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
export interface RequestContextTag<RequestContextMap extends Record<string, RpcContextMap.Any>>
|
|
70
|
-
extends
|
|
71
|
+
extends Context.Service<"RequestContextConfig", GetContextConfig<RequestContextMap>>
|
|
71
72
|
{}
|
|
72
73
|
|
|
73
74
|
export namespace MiddlewareMaker {
|
|
@@ -275,7 +276,7 @@ const middlewareMaker = <
|
|
|
275
276
|
middlewares = middlewares.toReversed() as any
|
|
276
277
|
|
|
277
278
|
return Effect.gen(function*() {
|
|
278
|
-
const context = yield* Effect.
|
|
279
|
+
const context = yield* Effect.context()
|
|
279
280
|
|
|
280
281
|
// returns a Effect/RpcMiddlewareV4 with Scope.Scope in requirements
|
|
281
282
|
// v4: wrap middleware takes (effect, options) as two params instead of a single options bag
|
|
@@ -295,7 +296,7 @@ const middlewareMaker = <
|
|
|
295
296
|
// inspired from Effect/RpcMiddleware
|
|
296
297
|
for (const tag of middlewares) {
|
|
297
298
|
// use the tag to get the middleware from context
|
|
298
|
-
const middleware =
|
|
299
|
+
const middleware = Context.getUnsafe(context, tag)
|
|
299
300
|
|
|
300
301
|
// wrap the current handler, allowing the middleware to run before and after it
|
|
301
302
|
handler = PreludeLogger.logDebug("Applying middleware wrap " + tag.key).pipe(
|
|
@@ -367,7 +368,7 @@ const makeMiddlewareBasic = <Self>() =>
|
|
|
367
368
|
return Object.assign(MiddlewareMaker, {
|
|
368
369
|
layer,
|
|
369
370
|
// tag to be used to retrieve the RequestContextConfig from Rpc annotations
|
|
370
|
-
requestContext:
|
|
371
|
+
requestContext: Context.Service<"RequestContextConfig", GetContextConfig<RequestContextMap>>(
|
|
371
372
|
"RequestContextConfig"
|
|
372
373
|
),
|
|
373
374
|
requestContextMap: rcm
|
|
@@ -380,7 +381,7 @@ export const Tag = <Self>() =>
|
|
|
380
381
|
RequestContextMap extends RequestContextMapTagAny
|
|
381
382
|
>(id: Id, rcm: RequestContextMap): MiddlewaresBuilder<Self, Id, RequestContextMap["config"]> => {
|
|
382
383
|
let allMiddleware: MiddlewareMaker.Any[] = []
|
|
383
|
-
const requestContext =
|
|
384
|
+
const requestContext = Context.Service<"RequestContextConfig", GetContextConfig<RequestContextMap["config"]>>(
|
|
384
385
|
"RequestContextConfig"
|
|
385
386
|
)
|
|
386
387
|
const it = {
|
package/src/rpc/RpcContextMap.ts
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
4
|
|
|
5
|
-
import { type Schema as S
|
|
5
|
+
import { type Schema as S } from "effect"
|
|
6
6
|
import { type AnyWithProps } from "effect/unstable/rpc/Rpc"
|
|
7
|
+
import * as Context from "../Context.js"
|
|
7
8
|
import { type RpcDynamic } from "./RpcMiddleware.js"
|
|
8
9
|
|
|
9
10
|
type Values<T extends Record<any, any>> = T[keyof T]
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
* Middleware is inactivate by default, the Key is optional in route context, and the service is optionally provided as Effect
|
|
13
|
+
* Middleware is inactivate by default, the Key is optional in route context, and the service is optionally provided as Effect Context.
|
|
13
14
|
* Unless explicitly configured as `true`.
|
|
14
15
|
*/
|
|
15
16
|
export type RpcContextMap<Service, E> = {
|
|
@@ -22,7 +23,7 @@ export type RpcContextMap<Service, E> = {
|
|
|
22
23
|
|
|
23
24
|
export declare namespace RpcContextMap {
|
|
24
25
|
/**
|
|
25
|
-
* Middleware is active by default, and provides the Service at Key in route context, and the Service is provided as Effect
|
|
26
|
+
* Middleware is active by default, and provides the Service at Key in route context, and the Service is provided as Effect Context.
|
|
26
27
|
* Unless explicitly omitted.
|
|
27
28
|
*/
|
|
28
29
|
export type Inverted<Service, E> = {
|
|
@@ -97,7 +98,7 @@ export type GetEffectError<RequestContextMap extends Record<string, RpcContextMa
|
|
|
97
98
|
}
|
|
98
99
|
>
|
|
99
100
|
|
|
100
|
-
const tag =
|
|
101
|
+
const tag = Context.Service("RequestContextConfig")
|
|
101
102
|
|
|
102
103
|
export const makeMap = <const Config extends Record<string, RpcContextMap.Any>>(config: Config) => {
|
|
103
104
|
const cls = class {
|
|
@@ -109,7 +110,7 @@ export const makeMap = <const Config extends Record<string, RpcContextMap.Any>>(
|
|
|
109
110
|
return Object.assign(cls, {
|
|
110
111
|
config, /** Retrieves RequestContextConfig out of the Rpc annotations */
|
|
111
112
|
getConfig: (rpc: AnyWithProps): GetContextConfig<Config> => {
|
|
112
|
-
return
|
|
113
|
+
return Context.getOrElse(rpc.annotations, tag as any, () => ({}))
|
|
113
114
|
},
|
|
114
115
|
/** Adapter used when setting the dynamic prop on a middleware implementation */
|
|
115
116
|
get: <
|
package/src/rpc/RpcMiddleware.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
|
-
import { type Effect, type Schema, type Schema as S, type Scope, type
|
|
4
|
+
import { type Effect, type Schema, type Schema as S, type Scope, type Stream } from "effect"
|
|
5
5
|
import { type NonEmptyReadonlyArray } from "effect/Array"
|
|
6
6
|
import { type Rpc, RpcMiddleware } from "effect/unstable/rpc"
|
|
7
7
|
import { type TypeId } from "effect/unstable/rpc/RpcMiddleware"
|
|
8
|
+
import type * as Context from "../Context.js"
|
|
8
9
|
import { type GetEffectContext, type RpcContextMap } from "./RpcContextMap.js"
|
|
9
10
|
|
|
10
11
|
export type RpcMiddlewareV4<Provides, E, Requires> = RpcMiddleware.RpcMiddleware<Provides, E, Requires>
|
|
@@ -102,8 +103,8 @@ export declare namespace TagClass {
|
|
|
102
103
|
requires?: any
|
|
103
104
|
provides?: any
|
|
104
105
|
}
|
|
105
|
-
> extends
|
|
106
|
-
new(_: never):
|
|
106
|
+
> extends Context.Service<Self, Service> {
|
|
107
|
+
new(_: never): Context.ServiceClass.Shape<Name, Service>
|
|
107
108
|
readonly [TypeId]: TypeId
|
|
108
109
|
readonly optional: Optional<Options>
|
|
109
110
|
readonly error: FailureSchema<Options>
|
|
@@ -226,7 +227,7 @@ export type ExtractProvides<R extends Rpc.Any, Tag extends string> = R extends
|
|
|
226
227
|
Rpc.Rpc<Tag, infer _Payload, infer _Success, infer _Error, infer _Middleware, infer _Requires> ? _Middleware extends {
|
|
227
228
|
readonly provides: infer _P
|
|
228
229
|
} ? [_P] extends [never] ? never
|
|
229
|
-
: _P /*_P extends
|
|
230
|
+
: _P /*_P extends Context.Service<infer _I, infer _S> ? _I
|
|
230
231
|
: never */
|
|
231
232
|
: never
|
|
232
233
|
: never
|
package/src/utils/gen.ts
CHANGED
|
@@ -15,7 +15,7 @@ export namespace EffectGenUtils {
|
|
|
15
15
|
: EG extends (..._: infer _3) => Generator<Yieldable<any, infer _, infer E, infer _R>, infer _A, infer _2> ? E
|
|
16
16
|
: never
|
|
17
17
|
|
|
18
|
-
export type
|
|
18
|
+
export type Context<EG> = EG extends Effect<infer _A, infer _E, infer R> ? R
|
|
19
19
|
// there could be a case where the generator function does not yield anything, so we need to handle that
|
|
20
20
|
: EG extends (..._: infer _3) => Generator<never, infer _A, infer _2> ? never
|
|
21
21
|
// v4: generators can yield Yieldable (Effect, Service, etc.), all have asEffect()
|
package/src/utils/logger.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
3
|
|
|
4
4
|
import { Effect, type LogLevel } from "effect"
|
|
5
|
-
import * as
|
|
5
|
+
import * as Context from "../Context.js"
|
|
6
6
|
|
|
7
7
|
type Levels = "info" | "debug" | "warn" | "error"
|
|
8
8
|
|
|
9
|
-
export class LogLevels extends
|
|
9
|
+
export class LogLevels extends Context.Reference("LogLevels", {
|
|
10
10
|
defaultValue: () => new Map<string, Levels>()
|
|
11
11
|
}) {}
|
|
12
12
|
|