@atproto/lex-schema 0.0.2 → 0.0.3

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 (289) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/core/$type.d.ts +6 -3
  3. package/dist/core/$type.d.ts.map +1 -1
  4. package/dist/core/$type.js +1 -0
  5. package/dist/core/$type.js.map +1 -1
  6. package/dist/core/record-key.d.ts +3 -3
  7. package/dist/core/record-key.d.ts.map +1 -1
  8. package/dist/core/record-key.js +12 -6
  9. package/dist/core/record-key.js.map +1 -1
  10. package/dist/core/result.d.ts.map +1 -1
  11. package/dist/core/result.js +6 -0
  12. package/dist/core/result.js.map +1 -1
  13. package/dist/core/string-format.d.ts +30 -27
  14. package/dist/core/string-format.d.ts.map +1 -1
  15. package/dist/core/string-format.js +56 -42
  16. package/dist/core/string-format.js.map +1 -1
  17. package/dist/core/types.d.ts +9 -1
  18. package/dist/core/types.d.ts.map +1 -1
  19. package/dist/core/types.js.map +1 -1
  20. package/dist/external.d.ts +31 -28
  21. package/dist/external.d.ts.map +1 -1
  22. package/dist/external.js +33 -17
  23. package/dist/external.js.map +1 -1
  24. package/dist/schema/_parameters.d.ts +2 -2
  25. package/dist/schema/_parameters.d.ts.map +1 -1
  26. package/dist/schema/array.d.ts +5 -6
  27. package/dist/schema/array.d.ts.map +1 -1
  28. package/dist/schema/array.js +5 -6
  29. package/dist/schema/array.js.map +1 -1
  30. package/dist/schema/blob.d.ts +2 -3
  31. package/dist/schema/blob.d.ts.map +1 -1
  32. package/dist/schema/blob.js +1 -2
  33. package/dist/schema/blob.js.map +1 -1
  34. package/dist/schema/boolean.d.ts +4 -5
  35. package/dist/schema/boolean.d.ts.map +1 -1
  36. package/dist/schema/boolean.js +2 -3
  37. package/dist/schema/boolean.js.map +1 -1
  38. package/dist/schema/bytes.d.ts +3 -4
  39. package/dist/schema/bytes.d.ts.map +1 -1
  40. package/dist/schema/bytes.js +2 -3
  41. package/dist/schema/bytes.js.map +1 -1
  42. package/dist/schema/cid.d.ts +13 -6
  43. package/dist/schema/cid.d.ts.map +1 -1
  44. package/dist/schema/cid.js +2 -4
  45. package/dist/schema/cid.js.map +1 -1
  46. package/dist/schema/custom.d.ts +3 -4
  47. package/dist/schema/custom.d.ts.map +1 -1
  48. package/dist/schema/custom.js +4 -3
  49. package/dist/schema/custom.js.map +1 -1
  50. package/dist/schema/dict.d.ts +3 -3
  51. package/dist/schema/dict.d.ts.map +1 -1
  52. package/dist/schema/dict.js +1 -1
  53. package/dist/schema/dict.js.map +1 -1
  54. package/dist/schema/discriminated-union.d.ts +15 -24
  55. package/dist/schema/discriminated-union.d.ts.map +1 -1
  56. package/dist/schema/discriminated-union.js +40 -64
  57. package/dist/schema/discriminated-union.js.map +1 -1
  58. package/dist/schema/enum.d.ts +8 -4
  59. package/dist/schema/enum.d.ts.map +1 -1
  60. package/dist/schema/enum.js +5 -3
  61. package/dist/schema/enum.js.map +1 -1
  62. package/dist/schema/integer.d.ts +3 -4
  63. package/dist/schema/integer.d.ts.map +1 -1
  64. package/dist/schema/integer.js +3 -4
  65. package/dist/schema/integer.js.map +1 -1
  66. package/dist/schema/intersection.d.ts +22 -14
  67. package/dist/schema/intersection.d.ts.map +1 -1
  68. package/dist/schema/intersection.js +12 -22
  69. package/dist/schema/intersection.js.map +1 -1
  70. package/dist/schema/literal.d.ts +7 -3
  71. package/dist/schema/literal.d.ts.map +1 -1
  72. package/dist/schema/literal.js +5 -3
  73. package/dist/schema/literal.js.map +1 -1
  74. package/dist/schema/never.d.ts +2 -2
  75. package/dist/schema/never.d.ts.map +1 -1
  76. package/dist/schema/never.js +1 -1
  77. package/dist/schema/never.js.map +1 -1
  78. package/dist/schema/null.d.ts +2 -3
  79. package/dist/schema/null.d.ts.map +1 -1
  80. package/dist/schema/null.js +1 -2
  81. package/dist/schema/null.js.map +1 -1
  82. package/dist/schema/nullable.d.ts +7 -0
  83. package/dist/schema/nullable.d.ts.map +1 -0
  84. package/dist/schema/nullable.js +19 -0
  85. package/dist/schema/nullable.js.map +1 -0
  86. package/dist/schema/object.d.ts +10 -44
  87. package/dist/schema/object.d.ts.map +1 -1
  88. package/dist/schema/object.js +10 -46
  89. package/dist/schema/object.js.map +1 -1
  90. package/dist/schema/optional.d.ts +7 -0
  91. package/dist/schema/optional.d.ts.map +1 -0
  92. package/dist/schema/optional.js +25 -0
  93. package/dist/schema/optional.js.map +1 -0
  94. package/dist/schema/params.d.ts +14 -19
  95. package/dist/schema/params.d.ts.map +1 -1
  96. package/dist/schema/params.js +10 -24
  97. package/dist/schema/params.js.map +1 -1
  98. package/dist/schema/payload.d.ts +4 -4
  99. package/dist/schema/payload.d.ts.map +1 -1
  100. package/dist/schema/payload.js.map +1 -1
  101. package/dist/schema/permission-set.d.ts +6 -6
  102. package/dist/schema/permission-set.d.ts.map +1 -1
  103. package/dist/schema/permission-set.js +1 -2
  104. package/dist/schema/permission-set.js.map +1 -1
  105. package/dist/schema/permission.d.ts +0 -1
  106. package/dist/schema/permission.d.ts.map +1 -1
  107. package/dist/schema/permission.js +0 -1
  108. package/dist/schema/permission.js.map +1 -1
  109. package/dist/schema/procedure.d.ts +8 -9
  110. package/dist/schema/procedure.d.ts.map +1 -1
  111. package/dist/schema/procedure.js +0 -1
  112. package/dist/schema/procedure.js.map +1 -1
  113. package/dist/schema/query.d.ts +7 -8
  114. package/dist/schema/query.d.ts.map +1 -1
  115. package/dist/schema/query.js +0 -1
  116. package/dist/schema/query.js.map +1 -1
  117. package/dist/schema/record.d.ts +34 -28
  118. package/dist/schema/record.d.ts.map +1 -1
  119. package/dist/schema/record.js +1 -2
  120. package/dist/schema/record.js.map +1 -1
  121. package/dist/schema/ref.d.ts +2 -3
  122. package/dist/schema/ref.d.ts.map +1 -1
  123. package/dist/schema/ref.js +1 -2
  124. package/dist/schema/ref.js.map +1 -1
  125. package/dist/schema/refine.d.ts +18 -0
  126. package/dist/schema/refine.d.ts.map +1 -0
  127. package/dist/schema/refine.js +33 -0
  128. package/dist/schema/refine.js.map +1 -0
  129. package/dist/schema/regexp.d.ts +7 -0
  130. package/dist/schema/regexp.d.ts.map +1 -0
  131. package/dist/schema/regexp.js +22 -0
  132. package/dist/schema/regexp.js.map +1 -0
  133. package/dist/schema/string.d.ts +4 -8
  134. package/dist/schema/string.d.ts.map +1 -1
  135. package/dist/schema/string.js +6 -3
  136. package/dist/schema/string.js.map +1 -1
  137. package/dist/schema/subscription.d.ts +7 -6
  138. package/dist/schema/subscription.d.ts.map +1 -1
  139. package/dist/schema/subscription.js.map +1 -1
  140. package/dist/schema/token.d.ts +2 -3
  141. package/dist/schema/token.d.ts.map +1 -1
  142. package/dist/schema/token.js +1 -2
  143. package/dist/schema/token.js.map +1 -1
  144. package/dist/schema/typed-object.d.ts +29 -27
  145. package/dist/schema/typed-object.d.ts.map +1 -1
  146. package/dist/schema/typed-object.js +1 -2
  147. package/dist/schema/typed-object.js.map +1 -1
  148. package/dist/schema/typed-ref.d.ts +2 -2
  149. package/dist/schema/typed-ref.d.ts.map +1 -1
  150. package/dist/schema/typed-ref.js +1 -1
  151. package/dist/schema/typed-ref.js.map +1 -1
  152. package/dist/schema/typed-union.d.ts +3 -4
  153. package/dist/schema/typed-union.d.ts.map +1 -1
  154. package/dist/schema/typed-union.js +3 -10
  155. package/dist/schema/typed-union.js.map +1 -1
  156. package/dist/schema/union.d.ts +2 -2
  157. package/dist/schema/union.d.ts.map +1 -1
  158. package/dist/schema/union.js +1 -1
  159. package/dist/schema/union.js.map +1 -1
  160. package/dist/schema/unknown-object.d.ts +2 -3
  161. package/dist/schema/unknown-object.d.ts.map +1 -1
  162. package/dist/schema/unknown-object.js +1 -2
  163. package/dist/schema/unknown-object.js.map +1 -1
  164. package/dist/schema/unknown.d.ts +2 -2
  165. package/dist/schema/unknown.d.ts.map +1 -1
  166. package/dist/schema/unknown.js +1 -1
  167. package/dist/schema/unknown.js.map +1 -1
  168. package/dist/schema.d.ts +4 -0
  169. package/dist/schema.d.ts.map +1 -1
  170. package/dist/schema.js +6 -1
  171. package/dist/schema.js.map +1 -1
  172. package/dist/util/array-agg.d.ts.map +1 -1
  173. package/dist/util/array-agg.js +1 -0
  174. package/dist/util/array-agg.js.map +1 -1
  175. package/dist/util/lazy-property.d.ts +2 -0
  176. package/dist/util/lazy-property.d.ts.map +1 -0
  177. package/dist/util/lazy-property.js +14 -0
  178. package/dist/util/lazy-property.js.map +1 -0
  179. package/dist/validation/schema.d.ts +24 -0
  180. package/dist/validation/schema.d.ts.map +1 -0
  181. package/dist/validation/schema.js +57 -0
  182. package/dist/validation/schema.js.map +1 -0
  183. package/dist/validation/validation-error.d.ts +3 -3
  184. package/dist/validation/validation-error.d.ts.map +1 -1
  185. package/dist/validation/validation-error.js +32 -4
  186. package/dist/validation/validation-error.js.map +1 -1
  187. package/dist/validation/validation-issue.d.ts +32 -24
  188. package/dist/validation/validation-issue.d.ts.map +1 -1
  189. package/dist/validation/validation-issue.js +136 -92
  190. package/dist/validation/validation-issue.js.map +1 -1
  191. package/dist/validation/validator.d.ts +20 -50
  192. package/dist/validation/validator.d.ts.map +1 -1
  193. package/dist/validation/validator.js +40 -134
  194. package/dist/validation/validator.js.map +1 -1
  195. package/dist/validation.d.ts +1 -0
  196. package/dist/validation.d.ts.map +1 -1
  197. package/dist/validation.js +1 -0
  198. package/dist/validation.js.map +1 -1
  199. package/package.json +8 -4
  200. package/src/core/$type.ts +7 -4
  201. package/src/core/record-key.ts +12 -5
  202. package/src/core/result.ts +6 -0
  203. package/src/core/string-format.ts +97 -61
  204. package/src/core/types.ts +12 -6
  205. package/src/external.ts +92 -70
  206. package/src/schema/_parameters.test.ts +416 -0
  207. package/src/schema/array.test.ts +237 -0
  208. package/src/schema/array.ts +17 -11
  209. package/src/schema/blob.test.ts +506 -0
  210. package/src/schema/blob.ts +3 -5
  211. package/src/schema/boolean.test.ts +116 -0
  212. package/src/schema/boolean.ts +5 -7
  213. package/src/schema/bytes.test.ts +226 -0
  214. package/src/schema/bytes.ts +4 -6
  215. package/src/schema/cid.test.ts +155 -0
  216. package/src/schema/cid.ts +14 -8
  217. package/src/schema/custom.test.ts +413 -0
  218. package/src/schema/custom.ts +10 -8
  219. package/src/schema/dict.test.ts +198 -0
  220. package/src/schema/dict.ts +6 -8
  221. package/src/schema/discriminated-union.test.ts +675 -0
  222. package/src/schema/discriminated-union.ts +68 -95
  223. package/src/schema/enum.test.ts +396 -0
  224. package/src/schema/enum.ts +12 -5
  225. package/src/schema/integer.test.ts +312 -0
  226. package/src/schema/integer.ts +5 -7
  227. package/src/schema/intersection.test.ts +32 -0
  228. package/src/schema/intersection.ts +37 -40
  229. package/src/schema/literal.test.ts +531 -0
  230. package/src/schema/literal.ts +12 -5
  231. package/src/schema/never.test.ts +174 -0
  232. package/src/schema/never.ts +3 -10
  233. package/src/schema/null.test.ts +79 -0
  234. package/src/schema/null.ts +3 -5
  235. package/src/schema/nullable.test.ts +480 -0
  236. package/src/schema/nullable.ts +23 -0
  237. package/src/schema/object.test.ts +47 -115
  238. package/src/schema/object.ts +19 -123
  239. package/src/schema/optional.test.ts +485 -0
  240. package/src/schema/optional.ts +31 -0
  241. package/src/schema/params.test.ts +582 -0
  242. package/src/schema/params.ts +37 -55
  243. package/src/schema/payload.test.ts +345 -0
  244. package/src/schema/payload.ts +5 -5
  245. package/src/schema/permission-set.test.ts +679 -0
  246. package/src/schema/permission-set.ts +6 -8
  247. package/src/schema/permission.test.ts +536 -0
  248. package/src/schema/permission.ts +0 -2
  249. package/src/schema/procedure.test.ts +443 -0
  250. package/src/schema/procedure.ts +11 -13
  251. package/src/schema/query.test.ts +408 -0
  252. package/src/schema/query.ts +9 -11
  253. package/src/schema/record.test.ts +694 -0
  254. package/src/schema/record.ts +38 -36
  255. package/src/schema/ref.test.ts +365 -0
  256. package/src/schema/ref.ts +8 -5
  257. package/src/schema/refine.test.ts +578 -0
  258. package/src/schema/refine.ts +85 -0
  259. package/src/schema/regexp.test.ts +580 -0
  260. package/src/schema/regexp.ts +22 -0
  261. package/src/schema/string.test.ts +612 -0
  262. package/src/schema/string.ts +11 -17
  263. package/src/schema/subscription.test.ts +689 -0
  264. package/src/schema/subscription.ts +13 -8
  265. package/src/schema/token.test.ts +428 -0
  266. package/src/schema/token.ts +3 -5
  267. package/src/schema/typed-object.test.ts +612 -0
  268. package/src/schema/typed-object.ts +23 -20
  269. package/src/schema/typed-ref.test.ts +823 -0
  270. package/src/schema/typed-ref.ts +10 -5
  271. package/src/schema/typed-union.test.ts +378 -0
  272. package/src/schema/typed-union.ts +6 -15
  273. package/src/schema/union.test.ts +200 -0
  274. package/src/schema/union.ts +5 -4
  275. package/src/schema/unknown-object.test.ts +592 -0
  276. package/src/schema/unknown-object.ts +3 -5
  277. package/src/schema/unknown.test.ts +312 -0
  278. package/src/schema/unknown.ts +3 -3
  279. package/src/schema.ts +7 -1
  280. package/src/util/array-agg.ts +1 -0
  281. package/src/util/lazy-property.ts +14 -0
  282. package/src/validation/schema.ts +92 -0
  283. package/src/validation/validation-error.ts +60 -9
  284. package/src/validation/validation-issue.ts +141 -144
  285. package/src/validation/validator.ts +67 -206
  286. package/src/validation.ts +1 -0
  287. package/tsconfig.build.json +12 -0
  288. package/tsconfig.json +7 -0
  289. package/tsconfig.tests.json +9 -0
@@ -0,0 +1,416 @@
1
+ import { paramSchema, paramsSchema } from './_parameters.js'
2
+
3
+ describe('paramSchema', () => {
4
+ describe('scalar values', () => {
5
+ it('validates boolean values', () => {
6
+ const result = paramSchema.safeParse(true)
7
+ expect(result.success).toBe(true)
8
+ })
9
+
10
+ it('validates integer values', () => {
11
+ const result = paramSchema.safeParse(42)
12
+ expect(result.success).toBe(true)
13
+ })
14
+
15
+ it('validates string values', () => {
16
+ const result = paramSchema.safeParse('hello')
17
+ expect(result.success).toBe(true)
18
+ })
19
+
20
+ it('validates empty strings', () => {
21
+ const result = paramSchema.safeParse('')
22
+ expect(result.success).toBe(true)
23
+ })
24
+
25
+ it('validates zero', () => {
26
+ const result = paramSchema.safeParse(0)
27
+ expect(result.success).toBe(true)
28
+ })
29
+
30
+ it('validates negative integers', () => {
31
+ const result = paramSchema.safeParse(-50)
32
+ expect(result.success).toBe(true)
33
+ })
34
+
35
+ it('validates false boolean', () => {
36
+ const result = paramSchema.safeParse(false)
37
+ expect(result.success).toBe(true)
38
+ })
39
+ })
40
+
41
+ describe('array values', () => {
42
+ it('validates arrays of booleans', () => {
43
+ const result = paramSchema.safeParse([true, false, true])
44
+ expect(result.success).toBe(true)
45
+ })
46
+
47
+ it('validates arrays of integers', () => {
48
+ const result = paramSchema.safeParse([1, 2, 3, 4])
49
+ expect(result.success).toBe(true)
50
+ })
51
+
52
+ it('validates arrays of strings', () => {
53
+ const result = paramSchema.safeParse(['foo', 'bar', 'baz'])
54
+ expect(result.success).toBe(true)
55
+ })
56
+
57
+ it('validates empty arrays', () => {
58
+ const result = paramSchema.safeParse([])
59
+ expect(result.success).toBe(true)
60
+ })
61
+
62
+ it('validates arrays with single element', () => {
63
+ const result = paramSchema.safeParse(['single'])
64
+ expect(result.success).toBe(true)
65
+ })
66
+
67
+ it('validates arrays with mixed scalar types', () => {
68
+ const result = paramSchema.safeParse([true, 42, 'text'])
69
+ expect(result.success).toBe(true)
70
+ })
71
+
72
+ it('validates arrays with negative integers', () => {
73
+ const result = paramSchema.safeParse([-1, -2, -3])
74
+ expect(result.success).toBe(true)
75
+ })
76
+
77
+ it('validates arrays with empty strings', () => {
78
+ const result = paramSchema.safeParse(['', 'non-empty', ''])
79
+ expect(result.success).toBe(true)
80
+ })
81
+
82
+ it('validates arrays with zero', () => {
83
+ const result = paramSchema.safeParse([0, 1, 2])
84
+ expect(result.success).toBe(true)
85
+ })
86
+
87
+ it('rejects nested arrays', () => {
88
+ const result = paramSchema.safeParse([
89
+ [1, 2],
90
+ [3, 4],
91
+ ])
92
+ expect(result.success).toBe(false)
93
+ })
94
+
95
+ it('rejects arrays with null values', () => {
96
+ const result = paramSchema.safeParse([1, null, 3])
97
+ expect(result.success).toBe(false)
98
+ })
99
+
100
+ it('rejects arrays with undefined values', () => {
101
+ const result = paramSchema.safeParse([1, undefined, 3])
102
+ expect(result.success).toBe(false)
103
+ })
104
+
105
+ it('rejects arrays with object values', () => {
106
+ const result = paramSchema.safeParse([1, { key: 'value' }, 3])
107
+ expect(result.success).toBe(false)
108
+ })
109
+
110
+ it('rejects arrays with floating point numbers', () => {
111
+ const result = paramSchema.safeParse([1, 2.5, 3])
112
+ expect(result.success).toBe(false)
113
+ })
114
+
115
+ it('rejects arrays with NaN', () => {
116
+ const result = paramSchema.safeParse([1, NaN, 3])
117
+ expect(result.success).toBe(false)
118
+ })
119
+
120
+ it('rejects arrays with Infinity', () => {
121
+ const result = paramSchema.safeParse([1, Infinity, 3])
122
+ expect(result.success).toBe(false)
123
+ })
124
+ })
125
+
126
+ describe('invalid values', () => {
127
+ it('rejects null values', () => {
128
+ const result = paramSchema.safeParse(null)
129
+ expect(result.success).toBe(false)
130
+ })
131
+
132
+ it('rejects undefined values', () => {
133
+ const result = paramSchema.safeParse(undefined)
134
+ expect(result.success).toBe(false)
135
+ })
136
+
137
+ it('rejects object values', () => {
138
+ const result = paramSchema.safeParse({ key: 'value' })
139
+ expect(result.success).toBe(false)
140
+ })
141
+
142
+ it('rejects floating point numbers', () => {
143
+ const result = paramSchema.safeParse(3.14)
144
+ expect(result.success).toBe(false)
145
+ })
146
+
147
+ it('rejects NaN', () => {
148
+ const result = paramSchema.safeParse(NaN)
149
+ expect(result.success).toBe(false)
150
+ })
151
+
152
+ it('rejects Infinity', () => {
153
+ const result = paramSchema.safeParse(Infinity)
154
+ expect(result.success).toBe(false)
155
+ })
156
+
157
+ it('rejects -Infinity', () => {
158
+ const result = paramSchema.safeParse(-Infinity)
159
+ expect(result.success).toBe(false)
160
+ })
161
+ })
162
+ })
163
+
164
+ describe('paramsSchema', () => {
165
+ it('validates empty object', () => {
166
+ const result = paramsSchema.safeParse({})
167
+ expect(result.success).toBe(true)
168
+ })
169
+
170
+ it('validates object with boolean parameters', () => {
171
+ const result = paramsSchema.safeParse({
172
+ enabled: true,
173
+ disabled: false,
174
+ })
175
+ expect(result.success).toBe(true)
176
+ })
177
+
178
+ it('validates object with integer parameters', () => {
179
+ const result = paramsSchema.safeParse({
180
+ limit: 10,
181
+ offset: 0,
182
+ count: 100,
183
+ })
184
+ expect(result.success).toBe(true)
185
+ })
186
+
187
+ it('validates object with string parameters', () => {
188
+ const result = paramsSchema.safeParse({
189
+ name: 'Alice',
190
+ query: 'search term',
191
+ cursor: 'abc123',
192
+ })
193
+ expect(result.success).toBe(true)
194
+ })
195
+
196
+ it('validates object with array parameters', () => {
197
+ const result = paramsSchema.safeParse({
198
+ tags: ['tag1', 'tag2', 'tag3'],
199
+ ids: [1, 2, 3, 4],
200
+ flags: [true, false, true],
201
+ })
202
+ expect(result.success).toBe(true)
203
+ })
204
+
205
+ it('validates object with mixed parameter types', () => {
206
+ const result = paramsSchema.safeParse({
207
+ name: 'Alice',
208
+ age: 30,
209
+ active: true,
210
+ tags: ['user', 'admin'],
211
+ limit: 50,
212
+ })
213
+ expect(result.success).toBe(true)
214
+ })
215
+
216
+ it('validates object with empty string parameters', () => {
217
+ const result = paramsSchema.safeParse({
218
+ query: '',
219
+ cursor: '',
220
+ })
221
+ expect(result.success).toBe(true)
222
+ })
223
+
224
+ it('validates object with negative integer parameters', () => {
225
+ const result = paramsSchema.safeParse({
226
+ offset: -10,
227
+ delta: -5,
228
+ })
229
+ expect(result.success).toBe(true)
230
+ })
231
+
232
+ it('validates object with empty array parameters', () => {
233
+ const result = paramsSchema.safeParse({
234
+ tags: [],
235
+ ids: [],
236
+ })
237
+ expect(result.success).toBe(true)
238
+ })
239
+
240
+ it('validates object with arrays of mixed scalar types', () => {
241
+ const result = paramsSchema.safeParse({
242
+ values: [true, 42, 'text'],
243
+ })
244
+ expect(result.success).toBe(true)
245
+ })
246
+
247
+ it('validates object with numeric string keys', () => {
248
+ const result = paramsSchema.safeParse({
249
+ '0': 'value0',
250
+ '1': 'value1',
251
+ '2': 'value2',
252
+ })
253
+ expect(result.success).toBe(true)
254
+ })
255
+
256
+ it('rejects non-object values', () => {
257
+ const result = paramsSchema.safeParse('not an object')
258
+ expect(result.success).toBe(false)
259
+ })
260
+
261
+ it('rejects null values', () => {
262
+ const result = paramsSchema.safeParse(null)
263
+ expect(result.success).toBe(false)
264
+ })
265
+
266
+ it('rejects undefined values', () => {
267
+ const result = paramsSchema.safeParse(undefined)
268
+ expect(result.success).toBe(false)
269
+ })
270
+
271
+ it('rejects arrays', () => {
272
+ const result = paramsSchema.safeParse([1, 2, 3])
273
+ expect(result.success).toBe(false)
274
+ })
275
+
276
+ it('rejects object with null parameter values', () => {
277
+ const result = paramsSchema.safeParse({
278
+ name: 'Alice',
279
+ invalid: null,
280
+ })
281
+ expect(result.success).toBe(false)
282
+ })
283
+
284
+ it('rejects object with floating point parameter values', () => {
285
+ const result = paramsSchema.safeParse({
286
+ value: 3.14,
287
+ })
288
+ expect(result.success).toBe(false)
289
+ })
290
+
291
+ it('rejects object with NaN parameter values', () => {
292
+ const result = paramsSchema.safeParse({
293
+ value: NaN,
294
+ })
295
+ expect(result.success).toBe(false)
296
+ })
297
+
298
+ it('rejects object with Infinity parameter values', () => {
299
+ const result = paramsSchema.safeParse({
300
+ value: Infinity,
301
+ })
302
+ expect(result.success).toBe(false)
303
+ })
304
+
305
+ it('rejects object with object parameter values', () => {
306
+ const result = paramsSchema.safeParse({
307
+ nested: { key: 'value' },
308
+ })
309
+ expect(result.success).toBe(false)
310
+ })
311
+
312
+ it('rejects object with nested array parameter values', () => {
313
+ const result = paramsSchema.safeParse({
314
+ nested: [
315
+ [1, 2],
316
+ [3, 4],
317
+ ],
318
+ })
319
+ expect(result.success).toBe(false)
320
+ })
321
+
322
+ it('rejects object with arrays containing invalid values', () => {
323
+ const result = paramsSchema.safeParse({
324
+ tags: ['valid', null, 'also valid'],
325
+ })
326
+ expect(result.success).toBe(false)
327
+ })
328
+
329
+ it('rejects object with arrays containing objects', () => {
330
+ const result = paramsSchema.safeParse({
331
+ items: [1, { key: 'value' }, 3],
332
+ })
333
+ expect(result.success).toBe(false)
334
+ })
335
+
336
+ it('rejects object with arrays containing floating point numbers', () => {
337
+ const result = paramsSchema.safeParse({
338
+ values: [1, 2.5, 3],
339
+ })
340
+ expect(result.success).toBe(false)
341
+ })
342
+
343
+ it('rejects when one parameter is invalid', () => {
344
+ const result = paramsSchema.safeParse({
345
+ valid1: 'string',
346
+ valid2: 42,
347
+ invalid: null,
348
+ valid3: true,
349
+ })
350
+ expect(result.success).toBe(false)
351
+ })
352
+
353
+ describe('edge cases', () => {
354
+ it('validates single parameter', () => {
355
+ const result = paramsSchema.safeParse({
356
+ single: 'value',
357
+ })
358
+ expect(result.success).toBe(true)
359
+ })
360
+
361
+ it('validates many parameters', () => {
362
+ const manyParams: Record<string, string> = {}
363
+ for (let i = 0; i < 100; i++) {
364
+ manyParams[`param${i}`] = `value${i}`
365
+ }
366
+ const result = paramsSchema.safeParse(manyParams)
367
+ expect(result.success).toBe(true)
368
+ })
369
+
370
+ it('validates parameters with long string values', () => {
371
+ const result = paramsSchema.safeParse({
372
+ longString: 'a'.repeat(1000),
373
+ })
374
+ expect(result.success).toBe(true)
375
+ })
376
+
377
+ it('validates parameters with large integer values', () => {
378
+ const result = paramsSchema.safeParse({
379
+ largeInt: 2147483647,
380
+ negativeInt: -2147483648,
381
+ })
382
+ expect(result.success).toBe(true)
383
+ })
384
+
385
+ it('validates parameters with long arrays', () => {
386
+ const result = paramsSchema.safeParse({
387
+ longArray: Array.from({ length: 100 }, (_, i) => i),
388
+ })
389
+ expect(result.success).toBe(true)
390
+ })
391
+
392
+ it('validates parameters with special characters in keys', () => {
393
+ const result = paramsSchema.safeParse({
394
+ 'key-with-dashes': 'value',
395
+ key_with_underscores: 'value',
396
+ 'key.with.dots': 'value',
397
+ })
398
+ expect(result.success).toBe(true)
399
+ })
400
+
401
+ it('preserves original object when no transformations occur', () => {
402
+ const input = {
403
+ name: 'Alice',
404
+ age: 30,
405
+ tags: ['user'],
406
+ }
407
+ const result = paramsSchema.safeParse(input)
408
+
409
+ if (result.success) {
410
+ expect(result.value).toBe(input)
411
+ } else {
412
+ throw new Error('Expected validation to succeed')
413
+ }
414
+ })
415
+ })
416
+ })
@@ -0,0 +1,237 @@
1
+ import { ArraySchema } from './array.js'
2
+ import { IntegerSchema } from './integer.js'
3
+ import { ObjectSchema } from './object.js'
4
+ import { StringSchema } from './string.js'
5
+
6
+ describe('ArraySchema', () => {
7
+ it('validates arrays with string items', () => {
8
+ const schema = new ArraySchema(new StringSchema({}))
9
+ const result = schema.safeParse(['hello', 'world'])
10
+ expect(result.success).toBe(true)
11
+ })
12
+
13
+ it('validates arrays with integer items', () => {
14
+ const schema = new ArraySchema(new IntegerSchema({}))
15
+ const result = schema.safeParse([1, 2, 3])
16
+ expect(result.success).toBe(true)
17
+ })
18
+
19
+ it('validates arrays with object items', () => {
20
+ const schema = new ArraySchema(
21
+ new ObjectSchema({
22
+ name: new StringSchema({}),
23
+ age: new IntegerSchema({}),
24
+ }),
25
+ )
26
+ const result = schema.safeParse([
27
+ { name: 'Alice', age: 30 },
28
+ { name: 'Bob', age: 25 },
29
+ ])
30
+ expect(result.success).toBe(true)
31
+ })
32
+
33
+ it('validates empty arrays', () => {
34
+ const schema = new ArraySchema(new StringSchema({}))
35
+ const result = schema.safeParse([])
36
+ expect(result.success).toBe(true)
37
+ })
38
+
39
+ it('rejects non-array values', () => {
40
+ const schema = new ArraySchema(new StringSchema({}))
41
+ const result = schema.safeParse('not an array')
42
+ expect(result.success).toBe(false)
43
+ })
44
+
45
+ it('rejects null values', () => {
46
+ const schema = new ArraySchema(new StringSchema({}))
47
+ const result = schema.safeParse(null)
48
+ expect(result.success).toBe(false)
49
+ })
50
+
51
+ it('rejects undefined values', () => {
52
+ const schema = new ArraySchema(new StringSchema({}))
53
+ const result = schema.safeParse(undefined)
54
+ expect(result.success).toBe(false)
55
+ })
56
+
57
+ it('rejects objects that look like arrays', () => {
58
+ const schema = new ArraySchema(new StringSchema({}))
59
+ const result = schema.safeParse({ 0: 'a', 1: 'b', length: 2 })
60
+ expect(result.success).toBe(false)
61
+ })
62
+
63
+ it('rejects arrays with invalid items', () => {
64
+ const schema = new ArraySchema(new IntegerSchema({}))
65
+ const result = schema.safeParse([1, 2, 'three'])
66
+ expect(result.success).toBe(false)
67
+ })
68
+
69
+ it('rejects arrays with some invalid items', () => {
70
+ const schema = new ArraySchema(new StringSchema({}))
71
+ const result = schema.safeParse(['valid', null, 'also valid'])
72
+ expect(result.success).toBe(false)
73
+ })
74
+
75
+ describe('minLength constraint', () => {
76
+ it('validates arrays meeting minLength', () => {
77
+ const schema = new ArraySchema(new StringSchema({}), { minLength: 2 })
78
+ const result = schema.safeParse(['a', 'b'])
79
+ expect(result.success).toBe(true)
80
+ })
81
+
82
+ it('validates arrays exceeding minLength', () => {
83
+ const schema = new ArraySchema(new StringSchema({}), { minLength: 2 })
84
+ const result = schema.safeParse(['a', 'b', 'c'])
85
+ expect(result.success).toBe(true)
86
+ })
87
+
88
+ it('rejects arrays below minLength', () => {
89
+ const schema = new ArraySchema(new StringSchema({}), { minLength: 3 })
90
+ const result = schema.safeParse(['a', 'b'])
91
+ expect(result.success).toBe(false)
92
+ })
93
+
94
+ it('rejects empty arrays when minLength is set', () => {
95
+ const schema = new ArraySchema(new StringSchema({}), { minLength: 1 })
96
+ const result = schema.safeParse([])
97
+ expect(result.success).toBe(false)
98
+ })
99
+
100
+ it('validates empty arrays when minLength is 0', () => {
101
+ const schema = new ArraySchema(new StringSchema({}), { minLength: 0 })
102
+ const result = schema.safeParse([])
103
+ expect(result.success).toBe(true)
104
+ })
105
+ })
106
+
107
+ describe('maxLength constraint', () => {
108
+ it('validates arrays meeting maxLength', () => {
109
+ const schema = new ArraySchema(new StringSchema({}), { maxLength: 3 })
110
+ const result = schema.safeParse(['a', 'b', 'c'])
111
+ expect(result.success).toBe(true)
112
+ })
113
+
114
+ it('validates arrays below maxLength', () => {
115
+ const schema = new ArraySchema(new StringSchema({}), { maxLength: 3 })
116
+ const result = schema.safeParse(['a', 'b'])
117
+ expect(result.success).toBe(true)
118
+ })
119
+
120
+ it('rejects arrays exceeding maxLength', () => {
121
+ const schema = new ArraySchema(new StringSchema({}), { maxLength: 2 })
122
+ const result = schema.safeParse(['a', 'b', 'c'])
123
+ expect(result.success).toBe(false)
124
+ })
125
+
126
+ it('validates empty arrays with maxLength', () => {
127
+ const schema = new ArraySchema(new StringSchema({}), { maxLength: 5 })
128
+ const result = schema.safeParse([])
129
+ expect(result.success).toBe(true)
130
+ })
131
+
132
+ it('rejects empty arrays when maxLength is 0', () => {
133
+ const schema = new ArraySchema(new StringSchema({}), { maxLength: 0 })
134
+ const result = schema.safeParse(['a'])
135
+ expect(result.success).toBe(false)
136
+ })
137
+
138
+ it('validates empty arrays when maxLength is 0', () => {
139
+ const schema = new ArraySchema(new StringSchema({}), { maxLength: 0 })
140
+ const result = schema.safeParse([])
141
+ expect(result.success).toBe(true)
142
+ })
143
+ })
144
+
145
+ describe('minLength and maxLength together', () => {
146
+ it('validates arrays within range', () => {
147
+ const schema = new ArraySchema(new StringSchema({}), {
148
+ minLength: 2,
149
+ maxLength: 4,
150
+ })
151
+ const result = schema.safeParse(['a', 'b', 'c'])
152
+ expect(result.success).toBe(true)
153
+ })
154
+
155
+ it('validates arrays at min boundary', () => {
156
+ const schema = new ArraySchema(new StringSchema({}), {
157
+ minLength: 2,
158
+ maxLength: 4,
159
+ })
160
+ const result = schema.safeParse(['a', 'b'])
161
+ expect(result.success).toBe(true)
162
+ })
163
+
164
+ it('validates arrays at max boundary', () => {
165
+ const schema = new ArraySchema(new StringSchema({}), {
166
+ minLength: 2,
167
+ maxLength: 4,
168
+ })
169
+ const result = schema.safeParse(['a', 'b', 'c', 'd'])
170
+ expect(result.success).toBe(true)
171
+ })
172
+
173
+ it('rejects arrays below minLength', () => {
174
+ const schema = new ArraySchema(new StringSchema({}), {
175
+ minLength: 2,
176
+ maxLength: 4,
177
+ })
178
+ const result = schema.safeParse(['a'])
179
+ expect(result.success).toBe(false)
180
+ })
181
+
182
+ it('rejects arrays above maxLength', () => {
183
+ const schema = new ArraySchema(new StringSchema({}), {
184
+ minLength: 2,
185
+ maxLength: 4,
186
+ })
187
+ const result = schema.safeParse(['a', 'b', 'c', 'd', 'e'])
188
+ expect(result.success).toBe(false)
189
+ })
190
+
191
+ it('validates single-length range', () => {
192
+ const schema = new ArraySchema(new StringSchema({}), {
193
+ minLength: 3,
194
+ maxLength: 3,
195
+ })
196
+ const result = schema.safeParse(['a', 'b', 'c'])
197
+ expect(result.success).toBe(true)
198
+ })
199
+
200
+ it('rejects arrays not matching exact length', () => {
201
+ const schema = new ArraySchema(new StringSchema({}), {
202
+ minLength: 3,
203
+ maxLength: 3,
204
+ })
205
+ const result = schema.safeParse(['a', 'b'])
206
+ expect(result.success).toBe(false)
207
+ })
208
+ })
209
+
210
+ describe('nested arrays', () => {
211
+ it('validates arrays of arrays', () => {
212
+ const schema = new ArraySchema(new ArraySchema(new StringSchema({})))
213
+ const result = schema.safeParse([
214
+ ['a', 'b'],
215
+ ['c', 'd'],
216
+ ])
217
+ expect(result.success).toBe(true)
218
+ })
219
+
220
+ it('rejects invalid nested arrays', () => {
221
+ const schema = new ArraySchema(new ArraySchema(new IntegerSchema({})))
222
+ const result = schema.safeParse([
223
+ [1, 2],
224
+ [3, 'four'],
225
+ ])
226
+ expect(result.success).toBe(false)
227
+ })
228
+
229
+ it('validates deeply nested arrays', () => {
230
+ const schema = new ArraySchema(
231
+ new ArraySchema(new ArraySchema(new IntegerSchema({}))),
232
+ )
233
+ const result = schema.safeParse([[[1, 2], [3]], [[4, 5, 6]]])
234
+ expect(result.success).toBe(true)
235
+ })
236
+ })
237
+ })
@@ -1,24 +1,30 @@
1
- import { ValidationResult, Validator, ValidatorContext } from '../validation.js'
1
+ import {
2
+ Infer,
3
+ Schema,
4
+ ValidationResult,
5
+ Validator,
6
+ ValidatorContext,
7
+ } from '../validation.js'
2
8
 
3
9
  export type ArraySchemaOptions = {
4
10
  minLength?: number
5
11
  maxLength?: number
6
12
  }
7
13
 
8
- export class ArraySchema<Item = any> extends Validator<Array<Item>> {
9
- readonly lexiconType = 'array' as const
10
-
14
+ export class ArraySchema<const T extends Validator> extends Schema<
15
+ Array<Infer<T>>
16
+ > {
11
17
  constructor(
12
- readonly items: Validator<Item>,
13
- readonly options: ArraySchemaOptions,
18
+ readonly itemsSchema: T,
19
+ readonly options: ArraySchemaOptions = {},
14
20
  ) {
15
21
  super()
16
22
  }
17
23
 
18
- override validateInContext(
24
+ validateInContext(
19
25
  input: unknown,
20
26
  ctx: ValidatorContext,
21
- ): ValidationResult<Array<Item>> {
27
+ ): ValidationResult<Array<Infer<T>>> {
22
28
  if (!Array.isArray(input)) {
23
29
  return ctx.issueInvalidType(input, 'array')
24
30
  }
@@ -33,10 +39,10 @@ export class ArraySchema<Item = any> extends Validator<Array<Item>> {
33
39
  return ctx.issueTooBig(input, 'array', maxLength, input.length)
34
40
  }
35
41
 
36
- let copy: undefined | Array<Item>
42
+ let copy: undefined | Array<Infer<T>>
37
43
 
38
44
  for (let i = 0; i < input.length; i++) {
39
- const result = ctx.validateChild(input, i, this.items)
45
+ const result = ctx.validateChild(input, i, this.itemsSchema)
40
46
  if (!result.success) return result
41
47
 
42
48
  if (result.value !== input[i]) {
@@ -46,6 +52,6 @@ export class ArraySchema<Item = any> extends Validator<Array<Item>> {
46
52
  }
47
53
  }
48
54
 
49
- return ctx.success(copy ?? input) as ValidationResult<Array<Item>>
55
+ return ctx.success(copy ?? input) as ValidationResult<Array<Infer<T>>>
50
56
  }
51
57
  }