@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/util.d.ts CHANGED
@@ -2,5 +2,5 @@ import { Lexicons } from './lexicons';
2
2
  import { LexUserType, LexRefVariant, ValidationResult } from './types';
3
3
  export declare function toLexUri(str: string, baseUri?: string): string;
4
4
  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;
5
+ export declare function assertValidOneOf(lexicons: Lexicons, path: string, def: LexRefVariant | LexUserType, value: unknown, mustBeObj?: boolean): unknown;
6
6
  export declare function toConcreteTypes(lexicons: Lexicons, def: LexRefVariant | LexUserType): LexUserType[];
@@ -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,9 @@
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;
@@ -2,8 +2,9 @@ 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;
5
+ export declare function float(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
6
6
  export declare function integer(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
7
7
  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;
8
+ export declare function bytes(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
9
+ export declare function cidLink(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
9
10
  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,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/lexicon",
3
- "version": "0.0.4",
3
+ "version": "0.1.0",
4
4
  "main": "dist/index.js",
5
5
  "scripts": {
6
6
  "test": "jest",
@@ -19,8 +19,12 @@
19
19
  },
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
+ "@atproto/common-web": "*",
23
+ "@atproto/identifier": "*",
22
24
  "@atproto/nsid": "*",
25
+ "@atproto/uri": "*",
23
26
  "iso-datestring-validator": "^2.2.2",
27
+ "multiformats": "^9.6.4",
24
28
  "zod": "^3.14.2"
25
29
  }
26
30
  }
@@ -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
+ }
package/src/types.ts CHANGED
@@ -12,8 +12,8 @@ export const lexBoolean = z.object({
12
12
  })
13
13
  export type LexBoolean = z.infer<typeof lexBoolean>
14
14
 
15
- export const lexNumber = z.object({
16
- type: z.literal('number'),
15
+ export const lexFloat = z.object({
16
+ type: z.literal('float'),
17
17
  description: z.string().optional(),
18
18
  default: z.number().optional(),
19
19
  minimum: z.number().optional(),
@@ -21,7 +21,7 @@ export const lexNumber = z.object({
21
21
  enum: z.number().array().optional(),
22
22
  const: z.number().optional(),
23
23
  })
24
- export type LexNumber = z.infer<typeof lexNumber>
24
+ export type LexFloat = z.infer<typeof lexFloat>
25
25
 
26
26
  export const lexInteger = z.object({
27
27
  type: z.literal('integer'),
@@ -34,24 +34,33 @@ export const lexInteger = z.object({
34
34
  })
35
35
  export type LexInteger = z.infer<typeof lexInteger>
36
36
 
37
+ export const lexStringFormat = z.enum([
38
+ 'datetime',
39
+ 'uri',
40
+ 'at-uri',
41
+ 'did',
42
+ 'handle',
43
+ 'at-identifier',
44
+ 'nsid',
45
+ 'cid',
46
+ ])
47
+ export type LexStringFormat = z.infer<typeof lexStringFormat>
48
+
37
49
  export const lexString = z.object({
38
50
  type: z.literal('string'),
51
+ format: lexStringFormat.optional(),
39
52
  description: z.string().optional(),
40
53
  default: z.string().optional(),
41
54
  minLength: z.number().int().optional(),
42
55
  maxLength: z.number().int().optional(),
56
+ minGraphemes: z.number().int().optional(),
57
+ maxGraphemes: z.number().int().optional(),
43
58
  enum: z.string().array().optional(),
44
59
  const: z.string().optional(),
45
60
  knownValues: z.string().array().optional(),
46
61
  })
47
62
  export type LexString = z.infer<typeof lexString>
48
63
 
49
- export const lexDatetime = z.object({
50
- type: z.literal('datetime'),
51
- description: z.string().optional(),
52
- })
53
- export type LexDatetime = z.infer<typeof lexDatetime>
54
-
55
64
  export const lexUnknown = z.object({
56
65
  type: z.literal('unknown'),
57
66
  description: z.string().optional(),
@@ -60,14 +69,33 @@ export type LexUnknown = z.infer<typeof lexUnknown>
60
69
 
61
70
  export const lexPrimitive = z.union([
62
71
  lexBoolean,
63
- lexNumber,
72
+ lexFloat,
64
73
  lexInteger,
65
74
  lexString,
66
- lexDatetime,
67
75
  lexUnknown,
68
76
  ])
69
77
  export type LexPrimitive = z.infer<typeof lexPrimitive>
70
78
 
79
+ // ipld types
80
+ // =
81
+
82
+ export const lexBytes = z.object({
83
+ type: z.literal('bytes'),
84
+ description: z.string().optional(),
85
+ maxLength: z.number().optional(),
86
+ minLength: z.number().optional(),
87
+ })
88
+ export type LexBytes = z.infer<typeof lexBytes>
89
+
90
+ export const lexCidLink = z.object({
91
+ type: z.literal('cid-link'),
92
+ description: z.string().optional(),
93
+ })
94
+ export type LexCidLink = z.infer<typeof lexCidLink>
95
+
96
+ export const lexIpldType = z.union([lexBytes, lexCidLink])
97
+ export type LexIpldType = z.infer<typeof lexIpldType>
98
+
71
99
  // references
72
100
  // =
73
101
 
@@ -100,51 +128,25 @@ export const lexBlob = z.object({
100
128
  })
101
129
  export type LexBlob = z.infer<typeof lexBlob>
102
130
 
103
- export const lexImage = z.object({
104
- type: z.literal('image'),
105
- description: z.string().optional(),
106
- accept: z.string().array().optional(),
107
- maxSize: z.number().optional(),
108
- maxWidth: z.number().int().optional(),
109
- maxHeight: z.number().int().optional(),
110
- })
111
- export type LexImage = z.infer<typeof lexImage>
112
-
113
- export const lexVideo = z.object({
114
- type: z.literal('video'),
115
- description: z.string().optional(),
116
- accept: z.string().array().optional(),
117
- maxSize: z.number().optional(),
118
- maxWidth: z.number().int().optional(),
119
- maxHeight: z.number().int().optional(),
120
- maxLength: z.number().int().optional(),
121
- })
122
- export type LexVideo = z.infer<typeof lexVideo>
123
-
124
- export const lexAudio = z.object({
125
- type: z.literal('audio'),
126
- description: z.string().optional(),
127
- accept: z.string().array().optional(),
128
- maxSize: z.number().optional(),
129
- maxLength: z.number().int().optional(),
130
- })
131
- export type LexAudio = z.infer<typeof lexAudio>
132
-
133
- export const lexBlobVariant = z.union([lexBlob, lexImage, lexVideo, lexAudio])
134
- export type LexBlobVariant = z.infer<typeof lexBlobVariant>
135
-
136
131
  // complex types
137
132
  // =
138
133
 
139
134
  export const lexArray = z.object({
140
135
  type: z.literal('array'),
141
136
  description: z.string().optional(),
142
- items: z.union([lexPrimitive, lexBlobVariant, lexRefVariant]),
137
+ items: z.union([lexPrimitive, lexIpldType, lexBlob, lexRefVariant]),
143
138
  minLength: z.number().int().optional(),
144
139
  maxLength: z.number().int().optional(),
145
140
  })
146
141
  export type LexArray = z.infer<typeof lexArray>
147
142
 
143
+ export const lexPrimitiveArray = lexArray.merge(
144
+ z.object({
145
+ items: lexPrimitive,
146
+ }),
147
+ )
148
+ export type LexPrimitiveArray = z.infer<typeof lexPrimitiveArray>
149
+
148
150
  export const lexToken = z.object({
149
151
  type: z.literal('token'),
150
152
  description: z.string().optional(),
@@ -155,8 +157,11 @@ export const lexObject = z.object({
155
157
  type: z.literal('object'),
156
158
  description: z.string().optional(),
157
159
  required: z.string().array().optional(),
160
+ nullable: z.string().array().optional(),
158
161
  properties: z
159
- .record(z.union([lexRefVariant, lexArray, lexBlobVariant, lexPrimitive]))
162
+ .record(
163
+ z.union([lexRefVariant, lexIpldType, lexArray, lexBlob, lexPrimitive]),
164
+ )
160
165
  .optional(),
161
166
  })
162
167
  export type LexObject = z.infer<typeof lexObject>
@@ -168,7 +173,7 @@ export const lexXrpcParameters = z.object({
168
173
  type: z.literal('params'),
169
174
  description: z.string().optional(),
170
175
  required: z.string().array().optional(),
171
- properties: z.record(lexPrimitive),
176
+ properties: z.record(z.union([lexPrimitive, lexPrimitiveArray])),
172
177
  })
173
178
  export type LexXrpcParameters = z.infer<typeof lexXrpcParameters>
174
179
 
@@ -179,6 +184,14 @@ export const lexXrpcBody = z.object({
179
184
  })
180
185
  export type LexXrpcBody = z.infer<typeof lexXrpcBody>
181
186
 
187
+ export const lexXrpcSubscriptionMessage = z.object({
188
+ description: z.string().optional(),
189
+ schema: z.union([lexRefVariant, lexObject]).optional(),
190
+ })
191
+ export type LexXrpcSubscriptionMessage = z.infer<
192
+ typeof lexXrpcSubscriptionMessage
193
+ >
194
+
182
195
  export const lexXrpcError = z.object({
183
196
  name: z.string(),
184
197
  description: z.string().optional(),
@@ -204,6 +217,16 @@ export const lexXrpcProcedure = z.object({
204
217
  })
205
218
  export type LexXrpcProcedure = z.infer<typeof lexXrpcProcedure>
206
219
 
220
+ export const lexXrpcSubscription = z.object({
221
+ type: z.literal('subscription'),
222
+ description: z.string().optional(),
223
+ parameters: lexXrpcParameters.optional(),
224
+ message: lexXrpcSubscriptionMessage.optional(),
225
+ infos: lexXrpcError.array().optional(),
226
+ errors: lexXrpcError.array().optional(),
227
+ })
228
+ export type LexXrpcSubscription = z.infer<typeof lexXrpcSubscription>
229
+
207
230
  // database
208
231
  // =
209
232
 
@@ -223,21 +246,20 @@ export const lexUserType = z.union([
223
246
 
224
247
  lexXrpcQuery,
225
248
  lexXrpcProcedure,
249
+ lexXrpcSubscription,
226
250
 
227
251
  lexBlob,
228
- lexImage,
229
- lexVideo,
230
- lexAudio,
231
252
 
232
253
  lexArray,
233
254
  lexToken,
234
255
  lexObject,
235
256
 
236
257
  lexBoolean,
237
- lexNumber,
258
+ lexFloat,
238
259
  lexInteger,
239
260
  lexString,
240
- lexDatetime,
261
+ lexBytes,
262
+ lexCidLink,
241
263
  lexUnknown,
242
264
  ])
243
265
  export type LexUserType = z.infer<typeof lexUserType>
@@ -259,11 +281,12 @@ export const lexiconDoc = z
259
281
  defId !== 'main' &&
260
282
  (def.type === 'record' ||
261
283
  def.type === 'procedure' ||
262
- def.type === 'query')
284
+ def.type === 'query' ||
285
+ def.type === 'subscription')
263
286
  ) {
264
287
  ctx.addIssue({
265
288
  code: z.ZodIssueCode.custom,
266
- message: `Records, procedures, and queries must be the main definition.`,
289
+ message: `Records, procedures, queries, and subscriptions must be the main definition.`,
267
290
  })
268
291
  }
269
292
  }
@@ -308,10 +331,15 @@ export class LexiconDocMalformedError extends Error {
308
331
  }
309
332
  }
310
333
 
311
- export interface ValidationResult {
312
- success: boolean
313
- error?: ValidationError
314
- }
334
+ export type ValidationResult =
335
+ | {
336
+ success: true
337
+ value: unknown
338
+ }
339
+ | {
340
+ success: false
341
+ error: ValidationError
342
+ }
315
343
 
316
344
  export class ValidationError extends Error {}
317
345
  export class InvalidLexiconError extends Error {}
package/src/util.ts CHANGED
@@ -49,7 +49,7 @@ export function validateOneOf(
49
49
  ),
50
50
  }
51
51
  }
52
- return { success: true }
52
+ return { success: true, value }
53
53
  } else {
54
54
  concreteDefs = toConcreteTypes(lexicons, {
55
55
  type: 'ref',
@@ -88,9 +88,8 @@ export function assertValidOneOf(
88
88
  mustBeObj = false,
89
89
  ) {
90
90
  const res = validateOneOf(lexicons, path, def, value, mustBeObj)
91
- if (!res.success) {
92
- throw res.error
93
- }
91
+ if (!res.success) throw res.error
92
+ return res.value
94
93
  }
95
94
 
96
95
  export function toConcreteTypes(