@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/src/types.ts CHANGED
@@ -1,245 +1,364 @@
1
1
  import { z } from 'zod'
2
2
  import { NSID } from '@atproto/nsid'
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 lexNumber = z.object({
16
- type: z.literal('number'),
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 LexNumber = z.infer<typeof lexNumber>
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
- export const lexString = z.object({
38
- type: z.literal('string'),
39
- description: z.string().optional(),
40
- default: z.string().optional(),
41
- minLength: z.number().int().optional(),
42
- maxLength: z.number().int().optional(),
43
- enum: z.string().array().optional(),
44
- const: z.string().optional(),
45
- knownValues: z.string().array().optional(),
46
- })
47
- export type LexString = z.infer<typeof lexString>
31
+ export const lexStringFormat = z.enum([
32
+ 'datetime',
33
+ 'uri',
34
+ 'at-uri',
35
+ 'did',
36
+ 'handle',
37
+ 'at-identifier',
38
+ 'nsid',
39
+ 'cid',
40
+ 'language',
41
+ ])
42
+ export type LexStringFormat = z.infer<typeof lexStringFormat>
48
43
 
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>
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()
59
+ export type LexString = z.infer<typeof lexString>
54
60
 
55
- export const lexUnknown = z.object({
56
- type: z.literal('unknown'),
57
- description: z.string().optional(),
58
- })
61
+ export const lexUnknown = z
62
+ .object({
63
+ type: z.literal('unknown'),
64
+ description: z.string().optional(),
65
+ })
66
+ .strict()
59
67
  export type LexUnknown = z.infer<typeof lexUnknown>
60
68
 
61
- export const lexPrimitive = z.union([
69
+ export const lexPrimitive = z.discriminatedUnion('type', [
62
70
  lexBoolean,
63
- lexNumber,
64
71
  lexInteger,
65
72
  lexString,
66
- lexDatetime,
67
73
  lexUnknown,
68
74
  ])
69
75
  export type LexPrimitive = z.infer<typeof lexPrimitive>
70
76
 
77
+ // ipld types
78
+ // =
79
+
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
+ export type LexBytes = z.infer<typeof lexBytes>
89
+
90
+ export const lexCidLink = z
91
+ .object({
92
+ type: z.literal('cid-link'),
93
+ description: z.string().optional(),
94
+ })
95
+ .strict()
96
+ export type LexCidLink = z.infer<typeof lexCidLink>
97
+
98
+ export const lexIpldType = z.discriminatedUnion('type', [lexBytes, lexCidLink])
99
+ export type LexIpldType = z.infer<typeof lexIpldType>
100
+
71
101
  // references
72
102
  // =
73
103
 
74
- export const lexRef = z.object({
75
- type: z.literal('ref'),
76
- description: z.string().optional(),
77
- ref: z.string(),
78
- })
104
+ export const lexRef = z
105
+ .object({
106
+ type: z.literal('ref'),
107
+ description: z.string().optional(),
108
+ ref: z.string(),
109
+ })
110
+ .strict()
79
111
  export type LexRef = z.infer<typeof lexRef>
80
112
 
81
- export const lexRefUnion = z.object({
82
- type: z.literal('union'),
83
- description: z.string().optional(),
84
- refs: z.string().array(),
85
- closed: z.boolean().optional(),
86
- })
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()
87
121
  export type LexRefUnion = z.infer<typeof lexRefUnion>
88
122
 
89
- export const lexRefVariant = z.union([lexRef, lexRefUnion])
123
+ export const lexRefVariant = z.discriminatedUnion('type', [lexRef, lexRefUnion])
90
124
  export type LexRefVariant = z.infer<typeof lexRefVariant>
91
125
 
92
126
  // blobs
93
127
  // =
94
128
 
95
- export const lexBlob = z.object({
96
- type: z.literal('blob'),
97
- description: z.string().optional(),
98
- accept: z.string().array().optional(),
99
- maxSize: z.number().optional(),
100
- })
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()
101
137
  export type LexBlob = z.infer<typeof lexBlob>
102
138
 
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
139
  // complex types
137
140
  // =
138
141
 
139
- export const lexArray = z.object({
140
- type: z.literal('array'),
141
- description: z.string().optional(),
142
- items: z.union([lexPrimitive, lexBlobVariant, lexRefVariant]),
143
- minLength: z.number().int().optional(),
144
- maxLength: z.number().int().optional(),
145
- })
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()
146
151
  export type LexArray = z.infer<typeof lexArray>
147
152
 
148
- export const lexToken = z.object({
149
- type: z.literal('token'),
150
- description: z.string().optional(),
151
- })
153
+ export const lexPrimitiveArray = lexArray.merge(
154
+ z
155
+ .object({
156
+ items: lexPrimitive,
157
+ })
158
+ .strict(),
159
+ )
160
+ export type LexPrimitiveArray = z.infer<typeof lexPrimitiveArray>
161
+
162
+ export const lexToken = z
163
+ .object({
164
+ type: z.literal('token'),
165
+ description: z.string().optional(),
166
+ })
167
+ .strict()
152
168
  export type LexToken = z.infer<typeof lexToken>
153
169
 
154
- export const lexObject = z.object({
155
- type: z.literal('object'),
156
- description: z.string().optional(),
157
- required: z.string().array().optional(),
158
- properties: z
159
- .record(z.union([lexRefVariant, lexArray, lexBlobVariant, lexPrimitive]))
160
- .optional(),
161
- })
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)
162
184
  export type LexObject = z.infer<typeof lexObject>
163
185
 
164
186
  // xrpc
165
187
  // =
166
188
 
167
- export const lexXrpcParameters = z.object({
168
- type: z.literal('params'),
169
- description: z.string().optional(),
170
- required: z.string().array().optional(),
171
- properties: z.record(lexPrimitive),
172
- })
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)
173
198
  export type LexXrpcParameters = z.infer<typeof lexXrpcParameters>
174
199
 
175
- export const lexXrpcBody = z.object({
176
- description: z.string().optional(),
177
- encoding: z.string(),
178
- schema: z.union([lexRefVariant, lexObject]).optional(),
179
- })
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()
180
207
  export type LexXrpcBody = z.infer<typeof lexXrpcBody>
181
208
 
182
- export const lexXrpcError = z.object({
183
- name: z.string(),
184
- description: z.string().optional(),
185
- })
209
+ export const lexXrpcSubscriptionMessage = z
210
+ .object({
211
+ description: z.string().optional(),
212
+ schema: z.union([lexRefVariant, lexObject]).optional(),
213
+ })
214
+ .strict()
215
+ export type LexXrpcSubscriptionMessage = z.infer<
216
+ typeof lexXrpcSubscriptionMessage
217
+ >
218
+
219
+ export const lexXrpcError = z
220
+ .object({
221
+ name: z.string(),
222
+ description: z.string().optional(),
223
+ })
224
+ .strict()
186
225
  export type LexXrpcError = z.infer<typeof lexXrpcError>
187
226
 
188
- export const lexXrpcQuery = z.object({
189
- type: z.literal('query'),
190
- description: z.string().optional(),
191
- parameters: lexXrpcParameters.optional(),
192
- output: lexXrpcBody.optional(),
193
- errors: lexXrpcError.array().optional(),
194
- })
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()
195
236
  export type LexXrpcQuery = z.infer<typeof lexXrpcQuery>
196
237
 
197
- export const lexXrpcProcedure = z.object({
198
- type: z.literal('procedure'),
199
- description: z.string().optional(),
200
- parameters: lexXrpcParameters.optional(),
201
- input: lexXrpcBody.optional(),
202
- output: lexXrpcBody.optional(),
203
- errors: lexXrpcError.array().optional(),
204
- })
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()
205
248
  export type LexXrpcProcedure = z.infer<typeof lexXrpcProcedure>
206
249
 
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()
259
+ export type LexXrpcSubscription = z.infer<typeof lexXrpcSubscription>
260
+
207
261
  // database
208
262
  // =
209
263
 
210
- export const lexRecord = z.object({
211
- type: z.literal('record'),
212
- description: z.string().optional(),
213
- key: z.string().optional(),
214
- record: lexObject,
215
- })
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()
216
272
  export type LexRecord = z.infer<typeof lexRecord>
217
273
 
218
274
  // core
219
275
  // =
220
276
 
221
- export const lexUserType = z.union([
222
- 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
+ }
223
301
 
224
- lexXrpcQuery,
225
- lexXrpcProcedure,
302
+ if (val['type'] === undefined) {
303
+ return
304
+ }
226
305
 
227
- lexBlob,
228
- lexImage,
229
- lexVideo,
230
- lexAudio,
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
+ }
231
348
 
232
- lexArray,
233
- lexToken,
234
- lexObject,
349
+ if (val['type'] === undefined) {
350
+ return {
351
+ message: 'Must have a type',
352
+ fatal: true,
353
+ }
354
+ }
235
355
 
236
- lexBoolean,
237
- lexNumber,
238
- lexInteger,
239
- lexString,
240
- lexDatetime,
241
- lexUnknown,
242
- ])
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
+ )
243
362
  export type LexUserType = z.infer<typeof lexUserType>
244
363
 
245
364
  export const lexiconDoc = z
@@ -252,18 +371,20 @@ export const lexiconDoc = z
252
371
  description: z.string().optional(),
253
372
  defs: z.record(lexUserType),
254
373
  })
255
- .superRefine((doc: LexiconDoc, ctx) => {
374
+ .strict()
375
+ .superRefine((doc, ctx) => {
256
376
  for (const defId in doc.defs) {
257
377
  const def = doc.defs[defId]
258
378
  if (
259
379
  defId !== 'main' &&
260
380
  (def.type === 'record' ||
261
381
  def.type === 'procedure' ||
262
- def.type === 'query')
382
+ def.type === 'query' ||
383
+ def.type === 'subscription')
263
384
  ) {
264
385
  ctx.addIssue({
265
386
  code: z.ZodIssueCode.custom,
266
- message: `Records, procedures, and queries must be the main definition.`,
387
+ message: `Records, procedures, queries, and subscriptions must be the main definition.`,
267
388
  })
268
389
  }
269
390
  }
@@ -308,10 +429,15 @@ export class LexiconDocMalformedError extends Error {
308
429
  }
309
430
  }
310
431
 
311
- export interface ValidationResult {
312
- success: boolean
313
- error?: ValidationError
314
- }
432
+ export type ValidationResult =
433
+ | {
434
+ success: true
435
+ value: unknown
436
+ }
437
+ | {
438
+ success: false
439
+ error: ValidationError
440
+ }
315
441
 
316
442
  export class ValidationError extends Error {}
317
443
  export class InvalidLexiconError extends Error {}
package/src/util.ts CHANGED
@@ -7,8 +7,13 @@ import {
7
7
  ValidationResult,
8
8
  isDiscriminatedObject,
9
9
  } from './types'
10
+ import { z } from 'zod'
10
11
 
11
12
  export function toLexUri(str: string, baseUri?: string): string {
13
+ if (str.split('#').length > 2) {
14
+ throw new Error('Uri can only have one hash segment')
15
+ }
16
+
12
17
  if (str.startsWith('lex:')) {
13
18
  return str
14
19
  }
@@ -40,7 +45,7 @@ export function validateOneOf(
40
45
  ),
41
46
  }
42
47
  }
43
- if (!def.refs.includes(toLexUri(value.$type))) {
48
+ if (!refsContainType(def.refs, value.$type)) {
44
49
  if (def.closed) {
45
50
  return {
46
51
  success: false,
@@ -49,7 +54,7 @@ export function validateOneOf(
49
54
  ),
50
55
  }
51
56
  }
52
- return { success: true }
57
+ return { success: true, value }
53
58
  } else {
54
59
  concreteDefs = toConcreteTypes(lexicons, {
55
60
  type: 'ref',
@@ -88,9 +93,8 @@ export function assertValidOneOf(
88
93
  mustBeObj = false,
89
94
  ) {
90
95
  const res = validateOneOf(lexicons, path, def, value, mustBeObj)
91
- if (!res.success) {
92
- throw res.error
93
- }
96
+ if (!res.success) throw res.error
97
+ return res.value
94
98
  }
95
99
 
96
100
  export function toConcreteTypes(
@@ -105,3 +109,58 @@ export function toConcreteTypes(
105
109
  return [def]
106
110
  }
107
111
  }
112
+
113
+ export function requiredPropertiesRefinement<
114
+ ObjectType extends {
115
+ required?: string[]
116
+ properties?: Record<string, unknown>
117
+ },
118
+ >(object: ObjectType, ctx: z.RefinementCtx) {
119
+ // Required fields check
120
+ if (object.required === undefined) {
121
+ return
122
+ }
123
+
124
+ if (!Array.isArray(object.required)) {
125
+ ctx.addIssue({
126
+ code: z.ZodIssueCode.invalid_type,
127
+ received: typeof object.required,
128
+ expected: 'array',
129
+ })
130
+ return
131
+ }
132
+
133
+ if (object.properties === undefined) {
134
+ if (object.required.length > 0) {
135
+ ctx.addIssue({
136
+ code: z.ZodIssueCode.custom,
137
+ message: `Required fields defined but no properties defined`,
138
+ })
139
+ }
140
+ return
141
+ }
142
+
143
+ for (const field of object.required) {
144
+ if (object.properties[field] === undefined) {
145
+ ctx.addIssue({
146
+ code: z.ZodIssueCode.custom,
147
+ message: `Required field "${field}" not defined`,
148
+ })
149
+ }
150
+ }
151
+ }
152
+
153
+ // to avoid bugs like #0189 this needs to handle both
154
+ // explicit and implicit #main
155
+ const refsContainType = (refs: string[], type: string) => {
156
+ const lexUri = toLexUri(type)
157
+ if (refs.includes(lexUri)) {
158
+ return true
159
+ }
160
+
161
+ if (lexUri.endsWith('#main')) {
162
+ return refs.includes(lexUri.replace('#main', ''))
163
+ } else {
164
+ return refs.includes(lexUri + '#main')
165
+ }
166
+ }