@atproto/lex-schema 0.0.2 → 0.0.4
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 +75 -0
- package/dist/core/$type.d.ts +6 -3
- package/dist/core/$type.d.ts.map +1 -1
- package/dist/core/$type.js +1 -0
- package/dist/core/$type.js.map +1 -1
- package/dist/core/record-key.d.ts +3 -3
- package/dist/core/record-key.d.ts.map +1 -1
- package/dist/core/record-key.js +12 -6
- package/dist/core/record-key.js.map +1 -1
- package/dist/core/result.d.ts.map +1 -1
- package/dist/core/result.js +6 -0
- package/dist/core/result.js.map +1 -1
- package/dist/core/string-format.d.ts +30 -27
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +56 -42
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/types.d.ts +9 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/external.d.ts +31 -28
- package/dist/external.d.ts.map +1 -1
- package/dist/external.js +33 -17
- package/dist/external.js.map +1 -1
- package/dist/schema/_parameters.d.ts +2 -2
- package/dist/schema/_parameters.d.ts.map +1 -1
- package/dist/schema/array.d.ts +5 -6
- package/dist/schema/array.d.ts.map +1 -1
- package/dist/schema/array.js +5 -6
- package/dist/schema/array.js.map +1 -1
- package/dist/schema/blob.d.ts +2 -3
- package/dist/schema/blob.d.ts.map +1 -1
- package/dist/schema/blob.js +1 -2
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/boolean.d.ts +4 -5
- package/dist/schema/boolean.d.ts.map +1 -1
- package/dist/schema/boolean.js +2 -3
- package/dist/schema/boolean.js.map +1 -1
- package/dist/schema/bytes.d.ts +3 -4
- package/dist/schema/bytes.d.ts.map +1 -1
- package/dist/schema/bytes.js +2 -3
- package/dist/schema/bytes.js.map +1 -1
- package/dist/schema/cid.d.ts +13 -6
- package/dist/schema/cid.d.ts.map +1 -1
- package/dist/schema/cid.js +2 -4
- package/dist/schema/cid.js.map +1 -1
- package/dist/schema/custom.d.ts +3 -4
- package/dist/schema/custom.d.ts.map +1 -1
- package/dist/schema/custom.js +4 -3
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/dict.d.ts +3 -3
- package/dist/schema/dict.d.ts.map +1 -1
- package/dist/schema/dict.js +1 -1
- package/dist/schema/dict.js.map +1 -1
- package/dist/schema/discriminated-union.d.ts +15 -24
- package/dist/schema/discriminated-union.d.ts.map +1 -1
- package/dist/schema/discriminated-union.js +40 -64
- package/dist/schema/discriminated-union.js.map +1 -1
- package/dist/schema/enum.d.ts +8 -4
- package/dist/schema/enum.d.ts.map +1 -1
- package/dist/schema/enum.js +5 -3
- package/dist/schema/enum.js.map +1 -1
- package/dist/schema/integer.d.ts +3 -4
- package/dist/schema/integer.d.ts.map +1 -1
- package/dist/schema/integer.js +3 -4
- package/dist/schema/integer.js.map +1 -1
- package/dist/schema/intersection.d.ts +22 -14
- package/dist/schema/intersection.d.ts.map +1 -1
- package/dist/schema/intersection.js +12 -22
- package/dist/schema/intersection.js.map +1 -1
- package/dist/schema/literal.d.ts +7 -3
- package/dist/schema/literal.d.ts.map +1 -1
- package/dist/schema/literal.js +5 -3
- package/dist/schema/literal.js.map +1 -1
- package/dist/schema/never.d.ts +2 -2
- package/dist/schema/never.d.ts.map +1 -1
- package/dist/schema/never.js +1 -1
- package/dist/schema/never.js.map +1 -1
- package/dist/schema/null.d.ts +2 -3
- package/dist/schema/null.d.ts.map +1 -1
- package/dist/schema/null.js +1 -2
- package/dist/schema/null.js.map +1 -1
- package/dist/schema/nullable.d.ts +7 -0
- package/dist/schema/nullable.d.ts.map +1 -0
- package/dist/schema/nullable.js +19 -0
- package/dist/schema/nullable.js.map +1 -0
- package/dist/schema/object.d.ts +10 -44
- package/dist/schema/object.d.ts.map +1 -1
- package/dist/schema/object.js +10 -46
- package/dist/schema/object.js.map +1 -1
- package/dist/schema/optional.d.ts +7 -0
- package/dist/schema/optional.d.ts.map +1 -0
- package/dist/schema/optional.js +25 -0
- package/dist/schema/optional.js.map +1 -0
- package/dist/schema/params.d.ts +14 -19
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +10 -24
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/payload.d.ts +4 -4
- package/dist/schema/payload.d.ts.map +1 -1
- package/dist/schema/payload.js.map +1 -1
- package/dist/schema/permission-set.d.ts +6 -6
- package/dist/schema/permission-set.d.ts.map +1 -1
- package/dist/schema/permission-set.js +1 -2
- package/dist/schema/permission-set.js.map +1 -1
- package/dist/schema/permission.d.ts +0 -1
- package/dist/schema/permission.d.ts.map +1 -1
- package/dist/schema/permission.js +0 -1
- package/dist/schema/permission.js.map +1 -1
- package/dist/schema/procedure.d.ts +8 -9
- package/dist/schema/procedure.d.ts.map +1 -1
- package/dist/schema/procedure.js +0 -1
- package/dist/schema/procedure.js.map +1 -1
- package/dist/schema/query.d.ts +7 -8
- package/dist/schema/query.d.ts.map +1 -1
- package/dist/schema/query.js +0 -1
- package/dist/schema/query.js.map +1 -1
- package/dist/schema/record.d.ts +34 -28
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +1 -2
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/ref.d.ts +2 -3
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js +1 -2
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/refine.d.ts +18 -0
- package/dist/schema/refine.d.ts.map +1 -0
- package/dist/schema/refine.js +33 -0
- package/dist/schema/refine.js.map +1 -0
- package/dist/schema/regexp.d.ts +7 -0
- package/dist/schema/regexp.d.ts.map +1 -0
- package/dist/schema/regexp.js +22 -0
- package/dist/schema/regexp.js.map +1 -0
- package/dist/schema/string.d.ts +4 -8
- package/dist/schema/string.d.ts.map +1 -1
- package/dist/schema/string.js +6 -3
- package/dist/schema/string.js.map +1 -1
- package/dist/schema/subscription.d.ts +7 -6
- package/dist/schema/subscription.d.ts.map +1 -1
- package/dist/schema/subscription.js.map +1 -1
- package/dist/schema/token.d.ts +2 -3
- package/dist/schema/token.d.ts.map +1 -1
- package/dist/schema/token.js +1 -2
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-object.d.ts +29 -27
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +1 -2
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/typed-ref.d.ts +2 -2
- package/dist/schema/typed-ref.d.ts.map +1 -1
- package/dist/schema/typed-ref.js +1 -1
- package/dist/schema/typed-ref.js.map +1 -1
- package/dist/schema/typed-union.d.ts +3 -4
- package/dist/schema/typed-union.d.ts.map +1 -1
- package/dist/schema/typed-union.js +3 -10
- package/dist/schema/typed-union.js.map +1 -1
- package/dist/schema/union.d.ts +2 -2
- package/dist/schema/union.d.ts.map +1 -1
- package/dist/schema/union.js +1 -1
- package/dist/schema/union.js.map +1 -1
- package/dist/schema/unknown-object.d.ts +2 -3
- package/dist/schema/unknown-object.d.ts.map +1 -1
- package/dist/schema/unknown-object.js +1 -2
- package/dist/schema/unknown-object.js.map +1 -1
- package/dist/schema/unknown.d.ts +2 -2
- package/dist/schema/unknown.d.ts.map +1 -1
- package/dist/schema/unknown.js +1 -1
- package/dist/schema/unknown.js.map +1 -1
- package/dist/schema.d.ts +4 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +6 -1
- package/dist/schema.js.map +1 -1
- package/dist/util/array-agg.d.ts.map +1 -1
- package/dist/util/array-agg.js +1 -0
- package/dist/util/array-agg.js.map +1 -1
- package/dist/util/lazy-property.d.ts +2 -0
- package/dist/util/lazy-property.d.ts.map +1 -0
- package/dist/util/lazy-property.js +14 -0
- package/dist/util/lazy-property.js.map +1 -0
- package/dist/validation/schema.d.ts +24 -0
- package/dist/validation/schema.d.ts.map +1 -0
- package/dist/validation/schema.js +57 -0
- package/dist/validation/schema.js.map +1 -0
- package/dist/validation/validation-error.d.ts +3 -3
- package/dist/validation/validation-error.d.ts.map +1 -1
- package/dist/validation/validation-error.js +32 -4
- package/dist/validation/validation-error.js.map +1 -1
- package/dist/validation/validation-issue.d.ts +32 -24
- package/dist/validation/validation-issue.d.ts.map +1 -1
- package/dist/validation/validation-issue.js +136 -92
- package/dist/validation/validation-issue.js.map +1 -1
- package/dist/validation/validator.d.ts +20 -50
- package/dist/validation/validator.d.ts.map +1 -1
- package/dist/validation/validator.js +40 -134
- package/dist/validation/validator.js.map +1 -1
- package/dist/validation.d.ts +1 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +1 -0
- package/dist/validation.js.map +1 -1
- package/package.json +8 -4
- package/src/core/$type.ts +7 -4
- package/src/core/record-key.ts +12 -5
- package/src/core/result.ts +6 -0
- package/src/core/string-format.ts +97 -61
- package/src/core/types.ts +12 -6
- package/src/external.ts +92 -70
- package/src/schema/_parameters.test.ts +416 -0
- package/src/schema/array.test.ts +237 -0
- package/src/schema/array.ts +17 -11
- package/src/schema/blob.test.ts +506 -0
- package/src/schema/blob.ts +3 -5
- package/src/schema/boolean.test.ts +116 -0
- package/src/schema/boolean.ts +5 -7
- package/src/schema/bytes.test.ts +226 -0
- package/src/schema/bytes.ts +4 -6
- package/src/schema/cid.test.ts +155 -0
- package/src/schema/cid.ts +14 -8
- package/src/schema/custom.test.ts +413 -0
- package/src/schema/custom.ts +10 -8
- package/src/schema/dict.test.ts +198 -0
- package/src/schema/dict.ts +6 -8
- package/src/schema/discriminated-union.test.ts +675 -0
- package/src/schema/discriminated-union.ts +68 -95
- package/src/schema/enum.test.ts +396 -0
- package/src/schema/enum.ts +12 -5
- package/src/schema/integer.test.ts +312 -0
- package/src/schema/integer.ts +5 -7
- package/src/schema/intersection.test.ts +32 -0
- package/src/schema/intersection.ts +37 -40
- package/src/schema/literal.test.ts +531 -0
- package/src/schema/literal.ts +12 -5
- package/src/schema/never.test.ts +174 -0
- package/src/schema/never.ts +3 -10
- package/src/schema/null.test.ts +79 -0
- package/src/schema/null.ts +3 -5
- package/src/schema/nullable.test.ts +480 -0
- package/src/schema/nullable.ts +23 -0
- package/src/schema/object.test.ts +47 -115
- package/src/schema/object.ts +19 -123
- package/src/schema/optional.test.ts +485 -0
- package/src/schema/optional.ts +31 -0
- package/src/schema/params.test.ts +582 -0
- package/src/schema/params.ts +37 -55
- package/src/schema/payload.test.ts +345 -0
- package/src/schema/payload.ts +5 -5
- package/src/schema/permission-set.test.ts +679 -0
- package/src/schema/permission-set.ts +6 -8
- package/src/schema/permission.test.ts +536 -0
- package/src/schema/permission.ts +0 -2
- package/src/schema/procedure.test.ts +443 -0
- package/src/schema/procedure.ts +11 -13
- package/src/schema/query.test.ts +408 -0
- package/src/schema/query.ts +9 -11
- package/src/schema/record.test.ts +694 -0
- package/src/schema/record.ts +38 -36
- package/src/schema/ref.test.ts +365 -0
- package/src/schema/ref.ts +8 -5
- package/src/schema/refine.test.ts +578 -0
- package/src/schema/refine.ts +85 -0
- package/src/schema/regexp.test.ts +580 -0
- package/src/schema/regexp.ts +22 -0
- package/src/schema/string.test.ts +612 -0
- package/src/schema/string.ts +11 -17
- package/src/schema/subscription.test.ts +689 -0
- package/src/schema/subscription.ts +13 -8
- package/src/schema/token.test.ts +428 -0
- package/src/schema/token.ts +3 -5
- package/src/schema/typed-object.test.ts +612 -0
- package/src/schema/typed-object.ts +23 -20
- package/src/schema/typed-ref.test.ts +823 -0
- package/src/schema/typed-ref.ts +10 -5
- package/src/schema/typed-union.test.ts +378 -0
- package/src/schema/typed-union.ts +6 -15
- package/src/schema/union.test.ts +200 -0
- package/src/schema/union.ts +5 -4
- package/src/schema/unknown-object.test.ts +592 -0
- package/src/schema/unknown-object.ts +3 -5
- package/src/schema/unknown.test.ts +312 -0
- package/src/schema/unknown.ts +3 -3
- package/src/schema.ts +7 -1
- package/src/util/array-agg.ts +1 -0
- package/src/util/lazy-property.ts +14 -0
- package/src/validation/schema.ts +92 -0
- package/src/validation/validation-error.ts +60 -9
- package/src/validation/validation-issue.ts +141 -144
- package/src/validation/validator.ts +67 -206
- package/src/validation.ts +1 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +7 -0
- package/tsconfig.tests.json +9 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { UnknownSchema } from './unknown.js'
|
|
2
|
+
|
|
3
|
+
describe('UnknownSchema', () => {
|
|
4
|
+
describe('basic validation', () => {
|
|
5
|
+
const schema = new UnknownSchema()
|
|
6
|
+
|
|
7
|
+
it('accepts strings', () => {
|
|
8
|
+
const result = schema.safeParse('hello')
|
|
9
|
+
expect(result.success).toBe(true)
|
|
10
|
+
if (result.success) {
|
|
11
|
+
expect(result.value).toBe('hello')
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('accepts numbers', () => {
|
|
16
|
+
const result = schema.safeParse(42)
|
|
17
|
+
expect(result.success).toBe(true)
|
|
18
|
+
if (result.success) {
|
|
19
|
+
expect(result.value).toBe(42)
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('accepts booleans', () => {
|
|
24
|
+
const result = schema.safeParse(true)
|
|
25
|
+
expect(result.success).toBe(true)
|
|
26
|
+
if (result.success) {
|
|
27
|
+
expect(result.value).toBe(true)
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('accepts null', () => {
|
|
32
|
+
const result = schema.safeParse(null)
|
|
33
|
+
expect(result.success).toBe(true)
|
|
34
|
+
if (result.success) {
|
|
35
|
+
expect(result.value).toBe(null)
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('accepts undefined', () => {
|
|
40
|
+
const result = schema.safeParse(undefined)
|
|
41
|
+
expect(result.success).toBe(true)
|
|
42
|
+
if (result.success) {
|
|
43
|
+
expect(result.value).toBe(undefined)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('accepts plain objects', () => {
|
|
48
|
+
const obj = { key: 'value', nested: { prop: 123 } }
|
|
49
|
+
const result = schema.safeParse(obj)
|
|
50
|
+
expect(result.success).toBe(true)
|
|
51
|
+
if (result.success) {
|
|
52
|
+
expect(result.value).toEqual(obj)
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('accepts arrays', () => {
|
|
57
|
+
const arr = [1, 2, 3, 'four', { five: 5 }]
|
|
58
|
+
const result = schema.safeParse(arr)
|
|
59
|
+
expect(result.success).toBe(true)
|
|
60
|
+
if (result.success) {
|
|
61
|
+
expect(result.value).toEqual(arr)
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('accepts empty arrays', () => {
|
|
66
|
+
const result = schema.safeParse([])
|
|
67
|
+
expect(result.success).toBe(true)
|
|
68
|
+
if (result.success) {
|
|
69
|
+
expect(result.value).toEqual([])
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('accepts empty objects', () => {
|
|
74
|
+
const result = schema.safeParse({})
|
|
75
|
+
expect(result.success).toBe(true)
|
|
76
|
+
if (result.success) {
|
|
77
|
+
expect(result.value).toEqual({})
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('edge cases', () => {
|
|
83
|
+
const schema = new UnknownSchema()
|
|
84
|
+
|
|
85
|
+
it('accepts zero', () => {
|
|
86
|
+
const result = schema.safeParse(0)
|
|
87
|
+
expect(result.success).toBe(true)
|
|
88
|
+
if (result.success) {
|
|
89
|
+
expect(result.value).toBe(0)
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('accepts empty string', () => {
|
|
94
|
+
const result = schema.safeParse('')
|
|
95
|
+
expect(result.success).toBe(true)
|
|
96
|
+
if (result.success) {
|
|
97
|
+
expect(result.value).toBe('')
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('accepts negative numbers', () => {
|
|
102
|
+
const result = schema.safeParse(-123)
|
|
103
|
+
expect(result.success).toBe(true)
|
|
104
|
+
if (result.success) {
|
|
105
|
+
expect(result.value).toBe(-123)
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('accepts floating point numbers', () => {
|
|
110
|
+
const result = schema.safeParse(3.14159)
|
|
111
|
+
expect(result.success).toBe(true)
|
|
112
|
+
if (result.success) {
|
|
113
|
+
expect(result.value).toBe(3.14159)
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('accepts NaN', () => {
|
|
118
|
+
const result = schema.safeParse(NaN)
|
|
119
|
+
expect(result.success).toBe(true)
|
|
120
|
+
if (result.success) {
|
|
121
|
+
expect(result.value).toBeNaN()
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('accepts Infinity', () => {
|
|
126
|
+
const result = schema.safeParse(Infinity)
|
|
127
|
+
expect(result.success).toBe(true)
|
|
128
|
+
if (result.success) {
|
|
129
|
+
expect(result.value).toBe(Infinity)
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('accepts -Infinity', () => {
|
|
134
|
+
const result = schema.safeParse(-Infinity)
|
|
135
|
+
expect(result.success).toBe(true)
|
|
136
|
+
if (result.success) {
|
|
137
|
+
expect(result.value).toBe(-Infinity)
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('accepts BigInt', () => {
|
|
142
|
+
const bigIntValue = BigInt(9007199254740991)
|
|
143
|
+
const result = schema.safeParse(bigIntValue)
|
|
144
|
+
expect(result.success).toBe(true)
|
|
145
|
+
if (result.success) {
|
|
146
|
+
expect(result.value).toBe(bigIntValue)
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('accepts functions', () => {
|
|
151
|
+
const fn = () => 'test'
|
|
152
|
+
const result = schema.safeParse(fn)
|
|
153
|
+
expect(result.success).toBe(true)
|
|
154
|
+
if (result.success) {
|
|
155
|
+
expect(result.value).toBe(fn)
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('accepts Date objects', () => {
|
|
160
|
+
const date = new Date('2023-01-01')
|
|
161
|
+
const result = schema.safeParse(date)
|
|
162
|
+
expect(result.success).toBe(true)
|
|
163
|
+
if (result.success) {
|
|
164
|
+
expect(result.value).toBe(date)
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('accepts RegExp objects', () => {
|
|
169
|
+
const regex = /test/gi
|
|
170
|
+
const result = schema.safeParse(regex)
|
|
171
|
+
expect(result.success).toBe(true)
|
|
172
|
+
if (result.success) {
|
|
173
|
+
expect(result.value).toBe(regex)
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('accepts Symbol', () => {
|
|
178
|
+
const sym = Symbol('test')
|
|
179
|
+
const result = schema.safeParse(sym)
|
|
180
|
+
expect(result.success).toBe(true)
|
|
181
|
+
if (result.success) {
|
|
182
|
+
expect(result.value).toBe(sym)
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it('accepts Map objects', () => {
|
|
187
|
+
const map = new Map([['key', 'value']])
|
|
188
|
+
const result = schema.safeParse(map)
|
|
189
|
+
expect(result.success).toBe(true)
|
|
190
|
+
if (result.success) {
|
|
191
|
+
expect(result.value).toBe(map)
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it('accepts Set objects', () => {
|
|
196
|
+
const set = new Set([1, 2, 3])
|
|
197
|
+
const result = schema.safeParse(set)
|
|
198
|
+
expect(result.success).toBe(true)
|
|
199
|
+
if (result.success) {
|
|
200
|
+
expect(result.value).toBe(set)
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('accepts WeakMap objects', () => {
|
|
205
|
+
const weakMap = new WeakMap()
|
|
206
|
+
const result = schema.safeParse(weakMap)
|
|
207
|
+
expect(result.success).toBe(true)
|
|
208
|
+
if (result.success) {
|
|
209
|
+
expect(result.value).toBe(weakMap)
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('accepts WeakSet objects', () => {
|
|
214
|
+
const weakSet = new WeakSet()
|
|
215
|
+
const result = schema.safeParse(weakSet)
|
|
216
|
+
expect(result.success).toBe(true)
|
|
217
|
+
if (result.success) {
|
|
218
|
+
expect(result.value).toBe(weakSet)
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('accepts class instances', () => {
|
|
223
|
+
class TestClass {
|
|
224
|
+
constructor(public value: string) {}
|
|
225
|
+
}
|
|
226
|
+
const instance = new TestClass('test')
|
|
227
|
+
const result = schema.safeParse(instance)
|
|
228
|
+
expect(result.success).toBe(true)
|
|
229
|
+
if (result.success) {
|
|
230
|
+
expect(result.value).toBe(instance)
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it('accepts nested complex structures', () => {
|
|
235
|
+
const complex = {
|
|
236
|
+
array: [1, 'two', { three: 3 }],
|
|
237
|
+
nested: {
|
|
238
|
+
deep: {
|
|
239
|
+
value: [null, undefined, true],
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
fn: () => 'test',
|
|
243
|
+
date: new Date(),
|
|
244
|
+
}
|
|
245
|
+
const result = schema.safeParse(complex)
|
|
246
|
+
expect(result.success).toBe(true)
|
|
247
|
+
if (result.success) {
|
|
248
|
+
expect(result.value).toEqual(complex)
|
|
249
|
+
}
|
|
250
|
+
})
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
describe('special JavaScript values', () => {
|
|
254
|
+
const schema = new UnknownSchema()
|
|
255
|
+
|
|
256
|
+
it('accepts objects with null prototype', () => {
|
|
257
|
+
const obj = Object.create(null)
|
|
258
|
+
obj.key = 'value'
|
|
259
|
+
const result = schema.safeParse(obj)
|
|
260
|
+
expect(result.success).toBe(true)
|
|
261
|
+
if (result.success) {
|
|
262
|
+
expect(result.value).toBe(obj)
|
|
263
|
+
}
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('accepts Proxy objects', () => {
|
|
267
|
+
const target = { value: 42 }
|
|
268
|
+
const proxy = new Proxy(target, {})
|
|
269
|
+
const result = schema.safeParse(proxy)
|
|
270
|
+
expect(result.success).toBe(true)
|
|
271
|
+
if (result.success) {
|
|
272
|
+
expect(result.value).toBe(proxy)
|
|
273
|
+
}
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
it('accepts typed arrays', () => {
|
|
277
|
+
const uint8Array = new Uint8Array([1, 2, 3])
|
|
278
|
+
const result = schema.safeParse(uint8Array)
|
|
279
|
+
expect(result.success).toBe(true)
|
|
280
|
+
if (result.success) {
|
|
281
|
+
expect(result.value).toBe(uint8Array)
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it('accepts ArrayBuffer', () => {
|
|
286
|
+
const buffer = new ArrayBuffer(8)
|
|
287
|
+
const result = schema.safeParse(buffer)
|
|
288
|
+
expect(result.success).toBe(true)
|
|
289
|
+
if (result.success) {
|
|
290
|
+
expect(result.value).toBe(buffer)
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
it('accepts Promise objects', () => {
|
|
295
|
+
const promise = Promise.resolve(42)
|
|
296
|
+
const result = schema.safeParse(promise)
|
|
297
|
+
expect(result.success).toBe(true)
|
|
298
|
+
if (result.success) {
|
|
299
|
+
expect(result.value).toBe(promise)
|
|
300
|
+
}
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
it('accepts Error objects', () => {
|
|
304
|
+
const error = new Error('test error')
|
|
305
|
+
const result = schema.safeParse(error)
|
|
306
|
+
expect(result.success).toBe(true)
|
|
307
|
+
if (result.success) {
|
|
308
|
+
expect(result.value).toBe(error)
|
|
309
|
+
}
|
|
310
|
+
})
|
|
311
|
+
})
|
|
312
|
+
})
|
package/src/schema/unknown.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Schema, ValidationResult, ValidatorContext } from '../validation'
|
|
2
2
|
|
|
3
|
-
export class UnknownSchema extends
|
|
4
|
-
|
|
3
|
+
export class UnknownSchema extends Schema<unknown> {
|
|
4
|
+
validateInContext(
|
|
5
5
|
input: unknown,
|
|
6
6
|
ctx: ValidatorContext,
|
|
7
7
|
): ValidationResult<unknown> {
|
package/src/schema.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Global schemas
|
|
2
2
|
export * from './schema/_parameters.js'
|
|
3
3
|
|
|
4
|
+
// Utilities
|
|
5
|
+
export * from './schema/refine.js'
|
|
6
|
+
|
|
4
7
|
// Concrete Types
|
|
5
8
|
export * from './schema/array.js'
|
|
6
9
|
export * from './schema/blob.js'
|
|
@@ -14,6 +17,7 @@ export * from './schema/literal.js'
|
|
|
14
17
|
export * from './schema/never.js'
|
|
15
18
|
export * from './schema/null.js'
|
|
16
19
|
export * from './schema/object.js'
|
|
20
|
+
export * from './schema/regexp.js'
|
|
17
21
|
export * from './schema/string.js'
|
|
18
22
|
export * from './schema/unknown-object.js'
|
|
19
23
|
export * from './schema/unknown.js'
|
|
@@ -22,6 +26,8 @@ export * from './schema/unknown.js'
|
|
|
22
26
|
export * from './schema/custom.js'
|
|
23
27
|
export * from './schema/discriminated-union.js'
|
|
24
28
|
export * from './schema/intersection.js'
|
|
29
|
+
export * from './schema/nullable.js'
|
|
30
|
+
export * from './schema/optional.js'
|
|
25
31
|
export * from './schema/ref.js'
|
|
26
32
|
export * from './schema/union.js'
|
|
27
33
|
|
package/src/util/array-agg.ts
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*@__NO_SIDE_EFFECTS__*/
|
|
2
|
+
export function lazyProperty<
|
|
3
|
+
O extends object,
|
|
4
|
+
const K extends keyof O,
|
|
5
|
+
const V extends O[K],
|
|
6
|
+
>(obj: O, key: K, value: V): V {
|
|
7
|
+
Object.defineProperty(obj, key, {
|
|
8
|
+
value,
|
|
9
|
+
writable: false,
|
|
10
|
+
enumerable: false,
|
|
11
|
+
configurable: true,
|
|
12
|
+
})
|
|
13
|
+
return value
|
|
14
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ValidationOptions,
|
|
3
|
+
ValidationResult,
|
|
4
|
+
Validator,
|
|
5
|
+
ValidatorContext,
|
|
6
|
+
} from './validator.js'
|
|
7
|
+
|
|
8
|
+
export abstract class Schema<Output> implements Validator<Output> {
|
|
9
|
+
declare readonly ['_lex']: { output: Output }
|
|
10
|
+
|
|
11
|
+
abstract validateInContext(
|
|
12
|
+
input: unknown,
|
|
13
|
+
ctx: ValidatorContext,
|
|
14
|
+
): ValidationResult<Output>
|
|
15
|
+
|
|
16
|
+
assert(input: unknown): asserts input is Output {
|
|
17
|
+
const result = this.safeParse(input, { allowTransform: false })
|
|
18
|
+
if (!result.success) throw result.error
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
matches(input: unknown): input is Output {
|
|
22
|
+
const result = this.safeParse(input, { allowTransform: false })
|
|
23
|
+
return result.success
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
ifMatches<I>(input: I): (I & Output) | undefined {
|
|
27
|
+
return this.matches(input) ? input : undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
parse<I>(
|
|
31
|
+
input: I,
|
|
32
|
+
options: ValidationOptions & { allowTransform: false },
|
|
33
|
+
): I & Output
|
|
34
|
+
parse(input: unknown, options?: ValidationOptions): Output
|
|
35
|
+
parse(input: unknown, options?: ValidationOptions): Output {
|
|
36
|
+
const result = this.safeParse(input, options)
|
|
37
|
+
if (!result.success) throw result.error
|
|
38
|
+
return result.value
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
safeParse<I>(
|
|
42
|
+
input: I,
|
|
43
|
+
options: ValidationOptions & { allowTransform: false },
|
|
44
|
+
): ValidationResult<I & Output>
|
|
45
|
+
safeParse(
|
|
46
|
+
input: unknown,
|
|
47
|
+
options?: ValidationOptions,
|
|
48
|
+
): ValidationResult<Output>
|
|
49
|
+
safeParse(
|
|
50
|
+
input: unknown,
|
|
51
|
+
options?: ValidationOptions,
|
|
52
|
+
): ValidationResult<Output> {
|
|
53
|
+
return ValidatorContext.validate(input, this, options)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// @NOTE The built lexicons namespaces will export utility functions that
|
|
57
|
+
// allow accessing the schema's methods without the need to specify ".main."
|
|
58
|
+
// as part of the namespace. This way, a utility for a particular record type
|
|
59
|
+
// can be called like "app.bsky.feed.post.<utility>()" instead of
|
|
60
|
+
// "app.bsky.feed.post.main.<utility>()". Because those utilities could
|
|
61
|
+
// conflict with other schemas (e.g. if there is a lexicon definition at
|
|
62
|
+
// "#<utility>"), those exported utilities will be prefixed with "$". In order
|
|
63
|
+
// to be able to consistently call the utilities, when using the "main" and
|
|
64
|
+
// non "main" definitions, we also expose the same methods with a "$" prefix.
|
|
65
|
+
// Thanks to this, both of the following call will be possible:
|
|
66
|
+
//
|
|
67
|
+
// - "app.bsky.feed.post.$parse(...)" // calls a utility function created by "lex build"
|
|
68
|
+
// - "app.bsky.feed.defs.postView.$parse(...)" // uses the alias defined below on the schema instance
|
|
69
|
+
|
|
70
|
+
$assert(input: unknown): asserts input is Output {
|
|
71
|
+
return this.assert(input)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
$matches(input: unknown): input is Output {
|
|
75
|
+
return this.matches(input)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
$ifMatches<I>(input: I): (I & Output) | undefined {
|
|
79
|
+
return this.ifMatches(input)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
$parse(input: unknown, options?: ValidationOptions): Output {
|
|
83
|
+
return this.parse(input, options)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
$safeParse(
|
|
87
|
+
input: unknown,
|
|
88
|
+
options?: ValidationOptions,
|
|
89
|
+
): ValidationResult<Output> {
|
|
90
|
+
return this.safeParse(input, options)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { ResultFailure, failureError } from '../core.js'
|
|
2
|
+
import { arrayAgg } from '../util/array-agg.js'
|
|
2
3
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Issue,
|
|
5
|
+
IssueInvalidType,
|
|
6
|
+
IssueInvalidValue,
|
|
6
7
|
} from './validation-issue.js'
|
|
7
8
|
|
|
8
9
|
export class ValidationError extends Error {
|
|
9
10
|
name = 'ValidationError'
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
super(
|
|
12
|
+
readonly issues: Issue[]
|
|
13
|
+
|
|
14
|
+
constructor(issues: Issue[], options?: ErrorOptions) {
|
|
15
|
+
const issuesAgg = aggregateIssues(issues)
|
|
16
|
+
super(issuesAgg.join(', '), options)
|
|
17
|
+
this.issues = issuesAgg
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
static fromFailures(
|
|
@@ -20,7 +22,7 @@ export class ValidationError extends Error {
|
|
|
20
22
|
): ValidationError {
|
|
21
23
|
if (failures.length === 1) return failures[0].error
|
|
22
24
|
const issues = failures.flatMap(extractFailureIssues)
|
|
23
|
-
return new ValidationError(
|
|
25
|
+
return new ValidationError(issues, {
|
|
24
26
|
// Keep the original errors as the cause chain
|
|
25
27
|
cause: failures.map(failureError),
|
|
26
28
|
})
|
|
@@ -30,3 +32,52 @@ export class ValidationError extends Error {
|
|
|
30
32
|
function extractFailureIssues(result: ResultFailure<ValidationError>) {
|
|
31
33
|
return result.error.issues
|
|
32
34
|
}
|
|
35
|
+
|
|
36
|
+
function aggregateIssues(issues: Issue[]): Issue[] {
|
|
37
|
+
// Quick path for common cases
|
|
38
|
+
if (issues.length <= 1) return issues
|
|
39
|
+
if (issues.length === 2 && issues[0].code !== issues[1].code) return issues
|
|
40
|
+
|
|
41
|
+
return [
|
|
42
|
+
// Aggregate invalid_type with identical paths
|
|
43
|
+
...arrayAgg(
|
|
44
|
+
issues.filter((issue) => issue instanceof IssueInvalidType),
|
|
45
|
+
(a, b) => comparePropertyPaths(a.path, b.path),
|
|
46
|
+
(issues) =>
|
|
47
|
+
new IssueInvalidType(
|
|
48
|
+
issues[0].path,
|
|
49
|
+
issues[0].input,
|
|
50
|
+
Array.from(new Set(issues.flatMap((iss) => iss.expected))),
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
// Aggregate invalid_value with identical paths
|
|
54
|
+
...arrayAgg(
|
|
55
|
+
issues.filter((issue) => issue instanceof IssueInvalidValue),
|
|
56
|
+
(a, b) => comparePropertyPaths(a.path, b.path),
|
|
57
|
+
(issues) =>
|
|
58
|
+
new IssueInvalidValue(
|
|
59
|
+
issues[0].path,
|
|
60
|
+
issues[0].input,
|
|
61
|
+
Array.from(new Set(issues.flatMap((iss) => iss.values))),
|
|
62
|
+
),
|
|
63
|
+
),
|
|
64
|
+
// Pass through other issues
|
|
65
|
+
...issues.filter(
|
|
66
|
+
(issue) =>
|
|
67
|
+
!(issue instanceof IssueInvalidType) &&
|
|
68
|
+
!(issue instanceof IssueInvalidValue),
|
|
69
|
+
),
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/*@__NO_SIDE_EFFECTS__*/
|
|
74
|
+
function comparePropertyPaths(
|
|
75
|
+
a: readonly PropertyKey[],
|
|
76
|
+
b: readonly PropertyKey[],
|
|
77
|
+
) {
|
|
78
|
+
if (a.length !== b.length) return false
|
|
79
|
+
for (let i = 0; i < a.length; i++) {
|
|
80
|
+
if (a[i] !== b[i]) return false
|
|
81
|
+
}
|
|
82
|
+
return true
|
|
83
|
+
}
|