effect-app 4.0.0-beta.13 → 4.0.0-beta.130
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 +501 -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/{ServiceMap.d.ts → Context.d.ts} +14 -18
- package/dist/Context.d.ts.map +1 -0
- package/dist/Context.js +66 -0
- package/dist/Effect.d.ts +8 -9
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +3 -6
- 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 +198 -33
- 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 +48 -10
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +120 -16
- package/dist/Schema/SpecialJsonSchema.d.ts +33 -0
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
- package/dist/Schema/SpecialJsonSchema.js +122 -0
- package/dist/Schema/SpecialOpenApi.d.ts +32 -0
- package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
- package/dist/Schema/SpecialOpenApi.js +123 -0
- package/dist/Schema/brand.d.ts +10 -1
- 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 +112 -47
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +115 -53
- package/dist/Schema/moreStrings.d.ts +110 -10
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +19 -10
- package/dist/Schema/numbers.d.ts +126 -14
- 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 +36 -4
- package/dist/Schema/strings.d.ts.map +1 -1
- package/dist/Schema/strings.js +1 -1
- package/dist/Schema.d.ts +74 -55
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +85 -64
- package/dist/client/apiClientFactory.d.ts +12 -28
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +16 -17
- 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 +73 -28
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +49 -23
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +5 -5
- package/dist/ids.d.ts +2 -2
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +3 -2
- package/dist/index.d.ts +3 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -8
- 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 +23 -24
- 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 +7 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +8 -2
- package/package.json +30 -14
- 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} +51 -59
- package/src/Effect.ts +11 -14
- package/src/Layer.ts +5 -4
- package/src/Pure.ts +17 -18
- package/src/Schema/Class.ts +157 -30
- package/src/Schema/SpecialJsonSchema.ts +137 -0
- package/src/Schema/SpecialOpenApi.ts +130 -0
- package/src/Schema/brand.ts +18 -3
- package/src/Schema/email.ts +10 -2
- package/src/Schema/ext.ts +196 -87
- package/src/Schema/moreStrings.ts +31 -17
- package/src/Schema/numbers.ts +14 -13
- package/src/Schema/phoneNumber.ts +8 -1
- package/src/Schema/strings.ts +4 -4
- package/src/Schema.ts +195 -104
- package/src/client/apiClientFactory.ts +104 -112
- package/src/client/clientFor.ts +6 -1
- package/src/client/errors.ts +42 -17
- package/src/client/makeClient.ts +150 -61
- package/src/http/Request.ts +7 -4
- package/src/ids.ts +2 -1
- package/src/index.ts +3 -10
- package/src/middleware.ts +2 -2
- package/src/rpc/MiddlewareMaker.ts +33 -44
- 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 +8 -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 +30 -6
- package/test/schema.test.ts +517 -4
- package/test/secretURL.test.ts +157 -0
- package/test/special.test.ts +862 -0
- package/test/utils.test.ts +2 -2
- package/tsconfig.base.json +0 -1
- package/tsconfig.json +0 -1
- package/dist/ServiceMap.d.ts.map +0 -1
- package/dist/ServiceMap.js +0 -91
package/src/Schema/strings.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type NonEmptyString = string & NonEmptyStringBrand
|
|
|
9
9
|
export const NonEmptyString = S
|
|
10
10
|
.NonEmptyString
|
|
11
11
|
.pipe(
|
|
12
|
-
fromBrand(nominal<NonEmptyString>(), {
|
|
12
|
+
fromBrand<NonEmptyString>(nominal<NonEmptyString>(), {
|
|
13
13
|
identifier: "NonEmptyString",
|
|
14
14
|
title: "NonEmptyString",
|
|
15
15
|
jsonSchema: {}
|
|
@@ -23,7 +23,7 @@ export const NonEmptyString64k = S
|
|
|
23
23
|
.NonEmptyString
|
|
24
24
|
.pipe(
|
|
25
25
|
S.check(S.isMaxLength(64 * 1024)),
|
|
26
|
-
fromBrand(nominal<NonEmptyString64k>(), {
|
|
26
|
+
fromBrand<NonEmptyString64k>(nominal<NonEmptyString64k>(), {
|
|
27
27
|
identifier: "NonEmptyString64k",
|
|
28
28
|
title: "NonEmptyString64k",
|
|
29
29
|
jsonSchema: {}
|
|
@@ -37,7 +37,7 @@ export const NonEmptyString2k = S
|
|
|
37
37
|
.NonEmptyString
|
|
38
38
|
.pipe(
|
|
39
39
|
S.check(S.isMaxLength(2 * 1024)),
|
|
40
|
-
fromBrand(nominal<NonEmptyString2k>(), {
|
|
40
|
+
fromBrand<NonEmptyString2k>(nominal<NonEmptyString2k>(), {
|
|
41
41
|
identifier: "NonEmptyString2k",
|
|
42
42
|
title: "NonEmptyString2k",
|
|
43
43
|
jsonSchema: {}
|
|
@@ -51,7 +51,7 @@ export const NonEmptyString255 = S
|
|
|
51
51
|
.NonEmptyString
|
|
52
52
|
.pipe(
|
|
53
53
|
S.check(S.isMaxLength(255)),
|
|
54
|
-
fromBrand(nominal<NonEmptyString255>(), {
|
|
54
|
+
fromBrand<NonEmptyString255>(nominal<NonEmptyString255>(), {
|
|
55
55
|
identifier: "NonEmptyString255",
|
|
56
56
|
title: "NonEmptyString255",
|
|
57
57
|
jsonSchema: {}
|
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"
|
|
6
|
-
import { withDefaultMake } from "./Schema/ext.js"
|
|
7
|
-
import { PhoneNumber as PhoneNumberT } from "./Schema/phoneNumber.js"
|
|
8
|
-
import type { AST } from "./Schema/schema.js"
|
|
5
|
+
import { Email as EmailT, type Email as EmailType } from "./Schema/email.js"
|
|
6
|
+
import { concurrencyUnbounded, withDefaultMake } from "./Schema/ext.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
|
|
|
@@ -33,6 +32,58 @@ export * as SchemaParser from "effect/SchemaParser"
|
|
|
33
32
|
|
|
34
33
|
export { Void as Void_ } from "effect/Schema"
|
|
35
34
|
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Struct / NonEmptyArray / Record — with concurrency: "unbounded"
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
export function Struct<const Fields extends S.Struct.Fields>(fields: Fields): S.Struct<Fields> {
|
|
40
|
+
const result = S.Struct(fields).annotate(concurrencyUnbounded)
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment
|
|
42
|
+
const origMapFields: any = result.mapFields
|
|
43
|
+
;(result as any).mapFields = function(this: any, f: any, options?: any) {
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
45
|
+
return origMapFields.call(this, f, options).annotate(concurrencyUnbounded)
|
|
46
|
+
}
|
|
47
|
+
return result
|
|
48
|
+
}
|
|
49
|
+
export interface Struct<Fields extends S.Struct.Fields> extends S.Struct<Fields> {}
|
|
50
|
+
export declare namespace Struct {
|
|
51
|
+
export type Fields = S.Struct.Fields
|
|
52
|
+
export type Type<F extends S.Struct.Fields> = S.Struct.Type<F>
|
|
53
|
+
export type Encoded<F extends S.Struct.Fields> = S.Struct.Encoded<F>
|
|
54
|
+
export type DecodingServices<F extends S.Struct.Fields> = S.Struct.DecodingServices<F>
|
|
55
|
+
export type EncodingServices<F extends S.Struct.Fields> = S.Struct.EncodingServices<F>
|
|
56
|
+
export type MakeIn<F extends S.Struct.Fields> = S.Struct.MakeIn<F>
|
|
57
|
+
export type Iso<F extends S.Struct.Fields> = S.Struct.Iso<F>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function NonEmptyArray<Value extends S.Top>(value: Value): S.NonEmptyArray<Value> {
|
|
61
|
+
return S.NonEmptyArray(value).annotate(concurrencyUnbounded)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function TaggedStruct<const Tag extends SchemaAST.LiteralValue, const Fields extends S.Struct.Fields>(
|
|
65
|
+
value: Tag,
|
|
66
|
+
fields: Fields
|
|
67
|
+
): S.TaggedStruct<Tag, Fields> {
|
|
68
|
+
return Struct({ _tag: S.tag(value), ...fields })
|
|
69
|
+
}
|
|
70
|
+
export type TaggedStruct<Tag extends SchemaAST.LiteralValue, Fields extends S.Struct.Fields> = S.TaggedStruct<
|
|
71
|
+
Tag,
|
|
72
|
+
Fields
|
|
73
|
+
>
|
|
74
|
+
|
|
75
|
+
export function Record<Key extends S.Record.Key, Value extends S.Top>(
|
|
76
|
+
key: Key,
|
|
77
|
+
value: Value
|
|
78
|
+
): S.$Record<Key, Value> {
|
|
79
|
+
return S.Record(key, value).annotate(concurrencyUnbounded)
|
|
80
|
+
}
|
|
81
|
+
export declare namespace Record {
|
|
82
|
+
export type Key = S.Record.Key
|
|
83
|
+
export type Type<K extends S.Record.Key, V extends S.Top> = S.Record.Type<K, V>
|
|
84
|
+
export type Encoded<K extends S.Record.Key, V extends S.Top> = S.Record.Encoded<K, V>
|
|
85
|
+
}
|
|
86
|
+
|
|
36
87
|
export const SpanId = Symbol()
|
|
37
88
|
export type SpanId = typeof SpanId
|
|
38
89
|
|
|
@@ -40,127 +91,167 @@ export interface WithOptionalSpan {
|
|
|
40
91
|
[SpanId]?: Tracer.Span
|
|
41
92
|
}
|
|
42
93
|
|
|
94
|
+
const makeEmail = S.decodeSync(EmailT as any) as (value: string) => EmailType
|
|
95
|
+
const makePhoneNumber = S.decodeSync(PhoneNumberT as any) as (value: string) => PhoneNumberType
|
|
96
|
+
|
|
43
97
|
export const Email = EmailT
|
|
44
98
|
.pipe(
|
|
45
99
|
S.annotate({
|
|
46
100
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
47
|
-
|
|
101
|
+
toArbitrary: () => (fc) => fakerArb((faker) => faker.internet.exampleEmail)(fc).map(makeEmail)
|
|
48
102
|
}),
|
|
49
103
|
withDefaultMake
|
|
50
104
|
)
|
|
51
105
|
|
|
52
|
-
export type Email =
|
|
106
|
+
export type Email = EmailType
|
|
53
107
|
|
|
54
108
|
export const PhoneNumber = PhoneNumberT
|
|
55
109
|
.pipe(
|
|
56
110
|
S.annotate({
|
|
57
|
-
|
|
111
|
+
toArbitrary: () => (fc) =>
|
|
58
112
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
59
|
-
fakerArb((faker) => faker.phone.number)(fc).map(
|
|
113
|
+
fakerArb((faker) => faker.phone.number)(fc).map(makePhoneNumber)
|
|
60
114
|
}),
|
|
61
115
|
withDefaultMake
|
|
62
116
|
)
|
|
63
117
|
|
|
64
|
-
export
|
|
65
|
-
schema: S.Codec<A, I, R>
|
|
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
|
-
}
|
|
118
|
+
export type PhoneNumber = PhoneNumberType
|
|
91
119
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
):
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
throw new Error("Unsupported")
|
|
120
|
+
// Copied from SchemaAST.collectSentinels (marked @internal in effect).
|
|
121
|
+
// Returns all { key, literal } pairs that can discriminate a union member.
|
|
122
|
+
const getTagFromAST = (schema: S.Top): string => {
|
|
123
|
+
const sentinels = collectSentinelsFromAST(schema.ast)
|
|
124
|
+
const sentinel = sentinels.find((s) => s.key === "_tag")
|
|
125
|
+
if (sentinel !== undefined && typeof sentinel.literal === "string") return sentinel.literal
|
|
126
|
+
throw new Error("No _tag literal found on schema member")
|
|
100
127
|
}
|
|
101
128
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
129
|
+
function collectSentinelsFromAST(
|
|
130
|
+
ast: SchemaAST.AST
|
|
131
|
+
): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> {
|
|
132
|
+
switch (ast._tag) {
|
|
133
|
+
case "Declaration": {
|
|
134
|
+
const s = ast.annotations?.["~sentinels"]
|
|
135
|
+
return Array.isArray(s) ? s : []
|
|
136
|
+
}
|
|
137
|
+
case "Objects":
|
|
138
|
+
return ast.propertySignatures.flatMap(
|
|
139
|
+
(ps): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> => {
|
|
140
|
+
const type = ps.type
|
|
141
|
+
if (!SchemaAST.isOptional(type)) {
|
|
142
|
+
if (SchemaAST.isLiteral(type)) return [{ key: ps.name, literal: type.literal }]
|
|
143
|
+
if (SchemaAST.isUniqueSymbol(type)) return [{ key: ps.name, literal: type.symbol }]
|
|
144
|
+
}
|
|
145
|
+
return []
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
case "Suspend":
|
|
149
|
+
return collectSentinelsFromAST(ast.thunk())
|
|
150
|
+
default:
|
|
151
|
+
return []
|
|
152
|
+
}
|
|
107
153
|
}
|
|
108
154
|
|
|
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[tag] = key as any
|
|
120
|
-
return acc
|
|
121
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
|
-
}, {} as any)
|
|
123
|
-
|
|
124
155
|
export const tags = <
|
|
125
|
-
|
|
126
|
-
Members extends NonEmptyReadonlyArray<(S.Top & { fields: { _tag: S.tag<string> } })>
|
|
156
|
+
Members extends NonEmptyReadonlyArray<(S.Top & { readonly Type: { readonly _tag: string } })>
|
|
127
157
|
>(
|
|
128
158
|
self: Members
|
|
129
159
|
) =>
|
|
130
|
-
S.Literals(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
schema: S.Codec<A, I, R>
|
|
140
|
-
) =>
|
|
141
|
-
extendM(
|
|
142
|
-
schema,
|
|
143
|
-
(_) => ({
|
|
144
|
-
is: S.is(schema as any),
|
|
145
|
-
isA: makeIs(_ as any),
|
|
146
|
-
isAnyOf: makeIsAnyOf(_ as any) /*, map: taggedUnionMap(a) */
|
|
147
|
-
})
|
|
148
|
-
)
|
|
160
|
+
S.Literals(
|
|
161
|
+
self.map(getTagFromAST) as {
|
|
162
|
+
[Index in keyof Members]: Members[Index]["Type"]["_tag"]
|
|
163
|
+
}
|
|
164
|
+
) as S.Literals<
|
|
165
|
+
{
|
|
166
|
+
[Index in keyof Members]: Members[Index]["Type"]["_tag"]
|
|
167
|
+
}
|
|
168
|
+
>
|
|
149
169
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
170
|
+
type TaggedUnionMembers = NonEmptyReadonlyArray<
|
|
171
|
+
S.Top & { readonly Type: { readonly _tag: string } }
|
|
172
|
+
>
|
|
173
|
+
|
|
174
|
+
type TaggedUnionTags<Members extends TaggedUnionMembers> = S.Literals<
|
|
175
|
+
{
|
|
176
|
+
[Index in keyof Members]: Members[Index]["Type"]["_tag"]
|
|
177
|
+
}
|
|
178
|
+
>
|
|
179
|
+
|
|
180
|
+
type TaggedPropertyKeys<A, Members extends TaggedUnionMembers> = {
|
|
181
|
+
[K in keyof A & string]: A[K] extends Members[number]["Type"] ? K : never
|
|
182
|
+
}[keyof A & string]
|
|
183
|
+
|
|
184
|
+
type PropertyGuardsFor<
|
|
185
|
+
Members extends TaggedUnionMembers,
|
|
186
|
+
K extends string,
|
|
187
|
+
A
|
|
188
|
+
> =
|
|
189
|
+
& {
|
|
190
|
+
readonly [M in Members[number] as `is${M["Type"]["_tag"]}`]: (
|
|
191
|
+
target: A
|
|
192
|
+
) => target is A & { readonly [P in K]: M["Type"] }
|
|
193
|
+
}
|
|
194
|
+
& {
|
|
195
|
+
readonly isAnyOf: <const Tags extends ReadonlyArray<Members[number]["Type"]["_tag"]>>(
|
|
196
|
+
tags: Tags
|
|
197
|
+
) => (
|
|
198
|
+
target: A
|
|
199
|
+
) => target is A & { readonly [P in K]: Extract<Members[number]["Type"], { readonly _tag: Tags[number] }> }
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
type PropertyGuards<
|
|
203
|
+
Members extends TaggedUnionMembers,
|
|
204
|
+
K extends string
|
|
205
|
+
> =
|
|
206
|
+
& {
|
|
207
|
+
readonly [M in Members[number] as `is${M["Type"]["_tag"]}`]: <
|
|
208
|
+
T extends { readonly [P in K]: Members[number]["Type"] }
|
|
209
|
+
>(target: T) => target is T & { readonly [P in K]: M["Type"] }
|
|
210
|
+
}
|
|
211
|
+
& {
|
|
212
|
+
readonly isAnyOf: <const Tags extends ReadonlyArray<Members[number]["Type"]["_tag"]>>(
|
|
213
|
+
tags: Tags
|
|
214
|
+
) => <T extends { readonly [P in K]: Members[number]["Type"] }>(
|
|
215
|
+
target: T
|
|
216
|
+
) => target is T & { readonly [P in K]: Extract<Members[number]["Type"], { readonly _tag: Tags[number] }> }
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
type TaggedUnionWithTags<Members extends TaggedUnionMembers> = S.toTaggedUnion<"_tag", Members> & {
|
|
220
|
+
readonly tags: TaggedUnionTags<Members>
|
|
221
|
+
readonly generateGuards: <K extends string>(property: K) => PropertyGuards<Members, K>
|
|
222
|
+
readonly generateGuardsFor: <A>() => <K extends TaggedPropertyKeys<A, Members>>(
|
|
223
|
+
property: K
|
|
224
|
+
) => PropertyGuardsFor<Members, K, A>
|
|
225
|
+
}
|
|
165
226
|
|
|
166
|
-
|
|
227
|
+
const extendTaggedUnionWithTags = <Members extends TaggedUnionMembers>(
|
|
228
|
+
schema: S.Union<Members>
|
|
229
|
+
): TaggedUnionWithTags<Members> =>
|
|
230
|
+
extendM(schema.pipe(S.toTaggedUnion("_tag")), (tagged) => {
|
|
231
|
+
const makeGuards = (property: string) => {
|
|
232
|
+
const result: any = {}
|
|
233
|
+
const guards: Record<string, (u: unknown) => boolean> = tagged.guards
|
|
234
|
+
for (const tag of Object.keys(guards)) {
|
|
235
|
+
const guard = guards[tag]!
|
|
236
|
+
result[`is${tag}`] = (target: any) => guard(target[property])
|
|
237
|
+
}
|
|
238
|
+
result.isAnyOf = (memberTags: Array<string>) => {
|
|
239
|
+
const check = tagged.isAnyOf(memberTags)
|
|
240
|
+
return (target: any) => check(target[property])
|
|
241
|
+
}
|
|
242
|
+
return result
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
tags: tags(schema.members),
|
|
246
|
+
generateGuards: makeGuards,
|
|
247
|
+
generateGuardsFor: () => makeGuards
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
export const ExtendTaggedUnion = <Members extends TaggedUnionMembers>(
|
|
252
|
+
schema: S.Union<Members>
|
|
253
|
+
): TaggedUnionWithTags<Members> => extendTaggedUnionWithTags(schema)
|
|
254
|
+
|
|
255
|
+
export const TaggedUnion = <
|
|
256
|
+
Members extends TaggedUnionMembers
|
|
257
|
+
>(...a: Members): TaggedUnionWithTags<Members> => extendTaggedUnionWithTags(S.Union(a))
|