effect-app 4.0.0-beta.20 → 4.0.0-beta.201

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 (221) hide show
  1. package/CHANGELOG.md +888 -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 +7 -6
  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/Option.d.ts +1 -1
  31. package/dist/Option.d.ts.map +1 -1
  32. package/dist/Pure.d.ts +5 -5
  33. package/dist/Pure.d.ts.map +1 -1
  34. package/dist/Pure.js +13 -13
  35. package/dist/Schema/Class.d.ts +66 -20
  36. package/dist/Schema/Class.d.ts.map +1 -1
  37. package/dist/Schema/Class.js +189 -22
  38. package/dist/Schema/FastCheck.d.ts +1 -1
  39. package/dist/Schema/FastCheck.d.ts.map +1 -1
  40. package/dist/Schema/Methods.d.ts +1 -1
  41. package/dist/Schema/SchemaParser.d.ts +5 -0
  42. package/dist/Schema/SchemaParser.d.ts.map +1 -0
  43. package/dist/Schema/SchemaParser.js +6 -0
  44. package/dist/Schema/SpecialJsonSchema.d.ts +33 -0
  45. package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
  46. package/dist/Schema/SpecialJsonSchema.js +122 -0
  47. package/dist/Schema/SpecialOpenApi.d.ts +32 -0
  48. package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
  49. package/dist/Schema/SpecialOpenApi.js +123 -0
  50. package/dist/Schema/brand.d.ts +7 -2
  51. package/dist/Schema/brand.d.ts.map +1 -1
  52. package/dist/Schema/brand.js +1 -1
  53. package/dist/Schema/email.d.ts +1 -1
  54. package/dist/Schema/email.d.ts.map +1 -1
  55. package/dist/Schema/email.js +7 -4
  56. package/dist/Schema/ext.d.ts +117 -45
  57. package/dist/Schema/ext.d.ts.map +1 -1
  58. package/dist/Schema/ext.js +131 -42
  59. package/dist/Schema/moreStrings.d.ts +111 -11
  60. package/dist/Schema/moreStrings.d.ts.map +1 -1
  61. package/dist/Schema/moreStrings.js +14 -15
  62. package/dist/Schema/numbers.d.ts +127 -15
  63. package/dist/Schema/numbers.d.ts.map +1 -1
  64. package/dist/Schema/numbers.js +10 -12
  65. package/dist/Schema/phoneNumber.d.ts +1 -1
  66. package/dist/Schema/phoneNumber.d.ts.map +1 -1
  67. package/dist/Schema/phoneNumber.js +6 -3
  68. package/dist/Schema/schema.d.ts +1 -1
  69. package/dist/Schema/strings.d.ts +37 -5
  70. package/dist/Schema/strings.d.ts.map +1 -1
  71. package/dist/Schema/strings.js +1 -5
  72. package/dist/Schema.d.ts +154 -56
  73. package/dist/Schema.d.ts.map +1 -1
  74. package/dist/Schema.js +131 -64
  75. package/dist/Set.d.ts +1 -1
  76. package/dist/Set.d.ts.map +1 -1
  77. package/dist/TypeTest.d.ts +1 -1
  78. package/dist/Types.d.ts +1 -1
  79. package/dist/Widen.type.d.ts +1 -1
  80. package/dist/_ext/Array.d.ts +1 -1
  81. package/dist/_ext/Array.d.ts.map +1 -1
  82. package/dist/_ext/date.d.ts +1 -1
  83. package/dist/_ext/misc.d.ts +1 -1
  84. package/dist/_ext/ord.ext.d.ts +1 -1
  85. package/dist/_ext/ord.ext.d.ts.map +1 -1
  86. package/dist/builtin.d.ts +1 -1
  87. package/dist/builtin.d.ts.map +1 -1
  88. package/dist/client/InvalidationKeys.d.ts +29 -0
  89. package/dist/client/InvalidationKeys.d.ts.map +1 -0
  90. package/dist/client/InvalidationKeys.js +33 -0
  91. package/dist/client/apiClientFactory.d.ts +18 -32
  92. package/dist/client/apiClientFactory.d.ts.map +1 -1
  93. package/dist/client/apiClientFactory.js +95 -32
  94. package/dist/client/clientFor.d.ts +61 -17
  95. package/dist/client/clientFor.d.ts.map +1 -1
  96. package/dist/client/clientFor.js +9 -1
  97. package/dist/client/errors.d.ts +49 -25
  98. package/dist/client/errors.d.ts.map +1 -1
  99. package/dist/client/errors.js +43 -17
  100. package/dist/client/makeClient.d.ts +468 -32
  101. package/dist/client/makeClient.d.ts.map +1 -1
  102. package/dist/client/makeClient.js +62 -23
  103. package/dist/client.d.ts +2 -1
  104. package/dist/client.d.ts.map +1 -1
  105. package/dist/client.js +2 -1
  106. package/dist/faker.d.ts +1 -1
  107. package/dist/faker.d.ts.map +1 -1
  108. package/dist/http/Request.d.ts +2 -2
  109. package/dist/http/Request.d.ts.map +1 -1
  110. package/dist/http/Request.js +5 -5
  111. package/dist/http/internal/lib.d.ts +1 -1
  112. package/dist/http.d.ts +1 -1
  113. package/dist/ids.d.ts +3 -3
  114. package/dist/ids.d.ts.map +1 -1
  115. package/dist/ids.js +3 -2
  116. package/dist/index.d.ts +5 -8
  117. package/dist/index.d.ts.map +1 -1
  118. package/dist/index.js +6 -8
  119. package/dist/logger.d.ts +1 -1
  120. package/dist/middleware.d.ts +14 -8
  121. package/dist/middleware.d.ts.map +1 -1
  122. package/dist/middleware.js +14 -8
  123. package/dist/rpc/Invalidation.d.ts +402 -0
  124. package/dist/rpc/Invalidation.d.ts.map +1 -0
  125. package/dist/rpc/Invalidation.js +150 -0
  126. package/dist/rpc/MiddlewareMaker.d.ts +5 -4
  127. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  128. package/dist/rpc/MiddlewareMaker.js +57 -37
  129. package/dist/rpc/RpcContextMap.d.ts +3 -3
  130. package/dist/rpc/RpcContextMap.d.ts.map +1 -1
  131. package/dist/rpc/RpcContextMap.js +4 -4
  132. package/dist/rpc/RpcMiddleware.d.ts +5 -4
  133. package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
  134. package/dist/rpc/RpcMiddleware.js +1 -1
  135. package/dist/rpc.d.ts +2 -2
  136. package/dist/rpc.d.ts.map +1 -1
  137. package/dist/rpc.js +2 -2
  138. package/dist/transform.d.ts +1 -1
  139. package/dist/transform.d.ts.map +1 -1
  140. package/dist/transform.js +3 -3
  141. package/dist/utils/effectify.d.ts +1 -1
  142. package/dist/utils/extend.d.ts +1 -1
  143. package/dist/utils/extend.d.ts.map +1 -1
  144. package/dist/utils/gen.d.ts +2 -2
  145. package/dist/utils/gen.d.ts.map +1 -1
  146. package/dist/utils/logLevel.d.ts +2 -2
  147. package/dist/utils/logLevel.d.ts.map +1 -1
  148. package/dist/utils/logger.d.ts +3 -3
  149. package/dist/utils/logger.d.ts.map +1 -1
  150. package/dist/utils/logger.js +3 -3
  151. package/dist/utils.d.ts +31 -11
  152. package/dist/utils.d.ts.map +1 -1
  153. package/dist/utils.js +13 -7
  154. package/dist/validation/validators.d.ts +1 -1
  155. package/dist/validation/validators.d.ts.map +1 -1
  156. package/dist/validation.d.ts +1 -1
  157. package/dist/validation.d.ts.map +1 -1
  158. package/package.json +46 -24
  159. package/src/Config/SecretURL.ts +2 -1
  160. package/src/Config.ts +14 -0
  161. package/src/ConfigProvider.ts +48 -0
  162. package/src/{ServiceMap.ts → Context.ts} +52 -59
  163. package/src/Effect.ts +12 -14
  164. package/src/Layer.ts +6 -5
  165. package/src/Pure.ts +17 -18
  166. package/src/Schema/Class.ts +268 -62
  167. package/src/Schema/SchemaParser.ts +12 -0
  168. package/src/Schema/SpecialJsonSchema.ts +137 -0
  169. package/src/Schema/SpecialOpenApi.ts +130 -0
  170. package/src/Schema/brand.ts +9 -1
  171. package/src/Schema/email.ts +7 -2
  172. package/src/Schema/ext.ts +204 -72
  173. package/src/Schema/moreStrings.ts +22 -20
  174. package/src/Schema/numbers.ts +14 -16
  175. package/src/Schema/phoneNumber.ts +5 -1
  176. package/src/Schema/strings.ts +4 -8
  177. package/src/Schema.ts +332 -105
  178. package/src/client/InvalidationKeys.ts +50 -0
  179. package/src/client/apiClientFactory.ts +220 -129
  180. package/src/client/clientFor.ts +97 -29
  181. package/src/client/errors.ts +52 -26
  182. package/src/client/makeClient.ts +538 -68
  183. package/src/client.ts +1 -0
  184. package/src/http/Request.ts +7 -4
  185. package/src/ids.ts +2 -1
  186. package/src/index.ts +5 -10
  187. package/src/middleware.ts +13 -9
  188. package/src/rpc/Invalidation.ts +226 -0
  189. package/src/rpc/MiddlewareMaker.ts +65 -60
  190. package/src/rpc/README.md +2 -2
  191. package/src/rpc/RpcContextMap.ts +6 -5
  192. package/src/rpc/RpcMiddleware.ts +5 -4
  193. package/src/rpc.ts +1 -1
  194. package/src/transform.ts +2 -2
  195. package/src/utils/gen.ts +1 -1
  196. package/src/utils/logger.ts +2 -2
  197. package/src/utils.ts +51 -15
  198. package/test/dist/rpc.test.d.ts.map +1 -1
  199. package/test/dist/secretURL.test.d.ts.map +1 -0
  200. package/test/dist/special.test.d.ts.map +1 -0
  201. package/test/dist/stream-error.types.d.ts +2 -0
  202. package/test/dist/stream-error.types.d.ts.map +1 -0
  203. package/test/dist/stream-error.types.js +27 -0
  204. package/test/rpc.test.ts +41 -6
  205. package/test/schema.test.ts +591 -17
  206. package/test/secretURL.test.ts +157 -0
  207. package/test/special.test.ts +1023 -0
  208. package/test/utils.test.ts +6 -6
  209. package/tsconfig.base.json +3 -4
  210. package/tsconfig.json +0 -1
  211. package/tsconfig.json.bak +2 -2
  212. package/tsconfig.src.json +29 -29
  213. package/tsconfig.test.json +2 -2
  214. package/dist/Operations.d.ts +0 -87
  215. package/dist/Operations.d.ts.map +0 -1
  216. package/dist/Operations.js +0 -29
  217. package/dist/ServiceMap.d.ts +0 -44
  218. package/dist/ServiceMap.d.ts.map +0 -1
  219. package/dist/ServiceMap.js +0 -91
  220. package/eslint.config.mjs +0 -26
  221. package/src/Operations.ts +0 -55
@@ -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,22 @@
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
+ import { type Simplify } from "effect/Struct"
4
+ import type { RequiredKeys } from "effect/Types"
3
5
  import type { NonEmptyReadonlyArray } from "./Array.js"
4
6
  import { fakerArb } from "./faker.js"
5
7
  import { Email as EmailT, type Email as EmailType } from "./Schema/email.js"
6
- import { withDefaultMake } from "./Schema/ext.js"
8
+ import { concurrencyUnbounded, withDefaultMake, withDefaultParseOptions } from "./Schema/ext.js"
7
9
  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"
10
+ import { type AST } from "./Schema/schema.js"
11
+ import { copy, extendM, type StructuralCopyOrigin } from "./utils.js"
10
12
 
11
13
  export * from "effect/Schema"
12
- // v4: TaggedError renamed to TaggedErrorClass
13
- export { TaggedErrorClass as TaggedError } from "effect/Schema"
14
14
 
15
15
  export * from "./Schema/Class.js"
16
- export { Class, TaggedClass } from "./Schema/Class.js"
16
+ export { Class, ErrorClass, Opaque, TaggedClass, TaggedErrorClass } from "./Schema/Class.js"
17
17
 
18
18
  export { fromBrand, nominal } from "./Schema/brand.js"
19
- export { Array, Boolean, Date, Literal, Map, NullOr, Number, ReadonlyMap, ReadonlySet } from "./Schema/ext.js"
19
+ export { Array, Boolean, Date, DateFromString, DateValid, Finite, Literals, NullOr, Number, ReadonlyMap, ReadonlySet } from "./Schema/ext.js"
20
20
  export { Int, NonNegativeInt } from "./Schema/numbers.js"
21
21
 
22
22
  export * from "./Schema/email.js"
@@ -25,14 +25,217 @@ export * from "./Schema/moreStrings.js"
25
25
  export * from "./Schema/numbers.js"
26
26
  export * from "./Schema/phoneNumber.js"
27
27
  export * from "./Schema/schema.js"
28
+ export * from "./Schema/SpecialJsonSchema.js"
29
+ export * from "./Schema/SpecialOpenApi.js"
28
30
  export * from "./Schema/strings.js"
29
31
  export { NonEmptyString } from "./Schema/strings.js"
30
32
 
31
33
  export * as SchemaIssue from "effect/SchemaIssue"
32
- export * as SchemaParser from "effect/SchemaParser"
34
+
35
+ export const decodeEffectConcurrently: typeof S.decodeEffect = withDefaultParseOptions(S.decodeEffect)
36
+ export const decodeUnknownEffectConcurrently: typeof S.decodeUnknownEffect = withDefaultParseOptions(
37
+ S.decodeUnknownEffect
38
+ )
39
+ export * as SchemaParser from "./Schema/SchemaParser.js"
33
40
 
34
41
  export { Void as Void_ } from "effect/Schema"
35
42
 
43
+ // ---------------------------------------------------------------------------
44
+ // Struct / NonEmptyArray / Record
45
+ // ---------------------------------------------------------------------------
46
+
47
+ export function Struct<const Fields extends S.Struct.Fields>(
48
+ fields: Fields
49
+ ): Struct<Fields> {
50
+ const result = S.Struct(fields).annotate(concurrencyUnbounded)
51
+ const allowVoidMake = (schema: any): any => {
52
+ // Normalize omitted input to an empty object so optional/default-only structs can be constructed with make().
53
+ const origMake: any = schema.make
54
+ const origMakeOption: any = schema.makeOption
55
+ const origMakeEffect: any = schema.makeEffect
56
+ schema.make = function(this: any, input: any, options?: any) {
57
+ return origMake.call(this, input === undefined ? {} : input, options)
58
+ }
59
+ schema.makeOption = function(this: any, input: any, options?: any) {
60
+ return origMakeOption.call(this, input === undefined ? {} : input, options)
61
+ }
62
+ schema.makeEffect = function(this: any, input: any, options?: any) {
63
+ return origMakeEffect.call(this, input === undefined ? {} : input, options)
64
+ }
65
+ return schema
66
+ }
67
+ // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment
68
+ const origMapFields: any = result.mapFields
69
+ // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment
70
+ const origAnnotate: any = result.annotate
71
+ // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment
72
+ const origAnnotateKey: any = result.annotateKey
73
+
74
+ const preserveCopyAndMethods = (schema: any): any => {
75
+ schema.copy = copy
76
+ schema.mapFields = function(this: any, f: any, options?: any) {
77
+ return (result as any).mapFields.call(this, f, options)
78
+ }
79
+ schema.annotate = function(this: any, annotations?: any) {
80
+ return (result as any).annotate.call(this, annotations)
81
+ }
82
+ schema.annotateKey = function(this: any, annotations?: any) {
83
+ return (result as any).annotateKey.call(this, annotations)
84
+ }
85
+ return allowVoidMake(schema)
86
+ }
87
+ ;(result as any).mapFields = function(this: any, f: any, options?: any) {
88
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
89
+ const mapped = origMapFields.call(this, f, options).annotate(concurrencyUnbounded)
90
+ return preserveCopyAndMethods(mapped)
91
+ }
92
+ ;(result as any).annotate = function(this: any, annotations?: any) {
93
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
94
+ const annotated = origAnnotate.call(this, annotations)
95
+ return preserveCopyAndMethods(annotated)
96
+ }
97
+ ;(result as any).annotateKey = function(this: any, annotations?: any) {
98
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
99
+ const annotated = origAnnotateKey.call(this, annotations)
100
+ return preserveCopyAndMethods(annotated)
101
+ }
102
+ ;(result as any).copy = copy
103
+ allowVoidMake(result)
104
+ return result as Struct<Fields>
105
+ }
106
+
107
+ export interface Struct<Fields extends S.Struct.Fields> extends
108
+ S.Bottom<
109
+ Struct.Type<Fields>,
110
+ Struct.Encoded<Fields>,
111
+ Struct.DecodingServices<Fields>,
112
+ Struct.EncodingServices<Fields>,
113
+ AST.Objects,
114
+ // Rebuild is what's returned from annotate etc
115
+ Struct<Fields>,
116
+ Struct.MakeIn<Fields>,
117
+ Struct.Iso<Fields>
118
+ >
119
+ {
120
+ /**
121
+ * The field definitions of this struct. Spread them into a new struct to
122
+ * reuse fields across schemas.
123
+ *
124
+ * **Example** (Reusing fields across structs)
125
+ *
126
+ * ```ts
127
+ * import { Schema } from "effect"
128
+ *
129
+ * const Timestamped = Schema.Struct({
130
+ * createdAt: Schema.Date,
131
+ * updatedAt: Schema.Date
132
+ * })
133
+ *
134
+ * const User = Schema.Struct({
135
+ * ...Timestamped.fields,
136
+ * name: Schema.String,
137
+ * email: Schema.String
138
+ * })
139
+ * ```
140
+ */
141
+ readonly fields: Fields
142
+ /**
143
+ * Returns a new struct with the fields modified by the provided function.
144
+ *
145
+ * **Options**
146
+ *
147
+ * - `unsafePreserveChecks` - if `true`, keep any `.check(...)` constraints
148
+ * that were attached to the original union. Defaults to `false`.
149
+ *
150
+ * **Warning**: This is an unsafe operation. Since `mapFields`
151
+ * transformations change the schema type, the original refinement functions
152
+ * may no longer be valid or safe to apply to the transformed schema. Only
153
+ * use this option if you have verified that your refinements remain correct
154
+ * after the transformation.
155
+ */
156
+ mapFields<To extends Struct.Fields>(
157
+ f: (fields: Fields) => To,
158
+ options?: {
159
+ readonly unsafePreserveChecks?: boolean | undefined
160
+ } | undefined
161
+ ): Struct<Simplify<Readonly<To>>>
162
+
163
+ // added copy
164
+ readonly copy: StructuralCopyOrigin<Struct.Type<Fields>>
165
+ }
166
+
167
+ export declare namespace Struct {
168
+ export type Fields = S.Struct.Fields
169
+ export type Type<F extends S.Struct.Fields> = S.Struct.Type<F>
170
+ export type Encoded<F extends S.Struct.Fields> = S.Struct.Encoded<F>
171
+ export type DecodingServices<F extends S.Struct.Fields> = S.Struct.DecodingServices<F>
172
+ export type EncodingServices<F extends S.Struct.Fields> = S.Struct.EncodingServices<F>
173
+ // changed; all optional allows void
174
+ export type MakeIn<F extends S.Struct.Fields> = RequiredKeys<S.Struct.MakeIn<F>> extends never
175
+ ? void | S.Struct.MakeIn<F>
176
+ : S.Struct.MakeIn<F>
177
+ export type Iso<F extends S.Struct.Fields> = S.Struct.Iso<F>
178
+ }
179
+
180
+ export type StructNestedEncodedError<T> = {
181
+ readonly _tag: "StructNestedEncodedError"
182
+ readonly message: "Expected a Struct schema or a schema with from.Encoded"
183
+ readonly schema: T
184
+ }
185
+
186
+ export type StructNestedEncoded<T> = T extends { fields: infer Fields extends S.Struct.Fields } ? Struct.Encoded<Fields>
187
+ : T extends { readonly from: { readonly Encoded: infer Encoded } } ? Encoded
188
+ : StructNestedEncodedError<T>
189
+
190
+ export function NonEmptyArray<Value extends S.Top>(value: Value): S.NonEmptyArray<Value> {
191
+ return S.NonEmptyArray(value).annotate(concurrencyUnbounded)
192
+ }
193
+
194
+ export function TaggedStruct<const Tag extends SchemaAST.LiteralValue, const Fields extends S.Struct.Fields>(
195
+ value: Tag,
196
+ fields: Fields
197
+ ): TaggedStruct<Tag, Fields> {
198
+ return Struct({ _tag: S.tag(value), ...fields }) as any
199
+ }
200
+ export interface TaggedStruct<Tag extends SchemaAST.LiteralValue, Fields extends S.Struct.Fields>
201
+ extends Struct<{ readonly _tag: S.tag<Tag> } & Fields>
202
+ {}
203
+ export declare namespace TaggedStruct {
204
+ export type Fields = S.Struct.Fields
205
+ export type Type<Tag extends SchemaAST.LiteralValue, F extends S.Struct.Fields> = S.Struct.Type<
206
+ { readonly _tag: S.tag<Tag> } & F
207
+ >
208
+ export type Encoded<Tag extends SchemaAST.LiteralValue, F extends S.Struct.Fields> = S.Struct.Encoded<
209
+ { readonly _tag: S.tag<Tag> } & F
210
+ >
211
+ export type DecodingServices<Tag extends SchemaAST.LiteralValue, F extends S.Struct.Fields> =
212
+ S.Struct.DecodingServices<
213
+ { readonly _tag: S.tag<Tag> } & F
214
+ >
215
+ export type EncodingServices<Tag extends SchemaAST.LiteralValue, F extends S.Struct.Fields> =
216
+ S.Struct.EncodingServices<
217
+ { readonly _tag: S.tag<Tag> } & F
218
+ >
219
+ export type MakeIn<Tag extends SchemaAST.LiteralValue, F extends S.Struct.Fields> = S.Struct.MakeIn<
220
+ { readonly _tag: S.tag<Tag> } & F
221
+ >
222
+ export type Iso<Tag extends SchemaAST.LiteralValue, F extends S.Struct.Fields> = S.Struct.Iso<
223
+ { readonly _tag: S.tag<Tag> } & F
224
+ >
225
+ }
226
+
227
+ export function Record<Key extends S.Record.Key, Value extends S.Top>(
228
+ key: Key,
229
+ value: Value
230
+ ): S.$Record<Key, Value> {
231
+ return S.Record(key, value).annotate(concurrencyUnbounded)
232
+ }
233
+ export declare namespace Record {
234
+ export type Key = S.Record.Key
235
+ export type Type<K extends S.Record.Key, V extends S.Top> = S.Record.Type<K, V>
236
+ export type Encoded<K extends S.Record.Key, V extends S.Top> = S.Record.Encoded<K, V>
237
+ }
238
+
36
239
  export const SpanId = Symbol()
37
240
  export type SpanId = typeof SpanId
38
241
 
@@ -66,117 +269,141 @@ export const PhoneNumber = PhoneNumberT
66
269
 
67
270
  export type PhoneNumber = PhoneNumberType
68
271
 
69
- export const makeIs = <A extends { _tag: string }, I, RD, RE>(
70
- schema: S.Codec<A, I, RD, RE>
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")
95
- }
96
-
97
- export const makeIsAnyOf = <A extends { _tag: string }, I, RD, RE>(
98
- schema: S.Codec<A, I, RD, RE>
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)
103
- }
104
- throw new Error("Unsupported")
105
- }
106
-
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>>
272
+ // Copied from SchemaAST.collectSentinels (marked @internal in effect).
273
+ // Returns all { key, literal } pairs that can discriminate a union member.
274
+ const getTagFromAST = (schema: S.Top): string => {
275
+ const sentinels = collectSentinelsFromAST(schema.ast)
276
+ const sentinel = sentinels.find((s) => s.key === "_tag")
277
+ if (sentinel !== undefined && typeof sentinel.literal === "string") return sentinel.literal
278
+ throw new Error("No _tag literal found on schema member")
112
279
  }
113
280
 
114
- const getTagLiteral = <Tag extends string>(schema: S.tag<Tag>): Tag => {
115
- if (!SchemaAST.isLiteral(schema.ast)) {
116
- throw new Error("Unsupported _tag schema: expected a literal AST")
117
- }
118
- return schema.ast.literal as Tag
119
- }
120
-
121
- type TaggedUnionMap<Members extends readonly (S.Top & { fields: { _tag: S.tag<string> } })[]> = {
122
- [Key in Members[number] as Key["fields"]["_tag"]["Type"]]: Key
123
- }
124
-
125
- export const taggedUnionMap = <
126
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
- Members extends readonly (S.Top & { fields: { _tag: S.tag<string> } })[]
128
- >(
129
- self: Members
130
- ) => {
131
- const out = {} as TaggedUnionMap<Members>
132
- for (const key of self) {
133
- const tag = getTagLiteral(key.fields._tag) as keyof TaggedUnionMap<Members>
134
- out[tag] = key as TaggedUnionMap<Members>[typeof tag]
281
+ function collectSentinelsFromAST(
282
+ ast: SchemaAST.AST
283
+ ): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> {
284
+ switch (ast._tag) {
285
+ case "Declaration": {
286
+ const s = ast.annotations?.["~sentinels"]
287
+ return Array.isArray(s) ? s : []
288
+ }
289
+ case "Objects":
290
+ return ast.propertySignatures.flatMap(
291
+ (ps): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> => {
292
+ const type = ps.type
293
+ if (!SchemaAST.isOptional(type)) {
294
+ if (SchemaAST.isLiteral(type)) return [{ key: ps.name, literal: type.literal }]
295
+ if (SchemaAST.isUniqueSymbol(type)) return [{ key: ps.name, literal: type.symbol }]
296
+ }
297
+ return []
298
+ }
299
+ )
300
+ case "Suspend":
301
+ return collectSentinelsFromAST(ast.thunk())
302
+ default:
303
+ return []
135
304
  }
136
- return out
137
305
  }
138
306
 
139
307
  export const tags = <
140
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
- Members extends NonEmptyReadonlyArray<(S.Top & { fields: { _tag: S.tag<string> } })>
308
+ Members extends NonEmptyReadonlyArray<(S.Top & { readonly Type: { readonly _tag: string } })>
142
309
  >(
143
310
  self: Members
144
311
  ) =>
145
312
  S.Literals(
146
- self.map((key) => getTagLiteral(key.fields._tag)) as {
147
- [Index in keyof Members]: S.Schema.Type<Members[Index]["fields"]["_tag"]>
313
+ self.map(getTagFromAST) as {
314
+ [Index in keyof Members]: Members[Index]["Type"]["_tag"]
148
315
  }
149
316
  ) as S.Literals<
150
317
  {
151
- [Index in keyof Members]: S.Schema.Type<Members[Index]["fields"]["_tag"]>
318
+ [Index in keyof Members]: Members[Index]["Type"]["_tag"]
152
319
  }
153
320
  >
154
321
 
155
- export const ExtendTaggedUnion = <A extends { readonly _tag: string }, I, R>(
156
- schema: S.Codec<A, I, R>
157
- ) =>
158
- extendM(
159
- schema,
160
- (_) => ({
161
- // is: S.is(schema), // only works with never DecodingServices
162
- isA: makeIs(_),
163
- isAnyOf: makeIsAnyOf(_) /*, map: taggedUnionMap(a) */
164
- })
165
- )
322
+ type TaggedUnionMembers = NonEmptyReadonlyArray<
323
+ S.Top & { readonly Type: { readonly _tag: string } }
324
+ >
325
+
326
+ type TaggedUnionTags<Members extends TaggedUnionMembers> = S.Literals<
327
+ {
328
+ [Index in keyof Members]: Members[Index]["Type"]["_tag"]
329
+ }
330
+ >
331
+
332
+ type TaggedPropertyKeys<A, Members extends TaggedUnionMembers> = {
333
+ [K in keyof A & string]: A[K] extends Members[number]["Type"] ? K : never
334
+ }[keyof A & string]
335
+
336
+ type PropertyGuardsFor<
337
+ Members extends TaggedUnionMembers,
338
+ K extends string,
339
+ A
340
+ > =
341
+ & {
342
+ readonly [M in Members[number] as `is${M["Type"]["_tag"]}`]: (
343
+ target: A
344
+ ) => target is A & { readonly [P in K]: M["Type"] }
345
+ }
346
+ & {
347
+ readonly isAnyOf: <const Tags extends ReadonlyArray<Members[number]["Type"]["_tag"]>>(
348
+ tags: Tags
349
+ ) => (
350
+ target: A
351
+ ) => target is A & { readonly [P in K]: Extract<Members[number]["Type"], { readonly _tag: Tags[number] }> }
352
+ }
353
+
354
+ type PropertyGuards<
355
+ Members extends TaggedUnionMembers,
356
+ K extends string
357
+ > =
358
+ & {
359
+ readonly [M in Members[number] as `is${M["Type"]["_tag"]}`]: <
360
+ T extends { readonly [P in K]: Members[number]["Type"] }
361
+ >(target: T) => target is T & { readonly [P in K]: M["Type"] }
362
+ }
363
+ & {
364
+ readonly isAnyOf: <const Tags extends ReadonlyArray<Members[number]["Type"]["_tag"]>>(
365
+ tags: Tags
366
+ ) => <T extends { readonly [P in K]: Members[number]["Type"] }>(
367
+ target: T
368
+ ) => target is T & { readonly [P in K]: Extract<Members[number]["Type"], { readonly _tag: Tags[number] }> }
369
+ }
370
+
371
+ type TaggedUnionWithTags<Members extends TaggedUnionMembers> = S.toTaggedUnion<"_tag", Members> & {
372
+ readonly tags: TaggedUnionTags<Members>
373
+ readonly generateGuards: <K extends string>(property: K) => PropertyGuards<Members, K>
374
+ readonly generateGuardsFor: <A>() => <K extends TaggedPropertyKeys<A, Members>>(
375
+ property: K
376
+ ) => PropertyGuardsFor<Members, K, A>
377
+ }
378
+
379
+ const extendTaggedUnionWithTags = <Members extends TaggedUnionMembers>(
380
+ schema: S.Union<Members>
381
+ ): TaggedUnionWithTags<Members> =>
382
+ extendM(schema.pipe(S.toTaggedUnion("_tag")), (tagged) => {
383
+ const makeGuards = (property: string) => {
384
+ const result: any = {}
385
+ const guards: Record<string, (u: unknown) => boolean> = tagged.guards
386
+ for (const tag of Object.keys(guards)) {
387
+ const guard = guards[tag]!
388
+ result[`is${tag}`] = (target: any) => guard(target[property])
389
+ }
390
+ result.isAnyOf = (memberTags: Array<string>) => {
391
+ const check = tagged.isAnyOf(memberTags)
392
+ return (target: any) => check(target[property])
393
+ }
394
+ return result
395
+ }
396
+ return {
397
+ tags: tags(schema.members),
398
+ generateGuards: makeGuards,
399
+ generateGuardsFor: () => makeGuards
400
+ }
401
+ })
402
+
403
+ export const ExtendTaggedUnion = <Members extends TaggedUnionMembers>(
404
+ schema: S.Union<Members>
405
+ ): TaggedUnionWithTags<Members> => extendTaggedUnionWithTags(schema)
166
406
 
167
407
  export const TaggedUnion = <
168
- Members extends NonEmptyReadonlyArray<
169
- S.Codec<{ readonly _tag: string }, any, any, any> & { fields: { _tag: S.tag<string> } }
170
- >
171
- >(...a: Members) =>
172
- pipe(
173
- S.Union(a),
174
- (_) =>
175
- extendM(_, (_) => ({
176
- // is: S.is(_), // only works with never DecodingServices
177
- isA: makeIs(_),
178
- isAnyOf: makeIsAnyOf(_),
179
- tagMap: taggedUnionMap(a),
180
- tags: tags(a)
181
- }))
182
- )
408
+ Members extends TaggedUnionMembers
409
+ >(members: Members): TaggedUnionWithTags<Members> => extendTaggedUnionWithTags(S.Union(members))
@@ -0,0 +1,50 @@
1
+ import * as Ref from "effect/Ref"
2
+ import * as Context from "../Context.js"
3
+ import * as Effect from "../Effect.js"
4
+ import type { InvalidationKey } from "../rpc/Invalidation.js"
5
+
6
+ export type { InvalidationKey }
7
+ /** Shape of the per-mutation service that accumulates server-provided invalidation keys. */
8
+ export interface InvalidationKeysService {
9
+ readonly add: (key: InvalidationKey) => Effect.Effect<void>
10
+ readonly get: Effect.Effect<ReadonlyArray<InvalidationKey>>
11
+ }
12
+
13
+ /**
14
+ * Context.Reference that accumulates invalidation keys received from the server via the
15
+ * `x-invalidate` HTTP response header.
16
+ *
17
+ * The default is a no-op: when not explicitly provided (e.g. outside a mutation wrapper)
18
+ * all calls are ignored. The mutation wrapper in `@effect-app/vue` provides a real
19
+ * implementation backed by a `Ref`.
20
+ */
21
+ export const InvalidationKeysFromServer = Context.Reference<InvalidationKeysService>(
22
+ "effect-app/client/InvalidationKeysFromServer",
23
+ {
24
+ defaultValue: () => ({
25
+ add: (_key: InvalidationKey) => Effect.void,
26
+ get: Effect.succeed([] as ReadonlyArray<InvalidationKey>)
27
+ })
28
+ }
29
+ )
30
+ export type InvalidationKeysFromServer = typeof InvalidationKeysFromServer
31
+
32
+ /**
33
+ * Creates a fresh `InvalidationKeysService` implementation backed by a `Ref`.
34
+ *
35
+ * @param ref - The `Ref` that stores the accumulated keys.
36
+ * @param onAdded - V3: Optional Effect run after a key is added. Use to trigger mid-stream
37
+ * query invalidation without waiting for the stream to complete.
38
+ */
39
+ export const makeInvalidationKeysService = (
40
+ ref: Ref.Ref<ReadonlyArray<InvalidationKey>>,
41
+ onAdded?: (key: InvalidationKey) => Effect.Effect<void>
42
+ ): InvalidationKeysService => ({
43
+ // When onAdded is set, fire it immediately without accumulating in the ref —
44
+ // the key is handled on arrival and must not be re-processed at stream end.
45
+ add: (key) =>
46
+ onAdded
47
+ ? onAdded(key)
48
+ : Ref.update(ref, (keys) => [...keys, key]),
49
+ get: Ref.get(ref)
50
+ })