effect-app 4.0.0-beta.99 → 4.0.0

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 (371) hide show
  1. package/CHANGELOG.md +2028 -0
  2. package/dist/Array.d.ts +2 -1
  3. package/dist/Array.d.ts.map +1 -1
  4. package/dist/Array.js +4 -4
  5. package/dist/Chunk.d.ts +2 -2
  6. package/dist/Chunk.d.ts.map +1 -1
  7. package/dist/Config/SecretURL.d.ts +3 -1
  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.map +1 -1
  11. package/dist/Config/internal/configSecretURL.js +2 -2
  12. package/dist/Config.d.ts +0 -5
  13. package/dist/Config.d.ts.map +1 -1
  14. package/dist/Config.js +7 -5
  15. package/dist/ConfigProvider.d.ts +0 -37
  16. package/dist/ConfigProvider.d.ts.map +1 -1
  17. package/dist/ConfigProvider.js +5 -41
  18. package/dist/Context.d.ts +6 -4
  19. package/dist/Context.d.ts.map +1 -1
  20. package/dist/Context.js +4 -3
  21. package/dist/DataDependencies.d.ts +136 -0
  22. package/dist/DataDependencies.d.ts.map +1 -0
  23. package/dist/DataDependencies.js +112 -0
  24. package/dist/Effect.d.ts +3 -3
  25. package/dist/Effect.d.ts.map +1 -1
  26. package/dist/Effect.js +6 -9
  27. package/dist/Emailer.d.ts +51 -0
  28. package/dist/Emailer.d.ts.map +1 -0
  29. package/dist/Emailer.js +7 -0
  30. package/dist/Function.d.ts.map +1 -1
  31. package/dist/Layer.d.ts +6 -3
  32. package/dist/Layer.d.ts.map +1 -1
  33. package/dist/Layer.js +3 -2
  34. package/dist/Model/Repository/Registry.d.ts +22 -0
  35. package/dist/Model/Repository/Registry.d.ts.map +1 -0
  36. package/dist/Model/Repository/Registry.js +18 -0
  37. package/dist/Model/Repository/dependency.d.ts +6 -0
  38. package/dist/Model/Repository/dependency.d.ts.map +1 -0
  39. package/dist/Model/Repository/dependency.js +29 -0
  40. package/dist/Model/Repository/ext.d.ts +60 -0
  41. package/dist/Model/Repository/ext.d.ts.map +1 -0
  42. package/dist/Model/Repository/ext.js +124 -0
  43. package/dist/Model/Repository/internal/internal.d.ts +68 -0
  44. package/dist/Model/Repository/internal/internal.d.ts.map +1 -0
  45. package/dist/Model/Repository/internal/internal.js +611 -0
  46. package/dist/Model/Repository/legacy.d.ts +21 -0
  47. package/dist/Model/Repository/legacy.d.ts.map +1 -0
  48. package/dist/Model/Repository/legacy.js +2 -0
  49. package/dist/Model/Repository/makeRepo.d.ts +71 -0
  50. package/dist/Model/Repository/makeRepo.d.ts.map +1 -0
  51. package/dist/Model/Repository/makeRepo.js +30 -0
  52. package/dist/Model/Repository/service.d.ts +121 -0
  53. package/dist/Model/Repository/service.d.ts.map +1 -0
  54. package/dist/Model/Repository/service.js +2 -0
  55. package/dist/Model/Repository/validation.d.ts +72 -0
  56. package/dist/Model/Repository/validation.d.ts.map +1 -0
  57. package/dist/Model/Repository/validation.js +39 -0
  58. package/dist/Model/Repository.d.ts +8 -0
  59. package/dist/Model/Repository.d.ts.map +1 -0
  60. package/dist/Model/Repository.js +8 -0
  61. package/dist/Model/dsl.d.ts +33 -0
  62. package/dist/Model/dsl.d.ts.map +1 -0
  63. package/dist/Model/dsl.js +43 -0
  64. package/dist/Model/filter/filterApi.d.ts +30 -0
  65. package/dist/Model/filter/filterApi.d.ts.map +1 -0
  66. package/dist/Model/filter/filterApi.js +2 -0
  67. package/dist/Model/filter/types/errors.d.ts +29 -0
  68. package/dist/Model/filter/types/errors.d.ts.map +1 -0
  69. package/dist/Model/filter/types/errors.js +2 -0
  70. package/dist/Model/filter/types/fields.d.ts +15 -0
  71. package/dist/Model/filter/types/fields.d.ts.map +1 -0
  72. package/dist/Model/filter/types/fields.js +2 -0
  73. package/dist/Model/filter/types/path/common.d.ts +316 -0
  74. package/dist/Model/filter/types/path/common.d.ts.map +1 -0
  75. package/dist/Model/filter/types/path/common.js +2 -0
  76. package/dist/Model/filter/types/path/eager.d.ts +94 -0
  77. package/dist/Model/filter/types/path/eager.d.ts.map +1 -0
  78. package/dist/Model/filter/types/path/eager.js +36 -0
  79. package/dist/Model/filter/types/path/index.d.ts +4 -0
  80. package/dist/Model/filter/types/path/index.d.ts.map +1 -0
  81. package/dist/Model/filter/types/path/index.js +3 -0
  82. package/dist/Model/filter/types/utils.d.ts +79 -0
  83. package/dist/Model/filter/types/utils.d.ts.map +1 -0
  84. package/dist/Model/filter/types/utils.js +2 -0
  85. package/dist/Model/filter/types/validator.d.ts +30 -0
  86. package/dist/Model/filter/types/validator.d.ts.map +1 -0
  87. package/dist/Model/filter/types/validator.js +2 -0
  88. package/dist/Model/filter/types.d.ts +5 -0
  89. package/dist/Model/filter/types.d.ts.map +1 -0
  90. package/dist/Model/filter/types.js +7 -0
  91. package/dist/Model/query/dsl.d.ts +630 -0
  92. package/dist/Model/query/dsl.d.ts.map +1 -0
  93. package/dist/Model/query/dsl.js +376 -0
  94. package/dist/Model/query/new-kid-interpreter.d.ts +132 -0
  95. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -0
  96. package/dist/Model/query/new-kid-interpreter.js +370 -0
  97. package/dist/Model/query.d.ts +15 -0
  98. package/dist/Model/query.d.ts.map +1 -0
  99. package/dist/Model/query.js +3 -0
  100. package/dist/Model.d.ts +6 -0
  101. package/dist/Model.d.ts.map +1 -0
  102. package/dist/Model.js +6 -0
  103. package/dist/NonEmptySet.d.ts +3 -1
  104. package/dist/NonEmptySet.d.ts.map +1 -1
  105. package/dist/NonEmptySet.js +2 -2
  106. package/dist/Option.d.ts +1 -0
  107. package/dist/Option.d.ts.map +1 -1
  108. package/dist/Option.js +3 -1
  109. package/dist/Pure.d.ts +5 -3
  110. package/dist/Pure.d.ts.map +1 -1
  111. package/dist/Pure.js +5 -2
  112. package/dist/QueueMaker.d.ts +11 -0
  113. package/dist/QueueMaker.d.ts.map +1 -0
  114. package/dist/QueueMaker.js +11 -0
  115. package/dist/RequestContext.d.ts +87 -0
  116. package/dist/RequestContext.d.ts.map +1 -0
  117. package/dist/RequestContext.js +45 -0
  118. package/dist/RequestScopedDependencies.d.ts +21 -0
  119. package/dist/RequestScopedDependencies.d.ts.map +1 -0
  120. package/dist/RequestScopedDependencies.js +16 -0
  121. package/dist/Schema/Class.d.ts +163 -16
  122. package/dist/Schema/Class.d.ts.map +1 -1
  123. package/dist/Schema/Class.js +193 -21
  124. package/dist/Schema/FastCheck.d.ts.map +1 -1
  125. package/dist/Schema/SchemaParser.d.ts +5 -0
  126. package/dist/Schema/SchemaParser.d.ts.map +1 -0
  127. package/dist/Schema/SchemaParser.js +6 -0
  128. package/dist/Schema/SpecialJsonSchema.d.ts +15 -2
  129. package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -1
  130. package/dist/Schema/SpecialJsonSchema.js +70 -12
  131. package/dist/Schema/SpecialOpenApi.d.ts +1 -1
  132. package/dist/Schema/SpecialOpenApi.js +3 -3
  133. package/dist/Schema/brand.d.ts +5 -5
  134. package/dist/Schema/brand.d.ts.map +1 -1
  135. package/dist/Schema/brand.js +6 -4
  136. package/dist/Schema/email.d.ts.map +1 -1
  137. package/dist/Schema/email.js +8 -5
  138. package/dist/Schema/ext.d.ts +297 -72
  139. package/dist/Schema/ext.d.ts.map +1 -1
  140. package/dist/Schema/ext.js +331 -67
  141. package/dist/Schema/moreStrings.d.ts +82 -48
  142. package/dist/Schema/moreStrings.d.ts.map +1 -1
  143. package/dist/Schema/moreStrings.js +85 -51
  144. package/dist/Schema/numbers.d.ts +33 -21
  145. package/dist/Schema/numbers.d.ts.map +1 -1
  146. package/dist/Schema/numbers.js +64 -12
  147. package/dist/Schema/phoneNumber.d.ts.map +1 -1
  148. package/dist/Schema/phoneNumber.js +6 -6
  149. package/dist/Schema/schema.d.ts +1 -1
  150. package/dist/Schema/schema.d.ts.map +1 -1
  151. package/dist/Schema/schema.js +2 -2
  152. package/dist/Schema/strings.d.ts +18 -4
  153. package/dist/Schema/strings.d.ts.map +1 -1
  154. package/dist/Schema/strings.js +1 -5
  155. package/dist/Schema.d.ts +223 -6
  156. package/dist/Schema.d.ts.map +1 -1
  157. package/dist/Schema.js +189 -23
  158. package/dist/SchemaAST.d.ts +2 -0
  159. package/dist/SchemaAST.d.ts.map +1 -0
  160. package/dist/SchemaAST.js +2 -0
  161. package/dist/Set.d.ts +4 -1
  162. package/dist/Set.d.ts.map +1 -1
  163. package/dist/Set.js +3 -2
  164. package/dist/Store.d.ts +215 -0
  165. package/dist/Store.d.ts.map +1 -0
  166. package/dist/Store.js +113 -0
  167. package/dist/_ext/Array.d.ts +1 -1
  168. package/dist/_ext/Array.d.ts.map +1 -1
  169. package/dist/_ext/Array.js +4 -2
  170. package/dist/_ext/misc.d.ts +4 -1
  171. package/dist/_ext/misc.d.ts.map +1 -1
  172. package/dist/_ext/misc.js +4 -2
  173. package/dist/_ext/ord.ext.d.ts +2 -1
  174. package/dist/_ext/ord.ext.d.ts.map +1 -1
  175. package/dist/_ext/ord.ext.js +2 -2
  176. package/dist/builtin.d.ts +2 -14
  177. package/dist/builtin.d.ts.map +1 -1
  178. package/dist/builtin.js +1 -1
  179. package/dist/client/InvalidationKeys.d.ts +30 -0
  180. package/dist/client/InvalidationKeys.d.ts.map +1 -0
  181. package/dist/client/InvalidationKeys.js +37 -0
  182. package/dist/client/apiClientFactory.d.ts +13 -8
  183. package/dist/client/apiClientFactory.d.ts.map +1 -1
  184. package/dist/client/apiClientFactory.js +148 -29
  185. package/dist/client/clientFor.d.ts +48 -13
  186. package/dist/client/clientFor.d.ts.map +1 -1
  187. package/dist/client/clientFor.js +9 -1
  188. package/dist/client/errors.d.ts +74 -28
  189. package/dist/client/errors.d.ts.map +1 -1
  190. package/dist/client/errors.js +41 -10
  191. package/dist/client/makeClient.d.ts +487 -32
  192. package/dist/client/makeClient.d.ts.map +1 -1
  193. package/dist/client/makeClient.js +47 -21
  194. package/dist/client.d.ts +2 -0
  195. package/dist/client.d.ts.map +1 -1
  196. package/dist/client.js +3 -1
  197. package/dist/faker.d.ts.map +1 -1
  198. package/dist/http/Request.d.ts +2 -2
  199. package/dist/http/Request.d.ts.map +1 -1
  200. package/dist/http/Request.js +2 -2
  201. package/dist/http/internal/lib.d.ts +12 -12
  202. package/dist/http/internal/lib.d.ts.map +1 -1
  203. package/dist/http/internal/lib.js +13 -13
  204. package/dist/ids.d.ts +32 -14
  205. package/dist/ids.d.ts.map +1 -1
  206. package/dist/ids.js +38 -7
  207. package/dist/index.d.ts +3 -0
  208. package/dist/index.d.ts.map +1 -1
  209. package/dist/index.js +5 -1
  210. package/dist/middleware.d.ts +12 -6
  211. package/dist/middleware.d.ts.map +1 -1
  212. package/dist/middleware.js +13 -7
  213. package/dist/rpc/Invalidation.d.ts +1124 -0
  214. package/dist/rpc/Invalidation.d.ts.map +1 -0
  215. package/dist/rpc/Invalidation.js +178 -0
  216. package/dist/rpc/MiddlewareMaker.d.ts +10 -7
  217. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  218. package/dist/rpc/MiddlewareMaker.js +57 -37
  219. package/dist/rpc/RpcContextMap.d.ts +2 -2
  220. package/dist/rpc/RpcContextMap.d.ts.map +1 -1
  221. package/dist/rpc/RpcMiddleware.d.ts +13 -10
  222. package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
  223. package/dist/rpc/RpcMiddleware.js +2 -2
  224. package/dist/rpc.d.ts +1 -1
  225. package/dist/rpc.d.ts.map +1 -1
  226. package/dist/rpc.js +2 -2
  227. package/dist/runtime.d.ts +19 -0
  228. package/dist/runtime.d.ts.map +1 -0
  229. package/dist/runtime.js +40 -0
  230. package/dist/setupRequest.d.ts +20 -0
  231. package/dist/setupRequest.d.ts.map +1 -0
  232. package/dist/setupRequest.js +72 -0
  233. package/dist/toast.d.ts +51 -0
  234. package/dist/toast.d.ts.map +1 -0
  235. package/dist/toast.js +34 -0
  236. package/dist/transform.d.ts +1 -1
  237. package/dist/transform.d.ts.map +1 -1
  238. package/dist/transform.js +4 -5
  239. package/dist/utils/effectify.d.ts +1 -1
  240. package/dist/utils/effectify.d.ts.map +1 -1
  241. package/dist/utils/effectify.js +2 -2
  242. package/dist/utils/extend.d.ts +6 -1
  243. package/dist/utils/extend.d.ts.map +1 -1
  244. package/dist/utils/extend.js +24 -6
  245. package/dist/utils/gen.d.ts +4 -4
  246. package/dist/utils/gen.d.ts.map +1 -1
  247. package/dist/utils/logLevel.d.ts +2 -2
  248. package/dist/utils/logLevel.d.ts.map +1 -1
  249. package/dist/utils/logger.d.ts +2 -1
  250. package/dist/utils/logger.d.ts.map +1 -1
  251. package/dist/utils/logger.js +2 -2
  252. package/dist/utils.d.ts +34 -39
  253. package/dist/utils.d.ts.map +1 -1
  254. package/dist/utils.js +33 -37
  255. package/dist/validation/validators.d.ts.map +1 -1
  256. package/dist/validation.d.ts.map +1 -1
  257. package/dist/withToast.d.ts +30 -0
  258. package/dist/withToast.d.ts.map +1 -0
  259. package/dist/withToast.js +64 -0
  260. package/package.json +26 -266
  261. package/src/Array.ts +5 -5
  262. package/src/Chunk.ts +3 -3
  263. package/src/Config/SecretURL.ts +6 -3
  264. package/src/Config/internal/configSecretURL.ts +2 -2
  265. package/src/Config.ts +6 -13
  266. package/src/ConfigProvider.ts +4 -47
  267. package/src/Context.ts +9 -8
  268. package/src/DataDependencies.ts +171 -0
  269. package/src/Effect.ts +9 -12
  270. package/src/Emailer.ts +51 -0
  271. package/src/Inputify.type.ts +1 -1
  272. package/src/Layer.ts +8 -5
  273. package/src/Model/Repository/Registry.ts +35 -0
  274. package/src/Model/Repository/dependency.ts +32 -0
  275. package/src/Model/Repository/ext.ts +374 -0
  276. package/src/Model/Repository/internal/internal.ts +988 -0
  277. package/src/Model/Repository/legacy.ts +29 -0
  278. package/src/Model/Repository/makeRepo.ts +168 -0
  279. package/src/Model/Repository/service.ts +676 -0
  280. package/src/Model/Repository/validation.ts +38 -0
  281. package/src/Model/Repository.ts +7 -0
  282. package/src/Model/dsl.ts +129 -0
  283. package/src/Model/filter/filterApi.ts +78 -0
  284. package/src/Model/filter/types/errors.ts +47 -0
  285. package/src/Model/filter/types/fields.ts +50 -0
  286. package/src/Model/filter/types/path/common.ts +404 -0
  287. package/src/Model/filter/types/path/eager.ts +329 -0
  288. package/src/Model/filter/types/path/index.ts +4 -0
  289. package/src/Model/filter/types/utils.ts +128 -0
  290. package/src/Model/filter/types/validator.ts +46 -0
  291. package/src/Model/filter/types.ts +6 -0
  292. package/src/Model/query/dsl.ts +3092 -0
  293. package/src/Model/query/new-kid-interpreter.ts +532 -0
  294. package/src/Model/query.ts +13 -0
  295. package/src/Model.ts +5 -0
  296. package/src/NonEmptySet.ts +6 -4
  297. package/src/Option.ts +2 -0
  298. package/src/Pure.ts +6 -3
  299. package/src/QueueMaker.ts +11 -0
  300. package/src/RequestContext.ts +48 -0
  301. package/src/RequestScopedDependencies.ts +45 -0
  302. package/src/Schema/Class.ts +550 -56
  303. package/src/Schema/SchemaParser.ts +12 -0
  304. package/src/Schema/SpecialJsonSchema.ts +75 -7
  305. package/src/Schema/SpecialOpenApi.ts +5 -5
  306. package/src/Schema/brand.ts +27 -12
  307. package/src/Schema/email.ts +9 -7
  308. package/src/Schema/ext.ts +408 -91
  309. package/src/Schema/moreStrings.ts +213 -88
  310. package/src/Schema/numbers.ts +107 -29
  311. package/src/Schema/phoneNumber.ts +8 -9
  312. package/src/Schema/schema.ts +1 -1
  313. package/src/Schema/strings.ts +23 -14
  314. package/src/Schema.ts +403 -46
  315. package/src/SchemaAST.ts +1 -0
  316. package/src/Set.ts +6 -2
  317. package/src/Store.ts +325 -0
  318. package/src/_ext/Array.ts +4 -2
  319. package/src/_ext/misc.ts +4 -1
  320. package/src/_ext/ord.ext.ts +2 -1
  321. package/src/builtin-json.d.ts +24 -0
  322. package/src/builtin.ts +16 -21
  323. package/src/client/InvalidationKeys.ts +55 -0
  324. package/src/client/apiClientFactory.ts +291 -126
  325. package/src/client/clientFor.ts +101 -33
  326. package/src/client/errors.ts +66 -20
  327. package/src/client/makeClient.ts +519 -60
  328. package/src/client.ts +6 -4
  329. package/src/http/Request.ts +4 -4
  330. package/src/http/internal/lib.ts +12 -12
  331. package/src/http.ts +1 -1
  332. package/src/ids.ts +45 -8
  333. package/src/index.ts +20 -16
  334. package/src/middleware.ts +12 -8
  335. package/src/rpc/Invalidation.ts +278 -0
  336. package/src/rpc/MiddlewareMaker.ts +86 -79
  337. package/src/rpc/README.md +2 -2
  338. package/src/rpc/RpcContextMap.ts +4 -4
  339. package/src/rpc/RpcMiddleware.ts +18 -13
  340. package/src/rpc.ts +4 -4
  341. package/src/runtime.ts +57 -0
  342. package/src/setupRequest.ts +138 -0
  343. package/src/toast.ts +54 -0
  344. package/src/transform.ts +4 -4
  345. package/src/utils/effectify.ts +1 -1
  346. package/src/utils/extend.ts +24 -5
  347. package/src/utils/gen.ts +7 -7
  348. package/src/utils/logLevel.ts +1 -1
  349. package/src/utils/logger.ts +3 -2
  350. package/src/utils.ts +85 -158
  351. package/src/validation.ts +2 -2
  352. package/src/withToast.ts +133 -0
  353. package/test/dist/rpc.test.d.ts.map +1 -1
  354. package/test/dist/rpcMiddlewareContext.test.d.ts.map +1 -0
  355. package/test/moreStrings.test.ts +55 -10
  356. package/test/rpc.test.ts +28 -11
  357. package/test/rpcMiddlewareContext.test.ts +63 -0
  358. package/test/schema.test.ts +318 -55
  359. package/test/secretURL.test.ts +4 -1
  360. package/test/special.test.ts +663 -131
  361. package/test/utils.test.ts +5 -5
  362. package/tsconfig.base.json +6 -4
  363. package/tsconfig.json +9 -1
  364. package/tsconfig.src.json +29 -29
  365. package/tsconfig.test.json +2 -2
  366. package/dist/Operations.d.ts +0 -124
  367. package/dist/Operations.d.ts.map +0 -1
  368. package/dist/Operations.js +0 -29
  369. package/eslint.config.mjs +0 -26
  370. package/src/Operations.ts +0 -55
  371. package/tsconfig.json.bak +0 -12
package/src/Schema/ext.ts CHANGED
@@ -1,12 +1,50 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-return */
3
- import { Effect, Option, pipe, type SchemaAST, SchemaIssue, SchemaTransformation } 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"
38
+ import * as Order from "effect/Order"
4
39
  import * as S from "effect/Schema"
5
- import { isDateValid } from "effect/Schema"
6
- import { type NonEmptyReadonlyArray } from "../Array.js"
7
- import * as Context from "../Context.js"
8
- import { extendM, typedKeysOf } from "../utils.js"
9
- import { type AST } from "./schema.js"
40
+ import * as SchemaIssue from "effect/SchemaIssue"
41
+ import * as SchemaTransformation from "effect/SchemaTransformation"
42
+ import * as Struct from "effect/Struct"
43
+ import { type NonEmptyReadonlyArray } from "../Array.ts"
44
+ import * as Context from "../Context.ts"
45
+ import * as SchemaAST from "../SchemaAST.ts"
46
+ import { extendM, typedKeysOf } from "../utils.ts"
47
+ import { type AST } from "./schema.ts"
10
48
 
11
49
  type ProvidedCodec<Self extends S.Top, R> = S.Codec<
12
50
  Self["Type"],
@@ -15,11 +53,39 @@ type ProvidedCodec<Self extends S.Top, R> = S.Codec<
15
53
  Exclude<Self["EncodingServices"], R>
16
54
  >
17
55
 
18
- // TODO: v4 migration - Date is no longer by default encoded to string.
56
+ const concurrencySetting = Effect.runSync(
57
+ Config
58
+ .Literal("unbounded", "SCHEMA_CONCURRENCY")
59
+ .pipe(Config.orElse(() => Config.Number("SCHEMA_CONCURRENCY")), Config.option)
60
+ )
61
+
62
+ export const DefaultParseOptions: SchemaAST.ParseOptions = {
63
+ concurrency: Option.getOrElse(concurrencySetting, () => "unbounded" as const)
64
+ }
65
+
66
+ /**
67
+ * Parse-options annotation used on schema constructors for decode paths where callers
68
+ * cannot currently pass parse options (notably some RPC / HttpApi integration paths).
69
+ *
70
+ * Keep this annotation in place so those framework-managed decodes still run with
71
+ * unbounded concurrency by default.
72
+ */
73
+ export const concurrencyUnbounded = { parseOptions: DefaultParseOptions } as const
74
+
75
+ type DecodeLike = (schema: any) => (input: any, options?: SchemaAST.ParseOptions) => any
76
+
77
+ export const withDefaultParseOptions = <Decode extends DecodeLike>(
78
+ decode: Decode,
79
+ defaultParseOptions: SchemaAST.ParseOptions = DefaultParseOptions
80
+ ): Decode =>
81
+ ((schema: any) => {
82
+ const run = decode(schema)
83
+ return (input: any, options?: SchemaAST.ParseOptions) => run(input, { ...defaultParseOptions, ...options })
84
+ }) as Decode
19
85
 
20
86
  const DateString = S.String.annotate({
21
- identifier: "Date",
22
- description: "a string in ISO 8601 format that will be decoded as a Date",
87
+ identifier: "DateOrInvalid",
88
+ description: "an ISO 8601 date string that will be decoded as a Date (may be invalid)",
23
89
  format: "date-time"
24
90
  })
25
91
 
@@ -40,81 +106,169 @@ export interface DateFromString extends S.decodeTo<S.Date, S.String> {}
40
106
  * Encoding:
41
107
  * - A `Date` is encoded as a `string`.
42
108
  *
109
+ * Use this when the Encoded form must be JSON (`string`). Domain models should
110
+ * prefer {@link Date}, whose Encoded form is `Date`; JSON stores convert via
111
+ * `Schema.toCodecJson`.
112
+ *
43
113
  * @since 4.0.0
44
114
  */
45
115
  export const DateFromString: DateFromString = DateString.pipe(S.decodeTo(S.Date, SchemaTransformation.dateFromString))
46
116
 
47
- /**
48
- * Like the default Schema `Date` but from String with `withDefault` => now
49
- */
50
- export const Date = Object.assign(DateFromString, {
51
- withDefault: DateFromString.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date()))),
52
- withDecodingDefaultType: DateFromString.pipe(S.withDecodingDefaultType(Effect.sync(() => new global.Date())))
117
+ const dateHelpers = (s: S.Date) => ({
118
+ /**
119
+ * Construction-only default `new Date()`. Applied only when the field is
120
+ * omitted from `.make(...)` input. NOT applied during decode — cannot be
121
+ * used to JIT-migrate database fields. See file-level note.
122
+ */
123
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))),
124
+ /**
125
+ * Decode-time default `new Date()`. **Discouraged for persisted data:** a
126
+ * missing field may be data corruption, not an old-shape document; silently
127
+ * substituting `new Date()` hides the problem. Prefer an explicit,
128
+ * preferably versioned migration over a decode-time fallback. See
129
+ * file-level note.
130
+ */
131
+ withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date())))
53
132
  })
54
133
 
55
- /**
56
- * Like the default Schema `DateValid` but from String with `withDefault` => now
57
- */
58
- export const DateValid = Object.assign(Date.check(isDateValid()), {
59
- withDefault: DateFromString.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date()))),
60
- withDecodingDefaultType: DateFromString.pipe(S.withDecodingDefaultType(Effect.sync(() => new global.Date())))
61
- })
134
+ /** Like the default Schema `Date` (Encoded is `Date`) with default helpers. */
135
+ export const Date = extendM(S.Date, dateHelpers)
62
136
 
63
137
  /**
64
- * Like the default Schema `Boolean` but with `withDefault` => false
138
+ * Alias of {@link Date}. Core `Schema.Date` already rejects invalid Dates.
139
+ *
140
+ * @deprecated Use {@link Date}.
65
141
  */
142
+ export const DateValid = Date
143
+
144
+ /** Like the default Schema `Boolean` but with default helpers. */
66
145
  export const Boolean = Object.assign(S.Boolean, {
67
- withDefault: S.Boolean.pipe(S.withConstructorDefault(Effect.succeed(false))),
146
+ /**
147
+ * Construction-only default `false`. Applied only when the field is
148
+ * omitted from `.make(...)` input. NOT applied during decode — cannot be
149
+ * used to JIT-migrate database fields. See file-level note.
150
+ */
151
+ withConstructorDefault: S.Boolean.pipe(S.withConstructorDefault(Effect.succeed(false))),
152
+ /**
153
+ * Decode-time default `false`. **Discouraged for persisted data:** a
154
+ * missing field may be data corruption, not an old-shape document; silently
155
+ * substituting `false` hides the problem. Prefer an explicit, preferably
156
+ * versioned migration over a decode-time fallback. See file-level note.
157
+ */
68
158
  withDecodingDefaultType: S.Boolean.pipe(S.withDecodingDefaultType(Effect.succeed(false)))
69
159
  })
70
160
 
71
161
  /**
72
- * You probably want to use `Finite` instead of this.
73
- * Like the default Schema `Number` but with `withDefault` => 0
162
+ * You probably want to use `Finite` instead of this. Like the default Schema
163
+ * `Number` but with default helpers.
74
164
  */
75
165
  export const Number = Object.assign(S.Number, {
76
- withDefault: S.Number.pipe(S.withConstructorDefault(Effect.succeed(0))),
166
+ /**
167
+ * Construction-only default `0`. Applied only when the field is omitted
168
+ * from `.make(...)` input. NOT applied during decode — cannot be used to
169
+ * JIT-migrate database fields. See file-level note.
170
+ */
171
+ withConstructorDefault: S.Number.pipe(S.withConstructorDefault(Effect.succeed(0))),
172
+ /**
173
+ * Decode-time default `0`. **Discouraged for persisted data:** a missing
174
+ * field may be data corruption, not an old-shape document; silently
175
+ * substituting `0` hides the problem. Prefer an explicit, preferably
176
+ * versioned migration over a decode-time fallback. See file-level note.
177
+ */
77
178
  withDecodingDefaultType: S.Number.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
78
179
  })
79
180
 
80
- /**
81
- * Like the default Schema `Finite` but with `withDefault` => 0
82
- */
83
- export const Finite = Object.assign(S.Finite, {
84
- withDefault: S.Finite.pipe(S.withConstructorDefault(Effect.succeed(0))),
85
- withDecodingDefaultType: S.Finite.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
181
+ // Generation-only: native Arbitrary otherwise samples up to ±Number.MAX_VALUE, so
182
+ // derived totals overflow to Infinity. Validation and JSON Schema are unaffected.
183
+ const realisticFiniteSamples = S.makeFilter<number>(() => undefined, {
184
+ arbitraryConstraint: { order: Order.Number, minimum: -1_000_000, maximum: 1_000_000 }
185
+ })
186
+ const FiniteSchema = S.Finite.pipe(S.check(realisticFiniteSamples))
187
+
188
+ /** Like the default Schema `Finite` but with default helpers. */
189
+ export const Finite = Object.assign(FiniteSchema, {
190
+ /**
191
+ * Construction-only default `0`. Applied only when the field is omitted
192
+ * from `.make(...)` input. NOT applied during decode — cannot be used to
193
+ * JIT-migrate database fields. See file-level note.
194
+ */
195
+ withConstructorDefault: FiniteSchema.pipe(S.withConstructorDefault(Effect.succeed(0))),
196
+ /**
197
+ * Decode-time default `0`. **Discouraged for persisted data:** a missing
198
+ * field may be data corruption, not an old-shape document; silently
199
+ * substituting `0` hides the problem. Prefer an explicit, preferably
200
+ * versioned migration over a decode-time fallback. See file-level note.
201
+ */
202
+ withDecodingDefaultType: FiniteSchema.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
86
203
  })
87
204
 
88
- /**
89
- * Like the default Schema `Literals` but with `withDefault` => literals[0]
90
- */
205
+ /** Like the default Schema `Literals` but with default helpers. Default value is `literals[0]`. */
91
206
  export const Literals = <const Literals extends NonEmptyReadonlyArray<AST.LiteralValue>>(literals: Literals) =>
92
207
  pipe(
93
208
  S.Literals(literals),
94
209
  (s) =>
95
210
  Object.assign(s, {
211
+ /** Override the default literal value used by `withConstructorDefault` / `withDecodingDefaultType`. */
96
212
  changeDefault: <A extends Literals[number]>(a: A) => {
97
213
  return Object.assign(S.Literals(literals), {
98
214
  Default: a,
99
- withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(a))),
215
+ /**
216
+ * Construction-only default. Applied only when the field is
217
+ * omitted from `.make(...)` input. NOT applied during decode —
218
+ * cannot be used to JIT-migrate database fields. See file-level
219
+ * note.
220
+ */
221
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(a))),
222
+ /**
223
+ * Decode-time default. **Discouraged for persisted data:** a
224
+ * missing field may be data corruption, not an old-shape
225
+ * document; silently substituting hides the problem. Prefer an
226
+ * explicit, preferably versioned migration over a decode-time
227
+ * fallback. See file-level note.
228
+ */
100
229
  withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(a)))
101
230
  }) // todo: copy annotations from original?
102
231
  },
103
- Default: literals[0] as typeof literals[0],
104
- withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(literals[0]))),
232
+ // 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
233
+ Default: literals[0] as Literals[0],
234
+ /**
235
+ * Construction-only default `literals[0]`. Applied only when the
236
+ * field is omitted from `.make(...)` input. NOT applied during
237
+ * decode — cannot be used to JIT-migrate database fields. See
238
+ * file-level note.
239
+ */
240
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(literals[0]))),
241
+ /**
242
+ * Decode-time default `literals[0]`. **Discouraged for persisted
243
+ * data:** a missing field may be data corruption, not an old-shape
244
+ * document; silently substituting hides the problem. Prefer an
245
+ * explicit, preferably versioned migration over a decode-time
246
+ * fallback. See file-level note.
247
+ */
105
248
  withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(literals[0])))
106
249
  })
107
250
  )
108
251
 
109
- /**
110
- * Like the default Schema `Array` but with `withDefault` => []
111
- */
252
+ /** Like the default Schema `Array` but with default helpers. */
112
253
  export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
113
254
  return pipe(
114
- S.Array(value),
255
+ S.Array(value).annotate(concurrencyUnbounded),
115
256
  (s) =>
116
257
  Object.assign(s, {
117
- withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => []))),
258
+ /**
259
+ * Construction-only default `[]`. Applied only when the field is
260
+ * omitted from `.make(...)` input. NOT applied during decode —
261
+ * cannot be used to JIT-migrate database fields. See file-level
262
+ * note.
263
+ */
264
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => []))),
265
+ /**
266
+ * Decode-time default `[]`. **Discouraged for persisted data:** a
267
+ * missing field may be data corruption, not an old-shape document;
268
+ * silently substituting `[]` hides the problem. Prefer an explicit,
269
+ * preferably versioned migration over a decode-time fallback. See
270
+ * file-level note.
271
+ */
118
272
  withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => [])))
119
273
  })
120
274
  )
@@ -126,13 +280,13 @@ export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
126
280
  export const ReadonlySetFromArray = <ValueSchema extends S.Top>(value: ValueSchema) => {
127
281
  const from = S
128
282
  .Array(value)
129
- .annotate({ expected: "an array of unique items that will be decoded as a ReadonlySet" })
130
- const to = S.instanceOf(Set) as S.instanceOf<ReadonlySet<S.Schema.Type<ValueSchema>>>
283
+ .annotate({ ...concurrencyUnbounded, expected: "an array of unique items that will be decoded as a ReadonlySet" })
284
+ const to = S.instanceOf(Set) as S.instanceOf<ReadonlySet<ValueSchema["Type"]>>
131
285
  const schema = from.pipe(
132
286
  S.decodeTo(
133
287
  to,
134
288
  SchemaTransformation.transform({
135
- decode: (arr) => new Set(arr) as ReadonlySet<S.Schema.Type<ValueSchema>>,
289
+ decode: (arr) => new Set(arr) as ReadonlySet<ValueSchema["Type"]>,
136
290
  encode: (set) => [...set]
137
291
  })
138
292
  )
@@ -149,9 +303,12 @@ export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extend
149
303
  }) => {
150
304
  const from = S
151
305
  .Array(S.Tuple([pair.key, pair.value]))
152
- .annotate({ expected: "an array of key-value tuples that will be decoded as a ReadonlyMap" })
306
+ .annotate({
307
+ ...concurrencyUnbounded,
308
+ expected: "an array of key-value tuples that will be decoded as a ReadonlyMap"
309
+ })
153
310
  const to = S.instanceOf(Map) as S.instanceOf<
154
- ReadonlyMap<S.Schema.Type<KeySchema>, S.Schema.Type<ValueSchema>>
311
+ ReadonlyMap<KeySchema["Type"], ValueSchema["Type"]>
155
312
  >
156
313
  const schema = from.pipe(
157
314
  S.decodeTo(
@@ -159,7 +316,7 @@ export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extend
159
316
  SchemaTransformation.transform({
160
317
  decode: (
161
318
  arr
162
- ) => new Map(arr) as ReadonlyMap<S.Schema.Type<KeySchema>, S.Schema.Type<ValueSchema>>,
319
+ ) => new Map(arr) as ReadonlyMap<KeySchema["Type"], ValueSchema["Type"]>,
163
320
  encode: (
164
321
  map
165
322
  ) => [...map.entries()] as any // fu
@@ -169,65 +326,142 @@ export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extend
169
326
  return schema
170
327
  }
171
328
 
172
- /**
173
- * Like the default Schema `ReadonlySet` but from Array with `withDefault` => new Set()
174
- */
329
+ /** Like the default Schema `ReadonlySet` (Encoded is `Set`) with default helpers. */
175
330
  export const ReadonlySet = <ValueSchema extends S.Top>(value: ValueSchema) =>
176
331
  pipe(
177
- ReadonlySetFromArray(value),
332
+ S.ReadonlySet(value),
178
333
  (s) =>
179
334
  Object.assign(s, {
180
- withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Set<S.Schema.Type<ValueSchema>>()))),
335
+ /**
336
+ * Construction-only default `new Set()`. Applied only when the field
337
+ * is omitted from `.make(...)` input. NOT applied during decode —
338
+ * cannot be used to JIT-migrate database fields. See file-level
339
+ * note.
340
+ */
341
+ withConstructorDefault: s.pipe(
342
+ S.withConstructorDefault(Effect.sync(() => new Set<ValueSchema["Type"]>()))
343
+ ),
344
+ /**
345
+ * Decode-time default `new Set()`. **Discouraged for persisted
346
+ * data:** a missing field may be data corruption, not an old-shape
347
+ * document; silently substituting an empty set hides the problem.
348
+ * Prefer an explicit, preferably versioned migration over a
349
+ * decode-time fallback. See file-level note.
350
+ */
181
351
  withDecodingDefaultType: s.pipe(
182
- S.withDecodingDefaultType(Effect.sync(() => new Set<S.Schema.Type<ValueSchema>>()))
352
+ S.withDecodingDefaultType(Effect.sync(() => new Set<ValueSchema["Type"]>()))
183
353
  )
184
354
  })
185
355
  )
186
356
 
187
- /**
188
- * Like the default Schema `ReadonlyMap` but from Array with `withDefault` => new Map()
189
- */
357
+ /** Like the default Schema `ReadonlyMap` (Encoded is `Map`) with default helpers. */
190
358
  export const ReadonlyMap = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
191
359
  readonly key: KeySchema
192
360
  readonly value: ValueSchema
193
361
  }) =>
194
362
  pipe(
195
- ReadonlyMapFromArray(pair),
363
+ S.ReadonlyMap(pair.key, pair.value),
196
364
  (s) =>
197
365
  Object.assign(s, {
198
- withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Map()))),
366
+ /**
367
+ * Construction-only default `new Map()`. Applied only when the field
368
+ * is omitted from `.make(...)` input. NOT applied during decode —
369
+ * cannot be used to JIT-migrate database fields. See file-level
370
+ * note.
371
+ */
372
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Map()))),
373
+ /**
374
+ * Decode-time default `new Map()`. **Discouraged for persisted
375
+ * data:** a missing field may be data corruption, not an old-shape
376
+ * document; silently substituting an empty map hides the problem.
377
+ * Prefer an explicit, preferably versioned migration over a
378
+ * decode-time fallback. See file-level note.
379
+ */
199
380
  withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new Map())))
200
381
  })
201
382
  )
202
383
 
203
- /**
204
- * Like the default Schema `NullOr` but with `withDefault` => null
205
- */
384
+ /** Like the default Schema `NullOr` but with default helpers. */
206
385
  export const NullOr = <Schema extends S.Top>(self: Schema) =>
207
386
  pipe(
208
387
  S.NullOr(self),
209
388
  (s) =>
210
389
  Object.assign(s, {
211
- withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(null))),
390
+ /**
391
+ * Construction-only default `null`. Applied only when the field is
392
+ * omitted from `.make(...)` input. NOT applied during decode —
393
+ * cannot be used to JIT-migrate database fields. See file-level
394
+ * note.
395
+ */
396
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(null))),
397
+ /**
398
+ * Decode-time default `null`. **Discouraged for persisted data:** a
399
+ * missing field may be data corruption, not an old-shape document;
400
+ * silently substituting `null` hides the problem. Prefer an
401
+ * explicit, preferably versioned migration over a decode-time
402
+ * fallback. See file-level note.
403
+ */
212
404
  withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(null)))
213
405
  })
214
406
  )
215
407
 
408
+ /**
409
+ * Attach a `withConstructorDefault` of `new Date()` to any schema.
410
+ *
411
+ * **Construction-only.** Applied only when the field is omitted from
412
+ * `.make(...)` input. NOT applied during decode — cannot be used to
413
+ * JIT-migrate database fields. See file-level note.
414
+ */
216
415
  export const defaultDate = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
217
416
  schema.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date())))
218
417
 
418
+ /**
419
+ * Attach a `withConstructorDefault` of `false` to any schema.
420
+ *
421
+ * **Construction-only.** Applied only when the field is omitted from
422
+ * `.make(...)` input. NOT applied during decode — cannot be used to
423
+ * JIT-migrate database fields. See file-level note.
424
+ */
219
425
  export const defaultBool = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
220
426
  schema.pipe(S.withConstructorDefault(Effect.succeed(false)))
221
427
 
428
+ /**
429
+ * Attach a `withConstructorDefault` of `null` to any schema.
430
+ *
431
+ * **Construction-only.** Applied only when the field is omitted from
432
+ * `.make(...)` input. NOT applied during decode — cannot be used to
433
+ * JIT-migrate database fields. See file-level note.
434
+ */
222
435
  export const defaultNullable = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
223
436
  schema.pipe(S.withConstructorDefault(Effect.succeed(null)))
224
437
 
438
+ /**
439
+ * Attach a `withConstructorDefault` of `[]` to any schema.
440
+ *
441
+ * **Construction-only.** Applied only when the field is omitted from
442
+ * `.make(...)` input. NOT applied during decode — cannot be used to
443
+ * JIT-migrate database fields. See file-level note.
444
+ */
225
445
  export const defaultArray = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
226
446
  schema.pipe(S.withConstructorDefault(Effect.sync(() => [])))
227
447
 
448
+ /**
449
+ * Attach a `withConstructorDefault` of `new Map()` to any schema.
450
+ *
451
+ * **Construction-only.** Applied only when the field is omitted from
452
+ * `.make(...)` input. NOT applied during decode — cannot be used to
453
+ * JIT-migrate database fields. See file-level note.
454
+ */
228
455
  export const defaultMap = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
229
456
  schema.pipe(S.withConstructorDefault(Effect.sync(() => new Map())))
230
457
 
458
+ /**
459
+ * Attach a `withConstructorDefault` of `new Set()` to any schema.
460
+ *
461
+ * **Construction-only.** Applied only when the field is omitted from
462
+ * `.make(...)` input. NOT applied during decode — cannot be used to
463
+ * JIT-migrate database fields. See file-level note.
464
+ */
231
465
  export const defaultSet = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
232
466
  schema.pipe(S.withConstructorDefault(Effect.sync(() => new Set())))
233
467
 
@@ -258,10 +492,23 @@ export type WithDefaults<Self extends S.Top> = (
258
492
  // export type UnionToIntersection3<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I
259
493
  // : never
260
494
 
495
+ /** Union of core `Schema.Date` (Date objects) and string-encoded `DateFromString`, with default helpers. */
261
496
  export const inputDate = extendM(
262
- S.Union([S.DateValid, Date]),
497
+ S.Union([S.Date, DateFromString]),
263
498
  (s) => ({
264
- withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))),
499
+ /**
500
+ * Construction-only default `new Date()`. Applied only when the field is
501
+ * omitted from `.make(...)` input. NOT applied during decode — cannot be
502
+ * used to JIT-migrate database fields. See file-level note.
503
+ */
504
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))),
505
+ /**
506
+ * Decode-time default `new Date()`. **Discouraged for persisted data:** a
507
+ * missing field may be data corruption, not an old-shape document;
508
+ * silently substituting `new Date()` hides the problem. Prefer an
509
+ * explicit, preferably versioned migration over a decode-time fallback.
510
+ * See file-level note.
511
+ */
265
512
  withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date())))
266
513
  })
267
514
  )
@@ -281,13 +528,81 @@ export function makeOptional<NER extends S.Struct.Fields>(
281
528
  }, {} as any)
282
529
  }
283
530
 
531
+ // `Omit` erases the polymorphic `this` of "this-returning" methods (`annotate`, `annotateKey`,
532
+ // `check`, `rebuild`): extracting them through a mapped type binds their `this["Rebuild"]` back to
533
+ // the original `T`, not to this intersection. So overriding just `Rebuild` above is not enough, those
534
+ // four methods have to be redeclared here with a concrete return type instead of `this["Rebuild"]`.
535
+ export type DropConstructorDefault<T extends S.Top> =
536
+ & Omit<
537
+ T,
538
+ "~type.constructor.default" | "Rebuild" | "annotate" | "annotateKey" | "check" | "rebuild"
539
+ >
540
+ & {
541
+ readonly "~type.constructor.default": "no-default"
542
+ readonly "Rebuild": DropConstructorDefault<T>
543
+ annotate(annotations: S.Annotations.Bottom<T["Type"], T["~type.parameters"]>): DropConstructorDefault<T>
544
+ annotateKey(annotations: S.Annotations.Key<T["Type"]>): DropConstructorDefault<T>
545
+ check(
546
+ ...checks: readonly [SchemaAST.Check<T["Type"]>, ...Array<SchemaAST.Check<T["Type"]>>]
547
+ ): DropConstructorDefault<T>
548
+ rebuild(ast: T["ast"]): DropConstructorDefault<T>
549
+ }
550
+
551
+ // `replaceContext` is `@internal` in `effect/SchemaAST` (stripped from its public types, though the
552
+ // export itself still ships in the compiled JS). Effect's own `withConstructorDefault` goes through the
553
+ // same function internally, so this mirrors an existing code path rather than reaching for something
554
+ // novel, but it's still an internal API that could move or disappear without a signal.
555
+ const SchemaASTInternal = SchemaAST as unknown as {
556
+ readonly replaceContext: <A extends SchemaAST.AST>(ast: A, context: SchemaAST.Context | undefined) => A
557
+ }
558
+
559
+ export interface DropConstructorDefaultLambda extends Struct.Lambda {
560
+ <T extends S.Top>(self: T): DropConstructorDefault<T>
561
+ readonly "~lambda.out": DropConstructorDefault<this["~lambda.in"] & S.Top>
562
+ }
563
+
564
+ /**
565
+ * A `Struct.Lambda` that removes a field's `withConstructorDefault`, if any.
566
+ * Compose with `Struct.pick`/`Struct.omit` via `Struct.map` to derive a
567
+ * partial-update schema from a "create" schema without inheriting defaults
568
+ * that would resurrect omitted fields in `.make(...)` output:
569
+ *
570
+ * ```ts
571
+ * import { flow } from "effect/Function"
572
+ * import * as Struct from "effect/Struct"
573
+ * import * as S from "effect-app/Schema"
574
+ *
575
+ * const Person = S.Struct({
576
+ * id: S.String,
577
+ * name: S.String.pipe(S.optionalKey, S.withConstructorDefault(() => ""))
578
+ * })
579
+ *
580
+ * const UpdatePerson = Person.mapFields(
581
+ * flow(Struct.omit(["id"]), Struct.map(S.dropConstructorDefault))
582
+ * )
583
+ * ```
584
+ */
585
+ export const dropConstructorDefault: DropConstructorDefaultLambda = Struct.lambda<DropConstructorDefaultLambda>(
586
+ (schema) => {
587
+ const context = schema.ast.context
588
+ if (!context?.constructorDefault) return schema
589
+ return schema.rebuild(
590
+ SchemaASTInternal.replaceContext(
591
+ schema.ast,
592
+ new SchemaAST.Context(context.isOptional, context.isMutable, undefined, context.annotations)
593
+ )
594
+ )
595
+ }
596
+ )
597
+
284
598
  export function makeExactOptional<NER extends S.Struct.Fields>(
285
599
  t: NER
286
600
  ): {
287
- [K in keyof NER]: NER[K] extends S.Top ? ReturnType<typeof S.optionalKey<NER[K] & S.Top>> : any
601
+ [K in keyof NER]: NER[K] extends S.Top ? DropConstructorDefault<ReturnType<typeof S.optionalKey<NER[K] & S.Top>>>
602
+ : any
288
603
  } {
289
604
  return typedKeysOf(t).reduce((prev, cur) => {
290
- prev[cur] = S.optionalKey(t[cur] as any)
605
+ prev[cur] = dropConstructorDefault(S.optionalKey(t[cur] as any))
291
606
  return prev
292
607
  }, {} as any)
293
608
  }
@@ -304,20 +619,20 @@ export const transformTo = <To extends S.Top, From extends S.Top>(
304
619
  from.pipe(
305
620
  S.decodeTo(
306
621
  to,
307
- SchemaTransformation.transformOrFail({
622
+ SchemaTransformation.transformEffect({
308
623
  decode: (input: any, options: any) => Effect.sync(() => decode(input, options)),
309
624
  encode: (i: any) =>
310
625
  Effect.fail(
311
626
  new SchemaIssue.Forbidden(
312
- Option.some(i),
313
- { message: "One way schema transformation, encoding is not allowed" }
627
+ { message: "One way schema transformation, encoding is not allowed" },
628
+ i
314
629
  )
315
630
  )
316
631
  })
317
632
  )
318
633
  )
319
634
 
320
- /** A version of transformOrFail which is only a one way mapping of From->To */
635
+ /** A version of transformEffect which is only a one way mapping of From->To */
321
636
  export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
322
637
  from: From,
323
638
  to: To,
@@ -329,43 +644,45 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
329
644
  from.pipe(
330
645
  S.decodeTo(
331
646
  to,
332
- SchemaTransformation.transformOrFail({
647
+ SchemaTransformation.transformEffect({
333
648
  decode,
334
649
  encode: (i: any) =>
335
650
  Effect.fail(
336
651
  new SchemaIssue.Forbidden(
337
- Option.some(i),
338
- { message: "One way schema transformation, encoding is not allowed" }
652
+ { message: "One way schema transformation, encoding is not allowed" },
653
+ i
339
654
  )
340
655
  )
341
656
  })
342
657
  )
343
658
  )
344
659
 
345
- export const provide = <Self extends S.Top, R>(
346
- self: Self,
347
- context: Context.Context<R>
348
- ): ProvidedCodec<Self, R> => {
660
+ export const provide: {
661
+ <R>(context: Context.Context<R>): <Self extends S.Top>(self: Self) => ProvidedCodec<Self, R>
662
+ <Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R>
663
+ } = Function.dual(2, <Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R> => {
349
664
  const prov = Effect.provide(context)
350
665
  return self.pipe(
351
666
  S.middlewareDecoding((effect) => prov(effect)),
352
667
  S.middlewareEncoding((effect) => prov(effect))
353
- ) as ProvidedCodec<Self, R>
354
- }
355
- export const contextFromServices = <
668
+ )
669
+ })
670
+ export const contextFromServices = Effect.fnUntraced(function*<
671
+ Self extends S.Top,
672
+ Tags extends ReadonlyArray<Context.Key<any, any>>
673
+ >(self: Self, ...services: Tags) {
674
+ const context: Context.Context<Context.Service.Identifier<Tags[number]>> = Context.pick(...services)(
675
+ yield* Effect.context<Context.Service.Identifier<Tags[number]>>()
676
+ )
677
+ return provide(self, context)
678
+ }) as <
356
679
  Self extends S.Top,
357
680
  Tags extends ReadonlyArray<Context.Key<any, any>>
358
681
  >(
359
682
  self: Self,
360
683
  ...services: Tags
361
- ): Effect.Effect<
684
+ ) => Effect.Effect<
362
685
  ProvidedCodec<Self, Context.Service.Identifier<Tags[number]>>,
363
686
  never,
364
687
  Context.Service.Identifier<Tags[number]>
365
- > =>
366
- Effect.gen(function*() {
367
- const context: Context.Context<Context.Service.Identifier<Tags[number]>> = Context.pick(...services)(
368
- yield* Effect.context<Context.Service.Identifier<Tags[number]>>()
369
- )
370
- return provide(self, context)
371
- })
688
+ >