@atproto/lex-schema 0.0.12 → 0.0.14
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 +53 -0
- package/dist/core/schema.d.ts +27 -36
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +68 -54
- package/dist/core/schema.js.map +1 -1
- package/dist/core/string-format.d.ts +1 -14
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +12 -9
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/validation-error.d.ts +5 -5
- package/dist/core/validation-error.d.ts.map +1 -1
- package/dist/core/validation-error.js +8 -8
- package/dist/core/validation-error.js.map +1 -1
- package/dist/core/validation-issue.js +3 -1
- package/dist/core/validation-issue.js.map +1 -1
- package/dist/core/validator.d.ts +16 -8
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +24 -6
- package/dist/core/validator.js.map +1 -1
- package/dist/helpers.d.ts +10 -11
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js.map +1 -1
- package/dist/schema/array.d.ts +1 -0
- package/dist/schema/array.d.ts.map +1 -1
- package/dist/schema/array.js +2 -1
- package/dist/schema/array.js.map +1 -1
- package/dist/schema/blob.d.ts +4 -2
- package/dist/schema/blob.d.ts.map +1 -1
- package/dist/schema/blob.js +5 -2
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/boolean.d.ts +1 -0
- package/dist/schema/boolean.d.ts.map +1 -1
- package/dist/schema/boolean.js +2 -1
- package/dist/schema/boolean.js.map +1 -1
- package/dist/schema/bytes.d.ts +1 -0
- package/dist/schema/bytes.d.ts.map +1 -1
- package/dist/schema/bytes.js +2 -1
- package/dist/schema/bytes.js.map +1 -1
- package/dist/schema/cid.d.ts +1 -0
- package/dist/schema/cid.d.ts.map +1 -1
- package/dist/schema/cid.js +2 -1
- package/dist/schema/cid.js.map +1 -1
- package/dist/schema/custom.d.ts +1 -0
- package/dist/schema/custom.d.ts.map +1 -1
- package/dist/schema/custom.js +1 -0
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/dict.d.ts +1 -0
- package/dist/schema/dict.d.ts.map +1 -1
- package/dist/schema/dict.js +2 -1
- package/dist/schema/dict.js.map +1 -1
- package/dist/schema/discriminated-union.d.ts +1 -0
- package/dist/schema/discriminated-union.d.ts.map +1 -1
- package/dist/schema/discriminated-union.js +2 -1
- package/dist/schema/discriminated-union.js.map +1 -1
- package/dist/schema/enum.d.ts +1 -0
- package/dist/schema/enum.d.ts.map +1 -1
- package/dist/schema/enum.js +1 -0
- package/dist/schema/enum.js.map +1 -1
- package/dist/schema/integer.d.ts +1 -0
- package/dist/schema/integer.d.ts.map +1 -1
- package/dist/schema/integer.js +2 -1
- package/dist/schema/integer.js.map +1 -1
- package/dist/schema/intersection.d.ts +1 -0
- package/dist/schema/intersection.d.ts.map +1 -1
- package/dist/schema/intersection.js +1 -0
- package/dist/schema/intersection.js.map +1 -1
- package/dist/schema/lex-map.d.ts +37 -0
- package/dist/schema/lex-map.d.ts.map +1 -0
- package/dist/schema/lex-map.js +60 -0
- package/dist/schema/lex-map.js.map +1 -0
- package/dist/schema/lex-value.d.ts +35 -0
- package/dist/schema/lex-value.d.ts.map +1 -0
- package/dist/schema/lex-value.js +87 -0
- package/dist/schema/lex-value.js.map +1 -0
- package/dist/schema/literal.d.ts +1 -0
- package/dist/schema/literal.d.ts.map +1 -1
- package/dist/schema/literal.js +1 -0
- package/dist/schema/literal.js.map +1 -1
- package/dist/schema/never.d.ts +1 -0
- package/dist/schema/never.d.ts.map +1 -1
- package/dist/schema/never.js +2 -1
- package/dist/schema/never.js.map +1 -1
- package/dist/schema/null.d.ts +1 -0
- package/dist/schema/null.d.ts.map +1 -1
- package/dist/schema/null.js +2 -1
- package/dist/schema/null.js.map +1 -1
- package/dist/schema/nullable.d.ts +1 -0
- package/dist/schema/nullable.d.ts.map +1 -1
- package/dist/schema/nullable.js +1 -0
- package/dist/schema/nullable.js.map +1 -1
- package/dist/schema/object.d.ts +1 -0
- package/dist/schema/object.d.ts.map +1 -1
- package/dist/schema/object.js +2 -1
- package/dist/schema/object.js.map +1 -1
- package/dist/schema/optional.d.ts +1 -0
- package/dist/schema/optional.d.ts.map +1 -1
- package/dist/schema/optional.js +1 -0
- package/dist/schema/optional.js.map +1 -1
- package/dist/schema/params.d.ts +14 -10
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +87 -24
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/payload.d.ts.map +1 -1
- package/dist/schema/payload.js +3 -3
- package/dist/schema/payload.js.map +1 -1
- package/dist/schema/record.d.ts +21 -19
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +22 -12
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/ref.d.ts +1 -0
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js +1 -0
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/regexp.d.ts +1 -0
- package/dist/schema/regexp.d.ts.map +1 -1
- package/dist/schema/regexp.js +2 -1
- package/dist/schema/regexp.js.map +1 -1
- package/dist/schema/string.d.ts +22 -6
- package/dist/schema/string.d.ts.map +1 -1
- package/dist/schema/string.js +16 -9
- package/dist/schema/string.js.map +1 -1
- package/dist/schema/token.d.ts +1 -0
- package/dist/schema/token.d.ts.map +1 -1
- package/dist/schema/token.js +2 -1
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-object.d.ts +20 -16
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +23 -13
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/typed-ref.d.ts +1 -0
- package/dist/schema/typed-ref.d.ts.map +1 -1
- package/dist/schema/typed-ref.js +1 -0
- package/dist/schema/typed-ref.js.map +1 -1
- package/dist/schema/typed-union.d.ts +1 -0
- package/dist/schema/typed-union.d.ts.map +1 -1
- package/dist/schema/typed-union.js +2 -1
- package/dist/schema/typed-union.js.map +1 -1
- package/dist/schema/union.d.ts +1 -0
- package/dist/schema/union.d.ts.map +1 -1
- package/dist/schema/union.js +2 -1
- package/dist/schema/union.js.map +1 -1
- package/dist/schema/unknown.d.ts +1 -0
- package/dist/schema/unknown.d.ts.map +1 -1
- package/dist/schema/unknown.js +1 -0
- package/dist/schema/unknown.js.map +1 -1
- package/dist/schema/with-default.d.ts +1 -0
- package/dist/schema/with-default.d.ts.map +1 -1
- package/dist/schema/with-default.js +1 -0
- package/dist/schema/with-default.js.map +1 -1
- package/dist/schema.d.ts +2 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +2 -1
- package/dist/schema.js.map +1 -1
- package/dist/util/if-any.d.ts +2 -0
- package/dist/util/if-any.d.ts.map +1 -0
- package/dist/util/if-any.js +3 -0
- package/dist/util/if-any.js.map +1 -0
- package/package.json +3 -3
- package/src/core/schema.ts +76 -62
- package/src/core/string-format.ts +14 -17
- package/src/core/validation-error.ts +10 -10
- package/src/core/validation-issue.ts +3 -2
- package/src/core/validator.ts +32 -12
- package/src/helpers.test.ts +1 -1
- package/src/helpers.ts +53 -19
- package/src/schema/array.ts +3 -1
- package/src/schema/blob.ts +4 -1
- package/src/schema/boolean.ts +3 -1
- package/src/schema/bytes.ts +3 -1
- package/src/schema/cid.ts +3 -1
- package/src/schema/custom.ts +2 -0
- package/src/schema/dict.ts +3 -1
- package/src/schema/discriminated-union.ts +3 -1
- package/src/schema/enum.ts +2 -0
- package/src/schema/integer.ts +3 -1
- package/src/schema/intersection.ts +2 -0
- package/src/schema/{unknown-object.test.ts → lex-map.test.ts} +9 -9
- package/src/schema/lex-map.ts +63 -0
- package/src/schema/lex-value.test.ts +81 -0
- package/src/schema/lex-value.ts +86 -0
- package/src/schema/literal.ts +2 -0
- package/src/schema/never.ts +3 -1
- package/src/schema/null.ts +3 -1
- package/src/schema/nullable.ts +2 -0
- package/src/schema/object.ts +3 -1
- package/src/schema/optional.ts +2 -0
- package/src/schema/params.test.ts +98 -43
- package/src/schema/params.ts +136 -39
- package/src/schema/payload.test.ts +2 -2
- package/src/schema/payload.ts +3 -4
- package/src/schema/record.ts +38 -22
- package/src/schema/ref.ts +2 -0
- package/src/schema/regexp.ts +3 -1
- package/src/schema/string.test.ts +99 -2
- package/src/schema/string.ts +58 -15
- package/src/schema/token.ts +3 -1
- package/src/schema/typed-object.test.ts +38 -0
- package/src/schema/typed-object.ts +40 -24
- package/src/schema/typed-ref.ts +2 -0
- package/src/schema/typed-union.ts +3 -1
- package/src/schema/union.ts +4 -2
- package/src/schema/unknown.ts +2 -0
- package/src/schema/with-default.ts +2 -0
- package/src/schema.ts +2 -1
- package/src/util/if-any.ts +3 -0
- package/dist/schema/unknown-object.d.ts +0 -42
- package/dist/schema/unknown-object.d.ts.map +0 -1
- package/dist/schema/unknown-object.js +0 -50
- package/dist/schema/unknown-object.js.map +0 -1
- package/src/schema/unknown-object.ts +0 -53
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
1
|
+
import { describe, expect, expectTypeOf, it } from 'vitest'
|
|
2
|
+
import { Infer, UnknownString } from '../core.js'
|
|
3
|
+
import { StringSchemaOptions, string } from './string.js'
|
|
3
4
|
import { token } from './token.js'
|
|
4
5
|
import { withDefault } from './with-default.js'
|
|
5
6
|
|
|
@@ -610,4 +611,100 @@ describe('StringSchema', () => {
|
|
|
610
611
|
expect(result.success).toBe(true)
|
|
611
612
|
})
|
|
612
613
|
})
|
|
614
|
+
|
|
615
|
+
describe('knownValues option', () => {
|
|
616
|
+
it('allows omitting knownValues at runtime', () => {
|
|
617
|
+
string<{ knownValues: ['active', 'inactive'] }>()
|
|
618
|
+
|
|
619
|
+
// @ts-expect-error format requires options to be set
|
|
620
|
+
string<{ knownValues: ['active', 'inactive']; format: 'did' }>()
|
|
621
|
+
|
|
622
|
+
// @ts-expect-error any options, besides knownValues, must be provided
|
|
623
|
+
string<{ knownValues: ['active', 'inactive']; minLength: 5 }>()
|
|
624
|
+
|
|
625
|
+
string<{
|
|
626
|
+
knownValues: ['john.doe', 'someone.else']
|
|
627
|
+
format: 'handle'
|
|
628
|
+
}>({
|
|
629
|
+
format: 'handle',
|
|
630
|
+
})
|
|
631
|
+
|
|
632
|
+
string<{
|
|
633
|
+
knownValues: ['john.doe', 'someone.else']
|
|
634
|
+
}>({
|
|
635
|
+
// Being *more* precise than the generic if fine
|
|
636
|
+
format: 'handle',
|
|
637
|
+
})
|
|
638
|
+
|
|
639
|
+
string<{
|
|
640
|
+
knownValues: ['did', 'inactive']
|
|
641
|
+
format: 'did'
|
|
642
|
+
}>({
|
|
643
|
+
// @ts-expect-error does not match format form generic constraint
|
|
644
|
+
format: 'handle',
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
string<{
|
|
648
|
+
knownValues: ['active', 'inactive']
|
|
649
|
+
minLength: 10
|
|
650
|
+
}>({
|
|
651
|
+
minLength: 10,
|
|
652
|
+
})
|
|
653
|
+
|
|
654
|
+
string<{
|
|
655
|
+
knownValues: ['active', 'inactive']
|
|
656
|
+
minLength: 5
|
|
657
|
+
}>({
|
|
658
|
+
// @ts-expect-error mismatch
|
|
659
|
+
minLength: 10,
|
|
660
|
+
})
|
|
661
|
+
})
|
|
662
|
+
})
|
|
663
|
+
|
|
664
|
+
it('properly types knownValues in parameters', () => {
|
|
665
|
+
const schema = string({
|
|
666
|
+
knownValues: ['active', 'inactive'],
|
|
667
|
+
})
|
|
668
|
+
type SchemaType = Infer<typeof schema>
|
|
669
|
+
expectTypeOf<{
|
|
670
|
+
foo: SchemaType
|
|
671
|
+
}>().toMatchObjectType<{
|
|
672
|
+
foo: 'active' | 'inactive' | UnknownString
|
|
673
|
+
}>()
|
|
674
|
+
expectTypeOf<{
|
|
675
|
+
foo: SchemaType
|
|
676
|
+
}>().not.toMatchObjectType<{
|
|
677
|
+
foo: string
|
|
678
|
+
}>()
|
|
679
|
+
expectTypeOf<{
|
|
680
|
+
foo: SchemaType
|
|
681
|
+
}>().not.toMatchObjectType<{
|
|
682
|
+
foo: 'active' | 'inactive'
|
|
683
|
+
}>()
|
|
684
|
+
expectTypeOf<{
|
|
685
|
+
foo: SchemaType
|
|
686
|
+
}>().not.toMatchObjectType<{
|
|
687
|
+
foo: UnknownString
|
|
688
|
+
}>()
|
|
689
|
+
})
|
|
690
|
+
|
|
691
|
+
it('type string<any>() as string', () => {
|
|
692
|
+
const schema = string<any>()
|
|
693
|
+
type SchemaType = Infer<typeof schema>
|
|
694
|
+
expectTypeOf<{
|
|
695
|
+
foo: SchemaType
|
|
696
|
+
}>().toMatchObjectType<{
|
|
697
|
+
foo: string
|
|
698
|
+
}>()
|
|
699
|
+
})
|
|
700
|
+
|
|
701
|
+
it('type string<StringSchemaOptions>({}) as string', () => {
|
|
702
|
+
const schema = string<StringSchemaOptions>({})
|
|
703
|
+
type SchemaType = Infer<typeof schema>
|
|
704
|
+
expectTypeOf<{
|
|
705
|
+
foo: SchemaType
|
|
706
|
+
}>().toMatchObjectType<{
|
|
707
|
+
foo: string
|
|
708
|
+
}>()
|
|
709
|
+
})
|
|
613
710
|
})
|
package/src/schema/string.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { graphemeLen, ifCid, utf8Len } from '@atproto/lex-data'
|
|
2
2
|
import {
|
|
3
3
|
InferStringFormat,
|
|
4
|
+
Restricted,
|
|
4
5
|
Schema,
|
|
5
6
|
StringFormat,
|
|
7
|
+
UnknownString,
|
|
6
8
|
ValidationContext,
|
|
7
9
|
isStringFormat,
|
|
8
10
|
} from '../core.js'
|
|
11
|
+
import { IfAny } from '../util/if-any.js'
|
|
9
12
|
import { memoizedOptions } from '../util/memoize.js'
|
|
10
13
|
import { TokenSchema } from './token.js'
|
|
11
14
|
|
|
@@ -13,6 +16,7 @@ import { TokenSchema } from './token.js'
|
|
|
13
16
|
* Configuration options for string schema validation.
|
|
14
17
|
*
|
|
15
18
|
* @property format - Expected string format (e.g., 'datetime', 'uri', 'at-uri', 'did', 'handle', 'nsid', 'cid', 'tid', 'record-key', 'at-identifier', 'language')
|
|
19
|
+
* @property knownValues - Known string literal values for type narrowing
|
|
16
20
|
* @property minLength - Minimum length in UTF-8 bytes
|
|
17
21
|
* @property maxLength - Maximum length in UTF-8 bytes
|
|
18
22
|
* @property minGraphemes - Minimum number of grapheme clusters
|
|
@@ -20,6 +24,7 @@ import { TokenSchema } from './token.js'
|
|
|
20
24
|
*/
|
|
21
25
|
export type StringSchemaOptions = {
|
|
22
26
|
format?: StringFormat
|
|
27
|
+
knownValues?: readonly string[]
|
|
23
28
|
minLength?: number
|
|
24
29
|
maxLength?: number
|
|
25
30
|
minGraphemes?: number
|
|
@@ -43,30 +48,46 @@ export type StringSchemaOptions = {
|
|
|
43
48
|
export class StringSchema<
|
|
44
49
|
const TOptions extends StringSchemaOptions = StringSchemaOptions,
|
|
45
50
|
> extends Schema<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
IfAny<
|
|
52
|
+
TOptions,
|
|
53
|
+
string,
|
|
54
|
+
TOptions extends { format: infer F extends StringFormat }
|
|
55
|
+
? InferStringFormat<F>
|
|
56
|
+
: TOptions extends { knownValues: readonly (infer V extends string)[] }
|
|
57
|
+
? V | UnknownString
|
|
58
|
+
: string
|
|
59
|
+
>
|
|
49
60
|
> {
|
|
50
|
-
|
|
61
|
+
readonly type = 'string' as const
|
|
62
|
+
|
|
63
|
+
// @NOTE since the _string utility allows omitting knownValues when TOptions
|
|
64
|
+
// *does* include it (since it's only used for typing), we cannot type options
|
|
65
|
+
// as TOptions directly since it may not actually include knownValues at
|
|
66
|
+
// runtime, making schema.options.knownValues potentially undefined even when
|
|
67
|
+
// TOptions includes it.
|
|
68
|
+
readonly options: StringSchemaOptions
|
|
69
|
+
|
|
70
|
+
constructor(options: TOptions) {
|
|
51
71
|
super()
|
|
72
|
+
this.options = options
|
|
52
73
|
}
|
|
53
74
|
|
|
54
75
|
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
55
76
|
const str = coerceToString(input)
|
|
56
77
|
if (str == null) {
|
|
57
|
-
return ctx.
|
|
78
|
+
return ctx.issueUnexpectedType(input, 'string')
|
|
58
79
|
}
|
|
59
80
|
|
|
60
81
|
let lazyUtf8Len: number
|
|
61
82
|
|
|
62
|
-
const minLength = this.options
|
|
83
|
+
const minLength = this.options.minLength
|
|
63
84
|
if (minLength != null) {
|
|
64
85
|
if ((lazyUtf8Len ??= utf8Len(str)) < minLength) {
|
|
65
86
|
return ctx.issueTooSmall(str, 'string', minLength, lazyUtf8Len)
|
|
66
87
|
}
|
|
67
88
|
}
|
|
68
89
|
|
|
69
|
-
const maxLength = this.options
|
|
90
|
+
const maxLength = this.options.maxLength
|
|
70
91
|
if (maxLength != null) {
|
|
71
92
|
// Optimization: we can avoid computing the UTF-8 length if the maximum
|
|
72
93
|
// possible length, in bytes, of the input JS string is smaller than the
|
|
@@ -80,7 +101,7 @@ export class StringSchema<
|
|
|
80
101
|
|
|
81
102
|
let lazyGraphLen: number
|
|
82
103
|
|
|
83
|
-
const minGraphemes = this.options
|
|
104
|
+
const minGraphemes = this.options.minGraphemes
|
|
84
105
|
if (minGraphemes != null) {
|
|
85
106
|
// Optimization: avoid counting graphemes if the length check already fails
|
|
86
107
|
if (str.length < minGraphemes) {
|
|
@@ -90,14 +111,14 @@ export class StringSchema<
|
|
|
90
111
|
}
|
|
91
112
|
}
|
|
92
113
|
|
|
93
|
-
const maxGraphemes = this.options
|
|
114
|
+
const maxGraphemes = this.options.maxGraphemes
|
|
94
115
|
if (maxGraphemes != null) {
|
|
95
116
|
if ((lazyGraphLen ??= graphemeLen(str)) > maxGraphemes) {
|
|
96
117
|
return ctx.issueTooBig(str, 'grapheme', maxGraphemes, lazyGraphLen)
|
|
97
118
|
}
|
|
98
119
|
}
|
|
99
120
|
|
|
100
|
-
const format = this.options
|
|
121
|
+
const format = this.options.format
|
|
101
122
|
if (format != null && !isStringFormat(str, format)) {
|
|
102
123
|
return ctx.issueInvalidFormat(str, format)
|
|
103
124
|
}
|
|
@@ -146,6 +167,32 @@ export function coerceToString(input: unknown): string | null {
|
|
|
146
167
|
}
|
|
147
168
|
}
|
|
148
169
|
|
|
170
|
+
function _string(): StringSchema<NonNullable<unknown>>
|
|
171
|
+
function _string<
|
|
172
|
+
// Allow calling `string<{ knownValues: [...] }>()` without passing an options
|
|
173
|
+
// object, since knownValues is only used for typing and has no runtime
|
|
174
|
+
// effect, so it can be safely omitted at runtime.
|
|
175
|
+
const TOptions extends {
|
|
176
|
+
knownValues: StringSchemaOptions['knownValues']
|
|
177
|
+
} & {
|
|
178
|
+
[K in Exclude<
|
|
179
|
+
keyof StringSchemaOptions,
|
|
180
|
+
'knownValues'
|
|
181
|
+
>]?: Restricted<`An options argument is required when using the "${K}" option`>
|
|
182
|
+
},
|
|
183
|
+
>(): StringSchema<
|
|
184
|
+
IfAny<TOptions, any, { knownValues: TOptions['knownValues'] }>
|
|
185
|
+
>
|
|
186
|
+
function _string<const TOptions extends StringSchemaOptions>(
|
|
187
|
+
// If TOptions is explicitly provided (e.g. `string<{ ... }>({ ... })`), we
|
|
188
|
+
// allow the actual options argument to omit the "knownValues" property since
|
|
189
|
+
// it's only used for inferring the type and has no runtime effect.
|
|
190
|
+
options: TOptions | Omit<TOptions, 'knownValues'>,
|
|
191
|
+
): StringSchema<TOptions>
|
|
192
|
+
function _string(options: StringSchemaOptions = {}) {
|
|
193
|
+
return new StringSchema(options)
|
|
194
|
+
}
|
|
195
|
+
|
|
149
196
|
/**
|
|
150
197
|
* Creates a string schema with optional format and length constraints.
|
|
151
198
|
*
|
|
@@ -173,8 +220,4 @@ export function coerceToString(input: unknown): string | null {
|
|
|
173
220
|
* const handleSchema = l.string({ format: 'handle', minLength: 3, maxLength: 253 })
|
|
174
221
|
* ```
|
|
175
222
|
*/
|
|
176
|
-
export const string = /*#__PURE__*/ memoizedOptions(
|
|
177
|
-
const O extends StringSchemaOptions = NonNullable<unknown>,
|
|
178
|
-
>(options?: StringSchemaOptions & O) {
|
|
179
|
-
return new StringSchema<O>(options)
|
|
180
|
-
})
|
|
223
|
+
export const string = /*#__PURE__*/ memoizedOptions(_string)
|
package/src/schema/token.ts
CHANGED
|
@@ -18,6 +18,8 @@ import { $type, NsidString, Schema, ValidationContext } from '../core.js'
|
|
|
18
18
|
export class TokenSchema<
|
|
19
19
|
const TValue extends string = string,
|
|
20
20
|
> extends Schema<TValue> {
|
|
21
|
+
readonly type = 'token' as const
|
|
22
|
+
|
|
21
23
|
constructor(readonly value: TValue) {
|
|
22
24
|
super()
|
|
23
25
|
}
|
|
@@ -34,7 +36,7 @@ export class TokenSchema<
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
if (typeof input !== 'string') {
|
|
37
|
-
return ctx.
|
|
39
|
+
return ctx.issueUnexpectedType(input, 'token')
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
return ctx.issueInvalidValue(input, [this.value])
|
|
@@ -332,6 +332,44 @@ describe('TypedObjectSchema', () => {
|
|
|
332
332
|
})
|
|
333
333
|
})
|
|
334
334
|
|
|
335
|
+
describe('bound $ methods', () => {
|
|
336
|
+
it('$build can be used as a detached function', () => {
|
|
337
|
+
const { $build } = schema
|
|
338
|
+
const result = $build({ text: 'Hello' })
|
|
339
|
+
expect(result).toEqual({
|
|
340
|
+
text: 'Hello',
|
|
341
|
+
$type: 'app.bsky.feed.post',
|
|
342
|
+
})
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
it('$isTypeOf can be used as a detached function', () => {
|
|
346
|
+
const { $isTypeOf } = schema
|
|
347
|
+
expect($isTypeOf({ text: 'Hello' })).toBe(true)
|
|
348
|
+
expect($isTypeOf({ $type: 'app.bsky.feed.post', text: 'Hello' })).toBe(
|
|
349
|
+
true,
|
|
350
|
+
)
|
|
351
|
+
expect($isTypeOf({ $type: 'other.type', text: 'Hello' })).toBe(false)
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
it('$parse can be used as a detached function', () => {
|
|
355
|
+
const { $parse } = schema
|
|
356
|
+
const result = $parse({ text: 'Hello' })
|
|
357
|
+
expect(result).toEqual({ text: 'Hello' })
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
it('$matches can be used as a detached function', () => {
|
|
361
|
+
const { $matches } = schema
|
|
362
|
+
expect($matches({ text: 'Hello' })).toBe(true)
|
|
363
|
+
expect($matches(42)).toBe(false)
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
it('lazy property returns the same function on repeated access', () => {
|
|
367
|
+
const fn1 = schema.$build
|
|
368
|
+
const fn2 = schema.$build
|
|
369
|
+
expect(fn1).toBe(fn2)
|
|
370
|
+
})
|
|
371
|
+
})
|
|
372
|
+
|
|
335
373
|
describe('with complex nested schemas', () => {
|
|
336
374
|
const complexSchema = typedObject(
|
|
337
375
|
'app.bsky.actor.profile',
|
|
@@ -14,6 +14,14 @@ import {
|
|
|
14
14
|
ValidationContext,
|
|
15
15
|
Validator,
|
|
16
16
|
} from '../core.js'
|
|
17
|
+
import { lazyProperty } from '../util/lazy-property.js'
|
|
18
|
+
|
|
19
|
+
export type MaybeTypedObject<
|
|
20
|
+
TType extends $Type,
|
|
21
|
+
TValue extends { $type?: unknown } = { $type?: unknown },
|
|
22
|
+
> = TValue extends { $type?: TType }
|
|
23
|
+
? TValue
|
|
24
|
+
: $TypedMaybe<Exclude<TValue, Unknown$TypedObject>, TType>
|
|
17
25
|
|
|
18
26
|
/**
|
|
19
27
|
* Schema for typed objects in Lexicon unions.
|
|
@@ -40,6 +48,8 @@ export class TypedObjectSchema<
|
|
|
40
48
|
$TypedMaybe<InferInput<TShape>, TType>,
|
|
41
49
|
$TypedMaybe<InferOutput<TShape>, TType>
|
|
42
50
|
> {
|
|
51
|
+
readonly type = 'typedObject' as const
|
|
52
|
+
|
|
43
53
|
constructor(
|
|
44
54
|
readonly $type: TType,
|
|
45
55
|
readonly schema: TShape,
|
|
@@ -47,12 +57,20 @@ export class TypedObjectSchema<
|
|
|
47
57
|
super()
|
|
48
58
|
}
|
|
49
59
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
61
|
+
if (!isPlainObject(input)) {
|
|
62
|
+
return ctx.issueUnexpectedType(input, 'object')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
'$type' in input &&
|
|
67
|
+
input.$type !== undefined &&
|
|
68
|
+
input.$type !== this.$type
|
|
69
|
+
) {
|
|
70
|
+
return ctx.issueInvalidPropertyValue(input, '$type', [this.$type])
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return ctx.validate(input, this.schema)
|
|
56
74
|
}
|
|
57
75
|
|
|
58
76
|
build(
|
|
@@ -64,28 +82,26 @@ export class TypedObjectSchema<
|
|
|
64
82
|
>
|
|
65
83
|
}
|
|
66
84
|
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
isTypeOf<TValue extends Record<string, unknown>>(
|
|
86
|
+
value: TValue,
|
|
87
|
+
): value is MaybeTypedObject<TType, TValue> {
|
|
88
|
+
return value.$type === undefined || value.$type === this.$type
|
|
69
89
|
}
|
|
70
90
|
|
|
71
|
-
|
|
72
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Bound alias for {@link build} for compatibility with generated utilities.
|
|
93
|
+
* @see {@link build}
|
|
94
|
+
*/
|
|
95
|
+
get $build(): typeof this.build {
|
|
96
|
+
return lazyProperty(this, '$build', this.build.bind(this))
|
|
73
97
|
}
|
|
74
98
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
'$type' in input &&
|
|
82
|
-
input.$type !== undefined &&
|
|
83
|
-
input.$type !== this.$type
|
|
84
|
-
) {
|
|
85
|
-
return ctx.issueInvalidPropertyValue(input, '$type', [this.$type])
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return ctx.validate(input, this.schema)
|
|
99
|
+
/**
|
|
100
|
+
* Bound alias for {@link isTypeOf} for compatibility with generated utilities.
|
|
101
|
+
* @see {@link isTypeOf}
|
|
102
|
+
*/
|
|
103
|
+
get $isTypeOf(): typeof this.isTypeOf {
|
|
104
|
+
return lazyProperty(this, '$isTypeOf', this.isTypeOf.bind(this))
|
|
89
105
|
}
|
|
90
106
|
}
|
|
91
107
|
|
package/src/schema/typed-ref.ts
CHANGED
|
@@ -42,6 +42,8 @@ export class TypedUnionSchema<
|
|
|
42
42
|
? InferOutput<TValidators[number]>
|
|
43
43
|
: InferOutput<TValidators[number]> | Unknown$TypedObject
|
|
44
44
|
> {
|
|
45
|
+
readonly type = 'typedUnion' as const
|
|
46
|
+
|
|
45
47
|
constructor(
|
|
46
48
|
protected readonly validators: TValidators,
|
|
47
49
|
public readonly closed: TClosed,
|
|
@@ -67,7 +69,7 @@ export class TypedUnionSchema<
|
|
|
67
69
|
|
|
68
70
|
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
69
71
|
if (!isPlainObject(input) || !('$type' in input)) {
|
|
70
|
-
return ctx.
|
|
72
|
+
return ctx.issueUnexpectedType(input, '$typed')
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
const { $type } = input
|
package/src/schema/union.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
InferInput,
|
|
3
3
|
InferOutput,
|
|
4
|
+
LexValidationError,
|
|
4
5
|
Schema,
|
|
5
6
|
ValidationContext,
|
|
6
|
-
ValidationError,
|
|
7
7
|
ValidationFailure,
|
|
8
8
|
Validator,
|
|
9
9
|
} from '../core.js'
|
|
@@ -37,6 +37,8 @@ export class UnionSchema<
|
|
|
37
37
|
InferInput<TValidators[number]>,
|
|
38
38
|
InferOutput<TValidators[number]>
|
|
39
39
|
> {
|
|
40
|
+
readonly type = 'union' as const
|
|
41
|
+
|
|
40
42
|
constructor(protected readonly validators: TValidators) {
|
|
41
43
|
super()
|
|
42
44
|
}
|
|
@@ -51,7 +53,7 @@ export class UnionSchema<
|
|
|
51
53
|
failures.push(result)
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
return ctx.failure(
|
|
56
|
+
return ctx.failure(LexValidationError.fromFailures(failures))
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
|
package/src/schema/unknown.ts
CHANGED
|
@@ -15,6 +15,8 @@ import { memoizedOptions } from '../util/memoize.js'
|
|
|
15
15
|
* ```
|
|
16
16
|
*/
|
|
17
17
|
export class UnknownSchema extends Schema<unknown> {
|
|
18
|
+
readonly type = 'unknown' as const
|
|
19
|
+
|
|
18
20
|
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
19
21
|
return ctx.success(input)
|
|
20
22
|
}
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
export class WithDefaultSchema<
|
|
26
26
|
const TValidator extends Validator,
|
|
27
27
|
> extends Schema<InferInput<TValidator>, InferOutput<TValidator>> {
|
|
28
|
+
readonly type = 'withDefault' as const
|
|
29
|
+
|
|
28
30
|
constructor(
|
|
29
31
|
readonly validator: TValidator,
|
|
30
32
|
readonly defaultValue: InferInput<TValidator>,
|
package/src/schema.ts
CHANGED
|
@@ -7,13 +7,14 @@ export * from './schema/cid.js'
|
|
|
7
7
|
export * from './schema/dict.js'
|
|
8
8
|
export * from './schema/enum.js'
|
|
9
9
|
export * from './schema/integer.js'
|
|
10
|
+
export * from './schema/lex-map.js'
|
|
11
|
+
export * from './schema/lex-value.js'
|
|
10
12
|
export * from './schema/literal.js'
|
|
11
13
|
export * from './schema/never.js'
|
|
12
14
|
export * from './schema/null.js'
|
|
13
15
|
export * from './schema/object.js'
|
|
14
16
|
export * from './schema/regexp.js'
|
|
15
17
|
export * from './schema/string.js'
|
|
16
|
-
export * from './schema/unknown-object.js'
|
|
17
18
|
export * from './schema/unknown.js'
|
|
18
19
|
|
|
19
20
|
// Composite Types
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { LexMap } from '@atproto/lex-data';
|
|
2
|
-
import { Schema, ValidationContext } from '../core.js';
|
|
3
|
-
/**
|
|
4
|
-
* Type alias for a plain object with unknown values.
|
|
5
|
-
*/
|
|
6
|
-
export type UnknownObject = LexMap;
|
|
7
|
-
/**
|
|
8
|
-
* Schema that accepts any plain object without validating its properties.
|
|
9
|
-
*
|
|
10
|
-
* Validates that the input is a plain object (not an array, Date, or other
|
|
11
|
-
* special object type), but does not validate the object's properties.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```ts
|
|
15
|
-
* const schema = new UnknownObjectSchema()
|
|
16
|
-
* schema.validate({ any: 'props' }) // success
|
|
17
|
-
* schema.validate([1, 2, 3]) // fails - arrays not accepted
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
export declare class UnknownObjectSchema extends Schema<UnknownObject> {
|
|
21
|
-
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").ValidationResult<LexMap>;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Creates a schema that accepts any plain object.
|
|
25
|
-
*
|
|
26
|
-
* Unlike `l.unknown()` which accepts any value, this validates that the input
|
|
27
|
-
* is specifically a plain object (not an array, null, or primitive).
|
|
28
|
-
*
|
|
29
|
-
* @returns A new {@link UnknownObjectSchema} instance
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```ts
|
|
33
|
-
* // Accept any object shape
|
|
34
|
-
* const metadataSchema = l.unknownObject()
|
|
35
|
-
*
|
|
36
|
-
* metadataSchema.parse({ foo: 1, bar: 'baz' }) // success
|
|
37
|
-
* metadataSchema.parse([1, 2, 3]) // throws - not a plain object
|
|
38
|
-
* metadataSchema.parse(null) // throws
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
export declare const unknownObject: () => UnknownObjectSchema;
|
|
42
|
-
//# sourceMappingURL=unknown-object.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"unknown-object.d.ts","sourceRoot":"","sources":["../../src/schema/unknown-object.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAY,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGtD;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAA;AAElC;;;;;;;;;;;;GAYG;AACH,qBAAa,mBAAoB,SAAQ,MAAM,CAAC,aAAa,CAAC;IAC5D,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;CAOzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,aAAa,2BAExB,CAAA"}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.unknownObject = exports.UnknownObjectSchema = void 0;
|
|
4
|
-
const lex_data_1 = require("@atproto/lex-data");
|
|
5
|
-
const core_js_1 = require("../core.js");
|
|
6
|
-
const memoize_js_1 = require("../util/memoize.js");
|
|
7
|
-
/**
|
|
8
|
-
* Schema that accepts any plain object without validating its properties.
|
|
9
|
-
*
|
|
10
|
-
* Validates that the input is a plain object (not an array, Date, or other
|
|
11
|
-
* special object type), but does not validate the object's properties.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```ts
|
|
15
|
-
* const schema = new UnknownObjectSchema()
|
|
16
|
-
* schema.validate({ any: 'props' }) // success
|
|
17
|
-
* schema.validate([1, 2, 3]) // fails - arrays not accepted
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
class UnknownObjectSchema extends core_js_1.Schema {
|
|
21
|
-
validateInContext(input, ctx) {
|
|
22
|
-
if ((0, lex_data_1.isLexMap)(input)) {
|
|
23
|
-
return ctx.success(input);
|
|
24
|
-
}
|
|
25
|
-
return ctx.issueInvalidType(input, 'unknown');
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
exports.UnknownObjectSchema = UnknownObjectSchema;
|
|
29
|
-
/**
|
|
30
|
-
* Creates a schema that accepts any plain object.
|
|
31
|
-
*
|
|
32
|
-
* Unlike `l.unknown()` which accepts any value, this validates that the input
|
|
33
|
-
* is specifically a plain object (not an array, null, or primitive).
|
|
34
|
-
*
|
|
35
|
-
* @returns A new {@link UnknownObjectSchema} instance
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* ```ts
|
|
39
|
-
* // Accept any object shape
|
|
40
|
-
* const metadataSchema = l.unknownObject()
|
|
41
|
-
*
|
|
42
|
-
* metadataSchema.parse({ foo: 1, bar: 'baz' }) // success
|
|
43
|
-
* metadataSchema.parse([1, 2, 3]) // throws - not a plain object
|
|
44
|
-
* metadataSchema.parse(null) // throws
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
exports.unknownObject = (0, memoize_js_1.memoizedOptions)(function () {
|
|
48
|
-
return new UnknownObjectSchema();
|
|
49
|
-
});
|
|
50
|
-
//# sourceMappingURL=unknown-object.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"unknown-object.js","sourceRoot":"","sources":["../../src/schema/unknown-object.ts"],"names":[],"mappings":";;;AAAA,gDAAoD;AACpD,wCAAsD;AACtD,mDAAoD;AAOpD;;;;;;;;;;;;GAYG;AACH,MAAa,mBAAoB,SAAQ,gBAAqB;IAC5D,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,IAAI,IAAA,mBAAQ,EAAC,KAAK,CAAC,EAAE,CAAC;YACpB,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAC3B,CAAC;QAED,OAAO,GAAG,CAAC,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;IAC/C,CAAC;CACF;AARD,kDAQC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACU,QAAA,aAAa,GAAiB,IAAA,4BAAe,EAAC;IACzD,OAAO,IAAI,mBAAmB,EAAE,CAAA;AAClC,CAAC,CAAC,CAAA","sourcesContent":["import { LexMap, isLexMap } from '@atproto/lex-data'\nimport { Schema, ValidationContext } from '../core.js'\nimport { memoizedOptions } from '../util/memoize.js'\n\n/**\n * Type alias for a plain object with unknown values.\n */\nexport type UnknownObject = LexMap\n\n/**\n * Schema that accepts any plain object without validating its properties.\n *\n * Validates that the input is a plain object (not an array, Date, or other\n * special object type), but does not validate the object's properties.\n *\n * @example\n * ```ts\n * const schema = new UnknownObjectSchema()\n * schema.validate({ any: 'props' }) // success\n * schema.validate([1, 2, 3]) // fails - arrays not accepted\n * ```\n */\nexport class UnknownObjectSchema extends Schema<UnknownObject> {\n validateInContext(input: unknown, ctx: ValidationContext) {\n if (isLexMap(input)) {\n return ctx.success(input)\n }\n\n return ctx.issueInvalidType(input, 'unknown')\n }\n}\n\n/**\n * Creates a schema that accepts any plain object.\n *\n * Unlike `l.unknown()` which accepts any value, this validates that the input\n * is specifically a plain object (not an array, null, or primitive).\n *\n * @returns A new {@link UnknownObjectSchema} instance\n *\n * @example\n * ```ts\n * // Accept any object shape\n * const metadataSchema = l.unknownObject()\n *\n * metadataSchema.parse({ foo: 1, bar: 'baz' }) // success\n * metadataSchema.parse([1, 2, 3]) // throws - not a plain object\n * metadataSchema.parse(null) // throws\n * ```\n */\nexport const unknownObject = /*#__PURE__*/ memoizedOptions(function () {\n return new UnknownObjectSchema()\n})\n"]}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { LexMap, isLexMap } from '@atproto/lex-data'
|
|
2
|
-
import { Schema, ValidationContext } from '../core.js'
|
|
3
|
-
import { memoizedOptions } from '../util/memoize.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Type alias for a plain object with unknown values.
|
|
7
|
-
*/
|
|
8
|
-
export type UnknownObject = LexMap
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Schema that accepts any plain object without validating its properties.
|
|
12
|
-
*
|
|
13
|
-
* Validates that the input is a plain object (not an array, Date, or other
|
|
14
|
-
* special object type), but does not validate the object's properties.
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```ts
|
|
18
|
-
* const schema = new UnknownObjectSchema()
|
|
19
|
-
* schema.validate({ any: 'props' }) // success
|
|
20
|
-
* schema.validate([1, 2, 3]) // fails - arrays not accepted
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
export class UnknownObjectSchema extends Schema<UnknownObject> {
|
|
24
|
-
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
25
|
-
if (isLexMap(input)) {
|
|
26
|
-
return ctx.success(input)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return ctx.issueInvalidType(input, 'unknown')
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Creates a schema that accepts any plain object.
|
|
35
|
-
*
|
|
36
|
-
* Unlike `l.unknown()` which accepts any value, this validates that the input
|
|
37
|
-
* is specifically a plain object (not an array, null, or primitive).
|
|
38
|
-
*
|
|
39
|
-
* @returns A new {@link UnknownObjectSchema} instance
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```ts
|
|
43
|
-
* // Accept any object shape
|
|
44
|
-
* const metadataSchema = l.unknownObject()
|
|
45
|
-
*
|
|
46
|
-
* metadataSchema.parse({ foo: 1, bar: 'baz' }) // success
|
|
47
|
-
* metadataSchema.parse([1, 2, 3]) // throws - not a plain object
|
|
48
|
-
* metadataSchema.parse(null) // throws
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
export const unknownObject = /*#__PURE__*/ memoizedOptions(function () {
|
|
52
|
-
return new UnknownObjectSchema()
|
|
53
|
-
})
|