@atproto/lexicon 0.0.4 → 0.2.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 +12944 -680
- package/dist/index.js.map +4 -4
- package/dist/lexicons.d.ts +6 -4
- package/dist/serialize.d.ts +12 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/lexicons.d.ts +15 -0
- package/dist/src/record/index.d.ts +4 -0
- package/dist/src/record/schema.d.ts +9 -0
- package/dist/src/record/schemas.d.ts +10 -0
- package/dist/src/record/util.d.ts +1 -0
- package/dist/src/record/validation.d.ts +24 -0
- package/dist/src/record/validator.d.ts +17 -0
- package/dist/src/types.d.ts +30268 -0
- package/dist/src/util.d.ts +6 -0
- package/dist/src/validation.d.ts +6 -0
- package/dist/src/validators/blob.d.ts +6 -0
- package/dist/src/validators/complex.d.ts +5 -0
- package/dist/src/validators/primitives.d.ts +9 -0
- package/dist/src/validators/xrpc.d.ts +3 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/types.d.ts +14999 -23510
- package/dist/util.d.ts +6 -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 +10 -0
- package/dist/validators/primitives.d.ts +2 -2
- package/dist/validators/xrpc.d.ts +1 -1
- package/package.json +13 -4
- 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 +306 -180
- package/src/util.ts +64 -5
- package/src/validation.ts +28 -4
- package/src/validators/blob.ts +5 -43
- package/src/validators/complex.ts +31 -32
- package/src/validators/formats.ts +121 -0
- package/src/validators/primitives.ts +114 -67
- package/src/validators/xrpc.ts +29 -29
- package/tests/_scaffolds/lexicons.ts +178 -51
- package/tests/general.test.ts +496 -177
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.json +3 -1
package/dist/util.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { Lexicons } from './lexicons';
|
|
2
2
|
import { LexUserType, LexRefVariant, ValidationResult } from './types';
|
|
3
|
+
import { z } from 'zod';
|
|
3
4
|
export declare function toLexUri(str: string, baseUri?: string): string;
|
|
4
5
|
export declare function validateOneOf(lexicons: Lexicons, path: string, def: LexRefVariant | LexUserType, value: unknown, mustBeObj?: boolean): ValidationResult;
|
|
5
|
-
export declare function assertValidOneOf(lexicons: Lexicons, path: string, def: LexRefVariant | LexUserType, value: unknown, mustBeObj?: boolean):
|
|
6
|
+
export declare function assertValidOneOf(lexicons: Lexicons, path: string, def: LexRefVariant | LexUserType, value: unknown, mustBeObj?: boolean): unknown;
|
|
6
7
|
export declare function toConcreteTypes(lexicons: Lexicons, def: LexRefVariant | LexUserType): LexUserType[];
|
|
8
|
+
export declare function requiredPropertiesRefinement<ObjectType extends {
|
|
9
|
+
required?: string[];
|
|
10
|
+
properties?: Record<string, unknown>;
|
|
11
|
+
}>(object: ObjectType, ctx: z.RefinementCtx): void;
|
package/dist/validation.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Lexicons } from './lexicons';
|
|
2
|
-
import { LexRecord, LexXrpcProcedure, LexXrpcQuery } from './types';
|
|
3
|
-
export declare function assertValidRecord(lexicons: Lexicons, def: LexRecord, value: unknown):
|
|
4
|
-
export declare function assertValidXrpcParams(lexicons: Lexicons, def: LexXrpcProcedure | LexXrpcQuery, value: unknown):
|
|
5
|
-
export declare function assertValidXrpcInput(lexicons: Lexicons, def: LexXrpcProcedure, value: unknown):
|
|
6
|
-
export declare function assertValidXrpcOutput(lexicons: Lexicons, def: LexXrpcProcedure | LexXrpcQuery, value: unknown):
|
|
2
|
+
import { LexRecord, LexXrpcProcedure, LexXrpcQuery, LexXrpcSubscription } from './types';
|
|
3
|
+
export declare function assertValidRecord(lexicons: Lexicons, def: LexRecord, value: unknown): unknown;
|
|
4
|
+
export declare function assertValidXrpcParams(lexicons: Lexicons, def: LexXrpcProcedure | LexXrpcQuery | LexXrpcSubscription, value: unknown): unknown;
|
|
5
|
+
export declare function assertValidXrpcInput(lexicons: Lexicons, def: LexXrpcProcedure, value: unknown): unknown;
|
|
6
|
+
export declare function assertValidXrpcOutput(lexicons: Lexicons, def: LexXrpcProcedure | LexXrpcQuery, value: unknown): unknown;
|
|
7
|
+
export declare function assertValidXrpcMessage(lexicons: Lexicons, def: LexXrpcSubscription, value: unknown): unknown;
|
|
@@ -1,6 +1,3 @@
|
|
|
1
1
|
import { Lexicons } from '../lexicons';
|
|
2
2
|
import { LexUserType, ValidationResult } from '../types';
|
|
3
3
|
export declare function blob(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
4
|
-
export declare function image(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
5
|
-
export declare function video(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
6
|
-
export declare function audio(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Lexicons } from '../lexicons';
|
|
2
|
-
import { LexUserType, ValidationResult } from '../types';
|
|
2
|
+
import { LexArray, LexUserType, ValidationResult } from '../types';
|
|
3
3
|
export declare function validate(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
4
|
-
export declare function array(lexicons: Lexicons, path: string, def:
|
|
4
|
+
export declare function array(lexicons: Lexicons, path: string, def: LexArray, value: unknown): ValidationResult;
|
|
5
5
|
export declare function object(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ValidationResult } from '../types';
|
|
2
|
+
export declare function datetime(path: string, value: string): ValidationResult;
|
|
3
|
+
export declare function uri(path: string, value: string): ValidationResult;
|
|
4
|
+
export declare function atUri(path: string, value: string): ValidationResult;
|
|
5
|
+
export declare function did(path: string, value: string): ValidationResult;
|
|
6
|
+
export declare function handle(path: string, value: string): ValidationResult;
|
|
7
|
+
export declare function atIdentifier(path: string, value: string): ValidationResult;
|
|
8
|
+
export declare function nsid(path: string, value: string): ValidationResult;
|
|
9
|
+
export declare function cid(path: string, value: string): ValidationResult;
|
|
10
|
+
export declare function language(path: string, value: string): ValidationResult;
|
|
@@ -2,8 +2,8 @@ import { Lexicons } from '../lexicons';
|
|
|
2
2
|
import { LexUserType, ValidationResult } from '../types';
|
|
3
3
|
export declare function validate(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
4
4
|
export declare function boolean(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
5
|
-
export declare function number(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
6
5
|
export declare function integer(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
7
6
|
export declare function string(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
8
|
-
export declare function
|
|
7
|
+
export declare function bytes(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
8
|
+
export declare function cidLink(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
9
9
|
export declare function unknown(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Lexicons } from '../lexicons';
|
|
2
2
|
import { LexXrpcParameters, ValidationResult } from '../types';
|
|
3
|
-
export declare function params(lexicons: Lexicons, path: string, def: LexXrpcParameters,
|
|
3
|
+
export declare function params(lexicons: Lexicons, path: string, def: LexXrpcParameters, val: unknown): ValidationResult;
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/lexicon",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "jest",
|
|
7
|
-
"prettier": "prettier --check src/",
|
|
8
|
-
"prettier:fix": "prettier --write src/",
|
|
7
|
+
"prettier": "prettier --check src/ tests/",
|
|
8
|
+
"prettier:fix": "prettier --write src/ tests/",
|
|
9
9
|
"lint": "eslint . --ext .ts,.tsx",
|
|
10
10
|
"lint:fix": "yarn lint --fix",
|
|
11
11
|
"verify": "run-p prettier lint",
|
|
@@ -18,9 +18,18 @@
|
|
|
18
18
|
"postpublish": "npm run update-main-to-src"
|
|
19
19
|
},
|
|
20
20
|
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/bluesky-social/atproto.git",
|
|
24
|
+
"directory": "packages/lexicon"
|
|
25
|
+
},
|
|
21
26
|
"dependencies": {
|
|
27
|
+
"@atproto/common-web": "*",
|
|
28
|
+
"@atproto/identifier": "*",
|
|
22
29
|
"@atproto/nsid": "*",
|
|
30
|
+
"@atproto/uri": "*",
|
|
23
31
|
"iso-datestring-validator": "^2.2.2",
|
|
24
|
-
"
|
|
32
|
+
"multiformats": "^9.6.4",
|
|
33
|
+
"zod": "^3.21.4"
|
|
25
34
|
}
|
|
26
35
|
}
|
package/src/blob-refs.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { check, ipldToJson, schema } from '@atproto/common-web'
|
|
2
|
+
import { CID } from 'multiformats/cid'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
export const typedJsonBlobRef = z
|
|
6
|
+
.object({
|
|
7
|
+
$type: z.literal('blob'),
|
|
8
|
+
ref: schema.cid,
|
|
9
|
+
mimeType: z.string(),
|
|
10
|
+
size: z.number(),
|
|
11
|
+
})
|
|
12
|
+
.strict()
|
|
13
|
+
export type TypedJsonBlobRef = z.infer<typeof typedJsonBlobRef>
|
|
14
|
+
|
|
15
|
+
export const untypedJsonBlobRef = z
|
|
16
|
+
.object({
|
|
17
|
+
cid: z.string(),
|
|
18
|
+
mimeType: z.string(),
|
|
19
|
+
})
|
|
20
|
+
.strict()
|
|
21
|
+
export type UntypedJsonBlobRef = z.infer<typeof untypedJsonBlobRef>
|
|
22
|
+
|
|
23
|
+
export const jsonBlobRef = z.union([typedJsonBlobRef, untypedJsonBlobRef])
|
|
24
|
+
export type JsonBlobRef = z.infer<typeof jsonBlobRef>
|
|
25
|
+
|
|
26
|
+
export class BlobRef {
|
|
27
|
+
public original: JsonBlobRef
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
public ref: CID,
|
|
31
|
+
public mimeType: string,
|
|
32
|
+
public size: number,
|
|
33
|
+
original?: JsonBlobRef,
|
|
34
|
+
) {
|
|
35
|
+
this.original = original ?? {
|
|
36
|
+
$type: 'blob',
|
|
37
|
+
ref,
|
|
38
|
+
mimeType,
|
|
39
|
+
size,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static asBlobRef(obj: unknown): BlobRef | null {
|
|
44
|
+
if (check.is(obj, jsonBlobRef)) {
|
|
45
|
+
return BlobRef.fromJsonRef(obj)
|
|
46
|
+
}
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static fromJsonRef(json: JsonBlobRef): BlobRef {
|
|
51
|
+
if (check.is(json, typedJsonBlobRef)) {
|
|
52
|
+
return new BlobRef(json.ref, json.mimeType, json.size)
|
|
53
|
+
} else {
|
|
54
|
+
return new BlobRef(CID.parse(json.cid), json.mimeType, -1, json)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
ipld(): TypedJsonBlobRef {
|
|
59
|
+
return {
|
|
60
|
+
$type: 'blob',
|
|
61
|
+
ref: this.ref,
|
|
62
|
+
mimeType: this.mimeType,
|
|
63
|
+
size: this.size,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
toJSON() {
|
|
68
|
+
return ipldToJson(this.ipld())
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/index.ts
CHANGED
package/src/lexicons.ts
CHANGED
|
@@ -13,12 +13,14 @@ import {
|
|
|
13
13
|
ValidationError,
|
|
14
14
|
isObj,
|
|
15
15
|
hasProp,
|
|
16
|
+
LexXrpcSubscription,
|
|
16
17
|
} from './types'
|
|
17
18
|
import {
|
|
18
19
|
assertValidRecord,
|
|
19
20
|
assertValidXrpcParams,
|
|
20
21
|
assertValidXrpcInput,
|
|
21
22
|
assertValidXrpcOutput,
|
|
23
|
+
assertValidXrpcMessage,
|
|
22
24
|
} from './validation'
|
|
23
25
|
import { toLexUri } from './util'
|
|
24
26
|
import * as ComplexValidators from './validators/complex'
|
|
@@ -158,7 +160,7 @@ export class Lexicons {
|
|
|
158
160
|
`Invalid $type: must be ${lexUri}, got ${$type}`,
|
|
159
161
|
)
|
|
160
162
|
}
|
|
161
|
-
assertValidRecord(this, def as LexRecord, value)
|
|
163
|
+
return assertValidRecord(this, def as LexRecord, value)
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
/**
|
|
@@ -166,8 +168,16 @@ export class Lexicons {
|
|
|
166
168
|
*/
|
|
167
169
|
assertValidXrpcParams(lexUri: string, value: unknown) {
|
|
168
170
|
lexUri = toLexUri(lexUri)
|
|
169
|
-
const def = this.getDefOrThrow(lexUri, [
|
|
170
|
-
|
|
171
|
+
const def = this.getDefOrThrow(lexUri, [
|
|
172
|
+
'query',
|
|
173
|
+
'procedure',
|
|
174
|
+
'subscription',
|
|
175
|
+
])
|
|
176
|
+
return assertValidXrpcParams(
|
|
177
|
+
this,
|
|
178
|
+
def as LexXrpcProcedure | LexXrpcQuery | LexXrpcSubscription,
|
|
179
|
+
value,
|
|
180
|
+
)
|
|
171
181
|
}
|
|
172
182
|
|
|
173
183
|
/**
|
|
@@ -176,7 +186,7 @@ export class Lexicons {
|
|
|
176
186
|
assertValidXrpcInput(lexUri: string, value: unknown) {
|
|
177
187
|
lexUri = toLexUri(lexUri)
|
|
178
188
|
const def = this.getDefOrThrow(lexUri, ['procedure'])
|
|
179
|
-
assertValidXrpcInput(this, def as LexXrpcProcedure, value)
|
|
189
|
+
return assertValidXrpcInput(this, def as LexXrpcProcedure, value)
|
|
180
190
|
}
|
|
181
191
|
|
|
182
192
|
/**
|
|
@@ -185,7 +195,28 @@ export class Lexicons {
|
|
|
185
195
|
assertValidXrpcOutput(lexUri: string, value: unknown) {
|
|
186
196
|
lexUri = toLexUri(lexUri)
|
|
187
197
|
const def = this.getDefOrThrow(lexUri, ['query', 'procedure'])
|
|
188
|
-
assertValidXrpcOutput(
|
|
198
|
+
return assertValidXrpcOutput(
|
|
199
|
+
this,
|
|
200
|
+
def as LexXrpcProcedure | LexXrpcQuery,
|
|
201
|
+
value,
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Validate xrpc subscription message and throw on any error.
|
|
207
|
+
*/
|
|
208
|
+
assertValidXrpcMessage<T = unknown>(lexUri: string, value: unknown): T {
|
|
209
|
+
lexUri = toLexUri(lexUri)
|
|
210
|
+
const def = this.getDefOrThrow(lexUri, ['subscription'])
|
|
211
|
+
return assertValidXrpcMessage(this, def as LexXrpcSubscription, value) as T
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Resolve a lex uri given a ref
|
|
216
|
+
*/
|
|
217
|
+
resolveLexUri(lexUri: string, ref: string) {
|
|
218
|
+
lexUri = toLexUri(lexUri)
|
|
219
|
+
return toLexUri(ref, lexUri)
|
|
189
220
|
}
|
|
190
221
|
}
|
|
191
222
|
|
package/src/serialize.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
check,
|
|
3
|
+
IpldValue,
|
|
4
|
+
ipldToJson,
|
|
5
|
+
jsonToIpld,
|
|
6
|
+
JsonValue,
|
|
7
|
+
} from '@atproto/common-web'
|
|
8
|
+
import { CID } from 'multiformats/cid'
|
|
9
|
+
import { BlobRef, jsonBlobRef } from './blob-refs'
|
|
10
|
+
|
|
11
|
+
export type LexValue =
|
|
12
|
+
| IpldValue
|
|
13
|
+
| BlobRef
|
|
14
|
+
| Array<LexValue>
|
|
15
|
+
| { [key: string]: LexValue }
|
|
16
|
+
|
|
17
|
+
export type RepoRecord = Record<string, LexValue>
|
|
18
|
+
|
|
19
|
+
// @NOTE avoiding use of check.is() here only because it makes
|
|
20
|
+
// these implementations slow, and they often live in hot paths.
|
|
21
|
+
|
|
22
|
+
export const lexToIpld = (val: LexValue): IpldValue => {
|
|
23
|
+
// walk arrays
|
|
24
|
+
if (Array.isArray(val)) {
|
|
25
|
+
return val.map((item) => lexToIpld(item))
|
|
26
|
+
}
|
|
27
|
+
// objects
|
|
28
|
+
if (val && typeof val === 'object') {
|
|
29
|
+
// convert blobs, leaving the original encoding so that we don't change CIDs on re-encode
|
|
30
|
+
if (val instanceof BlobRef) {
|
|
31
|
+
return val.original
|
|
32
|
+
}
|
|
33
|
+
// retain cids & bytes
|
|
34
|
+
if (CID.asCID(val) || val instanceof Uint8Array) {
|
|
35
|
+
return val
|
|
36
|
+
}
|
|
37
|
+
// walk plain objects
|
|
38
|
+
const toReturn = {}
|
|
39
|
+
for (const key of Object.keys(val)) {
|
|
40
|
+
toReturn[key] = lexToIpld(val[key])
|
|
41
|
+
}
|
|
42
|
+
return toReturn
|
|
43
|
+
}
|
|
44
|
+
// pass through
|
|
45
|
+
return val
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const ipldToLex = (val: IpldValue): LexValue => {
|
|
49
|
+
// map arrays
|
|
50
|
+
if (Array.isArray(val)) {
|
|
51
|
+
return val.map((item) => ipldToLex(item))
|
|
52
|
+
}
|
|
53
|
+
// objects
|
|
54
|
+
if (val && typeof val === 'object') {
|
|
55
|
+
// convert blobs, using hints to avoid expensive is() check
|
|
56
|
+
if (
|
|
57
|
+
(val['$type'] === 'blob' ||
|
|
58
|
+
(typeof val['cid'] === 'string' &&
|
|
59
|
+
typeof val['mimeType'] === 'string')) &&
|
|
60
|
+
check.is(val, jsonBlobRef)
|
|
61
|
+
) {
|
|
62
|
+
return BlobRef.fromJsonRef(val)
|
|
63
|
+
}
|
|
64
|
+
// retain cids, bytes
|
|
65
|
+
if (CID.asCID(val) || val instanceof Uint8Array) {
|
|
66
|
+
return val
|
|
67
|
+
}
|
|
68
|
+
// map plain objects
|
|
69
|
+
const toReturn = {}
|
|
70
|
+
for (const key of Object.keys(val)) {
|
|
71
|
+
toReturn[key] = ipldToLex(val[key])
|
|
72
|
+
}
|
|
73
|
+
return toReturn
|
|
74
|
+
}
|
|
75
|
+
// pass through
|
|
76
|
+
return val
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const lexToJson = (val: LexValue): JsonValue => {
|
|
80
|
+
return ipldToJson(lexToIpld(val))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const stringifyLex = (val: LexValue): string => {
|
|
84
|
+
return JSON.stringify(lexToJson(val))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const jsonToLex = (val: JsonValue): LexValue => {
|
|
88
|
+
return ipldToLex(jsonToIpld(val))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const jsonStringToLex = (val: string): LexValue => {
|
|
92
|
+
return jsonToLex(JSON.parse(val))
|
|
93
|
+
}
|