effect-app 4.0.0-beta.99 → 4.0.1

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