effect-app 4.0.0-beta.2 → 4.0.0-beta.200

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 (224) hide show
  1. package/CHANGELOG.md +960 -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 +14 -6
  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 +9 -5
  56. package/dist/Schema/ext.d.ts +121 -48
  57. package/dist/Schema/ext.d.ts.map +1 -1
  58. package/dist/Schema/ext.js +134 -52
  59. package/dist/Schema/moreStrings.d.ts +117 -17
  60. package/dist/Schema/moreStrings.d.ts.map +1 -1
  61. package/dist/Schema/moreStrings.js +19 -18
  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 +8 -4
  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 +159 -58
  73. package/dist/Schema.d.ts.map +1 -1
  74. package/dist/Schema.js +136 -68
  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 +98 -36
  94. package/dist/client/clientFor.d.ts +51 -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 +61 -34
  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 +9 -9
  114. package/dist/ids.d.ts.map +1 -1
  115. package/dist/ids.js +3 -2
  116. package/dist/index.d.ts +5 -9
  117. package/dist/index.d.ts.map +1 -1
  118. package/dist/index.js +6 -9
  119. package/dist/logger.d.ts +1 -1
  120. package/dist/middleware.d.ts +16 -9
  121. package/dist/middleware.d.ts.map +1 -1
  122. package/dist/middleware.js +13 -9
  123. package/dist/rpc/Invalidation.d.ts +397 -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 +6 -5
  127. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  128. package/dist/rpc/MiddlewareMaker.js +51 -28
  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 +6 -5
  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 +48 -10
  152. package/dist/utils.d.ts.map +1 -1
  153. package/dist/utils.js +33 -8
  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 -28
  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} +58 -64
  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 +21 -7
  171. package/src/Schema/email.ts +10 -3
  172. package/src/Schema/ext.ts +226 -86
  173. package/src/Schema/moreStrings.ts +37 -32
  174. package/src/Schema/numbers.ts +14 -16
  175. package/src/Schema/phoneNumber.ts +8 -2
  176. package/src/Schema/strings.ts +4 -8
  177. package/src/Schema.ts +350 -107
  178. package/src/client/InvalidationKeys.ts +50 -0
  179. package/src/client/apiClientFactory.ts +227 -136
  180. package/src/client/clientFor.ts +86 -29
  181. package/src/client/errors.ts +61 -26
  182. package/src/client/makeClient.ts +530 -80
  183. package/src/client.ts +1 -0
  184. package/src/http/Request.ts +7 -4
  185. package/src/ids.ts +3 -2
  186. package/src/index.ts +5 -11
  187. package/src/middleware.ts +12 -10
  188. package/src/rpc/Invalidation.ts +221 -0
  189. package/src/rpc/MiddlewareMaker.ts +61 -51
  190. package/src/rpc/README.md +2 -2
  191. package/src/rpc/RpcContextMap.ts +6 -5
  192. package/src/rpc/RpcMiddleware.ts +6 -5
  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 +73 -15
  198. package/test/dist/moreStrings.test.d.ts.map +1 -0
  199. package/test/dist/rpc.test.d.ts.map +1 -1
  200. package/test/dist/secretURL.test.d.ts.map +1 -0
  201. package/test/dist/special.test.d.ts.map +1 -0
  202. package/test/moreStrings.test.ts +17 -0
  203. package/test/rpc.test.ts +38 -6
  204. package/test/schema.test.ts +609 -4
  205. package/test/secretURL.test.ts +157 -0
  206. package/test/special.test.ts +1023 -0
  207. package/test/utils.test.ts +6 -6
  208. package/tsconfig.base.json +3 -4
  209. package/tsconfig.json +0 -1
  210. package/tsconfig.json.bak +2 -2
  211. package/tsconfig.src.json +29 -29
  212. package/tsconfig.test.json +2 -2
  213. package/dist/Operations.d.ts +0 -87
  214. package/dist/Operations.d.ts.map +0 -1
  215. package/dist/Operations.js +0 -29
  216. package/dist/ServiceMap.d.ts +0 -44
  217. package/dist/ServiceMap.d.ts.map +0 -1
  218. package/dist/ServiceMap.js +0 -91
  219. package/dist/Struct.d.ts +0 -44
  220. package/dist/Struct.d.ts.map +0 -1
  221. package/dist/Struct.js +0 -29
  222. package/eslint.config.mjs +0 -26
  223. package/src/Operations.ts +0 -55
  224. package/src/Struct.ts +0 -54
@@ -2,7 +2,6 @@
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-return */
3
3
  import type { Option } from "effect"
4
4
  import * as B from "effect/Brand"
5
- import type * as Brand from "effect/Brand"
6
5
  import type * as Result from "effect/Result"
7
6
  import * as S from "effect/Schema"
8
7
 
@@ -21,7 +20,7 @@ export interface Constructor<in out A extends B.Brand<any>> {
21
20
  * Constructs a branded type from a value of type `A`, returning `Result.succeed`
22
21
  * if the provided `A` is valid, `Result.fail` otherwise.
23
22
  */
24
- result(args: Unbranded<A>): Result.Result<A, Brand.BrandError>
23
+ result(args: Unbranded<A>): Result.Result<A, B.BrandError>
25
24
  /**
26
25
  * Attempts to refine the provided value of type `A`, returning `true` if
27
26
  * the provided `A` is valid, `false` otherwise.
@@ -29,19 +28,34 @@ export interface Constructor<in out A extends B.Brand<any>> {
29
28
  is(a: Unbranded<A>): a is Unbranded<A> & A
30
29
  }
31
30
 
32
- export const fromBrand = <C extends Brand.Brand<string>>(
31
+ type BrandAnnotations<C extends B.Brand<any>> =
32
+ & S.Annotations.Filter
33
+ & (
34
+ C extends string ? { readonly toArbitrary?: S.Annotations.ToArbitrary.Declaration<C, readonly []> }
35
+ : {}
36
+ )
37
+
38
+ type BrandedSchema<Self extends S.Top, C extends B.Brand<any>> =
39
+ & Omit<S.brand<Self["Rebuild"], B.Brand.Keys<C>>, "Type" | "Iso" | "~type.make">
40
+ & {
41
+ readonly Type: C
42
+ readonly Iso: C
43
+ readonly "~type.make": C
44
+ }
45
+
46
+ export const fromBrand = <C extends B.Brand<any>>(
33
47
  constructor: Constructor<C>,
34
- options?: S.Annotations.Filter
48
+ options?: BrandAnnotations<C>
35
49
  ) =>
36
- <Self extends S.Top>(self: Self): S.brand<Self["~rebuild.out"], Brand.Brand.Keys<C>> => {
50
+ <Self extends S.Top>(self: Self): BrandedSchema<Self, C> => {
37
51
  const branded = S.fromBrand(options?.identifier ?? "Brand", constructor as any)(self as any)
38
52
  return options ? (branded as any).pipe(S.annotate(options)) : branded as any
39
53
  }
40
54
 
41
- export type Brands<P> = P extends B.Brand<any> ? Brand.Brand.Brands<P>
55
+ export type Brands<P> = P extends B.Brand<any> ? B.Brand.Brands<P>
42
56
  : never
43
57
 
44
- export type Unbranded<P> = P extends B.Brand<any> ? Brand.Brand.Unbranded<P> : P
58
+ export type Unbranded<P> = P extends B.Brand<any> ? B.Brand.Unbranded<P> : P
45
59
 
46
60
  export const nominal: <A extends B.Brand<any>>() => Constructor<A> = <
47
61
  A extends B.Brand<any>
@@ -12,11 +12,18 @@ export type Email = string & EmailBrand
12
12
  export const Email = S
13
13
  .String
14
14
  .pipe(
15
+ S.annotate({
16
+ title: "Email",
17
+ description: "an email according to RFC 5322",
18
+ format: "email"
19
+ }),
20
+ S.check(S.isMinLength(3), /* a@b */ S.isMaxLength(998)),
15
21
  S.refine(isValidEmail as Refinement<string, Email>, {
16
22
  identifier: "Email",
17
- title: "Email",
18
23
  description: "an email according to RFC 5322",
19
- jsonSchema: { format: "email", minLength: 3, /* a@b */ maxLength: 998 },
20
- arbitrary: () => (fc: any) => fc.emailAddress().map((_: any) => _ as Email)
24
+ jsonSchema: { format: "email", minLength: 3, maxLength: 998 }
25
+ }),
26
+ S.annotate({
27
+ toArbitrary: () => (fc) => fc.emailAddress().map((_) => _ as Email)
21
28
  })
22
29
  )
package/src/Schema/ext.ts CHANGED
@@ -1,56 +1,125 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-return */
3
- import { Effect, Option, pipe, Schema, type SchemaAST, SchemaGetter, SchemaIssue, SchemaParser, SchemaTransformation, type ServiceMap } from "effect"
3
+ import { Config, Effect, Function, Option, pipe, type SchemaAST, SchemaIssue, SchemaTransformation } from "effect"
4
4
  import * as S from "effect/Schema"
5
+ import { isDateValid } from "effect/Schema"
5
6
  import { type NonEmptyReadonlyArray } from "../Array.js"
7
+ import * as Context from "../Context.js"
6
8
  import { extendM, typedKeysOf } from "../utils.js"
7
9
  import { type AST } from "./schema.js"
8
10
 
9
- // TODO: v4 migration withConstructorDefault signature changed, propertySignature removed
10
- // Constraint relaxed from `Self extends S.Top & S.WithoutConstructorDefault` to `Self extends S.Top`
11
- // because `.pipe()` widens the schema type to `Top` which doesn't satisfy `WithoutConstructorDefault`.
12
- // The narrowing assertions below are safe — we're asserting "this schema hasn't had a default applied yet".
13
- export const withDefaultConstructor = <A>(
14
- makeDefault: () => NoInfer<A>
15
- ) =>
16
- <Self extends S.Top>(self: Self): S.withConstructorDefault<Self & S.WithoutConstructorDefault> => {
17
- type Narrowed = Self & S.WithoutConstructorDefault
18
- return S.withConstructorDefault<Narrowed>(
19
- () => Option.some(makeDefault() as Narrowed["~type.make.in"])
20
- )(self as Narrowed)
11
+ type ProvidedCodec<Self extends S.Top, R> = S.Codec<
12
+ Self["Type"],
13
+ Self["Encoded"],
14
+ Exclude<Self["DecodingServices"], R>,
15
+ Exclude<Self["EncodingServices"], R>
16
+ >
17
+
18
+ const concurrencySetting = Effect.runSync(
19
+ Config
20
+ .literal("unbounded", "SCHEMA_CONCURRENCY")
21
+ .pipe(Config.orElse(() => Config.number("SCHEMA_CONCURRENCY")), Config.option)
22
+ .asEffect()
23
+ )
24
+
25
+ export const DefaultParseOptions: SchemaAST.ParseOptions = {
26
+ concurrency: Option.getOrElse(concurrencySetting, () => "unbounded" as const)
21
27
  }
22
28
 
29
+ /**
30
+ * Parse-options annotation used on schema constructors for decode paths where callers
31
+ * cannot currently pass parse options (notably some RPC / HttpApi integration paths).
32
+ *
33
+ * Keep this annotation in place so those framework-managed decodes still run with
34
+ * unbounded concurrency by default.
35
+ */
36
+ export const concurrencyUnbounded = { parseOptions: DefaultParseOptions } as const
37
+
38
+ type DecodeLike = (schema: any) => (input: any, options?: SchemaAST.ParseOptions) => any
39
+
40
+ export const withDefaultParseOptions = <Decode extends DecodeLike>(
41
+ decode: Decode,
42
+ defaultParseOptions: SchemaAST.ParseOptions = DefaultParseOptions
43
+ ): Decode =>
44
+ ((schema: any) => {
45
+ const run = decode(schema)
46
+ return (input: any, options?: SchemaAST.ParseOptions) => run(input, { ...defaultParseOptions, ...options })
47
+ }) as Decode
48
+
23
49
  // TODO: v4 migration - Date is no longer by default encoded to string.
24
- const DateFromString = Schema.Date.pipe(
25
- Schema.encodeTo(Schema.String, {
26
- decode: SchemaGetter.Date(),
27
- encode: SchemaGetter.transform((_) => _.toISOString())
28
- })
29
- )
50
+
51
+ const DateString = S.String.annotate({
52
+ identifier: "Date",
53
+ description: "a string in ISO 8601 format that will be decoded as a Date",
54
+ format: "date-time"
55
+ })
56
+
57
+ /**
58
+ * Schema type for {@link DateFromString}.
59
+ *
60
+ * @category Schemas
61
+ * @since 4.0.0
62
+ */
63
+ export interface DateFromString extends S.decodeTo<S.Date, S.String> {}
64
+
65
+ /**
66
+ * A transformation schema that parses an ISO 8601 string into a `Date`.
67
+ *
68
+ * Decoding:
69
+ * - A `string` is decoded as a `Date`.
70
+ *
71
+ * Encoding:
72
+ * - A `Date` is encoded as a `string`.
73
+ *
74
+ * @since 4.0.0
75
+ */
76
+ export const DateFromString: DateFromString = DateString.pipe(S.decodeTo(S.Date, SchemaTransformation.dateFromString))
30
77
 
31
78
  /**
32
79
  * Like the default Schema `Date` but from String with `withDefault` => now
33
80
  */
34
81
  export const Date = Object.assign(DateFromString, {
35
- withDefault: DateFromString.pipe(withDefaultConstructor(() => new global.Date()))
82
+ withDefault: DateFromString.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date()))),
83
+ withDecodingDefaultType: DateFromString.pipe(S.withDecodingDefaultType(Effect.sync(() => new global.Date())))
84
+ })
85
+
86
+ /**
87
+ * Like the default Schema `DateValid` but from String with `withDefault` => now
88
+ */
89
+ export const DateValid = Object.assign(Date.check(isDateValid()), {
90
+ withDefault: DateFromString.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date()))),
91
+ withDecodingDefaultType: DateFromString.pipe(S.withDecodingDefaultType(Effect.sync(() => new global.Date())))
36
92
  })
37
93
 
38
94
  /**
39
95
  * Like the default Schema `Boolean` but with `withDefault` => false
40
96
  */
41
97
  export const Boolean = Object.assign(S.Boolean, {
42
- withDefault: S.Boolean.pipe(withDefaultConstructor(() => false))
98
+ withDefault: S.Boolean.pipe(S.withConstructorDefault(Effect.succeed(false))),
99
+ withDecodingDefaultType: S.Boolean.pipe(S.withDecodingDefaultType(Effect.succeed(false)))
43
100
  })
44
101
 
45
102
  /**
103
+ * You probably want to use `Finite` instead of this.
46
104
  * Like the default Schema `Number` but with `withDefault` => 0
47
105
  */
48
- export const Number = Object.assign(S.Number, { withDefault: S.Number.pipe(withDefaultConstructor(() => 0)) })
106
+ export const Number = Object.assign(S.Number, {
107
+ withDefault: S.Number.pipe(S.withConstructorDefault(Effect.succeed(0))),
108
+ withDecodingDefaultType: S.Number.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
109
+ })
110
+
111
+ /**
112
+ * Like the default Schema `Finite` but with `withDefault` => 0
113
+ */
114
+ export const Finite = Object.assign(S.Finite, {
115
+ withDefault: S.Finite.pipe(S.withConstructorDefault(Effect.succeed(0))),
116
+ withDecodingDefaultType: S.Finite.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
117
+ })
49
118
 
50
119
  /**
51
- * Like the default Schema `Literal` but with `withDefault` => literals[0]
120
+ * Like the default Schema `Literals` but with `withDefault` => literals[0]
52
121
  */
53
- export const Literal = <Literals extends NonEmptyReadonlyArray<AST.LiteralValue>>(...literals: Literals) =>
122
+ export const Literals = <const Literals extends NonEmptyReadonlyArray<AST.LiteralValue>>(literals: Literals) =>
54
123
  pipe(
55
124
  S.Literals(literals),
56
125
  (s) =>
@@ -58,79 +127,144 @@ export const Literal = <Literals extends NonEmptyReadonlyArray<AST.LiteralValue>
58
127
  changeDefault: <A extends Literals[number]>(a: A) => {
59
128
  return Object.assign(S.Literals(literals), {
60
129
  Default: a,
61
- withDefault: s.pipe(withDefaultConstructor(() => a))
130
+ withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(a))),
131
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(a)))
62
132
  }) // todo: copy annotations from original?
63
133
  },
64
- Default: literals[0] as typeof literals[0],
65
- withDefault: s.pipe(withDefaultConstructor(() => literals[0]))
134
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- load-bearing: Object.assign widens the field type without it, breaking `expectTypeOf(l.Default).toEqualTypeOf<"a">()` in tests
135
+ Default: literals[0] as Literals[0],
136
+ withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(literals[0]))),
137
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(literals[0])))
66
138
  })
67
139
  )
68
140
 
69
141
  /**
70
142
  * Like the default Schema `Array` but with `withDefault` => []
71
143
  */
72
- export function Array<Value extends S.Top>(value: Value) {
144
+ export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
73
145
  return pipe(
74
- S.Array(value),
75
- (s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => [])) })
146
+ S.Array(value).annotate(concurrencyUnbounded),
147
+ (s) =>
148
+ Object.assign(s, {
149
+ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => []))),
150
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => [])))
151
+ })
76
152
  )
77
153
  }
78
154
 
79
155
  /**
80
- * Like the default Schema `Map` but with `withDefault` => []
156
+ * An annotated `S.Array` of unique items that decodes to a `ReadonlySet`.
81
157
  */
82
- function Map_<Key extends S.Top, Value extends S.Top>(input: { key: Key; value: Value }) {
83
- return pipe(
84
- S.ReadonlyMap(input.key, input.value),
85
- (s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => new global.Map())) })
158
+ export const ReadonlySetFromArray = <ValueSchema extends S.Top>(value: ValueSchema) => {
159
+ const from = S
160
+ .Array(value)
161
+ .annotate({ ...concurrencyUnbounded, expected: "an array of unique items that will be decoded as a ReadonlySet" })
162
+ const to = S.instanceOf(Set) as S.instanceOf<ReadonlySet<S.Schema.Type<ValueSchema>>>
163
+ const schema = from.pipe(
164
+ S.decodeTo(
165
+ to,
166
+ SchemaTransformation.transform({
167
+ decode: (arr) => new Set(arr) as ReadonlySet<S.Schema.Type<ValueSchema>>,
168
+ encode: (set) => [...set]
169
+ })
170
+ )
86
171
  )
172
+ return schema
87
173
  }
88
174
 
89
- export { Map_ as Map }
175
+ /**
176
+ * An annotated `S.Array` of key-value tuples that decodes to a `ReadonlyMap`.
177
+ */
178
+ export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
179
+ readonly key: KeySchema
180
+ readonly value: ValueSchema
181
+ }) => {
182
+ const from = S
183
+ .Array(S.Tuple([pair.key, pair.value]))
184
+ .annotate({
185
+ ...concurrencyUnbounded,
186
+ expected: "an array of key-value tuples that will be decoded as a ReadonlyMap"
187
+ })
188
+ const to = S.instanceOf(Map) as S.instanceOf<
189
+ ReadonlyMap<S.Schema.Type<KeySchema>, S.Schema.Type<ValueSchema>>
190
+ >
191
+ const schema = from.pipe(
192
+ S.decodeTo(
193
+ to,
194
+ SchemaTransformation.transform({
195
+ decode: (
196
+ arr
197
+ ) => new Map(arr) as ReadonlyMap<S.Schema.Type<KeySchema>, S.Schema.Type<ValueSchema>>,
198
+ encode: (
199
+ map
200
+ ) => [...map.entries()] as any // fu
201
+ })
202
+ )
203
+ )
204
+ return schema
205
+ }
90
206
 
91
207
  /**
92
- * Like the default Schema `ReadonlySet` but with `withDefault` => new Set()
208
+ * Like the default Schema `ReadonlySet` but from Array with `withDefault` => new Set()
93
209
  */
94
- export const ReadonlySet = <Value extends S.Top>(value: Value) =>
210
+ export const ReadonlySet = <ValueSchema extends S.Top>(value: ValueSchema) =>
95
211
  pipe(
96
- S.ReadonlySet(value),
97
- (s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => new Set<S.Schema.Type<Value>>())) })
212
+ ReadonlySetFromArray(value),
213
+ (s) =>
214
+ Object.assign(s, {
215
+ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Set<S.Schema.Type<ValueSchema>>()))),
216
+ withDecodingDefaultType: s.pipe(
217
+ S.withDecodingDefaultType(Effect.sync(() => new Set<S.Schema.Type<ValueSchema>>()))
218
+ )
219
+ })
98
220
  )
99
221
 
100
222
  /**
101
- * Like the default Schema `ReadonlyMap` but with `withDefault` => new Map()
223
+ * Like the default Schema `ReadonlyMap` but from Array with `withDefault` => new Map()
102
224
  */
103
- export const ReadonlyMap = <K extends S.Top, V extends S.Top>(pair: {
104
- readonly key: K
105
- readonly value: V
225
+ export const ReadonlyMap = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
226
+ readonly key: KeySchema
227
+ readonly value: ValueSchema
106
228
  }) =>
107
229
  pipe(
108
- S.ReadonlyMap(pair.key, pair.value),
109
- (s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => new Map())) })
230
+ ReadonlyMapFromArray(pair),
231
+ (s) =>
232
+ Object.assign(s, {
233
+ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Map()))),
234
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new Map())))
235
+ })
110
236
  )
111
237
 
112
238
  /**
113
239
  * Like the default Schema `NullOr` but with `withDefault` => null
114
240
  */
115
- export const NullOr = <S extends S.Top>(self: S) =>
241
+ export const NullOr = <Schema extends S.Top>(self: Schema) =>
116
242
  pipe(
117
243
  S.NullOr(self),
118
- (s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => null)) })
244
+ (s) =>
245
+ Object.assign(s, {
246
+ withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(null))),
247
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(null)))
248
+ })
119
249
  )
120
250
 
121
- export const defaultDate = (s: S.Top) => s.pipe(withDefaultConstructor(() => new global.Date()))
251
+ export const defaultDate = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
252
+ schema.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date())))
122
253
 
123
- export const defaultBool = (s: S.Top) => s.pipe(withDefaultConstructor(() => false))
254
+ export const defaultBool = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
255
+ schema.pipe(S.withConstructorDefault(Effect.succeed(false)))
124
256
 
125
- export const defaultNullable = (
126
- s: S.Top
127
- ) => s.pipe(withDefaultConstructor(() => null))
257
+ export const defaultNullable = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
258
+ schema.pipe(S.withConstructorDefault(Effect.succeed(null)))
128
259
 
129
- export const defaultArray = (s: S.Top) => s.pipe(withDefaultConstructor(() => []))
260
+ export const defaultArray = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
261
+ schema.pipe(S.withConstructorDefault(Effect.sync(() => [])))
130
262
 
131
- export const defaultMap = (s: S.Top) => s.pipe(withDefaultConstructor(() => new Map()))
263
+ export const defaultMap = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
264
+ schema.pipe(S.withConstructorDefault(Effect.sync(() => new Map())))
132
265
 
133
- export const defaultSet = (s: S.Top) => s.pipe(withDefaultConstructor(() => new Set()))
266
+ export const defaultSet = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
267
+ schema.pipe(S.withConstructorDefault(Effect.sync(() => new Set())))
134
268
 
135
269
  export const withDefaultMake = <Self extends S.Top>(s: Self) => {
136
270
  const a = Object.assign(S.decodeSync(s as any) as WithDefaults<Self>, s)
@@ -160,8 +294,11 @@ export type WithDefaults<Self extends S.Top> = (
160
294
  // : never
161
295
 
162
296
  export const inputDate = extendM(
163
- S.Union([S.DateValid, S.Date]),
164
- (s) => ({ withDefault: s.pipe(withDefaultConstructor(() => new globalThis.Date())) })
297
+ S.Union([S.DateValid, Date]),
298
+ (s) => ({
299
+ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))),
300
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date())))
301
+ })
165
302
  )
166
303
 
167
304
  export interface UnionBrand {}
@@ -211,7 +348,7 @@ export const transformTo = <To extends S.Top, From extends S.Top>(
211
348
  { message: "One way schema transformation, encoding is not allowed" }
212
349
  )
213
350
  )
214
- }) as any
351
+ })
215
352
  )
216
353
  )
217
354
 
@@ -228,7 +365,7 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
228
365
  S.decodeTo(
229
366
  to,
230
367
  SchemaTransformation.transformOrFail({
231
- decode: decode as any,
368
+ decode,
232
369
  encode: (i: any) =>
233
370
  Effect.fail(
234
371
  new SchemaIssue.Forbidden(
@@ -236,33 +373,36 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
236
373
  { message: "One way schema transformation, encoding is not allowed" }
237
374
  )
238
375
  )
239
- }) as any
376
+ })
240
377
  )
241
378
  )
242
379
 
243
- // TODO: v4 migration — S.declare API changed (no [self] + decode/encode pattern)
244
- // Need to find v4 equivalent for contextual schema wrapping
245
- export const provide = <Self extends S.Top, R>(
246
- self: Self,
247
- context: ServiceMap.ServiceMap<R>
248
- ): any => {
380
+ export const provide: {
381
+ <R>(context: Context.Context<R>): <Self extends S.Top>(self: Self) => ProvidedCodec<Self, R>
382
+ <Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R>
383
+ } = Function.dual(2, <Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R> => {
249
384
  const prov = Effect.provide(context)
250
- return S
251
- .declare((_u: unknown): _u is unknown => true) // placeholder — needs proper v4 declare
252
- .pipe(
253
- S.decodeTo(
254
- self,
255
- SchemaTransformation.transformOrFail({
256
- decode: (n: any) => prov(SchemaParser.decodeUnknownEffect(self)(n)),
257
- encode: (n: any) => prov(SchemaParser.encodeUnknownEffect(self)(n))
258
- }) as any
259
- ) as any
260
- )
261
- }
262
- // TODO: v4 migration — ServiceMap.pick and S.declare pattern removed
263
- export const contextFromServices = <Self extends S.Top, Tags extends readonly any[]>(
264
- _self: Self,
265
- ..._services: Tags
266
- ): any => {
267
- throw new Error("contextFromServices: not yet migrated to v4")
268
- }
385
+ return self.pipe(
386
+ S.middlewareDecoding((effect) => prov(effect)),
387
+ S.middlewareEncoding((effect) => prov(effect))
388
+ )
389
+ })
390
+ export const contextFromServices = Effect.fnUntraced(function*<
391
+ Self extends S.Top,
392
+ Tags extends ReadonlyArray<Context.Key<any, any>>
393
+ >(self: Self, ...services: Tags) {
394
+ const context: Context.Context<Context.Service.Identifier<Tags[number]>> = Context.pick(...services)(
395
+ yield* Effect.context<Context.Service.Identifier<Tags[number]>>()
396
+ )
397
+ return provide(self, context)
398
+ }) as <
399
+ Self extends S.Top,
400
+ Tags extends ReadonlyArray<Context.Key<any, any>>
401
+ >(
402
+ self: Self,
403
+ ...services: Tags
404
+ ) => Effect.Effect<
405
+ ProvidedCodec<Self, Context.Service.Identifier<Tags[number]>>,
406
+ never,
407
+ Context.Service.Identifier<Tags[number]>
408
+ >