@atproto/lex-schema 0.1.5 → 0.1.6
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 +14 -0
- package/dist/core/$type.d.ts +2 -2
- package/dist/core/$type.d.ts.map +1 -1
- package/dist/core/$type.js.map +1 -1
- package/dist/core/record-key.d.ts +1 -1
- package/dist/core/record-key.d.ts.map +1 -1
- package/dist/core/record-key.js.map +1 -1
- package/dist/core/schema.d.ts +3 -2
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +1 -1
- package/dist/core/schema.js.map +1 -1
- package/dist/core/standard-schema.d.ts +2 -2
- package/dist/core/standard-schema.d.ts.map +1 -1
- package/dist/core/standard-schema.js.map +1 -1
- package/dist/core/string-format.d.ts +2 -2
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/validation-error.d.ts +1 -1
- 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/validator.d.ts +1 -1
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +1 -1
- package/dist/core/validator.js.map +1 -1
- package/dist/helpers.d.ts +2 -2
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +2 -2
- package/dist/helpers.js.map +1 -1
- package/dist/schema/array.d.ts +1 -1
- package/dist/schema/array.d.ts.map +1 -1
- package/dist/schema/array.js +1 -1
- package/dist/schema/array.js.map +1 -1
- package/dist/schema/blob.d.ts +1 -1
- package/dist/schema/blob.d.ts.map +1 -1
- package/dist/schema/blob.js +2 -2
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/boolean.js +1 -1
- package/dist/schema/boolean.js.map +1 -1
- package/dist/schema/bytes.js +1 -1
- package/dist/schema/bytes.js.map +1 -1
- package/dist/schema/cid.d.ts +1 -1
- package/dist/schema/cid.d.ts.map +1 -1
- package/dist/schema/cid.js +3 -3
- package/dist/schema/cid.js.map +1 -1
- package/dist/schema/custom.js +1 -1
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/dict.d.ts +1 -1
- 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 +1 -1
- package/dist/schema/discriminated-union.d.ts.map +1 -1
- package/dist/schema/discriminated-union.js +2 -1
- package/dist/schema/discriminated-union.js.map +1 -1
- package/dist/schema/enum.js +1 -1
- package/dist/schema/enum.js.map +1 -1
- package/dist/schema/integer.js +1 -1
- package/dist/schema/integer.js.map +1 -1
- package/dist/schema/intersection.d.ts +1 -1
- package/dist/schema/intersection.d.ts.map +1 -1
- package/dist/schema/intersection.js +3 -1
- package/dist/schema/intersection.js.map +1 -1
- package/dist/schema/lex-map.d.ts +1 -1
- package/dist/schema/lex-map.d.ts.map +1 -1
- package/dist/schema/lex-map.js +1 -1
- package/dist/schema/lex-map.js.map +1 -1
- package/dist/schema/lex-value.d.ts +1 -1
- package/dist/schema/lex-value.d.ts.map +1 -1
- package/dist/schema/lex-value.js +1 -1
- package/dist/schema/lex-value.js.map +1 -1
- package/dist/schema/literal.js +1 -1
- package/dist/schema/literal.js.map +1 -1
- package/dist/schema/never.js +1 -1
- package/dist/schema/never.js.map +1 -1
- package/dist/schema/null.js +1 -1
- package/dist/schema/null.js.map +1 -1
- package/dist/schema/nullable.d.ts +1 -1
- package/dist/schema/nullable.d.ts.map +1 -1
- package/dist/schema/nullable.js +1 -1
- package/dist/schema/nullable.js.map +1 -1
- package/dist/schema/object.d.ts +2 -1
- package/dist/schema/object.d.ts.map +1 -1
- package/dist/schema/object.js +1 -1
- package/dist/schema/object.js.map +1 -1
- package/dist/schema/optional.d.ts +2 -1
- package/dist/schema/optional.d.ts.map +1 -1
- package/dist/schema/optional.js +2 -1
- package/dist/schema/optional.js.map +1 -1
- package/dist/schema/params.d.ts +1 -1
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +1 -1
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/payload.d.ts +3 -2
- package/dist/schema/payload.d.ts.map +1 -1
- package/dist/schema/payload.js +2 -1
- package/dist/schema/payload.js.map +1 -1
- package/dist/schema/permission-set.d.ts +1 -1
- package/dist/schema/permission-set.d.ts.map +1 -1
- package/dist/schema/permission-set.js +1 -0
- package/dist/schema/permission-set.js.map +1 -1
- package/dist/schema/permission.d.ts +1 -1
- package/dist/schema/permission.d.ts.map +1 -1
- package/dist/schema/permission.js.map +1 -1
- package/dist/schema/procedure.d.ts +1 -1
- package/dist/schema/procedure.d.ts.map +1 -1
- package/dist/schema/procedure.js +2 -0
- package/dist/schema/procedure.js.map +1 -1
- package/dist/schema/query.d.ts +1 -1
- package/dist/schema/query.d.ts.map +1 -1
- package/dist/schema/query.js +2 -0
- package/dist/schema/query.js.map +1 -1
- package/dist/schema/record.d.ts +2 -2
- 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/ref.d.ts +1 -1
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js +1 -1
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/refine.d.ts +2 -2
- package/dist/schema/refine.d.ts.map +1 -1
- package/dist/schema/refine.js +1 -1
- package/dist/schema/refine.js.map +1 -1
- package/dist/schema/regexp.js +1 -1
- package/dist/schema/regexp.js.map +1 -1
- package/dist/schema/string.d.ts +2 -2
- package/dist/schema/string.d.ts.map +1 -1
- package/dist/schema/string.js +1 -1
- package/dist/schema/string.js.map +1 -1
- package/dist/schema/subscription.d.ts +3 -2
- package/dist/schema/subscription.d.ts.map +1 -1
- package/dist/schema/subscription.js +2 -0
- package/dist/schema/subscription.js.map +1 -1
- package/dist/schema/token.d.ts +1 -1
- package/dist/schema/token.d.ts.map +1 -1
- package/dist/schema/token.js +1 -1
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-object.d.ts +2 -2
- 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/dist/schema/typed-ref.d.ts +1 -1
- 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 +1 -1
- package/dist/schema/typed-union.d.ts.map +1 -1
- package/dist/schema/typed-union.js +3 -1
- package/dist/schema/typed-union.js.map +1 -1
- package/dist/schema/union.d.ts +1 -1
- 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.js +1 -1
- package/dist/schema/unknown.js.map +1 -1
- package/dist/schema/with-default.d.ts +1 -1
- package/dist/schema/with-default.d.ts.map +1 -1
- package/dist/schema/with-default.js +1 -1
- package/dist/schema/with-default.js.map +1 -1
- package/package.json +6 -10
- package/src/core/$type.test.ts +0 -24
- package/src/core/$type.ts +0 -199
- package/src/core/record-key.ts +0 -85
- package/src/core/result.ts +0 -15
- package/src/core/schema.ts +0 -412
- package/src/core/standard-schema.test.ts +0 -124
- package/src/core/standard-schema.ts +0 -31
- package/src/core/string-format.ts +0 -411
- package/src/core/types.ts +0 -120
- package/src/core/validation-error.ts +0 -134
- package/src/core/validation-issue.ts +0 -340
- package/src/core/validator.ts +0 -636
- package/src/core.ts +0 -9
- package/src/external.ts +0 -3
- package/src/helpers.test.ts +0 -694
- package/src/helpers.ts +0 -222
- package/src/index.ts +0 -3
- package/src/schema/array.test.ts +0 -251
- package/src/schema/array.ts +0 -126
- package/src/schema/blob.test.ts +0 -733
- package/src/schema/blob.ts +0 -150
- package/src/schema/boolean.test.ts +0 -118
- package/src/schema/boolean.ts +0 -46
- package/src/schema/bytes.test.ts +0 -227
- package/src/schema/bytes.ts +0 -81
- package/src/schema/cid.test.ts +0 -125
- package/src/schema/cid.ts +0 -69
- package/src/schema/custom.test.ts +0 -414
- package/src/schema/custom.ts +0 -106
- package/src/schema/dict.test.ts +0 -181
- package/src/schema/dict.ts +0 -122
- package/src/schema/discriminated-union.test.ts +0 -676
- package/src/schema/discriminated-union.ts +0 -196
- package/src/schema/enum.test.ts +0 -398
- package/src/schema/enum.ts +0 -77
- package/src/schema/integer.test.ts +0 -314
- package/src/schema/integer.ts +0 -86
- package/src/schema/intersection.test.ts +0 -33
- package/src/schema/intersection.ts +0 -113
- package/src/schema/lex-map.test.ts +0 -593
- package/src/schema/lex-map.ts +0 -63
- package/src/schema/lex-value.test.ts +0 -81
- package/src/schema/lex-value.ts +0 -86
- package/src/schema/literal.test.ts +0 -533
- package/src/schema/literal.ts +0 -70
- package/src/schema/never.test.ts +0 -175
- package/src/schema/never.ts +0 -56
- package/src/schema/null.test.ts +0 -80
- package/src/schema/null.ts +0 -49
- package/src/schema/nullable.test.ts +0 -470
- package/src/schema/nullable.ts +0 -74
- package/src/schema/object.test.ts +0 -69
- package/src/schema/object.ts +0 -136
- package/src/schema/optional.test.ts +0 -479
- package/src/schema/optional.ts +0 -92
- package/src/schema/params.test.ts +0 -1118
- package/src/schema/params.ts +0 -371
- package/src/schema/payload.test.ts +0 -340
- package/src/schema/payload.ts +0 -204
- package/src/schema/permission-set.test.ts +0 -613
- package/src/schema/permission-set.ts +0 -86
- package/src/schema/permission.test.ts +0 -537
- package/src/schema/permission.ts +0 -63
- package/src/schema/procedure.test.ts +0 -324
- package/src/schema/procedure.ts +0 -98
- package/src/schema/query.test.ts +0 -348
- package/src/schema/query.ts +0 -86
- package/src/schema/record.test.ts +0 -812
- package/src/schema/record.ts +0 -217
- package/src/schema/ref.test.ts +0 -349
- package/src/schema/ref.ts +0 -103
- package/src/schema/refine.test.ts +0 -579
- package/src/schema/refine.ts +0 -153
- package/src/schema/regexp.test.ts +0 -577
- package/src/schema/regexp.ts +0 -82
- package/src/schema/string.test.ts +0 -773
- package/src/schema/string.ts +0 -229
- package/src/schema/subscription.test.ts +0 -499
- package/src/schema/subscription.ts +0 -108
- package/src/schema/token.test.ts +0 -152
- package/src/schema/token.ts +0 -103
- package/src/schema/typed-object.test.ts +0 -745
- package/src/schema/typed-object.ts +0 -181
- package/src/schema/typed-ref.test.ts +0 -796
- package/src/schema/typed-ref.ts +0 -126
- package/src/schema/typed-union.test.ts +0 -355
- package/src/schema/typed-union.ts +0 -130
- package/src/schema/union.test.ts +0 -191
- package/src/schema/union.ts +0 -89
- package/src/schema/unknown.test.ts +0 -313
- package/src/schema/unknown.ts +0 -47
- package/src/schema/with-default.ts +0 -81
- package/src/schema.ts +0 -43
- package/src/util/array-agg.test.ts +0 -42
- package/src/util/array-agg.ts +0 -44
- package/src/util/assertion-util.ts +0 -1
- package/src/util/if-any.ts +0 -3
- package/src/util/lazy-property.ts +0 -14
- package/src/util/memoize.ts +0 -37
- package/tsconfig.build.json +0 -12
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -8
package/src/schema/typed-ref.ts
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
$Typed,
|
|
3
|
-
InferInput,
|
|
4
|
-
InferOutput,
|
|
5
|
-
Schema,
|
|
6
|
-
ValidationContext,
|
|
7
|
-
Validator,
|
|
8
|
-
} from '../core.js'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Interface for validators that have a $type property.
|
|
12
|
-
*
|
|
13
|
-
* Used by typed objects and records to identify their type in unions.
|
|
14
|
-
*
|
|
15
|
-
* @template TInput - The input type (with optional $type)
|
|
16
|
-
* @template TOutput - The output type (with non-optional $type)
|
|
17
|
-
*/
|
|
18
|
-
export interface TypedObjectValidator<
|
|
19
|
-
TInput extends { $type?: string } = { $type?: string },
|
|
20
|
-
TOutput extends TInput = TInput,
|
|
21
|
-
> extends Validator<TInput, TOutput> {
|
|
22
|
-
$type: NonNullable<TOutput['$type']>
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Function type that returns a typed object validator, used for lazy resolution.
|
|
27
|
-
*
|
|
28
|
-
* @template TValidator - The typed object validator type
|
|
29
|
-
*/
|
|
30
|
-
export type TypedRefGetter<out TValidator extends TypedObjectValidator> =
|
|
31
|
-
() => TValidator
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Schema for referencing typed objects with lazy resolution.
|
|
35
|
-
*
|
|
36
|
-
* Used in typed unions to reference typed object schemas. Requires the
|
|
37
|
-
* `$type` field to be present and match the referenced schema's type.
|
|
38
|
-
* The referenced schema is resolved lazily to support circular references.
|
|
39
|
-
*
|
|
40
|
-
* @template TValidator - The referenced typed object validator type
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* ```ts
|
|
44
|
-
* const ref = new TypedRefSchema(() => imageViewSchema)
|
|
45
|
-
* // ref.$type === 'app.bsky.embed.images#view'
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
|
-
export class TypedRefSchema<
|
|
49
|
-
const TValidator extends TypedObjectValidator = TypedObjectValidator,
|
|
50
|
-
> extends Schema<
|
|
51
|
-
$Typed<InferInput<TValidator>>,
|
|
52
|
-
$Typed<InferOutput<TValidator>>
|
|
53
|
-
> {
|
|
54
|
-
readonly type = 'typedRef' as const
|
|
55
|
-
|
|
56
|
-
#getter: TypedRefGetter<TValidator>
|
|
57
|
-
|
|
58
|
-
constructor(getter: TypedRefGetter<TValidator>) {
|
|
59
|
-
// @NOTE In order to avoid circular dependency issues, we don't resolve
|
|
60
|
-
// the schema here. Instead, we resolve it lazily when first accessed.
|
|
61
|
-
|
|
62
|
-
super()
|
|
63
|
-
|
|
64
|
-
this.#getter = getter
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
get validator(): TValidator {
|
|
68
|
-
return this.#getter.call(null)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
get $type(): TValidator['$type'] {
|
|
72
|
-
return this.validator.$type
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
76
|
-
const result = ctx.validate(input, this.validator)
|
|
77
|
-
if (!result.success) return result
|
|
78
|
-
|
|
79
|
-
if (result.value.$type !== this.$type) {
|
|
80
|
-
return ctx.issueInvalidPropertyValue(result.value, '$type', [this.$type])
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return result
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Creates a reference to a typed object schema for use in typed unions.
|
|
89
|
-
*
|
|
90
|
-
* Unlike regular `ref()`, this requires the referenced schema to have a
|
|
91
|
-
* `$type` property, and validates that the input's `$type` matches.
|
|
92
|
-
*
|
|
93
|
-
* @param get - Function that returns the typed object validator
|
|
94
|
-
* @returns A new {@link TypedRefSchema} instance
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* ```ts
|
|
98
|
-
* // Reference to image embed view
|
|
99
|
-
* const imageRef = l.typedRef(() => imageViewSchema)
|
|
100
|
-
*
|
|
101
|
-
* // Use in a typed union
|
|
102
|
-
* const embedUnion = l.typedUnion([
|
|
103
|
-
* l.typedRef(() => imageViewSchema),
|
|
104
|
-
* l.typedRef(() => videoViewSchema),
|
|
105
|
-
* l.typedRef(() => externalViewSchema),
|
|
106
|
-
* ], true) // closed union
|
|
107
|
-
*
|
|
108
|
-
* // The $type is accessible on the ref
|
|
109
|
-
* console.log(imageRef.$type) // 'app.bsky.embed.images#view'
|
|
110
|
-
* ```
|
|
111
|
-
*/
|
|
112
|
-
/*@__NO_SIDE_EFFECTS__*/
|
|
113
|
-
export function typedRef<const TValidator extends TypedObjectValidator>(
|
|
114
|
-
get: TypedRefGetter<TValidator>,
|
|
115
|
-
): TypedRefSchema<TValidator>
|
|
116
|
-
export function typedRef<
|
|
117
|
-
TInput extends { $type?: string },
|
|
118
|
-
TOutput extends TInput = TInput,
|
|
119
|
-
>(
|
|
120
|
-
get: TypedRefGetter<TypedObjectValidator<TInput, TOutput>>,
|
|
121
|
-
): TypedRefSchema<TypedObjectValidator<TInput, TOutput>>
|
|
122
|
-
export function typedRef<const TValidator extends TypedObjectValidator>(
|
|
123
|
-
get: TypedRefGetter<TValidator>,
|
|
124
|
-
): TypedRefSchema<TValidator> {
|
|
125
|
-
return new TypedRefSchema<TValidator>(get)
|
|
126
|
-
}
|
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { integer } from './integer.js'
|
|
3
|
-
import { object } from './object.js'
|
|
4
|
-
import { string } from './string.js'
|
|
5
|
-
import { typedObject } from './typed-object.js'
|
|
6
|
-
import { typedRef } from './typed-ref.js'
|
|
7
|
-
import { typedUnion } from './typed-union.js'
|
|
8
|
-
|
|
9
|
-
describe('TypedUnionSchema', () => {
|
|
10
|
-
const personSchema = typedObject(
|
|
11
|
-
'app.bsky.actor.person',
|
|
12
|
-
'main',
|
|
13
|
-
object({
|
|
14
|
-
name: string(),
|
|
15
|
-
age: integer(),
|
|
16
|
-
}),
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
const postSchema = typedObject(
|
|
20
|
-
'app.bsky.feed.post',
|
|
21
|
-
'main',
|
|
22
|
-
object({
|
|
23
|
-
text: string(),
|
|
24
|
-
createdAt: string(),
|
|
25
|
-
}),
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
const commentSchema = typedObject(
|
|
29
|
-
'app.bsky.feed.comment',
|
|
30
|
-
'main',
|
|
31
|
-
object({
|
|
32
|
-
text: string(),
|
|
33
|
-
parentUri: string(),
|
|
34
|
-
}),
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
describe('closed union', () => {
|
|
38
|
-
const schema = typedUnion(
|
|
39
|
-
[typedRef(() => personSchema), typedRef(() => postSchema)],
|
|
40
|
-
true,
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
it('validates first type in union', () => {
|
|
44
|
-
const result = schema.safeParse({
|
|
45
|
-
$type: 'app.bsky.actor.person',
|
|
46
|
-
name: 'Alice',
|
|
47
|
-
age: 30,
|
|
48
|
-
})
|
|
49
|
-
expect(result.success).toBe(true)
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('validates second type in union', () => {
|
|
53
|
-
const result = schema.safeParse({
|
|
54
|
-
$type: 'app.bsky.feed.post',
|
|
55
|
-
text: 'Hello world',
|
|
56
|
-
createdAt: '2023-01-01T00:00:00Z',
|
|
57
|
-
})
|
|
58
|
-
expect(result.success).toBe(true)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('rejects unknown $type in closed union', () => {
|
|
62
|
-
const result = schema.safeParse({
|
|
63
|
-
$type: 'app.bsky.feed.like',
|
|
64
|
-
subject: 'some-uri',
|
|
65
|
-
})
|
|
66
|
-
expect(result.success).toBe(false)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('rejects object without $type', () => {
|
|
70
|
-
const result = schema.safeParse({
|
|
71
|
-
name: 'Alice',
|
|
72
|
-
age: 30,
|
|
73
|
-
})
|
|
74
|
-
expect(result.success).toBe(false)
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
it('rejects object with invalid property for the $type', () => {
|
|
78
|
-
const result = schema.safeParse({
|
|
79
|
-
$type: 'app.bsky.actor.person',
|
|
80
|
-
name: 'Alice',
|
|
81
|
-
age: 'thirty',
|
|
82
|
-
})
|
|
83
|
-
expect(result.success).toBe(false)
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it('rejects object missing required properties', () => {
|
|
87
|
-
const result = schema.safeParse({
|
|
88
|
-
$type: 'app.bsky.actor.person',
|
|
89
|
-
name: 'Alice',
|
|
90
|
-
})
|
|
91
|
-
expect(result.success).toBe(false)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('rejects non-object input', () => {
|
|
95
|
-
const result = schema.safeParse('not an object')
|
|
96
|
-
expect(result.success).toBe(false)
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it('rejects null', () => {
|
|
100
|
-
const result = schema.safeParse(null)
|
|
101
|
-
expect(result.success).toBe(false)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('rejects undefined', () => {
|
|
105
|
-
const result = schema.safeParse(undefined)
|
|
106
|
-
expect(result.success).toBe(false)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
it('rejects array', () => {
|
|
110
|
-
const result = schema.safeParse([])
|
|
111
|
-
expect(result.success).toBe(false)
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
it('rejects number', () => {
|
|
115
|
-
const result = schema.safeParse(42)
|
|
116
|
-
expect(result.success).toBe(false)
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it('rejects boolean', () => {
|
|
120
|
-
const result = schema.safeParse(true)
|
|
121
|
-
expect(result.success).toBe(false)
|
|
122
|
-
})
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
describe('open union', () => {
|
|
126
|
-
const schema = typedUnion(
|
|
127
|
-
[typedRef(() => personSchema), typedRef(() => postSchema)],
|
|
128
|
-
false,
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
it('validates known type', () => {
|
|
132
|
-
const result = schema.safeParse({
|
|
133
|
-
$type: 'app.bsky.actor.person',
|
|
134
|
-
name: 'Alice',
|
|
135
|
-
age: 30,
|
|
136
|
-
})
|
|
137
|
-
expect(result.success).toBe(true)
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
it('validates unknown $type with valid structure', () => {
|
|
141
|
-
const result = schema.safeParse({
|
|
142
|
-
$type: 'app.bsky.feed.like',
|
|
143
|
-
subject: 'some-uri',
|
|
144
|
-
createdAt: '2023-01-01T00:00:00Z',
|
|
145
|
-
})
|
|
146
|
-
expect(result.success).toBe(true)
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
it('accepts any properties for unknown $type', () => {
|
|
150
|
-
const result = schema.safeParse({
|
|
151
|
-
$type: 'unknown.nsid.type',
|
|
152
|
-
anyProperty: 'any value',
|
|
153
|
-
anotherProperty: 123,
|
|
154
|
-
})
|
|
155
|
-
expect(result.success).toBe(true)
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
it('rejects unknown $type with non-string $type', () => {
|
|
159
|
-
const result = schema.safeParse({
|
|
160
|
-
$type: 123,
|
|
161
|
-
someProperty: 'value',
|
|
162
|
-
})
|
|
163
|
-
expect(result.success).toBe(false)
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
it('rejects unknown $type with null $type', () => {
|
|
167
|
-
const result = schema.safeParse({
|
|
168
|
-
$type: null,
|
|
169
|
-
someProperty: 'value',
|
|
170
|
-
})
|
|
171
|
-
expect(result.success).toBe(false)
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
it('rejects object without $type', () => {
|
|
175
|
-
const result = schema.safeParse({
|
|
176
|
-
name: 'Alice',
|
|
177
|
-
age: 30,
|
|
178
|
-
})
|
|
179
|
-
expect(result.success).toBe(false)
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
it('rejects non-object input', () => {
|
|
183
|
-
const result = schema.safeParse('not an object')
|
|
184
|
-
expect(result.success).toBe(false)
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
it('validates known type with extra properties', () => {
|
|
188
|
-
const result = schema.safeParse({
|
|
189
|
-
$type: 'app.bsky.actor.person',
|
|
190
|
-
name: 'Alice',
|
|
191
|
-
age: 30,
|
|
192
|
-
extraProperty: 'extra',
|
|
193
|
-
})
|
|
194
|
-
expect(result.success).toBe(true)
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
it('rejects known type with invalid property types', () => {
|
|
198
|
-
const result = schema.safeParse({
|
|
199
|
-
$type: 'app.bsky.actor.person',
|
|
200
|
-
name: 123,
|
|
201
|
-
age: 30,
|
|
202
|
-
})
|
|
203
|
-
expect(result.success).toBe(false)
|
|
204
|
-
})
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
describe('with three types', () => {
|
|
208
|
-
const schema = typedUnion(
|
|
209
|
-
[
|
|
210
|
-
typedRef(() => personSchema),
|
|
211
|
-
typedRef(() => postSchema),
|
|
212
|
-
typedRef(() => commentSchema),
|
|
213
|
-
],
|
|
214
|
-
true,
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
it('validates first type', () => {
|
|
218
|
-
const result = schema.safeParse({
|
|
219
|
-
$type: 'app.bsky.actor.person',
|
|
220
|
-
name: 'Alice',
|
|
221
|
-
age: 30,
|
|
222
|
-
})
|
|
223
|
-
expect(result.success).toBe(true)
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
it('validates second type', () => {
|
|
227
|
-
const result = schema.safeParse({
|
|
228
|
-
$type: 'app.bsky.feed.post',
|
|
229
|
-
text: 'Hello',
|
|
230
|
-
createdAt: '2023-01-01T00:00:00Z',
|
|
231
|
-
})
|
|
232
|
-
expect(result.success).toBe(true)
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
it('validates third type', () => {
|
|
236
|
-
const result = schema.safeParse({
|
|
237
|
-
$type: 'app.bsky.feed.comment',
|
|
238
|
-
text: 'Nice post!',
|
|
239
|
-
parentUri: 'at://did:plc:xyz/app.bsky.feed.post/123',
|
|
240
|
-
})
|
|
241
|
-
expect(result.success).toBe(true)
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
it('rejects unknown type', () => {
|
|
245
|
-
const result = schema.safeParse({
|
|
246
|
-
$type: 'app.bsky.feed.like',
|
|
247
|
-
subject: 'some-uri',
|
|
248
|
-
})
|
|
249
|
-
expect(result.success).toBe(false)
|
|
250
|
-
})
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
describe('with single type', () => {
|
|
254
|
-
const schema = typedUnion([typedRef(() => personSchema)], true)
|
|
255
|
-
|
|
256
|
-
it('validates the single type', () => {
|
|
257
|
-
const result = schema.safeParse({
|
|
258
|
-
$type: 'app.bsky.actor.person',
|
|
259
|
-
name: 'Alice',
|
|
260
|
-
age: 30,
|
|
261
|
-
})
|
|
262
|
-
expect(result.success).toBe(true)
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
it('rejects different type', () => {
|
|
266
|
-
const result = schema.safeParse({
|
|
267
|
-
$type: 'app.bsky.feed.post',
|
|
268
|
-
text: 'Hello',
|
|
269
|
-
createdAt: '2023-01-01T00:00:00Z',
|
|
270
|
-
})
|
|
271
|
-
expect(result.success).toBe(false)
|
|
272
|
-
})
|
|
273
|
-
})
|
|
274
|
-
|
|
275
|
-
describe('$types getter', () => {
|
|
276
|
-
const schema = typedUnion(
|
|
277
|
-
[typedRef(() => personSchema), typedRef(() => postSchema)],
|
|
278
|
-
true,
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
it('returns array of valid $type values', () => {
|
|
282
|
-
const types = schema.$types
|
|
283
|
-
expect(types).toContain('app.bsky.actor.person')
|
|
284
|
-
expect(types).toContain('app.bsky.feed.post')
|
|
285
|
-
expect(types.length).toBe(2)
|
|
286
|
-
})
|
|
287
|
-
})
|
|
288
|
-
|
|
289
|
-
describe('refsMap getter', () => {
|
|
290
|
-
const schema = typedUnion(
|
|
291
|
-
[typedRef(() => personSchema), typedRef(() => postSchema)],
|
|
292
|
-
true,
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
it('returns map of $type to ref schema', () => {
|
|
296
|
-
const refsMap = schema.validatorsMap
|
|
297
|
-
expect(refsMap.size).toBe(2)
|
|
298
|
-
expect(refsMap.has('app.bsky.actor.person')).toBe(true)
|
|
299
|
-
expect(refsMap.has('app.bsky.feed.post')).toBe(true)
|
|
300
|
-
})
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
describe('edge cases', () => {
|
|
304
|
-
const schema = typedUnion(
|
|
305
|
-
[typedRef(() => personSchema), typedRef(() => postSchema)],
|
|
306
|
-
true,
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
it('rejects object with $type as empty string in closed union', () => {
|
|
310
|
-
const result = schema.safeParse({
|
|
311
|
-
$type: '',
|
|
312
|
-
name: 'Alice',
|
|
313
|
-
})
|
|
314
|
-
expect(result.success).toBe(false)
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
it('validates object with $type as empty string in open union', () => {
|
|
318
|
-
const openSchema = typedUnion([typedRef(() => personSchema)], false)
|
|
319
|
-
const result = openSchema.safeParse({
|
|
320
|
-
$type: '',
|
|
321
|
-
someProperty: 'value',
|
|
322
|
-
})
|
|
323
|
-
expect(result.success).toBe(true)
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
it('rejects plain object with only $type', () => {
|
|
327
|
-
const result = schema.safeParse({
|
|
328
|
-
$type: 'app.bsky.actor.person',
|
|
329
|
-
})
|
|
330
|
-
expect(result.success).toBe(false)
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
it('handles object with $type and undefined properties', () => {
|
|
334
|
-
const result = schema.safeParse({
|
|
335
|
-
$type: 'app.bsky.actor.person',
|
|
336
|
-
name: 'Alice',
|
|
337
|
-
age: 30,
|
|
338
|
-
extra: undefined,
|
|
339
|
-
})
|
|
340
|
-
expect(result.success).toBe(true)
|
|
341
|
-
})
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
describe('closed property', () => {
|
|
345
|
-
it('exposes closed property as true', () => {
|
|
346
|
-
const schema = typedUnion([typedRef(() => personSchema)], true)
|
|
347
|
-
expect(schema.closed).toBe(true)
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
it('exposes closed property as false', () => {
|
|
351
|
-
const schema = typedUnion([typedRef(() => personSchema)], false)
|
|
352
|
-
expect(schema.closed).toBe(false)
|
|
353
|
-
})
|
|
354
|
-
})
|
|
355
|
-
})
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { isPlainObject } from '@atproto/lex-data'
|
|
2
|
-
import {
|
|
3
|
-
InferInput,
|
|
4
|
-
InferOutput,
|
|
5
|
-
Schema,
|
|
6
|
-
Unknown$TypedObject,
|
|
7
|
-
ValidationContext,
|
|
8
|
-
} from '../core.js'
|
|
9
|
-
import { lazyProperty } from '../util/lazy-property.js'
|
|
10
|
-
import { TypedObjectSchema } from './typed-object.js'
|
|
11
|
-
import { TypedRefSchema } from './typed-ref.js'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Schema for Lexicon typed unions (unions discriminated by $type).
|
|
15
|
-
*
|
|
16
|
-
* Typed unions are collections of typed objects identified by their `$type`
|
|
17
|
-
* field. Can be "open" (accept unknown types) or "closed" (only accept
|
|
18
|
-
* known types).
|
|
19
|
-
*
|
|
20
|
-
* @template TValidators - Tuple of {@link TypedRefSchema} or {@link TypedObjectSchema} instances
|
|
21
|
-
* @template TClosed - Whether the union is closed (rejects unknown $types)
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```ts
|
|
25
|
-
* const embedUnion = new TypedUnionSchema([
|
|
26
|
-
* l.typedRef(() => imageSchema),
|
|
27
|
-
* l.typedRef(() => videoSchema),
|
|
28
|
-
* ], true) // closed - only accepts images and videos
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export class TypedUnionSchema<
|
|
32
|
-
const TValidators extends readonly (
|
|
33
|
-
| TypedRefSchema
|
|
34
|
-
| TypedObjectSchema
|
|
35
|
-
)[] = [],
|
|
36
|
-
const TClosed extends boolean = boolean,
|
|
37
|
-
> extends Schema<
|
|
38
|
-
TClosed extends true
|
|
39
|
-
? InferInput<TValidators[number]>
|
|
40
|
-
: InferInput<TValidators[number]> | Unknown$TypedObject,
|
|
41
|
-
TClosed extends true
|
|
42
|
-
? InferOutput<TValidators[number]>
|
|
43
|
-
: InferOutput<TValidators[number]> | Unknown$TypedObject
|
|
44
|
-
> {
|
|
45
|
-
readonly type = 'typedUnion' as const
|
|
46
|
-
|
|
47
|
-
constructor(
|
|
48
|
-
protected readonly validators: TValidators,
|
|
49
|
-
public readonly closed: TClosed,
|
|
50
|
-
) {
|
|
51
|
-
// @NOTE In order to avoid circular dependency issues, we don't access the
|
|
52
|
-
// refs's schema (or $type) here. Instead, we access them lazily when first
|
|
53
|
-
// needed. The biggest issue with this strategy is that we can't throw
|
|
54
|
-
// early if the refs contain multiple refs with the same $type.
|
|
55
|
-
|
|
56
|
-
super()
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
get validatorsMap(): Map<unknown, TValidators[number]> {
|
|
60
|
-
const map = new Map<unknown, TValidators[number]>()
|
|
61
|
-
for (const ref of this.validators) map.set(ref.$type, ref)
|
|
62
|
-
|
|
63
|
-
return lazyProperty(this, 'validatorsMap', map)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
get $types() {
|
|
67
|
-
return Array.from(this.validatorsMap.keys())
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
71
|
-
if (!isPlainObject(input) || !('$type' in input)) {
|
|
72
|
-
return ctx.issueUnexpectedType(input, '$typed')
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const { $type } = input
|
|
76
|
-
|
|
77
|
-
const validator = this.validatorsMap.get($type)
|
|
78
|
-
if (validator) {
|
|
79
|
-
return ctx.validate(input, validator)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (this.closed) {
|
|
83
|
-
return ctx.issueInvalidPropertyValue(input, '$type', this.$types)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (typeof $type !== 'string') {
|
|
87
|
-
return ctx.issueInvalidPropertyType(input, '$type', 'string')
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return ctx.success(input)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Creates a typed union schema for Lexicon unions.
|
|
96
|
-
*
|
|
97
|
-
* Typed unions discriminate variants by their `$type` field. Can be open
|
|
98
|
-
* (accepts unknown types, useful for extensibility) or closed (strict).
|
|
99
|
-
*
|
|
100
|
-
* @param refs - Array of typed refs for the union variants
|
|
101
|
-
* @param closed - Whether to reject unknown $type values
|
|
102
|
-
* @returns A new {@link TypedUnionSchema} instance
|
|
103
|
-
*
|
|
104
|
-
* @example
|
|
105
|
-
* ```ts
|
|
106
|
-
* // Closed union - only accepts known types
|
|
107
|
-
* const embedSchema = l.typedUnion([
|
|
108
|
-
* l.typedRef(() => imageViewSchema),
|
|
109
|
-
* l.typedRef(() => videoViewSchema),
|
|
110
|
-
* l.typedRef(() => externalViewSchema),
|
|
111
|
-
* ], true)
|
|
112
|
-
*
|
|
113
|
-
* // Open union - accepts unknown types for forward compatibility
|
|
114
|
-
* const feedItemSchema = l.typedUnion([
|
|
115
|
-
* l.typedRef(() => postSchema),
|
|
116
|
-
* l.typedRef(() => repostSchema),
|
|
117
|
-
* ], false) // unknown types pass through
|
|
118
|
-
*
|
|
119
|
-
* // Get all known $types
|
|
120
|
-
* console.log(embedSchema.$types)
|
|
121
|
-
* // ['app.bsky.embed.images#view', 'app.bsky.embed.video#view', ...]
|
|
122
|
-
* ```
|
|
123
|
-
*/
|
|
124
|
-
/*@__NO_SIDE_EFFECTS__*/
|
|
125
|
-
export function typedUnion<
|
|
126
|
-
const R extends readonly TypedRefSchema[],
|
|
127
|
-
const C extends boolean,
|
|
128
|
-
>(refs: R, closed: C) {
|
|
129
|
-
return new TypedUnionSchema<R, C>(refs, closed)
|
|
130
|
-
}
|