@atproto/lex-schema 0.0.10 → 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 (239) hide show
  1. package/CHANGELOG.md +26 -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 +61 -1
  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 +13 -25
  38. package/dist/helpers.d.ts.map +1 -1
  39. package/dist/helpers.js +2 -2
  40. package/dist/helpers.js.map +1 -1
  41. package/dist/schema/array.d.ts +45 -0
  42. package/dist/schema/array.d.ts.map +1 -1
  43. package/dist/schema/array.js +14 -0
  44. package/dist/schema/array.js.map +1 -1
  45. package/dist/schema/blob.d.ts +46 -0
  46. package/dist/schema/blob.d.ts.map +1 -1
  47. package/dist/schema/blob.js +39 -0
  48. package/dist/schema/blob.js.map +1 -1
  49. package/dist/schema/boolean.d.ts +28 -0
  50. package/dist/schema/boolean.d.ts.map +1 -1
  51. package/dist/schema/boolean.js +28 -0
  52. package/dist/schema/boolean.js.map +1 -1
  53. package/dist/schema/bytes.d.ts +38 -0
  54. package/dist/schema/bytes.d.ts.map +1 -1
  55. package/dist/schema/bytes.js +32 -0
  56. package/dist/schema/bytes.js.map +1 -1
  57. package/dist/schema/cid.d.ts +38 -0
  58. package/dist/schema/cid.d.ts.map +1 -1
  59. package/dist/schema/cid.js +33 -0
  60. package/dist/schema/cid.js.map +1 -1
  61. package/dist/schema/custom.d.ts +66 -1
  62. package/dist/schema/custom.d.ts.map +1 -1
  63. package/dist/schema/custom.js +54 -0
  64. package/dist/schema/custom.js.map +1 -1
  65. package/dist/schema/dict.d.ts +44 -0
  66. package/dist/schema/dict.d.ts.map +1 -1
  67. package/dist/schema/dict.js +44 -0
  68. package/dist/schema/dict.js.map +1 -1
  69. package/dist/schema/discriminated-union.d.ts +58 -0
  70. package/dist/schema/discriminated-union.d.ts.map +1 -1
  71. package/dist/schema/discriminated-union.js +45 -0
  72. package/dist/schema/discriminated-union.js.map +1 -1
  73. package/dist/schema/enum.d.ts +48 -0
  74. package/dist/schema/enum.d.ts.map +1 -1
  75. package/dist/schema/enum.js +48 -0
  76. package/dist/schema/enum.js.map +1 -1
  77. package/dist/schema/integer.d.ts +42 -0
  78. package/dist/schema/integer.d.ts.map +1 -1
  79. package/dist/schema/integer.js +36 -0
  80. package/dist/schema/integer.js.map +1 -1
  81. package/dist/schema/intersection.d.ts +54 -0
  82. package/dist/schema/intersection.d.ts.map +1 -1
  83. package/dist/schema/intersection.js +49 -0
  84. package/dist/schema/intersection.js.map +1 -1
  85. package/dist/schema/literal.d.ts +44 -0
  86. package/dist/schema/literal.d.ts.map +1 -1
  87. package/dist/schema/literal.js +44 -0
  88. package/dist/schema/literal.js.map +1 -1
  89. package/dist/schema/never.d.ts +42 -0
  90. package/dist/schema/never.d.ts.map +1 -1
  91. package/dist/schema/never.js +42 -0
  92. package/dist/schema/never.js.map +1 -1
  93. package/dist/schema/null.d.ts +29 -0
  94. package/dist/schema/null.d.ts.map +1 -1
  95. package/dist/schema/null.js +29 -0
  96. package/dist/schema/null.js.map +1 -1
  97. package/dist/schema/nullable.d.ts +41 -0
  98. package/dist/schema/nullable.d.ts.map +1 -1
  99. package/dist/schema/nullable.js +41 -0
  100. package/dist/schema/nullable.js.map +1 -1
  101. package/dist/schema/object.d.ts +56 -0
  102. package/dist/schema/object.d.ts.map +1 -1
  103. package/dist/schema/object.js +51 -0
  104. package/dist/schema/object.js.map +1 -1
  105. package/dist/schema/optional.d.ts +42 -0
  106. package/dist/schema/optional.d.ts.map +1 -1
  107. package/dist/schema/optional.js +42 -0
  108. package/dist/schema/optional.js.map +1 -1
  109. package/dist/schema/params.d.ts +89 -7
  110. package/dist/schema/params.d.ts.map +1 -1
  111. package/dist/schema/params.js +84 -10
  112. package/dist/schema/params.js.map +1 -1
  113. package/dist/schema/payload.d.ts +111 -15
  114. package/dist/schema/payload.d.ts.map +1 -1
  115. package/dist/schema/payload.js +70 -0
  116. package/dist/schema/payload.js.map +1 -1
  117. package/dist/schema/permission-set.d.ts +58 -0
  118. package/dist/schema/permission-set.d.ts.map +1 -1
  119. package/dist/schema/permission-set.js +50 -0
  120. package/dist/schema/permission-set.js.map +1 -1
  121. package/dist/schema/permission.d.ts +42 -0
  122. package/dist/schema/permission.d.ts.map +1 -1
  123. package/dist/schema/permission.js +39 -0
  124. package/dist/schema/permission.js.map +1 -1
  125. package/dist/schema/procedure.d.ts +64 -0
  126. package/dist/schema/procedure.d.ts.map +1 -1
  127. package/dist/schema/procedure.js +64 -0
  128. package/dist/schema/procedure.js.map +1 -1
  129. package/dist/schema/query.d.ts +55 -0
  130. package/dist/schema/query.d.ts.map +1 -1
  131. package/dist/schema/query.js +55 -0
  132. package/dist/schema/query.js.map +1 -1
  133. package/dist/schema/record.d.ts +63 -8
  134. package/dist/schema/record.d.ts.map +1 -1
  135. package/dist/schema/record.js +20 -0
  136. package/dist/schema/record.js.map +1 -1
  137. package/dist/schema/ref.d.ts +50 -0
  138. package/dist/schema/ref.d.ts.map +1 -1
  139. package/dist/schema/ref.js +17 -0
  140. package/dist/schema/ref.js.map +1 -1
  141. package/dist/schema/refine.d.ts +58 -9
  142. package/dist/schema/refine.d.ts.map +1 -1
  143. package/dist/schema/refine.js.map +1 -1
  144. package/dist/schema/regexp.d.ts +44 -0
  145. package/dist/schema/regexp.d.ts.map +1 -1
  146. package/dist/schema/regexp.js +44 -0
  147. package/dist/schema/regexp.js.map +1 -1
  148. package/dist/schema/string.d.ts +50 -0
  149. package/dist/schema/string.d.ts.map +1 -1
  150. package/dist/schema/string.js +41 -0
  151. package/dist/schema/string.js.map +1 -1
  152. package/dist/schema/subscription.d.ts +72 -2
  153. package/dist/schema/subscription.d.ts.map +1 -1
  154. package/dist/schema/subscription.js +59 -0
  155. package/dist/schema/subscription.js.map +1 -1
  156. package/dist/schema/token.d.ts +47 -0
  157. package/dist/schema/token.d.ts.map +1 -1
  158. package/dist/schema/token.js +47 -0
  159. package/dist/schema/token.js.map +1 -1
  160. package/dist/schema/typed-object.d.ts +62 -8
  161. package/dist/schema/typed-object.d.ts.map +1 -1
  162. package/dist/schema/typed-object.js +18 -0
  163. package/dist/schema/typed-object.js.map +1 -1
  164. package/dist/schema/typed-ref.d.ts +53 -0
  165. package/dist/schema/typed-ref.d.ts.map +1 -1
  166. package/dist/schema/typed-ref.js +15 -0
  167. package/dist/schema/typed-ref.js.map +1 -1
  168. package/dist/schema/typed-union.d.ts +50 -1
  169. package/dist/schema/typed-union.d.ts.map +1 -1
  170. package/dist/schema/typed-union.js +50 -1
  171. package/dist/schema/typed-union.js.map +1 -1
  172. package/dist/schema/union.d.ts +45 -0
  173. package/dist/schema/union.d.ts.map +1 -1
  174. package/dist/schema/union.js +40 -0
  175. package/dist/schema/union.js.map +1 -1
  176. package/dist/schema/unknown-object.d.ts +34 -0
  177. package/dist/schema/unknown-object.d.ts.map +1 -1
  178. package/dist/schema/unknown-object.js +31 -0
  179. package/dist/schema/unknown-object.js.map +1 -1
  180. package/dist/schema/unknown.d.ts +33 -0
  181. package/dist/schema/unknown.d.ts.map +1 -1
  182. package/dist/schema/unknown.js +33 -0
  183. package/dist/schema/unknown.js.map +1 -1
  184. package/dist/schema/with-default.d.ts +44 -0
  185. package/dist/schema/with-default.d.ts.map +1 -1
  186. package/dist/schema/with-default.js +44 -0
  187. package/dist/schema/with-default.js.map +1 -1
  188. package/package.json +4 -4
  189. package/src/core/$type.ts +150 -18
  190. package/src/core/record-key.ts +44 -0
  191. package/src/core/result.ts +86 -4
  192. package/src/core/schema.ts +236 -7
  193. package/src/core/string-format.ts +259 -13
  194. package/src/core/types.ts +91 -3
  195. package/src/core/validation-error.ts +60 -0
  196. package/src/core/validation-issue.ts +65 -0
  197. package/src/core/validator.ts +351 -10
  198. package/src/helpers.test.ts +110 -29
  199. package/src/helpers.ts +14 -14
  200. package/src/schema/array.test.ts +94 -79
  201. package/src/schema/array.ts +45 -0
  202. package/src/schema/blob.ts +46 -0
  203. package/src/schema/boolean.ts +28 -0
  204. package/src/schema/bytes.ts +38 -0
  205. package/src/schema/cid.ts +38 -0
  206. package/src/schema/custom.ts +66 -1
  207. package/src/schema/dict.ts +44 -0
  208. package/src/schema/discriminated-union.ts +58 -0
  209. package/src/schema/enum.ts +48 -0
  210. package/src/schema/integer.ts +42 -0
  211. package/src/schema/intersection.ts +54 -0
  212. package/src/schema/literal.ts +44 -0
  213. package/src/schema/never.ts +42 -0
  214. package/src/schema/null.ts +29 -0
  215. package/src/schema/nullable.ts +41 -0
  216. package/src/schema/object.ts +56 -0
  217. package/src/schema/optional.ts +42 -0
  218. package/src/schema/params.test.ts +58 -2
  219. package/src/schema/params.ts +124 -16
  220. package/src/schema/payload.test.ts +3 -3
  221. package/src/schema/payload.ts +142 -38
  222. package/src/schema/permission-set.ts +58 -0
  223. package/src/schema/permission.ts +42 -0
  224. package/src/schema/procedure.ts +64 -0
  225. package/src/schema/query.ts +55 -0
  226. package/src/schema/record.ts +63 -8
  227. package/src/schema/ref.ts +50 -0
  228. package/src/schema/refine.ts +58 -9
  229. package/src/schema/regexp.ts +44 -0
  230. package/src/schema/string.ts +50 -0
  231. package/src/schema/subscription.ts +72 -2
  232. package/src/schema/token.ts +47 -0
  233. package/src/schema/typed-object.ts +62 -8
  234. package/src/schema/typed-ref.ts +53 -0
  235. package/src/schema/typed-union.ts +55 -2
  236. package/src/schema/union.ts +45 -0
  237. package/src/schema/unknown-object.ts +34 -0
  238. package/src/schema/unknown.ts +33 -0
  239. package/src/schema/with-default.ts +44 -0
@@ -22,42 +22,193 @@ import {
22
22
  } from '@atproto/syntax'
23
23
  import { CheckFn } from '../util/assertion-util.js'
24
24
 
25
- // Expose all individual string format types and type guards
25
+ // -----------------------------------------------------------------------------
26
+ // Individual string format types and type guards
27
+ // -----------------------------------------------------------------------------
26
28
 
27
- export type { AtIdentifierString }
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
- export type { AtUriString }
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
- export type CidString = string
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
- export type { DatetimeString }
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
- export type { DidString }
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
- export type { HandleString }
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
- export type LanguageString = string
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
- export type { NsidString }
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
- export type { RecordKeyString }
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
- export type { TidString }
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
- export type { UriString }
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
- // String format registry (maps format names to their types and type guards)
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[]
package/src/core/types.ts CHANGED
@@ -1,23 +1,95 @@
1
1
  /**
2
- * Same as {@link string} but prevents TypeScript allowing union types to
2
+ * Same as `string` but prevents TypeScript from allowing union types to
3
3
  * be widened to `string` in IDEs.
4
+ *
5
+ * This is useful when you want autocompletion for known string values
6
+ * while still allowing arbitrary strings.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * // With plain string, union is widened:
11
+ * type Status1 = 'active' | 'inactive' | string // just becomes "string"
12
+ *
13
+ * // With UnknownString, union is preserved:
14
+ * type Status2 = 'active' | 'inactive' | UnknownString
15
+ * // Autocomplete will suggest 'active' and 'inactive'
16
+ * ```
4
17
  */
5
18
  export type UnknownString = string & NonNullable<unknown>
6
19
 
20
+ /**
21
+ * Simplifies a type by expanding intersections and mapped types.
22
+ *
23
+ * This improves IDE tooltips by showing the actual shape of a type
24
+ * rather than complex intersections.
25
+ *
26
+ * @typeParam T - The type to simplify
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * type Complex = { a: string } & { b: number }
31
+ * type Simple = Simplify<Complex>
32
+ * // Hover shows: { a: string; b: number }
33
+ * ```
34
+ */
7
35
  export type Simplify<T> = { [K in keyof T]: T[K] } & NonNullable<unknown>
8
36
 
37
+ /**
38
+ * Internal symbol for branding restricted types.
39
+ * @internal
40
+ */
9
41
  declare const __restricted: unique symbol
42
+
10
43
  /**
11
44
  * A type that represents a value that cannot be used, with a custom
12
45
  * message explaining the restriction.
46
+ *
47
+ * This is useful for creating "never use this" types that provide
48
+ * helpful error messages when someone tries to use them.
49
+ *
50
+ * @typeParam Message - A string literal type containing the error message
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * type DeprecatedField = Restricted<'This field has been deprecated. Use newField instead.'>
55
+ *
56
+ * interface MyType {
57
+ * oldField?: DeprecatedField
58
+ * newField: string
59
+ * }
60
+ *
61
+ * const obj: MyType = {
62
+ * oldField: 'value', // Error: Type 'string' is not assignable to type 'Restricted<...>'
63
+ * newField: 'value'
64
+ * }
65
+ * ```
13
66
  */
14
67
  export type Restricted<Message extends string> = typeof __restricted & {
15
68
  [__restricted]: Message
16
69
  }
17
70
 
18
71
  /**
19
- * Converts all properties of `P` that are optional (i.e. may be `undefined`)
20
- * into actual optional properties on the resulting type.
72
+ * Converts all properties of `P` that may be `undefined` into actual
73
+ * optional properties on the resulting type.
74
+ *
75
+ * This is useful when working with types where some properties are typed as
76
+ * `T | undefined` but should really be optional (`T?`).
77
+ *
78
+ * @typeParam P - The object type to transform
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * type Input = {
83
+ * required: string
84
+ * optional: string | undefined
85
+ * }
86
+ *
87
+ * type Output = WithOptionalProperties<Input>
88
+ * // Result: {
89
+ * // required: string
90
+ * // optional?: string | undefined
91
+ * // }
92
+ * ```
21
93
  */
22
94
  export type WithOptionalProperties<P> = Simplify<
23
95
  {
@@ -27,6 +99,22 @@ export type WithOptionalProperties<P> = Simplify<
27
99
  }
28
100
  >
29
101
 
102
+ /**
103
+ * Creates a type by omitting a specific key from an object type.
104
+ *
105
+ * Similar to TypeScript's built-in `Omit`, but preserves the type structure
106
+ * more accurately in some edge cases.
107
+ *
108
+ * @typeParam T - The object type to transform
109
+ * @typeParam K - The key to omit (must be a key of T)
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * type Person = { name: string; age: number; email: string }
114
+ * type PersonWithoutEmail = OmitKey<Person, 'email'>
115
+ * // Result: { name: string; age: number }
116
+ * ```
117
+ */
30
118
  export type OmitKey<T, K extends keyof T> = {
31
119
  [K2 in keyof T as K2 extends K ? never : K2]: T[K2]
32
120
  }
@@ -7,17 +7,60 @@ import {
7
7
  IssueInvalidValue,
8
8
  } from './validation-issue.js'
9
9
 
10
+ /**
11
+ * Error thrown when validation fails.
12
+ *
13
+ * Contains detailed information about all validation issues encountered,
14
+ * including the path to each invalid value and descriptions of what was
15
+ * expected vs what was received.
16
+ *
17
+ * Extends {@link LexError} with the error name "InvalidRequest" for
18
+ * consistency with the AT Protocol error handling conventions.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const error = new ValidationError([
23
+ * new IssueInvalidType(['user', 'age'], 'hello', ['number'])
24
+ * ])
25
+ * console.log(error.message)
26
+ * // "Expected number value type at $.user.age (got string)"
27
+ *
28
+ * console.log(error.issues.length) // 1
29
+ * console.log(error.toJSON())
30
+ * // { error: 'InvalidRequest', message: '...', issues: [...] }
31
+ * ```
32
+ */
10
33
  export class ValidationError extends LexError {
11
34
  name = 'ValidationError'
12
35
 
36
+ /**
37
+ * The list of validation issues that caused this error.
38
+ *
39
+ * Issues are aggregated when possible (e.g., multiple invalid type issues
40
+ * at the same path are combined into a single issue listing all expected types).
41
+ */
13
42
  readonly issues: Issue[]
14
43
 
44
+ /**
45
+ * Creates a new validation error from a list of issues.
46
+ *
47
+ * Issues are automatically aggregated to combine related issues at the same
48
+ * path (e.g., multiple type expectations from a union schema).
49
+ *
50
+ * @param issues - The validation issues that caused this error
51
+ * @param options - Standard Error options (e.g., `cause`)
52
+ */
15
53
  constructor(issues: Issue[], options?: ErrorOptions) {
16
54
  const issuesAgg = aggregateIssues(issues)
17
55
  super('InvalidRequest', issuesAgg.join(', '), options)
18
56
  this.issues = issuesAgg
19
57
  }
20
58
 
59
+ /**
60
+ * Converts the error to a JSON-serializable object.
61
+ *
62
+ * @returns An object containing the error details and all issues in JSON format
63
+ */
21
64
  toJSON() {
22
65
  return {
23
66
  ...super.toJSON(),
@@ -25,6 +68,23 @@ export class ValidationError extends LexError {
25
68
  }
26
69
  }
27
70
 
71
+ /**
72
+ * Creates a validation error by combining multiple validation failures.
73
+ *
74
+ * This is useful when validating against multiple possible schemas (e.g., unions)
75
+ * and all branches fail. The resulting error contains issues from all failures.
76
+ *
77
+ * @param failures - The validation failures to combine
78
+ * @returns A single validation error containing all issues from the failures
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * const failures = schemas.map(s => s.safeValidate(data)).filter(r => !r.success)
83
+ * if (failures.length === schemas.length) {
84
+ * throw ValidationError.fromFailures(failures)
85
+ * }
86
+ * ```
87
+ */
28
88
  static fromFailures(
29
89
  failures: ResultFailure<ValidationError>[],
30
90
  ): ValidationError {
@@ -1,6 +1,17 @@
1
1
  import { ifCid, isPlainObject } from '@atproto/lex-data'
2
2
  import { PropertyKey } from './property-key.js'
3
3
 
4
+ /**
5
+ * Abstract base class for all validation issues.
6
+ *
7
+ * An issue represents a single validation failure, containing:
8
+ * - A code identifying the type of issue
9
+ * - The path to the invalid value in the data structure
10
+ * - The actual input value that failed validation
11
+ *
12
+ * Subclasses add specific properties relevant to each issue type and
13
+ * implement the {@link toString} method for human-readable error messages.
14
+ */
4
15
  export abstract class Issue {
5
16
  constructor(
6
17
  readonly code: string,
@@ -8,8 +19,16 @@ export abstract class Issue {
8
19
  readonly input: unknown,
9
20
  ) {}
10
21
 
22
+ /**
23
+ * Returns a human-readable description of the validation issue.
24
+ */
11
25
  abstract toString(): string
12
26
 
27
+ /**
28
+ * Converts the issue to a JSON-serializable object.
29
+ *
30
+ * @returns An object containing the issue code, path, and message
31
+ */
13
32
  toJSON() {
14
33
  return {
15
34
  code: this.code,
@@ -19,6 +38,11 @@ export abstract class Issue {
19
38
  }
20
39
  }
21
40
 
41
+ /**
42
+ * A custom validation issue with a user-defined message.
43
+ *
44
+ * Use this for validation rules that don't fit into the standard issue categories.
45
+ */
22
46
  export class IssueCustom extends Issue {
23
47
  constructor(
24
48
  readonly path: readonly PropertyKey[],
@@ -33,6 +57,11 @@ export class IssueCustom extends Issue {
33
57
  }
34
58
  }
35
59
 
60
+ /**
61
+ * Issue for string values that don't match an expected format.
62
+ *
63
+ * Used for AT Protocol specific formats like DID, handle, NSID, AT-URI, etc.
64
+ */
36
65
  export class IssueInvalidFormat extends Issue {
37
66
  constructor(
38
67
  path: readonly PropertyKey[],
@@ -54,6 +83,7 @@ export class IssueInvalidFormat extends Issue {
54
83
  }
55
84
  }
56
85
 
86
+ /** Returns a human-readable description of the expected format. */
57
87
  get formatDescription(): string {
58
88
  switch (this.format) {
59
89
  case 'at-identifier':
@@ -74,6 +104,12 @@ export class IssueInvalidFormat extends Issue {
74
104
  }
75
105
  }
76
106
 
107
+ /**
108
+ * Issue for values that have an unexpected type.
109
+ *
110
+ * This is one of the most common validation issues, occurring when the
111
+ * runtime type of a value doesn't match the expected schema type.
112
+ */
77
113
  export class IssueInvalidType extends Issue {
78
114
  constructor(
79
115
  path: readonly PropertyKey[],
@@ -95,6 +131,12 @@ export class IssueInvalidType extends Issue {
95
131
  }
96
132
  }
97
133
 
134
+ /**
135
+ * Issue for values that don't match any of the expected literal values.
136
+ *
137
+ * Used when a value must be one of a specific set of allowed values
138
+ * (e.g., enum-like constraints).
139
+ */
98
140
  export class IssueInvalidValue extends Issue {
99
141
  constructor(
100
142
  path: readonly PropertyKey[],
@@ -116,6 +158,9 @@ export class IssueInvalidValue extends Issue {
116
158
  }
117
159
  }
118
160
 
161
+ /**
162
+ * Issue for missing required object properties.
163
+ */
119
164
  export class IssueRequiredKey extends Issue {
120
165
  constructor(
121
166
  path: readonly PropertyKey[],
@@ -137,6 +182,16 @@ export class IssueRequiredKey extends Issue {
137
182
  }
138
183
  }
139
184
 
185
+ /**
186
+ * The type of measurement for size constraint issues.
187
+ *
188
+ * - `'array'` - Array length
189
+ * - `'string'` - String length in characters
190
+ * - `'integer'` - Numeric value
191
+ * - `'grapheme'` - String length in grapheme clusters
192
+ * - `'bytes'` - Byte length
193
+ * - `'blob'` - Blob size
194
+ */
140
195
  export type MeasurableType =
141
196
  | 'array'
142
197
  | 'string'
@@ -145,6 +200,9 @@ export type MeasurableType =
145
200
  | 'bytes'
146
201
  | 'blob'
147
202
 
203
+ /**
204
+ * Issue for values that exceed a maximum constraint.
205
+ */
148
206
  export class IssueTooBig extends Issue {
149
207
  constructor(
150
208
  path: readonly PropertyKey[],
@@ -169,6 +227,9 @@ export class IssueTooBig extends Issue {
169
227
  }
170
228
  }
171
229
 
230
+ /**
231
+ * Issue for values that are below a minimum constraint.
232
+ */
172
233
  export class IssueTooSmall extends Issue {
173
234
  constructor(
174
235
  path: readonly PropertyKey[],
@@ -193,6 +254,10 @@ export class IssueTooSmall extends Issue {
193
254
  }
194
255
  }
195
256
 
257
+ // -----------------------------------------------------------------------------
258
+ // Helper functions for formatting error messages
259
+ // -----------------------------------------------------------------------------
260
+
196
261
  function stringifyExpectedType(expected: string): string {
197
262
  if (expected === '$typed') {
198
263
  return 'an object or record which includes a "$type" property'