@atproto/lex-schema 0.0.11 → 0.0.12
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 +19 -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 +229 -2
- 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 +51 -0
- package/dist/core/validation-issue.js.map +1 -1
- package/dist/core/validator.d.ts +347 -10
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +184 -3
- package/dist/core/validator.js.map +1 -1
- package/dist/helpers.d.ts +9 -24
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js.map +1 -1
- package/dist/schema/array.d.ts +45 -0
- package/dist/schema/array.d.ts.map +1 -1
- package/dist/schema/array.js +14 -0
- package/dist/schema/array.js.map +1 -1
- package/dist/schema/blob.d.ts +46 -0
- package/dist/schema/blob.d.ts.map +1 -1
- package/dist/schema/blob.js +39 -0
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/boolean.d.ts +28 -0
- package/dist/schema/boolean.d.ts.map +1 -1
- package/dist/schema/boolean.js +28 -0
- package/dist/schema/boolean.js.map +1 -1
- package/dist/schema/bytes.d.ts +38 -0
- package/dist/schema/bytes.d.ts.map +1 -1
- package/dist/schema/bytes.js +32 -0
- package/dist/schema/bytes.js.map +1 -1
- package/dist/schema/cid.d.ts +38 -0
- package/dist/schema/cid.d.ts.map +1 -1
- package/dist/schema/cid.js +33 -0
- package/dist/schema/cid.js.map +1 -1
- package/dist/schema/custom.d.ts +66 -1
- package/dist/schema/custom.d.ts.map +1 -1
- package/dist/schema/custom.js +54 -0
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/dict.d.ts +44 -0
- package/dist/schema/dict.d.ts.map +1 -1
- package/dist/schema/dict.js +44 -0
- package/dist/schema/dict.js.map +1 -1
- package/dist/schema/discriminated-union.d.ts +58 -0
- package/dist/schema/discriminated-union.d.ts.map +1 -1
- package/dist/schema/discriminated-union.js +45 -0
- package/dist/schema/discriminated-union.js.map +1 -1
- package/dist/schema/enum.d.ts +48 -0
- package/dist/schema/enum.d.ts.map +1 -1
- package/dist/schema/enum.js +48 -0
- package/dist/schema/enum.js.map +1 -1
- package/dist/schema/integer.d.ts +42 -0
- package/dist/schema/integer.d.ts.map +1 -1
- package/dist/schema/integer.js +36 -0
- package/dist/schema/integer.js.map +1 -1
- package/dist/schema/intersection.d.ts +54 -0
- package/dist/schema/intersection.d.ts.map +1 -1
- package/dist/schema/intersection.js +49 -0
- package/dist/schema/intersection.js.map +1 -1
- package/dist/schema/literal.d.ts +44 -0
- package/dist/schema/literal.d.ts.map +1 -1
- package/dist/schema/literal.js +44 -0
- package/dist/schema/literal.js.map +1 -1
- package/dist/schema/never.d.ts +42 -0
- package/dist/schema/never.d.ts.map +1 -1
- package/dist/schema/never.js +42 -0
- package/dist/schema/never.js.map +1 -1
- package/dist/schema/null.d.ts +29 -0
- package/dist/schema/null.d.ts.map +1 -1
- package/dist/schema/null.js +29 -0
- package/dist/schema/null.js.map +1 -1
- package/dist/schema/nullable.d.ts +41 -0
- package/dist/schema/nullable.d.ts.map +1 -1
- package/dist/schema/nullable.js +41 -0
- package/dist/schema/nullable.js.map +1 -1
- package/dist/schema/object.d.ts +56 -0
- package/dist/schema/object.d.ts.map +1 -1
- package/dist/schema/object.js +51 -0
- package/dist/schema/object.js.map +1 -1
- package/dist/schema/optional.d.ts +42 -0
- package/dist/schema/optional.d.ts.map +1 -1
- package/dist/schema/optional.js +42 -0
- package/dist/schema/optional.js.map +1 -1
- package/dist/schema/params.d.ts +90 -10
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +84 -10
- 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 +70 -0
- 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 +63 -8
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +20 -0
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/ref.d.ts +50 -0
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js +17 -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 +44 -0
- package/dist/schema/regexp.d.ts.map +1 -1
- package/dist/schema/regexp.js +44 -0
- package/dist/schema/regexp.js.map +1 -1
- package/dist/schema/string.d.ts +50 -0
- package/dist/schema/string.d.ts.map +1 -1
- package/dist/schema/string.js +41 -0
- 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 +47 -0
- package/dist/schema/token.d.ts.map +1 -1
- package/dist/schema/token.js +47 -0
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-object.d.ts +62 -8
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +18 -0
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/typed-ref.d.ts +53 -0
- package/dist/schema/typed-ref.d.ts.map +1 -1
- package/dist/schema/typed-ref.js +15 -0
- package/dist/schema/typed-ref.js.map +1 -1
- package/dist/schema/typed-union.d.ts +50 -1
- package/dist/schema/typed-union.d.ts.map +1 -1
- package/dist/schema/typed-union.js +50 -1
- package/dist/schema/typed-union.js.map +1 -1
- package/dist/schema/union.d.ts +45 -0
- package/dist/schema/union.d.ts.map +1 -1
- package/dist/schema/union.js +40 -0
- package/dist/schema/union.js.map +1 -1
- package/dist/schema/unknown-object.d.ts +34 -0
- package/dist/schema/unknown-object.d.ts.map +1 -1
- package/dist/schema/unknown-object.js +31 -0
- package/dist/schema/unknown-object.js.map +1 -1
- package/dist/schema/unknown.d.ts +33 -0
- package/dist/schema/unknown.d.ts.map +1 -1
- package/dist/schema/unknown.js +33 -0
- package/dist/schema/unknown.js.map +1 -1
- package/dist/schema/with-default.d.ts +44 -0
- package/dist/schema/with-default.d.ts.map +1 -1
- package/dist/schema/with-default.js +44 -0
- package/dist/schema/with-default.js.map +1 -1
- 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 +236 -7
- 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 +65 -0
- package/src/core/validator.ts +351 -10
- package/src/helpers.test.ts +110 -29
- package/src/helpers.ts +9 -14
- package/src/schema/array.test.ts +94 -79
- package/src/schema/array.ts +45 -0
- package/src/schema/blob.ts +46 -0
- package/src/schema/boolean.ts +28 -0
- package/src/schema/bytes.ts +38 -0
- package/src/schema/cid.ts +38 -0
- package/src/schema/custom.ts +66 -1
- package/src/schema/dict.ts +44 -0
- package/src/schema/discriminated-union.ts +58 -0
- package/src/schema/enum.ts +48 -0
- package/src/schema/integer.ts +42 -0
- package/src/schema/intersection.ts +54 -0
- package/src/schema/literal.ts +44 -0
- package/src/schema/never.ts +42 -0
- package/src/schema/null.ts +29 -0
- package/src/schema/nullable.ts +41 -0
- package/src/schema/object.ts +56 -0
- package/src/schema/optional.ts +42 -0
- package/src/schema/params.test.ts +58 -2
- package/src/schema/params.ts +125 -19
- package/src/schema/payload.test.ts +3 -3
- package/src/schema/payload.ts +142 -38
- 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 +63 -8
- package/src/schema/ref.ts +50 -0
- package/src/schema/refine.ts +58 -9
- package/src/schema/regexp.ts +44 -0
- package/src/schema/string.ts +50 -0
- package/src/schema/subscription.ts +72 -2
- package/src/schema/token.ts +47 -0
- package/src/schema/typed-object.ts +62 -8
- package/src/schema/typed-ref.ts +53 -0
- package/src/schema/typed-union.ts +55 -2
- package/src/schema/union.ts +45 -0
- package/src/schema/unknown-object.ts +34 -0
- package/src/schema/unknown.ts +33 -0
- package/src/schema/with-default.ts +44 -0
package/src/schema/query.ts
CHANGED
|
@@ -2,6 +2,28 @@ import { NsidString } from '../core.js'
|
|
|
2
2
|
import { ParamsSchema } from './params.js'
|
|
3
3
|
import { Payload } from './payload.js'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Represents a Lexicon query (HTTP GET) endpoint definition.
|
|
7
|
+
*
|
|
8
|
+
* Queries are read-only operations that retrieve data from a server.
|
|
9
|
+
* They have parameters (passed as URL query parameters), an output
|
|
10
|
+
* payload, and optional error types.
|
|
11
|
+
*
|
|
12
|
+
* @template TNsid - The NSID identifying this query
|
|
13
|
+
* @template TParameters - The parameters schema type
|
|
14
|
+
* @template TOutputPayload - The output payload type
|
|
15
|
+
* @template TErrors - Array of error type strings, or undefined
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const getPostQuery = new Query(
|
|
20
|
+
* 'app.bsky.feed.getPost',
|
|
21
|
+
* l.params({ uri: l.string({ format: 'at-uri' }) }),
|
|
22
|
+
* l.payload('application/json', postSchema),
|
|
23
|
+
* ['NotFound']
|
|
24
|
+
* )
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
5
27
|
export class Query<
|
|
6
28
|
const TNsid extends NsidString = NsidString,
|
|
7
29
|
const TParameters extends ParamsSchema = ParamsSchema,
|
|
@@ -20,6 +42,39 @@ export class Query<
|
|
|
20
42
|
) {}
|
|
21
43
|
}
|
|
22
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Creates a query definition for a Lexicon GET endpoint.
|
|
47
|
+
*
|
|
48
|
+
* Queries retrieve data without side effects. Parameters are sent as
|
|
49
|
+
* URL query string parameters.
|
|
50
|
+
*
|
|
51
|
+
* @param nsid - The NSID identifying this query endpoint
|
|
52
|
+
* @param parameters - Schema for URL query parameters
|
|
53
|
+
* @param output - Expected response payload schema
|
|
54
|
+
* @param errors - Optional array of error type strings
|
|
55
|
+
* @returns A new {@link Query} instance
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* // Simple query with JSON output
|
|
60
|
+
* const getProfile = l.query(
|
|
61
|
+
* 'app.bsky.actor.getProfile',
|
|
62
|
+
* l.params({ actor: l.string({ format: 'at-identifier' }) }),
|
|
63
|
+
* l.jsonPayload({ displayName: l.string(), handle: l.string() }),
|
|
64
|
+
* )
|
|
65
|
+
*
|
|
66
|
+
* // Query with pagination and errors
|
|
67
|
+
* const getTimeline = l.query(
|
|
68
|
+
* 'app.bsky.feed.getTimeline',
|
|
69
|
+
* l.params({
|
|
70
|
+
* limit: l.optional(l.integer({ minimum: 1, maximum: 100 })),
|
|
71
|
+
* cursor: l.optional(l.string()),
|
|
72
|
+
* }),
|
|
73
|
+
* l.jsonPayload({ feed: l.array(feedItemSchema), cursor: l.optional(l.string()) }),
|
|
74
|
+
* ['BlockedActor', 'BlockedByActor'],
|
|
75
|
+
* )
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
23
78
|
/*@__NO_SIDE_EFFECTS__*/
|
|
24
79
|
export function query<
|
|
25
80
|
const N extends NsidString,
|
package/src/schema/record.ts
CHANGED
|
@@ -14,9 +14,34 @@ import {
|
|
|
14
14
|
import { literal } from './literal.js'
|
|
15
15
|
import { string } from './string.js'
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Infers the record key type from a RecordSchema.
|
|
19
|
+
*
|
|
20
|
+
* @template R - The RecordSchema type
|
|
21
|
+
*/
|
|
17
22
|
export type InferRecordKey<R extends RecordSchema> =
|
|
18
23
|
R extends RecordSchema<infer TKey> ? RecordKeySchemaOutput<TKey> : never
|
|
19
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Schema for AT Protocol records with a type identifier and key constraints.
|
|
27
|
+
*
|
|
28
|
+
* Records are the primary data unit in AT Protocol. Each record has a `$type`
|
|
29
|
+
* field identifying its Lexicon schema, and is stored at a specific key
|
|
30
|
+
* (TID, NSID, or other format) in a repository.
|
|
31
|
+
*
|
|
32
|
+
* @template TKey - The record key type ('tid', 'nsid', 'any', or 'literal:...')
|
|
33
|
+
* @template TType - The NSID string identifying this record type
|
|
34
|
+
* @template TShape - The validator type for the record's data shape
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const postSchema = new RecordSchema(
|
|
39
|
+
* 'tid',
|
|
40
|
+
* 'app.bsky.feed.post',
|
|
41
|
+
* l.object({ text: l.string(), createdAt: l.string() })
|
|
42
|
+
* )
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
20
45
|
export class RecordSchema<
|
|
21
46
|
const TKey extends LexiconRecordKey = any,
|
|
22
47
|
const TType extends NsidString = any,
|
|
@@ -115,15 +140,45 @@ function recordKey<Key extends LexiconRecordKey>(
|
|
|
115
140
|
type AsNsid<T> = T extends `${string}#${string}` ? never : T
|
|
116
141
|
|
|
117
142
|
/**
|
|
143
|
+
* Creates a record schema for AT Protocol records.
|
|
144
|
+
*
|
|
145
|
+
* Records are the primary data unit in AT Protocol repositories. They have
|
|
146
|
+
* a `$type` field identifying their Lexicon schema, and are stored at keys
|
|
147
|
+
* following a specific format (TID, NSID, etc.).
|
|
148
|
+
*
|
|
118
149
|
* This function offers two overloads:
|
|
119
|
-
* - One that
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* -
|
|
125
|
-
*
|
|
126
|
-
*
|
|
150
|
+
* - One that infers the output type from the provided arguments (does not
|
|
151
|
+
* support circular references)
|
|
152
|
+
* - One with an explicitly defined interface for use with codegen and
|
|
153
|
+
* circular references
|
|
154
|
+
*
|
|
155
|
+
* @param key - The record key type: 'tid', 'nsid', 'any', or 'literal:value'
|
|
156
|
+
* @param type - The NSID identifying this record type (e.g., 'app.bsky.feed.post')
|
|
157
|
+
* @param validator - Schema validator for the record's properties
|
|
158
|
+
* @returns A new {@link RecordSchema} instance
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* // Post record with TID key
|
|
163
|
+
* const postSchema = l.record('tid', 'app.bsky.feed.post', l.object({
|
|
164
|
+
* text: l.string({ maxGraphemes: 300 }),
|
|
165
|
+
* createdAt: l.string({ format: 'datetime' }),
|
|
166
|
+
* reply: l.optional(l.object({
|
|
167
|
+
* root: l.ref(() => strongRefSchema),
|
|
168
|
+
* parent: l.ref(() => strongRefSchema),
|
|
169
|
+
* })),
|
|
170
|
+
* }))
|
|
171
|
+
*
|
|
172
|
+
* // Profile record with literal 'self' key
|
|
173
|
+
* const profileSchema = l.record('literal:self', 'app.bsky.actor.profile', l.object({
|
|
174
|
+
* displayName: l.optional(l.string({ maxGraphemes: 64 })),
|
|
175
|
+
* description: l.optional(l.string({ maxGraphemes: 256 })),
|
|
176
|
+
* avatar: l.optional(l.blob({ accept: ['image/*'] })),
|
|
177
|
+
* }))
|
|
178
|
+
*
|
|
179
|
+
* // Build a record with automatic $type injection
|
|
180
|
+
* const post = postSchema.build({ text: 'Hello!', createdAt: new Date().toISOString() })
|
|
181
|
+
* ```
|
|
127
182
|
*/
|
|
128
183
|
export function record<
|
|
129
184
|
const K extends LexiconRecordKey,
|
package/src/schema/ref.ts
CHANGED
|
@@ -7,8 +7,30 @@ import {
|
|
|
7
7
|
WrappedValidator,
|
|
8
8
|
} from '../core.js'
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Function type that returns a validator, used for lazy schema resolution.
|
|
12
|
+
*
|
|
13
|
+
* @template TValidator - The validator type that will be returned
|
|
14
|
+
*/
|
|
10
15
|
export type RefSchemaGetter<out TValidator extends Validator> = () => TValidator
|
|
11
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Schema for creating references to other schemas with lazy resolution.
|
|
19
|
+
*
|
|
20
|
+
* Useful for handling circular references or breaking module dependency cycles.
|
|
21
|
+
* The referenced schema is resolved lazily when first needed for validation.
|
|
22
|
+
*
|
|
23
|
+
* @template TValidator - The referenced validator type
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* // Self-referential schema for tree structure
|
|
28
|
+
* const nodeSchema = l.object({
|
|
29
|
+
* value: l.string(),
|
|
30
|
+
* children: l.array(l.ref(() => nodeSchema)),
|
|
31
|
+
* })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
12
34
|
export class RefSchema<const TValidator extends Validator>
|
|
13
35
|
extends Schema<
|
|
14
36
|
InferInput<TValidator>,
|
|
@@ -41,6 +63,34 @@ export class RefSchema<const TValidator extends Validator>
|
|
|
41
63
|
}
|
|
42
64
|
}
|
|
43
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Creates a reference schema with lazy resolution.
|
|
68
|
+
*
|
|
69
|
+
* Allows referencing schemas that may not be defined yet, enabling
|
|
70
|
+
* circular references and breaking dependency cycles. The getter function
|
|
71
|
+
* is called lazily when validation is first performed.
|
|
72
|
+
*
|
|
73
|
+
* @param get - Function that returns the referenced validator
|
|
74
|
+
* @returns A new {@link RefSchema} instance
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* // Circular reference - tree node that contains children of the same type
|
|
79
|
+
* const treeNodeSchema = l.object({
|
|
80
|
+
* name: l.string(),
|
|
81
|
+
* children: l.optional(l.array(l.ref(() => treeNodeSchema))),
|
|
82
|
+
* })
|
|
83
|
+
*
|
|
84
|
+
* // Cross-module reference
|
|
85
|
+
* const commentSchema = l.object({
|
|
86
|
+
* text: l.string(),
|
|
87
|
+
* author: l.ref(() => userSchema), // userSchema defined elsewhere
|
|
88
|
+
* })
|
|
89
|
+
*
|
|
90
|
+
* // Explicitly typed reference
|
|
91
|
+
* const itemSchema = l.ref<Item>(() => complexItemSchema)
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
44
94
|
/*@__NO_SIDE_EFFECTS__*/
|
|
45
95
|
export function ref<const TValidator extends Validator>(
|
|
46
96
|
get: RefSchemaGetter<TValidator>,
|
package/src/schema/refine.ts
CHANGED
|
@@ -8,18 +8,40 @@ import {
|
|
|
8
8
|
} from '../core.js'
|
|
9
9
|
import { CustomAssertionContext } from './custom.js'
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Configuration for a refinement check that validates a condition.
|
|
13
|
+
*
|
|
14
|
+
* @template T - The type being validated
|
|
15
|
+
* @property check - Function that returns true if the value passes the check
|
|
16
|
+
* @property message - Error message when the check fails
|
|
17
|
+
* @property path - Optional path to associate with the error
|
|
18
|
+
*/
|
|
11
19
|
export type RefinementCheck<T> = {
|
|
12
20
|
check: (value: T, ctx: CustomAssertionContext) => boolean
|
|
13
21
|
message: string
|
|
14
22
|
path?: PropertyKey | readonly PropertyKey[]
|
|
15
23
|
}
|
|
16
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Configuration for a refinement assertion that narrows the type.
|
|
27
|
+
*
|
|
28
|
+
* @template T - The input type being validated
|
|
29
|
+
* @template Out - The narrowed output type
|
|
30
|
+
* @property check - Type guard function that narrows the type
|
|
31
|
+
* @property message - Error message when the assertion fails
|
|
32
|
+
* @property path - Optional path to associate with the error
|
|
33
|
+
*/
|
|
17
34
|
export type RefinementAssertion<T, Out extends T> = {
|
|
18
35
|
check: (this: null, value: T, ctx: CustomAssertionContext) => value is Out
|
|
19
36
|
message: string
|
|
20
37
|
path?: PropertyKey | readonly PropertyKey[]
|
|
21
38
|
}
|
|
22
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Infers the input type from a refinement configuration.
|
|
42
|
+
*
|
|
43
|
+
* @template R - The refinement type
|
|
44
|
+
*/
|
|
23
45
|
export type InferRefinement<R> =
|
|
24
46
|
R extends RefinementCheck<infer T>
|
|
25
47
|
? T
|
|
@@ -27,25 +49,52 @@ export type InferRefinement<R> =
|
|
|
27
49
|
? T
|
|
28
50
|
: never
|
|
29
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Union type of refinement check or assertion.
|
|
54
|
+
*
|
|
55
|
+
* @template T - The input type being validated
|
|
56
|
+
* @template Out - The output type (same as T for checks, narrowed for assertions)
|
|
57
|
+
*/
|
|
30
58
|
export type Refinement<T = any, Out extends T = T> =
|
|
31
59
|
| RefinementCheck<T>
|
|
32
60
|
| RefinementAssertion<T, Out>
|
|
33
61
|
|
|
34
62
|
/**
|
|
35
|
-
*
|
|
63
|
+
* Creates a refined schema by adding additional validation constraints.
|
|
36
64
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* @returns A new schema that includes the refinement.
|
|
40
|
-
* @example
|
|
65
|
+
* Wraps an existing schema with an additional check function. The base schema
|
|
66
|
+
* is validated first, then the refinement check is applied to the result.
|
|
41
67
|
*
|
|
68
|
+
* @param schema - The base schema to refine
|
|
69
|
+
* @param refinement - The refinement check or assertion to apply
|
|
70
|
+
* @returns A new schema that includes the refinement
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
42
73
|
* ```ts
|
|
43
|
-
*
|
|
74
|
+
* // Simple check refinement
|
|
75
|
+
* const positiveInt = l.refine(l.integer(), {
|
|
44
76
|
* check: (value) => value > 0,
|
|
45
|
-
* message: 'Value must be
|
|
77
|
+
* message: 'Value must be positive',
|
|
46
78
|
* })
|
|
47
|
-
*
|
|
48
|
-
* //
|
|
79
|
+
*
|
|
80
|
+
* positiveInt.parse(5) // 5
|
|
81
|
+
* positiveInt.parse(-1) // throws
|
|
82
|
+
*
|
|
83
|
+
* // Type-narrowing assertion
|
|
84
|
+
* const nonEmptyString = l.refine(l.string(), {
|
|
85
|
+
* check: (value): value is string & { length: number } => value.length > 0,
|
|
86
|
+
* message: 'String must not be empty',
|
|
87
|
+
* })
|
|
88
|
+
*
|
|
89
|
+
* // With custom path for nested errors
|
|
90
|
+
* const validDateRange = l.refine(
|
|
91
|
+
* l.object({ start: l.string(), end: l.string() }),
|
|
92
|
+
* {
|
|
93
|
+
* check: (v) => new Date(v.start) < new Date(v.end),
|
|
94
|
+
* message: 'Start date must be before end date',
|
|
95
|
+
* path: ['end'],
|
|
96
|
+
* }
|
|
97
|
+
* )
|
|
49
98
|
* ```
|
|
50
99
|
*/
|
|
51
100
|
export function refine<
|
package/src/schema/regexp.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { Schema, ValidationContext } from '../core.js'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Schema for validating strings against a regular expression pattern.
|
|
5
|
+
*
|
|
6
|
+
* Validates that the input is a string and matches the provided pattern.
|
|
7
|
+
* The pattern is tested using RegExp.test().
|
|
8
|
+
*
|
|
9
|
+
* @template TValue - The string type (can be narrowed with branded types)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const schema = new RegexpSchema(/^[a-z]+$/)
|
|
14
|
+
* schema.validate('hello') // success
|
|
15
|
+
* schema.validate('Hello') // fails - uppercase not allowed
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
3
18
|
export class RegexpSchema<
|
|
4
19
|
TValue extends string = string,
|
|
5
20
|
> extends Schema<TValue> {
|
|
@@ -20,6 +35,35 @@ export class RegexpSchema<
|
|
|
20
35
|
}
|
|
21
36
|
}
|
|
22
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Creates a regexp schema that validates strings against a pattern.
|
|
40
|
+
*
|
|
41
|
+
* Useful for custom string formats not covered by the built-in format
|
|
42
|
+
* validators.
|
|
43
|
+
*
|
|
44
|
+
* @param pattern - Regular expression pattern to match against
|
|
45
|
+
* @returns A new {@link RegexpSchema} instance
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* // Simple pattern
|
|
50
|
+
* const slugSchema = l.regexp(/^[a-z0-9-]+$/)
|
|
51
|
+
*
|
|
52
|
+
* // With anchors for exact match
|
|
53
|
+
* const uuidSchema = l.regexp(
|
|
54
|
+
* /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
|
55
|
+
* )
|
|
56
|
+
*
|
|
57
|
+
* // Semantic versioning
|
|
58
|
+
* const semverSchema = l.regexp(/^\d+\.\d+\.\d+(-[\w.]+)?(\+[\w.]+)?$/)
|
|
59
|
+
*
|
|
60
|
+
* // Use in object
|
|
61
|
+
* const configSchema = l.object({
|
|
62
|
+
* name: l.regexp(/^[a-z][a-z0-9-]*$/), // kebab-case identifier
|
|
63
|
+
* version: semverSchema,
|
|
64
|
+
* })
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
23
67
|
/*@__NO_SIDE_EFFECTS__*/
|
|
24
68
|
export function regexp<TInput extends string = string>(pattern: RegExp) {
|
|
25
69
|
return new RegexpSchema<TInput>(pattern)
|
package/src/schema/string.ts
CHANGED
|
@@ -9,6 +9,15 @@ import {
|
|
|
9
9
|
import { memoizedOptions } from '../util/memoize.js'
|
|
10
10
|
import { TokenSchema } from './token.js'
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Configuration options for string schema validation.
|
|
14
|
+
*
|
|
15
|
+
* @property format - Expected string format (e.g., 'datetime', 'uri', 'at-uri', 'did', 'handle', 'nsid', 'cid', 'tid', 'record-key', 'at-identifier', 'language')
|
|
16
|
+
* @property minLength - Minimum length in UTF-8 bytes
|
|
17
|
+
* @property maxLength - Maximum length in UTF-8 bytes
|
|
18
|
+
* @property minGraphemes - Minimum number of grapheme clusters
|
|
19
|
+
* @property maxGraphemes - Maximum number of grapheme clusters
|
|
20
|
+
*/
|
|
12
21
|
export type StringSchemaOptions = {
|
|
13
22
|
format?: StringFormat
|
|
14
23
|
minLength?: number
|
|
@@ -17,6 +26,20 @@ export type StringSchemaOptions = {
|
|
|
17
26
|
maxGraphemes?: number
|
|
18
27
|
}
|
|
19
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Schema for validating string values with optional format and length constraints.
|
|
31
|
+
*
|
|
32
|
+
* Supports various string formats defined in the Lexicon specification, as well as
|
|
33
|
+
* length constraints measured in UTF-8 bytes or grapheme clusters.
|
|
34
|
+
*
|
|
35
|
+
* @template TOptions - The configuration options type
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const schema = new StringSchema({ format: 'datetime', maxLength: 64 })
|
|
40
|
+
* const result = schema.validate('2024-01-15T10:30:00Z')
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
20
43
|
export class StringSchema<
|
|
21
44
|
const TOptions extends StringSchemaOptions = StringSchemaOptions,
|
|
22
45
|
> extends Schema<
|
|
@@ -123,6 +146,33 @@ export function coerceToString(input: unknown): string | null {
|
|
|
123
146
|
}
|
|
124
147
|
}
|
|
125
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Creates a string schema with optional format and length constraints.
|
|
151
|
+
*
|
|
152
|
+
* Strings can be validated against various formats (datetime, uri, did, handle, etc.)
|
|
153
|
+
* and constrained by length in UTF-8 bytes or grapheme clusters.
|
|
154
|
+
*
|
|
155
|
+
* @param options - Optional configuration for format and length constraints
|
|
156
|
+
* @returns A new {@link StringSchema} instance
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* // Basic string
|
|
161
|
+
* const nameSchema = l.string()
|
|
162
|
+
*
|
|
163
|
+
* // With format validation
|
|
164
|
+
* const dateSchema = l.string({ format: 'datetime' })
|
|
165
|
+
*
|
|
166
|
+
* // With length constraints (UTF-8 bytes)
|
|
167
|
+
* const bioSchema = l.string({ maxLength: 256 })
|
|
168
|
+
*
|
|
169
|
+
* // With grapheme constraints (user-perceived characters)
|
|
170
|
+
* const displayNameSchema = l.string({ maxGraphemes: 64 })
|
|
171
|
+
*
|
|
172
|
+
* // Combining constraints
|
|
173
|
+
* const handleSchema = l.string({ format: 'handle', minLength: 3, maxLength: 253 })
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
126
176
|
export const string = /*#__PURE__*/ memoizedOptions(function <
|
|
127
177
|
const O extends StringSchemaOptions = NonNullable<unknown>,
|
|
128
178
|
>(options?: StringSchemaOptions & O) {
|
|
@@ -1,18 +1,51 @@
|
|
|
1
|
+
import { LexValue } from '@atproto/lex-data'
|
|
1
2
|
import { Infer, NsidString, Schema } from '../core.js'
|
|
2
3
|
import { ParamsSchema } from './params.js'
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Infers the parameters type from a Subscription definition.
|
|
7
|
+
*
|
|
8
|
+
* @template S - The Subscription type
|
|
9
|
+
*/
|
|
4
10
|
export type InferSubscriptionParameters<S extends Subscription> = Infer<
|
|
5
11
|
S['parameters']
|
|
6
12
|
>
|
|
7
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Infers the message type from a Subscription definition.
|
|
16
|
+
*
|
|
17
|
+
* @template S - The Subscription type
|
|
18
|
+
*/
|
|
8
19
|
export type InferSubscriptionMessage<S extends Subscription> = Infer<
|
|
9
20
|
S['message']
|
|
10
21
|
>
|
|
11
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Represents a Lexicon subscription (WebSocket) endpoint definition.
|
|
25
|
+
*
|
|
26
|
+
* Subscriptions are real-time event streams delivered over WebSocket.
|
|
27
|
+
* They have parameters for initializing the connection and a message
|
|
28
|
+
* schema for validating incoming events.
|
|
29
|
+
*
|
|
30
|
+
* @template TNsid - The NSID identifying this subscription
|
|
31
|
+
* @template TParameters - The connection parameters schema type
|
|
32
|
+
* @template TMessage - The message schema type
|
|
33
|
+
* @template TErrors - Array of error type strings, or undefined
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const firehose = new Subscription(
|
|
38
|
+
* 'com.atproto.sync.subscribeRepos',
|
|
39
|
+
* l.params({ cursor: l.optional(l.integer()) }),
|
|
40
|
+
* repoEventSchema,
|
|
41
|
+
* ['FutureCursor']
|
|
42
|
+
* )
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
12
45
|
export class Subscription<
|
|
13
46
|
const TNsid extends NsidString = NsidString,
|
|
14
47
|
const TParameters extends ParamsSchema = ParamsSchema,
|
|
15
|
-
const TMessage extends Schema = Schema
|
|
48
|
+
const TMessage extends Schema<LexValue> = Schema<LexValue>,
|
|
16
49
|
const TErrors extends undefined | readonly string[] =
|
|
17
50
|
| undefined
|
|
18
51
|
| readonly string[],
|
|
@@ -27,11 +60,48 @@ export class Subscription<
|
|
|
27
60
|
) {}
|
|
28
61
|
}
|
|
29
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Creates a subscription definition for a Lexicon WebSocket endpoint.
|
|
65
|
+
*
|
|
66
|
+
* Subscriptions enable real-time event streaming. The connection is
|
|
67
|
+
* initialized with parameters, and the server sends messages matching
|
|
68
|
+
* the message schema.
|
|
69
|
+
*
|
|
70
|
+
* @param nsid - The NSID identifying this subscription endpoint
|
|
71
|
+
* @param parameters - Schema for connection parameters
|
|
72
|
+
* @param message - Schema for validating incoming messages
|
|
73
|
+
* @param errors - Optional array of error type strings
|
|
74
|
+
* @returns A new {@link Subscription} instance
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* // Repository event stream
|
|
79
|
+
* const subscribeRepos = l.subscription(
|
|
80
|
+
* 'com.atproto.sync.subscribeRepos',
|
|
81
|
+
* l.params({
|
|
82
|
+
* cursor: l.optional(l.integer()),
|
|
83
|
+
* }),
|
|
84
|
+
* l.typedUnion([
|
|
85
|
+
* l.typedRef(() => commitEventSchema),
|
|
86
|
+
* l.typedRef(() => handleEventSchema),
|
|
87
|
+
* l.typedRef(() => identityEventSchema),
|
|
88
|
+
* ], false),
|
|
89
|
+
* ['FutureCursor', 'ConsumerTooSlow'],
|
|
90
|
+
* )
|
|
91
|
+
*
|
|
92
|
+
* // Label stream
|
|
93
|
+
* const subscribeLabels = l.subscription(
|
|
94
|
+
* 'com.atproto.label.subscribeLabels',
|
|
95
|
+
* l.params({ cursor: l.optional(l.integer()) }),
|
|
96
|
+
* labelEventSchema,
|
|
97
|
+
* )
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
30
100
|
/*@__NO_SIDE_EFFECTS__*/
|
|
31
101
|
export function subscription<
|
|
32
102
|
const N extends NsidString,
|
|
33
103
|
const P extends ParamsSchema,
|
|
34
|
-
const M extends Schema
|
|
104
|
+
const M extends Schema<LexValue>,
|
|
35
105
|
const E extends undefined | readonly string[] = undefined,
|
|
36
106
|
>(nsid: N, parameters: P, message: M, errors: E = undefined as E) {
|
|
37
107
|
return new Subscription<N, P, M, E>(nsid, parameters, message, errors)
|
package/src/schema/token.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { $type, NsidString, Schema, ValidationContext } from '../core.js'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Schema for Lexicon token values.
|
|
5
|
+
*
|
|
6
|
+
* Tokens are named constants in Lexicon, identified by their NSID and hash.
|
|
7
|
+
* They validate to their string value (e.g., 'app.bsky.feed.defs#requestLess').
|
|
8
|
+
* TokenSchema instances can also be used as values themselves.
|
|
9
|
+
*
|
|
10
|
+
* @template TValue - The token string literal type
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const schema = new TokenSchema('app.bsky.feed.defs#requestLess')
|
|
15
|
+
* schema.validate('app.bsky.feed.defs#requestLess') // success
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
3
18
|
export class TokenSchema<
|
|
4
19
|
const TValue extends string = string,
|
|
5
20
|
> extends Schema<TValue> {
|
|
@@ -37,6 +52,38 @@ export class TokenSchema<
|
|
|
37
52
|
}
|
|
38
53
|
}
|
|
39
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Creates a token schema for Lexicon named constants.
|
|
57
|
+
*
|
|
58
|
+
* Tokens are used in Lexicon as named constants or enum-like values.
|
|
59
|
+
* The token instance can be used both as a schema validator and as
|
|
60
|
+
* the token value itself (it serializes to its string value).
|
|
61
|
+
*
|
|
62
|
+
* @param nsid - The NSID part of the token
|
|
63
|
+
* @param hash - The hash part of the token (defaults to 'main')
|
|
64
|
+
* @returns A new {@link TokenSchema} instance
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* // Define tokens
|
|
69
|
+
* const requestLess = l.token('app.bsky.feed.defs', 'requestLess')
|
|
70
|
+
* const requestMore = l.token('app.bsky.feed.defs', 'requestMore')
|
|
71
|
+
*
|
|
72
|
+
* // Use as a value
|
|
73
|
+
* console.log(requestLess.toString()) // 'app.bsky.feed.defs#requestLess'
|
|
74
|
+
*
|
|
75
|
+
* // Use in union for validation
|
|
76
|
+
* const feedbackSchema = l.union([requestLess, requestMore])
|
|
77
|
+
*
|
|
78
|
+
* // Validate
|
|
79
|
+
* feedbackSchema.parse('app.bsky.feed.defs#requestLess') // success
|
|
80
|
+
*
|
|
81
|
+
* // Token instances can be used as values in other schemas
|
|
82
|
+
* const feedbackRequest = l.object({
|
|
83
|
+
* feedback: requestLess, // Accepts the token value
|
|
84
|
+
* })
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
40
87
|
/*@__NO_SIDE_EFFECTS__*/
|
|
41
88
|
export function token<
|
|
42
89
|
const N extends NsidString,
|