@atproto/lex-schema 0.0.1 → 0.0.3
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 +68 -0
- package/dist/core/$type.d.ts +6 -3
- package/dist/core/$type.d.ts.map +1 -1
- package/dist/core/$type.js +1 -0
- package/dist/core/$type.js.map +1 -1
- package/dist/core/record-key.d.ts +3 -3
- package/dist/core/record-key.d.ts.map +1 -1
- package/dist/core/record-key.js +12 -6
- package/dist/core/record-key.js.map +1 -1
- package/dist/core/result.d.ts.map +1 -1
- package/dist/core/result.js +6 -0
- package/dist/core/result.js.map +1 -1
- package/dist/core/string-format.d.ts +30 -27
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +56 -42
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/types.d.ts +9 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/external.d.ts +31 -28
- package/dist/external.d.ts.map +1 -1
- package/dist/external.js +33 -17
- package/dist/external.js.map +1 -1
- package/dist/schema/_parameters.d.ts +2 -2
- package/dist/schema/_parameters.d.ts.map +1 -1
- package/dist/schema/array.d.ts +5 -6
- package/dist/schema/array.d.ts.map +1 -1
- package/dist/schema/array.js +5 -6
- package/dist/schema/array.js.map +1 -1
- package/dist/schema/blob.d.ts +2 -3
- package/dist/schema/blob.d.ts.map +1 -1
- package/dist/schema/blob.js +1 -2
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/boolean.d.ts +4 -5
- package/dist/schema/boolean.d.ts.map +1 -1
- package/dist/schema/boolean.js +2 -3
- package/dist/schema/boolean.js.map +1 -1
- package/dist/schema/bytes.d.ts +3 -4
- package/dist/schema/bytes.d.ts.map +1 -1
- package/dist/schema/bytes.js +2 -3
- package/dist/schema/bytes.js.map +1 -1
- package/dist/schema/cid.d.ts +13 -6
- package/dist/schema/cid.d.ts.map +1 -1
- package/dist/schema/cid.js +2 -4
- package/dist/schema/cid.js.map +1 -1
- package/dist/schema/custom.d.ts +3 -4
- package/dist/schema/custom.d.ts.map +1 -1
- package/dist/schema/custom.js +4 -3
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/dict.d.ts +3 -3
- package/dist/schema/dict.d.ts.map +1 -1
- package/dist/schema/dict.js +1 -1
- package/dist/schema/dict.js.map +1 -1
- package/dist/schema/discriminated-union.d.ts +15 -24
- package/dist/schema/discriminated-union.d.ts.map +1 -1
- package/dist/schema/discriminated-union.js +40 -64
- package/dist/schema/discriminated-union.js.map +1 -1
- package/dist/schema/enum.d.ts +8 -4
- package/dist/schema/enum.d.ts.map +1 -1
- package/dist/schema/enum.js +5 -3
- package/dist/schema/enum.js.map +1 -1
- package/dist/schema/integer.d.ts +3 -4
- package/dist/schema/integer.d.ts.map +1 -1
- package/dist/schema/integer.js +3 -4
- package/dist/schema/integer.js.map +1 -1
- package/dist/schema/intersection.d.ts +22 -14
- package/dist/schema/intersection.d.ts.map +1 -1
- package/dist/schema/intersection.js +12 -22
- package/dist/schema/intersection.js.map +1 -1
- package/dist/schema/literal.d.ts +8 -4
- package/dist/schema/literal.d.ts.map +1 -1
- package/dist/schema/literal.js +5 -3
- package/dist/schema/literal.js.map +1 -1
- package/dist/schema/never.d.ts +2 -2
- package/dist/schema/never.d.ts.map +1 -1
- package/dist/schema/never.js +1 -1
- package/dist/schema/never.js.map +1 -1
- package/dist/schema/null.d.ts +2 -3
- package/dist/schema/null.d.ts.map +1 -1
- package/dist/schema/null.js +1 -2
- package/dist/schema/null.js.map +1 -1
- package/dist/schema/nullable.d.ts +7 -0
- package/dist/schema/nullable.d.ts.map +1 -0
- package/dist/schema/nullable.js +19 -0
- package/dist/schema/nullable.js.map +1 -0
- package/dist/schema/object.d.ts +10 -44
- package/dist/schema/object.d.ts.map +1 -1
- package/dist/schema/object.js +13 -56
- package/dist/schema/object.js.map +1 -1
- package/dist/schema/optional.d.ts +7 -0
- package/dist/schema/optional.d.ts.map +1 -0
- package/dist/schema/optional.js +25 -0
- package/dist/schema/optional.js.map +1 -0
- package/dist/schema/params.d.ts +14 -19
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +10 -24
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/payload.d.ts +4 -4
- package/dist/schema/payload.d.ts.map +1 -1
- package/dist/schema/payload.js.map +1 -1
- package/dist/schema/permission-set.d.ts +6 -6
- package/dist/schema/permission-set.d.ts.map +1 -1
- package/dist/schema/permission-set.js +1 -2
- package/dist/schema/permission-set.js.map +1 -1
- package/dist/schema/permission.d.ts +0 -1
- package/dist/schema/permission.d.ts.map +1 -1
- package/dist/schema/permission.js +0 -1
- package/dist/schema/permission.js.map +1 -1
- package/dist/schema/procedure.d.ts +8 -9
- package/dist/schema/procedure.d.ts.map +1 -1
- package/dist/schema/procedure.js +0 -1
- package/dist/schema/procedure.js.map +1 -1
- package/dist/schema/query.d.ts +7 -8
- package/dist/schema/query.d.ts.map +1 -1
- package/dist/schema/query.js +0 -1
- package/dist/schema/query.js.map +1 -1
- package/dist/schema/record.d.ts +34 -28
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +1 -2
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/ref.d.ts +2 -3
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js +1 -2
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/refine.d.ts +18 -0
- package/dist/schema/refine.d.ts.map +1 -0
- package/dist/schema/refine.js +33 -0
- package/dist/schema/refine.js.map +1 -0
- package/dist/schema/regexp.d.ts +7 -0
- package/dist/schema/regexp.d.ts.map +1 -0
- package/dist/schema/regexp.js +22 -0
- package/dist/schema/regexp.js.map +1 -0
- package/dist/schema/string.d.ts +4 -8
- package/dist/schema/string.d.ts.map +1 -1
- package/dist/schema/string.js +6 -3
- package/dist/schema/string.js.map +1 -1
- package/dist/schema/subscription.d.ts +7 -6
- package/dist/schema/subscription.d.ts.map +1 -1
- package/dist/schema/subscription.js.map +1 -1
- package/dist/schema/token.d.ts +2 -3
- package/dist/schema/token.d.ts.map +1 -1
- package/dist/schema/token.js +1 -2
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-object.d.ts +29 -27
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +1 -2
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/typed-ref.d.ts +2 -2
- package/dist/schema/typed-ref.d.ts.map +1 -1
- package/dist/schema/typed-ref.js +1 -1
- package/dist/schema/typed-ref.js.map +1 -1
- package/dist/schema/typed-union.d.ts +3 -4
- package/dist/schema/typed-union.d.ts.map +1 -1
- package/dist/schema/typed-union.js +3 -10
- package/dist/schema/typed-union.js.map +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/dist/schema/unknown-object.d.ts +2 -3
- package/dist/schema/unknown-object.d.ts.map +1 -1
- package/dist/schema/unknown-object.js +1 -2
- package/dist/schema/unknown-object.js.map +1 -1
- package/dist/schema/unknown.d.ts +2 -2
- package/dist/schema/unknown.d.ts.map +1 -1
- package/dist/schema/unknown.js +1 -1
- package/dist/schema/unknown.js.map +1 -1
- package/dist/schema.d.ts +4 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +6 -1
- package/dist/schema.js.map +1 -1
- package/dist/util/array-agg.d.ts.map +1 -1
- package/dist/util/array-agg.js +1 -0
- package/dist/util/array-agg.js.map +1 -1
- package/dist/util/lazy-property.d.ts +2 -0
- package/dist/util/lazy-property.d.ts.map +1 -0
- package/dist/util/lazy-property.js +14 -0
- package/dist/util/lazy-property.js.map +1 -0
- package/dist/validation/schema.d.ts +24 -0
- package/dist/validation/schema.d.ts.map +1 -0
- package/dist/validation/schema.js +57 -0
- package/dist/validation/schema.js.map +1 -0
- package/dist/validation/validation-error.d.ts +3 -3
- package/dist/validation/validation-error.d.ts.map +1 -1
- package/dist/validation/validation-error.js +32 -4
- package/dist/validation/validation-error.js.map +1 -1
- package/dist/validation/validation-issue.d.ts +32 -24
- package/dist/validation/validation-issue.d.ts.map +1 -1
- package/dist/validation/validation-issue.js +136 -92
- package/dist/validation/validation-issue.js.map +1 -1
- package/dist/validation/validator.d.ts +20 -50
- package/dist/validation/validator.d.ts.map +1 -1
- package/dist/validation/validator.js +40 -134
- package/dist/validation/validator.js.map +1 -1
- package/dist/validation.d.ts +1 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +1 -0
- package/dist/validation.js.map +1 -1
- package/package.json +8 -4
- package/src/core/$type.ts +7 -4
- package/src/core/record-key.ts +12 -5
- package/src/core/result.ts +6 -0
- package/src/core/string-format.ts +97 -61
- package/src/core/types.ts +12 -6
- package/src/external.ts +92 -70
- package/src/schema/_parameters.test.ts +416 -0
- package/src/schema/array.test.ts +237 -0
- package/src/schema/array.ts +17 -11
- package/src/schema/blob.test.ts +506 -0
- package/src/schema/blob.ts +3 -5
- package/src/schema/boolean.test.ts +116 -0
- package/src/schema/boolean.ts +5 -7
- package/src/schema/bytes.test.ts +226 -0
- package/src/schema/bytes.ts +4 -6
- package/src/schema/cid.test.ts +155 -0
- package/src/schema/cid.ts +14 -8
- package/src/schema/custom.test.ts +413 -0
- package/src/schema/custom.ts +10 -8
- package/src/schema/dict.test.ts +198 -0
- package/src/schema/dict.ts +6 -8
- package/src/schema/discriminated-union.test.ts +675 -0
- package/src/schema/discriminated-union.ts +68 -95
- package/src/schema/enum.test.ts +396 -0
- package/src/schema/enum.ts +12 -5
- package/src/schema/integer.test.ts +312 -0
- package/src/schema/integer.ts +5 -7
- package/src/schema/intersection.test.ts +32 -0
- package/src/schema/intersection.ts +37 -40
- package/src/schema/literal.test.ts +531 -0
- package/src/schema/literal.ts +12 -5
- package/src/schema/never.test.ts +174 -0
- package/src/schema/never.ts +3 -10
- package/src/schema/null.test.ts +79 -0
- package/src/schema/null.ts +3 -5
- package/src/schema/nullable.test.ts +480 -0
- package/src/schema/nullable.ts +23 -0
- package/src/schema/object.test.ts +47 -115
- package/src/schema/object.ts +23 -134
- package/src/schema/optional.test.ts +485 -0
- package/src/schema/optional.ts +31 -0
- package/src/schema/params.test.ts +582 -0
- package/src/schema/params.ts +37 -55
- package/src/schema/payload.test.ts +345 -0
- package/src/schema/payload.ts +5 -5
- package/src/schema/permission-set.test.ts +679 -0
- package/src/schema/permission-set.ts +6 -8
- package/src/schema/permission.test.ts +536 -0
- package/src/schema/permission.ts +0 -2
- package/src/schema/procedure.test.ts +443 -0
- package/src/schema/procedure.ts +11 -13
- package/src/schema/query.test.ts +408 -0
- package/src/schema/query.ts +9 -11
- package/src/schema/record.test.ts +694 -0
- package/src/schema/record.ts +38 -36
- package/src/schema/ref.test.ts +365 -0
- package/src/schema/ref.ts +8 -5
- package/src/schema/refine.test.ts +578 -0
- package/src/schema/refine.ts +85 -0
- package/src/schema/regexp.test.ts +580 -0
- package/src/schema/regexp.ts +22 -0
- package/src/schema/string.test.ts +612 -0
- package/src/schema/string.ts +11 -17
- package/src/schema/subscription.test.ts +689 -0
- package/src/schema/subscription.ts +13 -8
- package/src/schema/token.test.ts +428 -0
- package/src/schema/token.ts +3 -5
- package/src/schema/typed-object.test.ts +612 -0
- package/src/schema/typed-object.ts +23 -20
- package/src/schema/typed-ref.test.ts +823 -0
- package/src/schema/typed-ref.ts +10 -5
- package/src/schema/typed-union.test.ts +378 -0
- package/src/schema/typed-union.ts +6 -15
- package/src/schema/union.test.ts +200 -0
- package/src/schema/union.ts +5 -4
- package/src/schema/unknown-object.test.ts +592 -0
- package/src/schema/unknown-object.ts +3 -5
- package/src/schema/unknown.test.ts +312 -0
- package/src/schema/unknown.ts +3 -3
- package/src/schema.ts +7 -1
- package/src/util/array-agg.ts +1 -0
- package/src/util/lazy-property.ts +14 -0
- package/src/validation/schema.ts +92 -0
- package/src/validation/validation-error.ts +60 -9
- package/src/validation/validation-issue.ts +141 -144
- package/src/validation/validator.ts +67 -206
- package/src/validation.ts +1 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +7 -0
- package/tsconfig.tests.json +9 -0
package/src/schema/params.ts
CHANGED
|
@@ -1,64 +1,47 @@
|
|
|
1
1
|
import { isPlainObject } from '@atproto/lex-data'
|
|
2
|
-
import {
|
|
2
|
+
import { WithOptionalProperties } from '../core.js'
|
|
3
|
+
import { lazyProperty } from '../util/lazy-property.js'
|
|
4
|
+
import {
|
|
5
|
+
Infer,
|
|
6
|
+
Schema,
|
|
7
|
+
ValidationResult,
|
|
8
|
+
Validator,
|
|
9
|
+
ValidatorContext,
|
|
10
|
+
} from '../validation.js'
|
|
3
11
|
import { Param, ParamScalar, paramSchema } from './_parameters.js'
|
|
4
|
-
import { ObjectSchemaPropertiesOutput } from './object.js'
|
|
5
12
|
import { StringSchema } from './string.js'
|
|
6
13
|
|
|
7
|
-
export type
|
|
8
|
-
[_ in string]: Validator<Param>
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type ParamsSchemaOptions = {
|
|
12
|
-
required?: readonly string[]
|
|
13
|
-
}
|
|
14
|
+
export type ParamsSchemaShape = Record<string, Validator<Param | undefined>>
|
|
14
15
|
|
|
15
|
-
export type ParamsSchemaOutput<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
>
|
|
16
|
+
export type ParamsSchemaOutput<Shape extends ParamsSchemaShape> =
|
|
17
|
+
WithOptionalProperties<{
|
|
18
|
+
[K in keyof Shape]: Infer<Shape[K]>
|
|
19
|
+
}>
|
|
19
20
|
|
|
20
21
|
export type InferParamsSchema<T> =
|
|
21
|
-
T extends ParamsSchema<infer P
|
|
22
|
-
? NonNullable<unknown> extends ParamsSchemaOutput<P
|
|
23
|
-
? ParamsSchemaOutput<P
|
|
24
|
-
: ParamsSchemaOutput<P
|
|
22
|
+
T extends ParamsSchema<infer P>
|
|
23
|
+
? NonNullable<unknown> extends ParamsSchemaOutput<P>
|
|
24
|
+
? ParamsSchemaOutput<P> | undefined // Allow undefined if all params are optional
|
|
25
|
+
: ParamsSchemaOutput<P>
|
|
25
26
|
: never
|
|
26
27
|
|
|
27
28
|
export class ParamsSchema<
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Validators,
|
|
32
|
-
Options
|
|
33
|
-
>,
|
|
34
|
-
> extends Validator<Output> {
|
|
35
|
-
readonly lexiconType = 'params' as const
|
|
36
|
-
|
|
37
|
-
constructor(
|
|
38
|
-
readonly validators: Validators,
|
|
39
|
-
readonly options: Options,
|
|
40
|
-
) {
|
|
29
|
+
const Shape extends ParamsSchemaShape = ParamsSchemaShape,
|
|
30
|
+
> extends Schema<ParamsSchemaOutput<Shape>> {
|
|
31
|
+
constructor(readonly validators: Shape) {
|
|
41
32
|
super()
|
|
42
33
|
}
|
|
43
34
|
|
|
44
|
-
get validatorsMap(): Map<string, Validator<Param>> {
|
|
35
|
+
get validatorsMap(): Map<string, Validator<Param | undefined>> {
|
|
45
36
|
const map = new Map(Object.entries(this.validators))
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
Object.defineProperty(this, 'validatorsMap', {
|
|
49
|
-
value: map,
|
|
50
|
-
writable: false,
|
|
51
|
-
enumerable: false,
|
|
52
|
-
configurable: true,
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
return map
|
|
38
|
+
return lazyProperty(this, 'validatorsMap', map)
|
|
56
39
|
}
|
|
57
40
|
|
|
58
|
-
|
|
41
|
+
validateInContext(
|
|
59
42
|
input: unknown = {},
|
|
60
43
|
ctx: ValidatorContext,
|
|
61
|
-
): ValidationResult<
|
|
44
|
+
): ValidationResult<ParamsSchemaOutput<Shape>> {
|
|
62
45
|
if (!isPlainObject(input)) {
|
|
63
46
|
return ctx.issueInvalidType(input, 'object')
|
|
64
47
|
}
|
|
@@ -82,22 +65,19 @@ export class ParamsSchema<
|
|
|
82
65
|
for (const [key, propDef] of this.validatorsMap) {
|
|
83
66
|
const result = ctx.validateChild(input, key, propDef)
|
|
84
67
|
if (!result.success) {
|
|
85
|
-
// Because default values are provided by child validators, we need to
|
|
86
|
-
// run the validator to get the default value and, in case of failure,
|
|
87
|
-
// ignore validation error that were caused by missing keys.
|
|
88
68
|
if (!(key in input)) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
continue
|
|
92
|
-
} else {
|
|
93
|
-
// Transform into "required key" issue
|
|
94
|
-
return ctx.issueRequiredKey(input, key)
|
|
95
|
-
}
|
|
69
|
+
// Transform into "required key" issue
|
|
70
|
+
return ctx.issueRequiredKey(input, key)
|
|
96
71
|
}
|
|
97
72
|
|
|
98
73
|
return result
|
|
99
74
|
}
|
|
100
75
|
|
|
76
|
+
// Skip copying if key is not present in input (and value is undefined)
|
|
77
|
+
if (result.value === undefined && !(key in input)) {
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
|
|
101
81
|
if (result.value !== input[key]) {
|
|
102
82
|
// Copy on write
|
|
103
83
|
copy ??= { ...input }
|
|
@@ -105,10 +85,12 @@ export class ParamsSchema<
|
|
|
105
85
|
}
|
|
106
86
|
}
|
|
107
87
|
|
|
108
|
-
return ctx.success((copy ?? input) as
|
|
88
|
+
return ctx.success((copy ?? input) as ParamsSchemaOutput<Shape>)
|
|
109
89
|
}
|
|
110
90
|
|
|
111
|
-
fromURLSearchParams(
|
|
91
|
+
fromURLSearchParams(
|
|
92
|
+
urlSearchParams: URLSearchParams,
|
|
93
|
+
): ParamsSchemaOutput<Shape> {
|
|
112
94
|
const params: Record<string, Param> = {}
|
|
113
95
|
|
|
114
96
|
for (const [key, value] of urlSearchParams.entries()) {
|
|
@@ -137,7 +119,7 @@ export class ParamsSchema<
|
|
|
137
119
|
return this.parse(params)
|
|
138
120
|
}
|
|
139
121
|
|
|
140
|
-
toURLSearchParams(input:
|
|
122
|
+
toURLSearchParams(input: ParamsSchemaOutput<Shape>): URLSearchParams {
|
|
141
123
|
const urlSearchParams = new URLSearchParams()
|
|
142
124
|
|
|
143
125
|
if (input !== undefined) {
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { IntegerSchema } from './integer.js'
|
|
2
|
+
import { ObjectSchema } from './object.js'
|
|
3
|
+
import { Payload } from './payload.js'
|
|
4
|
+
import { StringSchema } from './string.js'
|
|
5
|
+
import { UnknownSchema } from './unknown.js'
|
|
6
|
+
|
|
7
|
+
describe('Payload', () => {
|
|
8
|
+
describe('basic construction', () => {
|
|
9
|
+
it('creates payload with encoding and no schema', () => {
|
|
10
|
+
const payload = new Payload('application/json', undefined)
|
|
11
|
+
expect(payload.encoding).toBe('application/json')
|
|
12
|
+
expect(payload.schema).toBeUndefined()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('creates payload with encoding and schema', () => {
|
|
16
|
+
const schema = new ObjectSchema({
|
|
17
|
+
name: new StringSchema({}),
|
|
18
|
+
})
|
|
19
|
+
const payload = new Payload('application/json', schema)
|
|
20
|
+
expect(payload.encoding).toBe('application/json')
|
|
21
|
+
expect(payload.schema).toBe(schema)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('creates payload with undefined encoding and undefined schema', () => {
|
|
25
|
+
const payload = new Payload(undefined, undefined)
|
|
26
|
+
expect(payload.encoding).toBeUndefined()
|
|
27
|
+
expect(payload.schema).toBeUndefined()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('creates payload with text encoding', () => {
|
|
31
|
+
const payload = new Payload('text/plain', undefined)
|
|
32
|
+
expect(payload.encoding).toBe('text/plain')
|
|
33
|
+
expect(payload.schema).toBeUndefined()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('creates payload with text/html encoding', () => {
|
|
37
|
+
const payload = new Payload('text/html', undefined)
|
|
38
|
+
expect(payload.encoding).toBe('text/html')
|
|
39
|
+
expect(payload.schema).toBeUndefined()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('creates payload with application/octet-stream encoding', () => {
|
|
43
|
+
const payload = new Payload('application/octet-stream', undefined)
|
|
44
|
+
expect(payload.encoding).toBe('application/octet-stream')
|
|
45
|
+
expect(payload.schema).toBeUndefined()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('creates payload with image encoding', () => {
|
|
49
|
+
const payload = new Payload('image/png', undefined)
|
|
50
|
+
expect(payload.encoding).toBe('image/png')
|
|
51
|
+
expect(payload.schema).toBeUndefined()
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
describe('encoding types', () => {
|
|
56
|
+
it('handles application/json encoding', () => {
|
|
57
|
+
const payload = new Payload('application/json', undefined)
|
|
58
|
+
expect(payload.encoding).toBe('application/json')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('handles text/* encodings', () => {
|
|
62
|
+
const textPlain = new Payload('text/plain', undefined)
|
|
63
|
+
expect(textPlain.encoding).toBe('text/plain')
|
|
64
|
+
|
|
65
|
+
const textHtml = new Payload('text/html', undefined)
|
|
66
|
+
expect(textHtml.encoding).toBe('text/html')
|
|
67
|
+
|
|
68
|
+
const textCss = new Payload('text/css', undefined)
|
|
69
|
+
expect(textCss.encoding).toBe('text/css')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('handles binary encodings', () => {
|
|
73
|
+
const octetStream = new Payload('application/octet-stream', undefined)
|
|
74
|
+
expect(octetStream.encoding).toBe('application/octet-stream')
|
|
75
|
+
|
|
76
|
+
const imagePng = new Payload('image/png', undefined)
|
|
77
|
+
expect(imagePng.encoding).toBe('image/png')
|
|
78
|
+
|
|
79
|
+
const imageJpeg = new Payload('image/jpeg', undefined)
|
|
80
|
+
expect(imageJpeg.encoding).toBe('image/jpeg')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('handles custom mime types', () => {
|
|
84
|
+
const payload = new Payload('application/vnd.custom+json', undefined)
|
|
85
|
+
expect(payload.encoding).toBe('application/vnd.custom+json')
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
describe('with schemas', () => {
|
|
90
|
+
it('creates payload with object schema', () => {
|
|
91
|
+
const schema = new ObjectSchema({
|
|
92
|
+
id: new IntegerSchema({}),
|
|
93
|
+
name: new StringSchema({}),
|
|
94
|
+
})
|
|
95
|
+
const payload = new Payload('application/json', schema)
|
|
96
|
+
expect(payload.encoding).toBe('application/json')
|
|
97
|
+
expect(payload.schema).toBe(schema)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('creates payload with string schema', () => {
|
|
101
|
+
const schema = new StringSchema({})
|
|
102
|
+
const payload = new Payload('application/json', schema)
|
|
103
|
+
expect(payload.encoding).toBe('application/json')
|
|
104
|
+
expect(payload.schema).toBe(schema)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('creates payload with integer schema', () => {
|
|
108
|
+
const schema = new IntegerSchema({})
|
|
109
|
+
const payload = new Payload('application/json', schema)
|
|
110
|
+
expect(payload.encoding).toBe('application/json')
|
|
111
|
+
expect(payload.schema).toBe(schema)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('creates payload with unknown schema', () => {
|
|
115
|
+
const schema = new UnknownSchema()
|
|
116
|
+
const payload = new Payload('application/json', schema)
|
|
117
|
+
expect(payload.encoding).toBe('application/json')
|
|
118
|
+
expect(payload.schema).toBe(schema)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('creates payload with complex nested schema', () => {
|
|
122
|
+
const schema = new ObjectSchema({
|
|
123
|
+
user: new ObjectSchema({
|
|
124
|
+
id: new IntegerSchema({}),
|
|
125
|
+
name: new StringSchema({}),
|
|
126
|
+
email: new StringSchema({ format: 'uri' }),
|
|
127
|
+
}),
|
|
128
|
+
metadata: new ObjectSchema({
|
|
129
|
+
createdAt: new StringSchema({ format: 'datetime' }),
|
|
130
|
+
updatedAt: new StringSchema({ format: 'datetime' }),
|
|
131
|
+
}),
|
|
132
|
+
})
|
|
133
|
+
const payload = new Payload('application/json', schema)
|
|
134
|
+
expect(payload.encoding).toBe('application/json')
|
|
135
|
+
expect(payload.schema).toBe(schema)
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
describe('validation constraints', () => {
|
|
140
|
+
it('throws error when schema is defined but encoding is undefined', () => {
|
|
141
|
+
const schema = new StringSchema({})
|
|
142
|
+
expect(() => {
|
|
143
|
+
// @ts-expect-error
|
|
144
|
+
new Payload(undefined, schema)
|
|
145
|
+
}).toThrow(TypeError)
|
|
146
|
+
expect(() => {
|
|
147
|
+
// @ts-expect-error
|
|
148
|
+
new Payload(undefined, schema)
|
|
149
|
+
}).toThrow('schema cannot be defined when encoding is undefined')
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('throws error when object schema is defined but encoding is undefined', () => {
|
|
153
|
+
const schema = new ObjectSchema({
|
|
154
|
+
name: new StringSchema({}),
|
|
155
|
+
})
|
|
156
|
+
expect(() => {
|
|
157
|
+
// @ts-expect-error
|
|
158
|
+
new Payload(undefined, schema)
|
|
159
|
+
}).toThrow(TypeError)
|
|
160
|
+
expect(() => {
|
|
161
|
+
// @ts-expect-error
|
|
162
|
+
new Payload(undefined, schema)
|
|
163
|
+
}).toThrow('schema cannot be defined when encoding is undefined')
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('throws error when integer schema is defined but encoding is undefined', () => {
|
|
167
|
+
const schema = new IntegerSchema({})
|
|
168
|
+
expect(() => {
|
|
169
|
+
// @ts-expect-error
|
|
170
|
+
new Payload(undefined, schema)
|
|
171
|
+
}).toThrow(TypeError)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('allows undefined encoding with undefined schema', () => {
|
|
175
|
+
expect(() => {
|
|
176
|
+
new Payload(undefined, undefined)
|
|
177
|
+
}).not.toThrow()
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('allows defined encoding with undefined schema', () => {
|
|
181
|
+
expect(() => {
|
|
182
|
+
new Payload('application/json', undefined)
|
|
183
|
+
}).not.toThrow()
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it('allows defined encoding with defined schema', () => {
|
|
187
|
+
const schema = new StringSchema({})
|
|
188
|
+
expect(() => {
|
|
189
|
+
new Payload('application/json', schema)
|
|
190
|
+
}).not.toThrow()
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
describe('property access', () => {
|
|
195
|
+
it('has accessible encoding property', () => {
|
|
196
|
+
const payload = new Payload('application/json', undefined)
|
|
197
|
+
expect(payload.encoding).toBe('application/json')
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('has accessible schema property', () => {
|
|
201
|
+
const schema = new StringSchema({})
|
|
202
|
+
const payload = new Payload('application/json', schema)
|
|
203
|
+
expect(payload.schema).toBe(schema)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('encoding property is immutable in TypeScript', () => {
|
|
207
|
+
const payload = new Payload('application/json', undefined)
|
|
208
|
+
// TypeScript enforces readonly at compile time
|
|
209
|
+
expect(payload.encoding).toBe('application/json')
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('schema property is immutable in TypeScript', () => {
|
|
213
|
+
const schema = new StringSchema({})
|
|
214
|
+
const payload = new Payload('application/json', schema)
|
|
215
|
+
// TypeScript enforces readonly at compile time
|
|
216
|
+
expect(payload.schema).toBe(schema)
|
|
217
|
+
})
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
describe('usage scenarios', () => {
|
|
221
|
+
it('creates payload for JSON API response', () => {
|
|
222
|
+
const payload = new Payload(
|
|
223
|
+
'application/json',
|
|
224
|
+
new ObjectSchema({
|
|
225
|
+
success: new StringSchema({}),
|
|
226
|
+
data: new UnknownSchema(),
|
|
227
|
+
}),
|
|
228
|
+
)
|
|
229
|
+
expect(payload.encoding).toBe('application/json')
|
|
230
|
+
expect(payload.schema).toBeDefined()
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('creates payload for plain text response', () => {
|
|
234
|
+
const payload = new Payload('text/plain', undefined)
|
|
235
|
+
expect(payload.encoding).toBe('text/plain')
|
|
236
|
+
expect(payload.schema).toBeUndefined()
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it('creates payload for binary data', () => {
|
|
240
|
+
const payload = new Payload('application/octet-stream', undefined)
|
|
241
|
+
expect(payload.encoding).toBe('application/octet-stream')
|
|
242
|
+
expect(payload.schema).toBeUndefined()
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it('creates payload for image upload', () => {
|
|
246
|
+
const payload = new Payload('image/jpeg', undefined)
|
|
247
|
+
expect(payload.encoding).toBe('image/jpeg')
|
|
248
|
+
expect(payload.schema).toBeUndefined()
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it('creates payload for multipart form data', () => {
|
|
252
|
+
const payload = new Payload('multipart/form-data', undefined)
|
|
253
|
+
expect(payload.encoding).toBe('multipart/form-data')
|
|
254
|
+
expect(payload.schema).toBeUndefined()
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it('creates payload for URL encoded form', () => {
|
|
258
|
+
const payload = new Payload(
|
|
259
|
+
'application/x-www-form-urlencoded',
|
|
260
|
+
undefined,
|
|
261
|
+
)
|
|
262
|
+
expect(payload.encoding).toBe('application/x-www-form-urlencoded')
|
|
263
|
+
expect(payload.schema).toBeUndefined()
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('creates empty payload (no encoding, no schema)', () => {
|
|
267
|
+
const payload = new Payload(undefined, undefined)
|
|
268
|
+
expect(payload.encoding).toBeUndefined()
|
|
269
|
+
expect(payload.schema).toBeUndefined()
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
describe('edge cases', () => {
|
|
274
|
+
it('handles encoding with charset parameter', () => {
|
|
275
|
+
const payload = new Payload('application/json; charset=utf-8', undefined)
|
|
276
|
+
expect(payload.encoding).toBe('application/json; charset=utf-8')
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('handles encoding with multiple parameters', () => {
|
|
280
|
+
const payload = new Payload(
|
|
281
|
+
'multipart/form-data; boundary=something',
|
|
282
|
+
undefined,
|
|
283
|
+
)
|
|
284
|
+
expect(payload.encoding).toBe('multipart/form-data; boundary=something')
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
it('handles empty string encoding', () => {
|
|
288
|
+
const payload = new Payload('', undefined)
|
|
289
|
+
expect(payload.encoding).toBe('')
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('creates multiple payloads with same schema reference', () => {
|
|
293
|
+
const sharedSchema = new ObjectSchema({
|
|
294
|
+
id: new IntegerSchema({}),
|
|
295
|
+
})
|
|
296
|
+
const payload1 = new Payload('application/json', sharedSchema)
|
|
297
|
+
const payload2 = new Payload('application/json', sharedSchema)
|
|
298
|
+
|
|
299
|
+
expect(payload1.schema).toBe(payload2.schema)
|
|
300
|
+
expect(payload1.schema).toBe(sharedSchema)
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
it('creates multiple payloads with different schemas', () => {
|
|
304
|
+
const schema1 = new StringSchema({})
|
|
305
|
+
const schema2 = new IntegerSchema({})
|
|
306
|
+
const payload1 = new Payload('application/json', schema1)
|
|
307
|
+
const payload2 = new Payload('application/json', schema2)
|
|
308
|
+
|
|
309
|
+
expect(payload1.schema).not.toBe(payload2.schema)
|
|
310
|
+
expect(payload1.schema).toBe(schema1)
|
|
311
|
+
expect(payload2.schema).toBe(schema2)
|
|
312
|
+
})
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
describe('type inference scenarios', () => {
|
|
316
|
+
it('works with application/json and object schema', () => {
|
|
317
|
+
const schema = new ObjectSchema({
|
|
318
|
+
message: new StringSchema({}),
|
|
319
|
+
})
|
|
320
|
+
const payload = new Payload('application/json', schema)
|
|
321
|
+
expect(payload.encoding).toBe('application/json')
|
|
322
|
+
expect(payload.schema).toBe(schema)
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
it('works with text/* encodings expecting string bodies', () => {
|
|
326
|
+
const payload1 = new Payload('text/plain', undefined)
|
|
327
|
+
const payload2 = new Payload('text/html', undefined)
|
|
328
|
+
const payload3 = new Payload('text/csv', undefined)
|
|
329
|
+
|
|
330
|
+
expect(payload1.encoding).toBe('text/plain')
|
|
331
|
+
expect(payload2.encoding).toBe('text/html')
|
|
332
|
+
expect(payload3.encoding).toBe('text/csv')
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
it('works with binary encodings expecting Uint8Array bodies', () => {
|
|
336
|
+
const payload1 = new Payload('image/png', undefined)
|
|
337
|
+
const payload2 = new Payload('application/octet-stream', undefined)
|
|
338
|
+
const payload3 = new Payload('video/mp4', undefined)
|
|
339
|
+
|
|
340
|
+
expect(payload1.encoding).toBe('image/png')
|
|
341
|
+
expect(payload2.encoding).toBe('application/octet-stream')
|
|
342
|
+
expect(payload3.encoding).toBe('video/mp4')
|
|
343
|
+
})
|
|
344
|
+
})
|
|
345
|
+
})
|
package/src/schema/payload.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LexValue } from '@atproto/lex-data'
|
|
2
|
-
import { Validator } from '../validation.js'
|
|
2
|
+
import { Infer, Validator } from '../validation.js'
|
|
3
3
|
|
|
4
4
|
export type LexBody<E extends string = any> = E extends `text/${string}`
|
|
5
5
|
? string // Text encodings always yield string bodies
|
|
@@ -12,8 +12,8 @@ export type InferPayloadEncoding<P extends Payload> =
|
|
|
12
12
|
|
|
13
13
|
export type InferPayloadBody<P extends Payload> =
|
|
14
14
|
P extends Payload<any, infer S>
|
|
15
|
-
? S extends Validator
|
|
16
|
-
?
|
|
15
|
+
? S extends Validator
|
|
16
|
+
? Infer<S>
|
|
17
17
|
: P extends Payload<infer E extends string>
|
|
18
18
|
? LexBody<E>
|
|
19
19
|
: undefined
|
|
@@ -23,10 +23,10 @@ export type PayloadOutput<
|
|
|
23
23
|
E extends string | undefined = any,
|
|
24
24
|
S extends Validator | undefined = any,
|
|
25
25
|
> = E extends string
|
|
26
|
-
? S extends Validator
|
|
26
|
+
? S extends Validator
|
|
27
27
|
? {
|
|
28
28
|
encoding: E
|
|
29
|
-
body:
|
|
29
|
+
body: Infer<S>
|
|
30
30
|
}
|
|
31
31
|
: {
|
|
32
32
|
encoding: E
|