effect-app 4.0.0-beta.22 → 4.0.0-beta.220

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 (219) hide show
  1. package/CHANGELOG.md +986 -0
  2. package/dist/Array.d.ts +1 -1
  3. package/dist/Chunk.d.ts +1 -1
  4. package/dist/Chunk.d.ts.map +1 -1
  5. package/dist/Config/SecretURL.d.ts +1 -1
  6. package/dist/Config/SecretURL.d.ts.map +1 -1
  7. package/dist/Config/SecretURL.js +2 -2
  8. package/dist/Config/internal/configSecretURL.d.ts +1 -1
  9. package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
  10. package/dist/Config.d.ts +7 -0
  11. package/dist/Config.d.ts.map +1 -0
  12. package/dist/Config.js +6 -0
  13. package/dist/ConfigProvider.d.ts +39 -0
  14. package/dist/ConfigProvider.d.ts.map +1 -0
  15. package/dist/ConfigProvider.js +42 -0
  16. package/dist/Context.d.ts +40 -0
  17. package/dist/Context.d.ts.map +1 -0
  18. package/dist/Context.js +67 -0
  19. package/dist/Effect.d.ts +9 -10
  20. package/dist/Effect.d.ts.map +1 -1
  21. package/dist/Effect.js +3 -6
  22. package/dist/Function.d.ts +1 -1
  23. package/dist/Function.d.ts.map +1 -1
  24. package/dist/Inputify.type.d.ts +1 -1
  25. package/dist/Layer.d.ts +7 -6
  26. package/dist/Layer.d.ts.map +1 -1
  27. package/dist/Layer.js +1 -1
  28. package/dist/NonEmptySet.d.ts +1 -1
  29. package/dist/NonEmptySet.d.ts.map +1 -1
  30. package/dist/Option.d.ts +1 -1
  31. package/dist/Option.d.ts.map +1 -1
  32. package/dist/Pure.d.ts +5 -5
  33. package/dist/Pure.d.ts.map +1 -1
  34. package/dist/Pure.js +13 -13
  35. package/dist/Schema/Class.d.ts +66 -20
  36. package/dist/Schema/Class.d.ts.map +1 -1
  37. package/dist/Schema/Class.js +189 -22
  38. package/dist/Schema/FastCheck.d.ts +1 -1
  39. package/dist/Schema/FastCheck.d.ts.map +1 -1
  40. package/dist/Schema/Methods.d.ts +1 -1
  41. package/dist/Schema/SchemaParser.d.ts +5 -0
  42. package/dist/Schema/SchemaParser.d.ts.map +1 -0
  43. package/dist/Schema/SchemaParser.js +6 -0
  44. package/dist/Schema/SpecialJsonSchema.d.ts +33 -0
  45. package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
  46. package/dist/Schema/SpecialJsonSchema.js +122 -0
  47. package/dist/Schema/SpecialOpenApi.d.ts +32 -0
  48. package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
  49. package/dist/Schema/SpecialOpenApi.js +123 -0
  50. package/dist/Schema/brand.d.ts +4 -2
  51. package/dist/Schema/brand.d.ts.map +1 -1
  52. package/dist/Schema/brand.js +1 -1
  53. package/dist/Schema/email.d.ts +1 -1
  54. package/dist/Schema/email.d.ts.map +1 -1
  55. package/dist/Schema/email.js +7 -4
  56. package/dist/Schema/ext.d.ts +367 -56
  57. package/dist/Schema/ext.d.ts.map +1 -1
  58. package/dist/Schema/ext.js +345 -53
  59. package/dist/Schema/moreStrings.d.ts +108 -26
  60. package/dist/Schema/moreStrings.d.ts.map +1 -1
  61. package/dist/Schema/moreStrings.js +54 -16
  62. package/dist/Schema/numbers.d.ts +55 -15
  63. package/dist/Schema/numbers.d.ts.map +1 -1
  64. package/dist/Schema/numbers.js +60 -12
  65. package/dist/Schema/phoneNumber.d.ts +1 -1
  66. package/dist/Schema/phoneNumber.d.ts.map +1 -1
  67. package/dist/Schema/phoneNumber.js +6 -3
  68. package/dist/Schema/schema.d.ts +1 -1
  69. package/dist/Schema/strings.d.ts +5 -5
  70. package/dist/Schema/strings.d.ts.map +1 -1
  71. package/dist/Schema/strings.js +1 -5
  72. package/dist/Schema.d.ts +217 -15
  73. package/dist/Schema.d.ts.map +1 -1
  74. package/dist/Schema.js +221 -16
  75. package/dist/Set.d.ts +1 -1
  76. package/dist/Set.d.ts.map +1 -1
  77. package/dist/TypeTest.d.ts +1 -1
  78. package/dist/Types.d.ts +1 -1
  79. package/dist/Widen.type.d.ts +1 -1
  80. package/dist/_ext/Array.d.ts +1 -1
  81. package/dist/_ext/Array.d.ts.map +1 -1
  82. package/dist/_ext/date.d.ts +1 -1
  83. package/dist/_ext/misc.d.ts +1 -1
  84. package/dist/_ext/ord.ext.d.ts +1 -1
  85. package/dist/_ext/ord.ext.d.ts.map +1 -1
  86. package/dist/builtin.d.ts +1 -1
  87. package/dist/builtin.d.ts.map +1 -1
  88. package/dist/client/InvalidationKeys.d.ts +29 -0
  89. package/dist/client/InvalidationKeys.d.ts.map +1 -0
  90. package/dist/client/InvalidationKeys.js +33 -0
  91. package/dist/client/apiClientFactory.d.ts +20 -32
  92. package/dist/client/apiClientFactory.d.ts.map +1 -1
  93. package/dist/client/apiClientFactory.js +96 -33
  94. package/dist/client/clientFor.d.ts +51 -17
  95. package/dist/client/clientFor.d.ts.map +1 -1
  96. package/dist/client/clientFor.js +9 -1
  97. package/dist/client/errors.d.ts +49 -25
  98. package/dist/client/errors.d.ts.map +1 -1
  99. package/dist/client/errors.js +43 -17
  100. package/dist/client/makeClient.d.ts +481 -33
  101. package/dist/client/makeClient.d.ts.map +1 -1
  102. package/dist/client/makeClient.js +66 -24
  103. package/dist/client.d.ts +2 -1
  104. package/dist/client.d.ts.map +1 -1
  105. package/dist/client.js +2 -1
  106. package/dist/faker.d.ts +1 -1
  107. package/dist/faker.d.ts.map +1 -1
  108. package/dist/http/Request.d.ts +2 -2
  109. package/dist/http/Request.d.ts.map +1 -1
  110. package/dist/http/internal/lib.d.ts +1 -1
  111. package/dist/http.d.ts +1 -1
  112. package/dist/ids.d.ts +40 -12
  113. package/dist/ids.d.ts.map +1 -1
  114. package/dist/ids.js +25 -3
  115. package/dist/index.d.ts +5 -8
  116. package/dist/index.d.ts.map +1 -1
  117. package/dist/index.js +6 -8
  118. package/dist/logger.d.ts +1 -1
  119. package/dist/middleware.d.ts +14 -8
  120. package/dist/middleware.d.ts.map +1 -1
  121. package/dist/middleware.js +14 -8
  122. package/dist/rpc/Invalidation.d.ts +402 -0
  123. package/dist/rpc/Invalidation.d.ts.map +1 -0
  124. package/dist/rpc/Invalidation.js +150 -0
  125. package/dist/rpc/MiddlewareMaker.d.ts +5 -4
  126. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  127. package/dist/rpc/MiddlewareMaker.js +57 -37
  128. package/dist/rpc/RpcContextMap.d.ts +3 -3
  129. package/dist/rpc/RpcContextMap.d.ts.map +1 -1
  130. package/dist/rpc/RpcContextMap.js +4 -4
  131. package/dist/rpc/RpcMiddleware.d.ts +5 -4
  132. package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
  133. package/dist/rpc/RpcMiddleware.js +1 -1
  134. package/dist/rpc.d.ts +2 -2
  135. package/dist/rpc.d.ts.map +1 -1
  136. package/dist/rpc.js +2 -2
  137. package/dist/transform.d.ts +1 -1
  138. package/dist/transform.d.ts.map +1 -1
  139. package/dist/transform.js +3 -3
  140. package/dist/utils/effectify.d.ts +1 -1
  141. package/dist/utils/extend.d.ts +1 -1
  142. package/dist/utils/extend.d.ts.map +1 -1
  143. package/dist/utils/gen.d.ts +2 -2
  144. package/dist/utils/gen.d.ts.map +1 -1
  145. package/dist/utils/logLevel.d.ts +2 -2
  146. package/dist/utils/logLevel.d.ts.map +1 -1
  147. package/dist/utils/logger.d.ts +3 -3
  148. package/dist/utils/logger.d.ts.map +1 -1
  149. package/dist/utils/logger.js +3 -3
  150. package/dist/utils.d.ts +31 -38
  151. package/dist/utils.d.ts.map +1 -1
  152. package/dist/utils.js +12 -25
  153. package/dist/validation/validators.d.ts +1 -1
  154. package/dist/validation/validators.d.ts.map +1 -1
  155. package/dist/validation.d.ts +1 -1
  156. package/dist/validation.d.ts.map +1 -1
  157. package/package.json +46 -24
  158. package/src/Config/SecretURL.ts +2 -1
  159. package/src/Config.ts +14 -0
  160. package/src/ConfigProvider.ts +48 -0
  161. package/src/{ServiceMap.ts → Context.ts} +52 -59
  162. package/src/Effect.ts +12 -14
  163. package/src/Layer.ts +6 -5
  164. package/src/Pure.ts +17 -18
  165. package/src/Schema/Class.ts +270 -64
  166. package/src/Schema/SchemaParser.ts +12 -0
  167. package/src/Schema/SpecialJsonSchema.ts +137 -0
  168. package/src/Schema/SpecialOpenApi.ts +130 -0
  169. package/src/Schema/brand.ts +21 -1
  170. package/src/Schema/email.ts +7 -2
  171. package/src/Schema/ext.ts +425 -88
  172. package/src/Schema/moreStrings.ts +92 -37
  173. package/src/Schema/numbers.ts +64 -16
  174. package/src/Schema/phoneNumber.ts +5 -1
  175. package/src/Schema/strings.ts +4 -8
  176. package/src/Schema.ts +410 -20
  177. package/src/client/InvalidationKeys.ts +50 -0
  178. package/src/client/apiClientFactory.ts +224 -130
  179. package/src/client/clientFor.ts +95 -29
  180. package/src/client/errors.ts +52 -26
  181. package/src/client/makeClient.ts +572 -71
  182. package/src/client.ts +1 -0
  183. package/src/ids.ts +25 -3
  184. package/src/index.ts +5 -10
  185. package/src/middleware.ts +13 -9
  186. package/src/rpc/Invalidation.ts +226 -0
  187. package/src/rpc/MiddlewareMaker.ts +65 -60
  188. package/src/rpc/README.md +2 -2
  189. package/src/rpc/RpcContextMap.ts +6 -5
  190. package/src/rpc/RpcMiddleware.ts +5 -4
  191. package/src/rpc.ts +1 -1
  192. package/src/transform.ts +2 -2
  193. package/src/utils/gen.ts +1 -1
  194. package/src/utils/logger.ts +2 -2
  195. package/src/utils.ts +50 -132
  196. package/test/dist/rpc.test.d.ts.map +1 -1
  197. package/test/dist/secretURL.test.d.ts.map +1 -0
  198. package/test/dist/special.test.d.ts.map +1 -0
  199. package/test/dist/stream-error.types.d.ts +2 -0
  200. package/test/dist/stream-error.types.d.ts.map +1 -0
  201. package/test/dist/stream-error.types.js +27 -0
  202. package/test/rpc.test.ts +45 -6
  203. package/test/schema.test.ts +583 -9
  204. package/test/secretURL.test.ts +157 -0
  205. package/test/special.test.ts +1023 -0
  206. package/test/utils.test.ts +6 -6
  207. package/tsconfig.base.json +3 -4
  208. package/tsconfig.json +0 -1
  209. package/tsconfig.json.bak +2 -2
  210. package/tsconfig.src.json +29 -29
  211. package/tsconfig.test.json +2 -2
  212. package/dist/Operations.d.ts +0 -123
  213. package/dist/Operations.d.ts.map +0 -1
  214. package/dist/Operations.js +0 -29
  215. package/dist/ServiceMap.d.ts +0 -44
  216. package/dist/ServiceMap.d.ts.map +0 -1
  217. package/dist/ServiceMap.js +0 -91
  218. package/eslint.config.mjs +0 -26
  219. package/src/Operations.ts +0 -55
@@ -1,4 +1,14 @@
1
- import { pipe } from "effect"
1
+ /**
2
+ * Branded string ID schemas with `.withConstructorDefault` extensions.
3
+ *
4
+ * Each `.withConstructorDefault` here is **only** applied when the field is
5
+ * omitted during construction (`.make(...)`). It is **not** applied during
6
+ * decode and therefore cannot be used to JIT-migrate database fields.
7
+ *
8
+ * For persisted data, prefer an explicit, preferably versioned migration
9
+ * over decode-time fallbacks. See `./ext.ts` for the full policy note.
10
+ */
11
+ import { Effect, pipe } from "effect"
2
12
  import type { Refinement } from "effect-app/Function"
3
13
  import { extendM } from "effect-app/utils"
4
14
  import * as S from "effect/Schema"
@@ -6,7 +16,7 @@ import type { Simplify } from "effect/Types"
6
16
  import { customRandom, nanoid, urlAlphabet } from "nanoid"
7
17
  import validator from "validator"
8
18
  import { fromBrand, nominal } from "./brand.js"
9
- import { withDefaultConstructor, withDefaultMake, type WithDefaults } from "./ext.js"
19
+ import { withDefaultMake, type WithDefaults } from "./ext.js"
10
20
  import { type B } from "./schema.js"
11
21
  import type { NonEmptyString255Brand, NonEmptyStringBrand } from "./strings.js"
12
22
 
@@ -27,9 +37,8 @@ export type NonEmptyString50 = string & NonEmptyString50Brand
27
37
  */
28
38
  export const NonEmptyString50 = nonEmptyString.pipe(
29
39
  S.check(S.isMaxLength(50)),
30
- fromBrand(nominal<NonEmptyString50>(), {
40
+ fromBrand<NonEmptyString50>(nominal<NonEmptyString50>(), {
31
41
  identifier: "NonEmptyString50",
32
- title: "NonEmptyString50",
33
42
  jsonSchema: {}
34
43
  }),
35
44
  withDefaultMake
@@ -50,9 +59,8 @@ export type NonEmptyString64 = string & NonEmptyString64Brand
50
59
  */
51
60
  export const NonEmptyString64 = nonEmptyString.pipe(
52
61
  S.check(S.isMaxLength(64)),
53
- fromBrand(nominal<NonEmptyString64>(), {
62
+ fromBrand<NonEmptyString64>(nominal<NonEmptyString64>(), {
54
63
  identifier: "NonEmptyString64",
55
- title: "NonEmptyString64",
56
64
  jsonSchema: {}
57
65
  }),
58
66
  withDefaultMake
@@ -74,9 +82,8 @@ export type NonEmptyString80 = string & NonEmptyString80Brand
74
82
 
75
83
  export const NonEmptyString80 = nonEmptyString.pipe(
76
84
  S.check(S.isMaxLength(80)),
77
- fromBrand(nominal<NonEmptyString80>(), {
85
+ fromBrand<NonEmptyString80>(nominal<NonEmptyString80>(), {
78
86
  identifier: "NonEmptyString80",
79
- title: "NonEmptyString80",
80
87
  jsonSchema: {}
81
88
  }),
82
89
  withDefaultMake
@@ -97,9 +104,8 @@ export type NonEmptyString100 = string & NonEmptyString100Brand
97
104
  */
98
105
  export const NonEmptyString100 = nonEmptyString.pipe(
99
106
  S.check(S.isMaxLength(100)),
100
- fromBrand(nominal<NonEmptyString100>(), {
107
+ fromBrand<NonEmptyString100>(nominal<NonEmptyString100>(), {
101
108
  identifier: "NonEmptyString100",
102
- title: "NonEmptyString100",
103
109
  jsonSchema: {}
104
110
  }),
105
111
  withDefaultMake
@@ -121,7 +127,10 @@ export type Min3String255 = string & Min3String255Brand
121
127
  export const Min3String255 = pipe(
122
128
  S.String,
123
129
  S.check(S.isMinLength(3), S.isMaxLength(255)),
124
- fromBrand(nominal<Min3String255>(), { identifier: "Min3String255", title: "Min3String255", jsonSchema: {} }),
130
+ fromBrand<Min3String255>(nominal<Min3String255>(), {
131
+ identifier: "Min3String255",
132
+ jsonSchema: {}
133
+ }),
125
134
  withDefaultMake
126
135
  )
127
136
 
@@ -135,7 +144,8 @@ export interface StringIdBrand extends Simplify<B.Brand<"StringId"> & NonEmptySt
135
144
  */
136
145
  export type StringId = string & StringIdBrand
137
146
 
138
- const makeStringId = (): StringId => nanoid() as unknown as StringId
147
+ const makeStringId = (s?: string): StringId =>
148
+ s !== undefined ? S.decodeSync(StringId)(s) : nanoid() as unknown as StringId
139
149
  const minLength = 6
140
150
  const maxLength = 50
141
151
  const size = 21
@@ -146,21 +156,29 @@ const StringIdArb = (): S.LazyArbitrary<StringId> => (fc) =>
146
156
  .map((_) => customRandom(urlAlphabet, size, (size) => _.subarray(0, size))() as StringId)
147
157
  /**
148
158
  * A string that is at least 6 characters long and a maximum of 50.
159
+ *
160
+ * `.withConstructorDefault` => fresh `nanoid()` (construction-only; not
161
+ * applied during decode — see file-level note).
149
162
  */
150
163
  export const StringId = extendM(
151
164
  pipe(
152
165
  S.String,
153
166
  S.check(S.isMinLength(minLength), S.isMaxLength(maxLength)),
154
- fromBrand(nominal<StringId>(), {
167
+ fromBrand<StringId>(nominal<StringId>(), {
155
168
  identifier: "StringId",
156
- title: "StringId",
157
169
  toArbitrary: () => (fc) => StringIdArb()(fc),
158
170
  jsonSchema: {}
159
171
  })
160
172
  ),
161
173
  (s) => ({
162
174
  make: makeStringId,
163
- withDefault: s.pipe(withDefaultConstructor(makeStringId))
175
+ /**
176
+ * Construction-only default: fresh `nanoid()`-shaped `StringId`. Applied
177
+ * only when the field is omitted from `.make(...)` input. NOT applied
178
+ * during decode — cannot be used to JIT-migrate database fields. See
179
+ * file-level note.
180
+ */
181
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(makeStringId)))
164
182
  })
165
183
  )
166
184
  .pipe(withDefaultMake)
@@ -169,7 +187,15 @@ export const StringId = extendM(
169
187
 
170
188
  // const prefixedStringIdUnsafeThunk = (prefix: string) => () => prefixedStringIdUnsafe(prefix)
171
189
 
172
- export function prefixedStringId<Brand extends StringId>() {
190
+ /**
191
+ * Build a `StringId` schema whose values are required to start with a fixed
192
+ * `prefix` (joined with `separator`, default `-`).
193
+ *
194
+ * The returned schema exposes `.withConstructorDefault` that mints a fresh
195
+ * prefixed id. Construction-only — not applied during decode; see file-level
196
+ * note.
197
+ */
198
+ export function prefixedStringId<Type extends StringId>() {
173
199
  return <Prefix extends string, Separator extends string = "-">(
174
200
  prefix: Prefix,
175
201
  name: string,
@@ -177,27 +203,26 @@ export function prefixedStringId<Brand extends StringId>() {
177
203
  ) => {
178
204
  type FullPrefix = `${Prefix}${Separator}`
179
205
  const pref = `${prefix}${separator ?? "-"}` as FullPrefix
180
- const arb = (): S.LazyArbitrary<string & Brand> => (fc) =>
206
+ const arb = (): S.LazyArbitrary<Type> => (fc) =>
181
207
  StringIdArb()(fc).map(
182
- (x) => (pref + x.substring(0, 50 - pref.length)) as Brand
208
+ (x) => (pref + x.substring(0, 50 - pref.length)) as Type
183
209
  )
184
210
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
185
- const s: S.Codec<string & Brand, string> = StringId
211
+ const s = StringId
186
212
  .pipe(
187
- S.refine((x: string): x is string & Brand => x.startsWith(pref), {
188
- identifier: name,
189
- title: name
213
+ S.refine((x: string): x is Type => x.startsWith(pref), {
214
+ identifier: name
190
215
  }),
191
216
  S.annotate({
192
217
  toArbitrary: () => (fc) => arb()(fc)
193
218
  })
194
- ) as S.Codec<string & Brand, string>
219
+ )
195
220
  const schema = s.pipe(withDefaultMake)
196
- const make = () => (pref + StringId.make().substring(0, 50 - pref.length)) as Brand
221
+ const make = () => (pref + StringId.make().substring(0, 50 - pref.length)) as Type
197
222
 
198
223
  return extendM(
199
224
  schema,
200
- (ex): PrefixedStringUtils<Brand, Prefix, Separator> => ({
225
+ (ex): PrefixedStringUtils<Type, Prefix, Separator> => ({
201
226
  make,
202
227
  /**
203
228
  * Automatically adds the prefix.
@@ -208,32 +233,59 @@ export function prefixedStringId<Brand extends StringId>() {
208
233
  */
209
234
  prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) => ex(str),
210
235
  prefix,
211
- withDefault: schema.pipe(withDefaultConstructor(make))
236
+ /**
237
+ * Construction-only default: fresh prefixed id. Applied only when
238
+ * the field is omitted from `.make(...)` input. NOT applied during
239
+ * decode — cannot be used to JIT-migrate database fields. See
240
+ * file-level note.
241
+ */
242
+ withConstructorDefault: schema.pipe(
243
+ S.withConstructorDefault<S.Codec<Type, string> & S.WithoutConstructorDefault>(
244
+ Effect.sync(make)
245
+ )
246
+ )
212
247
  })
213
248
  )
214
249
  }
215
250
  }
216
251
 
252
+ /**
253
+ * Build a branded `StringId` schema for the given branded `Id` type.
254
+ *
255
+ * Exposes `.withConstructorDefault` that mints a fresh `nanoid()`-shaped id.
256
+ * Construction-only — not applied during decode; see file-level note.
257
+ */
217
258
  export const brandedStringId = <
218
- Brand extends StringIdBrand
259
+ Id
219
260
  >() =>
220
261
  withDefaultMake(
221
- Object.assign(Object.create(StringId), StringId) as S.Codec<string & Brand, string> & {
222
- make: () => string & Brand
223
- withDefault: S.withConstructorDefault<S.Codec<string & Brand, string> & S.WithoutConstructorDefault>
224
- } & WithDefaults<S.Codec<string & Brand, string>>
262
+ Object.assign(Object.create(StringId), StringId) as S.Codec<Id, string> & {
263
+ /**
264
+ * Construction-only default: fresh `nanoid()`-shaped id. Applied only
265
+ * when the field is omitted from `.make(...)` input. NOT applied
266
+ * during decode — cannot be used to JIT-migrate database fields. See
267
+ * file-level note.
268
+ */
269
+ withConstructorDefault: S.withConstructorDefault<S.Codec<Id, string> & S.WithoutConstructorDefault>
270
+ make: () => Id
271
+ } & WithDefaults<S.Codec<Id, string>>
225
272
  )
226
273
 
227
274
  export interface PrefixedStringUtils<
228
- Brand extends StringId,
275
+ Type extends StringId,
229
276
  Prefix extends string,
230
277
  Separator extends string
231
278
  > {
232
- readonly make: () => Brand
233
- readonly unsafeFrom: (str: string) => Brand
234
- prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) => Brand
279
+ readonly make: () => Type
280
+ readonly unsafeFrom: (str: string) => Type
281
+ prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) => Type
235
282
  readonly prefix: Prefix
236
- readonly withDefault: S.withConstructorDefault<S.Codec<Brand, string> & S.WithoutConstructorDefault>
283
+ /**
284
+ * Construction-only default: fresh prefixed id. Applied only when the
285
+ * field is omitted from `.make(...)` input. NOT applied during decode —
286
+ * cannot be used to JIT-migrate database fields. See file-level note.
287
+ */
288
+ readonly withConstructorDefault: S.withConstructorDefault<S.Codec<Type, string> & S.WithoutConstructorDefault>
237
289
  }
238
290
 
239
291
  export interface UrlBrand extends Simplify<B.Brand<"Url"> & NonEmptyStringBrand> {}
@@ -247,9 +299,12 @@ const isUrl: Refinement<string, Url> = (s: string): s is Url => {
247
299
  export const Url = S
248
300
  .String
249
301
  .pipe(
302
+ S.annotate({
303
+ title: "Url",
304
+ format: "uri"
305
+ }),
250
306
  S.refine(isUrl, {
251
307
  identifier: "Url",
252
- title: "Url",
253
308
  jsonSchema: { format: "uri" }
254
309
  }),
255
310
  S.annotate({
@@ -1,74 +1,122 @@
1
+ /**
2
+ * Numeric brand schemas with `.withConstructorDefault` extensions.
3
+ *
4
+ * Each `.withConstructorDefault` here is **only** applied when the field is
5
+ * omitted during construction (`.make(...)`). It is **not** applied during
6
+ * decode and therefore cannot be used to JIT-migrate database fields.
7
+ *
8
+ * For persisted data, prefer an explicit, preferably versioned migration
9
+ * over decode-time fallbacks. See `./ext.ts` for the full policy note.
10
+ */
11
+ import { Effect } from "effect"
1
12
  import { extendM } from "effect-app/utils"
2
13
  import * as S from "effect/Schema"
3
14
  import type { Simplify } from "effect/Types"
4
15
  import { fromBrand, nominal } from "./brand.js"
5
- import { withDefaultConstructor, withDefaultMake } from "./ext.js"
16
+ import { withDefaultMake } from "./ext.js"
6
17
  import { type B } from "./schema.js"
7
18
 
8
19
  export interface PositiveIntBrand
9
20
  extends Simplify<B.Brand<"PositiveInt"> & NonNegativeIntBrand & PositiveNumberBrand>
10
21
  {}
22
+ /** Positive integer. `.withConstructorDefault` => `1` (construction-only). */
11
23
  export const PositiveInt = extendM(
12
24
  S.Int.pipe(
13
25
  S.check(S.isGreaterThan(0)),
14
- fromBrand(nominal<PositiveInt>(), { identifier: "PositiveInt", title: "PositiveInt", jsonSchema: {} }),
26
+ fromBrand<PositiveInt>(nominal<PositiveInt>(), { identifier: "PositiveInt", jsonSchema: {} }),
15
27
  withDefaultMake
16
28
  ),
17
- (s) => ({ withDefault: s.pipe(withDefaultConstructor(() => s(1))) })
29
+ (s) => ({
30
+ /**
31
+ * Construction-only default `1`. Applied only when the field is omitted
32
+ * from `.make(...)` input. NOT applied during decode — cannot be used to
33
+ * JIT-migrate database fields. See file-level note.
34
+ */
35
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(1))))
36
+ })
18
37
  )
19
38
  export type PositiveInt = number & PositiveIntBrand
20
39
 
21
40
  export interface NonNegativeIntBrand extends Simplify<B.Brand<"NonNegativeInt"> & IntBrand & NonNegativeNumberBrand> {}
41
+ /** Non-negative integer. `.withConstructorDefault` => `0` (construction-only). */
22
42
  export const NonNegativeInt = extendM(
23
43
  S.Int.pipe(
24
44
  S.check(S.isGreaterThanOrEqualTo(0)),
25
- fromBrand(nominal<NonNegativeInt>(), {
45
+ fromBrand<NonNegativeInt>(nominal<NonNegativeInt>(), {
26
46
  identifier: "NonNegativeInt",
27
- title: "NonNegativeInt",
28
47
  jsonSchema: {}
29
48
  }),
30
49
  withDefaultMake
31
50
  ),
32
- (s) => ({ withDefault: s.pipe(withDefaultConstructor(() => s(0))) })
51
+ (s) => ({
52
+ /**
53
+ * Construction-only default `0`. Applied only when the field is omitted
54
+ * from `.make(...)` input. NOT applied during decode — cannot be used to
55
+ * JIT-migrate database fields. See file-level note.
56
+ */
57
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0))))
58
+ })
33
59
  )
34
60
  export type NonNegativeInt = number & NonNegativeIntBrand
35
61
 
36
62
  export interface IntBrand extends Simplify<B.Brand<"Int">> {}
63
+ /** Integer. `.withConstructorDefault` => `0` (construction-only). */
37
64
  export const Int = extendM(
38
- S.Int.pipe(fromBrand(nominal<Int>(), { identifier: "Int", title: "Int", jsonSchema: {} }), withDefaultMake),
39
- (s) => ({ withDefault: s.pipe(withDefaultConstructor(() => s(0))) })
65
+ S.Int.pipe(fromBrand<Int>(nominal<Int>(), { identifier: "Int", jsonSchema: {} }), withDefaultMake),
66
+ (s) => ({
67
+ /**
68
+ * Construction-only default `0`. Applied only when the field is omitted
69
+ * from `.make(...)` input. NOT applied during decode — cannot be used to
70
+ * JIT-migrate database fields. See file-level note.
71
+ */
72
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0))))
73
+ })
40
74
  )
41
75
  export type Int = number & IntBrand
42
76
 
43
77
  export interface PositiveNumberBrand extends Simplify<B.Brand<"PositiveNumber"> & NonNegativeNumberBrand> {}
78
+ /** Positive finite number. `.withConstructorDefault` => `1` (construction-only). */
44
79
  export const PositiveNumber = extendM(
45
- S.Number.pipe(
80
+ S.Finite.pipe(
46
81
  S.check(S.isGreaterThan(0)),
47
- fromBrand(nominal<PositiveNumber>(), {
82
+ fromBrand<PositiveNumber>(nominal<PositiveNumber>(), {
48
83
  identifier: "PositiveNumber",
49
- title: "PositiveNumber",
50
84
  jsonSchema: {}
51
85
  }),
52
86
  withDefaultMake
53
87
  ),
54
- (s) => ({ withDefault: s.pipe(withDefaultConstructor(() => s(1))) })
88
+ (s) => ({
89
+ /**
90
+ * Construction-only default `1`. Applied only when the field is omitted
91
+ * from `.make(...)` input. NOT applied during decode — cannot be used to
92
+ * JIT-migrate database fields. See file-level note.
93
+ */
94
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(1))))
95
+ })
55
96
  )
56
97
  export type PositiveNumber = number & PositiveNumberBrand
57
98
 
58
99
  export interface NonNegativeNumberBrand extends Simplify<B.Brand<"NonNegativeNumber">> {}
100
+ /** Non-negative finite number. `.withConstructorDefault` => `0` (construction-only). */
59
101
  export const NonNegativeNumber = extendM(
60
102
  S
61
- .Number
103
+ .Finite
62
104
  .pipe(
63
105
  S.check(S.isGreaterThanOrEqualTo(0)),
64
- fromBrand(nominal<NonNegativeNumber>(), {
106
+ fromBrand<NonNegativeNumber>(nominal<NonNegativeNumber>(), {
65
107
  identifier: "NonNegativeNumber",
66
- title: "NonNegativeNumber",
67
108
  jsonSchema: {}
68
109
  }),
69
110
  withDefaultMake
70
111
  ),
71
- (s) => ({ withDefault: s.pipe(withDefaultConstructor(() => s(0))) })
112
+ (s) => ({
113
+ /**
114
+ * Construction-only default `0`. Applied only when the field is omitted
115
+ * from `.make(...)` input. NOT applied during decode — cannot be used to
116
+ * JIT-migrate database fields. See file-level note.
117
+ */
118
+ withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0))))
119
+ })
72
120
  )
73
121
  export type NonNegativeNumber = number & NonNegativeNumberBrand
74
122
 
@@ -13,9 +13,13 @@ export type PhoneNumber = string & PhoneNumberBrand
13
13
  export const PhoneNumber = S
14
14
  .String
15
15
  .pipe(
16
+ S.annotate({
17
+ title: "PhoneNumber",
18
+ description: "a phone number with at least 7 digits",
19
+ format: "phone"
20
+ }),
16
21
  S.refine(isValidPhone as Refinement<string, PhoneNumber>, {
17
22
  identifier: "PhoneNumber",
18
- title: "PhoneNumber",
19
23
  description: "a phone number with at least 7 digits",
20
24
  jsonSchema: { format: "phone" }
21
25
  }),
@@ -9,9 +9,8 @@ export type NonEmptyString = string & NonEmptyStringBrand
9
9
  export const NonEmptyString = S
10
10
  .NonEmptyString
11
11
  .pipe(
12
- fromBrand(nominal<NonEmptyString>(), {
12
+ fromBrand<NonEmptyString>(nominal<NonEmptyString>(), {
13
13
  identifier: "NonEmptyString",
14
- title: "NonEmptyString",
15
14
  jsonSchema: {}
16
15
  }),
17
16
  withDefaultMake
@@ -23,9 +22,8 @@ export const NonEmptyString64k = S
23
22
  .NonEmptyString
24
23
  .pipe(
25
24
  S.check(S.isMaxLength(64 * 1024)),
26
- fromBrand(nominal<NonEmptyString64k>(), {
25
+ fromBrand<NonEmptyString64k>(nominal<NonEmptyString64k>(), {
27
26
  identifier: "NonEmptyString64k",
28
- title: "NonEmptyString64k",
29
27
  jsonSchema: {}
30
28
  }),
31
29
  withDefaultMake
@@ -37,9 +35,8 @@ export const NonEmptyString2k = S
37
35
  .NonEmptyString
38
36
  .pipe(
39
37
  S.check(S.isMaxLength(2 * 1024)),
40
- fromBrand(nominal<NonEmptyString2k>(), {
38
+ fromBrand<NonEmptyString2k>(nominal<NonEmptyString2k>(), {
41
39
  identifier: "NonEmptyString2k",
42
- title: "NonEmptyString2k",
43
40
  jsonSchema: {}
44
41
  }),
45
42
  withDefaultMake
@@ -51,9 +48,8 @@ export const NonEmptyString255 = S
51
48
  .NonEmptyString
52
49
  .pipe(
53
50
  S.check(S.isMaxLength(255)),
54
- fromBrand(nominal<NonEmptyString255>(), {
51
+ fromBrand<NonEmptyString255>(nominal<NonEmptyString255>(), {
55
52
  identifier: "NonEmptyString255",
56
- title: "NonEmptyString255",
57
53
  jsonSchema: {}
58
54
  }),
59
55
  withDefaultMake