@atproto/lex-schema 0.0.13 → 0.0.15
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 +39 -0
- package/dist/core/schema.d.ts +27 -35
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +73 -54
- package/dist/core/schema.js.map +1 -1
- package/dist/core/standard-schema.d.ts +14 -0
- package/dist/core/standard-schema.d.ts.map +1 -0
- package/dist/core/standard-schema.js +27 -0
- package/dist/core/standard-schema.js.map +1 -0
- package/dist/core/string-format.d.ts +4 -28
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +23 -17
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/validation-error.d.ts +14 -6
- package/dist/core/validation-error.d.ts.map +1 -1
- package/dist/core/validation-error.js +18 -8
- package/dist/core/validation-error.js.map +1 -1
- package/dist/core/validation-issue.d.ts +15 -15
- package/dist/core/validation-issue.d.ts.map +1 -1
- package/dist/core/validation-issue.js +33 -29
- package/dist/core/validation-issue.js.map +1 -1
- package/dist/core/validator.d.ts +21 -18
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +5 -4
- package/dist/core/validator.js.map +1 -1
- package/dist/core.d.ts +0 -1
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +0 -1
- package/dist/core.js.map +1 -1
- package/dist/schema/custom.d.ts +1 -1
- package/dist/schema/custom.d.ts.map +1 -1
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/never.d.ts +1 -1
- package/dist/schema/nullable.d.ts +1 -1
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +4 -1
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/record.d.ts +12 -6
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +21 -12
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/ref.d.ts +1 -1
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/refine.d.ts +1 -1
- package/dist/schema/refine.d.ts.map +1 -1
- package/dist/schema/refine.js.map +1 -1
- package/dist/schema/typed-object.d.ts +12 -4
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +21 -12
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/typed-ref.d.ts +1 -1
- package/dist/schema/typed-union.d.ts +1 -1
- package/dist/schema/union.d.ts +2 -2
- package/dist/schema/union.d.ts.map +1 -1
- package/dist/schema/union.js +1 -1
- package/dist/schema/union.js.map +1 -1
- package/package.json +4 -3
- package/src/core/schema.ts +78 -69
- package/src/core/standard-schema.test.ts +124 -0
- package/src/core/standard-schema.ts +31 -0
- package/src/core/string-format.ts +26 -33
- package/src/core/validation-error.ts +25 -10
- package/src/core/validation-issue.ts +32 -32
- package/src/core/validator.ts +16 -12
- package/src/core.ts +0 -1
- package/src/schema/array.test.ts +2 -2
- package/src/schema/custom.ts +1 -7
- package/src/schema/params.test.ts +18 -2
- package/src/schema/params.ts +5 -2
- package/src/schema/record.ts +27 -22
- package/src/schema/ref.ts +1 -5
- package/src/schema/refine.ts +0 -1
- package/src/schema/typed-object.test.ts +38 -0
- package/src/schema/typed-object.ts +29 -24
- package/src/schema/union.ts +2 -2
- package/dist/core/property-key.d.ts +0 -2
- package/dist/core/property-key.d.ts.map +0 -1
- package/dist/core/property-key.js +0 -3
- package/dist/core/property-key.js.map +0 -1
- package/src/core/property-key.ts +0 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ifCid, isLegacyBlobRef, isPlainObject } from '@atproto/lex-data'
|
|
2
|
-
import { PropertyKey } from './property-key.js'
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Abstract base class for all validation issues.
|
|
@@ -9,10 +8,13 @@ import { PropertyKey } from './property-key.js'
|
|
|
9
8
|
* - The path to the invalid value in the data structure
|
|
10
9
|
* - The actual input value that failed validation
|
|
11
10
|
*
|
|
12
|
-
* Subclasses add specific properties relevant to each issue type and
|
|
13
|
-
*
|
|
11
|
+
* Subclasses add specific properties relevant to each issue type and implement
|
|
12
|
+
* the {@link message} property for human-readable error messages (that don't
|
|
13
|
+
* contain the error path)
|
|
14
14
|
*/
|
|
15
15
|
export abstract class Issue {
|
|
16
|
+
abstract readonly message: string
|
|
17
|
+
|
|
16
18
|
constructor(
|
|
17
19
|
readonly code: string,
|
|
18
20
|
readonly path: readonly PropertyKey[],
|
|
@@ -22,7 +24,9 @@ export abstract class Issue {
|
|
|
22
24
|
/**
|
|
23
25
|
* Returns a human-readable description of the validation issue.
|
|
24
26
|
*/
|
|
25
|
-
|
|
27
|
+
toString() {
|
|
28
|
+
return `${this.message}${stringifyPath(this.path)}`
|
|
29
|
+
}
|
|
26
30
|
|
|
27
31
|
/**
|
|
28
32
|
* Converts the issue to a JSON-serializable object.
|
|
@@ -33,7 +37,7 @@ export abstract class Issue {
|
|
|
33
37
|
return {
|
|
34
38
|
code: this.code,
|
|
35
39
|
path: this.path,
|
|
36
|
-
message: this.
|
|
40
|
+
message: this.message,
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
43
|
}
|
|
@@ -51,10 +55,6 @@ export class IssueCustom extends Issue {
|
|
|
51
55
|
) {
|
|
52
56
|
super('custom', path, input)
|
|
53
57
|
}
|
|
54
|
-
|
|
55
|
-
toString() {
|
|
56
|
-
return `${this.message}${stringifyPath(this.path)}`
|
|
57
|
-
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
@@ -67,20 +67,13 @@ export class IssueInvalidFormat extends Issue {
|
|
|
67
67
|
path: readonly PropertyKey[],
|
|
68
68
|
input: unknown,
|
|
69
69
|
readonly format: string,
|
|
70
|
-
readonly
|
|
70
|
+
readonly detail?: string,
|
|
71
71
|
) {
|
|
72
72
|
super('invalid_format', path, input)
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
return `Invalid ${this.formatDescription}${this.
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
toJSON() {
|
|
80
|
-
return {
|
|
81
|
-
...super.toJSON(),
|
|
82
|
-
format: this.format,
|
|
83
|
-
}
|
|
75
|
+
override get message(): string {
|
|
76
|
+
return `Invalid ${this.formatDescription}${this.detail ? ` (${this.detail}, ` : ' ('}got ${stringifyValue(this.input)})`
|
|
84
77
|
}
|
|
85
78
|
|
|
86
79
|
/** Returns a human-readable description of the expected format. */
|
|
@@ -102,6 +95,13 @@ export class IssueInvalidFormat extends Issue {
|
|
|
102
95
|
return this.format
|
|
103
96
|
}
|
|
104
97
|
}
|
|
98
|
+
|
|
99
|
+
toJSON() {
|
|
100
|
+
return {
|
|
101
|
+
...super.toJSON(),
|
|
102
|
+
format: this.format,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
@@ -119,8 +119,8 @@ export class IssueInvalidType extends Issue {
|
|
|
119
119
|
super('invalid_type', path, input)
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
return `Expected ${oneOf(this.expected.map(stringifyExpectedType))} value type
|
|
122
|
+
override get message(): string {
|
|
123
|
+
return `Expected ${oneOf(this.expected.map(stringifyExpectedType))} value type (got ${stringifyType(this.input)})`
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
toJSON() {
|
|
@@ -146,8 +146,8 @@ export class IssueInvalidValue extends Issue {
|
|
|
146
146
|
super('invalid_value', path, input)
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
return `Expected ${oneOf(this.values.map(stringifyValue))}
|
|
149
|
+
override get message(): string {
|
|
150
|
+
return `Expected ${oneOf(this.values.map(stringifyValue))} (got ${stringifyValue(this.input)})`
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
toJSON() {
|
|
@@ -170,8 +170,8 @@ export class IssueRequiredKey extends Issue {
|
|
|
170
170
|
super('required_key', path, input)
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
return `Missing required key "${String(this.key)}"
|
|
173
|
+
override get message(): string {
|
|
174
|
+
return `Missing required key "${String(this.key)}"`
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
toJSON() {
|
|
@@ -214,8 +214,8 @@ export class IssueTooBig extends Issue {
|
|
|
214
214
|
super('too_big', path, input)
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
|
|
218
|
-
return `${this.type} too big (maximum ${this.maximum}
|
|
217
|
+
override get message(): string {
|
|
218
|
+
return `${this.type} too big (maximum ${this.maximum}, got ${this.actual})`
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
toJSON() {
|
|
@@ -241,8 +241,8 @@ export class IssueTooSmall extends Issue {
|
|
|
241
241
|
super('too_small', path, input)
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
|
|
245
|
-
return `${this.type} too small (minimum ${this.minimum}
|
|
244
|
+
override get message(): string {
|
|
245
|
+
return `${this.type} too small (minimum ${this.minimum}, got ${this.actual})`
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
toJSON() {
|
|
@@ -274,9 +274,9 @@ function buildJsonPath(path: readonly PropertyKey[]): string {
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
function toJsonPathSegment(segment: PropertyKey): string {
|
|
277
|
-
if (typeof segment === 'number') {
|
|
278
|
-
return `[${segment}]`
|
|
279
|
-
} else if (/^[a-zA-Z_$][a-zA-Z0-9_]*$/.test(segment
|
|
277
|
+
if (typeof segment === 'number' || typeof segment === 'symbol') {
|
|
278
|
+
return `[${String(segment)}]`
|
|
279
|
+
} else if (/^[a-zA-Z_$][a-zA-Z0-9_]*$/.test(segment)) {
|
|
280
280
|
return `.${segment}`
|
|
281
281
|
} else {
|
|
282
282
|
return `[${JSON.stringify(segment)}]`
|
package/src/core/validator.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import { ResultFailure, ResultSuccess,
|
|
3
|
-
import {
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2
|
+
import { ResultFailure, ResultSuccess, success } from './result.js'
|
|
3
|
+
import { LexValidationError } from './validation-error.js'
|
|
4
4
|
import {
|
|
5
5
|
Issue,
|
|
6
6
|
IssueInvalidFormat,
|
|
@@ -20,16 +20,20 @@ import {
|
|
|
20
20
|
export type ValidationSuccess<Value = unknown> = ResultSuccess<Value>
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Represents a failed validation result containing a {@link
|
|
23
|
+
* Represents a failed validation result containing a {@link LexValidationError}.
|
|
24
|
+
*
|
|
25
|
+
* @extends ResultFailure<LexValidationError>
|
|
26
|
+
* @see {@link ResultFailure}
|
|
27
|
+
* @see {@link LexValidationError}
|
|
24
28
|
*/
|
|
25
|
-
export type ValidationFailure =
|
|
29
|
+
export type ValidationFailure = LexValidationError
|
|
26
30
|
|
|
27
31
|
/**
|
|
28
32
|
* Discriminated union representing the outcome of a validation operation.
|
|
29
33
|
*
|
|
30
34
|
* Check the `success` property to determine if validation passed or failed:
|
|
31
35
|
* - If `success` is `true`, the `value` property contains the validated data
|
|
32
|
-
* - If `success` is `false`, the `reason` property contains the {@link
|
|
36
|
+
* - If `success` is `false`, the `reason` property contains the {@link LexValidationError}
|
|
33
37
|
*
|
|
34
38
|
* @typeParam Value - The type of the validated value on success
|
|
35
39
|
*
|
|
@@ -39,7 +43,7 @@ export type ValidationFailure = ResultFailure<ValidationError>
|
|
|
39
43
|
* if (result.success) {
|
|
40
44
|
* // result.value is string
|
|
41
45
|
* } else {
|
|
42
|
-
* // result.reason is
|
|
46
|
+
* // result.reason is LexValidationError
|
|
43
47
|
* }
|
|
44
48
|
* ```
|
|
45
49
|
*/
|
|
@@ -326,7 +330,7 @@ export class ValidationContext {
|
|
|
326
330
|
if (this.issues.length > 0) {
|
|
327
331
|
// Validator returned a success but issues were added via the context.
|
|
328
332
|
// This means the overall validation failed.
|
|
329
|
-
return
|
|
333
|
+
return new LexValidationError(Array.from(this.issues))
|
|
330
334
|
}
|
|
331
335
|
|
|
332
336
|
if (this.options.mode !== 'parse' && !Object.is(result.value, input)) {
|
|
@@ -341,7 +345,7 @@ export class ValidationContext {
|
|
|
341
345
|
// another.
|
|
342
346
|
|
|
343
347
|
// This if block comes before the next one because 'this.issues' will
|
|
344
|
-
// end-up being appended to the returned
|
|
348
|
+
// end-up being appended to the returned LexValidationError (see the
|
|
345
349
|
// "failure" method below), resulting in a more complete error report.
|
|
346
350
|
return this.issueInvalidValue(input, [result.value])
|
|
347
351
|
}
|
|
@@ -425,8 +429,8 @@ export class ValidationContext {
|
|
|
425
429
|
* @param reason - The validation error
|
|
426
430
|
* @returns A failed validation result
|
|
427
431
|
*/
|
|
428
|
-
failure(reason:
|
|
429
|
-
return
|
|
432
|
+
failure(reason: LexValidationError): ValidationFailure {
|
|
433
|
+
return reason
|
|
430
434
|
}
|
|
431
435
|
|
|
432
436
|
/**
|
|
@@ -438,7 +442,7 @@ export class ValidationContext {
|
|
|
438
442
|
* @returns A failed validation result
|
|
439
443
|
*/
|
|
440
444
|
issue(issue: Issue) {
|
|
441
|
-
return this.failure(new
|
|
445
|
+
return this.failure(new LexValidationError([...this.issues, issue]))
|
|
442
446
|
}
|
|
443
447
|
|
|
444
448
|
/**
|
package/src/core.ts
CHANGED
package/src/schema/array.test.ts
CHANGED
|
@@ -77,11 +77,11 @@ describe('ArraySchema', () => {
|
|
|
77
77
|
it('rejects single values', () => {
|
|
78
78
|
const schema = array(string())
|
|
79
79
|
const result = schema.safeValidate(3)
|
|
80
|
-
expect(result).
|
|
80
|
+
expect(result).toMatchObject({
|
|
81
81
|
success: false,
|
|
82
82
|
reason: expect.objectContaining({
|
|
83
83
|
message: expect.stringContaining(
|
|
84
|
-
'Expected array value type
|
|
84
|
+
'Expected array value type (got integer) at $',
|
|
85
85
|
),
|
|
86
86
|
}),
|
|
87
87
|
})
|
package/src/schema/custom.ts
CHANGED
|
@@ -419,7 +419,7 @@ describe('ParamsSchema', () => {
|
|
|
419
419
|
['name', 'Alice'],
|
|
420
420
|
['bools', 'notabool'],
|
|
421
421
|
]),
|
|
422
|
-
).toThrow('Expected boolean value type
|
|
422
|
+
).toThrow('Expected boolean value type (got string) at $.bools')
|
|
423
423
|
|
|
424
424
|
expect(() =>
|
|
425
425
|
schema.fromURLSearchParams(
|
|
@@ -431,7 +431,23 @@ describe('ParamsSchema', () => {
|
|
|
431
431
|
path: ['foo', 'bar'],
|
|
432
432
|
},
|
|
433
433
|
),
|
|
434
|
-
).toThrow('Expected boolean value type at $.foo.bar.bools
|
|
434
|
+
).toThrow('Expected boolean value type (got string) at $.foo.bar.bools')
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
it('ignores empty string values', () => {
|
|
438
|
+
const result = schema.fromURLSearchParams([
|
|
439
|
+
['name', 'Alice'],
|
|
440
|
+
['extra', ''],
|
|
441
|
+
])
|
|
442
|
+
expect(result).toEqual({ name: 'Alice' })
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
it('ignores empty string values for known parameters', () => {
|
|
446
|
+
const result = schema.fromURLSearchParams([
|
|
447
|
+
['name', 'Alice'],
|
|
448
|
+
['age', ''],
|
|
449
|
+
])
|
|
450
|
+
expect(result).toEqual({ name: 'Alice' })
|
|
435
451
|
})
|
|
436
452
|
})
|
|
437
453
|
|
package/src/schema/params.ts
CHANGED
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
Issue,
|
|
7
7
|
IssueInvalidType,
|
|
8
8
|
IssueInvalidValue,
|
|
9
|
+
LexValidationError,
|
|
9
10
|
ParseOptions,
|
|
10
11
|
Schema,
|
|
11
12
|
ValidationContext,
|
|
12
|
-
ValidationError,
|
|
13
13
|
Validator,
|
|
14
14
|
WithOptionalProperties,
|
|
15
15
|
} from '../core.js'
|
|
@@ -209,6 +209,9 @@ export class ParamsSchema<
|
|
|
209
209
|
iterable instanceof URLSearchParams ? iterable.entries() : iterable
|
|
210
210
|
|
|
211
211
|
for (const [name, value] of entries) {
|
|
212
|
+
// Ignore empty strings
|
|
213
|
+
if (!value) continue
|
|
214
|
+
|
|
212
215
|
const validator = this.shapeValidators.get(name)
|
|
213
216
|
const innerValidator = validator ? unwrapSchema(validator) : undefined
|
|
214
217
|
const expectsArray = innerValidator instanceof ArraySchema
|
|
@@ -302,7 +305,7 @@ function coerceParam(
|
|
|
302
305
|
// the index of the param in case of array params (e.g. "tags[1]"), which
|
|
303
306
|
// could be helpful for debugging. The cost overhead is not worth it though
|
|
304
307
|
// (IMO).
|
|
305
|
-
throw new
|
|
308
|
+
throw new LexValidationError([issue])
|
|
306
309
|
}
|
|
307
310
|
|
|
308
311
|
function paramPath(key: string, options?: ParseOptions) {
|
package/src/schema/record.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
ValidationContext,
|
|
12
12
|
Validator,
|
|
13
13
|
} from '../core.js'
|
|
14
|
+
import { lazyProperty } from '../util/lazy-property.js'
|
|
14
15
|
import { literal } from './literal.js'
|
|
15
16
|
import { string } from './string.js'
|
|
16
17
|
|
|
@@ -70,10 +71,18 @@ export class RecordSchema<
|
|
|
70
71
|
this.keySchema = recordKey(key)
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
75
|
+
const result = ctx.validate(input, this.schema)
|
|
76
|
+
|
|
77
|
+
if (!result.success) {
|
|
78
|
+
return result
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (result.value.$type !== this.$type) {
|
|
82
|
+
return ctx.issueInvalidPropertyValue(result.value, '$type', [this.$type])
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return result
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
build(
|
|
@@ -82,30 +91,26 @@ export class RecordSchema<
|
|
|
82
91
|
return this.parse($typed(input, this.$type))
|
|
83
92
|
}
|
|
84
93
|
|
|
85
|
-
|
|
94
|
+
isTypeOf<TValue extends { $type?: unknown }>(
|
|
86
95
|
value: TValue,
|
|
87
96
|
): value is TypedRecord<TType, TValue> {
|
|
88
|
-
return this
|
|
97
|
+
return value.$type === this.$type
|
|
89
98
|
}
|
|
90
99
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Bound alias for {@link build} for compatibility with generated utilities.
|
|
102
|
+
* @see {@link build}
|
|
103
|
+
*/
|
|
104
|
+
get $build(): typeof this.build {
|
|
105
|
+
return lazyProperty(this, '$build', this.build.bind(this))
|
|
95
106
|
}
|
|
96
107
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (result.value.$type !== this.$type) {
|
|
105
|
-
return ctx.issueInvalidPropertyValue(result.value, '$type', [this.$type])
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return result
|
|
108
|
+
/**
|
|
109
|
+
* Bound alias for {@link isTypeOf} for compatibility with generated utilities.
|
|
110
|
+
* @see {@link isTypeOf}
|
|
111
|
+
*/
|
|
112
|
+
get $isTypeOf(): typeof this.isTypeOf {
|
|
113
|
+
return lazyProperty(this, '$isTypeOf', this.isTypeOf.bind(this))
|
|
109
114
|
}
|
|
110
115
|
}
|
|
111
116
|
|
package/src/schema/ref.ts
CHANGED
|
@@ -32,11 +32,7 @@ export type RefSchemaGetter<out TValidator extends Validator> = () => TValidator
|
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
34
|
export class RefSchema<const TValidator extends Validator>
|
|
35
|
-
extends Schema<
|
|
36
|
-
InferInput<TValidator>,
|
|
37
|
-
InferOutput<TValidator>,
|
|
38
|
-
TValidator['__lex']
|
|
39
|
-
>
|
|
35
|
+
extends Schema<InferInput<TValidator>, InferOutput<TValidator>>
|
|
40
36
|
implements WrappedValidator<TValidator>
|
|
41
37
|
{
|
|
42
38
|
readonly type = 'ref' as const
|
package/src/schema/refine.ts
CHANGED
|
@@ -332,6 +332,44 @@ describe('TypedObjectSchema', () => {
|
|
|
332
332
|
})
|
|
333
333
|
})
|
|
334
334
|
|
|
335
|
+
describe('bound $ methods', () => {
|
|
336
|
+
it('$build can be used as a detached function', () => {
|
|
337
|
+
const { $build } = schema
|
|
338
|
+
const result = $build({ text: 'Hello' })
|
|
339
|
+
expect(result).toEqual({
|
|
340
|
+
text: 'Hello',
|
|
341
|
+
$type: 'app.bsky.feed.post',
|
|
342
|
+
})
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
it('$isTypeOf can be used as a detached function', () => {
|
|
346
|
+
const { $isTypeOf } = schema
|
|
347
|
+
expect($isTypeOf({ text: 'Hello' })).toBe(true)
|
|
348
|
+
expect($isTypeOf({ $type: 'app.bsky.feed.post', text: 'Hello' })).toBe(
|
|
349
|
+
true,
|
|
350
|
+
)
|
|
351
|
+
expect($isTypeOf({ $type: 'other.type', text: 'Hello' })).toBe(false)
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
it('$parse can be used as a detached function', () => {
|
|
355
|
+
const { $parse } = schema
|
|
356
|
+
const result = $parse({ text: 'Hello' })
|
|
357
|
+
expect(result).toEqual({ text: 'Hello' })
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
it('$matches can be used as a detached function', () => {
|
|
361
|
+
const { $matches } = schema
|
|
362
|
+
expect($matches({ text: 'Hello' })).toBe(true)
|
|
363
|
+
expect($matches(42)).toBe(false)
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
it('lazy property returns the same function on repeated access', () => {
|
|
367
|
+
const fn1 = schema.$build
|
|
368
|
+
const fn2 = schema.$build
|
|
369
|
+
expect(fn1).toBe(fn2)
|
|
370
|
+
})
|
|
371
|
+
})
|
|
372
|
+
|
|
335
373
|
describe('with complex nested schemas', () => {
|
|
336
374
|
const complexSchema = typedObject(
|
|
337
375
|
'app.bsky.actor.profile',
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
ValidationContext,
|
|
15
15
|
Validator,
|
|
16
16
|
} from '../core.js'
|
|
17
|
+
import { lazyProperty } from '../util/lazy-property.js'
|
|
17
18
|
|
|
18
19
|
export type MaybeTypedObject<
|
|
19
20
|
TType extends $Type,
|
|
@@ -56,10 +57,20 @@ export class TypedObjectSchema<
|
|
|
56
57
|
super()
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
61
|
+
if (!isPlainObject(input)) {
|
|
62
|
+
return ctx.issueUnexpectedType(input, 'object')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
'$type' in input &&
|
|
67
|
+
input.$type !== undefined &&
|
|
68
|
+
input.$type !== this.$type
|
|
69
|
+
) {
|
|
70
|
+
return ctx.issueInvalidPropertyValue(input, '$type', [this.$type])
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return ctx.validate(input, this.schema)
|
|
63
74
|
}
|
|
64
75
|
|
|
65
76
|
build(
|
|
@@ -71,32 +82,26 @@ export class TypedObjectSchema<
|
|
|
71
82
|
>
|
|
72
83
|
}
|
|
73
84
|
|
|
74
|
-
|
|
85
|
+
isTypeOf<TValue extends Record<string, unknown>>(
|
|
75
86
|
value: TValue,
|
|
76
87
|
): value is MaybeTypedObject<TType, TValue> {
|
|
77
|
-
return this
|
|
88
|
+
return value.$type === undefined || value.$type === this.$type
|
|
78
89
|
}
|
|
79
90
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Bound alias for {@link build} for compatibility with generated utilities.
|
|
93
|
+
* @see {@link build}
|
|
94
|
+
*/
|
|
95
|
+
get $build(): typeof this.build {
|
|
96
|
+
return lazyProperty(this, '$build', this.build.bind(this))
|
|
84
97
|
}
|
|
85
98
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
'$type' in input &&
|
|
93
|
-
input.$type !== undefined &&
|
|
94
|
-
input.$type !== this.$type
|
|
95
|
-
) {
|
|
96
|
-
return ctx.issueInvalidPropertyValue(input, '$type', [this.$type])
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return ctx.validate(input, this.schema)
|
|
99
|
+
/**
|
|
100
|
+
* Bound alias for {@link isTypeOf} for compatibility with generated utilities.
|
|
101
|
+
* @see {@link isTypeOf}
|
|
102
|
+
*/
|
|
103
|
+
get $isTypeOf(): typeof this.isTypeOf {
|
|
104
|
+
return lazyProperty(this, '$isTypeOf', this.isTypeOf.bind(this))
|
|
100
105
|
}
|
|
101
106
|
}
|
|
102
107
|
|
package/src/schema/union.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
InferInput,
|
|
3
3
|
InferOutput,
|
|
4
|
+
LexValidationError,
|
|
4
5
|
Schema,
|
|
5
6
|
ValidationContext,
|
|
6
|
-
ValidationError,
|
|
7
7
|
ValidationFailure,
|
|
8
8
|
Validator,
|
|
9
9
|
} from '../core.js'
|
|
@@ -53,7 +53,7 @@ export class UnionSchema<
|
|
|
53
53
|
failures.push(result)
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
return ctx.failure(
|
|
56
|
+
return ctx.failure(LexValidationError.fromFailures(failures))
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"property-key.d.ts","sourceRoot":"","sources":["../../src/core/property-key.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"property-key.js","sourceRoot":"","sources":["../../src/core/property-key.ts"],"names":[],"mappings":"","sourcesContent":["export type PropertyKey = string | number\n"]}
|
package/src/core/property-key.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type PropertyKey = string | number
|