effect-app 4.0.0-beta.32 → 4.0.0-beta.321

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