@atproto/lexicon 0.7.3 → 0.7.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 +16 -0
- package/dist/lexicons.d.ts +1 -1
- package/dist/lexicons.d.ts.map +1 -1
- package/dist/lexicons.js.map +1 -1
- package/dist/serialize.d.ts +1 -1
- package/dist/serialize.d.ts.map +1 -1
- package/dist/serialize.js +8 -7
- package/dist/serialize.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +1 -0
- package/dist/validation.js.map +1 -1
- package/dist/validators/blob.d.ts +1 -1
- package/dist/validators/blob.d.ts.map +1 -1
- package/dist/validators/blob.js +1 -0
- package/dist/validators/blob.js.map +1 -1
- package/dist/validators/complex.d.ts +1 -1
- package/dist/validators/complex.d.ts.map +1 -1
- package/dist/validators/complex.js +2 -1
- package/dist/validators/complex.js.map +1 -1
- package/dist/validators/formats.d.ts +1 -1
- package/dist/validators/formats.d.ts.map +1 -1
- package/dist/validators/formats.js.map +1 -1
- package/dist/validators/primitives.d.ts +1 -1
- package/dist/validators/primitives.d.ts.map +1 -1
- package/dist/validators/primitives.js +2 -1
- package/dist/validators/primitives.js.map +1 -1
- package/dist/validators/xrpc.d.ts +1 -1
- package/dist/validators/xrpc.d.ts.map +1 -1
- package/dist/validators/xrpc.js +3 -2
- package/dist/validators/xrpc.js.map +1 -1
- package/package.json +18 -13
- package/jest.config.cjs +0 -21
- package/src/blob-refs.ts +0 -65
- package/src/index.ts +0 -4
- package/src/lexicons.ts +0 -253
- package/src/serialize.ts +0 -102
- package/src/types.ts +0 -483
- package/src/util.ts +0 -58
- package/src/validation.ts +0 -84
- package/src/validators/blob.ts +0 -19
- package/src/validators/complex.ts +0 -212
- package/src/validators/formats.ts +0 -72
- package/src/validators/primitives.ts +0 -416
- package/src/validators/xrpc.ts +0 -53
- package/tests/_scaffolds/lexicons.ts +0 -545
- package/tests/general.test.ts +0 -1243
- package/tsconfig.build.json +0 -8
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -7
package/src/validation.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { Lexicons } from './lexicons.js'
|
|
2
|
-
import {
|
|
3
|
-
LexRecord,
|
|
4
|
-
LexRefVariant,
|
|
5
|
-
LexUserType,
|
|
6
|
-
LexXrpcProcedure,
|
|
7
|
-
LexXrpcQuery,
|
|
8
|
-
LexXrpcSubscription,
|
|
9
|
-
} from './types.js'
|
|
10
|
-
import { object, validateOneOf } from './validators/complex.js'
|
|
11
|
-
import { params } from './validators/xrpc.js'
|
|
12
|
-
|
|
13
|
-
export function assertValidRecord(
|
|
14
|
-
lexicons: Lexicons,
|
|
15
|
-
def: LexRecord,
|
|
16
|
-
value: unknown,
|
|
17
|
-
) {
|
|
18
|
-
const res = object(lexicons, 'Record', def.record, value)
|
|
19
|
-
if (!res.success) throw res.error
|
|
20
|
-
return res.value
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function assertValidXrpcParams(
|
|
24
|
-
lexicons: Lexicons,
|
|
25
|
-
def: LexXrpcProcedure | LexXrpcQuery | LexXrpcSubscription,
|
|
26
|
-
value: unknown,
|
|
27
|
-
) {
|
|
28
|
-
if (def.parameters) {
|
|
29
|
-
const res = params(lexicons, 'Params', def.parameters, value)
|
|
30
|
-
if (!res.success) throw res.error
|
|
31
|
-
return res.value
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function assertValidXrpcInput(
|
|
36
|
-
lexicons: Lexicons,
|
|
37
|
-
def: LexXrpcProcedure,
|
|
38
|
-
value: unknown,
|
|
39
|
-
) {
|
|
40
|
-
if (def.input?.schema) {
|
|
41
|
-
// loop: all input schema definitions
|
|
42
|
-
return assertValidOneOf(lexicons, 'Input', def.input.schema, value, true)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function assertValidXrpcOutput(
|
|
47
|
-
lexicons: Lexicons,
|
|
48
|
-
def: LexXrpcProcedure | LexXrpcQuery,
|
|
49
|
-
value: unknown,
|
|
50
|
-
) {
|
|
51
|
-
if (def.output?.schema) {
|
|
52
|
-
// loop: all output schema definitions
|
|
53
|
-
return assertValidOneOf(lexicons, 'Output', def.output.schema, value, true)
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function assertValidXrpcMessage(
|
|
58
|
-
lexicons: Lexicons,
|
|
59
|
-
def: LexXrpcSubscription,
|
|
60
|
-
value: unknown,
|
|
61
|
-
) {
|
|
62
|
-
if (def.message?.schema) {
|
|
63
|
-
// loop: all output schema definitions
|
|
64
|
-
return assertValidOneOf(
|
|
65
|
-
lexicons,
|
|
66
|
-
'Message',
|
|
67
|
-
def.message.schema,
|
|
68
|
-
value,
|
|
69
|
-
true,
|
|
70
|
-
)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function assertValidOneOf(
|
|
75
|
-
lexicons: Lexicons,
|
|
76
|
-
path: string,
|
|
77
|
-
def: LexRefVariant | LexUserType,
|
|
78
|
-
value: unknown,
|
|
79
|
-
mustBeObj = false,
|
|
80
|
-
) {
|
|
81
|
-
const res = validateOneOf(lexicons, path, def, value, mustBeObj)
|
|
82
|
-
if (!res.success) throw res.error
|
|
83
|
-
return res.value
|
|
84
|
-
}
|
package/src/validators/blob.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { BlobRef } from '../blob-refs.js'
|
|
2
|
-
import { Lexicons } from '../lexicons.js'
|
|
3
|
-
import { LexUserType, ValidationError, ValidationResult } from '../types.js'
|
|
4
|
-
|
|
5
|
-
export function blob(
|
|
6
|
-
lexicons: Lexicons,
|
|
7
|
-
path: string,
|
|
8
|
-
def: LexUserType,
|
|
9
|
-
value: unknown,
|
|
10
|
-
): ValidationResult {
|
|
11
|
-
// check
|
|
12
|
-
if (!value || !(value instanceof BlobRef)) {
|
|
13
|
-
return {
|
|
14
|
-
success: false,
|
|
15
|
-
error: new ValidationError(`${path} should be a blob ref`),
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return { success: true, value }
|
|
19
|
-
}
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import { Lexicons } from '../lexicons.js'
|
|
2
|
-
import {
|
|
3
|
-
LexArray,
|
|
4
|
-
LexRefVariant,
|
|
5
|
-
LexUserType,
|
|
6
|
-
ValidationError,
|
|
7
|
-
ValidationResult,
|
|
8
|
-
isDiscriminatedObject,
|
|
9
|
-
isObj,
|
|
10
|
-
} from '../types.js'
|
|
11
|
-
import { toLexUri } from '../util.js'
|
|
12
|
-
import { blob } from './blob.js'
|
|
13
|
-
import { validate as validatePrimitive } from './primitives.js'
|
|
14
|
-
|
|
15
|
-
export function validate(
|
|
16
|
-
lexicons: Lexicons,
|
|
17
|
-
path: string,
|
|
18
|
-
def: LexUserType,
|
|
19
|
-
value: unknown,
|
|
20
|
-
): ValidationResult {
|
|
21
|
-
switch (def.type) {
|
|
22
|
-
case 'object':
|
|
23
|
-
return object(lexicons, path, def, value)
|
|
24
|
-
case 'array':
|
|
25
|
-
return array(lexicons, path, def, value)
|
|
26
|
-
case 'blob':
|
|
27
|
-
return blob(lexicons, path, def, value)
|
|
28
|
-
default:
|
|
29
|
-
return validatePrimitive(lexicons, path, def, value)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function array(
|
|
34
|
-
lexicons: Lexicons,
|
|
35
|
-
path: string,
|
|
36
|
-
def: LexArray,
|
|
37
|
-
value: unknown,
|
|
38
|
-
): ValidationResult {
|
|
39
|
-
// type
|
|
40
|
-
if (!Array.isArray(value)) {
|
|
41
|
-
return {
|
|
42
|
-
success: false,
|
|
43
|
-
error: new ValidationError(`${path} must be an array`),
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// maxLength
|
|
48
|
-
if (typeof def.maxLength === 'number') {
|
|
49
|
-
if ((value as Array<unknown>).length > def.maxLength) {
|
|
50
|
-
return {
|
|
51
|
-
success: false,
|
|
52
|
-
error: new ValidationError(
|
|
53
|
-
`${path} must not have more than ${def.maxLength} elements`,
|
|
54
|
-
),
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// minLength
|
|
60
|
-
if (typeof def.minLength === 'number') {
|
|
61
|
-
if ((value as Array<unknown>).length < def.minLength) {
|
|
62
|
-
return {
|
|
63
|
-
success: false,
|
|
64
|
-
error: new ValidationError(
|
|
65
|
-
`${path} must not have fewer than ${def.minLength} elements`,
|
|
66
|
-
),
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// items
|
|
72
|
-
const itemsDef = def.items
|
|
73
|
-
for (let i = 0; i < (value as Array<unknown>).length; i++) {
|
|
74
|
-
const itemValue = value[i]
|
|
75
|
-
const itemPath = `${path}/${i}`
|
|
76
|
-
const res = validateOneOf(lexicons, itemPath, itemsDef, itemValue)
|
|
77
|
-
if (!res.success) {
|
|
78
|
-
return res
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return { success: true, value }
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function object(
|
|
86
|
-
lexicons: Lexicons,
|
|
87
|
-
path: string,
|
|
88
|
-
def: LexUserType,
|
|
89
|
-
value: unknown,
|
|
90
|
-
): ValidationResult {
|
|
91
|
-
// type
|
|
92
|
-
if (!isObj(value)) {
|
|
93
|
-
return {
|
|
94
|
-
success: false,
|
|
95
|
-
error: new ValidationError(`${path} must be an object`),
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// properties
|
|
100
|
-
let resultValue = value
|
|
101
|
-
if ('properties' in def && def.properties != null) {
|
|
102
|
-
for (const key in def.properties) {
|
|
103
|
-
const keyValue = value[key]
|
|
104
|
-
if (keyValue === null && def.nullable?.includes(key)) {
|
|
105
|
-
continue
|
|
106
|
-
}
|
|
107
|
-
const propDef = def.properties[key]
|
|
108
|
-
if (keyValue === undefined && !def.required?.includes(key)) {
|
|
109
|
-
// Fast path for non-required undefined props.
|
|
110
|
-
if (
|
|
111
|
-
propDef.type === 'integer' ||
|
|
112
|
-
propDef.type === 'boolean' ||
|
|
113
|
-
propDef.type === 'string'
|
|
114
|
-
) {
|
|
115
|
-
if (propDef.default === undefined) {
|
|
116
|
-
continue
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
// Other types have no defaults.
|
|
120
|
-
continue
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
const propPath = `${path}/${key}`
|
|
124
|
-
const validated = validateOneOf(lexicons, propPath, propDef, keyValue)
|
|
125
|
-
const propValue = validated.success ? validated.value : keyValue
|
|
126
|
-
|
|
127
|
-
// Return error for bad validation, giving required rule precedence
|
|
128
|
-
if (propValue === undefined) {
|
|
129
|
-
if (def.required?.includes(key)) {
|
|
130
|
-
return {
|
|
131
|
-
success: false,
|
|
132
|
-
error: new ValidationError(
|
|
133
|
-
`${path} must have the property "${key}"`,
|
|
134
|
-
),
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
} else {
|
|
138
|
-
if (!validated.success) {
|
|
139
|
-
return validated
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Adjust value based on e.g. applied defaults, cloning shallowly if there was a changed value
|
|
144
|
-
if (propValue !== keyValue) {
|
|
145
|
-
if (resultValue === value) {
|
|
146
|
-
// Lazy shallow clone
|
|
147
|
-
resultValue = { ...value }
|
|
148
|
-
}
|
|
149
|
-
resultValue[key] = propValue
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return { success: true, value: resultValue }
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export function validateOneOf(
|
|
158
|
-
lexicons: Lexicons,
|
|
159
|
-
path: string,
|
|
160
|
-
def: LexRefVariant | LexUserType,
|
|
161
|
-
value: unknown,
|
|
162
|
-
mustBeObj = false, // this is the only type constraint we need currently (used by xrpc body schema validators)
|
|
163
|
-
): ValidationResult {
|
|
164
|
-
let concreteDef: LexUserType
|
|
165
|
-
|
|
166
|
-
if (def.type === 'union') {
|
|
167
|
-
if (!isDiscriminatedObject(value)) {
|
|
168
|
-
return {
|
|
169
|
-
success: false,
|
|
170
|
-
error: new ValidationError(
|
|
171
|
-
`${path} must be an object which includes the "$type" property`,
|
|
172
|
-
),
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
if (!refsContainType(def.refs, value.$type)) {
|
|
176
|
-
if (def.closed) {
|
|
177
|
-
return {
|
|
178
|
-
success: false,
|
|
179
|
-
error: new ValidationError(
|
|
180
|
-
`${path} $type must be one of ${def.refs.join(', ')}`,
|
|
181
|
-
),
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return { success: true, value }
|
|
185
|
-
} else {
|
|
186
|
-
concreteDef = lexicons.getDefOrThrow(value.$type)
|
|
187
|
-
}
|
|
188
|
-
} else if (def.type === 'ref') {
|
|
189
|
-
concreteDef = lexicons.getDefOrThrow(def.ref)
|
|
190
|
-
} else {
|
|
191
|
-
concreteDef = def
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return mustBeObj
|
|
195
|
-
? object(lexicons, path, concreteDef, value)
|
|
196
|
-
: validate(lexicons, path, concreteDef, value)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// to avoid bugs like #0189 this needs to handle both
|
|
200
|
-
// explicit and implicit #main
|
|
201
|
-
const refsContainType = (refs: string[], type: string) => {
|
|
202
|
-
const lexUri = toLexUri(type)
|
|
203
|
-
if (refs.includes(lexUri)) {
|
|
204
|
-
return true
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (lexUri.endsWith('#main')) {
|
|
208
|
-
return refs.includes(lexUri.slice(0, -5))
|
|
209
|
-
} else {
|
|
210
|
-
return !lexUri.includes('#') && refs.includes(`${lexUri}#main`)
|
|
211
|
-
}
|
|
212
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { CID } from 'multiformats/cid'
|
|
2
|
-
import {
|
|
3
|
-
isAtIdentifierString,
|
|
4
|
-
isAtUriString,
|
|
5
|
-
isDatetimeStringLenient,
|
|
6
|
-
isValidDid,
|
|
7
|
-
isValidHandle,
|
|
8
|
-
isValidLanguage,
|
|
9
|
-
isValidNsid,
|
|
10
|
-
isValidRecordKey,
|
|
11
|
-
isValidTid,
|
|
12
|
-
isValidUri,
|
|
13
|
-
} from '@atproto/syntax'
|
|
14
|
-
import { ValidationError, ValidationResult } from '../types.js'
|
|
15
|
-
|
|
16
|
-
export const datetime = createValidator(
|
|
17
|
-
isDatetimeStringLenient,
|
|
18
|
-
'must be an valid atproto datetime (both RFC-3339 and ISO-8601)',
|
|
19
|
-
)
|
|
20
|
-
export const uri = createValidator(isValidUri, 'must be a uri')
|
|
21
|
-
export const atUri = createValidator(isAtUriString, 'must be a valid at-uri')
|
|
22
|
-
export const did = createValidator(isValidDid, 'must be a valid did')
|
|
23
|
-
export const handle = createValidator(isValidHandle, 'must be a valid handle')
|
|
24
|
-
export const atIdentifier = createValidator(
|
|
25
|
-
isAtIdentifierString,
|
|
26
|
-
'must be a valid did or a handle',
|
|
27
|
-
)
|
|
28
|
-
export const nsid = createValidator(isValidNsid, 'must be a valid nsid')
|
|
29
|
-
export const cid = createValidator(isCidString, 'must be a cid string')
|
|
30
|
-
export const language = createValidator(
|
|
31
|
-
isValidLanguage,
|
|
32
|
-
'must be a well-formed BCP 47 language tag',
|
|
33
|
-
)
|
|
34
|
-
export const tid = createValidator(isValidTid, 'must be a valid TID')
|
|
35
|
-
export const recordKey = createValidator(
|
|
36
|
-
isValidRecordKey,
|
|
37
|
-
'must be a valid Record Key',
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
// Internal helpers
|
|
41
|
-
|
|
42
|
-
function createValidator<T extends string>(
|
|
43
|
-
assertionFn: (value: string) => value is T,
|
|
44
|
-
errorMessage: string,
|
|
45
|
-
): <V extends string>(path: string, value: V) => ValidationResult<V & T>
|
|
46
|
-
function createValidator(
|
|
47
|
-
assertionFn: (value: string) => boolean,
|
|
48
|
-
errorMessage: string,
|
|
49
|
-
): (path: string, value: string) => ValidationResult<string>
|
|
50
|
-
function createValidator(
|
|
51
|
-
assertionFn: (value: string) => boolean,
|
|
52
|
-
errorMessage: string,
|
|
53
|
-
) {
|
|
54
|
-
return (path: string, value: string): ValidationResult => {
|
|
55
|
-
if (assertionFn(value)) {
|
|
56
|
-
return { success: true, value }
|
|
57
|
-
}
|
|
58
|
-
return {
|
|
59
|
-
success: false,
|
|
60
|
-
error: new ValidationError(`${path} ${errorMessage}`),
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function isCidString(v: string): v is string {
|
|
66
|
-
try {
|
|
67
|
-
CID.parse(v)
|
|
68
|
-
return true
|
|
69
|
-
} catch {
|
|
70
|
-
return false
|
|
71
|
-
}
|
|
72
|
-
}
|