@atproto/lexicon 0.0.4 → 0.1.0
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/dist/blob-refs.d.ts +67 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3072 -392
- package/dist/index.js.map +4 -4
- package/dist/lexicons.d.ts +6 -4
- package/dist/serialize.d.ts +12 -0
- package/dist/types.d.ts +24498 -15979
- package/dist/util.d.ts +1 -1
- package/dist/validation.d.ts +6 -5
- package/dist/validators/blob.d.ts +0 -3
- package/dist/validators/complex.d.ts +2 -2
- package/dist/validators/formats.d.ts +9 -0
- package/dist/validators/primitives.d.ts +3 -2
- package/dist/validators/xrpc.d.ts +1 -1
- package/package.json +5 -1
- package/src/blob-refs.ts +70 -0
- package/src/index.ts +2 -0
- package/src/lexicons.ts +36 -5
- package/src/serialize.ts +93 -0
- package/src/types.ts +86 -58
- package/src/util.ts +3 -4
- package/src/validation.ts +28 -4
- package/src/validators/blob.ts +5 -43
- package/src/validators/complex.ts +33 -32
- package/src/validators/formats.ts +107 -0
- package/src/validators/primitives.ts +116 -41
- package/src/validators/xrpc.ts +29 -29
- package/tests/_scaffolds/lexicons.ts +198 -23
- package/tests/general.test.ts +365 -154
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.json +3 -1
package/src/validation.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { Lexicons } from './lexicons'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
LexRecord,
|
|
4
|
+
LexXrpcProcedure,
|
|
5
|
+
LexXrpcQuery,
|
|
6
|
+
LexXrpcSubscription,
|
|
7
|
+
} from './types'
|
|
3
8
|
import { assertValidOneOf } from './util'
|
|
4
9
|
|
|
5
10
|
import * as ComplexValidators from './validators/complex'
|
|
@@ -12,16 +17,18 @@ export function assertValidRecord(
|
|
|
12
17
|
) {
|
|
13
18
|
const res = ComplexValidators.object(lexicons, 'Record', def.record, value)
|
|
14
19
|
if (!res.success) throw res.error
|
|
20
|
+
return res.value
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
export function assertValidXrpcParams(
|
|
18
24
|
lexicons: Lexicons,
|
|
19
|
-
def: LexXrpcProcedure | LexXrpcQuery,
|
|
25
|
+
def: LexXrpcProcedure | LexXrpcQuery | LexXrpcSubscription,
|
|
20
26
|
value: unknown,
|
|
21
27
|
) {
|
|
22
28
|
if (def.parameters) {
|
|
23
29
|
const res = XrpcValidators.params(lexicons, 'Params', def.parameters, value)
|
|
24
30
|
if (!res.success) throw res.error
|
|
31
|
+
return res.value
|
|
25
32
|
}
|
|
26
33
|
}
|
|
27
34
|
|
|
@@ -32,7 +39,7 @@ export function assertValidXrpcInput(
|
|
|
32
39
|
) {
|
|
33
40
|
if (def.input?.schema) {
|
|
34
41
|
// loop: all input schema definitions
|
|
35
|
-
assertValidOneOf(lexicons, 'Input', def.input.schema, value, true)
|
|
42
|
+
return assertValidOneOf(lexicons, 'Input', def.input.schema, value, true)
|
|
36
43
|
}
|
|
37
44
|
}
|
|
38
45
|
|
|
@@ -43,6 +50,23 @@ export function assertValidXrpcOutput(
|
|
|
43
50
|
) {
|
|
44
51
|
if (def.output?.schema) {
|
|
45
52
|
// loop: all output schema definitions
|
|
46
|
-
assertValidOneOf(lexicons, 'Output', def.output.schema, value, true)
|
|
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
|
+
)
|
|
47
71
|
}
|
|
48
72
|
}
|
package/src/validators/blob.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { BlobRef } from '../blob-refs'
|
|
1
2
|
import { Lexicons } from '../lexicons'
|
|
2
3
|
import { LexUserType, ValidationResult, ValidationError } from '../types'
|
|
3
|
-
import { isObj, hasProp } from '../types'
|
|
4
4
|
|
|
5
5
|
export function blob(
|
|
6
6
|
lexicons: Lexicons,
|
|
@@ -8,50 +8,12 @@ export function blob(
|
|
|
8
8
|
def: LexUserType,
|
|
9
9
|
value: unknown,
|
|
10
10
|
): ValidationResult {
|
|
11
|
-
|
|
11
|
+
// check
|
|
12
|
+
if (!value || !(value instanceof BlobRef)) {
|
|
12
13
|
return {
|
|
13
14
|
success: false,
|
|
14
|
-
error: new ValidationError(`${path} should be
|
|
15
|
+
error: new ValidationError(`${path} should be a blob ref`),
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
success: false,
|
|
20
|
-
error: new ValidationError(`${path}/cid should be a string`),
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
if (!hasProp(value, 'mimeType') || typeof value.mimeType !== 'string') {
|
|
24
|
-
return {
|
|
25
|
-
success: false,
|
|
26
|
-
error: new ValidationError(`${path}/mimeType should be a string`),
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return { success: true }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function image(
|
|
33
|
-
lexicons: Lexicons,
|
|
34
|
-
path: string,
|
|
35
|
-
def: LexUserType,
|
|
36
|
-
value: unknown,
|
|
37
|
-
): ValidationResult {
|
|
38
|
-
return blob(lexicons, path, def, value)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function video(
|
|
42
|
-
lexicons: Lexicons,
|
|
43
|
-
path: string,
|
|
44
|
-
def: LexUserType,
|
|
45
|
-
value: unknown,
|
|
46
|
-
): ValidationResult {
|
|
47
|
-
return blob(lexicons, path, def, value)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function audio(
|
|
51
|
-
lexicons: Lexicons,
|
|
52
|
-
path: string,
|
|
53
|
-
def: LexUserType,
|
|
54
|
-
value: unknown,
|
|
55
|
-
): ValidationResult {
|
|
56
|
-
return blob(lexicons, path, def, value)
|
|
18
|
+
return { success: true, value }
|
|
57
19
|
}
|
|
@@ -20,14 +20,16 @@ export function validate(
|
|
|
20
20
|
switch (def.type) {
|
|
21
21
|
case 'boolean':
|
|
22
22
|
return Primitives.boolean(lexicons, path, def, value)
|
|
23
|
-
case '
|
|
24
|
-
return Primitives.
|
|
23
|
+
case 'float':
|
|
24
|
+
return Primitives.float(lexicons, path, def, value)
|
|
25
25
|
case 'integer':
|
|
26
26
|
return Primitives.integer(lexicons, path, def, value)
|
|
27
27
|
case 'string':
|
|
28
28
|
return Primitives.string(lexicons, path, def, value)
|
|
29
|
-
case '
|
|
30
|
-
return Primitives.
|
|
29
|
+
case 'bytes':
|
|
30
|
+
return Primitives.bytes(lexicons, path, def, value)
|
|
31
|
+
case 'cid-link':
|
|
32
|
+
return Primitives.cidLink(lexicons, path, def, value)
|
|
31
33
|
case 'unknown':
|
|
32
34
|
return Primitives.unknown(lexicons, path, def, value)
|
|
33
35
|
case 'object':
|
|
@@ -36,12 +38,6 @@ export function validate(
|
|
|
36
38
|
return array(lexicons, path, def, value)
|
|
37
39
|
case 'blob':
|
|
38
40
|
return Blob.blob(lexicons, path, def, value)
|
|
39
|
-
case 'image':
|
|
40
|
-
return Blob.image(lexicons, path, def, value)
|
|
41
|
-
case 'video':
|
|
42
|
-
return Blob.video(lexicons, path, def, value)
|
|
43
|
-
case 'audio':
|
|
44
|
-
return Blob.audio(lexicons, path, def, value)
|
|
45
41
|
default:
|
|
46
42
|
return {
|
|
47
43
|
success: false,
|
|
@@ -53,11 +49,9 @@ export function validate(
|
|
|
53
49
|
export function array(
|
|
54
50
|
lexicons: Lexicons,
|
|
55
51
|
path: string,
|
|
56
|
-
def:
|
|
52
|
+
def: LexArray,
|
|
57
53
|
value: unknown,
|
|
58
54
|
): ValidationResult {
|
|
59
|
-
def = def as LexArray
|
|
60
|
-
|
|
61
55
|
// type
|
|
62
56
|
if (!Array.isArray(value)) {
|
|
63
57
|
return {
|
|
@@ -101,7 +95,7 @@ export function array(
|
|
|
101
95
|
}
|
|
102
96
|
}
|
|
103
97
|
|
|
104
|
-
return { success: true }
|
|
98
|
+
return { success: true, value }
|
|
105
99
|
}
|
|
106
100
|
|
|
107
101
|
export function object(
|
|
@@ -120,33 +114,40 @@ export function object(
|
|
|
120
114
|
}
|
|
121
115
|
}
|
|
122
116
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
for (const key of def.required) {
|
|
126
|
-
if (!(key in value)) {
|
|
127
|
-
return {
|
|
128
|
-
success: false,
|
|
129
|
-
error: new ValidationError(`${path} must have the property "${key}"`),
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
117
|
+
const requiredProps = new Set(def.required)
|
|
118
|
+
const nullableProps = new Set(def.nullable)
|
|
134
119
|
|
|
135
120
|
// properties
|
|
121
|
+
let resultValue = value
|
|
136
122
|
if (typeof def.properties === 'object') {
|
|
137
123
|
for (const key in def.properties) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
continue // skip- if required, will have already failed
|
|
124
|
+
if (value[key] === null && nullableProps.has(key)) {
|
|
125
|
+
continue
|
|
141
126
|
}
|
|
142
127
|
const propDef = def.properties[key]
|
|
143
128
|
const propPath = `${path}/${key}`
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
129
|
+
const validated = validateOneOf(lexicons, propPath, propDef, value[key])
|
|
130
|
+
const propValue = validated.success ? validated.value : value[key]
|
|
131
|
+
const propIsUndefined = typeof propValue === 'undefined'
|
|
132
|
+
// Return error for bad validation, giving required rule precedence
|
|
133
|
+
if (propIsUndefined && requiredProps.has(key)) {
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
error: new ValidationError(`${path} must have the property "${key}"`),
|
|
137
|
+
}
|
|
138
|
+
} else if (!propIsUndefined && !validated.success) {
|
|
139
|
+
return validated
|
|
140
|
+
}
|
|
141
|
+
// Adjust value based on e.g. applied defaults, cloning shallowly if there was a changed value
|
|
142
|
+
if (propValue !== value[key]) {
|
|
143
|
+
if (resultValue === value) {
|
|
144
|
+
// Lazy shallow clone
|
|
145
|
+
resultValue = { ...value }
|
|
146
|
+
}
|
|
147
|
+
resultValue[key] = propValue
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
150
|
}
|
|
150
151
|
|
|
151
|
-
return { success: true }
|
|
152
|
+
return { success: true, value: resultValue }
|
|
152
153
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { ensureValidAtUri } from '@atproto/uri'
|
|
2
|
+
import { isValidISODateString } from 'iso-datestring-validator'
|
|
3
|
+
import { CID } from 'multiformats/cid'
|
|
4
|
+
import { ValidationResult, ValidationError } from '../types'
|
|
5
|
+
import { ensureValidDid, ensureValidHandle } from '@atproto/identifier'
|
|
6
|
+
import { ensureValidNsid } from '@atproto/nsid'
|
|
7
|
+
|
|
8
|
+
export function datetime(path: string, value: string): ValidationResult {
|
|
9
|
+
try {
|
|
10
|
+
if (!isValidISODateString(value)) {
|
|
11
|
+
throw new Error()
|
|
12
|
+
}
|
|
13
|
+
} catch {
|
|
14
|
+
return {
|
|
15
|
+
success: false,
|
|
16
|
+
error: new ValidationError(
|
|
17
|
+
`${path} must be an iso8601 formatted datetime`,
|
|
18
|
+
),
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return { success: true, value }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function uri(path: string, value: string): ValidationResult {
|
|
25
|
+
const isUri = value.match(/^\w+:(?:\/\/)?[^\s/][^\s]*$/) !== null
|
|
26
|
+
if (!isUri) {
|
|
27
|
+
return {
|
|
28
|
+
success: false,
|
|
29
|
+
error: new ValidationError(`${path} must be a uri`),
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { success: true, value }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function atUri(path: string, value: string): ValidationResult {
|
|
36
|
+
try {
|
|
37
|
+
ensureValidAtUri(value)
|
|
38
|
+
} catch {
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
error: new ValidationError(`${path} must be a valid at-uri`),
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { success: true, value }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function did(path: string, value: string): ValidationResult {
|
|
48
|
+
try {
|
|
49
|
+
ensureValidDid(value)
|
|
50
|
+
} catch {
|
|
51
|
+
return {
|
|
52
|
+
success: false,
|
|
53
|
+
error: new ValidationError(`${path} must be a valid did`),
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { success: true, value }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function handle(path: string, value: string): ValidationResult {
|
|
60
|
+
try {
|
|
61
|
+
ensureValidHandle(value)
|
|
62
|
+
} catch {
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
error: new ValidationError(`${path} must be a valid handle`),
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { success: true, value }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function atIdentifier(path: string, value: string): ValidationResult {
|
|
72
|
+
const isDid = did(path, value)
|
|
73
|
+
if (!isDid.success) {
|
|
74
|
+
const isHandle = handle(path, value)
|
|
75
|
+
if (!isHandle.success) {
|
|
76
|
+
return {
|
|
77
|
+
success: false,
|
|
78
|
+
error: new ValidationError(`${path} must be a valid did or a handle`),
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return { success: true, value }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function nsid(path: string, value: string): ValidationResult {
|
|
86
|
+
try {
|
|
87
|
+
ensureValidNsid(value)
|
|
88
|
+
} catch {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: new ValidationError(`${path} must be a valid nsid`),
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { success: true, value }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function cid(path: string, value: string): ValidationResult {
|
|
98
|
+
try {
|
|
99
|
+
CID.parse(value)
|
|
100
|
+
} catch {
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
error: new ValidationError(`${path} must be a cid string`),
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { success: true, value }
|
|
107
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { utf8Len, graphemeLen } from '@atproto/common-web'
|
|
2
|
+
import { CID } from 'multiformats/cid'
|
|
2
3
|
import { Lexicons } from '../lexicons'
|
|
4
|
+
import * as formats from './formats'
|
|
3
5
|
import {
|
|
4
6
|
LexUserType,
|
|
5
7
|
LexBoolean,
|
|
6
|
-
|
|
8
|
+
LexFloat,
|
|
7
9
|
LexInteger,
|
|
8
10
|
LexString,
|
|
9
|
-
LexDatetime,
|
|
10
11
|
ValidationResult,
|
|
11
12
|
ValidationError,
|
|
13
|
+
LexBytes,
|
|
12
14
|
} from '../types'
|
|
13
15
|
|
|
14
16
|
export function validate(
|
|
@@ -20,14 +22,16 @@ export function validate(
|
|
|
20
22
|
switch (def.type) {
|
|
21
23
|
case 'boolean':
|
|
22
24
|
return boolean(lexicons, path, def, value)
|
|
23
|
-
case '
|
|
24
|
-
return
|
|
25
|
+
case 'float':
|
|
26
|
+
return float(lexicons, path, def, value)
|
|
25
27
|
case 'integer':
|
|
26
28
|
return integer(lexicons, path, def, value)
|
|
27
29
|
case 'string':
|
|
28
30
|
return string(lexicons, path, def, value)
|
|
29
|
-
case '
|
|
30
|
-
return
|
|
31
|
+
case 'bytes':
|
|
32
|
+
return bytes(lexicons, path, def, value)
|
|
33
|
+
case 'cid-link':
|
|
34
|
+
return cidLink(lexicons, path, def, value)
|
|
31
35
|
case 'unknown':
|
|
32
36
|
return unknown(lexicons, path, def, value)
|
|
33
37
|
default:
|
|
@@ -48,9 +52,9 @@ export function boolean(
|
|
|
48
52
|
|
|
49
53
|
// type
|
|
50
54
|
const type = typeof value
|
|
51
|
-
if (type
|
|
55
|
+
if (type === 'undefined') {
|
|
52
56
|
if (typeof def.default === 'boolean') {
|
|
53
|
-
return { success: true }
|
|
57
|
+
return { success: true, value: def.default }
|
|
54
58
|
}
|
|
55
59
|
return {
|
|
56
60
|
success: false,
|
|
@@ -73,22 +77,22 @@ export function boolean(
|
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
return { success: true }
|
|
80
|
+
return { success: true, value }
|
|
77
81
|
}
|
|
78
82
|
|
|
79
|
-
export function
|
|
83
|
+
export function float(
|
|
80
84
|
lexicons: Lexicons,
|
|
81
85
|
path: string,
|
|
82
86
|
def: LexUserType,
|
|
83
87
|
value: unknown,
|
|
84
88
|
): ValidationResult {
|
|
85
|
-
def = def as
|
|
89
|
+
def = def as LexFloat
|
|
86
90
|
|
|
87
91
|
// type
|
|
88
92
|
const type = typeof value
|
|
89
|
-
if (type
|
|
93
|
+
if (type === 'undefined') {
|
|
90
94
|
if (typeof def.default === 'number') {
|
|
91
|
-
return { success: true }
|
|
95
|
+
return { success: true, value: def.default }
|
|
92
96
|
}
|
|
93
97
|
return {
|
|
94
98
|
success: false,
|
|
@@ -147,7 +151,7 @@ export function number(
|
|
|
147
151
|
}
|
|
148
152
|
}
|
|
149
153
|
|
|
150
|
-
return { success: true }
|
|
154
|
+
return { success: true, value }
|
|
151
155
|
}
|
|
152
156
|
|
|
153
157
|
export function integer(
|
|
@@ -159,9 +163,11 @@ export function integer(
|
|
|
159
163
|
def = def as LexInteger
|
|
160
164
|
|
|
161
165
|
// run number validation
|
|
162
|
-
const numRes =
|
|
166
|
+
const numRes = float(lexicons, path, def, value)
|
|
163
167
|
if (!numRes.success) {
|
|
164
168
|
return numRes
|
|
169
|
+
} else {
|
|
170
|
+
value = numRes.value
|
|
165
171
|
}
|
|
166
172
|
|
|
167
173
|
// whole numbers only
|
|
@@ -172,7 +178,7 @@ export function integer(
|
|
|
172
178
|
}
|
|
173
179
|
}
|
|
174
180
|
|
|
175
|
-
return { success: true }
|
|
181
|
+
return { success: true, value }
|
|
176
182
|
}
|
|
177
183
|
|
|
178
184
|
export function string(
|
|
@@ -184,16 +190,15 @@ export function string(
|
|
|
184
190
|
def = def as LexString
|
|
185
191
|
|
|
186
192
|
// type
|
|
187
|
-
|
|
188
|
-
if (type == 'undefined') {
|
|
193
|
+
if (typeof value === 'undefined') {
|
|
189
194
|
if (typeof def.default === 'string') {
|
|
190
|
-
return { success: true }
|
|
195
|
+
return { success: true, value: def.default }
|
|
191
196
|
}
|
|
192
197
|
return {
|
|
193
198
|
success: false,
|
|
194
199
|
error: new ValidationError(`${path} must be a string`),
|
|
195
200
|
}
|
|
196
|
-
} else if (
|
|
201
|
+
} else if (typeof value !== 'string') {
|
|
197
202
|
return {
|
|
198
203
|
success: false,
|
|
199
204
|
error: new ValidationError(`${path} must be a string`),
|
|
@@ -224,7 +229,7 @@ export function string(
|
|
|
224
229
|
|
|
225
230
|
// maxLength
|
|
226
231
|
if (typeof def.maxLength === 'number') {
|
|
227
|
-
if ((value
|
|
232
|
+
if (utf8Len(value) > def.maxLength) {
|
|
228
233
|
return {
|
|
229
234
|
success: false,
|
|
230
235
|
error: new ValidationError(
|
|
@@ -236,7 +241,7 @@ export function string(
|
|
|
236
241
|
|
|
237
242
|
// minLength
|
|
238
243
|
if (typeof def.minLength === 'number') {
|
|
239
|
-
if ((value
|
|
244
|
+
if (utf8Len(value) < def.minLength) {
|
|
240
245
|
return {
|
|
241
246
|
success: false,
|
|
242
247
|
error: new ValidationError(
|
|
@@ -246,40 +251,110 @@ export function string(
|
|
|
246
251
|
}
|
|
247
252
|
}
|
|
248
253
|
|
|
249
|
-
|
|
254
|
+
// maxGraphemes
|
|
255
|
+
if (typeof def.maxGraphemes === 'number') {
|
|
256
|
+
if (graphemeLen(value) > def.maxGraphemes) {
|
|
257
|
+
return {
|
|
258
|
+
success: false,
|
|
259
|
+
error: new ValidationError(
|
|
260
|
+
`${path} must not be longer than ${def.maxGraphemes} graphemes`,
|
|
261
|
+
),
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// minGraphemes
|
|
267
|
+
if (typeof def.minGraphemes === 'number') {
|
|
268
|
+
if (graphemeLen(value) < def.minGraphemes) {
|
|
269
|
+
return {
|
|
270
|
+
success: false,
|
|
271
|
+
error: new ValidationError(
|
|
272
|
+
`${path} must not be shorter than ${def.minGraphemes} graphemes`,
|
|
273
|
+
),
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (typeof def.format === 'string') {
|
|
279
|
+
switch (def.format) {
|
|
280
|
+
case 'datetime':
|
|
281
|
+
return formats.datetime(path, value)
|
|
282
|
+
case 'uri':
|
|
283
|
+
return formats.uri(path, value)
|
|
284
|
+
case 'at-uri':
|
|
285
|
+
return formats.atUri(path, value)
|
|
286
|
+
case 'did':
|
|
287
|
+
return formats.did(path, value)
|
|
288
|
+
case 'handle':
|
|
289
|
+
return formats.handle(path, value)
|
|
290
|
+
case 'at-identifier':
|
|
291
|
+
return formats.atIdentifier(path, value)
|
|
292
|
+
case 'nsid':
|
|
293
|
+
return formats.nsid(path, value)
|
|
294
|
+
case 'cid':
|
|
295
|
+
return formats.cid(path, value)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return { success: true, value }
|
|
250
300
|
}
|
|
251
301
|
|
|
252
|
-
export function
|
|
302
|
+
export function bytes(
|
|
253
303
|
lexicons: Lexicons,
|
|
254
304
|
path: string,
|
|
255
305
|
def: LexUserType,
|
|
256
306
|
value: unknown,
|
|
257
307
|
): ValidationResult {
|
|
258
|
-
def = def as
|
|
308
|
+
def = def as LexBytes
|
|
259
309
|
|
|
260
|
-
|
|
261
|
-
const type = typeof value
|
|
262
|
-
if (type !== 'string') {
|
|
310
|
+
if (!value || !(value instanceof Uint8Array)) {
|
|
263
311
|
return {
|
|
264
312
|
success: false,
|
|
265
|
-
error: new ValidationError(`${path} must be a
|
|
313
|
+
error: new ValidationError(`${path} must be a byte array`),
|
|
266
314
|
}
|
|
267
315
|
}
|
|
268
316
|
|
|
269
|
-
//
|
|
270
|
-
{
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
317
|
+
// maxLength
|
|
318
|
+
if (typeof def.maxLength === 'number') {
|
|
319
|
+
if (value.byteLength > def.maxLength) {
|
|
320
|
+
return {
|
|
321
|
+
success: false,
|
|
322
|
+
error: new ValidationError(
|
|
323
|
+
`${path} must not be larger than ${def.maxLength} bytes`,
|
|
324
|
+
),
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// minLength
|
|
330
|
+
if (typeof def.minLength === 'number') {
|
|
331
|
+
if (value.byteLength < def.minLength) {
|
|
332
|
+
return {
|
|
333
|
+
success: false,
|
|
334
|
+
error: new ValidationError(
|
|
335
|
+
`${path} must not be smaller than ${def.minLength} bytes`,
|
|
336
|
+
),
|
|
276
337
|
}
|
|
277
|
-
} catch {
|
|
278
|
-
throw new ValidationError(`${path} must be an iso8601 formatted datetime`)
|
|
279
338
|
}
|
|
280
339
|
}
|
|
281
340
|
|
|
282
|
-
return { success: true }
|
|
341
|
+
return { success: true, value }
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export function cidLink(
|
|
345
|
+
lexicons: Lexicons,
|
|
346
|
+
path: string,
|
|
347
|
+
def: LexUserType,
|
|
348
|
+
value: unknown,
|
|
349
|
+
): ValidationResult {
|
|
350
|
+
if (CID.asCID(value) === null) {
|
|
351
|
+
return {
|
|
352
|
+
success: false,
|
|
353
|
+
error: new ValidationError(`${path} must be a CID`),
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return { success: true, value }
|
|
283
358
|
}
|
|
284
359
|
|
|
285
360
|
export function unknown(
|
|
@@ -296,5 +371,5 @@ export function unknown(
|
|
|
296
371
|
}
|
|
297
372
|
}
|
|
298
373
|
|
|
299
|
-
return { success: true }
|
|
374
|
+
return { success: true, value }
|
|
300
375
|
}
|