@atproto/lex-schema 0.0.15 → 0.0.17
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/CHANGELOG.md +28 -0
- package/dist/core/schema.d.ts +6 -12
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +11 -17
- package/dist/core/schema.js.map +1 -1
- package/dist/core/string-format.d.ts +22 -4
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +43 -19
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/validation-error.d.ts +2 -2
- package/dist/core/validation-error.d.ts.map +1 -1
- package/dist/core/validation-error.js +1 -1
- package/dist/core/validation-error.js.map +1 -1
- package/dist/core/validation-issue.d.ts.map +1 -1
- package/dist/core/validation-issue.js +19 -38
- package/dist/core/validation-issue.js.map +1 -1
- package/dist/core/validator.d.ts +13 -1
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +1 -0
- package/dist/core/validator.js.map +1 -1
- package/dist/helpers.d.ts +4 -3
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +6 -1
- package/dist/helpers.js.map +1 -1
- package/dist/schema/blob.d.ts +10 -8
- package/dist/schema/blob.d.ts.map +1 -1
- package/dist/schema/blob.js +39 -14
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/payload.d.ts +2 -2
- package/dist/schema/payload.d.ts.map +1 -1
- package/dist/schema/payload.js +2 -3
- package/dist/schema/payload.js.map +1 -1
- package/dist/schema/record.d.ts +6 -8
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +1 -1
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/regexp.d.ts +3 -2
- package/dist/schema/regexp.d.ts.map +1 -1
- package/dist/schema/regexp.js +6 -4
- package/dist/schema/regexp.js.map +1 -1
- package/dist/schema/string.d.ts.map +1 -1
- package/dist/schema/string.js +10 -3
- package/dist/schema/string.js.map +1 -1
- package/dist/schema/typed-object.d.ts +5 -7
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +1 -1
- package/dist/schema/typed-object.js.map +1 -1
- package/package.json +4 -3
- package/src/core/$type.test.ts +9 -5
- package/src/core/schema.ts +20 -17
- package/src/core/string-format.ts +62 -16
- package/src/core/validation-error.ts +2 -2
- package/src/core/validation-issue.ts +20 -36
- package/src/core/validator.ts +17 -1
- package/src/helpers.ts +7 -1
- package/src/schema/array.test.ts +1 -1
- package/src/schema/blob.test.ts +317 -49
- package/src/schema/blob.ts +56 -23
- package/src/schema/params.test.ts +2 -2
- package/src/schema/payload.ts +4 -5
- package/src/schema/record.test.ts +135 -17
- package/src/schema/record.ts +14 -9
- package/src/schema/regexp.ts +14 -4
- package/src/schema/string.test.ts +63 -0
- package/src/schema/string.ts +9 -3
- package/src/schema/typed-object.test.ts +77 -0
- package/src/schema/typed-object.ts +11 -10
package/src/core/schema.ts
CHANGED
|
@@ -47,7 +47,7 @@ export interface SchemaInternals<out TInput = unknown, out TOutput = TInput> {
|
|
|
47
47
|
* - **Assertion methods**: `assert()`, `check()` - throw on invalid input
|
|
48
48
|
* - **Type guard methods**: `matches()`, `ifMatches()` - return boolean or optional value
|
|
49
49
|
* - **Parse methods**: `parse()`, `safeParse()` - allow value transformation/coercion
|
|
50
|
-
* - **Validate methods**: `validate()`, `safeValidate()` -
|
|
50
|
+
* - **Validate methods**: `validate()`, `safeValidate()` - validation without coercion
|
|
51
51
|
*
|
|
52
52
|
* All methods are also available with a `$` prefix (e.g., `$parse()`, `$validate()`)
|
|
53
53
|
* for consistent access in generated lexicon namespaces.
|
|
@@ -120,8 +120,11 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
|
|
|
120
120
|
* will typically arise in generic contexts, where the narrowed type is not
|
|
121
121
|
* needed.
|
|
122
122
|
*/
|
|
123
|
-
assert(
|
|
124
|
-
|
|
123
|
+
assert(
|
|
124
|
+
input: unknown,
|
|
125
|
+
options?: ValidateOptions,
|
|
126
|
+
): asserts input is InferInput<this> {
|
|
127
|
+
const result = this.safeValidate(input, options)
|
|
125
128
|
if (!result.success) throw result.reason
|
|
126
129
|
}
|
|
127
130
|
|
|
@@ -131,8 +134,8 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
|
|
|
131
134
|
* every name in the call target to be declared with an explicit type
|
|
132
135
|
* annotation. ts(2775)_" errors.
|
|
133
136
|
*/
|
|
134
|
-
check(input: unknown): void {
|
|
135
|
-
this.assert(input)
|
|
137
|
+
check(input: unknown, options?: ValidateOptions): void {
|
|
138
|
+
this.assert(input, options)
|
|
136
139
|
}
|
|
137
140
|
|
|
138
141
|
/**
|
|
@@ -140,8 +143,8 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
|
|
|
140
143
|
* schema, otherwise throws. This is the same as calling {@link parse}() with
|
|
141
144
|
* `mode: "validate"`.
|
|
142
145
|
*/
|
|
143
|
-
cast<I>(input: I): I & InferInput<this> {
|
|
144
|
-
const result =
|
|
146
|
+
cast<I>(input: I, options?: ValidateOptions): I & InferInput<this> {
|
|
147
|
+
const result = this.safeValidate(input, options)
|
|
145
148
|
if (result.success) return result.value
|
|
146
149
|
throw result.reason
|
|
147
150
|
}
|
|
@@ -149,9 +152,6 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
|
|
|
149
152
|
/**
|
|
150
153
|
* Type guard that checks if the input matches this schema.
|
|
151
154
|
*
|
|
152
|
-
* @param input - The value to check
|
|
153
|
-
* @returns `true` if the input is valid according to this schema
|
|
154
|
-
*
|
|
155
155
|
* @example
|
|
156
156
|
* ```typescript
|
|
157
157
|
* if (schema.matches(data)) {
|
|
@@ -160,8 +160,11 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
|
|
|
160
160
|
* }
|
|
161
161
|
* ```
|
|
162
162
|
*/
|
|
163
|
-
matches<I>(
|
|
164
|
-
|
|
163
|
+
matches<I>(
|
|
164
|
+
input: I,
|
|
165
|
+
options?: ValidateOptions,
|
|
166
|
+
): input is I & InferInput<this> {
|
|
167
|
+
const result = this.safeValidate(input, options)
|
|
165
168
|
return result.success
|
|
166
169
|
}
|
|
167
170
|
|
|
@@ -171,9 +174,6 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
|
|
|
171
174
|
* This is useful for optional filtering operations where you want to
|
|
172
175
|
* conditionally extract values that match a schema.
|
|
173
176
|
*
|
|
174
|
-
* @param input - The value to check
|
|
175
|
-
* @returns The input value with narrowed type if valid, otherwise `undefined`
|
|
176
|
-
*
|
|
177
177
|
* @example
|
|
178
178
|
* ```typescript
|
|
179
179
|
* const validData = schema.ifMatches(data)
|
|
@@ -183,8 +183,11 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
|
|
|
183
183
|
* }
|
|
184
184
|
* ```
|
|
185
185
|
*/
|
|
186
|
-
ifMatches<I>(
|
|
187
|
-
|
|
186
|
+
ifMatches<I>(
|
|
187
|
+
input: I,
|
|
188
|
+
options?: ValidateOptions,
|
|
189
|
+
): (I & InferInput<this>) | undefined {
|
|
190
|
+
return this.matches(input, options) ? input : undefined
|
|
188
191
|
}
|
|
189
192
|
|
|
190
193
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isValidISODateString } from 'iso-datestring-validator'
|
|
1
2
|
import { validateCidString } from '@atproto/lex-data'
|
|
2
3
|
import {
|
|
3
4
|
AtIdentifierString,
|
|
@@ -48,6 +49,26 @@ export {
|
|
|
48
49
|
isDatetimeString,
|
|
49
50
|
} from '@atproto/syntax'
|
|
50
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Matches any ISO-ish datetime string. This is a more lenient check than
|
|
54
|
+
* the strict {@link isDatetimeString} guard, which only allows datetimes that
|
|
55
|
+
* fully conform to the AT Protocol specification (e.g. must include timezone).
|
|
56
|
+
*/
|
|
57
|
+
export function isDatetimeStringLoose<I>(
|
|
58
|
+
input: I,
|
|
59
|
+
): input is I & DatetimeString {
|
|
60
|
+
// @NOTE the returned type assertion is inaccurate wrt. the DatetimeString
|
|
61
|
+
// type definition. A more accurate solution would be to use a branded type
|
|
62
|
+
// instead of a template literal for the "datetime" format
|
|
63
|
+
if (typeof input !== 'string') return false
|
|
64
|
+
try {
|
|
65
|
+
return isValidISODateString(input)
|
|
66
|
+
} catch {
|
|
67
|
+
// @NOTE isValidISODateString throws on some inputs
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
51
72
|
// DatetimeString utilities
|
|
52
73
|
export { currentDatetimeString, toDatetimeString } from '@atproto/syntax'
|
|
53
74
|
|
|
@@ -223,23 +244,39 @@ type StringFormats = {
|
|
|
223
244
|
export type StringFormat = Extract<keyof StringFormats, string>
|
|
224
245
|
|
|
225
246
|
const stringFormatVerifiers: {
|
|
226
|
-
readonly [K in StringFormat]:
|
|
247
|
+
readonly [K in StringFormat]: readonly [
|
|
248
|
+
strict: CheckFn<StringFormats[K]>,
|
|
249
|
+
loose?: CheckFn<StringFormats[K]>,
|
|
250
|
+
]
|
|
227
251
|
} = /*#__PURE__*/ Object.freeze({
|
|
228
252
|
__proto__: null,
|
|
229
253
|
|
|
230
|
-
'at-identifier': isAtIdentifierString,
|
|
231
|
-
'at-uri': isAtUriString,
|
|
232
|
-
cid: isCidString,
|
|
233
|
-
datetime: isDatetimeString,
|
|
234
|
-
did: isDidString,
|
|
235
|
-
handle: isHandleString,
|
|
236
|
-
language: isLanguageString,
|
|
237
|
-
nsid: isNsidString,
|
|
238
|
-
'record-key': isRecordKeyString,
|
|
239
|
-
tid: isTidString,
|
|
240
|
-
uri: isUriString,
|
|
254
|
+
'at-identifier': [isAtIdentifierString],
|
|
255
|
+
'at-uri': [isAtUriString],
|
|
256
|
+
cid: [isCidString],
|
|
257
|
+
datetime: [isDatetimeString, isDatetimeStringLoose],
|
|
258
|
+
did: [isDidString],
|
|
259
|
+
handle: [isHandleString],
|
|
260
|
+
language: [isLanguageString],
|
|
261
|
+
nsid: [isNsidString],
|
|
262
|
+
'record-key': [isRecordKeyString],
|
|
263
|
+
tid: [isTidString],
|
|
264
|
+
uri: [isUriString],
|
|
241
265
|
})
|
|
242
266
|
|
|
267
|
+
export type StringFormatValidationOptions = {
|
|
268
|
+
/**
|
|
269
|
+
* Allows to be more lenient in validation by using a "loose" verification
|
|
270
|
+
* function, if available. The behavior of the loose verifier depends on the
|
|
271
|
+
* specific format, but generally it may allow for a wider range of valid
|
|
272
|
+
* inputs, including values that are not compliant with the AT Protocol
|
|
273
|
+
* specification.
|
|
274
|
+
*
|
|
275
|
+
* @default true
|
|
276
|
+
*/
|
|
277
|
+
strict?: boolean
|
|
278
|
+
}
|
|
279
|
+
|
|
243
280
|
/**
|
|
244
281
|
* Infers the string type for a given format name.
|
|
245
282
|
*
|
|
@@ -277,12 +314,18 @@ export type InferStringFormat<F extends StringFormat> = F extends StringFormat
|
|
|
277
314
|
export function isStringFormat<I extends string, F extends StringFormat>(
|
|
278
315
|
input: I,
|
|
279
316
|
format: F,
|
|
317
|
+
options?: StringFormatValidationOptions,
|
|
280
318
|
): input is I & StringFormats[F] {
|
|
281
319
|
const formatVerifier = stringFormatVerifiers[format]
|
|
282
320
|
// Fool-proof
|
|
283
321
|
if (!formatVerifier) throw new TypeError(`Unknown string format: ${format}`)
|
|
284
322
|
|
|
285
|
-
|
|
323
|
+
const check: CheckFn<StringFormats[F]> =
|
|
324
|
+
options?.strict === false
|
|
325
|
+
? formatVerifier[1] ?? formatVerifier[0]
|
|
326
|
+
: formatVerifier[0]
|
|
327
|
+
|
|
328
|
+
return check(input)
|
|
286
329
|
}
|
|
287
330
|
|
|
288
331
|
/**
|
|
@@ -304,8 +347,9 @@ export function isStringFormat<I extends string, F extends StringFormat>(
|
|
|
304
347
|
export function assertStringFormat<I extends string, F extends StringFormat>(
|
|
305
348
|
input: I,
|
|
306
349
|
format: F,
|
|
350
|
+
options?: StringFormatValidationOptions,
|
|
307
351
|
): asserts input is I & StringFormats[F] {
|
|
308
|
-
if (!isStringFormat(input, format)) {
|
|
352
|
+
if (!isStringFormat(input, format, options)) {
|
|
309
353
|
throw new TypeError(`Invalid string format (${format}): ${input}`)
|
|
310
354
|
}
|
|
311
355
|
}
|
|
@@ -332,8 +376,9 @@ export function assertStringFormat<I extends string, F extends StringFormat>(
|
|
|
332
376
|
export function asStringFormat<I extends string, F extends StringFormat>(
|
|
333
377
|
input: I,
|
|
334
378
|
format: F,
|
|
379
|
+
options?: StringFormatValidationOptions,
|
|
335
380
|
): I & StringFormats[F] {
|
|
336
|
-
assertStringFormat(input, format)
|
|
381
|
+
assertStringFormat(input, format, options)
|
|
337
382
|
return input
|
|
338
383
|
}
|
|
339
384
|
|
|
@@ -361,8 +406,9 @@ export function asStringFormat<I extends string, F extends StringFormat>(
|
|
|
361
406
|
export function ifStringFormat<I extends string, F extends StringFormat>(
|
|
362
407
|
input: I,
|
|
363
408
|
format: F,
|
|
409
|
+
options?: StringFormatValidationOptions,
|
|
364
410
|
): undefined | (I & StringFormats[F]) {
|
|
365
|
-
return isStringFormat(input, format) ? input : undefined
|
|
411
|
+
return isStringFormat(input, format, options) ? input : undefined
|
|
366
412
|
}
|
|
367
413
|
|
|
368
414
|
/**
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
* new IssueInvalidType(['user', 'age'], 'hello', ['number'])
|
|
24
24
|
* ])
|
|
25
25
|
* console.log(error.message)
|
|
26
|
-
* // "Expected
|
|
26
|
+
* // "Expected integer value type (got "some-string") at $.user.age"
|
|
27
27
|
*
|
|
28
28
|
* console.log(error.issues.length) // 1
|
|
29
29
|
* console.log(error.toJSON())
|
|
@@ -46,7 +46,7 @@ export class LexValidationError
|
|
|
46
46
|
* Issues are aggregated when possible (e.g., multiple invalid type issues
|
|
47
47
|
* at the same path are combined into a single issue listing all expected types).
|
|
48
48
|
*/
|
|
49
|
-
readonly issues: Issue[]
|
|
49
|
+
readonly issues: readonly Issue[]
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Creates a new validation error from a list of issues.
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { ifCid, isLegacyBlobRef, isPlainObject } from '@atproto/lex-data'
|
|
2
2
|
|
|
3
|
+
const STRING_PREVIEW_MAX_LENGTH = 48
|
|
4
|
+
const STRING_PREVIEW_TRUNCATED_SUFFIX = '…'
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* Abstract base class for all validation issues.
|
|
5
8
|
*
|
|
@@ -120,7 +123,7 @@ export class IssueInvalidType extends Issue {
|
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
override get message(): string {
|
|
123
|
-
return `Expected ${oneOf(this.expected.map(stringifyExpectedType))} value type (got ${
|
|
126
|
+
return `Expected ${oneOf(this.expected.map(stringifyExpectedType))} value type (got ${stringifyValue(this.input)})`
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
toJSON() {
|
|
@@ -289,55 +292,36 @@ function oneOf(arr: readonly string[]): string {
|
|
|
289
292
|
return `one of ${arr.slice(0, -1).join(', ')} or ${arr.at(-1)}`
|
|
290
293
|
}
|
|
291
294
|
|
|
292
|
-
function stringifyType(value: unknown): string {
|
|
293
|
-
switch (typeof value) {
|
|
294
|
-
case 'object':
|
|
295
|
-
if (value === null) return 'null'
|
|
296
|
-
if (Array.isArray(value)) return 'array'
|
|
297
|
-
if (ifCid(value)) return 'cid'
|
|
298
|
-
if (isLegacyBlobRef(value)) return 'legacy-blob'
|
|
299
|
-
if (value instanceof Date) return 'date'
|
|
300
|
-
if (value instanceof RegExp) return 'regexp'
|
|
301
|
-
if (value instanceof Map) return 'map'
|
|
302
|
-
if (value instanceof Set) return 'set'
|
|
303
|
-
return 'object'
|
|
304
|
-
case 'number':
|
|
305
|
-
if (Number.isInteger(value) && Number.isSafeInteger(value)) {
|
|
306
|
-
return 'integer'
|
|
307
|
-
}
|
|
308
|
-
if (Number.isNaN(value)) {
|
|
309
|
-
return 'NaN'
|
|
310
|
-
}
|
|
311
|
-
if (value === Infinity) {
|
|
312
|
-
return 'Infinity'
|
|
313
|
-
}
|
|
314
|
-
if (value === -Infinity) {
|
|
315
|
-
return '-Infinity'
|
|
316
|
-
}
|
|
317
|
-
return 'float'
|
|
318
|
-
default:
|
|
319
|
-
return typeof value
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
295
|
function stringifyValue(value: unknown): string {
|
|
324
296
|
switch (typeof value) {
|
|
325
297
|
case 'bigint':
|
|
326
298
|
return `${value}n`
|
|
327
299
|
case 'number':
|
|
328
|
-
case 'string':
|
|
329
300
|
case 'boolean':
|
|
330
|
-
return
|
|
301
|
+
return String(value)
|
|
302
|
+
case 'string':
|
|
303
|
+
return JSON.stringify(
|
|
304
|
+
value.length < STRING_PREVIEW_MAX_LENGTH
|
|
305
|
+
? value
|
|
306
|
+
: `${value.slice(0, STRING_PREVIEW_MAX_LENGTH - STRING_PREVIEW_TRUNCATED_SUFFIX.length)}${STRING_PREVIEW_TRUNCATED_SUFFIX}`,
|
|
307
|
+
)
|
|
331
308
|
case 'object':
|
|
309
|
+
if (value === null) return 'null'
|
|
332
310
|
if (Array.isArray(value)) {
|
|
333
311
|
return `[${stringifyArray(value, stringifyValue)}]`
|
|
334
312
|
}
|
|
335
313
|
if (isPlainObject(value)) {
|
|
336
314
|
return `{${stringifyArray(Object.entries(value), stringifyObjectEntry)}}`
|
|
337
315
|
}
|
|
338
|
-
|
|
316
|
+
if (ifCid(value)) return 'cid'
|
|
317
|
+
if (isLegacyBlobRef(value)) return 'legacy-blob'
|
|
318
|
+
if (value instanceof Date) return 'date'
|
|
319
|
+
if (value instanceof RegExp) return 'regexp'
|
|
320
|
+
if (value instanceof Map) return 'map'
|
|
321
|
+
if (value instanceof Set) return 'set'
|
|
322
|
+
return 'object'
|
|
339
323
|
default:
|
|
340
|
-
return
|
|
324
|
+
return typeof value
|
|
341
325
|
}
|
|
342
326
|
}
|
|
343
327
|
|
package/src/core/validator.ts
CHANGED
|
@@ -152,11 +152,13 @@ export type ValidationOptions = {
|
|
|
152
152
|
/**
|
|
153
153
|
* The validation mode determining how transformations are handled.
|
|
154
154
|
*
|
|
155
|
-
* - `"validate"
|
|
155
|
+
* - `"validate"`: Strict validation where the result must be
|
|
156
156
|
* strictly equal to the input value. No transformations such as applying
|
|
157
157
|
* default values are allowed.
|
|
158
158
|
* - `"parse"`: Allows the schema to transform the input value, such as
|
|
159
159
|
* applying default values or performing type coercion.
|
|
160
|
+
*
|
|
161
|
+
* @default "validate"
|
|
160
162
|
*/
|
|
161
163
|
mode?: 'validate' | 'parse'
|
|
162
164
|
|
|
@@ -173,6 +175,17 @@ export type ValidationOptions = {
|
|
|
173
175
|
* ```
|
|
174
176
|
*/
|
|
175
177
|
path?: readonly PropertyKey[]
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Whether to enforce strict validation rules (e.g., MIME type matching, size
|
|
181
|
+
* limits, datetime format).
|
|
182
|
+
*
|
|
183
|
+
* This is typically useful to allow more lax validation when parsing server
|
|
184
|
+
* responses, while enforcing strict validation for user input.
|
|
185
|
+
*
|
|
186
|
+
* @default true
|
|
187
|
+
*/
|
|
188
|
+
strict?: boolean
|
|
176
189
|
}
|
|
177
190
|
|
|
178
191
|
/**
|
|
@@ -221,6 +234,7 @@ export class ValidationContext {
|
|
|
221
234
|
mode: 'parse'
|
|
222
235
|
},
|
|
223
236
|
): ValidationResult<InferOutput<V>>
|
|
237
|
+
|
|
224
238
|
/**
|
|
225
239
|
* Validates input against a validator in validate mode (default).
|
|
226
240
|
*
|
|
@@ -241,6 +255,7 @@ export class ValidationContext {
|
|
|
241
255
|
mode?: 'validate'
|
|
242
256
|
},
|
|
243
257
|
): ValidationResult<I & InferInput<V>>
|
|
258
|
+
|
|
244
259
|
/**
|
|
245
260
|
* Validates input against a validator with configurable options.
|
|
246
261
|
*
|
|
@@ -262,6 +277,7 @@ export class ValidationContext {
|
|
|
262
277
|
const context = new ValidationContext({
|
|
263
278
|
path: options?.path ?? [],
|
|
264
279
|
mode: options?.mode ?? 'validate',
|
|
280
|
+
strict: options?.strict ?? true,
|
|
265
281
|
})
|
|
266
282
|
return context.validate(input, validator)
|
|
267
283
|
}
|
package/src/helpers.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
Subscription,
|
|
10
10
|
object,
|
|
11
11
|
optional,
|
|
12
|
+
regexp,
|
|
12
13
|
string,
|
|
13
14
|
} from './schema.js'
|
|
14
15
|
|
|
@@ -103,7 +104,12 @@ export type InferMethodError<
|
|
|
103
104
|
M extends Procedure | Query | Subscription = Procedure | Query | Subscription,
|
|
104
105
|
> = M extends { errors: readonly (infer E extends string)[] } ? E : never
|
|
105
106
|
|
|
107
|
+
/**
|
|
108
|
+
* @see {@link https://atproto.com/specs/xrpc#error-responses}
|
|
109
|
+
*/
|
|
106
110
|
export const lexErrorDataSchema = object({
|
|
107
|
-
error
|
|
111
|
+
// type name of the error (generic ASCII constant, no whitespace)
|
|
112
|
+
error: regexp(/^[\w_-]+$/, 'Expected ASCII constant with no whitespace'),
|
|
113
|
+
// description of the error, appropriate for display to humans
|
|
108
114
|
message: optional(string()),
|
|
109
115
|
}) satisfies Schema<LexErrorData>
|
package/src/schema/array.test.ts
CHANGED