@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.
Files changed (238) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/core/$type.d.ts +149 -0
  3. package/dist/core/$type.d.ts.map +1 -1
  4. package/dist/core/$type.js +44 -0
  5. package/dist/core/$type.js.map +1 -1
  6. package/dist/core/record-key.d.ts +44 -0
  7. package/dist/core/record-key.d.ts.map +1 -1
  8. package/dist/core/record-key.js +30 -0
  9. package/dist/core/record-key.js.map +1 -1
  10. package/dist/core/result.d.ts +85 -4
  11. package/dist/core/result.d.ts.map +1 -1
  12. package/dist/core/result.js +60 -4
  13. package/dist/core/result.js.map +1 -1
  14. package/dist/core/schema.d.ts +229 -2
  15. package/dist/core/schema.d.ts.map +1 -1
  16. package/dist/core/schema.js +197 -4
  17. package/dist/core/schema.js.map +1 -1
  18. package/dist/core/string-format.d.ts +244 -11
  19. package/dist/core/string-format.d.ts.map +1 -1
  20. package/dist/core/string-format.js +150 -0
  21. package/dist/core/string-format.js.map +1 -1
  22. package/dist/core/types.d.ts +90 -3
  23. package/dist/core/types.d.ts.map +1 -1
  24. package/dist/core/types.js.map +1 -1
  25. package/dist/core/validation-error.d.ts +60 -0
  26. package/dist/core/validation-error.d.ts.map +1 -1
  27. package/dist/core/validation-error.js +60 -0
  28. package/dist/core/validation-error.js.map +1 -1
  29. package/dist/core/validation-issue.d.ts +61 -0
  30. package/dist/core/validation-issue.d.ts.map +1 -1
  31. package/dist/core/validation-issue.js +51 -0
  32. package/dist/core/validation-issue.js.map +1 -1
  33. package/dist/core/validator.d.ts +347 -10
  34. package/dist/core/validator.d.ts.map +1 -1
  35. package/dist/core/validator.js +184 -3
  36. package/dist/core/validator.js.map +1 -1
  37. package/dist/helpers.d.ts +9 -24
  38. package/dist/helpers.d.ts.map +1 -1
  39. package/dist/helpers.js.map +1 -1
  40. package/dist/schema/array.d.ts +45 -0
  41. package/dist/schema/array.d.ts.map +1 -1
  42. package/dist/schema/array.js +14 -0
  43. package/dist/schema/array.js.map +1 -1
  44. package/dist/schema/blob.d.ts +46 -0
  45. package/dist/schema/blob.d.ts.map +1 -1
  46. package/dist/schema/blob.js +39 -0
  47. package/dist/schema/blob.js.map +1 -1
  48. package/dist/schema/boolean.d.ts +28 -0
  49. package/dist/schema/boolean.d.ts.map +1 -1
  50. package/dist/schema/boolean.js +28 -0
  51. package/dist/schema/boolean.js.map +1 -1
  52. package/dist/schema/bytes.d.ts +38 -0
  53. package/dist/schema/bytes.d.ts.map +1 -1
  54. package/dist/schema/bytes.js +32 -0
  55. package/dist/schema/bytes.js.map +1 -1
  56. package/dist/schema/cid.d.ts +38 -0
  57. package/dist/schema/cid.d.ts.map +1 -1
  58. package/dist/schema/cid.js +33 -0
  59. package/dist/schema/cid.js.map +1 -1
  60. package/dist/schema/custom.d.ts +66 -1
  61. package/dist/schema/custom.d.ts.map +1 -1
  62. package/dist/schema/custom.js +54 -0
  63. package/dist/schema/custom.js.map +1 -1
  64. package/dist/schema/dict.d.ts +44 -0
  65. package/dist/schema/dict.d.ts.map +1 -1
  66. package/dist/schema/dict.js +44 -0
  67. package/dist/schema/dict.js.map +1 -1
  68. package/dist/schema/discriminated-union.d.ts +58 -0
  69. package/dist/schema/discriminated-union.d.ts.map +1 -1
  70. package/dist/schema/discriminated-union.js +45 -0
  71. package/dist/schema/discriminated-union.js.map +1 -1
  72. package/dist/schema/enum.d.ts +48 -0
  73. package/dist/schema/enum.d.ts.map +1 -1
  74. package/dist/schema/enum.js +48 -0
  75. package/dist/schema/enum.js.map +1 -1
  76. package/dist/schema/integer.d.ts +42 -0
  77. package/dist/schema/integer.d.ts.map +1 -1
  78. package/dist/schema/integer.js +36 -0
  79. package/dist/schema/integer.js.map +1 -1
  80. package/dist/schema/intersection.d.ts +54 -0
  81. package/dist/schema/intersection.d.ts.map +1 -1
  82. package/dist/schema/intersection.js +49 -0
  83. package/dist/schema/intersection.js.map +1 -1
  84. package/dist/schema/literal.d.ts +44 -0
  85. package/dist/schema/literal.d.ts.map +1 -1
  86. package/dist/schema/literal.js +44 -0
  87. package/dist/schema/literal.js.map +1 -1
  88. package/dist/schema/never.d.ts +42 -0
  89. package/dist/schema/never.d.ts.map +1 -1
  90. package/dist/schema/never.js +42 -0
  91. package/dist/schema/never.js.map +1 -1
  92. package/dist/schema/null.d.ts +29 -0
  93. package/dist/schema/null.d.ts.map +1 -1
  94. package/dist/schema/null.js +29 -0
  95. package/dist/schema/null.js.map +1 -1
  96. package/dist/schema/nullable.d.ts +41 -0
  97. package/dist/schema/nullable.d.ts.map +1 -1
  98. package/dist/schema/nullable.js +41 -0
  99. package/dist/schema/nullable.js.map +1 -1
  100. package/dist/schema/object.d.ts +56 -0
  101. package/dist/schema/object.d.ts.map +1 -1
  102. package/dist/schema/object.js +51 -0
  103. package/dist/schema/object.js.map +1 -1
  104. package/dist/schema/optional.d.ts +42 -0
  105. package/dist/schema/optional.d.ts.map +1 -1
  106. package/dist/schema/optional.js +42 -0
  107. package/dist/schema/optional.js.map +1 -1
  108. package/dist/schema/params.d.ts +90 -10
  109. package/dist/schema/params.d.ts.map +1 -1
  110. package/dist/schema/params.js +84 -10
  111. package/dist/schema/params.js.map +1 -1
  112. package/dist/schema/payload.d.ts +111 -15
  113. package/dist/schema/payload.d.ts.map +1 -1
  114. package/dist/schema/payload.js +70 -0
  115. package/dist/schema/payload.js.map +1 -1
  116. package/dist/schema/permission-set.d.ts +58 -0
  117. package/dist/schema/permission-set.d.ts.map +1 -1
  118. package/dist/schema/permission-set.js +50 -0
  119. package/dist/schema/permission-set.js.map +1 -1
  120. package/dist/schema/permission.d.ts +42 -0
  121. package/dist/schema/permission.d.ts.map +1 -1
  122. package/dist/schema/permission.js +39 -0
  123. package/dist/schema/permission.js.map +1 -1
  124. package/dist/schema/procedure.d.ts +64 -0
  125. package/dist/schema/procedure.d.ts.map +1 -1
  126. package/dist/schema/procedure.js +64 -0
  127. package/dist/schema/procedure.js.map +1 -1
  128. package/dist/schema/query.d.ts +55 -0
  129. package/dist/schema/query.d.ts.map +1 -1
  130. package/dist/schema/query.js +55 -0
  131. package/dist/schema/query.js.map +1 -1
  132. package/dist/schema/record.d.ts +63 -8
  133. package/dist/schema/record.d.ts.map +1 -1
  134. package/dist/schema/record.js +20 -0
  135. package/dist/schema/record.js.map +1 -1
  136. package/dist/schema/ref.d.ts +50 -0
  137. package/dist/schema/ref.d.ts.map +1 -1
  138. package/dist/schema/ref.js +17 -0
  139. package/dist/schema/ref.js.map +1 -1
  140. package/dist/schema/refine.d.ts +58 -9
  141. package/dist/schema/refine.d.ts.map +1 -1
  142. package/dist/schema/refine.js.map +1 -1
  143. package/dist/schema/regexp.d.ts +44 -0
  144. package/dist/schema/regexp.d.ts.map +1 -1
  145. package/dist/schema/regexp.js +44 -0
  146. package/dist/schema/regexp.js.map +1 -1
  147. package/dist/schema/string.d.ts +50 -0
  148. package/dist/schema/string.d.ts.map +1 -1
  149. package/dist/schema/string.js +41 -0
  150. package/dist/schema/string.js.map +1 -1
  151. package/dist/schema/subscription.d.ts +72 -2
  152. package/dist/schema/subscription.d.ts.map +1 -1
  153. package/dist/schema/subscription.js +59 -0
  154. package/dist/schema/subscription.js.map +1 -1
  155. package/dist/schema/token.d.ts +47 -0
  156. package/dist/schema/token.d.ts.map +1 -1
  157. package/dist/schema/token.js +47 -0
  158. package/dist/schema/token.js.map +1 -1
  159. package/dist/schema/typed-object.d.ts +62 -8
  160. package/dist/schema/typed-object.d.ts.map +1 -1
  161. package/dist/schema/typed-object.js +18 -0
  162. package/dist/schema/typed-object.js.map +1 -1
  163. package/dist/schema/typed-ref.d.ts +53 -0
  164. package/dist/schema/typed-ref.d.ts.map +1 -1
  165. package/dist/schema/typed-ref.js +15 -0
  166. package/dist/schema/typed-ref.js.map +1 -1
  167. package/dist/schema/typed-union.d.ts +50 -1
  168. package/dist/schema/typed-union.d.ts.map +1 -1
  169. package/dist/schema/typed-union.js +50 -1
  170. package/dist/schema/typed-union.js.map +1 -1
  171. package/dist/schema/union.d.ts +45 -0
  172. package/dist/schema/union.d.ts.map +1 -1
  173. package/dist/schema/union.js +40 -0
  174. package/dist/schema/union.js.map +1 -1
  175. package/dist/schema/unknown-object.d.ts +34 -0
  176. package/dist/schema/unknown-object.d.ts.map +1 -1
  177. package/dist/schema/unknown-object.js +31 -0
  178. package/dist/schema/unknown-object.js.map +1 -1
  179. package/dist/schema/unknown.d.ts +33 -0
  180. package/dist/schema/unknown.d.ts.map +1 -1
  181. package/dist/schema/unknown.js +33 -0
  182. package/dist/schema/unknown.js.map +1 -1
  183. package/dist/schema/with-default.d.ts +44 -0
  184. package/dist/schema/with-default.d.ts.map +1 -1
  185. package/dist/schema/with-default.js +44 -0
  186. package/dist/schema/with-default.js.map +1 -1
  187. package/package.json +3 -3
  188. package/src/core/$type.ts +150 -18
  189. package/src/core/record-key.ts +44 -0
  190. package/src/core/result.ts +86 -4
  191. package/src/core/schema.ts +236 -7
  192. package/src/core/string-format.ts +259 -13
  193. package/src/core/types.ts +91 -3
  194. package/src/core/validation-error.ts +60 -0
  195. package/src/core/validation-issue.ts +65 -0
  196. package/src/core/validator.ts +351 -10
  197. package/src/helpers.test.ts +110 -29
  198. package/src/helpers.ts +9 -14
  199. package/src/schema/array.test.ts +94 -79
  200. package/src/schema/array.ts +45 -0
  201. package/src/schema/blob.ts +46 -0
  202. package/src/schema/boolean.ts +28 -0
  203. package/src/schema/bytes.ts +38 -0
  204. package/src/schema/cid.ts +38 -0
  205. package/src/schema/custom.ts +66 -1
  206. package/src/schema/dict.ts +44 -0
  207. package/src/schema/discriminated-union.ts +58 -0
  208. package/src/schema/enum.ts +48 -0
  209. package/src/schema/integer.ts +42 -0
  210. package/src/schema/intersection.ts +54 -0
  211. package/src/schema/literal.ts +44 -0
  212. package/src/schema/never.ts +42 -0
  213. package/src/schema/null.ts +29 -0
  214. package/src/schema/nullable.ts +41 -0
  215. package/src/schema/object.ts +56 -0
  216. package/src/schema/optional.ts +42 -0
  217. package/src/schema/params.test.ts +58 -2
  218. package/src/schema/params.ts +125 -19
  219. package/src/schema/payload.test.ts +3 -3
  220. package/src/schema/payload.ts +142 -38
  221. package/src/schema/permission-set.ts +58 -0
  222. package/src/schema/permission.ts +42 -0
  223. package/src/schema/procedure.ts +64 -0
  224. package/src/schema/query.ts +55 -0
  225. package/src/schema/record.ts +63 -8
  226. package/src/schema/ref.ts +50 -0
  227. package/src/schema/refine.ts +58 -9
  228. package/src/schema/regexp.ts +44 -0
  229. package/src/schema/string.ts +50 -0
  230. package/src/schema/subscription.ts +72 -2
  231. package/src/schema/token.ts +47 -0
  232. package/src/schema/typed-object.ts +62 -8
  233. package/src/schema/typed-ref.ts +53 -0
  234. package/src/schema/typed-union.ts +55 -2
  235. package/src/schema/union.ts +45 -0
  236. package/src/schema/unknown-object.ts +34 -0
  237. package/src/schema/unknown.ts +33 -0
  238. package/src/schema/with-default.ts +44 -0
@@ -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,
@@ -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 allows creating a {@link RecordSchema}, and infer the output type
120
- * from the provided arguments, without requiring to specify any of the
121
- * generics. This is useful when you want to define a record without
122
- * explicitly defining its interface. This version does not support circular
123
- * references, as TypeScript cannot infer types in such cases.
124
- * - One allows creating a {@link RecordSchema} with an explicitly defined
125
- * interface. This will typically be used by codegen (`lex build`) to generate
126
- * schemas that work even if they contain circular references.
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>,
@@ -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
- * Create a refined schema based on an existing schema and a refinement check.
63
+ * Creates a refined schema by adding additional validation constraints.
36
64
  *
37
- * @param schema - The base schema to refine.
38
- * @param refinement - The refinement check to apply.
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
- * const PositiveInt = refine(l.integer(), {
74
+ * // Simple check refinement
75
+ * const positiveInt = l.refine(l.integer(), {
44
76
  * check: (value) => value > 0,
45
- * message: 'Value must be a positive integer',
77
+ * message: 'Value must be positive',
46
78
  * })
47
- * const result = PositiveInt.validate(-5)
48
- * // result.success === false
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<
@@ -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)
@@ -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)
@@ -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,