@atproto/lex-schema 0.0.9 → 0.0.10
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 +34 -0
- package/LICENSE.txt +1 -1
- package/dist/core/$type.d.ts +11 -0
- package/dist/core/$type.d.ts.map +1 -1
- package/dist/core/$type.js +4 -0
- package/dist/core/$type.js.map +1 -1
- package/dist/core/schema.d.ts +31 -24
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +38 -8
- package/dist/core/schema.js.map +1 -1
- package/dist/core/string-format.d.ts +35 -35
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +49 -91
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/validation-issue.js +1 -1
- package/dist/core/validation-issue.js.map +1 -1
- package/dist/core/validator.d.ts +53 -32
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +18 -22
- package/dist/core/validator.js.map +1 -1
- package/dist/external.d.ts +0 -85
- package/dist/external.d.ts.map +1 -1
- package/dist/external.js +0 -164
- package/dist/external.js.map +1 -1
- package/dist/helpers.d.ts +10 -5
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +3 -3
- package/dist/helpers.js.map +1 -1
- package/dist/schema/array.d.ts +9 -5
- package/dist/schema/array.d.ts.map +1 -1
- package/dist/schema/array.js +14 -5
- package/dist/schema/array.js.map +1 -1
- package/dist/schema/blob.d.ts +9 -7
- package/dist/schema/blob.d.ts.map +1 -1
- package/dist/schema/blob.js +9 -5
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/boolean.d.ts +3 -7
- package/dist/schema/boolean.d.ts.map +1 -1
- package/dist/schema/boolean.js +6 -7
- package/dist/schema/boolean.js.map +1 -1
- package/dist/schema/bytes.d.ts +3 -2
- package/dist/schema/bytes.d.ts.map +1 -1
- package/dist/schema/bytes.js +7 -3
- package/dist/schema/bytes.js.map +1 -1
- package/dist/schema/cid.d.ts +7 -7
- package/dist/schema/cid.d.ts.map +1 -1
- package/dist/schema/cid.js +5 -1
- package/dist/schema/cid.js.map +1 -1
- package/dist/schema/custom.d.ts +6 -5
- package/dist/schema/custom.d.ts.map +1 -1
- package/dist/schema/custom.js +10 -4
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/dict.d.ts +8 -8
- package/dist/schema/dict.d.ts.map +1 -1
- package/dist/schema/dict.js +11 -2
- package/dist/schema/dict.js.map +1 -1
- package/dist/schema/discriminated-union.d.ts +21 -14
- package/dist/schema/discriminated-union.d.ts.map +1 -1
- package/dist/schema/discriminated-union.js +7 -0
- package/dist/schema/discriminated-union.js.map +1 -1
- package/dist/schema/enum.d.ts +7 -9
- package/dist/schema/enum.d.ts.map +1 -1
- package/dist/schema/enum.js +8 -4
- package/dist/schema/enum.js.map +1 -1
- package/dist/schema/integer.d.ts +5 -5
- package/dist/schema/integer.d.ts.map +1 -1
- package/dist/schema/integer.js +9 -5
- package/dist/schema/integer.js.map +1 -1
- package/dist/schema/intersection.d.ts +4 -4
- package/dist/schema/intersection.d.ts.map +1 -1
- package/dist/schema/intersection.js +5 -0
- package/dist/schema/intersection.js.map +1 -1
- package/dist/schema/literal.d.ts +6 -9
- package/dist/schema/literal.d.ts.map +1 -1
- package/dist/schema/literal.js +7 -4
- package/dist/schema/literal.js.map +1 -1
- package/dist/schema/never.d.ts +3 -2
- package/dist/schema/never.d.ts.map +1 -1
- package/dist/schema/never.js +5 -1
- package/dist/schema/never.js.map +1 -1
- package/dist/schema/null.d.ts +4 -3
- package/dist/schema/null.d.ts.map +1 -1
- package/dist/schema/null.js +6 -4
- package/dist/schema/null.js.map +1 -1
- package/dist/schema/nullable.d.ts +6 -5
- package/dist/schema/nullable.d.ts.map +1 -1
- package/dist/schema/nullable.js +9 -5
- package/dist/schema/nullable.js.map +1 -1
- package/dist/schema/object.d.ts +10 -8
- package/dist/schema/object.d.ts.map +1 -1
- package/dist/schema/object.js +11 -3
- package/dist/schema/object.js.map +1 -1
- package/dist/schema/optional.d.ts +7 -5
- package/dist/schema/optional.d.ts.map +1 -1
- package/dist/schema/optional.js +14 -6
- package/dist/schema/optional.js.map +1 -1
- package/dist/schema/params.d.ts +24 -13
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +47 -25
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/payload.d.ts +12 -9
- package/dist/schema/payload.d.ts.map +1 -1
- package/dist/schema/payload.js +11 -0
- package/dist/schema/payload.js.map +1 -1
- package/dist/schema/permission-set.d.ts +1 -0
- package/dist/schema/permission-set.d.ts.map +1 -1
- package/dist/schema/permission-set.js +5 -0
- package/dist/schema/permission-set.js.map +1 -1
- package/dist/schema/permission.d.ts +6 -5
- package/dist/schema/permission.d.ts.map +1 -1
- package/dist/schema/permission.js +5 -0
- package/dist/schema/permission.js.map +1 -1
- package/dist/schema/procedure.d.ts +2 -1
- package/dist/schema/procedure.d.ts.map +1 -1
- package/dist/schema/procedure.js +5 -0
- package/dist/schema/procedure.js.map +1 -1
- package/dist/schema/query.d.ts +2 -1
- package/dist/schema/query.d.ts.map +1 -1
- package/dist/schema/query.js +5 -0
- package/dist/schema/query.js.map +1 -1
- package/dist/schema/record.d.ts +48 -30
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +12 -9
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/ref.d.ts +9 -6
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js +9 -16
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/refine.d.ts +4 -4
- package/dist/schema/refine.d.ts.map +1 -1
- package/dist/schema/refine.js.map +1 -1
- package/dist/schema/regexp.d.ts +4 -3
- package/dist/schema/regexp.d.ts.map +1 -1
- package/dist/schema/regexp.js +5 -0
- package/dist/schema/regexp.js.map +1 -1
- package/dist/schema/string.d.ts +7 -8
- package/dist/schema/string.d.ts.map +1 -1
- package/dist/schema/string.js +13 -19
- package/dist/schema/string.js.map +1 -1
- package/dist/schema/subscription.d.ts +2 -1
- package/dist/schema/subscription.d.ts.map +1 -1
- package/dist/schema/subscription.js +5 -0
- package/dist/schema/subscription.js.map +1 -1
- package/dist/schema/token.d.ts +6 -5
- package/dist/schema/token.d.ts.map +1 -1
- package/dist/schema/token.js +5 -0
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-object.d.ts +43 -26
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +6 -3
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/typed-ref.d.ts +16 -25
- package/dist/schema/typed-ref.d.ts.map +1 -1
- package/dist/schema/typed-ref.js +7 -17
- package/dist/schema/typed-ref.js.map +1 -1
- package/dist/schema/typed-union.d.ts +9 -21
- package/dist/schema/typed-union.d.ts.map +1 -1
- package/dist/schema/typed-union.js +15 -11
- package/dist/schema/typed-union.js.map +1 -1
- package/dist/schema/union.d.ts +6 -6
- package/dist/schema/union.d.ts.map +1 -1
- package/dist/schema/union.js +7 -5
- package/dist/schema/union.js.map +1 -1
- package/dist/schema/unknown-object.d.ts +5 -4
- package/dist/schema/unknown-object.d.ts.map +1 -1
- package/dist/schema/unknown-object.js +5 -1
- package/dist/schema/unknown-object.js.map +1 -1
- package/dist/schema/unknown.d.ts +3 -2
- package/dist/schema/unknown.d.ts.map +1 -1
- package/dist/schema/unknown.js +5 -1
- package/dist/schema/unknown.js.map +1 -1
- package/dist/schema/with-default.d.ts +9 -0
- package/dist/schema/with-default.d.ts.map +1 -0
- package/dist/schema/with-default.js +27 -0
- package/dist/schema/with-default.js.map +1 -0
- package/dist/schema.d.ts +2 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +2 -4
- package/dist/schema.js.map +1 -1
- package/dist/util/assertion-util.d.ts +0 -6
- package/dist/util/assertion-util.d.ts.map +1 -1
- package/dist/util/assertion-util.js +0 -28
- package/dist/util/assertion-util.js.map +1 -1
- package/dist/util/memoize.d.ts +2 -2
- package/dist/util/memoize.d.ts.map +1 -1
- package/dist/util/memoize.js +23 -39
- package/dist/util/memoize.js.map +1 -1
- package/package.json +3 -3
- package/src/core/$type.test.ts +20 -0
- package/src/core/$type.ts +30 -0
- package/src/core/schema.ts +86 -38
- package/src/core/string-format.ts +119 -158
- package/src/core/validation-issue.ts +1 -1
- package/src/core/validator.ts +93 -53
- package/src/external.ts +0 -404
- package/src/helpers.test.ts +22 -21
- package/src/helpers.ts +14 -14
- package/src/schema/array.test.ts +38 -40
- package/src/schema/array.ts +35 -13
- package/src/schema/blob.test.ts +21 -21
- package/src/schema/blob.ts +19 -17
- package/src/schema/boolean.test.ts +9 -8
- package/src/schema/boolean.ts +7 -13
- package/src/schema/bytes.test.ts +13 -13
- package/src/schema/bytes.ts +13 -8
- package/src/schema/cid.test.ts +3 -3
- package/src/schema/cid.ts +13 -12
- package/src/schema/custom.test.ts +26 -26
- package/src/schema/custom.ts +20 -13
- package/src/schema/dict.test.ts +21 -39
- package/src/schema/dict.ts +28 -19
- package/src/schema/discriminated-union.test.ts +128 -128
- package/src/schema/discriminated-union.ts +45 -26
- package/src/schema/enum.test.ts +17 -16
- package/src/schema/enum.ts +16 -16
- package/src/schema/integer.test.ts +22 -21
- package/src/schema/integer.ts +12 -9
- package/src/schema/intersection.test.ts +10 -10
- package/src/schema/intersection.ts +17 -14
- package/src/schema/literal.test.ts +35 -34
- package/src/schema/literal.ts +12 -15
- package/src/schema/never.test.ts +5 -5
- package/src/schema/never.ts +7 -2
- package/src/schema/null.test.ts +3 -3
- package/src/schema/null.ts +9 -9
- package/src/schema/nullable.test.ts +31 -42
- package/src/schema/nullable.ts +17 -9
- package/src/schema/object.test.ts +10 -12
- package/src/schema/object.ts +27 -18
- package/src/schema/optional.test.ts +21 -28
- package/src/schema/optional.ts +27 -10
- package/src/schema/params.test.ts +471 -47
- package/src/schema/params.ts +72 -38
- package/src/schema/payload.test.ts +150 -156
- package/src/schema/payload.ts +35 -19
- package/src/schema/permission-set.test.ts +206 -273
- package/src/schema/permission-set.ts +8 -0
- package/src/schema/permission.test.ts +177 -177
- package/src/schema/permission.ts +13 -5
- package/src/schema/procedure.test.ts +183 -242
- package/src/schema/procedure.ts +18 -5
- package/src/schema/query.test.ts +186 -200
- package/src/schema/query.ts +16 -4
- package/src/schema/record.test.ts +121 -101
- package/src/schema/record.ts +74 -40
- package/src/schema/ref.test.ts +101 -118
- package/src/schema/ref.ts +33 -28
- package/src/schema/refine.test.ts +28 -28
- package/src/schema/refine.ts +23 -20
- package/src/schema/regexp.test.ts +29 -33
- package/src/schema/regexp.ts +11 -7
- package/src/schema/string.test.ts +35 -35
- package/src/schema/string.ts +24 -33
- package/src/schema/subscription.test.ts +259 -387
- package/src/schema/subscription.ts +16 -4
- package/src/schema/token.test.ts +47 -324
- package/src/schema/token.ts +14 -7
- package/src/schema/typed-object.test.ts +98 -81
- package/src/schema/typed-object.ts +68 -33
- package/src/schema/typed-ref.test.ts +206 -234
- package/src/schema/typed-ref.ts +40 -42
- package/src/schema/typed-union.test.ts +40 -64
- package/src/schema/typed-union.ts +36 -58
- package/src/schema/union.test.ts +17 -27
- package/src/schema/union.ts +20 -16
- package/src/schema/unknown-object.test.ts +8 -8
- package/src/schema/unknown-object.ts +9 -7
- package/src/schema/unknown.test.ts +4 -4
- package/src/schema/unknown.ts +7 -5
- package/src/schema/with-default.ts +35 -0
- package/src/schema.ts +2 -6
- package/src/util/assertion-util.ts +0 -39
- package/src/util/memoize.ts +26 -46
- package/dist/schema/_parameters.d.ts +0 -17
- package/dist/schema/_parameters.d.ts.map +0 -1
- package/dist/schema/_parameters.js +0 -20
- package/dist/schema/_parameters.js.map +0 -1
- package/src/schema/_parameters.test.ts +0 -417
- package/src/schema/_parameters.ts +0 -26
package/src/schema/enum.ts
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { Schema,
|
|
2
|
-
|
|
3
|
-
export type EnumSchemaOptions<T extends null | string | number | boolean> = {
|
|
4
|
-
default?: T
|
|
5
|
-
}
|
|
1
|
+
import { Schema, ValidationContext } from '../core.js'
|
|
6
2
|
|
|
7
3
|
export class EnumSchema<
|
|
8
|
-
|
|
9
|
-
> extends Schema<
|
|
10
|
-
constructor(
|
|
11
|
-
readonly values: readonly Output[],
|
|
12
|
-
readonly options?: EnumSchemaOptions<Output>,
|
|
13
|
-
) {
|
|
4
|
+
const TValue extends null | string | number | boolean,
|
|
5
|
+
> extends Schema<TValue> {
|
|
6
|
+
constructor(readonly values: readonly TValue[]) {
|
|
14
7
|
super()
|
|
15
8
|
}
|
|
16
9
|
|
|
17
|
-
validateInContext(
|
|
18
|
-
input: unknown = this.options?.default,
|
|
19
|
-
ctx: ValidatorContext,
|
|
20
|
-
): ValidationResult<Output> {
|
|
10
|
+
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
21
11
|
if (!(this.values as readonly unknown[]).includes(input)) {
|
|
22
12
|
return ctx.issueInvalidValue(input, this.values)
|
|
23
13
|
}
|
|
24
14
|
|
|
25
|
-
return ctx.success(input as
|
|
15
|
+
return ctx.success(input as TValue)
|
|
26
16
|
}
|
|
27
17
|
}
|
|
18
|
+
|
|
19
|
+
/*@__NO_SIDE_EFFECTS__*/
|
|
20
|
+
export function enumSchema<const V extends null | string | number | boolean>(
|
|
21
|
+
value: readonly V[],
|
|
22
|
+
) {
|
|
23
|
+
return new EnumSchema<V>(value)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// @NOTE "enum" is a reserved keyword in JS/TS
|
|
27
|
+
export { enumSchema as enum }
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
2
|
+
import { integer } from './integer.js'
|
|
3
|
+
import { withDefault } from './with-default.js'
|
|
3
4
|
|
|
4
5
|
describe('IntegerSchema', () => {
|
|
5
6
|
describe('basic validation', () => {
|
|
6
|
-
const schema =
|
|
7
|
+
const schema = integer()
|
|
7
8
|
|
|
8
9
|
it('validates integers', () => {
|
|
9
10
|
const result = schema.safeParse(42)
|
|
@@ -82,22 +83,22 @@ describe('IntegerSchema', () => {
|
|
|
82
83
|
})
|
|
83
84
|
|
|
84
85
|
describe('default value', () => {
|
|
85
|
-
const schema =
|
|
86
|
+
const schema = withDefault(integer(), 10)
|
|
86
87
|
|
|
87
88
|
it('uses default when undefined is provided', () => {
|
|
88
89
|
const result = schema.safeParse(undefined)
|
|
89
|
-
expect(result
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
90
|
+
expect(result).toMatchObject({
|
|
91
|
+
success: true,
|
|
92
|
+
value: 10,
|
|
93
|
+
})
|
|
93
94
|
})
|
|
94
95
|
|
|
95
96
|
it('does not use default when explicit value is provided', () => {
|
|
96
97
|
const result = schema.safeParse(20)
|
|
97
|
-
expect(result
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
98
|
+
expect(result).toMatchObject({
|
|
99
|
+
success: true,
|
|
100
|
+
value: 20,
|
|
101
|
+
})
|
|
101
102
|
})
|
|
102
103
|
|
|
103
104
|
it('does not use default when zero is provided', () => {
|
|
@@ -110,7 +111,7 @@ describe('IntegerSchema', () => {
|
|
|
110
111
|
})
|
|
111
112
|
|
|
112
113
|
describe('minimum constraint', () => {
|
|
113
|
-
const schema =
|
|
114
|
+
const schema = integer({ minimum: 10 })
|
|
114
115
|
|
|
115
116
|
it('accepts values equal to minimum', () => {
|
|
116
117
|
const result = schema.safeParse(10)
|
|
@@ -139,7 +140,7 @@ describe('IntegerSchema', () => {
|
|
|
139
140
|
})
|
|
140
141
|
|
|
141
142
|
describe('maximum constraint', () => {
|
|
142
|
-
const schema =
|
|
143
|
+
const schema = integer({ maximum: 100 })
|
|
143
144
|
|
|
144
145
|
it('accepts values equal to maximum', () => {
|
|
145
146
|
const result = schema.safeParse(100)
|
|
@@ -168,7 +169,7 @@ describe('IntegerSchema', () => {
|
|
|
168
169
|
})
|
|
169
170
|
|
|
170
171
|
describe('minimum and maximum constraints', () => {
|
|
171
|
-
const schema =
|
|
172
|
+
const schema = integer({ minimum: 10, maximum: 100 })
|
|
172
173
|
|
|
173
174
|
it('accepts values within range', () => {
|
|
174
175
|
const result = schema.safeParse(50)
|
|
@@ -197,7 +198,7 @@ describe('IntegerSchema', () => {
|
|
|
197
198
|
})
|
|
198
199
|
|
|
199
200
|
describe('negative range constraints', () => {
|
|
200
|
-
const schema =
|
|
201
|
+
const schema = integer({ minimum: -100, maximum: -10 })
|
|
201
202
|
|
|
202
203
|
it('accepts negative values within range', () => {
|
|
203
204
|
const result = schema.safeParse(-50)
|
|
@@ -236,7 +237,7 @@ describe('IntegerSchema', () => {
|
|
|
236
237
|
})
|
|
237
238
|
|
|
238
239
|
describe('zero constraints', () => {
|
|
239
|
-
const schema =
|
|
240
|
+
const schema = integer({ minimum: 0, maximum: 0 })
|
|
240
241
|
|
|
241
242
|
it('accepts zero', () => {
|
|
242
243
|
const result = schema.safeParse(0)
|
|
@@ -255,7 +256,7 @@ describe('IntegerSchema', () => {
|
|
|
255
256
|
})
|
|
256
257
|
|
|
257
258
|
describe('combined with default value', () => {
|
|
258
|
-
const schema =
|
|
259
|
+
const schema = withDefault(integer({ minimum: 10, maximum: 100 }), 50)
|
|
259
260
|
|
|
260
261
|
it('uses default when undefined is provided', () => {
|
|
261
262
|
const result = schema.safeParse(undefined)
|
|
@@ -278,21 +279,21 @@ describe('IntegerSchema', () => {
|
|
|
278
279
|
|
|
279
280
|
describe('edge cases', () => {
|
|
280
281
|
it('handles minimum of 0', () => {
|
|
281
|
-
const schema =
|
|
282
|
+
const schema = integer({ minimum: 0 })
|
|
282
283
|
expect(schema.safeParse(0).success).toBe(true)
|
|
283
284
|
expect(schema.safeParse(-1).success).toBe(false)
|
|
284
285
|
expect(schema.safeParse(1).success).toBe(true)
|
|
285
286
|
})
|
|
286
287
|
|
|
287
288
|
it('handles maximum of 0', () => {
|
|
288
|
-
const schema =
|
|
289
|
+
const schema = integer({ maximum: 0 })
|
|
289
290
|
expect(schema.safeParse(0).success).toBe(true)
|
|
290
291
|
expect(schema.safeParse(1).success).toBe(false)
|
|
291
292
|
expect(schema.safeParse(-1).success).toBe(true)
|
|
292
293
|
})
|
|
293
294
|
|
|
294
295
|
it('handles very large ranges', () => {
|
|
295
|
-
const schema =
|
|
296
|
+
const schema = integer({
|
|
296
297
|
minimum: Number.MIN_SAFE_INTEGER,
|
|
297
298
|
maximum: Number.MAX_SAFE_INTEGER,
|
|
298
299
|
})
|
|
@@ -302,7 +303,7 @@ describe('IntegerSchema', () => {
|
|
|
302
303
|
})
|
|
303
304
|
|
|
304
305
|
it('allows unconstrained schema', () => {
|
|
305
|
-
const schema =
|
|
306
|
+
const schema = integer()
|
|
306
307
|
expect(schema.safeParse(Number.MIN_SAFE_INTEGER).success).toBe(true)
|
|
307
308
|
expect(schema.safeParse(Number.MAX_SAFE_INTEGER).success).toBe(true)
|
|
308
309
|
expect(schema.safeParse(0).success).toBe(true)
|
package/src/schema/integer.ts
CHANGED
|
@@ -1,29 +1,26 @@
|
|
|
1
|
-
import { Schema,
|
|
1
|
+
import { Schema, ValidationContext } from '../core.js'
|
|
2
|
+
import { memoizedOptions } from '../util/memoize.js'
|
|
2
3
|
|
|
3
4
|
export type IntegerSchemaOptions = {
|
|
4
|
-
default?: number
|
|
5
5
|
minimum?: number
|
|
6
6
|
maximum?: number
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export class IntegerSchema extends Schema<number> {
|
|
10
|
-
constructor(readonly options
|
|
10
|
+
constructor(readonly options?: IntegerSchemaOptions) {
|
|
11
11
|
super()
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
validateInContext(
|
|
15
|
-
input: unknown = this.options?.default,
|
|
16
|
-
ctx: ValidatorContext,
|
|
17
|
-
): ValidationResult<number> {
|
|
14
|
+
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
18
15
|
if (!isInteger(input)) {
|
|
19
16
|
return ctx.issueInvalidType(input, 'integer')
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
if (this.options
|
|
19
|
+
if (this.options?.minimum != null && input < this.options.minimum) {
|
|
23
20
|
return ctx.issueTooSmall(input, 'integer', this.options.minimum, input)
|
|
24
21
|
}
|
|
25
22
|
|
|
26
|
-
if (this.options
|
|
23
|
+
if (this.options?.maximum != null && input > this.options.maximum) {
|
|
27
24
|
return ctx.issueTooBig(input, 'integer', this.options.maximum, input)
|
|
28
25
|
}
|
|
29
26
|
|
|
@@ -37,3 +34,9 @@ export class IntegerSchema extends Schema<number> {
|
|
|
37
34
|
function isInteger(input: unknown): input is number {
|
|
38
35
|
return Number.isSafeInteger(input)
|
|
39
36
|
}
|
|
37
|
+
|
|
38
|
+
export const integer = /*#__PURE__*/ memoizedOptions(function (
|
|
39
|
+
options?: IntegerSchemaOptions,
|
|
40
|
+
) {
|
|
41
|
+
return new IntegerSchema(options)
|
|
42
|
+
})
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
2
|
+
import { boolean } from './boolean.js'
|
|
3
|
+
import { dict } from './dict.js'
|
|
4
|
+
import { enumSchema } from './enum.js'
|
|
5
|
+
import { intersection } from './intersection.js'
|
|
6
|
+
import { object } from './object.js'
|
|
7
|
+
import { string } from './string.js'
|
|
8
8
|
|
|
9
9
|
describe('IntersectionSchema', () => {
|
|
10
|
-
const schema =
|
|
11
|
-
|
|
12
|
-
title:
|
|
10
|
+
const schema = intersection(
|
|
11
|
+
object({
|
|
12
|
+
title: string(),
|
|
13
13
|
}),
|
|
14
|
-
|
|
14
|
+
dict(enumSchema(['tag1', 'tag2']), boolean()),
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
it('validates extra properties with the provided validator', () => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
InferInput,
|
|
3
|
+
InferOutput,
|
|
3
4
|
Schema,
|
|
4
5
|
Simplify,
|
|
5
|
-
|
|
6
|
-
ValidatorContext,
|
|
6
|
+
ValidationContext,
|
|
7
7
|
} from '../core.js'
|
|
8
8
|
import { DictSchema } from './dict.js'
|
|
9
9
|
import { ObjectSchema } from './object.js'
|
|
@@ -24,15 +24,13 @@ export type Intersect<A, B> = B[keyof B] extends never
|
|
|
24
24
|
// index signature could return a value from either A or B
|
|
25
25
|
A & { [K in keyof B]: B[K] | A[keyof A & K] }
|
|
26
26
|
|
|
27
|
-
export type IntersectionSchemaOutput<
|
|
28
|
-
Left extends ObjectSchema,
|
|
29
|
-
Right extends DictSchema,
|
|
30
|
-
> = Simplify<Intersect<Infer<Left>, Infer<Right>>>
|
|
31
|
-
|
|
32
27
|
export class IntersectionSchema<
|
|
33
28
|
const Left extends ObjectSchema = any,
|
|
34
29
|
const Right extends DictSchema = any,
|
|
35
|
-
> extends Schema<
|
|
30
|
+
> extends Schema<
|
|
31
|
+
Simplify<Intersect<InferInput<Left>, InferInput<Right>>>,
|
|
32
|
+
Simplify<Intersect<InferOutput<Left>, InferOutput<Right>>>
|
|
33
|
+
> {
|
|
36
34
|
constructor(
|
|
37
35
|
protected readonly left: Left,
|
|
38
36
|
protected readonly right: Right,
|
|
@@ -40,15 +38,20 @@ export class IntersectionSchema<
|
|
|
40
38
|
super()
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
validateInContext(
|
|
44
|
-
input: unknown,
|
|
45
|
-
ctx: ValidatorContext,
|
|
46
|
-
): ValidationResult<IntersectionSchemaOutput<Left, Right>> {
|
|
41
|
+
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
47
42
|
const leftResult = ctx.validate(input, this.left)
|
|
48
43
|
if (!leftResult.success) return leftResult
|
|
49
44
|
|
|
50
45
|
return this.right.validateInContext(leftResult.value, ctx, {
|
|
51
46
|
ignoredKeys: this.left.validatorsMap,
|
|
52
|
-
})
|
|
47
|
+
})
|
|
53
48
|
}
|
|
54
49
|
}
|
|
50
|
+
|
|
51
|
+
/*@__NO_SIDE_EFFECTS__*/
|
|
52
|
+
export function intersection<
|
|
53
|
+
const Left extends ObjectSchema,
|
|
54
|
+
const Right extends DictSchema,
|
|
55
|
+
>(left: Left, right: Right) {
|
|
56
|
+
return new IntersectionSchema<Left, Right>(left, right)
|
|
57
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
2
|
+
import { literal } from './literal.js'
|
|
3
|
+
import { withDefault } from './with-default.js'
|
|
3
4
|
|
|
4
5
|
describe('LiteralSchema', () => {
|
|
5
6
|
describe('string literals', () => {
|
|
6
|
-
const schema =
|
|
7
|
+
const schema = literal('hello')
|
|
7
8
|
|
|
8
9
|
it('validates exact string match', () => {
|
|
9
10
|
const result = schema.safeParse('hello')
|
|
@@ -60,7 +61,7 @@ describe('LiteralSchema', () => {
|
|
|
60
61
|
})
|
|
61
62
|
|
|
62
63
|
describe('empty string literal', () => {
|
|
63
|
-
const schema =
|
|
64
|
+
const schema = literal('')
|
|
64
65
|
|
|
65
66
|
it('validates empty string', () => {
|
|
66
67
|
const result = schema.safeParse('')
|
|
@@ -87,7 +88,7 @@ describe('LiteralSchema', () => {
|
|
|
87
88
|
})
|
|
88
89
|
|
|
89
90
|
describe('number literals', () => {
|
|
90
|
-
const schema =
|
|
91
|
+
const schema = literal(42)
|
|
91
92
|
|
|
92
93
|
it('validates exact number match', () => {
|
|
93
94
|
const result = schema.safeParse(42)
|
|
@@ -124,7 +125,7 @@ describe('LiteralSchema', () => {
|
|
|
124
125
|
})
|
|
125
126
|
|
|
126
127
|
describe('zero literal', () => {
|
|
127
|
-
const schema =
|
|
128
|
+
const schema = literal(0)
|
|
128
129
|
|
|
129
130
|
it('validates zero', () => {
|
|
130
131
|
const result = schema.safeParse(0)
|
|
@@ -156,7 +157,7 @@ describe('LiteralSchema', () => {
|
|
|
156
157
|
})
|
|
157
158
|
|
|
158
159
|
describe('negative number literals', () => {
|
|
159
|
-
const schema =
|
|
160
|
+
const schema = literal(-42)
|
|
160
161
|
|
|
161
162
|
it('validates exact negative number match', () => {
|
|
162
163
|
const result = schema.safeParse(-42)
|
|
@@ -178,7 +179,7 @@ describe('LiteralSchema', () => {
|
|
|
178
179
|
})
|
|
179
180
|
|
|
180
181
|
describe('decimal number literals', () => {
|
|
181
|
-
const schema =
|
|
182
|
+
const schema = literal(3.14)
|
|
182
183
|
|
|
183
184
|
it('validates exact decimal match', () => {
|
|
184
185
|
const result = schema.safeParse(3.14)
|
|
@@ -201,7 +202,7 @@ describe('LiteralSchema', () => {
|
|
|
201
202
|
|
|
202
203
|
describe('boolean literals', () => {
|
|
203
204
|
describe('true literal', () => {
|
|
204
|
-
const schema =
|
|
205
|
+
const schema = literal(true)
|
|
205
206
|
|
|
206
207
|
it('validates true', () => {
|
|
207
208
|
const result = schema.safeParse(true)
|
|
@@ -238,7 +239,7 @@ describe('LiteralSchema', () => {
|
|
|
238
239
|
})
|
|
239
240
|
|
|
240
241
|
describe('false literal', () => {
|
|
241
|
-
const schema =
|
|
242
|
+
const schema = literal(false)
|
|
242
243
|
|
|
243
244
|
it('validates false', () => {
|
|
244
245
|
const result = schema.safeParse(false)
|
|
@@ -271,7 +272,7 @@ describe('LiteralSchema', () => {
|
|
|
271
272
|
})
|
|
272
273
|
|
|
273
274
|
describe('null literal', () => {
|
|
274
|
-
const schema =
|
|
275
|
+
const schema = literal(null)
|
|
275
276
|
|
|
276
277
|
it('validates null', () => {
|
|
277
278
|
const result = schema.safeParse(null)
|
|
@@ -309,7 +310,7 @@ describe('LiteralSchema', () => {
|
|
|
309
310
|
|
|
310
311
|
describe('default values', () => {
|
|
311
312
|
describe('string literal with default', () => {
|
|
312
|
-
const schema =
|
|
313
|
+
const schema = withDefault(literal('hello'), 'hello')
|
|
313
314
|
|
|
314
315
|
it('uses default value when undefined is provided', () => {
|
|
315
316
|
const result = schema.safeParse(undefined)
|
|
@@ -334,7 +335,7 @@ describe('LiteralSchema', () => {
|
|
|
334
335
|
})
|
|
335
336
|
|
|
336
337
|
describe('number literal with default', () => {
|
|
337
|
-
const schema =
|
|
338
|
+
const schema = withDefault(literal(42), 42)
|
|
338
339
|
|
|
339
340
|
it('uses default value when undefined is provided', () => {
|
|
340
341
|
const result = schema.safeParse(undefined)
|
|
@@ -351,7 +352,7 @@ describe('LiteralSchema', () => {
|
|
|
351
352
|
})
|
|
352
353
|
|
|
353
354
|
describe('boolean literal with default', () => {
|
|
354
|
-
const schema =
|
|
355
|
+
const schema = withDefault(literal(true), true)
|
|
355
356
|
|
|
356
357
|
it('uses default value when undefined is provided', () => {
|
|
357
358
|
const result = schema.safeParse(undefined)
|
|
@@ -368,7 +369,7 @@ describe('LiteralSchema', () => {
|
|
|
368
369
|
})
|
|
369
370
|
|
|
370
371
|
describe('null literal with default', () => {
|
|
371
|
-
const schema =
|
|
372
|
+
const schema = withDefault(literal(null), null)
|
|
372
373
|
|
|
373
374
|
it('uses default value when undefined is provided', () => {
|
|
374
375
|
const result = schema.safeParse(undefined)
|
|
@@ -385,7 +386,7 @@ describe('LiteralSchema', () => {
|
|
|
385
386
|
})
|
|
386
387
|
|
|
387
388
|
describe('false literal with default', () => {
|
|
388
|
-
const schema =
|
|
389
|
+
const schema = withDefault(literal(false), false)
|
|
389
390
|
|
|
390
391
|
it('uses default value when undefined is provided', () => {
|
|
391
392
|
const result = schema.safeParse(undefined)
|
|
@@ -402,7 +403,7 @@ describe('LiteralSchema', () => {
|
|
|
402
403
|
})
|
|
403
404
|
|
|
404
405
|
describe('zero literal with default', () => {
|
|
405
|
-
const schema =
|
|
406
|
+
const schema = withDefault(literal(0), 0)
|
|
406
407
|
|
|
407
408
|
it('uses default value when undefined is provided', () => {
|
|
408
409
|
const result = schema.safeParse(undefined)
|
|
@@ -421,82 +422,82 @@ describe('LiteralSchema', () => {
|
|
|
421
422
|
|
|
422
423
|
describe('edge cases', () => {
|
|
423
424
|
it('handles special string characters', () => {
|
|
424
|
-
const schema =
|
|
425
|
+
const schema = literal('hello\nworld')
|
|
425
426
|
expect(schema.safeParse('hello\nworld').success).toBe(true)
|
|
426
427
|
expect(schema.safeParse('hello world').success).toBe(false)
|
|
427
428
|
})
|
|
428
429
|
|
|
429
430
|
it('handles unicode characters in strings', () => {
|
|
430
|
-
const schema =
|
|
431
|
+
const schema = literal('Hello 世界 🌍')
|
|
431
432
|
expect(schema.safeParse('Hello 世界 🌍').success).toBe(true)
|
|
432
433
|
expect(schema.safeParse('Hello world').success).toBe(false)
|
|
433
434
|
})
|
|
434
435
|
|
|
435
436
|
it('handles emoji literals', () => {
|
|
436
|
-
const schema =
|
|
437
|
+
const schema = literal('🚀')
|
|
437
438
|
expect(schema.safeParse('🚀').success).toBe(true)
|
|
438
439
|
expect(schema.safeParse('🌟').success).toBe(false)
|
|
439
440
|
})
|
|
440
441
|
|
|
441
442
|
it('handles very long string literals', () => {
|
|
442
443
|
const longString = 'a'.repeat(1000)
|
|
443
|
-
const schema =
|
|
444
|
+
const schema = literal(longString)
|
|
444
445
|
expect(schema.safeParse(longString).success).toBe(true)
|
|
445
446
|
expect(schema.safeParse(longString + 'b').success).toBe(false)
|
|
446
447
|
})
|
|
447
448
|
|
|
448
449
|
it('handles string with whitespace', () => {
|
|
449
|
-
const schema =
|
|
450
|
+
const schema = literal(' hello ')
|
|
450
451
|
expect(schema.safeParse(' hello ').success).toBe(true)
|
|
451
452
|
expect(schema.safeParse('hello').success).toBe(false)
|
|
452
453
|
expect(schema.safeParse(' hello').success).toBe(false)
|
|
453
454
|
})
|
|
454
455
|
|
|
455
456
|
it('handles Number.MAX_SAFE_INTEGER', () => {
|
|
456
|
-
const schema =
|
|
457
|
+
const schema = literal(Number.MAX_SAFE_INTEGER)
|
|
457
458
|
expect(schema.safeParse(Number.MAX_SAFE_INTEGER).success).toBe(true)
|
|
458
459
|
expect(schema.safeParse(Number.MAX_SAFE_INTEGER - 1).success).toBe(false)
|
|
459
460
|
})
|
|
460
461
|
|
|
461
462
|
it('handles Number.MIN_SAFE_INTEGER', () => {
|
|
462
|
-
const schema =
|
|
463
|
+
const schema = literal(Number.MIN_SAFE_INTEGER)
|
|
463
464
|
expect(schema.safeParse(Number.MIN_SAFE_INTEGER).success).toBe(true)
|
|
464
465
|
expect(schema.safeParse(Number.MIN_SAFE_INTEGER + 1).success).toBe(false)
|
|
465
466
|
})
|
|
466
467
|
|
|
467
468
|
it('rejects NaN', () => {
|
|
468
|
-
const schema =
|
|
469
|
+
const schema = literal(42)
|
|
469
470
|
expect(schema.safeParse(NaN).success).toBe(false)
|
|
470
471
|
})
|
|
471
472
|
|
|
472
473
|
it('rejects Infinity', () => {
|
|
473
|
-
const schema =
|
|
474
|
+
const schema = literal(42)
|
|
474
475
|
expect(schema.safeParse(Infinity).success).toBe(false)
|
|
475
476
|
})
|
|
476
477
|
|
|
477
478
|
it('rejects -Infinity', () => {
|
|
478
|
-
const schema =
|
|
479
|
+
const schema = literal(42)
|
|
479
480
|
expect(schema.safeParse(-Infinity).success).toBe(false)
|
|
480
481
|
})
|
|
481
482
|
|
|
482
483
|
it('rejects Boolean objects', () => {
|
|
483
|
-
const schema =
|
|
484
|
+
const schema = literal(true)
|
|
484
485
|
expect(schema.safeParse(new Boolean(true)).success).toBe(false)
|
|
485
486
|
})
|
|
486
487
|
|
|
487
488
|
it('rejects String objects', () => {
|
|
488
|
-
const schema =
|
|
489
|
+
const schema = literal('hello')
|
|
489
490
|
expect(schema.safeParse(new String('hello')).success).toBe(false)
|
|
490
491
|
})
|
|
491
492
|
|
|
492
493
|
it('rejects Number objects', () => {
|
|
493
|
-
const schema =
|
|
494
|
+
const schema = literal(42)
|
|
494
495
|
expect(schema.safeParse(new Number(42)).success).toBe(false)
|
|
495
496
|
})
|
|
496
497
|
|
|
497
498
|
it('distinguishes between -0 and +0', () => {
|
|
498
|
-
const schemaPositive =
|
|
499
|
-
const schemaNegative =
|
|
499
|
+
const schemaPositive = literal(0)
|
|
500
|
+
const schemaNegative = literal(-0)
|
|
500
501
|
// In JavaScript, 0 === -0, so both should validate for both schemas
|
|
501
502
|
expect(schemaPositive.safeParse(0).success).toBe(true)
|
|
502
503
|
expect(schemaPositive.safeParse(-0).success).toBe(true)
|
|
@@ -505,7 +506,7 @@ describe('LiteralSchema', () => {
|
|
|
505
506
|
})
|
|
506
507
|
|
|
507
508
|
it('handles very small decimal differences', () => {
|
|
508
|
-
const schema =
|
|
509
|
+
const schema = literal(0.1 + 0.2)
|
|
509
510
|
// Note: 0.1 + 0.2 !== 0.3 in JavaScript due to floating point precision
|
|
510
511
|
expect(schema.safeParse(0.1 + 0.2).success).toBe(true)
|
|
511
512
|
expect(schema.safeParse(0.3).success).toBe(false)
|
|
@@ -514,13 +515,13 @@ describe('LiteralSchema', () => {
|
|
|
514
515
|
|
|
515
516
|
describe('type safety', () => {
|
|
516
517
|
it('accepts exact literal types in TypeScript', () => {
|
|
517
|
-
const schema =
|
|
518
|
+
const schema = literal('specific' as const)
|
|
518
519
|
const result = schema.safeParse('specific')
|
|
519
520
|
expect(result.success).toBe(true)
|
|
520
521
|
})
|
|
521
522
|
|
|
522
523
|
it('preserves literal type in success result', () => {
|
|
523
|
-
const schema =
|
|
524
|
+
const schema = literal(42)
|
|
524
525
|
const result = schema.safeParse(42)
|
|
525
526
|
expect(result.success).toBe(true)
|
|
526
527
|
if (result.success) {
|
package/src/schema/literal.ts
CHANGED
|
@@ -1,23 +1,13 @@
|
|
|
1
|
-
import { Schema,
|
|
2
|
-
|
|
3
|
-
export type LiteralSchemaOptions<T extends null | string | number | boolean> = {
|
|
4
|
-
default?: T
|
|
5
|
-
}
|
|
1
|
+
import { Schema, ValidationContext } from '../core.js'
|
|
6
2
|
|
|
7
3
|
export class LiteralSchema<
|
|
8
|
-
|
|
9
|
-
> extends Schema<
|
|
10
|
-
constructor(
|
|
11
|
-
readonly value: Output,
|
|
12
|
-
readonly options?: LiteralSchemaOptions<Output>,
|
|
13
|
-
) {
|
|
4
|
+
const TValue extends null | string | number | boolean,
|
|
5
|
+
> extends Schema<TValue> {
|
|
6
|
+
constructor(readonly value: TValue) {
|
|
14
7
|
super()
|
|
15
8
|
}
|
|
16
9
|
|
|
17
|
-
validateInContext(
|
|
18
|
-
input: unknown = this.options?.default,
|
|
19
|
-
ctx: ValidatorContext,
|
|
20
|
-
): ValidationResult<Output> {
|
|
10
|
+
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
21
11
|
if (input !== this.value) {
|
|
22
12
|
return ctx.issueInvalidValue(input, [this.value])
|
|
23
13
|
}
|
|
@@ -25,3 +15,10 @@ export class LiteralSchema<
|
|
|
25
15
|
return ctx.success(this.value)
|
|
26
16
|
}
|
|
27
17
|
}
|
|
18
|
+
|
|
19
|
+
/*@__NO_SIDE_EFFECTS__*/
|
|
20
|
+
export function literal<const V extends null | string | number | boolean>(
|
|
21
|
+
value: V,
|
|
22
|
+
) {
|
|
23
|
+
return new LiteralSchema<V>(value)
|
|
24
|
+
}
|
package/src/schema/never.test.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
2
|
+
import { never } from './never.js'
|
|
3
3
|
|
|
4
4
|
describe('NeverSchema', () => {
|
|
5
5
|
describe('basic validation', () => {
|
|
6
|
-
const schema =
|
|
6
|
+
const schema = never()
|
|
7
7
|
|
|
8
8
|
it('rejects strings', () => {
|
|
9
9
|
const result = schema.safeParse('string')
|
|
@@ -57,7 +57,7 @@ describe('NeverSchema', () => {
|
|
|
57
57
|
})
|
|
58
58
|
|
|
59
59
|
describe('edge cases', () => {
|
|
60
|
-
const schema =
|
|
60
|
+
const schema = never()
|
|
61
61
|
|
|
62
62
|
it('rejects BigInt', () => {
|
|
63
63
|
const result = schema.safeParse(BigInt(123))
|
|
@@ -114,7 +114,7 @@ describe('NeverSchema', () => {
|
|
|
114
114
|
})
|
|
115
115
|
|
|
116
116
|
describe('complex data types', () => {
|
|
117
|
-
const schema =
|
|
117
|
+
const schema = never()
|
|
118
118
|
|
|
119
119
|
it('rejects class instances', () => {
|
|
120
120
|
class TestClass {
|
|
@@ -150,7 +150,7 @@ describe('NeverSchema', () => {
|
|
|
150
150
|
})
|
|
151
151
|
|
|
152
152
|
describe('special number values', () => {
|
|
153
|
-
const schema =
|
|
153
|
+
const schema = never()
|
|
154
154
|
|
|
155
155
|
it('rejects NaN', () => {
|
|
156
156
|
const result = schema.safeParse(NaN)
|
package/src/schema/never.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { Schema,
|
|
1
|
+
import { Schema, ValidationContext } from '../core.js'
|
|
2
|
+
import { memoizedOptions } from '../util/memoize.js'
|
|
2
3
|
|
|
3
4
|
export class NeverSchema extends Schema<never> {
|
|
4
|
-
validateInContext(input: unknown, ctx:
|
|
5
|
+
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
5
6
|
return ctx.issueInvalidType(input, 'never')
|
|
6
7
|
}
|
|
7
8
|
}
|
|
9
|
+
|
|
10
|
+
export const never = /*#__PURE__*/ memoizedOptions(function () {
|
|
11
|
+
return new NeverSchema()
|
|
12
|
+
})
|