effect-app 4.0.0-beta.99 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/CHANGELOG.md +2028 -0
  2. package/dist/Array.d.ts +2 -1
  3. package/dist/Array.d.ts.map +1 -1
  4. package/dist/Array.js +4 -4
  5. package/dist/Chunk.d.ts +2 -2
  6. package/dist/Chunk.d.ts.map +1 -1
  7. package/dist/Config/SecretURL.d.ts +3 -1
  8. package/dist/Config/SecretURL.d.ts.map +1 -1
  9. package/dist/Config/SecretURL.js +3 -6
  10. package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
  11. package/dist/Config/internal/configSecretURL.js +2 -2
  12. package/dist/Config.d.ts +0 -5
  13. package/dist/Config.d.ts.map +1 -1
  14. package/dist/Config.js +7 -5
  15. package/dist/ConfigProvider.d.ts +0 -37
  16. package/dist/ConfigProvider.d.ts.map +1 -1
  17. package/dist/ConfigProvider.js +5 -41
  18. package/dist/Context.d.ts +6 -4
  19. package/dist/Context.d.ts.map +1 -1
  20. package/dist/Context.js +4 -3
  21. package/dist/DataDependencies.d.ts +136 -0
  22. package/dist/DataDependencies.d.ts.map +1 -0
  23. package/dist/DataDependencies.js +112 -0
  24. package/dist/Effect.d.ts +3 -3
  25. package/dist/Effect.d.ts.map +1 -1
  26. package/dist/Effect.js +6 -9
  27. package/dist/Emailer.d.ts +51 -0
  28. package/dist/Emailer.d.ts.map +1 -0
  29. package/dist/Emailer.js +7 -0
  30. package/dist/Function.d.ts.map +1 -1
  31. package/dist/Layer.d.ts +6 -3
  32. package/dist/Layer.d.ts.map +1 -1
  33. package/dist/Layer.js +3 -2
  34. package/dist/Model/Repository/Registry.d.ts +22 -0
  35. package/dist/Model/Repository/Registry.d.ts.map +1 -0
  36. package/dist/Model/Repository/Registry.js +18 -0
  37. package/dist/Model/Repository/dependency.d.ts +6 -0
  38. package/dist/Model/Repository/dependency.d.ts.map +1 -0
  39. package/dist/Model/Repository/dependency.js +29 -0
  40. package/dist/Model/Repository/ext.d.ts +60 -0
  41. package/dist/Model/Repository/ext.d.ts.map +1 -0
  42. package/dist/Model/Repository/ext.js +124 -0
  43. package/dist/Model/Repository/internal/internal.d.ts +68 -0
  44. package/dist/Model/Repository/internal/internal.d.ts.map +1 -0
  45. package/dist/Model/Repository/internal/internal.js +611 -0
  46. package/dist/Model/Repository/legacy.d.ts +21 -0
  47. package/dist/Model/Repository/legacy.d.ts.map +1 -0
  48. package/dist/Model/Repository/legacy.js +2 -0
  49. package/dist/Model/Repository/makeRepo.d.ts +71 -0
  50. package/dist/Model/Repository/makeRepo.d.ts.map +1 -0
  51. package/dist/Model/Repository/makeRepo.js +30 -0
  52. package/dist/Model/Repository/service.d.ts +121 -0
  53. package/dist/Model/Repository/service.d.ts.map +1 -0
  54. package/dist/Model/Repository/service.js +2 -0
  55. package/dist/Model/Repository/validation.d.ts +72 -0
  56. package/dist/Model/Repository/validation.d.ts.map +1 -0
  57. package/dist/Model/Repository/validation.js +39 -0
  58. package/dist/Model/Repository.d.ts +8 -0
  59. package/dist/Model/Repository.d.ts.map +1 -0
  60. package/dist/Model/Repository.js +8 -0
  61. package/dist/Model/dsl.d.ts +33 -0
  62. package/dist/Model/dsl.d.ts.map +1 -0
  63. package/dist/Model/dsl.js +43 -0
  64. package/dist/Model/filter/filterApi.d.ts +30 -0
  65. package/dist/Model/filter/filterApi.d.ts.map +1 -0
  66. package/dist/Model/filter/filterApi.js +2 -0
  67. package/dist/Model/filter/types/errors.d.ts +29 -0
  68. package/dist/Model/filter/types/errors.d.ts.map +1 -0
  69. package/dist/Model/filter/types/errors.js +2 -0
  70. package/dist/Model/filter/types/fields.d.ts +15 -0
  71. package/dist/Model/filter/types/fields.d.ts.map +1 -0
  72. package/dist/Model/filter/types/fields.js +2 -0
  73. package/dist/Model/filter/types/path/common.d.ts +316 -0
  74. package/dist/Model/filter/types/path/common.d.ts.map +1 -0
  75. package/dist/Model/filter/types/path/common.js +2 -0
  76. package/dist/Model/filter/types/path/eager.d.ts +94 -0
  77. package/dist/Model/filter/types/path/eager.d.ts.map +1 -0
  78. package/dist/Model/filter/types/path/eager.js +36 -0
  79. package/dist/Model/filter/types/path/index.d.ts +4 -0
  80. package/dist/Model/filter/types/path/index.d.ts.map +1 -0
  81. package/dist/Model/filter/types/path/index.js +3 -0
  82. package/dist/Model/filter/types/utils.d.ts +79 -0
  83. package/dist/Model/filter/types/utils.d.ts.map +1 -0
  84. package/dist/Model/filter/types/utils.js +2 -0
  85. package/dist/Model/filter/types/validator.d.ts +30 -0
  86. package/dist/Model/filter/types/validator.d.ts.map +1 -0
  87. package/dist/Model/filter/types/validator.js +2 -0
  88. package/dist/Model/filter/types.d.ts +5 -0
  89. package/dist/Model/filter/types.d.ts.map +1 -0
  90. package/dist/Model/filter/types.js +7 -0
  91. package/dist/Model/query/dsl.d.ts +630 -0
  92. package/dist/Model/query/dsl.d.ts.map +1 -0
  93. package/dist/Model/query/dsl.js +376 -0
  94. package/dist/Model/query/new-kid-interpreter.d.ts +132 -0
  95. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -0
  96. package/dist/Model/query/new-kid-interpreter.js +370 -0
  97. package/dist/Model/query.d.ts +15 -0
  98. package/dist/Model/query.d.ts.map +1 -0
  99. package/dist/Model/query.js +3 -0
  100. package/dist/Model.d.ts +6 -0
  101. package/dist/Model.d.ts.map +1 -0
  102. package/dist/Model.js +6 -0
  103. package/dist/NonEmptySet.d.ts +3 -1
  104. package/dist/NonEmptySet.d.ts.map +1 -1
  105. package/dist/NonEmptySet.js +2 -2
  106. package/dist/Option.d.ts +1 -0
  107. package/dist/Option.d.ts.map +1 -1
  108. package/dist/Option.js +3 -1
  109. package/dist/Pure.d.ts +5 -3
  110. package/dist/Pure.d.ts.map +1 -1
  111. package/dist/Pure.js +5 -2
  112. package/dist/QueueMaker.d.ts +11 -0
  113. package/dist/QueueMaker.d.ts.map +1 -0
  114. package/dist/QueueMaker.js +11 -0
  115. package/dist/RequestContext.d.ts +87 -0
  116. package/dist/RequestContext.d.ts.map +1 -0
  117. package/dist/RequestContext.js +45 -0
  118. package/dist/RequestScopedDependencies.d.ts +21 -0
  119. package/dist/RequestScopedDependencies.d.ts.map +1 -0
  120. package/dist/RequestScopedDependencies.js +16 -0
  121. package/dist/Schema/Class.d.ts +163 -16
  122. package/dist/Schema/Class.d.ts.map +1 -1
  123. package/dist/Schema/Class.js +193 -21
  124. package/dist/Schema/FastCheck.d.ts.map +1 -1
  125. package/dist/Schema/SchemaParser.d.ts +5 -0
  126. package/dist/Schema/SchemaParser.d.ts.map +1 -0
  127. package/dist/Schema/SchemaParser.js +6 -0
  128. package/dist/Schema/SpecialJsonSchema.d.ts +15 -2
  129. package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -1
  130. package/dist/Schema/SpecialJsonSchema.js +70 -12
  131. package/dist/Schema/SpecialOpenApi.d.ts +1 -1
  132. package/dist/Schema/SpecialOpenApi.js +3 -3
  133. package/dist/Schema/brand.d.ts +5 -5
  134. package/dist/Schema/brand.d.ts.map +1 -1
  135. package/dist/Schema/brand.js +6 -4
  136. package/dist/Schema/email.d.ts.map +1 -1
  137. package/dist/Schema/email.js +8 -5
  138. package/dist/Schema/ext.d.ts +297 -72
  139. package/dist/Schema/ext.d.ts.map +1 -1
  140. package/dist/Schema/ext.js +331 -67
  141. package/dist/Schema/moreStrings.d.ts +82 -48
  142. package/dist/Schema/moreStrings.d.ts.map +1 -1
  143. package/dist/Schema/moreStrings.js +85 -51
  144. package/dist/Schema/numbers.d.ts +33 -21
  145. package/dist/Schema/numbers.d.ts.map +1 -1
  146. package/dist/Schema/numbers.js +64 -12
  147. package/dist/Schema/phoneNumber.d.ts.map +1 -1
  148. package/dist/Schema/phoneNumber.js +6 -6
  149. package/dist/Schema/schema.d.ts +1 -1
  150. package/dist/Schema/schema.d.ts.map +1 -1
  151. package/dist/Schema/schema.js +2 -2
  152. package/dist/Schema/strings.d.ts +18 -4
  153. package/dist/Schema/strings.d.ts.map +1 -1
  154. package/dist/Schema/strings.js +1 -5
  155. package/dist/Schema.d.ts +223 -6
  156. package/dist/Schema.d.ts.map +1 -1
  157. package/dist/Schema.js +189 -23
  158. package/dist/SchemaAST.d.ts +2 -0
  159. package/dist/SchemaAST.d.ts.map +1 -0
  160. package/dist/SchemaAST.js +2 -0
  161. package/dist/Set.d.ts +4 -1
  162. package/dist/Set.d.ts.map +1 -1
  163. package/dist/Set.js +3 -2
  164. package/dist/Store.d.ts +215 -0
  165. package/dist/Store.d.ts.map +1 -0
  166. package/dist/Store.js +113 -0
  167. package/dist/_ext/Array.d.ts +1 -1
  168. package/dist/_ext/Array.d.ts.map +1 -1
  169. package/dist/_ext/Array.js +4 -2
  170. package/dist/_ext/misc.d.ts +4 -1
  171. package/dist/_ext/misc.d.ts.map +1 -1
  172. package/dist/_ext/misc.js +4 -2
  173. package/dist/_ext/ord.ext.d.ts +2 -1
  174. package/dist/_ext/ord.ext.d.ts.map +1 -1
  175. package/dist/_ext/ord.ext.js +2 -2
  176. package/dist/builtin.d.ts +2 -14
  177. package/dist/builtin.d.ts.map +1 -1
  178. package/dist/builtin.js +1 -1
  179. package/dist/client/InvalidationKeys.d.ts +30 -0
  180. package/dist/client/InvalidationKeys.d.ts.map +1 -0
  181. package/dist/client/InvalidationKeys.js +37 -0
  182. package/dist/client/apiClientFactory.d.ts +13 -8
  183. package/dist/client/apiClientFactory.d.ts.map +1 -1
  184. package/dist/client/apiClientFactory.js +148 -29
  185. package/dist/client/clientFor.d.ts +48 -13
  186. package/dist/client/clientFor.d.ts.map +1 -1
  187. package/dist/client/clientFor.js +9 -1
  188. package/dist/client/errors.d.ts +74 -28
  189. package/dist/client/errors.d.ts.map +1 -1
  190. package/dist/client/errors.js +41 -10
  191. package/dist/client/makeClient.d.ts +487 -32
  192. package/dist/client/makeClient.d.ts.map +1 -1
  193. package/dist/client/makeClient.js +47 -21
  194. package/dist/client.d.ts +2 -0
  195. package/dist/client.d.ts.map +1 -1
  196. package/dist/client.js +3 -1
  197. package/dist/faker.d.ts.map +1 -1
  198. package/dist/http/Request.d.ts +2 -2
  199. package/dist/http/Request.d.ts.map +1 -1
  200. package/dist/http/Request.js +2 -2
  201. package/dist/http/internal/lib.d.ts +12 -12
  202. package/dist/http/internal/lib.d.ts.map +1 -1
  203. package/dist/http/internal/lib.js +13 -13
  204. package/dist/ids.d.ts +32 -14
  205. package/dist/ids.d.ts.map +1 -1
  206. package/dist/ids.js +38 -7
  207. package/dist/index.d.ts +3 -0
  208. package/dist/index.d.ts.map +1 -1
  209. package/dist/index.js +5 -1
  210. package/dist/middleware.d.ts +12 -6
  211. package/dist/middleware.d.ts.map +1 -1
  212. package/dist/middleware.js +13 -7
  213. package/dist/rpc/Invalidation.d.ts +1124 -0
  214. package/dist/rpc/Invalidation.d.ts.map +1 -0
  215. package/dist/rpc/Invalidation.js +178 -0
  216. package/dist/rpc/MiddlewareMaker.d.ts +10 -7
  217. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  218. package/dist/rpc/MiddlewareMaker.js +57 -37
  219. package/dist/rpc/RpcContextMap.d.ts +2 -2
  220. package/dist/rpc/RpcContextMap.d.ts.map +1 -1
  221. package/dist/rpc/RpcMiddleware.d.ts +13 -10
  222. package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
  223. package/dist/rpc/RpcMiddleware.js +2 -2
  224. package/dist/rpc.d.ts +1 -1
  225. package/dist/rpc.d.ts.map +1 -1
  226. package/dist/rpc.js +2 -2
  227. package/dist/runtime.d.ts +19 -0
  228. package/dist/runtime.d.ts.map +1 -0
  229. package/dist/runtime.js +40 -0
  230. package/dist/setupRequest.d.ts +20 -0
  231. package/dist/setupRequest.d.ts.map +1 -0
  232. package/dist/setupRequest.js +72 -0
  233. package/dist/toast.d.ts +51 -0
  234. package/dist/toast.d.ts.map +1 -0
  235. package/dist/toast.js +34 -0
  236. package/dist/transform.d.ts +1 -1
  237. package/dist/transform.d.ts.map +1 -1
  238. package/dist/transform.js +4 -5
  239. package/dist/utils/effectify.d.ts +1 -1
  240. package/dist/utils/effectify.d.ts.map +1 -1
  241. package/dist/utils/effectify.js +2 -2
  242. package/dist/utils/extend.d.ts +6 -1
  243. package/dist/utils/extend.d.ts.map +1 -1
  244. package/dist/utils/extend.js +24 -6
  245. package/dist/utils/gen.d.ts +4 -4
  246. package/dist/utils/gen.d.ts.map +1 -1
  247. package/dist/utils/logLevel.d.ts +2 -2
  248. package/dist/utils/logLevel.d.ts.map +1 -1
  249. package/dist/utils/logger.d.ts +2 -1
  250. package/dist/utils/logger.d.ts.map +1 -1
  251. package/dist/utils/logger.js +2 -2
  252. package/dist/utils.d.ts +34 -39
  253. package/dist/utils.d.ts.map +1 -1
  254. package/dist/utils.js +33 -37
  255. package/dist/validation/validators.d.ts.map +1 -1
  256. package/dist/validation.d.ts.map +1 -1
  257. package/dist/withToast.d.ts +30 -0
  258. package/dist/withToast.d.ts.map +1 -0
  259. package/dist/withToast.js +64 -0
  260. package/package.json +26 -266
  261. package/src/Array.ts +5 -5
  262. package/src/Chunk.ts +3 -3
  263. package/src/Config/SecretURL.ts +6 -3
  264. package/src/Config/internal/configSecretURL.ts +2 -2
  265. package/src/Config.ts +6 -13
  266. package/src/ConfigProvider.ts +4 -47
  267. package/src/Context.ts +9 -8
  268. package/src/DataDependencies.ts +171 -0
  269. package/src/Effect.ts +9 -12
  270. package/src/Emailer.ts +51 -0
  271. package/src/Inputify.type.ts +1 -1
  272. package/src/Layer.ts +8 -5
  273. package/src/Model/Repository/Registry.ts +35 -0
  274. package/src/Model/Repository/dependency.ts +32 -0
  275. package/src/Model/Repository/ext.ts +374 -0
  276. package/src/Model/Repository/internal/internal.ts +988 -0
  277. package/src/Model/Repository/legacy.ts +29 -0
  278. package/src/Model/Repository/makeRepo.ts +168 -0
  279. package/src/Model/Repository/service.ts +676 -0
  280. package/src/Model/Repository/validation.ts +38 -0
  281. package/src/Model/Repository.ts +7 -0
  282. package/src/Model/dsl.ts +129 -0
  283. package/src/Model/filter/filterApi.ts +78 -0
  284. package/src/Model/filter/types/errors.ts +47 -0
  285. package/src/Model/filter/types/fields.ts +50 -0
  286. package/src/Model/filter/types/path/common.ts +404 -0
  287. package/src/Model/filter/types/path/eager.ts +329 -0
  288. package/src/Model/filter/types/path/index.ts +4 -0
  289. package/src/Model/filter/types/utils.ts +128 -0
  290. package/src/Model/filter/types/validator.ts +46 -0
  291. package/src/Model/filter/types.ts +6 -0
  292. package/src/Model/query/dsl.ts +3092 -0
  293. package/src/Model/query/new-kid-interpreter.ts +532 -0
  294. package/src/Model/query.ts +13 -0
  295. package/src/Model.ts +5 -0
  296. package/src/NonEmptySet.ts +6 -4
  297. package/src/Option.ts +2 -0
  298. package/src/Pure.ts +6 -3
  299. package/src/QueueMaker.ts +11 -0
  300. package/src/RequestContext.ts +48 -0
  301. package/src/RequestScopedDependencies.ts +45 -0
  302. package/src/Schema/Class.ts +550 -56
  303. package/src/Schema/SchemaParser.ts +12 -0
  304. package/src/Schema/SpecialJsonSchema.ts +75 -7
  305. package/src/Schema/SpecialOpenApi.ts +5 -5
  306. package/src/Schema/brand.ts +27 -12
  307. package/src/Schema/email.ts +9 -7
  308. package/src/Schema/ext.ts +408 -91
  309. package/src/Schema/moreStrings.ts +213 -88
  310. package/src/Schema/numbers.ts +107 -29
  311. package/src/Schema/phoneNumber.ts +8 -9
  312. package/src/Schema/schema.ts +1 -1
  313. package/src/Schema/strings.ts +23 -14
  314. package/src/Schema.ts +403 -46
  315. package/src/SchemaAST.ts +1 -0
  316. package/src/Set.ts +6 -2
  317. package/src/Store.ts +325 -0
  318. package/src/_ext/Array.ts +4 -2
  319. package/src/_ext/misc.ts +4 -1
  320. package/src/_ext/ord.ext.ts +2 -1
  321. package/src/builtin-json.d.ts +24 -0
  322. package/src/builtin.ts +16 -21
  323. package/src/client/InvalidationKeys.ts +55 -0
  324. package/src/client/apiClientFactory.ts +291 -126
  325. package/src/client/clientFor.ts +101 -33
  326. package/src/client/errors.ts +66 -20
  327. package/src/client/makeClient.ts +519 -60
  328. package/src/client.ts +6 -4
  329. package/src/http/Request.ts +4 -4
  330. package/src/http/internal/lib.ts +12 -12
  331. package/src/http.ts +1 -1
  332. package/src/ids.ts +45 -8
  333. package/src/index.ts +20 -16
  334. package/src/middleware.ts +12 -8
  335. package/src/rpc/Invalidation.ts +278 -0
  336. package/src/rpc/MiddlewareMaker.ts +86 -79
  337. package/src/rpc/README.md +2 -2
  338. package/src/rpc/RpcContextMap.ts +4 -4
  339. package/src/rpc/RpcMiddleware.ts +18 -13
  340. package/src/rpc.ts +4 -4
  341. package/src/runtime.ts +57 -0
  342. package/src/setupRequest.ts +138 -0
  343. package/src/toast.ts +54 -0
  344. package/src/transform.ts +4 -4
  345. package/src/utils/effectify.ts +1 -1
  346. package/src/utils/extend.ts +24 -5
  347. package/src/utils/gen.ts +7 -7
  348. package/src/utils/logLevel.ts +1 -1
  349. package/src/utils/logger.ts +3 -2
  350. package/src/utils.ts +85 -158
  351. package/src/validation.ts +2 -2
  352. package/src/withToast.ts +133 -0
  353. package/test/dist/rpc.test.d.ts.map +1 -1
  354. package/test/dist/rpcMiddlewareContext.test.d.ts.map +1 -0
  355. package/test/moreStrings.test.ts +55 -10
  356. package/test/rpc.test.ts +28 -11
  357. package/test/rpcMiddlewareContext.test.ts +63 -0
  358. package/test/schema.test.ts +318 -55
  359. package/test/secretURL.test.ts +4 -1
  360. package/test/special.test.ts +663 -131
  361. package/test/utils.test.ts +5 -5
  362. package/tsconfig.base.json +6 -4
  363. package/tsconfig.json +9 -1
  364. package/tsconfig.src.json +29 -29
  365. package/tsconfig.test.json +2 -2
  366. package/dist/Operations.d.ts +0 -124
  367. package/dist/Operations.d.ts.map +0 -1
  368. package/dist/Operations.js +0 -29
  369. package/eslint.config.mjs +0 -26
  370. package/src/Operations.ts +0 -55
  371. package/tsconfig.json.bak +0 -12
@@ -1,6 +1,9 @@
1
1
  // import { generateFromArbitrary } from "@effect-app/infra/test"
2
- import { Array, S } from "effect-app"
2
+ import * as Array from "effect-app/Array"
3
+ import * as S from "effect-app/Schema"
3
4
  import { specialJsonSchemaDocument } from "effect-app/Schema/SpecialJsonSchema"
5
+ import * as Effect from "effect/Effect"
6
+ import * as Struct from "effect/Struct"
4
7
  import { describe, expect, expectTypeOf, test } from "vitest"
5
8
 
6
9
  const A = S.Struct({ a: S.NonEmptyString255, email: S.NullOr(S.Email) })
@@ -17,20 +20,168 @@ test("works", () => {
17
20
  test("literal default works", () => {
18
21
  const l = S.Literals(["a", "b"])
19
22
  expect(l.Default).toBe("a")
20
- const s = S.Struct({ l: l.withDefault })
23
+ expectTypeOf(l.Default).toEqualTypeOf<"a">()
24
+ const s = S.Struct({ l: l.withConstructorDefault })
21
25
  expect(s.make({}).l).toBe("a")
22
26
 
23
27
  const l2 = l.changeDefault("b")
24
- const s2 = S.Struct({ l: l2.withDefault })
28
+ const s2 = S.Struct({ l: l2.withConstructorDefault })
25
29
  expect(s2.make({}).l).toBe("b")
26
30
  })
27
31
 
32
+ // Toy schema for `dropConstructorDefault`: a field with a constructor
33
+ // default that should NOT auto-populate when deriving a partial-update
34
+ // schema from a "create" schema.
35
+ const Person = S.Struct({
36
+ name: S.String,
37
+ notes: S.NullOr(S.String).pipe(S.optionalKey, S.withConstructorDefault(Effect.succeed(null)))
38
+ })
39
+
40
+ test("plain Struct.omit leaks the inherited constructor default (the bug)", () => {
41
+ const UpdatePersonBuggy = S.Struct(Struct.omit(Person.fields, [] as const))
42
+ expect(UpdatePersonBuggy.make({ name: "Mario" })).toEqual({ name: "Mario", notes: null })
43
+ })
44
+
45
+ test("Struct.map(S.dropConstructorDefault) strips the inherited constructor default (the fix)", () => {
46
+ const UpdatePersonFixed = S.Struct(Struct.map(Struct.omit(Person.fields, [] as const), S.dropConstructorDefault))
47
+ expect(UpdatePersonFixed.make({ name: "Mario" })).toEqual({ name: "Mario" })
48
+ })
49
+
50
+ test("Struct.map(S.dropConstructorDefault) still omits the requested keys, like Struct.omit", () => {
51
+ const NameOnly = S.Struct(Struct.map(Struct.omit(Person.fields, ["notes"] as const), S.dropConstructorDefault))
52
+ expect(NameOnly.make({ name: "Mario" })).toEqual({ name: "Mario" })
53
+ expectTypeOf<typeof NameOnly.Type>().toEqualTypeOf<{ readonly name: string }>()
54
+ })
55
+
56
+ test("makeExactOptional drops the inherited constructor default too", () => {
57
+ const UpdatePerson = S.Struct(S.makeExactOptional(Person.fields))
58
+ expect(UpdatePerson.make({ name: "Mario" })).toEqual({ name: "Mario" })
59
+ })
60
+
61
+ test("dropConstructorDefault stays 'no-default' after annotate/annotateKey (Rebuild fix)", () => {
62
+ const notesDropped = S.dropConstructorDefault(Person.fields.notes)
63
+ expectTypeOf<(typeof notesDropped)["~type.constructor.default"]>().toEqualTypeOf<"no-default">()
64
+
65
+ const notesAnnotated = notesDropped.annotate({ description: "no longer defaulted" })
66
+ expectTypeOf<(typeof notesAnnotated)["~type.constructor.default"]>().toEqualTypeOf<"no-default">()
67
+
68
+ const notesKeyAnnotated = notesDropped.annotateKey({ description: "no longer defaulted (key)" })
69
+ expectTypeOf<(typeof notesKeyAnnotated)["~type.constructor.default"]>().toEqualTypeOf<"no-default">()
70
+
71
+ const UpdatePerson = S.Struct({ name: Person.fields.name, notes: notesAnnotated })
72
+ expect(UpdatePerson.make({ name: "Mario" })).toEqual({ name: "Mario" })
73
+ })
74
+
75
+ test("NonEmptyString255.Type uses the named brand alias", () => {
76
+ type A = typeof S.NonEmptyString255.Type
77
+ type B = string & S.NonEmptyString255Brand
78
+ expectTypeOf<A>().toEqualTypeOf<B>()
79
+ })
80
+
81
+ test("Opaque accepts an explicit Encoded type", () => {
82
+ interface User {
83
+ readonly id: string
84
+ readonly _tag: "User"
85
+ }
86
+
87
+ interface UserEncoded {
88
+ readonly id: string
89
+ }
90
+
91
+ const baseSchema = S.Struct({ id: S.String, name: S.String })
92
+ const _UserSchema = S.Opaque<User, UserEncoded>()(baseSchema)
93
+
94
+ expectTypeOf<S.Codec.Encoded<typeof _UserSchema>>().toEqualTypeOf<UserEncoded>()
95
+ expectTypeOf<S.Schema.Type<typeof _UserSchema>>().toEqualTypeOf<User>()
96
+ expectTypeOf<S.Codec.Encoded<typeof _UserSchema>>().not.toEqualTypeOf<S.Codec.Encoded<typeof baseSchema>>()
97
+ })
98
+
99
+ test("Opaque with one generic keeps the base encoded shape", () => {
100
+ interface User {
101
+ readonly id: string
102
+ }
103
+
104
+ const baseSchema = S.Struct({ id: S.String })
105
+ const _UserSchema = S.Opaque<User>()(baseSchema)
106
+
107
+ expectTypeOf<S.Codec.Encoded<typeof _UserSchema>>().toEqualTypeOf<{ readonly id: string }>()
108
+ expectTypeOf<S.Schema.Type<typeof _UserSchema>>().toEqualTypeOf<User>()
109
+ })
110
+
111
+ test("Opaque preserves optional Struct.make input", () => {
112
+ interface User {
113
+ readonly a?: string | undefined
114
+ readonly b?: number | undefined
115
+ }
116
+
117
+ const schema = S.Opaque<User>()(S.Struct({
118
+ a: S.optional(S.String),
119
+ b: S.optional(S.Number)
120
+ }))
121
+
122
+ const made = schema.make()
123
+ expect(made).toEqual({})
124
+ expectTypeOf(made).toEqualTypeOf<User>()
125
+ })
126
+
127
+ test("Opaque preserves optional TaggedStruct.make input", () => {
128
+ interface OnlyTag {
129
+ readonly _tag: "OnlyTag"
130
+ }
131
+
132
+ const schema = S.Opaque<OnlyTag>()(S.TaggedStruct("OnlyTag", {}))
133
+
134
+ const made = schema.make()
135
+ expect(made).toEqual({ _tag: "OnlyTag" })
136
+ expectTypeOf(made).toEqualTypeOf<OnlyTag>()
137
+ })
138
+
139
+ test("S.Literals([\"A\", \"B\"]).Default is typed as \"A\"", () => {
140
+ const l = S.Literals(["A", "B"])
141
+ expect(l.Default).toBe("A")
142
+ expectTypeOf(l.Default).toEqualTypeOf<"A">()
143
+ })
144
+
145
+ test("Struct.make accepts void when all fields are optional", () => {
146
+ const schema = S.Struct({
147
+ a: S.optional(S.String),
148
+ b: S.optional(S.Number)
149
+ })
150
+
151
+ const made = schema.make()
152
+ expect(made).toEqual({})
153
+ expectTypeOf(made).toEqualTypeOf<{ readonly a?: string | undefined; readonly b?: number | undefined }>()
154
+ })
155
+
156
+ test("StructNestedEncoded resolves Struct and from.Encoded", () => {
157
+ const plain = S.Struct({ a: S.String, b: S.NullOr(S.String) })
158
+ const encodedKeys = plain.pipe(S.encodeKeys({ b: "b_encoded" }))
159
+
160
+ expectTypeOf<S.StructNestedEncoded<typeof plain>>().toEqualTypeOf<{
161
+ readonly a: string
162
+ readonly b: string | null
163
+ }>()
164
+
165
+ expectTypeOf<S.StructNestedEncoded<typeof encodedKeys>>().toEqualTypeOf<{
166
+ readonly a: string
167
+ readonly b_encoded: string | null
168
+ }>()
169
+ })
170
+
171
+ test("TaggedStruct.make accepts void when only constructor-default fields exist", () => {
172
+ const schema = S.TaggedStruct("OnlyTag", {})
173
+
174
+ const made = schema.make()
175
+ expect(made).toEqual({ _tag: "OnlyTag" })
176
+ expectTypeOf(made).toEqualTypeOf<{ readonly _tag: "OnlyTag" }>()
177
+ })
178
+
28
179
  test("tagged union derives tag map and tags from v4 literal ast", () => {
29
- const schema = S.TaggedUnion(
180
+ const schema = S.TaggedUnion([
30
181
  S.TaggedStruct("A", { a: S.String }),
31
182
  S.TaggedStruct("B", { b: S.Finite }),
32
183
  S.TaggedStruct("C", { c: S.Boolean })
33
- )
184
+ ])
34
185
  const caseA = schema.cases["A"]
35
186
  const caseB = schema.cases["B"]
36
187
  const caseC = schema.cases["C"]
@@ -57,21 +208,21 @@ test("tagged union derives tag map and tags from v4 literal ast", () => {
57
208
  })
58
209
 
59
210
  test("TaggedUnion tags returns a Literals schema with correct literal values", () => {
60
- const schema = S.TaggedUnion(
211
+ const schema = S.TaggedUnion([
61
212
  S.TaggedStruct("X", { x: S.String }),
62
213
  S.TaggedStruct("Y", { y: S.Finite })
63
- )
214
+ ])
64
215
 
65
216
  expect(schema.tags.literals).toEqual(["X", "Y"])
66
217
  expectTypeOf(schema.tags.literals).toMatchTypeOf<readonly ["X", "Y"]>()
67
218
  })
68
219
 
69
220
  test("TaggedUnion tags.pick returns a subset of the tag literals", () => {
70
- const schema = S.TaggedUnion(
221
+ const schema = S.TaggedUnion([
71
222
  S.TaggedStruct("A", { a: S.String }),
72
223
  S.TaggedStruct("B", { b: S.Finite }),
73
224
  S.TaggedStruct("C", { c: S.Boolean })
74
- )
225
+ ])
75
226
 
76
227
  const subset = schema.tags.pick(["A", "C"])
77
228
  expect(subset.literals).toEqual(["A", "C"])
@@ -110,24 +261,23 @@ test("ExtendTaggedUnion adds tags to an existing Union", () => {
110
261
  })
111
262
 
112
263
  test("TaggedUnion match dispatches on _tag", () => {
113
- const schema = S.TaggedUnion(
264
+ const schema = S.TaggedUnion([
114
265
  S.TaggedStruct("A", { a: S.String }),
115
266
  S.TaggedStruct("B", { b: S.Finite })
116
- )
117
- type T = S.Schema.Type<typeof schema>
267
+ ])
118
268
 
119
269
  const matcher = schema.match({
120
270
  A: (v) => `got A: ${v.a}`,
121
271
  B: (v) => `got B: ${v.b}`
122
272
  })
123
- expect(matcher({ _tag: "A", a: "hello" } as T)).toBe("got A: hello")
124
- expect(matcher({ _tag: "B", b: 42 } as T)).toBe("got B: 42")
273
+ expect(matcher({ _tag: "A", a: "hello" })).toBe("got A: hello")
274
+ expect(matcher({ _tag: "B", b: 42 })).toBe("got B: 42")
125
275
  })
126
276
 
127
277
  test("TaggedUnion with single member", () => {
128
- const schema = S.TaggedUnion(
278
+ const schema = S.TaggedUnion([
129
279
  S.TaggedStruct("Only", { val: S.String })
130
- )
280
+ ])
131
281
 
132
282
  expect(schema.tags.literals).toEqual(["Only"])
133
283
  expect(S.decodeSync(schema.tags)("Only")).toBe("Only")
@@ -136,11 +286,11 @@ test("TaggedUnion with single member", () => {
136
286
  })
137
287
 
138
288
  test("TaggedUnion tags type is narrowed to the exact tag literals", () => {
139
- const schema = S.TaggedUnion(
289
+ const schema = S.TaggedUnion([
140
290
  S.TaggedStruct("Alpha", { a: S.String }),
141
291
  S.TaggedStruct("Beta", { b: S.Finite }),
142
292
  S.TaggedStruct("Gamma", { c: S.Boolean })
143
- )
293
+ ])
144
294
 
145
295
  type Tags = S.Schema.Type<typeof schema.tags>
146
296
  expectTypeOf<Tags>().toEqualTypeOf<"Alpha" | "Beta" | "Gamma">()
@@ -154,7 +304,7 @@ test("TaggedUnion with encodeKeys renaming a non-tag key", () => {
154
304
  S.encodeKeys({ lastName: "last_name" })
155
305
  )
156
306
 
157
- const schema = S.TaggedUnion(MemberA, MemberB)
307
+ const schema = S.TaggedUnion([MemberA, MemberB])
158
308
 
159
309
  expect(schema.tags.literals).toEqual(["A", "B"])
160
310
  expect(S.decodeSync(schema.tags)("A")).toBe("A")
@@ -168,8 +318,7 @@ test("TaggedUnion with encodeKeys renaming a non-tag key", () => {
168
318
  expect(decoded2).toEqual({ _tag: "B", lastName: 42 })
169
319
 
170
320
  // encode back to snake_case
171
- type T = S.Schema.Type<typeof schema>
172
- const encoded = S.encodeSync(schema)({ _tag: "A", firstName: "Alice" } as T)
321
+ const encoded = S.encodeSync(schema)({ _tag: "A", firstName: "Alice" })
173
322
  expect(encoded).toEqual({ _tag: "A", first_name: "Alice" })
174
323
 
175
324
  // guards work on decoded values
@@ -182,7 +331,7 @@ test("TaggedUnion with TaggedClass members", () => {
182
331
  class Foo extends S.TaggedClass<Foo>()("Foo", { name: S.String }) {}
183
332
  class Bar extends S.TaggedClass<Bar>()("Bar", { count: S.Finite }) {}
184
333
 
185
- const schema = S.TaggedUnion(Foo, Bar)
334
+ const schema = S.TaggedUnion([Foo, Bar])
186
335
 
187
336
  expect(schema.tags.literals).toEqual(["Foo", "Bar"])
188
337
  expect(S.decodeSync(schema.tags)("Foo")).toBe("Foo")
@@ -236,7 +385,7 @@ describe("ReadonlySetFromArray", () => {
236
385
 
237
386
  describe("ReadonlyMapFromArray", () => {
238
387
  test("decodes an array of tuples to a Map", () => {
239
- const schema = S.ReadonlyMap({ key: S.String, value: S.Finite })
388
+ const schema = S.ReadonlyMapFromArray({ key: S.String, value: S.Finite })
240
389
  const decoded = S.decodeUnknownSync(schema)([["a", 1], ["b", 2]])
241
390
  expect(decoded).toEqual(new Map([["a", 1], ["b", 2]]))
242
391
  })
@@ -286,34 +435,48 @@ describe("ReadonlyMapFromArray", () => {
286
435
  })
287
436
  })
288
437
 
289
- describe("ReadonlySet (with withDefault)", () => {
290
- test("make provides withDefault", () => {
438
+ describe("ReadonlySet (with withConstructorDefault)", () => {
439
+ test("make provides withConstructorDefault", () => {
291
440
  const schema = S.ReadonlySet(S.NumberFromString)
292
- const struct = S.Struct({ items: schema.withDefault })
441
+ const struct = S.Struct({ items: schema.withConstructorDefault })
293
442
  const made = struct.make({})
294
443
  expect(made.items).toEqual(new Set())
295
444
  })
296
445
 
297
- test("decodes array with NumberFromString values", () => {
446
+ test("decodes a Set with NumberFromString values", () => {
298
447
  const schema = S.ReadonlySet(S.NumberFromString)
299
- const decoded = S.decodeUnknownSync(schema)(["1", "2"])
448
+ const decoded = S.decodeUnknownSync(schema)(new Set(["1", "2"]))
300
449
  expect(decoded).toEqual(new Set([1, 2]))
301
450
  })
451
+
452
+ test("Encoded is a Set, not an array", () => {
453
+ const schema = S.ReadonlySet(S.String)
454
+ const encoded = S.encodeSync(schema)(new Set(["a"]))
455
+ expect(encoded).toEqual(new Set(["a"]))
456
+ expectTypeOf(encoded).toEqualTypeOf<ReadonlySet<string>>()
457
+ })
302
458
  })
303
459
 
304
- describe("ReadonlyMap (with withDefault)", () => {
305
- test("make provides withDefault", () => {
460
+ describe("ReadonlyMap (with withConstructorDefault)", () => {
461
+ test("make provides withConstructorDefault", () => {
306
462
  const schema = S.ReadonlyMap({ key: S.NumberFromString, value: S.String })
307
- const struct = S.Struct({ items: schema.withDefault })
463
+ const struct = S.Struct({ items: schema.withConstructorDefault })
308
464
  const made = struct.make({})
309
465
  expect(made.items).toEqual(new Map())
310
466
  })
311
467
 
312
- test("decodes array of tuples with NumberFromString keys", () => {
468
+ test("decodes a Map with NumberFromString keys", () => {
313
469
  const schema = S.ReadonlyMap({ key: S.NumberFromString, value: S.String })
314
- const decoded = S.decodeUnknownSync(schema)([["1", "one"]])
470
+ const decoded = S.decodeUnknownSync(schema)(new Map([["1", "one"]]))
315
471
  expect(decoded).toEqual(new Map([[1, "one"]]))
316
472
  })
473
+
474
+ test("Encoded is a Map, not an array of tuples", () => {
475
+ const schema = S.ReadonlyMap({ key: S.String, value: S.Finite })
476
+ const encoded = S.encodeSync(schema)(new Map([["a", 1]]))
477
+ expect(encoded).toEqual(new Map([["a", 1]]))
478
+ expectTypeOf(encoded).toEqualTypeOf<ReadonlyMap<string, number>>()
479
+ })
317
480
  })
318
481
 
319
482
  describe("JSON Schema", () => {
@@ -328,10 +491,8 @@ describe("JSON Schema", () => {
328
491
  title: "Email",
329
492
  description: "an email according to RFC 5322",
330
493
  format: "email",
331
- allOf: [
332
- { minLength: 3 },
333
- { maxLength: 998 }
334
- ]
494
+ minLength: 2,
495
+ maxLength: 998
335
496
  }
336
497
  }
337
498
  })
@@ -348,37 +509,32 @@ describe("JSON Schema", () => {
348
509
  title: "Email",
349
510
  description: "an email according to RFC 5322",
350
511
  format: "email",
351
- minLength: 3,
512
+ minLength: 2,
352
513
  maxLength: 998
353
514
  }
354
515
  }
355
516
  })
356
517
  })
357
518
 
358
- test("Date has format date-time and description", () => {
519
+ test("Date Encoded is Date; JSON codec encodes ISO strings", () => {
520
+ const d = new Date("2024-01-01T00:00:00.000Z")
521
+ expect(S.decodeUnknownSync(S.Date)(d)).toBe(d)
522
+ expect(S.encodeSync(S.Date)(d)).toBe(d)
523
+ expect(S.encodeSync(S.toCodecJson(S.Date))(d)).toBe("2024-01-01T00:00:00.000Z")
359
524
  const doc = S.toJsonSchemaDocument(S.Date)
360
- expect(doc).toStrictEqual({
361
- dialect: "draft-2020-12",
362
- schema: { "$ref": "#/$defs/Date" },
363
- definitions: {
364
- Date: {
365
- type: "string",
366
- description: "a string in ISO 8601 format that will be decoded as a Date",
367
- format: "date-time"
368
- }
369
- }
370
- })
525
+ expect(doc.dialect).toBe("draft-2020-12")
526
+ expect(doc.schema).toEqual({ type: "string" })
371
527
  })
372
528
 
373
- test("DateValid has format date-time", () => {
374
- const doc = S.toJsonSchemaDocument(S.DateValid)
529
+ test("DateFromString keeps string Encoded", () => {
530
+ const doc = S.toJsonSchemaDocument(S.DateFromString)
375
531
  expect(doc).toStrictEqual({
376
532
  dialect: "draft-2020-12",
377
- schema: { "$ref": "#/$defs/Date" },
533
+ schema: { "$ref": "#/$defs/DateOrInvalid" },
378
534
  definitions: {
379
- Date: {
535
+ DateOrInvalid: {
380
536
  type: "string",
381
- description: "a string in ISO 8601 format that will be decoded as a Date",
537
+ description: "an ISO 8601 date string that will be decoded as a Date (may be invalid)",
382
538
  format: "date-time"
383
539
  }
384
540
  }
@@ -416,3 +572,110 @@ describe("JSON Schema", () => {
416
572
  })
417
573
  })
418
574
  })
575
+
576
+ describe("generateGuards", () => {
577
+ const StateSchema = S.TaggedUnion([
578
+ S.TaggedStruct("Active", { since: S.String }),
579
+ S.TaggedStruct("Inactive", { reason: S.String }),
580
+ S.TaggedStruct("Pending", { eta: S.Finite })
581
+ ])
582
+
583
+ type State = S.Schema.Type<typeof StateSchema>
584
+ type Entity = { readonly state: State; readonly name: string }
585
+
586
+ const { isActive, isAnyOf, isInactive, isPending } = StateSchema.generateGuards("state")
587
+
588
+ test("isActive narrows to Active member", () => {
589
+ const entity: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" }
590
+ expect(isActive(entity)).toBe(true)
591
+ if (isActive(entity)) {
592
+ expectTypeOf(entity.state).toEqualTypeOf<{ readonly _tag: "Active"; readonly since: string }>()
593
+ }
594
+ })
595
+
596
+ test("isActive returns false for non-Active", () => {
597
+ const entity: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "foo" }
598
+ expect(isActive(entity)).toBe(false)
599
+ })
600
+
601
+ test("isInactive narrows to Inactive member", () => {
602
+ const entity: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "foo" }
603
+ expect(isInactive(entity)).toBe(true)
604
+ })
605
+
606
+ test("isPending narrows to Pending member", () => {
607
+ const entity: Entity = { state: { _tag: "Pending", eta: 42 }, name: "foo" }
608
+ expect(isPending(entity)).toBe(true)
609
+ })
610
+
611
+ test("isAnyOf narrows to union of specified members", () => {
612
+ const isActiveOrPending = isAnyOf(["Active", "Pending"])
613
+ const active: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" }
614
+ const pending: Entity = { state: { _tag: "Pending", eta: 5 }, name: "bar" }
615
+ const inactive: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "baz" }
616
+
617
+ expect(isActiveOrPending(active)).toBe(true)
618
+ expect(isActiveOrPending(pending)).toBe(true)
619
+ expect(isActiveOrPending(inactive)).toBe(false)
620
+
621
+ if (isActiveOrPending(active)) {
622
+ expectTypeOf(active.state).toEqualTypeOf<
623
+ { readonly _tag: "Active"; readonly since: string } | { readonly _tag: "Pending"; readonly eta: number }
624
+ >()
625
+ }
626
+ })
627
+
628
+ test("guards use schema-based validation (built-in guards)", () => {
629
+ expect(StateSchema.guards.Active({ _tag: "Active" })).toBe(false)
630
+ expect(StateSchema.guards.Active({ _tag: "Active", since: "2024-01-01" })).toBe(true)
631
+ })
632
+ })
633
+
634
+ describe("generateGuardsFor", () => {
635
+ const StateSchema = S.TaggedUnion([
636
+ S.TaggedStruct("Active", { since: S.String }),
637
+ S.TaggedStruct("Inactive", { reason: S.String }),
638
+ S.TaggedStruct("Pending", { eta: S.Finite })
639
+ ])
640
+
641
+ type State = S.Schema.Type<typeof StateSchema>
642
+ type Entity = { readonly state: State; readonly name: string }
643
+
644
+ const { isActive, isAnyOf } = StateSchema.generateGuardsFor<Entity>()("state")
645
+
646
+ test("isActive narrows to Active member", () => {
647
+ const entity: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" }
648
+ expect(isActive(entity)).toBe(true)
649
+ if (isActive(entity)) {
650
+ expectTypeOf(entity.state).toEqualTypeOf<{ readonly _tag: "Active"; readonly since: string }>()
651
+ }
652
+ })
653
+
654
+ test("isActive returns false for non-Active", () => {
655
+ const entity: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "foo" }
656
+ expect(isActive(entity)).toBe(false)
657
+ })
658
+
659
+ test("isAnyOf narrows to union of specified members", () => {
660
+ const isActiveOrPending = isAnyOf(["Active", "Pending"])
661
+ const active: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" }
662
+ const inactive: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "baz" }
663
+
664
+ expect(isActiveOrPending(active)).toBe(true)
665
+ expect(isActiveOrPending(inactive)).toBe(false)
666
+ })
667
+
668
+ test("ExtendTaggedUnion also exposes generateGuardsFor", () => {
669
+ const union = S.Union([
670
+ S.TaggedStruct("X", { x: S.String }),
671
+ S.TaggedStruct("Y", { y: S.Finite })
672
+ ])
673
+ const extended = S.ExtendTaggedUnion(union)
674
+ type Obj = { readonly field: S.Schema.Type<typeof extended> }
675
+ const { isX, isY } = extended.generateGuardsFor<Obj>()("field")
676
+
677
+ expect(isX({ field: { _tag: "X", x: "hi" } })).toBe(true)
678
+ expect(isX({ field: { _tag: "Y", y: 1 } })).toBe(false)
679
+ expect(isY({ field: { _tag: "Y", y: 1 } })).toBe(true)
680
+ })
681
+ })
@@ -1,4 +1,7 @@
1
- import { Chunk, Config, ConfigProvider, Effect } from "effect"
1
+ import * as Chunk from "effect/Chunk"
2
+ import * as Config from "effect/Config"
3
+ import * as ConfigProvider from "effect/ConfigProvider"
4
+ import * as Effect from "effect/Effect"
2
5
  import { describe, expect, test } from "vitest"
3
6
  import { fromChunk, fromString, isSecretURL, make, secretURL, unsafeWipe, value } from "../src/Config/SecretURL.js"
4
7