@atproto/lexicon 0.1.0 → 0.2.1

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
@@ -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
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;
@@ -7,3 +7,4 @@ export declare function handle(path: string, value: string): ValidationResult;
7
7
  export declare function atIdentifier(path: string, value: string): ValidationResult;
8
8
  export declare function nsid(path: string, value: string): ValidationResult;
9
9
  export declare function cid(path: string, value: string): ValidationResult;
10
+ export declare function language(path: string, value: string): ValidationResult;
@@ -2,7 +2,6 @@ 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 float(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
7
  export declare function bytes(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
package/package.json CHANGED
@@ -1,30 +1,25 @@
1
1
  {
2
2
  "name": "@atproto/lexicon",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "main": "dist/index.js",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/bluesky-social/atproto.git",
9
+ "directory": "packages/lexicon"
10
+ },
11
+ "dependencies": {
12
+ "iso-datestring-validator": "^2.2.2",
13
+ "multiformats": "^9.9.0",
14
+ "zod": "^3.21.4",
15
+ "@atproto/common-web": "^0.2.0",
16
+ "@atproto/syntax": "^0.1.1"
17
+ },
5
18
  "scripts": {
6
19
  "test": "jest",
7
- "prettier": "prettier --check src/",
8
- "prettier:fix": "prettier --write src/",
9
- "lint": "eslint . --ext .ts,.tsx",
10
- "lint:fix": "yarn lint --fix",
11
- "verify": "run-p prettier lint",
12
- "verify:fix": "yarn prettier:fix && yarn lint:fix",
13
20
  "build": "node ./build.js",
14
21
  "postbuild": "tsc --build tsconfig.build.json",
15
- "update-main-to-dist": "node ./update-pkg.js --update-main-to-dist",
16
- "update-main-to-src": "node ./update-pkg.js --update-main-to-src",
17
- "prepublish": "npm run update-main-to-dist",
18
- "postpublish": "npm run update-main-to-src"
22
+ "update-main-to-dist": "node ../../update-main-to-dist.js packages/lexicon"
19
23
  },
20
- "license": "MIT",
21
- "dependencies": {
22
- "@atproto/common-web": "*",
23
- "@atproto/identifier": "*",
24
- "@atproto/nsid": "*",
25
- "@atproto/uri": "*",
26
- "iso-datestring-validator": "^2.2.2",
27
- "multiformats": "^9.6.4",
28
- "zod": "^3.14.2"
29
- }
30
- }
24
+ "types": "dist/index.d.ts"
25
+ }
package/src/types.ts CHANGED
@@ -1,37 +1,31 @@
1
1
  import { z } from 'zod'
2
- import { NSID } from '@atproto/nsid'
2
+ import { NSID } from '@atproto/syntax'
3
+ import { requiredPropertiesRefinement } from './util'
3
4
 
4
5
  // primitives
5
6
  // =
6
7
 
7
- export const lexBoolean = z.object({
8
- type: z.literal('boolean'),
9
- description: z.string().optional(),
10
- default: z.boolean().optional(),
11
- const: z.boolean().optional(),
12
- })
8
+ export const lexBoolean = z
9
+ .object({
10
+ type: z.literal('boolean'),
11
+ description: z.string().optional(),
12
+ default: z.boolean().optional(),
13
+ const: z.boolean().optional(),
14
+ })
15
+ .strict()
13
16
  export type LexBoolean = z.infer<typeof lexBoolean>
14
17
 
15
- export const lexFloat = z.object({
16
- type: z.literal('float'),
17
- description: z.string().optional(),
18
- default: z.number().optional(),
19
- minimum: z.number().optional(),
20
- maximum: z.number().optional(),
21
- enum: z.number().array().optional(),
22
- const: z.number().optional(),
23
- })
24
- export type LexFloat = z.infer<typeof lexFloat>
25
-
26
- export const lexInteger = z.object({
27
- type: z.literal('integer'),
28
- description: z.string().optional(),
29
- default: z.number().int().optional(),
30
- minimum: z.number().int().optional(),
31
- maximum: z.number().int().optional(),
32
- enum: z.number().int().array().optional(),
33
- const: z.number().int().optional(),
34
- })
18
+ export const lexInteger = z
19
+ .object({
20
+ type: z.literal('integer'),
21
+ description: z.string().optional(),
22
+ default: z.number().int().optional(),
23
+ minimum: z.number().int().optional(),
24
+ maximum: z.number().int().optional(),
25
+ enum: z.number().int().array().optional(),
26
+ const: z.number().int().optional(),
27
+ })
28
+ .strict()
35
29
  export type LexInteger = z.infer<typeof lexInteger>
36
30
 
37
31
  export const lexStringFormat = z.enum([
@@ -43,33 +37,37 @@ export const lexStringFormat = z.enum([
43
37
  'at-identifier',
44
38
  'nsid',
45
39
  'cid',
40
+ 'language',
46
41
  ])
47
42
  export type LexStringFormat = z.infer<typeof lexStringFormat>
48
43
 
49
- export const lexString = z.object({
50
- type: z.literal('string'),
51
- format: lexStringFormat.optional(),
52
- description: z.string().optional(),
53
- default: z.string().optional(),
54
- minLength: z.number().int().optional(),
55
- maxLength: z.number().int().optional(),
56
- minGraphemes: z.number().int().optional(),
57
- maxGraphemes: z.number().int().optional(),
58
- enum: z.string().array().optional(),
59
- const: z.string().optional(),
60
- knownValues: z.string().array().optional(),
61
- })
44
+ export const lexString = z
45
+ .object({
46
+ type: z.literal('string'),
47
+ format: lexStringFormat.optional(),
48
+ description: z.string().optional(),
49
+ default: z.string().optional(),
50
+ minLength: z.number().int().optional(),
51
+ maxLength: z.number().int().optional(),
52
+ minGraphemes: z.number().int().optional(),
53
+ maxGraphemes: z.number().int().optional(),
54
+ enum: z.string().array().optional(),
55
+ const: z.string().optional(),
56
+ knownValues: z.string().array().optional(),
57
+ })
58
+ .strict()
62
59
  export type LexString = z.infer<typeof lexString>
63
60
 
64
- export const lexUnknown = z.object({
65
- type: z.literal('unknown'),
66
- description: z.string().optional(),
67
- })
61
+ export const lexUnknown = z
62
+ .object({
63
+ type: z.literal('unknown'),
64
+ description: z.string().optional(),
65
+ })
66
+ .strict()
68
67
  export type LexUnknown = z.infer<typeof lexUnknown>
69
68
 
70
- export const lexPrimitive = z.union([
69
+ export const lexPrimitive = z.discriminatedUnion('type', [
71
70
  lexBoolean,
72
- lexFloat,
73
71
  lexInteger,
74
72
  lexString,
75
73
  lexUnknown,
@@ -79,189 +77,288 @@ export type LexPrimitive = z.infer<typeof lexPrimitive>
79
77
  // ipld types
80
78
  // =
81
79
 
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
- })
80
+ export const lexBytes = z
81
+ .object({
82
+ type: z.literal('bytes'),
83
+ description: z.string().optional(),
84
+ maxLength: z.number().optional(),
85
+ minLength: z.number().optional(),
86
+ })
87
+ .strict()
88
88
  export type LexBytes = z.infer<typeof lexBytes>
89
89
 
90
- export const lexCidLink = z.object({
91
- type: z.literal('cid-link'),
92
- description: z.string().optional(),
93
- })
90
+ export const lexCidLink = z
91
+ .object({
92
+ type: z.literal('cid-link'),
93
+ description: z.string().optional(),
94
+ })
95
+ .strict()
94
96
  export type LexCidLink = z.infer<typeof lexCidLink>
95
97
 
96
- export const lexIpldType = z.union([lexBytes, lexCidLink])
98
+ export const lexIpldType = z.discriminatedUnion('type', [lexBytes, lexCidLink])
97
99
  export type LexIpldType = z.infer<typeof lexIpldType>
98
100
 
99
101
  // references
100
102
  // =
101
103
 
102
- export const lexRef = z.object({
103
- type: z.literal('ref'),
104
- description: z.string().optional(),
105
- ref: z.string(),
106
- })
104
+ export const lexRef = z
105
+ .object({
106
+ type: z.literal('ref'),
107
+ description: z.string().optional(),
108
+ ref: z.string(),
109
+ })
110
+ .strict()
107
111
  export type LexRef = z.infer<typeof lexRef>
108
112
 
109
- export const lexRefUnion = z.object({
110
- type: z.literal('union'),
111
- description: z.string().optional(),
112
- refs: z.string().array(),
113
- closed: z.boolean().optional(),
114
- })
113
+ export const lexRefUnion = z
114
+ .object({
115
+ type: z.literal('union'),
116
+ description: z.string().optional(),
117
+ refs: z.string().array(),
118
+ closed: z.boolean().optional(),
119
+ })
120
+ .strict()
115
121
  export type LexRefUnion = z.infer<typeof lexRefUnion>
116
122
 
117
- export const lexRefVariant = z.union([lexRef, lexRefUnion])
123
+ export const lexRefVariant = z.discriminatedUnion('type', [lexRef, lexRefUnion])
118
124
  export type LexRefVariant = z.infer<typeof lexRefVariant>
119
125
 
120
126
  // blobs
121
127
  // =
122
128
 
123
- export const lexBlob = z.object({
124
- type: z.literal('blob'),
125
- description: z.string().optional(),
126
- accept: z.string().array().optional(),
127
- maxSize: z.number().optional(),
128
- })
129
+ export const lexBlob = z
130
+ .object({
131
+ type: z.literal('blob'),
132
+ description: z.string().optional(),
133
+ accept: z.string().array().optional(),
134
+ maxSize: z.number().optional(),
135
+ })
136
+ .strict()
129
137
  export type LexBlob = z.infer<typeof lexBlob>
130
138
 
131
139
  // complex types
132
140
  // =
133
141
 
134
- export const lexArray = z.object({
135
- type: z.literal('array'),
136
- description: z.string().optional(),
137
- items: z.union([lexPrimitive, lexIpldType, lexBlob, lexRefVariant]),
138
- minLength: z.number().int().optional(),
139
- maxLength: z.number().int().optional(),
140
- })
142
+ export const lexArray = z
143
+ .object({
144
+ type: z.literal('array'),
145
+ description: z.string().optional(),
146
+ items: z.union([lexPrimitive, lexIpldType, lexBlob, lexRefVariant]),
147
+ minLength: z.number().int().optional(),
148
+ maxLength: z.number().int().optional(),
149
+ })
150
+ .strict()
141
151
  export type LexArray = z.infer<typeof lexArray>
142
152
 
143
153
  export const lexPrimitiveArray = lexArray.merge(
144
- z.object({
145
- items: lexPrimitive,
146
- }),
154
+ z
155
+ .object({
156
+ items: lexPrimitive,
157
+ })
158
+ .strict(),
147
159
  )
148
160
  export type LexPrimitiveArray = z.infer<typeof lexPrimitiveArray>
149
161
 
150
- export const lexToken = z.object({
151
- type: z.literal('token'),
152
- description: z.string().optional(),
153
- })
162
+ export const lexToken = z
163
+ .object({
164
+ type: z.literal('token'),
165
+ description: z.string().optional(),
166
+ })
167
+ .strict()
154
168
  export type LexToken = z.infer<typeof lexToken>
155
169
 
156
- export const lexObject = z.object({
157
- type: z.literal('object'),
158
- description: z.string().optional(),
159
- required: z.string().array().optional(),
160
- nullable: z.string().array().optional(),
161
- properties: z
162
- .record(
163
- z.union([lexRefVariant, lexIpldType, lexArray, lexBlob, lexPrimitive]),
164
- )
165
- .optional(),
166
- })
170
+ export const lexObject = z
171
+ .object({
172
+ type: z.literal('object'),
173
+ description: z.string().optional(),
174
+ required: z.string().array().optional(),
175
+ nullable: z.string().array().optional(),
176
+ properties: z
177
+ .record(
178
+ z.union([lexRefVariant, lexIpldType, lexArray, lexBlob, lexPrimitive]),
179
+ )
180
+ .optional(),
181
+ })
182
+ .strict()
183
+ .superRefine(requiredPropertiesRefinement)
167
184
  export type LexObject = z.infer<typeof lexObject>
168
185
 
169
186
  // xrpc
170
187
  // =
171
188
 
172
- export const lexXrpcParameters = z.object({
173
- type: z.literal('params'),
174
- description: z.string().optional(),
175
- required: z.string().array().optional(),
176
- properties: z.record(z.union([lexPrimitive, lexPrimitiveArray])),
177
- })
189
+ export const lexXrpcParameters = z
190
+ .object({
191
+ type: z.literal('params'),
192
+ description: z.string().optional(),
193
+ required: z.string().array().optional(),
194
+ properties: z.record(z.union([lexPrimitive, lexPrimitiveArray])),
195
+ })
196
+ .strict()
197
+ .superRefine(requiredPropertiesRefinement)
178
198
  export type LexXrpcParameters = z.infer<typeof lexXrpcParameters>
179
199
 
180
- export const lexXrpcBody = z.object({
181
- description: z.string().optional(),
182
- encoding: z.string(),
183
- schema: z.union([lexRefVariant, lexObject]).optional(),
184
- })
200
+ export const lexXrpcBody = z
201
+ .object({
202
+ description: z.string().optional(),
203
+ encoding: z.string(),
204
+ schema: z.union([lexRefVariant, lexObject]).optional(),
205
+ })
206
+ .strict()
185
207
  export type LexXrpcBody = z.infer<typeof lexXrpcBody>
186
208
 
187
- export const lexXrpcSubscriptionMessage = z.object({
188
- description: z.string().optional(),
189
- schema: z.union([lexRefVariant, lexObject]).optional(),
190
- })
209
+ export const lexXrpcSubscriptionMessage = z
210
+ .object({
211
+ description: z.string().optional(),
212
+ schema: z.union([lexRefVariant, lexObject]).optional(),
213
+ })
214
+ .strict()
191
215
  export type LexXrpcSubscriptionMessage = z.infer<
192
216
  typeof lexXrpcSubscriptionMessage
193
217
  >
194
218
 
195
- export const lexXrpcError = z.object({
196
- name: z.string(),
197
- description: z.string().optional(),
198
- })
219
+ export const lexXrpcError = z
220
+ .object({
221
+ name: z.string(),
222
+ description: z.string().optional(),
223
+ })
224
+ .strict()
199
225
  export type LexXrpcError = z.infer<typeof lexXrpcError>
200
226
 
201
- export const lexXrpcQuery = z.object({
202
- type: z.literal('query'),
203
- description: z.string().optional(),
204
- parameters: lexXrpcParameters.optional(),
205
- output: lexXrpcBody.optional(),
206
- errors: lexXrpcError.array().optional(),
207
- })
227
+ export const lexXrpcQuery = z
228
+ .object({
229
+ type: z.literal('query'),
230
+ description: z.string().optional(),
231
+ parameters: lexXrpcParameters.optional(),
232
+ output: lexXrpcBody.optional(),
233
+ errors: lexXrpcError.array().optional(),
234
+ })
235
+ .strict()
208
236
  export type LexXrpcQuery = z.infer<typeof lexXrpcQuery>
209
237
 
210
- export const lexXrpcProcedure = z.object({
211
- type: z.literal('procedure'),
212
- description: z.string().optional(),
213
- parameters: lexXrpcParameters.optional(),
214
- input: lexXrpcBody.optional(),
215
- output: lexXrpcBody.optional(),
216
- errors: lexXrpcError.array().optional(),
217
- })
238
+ export const lexXrpcProcedure = z
239
+ .object({
240
+ type: z.literal('procedure'),
241
+ description: z.string().optional(),
242
+ parameters: lexXrpcParameters.optional(),
243
+ input: lexXrpcBody.optional(),
244
+ output: lexXrpcBody.optional(),
245
+ errors: lexXrpcError.array().optional(),
246
+ })
247
+ .strict()
218
248
  export type LexXrpcProcedure = z.infer<typeof lexXrpcProcedure>
219
249
 
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
- })
250
+ export const lexXrpcSubscription = z
251
+ .object({
252
+ type: z.literal('subscription'),
253
+ description: z.string().optional(),
254
+ parameters: lexXrpcParameters.optional(),
255
+ message: lexXrpcSubscriptionMessage.optional(),
256
+ errors: lexXrpcError.array().optional(),
257
+ })
258
+ .strict()
228
259
  export type LexXrpcSubscription = z.infer<typeof lexXrpcSubscription>
229
260
 
230
261
  // database
231
262
  // =
232
263
 
233
- export const lexRecord = z.object({
234
- type: z.literal('record'),
235
- description: z.string().optional(),
236
- key: z.string().optional(),
237
- record: lexObject,
238
- })
264
+ export const lexRecord = z
265
+ .object({
266
+ type: z.literal('record'),
267
+ description: z.string().optional(),
268
+ key: z.string().optional(),
269
+ record: lexObject,
270
+ })
271
+ .strict()
239
272
  export type LexRecord = z.infer<typeof lexRecord>
240
273
 
241
274
  // core
242
275
  // =
243
276
 
244
- export const lexUserType = z.union([
245
- lexRecord,
277
+ // We need to use `z.custom` here because
278
+ // lexXrpcProperty and lexObject are refined
279
+ // `z.union` would work, but it's too slow
280
+ // see #915 for details
281
+ export const lexUserType = z.custom<
282
+ | LexRecord
283
+ | LexXrpcQuery
284
+ | LexXrpcProcedure
285
+ | LexXrpcSubscription
286
+ | LexBlob
287
+ | LexArray
288
+ | LexToken
289
+ | LexObject
290
+ | LexBoolean
291
+ | LexInteger
292
+ | LexString
293
+ | LexBytes
294
+ | LexCidLink
295
+ | LexUnknown
296
+ >(
297
+ (val) => {
298
+ if (!val || typeof val !== 'object') {
299
+ return
300
+ }
246
301
 
247
- lexXrpcQuery,
248
- lexXrpcProcedure,
249
- lexXrpcSubscription,
302
+ if (val['type'] === undefined) {
303
+ return
304
+ }
250
305
 
251
- lexBlob,
306
+ switch (val['type']) {
307
+ case 'record':
308
+ return lexRecord.parse(val)
309
+
310
+ case 'query':
311
+ return lexXrpcQuery.parse(val)
312
+ case 'procedure':
313
+ return lexXrpcProcedure.parse(val)
314
+ case 'subscription':
315
+ return lexXrpcSubscription.parse(val)
316
+
317
+ case 'blob':
318
+ return lexBlob.parse(val)
319
+
320
+ case 'array':
321
+ return lexArray.parse(val)
322
+ case 'token':
323
+ return lexToken.parse(val)
324
+ case 'object':
325
+ return lexObject.parse(val)
326
+
327
+ case 'boolean':
328
+ return lexBoolean.parse(val)
329
+ case 'integer':
330
+ return lexInteger.parse(val)
331
+ case 'string':
332
+ return lexString.parse(val)
333
+ case 'bytes':
334
+ return lexBytes.parse(val)
335
+ case 'cid-link':
336
+ return lexCidLink.parse(val)
337
+ case 'unknown':
338
+ return lexUnknown.parse(val)
339
+ }
340
+ },
341
+ (val) => {
342
+ if (!val || typeof val !== 'object') {
343
+ return {
344
+ message: 'Must be an object',
345
+ fatal: true,
346
+ }
347
+ }
252
348
 
253
- lexArray,
254
- lexToken,
255
- lexObject,
349
+ if (val['type'] === undefined) {
350
+ return {
351
+ message: 'Must have a type',
352
+ fatal: true,
353
+ }
354
+ }
256
355
 
257
- lexBoolean,
258
- lexFloat,
259
- lexInteger,
260
- lexString,
261
- lexBytes,
262
- lexCidLink,
263
- lexUnknown,
264
- ])
356
+ return {
357
+ message: `Invalid type: ${val['type']} must be one of: record, query, procedure, subscription, blob, array, token, object, boolean, integer, string, bytes, cid-link, unknown`,
358
+ fatal: true,
359
+ }
360
+ },
361
+ )
265
362
  export type LexUserType = z.infer<typeof lexUserType>
266
363
 
267
364
  export const lexiconDoc = z
@@ -274,7 +371,8 @@ export const lexiconDoc = z
274
371
  description: z.string().optional(),
275
372
  defs: z.record(lexUserType),
276
373
  })
277
- .superRefine((doc: LexiconDoc, ctx) => {
374
+ .strict()
375
+ .superRefine((doc, ctx) => {
278
376
  for (const defId in doc.defs) {
279
377
  const def = doc.defs[defId]
280
378
  if (