@atproto/lex-schema 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/core/$type.d.ts +2 -2
  3. package/dist/core/$type.d.ts.map +1 -1
  4. package/dist/core/$type.js.map +1 -1
  5. package/dist/core/record-key.d.ts +1 -1
  6. package/dist/core/record-key.d.ts.map +1 -1
  7. package/dist/core/record-key.js.map +1 -1
  8. package/dist/core/schema.d.ts +3 -2
  9. package/dist/core/schema.d.ts.map +1 -1
  10. package/dist/core/schema.js +1 -1
  11. package/dist/core/schema.js.map +1 -1
  12. package/dist/core/standard-schema.d.ts +2 -2
  13. package/dist/core/standard-schema.d.ts.map +1 -1
  14. package/dist/core/standard-schema.js.map +1 -1
  15. package/dist/core/string-format.d.ts +2 -2
  16. package/dist/core/string-format.d.ts.map +1 -1
  17. package/dist/core/string-format.js.map +1 -1
  18. package/dist/core/validation-error.d.ts +1 -1
  19. package/dist/core/validation-error.d.ts.map +1 -1
  20. package/dist/core/validation-error.js +1 -1
  21. package/dist/core/validation-error.js.map +1 -1
  22. package/dist/core/validator.d.ts +1 -1
  23. package/dist/core/validator.d.ts.map +1 -1
  24. package/dist/core/validator.js +1 -1
  25. package/dist/core/validator.js.map +1 -1
  26. package/dist/helpers.d.ts +2 -2
  27. package/dist/helpers.d.ts.map +1 -1
  28. package/dist/helpers.js +2 -2
  29. package/dist/helpers.js.map +1 -1
  30. package/dist/schema/array.d.ts +1 -1
  31. package/dist/schema/array.d.ts.map +1 -1
  32. package/dist/schema/array.js +1 -1
  33. package/dist/schema/array.js.map +1 -1
  34. package/dist/schema/blob.d.ts +1 -1
  35. package/dist/schema/blob.d.ts.map +1 -1
  36. package/dist/schema/blob.js +2 -2
  37. package/dist/schema/blob.js.map +1 -1
  38. package/dist/schema/boolean.js +1 -1
  39. package/dist/schema/boolean.js.map +1 -1
  40. package/dist/schema/bytes.js +1 -1
  41. package/dist/schema/bytes.js.map +1 -1
  42. package/dist/schema/cid.d.ts +1 -1
  43. package/dist/schema/cid.d.ts.map +1 -1
  44. package/dist/schema/cid.js +3 -3
  45. package/dist/schema/cid.js.map +1 -1
  46. package/dist/schema/custom.js +1 -1
  47. package/dist/schema/custom.js.map +1 -1
  48. package/dist/schema/dict.d.ts +1 -1
  49. package/dist/schema/dict.d.ts.map +1 -1
  50. package/dist/schema/dict.js +1 -1
  51. package/dist/schema/dict.js.map +1 -1
  52. package/dist/schema/discriminated-union.d.ts +1 -1
  53. package/dist/schema/discriminated-union.d.ts.map +1 -1
  54. package/dist/schema/discriminated-union.js +2 -1
  55. package/dist/schema/discriminated-union.js.map +1 -1
  56. package/dist/schema/enum.js +1 -1
  57. package/dist/schema/enum.js.map +1 -1
  58. package/dist/schema/integer.js +1 -1
  59. package/dist/schema/integer.js.map +1 -1
  60. package/dist/schema/intersection.d.ts +1 -1
  61. package/dist/schema/intersection.d.ts.map +1 -1
  62. package/dist/schema/intersection.js +3 -1
  63. package/dist/schema/intersection.js.map +1 -1
  64. package/dist/schema/lex-map.d.ts +1 -1
  65. package/dist/schema/lex-map.d.ts.map +1 -1
  66. package/dist/schema/lex-map.js +1 -1
  67. package/dist/schema/lex-map.js.map +1 -1
  68. package/dist/schema/lex-value.d.ts +1 -1
  69. package/dist/schema/lex-value.d.ts.map +1 -1
  70. package/dist/schema/lex-value.js +1 -1
  71. package/dist/schema/lex-value.js.map +1 -1
  72. package/dist/schema/literal.js +1 -1
  73. package/dist/schema/literal.js.map +1 -1
  74. package/dist/schema/never.js +1 -1
  75. package/dist/schema/never.js.map +1 -1
  76. package/dist/schema/null.js +1 -1
  77. package/dist/schema/null.js.map +1 -1
  78. package/dist/schema/nullable.d.ts +1 -1
  79. package/dist/schema/nullable.d.ts.map +1 -1
  80. package/dist/schema/nullable.js +1 -1
  81. package/dist/schema/nullable.js.map +1 -1
  82. package/dist/schema/object.d.ts +2 -1
  83. package/dist/schema/object.d.ts.map +1 -1
  84. package/dist/schema/object.js +1 -1
  85. package/dist/schema/object.js.map +1 -1
  86. package/dist/schema/optional.d.ts +2 -1
  87. package/dist/schema/optional.d.ts.map +1 -1
  88. package/dist/schema/optional.js +2 -1
  89. package/dist/schema/optional.js.map +1 -1
  90. package/dist/schema/params.d.ts +1 -1
  91. package/dist/schema/params.d.ts.map +1 -1
  92. package/dist/schema/params.js +1 -1
  93. package/dist/schema/params.js.map +1 -1
  94. package/dist/schema/payload.d.ts +3 -2
  95. package/dist/schema/payload.d.ts.map +1 -1
  96. package/dist/schema/payload.js +2 -1
  97. package/dist/schema/payload.js.map +1 -1
  98. package/dist/schema/permission-set.d.ts +1 -1
  99. package/dist/schema/permission-set.d.ts.map +1 -1
  100. package/dist/schema/permission-set.js +1 -0
  101. package/dist/schema/permission-set.js.map +1 -1
  102. package/dist/schema/permission.d.ts +1 -1
  103. package/dist/schema/permission.d.ts.map +1 -1
  104. package/dist/schema/permission.js.map +1 -1
  105. package/dist/schema/procedure.d.ts +1 -1
  106. package/dist/schema/procedure.d.ts.map +1 -1
  107. package/dist/schema/procedure.js +2 -0
  108. package/dist/schema/procedure.js.map +1 -1
  109. package/dist/schema/query.d.ts +1 -1
  110. package/dist/schema/query.d.ts.map +1 -1
  111. package/dist/schema/query.js +2 -0
  112. package/dist/schema/query.js.map +1 -1
  113. package/dist/schema/record.d.ts +2 -2
  114. package/dist/schema/record.d.ts.map +1 -1
  115. package/dist/schema/record.js +1 -1
  116. package/dist/schema/record.js.map +1 -1
  117. package/dist/schema/ref.d.ts +1 -1
  118. package/dist/schema/ref.d.ts.map +1 -1
  119. package/dist/schema/ref.js +1 -1
  120. package/dist/schema/ref.js.map +1 -1
  121. package/dist/schema/refine.d.ts +2 -2
  122. package/dist/schema/refine.d.ts.map +1 -1
  123. package/dist/schema/refine.js +1 -1
  124. package/dist/schema/refine.js.map +1 -1
  125. package/dist/schema/regexp.js +1 -1
  126. package/dist/schema/regexp.js.map +1 -1
  127. package/dist/schema/string.d.ts +2 -2
  128. package/dist/schema/string.d.ts.map +1 -1
  129. package/dist/schema/string.js +1 -1
  130. package/dist/schema/string.js.map +1 -1
  131. package/dist/schema/subscription.d.ts +3 -2
  132. package/dist/schema/subscription.d.ts.map +1 -1
  133. package/dist/schema/subscription.js +2 -0
  134. package/dist/schema/subscription.js.map +1 -1
  135. package/dist/schema/token.d.ts +1 -1
  136. package/dist/schema/token.d.ts.map +1 -1
  137. package/dist/schema/token.js +1 -1
  138. package/dist/schema/token.js.map +1 -1
  139. package/dist/schema/typed-object.d.ts +2 -2
  140. package/dist/schema/typed-object.d.ts.map +1 -1
  141. package/dist/schema/typed-object.js +1 -1
  142. package/dist/schema/typed-object.js.map +1 -1
  143. package/dist/schema/typed-ref.d.ts +1 -1
  144. package/dist/schema/typed-ref.d.ts.map +1 -1
  145. package/dist/schema/typed-ref.js +1 -1
  146. package/dist/schema/typed-ref.js.map +1 -1
  147. package/dist/schema/typed-union.d.ts +1 -1
  148. package/dist/schema/typed-union.d.ts.map +1 -1
  149. package/dist/schema/typed-union.js +3 -1
  150. package/dist/schema/typed-union.js.map +1 -1
  151. package/dist/schema/union.d.ts +1 -1
  152. package/dist/schema/union.d.ts.map +1 -1
  153. package/dist/schema/union.js +1 -1
  154. package/dist/schema/union.js.map +1 -1
  155. package/dist/schema/unknown.js +1 -1
  156. package/dist/schema/unknown.js.map +1 -1
  157. package/dist/schema/with-default.d.ts +1 -1
  158. package/dist/schema/with-default.d.ts.map +1 -1
  159. package/dist/schema/with-default.js +1 -1
  160. package/dist/schema/with-default.js.map +1 -1
  161. package/package.json +6 -10
  162. package/src/core/$type.test.ts +0 -24
  163. package/src/core/$type.ts +0 -199
  164. package/src/core/record-key.ts +0 -85
  165. package/src/core/result.ts +0 -15
  166. package/src/core/schema.ts +0 -412
  167. package/src/core/standard-schema.test.ts +0 -124
  168. package/src/core/standard-schema.ts +0 -31
  169. package/src/core/string-format.ts +0 -411
  170. package/src/core/types.ts +0 -120
  171. package/src/core/validation-error.ts +0 -134
  172. package/src/core/validation-issue.ts +0 -340
  173. package/src/core/validator.ts +0 -636
  174. package/src/core.ts +0 -9
  175. package/src/external.ts +0 -3
  176. package/src/helpers.test.ts +0 -694
  177. package/src/helpers.ts +0 -222
  178. package/src/index.ts +0 -3
  179. package/src/schema/array.test.ts +0 -251
  180. package/src/schema/array.ts +0 -126
  181. package/src/schema/blob.test.ts +0 -733
  182. package/src/schema/blob.ts +0 -150
  183. package/src/schema/boolean.test.ts +0 -118
  184. package/src/schema/boolean.ts +0 -46
  185. package/src/schema/bytes.test.ts +0 -227
  186. package/src/schema/bytes.ts +0 -81
  187. package/src/schema/cid.test.ts +0 -125
  188. package/src/schema/cid.ts +0 -69
  189. package/src/schema/custom.test.ts +0 -414
  190. package/src/schema/custom.ts +0 -106
  191. package/src/schema/dict.test.ts +0 -181
  192. package/src/schema/dict.ts +0 -122
  193. package/src/schema/discriminated-union.test.ts +0 -676
  194. package/src/schema/discriminated-union.ts +0 -196
  195. package/src/schema/enum.test.ts +0 -398
  196. package/src/schema/enum.ts +0 -77
  197. package/src/schema/integer.test.ts +0 -314
  198. package/src/schema/integer.ts +0 -86
  199. package/src/schema/intersection.test.ts +0 -33
  200. package/src/schema/intersection.ts +0 -113
  201. package/src/schema/lex-map.test.ts +0 -593
  202. package/src/schema/lex-map.ts +0 -63
  203. package/src/schema/lex-value.test.ts +0 -81
  204. package/src/schema/lex-value.ts +0 -86
  205. package/src/schema/literal.test.ts +0 -533
  206. package/src/schema/literal.ts +0 -70
  207. package/src/schema/never.test.ts +0 -175
  208. package/src/schema/never.ts +0 -56
  209. package/src/schema/null.test.ts +0 -80
  210. package/src/schema/null.ts +0 -49
  211. package/src/schema/nullable.test.ts +0 -470
  212. package/src/schema/nullable.ts +0 -74
  213. package/src/schema/object.test.ts +0 -69
  214. package/src/schema/object.ts +0 -136
  215. package/src/schema/optional.test.ts +0 -479
  216. package/src/schema/optional.ts +0 -92
  217. package/src/schema/params.test.ts +0 -1118
  218. package/src/schema/params.ts +0 -371
  219. package/src/schema/payload.test.ts +0 -340
  220. package/src/schema/payload.ts +0 -204
  221. package/src/schema/permission-set.test.ts +0 -613
  222. package/src/schema/permission-set.ts +0 -86
  223. package/src/schema/permission.test.ts +0 -537
  224. package/src/schema/permission.ts +0 -63
  225. package/src/schema/procedure.test.ts +0 -324
  226. package/src/schema/procedure.ts +0 -98
  227. package/src/schema/query.test.ts +0 -348
  228. package/src/schema/query.ts +0 -86
  229. package/src/schema/record.test.ts +0 -812
  230. package/src/schema/record.ts +0 -217
  231. package/src/schema/ref.test.ts +0 -349
  232. package/src/schema/ref.ts +0 -103
  233. package/src/schema/refine.test.ts +0 -579
  234. package/src/schema/refine.ts +0 -153
  235. package/src/schema/regexp.test.ts +0 -577
  236. package/src/schema/regexp.ts +0 -82
  237. package/src/schema/string.test.ts +0 -773
  238. package/src/schema/string.ts +0 -229
  239. package/src/schema/subscription.test.ts +0 -499
  240. package/src/schema/subscription.ts +0 -108
  241. package/src/schema/token.test.ts +0 -152
  242. package/src/schema/token.ts +0 -103
  243. package/src/schema/typed-object.test.ts +0 -745
  244. package/src/schema/typed-object.ts +0 -181
  245. package/src/schema/typed-ref.test.ts +0 -796
  246. package/src/schema/typed-ref.ts +0 -126
  247. package/src/schema/typed-union.test.ts +0 -355
  248. package/src/schema/typed-union.ts +0 -130
  249. package/src/schema/union.test.ts +0 -191
  250. package/src/schema/union.ts +0 -89
  251. package/src/schema/unknown.test.ts +0 -313
  252. package/src/schema/unknown.ts +0 -47
  253. package/src/schema/with-default.ts +0 -81
  254. package/src/schema.ts +0 -43
  255. package/src/util/array-agg.test.ts +0 -42
  256. package/src/util/array-agg.ts +0 -44
  257. package/src/util/assertion-util.ts +0 -1
  258. package/src/util/if-any.ts +0 -3
  259. package/src/util/lazy-property.ts +0 -14
  260. package/src/util/memoize.ts +0 -37
  261. package/tsconfig.build.json +0 -12
  262. package/tsconfig.json +0 -7
  263. package/tsconfig.tests.json +0 -8
@@ -1,412 +0,0 @@
1
- import { StandardSchemaV1 } from '@standard-schema/spec'
2
- import { lazyProperty } from '../util/lazy-property.js'
3
- import { StandardSchemaAdapter } from './standard-schema.js'
4
- import {
5
- InferInput,
6
- InferOutput,
7
- ValidationContext,
8
- ValidationOptions,
9
- ValidationResult,
10
- Validator,
11
- } from './validator.js'
12
-
13
- /**
14
- * Options for parsing operations.
15
- * Excludes the `mode` option as it is implicitly set to `"parse"`.
16
- */
17
- export type ParseOptions = Omit<ValidationOptions, 'mode'>
18
-
19
- /**
20
- * Options for validation operations.
21
- * Excludes the `mode` option as it is implicitly set to `"validate"`.
22
- */
23
- export type ValidateOptions = Omit<ValidationOptions, 'mode'>
24
-
25
- /**
26
- * Internal type structure for schema type inference.
27
- *
28
- * This interface defines the phantom types used for compile-time type inference
29
- * without affecting runtime behavior. The `input` and `output` properties
30
- * represent the expected input type during validation and the resulting output
31
- * type after parsing, respectively.
32
- *
33
- * @typeParam TInput - The type accepted as input during validation
34
- * @typeParam TOutput - The type returned after parsing (may differ from input due to coercion)
35
- */
36
- export interface SchemaInternals<out TInput = unknown, out TOutput = TInput> {
37
- input: TInput
38
- output: TOutput
39
- }
40
-
41
- /**
42
- * Abstract base class for all schema validators in the lexicon system.
43
- *
44
- * This class provides the standard validation interface that all schema types
45
- * implement. It offers multiple methods for validating and parsing data:
46
- *
47
- * - **Assertion methods**: `assert()`, `check()` - throw on invalid input
48
- * - **Type guard methods**: `matches()`, `ifMatches()` - return boolean or optional value
49
- * - **Parse methods**: `parse()`, `safeParse()` - allow value transformation/coercion
50
- * - **Validate methods**: `validate()`, `safeValidate()` - validation without coercion
51
- *
52
- * All methods are also available with a `$` prefix (e.g., `$parse()`, `$validate()`)
53
- * for consistent access in generated lexicon namespaces.
54
- *
55
- * @typeParam TInput - The type accepted as valid input during validation
56
- * @typeParam TOutput - The type returned after parsing (may include transformations)
57
- *
58
- * @example
59
- * ```typescript
60
- * class MySchema extends Schema<string> {
61
- * validateInContext(input: unknown, ctx: ValidationContext): ValidationResult {
62
- * if (typeof input !== 'string') {
63
- * return ctx.issueUnexpectedType(input, 'string')
64
- * }
65
- * return ctx.success(input)
66
- * }
67
- * }
68
- *
69
- * const schema = new MySchema()
70
- * schema.assert('hello') // OK
71
- * schema.assert(123) // Throws LexValidationError
72
- * schema.matches('hello') // true
73
- * schema.matches(123) // false
74
- * ```
75
- */
76
- export abstract class Schema<out TInput = unknown, out TOutput = TInput>
77
- implements Validator<TInput, TOutput>, StandardSchemaV1<TInput, TOutput>
78
- {
79
- /**
80
- * Internal phantom property for type inference.
81
- * This property does not exist at runtime.
82
- *
83
- * @internal
84
- */
85
- declare readonly ['__lex']: SchemaInternals<TInput, TOutput>
86
-
87
- get '~standard'(): StandardSchemaV1.Props<TInput, TOutput> {
88
- // Lazily create, and cache, the Standard Schema adapter for this schema
89
- // instance.
90
- return lazyProperty(this, '~standard', new StandardSchemaAdapter(this))
91
- }
92
-
93
- // Needed to discriminate multiple schema types when used in unions. Without
94
- // this, Typescript could allow an EnumSchema<"foo" | "bar"> to be used where
95
- // a StringSchema is expected, since they would both be structurally
96
- // compatible.
97
- abstract readonly type: string
98
-
99
- /**
100
- * Performs validation of the input value within a validation context.
101
- *
102
- * This method must be implemented by subclasses to define the actual
103
- * validation logic. It should not be called directly; use
104
- * {@link ValidationContext.validate} instead to ensure proper mode enforcement.
105
- *
106
- * @param input - The value to validate
107
- * @param ctx - The validation context providing path tracking and issue reporting
108
- * @returns A validation result indicating success with the validated value or failure with issues
109
- *
110
- * @internal
111
- */
112
- abstract validateInContext(
113
- input: unknown,
114
- ctx: ValidationContext,
115
- ): ValidationResult
116
-
117
- /**
118
- * @note use {@link check}() instead of {@link assert}() if you encounter a
119
- * `ts(2775)` error and you are not able to fully type the validator. This
120
- * will typically arise in generic contexts, where the narrowed type is not
121
- * needed.
122
- */
123
- assert(
124
- input: unknown,
125
- options?: ValidateOptions,
126
- ): asserts input is InferInput<this> {
127
- const result = this.safeValidate(input, options)
128
- if (!result.success) throw result.reason
129
- }
130
-
131
- /**
132
- * Alias for {@link assert}(). Most useful in generic contexts where the
133
- * validator is not exactly typed, allowing to avoid "_Assertions require
134
- * every name in the call target to be declared with an explicit type
135
- * annotation. ts(2775)_" errors.
136
- */
137
- check(input: unknown, options?: ValidateOptions): void {
138
- this.assert(input, options)
139
- }
140
-
141
- /**
142
- * Casts the input (by validating it) to the output type if it matches the
143
- * schema, otherwise throws. This is the same as calling {@link parse}() with
144
- * `mode: "validate"`.
145
- */
146
- cast<I>(input: I, options?: ValidateOptions): I & InferInput<this> {
147
- const result = this.safeValidate(input, options)
148
- if (result.success) return result.value
149
- throw result.reason
150
- }
151
-
152
- /**
153
- * Type guard that checks if the input matches this schema.
154
- *
155
- * @example
156
- * ```typescript
157
- * if (schema.matches(data)) {
158
- * // data is narrowed to the schema's input type
159
- * console.log(data)
160
- * }
161
- * ```
162
- */
163
- matches<I>(
164
- input: I,
165
- options?: ValidateOptions,
166
- ): input is I & InferInput<this> {
167
- const result = this.safeValidate(input, options)
168
- return result.success
169
- }
170
-
171
- /**
172
- * Returns the input if it matches this schema, otherwise returns `undefined`.
173
- *
174
- * This is useful for optional filtering operations where you want to
175
- * conditionally extract values that match a schema.
176
- *
177
- * @example
178
- * ```typescript
179
- * const validData = schema.ifMatches(data)
180
- * if (validData !== undefined) {
181
- * // validData is the schema's input type
182
- * console.log(validData)
183
- * }
184
- * ```
185
- */
186
- ifMatches<I>(
187
- input: I,
188
- options?: ValidateOptions,
189
- ): (I & InferInput<this>) | undefined {
190
- return this.matches(input, options) ? input : undefined
191
- }
192
-
193
- /**
194
- * Parses the input, allowing value transformations and coercion.
195
- *
196
- * Unlike {@link validate}, this method allows the schema to transform
197
- * the input value (e.g., applying default values, type coercion).
198
- * Throws a {@link LexValidationError} if the input is invalid.
199
- *
200
- * @param input - The value to parse
201
- * @param options - Optional parsing configuration
202
- * @returns The parsed and potentially transformed value
203
- * @throws {LexValidationError} If the input fails validation
204
- *
205
- * @example
206
- * ```typescript
207
- * const result = schema.parse(rawData)
208
- * // result has defaults applied and is fully typed
209
- * ```
210
- */
211
- parse(input: unknown, options?: ParseOptions): InferOutput<this> {
212
- const result = this.safeParse(input, options)
213
- if (result.success) return result.value
214
- throw result.reason
215
- }
216
-
217
- /**
218
- * Safely parses the input without throwing, returning a result object.
219
- *
220
- * This method allows value transformations like {@link parse}, but
221
- * returns a discriminated union result instead of throwing on error.
222
- *
223
- * @param input - The value to parse
224
- * @param options - Optional parsing configuration
225
- * @returns A {@link ValidationResult} with either the parsed value or validation errors
226
- *
227
- * @example
228
- * ```typescript
229
- * const result = schema.safeParse(data)
230
- * if (result.success) {
231
- * console.log(result.value)
232
- * } else {
233
- * console.error(result.reason.issues)
234
- * }
235
- * ```
236
- */
237
- safeParse(
238
- input: unknown,
239
- options?: ParseOptions,
240
- ): ValidationResult<InferOutput<this>> {
241
- return ValidationContext.validate(input, this, {
242
- ...options,
243
- mode: 'parse',
244
- })
245
- }
246
-
247
- /**
248
- * Validates the input strictly without allowing transformations.
249
- *
250
- * Unlike {@link parse}, this method requires the input to exactly match
251
- * the schema without any transformations (no defaults applied, no coercion).
252
- * Throws a {@link LexValidationError} if the input is invalid or would require transformation.
253
- *
254
- * @typeParam I - The input type (preserved in the return type)
255
- * @param input - The value to validate
256
- * @param options - Optional validation configuration
257
- * @returns The validated input with narrowed type
258
- * @throws {LexValidationError} If the input fails validation or requires transformation
259
- *
260
- * @example
261
- * ```typescript
262
- * const validated = schema.validate(data)
263
- * // validated is typed as the intersection of input type and schema type
264
- * ```
265
- */
266
- validate<I>(input: I, options?: ValidateOptions): I & InferInput<this> {
267
- const result = this.safeValidate(input, options)
268
- if (result.success) return result.value
269
- throw result.reason
270
- }
271
-
272
- /**
273
- * Safely validates the input without throwing, returning a result object.
274
- *
275
- * This method performs strict validation like {@link validate}, but
276
- * returns a discriminated union result instead of throwing on error.
277
- *
278
- * @typeParam I - The input type (preserved in the result value type)
279
- * @param input - The value to validate
280
- * @param options - Optional validation configuration
281
- * @returns A {@link ValidationResult} with either the validated value or validation errors
282
- *
283
- * @example
284
- * ```typescript
285
- * const result = schema.safeValidate(data)
286
- * if (result.success) {
287
- * console.log(result.value)
288
- * } else {
289
- * console.error(result.reason.issues)
290
- * }
291
- * ```
292
- */
293
- safeValidate<I>(
294
- input: I,
295
- options?: ValidateOptions,
296
- ): ValidationResult<I & InferInput<this>> {
297
- return ValidationContext.validate(input, this, {
298
- ...options,
299
- mode: 'validate',
300
- })
301
- }
302
-
303
- // @NOTE Dollar-prefixed aliases
304
- //
305
- // The `lex-builder` lib generates namespaced utility functions that allow
306
- // accessing the schema's methods without the need to specify the ".main."
307
- // part of the namespace. This allows utilities for a particular record type
308
- // to be called like "app.bsky.feed.post.<utility>()" instead of
309
- // "app.bsky.feed.post.main.<utility>()".
310
- //
311
- // Because those utilities could conflict with other schemas (e.g. if there is
312
- // a lexicon definition with the same name as the "<utility>"), those exported
313
- // utilities will be prefixed with "$".
314
- //
315
- // Similarly, since those utilities are defined as simple "const", they are
316
- // also bound (using JS's .bind) to the schema instance, so that they can be
317
- // used without worrying about the context (e.g. "app.bsky.feed.post.$parse()"
318
- // will work regardless of how it is imported or called).
319
- //
320
- // In order to provide the same functionalities for non-main definitions, we
321
- // also define those aliases directly on the schema instance, so that they can
322
- // be used in the same way as the utilities generated by "lex-builder". For
323
- // example, if there is a non-main definition "app.bsky.feed.defs.postView",
324
- // it will also be possible to call "app.bsky.feed.defs.postView.$parse()".
325
- //
326
- // These methods are also "bound" to the instance so that they can be used
327
- // exactly like the utilities generated by "lex-builder", without worrying
328
- // about the context.
329
- //
330
- // There are two ways we could "bind" those methods to the instance:
331
- // 1. Define them as getters that return the bound method (e.g. get $parse() {
332
- // return this.parse.bind(this) })
333
- // 2. Define them as properties that are initialized in the constructor (e.g.
334
- // this.$parse = this.parse.bind(this))
335
- //
336
- // Since a **lot** of those methods would end-up being created in systems that
337
- // contains many schemas (e.g. the appview), we choose the first approach
338
- // (getters) in order to avoid the overhead of creating all those bound
339
- // functions upfront when instantiating the schemas.
340
-
341
- /**
342
- * Bound alias for {@link assert} for compatibility with generated utilities.
343
- * @see {@link assert}
344
- */
345
- get $assert(): typeof this.assert {
346
- return lazyProperty(this, '$assert', this.assert.bind(this))
347
- }
348
-
349
- /**
350
- * Bound alias for {@link check} for compatibility with generated utilities.
351
- * @see {@link check}
352
- */
353
- get $check(): typeof this.check {
354
- return lazyProperty(this, '$check', this.check.bind(this))
355
- }
356
-
357
- /**
358
- * Bound alias for {@link cast} for compatibility with generated utilities.
359
- * @see {@link cast}
360
- */
361
- get $cast(): typeof this.cast {
362
- return lazyProperty(this, '$cast', this.cast.bind(this))
363
- }
364
-
365
- /**
366
- * Bound alias for {@link matches} for compatibility with generated utilities.
367
- * @see {@link matches}
368
- */
369
- get $matches(): typeof this.matches {
370
- return lazyProperty(this, '$matches', this.matches.bind(this))
371
- }
372
-
373
- /**
374
- * Bound alias for {@link ifMatches} for compatibility with generated utilities.
375
- * @see {@link ifMatches}
376
- */
377
- get $ifMatches(): typeof this.ifMatches {
378
- return lazyProperty(this, '$ifMatches', this.ifMatches.bind(this))
379
- }
380
-
381
- /**
382
- * Bound alias for {@link parse} for compatibility with generated utilities.
383
- * @see {@link parse}
384
- */
385
- get $parse(): typeof this.parse {
386
- return lazyProperty(this, '$parse', this.parse.bind(this))
387
- }
388
-
389
- /**
390
- * Bound alias for {@link safeParse} for compatibility with generated utilities.
391
- * @see {@link safeParse}
392
- */
393
- get $safeParse(): typeof this.safeParse {
394
- return lazyProperty(this, '$safeParse', this.safeParse.bind(this))
395
- }
396
-
397
- /**
398
- * Bound alias for {@link validate} for compatibility with generated utilities.
399
- * @see {@link validate}
400
- */
401
- get $validate(): typeof this.validate {
402
- return lazyProperty(this, '$validate', this.validate.bind(this))
403
- }
404
-
405
- /**
406
- * Bound alias for {@link safeValidate} for compatibility with generated utilities.
407
- * @see {@link safeValidate}
408
- */
409
- get $safeValidate(): typeof this.safeValidate {
410
- return lazyProperty(this, '$safeValidate', this.safeValidate.bind(this))
411
- }
412
- }
@@ -1,124 +0,0 @@
1
- import { assert, describe, expect, it } from 'vitest'
2
- import { array } from '../schema/array.js'
3
- import { integer } from '../schema/integer.js'
4
- import { object } from '../schema/object.js'
5
- import { optional } from '../schema/optional.js'
6
- import { string } from '../schema/string.js'
7
- import { withDefault } from '../schema/with-default.js'
8
- import { LexValidationError } from './validation-error.js'
9
-
10
- describe('StandardSchemaAdapter', () => {
11
- describe('metadata', () => {
12
- const schema = integer()
13
-
14
- it('has version 1', () => {
15
- expect(schema['~standard'].version).toBe(1)
16
- })
17
-
18
- it('has vendor @atproto/lex-schema', () => {
19
- expect(schema['~standard'].vendor).toBe('@atproto/lex-schema')
20
- })
21
- })
22
-
23
- describe('lazy caching', () => {
24
- it('returns the same adapter instance on repeated accesses', () => {
25
- const schema = integer()
26
- const first = schema['~standard']
27
- const second = schema['~standard']
28
- expect(first).toBe(second)
29
- })
30
- })
31
-
32
- describe('validate() result shape on success', () => {
33
- it('returns a value property for a valid integer', () => {
34
- const result = integer()['~standard'].validate(42)
35
- expect(result).toMatchObject({ value: 42 })
36
- })
37
-
38
- it('returns a value property for a valid string', () => {
39
- const result = string()['~standard'].validate('hello')
40
- expect(result).toMatchObject({ value: 'hello' })
41
- })
42
-
43
- it('does not include an issues property on success', () => {
44
- const result = integer()['~standard'].validate(1)
45
- expect(result).not.toHaveProperty('issues')
46
- })
47
- })
48
-
49
- describe('validate() result shape on failure', () => {
50
- it('returns a LexValidationError with issues for an invalid value', () => {
51
- const result = integer()['~standard'].validate('not-a-number')
52
- assert(result instanceof LexValidationError)
53
- expect(Array.isArray(result.issues)).toBe(true)
54
- expect(result.issues.length).toBeGreaterThan(0)
55
- })
56
-
57
- it('does not include a value property on failure', () => {
58
- const result = integer()['~standard'].validate('not-a-number')
59
- expect(result).not.toHaveProperty('value')
60
- })
61
-
62
- describe('issues[].message', () => {
63
- it('is a non-empty string', () => {
64
- const result = integer()['~standard'].validate('not-a-number')
65
- assert(result instanceof LexValidationError)
66
- for (const issue of result.issues) {
67
- expect(typeof issue.message).toBe('string')
68
- expect(issue.message.length).toBeGreaterThan(0)
69
- }
70
- })
71
-
72
- it('describes the type mismatch', () => {
73
- const result = integer()['~standard'].validate('not-a-number')
74
- assert(result instanceof LexValidationError)
75
- expect(result.issues[0].message).toContain('integer')
76
- })
77
- })
78
-
79
- describe('issues[].path', () => {
80
- it('is an empty array for a root-level failure', () => {
81
- const result = integer()['~standard'].validate('not-a-number')
82
- assert(result instanceof LexValidationError)
83
- expect(result.issues[0].path).toEqual([])
84
- })
85
-
86
- it('reflects the property key for a nested object failure', () => {
87
- const schema = object({ age: integer() })
88
- const result = schema['~standard'].validate({ age: 'not-a-number' })
89
- assert(result instanceof LexValidationError)
90
- expect(result.issues[0].path).toContain('age')
91
- })
92
-
93
- it('reflects the index for an array element failure', () => {
94
- const schema = array(integer())
95
- const result = schema['~standard'].validate([1, 'bad', 3])
96
- assert(result instanceof LexValidationError)
97
- expect(result.issues[0].path).toContain(1)
98
- })
99
- })
100
- })
101
-
102
- describe('parse mode (default value application)', () => {
103
- it('applies default values when input is undefined', () => {
104
- const schema = withDefault(integer(), 10)
105
- const result = schema['~standard'].validate(undefined)
106
- expect(result).toMatchObject({ value: 10 })
107
- })
108
-
109
- it('uses the provided value instead of default when input is present', () => {
110
- const schema = withDefault(integer(), 10)
111
- const result = schema['~standard'].validate(42)
112
- expect(result).toMatchObject({ value: 42 })
113
- })
114
-
115
- it('applies defaults for optional object properties in parse mode', () => {
116
- const schema = object({
117
- name: string(),
118
- count: optional(withDefault(integer(), 0)),
119
- })
120
- const result = schema['~standard'].validate({ name: 'Alice' })
121
- expect(result).toMatchObject({ value: { name: 'Alice', count: 0 } })
122
- })
123
- })
124
- })
@@ -1,31 +0,0 @@
1
- import { StandardSchemaV1 } from '@standard-schema/spec'
2
- import { ValidationContext, Validator } from './validator.js'
3
-
4
- /**
5
- * The Standard Schema adapter for {@link Validator} instances.
6
- */
7
- export class StandardSchemaAdapter<TInput, TOutput>
8
- implements StandardSchemaV1.Props<TInput, TOutput>
9
- {
10
- readonly version = 1
11
-
12
- readonly vendor = '@atproto/lex-schema'
13
-
14
- declare readonly types: StandardSchemaV1.Types<TInput, TOutput>
15
-
16
- constructor(private readonly validator: Validator<TInput, TOutput>) {}
17
-
18
- validate(
19
- value: unknown,
20
- options?: StandardSchemaV1.Options,
21
- ): StandardSchemaV1.Result<TOutput> {
22
- // Perform validation in "parse" mode to ensure transformations (defaults,
23
- // coercions, etc.) are applied. Also ensures that the output type is
24
- // returned. Note that ValidationResult is compatible with
25
- // StandardSchemaV1.Result :-)
26
- return ValidationContext.validate(value, this.validator, {
27
- ...options?.libraryOptions,
28
- mode: 'parse',
29
- })
30
- }
31
- }