@atproto/lex-schema 0.0.11 → 0.0.13
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 +54 -0
- package/dist/core/$type.d.ts +149 -0
- package/dist/core/$type.d.ts.map +1 -1
- package/dist/core/$type.js +44 -0
- package/dist/core/$type.js.map +1 -1
- package/dist/core/record-key.d.ts +44 -0
- package/dist/core/record-key.d.ts.map +1 -1
- package/dist/core/record-key.js +30 -0
- package/dist/core/record-key.js.map +1 -1
- package/dist/core/result.d.ts +85 -4
- package/dist/core/result.d.ts.map +1 -1
- package/dist/core/result.js +60 -4
- package/dist/core/result.js.map +1 -1
- package/dist/core/schema.d.ts +232 -5
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +197 -4
- package/dist/core/schema.js.map +1 -1
- package/dist/core/string-format.d.ts +244 -11
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +150 -0
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/types.d.ts +90 -3
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/validation-error.d.ts +60 -0
- package/dist/core/validation-error.d.ts.map +1 -1
- package/dist/core/validation-error.js +60 -0
- package/dist/core/validation-error.js.map +1 -1
- package/dist/core/validation-issue.d.ts +61 -0
- package/dist/core/validation-issue.d.ts.map +1 -1
- package/dist/core/validation-issue.js +54 -1
- package/dist/core/validation-issue.js.map +1 -1
- package/dist/core/validator.d.ts +356 -11
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +203 -4
- package/dist/core/validator.js.map +1 -1
- package/dist/helpers.d.ts +12 -28
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js.map +1 -1
- package/dist/schema/array.d.ts +46 -0
- package/dist/schema/array.d.ts.map +1 -1
- package/dist/schema/array.js +16 -1
- package/dist/schema/array.js.map +1 -1
- package/dist/schema/blob.d.ts +50 -2
- package/dist/schema/blob.d.ts.map +1 -1
- package/dist/schema/blob.js +44 -2
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/boolean.d.ts +29 -0
- package/dist/schema/boolean.d.ts.map +1 -1
- package/dist/schema/boolean.js +30 -1
- package/dist/schema/boolean.js.map +1 -1
- package/dist/schema/bytes.d.ts +39 -0
- package/dist/schema/bytes.d.ts.map +1 -1
- package/dist/schema/bytes.js +34 -1
- package/dist/schema/bytes.js.map +1 -1
- package/dist/schema/cid.d.ts +39 -0
- package/dist/schema/cid.d.ts.map +1 -1
- package/dist/schema/cid.js +35 -1
- package/dist/schema/cid.js.map +1 -1
- package/dist/schema/custom.d.ts +67 -1
- package/dist/schema/custom.d.ts.map +1 -1
- package/dist/schema/custom.js +55 -0
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/dict.d.ts +45 -0
- package/dist/schema/dict.d.ts.map +1 -1
- package/dist/schema/dict.js +46 -1
- package/dist/schema/dict.js.map +1 -1
- package/dist/schema/discriminated-union.d.ts +59 -0
- package/dist/schema/discriminated-union.d.ts.map +1 -1
- package/dist/schema/discriminated-union.js +47 -1
- package/dist/schema/discriminated-union.js.map +1 -1
- package/dist/schema/enum.d.ts +49 -0
- package/dist/schema/enum.d.ts.map +1 -1
- package/dist/schema/enum.js +49 -0
- package/dist/schema/enum.js.map +1 -1
- package/dist/schema/integer.d.ts +43 -0
- package/dist/schema/integer.d.ts.map +1 -1
- package/dist/schema/integer.js +38 -1
- package/dist/schema/integer.js.map +1 -1
- package/dist/schema/intersection.d.ts +55 -0
- package/dist/schema/intersection.d.ts.map +1 -1
- package/dist/schema/intersection.js +50 -0
- package/dist/schema/intersection.js.map +1 -1
- package/dist/schema/lex-map.d.ts +37 -0
- package/dist/schema/lex-map.d.ts.map +1 -0
- package/dist/schema/lex-map.js +60 -0
- package/dist/schema/lex-map.js.map +1 -0
- package/dist/schema/lex-value.d.ts +35 -0
- package/dist/schema/lex-value.d.ts.map +1 -0
- package/dist/schema/lex-value.js +87 -0
- package/dist/schema/lex-value.js.map +1 -0
- package/dist/schema/literal.d.ts +45 -0
- package/dist/schema/literal.d.ts.map +1 -1
- package/dist/schema/literal.js +45 -0
- package/dist/schema/literal.js.map +1 -1
- package/dist/schema/never.d.ts +43 -0
- package/dist/schema/never.d.ts.map +1 -1
- package/dist/schema/never.js +44 -1
- package/dist/schema/never.js.map +1 -1
- package/dist/schema/null.d.ts +30 -0
- package/dist/schema/null.d.ts.map +1 -1
- package/dist/schema/null.js +31 -1
- package/dist/schema/null.js.map +1 -1
- package/dist/schema/nullable.d.ts +42 -0
- package/dist/schema/nullable.d.ts.map +1 -1
- package/dist/schema/nullable.js +42 -0
- package/dist/schema/nullable.js.map +1 -1
- package/dist/schema/object.d.ts +57 -0
- package/dist/schema/object.d.ts.map +1 -1
- package/dist/schema/object.js +53 -1
- package/dist/schema/object.js.map +1 -1
- package/dist/schema/optional.d.ts +43 -0
- package/dist/schema/optional.d.ts.map +1 -1
- package/dist/schema/optional.js +43 -0
- package/dist/schema/optional.js.map +1 -1
- package/dist/schema/params.d.ts +96 -12
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +155 -21
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/payload.d.ts +111 -15
- package/dist/schema/payload.d.ts.map +1 -1
- package/dist/schema/payload.js +73 -3
- package/dist/schema/payload.js.map +1 -1
- package/dist/schema/permission-set.d.ts +58 -0
- package/dist/schema/permission-set.d.ts.map +1 -1
- package/dist/schema/permission-set.js +50 -0
- package/dist/schema/permission-set.js.map +1 -1
- package/dist/schema/permission.d.ts +42 -0
- package/dist/schema/permission.d.ts.map +1 -1
- package/dist/schema/permission.js +39 -0
- package/dist/schema/permission.js.map +1 -1
- package/dist/schema/procedure.d.ts +64 -0
- package/dist/schema/procedure.d.ts.map +1 -1
- package/dist/schema/procedure.js +64 -0
- package/dist/schema/procedure.js.map +1 -1
- package/dist/schema/query.d.ts +55 -0
- package/dist/schema/query.d.ts.map +1 -1
- package/dist/schema/query.js +55 -0
- package/dist/schema/query.js.map +1 -1
- package/dist/schema/record.d.ts +76 -25
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +21 -0
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/ref.d.ts +51 -0
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js +18 -0
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/refine.d.ts +58 -9
- package/dist/schema/refine.d.ts.map +1 -1
- package/dist/schema/refine.js.map +1 -1
- package/dist/schema/regexp.d.ts +45 -0
- package/dist/schema/regexp.d.ts.map +1 -1
- package/dist/schema/regexp.js +46 -1
- package/dist/schema/regexp.js.map +1 -1
- package/dist/schema/string.d.ts +72 -6
- package/dist/schema/string.d.ts.map +1 -1
- package/dist/schema/string.js +56 -8
- package/dist/schema/string.js.map +1 -1
- package/dist/schema/subscription.d.ts +72 -2
- package/dist/schema/subscription.d.ts.map +1 -1
- package/dist/schema/subscription.js +59 -0
- package/dist/schema/subscription.js.map +1 -1
- package/dist/schema/token.d.ts +48 -0
- package/dist/schema/token.d.ts.map +1 -1
- package/dist/schema/token.js +49 -1
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-object.d.ts +73 -23
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +20 -1
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/typed-ref.d.ts +54 -0
- package/dist/schema/typed-ref.d.ts.map +1 -1
- package/dist/schema/typed-ref.js +16 -0
- package/dist/schema/typed-ref.js.map +1 -1
- package/dist/schema/typed-union.d.ts +51 -1
- package/dist/schema/typed-union.d.ts.map +1 -1
- package/dist/schema/typed-union.js +52 -2
- package/dist/schema/typed-union.js.map +1 -1
- package/dist/schema/union.d.ts +46 -0
- package/dist/schema/union.d.ts.map +1 -1
- package/dist/schema/union.js +41 -0
- package/dist/schema/union.js.map +1 -1
- package/dist/schema/unknown.d.ts +34 -0
- package/dist/schema/unknown.d.ts.map +1 -1
- package/dist/schema/unknown.js +34 -0
- package/dist/schema/unknown.js.map +1 -1
- package/dist/schema/with-default.d.ts +45 -0
- package/dist/schema/with-default.d.ts.map +1 -1
- package/dist/schema/with-default.js +45 -0
- package/dist/schema/with-default.js.map +1 -1
- package/dist/schema.d.ts +2 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +2 -1
- package/dist/schema.js.map +1 -1
- package/dist/util/if-any.d.ts +2 -0
- package/dist/util/if-any.d.ts.map +1 -0
- package/dist/util/if-any.js +3 -0
- package/dist/util/if-any.js.map +1 -0
- package/package.json +3 -3
- package/src/core/$type.ts +150 -18
- package/src/core/record-key.ts +44 -0
- package/src/core/result.ts +86 -4
- package/src/core/schema.ts +244 -9
- package/src/core/string-format.ts +259 -13
- package/src/core/types.ts +91 -3
- package/src/core/validation-error.ts +60 -0
- package/src/core/validation-issue.ts +68 -2
- package/src/core/validator.ts +373 -12
- package/src/helpers.test.ts +110 -29
- package/src/helpers.ts +54 -25
- package/src/schema/array.test.ts +94 -79
- package/src/schema/array.ts +48 -1
- package/src/schema/blob.ts +50 -1
- package/src/schema/boolean.ts +31 -1
- package/src/schema/bytes.ts +41 -1
- package/src/schema/cid.ts +41 -1
- package/src/schema/custom.ts +68 -1
- package/src/schema/dict.ts +47 -1
- package/src/schema/discriminated-union.ts +61 -1
- package/src/schema/enum.ts +50 -0
- package/src/schema/integer.ts +45 -1
- package/src/schema/intersection.ts +56 -0
- package/src/schema/{unknown-object.test.ts → lex-map.test.ts} +9 -9
- package/src/schema/lex-map.ts +63 -0
- package/src/schema/lex-value.test.ts +81 -0
- package/src/schema/lex-value.ts +86 -0
- package/src/schema/literal.ts +46 -0
- package/src/schema/never.ts +45 -1
- package/src/schema/null.ts +32 -1
- package/src/schema/nullable.ts +43 -0
- package/src/schema/object.ts +59 -1
- package/src/schema/optional.ts +44 -0
- package/src/schema/params.test.ts +133 -38
- package/src/schema/params.ts +237 -37
- package/src/schema/payload.test.ts +3 -3
- package/src/schema/payload.ts +145 -42
- package/src/schema/permission-set.ts +58 -0
- package/src/schema/permission.ts +42 -0
- package/src/schema/procedure.ts +64 -0
- package/src/schema/query.ts +55 -0
- package/src/schema/record.ts +82 -16
- package/src/schema/ref.ts +52 -0
- package/src/schema/refine.ts +58 -9
- package/src/schema/regexp.ts +47 -1
- package/src/schema/string.test.ts +99 -2
- package/src/schema/string.ts +108 -15
- package/src/schema/subscription.ts +72 -2
- package/src/schema/token.ts +50 -1
- package/src/schema/typed-object.ts +81 -16
- package/src/schema/typed-ref.ts +55 -0
- package/src/schema/typed-union.ts +58 -3
- package/src/schema/union.ts +47 -0
- package/src/schema/unknown.ts +35 -0
- package/src/schema/with-default.ts +46 -0
- package/src/schema.ts +2 -1
- package/src/util/if-any.ts +3 -0
- package/dist/schema/unknown-object.d.ts +0 -8
- package/dist/schema/unknown-object.d.ts.map +0 -1
- package/dist/schema/unknown-object.js +0 -19
- package/dist/schema/unknown-object.js.map +0 -1
- package/src/schema/unknown-object.ts +0 -19
package/src/core/schema.ts
CHANGED
|
@@ -7,17 +7,70 @@ import {
|
|
|
7
7
|
Validator,
|
|
8
8
|
} from './validator.js'
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Options for parsing operations.
|
|
12
|
+
* Excludes the `mode` option as it is implicitly set to `"parse"`.
|
|
13
|
+
*/
|
|
14
|
+
export type ParseOptions = Omit<ValidationOptions, 'mode'>
|
|
12
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Options for validation operations.
|
|
18
|
+
* Excludes the `mode` option as it is implicitly set to `"validate"`.
|
|
19
|
+
*/
|
|
20
|
+
export type ValidateOptions = Omit<ValidationOptions, 'mode'>
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Internal type structure for schema type inference.
|
|
24
|
+
*
|
|
25
|
+
* This interface defines the phantom types used for compile-time type inference
|
|
26
|
+
* without affecting runtime behavior. The `input` and `output` properties
|
|
27
|
+
* represent the expected input type during validation and the resulting output
|
|
28
|
+
* type after parsing, respectively.
|
|
29
|
+
*
|
|
30
|
+
* @typeParam TInput - The type accepted as input during validation
|
|
31
|
+
* @typeParam TOutput - The type returned after parsing (may differ from input due to coercion)
|
|
32
|
+
*/
|
|
13
33
|
export interface SchemaInternals<out TInput = unknown, out TOutput = TInput> {
|
|
14
|
-
/** @internal The inferred validation type */
|
|
15
34
|
input: TInput
|
|
16
|
-
|
|
17
|
-
/** @internal The inferred parse type */
|
|
18
35
|
output: TOutput
|
|
19
36
|
}
|
|
20
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Abstract base class for all schema validators in the lexicon system.
|
|
40
|
+
*
|
|
41
|
+
* This class provides the standard validation interface that all schema types
|
|
42
|
+
* implement. It offers multiple methods for validating and parsing data:
|
|
43
|
+
*
|
|
44
|
+
* - **Assertion methods**: `assert()`, `check()` - throw on invalid input
|
|
45
|
+
* - **Type guard methods**: `matches()`, `ifMatches()` - return boolean or optional value
|
|
46
|
+
* - **Parse methods**: `parse()`, `safeParse()` - allow value transformation/coercion
|
|
47
|
+
* - **Validate methods**: `validate()`, `safeValidate()` - strict validation without coercion
|
|
48
|
+
*
|
|
49
|
+
* All methods are also available with a `$` prefix (e.g., `$parse()`, `$validate()`)
|
|
50
|
+
* for consistent access in generated lexicon namespaces.
|
|
51
|
+
*
|
|
52
|
+
* @typeParam TInput - The type accepted as valid input during validation
|
|
53
|
+
* @typeParam TOutput - The type returned after parsing (may include transformations)
|
|
54
|
+
* @typeParam TInternals - Internal type structure for type inference
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* class MySchema extends Schema<string> {
|
|
59
|
+
* validateInContext(input: unknown, ctx: ValidationContext): ValidationResult {
|
|
60
|
+
* if (typeof input !== 'string') {
|
|
61
|
+
* return ctx.issueUnexpectedType(input, 'string')
|
|
62
|
+
* }
|
|
63
|
+
* return ctx.success(input)
|
|
64
|
+
* }
|
|
65
|
+
* }
|
|
66
|
+
*
|
|
67
|
+
* const schema = new MySchema()
|
|
68
|
+
* schema.assert('hello') // OK
|
|
69
|
+
* schema.assert(123) // Throws ValidationError
|
|
70
|
+
* schema.matches('hello') // true
|
|
71
|
+
* schema.matches(123) // false
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
21
74
|
export abstract class Schema<
|
|
22
75
|
out TInput = unknown,
|
|
23
76
|
out TOutput = TInput,
|
|
@@ -27,8 +80,33 @@ export abstract class Schema<
|
|
|
27
80
|
>,
|
|
28
81
|
> implements Validator<TInternals['input'], TInternals['output']>
|
|
29
82
|
{
|
|
83
|
+
/**
|
|
84
|
+
* Internal phantom property for type inference.
|
|
85
|
+
* This property does not exist at runtime.
|
|
86
|
+
*
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
30
89
|
declare readonly ['__lex']: TInternals
|
|
31
90
|
|
|
91
|
+
// Needed to discriminate multiple schema types when used in unions. Without
|
|
92
|
+
// this, Typescript could allow an EnumSchema<"foo" | "bar"> to be used where
|
|
93
|
+
// a StringSchema is expected, since they would both be structurally
|
|
94
|
+
// compatible.
|
|
95
|
+
abstract readonly type: string
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Performs validation of the input value within a validation context.
|
|
99
|
+
*
|
|
100
|
+
* This method must be implemented by subclasses to define the actual
|
|
101
|
+
* validation logic. It should not be called directly; use
|
|
102
|
+
* {@link ValidationContext.validate} instead to ensure proper mode enforcement.
|
|
103
|
+
*
|
|
104
|
+
* @param input - The value to validate
|
|
105
|
+
* @param ctx - The validation context providing path tracking and issue reporting
|
|
106
|
+
* @returns A validation result indicating success with the validated value or failure with issues
|
|
107
|
+
*
|
|
108
|
+
* @internal
|
|
109
|
+
*/
|
|
32
110
|
abstract validateInContext(
|
|
33
111
|
input: unknown,
|
|
34
112
|
ctx: ValidationContext,
|
|
@@ -66,21 +144,91 @@ export abstract class Schema<
|
|
|
66
144
|
throw result.reason
|
|
67
145
|
}
|
|
68
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Type guard that checks if the input matches this schema.
|
|
149
|
+
*
|
|
150
|
+
* @param input - The value to check
|
|
151
|
+
* @returns `true` if the input is valid according to this schema
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* if (schema.matches(data)) {
|
|
156
|
+
* // data is narrowed to the schema's input type
|
|
157
|
+
* console.log(data)
|
|
158
|
+
* }
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
69
161
|
matches<I>(input: I): input is I & InferInput<this> {
|
|
70
162
|
const result = ValidationContext.validate(input, this)
|
|
71
163
|
return result.success
|
|
72
164
|
}
|
|
73
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Returns the input if it matches this schema, otherwise returns `undefined`.
|
|
168
|
+
*
|
|
169
|
+
* This is useful for optional filtering operations where you want to
|
|
170
|
+
* conditionally extract values that match a schema.
|
|
171
|
+
*
|
|
172
|
+
* @param input - The value to check
|
|
173
|
+
* @returns The input value with narrowed type if valid, otherwise `undefined`
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* const validData = schema.ifMatches(data)
|
|
178
|
+
* if (validData !== undefined) {
|
|
179
|
+
* // validData is the schema's input type
|
|
180
|
+
* console.log(validData)
|
|
181
|
+
* }
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
74
184
|
ifMatches<I>(input: I): (I & InferInput<this>) | undefined {
|
|
75
185
|
return this.matches(input) ? input : undefined
|
|
76
186
|
}
|
|
77
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Parses the input, allowing value transformations and coercion.
|
|
190
|
+
*
|
|
191
|
+
* Unlike {@link validate}, this method allows the schema to transform
|
|
192
|
+
* the input value (e.g., applying default values, type coercion).
|
|
193
|
+
* Throws a {@link ValidationError} if the input is invalid.
|
|
194
|
+
*
|
|
195
|
+
* @param input - The value to parse
|
|
196
|
+
* @param options - Optional parsing configuration
|
|
197
|
+
* @returns The parsed and potentially transformed value
|
|
198
|
+
* @throws {ValidationError} If the input fails validation
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```typescript
|
|
202
|
+
* const result = schema.parse(rawData)
|
|
203
|
+
* // result has defaults applied and is fully typed
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
78
206
|
parse(input: unknown, options?: ParseOptions): InferOutput<this> {
|
|
79
207
|
const result = this.safeParse(input, options)
|
|
80
208
|
if (result.success) return result.value
|
|
81
209
|
throw result.reason
|
|
82
210
|
}
|
|
83
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Safely parses the input without throwing, returning a result object.
|
|
214
|
+
*
|
|
215
|
+
* This method allows value transformations like {@link parse}, but
|
|
216
|
+
* returns a discriminated union result instead of throwing on error.
|
|
217
|
+
*
|
|
218
|
+
* @param input - The value to parse
|
|
219
|
+
* @param options - Optional parsing configuration
|
|
220
|
+
* @returns A {@link ValidationResult} with either the parsed value or validation errors
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```typescript
|
|
224
|
+
* const result = schema.safeParse(data)
|
|
225
|
+
* if (result.success) {
|
|
226
|
+
* console.log(result.value)
|
|
227
|
+
* } else {
|
|
228
|
+
* console.error(result.reason.issues)
|
|
229
|
+
* }
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
84
232
|
safeParse(
|
|
85
233
|
input: unknown,
|
|
86
234
|
options?: ParseOptions,
|
|
@@ -91,12 +239,52 @@ export abstract class Schema<
|
|
|
91
239
|
})
|
|
92
240
|
}
|
|
93
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Validates the input strictly without allowing transformations.
|
|
244
|
+
*
|
|
245
|
+
* Unlike {@link parse}, this method requires the input to exactly match
|
|
246
|
+
* the schema without any transformations (no defaults applied, no coercion).
|
|
247
|
+
* Throws a {@link ValidationError} if the input is invalid or would require transformation.
|
|
248
|
+
*
|
|
249
|
+
* @typeParam I - The input type (preserved in the return type)
|
|
250
|
+
* @param input - The value to validate
|
|
251
|
+
* @param options - Optional validation configuration
|
|
252
|
+
* @returns The validated input with narrowed type
|
|
253
|
+
* @throws {ValidationError} If the input fails validation or requires transformation
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* const validated = schema.validate(data)
|
|
258
|
+
* // validated is typed as the intersection of input type and schema type
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
94
261
|
validate<I>(input: I, options?: ValidateOptions): I & InferInput<this> {
|
|
95
262
|
const result = this.safeValidate(input, options)
|
|
96
263
|
if (result.success) return result.value
|
|
97
264
|
throw result.reason
|
|
98
265
|
}
|
|
99
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Safely validates the input without throwing, returning a result object.
|
|
269
|
+
*
|
|
270
|
+
* This method performs strict validation like {@link validate}, but
|
|
271
|
+
* returns a discriminated union result instead of throwing on error.
|
|
272
|
+
*
|
|
273
|
+
* @typeParam I - The input type (preserved in the result value type)
|
|
274
|
+
* @param input - The value to validate
|
|
275
|
+
* @param options - Optional validation configuration
|
|
276
|
+
* @returns A {@link ValidationResult} with either the validated value or validation errors
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* const result = schema.safeValidate(data)
|
|
281
|
+
* if (result.success) {
|
|
282
|
+
* console.log(result.value)
|
|
283
|
+
* } else {
|
|
284
|
+
* console.error(result.reason.issues)
|
|
285
|
+
* }
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
100
288
|
safeValidate<I>(
|
|
101
289
|
input: I,
|
|
102
290
|
options?: ValidateOptions,
|
|
@@ -107,10 +295,12 @@ export abstract class Schema<
|
|
|
107
295
|
})
|
|
108
296
|
}
|
|
109
297
|
|
|
110
|
-
// @NOTE
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
298
|
+
// @NOTE Dollar-prefixed aliases
|
|
299
|
+
//
|
|
300
|
+
// The built lexicons namespaces export utility functions that allow accessing
|
|
301
|
+
// the schema's methods without the need to specify ".main." as part of the
|
|
302
|
+
// namespace. This way, a utility for a particular record type can be called
|
|
303
|
+
// like "app.bsky.feed.post.<utility>()" instead of
|
|
114
304
|
// "app.bsky.feed.post.main.<utility>()". Because those utilities could
|
|
115
305
|
// conflict with other schemas (e.g. if there is a lexicon definition at
|
|
116
306
|
// "#<utility>"), those exported utilities will be prefixed with "$". In order
|
|
@@ -121,30 +311,65 @@ export abstract class Schema<
|
|
|
121
311
|
// - "app.bsky.feed.post.$parse(...)" // calls a utility function created by "lex build"
|
|
122
312
|
// - "app.bsky.feed.defs.postView.$parse(...)" // uses the alias defined below on the schema instance
|
|
123
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Alias for {@link assert} with `$` prefix for namespace compatibility.
|
|
316
|
+
*
|
|
317
|
+
* @see {@link assert}
|
|
318
|
+
*/
|
|
124
319
|
$assert(input: unknown): asserts input is InferInput<this> {
|
|
125
320
|
return this.assert(input)
|
|
126
321
|
}
|
|
127
322
|
|
|
323
|
+
/**
|
|
324
|
+
* Alias for {@link check} with `$` prefix for namespace compatibility.
|
|
325
|
+
*
|
|
326
|
+
* @see {@link check}
|
|
327
|
+
*/
|
|
128
328
|
$check(input: unknown): void {
|
|
129
329
|
return this.check(input)
|
|
130
330
|
}
|
|
131
331
|
|
|
332
|
+
/**
|
|
333
|
+
* Alias for {@link cast} with `$` prefix for namespace compatibility.
|
|
334
|
+
*
|
|
335
|
+
* @see {@link cast}
|
|
336
|
+
*/
|
|
132
337
|
$cast<I>(input: I): I & InferInput<this> {
|
|
133
338
|
return this.cast(input)
|
|
134
339
|
}
|
|
135
340
|
|
|
341
|
+
/**
|
|
342
|
+
* Alias for {@link matches} with `$` prefix for namespace compatibility.
|
|
343
|
+
*
|
|
344
|
+
* @see {@link matches}
|
|
345
|
+
*/
|
|
136
346
|
$matches(input: unknown): input is InferInput<this> {
|
|
137
347
|
return this.matches(input)
|
|
138
348
|
}
|
|
139
349
|
|
|
350
|
+
/**
|
|
351
|
+
* Alias for {@link ifMatches} with `$` prefix for namespace compatibility.
|
|
352
|
+
*
|
|
353
|
+
* @see {@link ifMatches}
|
|
354
|
+
*/
|
|
140
355
|
$ifMatches<I>(input: I): (I & InferInput<this>) | undefined {
|
|
141
356
|
return this.ifMatches(input)
|
|
142
357
|
}
|
|
143
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Alias for {@link parse} with `$` prefix for namespace compatibility.
|
|
361
|
+
*
|
|
362
|
+
* @see {@link parse}
|
|
363
|
+
*/
|
|
144
364
|
$parse(input: unknown, options?: ValidateOptions): InferOutput<this> {
|
|
145
365
|
return this.parse(input, options)
|
|
146
366
|
}
|
|
147
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Alias for {@link safeParse} with `$` prefix for namespace compatibility.
|
|
370
|
+
*
|
|
371
|
+
* @see {@link safeParse}
|
|
372
|
+
*/
|
|
148
373
|
$safeParse(
|
|
149
374
|
input: unknown,
|
|
150
375
|
options?: ValidateOptions,
|
|
@@ -152,10 +377,20 @@ export abstract class Schema<
|
|
|
152
377
|
return this.safeParse(input, options)
|
|
153
378
|
}
|
|
154
379
|
|
|
380
|
+
/**
|
|
381
|
+
* Alias for {@link validate} with `$` prefix for namespace compatibility.
|
|
382
|
+
*
|
|
383
|
+
* @see {@link validate}
|
|
384
|
+
*/
|
|
155
385
|
$validate<I>(input: I, options?: ValidateOptions): I & InferInput<this> {
|
|
156
386
|
return this.validate(input, options)
|
|
157
387
|
}
|
|
158
388
|
|
|
389
|
+
/**
|
|
390
|
+
* Alias for {@link safeValidate} with `$` prefix for namespace compatibility.
|
|
391
|
+
*
|
|
392
|
+
* @see {@link safeValidate}
|
|
393
|
+
*/
|
|
159
394
|
$safeValidate<I>(
|
|
160
395
|
input: I,
|
|
161
396
|
options?: ValidateOptions,
|
|
@@ -22,42 +22,193 @@ import {
|
|
|
22
22
|
} from '@atproto/syntax'
|
|
23
23
|
import { CheckFn } from '../util/assertion-util.js'
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// -----------------------------------------------------------------------------
|
|
26
|
+
// Individual string format types and type guards
|
|
27
|
+
// -----------------------------------------------------------------------------
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Type guard that checks if a value is a valid AT identifier (DID or handle).
|
|
31
|
+
*
|
|
32
|
+
* @param value - The value to check
|
|
33
|
+
* @returns `true` if the value is a valid AT identifier
|
|
34
|
+
*/
|
|
28
35
|
export const isAtIdentifierString: CheckFn<AtIdentifierString> = isValidAtId
|
|
36
|
+
export type {
|
|
37
|
+
/**
|
|
38
|
+
* An AT identifier string - either a DID or a handle.
|
|
39
|
+
*
|
|
40
|
+
* @example `"did:plc:1234..."` or `"alice.bsky.social"`
|
|
41
|
+
*/
|
|
42
|
+
AtIdentifierString,
|
|
43
|
+
}
|
|
29
44
|
|
|
30
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Type guard that checks if a value is a valid AT URI.
|
|
47
|
+
*
|
|
48
|
+
* @param value - The value to check
|
|
49
|
+
* @returns `true` if the value is a valid AT URI
|
|
50
|
+
*/
|
|
31
51
|
export const isAtUriString: CheckFn<AtUriString> = isValidAtUri
|
|
52
|
+
export type {
|
|
53
|
+
/**
|
|
54
|
+
* An AT URI string pointing to a resource in the AT Protocol network.
|
|
55
|
+
*
|
|
56
|
+
* @example `"at://did:plc:1234.../app.bsky.feed.post/3k2..."`
|
|
57
|
+
*/
|
|
58
|
+
AtUriString,
|
|
59
|
+
}
|
|
32
60
|
|
|
33
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Type guard that checks if a value is a valid CID string.
|
|
63
|
+
*
|
|
64
|
+
* @param value - The value to check
|
|
65
|
+
* @returns `true` if the value is a valid CID string
|
|
66
|
+
*/
|
|
34
67
|
export const isCidString = ((v) => validateCidString(v)) as CheckFn<CidString>
|
|
68
|
+
/**
|
|
69
|
+
* A Content Identifier (CID) string.
|
|
70
|
+
*
|
|
71
|
+
* CIDs are self-describing content addresses used to identify data by its hash.
|
|
72
|
+
*
|
|
73
|
+
* @example `"bafyreig..."`
|
|
74
|
+
*/
|
|
75
|
+
export type CidString = string
|
|
35
76
|
|
|
36
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Type guard that checks if a value is a valid datetime string.
|
|
79
|
+
*
|
|
80
|
+
* @param value - The value to check
|
|
81
|
+
* @returns `true` if the value is a valid datetime string
|
|
82
|
+
*/
|
|
37
83
|
export const isDatetimeString: CheckFn<DatetimeString> = isValidDatetime
|
|
84
|
+
export type {
|
|
85
|
+
/**
|
|
86
|
+
* An ISO 8601 datetime string.
|
|
87
|
+
*
|
|
88
|
+
* @example `"2024-01-15T12:30:00.000Z"`
|
|
89
|
+
*/
|
|
90
|
+
DatetimeString,
|
|
91
|
+
}
|
|
38
92
|
|
|
39
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Type guard that checks if a value is a valid DID string.
|
|
95
|
+
*
|
|
96
|
+
* @param value - The value to check
|
|
97
|
+
* @returns `true` if the value is a valid DID string
|
|
98
|
+
*/
|
|
40
99
|
export const isDidString: CheckFn<DidString> = isValidDid
|
|
100
|
+
export type {
|
|
101
|
+
/**
|
|
102
|
+
* A Decentralized Identifier (DID) string.
|
|
103
|
+
*
|
|
104
|
+
* DIDs are globally unique identifiers that don't require a central authority.
|
|
105
|
+
*
|
|
106
|
+
* @example `"did:plc:1234abcd..."` or `"did:web:example.com"`
|
|
107
|
+
*/
|
|
108
|
+
DidString,
|
|
109
|
+
}
|
|
41
110
|
|
|
42
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Type guard that checks if a value is a valid handle string.
|
|
113
|
+
*
|
|
114
|
+
* @param value - The value to check
|
|
115
|
+
* @returns `true` if the value is a valid handle string
|
|
116
|
+
*/
|
|
43
117
|
export const isHandleString: CheckFn<HandleString> = isValidHandle
|
|
118
|
+
export type {
|
|
119
|
+
/**
|
|
120
|
+
* A handle string - a human-readable identifier for users.
|
|
121
|
+
*
|
|
122
|
+
* @example `"alice.bsky.social"` or `"bob.example.com"`
|
|
123
|
+
*/
|
|
124
|
+
HandleString,
|
|
125
|
+
}
|
|
44
126
|
|
|
45
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Type guard that checks if a value is a valid BCP-47 language tag.
|
|
129
|
+
*
|
|
130
|
+
* @param value - The value to check
|
|
131
|
+
* @returns `true` if the value is a valid language string
|
|
132
|
+
*/
|
|
46
133
|
export const isLanguageString = isValidLanguage as CheckFn<LanguageString>
|
|
134
|
+
/**
|
|
135
|
+
* A BCP-47 language tag string.
|
|
136
|
+
*
|
|
137
|
+
* @example `"en"`, `"en-US"`, `"zh-Hans"`
|
|
138
|
+
*/
|
|
139
|
+
export type LanguageString = string
|
|
47
140
|
|
|
48
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Type guard that checks if a value is a valid NSID string.
|
|
143
|
+
*
|
|
144
|
+
* @param value - The value to check
|
|
145
|
+
* @returns `true` if the value is a valid NSID string
|
|
146
|
+
*/
|
|
49
147
|
export const isNsidString: CheckFn<NsidString> = isValidNsid
|
|
148
|
+
export type {
|
|
149
|
+
/**
|
|
150
|
+
* A Namespaced Identifier (NSID) string identifying a lexicon.
|
|
151
|
+
*
|
|
152
|
+
* NSIDs use reverse-domain notation to identify schemas.
|
|
153
|
+
*
|
|
154
|
+
* @example `"app.bsky.feed.post"`, `"com.atproto.repo.createRecord"`
|
|
155
|
+
*/
|
|
156
|
+
NsidString,
|
|
157
|
+
}
|
|
50
158
|
|
|
51
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Type guard that checks if a value is a valid record key string.
|
|
161
|
+
*
|
|
162
|
+
* @param value - The value to check
|
|
163
|
+
* @returns `true` if the value is a valid record key string
|
|
164
|
+
*/
|
|
52
165
|
export const isRecordKeyString: CheckFn<RecordKeyString> = isValidRecordKey
|
|
166
|
+
export type {
|
|
167
|
+
/**
|
|
168
|
+
* A record key string identifying a record within a collection.
|
|
169
|
+
*
|
|
170
|
+
* @example `"3k2..."` (TID format) or `"self"` (literal key)
|
|
171
|
+
*/
|
|
172
|
+
RecordKeyString,
|
|
173
|
+
}
|
|
53
174
|
|
|
54
|
-
|
|
175
|
+
/**
|
|
176
|
+
* Type guard that checks if a value is a valid TID string.
|
|
177
|
+
*
|
|
178
|
+
* @param value - The value to check
|
|
179
|
+
* @returns `true` if the value is a valid TID string
|
|
180
|
+
*/
|
|
55
181
|
export const isTidString: CheckFn<TidString> = isValidTid
|
|
182
|
+
export type {
|
|
183
|
+
/**
|
|
184
|
+
* A Timestamp Identifier (TID) string.
|
|
185
|
+
*
|
|
186
|
+
* TIDs are time-based identifiers used for record keys.
|
|
187
|
+
*
|
|
188
|
+
* @example `"3k2..."`
|
|
189
|
+
*/
|
|
190
|
+
TidString,
|
|
191
|
+
}
|
|
56
192
|
|
|
57
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Type guard that checks if a value is a valid URI string.
|
|
195
|
+
*
|
|
196
|
+
* @param value - The value to check
|
|
197
|
+
* @returns `true` if the value is a valid URI string
|
|
198
|
+
*/
|
|
58
199
|
export const isUriString: CheckFn<UriString> = isValidUri
|
|
200
|
+
export type {
|
|
201
|
+
/**
|
|
202
|
+
* A standard URI string.
|
|
203
|
+
*
|
|
204
|
+
* @example `"https://example.com/path"`
|
|
205
|
+
*/
|
|
206
|
+
UriString,
|
|
207
|
+
}
|
|
59
208
|
|
|
60
|
-
//
|
|
209
|
+
// -----------------------------------------------------------------------------
|
|
210
|
+
// String format registry
|
|
211
|
+
// -----------------------------------------------------------------------------
|
|
61
212
|
|
|
62
213
|
type StringFormats = {
|
|
63
214
|
'at-identifier': AtIdentifierString
|
|
@@ -73,6 +224,9 @@ type StringFormats = {
|
|
|
73
224
|
uri: UriString
|
|
74
225
|
}
|
|
75
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Union type of all valid string format names.
|
|
229
|
+
*/
|
|
76
230
|
export type StringFormat = Extract<keyof StringFormats, string>
|
|
77
231
|
|
|
78
232
|
const stringFormatVerifiers: {
|
|
@@ -93,10 +247,39 @@ const stringFormatVerifiers: {
|
|
|
93
247
|
uri: isUriString,
|
|
94
248
|
})
|
|
95
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Infers the string type for a given format name.
|
|
252
|
+
*
|
|
253
|
+
* @typeParam F - The format name
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* type Did = InferStringFormat<'did'>
|
|
258
|
+
* // Result: DidString
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
96
261
|
export type InferStringFormat<F extends StringFormat> = F extends StringFormat
|
|
97
262
|
? StringFormats[F]
|
|
98
263
|
: never
|
|
99
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Type guard that checks if a string matches a specific format.
|
|
267
|
+
*
|
|
268
|
+
* @typeParam I - The input string type
|
|
269
|
+
* @typeParam F - The format to check
|
|
270
|
+
* @param input - The string to validate
|
|
271
|
+
* @param format - The format name to validate against
|
|
272
|
+
* @returns `true` if the string matches the format
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```typescript
|
|
276
|
+
* const value: string = 'did:plc:1234...'
|
|
277
|
+
* if (isStringFormat(value, 'did')) {
|
|
278
|
+
* // value is typed as DidString
|
|
279
|
+
* console.log('Valid DID:', value)
|
|
280
|
+
* }
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
100
283
|
/*@__NO_SIDE_EFFECTS__*/
|
|
101
284
|
export function isStringFormat<I extends string, F extends StringFormat>(
|
|
102
285
|
input: I,
|
|
@@ -109,6 +292,21 @@ export function isStringFormat<I extends string, F extends StringFormat>(
|
|
|
109
292
|
return formatVerifier(input)
|
|
110
293
|
}
|
|
111
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Asserts that a string matches a specific format, throwing if invalid.
|
|
297
|
+
*
|
|
298
|
+
* @typeParam I - The input string type
|
|
299
|
+
* @typeParam F - The format to check
|
|
300
|
+
* @param input - The string to validate
|
|
301
|
+
* @param format - The format name to validate against
|
|
302
|
+
* @throws {TypeError} If the string doesn't match the format
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```typescript
|
|
306
|
+
* assertStringFormat(value, 'handle')
|
|
307
|
+
* // value is now typed as HandleString
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
112
310
|
/*@__NO_SIDE_EFFECTS__*/
|
|
113
311
|
export function assertStringFormat<I extends string, F extends StringFormat>(
|
|
114
312
|
input: I,
|
|
@@ -119,6 +317,24 @@ export function assertStringFormat<I extends string, F extends StringFormat>(
|
|
|
119
317
|
}
|
|
120
318
|
}
|
|
121
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Validates and returns a string as the specified format type, throwing if invalid.
|
|
322
|
+
*
|
|
323
|
+
* This is useful when you need to convert a string to a format type in an expression.
|
|
324
|
+
*
|
|
325
|
+
* @typeParam I - The input string type
|
|
326
|
+
* @typeParam F - The format to validate against
|
|
327
|
+
* @param input - The string to validate
|
|
328
|
+
* @param format - The format name to validate against
|
|
329
|
+
* @returns The input typed as the format type
|
|
330
|
+
* @throws {TypeError} If the string doesn't match the format
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```typescript
|
|
334
|
+
* const did = asStringFormat(userInput, 'did')
|
|
335
|
+
* // did is typed as DidString
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
122
338
|
/*@__NO_SIDE_EFFECTS__*/
|
|
123
339
|
export function asStringFormat<I extends string, F extends StringFormat>(
|
|
124
340
|
input: I,
|
|
@@ -128,6 +344,26 @@ export function asStringFormat<I extends string, F extends StringFormat>(
|
|
|
128
344
|
return input
|
|
129
345
|
}
|
|
130
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Returns the string as the format type if valid, otherwise returns `undefined`.
|
|
349
|
+
*
|
|
350
|
+
* This is useful for optional validation where you want to handle invalid values
|
|
351
|
+
* without throwing.
|
|
352
|
+
*
|
|
353
|
+
* @typeParam I - The input string type
|
|
354
|
+
* @typeParam F - The format to validate against
|
|
355
|
+
* @param input - The string to validate
|
|
356
|
+
* @param format - The format name to validate against
|
|
357
|
+
* @returns The typed string if valid, otherwise `undefined`
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```typescript
|
|
361
|
+
* const did = ifStringFormat(maybeInvalid, 'did')
|
|
362
|
+
* if (did) {
|
|
363
|
+
* // did is typed as DidString
|
|
364
|
+
* }
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
131
367
|
/*@__NO_SIDE_EFFECTS__*/
|
|
132
368
|
export function ifStringFormat<I extends string, F extends StringFormat>(
|
|
133
369
|
input: I,
|
|
@@ -136,6 +372,16 @@ export function ifStringFormat<I extends string, F extends StringFormat>(
|
|
|
136
372
|
return isStringFormat(input, format) ? input : undefined
|
|
137
373
|
}
|
|
138
374
|
|
|
375
|
+
/**
|
|
376
|
+
* Array of all valid string format names.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```typescript
|
|
380
|
+
* for (const format of STRING_FORMATS) {
|
|
381
|
+
* console.log(format) // 'at-identifier', 'at-uri', 'cid', ...
|
|
382
|
+
* }
|
|
383
|
+
* ```
|
|
384
|
+
*/
|
|
139
385
|
export const STRING_FORMATS = /*#__PURE__*/ Object.freeze(
|
|
140
386
|
/*#__PURE__*/ Object.keys(stringFormatVerifiers),
|
|
141
387
|
) as readonly StringFormat[]
|