@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.
Files changed (47) hide show
  1. package/dist/blob-refs.d.ts +67 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +12944 -680
  4. package/dist/index.js.map +4 -4
  5. package/dist/lexicons.d.ts +6 -4
  6. package/dist/serialize.d.ts +12 -0
  7. package/dist/src/index.d.ts +2 -0
  8. package/dist/src/lexicons.d.ts +15 -0
  9. package/dist/src/record/index.d.ts +4 -0
  10. package/dist/src/record/schema.d.ts +9 -0
  11. package/dist/src/record/schemas.d.ts +10 -0
  12. package/dist/src/record/util.d.ts +1 -0
  13. package/dist/src/record/validation.d.ts +24 -0
  14. package/dist/src/record/validator.d.ts +17 -0
  15. package/dist/src/types.d.ts +30268 -0
  16. package/dist/src/util.d.ts +6 -0
  17. package/dist/src/validation.d.ts +6 -0
  18. package/dist/src/validators/blob.d.ts +6 -0
  19. package/dist/src/validators/complex.d.ts +5 -0
  20. package/dist/src/validators/primitives.d.ts +9 -0
  21. package/dist/src/validators/xrpc.d.ts +3 -0
  22. package/dist/tsconfig.build.tsbuildinfo +1 -0
  23. package/dist/types.d.ts +14999 -23510
  24. package/dist/util.d.ts +6 -1
  25. package/dist/validation.d.ts +6 -5
  26. package/dist/validators/blob.d.ts +0 -3
  27. package/dist/validators/complex.d.ts +2 -2
  28. package/dist/validators/formats.d.ts +10 -0
  29. package/dist/validators/primitives.d.ts +2 -2
  30. package/dist/validators/xrpc.d.ts +1 -1
  31. package/package.json +13 -4
  32. package/src/blob-refs.ts +70 -0
  33. package/src/index.ts +2 -0
  34. package/src/lexicons.ts +36 -5
  35. package/src/serialize.ts +93 -0
  36. package/src/types.ts +306 -180
  37. package/src/util.ts +64 -5
  38. package/src/validation.ts +28 -4
  39. package/src/validators/blob.ts +5 -43
  40. package/src/validators/complex.ts +31 -32
  41. package/src/validators/formats.ts +121 -0
  42. package/src/validators/primitives.ts +114 -67
  43. package/src/validators/xrpc.ts +29 -29
  44. package/tests/_scaffolds/lexicons.ts +178 -51
  45. package/tests/general.test.ts +496 -177
  46. package/tsconfig.build.tsbuildinfo +1 -1
  47. 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): void;
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;
@@ -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): void;
4
- export declare function assertValidXrpcParams(lexicons: Lexicons, def: LexXrpcProcedure | LexXrpcQuery, value: unknown): void;
5
- export declare function assertValidXrpcInput(lexicons: Lexicons, def: LexXrpcProcedure, value: unknown): void;
6
- export declare function assertValidXrpcOutput(lexicons: Lexicons, def: LexXrpcProcedure | LexXrpcQuery, value: unknown): void;
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: LexUserType, value: unknown): ValidationResult;
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 datetime(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
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, value: unknown): ValidationResult;
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.4",
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
- "zod": "^3.14.2"
32
+ "multiformats": "^9.6.4",
33
+ "zod": "^3.21.4"
25
34
  }
26
35
  }
@@ -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
@@ -1,2 +1,4 @@
1
1
  export * from './types'
2
2
  export * from './lexicons'
3
+ export * from './blob-refs'
4
+ export * from './serialize'
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, ['query', 'procedure'])
170
- assertValidXrpcParams(this, def as LexXrpcProcedure | LexXrpcQuery, value)
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(this, def as LexXrpcProcedure | LexXrpcQuery, value)
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
 
@@ -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
+ }