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