effect-app 4.0.0-beta.1 → 4.0.0-beta.100
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 +444 -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 +62 -25
- 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 +43 -10
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +89 -16
- 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 +25 -13
- 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 +22 -55
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +43 -64
- package/dist/client/apiClientFactory.d.ts +11 -28
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +21 -23
- package/dist/client/clientFor.d.ts +8 -7
- 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 -34
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +5 -5
- package/dist/ids.d.ts +10 -10
- 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 +6 -5
- 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 +6 -5
- 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 +118 -30
- 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 +27 -18
- package/src/Schema/numbers.ts +9 -8
- package/src/Schema/phoneNumber.ts +8 -1
- package/src/Schema.ts +79 -103
- package/src/client/apiClientFactory.ts +35 -43
- package/src/client/clientFor.ts +8 -3
- package/src/client/errors.ts +46 -12
- package/src/client/makeClient.ts +122 -75
- package/src/http/Request.ts +7 -4
- package/src/ids.ts +4 -3
- package/src/index.ts +3 -11
- package/src/middleware.ts +2 -2
- package/src/rpc/MiddlewareMaker.ts +12 -10
- package/src/rpc/RpcContextMap.ts +6 -5
- package/src/rpc/RpcMiddleware.ts +7 -6
- 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 +729 -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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pipe } from "effect"
|
|
1
|
+
import { Effect, pipe } from "effect"
|
|
2
2
|
import type { Refinement } from "effect-app/Function"
|
|
3
3
|
import { extendM } from "effect-app/utils"
|
|
4
4
|
import * as S from "effect/Schema"
|
|
@@ -6,7 +6,7 @@ import type { Simplify } from "effect/Types"
|
|
|
6
6
|
import { customRandom, nanoid, urlAlphabet } from "nanoid"
|
|
7
7
|
import validator from "validator"
|
|
8
8
|
import { fromBrand, nominal } from "./brand.js"
|
|
9
|
-
import {
|
|
9
|
+
import { withDefaultMake, type WithDefaults } from "./ext.js"
|
|
10
10
|
import { type B } from "./schema.js"
|
|
11
11
|
import type { NonEmptyString255Brand, NonEmptyStringBrand } from "./strings.js"
|
|
12
12
|
|
|
@@ -140,11 +140,10 @@ const minLength = 6
|
|
|
140
140
|
const maxLength = 50
|
|
141
141
|
const size = 21
|
|
142
142
|
const length = 10 * size
|
|
143
|
-
const StringIdArb = ():
|
|
143
|
+
const StringIdArb = (): S.LazyArbitrary<StringId> => (fc) =>
|
|
144
144
|
fc
|
|
145
145
|
.uint8Array({ minLength: length, maxLength: length })
|
|
146
|
-
.map((_
|
|
147
|
-
|
|
146
|
+
.map((_) => customRandom(urlAlphabet, size, (size) => _.subarray(0, size))() as StringId)
|
|
148
147
|
/**
|
|
149
148
|
* A string that is at least 6 characters long and a maximum of 50.
|
|
150
149
|
*/
|
|
@@ -155,13 +154,13 @@ export const StringId = extendM(
|
|
|
155
154
|
fromBrand(nominal<StringId>(), {
|
|
156
155
|
identifier: "StringId",
|
|
157
156
|
title: "StringId",
|
|
158
|
-
|
|
157
|
+
toArbitrary: () => (fc) => StringIdArb()(fc),
|
|
159
158
|
jsonSchema: {}
|
|
160
159
|
})
|
|
161
160
|
),
|
|
162
161
|
(s) => ({
|
|
163
162
|
make: makeStringId,
|
|
164
|
-
withDefault: s.pipe(
|
|
163
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.sync(makeStringId)))
|
|
165
164
|
})
|
|
166
165
|
)
|
|
167
166
|
.pipe(withDefaultMake)
|
|
@@ -178,19 +177,21 @@ export function prefixedStringId<Brand extends StringId>() {
|
|
|
178
177
|
) => {
|
|
179
178
|
type FullPrefix = `${Prefix}${Separator}`
|
|
180
179
|
const pref = `${prefix}${separator ?? "-"}` as FullPrefix
|
|
181
|
-
const arb = ():
|
|
180
|
+
const arb = (): S.LazyArbitrary<string & Brand> => (fc) =>
|
|
182
181
|
StringIdArb()(fc).map(
|
|
183
|
-
(x
|
|
182
|
+
(x) => (pref + x.substring(0, 50 - pref.length)) as Brand
|
|
184
183
|
)
|
|
185
184
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
186
|
-
const s
|
|
185
|
+
const s = StringId
|
|
187
186
|
.pipe(
|
|
188
187
|
S.refine((x: string): x is string & Brand => x.startsWith(pref), {
|
|
189
|
-
arbitrary: arb,
|
|
190
188
|
identifier: name,
|
|
191
189
|
title: name
|
|
190
|
+
}),
|
|
191
|
+
S.annotate({
|
|
192
|
+
toArbitrary: () => (fc) => arb()(fc)
|
|
192
193
|
})
|
|
193
|
-
)
|
|
194
|
+
)
|
|
194
195
|
const schema = s.pipe(withDefaultMake)
|
|
195
196
|
const make = () => (pref + StringId.make().substring(0, 50 - pref.length)) as Brand
|
|
196
197
|
|
|
@@ -207,7 +208,9 @@ export function prefixedStringId<Brand extends StringId>() {
|
|
|
207
208
|
*/
|
|
208
209
|
prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) => ex(str),
|
|
209
210
|
prefix,
|
|
210
|
-
withDefault: schema.pipe(
|
|
211
|
+
withDefault: schema.pipe(S.withConstructorDefault<S.Codec<Brand, string> & S.WithoutConstructorDefault>(
|
|
212
|
+
Effect.sync(make)
|
|
213
|
+
))
|
|
211
214
|
})
|
|
212
215
|
)
|
|
213
216
|
}
|
|
@@ -217,10 +220,10 @@ export const brandedStringId = <
|
|
|
217
220
|
Brand extends StringIdBrand
|
|
218
221
|
>() =>
|
|
219
222
|
withDefaultMake(
|
|
220
|
-
Object.assign(Object.create(StringId), StringId) as S.
|
|
223
|
+
Object.assign(Object.create(StringId), StringId) as S.Codec<string & Brand, string> & {
|
|
221
224
|
make: () => string & Brand
|
|
222
|
-
withDefault:
|
|
223
|
-
} & WithDefaults<S.
|
|
225
|
+
withDefault: S.withConstructorDefault<S.Codec<string & Brand, string> & S.WithoutConstructorDefault>
|
|
226
|
+
} & WithDefaults<S.Codec<string & Brand, string>>
|
|
224
227
|
)
|
|
225
228
|
|
|
226
229
|
export interface PrefixedStringUtils<
|
|
@@ -232,7 +235,7 @@ export interface PrefixedStringUtils<
|
|
|
232
235
|
readonly unsafeFrom: (str: string) => Brand
|
|
233
236
|
prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) => Brand
|
|
234
237
|
readonly prefix: Prefix
|
|
235
|
-
readonly withDefault:
|
|
238
|
+
readonly withDefault: S.withConstructorDefault<S.Codec<Brand, string> & S.WithoutConstructorDefault>
|
|
236
239
|
}
|
|
237
240
|
|
|
238
241
|
export interface UrlBrand extends Simplify<B.Brand<"Url"> & NonEmptyStringBrand> {}
|
|
@@ -246,11 +249,17 @@ const isUrl: Refinement<string, Url> = (s: string): s is Url => {
|
|
|
246
249
|
export const Url = S
|
|
247
250
|
.String
|
|
248
251
|
.pipe(
|
|
252
|
+
S.annotate({
|
|
253
|
+
title: "Url",
|
|
254
|
+
format: "uri"
|
|
255
|
+
}),
|
|
249
256
|
S.refine(isUrl, {
|
|
250
|
-
arbitrary: (): any => (fc: any) => fc.webUrl().map((_: any) => _ as Url),
|
|
251
257
|
identifier: "Url",
|
|
252
258
|
title: "Url",
|
|
253
259
|
jsonSchema: { format: "uri" }
|
|
254
260
|
}),
|
|
261
|
+
S.annotate({
|
|
262
|
+
toArbitrary: () => (fc) => fc.webUrl().map((_) => _ as Url)
|
|
263
|
+
}),
|
|
255
264
|
withDefaultMake
|
|
256
265
|
)
|
package/src/Schema/numbers.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { Effect } from "effect"
|
|
1
2
|
import { extendM } from "effect-app/utils"
|
|
2
3
|
import * as S from "effect/Schema"
|
|
3
4
|
import type { Simplify } from "effect/Types"
|
|
4
5
|
import { fromBrand, nominal } from "./brand.js"
|
|
5
|
-
import {
|
|
6
|
+
import { withDefaultMake } from "./ext.js"
|
|
6
7
|
import { type B } from "./schema.js"
|
|
7
8
|
|
|
8
9
|
export interface PositiveIntBrand
|
|
@@ -14,7 +15,7 @@ export const PositiveInt = extendM(
|
|
|
14
15
|
fromBrand(nominal<PositiveInt>(), { identifier: "PositiveInt", title: "PositiveInt", jsonSchema: {} }),
|
|
15
16
|
withDefaultMake
|
|
16
17
|
),
|
|
17
|
-
(s) => ({ withDefault: s.pipe(
|
|
18
|
+
(s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(1)))) })
|
|
18
19
|
)
|
|
19
20
|
export type PositiveInt = number & PositiveIntBrand
|
|
20
21
|
|
|
@@ -29,20 +30,20 @@ export const NonNegativeInt = extendM(
|
|
|
29
30
|
}),
|
|
30
31
|
withDefaultMake
|
|
31
32
|
),
|
|
32
|
-
(s) => ({ withDefault: s.pipe(
|
|
33
|
+
(s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) })
|
|
33
34
|
)
|
|
34
35
|
export type NonNegativeInt = number & NonNegativeIntBrand
|
|
35
36
|
|
|
36
37
|
export interface IntBrand extends Simplify<B.Brand<"Int">> {}
|
|
37
38
|
export const Int = extendM(
|
|
38
39
|
S.Int.pipe(fromBrand(nominal<Int>(), { identifier: "Int", title: "Int", jsonSchema: {} }), withDefaultMake),
|
|
39
|
-
(s) => ({ withDefault: s.pipe(
|
|
40
|
+
(s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) })
|
|
40
41
|
)
|
|
41
42
|
export type Int = number & IntBrand
|
|
42
43
|
|
|
43
44
|
export interface PositiveNumberBrand extends Simplify<B.Brand<"PositiveNumber"> & NonNegativeNumberBrand> {}
|
|
44
45
|
export const PositiveNumber = extendM(
|
|
45
|
-
S.
|
|
46
|
+
S.Finite.pipe(
|
|
46
47
|
S.check(S.isGreaterThan(0)),
|
|
47
48
|
fromBrand(nominal<PositiveNumber>(), {
|
|
48
49
|
identifier: "PositiveNumber",
|
|
@@ -51,14 +52,14 @@ export const PositiveNumber = extendM(
|
|
|
51
52
|
}),
|
|
52
53
|
withDefaultMake
|
|
53
54
|
),
|
|
54
|
-
(s) => ({ withDefault: s.pipe(
|
|
55
|
+
(s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(1)))) })
|
|
55
56
|
)
|
|
56
57
|
export type PositiveNumber = number & PositiveNumberBrand
|
|
57
58
|
|
|
58
59
|
export interface NonNegativeNumberBrand extends Simplify<B.Brand<"NonNegativeNumber">> {}
|
|
59
60
|
export const NonNegativeNumber = extendM(
|
|
60
61
|
S
|
|
61
|
-
.
|
|
62
|
+
.Finite
|
|
62
63
|
.pipe(
|
|
63
64
|
S.check(S.isGreaterThanOrEqualTo(0)),
|
|
64
65
|
fromBrand(nominal<NonNegativeNumber>(), {
|
|
@@ -68,7 +69,7 @@ export const NonNegativeNumber = extendM(
|
|
|
68
69
|
}),
|
|
69
70
|
withDefaultMake
|
|
70
71
|
),
|
|
71
|
-
(s) => ({ withDefault: s.pipe(
|
|
72
|
+
(s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) })
|
|
72
73
|
)
|
|
73
74
|
export type NonNegativeNumber = number & NonNegativeNumberBrand
|
|
74
75
|
|
|
@@ -13,12 +13,19 @@ export type PhoneNumber = string & PhoneNumberBrand
|
|
|
13
13
|
export const PhoneNumber = S
|
|
14
14
|
.String
|
|
15
15
|
.pipe(
|
|
16
|
+
S.annotate({
|
|
17
|
+
title: "PhoneNumber",
|
|
18
|
+
description: "a phone number with at least 7 digits",
|
|
19
|
+
format: "phone"
|
|
20
|
+
}),
|
|
16
21
|
S.refine(isValidPhone as Refinement<string, PhoneNumber>, {
|
|
17
22
|
identifier: "PhoneNumber",
|
|
18
23
|
title: "PhoneNumber",
|
|
19
24
|
description: "a phone number with at least 7 digits",
|
|
20
|
-
arbitrary: () => (fc: any) => Numbers(7, 10)(fc).map((_: any) => _ as PhoneNumber),
|
|
21
25
|
jsonSchema: { format: "phone" }
|
|
22
26
|
}),
|
|
27
|
+
S.annotate({
|
|
28
|
+
toArbitrary: () => (fc) => Numbers(7, 10)(fc).map((_) => _ as PhoneNumber)
|
|
29
|
+
}),
|
|
23
30
|
withDefaultMake
|
|
24
31
|
)
|
package/src/Schema.ts
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SchemaAST, type Tracer } from "effect"
|
|
2
2
|
import * as S from "effect/Schema"
|
|
3
3
|
import type { NonEmptyReadonlyArray } from "./Array.js"
|
|
4
4
|
import { fakerArb } from "./faker.js"
|
|
5
|
-
import { Email as EmailT } from "./Schema/email.js"
|
|
5
|
+
import { Email as EmailT, type Email as EmailType } from "./Schema/email.js"
|
|
6
6
|
import { withDefaultMake } from "./Schema/ext.js"
|
|
7
|
-
import { PhoneNumber as PhoneNumberT } from "./Schema/phoneNumber.js"
|
|
8
|
-
import type { AST } from "./Schema/schema.js"
|
|
7
|
+
import { PhoneNumber as PhoneNumberT, type PhoneNumber as PhoneNumberType } from "./Schema/phoneNumber.js"
|
|
9
8
|
import { extendM } from "./utils.js"
|
|
10
9
|
|
|
11
10
|
export * from "effect/Schema"
|
|
12
|
-
// v4: TaggedError renamed to TaggedErrorClass
|
|
13
|
-
export { TaggedErrorClass as TaggedError } from "effect/Schema"
|
|
14
11
|
|
|
15
12
|
export * from "./Schema/Class.js"
|
|
16
13
|
export { Class, TaggedClass } from "./Schema/Class.js"
|
|
17
14
|
|
|
18
15
|
export { fromBrand, nominal } from "./Schema/brand.js"
|
|
19
|
-
export { Array, Boolean, Date,
|
|
16
|
+
export { Array, Boolean, Date, DateFromString, DateValid, Finite, Literals, NullOr, Number, ReadonlyMap, ReadonlySet } from "./Schema/ext.js"
|
|
20
17
|
export { Int, NonNegativeInt } from "./Schema/numbers.js"
|
|
21
18
|
|
|
22
19
|
export * from "./Schema/email.js"
|
|
@@ -25,6 +22,8 @@ export * from "./Schema/moreStrings.js"
|
|
|
25
22
|
export * from "./Schema/numbers.js"
|
|
26
23
|
export * from "./Schema/phoneNumber.js"
|
|
27
24
|
export * from "./Schema/schema.js"
|
|
25
|
+
export * from "./Schema/SpecialJsonSchema.js"
|
|
26
|
+
export * from "./Schema/SpecialOpenApi.js"
|
|
28
27
|
export * from "./Schema/strings.js"
|
|
29
28
|
export { NonEmptyString } from "./Schema/strings.js"
|
|
30
29
|
|
|
@@ -40,127 +39,104 @@ export interface WithOptionalSpan {
|
|
|
40
39
|
[SpanId]?: Tracer.Span
|
|
41
40
|
}
|
|
42
41
|
|
|
42
|
+
const makeEmail = S.decodeSync(EmailT as any) as (value: string) => EmailType
|
|
43
|
+
const makePhoneNumber = S.decodeSync(PhoneNumberT as any) as (value: string) => PhoneNumberType
|
|
44
|
+
|
|
43
45
|
export const Email = EmailT
|
|
44
46
|
.pipe(
|
|
45
47
|
S.annotate({
|
|
46
48
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
47
|
-
|
|
49
|
+
toArbitrary: () => (fc) => fakerArb((faker) => faker.internet.exampleEmail)(fc).map(makeEmail)
|
|
48
50
|
}),
|
|
49
51
|
withDefaultMake
|
|
50
52
|
)
|
|
51
53
|
|
|
52
|
-
export type Email =
|
|
54
|
+
export type Email = EmailType
|
|
53
55
|
|
|
54
56
|
export const PhoneNumber = PhoneNumberT
|
|
55
57
|
.pipe(
|
|
56
58
|
S.annotate({
|
|
57
|
-
|
|
59
|
+
toArbitrary: () => (fc) =>
|
|
58
60
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
59
|
-
fakerArb((faker) => faker.phone.number)(fc).map(
|
|
61
|
+
fakerArb((faker) => faker.phone.number)(fc).map(makePhoneNumber)
|
|
60
62
|
}),
|
|
61
63
|
withDefaultMake
|
|
62
64
|
)
|
|
63
65
|
|
|
64
|
-
export
|
|
65
|
-
schema: S.Schema<A>
|
|
66
|
-
) => {
|
|
67
|
-
// In v4, transformations are stored as encoding on nodes, not as wrapper nodes.
|
|
68
|
-
// Union member ASTs are directly Objects (TypeLiteral equivalent).
|
|
69
|
-
if (SchemaAST.isUnion(schema.ast)) {
|
|
70
|
-
return schema.ast.types.reduce((acc: any, t: AST.AST) => {
|
|
71
|
-
if (!SchemaAST.isObjects(t)) return acc
|
|
72
|
-
const tag = Array.findFirst(t.propertySignatures, (_: any) => {
|
|
73
|
-
if (_.name === "_tag" && SchemaAST.isLiteral(_.type)) {
|
|
74
|
-
return Option.some(_.type)
|
|
75
|
-
}
|
|
76
|
-
return Option.none()
|
|
77
|
-
})
|
|
78
|
-
const ast = Option.getOrUndefined(tag)
|
|
79
|
-
if (!ast) {
|
|
80
|
-
return acc
|
|
81
|
-
}
|
|
82
|
-
return {
|
|
83
|
-
...acc,
|
|
84
|
-
[String((ast as SchemaAST.Literal).literal)]: (x: { _tag: string }) =>
|
|
85
|
-
x._tag === (ast as SchemaAST.Literal).literal
|
|
86
|
-
}
|
|
87
|
-
}, {} as Is<A>)
|
|
88
|
-
}
|
|
89
|
-
throw new Error("Unsupported")
|
|
90
|
-
}
|
|
66
|
+
export type PhoneNumber = PhoneNumberType
|
|
91
67
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
):
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
throw new Error("Unsupported")
|
|
68
|
+
// Copied from SchemaAST.collectSentinels (marked @internal in effect).
|
|
69
|
+
// Returns all { key, literal } pairs that can discriminate a union member.
|
|
70
|
+
const getTagFromAST = (schema: S.Top): string => {
|
|
71
|
+
const sentinels = collectSentinelsFromAST(schema.ast)
|
|
72
|
+
const sentinel = sentinels.find((s) => s.key === "_tag")
|
|
73
|
+
if (sentinel !== undefined && typeof sentinel.literal === "string") return sentinel.literal
|
|
74
|
+
throw new Error("No _tag literal found on schema member")
|
|
100
75
|
}
|
|
101
76
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
77
|
+
function collectSentinelsFromAST(
|
|
78
|
+
ast: SchemaAST.AST
|
|
79
|
+
): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> {
|
|
80
|
+
switch (ast._tag) {
|
|
81
|
+
case "Declaration": {
|
|
82
|
+
const s = ast.annotations?.["~sentinels"]
|
|
83
|
+
return Array.isArray(s) ? s : []
|
|
84
|
+
}
|
|
85
|
+
case "Objects":
|
|
86
|
+
return ast.propertySignatures.flatMap(
|
|
87
|
+
(ps): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> => {
|
|
88
|
+
const type = ps.type
|
|
89
|
+
if (!SchemaAST.isOptional(type)) {
|
|
90
|
+
if (SchemaAST.isLiteral(type)) return [{ key: ps.name, literal: type.literal }]
|
|
91
|
+
if (SchemaAST.isUniqueSymbol(type)) return [{ key: ps.name, literal: type.symbol }]
|
|
92
|
+
}
|
|
93
|
+
return []
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
case "Suspend":
|
|
97
|
+
return collectSentinelsFromAST(ast.thunk())
|
|
98
|
+
default:
|
|
99
|
+
return []
|
|
100
|
+
}
|
|
107
101
|
}
|
|
108
102
|
|
|
109
|
-
export const taggedUnionMap = <
|
|
110
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
|
-
Members extends readonly (S.Top & { fields: { _tag: S.tag<string> } })[]
|
|
112
|
-
>(
|
|
113
|
-
self: Members
|
|
114
|
-
) =>
|
|
115
|
-
self.reduce((acc, key) => {
|
|
116
|
-
// TODO: v4 migration — PropertySignatureDeclaration removed, need v4 AST traversal
|
|
117
|
-
const ast = key.fields._tag.ast as any
|
|
118
|
-
const tag = ((ast.type ?? ast) as SchemaAST.Literal).literal as string // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
119
|
-
;(acc as any)[tag] = key as any
|
|
120
|
-
return acc
|
|
121
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
|
-
}, {} as any)
|
|
123
|
-
|
|
124
103
|
export const tags = <
|
|
125
|
-
|
|
126
|
-
Members extends NonEmptyReadonlyArray<(S.Top & { fields: { _tag: S.tag<string> } })>
|
|
104
|
+
Members extends NonEmptyReadonlyArray<(S.Top & { readonly Type: { readonly _tag: string } })>
|
|
127
105
|
>(
|
|
128
106
|
self: Members
|
|
129
107
|
) =>
|
|
130
|
-
S.Literals(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
108
|
+
S.Literals(
|
|
109
|
+
self.map(getTagFromAST) as {
|
|
110
|
+
[Index in keyof Members]: Members[Index]["Type"]["_tag"]
|
|
111
|
+
}
|
|
112
|
+
) as S.Literals<
|
|
113
|
+
{
|
|
114
|
+
[Index in keyof Members]: Members[Index]["Type"]["_tag"]
|
|
115
|
+
}
|
|
116
|
+
>
|
|
117
|
+
|
|
118
|
+
type TaggedUnionMembers = NonEmptyReadonlyArray<
|
|
119
|
+
S.Top & { readonly Type: { readonly _tag: string } }
|
|
120
|
+
>
|
|
121
|
+
|
|
122
|
+
type TaggedUnionTags<Members extends TaggedUnionMembers> = S.Literals<
|
|
123
|
+
{
|
|
124
|
+
[Index in keyof Members]: Members[Index]["Type"]["_tag"]
|
|
125
|
+
}
|
|
126
|
+
>
|
|
149
127
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
extendM(_, (_) => ({
|
|
158
|
-
is: S.is(_ as any),
|
|
159
|
-
isA: makeIs(_ as any),
|
|
160
|
-
isAnyOf: makeIsAnyOf(_ as any),
|
|
161
|
-
tagMap: taggedUnionMap(a),
|
|
162
|
-
tags: tags(a as any)
|
|
163
|
-
}))
|
|
164
|
-
)
|
|
128
|
+
type TaggedUnionWithTags<Members extends TaggedUnionMembers> = S.toTaggedUnion<"_tag", Members> & {
|
|
129
|
+
readonly tags: TaggedUnionTags<Members>
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const extendTaggedUnionWithTags = <Members extends TaggedUnionMembers>(
|
|
133
|
+
schema: S.Union<Members>
|
|
134
|
+
): TaggedUnionWithTags<Members> => extendM(schema.pipe(S.toTaggedUnion("_tag")), () => ({ tags: tags(schema.members) }))
|
|
165
135
|
|
|
166
|
-
export
|
|
136
|
+
export const ExtendTaggedUnion = <Members extends TaggedUnionMembers>(
|
|
137
|
+
schema: S.Union<Members>
|
|
138
|
+
): TaggedUnionWithTags<Members> => extendTaggedUnionWithTags(schema)
|
|
139
|
+
|
|
140
|
+
export const TaggedUnion = <
|
|
141
|
+
Members extends TaggedUnionMembers
|
|
142
|
+
>(...a: Members): TaggedUnionWithTags<Members> => extendTaggedUnionWithTags(S.Union(a))
|