effect-app 4.0.0-beta.16 → 4.0.0-beta.161

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.
Files changed (206) hide show
  1. package/CHANGELOG.md +635 -0
  2. package/dist/Array.d.ts +1 -1
  3. package/dist/Chunk.d.ts +1 -1
  4. package/dist/Chunk.d.ts.map +1 -1
  5. package/dist/Config/SecretURL.d.ts +1 -1
  6. package/dist/Config/SecretURL.d.ts.map +1 -1
  7. package/dist/Config/SecretURL.js +2 -2
  8. package/dist/Config/internal/configSecretURL.d.ts +1 -1
  9. package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
  10. package/dist/Config.d.ts +7 -0
  11. package/dist/Config.d.ts.map +1 -0
  12. package/dist/Config.js +6 -0
  13. package/dist/ConfigProvider.d.ts +39 -0
  14. package/dist/ConfigProvider.d.ts.map +1 -0
  15. package/dist/ConfigProvider.js +42 -0
  16. package/dist/Context.d.ts +40 -0
  17. package/dist/Context.d.ts.map +1 -0
  18. package/dist/Context.js +67 -0
  19. package/dist/Effect.d.ts +9 -10
  20. package/dist/Effect.d.ts.map +1 -1
  21. package/dist/Effect.js +3 -6
  22. package/dist/Function.d.ts +1 -1
  23. package/dist/Function.d.ts.map +1 -1
  24. package/dist/Inputify.type.d.ts +1 -1
  25. package/dist/Layer.d.ts +6 -5
  26. package/dist/Layer.d.ts.map +1 -1
  27. package/dist/Layer.js +1 -1
  28. package/dist/NonEmptySet.d.ts +1 -1
  29. package/dist/NonEmptySet.d.ts.map +1 -1
  30. package/dist/Operations.d.ts +371 -53
  31. package/dist/Operations.d.ts.map +1 -1
  32. package/dist/Operations.js +10 -10
  33. package/dist/Option.d.ts +1 -1
  34. package/dist/Option.d.ts.map +1 -1
  35. package/dist/Pure.d.ts +5 -5
  36. package/dist/Pure.d.ts.map +1 -1
  37. package/dist/Pure.js +13 -13
  38. package/dist/Schema/Class.d.ts +69 -20
  39. package/dist/Schema/Class.d.ts.map +1 -1
  40. package/dist/Schema/Class.js +189 -22
  41. package/dist/Schema/FastCheck.d.ts +1 -1
  42. package/dist/Schema/FastCheck.d.ts.map +1 -1
  43. package/dist/Schema/Methods.d.ts +1 -1
  44. package/dist/Schema/SchemaParser.d.ts +5 -0
  45. package/dist/Schema/SchemaParser.d.ts.map +1 -0
  46. package/dist/Schema/SchemaParser.js +6 -0
  47. package/dist/Schema/SpecialJsonSchema.d.ts +33 -0
  48. package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
  49. package/dist/Schema/SpecialJsonSchema.js +122 -0
  50. package/dist/Schema/SpecialOpenApi.d.ts +32 -0
  51. package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
  52. package/dist/Schema/SpecialOpenApi.js +123 -0
  53. package/dist/Schema/brand.d.ts +7 -2
  54. package/dist/Schema/brand.d.ts.map +1 -1
  55. package/dist/Schema/brand.js +1 -1
  56. package/dist/Schema/email.d.ts +1 -1
  57. package/dist/Schema/email.d.ts.map +1 -1
  58. package/dist/Schema/email.js +7 -4
  59. package/dist/Schema/ext.d.ts +111 -48
  60. package/dist/Schema/ext.d.ts.map +1 -1
  61. package/dist/Schema/ext.js +118 -53
  62. package/dist/Schema/moreStrings.d.ts +111 -11
  63. package/dist/Schema/moreStrings.d.ts.map +1 -1
  64. package/dist/Schema/moreStrings.js +14 -15
  65. package/dist/Schema/numbers.d.ts +127 -15
  66. package/dist/Schema/numbers.d.ts.map +1 -1
  67. package/dist/Schema/numbers.js +10 -12
  68. package/dist/Schema/phoneNumber.d.ts +1 -1
  69. package/dist/Schema/phoneNumber.d.ts.map +1 -1
  70. package/dist/Schema/phoneNumber.js +6 -3
  71. package/dist/Schema/schema.d.ts +1 -1
  72. package/dist/Schema/strings.d.ts +37 -5
  73. package/dist/Schema/strings.d.ts.map +1 -1
  74. package/dist/Schema/strings.js +1 -5
  75. package/dist/Schema.d.ts +103 -58
  76. package/dist/Schema.d.ts.map +1 -1
  77. package/dist/Schema.js +128 -64
  78. package/dist/Set.d.ts +1 -1
  79. package/dist/Set.d.ts.map +1 -1
  80. package/dist/TypeTest.d.ts +1 -1
  81. package/dist/Types.d.ts +1 -1
  82. package/dist/Widen.type.d.ts +1 -1
  83. package/dist/_ext/Array.d.ts +1 -1
  84. package/dist/_ext/Array.d.ts.map +1 -1
  85. package/dist/_ext/date.d.ts +1 -1
  86. package/dist/_ext/misc.d.ts +1 -1
  87. package/dist/_ext/ord.ext.d.ts +1 -1
  88. package/dist/_ext/ord.ext.d.ts.map +1 -1
  89. package/dist/builtin.d.ts +1 -1
  90. package/dist/builtin.d.ts.map +1 -1
  91. package/dist/client/apiClientFactory.d.ts +14 -30
  92. package/dist/client/apiClientFactory.d.ts.map +1 -1
  93. package/dist/client/apiClientFactory.js +18 -19
  94. package/dist/client/clientFor.d.ts +7 -6
  95. package/dist/client/clientFor.d.ts.map +1 -1
  96. package/dist/client/errors.d.ts +44 -19
  97. package/dist/client/errors.d.ts.map +1 -1
  98. package/dist/client/errors.js +35 -10
  99. package/dist/client/makeClient.d.ts +77 -29
  100. package/dist/client/makeClient.d.ts.map +1 -1
  101. package/dist/client/makeClient.js +49 -23
  102. package/dist/client.d.ts +1 -1
  103. package/dist/faker.d.ts +1 -1
  104. package/dist/faker.d.ts.map +1 -1
  105. package/dist/http/Request.d.ts +2 -2
  106. package/dist/http/Request.d.ts.map +1 -1
  107. package/dist/http/Request.js +5 -5
  108. package/dist/http/internal/lib.d.ts +1 -1
  109. package/dist/http.d.ts +1 -1
  110. package/dist/ids.d.ts +3 -3
  111. package/dist/ids.d.ts.map +1 -1
  112. package/dist/ids.js +3 -2
  113. package/dist/index.d.ts +5 -8
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.js +6 -8
  116. package/dist/logger.d.ts +1 -1
  117. package/dist/middleware.d.ts +8 -8
  118. package/dist/middleware.d.ts.map +1 -1
  119. package/dist/middleware.js +8 -8
  120. package/dist/rpc/MiddlewareMaker.d.ts +5 -4
  121. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  122. package/dist/rpc/MiddlewareMaker.js +26 -27
  123. package/dist/rpc/RpcContextMap.d.ts +3 -3
  124. package/dist/rpc/RpcContextMap.d.ts.map +1 -1
  125. package/dist/rpc/RpcContextMap.js +4 -4
  126. package/dist/rpc/RpcMiddleware.d.ts +5 -4
  127. package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
  128. package/dist/rpc/RpcMiddleware.js +1 -1
  129. package/dist/rpc.d.ts +1 -2
  130. package/dist/rpc.d.ts.map +1 -1
  131. package/dist/rpc.js +1 -2
  132. package/dist/transform.d.ts +1 -1
  133. package/dist/transform.d.ts.map +1 -1
  134. package/dist/transform.js +3 -3
  135. package/dist/utils/effectify.d.ts +1 -1
  136. package/dist/utils/extend.d.ts +1 -1
  137. package/dist/utils/extend.d.ts.map +1 -1
  138. package/dist/utils/gen.d.ts +2 -2
  139. package/dist/utils/gen.d.ts.map +1 -1
  140. package/dist/utils/logLevel.d.ts +2 -2
  141. package/dist/utils/logLevel.d.ts.map +1 -1
  142. package/dist/utils/logger.d.ts +3 -3
  143. package/dist/utils/logger.d.ts.map +1 -1
  144. package/dist/utils/logger.js +3 -3
  145. package/dist/utils.d.ts +30 -10
  146. package/dist/utils.d.ts.map +1 -1
  147. package/dist/utils.js +10 -4
  148. package/dist/validation/validators.d.ts +1 -1
  149. package/dist/validation/validators.d.ts.map +1 -1
  150. package/dist/validation.d.ts +1 -1
  151. package/dist/validation.d.ts.map +1 -1
  152. package/eslint.config.mjs +2 -2
  153. package/package.json +39 -19
  154. package/src/Config/SecretURL.ts +2 -1
  155. package/src/Config.ts +14 -0
  156. package/src/ConfigProvider.ts +48 -0
  157. package/src/{ServiceMap.ts → Context.ts} +52 -59
  158. package/src/Effect.ts +12 -14
  159. package/src/Layer.ts +5 -4
  160. package/src/Operations.ts +14 -14
  161. package/src/Pure.ts +17 -18
  162. package/src/Schema/Class.ts +278 -62
  163. package/src/Schema/SchemaParser.ts +12 -0
  164. package/src/Schema/SpecialJsonSchema.ts +137 -0
  165. package/src/Schema/SpecialOpenApi.ts +130 -0
  166. package/src/Schema/brand.ts +9 -1
  167. package/src/Schema/email.ts +7 -2
  168. package/src/Schema/ext.ts +205 -86
  169. package/src/Schema/moreStrings.ts +22 -20
  170. package/src/Schema/numbers.ts +14 -16
  171. package/src/Schema/phoneNumber.ts +5 -1
  172. package/src/Schema/strings.ts +4 -8
  173. package/src/Schema.ts +272 -99
  174. package/src/client/apiClientFactory.ts +107 -113
  175. package/src/client/clientFor.ts +6 -1
  176. package/src/client/errors.ts +42 -17
  177. package/src/client/makeClient.ts +156 -63
  178. package/src/http/Request.ts +7 -4
  179. package/src/ids.ts +2 -1
  180. package/src/index.ts +5 -10
  181. package/src/middleware.ts +7 -9
  182. package/src/rpc/MiddlewareMaker.ts +36 -47
  183. package/src/rpc/README.md +2 -2
  184. package/src/rpc/RpcContextMap.ts +6 -5
  185. package/src/rpc/RpcMiddleware.ts +5 -4
  186. package/src/rpc.ts +0 -1
  187. package/src/transform.ts +2 -2
  188. package/src/utils/gen.ts +1 -1
  189. package/src/utils/logger.ts +2 -2
  190. package/src/utils.ts +47 -11
  191. package/test/dist/rpc.test.d.ts.map +1 -1
  192. package/test/dist/secretURL.test.d.ts.map +1 -0
  193. package/test/dist/special.test.d.ts.map +1 -0
  194. package/test/rpc.test.ts +38 -6
  195. package/test/schema.test.ts +609 -4
  196. package/test/secretURL.test.ts +157 -0
  197. package/test/special.test.ts +1023 -0
  198. package/test/utils.test.ts +6 -6
  199. package/tsconfig.base.json +3 -4
  200. package/tsconfig.json +0 -1
  201. package/tsconfig.json.bak +2 -2
  202. package/tsconfig.src.json +29 -29
  203. package/tsconfig.test.json +2 -2
  204. package/dist/ServiceMap.d.ts +0 -44
  205. package/dist/ServiceMap.d.ts.map +0 -1
  206. package/dist/ServiceMap.js +0 -91
@@ -9,9 +9,8 @@ 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
- title: "NonEmptyString",
15
14
  jsonSchema: {}
16
15
  }),
17
16
  withDefaultMake
@@ -23,9 +22,8 @@ export const NonEmptyString64k = S
23
22
  .NonEmptyString
24
23
  .pipe(
25
24
  S.check(S.isMaxLength(64 * 1024)),
26
- fromBrand(nominal<NonEmptyString64k>(), {
25
+ fromBrand<NonEmptyString64k>(nominal<NonEmptyString64k>(), {
27
26
  identifier: "NonEmptyString64k",
28
- title: "NonEmptyString64k",
29
27
  jsonSchema: {}
30
28
  }),
31
29
  withDefaultMake
@@ -37,9 +35,8 @@ export const NonEmptyString2k = S
37
35
  .NonEmptyString
38
36
  .pipe(
39
37
  S.check(S.isMaxLength(2 * 1024)),
40
- fromBrand(nominal<NonEmptyString2k>(), {
38
+ fromBrand<NonEmptyString2k>(nominal<NonEmptyString2k>(), {
41
39
  identifier: "NonEmptyString2k",
42
- title: "NonEmptyString2k",
43
40
  jsonSchema: {}
44
41
  }),
45
42
  withDefaultMake
@@ -51,9 +48,8 @@ export const NonEmptyString255 = S
51
48
  .NonEmptyString
52
49
  .pipe(
53
50
  S.check(S.isMaxLength(255)),
54
- fromBrand(nominal<NonEmptyString255>(), {
51
+ fromBrand<NonEmptyString255>(nominal<NonEmptyString255>(), {
55
52
  identifier: "NonEmptyString255",
56
- title: "NonEmptyString255",
57
53
  jsonSchema: {}
58
54
  }),
59
55
  withDefaultMake
package/src/Schema.ts CHANGED
@@ -1,22 +1,19 @@
1
- import { Array, Option, pipe, SchemaAST, type Tracer } from "effect"
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
5
  import { Email as EmailT, type Email as EmailType } from "./Schema/email.js"
6
- import { withDefaultMake } from "./Schema/ext.js"
6
+ import { withDefaultMake, withDefaultParseOptions } from "./Schema/ext.js"
7
7
  import { PhoneNumber as PhoneNumberT, type PhoneNumber as PhoneNumberType } from "./Schema/phoneNumber.js"
8
- import type { AST } from "./Schema/schema.js"
9
- import { extendM } from "./utils.js"
8
+ import { copy, extendM, type StructuralCopyOrigin } 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
- export { Class, TaggedClass } from "./Schema/Class.js"
13
+ export { Class, ErrorClass, Opaque, TaggedClass, TaggedErrorClass } from "./Schema/Class.js"
17
14
 
18
15
  export { fromBrand, nominal } from "./Schema/brand.js"
19
- export { Array, Boolean, Date, Literal, Map, NullOr, Number, ReadonlyMap, ReadonlySet } from "./Schema/ext.js"
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,14 +22,153 @@ 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
 
31
30
  export * as SchemaIssue from "effect/SchemaIssue"
32
- export * as SchemaParser from "effect/SchemaParser"
31
+
32
+ export const decodeEffectConcurrently: typeof S.decodeEffect = withDefaultParseOptions(S.decodeEffect)
33
+ export const decodeUnknownEffectConcurrently: typeof S.decodeUnknownEffect = withDefaultParseOptions(
34
+ S.decodeUnknownEffect
35
+ )
36
+ export * as SchemaParser from "./Schema/SchemaParser.js"
33
37
 
34
38
  export { Void as Void_ } from "effect/Schema"
35
39
 
40
+ // ---------------------------------------------------------------------------
41
+ // Struct / NonEmptyArray / Record
42
+ // ---------------------------------------------------------------------------
43
+
44
+ type WithSchemaCopy<Self extends S.Top & { readonly Type: object }> = Self & {
45
+ readonly copy: StructuralCopyOrigin<Self["Type"]>
46
+ }
47
+
48
+ type OptionalMakeInput<Fields extends S.Struct.Fields> = {} extends S.Struct.MakeIn<Fields> ? {
49
+ make(input?: S.Struct.MakeIn<Fields>, options?: S.MakeOptions): S.Struct.Type<Fields>
50
+ }
51
+ : {}
52
+
53
+ export function Struct<const Fields extends S.Struct.Fields>(
54
+ fields: Fields
55
+ ): Struct<Fields> & OptionalMakeInput<Fields> {
56
+ const result = S.Struct(fields)
57
+ const allowVoidMake = (schema: any): any => {
58
+ // Normalize omitted input to an empty object so optional/default-only structs can be constructed with make().
59
+ const origMake: any = schema.make
60
+ const origMakeOption: any = schema.makeOption
61
+ const origMakeEffect: any = schema.makeEffect
62
+ schema.make = function(this: any, input: any, options?: any) {
63
+ return origMake.call(this, input === undefined ? {} : input, options)
64
+ }
65
+ schema.makeOption = function(this: any, input: any, options?: any) {
66
+ return origMakeOption.call(this, input === undefined ? {} : input, options)
67
+ }
68
+ schema.makeEffect = function(this: any, input: any, options?: any) {
69
+ return origMakeEffect.call(this, input === undefined ? {} : input, options)
70
+ }
71
+ return schema
72
+ }
73
+ // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment
74
+ const origMapFields: any = result.mapFields
75
+ // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment
76
+ const origAnnotate: any = result.annotate
77
+ // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment
78
+ const origAnnotateKey: any = result.annotateKey
79
+
80
+ const preserveCopyAndMethods = (schema: any): any => {
81
+ schema.copy = copy
82
+ schema.mapFields = function(this: any, f: any, options?: any) {
83
+ return (result as any).mapFields.call(this, f, options)
84
+ }
85
+ schema.annotate = function(this: any, annotations?: any) {
86
+ return (result as any).annotate.call(this, annotations)
87
+ }
88
+ schema.annotateKey = function(this: any, annotations?: any) {
89
+ return (result as any).annotateKey.call(this, annotations)
90
+ }
91
+ return allowVoidMake(schema)
92
+ }
93
+ ;(result as any).mapFields = function(this: any, f: any, options?: any) {
94
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
95
+ const mapped = origMapFields.call(this, f, options)
96
+ return preserveCopyAndMethods(mapped)
97
+ }
98
+ ;(result as any).annotate = function(this: any, annotations?: any) {
99
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
100
+ const annotated = origAnnotate.call(this, annotations)
101
+ return preserveCopyAndMethods(annotated)
102
+ }
103
+ ;(result as any).annotateKey = function(this: any, annotations?: any) {
104
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
105
+ const annotated = origAnnotateKey.call(this, annotations)
106
+ return preserveCopyAndMethods(annotated)
107
+ }
108
+ ;(result as any).copy = copy
109
+ allowVoidMake(result)
110
+ return result as Struct<Fields> & OptionalMakeInput<Fields>
111
+ }
112
+ export interface Struct<Fields extends S.Struct.Fields> extends WithSchemaCopy<S.Struct<Fields>> {
113
+ annotate(
114
+ annotations: S.Annotations.Bottom<S.Struct.Type<Fields>, S.Struct<Fields>["~type.parameters"]>
115
+ ): Struct<Fields>
116
+ annotateKey(annotations: S.Annotations.Key<S.Struct.Type<Fields>>): Struct<Fields>
117
+ mapFields<To extends S.Struct.Fields>(
118
+ f: (fields: Fields) => To,
119
+ options?: {
120
+ readonly unsafePreserveChecks?: boolean | undefined
121
+ }
122
+ ): Struct<Readonly<To>>
123
+ }
124
+ export declare namespace Struct {
125
+ export type Fields = S.Struct.Fields
126
+ export type Type<F extends S.Struct.Fields> = S.Struct.Type<F>
127
+ export type Encoded<F extends S.Struct.Fields> = S.Struct.Encoded<F>
128
+ export type DecodingServices<F extends S.Struct.Fields> = S.Struct.DecodingServices<F>
129
+ export type EncodingServices<F extends S.Struct.Fields> = S.Struct.EncodingServices<F>
130
+ export type MakeIn<F extends S.Struct.Fields> = S.Struct.MakeIn<F>
131
+ export type Iso<F extends S.Struct.Fields> = S.Struct.Iso<F>
132
+ }
133
+
134
+ export type StructNestedEncodedError<T> = {
135
+ readonly _tag: "StructNestedEncodedError"
136
+ readonly message: "Expected a Struct schema or a schema with from.Encoded"
137
+ readonly schema: T
138
+ }
139
+
140
+ export type StructNestedEncoded<T> = T extends { fields: infer Fields extends S.Struct.Fields } ? Struct.Encoded<Fields>
141
+ : T extends { readonly from: { readonly Encoded: infer Encoded } } ? Encoded
142
+ : StructNestedEncodedError<T>
143
+
144
+ export function NonEmptyArray<Value extends S.Top>(value: Value): S.NonEmptyArray<Value> {
145
+ return S.NonEmptyArray(value)
146
+ }
147
+
148
+ export function TaggedStruct<const Tag extends SchemaAST.LiteralValue, const Fields extends S.Struct.Fields>(
149
+ value: Tag,
150
+ fields: Fields
151
+ ): TaggedStruct<Tag, Fields> {
152
+ return Struct({ _tag: S.tag(value), ...fields }) as any
153
+ }
154
+ export type TaggedStruct<Tag extends SchemaAST.LiteralValue, Fields extends S.Struct.Fields> =
155
+ & WithSchemaCopy<
156
+ S.TaggedStruct<Tag, Fields>
157
+ >
158
+ & OptionalMakeInput<Readonly<{ readonly _tag: S.tag<Tag> } & Fields>>
159
+
160
+ export function Record<Key extends S.Record.Key, Value extends S.Top>(
161
+ key: Key,
162
+ value: Value
163
+ ): S.$Record<Key, Value> {
164
+ return S.Record(key, value)
165
+ }
166
+ export declare namespace Record {
167
+ export type Key = S.Record.Key
168
+ export type Type<K extends S.Record.Key, V extends S.Top> = S.Record.Type<K, V>
169
+ export type Encoded<K extends S.Record.Key, V extends S.Top> = S.Record.Encoded<K, V>
170
+ }
171
+
36
172
  export const SpanId = Symbol()
37
173
  export type SpanId = typeof SpanId
38
174
 
@@ -66,104 +202,141 @@ export const PhoneNumber = PhoneNumberT
66
202
 
67
203
  export type PhoneNumber = PhoneNumberType
68
204
 
69
- export const makeIs = <A extends { _tag: string }, I, R>(
70
- schema: S.Codec<A, I, R>
71
- ) => {
72
- // In v4, transformations are stored as encoding on nodes, not as wrapper nodes.
73
- // Union member ASTs are directly Objects (TypeLiteral equivalent).
74
- if (SchemaAST.isUnion(schema.ast)) {
75
- return schema.ast.types.reduce((acc, t: AST.AST) => {
76
- if (!SchemaAST.isObjects(t)) return acc
77
- const tag = Array.findFirst(t.propertySignatures, (_: any) => {
78
- if (_.name === "_tag" && SchemaAST.isLiteral(_.type)) {
79
- return Option.some(_.type)
80
- }
81
- return Option.none()
82
- })
83
- const ast = Option.getOrUndefined(tag)
84
- if (!ast) {
85
- return acc
86
- }
87
- return {
88
- ...acc,
89
- [String((ast as SchemaAST.Literal).literal)]: (x: { _tag: string }) =>
90
- x._tag === (ast as SchemaAST.Literal).literal
91
- }
92
- }, {} as Is<A>)
93
- }
94
- throw new Error("Unsupported")
205
+ // Copied from SchemaAST.collectSentinels (marked @internal in effect).
206
+ // Returns all { key, literal } pairs that can discriminate a union member.
207
+ const getTagFromAST = (schema: S.Top): string => {
208
+ const sentinels = collectSentinelsFromAST(schema.ast)
209
+ const sentinel = sentinels.find((s) => s.key === "_tag")
210
+ if (sentinel !== undefined && typeof sentinel.literal === "string") return sentinel.literal
211
+ throw new Error("No _tag literal found on schema member")
95
212
  }
96
213
 
97
- export const makeIsAnyOf = <A extends { _tag: string }, I, R>(
98
- schema: S.Codec<A, I, R>
99
- ): IsAny<A> => {
100
- if (SchemaAST.isUnion(schema.ast)) {
101
- return <Keys extends A["_tag"][]>(...keys: Keys) => (a: A): a is ExtractUnion<A, ElemType<Keys>> =>
102
- keys.includes(a._tag)
214
+ function collectSentinelsFromAST(
215
+ ast: SchemaAST.AST
216
+ ): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> {
217
+ switch (ast._tag) {
218
+ case "Declaration": {
219
+ const s = ast.annotations?.["~sentinels"]
220
+ return Array.isArray(s) ? s : []
221
+ }
222
+ case "Objects":
223
+ return ast.propertySignatures.flatMap(
224
+ (ps): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> => {
225
+ const type = ps.type
226
+ if (!SchemaAST.isOptional(type)) {
227
+ if (SchemaAST.isLiteral(type)) return [{ key: ps.name, literal: type.literal }]
228
+ if (SchemaAST.isUniqueSymbol(type)) return [{ key: ps.name, literal: type.symbol }]
229
+ }
230
+ return []
231
+ }
232
+ )
233
+ case "Suspend":
234
+ return collectSentinelsFromAST(ast.thunk())
235
+ default:
236
+ return []
103
237
  }
104
- throw new Error("Unsupported")
105
238
  }
106
239
 
107
- export type ExtractUnion<A extends { _tag: string }, Tags extends A["_tag"]> = Extract<A, Record<"_tag", Tags>>
108
- export type Is<A extends { _tag: string }> = { [K in A as K["_tag"]]: (a: A) => a is K }
109
- export type ElemType<A> = A extends Array<infer E> ? E : never
110
- export interface IsAny<A extends { _tag: string }> {
111
- <Keys extends A["_tag"][]>(...keys: Keys): (a: A) => a is ExtractUnion<A, ElemType<Keys>>
112
- }
113
-
114
- export const taggedUnionMap = <
115
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
116
- Members extends readonly (S.Top & { fields: { _tag: S.tag<string> } })[]
117
- >(
118
- self: Members
119
- ) =>
120
- self.reduce((acc, key) => {
121
- // TODO: v4 migration — PropertySignatureDeclaration removed, need v4 AST traversal
122
- const ast = key.fields._tag.ast as any
123
- 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
124
- acc[tag] = key as any
125
- return acc
126
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
- }, {} as any)
128
-
129
240
  export const tags = <
130
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
131
- Members extends NonEmptyReadonlyArray<(S.Top & { fields: { _tag: S.tag<string> } })>
241
+ Members extends NonEmptyReadonlyArray<(S.Top & { readonly Type: { readonly _tag: string } })>
132
242
  >(
133
243
  self: Members
134
244
  ) =>
135
- S.Literals(self.map((key) => {
136
- // TODO: v4 migration — PropertySignatureDeclaration removed, need v4 AST traversal
137
- const ast = key.fields._tag.ast as any
138
- const tag = ((ast.type ?? ast) as SchemaAST.Literal).literal
139
- return tag
140
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
- })) as any
142
-
143
- export const ExtendTaggedUnion = <A extends { _tag: string }, I, R>(
144
- schema: S.Codec<A, I, R>
145
- ) =>
146
- extendM(
147
- schema,
148
- (_) => ({
149
- is: S.is(schema as any),
150
- isA: makeIs(_ as any),
151
- isAnyOf: makeIsAnyOf(_ as any) /*, map: taggedUnionMap(a) */
152
- })
153
- )
245
+ S.Literals(
246
+ self.map(getTagFromAST) as {
247
+ [Index in keyof Members]: Members[Index]["Type"]["_tag"]
248
+ }
249
+ ) as S.Literals<
250
+ {
251
+ [Index in keyof Members]: Members[Index]["Type"]["_tag"]
252
+ }
253
+ >
254
+
255
+ type TaggedUnionMembers = NonEmptyReadonlyArray<
256
+ S.Top & { readonly Type: { readonly _tag: string } }
257
+ >
258
+
259
+ type TaggedUnionTags<Members extends TaggedUnionMembers> = S.Literals<
260
+ {
261
+ [Index in keyof Members]: Members[Index]["Type"]["_tag"]
262
+ }
263
+ >
264
+
265
+ type TaggedPropertyKeys<A, Members extends TaggedUnionMembers> = {
266
+ [K in keyof A & string]: A[K] extends Members[number]["Type"] ? K : never
267
+ }[keyof A & string]
268
+
269
+ type PropertyGuardsFor<
270
+ Members extends TaggedUnionMembers,
271
+ K extends string,
272
+ A
273
+ > =
274
+ & {
275
+ readonly [M in Members[number] as `is${M["Type"]["_tag"]}`]: (
276
+ target: A
277
+ ) => target is A & { readonly [P in K]: M["Type"] }
278
+ }
279
+ & {
280
+ readonly isAnyOf: <const Tags extends ReadonlyArray<Members[number]["Type"]["_tag"]>>(
281
+ tags: Tags
282
+ ) => (
283
+ target: A
284
+ ) => target is A & { readonly [P in K]: Extract<Members[number]["Type"], { readonly _tag: Tags[number] }> }
285
+ }
286
+
287
+ type PropertyGuards<
288
+ Members extends TaggedUnionMembers,
289
+ K extends string
290
+ > =
291
+ & {
292
+ readonly [M in Members[number] as `is${M["Type"]["_tag"]}`]: <
293
+ T extends { readonly [P in K]: Members[number]["Type"] }
294
+ >(target: T) => target is T & { readonly [P in K]: M["Type"] }
295
+ }
296
+ & {
297
+ readonly isAnyOf: <const Tags extends ReadonlyArray<Members[number]["Type"]["_tag"]>>(
298
+ tags: Tags
299
+ ) => <T extends { readonly [P in K]: Members[number]["Type"] }>(
300
+ target: T
301
+ ) => target is T & { readonly [P in K]: Extract<Members[number]["Type"], { readonly _tag: Tags[number] }> }
302
+ }
303
+
304
+ type TaggedUnionWithTags<Members extends TaggedUnionMembers> = S.toTaggedUnion<"_tag", Members> & {
305
+ readonly tags: TaggedUnionTags<Members>
306
+ readonly generateGuards: <K extends string>(property: K) => PropertyGuards<Members, K>
307
+ readonly generateGuardsFor: <A>() => <K extends TaggedPropertyKeys<A, Members>>(
308
+ property: K
309
+ ) => PropertyGuardsFor<Members, K, A>
310
+ }
311
+
312
+ const extendTaggedUnionWithTags = <Members extends TaggedUnionMembers>(
313
+ schema: S.Union<Members>
314
+ ): TaggedUnionWithTags<Members> =>
315
+ extendM(schema.pipe(S.toTaggedUnion("_tag")), (tagged) => {
316
+ const makeGuards = (property: string) => {
317
+ const result: any = {}
318
+ const guards: Record<string, (u: unknown) => boolean> = tagged.guards
319
+ for (const tag of Object.keys(guards)) {
320
+ const guard = guards[tag]!
321
+ result[`is${tag}`] = (target: any) => guard(target[property])
322
+ }
323
+ result.isAnyOf = (memberTags: Array<string>) => {
324
+ const check = tagged.isAnyOf(memberTags)
325
+ return (target: any) => check(target[property])
326
+ }
327
+ return result
328
+ }
329
+ return {
330
+ tags: tags(schema.members),
331
+ generateGuards: makeGuards,
332
+ generateGuardsFor: () => makeGuards
333
+ }
334
+ })
335
+
336
+ export const ExtendTaggedUnion = <Members extends TaggedUnionMembers>(
337
+ schema: S.Union<Members>
338
+ ): TaggedUnionWithTags<Members> => extendTaggedUnionWithTags(schema)
154
339
 
155
340
  export const TaggedUnion = <
156
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
157
- Members extends readonly (S.Top & { fields: { _tag: S.tag<any> } })[]
158
- >(...a: Members) =>
159
- pipe(
160
- S.Union(a),
161
- (_) =>
162
- extendM(_, (_) => ({
163
- is: S.is(_ as any),
164
- isA: makeIs(_ as any),
165
- isAnyOf: makeIsAnyOf(_ as any),
166
- tagMap: taggedUnionMap(a),
167
- tags: tags(a as any)
168
- }))
169
- )
341
+ Members extends TaggedUnionMembers
342
+ >(members: Members): TaggedUnionWithTags<Members> => extendTaggedUnionWithTags(S.Union(members))