effect-app 4.0.0-beta.23 → 4.0.0-beta.231

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 (244) hide show
  1. package/CHANGELOG.md +1010 -0
  2. package/dist/Array.d.ts +3 -2
  3. package/dist/Array.d.ts.map +1 -1
  4. package/dist/Array.js +4 -4
  5. package/dist/Chunk.d.ts +1 -1
  6. package/dist/Chunk.d.ts.map +1 -1
  7. package/dist/Config/SecretURL.d.ts +4 -2
  8. package/dist/Config/SecretURL.d.ts.map +1 -1
  9. package/dist/Config/SecretURL.js +3 -6
  10. package/dist/Config/internal/configSecretURL.d.ts +1 -1
  11. package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
  12. package/dist/Config/internal/configSecretURL.js +2 -2
  13. package/dist/Config.d.ts +7 -0
  14. package/dist/Config.d.ts.map +1 -0
  15. package/dist/Config.js +6 -0
  16. package/dist/ConfigProvider.d.ts +39 -0
  17. package/dist/ConfigProvider.d.ts.map +1 -0
  18. package/dist/ConfigProvider.js +42 -0
  19. package/dist/Context.d.ts +42 -0
  20. package/dist/Context.d.ts.map +1 -0
  21. package/dist/Context.js +67 -0
  22. package/dist/Effect.d.ts +11 -10
  23. package/dist/Effect.d.ts.map +1 -1
  24. package/dist/Effect.js +5 -7
  25. package/dist/Function.d.ts +1 -1
  26. package/dist/Function.d.ts.map +1 -1
  27. package/dist/Inputify.type.d.ts +1 -1
  28. package/dist/Layer.d.ts +11 -7
  29. package/dist/Layer.d.ts.map +1 -1
  30. package/dist/Layer.js +3 -2
  31. package/dist/NonEmptySet.d.ts +4 -2
  32. package/dist/NonEmptySet.d.ts.map +1 -1
  33. package/dist/NonEmptySet.js +2 -2
  34. package/dist/Option.d.ts +2 -1
  35. package/dist/Option.d.ts.map +1 -1
  36. package/dist/Option.js +3 -1
  37. package/dist/Pure.d.ts +8 -6
  38. package/dist/Pure.d.ts.map +1 -1
  39. package/dist/Pure.js +17 -14
  40. package/dist/Schema/Class.d.ts +66 -20
  41. package/dist/Schema/Class.d.ts.map +1 -1
  42. package/dist/Schema/Class.js +192 -23
  43. package/dist/Schema/FastCheck.d.ts +1 -1
  44. package/dist/Schema/FastCheck.d.ts.map +1 -1
  45. package/dist/Schema/Methods.d.ts +1 -1
  46. package/dist/Schema/SchemaParser.d.ts +5 -0
  47. package/dist/Schema/SchemaParser.d.ts.map +1 -0
  48. package/dist/Schema/SchemaParser.js +6 -0
  49. package/dist/Schema/SpecialJsonSchema.d.ts +34 -0
  50. package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
  51. package/dist/Schema/SpecialJsonSchema.js +118 -0
  52. package/dist/Schema/SpecialOpenApi.d.ts +32 -0
  53. package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
  54. package/dist/Schema/SpecialOpenApi.js +123 -0
  55. package/dist/Schema/brand.d.ts +5 -3
  56. package/dist/Schema/brand.d.ts.map +1 -1
  57. package/dist/Schema/brand.js +3 -1
  58. package/dist/Schema/email.d.ts +1 -1
  59. package/dist/Schema/email.d.ts.map +1 -1
  60. package/dist/Schema/email.js +7 -4
  61. package/dist/Schema/ext.d.ts +339 -56
  62. package/dist/Schema/ext.d.ts.map +1 -1
  63. package/dist/Schema/ext.js +350 -53
  64. package/dist/Schema/moreStrings.d.ts +108 -26
  65. package/dist/Schema/moreStrings.d.ts.map +1 -1
  66. package/dist/Schema/moreStrings.js +45 -16
  67. package/dist/Schema/numbers.d.ts +55 -15
  68. package/dist/Schema/numbers.d.ts.map +1 -1
  69. package/dist/Schema/numbers.js +60 -12
  70. package/dist/Schema/phoneNumber.d.ts +1 -1
  71. package/dist/Schema/phoneNumber.d.ts.map +1 -1
  72. package/dist/Schema/phoneNumber.js +6 -3
  73. package/dist/Schema/schema.d.ts +1 -1
  74. package/dist/Schema/strings.d.ts +5 -5
  75. package/dist/Schema/strings.d.ts.map +1 -1
  76. package/dist/Schema/strings.js +1 -5
  77. package/dist/Schema.d.ts +214 -8
  78. package/dist/Schema.d.ts.map +1 -1
  79. package/dist/Schema.js +219 -21
  80. package/dist/Set.d.ts +5 -2
  81. package/dist/Set.d.ts.map +1 -1
  82. package/dist/Set.js +3 -2
  83. package/dist/TypeTest.d.ts +1 -1
  84. package/dist/Types.d.ts +1 -1
  85. package/dist/Widen.type.d.ts +1 -1
  86. package/dist/_ext/Array.d.ts +2 -2
  87. package/dist/_ext/Array.d.ts.map +1 -1
  88. package/dist/_ext/Array.js +4 -2
  89. package/dist/_ext/date.d.ts +1 -1
  90. package/dist/_ext/misc.d.ts +5 -2
  91. package/dist/_ext/misc.d.ts.map +1 -1
  92. package/dist/_ext/misc.js +4 -2
  93. package/dist/_ext/ord.ext.d.ts +3 -2
  94. package/dist/_ext/ord.ext.d.ts.map +1 -1
  95. package/dist/_ext/ord.ext.js +2 -2
  96. package/dist/builtin.d.ts +1 -1
  97. package/dist/builtin.d.ts.map +1 -1
  98. package/dist/client/InvalidationKeys.d.ts +29 -0
  99. package/dist/client/InvalidationKeys.d.ts.map +1 -0
  100. package/dist/client/InvalidationKeys.js +33 -0
  101. package/dist/client/apiClientFactory.d.ts +20 -32
  102. package/dist/client/apiClientFactory.d.ts.map +1 -1
  103. package/dist/client/apiClientFactory.js +97 -34
  104. package/dist/client/clientFor.d.ts +51 -17
  105. package/dist/client/clientFor.d.ts.map +1 -1
  106. package/dist/client/clientFor.js +9 -1
  107. package/dist/client/errors.d.ts +49 -25
  108. package/dist/client/errors.d.ts.map +1 -1
  109. package/dist/client/errors.js +43 -17
  110. package/dist/client/makeClient.d.ts +481 -33
  111. package/dist/client/makeClient.d.ts.map +1 -1
  112. package/dist/client/makeClient.js +66 -24
  113. package/dist/client.d.ts +2 -1
  114. package/dist/client.d.ts.map +1 -1
  115. package/dist/client.js +2 -1
  116. package/dist/faker.d.ts +1 -1
  117. package/dist/faker.d.ts.map +1 -1
  118. package/dist/http/Request.d.ts +2 -2
  119. package/dist/http/Request.d.ts.map +1 -1
  120. package/dist/http/Request.js +2 -2
  121. package/dist/http/internal/lib.d.ts +1 -1
  122. package/dist/http.d.ts +1 -1
  123. package/dist/ids.d.ts +40 -12
  124. package/dist/ids.d.ts.map +1 -1
  125. package/dist/ids.js +25 -3
  126. package/dist/index.d.ts +5 -8
  127. package/dist/index.d.ts.map +1 -1
  128. package/dist/index.js +6 -8
  129. package/dist/logger.d.ts +1 -1
  130. package/dist/middleware.d.ts +14 -8
  131. package/dist/middleware.d.ts.map +1 -1
  132. package/dist/middleware.js +14 -8
  133. package/dist/rpc/Invalidation.d.ts +402 -0
  134. package/dist/rpc/Invalidation.d.ts.map +1 -0
  135. package/dist/rpc/Invalidation.js +150 -0
  136. package/dist/rpc/MiddlewareMaker.d.ts +11 -7
  137. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  138. package/dist/rpc/MiddlewareMaker.js +59 -38
  139. package/dist/rpc/RpcContextMap.d.ts +4 -4
  140. package/dist/rpc/RpcContextMap.d.ts.map +1 -1
  141. package/dist/rpc/RpcContextMap.js +4 -4
  142. package/dist/rpc/RpcMiddleware.d.ts +14 -10
  143. package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
  144. package/dist/rpc/RpcMiddleware.js +1 -1
  145. package/dist/rpc.d.ts +2 -2
  146. package/dist/rpc.d.ts.map +1 -1
  147. package/dist/rpc.js +2 -2
  148. package/dist/transform.d.ts +2 -2
  149. package/dist/transform.d.ts.map +1 -1
  150. package/dist/transform.js +4 -5
  151. package/dist/utils/effectify.d.ts +2 -2
  152. package/dist/utils/effectify.d.ts.map +1 -1
  153. package/dist/utils/effectify.js +2 -2
  154. package/dist/utils/extend.d.ts +1 -1
  155. package/dist/utils/extend.d.ts.map +1 -1
  156. package/dist/utils/gen.d.ts +5 -5
  157. package/dist/utils/gen.d.ts.map +1 -1
  158. package/dist/utils/logLevel.d.ts +3 -3
  159. package/dist/utils/logLevel.d.ts.map +1 -1
  160. package/dist/utils/logger.d.ts +5 -4
  161. package/dist/utils/logger.d.ts.map +1 -1
  162. package/dist/utils/logger.js +4 -4
  163. package/dist/utils.d.ts +35 -40
  164. package/dist/utils.d.ts.map +1 -1
  165. package/dist/utils.js +19 -27
  166. package/dist/validation/validators.d.ts +1 -1
  167. package/dist/validation/validators.d.ts.map +1 -1
  168. package/dist/validation.d.ts +1 -1
  169. package/dist/validation.d.ts.map +1 -1
  170. package/package.json +46 -24
  171. package/src/Array.ts +3 -3
  172. package/src/Config/SecretURL.ts +5 -2
  173. package/src/Config/internal/configSecretURL.ts +1 -1
  174. package/src/Config.ts +14 -0
  175. package/src/ConfigProvider.ts +48 -0
  176. package/src/{ServiceMap.ts → Context.ts} +56 -63
  177. package/src/Effect.ts +13 -14
  178. package/src/Layer.ts +10 -6
  179. package/src/NonEmptySet.ts +3 -1
  180. package/src/Option.ts +2 -0
  181. package/src/Pure.ts +21 -19
  182. package/src/Schema/Class.ts +274 -64
  183. package/src/Schema/SchemaParser.ts +12 -0
  184. package/src/Schema/SpecialJsonSchema.ts +139 -0
  185. package/src/Schema/SpecialOpenApi.ts +130 -0
  186. package/src/Schema/brand.ts +22 -2
  187. package/src/Schema/email.ts +7 -2
  188. package/src/Schema/ext.ts +431 -88
  189. package/src/Schema/moreStrings.ts +93 -37
  190. package/src/Schema/numbers.ts +64 -16
  191. package/src/Schema/phoneNumber.ts +5 -1
  192. package/src/Schema/strings.ts +4 -8
  193. package/src/Schema.ts +404 -18
  194. package/src/Set.ts +5 -1
  195. package/src/_ext/Array.ts +3 -1
  196. package/src/_ext/misc.ts +4 -1
  197. package/src/_ext/ord.ext.ts +2 -1
  198. package/src/client/InvalidationKeys.ts +50 -0
  199. package/src/client/apiClientFactory.ts +225 -131
  200. package/src/client/clientFor.ts +95 -29
  201. package/src/client/errors.ts +52 -26
  202. package/src/client/makeClient.ts +572 -71
  203. package/src/client.ts +1 -0
  204. package/src/http/Request.ts +1 -1
  205. package/src/ids.ts +25 -3
  206. package/src/index.ts +5 -10
  207. package/src/middleware.ts +13 -9
  208. package/src/rpc/Invalidation.ts +226 -0
  209. package/src/rpc/MiddlewareMaker.ts +82 -74
  210. package/src/rpc/README.md +2 -2
  211. package/src/rpc/RpcContextMap.ts +6 -5
  212. package/src/rpc/RpcMiddleware.ts +14 -11
  213. package/src/rpc.ts +1 -1
  214. package/src/transform.ts +3 -3
  215. package/src/utils/effectify.ts +1 -1
  216. package/src/utils/gen.ts +8 -8
  217. package/src/utils/logLevel.ts +1 -1
  218. package/src/utils/logger.ts +4 -3
  219. package/src/utils.ts +57 -134
  220. package/test/dist/rpc.test.d.ts.map +1 -1
  221. package/test/dist/secretURL.test.d.ts.map +1 -0
  222. package/test/dist/special.test.d.ts.map +1 -0
  223. package/test/dist/stream-error.types.d.ts +2 -0
  224. package/test/dist/stream-error.types.d.ts.map +1 -0
  225. package/test/dist/stream-error.types.js +27 -0
  226. package/test/moreStrings.test.ts +1 -1
  227. package/test/rpc.test.ts +46 -6
  228. package/test/schema.test.ts +585 -10
  229. package/test/secretURL.test.ts +160 -0
  230. package/test/special.test.ts +1026 -0
  231. package/test/utils.test.ts +7 -7
  232. package/tsconfig.base.json +3 -4
  233. package/tsconfig.json +0 -1
  234. package/tsconfig.json.bak +2 -2
  235. package/tsconfig.src.json +29 -29
  236. package/tsconfig.test.json +2 -2
  237. package/dist/Operations.d.ts +0 -123
  238. package/dist/Operations.d.ts.map +0 -1
  239. package/dist/Operations.js +0 -29
  240. package/dist/ServiceMap.d.ts +0 -44
  241. package/dist/ServiceMap.d.ts.map +0 -1
  242. package/dist/ServiceMap.js +0 -91
  243. package/eslint.config.mjs +0 -26
  244. package/src/Operations.ts +0 -55
package/src/Schema/ext.ts CHANGED
@@ -1,8 +1,47 @@
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, SchemaTransformation, ServiceMap } from "effect"
3
+ /**
4
+ * # `withConstructorDefault` policy
5
+ *
6
+ * The `withConstructorDefault` properties exported throughout this module
7
+ * (and from `numbers.ts`, `moreStrings.ts`, `ids.ts`) attach a default value
8
+ * that is **only** applied during construction — i.e. when the field is
9
+ * omitted from the input to a Schema constructor / `.make(...)` call.
10
+ *
11
+ * They are **NOT** applied during `decode` (JSON, database rows, RPC payloads,
12
+ * etc.). Decoding a payload with a missing field will still fail with a parse
13
+ * error, exactly as if the default were not present.
14
+ *
15
+ * Concretely this means `withConstructorDefault` MUST NOT be relied on as a
16
+ * just-in-time migration mechanism for database fields. If a stored record is
17
+ * missing a newly added field, the constructor default will not fill it in on
18
+ * read — decoding will fail.
19
+ *
20
+ * ## Don't reach for `withDecodingDefault*` either
21
+ *
22
+ * The sibling `withDecodingDefaultType` (and `withDecodingDefault`) extensions
23
+ * exist, but they are discouraged for migrating persisted data. A missing
24
+ * field in a stored record is just as likely to be data corruption as it is
25
+ * an old-shape document; silently substituting a default hides the problem
26
+ * and can poison downstream aggregates.
27
+ *
28
+ * Prefer an **explicit, preferably versioned** migration of database data
29
+ * (a schema-version field, a one-shot backfill, or a transform on read that
30
+ * is gated on an explicit version marker) over shoving missing fields under
31
+ * the rug with a decode-time default.
32
+ */
33
+ import * as Config from "effect/Config"
34
+ import * as Effect from "effect/Effect"
35
+ import { pipe } from "effect/Function"
36
+ import * as Function from "effect/Function"
37
+ import * as Option from "effect/Option"
4
38
  import * as S from "effect/Schema"
39
+ import { isDateValid } from "effect/Schema"
40
+ import type * as SchemaAST from "effect/SchemaAST"
41
+ import * as SchemaIssue from "effect/SchemaIssue"
42
+ import * as SchemaTransformation from "effect/SchemaTransformation"
5
43
  import { type NonEmptyReadonlyArray } from "../Array.js"
44
+ import * as Context from "../Context.js"
6
45
  import { extendM, typedKeysOf } from "../utils.js"
7
46
  import { type AST } from "./schema.js"
8
47
 
@@ -13,131 +52,417 @@ type ProvidedCodec<Self extends S.Top, R> = S.Codec<
13
52
  Exclude<Self["EncodingServices"], R>
14
53
  >
15
54
 
16
- // TODO: v4 migration — withConstructorDefault signature changed, propertySignature removed
17
- // Constraint relaxed from `Self extends S.Top & S.WithoutConstructorDefault` to `Self extends S.Top`
18
- // because `.pipe()` widens the schema type to `Top` which doesn't satisfy `WithoutConstructorDefault`.
19
- // The narrowing assertions below are safe — we're asserting "this schema hasn't had a default applied yet".
20
- export const withDefaultConstructor = <A>(
21
- makeDefault: () => NoInfer<A>
22
- ) =>
23
- <Self extends S.Top>(self: Self): S.withConstructorDefault<Self & S.WithoutConstructorDefault> => {
24
- type Narrowed = Self & S.WithoutConstructorDefault
25
- return S.withConstructorDefault<Narrowed>(
26
- () => Option.some(makeDefault() as Narrowed["~type.make.in"])
27
- )(self as Narrowed)
55
+ const concurrencySetting = Effect.runSync(
56
+ Config
57
+ .literal("unbounded", "SCHEMA_CONCURRENCY")
58
+ .pipe(Config.orElse(() => Config.number("SCHEMA_CONCURRENCY")), Config.option)
59
+ )
60
+
61
+ export const DefaultParseOptions: SchemaAST.ParseOptions = {
62
+ concurrency: Option.getOrElse(concurrencySetting, () => "unbounded" as const)
28
63
  }
29
64
 
65
+ /**
66
+ * Parse-options annotation used on schema constructors for decode paths where callers
67
+ * cannot currently pass parse options (notably some RPC / HttpApi integration paths).
68
+ *
69
+ * Keep this annotation in place so those framework-managed decodes still run with
70
+ * unbounded concurrency by default.
71
+ */
72
+ export const concurrencyUnbounded = { parseOptions: DefaultParseOptions } as const
73
+
74
+ type DecodeLike = (schema: any) => (input: any, options?: SchemaAST.ParseOptions) => any
75
+
76
+ export const withDefaultParseOptions = <Decode extends DecodeLike>(
77
+ decode: Decode,
78
+ defaultParseOptions: SchemaAST.ParseOptions = DefaultParseOptions
79
+ ): Decode =>
80
+ ((schema: any) => {
81
+ const run = decode(schema)
82
+ return (input: any, options?: SchemaAST.ParseOptions) => run(input, { ...defaultParseOptions, ...options })
83
+ }) as Decode
84
+
30
85
  // TODO: v4 migration - Date is no longer by default encoded to string.
31
- const DateFromString = Schema.Date.pipe(
32
- Schema.encodeTo(Schema.String, {
33
- decode: SchemaGetter.Date(),
34
- encode: SchemaGetter.transform((_) => _.toISOString())
35
- })
36
- )
86
+
87
+ const DateString = S.String.annotate({
88
+ identifier: "Date",
89
+ description: "a string in ISO 8601 format that will be decoded as a Date",
90
+ format: "date-time"
91
+ })
37
92
 
38
93
  /**
39
- * Like the default Schema `Date` but from String with `withDefault` => now
94
+ * Schema type for {@link DateFromString}.
95
+ *
96
+ * @category Schemas
97
+ * @since 4.0.0
40
98
  */
41
- export const Date = Object.assign(DateFromString, {
42
- withDefault: DateFromString.pipe(withDefaultConstructor(() => new global.Date()))
43
- })
99
+ export interface DateFromString extends S.decodeTo<S.Date, S.String> {}
44
100
 
45
101
  /**
46
- * Like the default Schema `Boolean` but with `withDefault` => false
102
+ * A transformation schema that parses an ISO 8601 string into a `Date`.
103
+ *
104
+ * Decoding:
105
+ * - A `string` is decoded as a `Date`.
106
+ *
107
+ * Encoding:
108
+ * - A `Date` is encoded as a `string`.
109
+ *
110
+ * @since 4.0.0
47
111
  */
112
+ export const DateFromString: DateFromString = DateString.pipe(S.decodeTo(S.Date, SchemaTransformation.dateFromString))
113
+
114
+ /** Like the default Schema `Date` but from String, with default helpers. */
115
+ export const Date = Object.assign(DateFromString, {
116
+ /**
117
+ * Construction-only default `new Date()`. Applied only when the field is
118
+ * omitted from `.make(...)` input. NOT applied during decode — cannot be
119
+ * used to JIT-migrate database fields. See file-level note.
120
+ */
121
+ withConstructorDefault: DateFromString.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date()))),
122
+ /**
123
+ * Decode-time default `new Date()`. **Discouraged for persisted data:** a
124
+ * missing field may be data corruption, not an old-shape document; silently
125
+ * substituting `new Date()` hides the problem. Prefer an explicit,
126
+ * preferably versioned migration over a decode-time fallback. See
127
+ * file-level note.
128
+ */
129
+ withDecodingDefaultType: DateFromString.pipe(S.withDecodingDefaultType(Effect.sync(() => new global.Date())))
130
+ })
131
+
132
+ /** Like the default Schema `DateValid` but from String, with default helpers. */
133
+ export const DateValid = Object.assign(Date.check(isDateValid()), {
134
+ /**
135
+ * Construction-only default `new Date()`. Applied only when the field is
136
+ * omitted from `.make(...)` input. NOT applied during decode — cannot be
137
+ * used to JIT-migrate database fields. See file-level note.
138
+ */
139
+ withConstructorDefault: DateFromString.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date()))),
140
+ /**
141
+ * Decode-time default `new Date()`. **Discouraged for persisted data:** a
142
+ * missing field may be data corruption, not an old-shape document; silently
143
+ * substituting `new Date()` hides the problem. Prefer an explicit,
144
+ * preferably versioned migration over a decode-time fallback. See
145
+ * file-level note.
146
+ */
147
+ withDecodingDefaultType: DateFromString.pipe(S.withDecodingDefaultType(Effect.sync(() => new global.Date())))
148
+ })
149
+
150
+ /** Like the default Schema `Boolean` but with default helpers. */
48
151
  export const Boolean = Object.assign(S.Boolean, {
49
- withDefault: S.Boolean.pipe(withDefaultConstructor(() => false))
152
+ /**
153
+ * Construction-only default `false`. Applied only when the field is
154
+ * omitted from `.make(...)` input. NOT applied during decode — cannot be
155
+ * used to JIT-migrate database fields. See file-level note.
156
+ */
157
+ withConstructorDefault: S.Boolean.pipe(S.withConstructorDefault(Effect.succeed(false))),
158
+ /**
159
+ * Decode-time default `false`. **Discouraged for persisted data:** a
160
+ * missing field may be data corruption, not an old-shape document; silently
161
+ * substituting `false` hides the problem. Prefer an explicit, preferably
162
+ * versioned migration over a decode-time fallback. See file-level note.
163
+ */
164
+ withDecodingDefaultType: S.Boolean.pipe(S.withDecodingDefaultType(Effect.succeed(false)))
50
165
  })
51
166
 
52
167
  /**
53
- * Like the default Schema `Number` but with `withDefault` => 0
168
+ * You probably want to use `Finite` instead of this. Like the default Schema
169
+ * `Number` but with default helpers.
54
170
  */
55
- export const Number = Object.assign(S.Number, { withDefault: S.Number.pipe(withDefaultConstructor(() => 0)) })
171
+ export const Number = Object.assign(S.Number, {
172
+ /**
173
+ * Construction-only default `0`. Applied only when the field is omitted
174
+ * from `.make(...)` input. NOT applied during decode — cannot be used to
175
+ * JIT-migrate database fields. See file-level note.
176
+ */
177
+ withConstructorDefault: S.Number.pipe(S.withConstructorDefault(Effect.succeed(0))),
178
+ /**
179
+ * Decode-time default `0`. **Discouraged for persisted data:** a missing
180
+ * field may be data corruption, not an old-shape document; silently
181
+ * substituting `0` hides the problem. Prefer an explicit, preferably
182
+ * versioned migration over a decode-time fallback. See file-level note.
183
+ */
184
+ withDecodingDefaultType: S.Number.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
185
+ })
56
186
 
57
- /**
58
- * Like the default Schema `Literal` but with `withDefault` => literals[0]
59
- */
60
- export const Literal = <Literals extends NonEmptyReadonlyArray<AST.LiteralValue>>(...literals: Literals) =>
187
+ /** Like the default Schema `Finite` but with default helpers. */
188
+ export const Finite = Object.assign(S.Finite, {
189
+ /**
190
+ * Construction-only default `0`. Applied only when the field is omitted
191
+ * from `.make(...)` input. NOT applied during decode — cannot be used to
192
+ * JIT-migrate database fields. See file-level note.
193
+ */
194
+ withConstructorDefault: S.Finite.pipe(S.withConstructorDefault(Effect.succeed(0))),
195
+ /**
196
+ * Decode-time default `0`. **Discouraged for persisted data:** a missing
197
+ * field may be data corruption, not an old-shape document; silently
198
+ * substituting `0` hides the problem. Prefer an explicit, preferably
199
+ * versioned migration over a decode-time fallback. See file-level note.
200
+ */
201
+ withDecodingDefaultType: S.Finite.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
202
+ })
203
+
204
+ /** Like the default Schema `Literals` but with default helpers. Default value is `literals[0]`. */
205
+ export const Literals = <const Literals extends NonEmptyReadonlyArray<AST.LiteralValue>>(literals: Literals) =>
61
206
  pipe(
62
207
  S.Literals(literals),
63
208
  (s) =>
64
209
  Object.assign(s, {
210
+ /** Override the default literal value used by `withConstructorDefault` / `withDecodingDefaultType`. */
65
211
  changeDefault: <A extends Literals[number]>(a: A) => {
66
212
  return Object.assign(S.Literals(literals), {
67
213
  Default: a,
68
- withDefault: s.pipe(withDefaultConstructor(() => a))
214
+ /**
215
+ * Construction-only default. Applied only when the field is
216
+ * omitted from `.make(...)` input. NOT applied during decode —
217
+ * cannot be used to JIT-migrate database fields. See file-level
218
+ * note.
219
+ */
220
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(a))),
221
+ /**
222
+ * Decode-time default. **Discouraged for persisted data:** a
223
+ * missing field may be data corruption, not an old-shape
224
+ * document; silently substituting hides the problem. Prefer an
225
+ * explicit, preferably versioned migration over a decode-time
226
+ * fallback. See file-level note.
227
+ */
228
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(a)))
69
229
  }) // todo: copy annotations from original?
70
230
  },
71
- Default: literals[0] as typeof literals[0],
72
- withDefault: s.pipe(withDefaultConstructor(() => literals[0]))
231
+ // 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
232
+ Default: literals[0] as Literals[0],
233
+ /**
234
+ * Construction-only default `literals[0]`. Applied only when the
235
+ * field is omitted from `.make(...)` input. NOT applied during
236
+ * decode — cannot be used to JIT-migrate database fields. See
237
+ * file-level note.
238
+ */
239
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(literals[0]))),
240
+ /**
241
+ * Decode-time default `literals[0]`. **Discouraged for persisted
242
+ * data:** a missing field may be data corruption, not an old-shape
243
+ * document; silently substituting hides the problem. Prefer an
244
+ * explicit, preferably versioned migration over a decode-time
245
+ * fallback. See file-level note.
246
+ */
247
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(literals[0])))
73
248
  })
74
249
  )
75
250
 
76
- /**
77
- * Like the default Schema `Array` but with `withDefault` => []
78
- */
251
+ /** Like the default Schema `Array` but with default helpers. */
79
252
  export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
80
253
  return pipe(
81
- S.Array(value),
82
- (s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => [])) })
254
+ S.Array(value).annotate(concurrencyUnbounded),
255
+ (s) =>
256
+ Object.assign(s, {
257
+ /**
258
+ * Construction-only default `[]`. Applied only when the field is
259
+ * omitted from `.make(...)` input. NOT applied during decode —
260
+ * cannot be used to JIT-migrate database fields. See file-level
261
+ * note.
262
+ */
263
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => []))),
264
+ /**
265
+ * Decode-time default `[]`. **Discouraged for persisted data:** a
266
+ * missing field may be data corruption, not an old-shape document;
267
+ * silently substituting `[]` hides the problem. Prefer an explicit,
268
+ * preferably versioned migration over a decode-time fallback. See
269
+ * file-level note.
270
+ */
271
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => [])))
272
+ })
83
273
  )
84
274
  }
85
275
 
86
276
  /**
87
- * Like the default Schema `Map` but with `withDefault` => []
277
+ * An annotated `S.Array` of unique items that decodes to a `ReadonlySet`.
88
278
  */
89
- function Map_<KeySchema extends S.Top, ValueSchema extends S.Top>(input: { key: KeySchema; value: ValueSchema }) {
90
- return pipe(
91
- S.ReadonlyMap(input.key, input.value),
92
- (s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => new global.Map())) })
279
+ export const ReadonlySetFromArray = <ValueSchema extends S.Top>(value: ValueSchema) => {
280
+ const from = S
281
+ .Array(value)
282
+ .annotate({ ...concurrencyUnbounded, expected: "an array of unique items that will be decoded as a ReadonlySet" })
283
+ const to = S.instanceOf(Set) as S.instanceOf<ReadonlySet<S.Schema.Type<ValueSchema>>>
284
+ const schema = from.pipe(
285
+ S.decodeTo(
286
+ to,
287
+ SchemaTransformation.transform({
288
+ decode: (arr) => new Set(arr) as ReadonlySet<S.Schema.Type<ValueSchema>>,
289
+ encode: (set) => [...set]
290
+ })
291
+ )
93
292
  )
293
+ return schema
94
294
  }
95
295
 
96
- export { Map_ as Map }
97
-
98
296
  /**
99
- * Like the default Schema `ReadonlySet` but with `withDefault` => new Set()
297
+ * An annotated `S.Array` of key-value tuples that decodes to a `ReadonlyMap`.
100
298
  */
299
+ export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
300
+ readonly key: KeySchema
301
+ readonly value: ValueSchema
302
+ }) => {
303
+ const from = S
304
+ .Array(S.Tuple([pair.key, pair.value]))
305
+ .annotate({
306
+ ...concurrencyUnbounded,
307
+ expected: "an array of key-value tuples that will be decoded as a ReadonlyMap"
308
+ })
309
+ const to = S.instanceOf(Map) as S.instanceOf<
310
+ ReadonlyMap<S.Schema.Type<KeySchema>, S.Schema.Type<ValueSchema>>
311
+ >
312
+ const schema = from.pipe(
313
+ S.decodeTo(
314
+ to,
315
+ SchemaTransformation.transform({
316
+ decode: (
317
+ arr
318
+ ) => new Map(arr) as ReadonlyMap<S.Schema.Type<KeySchema>, S.Schema.Type<ValueSchema>>,
319
+ encode: (
320
+ map
321
+ ) => [...map.entries()] as any // fu
322
+ })
323
+ )
324
+ )
325
+ return schema
326
+ }
327
+
328
+ /** Like the default Schema `ReadonlySet` but from Array, with default helpers. */
101
329
  export const ReadonlySet = <ValueSchema extends S.Top>(value: ValueSchema) =>
102
330
  pipe(
103
- S.ReadonlySet(value),
331
+ ReadonlySetFromArray(value),
104
332
  (s) =>
105
- Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => new Set<S.Schema.Type<ValueSchema>>())) })
333
+ Object.assign(s, {
334
+ /**
335
+ * Construction-only default `new Set()`. Applied only when the field
336
+ * is omitted from `.make(...)` input. NOT applied during decode —
337
+ * cannot be used to JIT-migrate database fields. See file-level
338
+ * note.
339
+ */
340
+ withConstructorDefault: s.pipe(
341
+ S.withConstructorDefault(Effect.sync(() => new Set<S.Schema.Type<ValueSchema>>()))
342
+ ),
343
+ /**
344
+ * Decode-time default `new Set()`. **Discouraged for persisted
345
+ * data:** a missing field may be data corruption, not an old-shape
346
+ * document; silently substituting an empty set hides the problem.
347
+ * Prefer an explicit, preferably versioned migration over a
348
+ * decode-time fallback. See file-level note.
349
+ */
350
+ withDecodingDefaultType: s.pipe(
351
+ S.withDecodingDefaultType(Effect.sync(() => new Set<S.Schema.Type<ValueSchema>>()))
352
+ )
353
+ })
106
354
  )
107
355
 
108
- /**
109
- * Like the default Schema `ReadonlyMap` but with `withDefault` => new Map()
110
- */
356
+ /** Like the default Schema `ReadonlyMap` but from Array, with default helpers. */
111
357
  export const ReadonlyMap = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
112
358
  readonly key: KeySchema
113
359
  readonly value: ValueSchema
114
360
  }) =>
115
361
  pipe(
116
- S.ReadonlyMap(pair.key, pair.value),
117
- (s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => new Map())) })
362
+ ReadonlyMapFromArray(pair),
363
+ (s) =>
364
+ Object.assign(s, {
365
+ /**
366
+ * Construction-only default `new Map()`. Applied only when the field
367
+ * is omitted from `.make(...)` input. NOT applied during decode —
368
+ * cannot be used to JIT-migrate database fields. See file-level
369
+ * note.
370
+ */
371
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Map()))),
372
+ /**
373
+ * Decode-time default `new Map()`. **Discouraged for persisted
374
+ * data:** a missing field may be data corruption, not an old-shape
375
+ * document; silently substituting an empty map hides the problem.
376
+ * Prefer an explicit, preferably versioned migration over a
377
+ * decode-time fallback. See file-level note.
378
+ */
379
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new Map())))
380
+ })
118
381
  )
119
382
 
120
- /**
121
- * Like the default Schema `NullOr` but with `withDefault` => null
122
- */
383
+ /** Like the default Schema `NullOr` but with default helpers. */
123
384
  export const NullOr = <Schema extends S.Top>(self: Schema) =>
124
385
  pipe(
125
386
  S.NullOr(self),
126
- (s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => null)) })
387
+ (s) =>
388
+ Object.assign(s, {
389
+ /**
390
+ * Construction-only default `null`. Applied only when the field is
391
+ * omitted from `.make(...)` input. NOT applied during decode —
392
+ * cannot be used to JIT-migrate database fields. See file-level
393
+ * note.
394
+ */
395
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(null))),
396
+ /**
397
+ * Decode-time default `null`. **Discouraged for persisted data:** a
398
+ * missing field may be data corruption, not an old-shape document;
399
+ * silently substituting `null` hides the problem. Prefer an
400
+ * explicit, preferably versioned migration over a decode-time
401
+ * fallback. See file-level note.
402
+ */
403
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(null)))
404
+ })
127
405
  )
128
406
 
129
- export const defaultDate = <Schema extends S.Top>(schema: Schema) =>
130
- schema.pipe(withDefaultConstructor(() => new global.Date()))
407
+ /**
408
+ * Attach a `withConstructorDefault` of `new Date()` to any schema.
409
+ *
410
+ * **Construction-only.** Applied only when the field is omitted from
411
+ * `.make(...)` input. NOT applied during decode — cannot be used to
412
+ * JIT-migrate database fields. See file-level note.
413
+ */
414
+ export const defaultDate = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
415
+ schema.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date())))
131
416
 
132
- export const defaultBool = <Schema extends S.Top>(schema: Schema) => schema.pipe(withDefaultConstructor(() => false))
417
+ /**
418
+ * Attach a `withConstructorDefault` of `false` to any schema.
419
+ *
420
+ * **Construction-only.** Applied only when the field is omitted from
421
+ * `.make(...)` input. NOT applied during decode — cannot be used to
422
+ * JIT-migrate database fields. See file-level note.
423
+ */
424
+ export const defaultBool = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
425
+ schema.pipe(S.withConstructorDefault(Effect.succeed(false)))
133
426
 
134
- export const defaultNullable = <Schema extends S.Top>(schema: Schema) => schema.pipe(withDefaultConstructor(() => null))
427
+ /**
428
+ * Attach a `withConstructorDefault` of `null` to any schema.
429
+ *
430
+ * **Construction-only.** Applied only when the field is omitted from
431
+ * `.make(...)` input. NOT applied during decode — cannot be used to
432
+ * JIT-migrate database fields. See file-level note.
433
+ */
434
+ export const defaultNullable = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
435
+ schema.pipe(S.withConstructorDefault(Effect.succeed(null)))
135
436
 
136
- export const defaultArray = <Schema extends S.Top>(schema: Schema) => schema.pipe(withDefaultConstructor(() => []))
437
+ /**
438
+ * Attach a `withConstructorDefault` of `[]` to any schema.
439
+ *
440
+ * **Construction-only.** Applied only when the field is omitted from
441
+ * `.make(...)` input. NOT applied during decode — cannot be used to
442
+ * JIT-migrate database fields. See file-level note.
443
+ */
444
+ export const defaultArray = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
445
+ schema.pipe(S.withConstructorDefault(Effect.sync(() => [])))
137
446
 
138
- export const defaultMap = <Schema extends S.Top>(schema: Schema) => schema.pipe(withDefaultConstructor(() => new Map()))
447
+ /**
448
+ * Attach a `withConstructorDefault` of `new Map()` to any schema.
449
+ *
450
+ * **Construction-only.** Applied only when the field is omitted from
451
+ * `.make(...)` input. NOT applied during decode — cannot be used to
452
+ * JIT-migrate database fields. See file-level note.
453
+ */
454
+ export const defaultMap = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
455
+ schema.pipe(S.withConstructorDefault(Effect.sync(() => new Map())))
139
456
 
140
- export const defaultSet = <Schema extends S.Top>(schema: Schema) => schema.pipe(withDefaultConstructor(() => new Set()))
457
+ /**
458
+ * Attach a `withConstructorDefault` of `new Set()` to any schema.
459
+ *
460
+ * **Construction-only.** Applied only when the field is omitted from
461
+ * `.make(...)` input. NOT applied during decode — cannot be used to
462
+ * JIT-migrate database fields. See file-level note.
463
+ */
464
+ export const defaultSet = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
465
+ schema.pipe(S.withConstructorDefault(Effect.sync(() => new Set())))
141
466
 
142
467
  export const withDefaultMake = <Self extends S.Top>(s: Self) => {
143
468
  const a = Object.assign(S.decodeSync(s as any) as WithDefaults<Self>, s)
@@ -166,9 +491,25 @@ export type WithDefaults<Self extends S.Top> = (
166
491
  // export type UnionToIntersection3<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I
167
492
  // : never
168
493
 
494
+ /** Union of `DateValid` and `Date`, with default helpers. */
169
495
  export const inputDate = extendM(
170
- S.Union([S.DateValid, S.Date]),
171
- (s) => ({ withDefault: s.pipe(withDefaultConstructor(() => new globalThis.Date())) })
496
+ S.Union([S.DateValid, Date]),
497
+ (s) => ({
498
+ /**
499
+ * Construction-only default `new Date()`. Applied only when the field is
500
+ * omitted from `.make(...)` input. NOT applied during decode — cannot be
501
+ * used to JIT-migrate database fields. See file-level note.
502
+ */
503
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))),
504
+ /**
505
+ * Decode-time default `new Date()`. **Discouraged for persisted data:** a
506
+ * missing field may be data corruption, not an old-shape document;
507
+ * silently substituting `new Date()` hides the problem. Prefer an
508
+ * explicit, preferably versioned migration over a decode-time fallback.
509
+ * See file-level note.
510
+ */
511
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date())))
512
+ })
172
513
  )
173
514
 
174
515
  export interface UnionBrand {}
@@ -218,7 +559,7 @@ export const transformTo = <To extends S.Top, From extends S.Top>(
218
559
  { message: "One way schema transformation, encoding is not allowed" }
219
560
  )
220
561
  )
221
- }) as any
562
+ })
222
563
  )
223
564
  )
224
565
 
@@ -235,7 +576,7 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
235
576
  S.decodeTo(
236
577
  to,
237
578
  SchemaTransformation.transformOrFail({
238
- decode: decode as any,
579
+ decode,
239
580
  encode: (i: any) =>
240
581
  Effect.fail(
241
582
  new SchemaIssue.Forbidden(
@@ -243,34 +584,36 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
243
584
  { message: "One way schema transformation, encoding is not allowed" }
244
585
  )
245
586
  )
246
- }) as any
587
+ })
247
588
  )
248
589
  )
249
590
 
250
- export const provide = <Self extends S.Top, R>(
251
- self: Self,
252
- context: ServiceMap.ServiceMap<R>
253
- ): ProvidedCodec<Self, R> => {
591
+ export const provide: {
592
+ <R>(context: Context.Context<R>): <Self extends S.Top>(self: Self) => ProvidedCodec<Self, R>
593
+ <Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R>
594
+ } = Function.dual(2, <Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R> => {
254
595
  const prov = Effect.provide(context)
255
596
  return self.pipe(
256
597
  S.middlewareDecoding((effect) => prov(effect)),
257
598
  S.middlewareEncoding((effect) => prov(effect))
258
- ) as ProvidedCodec<Self, R>
259
- }
260
- export const contextFromServices = <
599
+ )
600
+ })
601
+ export const contextFromServices = Effect.fnUntraced(function*<
261
602
  Self extends S.Top,
262
- Tags extends ReadonlyArray<ServiceMap.Key<any, any>>
603
+ Tags extends ReadonlyArray<Context.Key<any, any>>
604
+ >(self: Self, ...services: Tags) {
605
+ const context: Context.Context<Context.Service.Identifier<Tags[number]>> = Context.pick(...services)(
606
+ yield* Effect.context<Context.Service.Identifier<Tags[number]>>()
607
+ )
608
+ return provide(self, context)
609
+ }) as <
610
+ Self extends S.Top,
611
+ Tags extends ReadonlyArray<Context.Key<any, any>>
263
612
  >(
264
613
  self: Self,
265
614
  ...services: Tags
266
- ): Effect.Effect<
267
- ProvidedCodec<Self, ServiceMap.Service.Identifier<Tags[number]>>,
615
+ ) => Effect.Effect<
616
+ ProvidedCodec<Self, Context.Service.Identifier<Tags[number]>>,
268
617
  never,
269
- ServiceMap.Service.Identifier<Tags[number]>
270
- > =>
271
- Effect.gen(function*() {
272
- const context: ServiceMap.ServiceMap<ServiceMap.Service.Identifier<Tags[number]>> = ServiceMap.pick(...services)(
273
- yield* Effect.services<ServiceMap.Service.Identifier<Tags[number]>>()
274
- )
275
- return provide(self, context)
276
- })
618
+ Context.Service.Identifier<Tags[number]>
619
+ >