@atproto/lex-schema 0.0.9 → 0.0.10

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 (279) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/LICENSE.txt +1 -1
  3. package/dist/core/$type.d.ts +11 -0
  4. package/dist/core/$type.d.ts.map +1 -1
  5. package/dist/core/$type.js +4 -0
  6. package/dist/core/$type.js.map +1 -1
  7. package/dist/core/schema.d.ts +31 -24
  8. package/dist/core/schema.d.ts.map +1 -1
  9. package/dist/core/schema.js +38 -8
  10. package/dist/core/schema.js.map +1 -1
  11. package/dist/core/string-format.d.ts +35 -35
  12. package/dist/core/string-format.d.ts.map +1 -1
  13. package/dist/core/string-format.js +49 -91
  14. package/dist/core/string-format.js.map +1 -1
  15. package/dist/core/validation-issue.js +1 -1
  16. package/dist/core/validation-issue.js.map +1 -1
  17. package/dist/core/validator.d.ts +53 -32
  18. package/dist/core/validator.d.ts.map +1 -1
  19. package/dist/core/validator.js +18 -22
  20. package/dist/core/validator.js.map +1 -1
  21. package/dist/external.d.ts +0 -85
  22. package/dist/external.d.ts.map +1 -1
  23. package/dist/external.js +0 -164
  24. package/dist/external.js.map +1 -1
  25. package/dist/helpers.d.ts +10 -5
  26. package/dist/helpers.d.ts.map +1 -1
  27. package/dist/helpers.js +3 -3
  28. package/dist/helpers.js.map +1 -1
  29. package/dist/schema/array.d.ts +9 -5
  30. package/dist/schema/array.d.ts.map +1 -1
  31. package/dist/schema/array.js +14 -5
  32. package/dist/schema/array.js.map +1 -1
  33. package/dist/schema/blob.d.ts +9 -7
  34. package/dist/schema/blob.d.ts.map +1 -1
  35. package/dist/schema/blob.js +9 -5
  36. package/dist/schema/blob.js.map +1 -1
  37. package/dist/schema/boolean.d.ts +3 -7
  38. package/dist/schema/boolean.d.ts.map +1 -1
  39. package/dist/schema/boolean.js +6 -7
  40. package/dist/schema/boolean.js.map +1 -1
  41. package/dist/schema/bytes.d.ts +3 -2
  42. package/dist/schema/bytes.d.ts.map +1 -1
  43. package/dist/schema/bytes.js +7 -3
  44. package/dist/schema/bytes.js.map +1 -1
  45. package/dist/schema/cid.d.ts +7 -7
  46. package/dist/schema/cid.d.ts.map +1 -1
  47. package/dist/schema/cid.js +5 -1
  48. package/dist/schema/cid.js.map +1 -1
  49. package/dist/schema/custom.d.ts +6 -5
  50. package/dist/schema/custom.d.ts.map +1 -1
  51. package/dist/schema/custom.js +10 -4
  52. package/dist/schema/custom.js.map +1 -1
  53. package/dist/schema/dict.d.ts +8 -8
  54. package/dist/schema/dict.d.ts.map +1 -1
  55. package/dist/schema/dict.js +11 -2
  56. package/dist/schema/dict.js.map +1 -1
  57. package/dist/schema/discriminated-union.d.ts +21 -14
  58. package/dist/schema/discriminated-union.d.ts.map +1 -1
  59. package/dist/schema/discriminated-union.js +7 -0
  60. package/dist/schema/discriminated-union.js.map +1 -1
  61. package/dist/schema/enum.d.ts +7 -9
  62. package/dist/schema/enum.d.ts.map +1 -1
  63. package/dist/schema/enum.js +8 -4
  64. package/dist/schema/enum.js.map +1 -1
  65. package/dist/schema/integer.d.ts +5 -5
  66. package/dist/schema/integer.d.ts.map +1 -1
  67. package/dist/schema/integer.js +9 -5
  68. package/dist/schema/integer.js.map +1 -1
  69. package/dist/schema/intersection.d.ts +4 -4
  70. package/dist/schema/intersection.d.ts.map +1 -1
  71. package/dist/schema/intersection.js +5 -0
  72. package/dist/schema/intersection.js.map +1 -1
  73. package/dist/schema/literal.d.ts +6 -9
  74. package/dist/schema/literal.d.ts.map +1 -1
  75. package/dist/schema/literal.js +7 -4
  76. package/dist/schema/literal.js.map +1 -1
  77. package/dist/schema/never.d.ts +3 -2
  78. package/dist/schema/never.d.ts.map +1 -1
  79. package/dist/schema/never.js +5 -1
  80. package/dist/schema/never.js.map +1 -1
  81. package/dist/schema/null.d.ts +4 -3
  82. package/dist/schema/null.d.ts.map +1 -1
  83. package/dist/schema/null.js +6 -4
  84. package/dist/schema/null.js.map +1 -1
  85. package/dist/schema/nullable.d.ts +6 -5
  86. package/dist/schema/nullable.d.ts.map +1 -1
  87. package/dist/schema/nullable.js +9 -5
  88. package/dist/schema/nullable.js.map +1 -1
  89. package/dist/schema/object.d.ts +10 -8
  90. package/dist/schema/object.d.ts.map +1 -1
  91. package/dist/schema/object.js +11 -3
  92. package/dist/schema/object.js.map +1 -1
  93. package/dist/schema/optional.d.ts +7 -5
  94. package/dist/schema/optional.d.ts.map +1 -1
  95. package/dist/schema/optional.js +14 -6
  96. package/dist/schema/optional.js.map +1 -1
  97. package/dist/schema/params.d.ts +24 -13
  98. package/dist/schema/params.d.ts.map +1 -1
  99. package/dist/schema/params.js +47 -25
  100. package/dist/schema/params.js.map +1 -1
  101. package/dist/schema/payload.d.ts +12 -9
  102. package/dist/schema/payload.d.ts.map +1 -1
  103. package/dist/schema/payload.js +11 -0
  104. package/dist/schema/payload.js.map +1 -1
  105. package/dist/schema/permission-set.d.ts +1 -0
  106. package/dist/schema/permission-set.d.ts.map +1 -1
  107. package/dist/schema/permission-set.js +5 -0
  108. package/dist/schema/permission-set.js.map +1 -1
  109. package/dist/schema/permission.d.ts +6 -5
  110. package/dist/schema/permission.d.ts.map +1 -1
  111. package/dist/schema/permission.js +5 -0
  112. package/dist/schema/permission.js.map +1 -1
  113. package/dist/schema/procedure.d.ts +2 -1
  114. package/dist/schema/procedure.d.ts.map +1 -1
  115. package/dist/schema/procedure.js +5 -0
  116. package/dist/schema/procedure.js.map +1 -1
  117. package/dist/schema/query.d.ts +2 -1
  118. package/dist/schema/query.d.ts.map +1 -1
  119. package/dist/schema/query.js +5 -0
  120. package/dist/schema/query.js.map +1 -1
  121. package/dist/schema/record.d.ts +48 -30
  122. package/dist/schema/record.d.ts.map +1 -1
  123. package/dist/schema/record.js +12 -9
  124. package/dist/schema/record.js.map +1 -1
  125. package/dist/schema/ref.d.ts +9 -6
  126. package/dist/schema/ref.d.ts.map +1 -1
  127. package/dist/schema/ref.js +9 -16
  128. package/dist/schema/ref.js.map +1 -1
  129. package/dist/schema/refine.d.ts +4 -4
  130. package/dist/schema/refine.d.ts.map +1 -1
  131. package/dist/schema/refine.js.map +1 -1
  132. package/dist/schema/regexp.d.ts +4 -3
  133. package/dist/schema/regexp.d.ts.map +1 -1
  134. package/dist/schema/regexp.js +5 -0
  135. package/dist/schema/regexp.js.map +1 -1
  136. package/dist/schema/string.d.ts +7 -8
  137. package/dist/schema/string.d.ts.map +1 -1
  138. package/dist/schema/string.js +13 -19
  139. package/dist/schema/string.js.map +1 -1
  140. package/dist/schema/subscription.d.ts +2 -1
  141. package/dist/schema/subscription.d.ts.map +1 -1
  142. package/dist/schema/subscription.js +5 -0
  143. package/dist/schema/subscription.js.map +1 -1
  144. package/dist/schema/token.d.ts +6 -5
  145. package/dist/schema/token.d.ts.map +1 -1
  146. package/dist/schema/token.js +5 -0
  147. package/dist/schema/token.js.map +1 -1
  148. package/dist/schema/typed-object.d.ts +43 -26
  149. package/dist/schema/typed-object.d.ts.map +1 -1
  150. package/dist/schema/typed-object.js +6 -3
  151. package/dist/schema/typed-object.js.map +1 -1
  152. package/dist/schema/typed-ref.d.ts +16 -25
  153. package/dist/schema/typed-ref.d.ts.map +1 -1
  154. package/dist/schema/typed-ref.js +7 -17
  155. package/dist/schema/typed-ref.js.map +1 -1
  156. package/dist/schema/typed-union.d.ts +9 -21
  157. package/dist/schema/typed-union.d.ts.map +1 -1
  158. package/dist/schema/typed-union.js +15 -11
  159. package/dist/schema/typed-union.js.map +1 -1
  160. package/dist/schema/union.d.ts +6 -6
  161. package/dist/schema/union.d.ts.map +1 -1
  162. package/dist/schema/union.js +7 -5
  163. package/dist/schema/union.js.map +1 -1
  164. package/dist/schema/unknown-object.d.ts +5 -4
  165. package/dist/schema/unknown-object.d.ts.map +1 -1
  166. package/dist/schema/unknown-object.js +5 -1
  167. package/dist/schema/unknown-object.js.map +1 -1
  168. package/dist/schema/unknown.d.ts +3 -2
  169. package/dist/schema/unknown.d.ts.map +1 -1
  170. package/dist/schema/unknown.js +5 -1
  171. package/dist/schema/unknown.js.map +1 -1
  172. package/dist/schema/with-default.d.ts +9 -0
  173. package/dist/schema/with-default.d.ts.map +1 -0
  174. package/dist/schema/with-default.js +27 -0
  175. package/dist/schema/with-default.js.map +1 -0
  176. package/dist/schema.d.ts +2 -2
  177. package/dist/schema.d.ts.map +1 -1
  178. package/dist/schema.js +2 -4
  179. package/dist/schema.js.map +1 -1
  180. package/dist/util/assertion-util.d.ts +0 -6
  181. package/dist/util/assertion-util.d.ts.map +1 -1
  182. package/dist/util/assertion-util.js +0 -28
  183. package/dist/util/assertion-util.js.map +1 -1
  184. package/dist/util/memoize.d.ts +2 -2
  185. package/dist/util/memoize.d.ts.map +1 -1
  186. package/dist/util/memoize.js +23 -39
  187. package/dist/util/memoize.js.map +1 -1
  188. package/package.json +3 -3
  189. package/src/core/$type.test.ts +20 -0
  190. package/src/core/$type.ts +30 -0
  191. package/src/core/schema.ts +86 -38
  192. package/src/core/string-format.ts +119 -158
  193. package/src/core/validation-issue.ts +1 -1
  194. package/src/core/validator.ts +93 -53
  195. package/src/external.ts +0 -404
  196. package/src/helpers.test.ts +22 -21
  197. package/src/helpers.ts +14 -14
  198. package/src/schema/array.test.ts +38 -40
  199. package/src/schema/array.ts +35 -13
  200. package/src/schema/blob.test.ts +21 -21
  201. package/src/schema/blob.ts +19 -17
  202. package/src/schema/boolean.test.ts +9 -8
  203. package/src/schema/boolean.ts +7 -13
  204. package/src/schema/bytes.test.ts +13 -13
  205. package/src/schema/bytes.ts +13 -8
  206. package/src/schema/cid.test.ts +3 -3
  207. package/src/schema/cid.ts +13 -12
  208. package/src/schema/custom.test.ts +26 -26
  209. package/src/schema/custom.ts +20 -13
  210. package/src/schema/dict.test.ts +21 -39
  211. package/src/schema/dict.ts +28 -19
  212. package/src/schema/discriminated-union.test.ts +128 -128
  213. package/src/schema/discriminated-union.ts +45 -26
  214. package/src/schema/enum.test.ts +17 -16
  215. package/src/schema/enum.ts +16 -16
  216. package/src/schema/integer.test.ts +22 -21
  217. package/src/schema/integer.ts +12 -9
  218. package/src/schema/intersection.test.ts +10 -10
  219. package/src/schema/intersection.ts +17 -14
  220. package/src/schema/literal.test.ts +35 -34
  221. package/src/schema/literal.ts +12 -15
  222. package/src/schema/never.test.ts +5 -5
  223. package/src/schema/never.ts +7 -2
  224. package/src/schema/null.test.ts +3 -3
  225. package/src/schema/null.ts +9 -9
  226. package/src/schema/nullable.test.ts +31 -42
  227. package/src/schema/nullable.ts +17 -9
  228. package/src/schema/object.test.ts +10 -12
  229. package/src/schema/object.ts +27 -18
  230. package/src/schema/optional.test.ts +21 -28
  231. package/src/schema/optional.ts +27 -10
  232. package/src/schema/params.test.ts +471 -47
  233. package/src/schema/params.ts +72 -38
  234. package/src/schema/payload.test.ts +150 -156
  235. package/src/schema/payload.ts +35 -19
  236. package/src/schema/permission-set.test.ts +206 -273
  237. package/src/schema/permission-set.ts +8 -0
  238. package/src/schema/permission.test.ts +177 -177
  239. package/src/schema/permission.ts +13 -5
  240. package/src/schema/procedure.test.ts +183 -242
  241. package/src/schema/procedure.ts +18 -5
  242. package/src/schema/query.test.ts +186 -200
  243. package/src/schema/query.ts +16 -4
  244. package/src/schema/record.test.ts +121 -101
  245. package/src/schema/record.ts +74 -40
  246. package/src/schema/ref.test.ts +101 -118
  247. package/src/schema/ref.ts +33 -28
  248. package/src/schema/refine.test.ts +28 -28
  249. package/src/schema/refine.ts +23 -20
  250. package/src/schema/regexp.test.ts +29 -33
  251. package/src/schema/regexp.ts +11 -7
  252. package/src/schema/string.test.ts +35 -35
  253. package/src/schema/string.ts +24 -33
  254. package/src/schema/subscription.test.ts +259 -387
  255. package/src/schema/subscription.ts +16 -4
  256. package/src/schema/token.test.ts +47 -324
  257. package/src/schema/token.ts +14 -7
  258. package/src/schema/typed-object.test.ts +98 -81
  259. package/src/schema/typed-object.ts +68 -33
  260. package/src/schema/typed-ref.test.ts +206 -234
  261. package/src/schema/typed-ref.ts +40 -42
  262. package/src/schema/typed-union.test.ts +40 -64
  263. package/src/schema/typed-union.ts +36 -58
  264. package/src/schema/union.test.ts +17 -27
  265. package/src/schema/union.ts +20 -16
  266. package/src/schema/unknown-object.test.ts +8 -8
  267. package/src/schema/unknown-object.ts +9 -7
  268. package/src/schema/unknown.test.ts +4 -4
  269. package/src/schema/unknown.ts +7 -5
  270. package/src/schema/with-default.ts +35 -0
  271. package/src/schema.ts +2 -6
  272. package/src/util/assertion-util.ts +0 -39
  273. package/src/util/memoize.ts +26 -46
  274. package/dist/schema/_parameters.d.ts +0 -17
  275. package/dist/schema/_parameters.d.ts.map +0 -1
  276. package/dist/schema/_parameters.js +0 -20
  277. package/dist/schema/_parameters.js.map +0 -1
  278. package/src/schema/_parameters.test.ts +0 -417
  279. package/src/schema/_parameters.ts +0 -26
@@ -1,27 +1,27 @@
1
- import { Schema, ValidationResult, ValidatorContext } from '../core.js'
2
-
3
- export type EnumSchemaOptions<T extends null | string | number | boolean> = {
4
- default?: T
5
- }
1
+ import { Schema, ValidationContext } from '../core.js'
6
2
 
7
3
  export class EnumSchema<
8
- Output extends null | string | number | boolean = any,
9
- > extends Schema<Output> {
10
- constructor(
11
- readonly values: readonly Output[],
12
- readonly options?: EnumSchemaOptions<Output>,
13
- ) {
4
+ const TValue extends null | string | number | boolean,
5
+ > extends Schema<TValue> {
6
+ constructor(readonly values: readonly TValue[]) {
14
7
  super()
15
8
  }
16
9
 
17
- validateInContext(
18
- input: unknown = this.options?.default,
19
- ctx: ValidatorContext,
20
- ): ValidationResult<Output> {
10
+ validateInContext(input: unknown, ctx: ValidationContext) {
21
11
  if (!(this.values as readonly unknown[]).includes(input)) {
22
12
  return ctx.issueInvalidValue(input, this.values)
23
13
  }
24
14
 
25
- return ctx.success(input as Output)
15
+ return ctx.success(input as TValue)
26
16
  }
27
17
  }
18
+
19
+ /*@__NO_SIDE_EFFECTS__*/
20
+ export function enumSchema<const V extends null | string | number | boolean>(
21
+ value: readonly V[],
22
+ ) {
23
+ return new EnumSchema<V>(value)
24
+ }
25
+
26
+ // @NOTE "enum" is a reserved keyword in JS/TS
27
+ export { enumSchema as enum }
@@ -1,9 +1,10 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { IntegerSchema } from './integer.js'
2
+ import { integer } from './integer.js'
3
+ import { withDefault } from './with-default.js'
3
4
 
4
5
  describe('IntegerSchema', () => {
5
6
  describe('basic validation', () => {
6
- const schema = new IntegerSchema({})
7
+ const schema = integer()
7
8
 
8
9
  it('validates integers', () => {
9
10
  const result = schema.safeParse(42)
@@ -82,22 +83,22 @@ describe('IntegerSchema', () => {
82
83
  })
83
84
 
84
85
  describe('default value', () => {
85
- const schema = new IntegerSchema({ default: 10 })
86
+ const schema = withDefault(integer(), 10)
86
87
 
87
88
  it('uses default when undefined is provided', () => {
88
89
  const result = schema.safeParse(undefined)
89
- expect(result.success).toBe(true)
90
- if (result.success) {
91
- expect(result.value).toBe(10)
92
- }
90
+ expect(result).toMatchObject({
91
+ success: true,
92
+ value: 10,
93
+ })
93
94
  })
94
95
 
95
96
  it('does not use default when explicit value is provided', () => {
96
97
  const result = schema.safeParse(20)
97
- expect(result.success).toBe(true)
98
- if (result.success) {
99
- expect(result.value).toBe(20)
100
- }
98
+ expect(result).toMatchObject({
99
+ success: true,
100
+ value: 20,
101
+ })
101
102
  })
102
103
 
103
104
  it('does not use default when zero is provided', () => {
@@ -110,7 +111,7 @@ describe('IntegerSchema', () => {
110
111
  })
111
112
 
112
113
  describe('minimum constraint', () => {
113
- const schema = new IntegerSchema({ minimum: 10 })
114
+ const schema = integer({ minimum: 10 })
114
115
 
115
116
  it('accepts values equal to minimum', () => {
116
117
  const result = schema.safeParse(10)
@@ -139,7 +140,7 @@ describe('IntegerSchema', () => {
139
140
  })
140
141
 
141
142
  describe('maximum constraint', () => {
142
- const schema = new IntegerSchema({ maximum: 100 })
143
+ const schema = integer({ maximum: 100 })
143
144
 
144
145
  it('accepts values equal to maximum', () => {
145
146
  const result = schema.safeParse(100)
@@ -168,7 +169,7 @@ describe('IntegerSchema', () => {
168
169
  })
169
170
 
170
171
  describe('minimum and maximum constraints', () => {
171
- const schema = new IntegerSchema({ minimum: 10, maximum: 100 })
172
+ const schema = integer({ minimum: 10, maximum: 100 })
172
173
 
173
174
  it('accepts values within range', () => {
174
175
  const result = schema.safeParse(50)
@@ -197,7 +198,7 @@ describe('IntegerSchema', () => {
197
198
  })
198
199
 
199
200
  describe('negative range constraints', () => {
200
- const schema = new IntegerSchema({ minimum: -100, maximum: -10 })
201
+ const schema = integer({ minimum: -100, maximum: -10 })
201
202
 
202
203
  it('accepts negative values within range', () => {
203
204
  const result = schema.safeParse(-50)
@@ -236,7 +237,7 @@ describe('IntegerSchema', () => {
236
237
  })
237
238
 
238
239
  describe('zero constraints', () => {
239
- const schema = new IntegerSchema({ minimum: 0, maximum: 0 })
240
+ const schema = integer({ minimum: 0, maximum: 0 })
240
241
 
241
242
  it('accepts zero', () => {
242
243
  const result = schema.safeParse(0)
@@ -255,7 +256,7 @@ describe('IntegerSchema', () => {
255
256
  })
256
257
 
257
258
  describe('combined with default value', () => {
258
- const schema = new IntegerSchema({ default: 50, minimum: 10, maximum: 100 })
259
+ const schema = withDefault(integer({ minimum: 10, maximum: 100 }), 50)
259
260
 
260
261
  it('uses default when undefined is provided', () => {
261
262
  const result = schema.safeParse(undefined)
@@ -278,21 +279,21 @@ describe('IntegerSchema', () => {
278
279
 
279
280
  describe('edge cases', () => {
280
281
  it('handles minimum of 0', () => {
281
- const schema = new IntegerSchema({ minimum: 0 })
282
+ const schema = integer({ minimum: 0 })
282
283
  expect(schema.safeParse(0).success).toBe(true)
283
284
  expect(schema.safeParse(-1).success).toBe(false)
284
285
  expect(schema.safeParse(1).success).toBe(true)
285
286
  })
286
287
 
287
288
  it('handles maximum of 0', () => {
288
- const schema = new IntegerSchema({ maximum: 0 })
289
+ const schema = integer({ maximum: 0 })
289
290
  expect(schema.safeParse(0).success).toBe(true)
290
291
  expect(schema.safeParse(1).success).toBe(false)
291
292
  expect(schema.safeParse(-1).success).toBe(true)
292
293
  })
293
294
 
294
295
  it('handles very large ranges', () => {
295
- const schema = new IntegerSchema({
296
+ const schema = integer({
296
297
  minimum: Number.MIN_SAFE_INTEGER,
297
298
  maximum: Number.MAX_SAFE_INTEGER,
298
299
  })
@@ -302,7 +303,7 @@ describe('IntegerSchema', () => {
302
303
  })
303
304
 
304
305
  it('allows unconstrained schema', () => {
305
- const schema = new IntegerSchema({})
306
+ const schema = integer()
306
307
  expect(schema.safeParse(Number.MIN_SAFE_INTEGER).success).toBe(true)
307
308
  expect(schema.safeParse(Number.MAX_SAFE_INTEGER).success).toBe(true)
308
309
  expect(schema.safeParse(0).success).toBe(true)
@@ -1,29 +1,26 @@
1
- import { Schema, ValidationResult, ValidatorContext } from '../core.js'
1
+ import { Schema, ValidationContext } from '../core.js'
2
+ import { memoizedOptions } from '../util/memoize.js'
2
3
 
3
4
  export type IntegerSchemaOptions = {
4
- default?: number
5
5
  minimum?: number
6
6
  maximum?: number
7
7
  }
8
8
 
9
9
  export class IntegerSchema extends Schema<number> {
10
- constructor(readonly options: IntegerSchemaOptions = {}) {
10
+ constructor(readonly options?: IntegerSchemaOptions) {
11
11
  super()
12
12
  }
13
13
 
14
- validateInContext(
15
- input: unknown = this.options?.default,
16
- ctx: ValidatorContext,
17
- ): ValidationResult<number> {
14
+ validateInContext(input: unknown, ctx: ValidationContext) {
18
15
  if (!isInteger(input)) {
19
16
  return ctx.issueInvalidType(input, 'integer')
20
17
  }
21
18
 
22
- if (this.options.minimum !== undefined && input < this.options.minimum) {
19
+ if (this.options?.minimum != null && input < this.options.minimum) {
23
20
  return ctx.issueTooSmall(input, 'integer', this.options.minimum, input)
24
21
  }
25
22
 
26
- if (this.options.maximum !== undefined && input > this.options.maximum) {
23
+ if (this.options?.maximum != null && input > this.options.maximum) {
27
24
  return ctx.issueTooBig(input, 'integer', this.options.maximum, input)
28
25
  }
29
26
 
@@ -37,3 +34,9 @@ export class IntegerSchema extends Schema<number> {
37
34
  function isInteger(input: unknown): input is number {
38
35
  return Number.isSafeInteger(input)
39
36
  }
37
+
38
+ export const integer = /*#__PURE__*/ memoizedOptions(function (
39
+ options?: IntegerSchemaOptions,
40
+ ) {
41
+ return new IntegerSchema(options)
42
+ })
@@ -1,17 +1,17 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { BooleanSchema } from './boolean.js'
3
- import { DictSchema } from './dict.js'
4
- import { EnumSchema } from './enum.js'
5
- import { IntersectionSchema } from './intersection.js'
6
- import { ObjectSchema } from './object.js'
7
- import { StringSchema } from './string.js'
2
+ import { boolean } from './boolean.js'
3
+ import { dict } from './dict.js'
4
+ import { enumSchema } from './enum.js'
5
+ import { intersection } from './intersection.js'
6
+ import { object } from './object.js'
7
+ import { string } from './string.js'
8
8
 
9
9
  describe('IntersectionSchema', () => {
10
- const schema = new IntersectionSchema(
11
- new ObjectSchema({
12
- title: new StringSchema({}),
10
+ const schema = intersection(
11
+ object({
12
+ title: string(),
13
13
  }),
14
- new DictSchema(new EnumSchema(['tag1', 'tag2']), new BooleanSchema({})),
14
+ dict(enumSchema(['tag1', 'tag2']), boolean()),
15
15
  )
16
16
 
17
17
  it('validates extra properties with the provided validator', () => {
@@ -1,9 +1,9 @@
1
1
  import {
2
- Infer,
2
+ InferInput,
3
+ InferOutput,
3
4
  Schema,
4
5
  Simplify,
5
- ValidationResult,
6
- ValidatorContext,
6
+ ValidationContext,
7
7
  } from '../core.js'
8
8
  import { DictSchema } from './dict.js'
9
9
  import { ObjectSchema } from './object.js'
@@ -24,15 +24,13 @@ export type Intersect<A, B> = B[keyof B] extends never
24
24
  // index signature could return a value from either A or B
25
25
  A & { [K in keyof B]: B[K] | A[keyof A & K] }
26
26
 
27
- export type IntersectionSchemaOutput<
28
- Left extends ObjectSchema,
29
- Right extends DictSchema,
30
- > = Simplify<Intersect<Infer<Left>, Infer<Right>>>
31
-
32
27
  export class IntersectionSchema<
33
28
  const Left extends ObjectSchema = any,
34
29
  const Right extends DictSchema = any,
35
- > extends Schema<IntersectionSchemaOutput<Left, Right>> {
30
+ > extends Schema<
31
+ Simplify<Intersect<InferInput<Left>, InferInput<Right>>>,
32
+ Simplify<Intersect<InferOutput<Left>, InferOutput<Right>>>
33
+ > {
36
34
  constructor(
37
35
  protected readonly left: Left,
38
36
  protected readonly right: Right,
@@ -40,15 +38,20 @@ export class IntersectionSchema<
40
38
  super()
41
39
  }
42
40
 
43
- validateInContext(
44
- input: unknown,
45
- ctx: ValidatorContext,
46
- ): ValidationResult<IntersectionSchemaOutput<Left, Right>> {
41
+ validateInContext(input: unknown, ctx: ValidationContext) {
47
42
  const leftResult = ctx.validate(input, this.left)
48
43
  if (!leftResult.success) return leftResult
49
44
 
50
45
  return this.right.validateInContext(leftResult.value, ctx, {
51
46
  ignoredKeys: this.left.validatorsMap,
52
- }) as ValidationResult<IntersectionSchemaOutput<Left, Right>>
47
+ })
53
48
  }
54
49
  }
50
+
51
+ /*@__NO_SIDE_EFFECTS__*/
52
+ export function intersection<
53
+ const Left extends ObjectSchema,
54
+ const Right extends DictSchema,
55
+ >(left: Left, right: Right) {
56
+ return new IntersectionSchema<Left, Right>(left, right)
57
+ }
@@ -1,9 +1,10 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { LiteralSchema } from './literal.js'
2
+ import { literal } from './literal.js'
3
+ import { withDefault } from './with-default.js'
3
4
 
4
5
  describe('LiteralSchema', () => {
5
6
  describe('string literals', () => {
6
- const schema = new LiteralSchema('hello')
7
+ const schema = literal('hello')
7
8
 
8
9
  it('validates exact string match', () => {
9
10
  const result = schema.safeParse('hello')
@@ -60,7 +61,7 @@ describe('LiteralSchema', () => {
60
61
  })
61
62
 
62
63
  describe('empty string literal', () => {
63
- const schema = new LiteralSchema('')
64
+ const schema = literal('')
64
65
 
65
66
  it('validates empty string', () => {
66
67
  const result = schema.safeParse('')
@@ -87,7 +88,7 @@ describe('LiteralSchema', () => {
87
88
  })
88
89
 
89
90
  describe('number literals', () => {
90
- const schema = new LiteralSchema(42)
91
+ const schema = literal(42)
91
92
 
92
93
  it('validates exact number match', () => {
93
94
  const result = schema.safeParse(42)
@@ -124,7 +125,7 @@ describe('LiteralSchema', () => {
124
125
  })
125
126
 
126
127
  describe('zero literal', () => {
127
- const schema = new LiteralSchema(0)
128
+ const schema = literal(0)
128
129
 
129
130
  it('validates zero', () => {
130
131
  const result = schema.safeParse(0)
@@ -156,7 +157,7 @@ describe('LiteralSchema', () => {
156
157
  })
157
158
 
158
159
  describe('negative number literals', () => {
159
- const schema = new LiteralSchema(-42)
160
+ const schema = literal(-42)
160
161
 
161
162
  it('validates exact negative number match', () => {
162
163
  const result = schema.safeParse(-42)
@@ -178,7 +179,7 @@ describe('LiteralSchema', () => {
178
179
  })
179
180
 
180
181
  describe('decimal number literals', () => {
181
- const schema = new LiteralSchema(3.14)
182
+ const schema = literal(3.14)
182
183
 
183
184
  it('validates exact decimal match', () => {
184
185
  const result = schema.safeParse(3.14)
@@ -201,7 +202,7 @@ describe('LiteralSchema', () => {
201
202
 
202
203
  describe('boolean literals', () => {
203
204
  describe('true literal', () => {
204
- const schema = new LiteralSchema(true)
205
+ const schema = literal(true)
205
206
 
206
207
  it('validates true', () => {
207
208
  const result = schema.safeParse(true)
@@ -238,7 +239,7 @@ describe('LiteralSchema', () => {
238
239
  })
239
240
 
240
241
  describe('false literal', () => {
241
- const schema = new LiteralSchema(false)
242
+ const schema = literal(false)
242
243
 
243
244
  it('validates false', () => {
244
245
  const result = schema.safeParse(false)
@@ -271,7 +272,7 @@ describe('LiteralSchema', () => {
271
272
  })
272
273
 
273
274
  describe('null literal', () => {
274
- const schema = new LiteralSchema(null)
275
+ const schema = literal(null)
275
276
 
276
277
  it('validates null', () => {
277
278
  const result = schema.safeParse(null)
@@ -309,7 +310,7 @@ describe('LiteralSchema', () => {
309
310
 
310
311
  describe('default values', () => {
311
312
  describe('string literal with default', () => {
312
- const schema = new LiteralSchema('hello', { default: 'hello' })
313
+ const schema = withDefault(literal('hello'), 'hello')
313
314
 
314
315
  it('uses default value when undefined is provided', () => {
315
316
  const result = schema.safeParse(undefined)
@@ -334,7 +335,7 @@ describe('LiteralSchema', () => {
334
335
  })
335
336
 
336
337
  describe('number literal with default', () => {
337
- const schema = new LiteralSchema(42, { default: 42 })
338
+ const schema = withDefault(literal(42), 42)
338
339
 
339
340
  it('uses default value when undefined is provided', () => {
340
341
  const result = schema.safeParse(undefined)
@@ -351,7 +352,7 @@ describe('LiteralSchema', () => {
351
352
  })
352
353
 
353
354
  describe('boolean literal with default', () => {
354
- const schema = new LiteralSchema(true, { default: true })
355
+ const schema = withDefault(literal(true), true)
355
356
 
356
357
  it('uses default value when undefined is provided', () => {
357
358
  const result = schema.safeParse(undefined)
@@ -368,7 +369,7 @@ describe('LiteralSchema', () => {
368
369
  })
369
370
 
370
371
  describe('null literal with default', () => {
371
- const schema = new LiteralSchema(null, { default: null })
372
+ const schema = withDefault(literal(null), null)
372
373
 
373
374
  it('uses default value when undefined is provided', () => {
374
375
  const result = schema.safeParse(undefined)
@@ -385,7 +386,7 @@ describe('LiteralSchema', () => {
385
386
  })
386
387
 
387
388
  describe('false literal with default', () => {
388
- const schema = new LiteralSchema(false, { default: false })
389
+ const schema = withDefault(literal(false), false)
389
390
 
390
391
  it('uses default value when undefined is provided', () => {
391
392
  const result = schema.safeParse(undefined)
@@ -402,7 +403,7 @@ describe('LiteralSchema', () => {
402
403
  })
403
404
 
404
405
  describe('zero literal with default', () => {
405
- const schema = new LiteralSchema(0, { default: 0 })
406
+ const schema = withDefault(literal(0), 0)
406
407
 
407
408
  it('uses default value when undefined is provided', () => {
408
409
  const result = schema.safeParse(undefined)
@@ -421,82 +422,82 @@ describe('LiteralSchema', () => {
421
422
 
422
423
  describe('edge cases', () => {
423
424
  it('handles special string characters', () => {
424
- const schema = new LiteralSchema('hello\nworld')
425
+ const schema = literal('hello\nworld')
425
426
  expect(schema.safeParse('hello\nworld').success).toBe(true)
426
427
  expect(schema.safeParse('hello world').success).toBe(false)
427
428
  })
428
429
 
429
430
  it('handles unicode characters in strings', () => {
430
- const schema = new LiteralSchema('Hello 世界 🌍')
431
+ const schema = literal('Hello 世界 🌍')
431
432
  expect(schema.safeParse('Hello 世界 🌍').success).toBe(true)
432
433
  expect(schema.safeParse('Hello world').success).toBe(false)
433
434
  })
434
435
 
435
436
  it('handles emoji literals', () => {
436
- const schema = new LiteralSchema('🚀')
437
+ const schema = literal('🚀')
437
438
  expect(schema.safeParse('🚀').success).toBe(true)
438
439
  expect(schema.safeParse('🌟').success).toBe(false)
439
440
  })
440
441
 
441
442
  it('handles very long string literals', () => {
442
443
  const longString = 'a'.repeat(1000)
443
- const schema = new LiteralSchema(longString)
444
+ const schema = literal(longString)
444
445
  expect(schema.safeParse(longString).success).toBe(true)
445
446
  expect(schema.safeParse(longString + 'b').success).toBe(false)
446
447
  })
447
448
 
448
449
  it('handles string with whitespace', () => {
449
- const schema = new LiteralSchema(' hello ')
450
+ const schema = literal(' hello ')
450
451
  expect(schema.safeParse(' hello ').success).toBe(true)
451
452
  expect(schema.safeParse('hello').success).toBe(false)
452
453
  expect(schema.safeParse(' hello').success).toBe(false)
453
454
  })
454
455
 
455
456
  it('handles Number.MAX_SAFE_INTEGER', () => {
456
- const schema = new LiteralSchema(Number.MAX_SAFE_INTEGER)
457
+ const schema = literal(Number.MAX_SAFE_INTEGER)
457
458
  expect(schema.safeParse(Number.MAX_SAFE_INTEGER).success).toBe(true)
458
459
  expect(schema.safeParse(Number.MAX_SAFE_INTEGER - 1).success).toBe(false)
459
460
  })
460
461
 
461
462
  it('handles Number.MIN_SAFE_INTEGER', () => {
462
- const schema = new LiteralSchema(Number.MIN_SAFE_INTEGER)
463
+ const schema = literal(Number.MIN_SAFE_INTEGER)
463
464
  expect(schema.safeParse(Number.MIN_SAFE_INTEGER).success).toBe(true)
464
465
  expect(schema.safeParse(Number.MIN_SAFE_INTEGER + 1).success).toBe(false)
465
466
  })
466
467
 
467
468
  it('rejects NaN', () => {
468
- const schema = new LiteralSchema(42)
469
+ const schema = literal(42)
469
470
  expect(schema.safeParse(NaN).success).toBe(false)
470
471
  })
471
472
 
472
473
  it('rejects Infinity', () => {
473
- const schema = new LiteralSchema(42)
474
+ const schema = literal(42)
474
475
  expect(schema.safeParse(Infinity).success).toBe(false)
475
476
  })
476
477
 
477
478
  it('rejects -Infinity', () => {
478
- const schema = new LiteralSchema(42)
479
+ const schema = literal(42)
479
480
  expect(schema.safeParse(-Infinity).success).toBe(false)
480
481
  })
481
482
 
482
483
  it('rejects Boolean objects', () => {
483
- const schema = new LiteralSchema(true)
484
+ const schema = literal(true)
484
485
  expect(schema.safeParse(new Boolean(true)).success).toBe(false)
485
486
  })
486
487
 
487
488
  it('rejects String objects', () => {
488
- const schema = new LiteralSchema('hello')
489
+ const schema = literal('hello')
489
490
  expect(schema.safeParse(new String('hello')).success).toBe(false)
490
491
  })
491
492
 
492
493
  it('rejects Number objects', () => {
493
- const schema = new LiteralSchema(42)
494
+ const schema = literal(42)
494
495
  expect(schema.safeParse(new Number(42)).success).toBe(false)
495
496
  })
496
497
 
497
498
  it('distinguishes between -0 and +0', () => {
498
- const schemaPositive = new LiteralSchema(0)
499
- const schemaNegative = new LiteralSchema(-0)
499
+ const schemaPositive = literal(0)
500
+ const schemaNegative = literal(-0)
500
501
  // In JavaScript, 0 === -0, so both should validate for both schemas
501
502
  expect(schemaPositive.safeParse(0).success).toBe(true)
502
503
  expect(schemaPositive.safeParse(-0).success).toBe(true)
@@ -505,7 +506,7 @@ describe('LiteralSchema', () => {
505
506
  })
506
507
 
507
508
  it('handles very small decimal differences', () => {
508
- const schema = new LiteralSchema(0.1 + 0.2)
509
+ const schema = literal(0.1 + 0.2)
509
510
  // Note: 0.1 + 0.2 !== 0.3 in JavaScript due to floating point precision
510
511
  expect(schema.safeParse(0.1 + 0.2).success).toBe(true)
511
512
  expect(schema.safeParse(0.3).success).toBe(false)
@@ -514,13 +515,13 @@ describe('LiteralSchema', () => {
514
515
 
515
516
  describe('type safety', () => {
516
517
  it('accepts exact literal types in TypeScript', () => {
517
- const schema = new LiteralSchema('specific' as const)
518
+ const schema = literal('specific' as const)
518
519
  const result = schema.safeParse('specific')
519
520
  expect(result.success).toBe(true)
520
521
  })
521
522
 
522
523
  it('preserves literal type in success result', () => {
523
- const schema = new LiteralSchema(42)
524
+ const schema = literal(42)
524
525
  const result = schema.safeParse(42)
525
526
  expect(result.success).toBe(true)
526
527
  if (result.success) {
@@ -1,23 +1,13 @@
1
- import { Schema, ValidationResult, ValidatorContext } from '../core.js'
2
-
3
- export type LiteralSchemaOptions<T extends null | string | number | boolean> = {
4
- default?: T
5
- }
1
+ import { Schema, ValidationContext } from '../core.js'
6
2
 
7
3
  export class LiteralSchema<
8
- Output extends null | string | number | boolean = any,
9
- > extends Schema<Output> {
10
- constructor(
11
- readonly value: Output,
12
- readonly options?: LiteralSchemaOptions<Output>,
13
- ) {
4
+ const TValue extends null | string | number | boolean,
5
+ > extends Schema<TValue> {
6
+ constructor(readonly value: TValue) {
14
7
  super()
15
8
  }
16
9
 
17
- validateInContext(
18
- input: unknown = this.options?.default,
19
- ctx: ValidatorContext,
20
- ): ValidationResult<Output> {
10
+ validateInContext(input: unknown, ctx: ValidationContext) {
21
11
  if (input !== this.value) {
22
12
  return ctx.issueInvalidValue(input, [this.value])
23
13
  }
@@ -25,3 +15,10 @@ export class LiteralSchema<
25
15
  return ctx.success(this.value)
26
16
  }
27
17
  }
18
+
19
+ /*@__NO_SIDE_EFFECTS__*/
20
+ export function literal<const V extends null | string | number | boolean>(
21
+ value: V,
22
+ ) {
23
+ return new LiteralSchema<V>(value)
24
+ }
@@ -1,9 +1,9 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { NeverSchema } from './never.js'
2
+ import { never } from './never.js'
3
3
 
4
4
  describe('NeverSchema', () => {
5
5
  describe('basic validation', () => {
6
- const schema = new NeverSchema()
6
+ const schema = never()
7
7
 
8
8
  it('rejects strings', () => {
9
9
  const result = schema.safeParse('string')
@@ -57,7 +57,7 @@ describe('NeverSchema', () => {
57
57
  })
58
58
 
59
59
  describe('edge cases', () => {
60
- const schema = new NeverSchema()
60
+ const schema = never()
61
61
 
62
62
  it('rejects BigInt', () => {
63
63
  const result = schema.safeParse(BigInt(123))
@@ -114,7 +114,7 @@ describe('NeverSchema', () => {
114
114
  })
115
115
 
116
116
  describe('complex data types', () => {
117
- const schema = new NeverSchema()
117
+ const schema = never()
118
118
 
119
119
  it('rejects class instances', () => {
120
120
  class TestClass {
@@ -150,7 +150,7 @@ describe('NeverSchema', () => {
150
150
  })
151
151
 
152
152
  describe('special number values', () => {
153
- const schema = new NeverSchema()
153
+ const schema = never()
154
154
 
155
155
  it('rejects NaN', () => {
156
156
  const result = schema.safeParse(NaN)
@@ -1,7 +1,12 @@
1
- import { Schema, ValidationFailure, ValidatorContext } from '../core.js'
1
+ import { Schema, ValidationContext } from '../core.js'
2
+ import { memoizedOptions } from '../util/memoize.js'
2
3
 
3
4
  export class NeverSchema extends Schema<never> {
4
- validateInContext(input: unknown, ctx: ValidatorContext): ValidationFailure {
5
+ validateInContext(input: unknown, ctx: ValidationContext) {
5
6
  return ctx.issueInvalidType(input, 'never')
6
7
  }
7
8
  }
9
+
10
+ export const never = /*#__PURE__*/ memoizedOptions(function () {
11
+ return new NeverSchema()
12
+ })