@atproto/lex-schema 0.0.9 → 0.0.11

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 (280) hide show
  1. package/CHANGELOG.md +41 -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-error.d.ts +1 -1
  16. package/dist/core/validation-issue.js +1 -1
  17. package/dist/core/validation-issue.js.map +1 -1
  18. package/dist/core/validator.d.ts +53 -32
  19. package/dist/core/validator.d.ts.map +1 -1
  20. package/dist/core/validator.js +18 -22
  21. package/dist/core/validator.js.map +1 -1
  22. package/dist/external.d.ts +0 -85
  23. package/dist/external.d.ts.map +1 -1
  24. package/dist/external.js +0 -164
  25. package/dist/external.js.map +1 -1
  26. package/dist/helpers.d.ts +13 -5
  27. package/dist/helpers.d.ts.map +1 -1
  28. package/dist/helpers.js +4 -4
  29. package/dist/helpers.js.map +1 -1
  30. package/dist/schema/array.d.ts +9 -5
  31. package/dist/schema/array.d.ts.map +1 -1
  32. package/dist/schema/array.js +14 -5
  33. package/dist/schema/array.js.map +1 -1
  34. package/dist/schema/blob.d.ts +9 -7
  35. package/dist/schema/blob.d.ts.map +1 -1
  36. package/dist/schema/blob.js +9 -5
  37. package/dist/schema/blob.js.map +1 -1
  38. package/dist/schema/boolean.d.ts +3 -7
  39. package/dist/schema/boolean.d.ts.map +1 -1
  40. package/dist/schema/boolean.js +6 -7
  41. package/dist/schema/boolean.js.map +1 -1
  42. package/dist/schema/bytes.d.ts +3 -2
  43. package/dist/schema/bytes.d.ts.map +1 -1
  44. package/dist/schema/bytes.js +7 -3
  45. package/dist/schema/bytes.js.map +1 -1
  46. package/dist/schema/cid.d.ts +7 -7
  47. package/dist/schema/cid.d.ts.map +1 -1
  48. package/dist/schema/cid.js +5 -1
  49. package/dist/schema/cid.js.map +1 -1
  50. package/dist/schema/custom.d.ts +6 -5
  51. package/dist/schema/custom.d.ts.map +1 -1
  52. package/dist/schema/custom.js +10 -4
  53. package/dist/schema/custom.js.map +1 -1
  54. package/dist/schema/dict.d.ts +8 -8
  55. package/dist/schema/dict.d.ts.map +1 -1
  56. package/dist/schema/dict.js +11 -2
  57. package/dist/schema/dict.js.map +1 -1
  58. package/dist/schema/discriminated-union.d.ts +21 -14
  59. package/dist/schema/discriminated-union.d.ts.map +1 -1
  60. package/dist/schema/discriminated-union.js +7 -0
  61. package/dist/schema/discriminated-union.js.map +1 -1
  62. package/dist/schema/enum.d.ts +7 -9
  63. package/dist/schema/enum.d.ts.map +1 -1
  64. package/dist/schema/enum.js +8 -4
  65. package/dist/schema/enum.js.map +1 -1
  66. package/dist/schema/integer.d.ts +5 -5
  67. package/dist/schema/integer.d.ts.map +1 -1
  68. package/dist/schema/integer.js +9 -5
  69. package/dist/schema/integer.js.map +1 -1
  70. package/dist/schema/intersection.d.ts +4 -4
  71. package/dist/schema/intersection.d.ts.map +1 -1
  72. package/dist/schema/intersection.js +5 -0
  73. package/dist/schema/intersection.js.map +1 -1
  74. package/dist/schema/literal.d.ts +6 -9
  75. package/dist/schema/literal.d.ts.map +1 -1
  76. package/dist/schema/literal.js +7 -4
  77. package/dist/schema/literal.js.map +1 -1
  78. package/dist/schema/never.d.ts +3 -2
  79. package/dist/schema/never.d.ts.map +1 -1
  80. package/dist/schema/never.js +5 -1
  81. package/dist/schema/never.js.map +1 -1
  82. package/dist/schema/null.d.ts +4 -3
  83. package/dist/schema/null.d.ts.map +1 -1
  84. package/dist/schema/null.js +6 -4
  85. package/dist/schema/null.js.map +1 -1
  86. package/dist/schema/nullable.d.ts +6 -5
  87. package/dist/schema/nullable.d.ts.map +1 -1
  88. package/dist/schema/nullable.js +9 -5
  89. package/dist/schema/nullable.js.map +1 -1
  90. package/dist/schema/object.d.ts +10 -8
  91. package/dist/schema/object.d.ts.map +1 -1
  92. package/dist/schema/object.js +11 -3
  93. package/dist/schema/object.js.map +1 -1
  94. package/dist/schema/optional.d.ts +7 -5
  95. package/dist/schema/optional.d.ts.map +1 -1
  96. package/dist/schema/optional.js +14 -6
  97. package/dist/schema/optional.js.map +1 -1
  98. package/dist/schema/params.d.ts +26 -13
  99. package/dist/schema/params.d.ts.map +1 -1
  100. package/dist/schema/params.js +47 -25
  101. package/dist/schema/params.js.map +1 -1
  102. package/dist/schema/payload.d.ts +12 -9
  103. package/dist/schema/payload.d.ts.map +1 -1
  104. package/dist/schema/payload.js +11 -0
  105. package/dist/schema/payload.js.map +1 -1
  106. package/dist/schema/permission-set.d.ts +1 -0
  107. package/dist/schema/permission-set.d.ts.map +1 -1
  108. package/dist/schema/permission-set.js +5 -0
  109. package/dist/schema/permission-set.js.map +1 -1
  110. package/dist/schema/permission.d.ts +6 -5
  111. package/dist/schema/permission.d.ts.map +1 -1
  112. package/dist/schema/permission.js +5 -0
  113. package/dist/schema/permission.js.map +1 -1
  114. package/dist/schema/procedure.d.ts +2 -1
  115. package/dist/schema/procedure.d.ts.map +1 -1
  116. package/dist/schema/procedure.js +5 -0
  117. package/dist/schema/procedure.js.map +1 -1
  118. package/dist/schema/query.d.ts +2 -1
  119. package/dist/schema/query.d.ts.map +1 -1
  120. package/dist/schema/query.js +5 -0
  121. package/dist/schema/query.js.map +1 -1
  122. package/dist/schema/record.d.ts +48 -30
  123. package/dist/schema/record.d.ts.map +1 -1
  124. package/dist/schema/record.js +12 -9
  125. package/dist/schema/record.js.map +1 -1
  126. package/dist/schema/ref.d.ts +9 -6
  127. package/dist/schema/ref.d.ts.map +1 -1
  128. package/dist/schema/ref.js +9 -16
  129. package/dist/schema/ref.js.map +1 -1
  130. package/dist/schema/refine.d.ts +4 -4
  131. package/dist/schema/refine.d.ts.map +1 -1
  132. package/dist/schema/refine.js.map +1 -1
  133. package/dist/schema/regexp.d.ts +4 -3
  134. package/dist/schema/regexp.d.ts.map +1 -1
  135. package/dist/schema/regexp.js +5 -0
  136. package/dist/schema/regexp.js.map +1 -1
  137. package/dist/schema/string.d.ts +7 -8
  138. package/dist/schema/string.d.ts.map +1 -1
  139. package/dist/schema/string.js +13 -19
  140. package/dist/schema/string.js.map +1 -1
  141. package/dist/schema/subscription.d.ts +2 -1
  142. package/dist/schema/subscription.d.ts.map +1 -1
  143. package/dist/schema/subscription.js +5 -0
  144. package/dist/schema/subscription.js.map +1 -1
  145. package/dist/schema/token.d.ts +6 -5
  146. package/dist/schema/token.d.ts.map +1 -1
  147. package/dist/schema/token.js +5 -0
  148. package/dist/schema/token.js.map +1 -1
  149. package/dist/schema/typed-object.d.ts +43 -26
  150. package/dist/schema/typed-object.d.ts.map +1 -1
  151. package/dist/schema/typed-object.js +6 -3
  152. package/dist/schema/typed-object.js.map +1 -1
  153. package/dist/schema/typed-ref.d.ts +16 -25
  154. package/dist/schema/typed-ref.d.ts.map +1 -1
  155. package/dist/schema/typed-ref.js +7 -17
  156. package/dist/schema/typed-ref.js.map +1 -1
  157. package/dist/schema/typed-union.d.ts +9 -21
  158. package/dist/schema/typed-union.d.ts.map +1 -1
  159. package/dist/schema/typed-union.js +15 -11
  160. package/dist/schema/typed-union.js.map +1 -1
  161. package/dist/schema/union.d.ts +6 -6
  162. package/dist/schema/union.d.ts.map +1 -1
  163. package/dist/schema/union.js +7 -5
  164. package/dist/schema/union.js.map +1 -1
  165. package/dist/schema/unknown-object.d.ts +5 -4
  166. package/dist/schema/unknown-object.d.ts.map +1 -1
  167. package/dist/schema/unknown-object.js +5 -1
  168. package/dist/schema/unknown-object.js.map +1 -1
  169. package/dist/schema/unknown.d.ts +3 -2
  170. package/dist/schema/unknown.d.ts.map +1 -1
  171. package/dist/schema/unknown.js +5 -1
  172. package/dist/schema/unknown.js.map +1 -1
  173. package/dist/schema/with-default.d.ts +9 -0
  174. package/dist/schema/with-default.d.ts.map +1 -0
  175. package/dist/schema/with-default.js +27 -0
  176. package/dist/schema/with-default.js.map +1 -0
  177. package/dist/schema.d.ts +2 -2
  178. package/dist/schema.d.ts.map +1 -1
  179. package/dist/schema.js +2 -4
  180. package/dist/schema.js.map +1 -1
  181. package/dist/util/assertion-util.d.ts +0 -6
  182. package/dist/util/assertion-util.d.ts.map +1 -1
  183. package/dist/util/assertion-util.js +0 -28
  184. package/dist/util/assertion-util.js.map +1 -1
  185. package/dist/util/memoize.d.ts +2 -2
  186. package/dist/util/memoize.d.ts.map +1 -1
  187. package/dist/util/memoize.js +23 -39
  188. package/dist/util/memoize.js.map +1 -1
  189. package/package.json +3 -3
  190. package/src/core/$type.test.ts +20 -0
  191. package/src/core/$type.ts +30 -0
  192. package/src/core/schema.ts +86 -38
  193. package/src/core/string-format.ts +119 -158
  194. package/src/core/validation-issue.ts +1 -1
  195. package/src/core/validator.ts +93 -53
  196. package/src/external.ts +0 -404
  197. package/src/helpers.test.ts +22 -21
  198. package/src/helpers.ts +19 -14
  199. package/src/schema/array.test.ts +38 -40
  200. package/src/schema/array.ts +35 -13
  201. package/src/schema/blob.test.ts +21 -21
  202. package/src/schema/blob.ts +19 -17
  203. package/src/schema/boolean.test.ts +9 -8
  204. package/src/schema/boolean.ts +7 -13
  205. package/src/schema/bytes.test.ts +13 -13
  206. package/src/schema/bytes.ts +13 -8
  207. package/src/schema/cid.test.ts +3 -3
  208. package/src/schema/cid.ts +13 -12
  209. package/src/schema/custom.test.ts +26 -26
  210. package/src/schema/custom.ts +20 -13
  211. package/src/schema/dict.test.ts +21 -39
  212. package/src/schema/dict.ts +28 -19
  213. package/src/schema/discriminated-union.test.ts +128 -128
  214. package/src/schema/discriminated-union.ts +45 -26
  215. package/src/schema/enum.test.ts +17 -16
  216. package/src/schema/enum.ts +16 -16
  217. package/src/schema/integer.test.ts +22 -21
  218. package/src/schema/integer.ts +12 -9
  219. package/src/schema/intersection.test.ts +10 -10
  220. package/src/schema/intersection.ts +17 -14
  221. package/src/schema/literal.test.ts +35 -34
  222. package/src/schema/literal.ts +12 -15
  223. package/src/schema/never.test.ts +5 -5
  224. package/src/schema/never.ts +7 -2
  225. package/src/schema/null.test.ts +3 -3
  226. package/src/schema/null.ts +9 -9
  227. package/src/schema/nullable.test.ts +31 -42
  228. package/src/schema/nullable.ts +17 -9
  229. package/src/schema/object.test.ts +10 -12
  230. package/src/schema/object.ts +27 -18
  231. package/src/schema/optional.test.ts +21 -28
  232. package/src/schema/optional.ts +27 -10
  233. package/src/schema/params.test.ts +471 -47
  234. package/src/schema/params.ts +74 -38
  235. package/src/schema/payload.test.ts +150 -156
  236. package/src/schema/payload.ts +35 -19
  237. package/src/schema/permission-set.test.ts +206 -273
  238. package/src/schema/permission-set.ts +8 -0
  239. package/src/schema/permission.test.ts +177 -177
  240. package/src/schema/permission.ts +13 -5
  241. package/src/schema/procedure.test.ts +183 -242
  242. package/src/schema/procedure.ts +18 -5
  243. package/src/schema/query.test.ts +186 -200
  244. package/src/schema/query.ts +16 -4
  245. package/src/schema/record.test.ts +121 -101
  246. package/src/schema/record.ts +74 -40
  247. package/src/schema/ref.test.ts +101 -118
  248. package/src/schema/ref.ts +33 -28
  249. package/src/schema/refine.test.ts +28 -28
  250. package/src/schema/refine.ts +23 -20
  251. package/src/schema/regexp.test.ts +29 -33
  252. package/src/schema/regexp.ts +11 -7
  253. package/src/schema/string.test.ts +35 -35
  254. package/src/schema/string.ts +24 -33
  255. package/src/schema/subscription.test.ts +259 -387
  256. package/src/schema/subscription.ts +16 -4
  257. package/src/schema/token.test.ts +47 -324
  258. package/src/schema/token.ts +14 -7
  259. package/src/schema/typed-object.test.ts +98 -81
  260. package/src/schema/typed-object.ts +68 -33
  261. package/src/schema/typed-ref.test.ts +206 -234
  262. package/src/schema/typed-ref.ts +40 -42
  263. package/src/schema/typed-union.test.ts +40 -64
  264. package/src/schema/typed-union.ts +36 -58
  265. package/src/schema/union.test.ts +17 -27
  266. package/src/schema/union.ts +20 -16
  267. package/src/schema/unknown-object.test.ts +8 -8
  268. package/src/schema/unknown-object.ts +9 -7
  269. package/src/schema/unknown.test.ts +4 -4
  270. package/src/schema/unknown.ts +7 -5
  271. package/src/schema/with-default.ts +35 -0
  272. package/src/schema.ts +2 -6
  273. package/src/util/assertion-util.ts +0 -39
  274. package/src/util/memoize.ts +26 -46
  275. package/dist/schema/_parameters.d.ts +0 -17
  276. package/dist/schema/_parameters.d.ts.map +0 -1
  277. package/dist/schema/_parameters.js +0 -20
  278. package/dist/schema/_parameters.js.map +0 -1
  279. package/src/schema/_parameters.test.ts +0 -417
  280. package/src/schema/_parameters.ts +0 -26
@@ -1,10 +1,11 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { StringSchema } from './string.js'
3
- import { TokenSchema } from './token.js'
2
+ import { string } from './string.js'
3
+ import { token } from './token.js'
4
+ import { withDefault } from './with-default.js'
4
5
 
5
6
  describe('StringSchema', () => {
6
7
  describe('basic validation', () => {
7
- const schema = new StringSchema({})
8
+ const schema = string()
8
9
 
9
10
  it('validates plain strings', () => {
10
11
  const result = schema.safeParse('hello world')
@@ -49,7 +50,7 @@ describe('StringSchema', () => {
49
50
 
50
51
  describe('default values', () => {
51
52
  it('uses default value when no input provided', () => {
52
- const schema = new StringSchema({ default: 'default value' })
53
+ const schema = withDefault(string(), 'default value')
53
54
  const result = schema.safeParse(undefined)
54
55
  expect(result.success).toBe(true)
55
56
  if (result.success) {
@@ -58,14 +59,14 @@ describe('StringSchema', () => {
58
59
  })
59
60
 
60
61
  it('validates default value against constraints', () => {
61
- const schema = new StringSchema({ default: 'hi', minLength: 5 })
62
+ const schema = string({ default: 'hi', minLength: 5 })
62
63
  const result = schema.safeParse(undefined)
63
64
  expect(result.success).toBe(false)
64
65
  })
65
66
  })
66
67
 
67
68
  describe('minLength constraint', () => {
68
- const schema = new StringSchema({ minLength: 5 })
69
+ const schema = string({ minLength: 5 })
69
70
 
70
71
  it('accepts strings meeting minimum length', () => {
71
72
  const result = schema.safeParse('hello')
@@ -89,7 +90,7 @@ describe('StringSchema', () => {
89
90
  })
90
91
 
91
92
  describe('maxLength constraint', () => {
92
- const schema = new StringSchema({ maxLength: 10 })
93
+ const schema = string({ maxLength: 10 })
93
94
 
94
95
  it('accepts strings meeting maximum length', () => {
95
96
  const result = schema.safeParse('1234567890')
@@ -113,20 +114,20 @@ describe('StringSchema', () => {
113
114
 
114
115
  it('correctly handles UTF-8 multi-byte characters', () => {
115
116
  // Emoji takes 4 bytes in UTF-8
116
- const schema = new StringSchema({ maxLength: 4 })
117
+ const schema = string({ maxLength: 4 })
117
118
  const result = schema.safeParse('😀')
118
119
  expect(result.success).toBe(true)
119
120
  })
120
121
 
121
122
  it('rejects when multi-byte characters exceed maxLength', () => {
122
- const schema = new StringSchema({ maxLength: 3 })
123
+ const schema = string({ maxLength: 3 })
123
124
  const result = schema.safeParse('😀')
124
125
  expect(result.success).toBe(false)
125
126
  })
126
127
  })
127
128
 
128
129
  describe('combined min and max length', () => {
129
- const schema = new StringSchema({ minLength: 3, maxLength: 10 })
130
+ const schema = string({ minLength: 3, maxLength: 10 })
130
131
 
131
132
  it('accepts strings within range', () => {
132
133
  const result = schema.safeParse('hello')
@@ -155,7 +156,7 @@ describe('StringSchema', () => {
155
156
  })
156
157
 
157
158
  describe('minGraphemes constraint', () => {
158
- const schema = new StringSchema({ minGraphemes: 3 })
159
+ const schema = string({ minGraphemes: 3 })
159
160
 
160
161
  it('accepts strings meeting minimum graphemes', () => {
161
162
  const result = schema.safeParse('abc')
@@ -184,7 +185,7 @@ describe('StringSchema', () => {
184
185
  })
185
186
 
186
187
  describe('maxGraphemes constraint', () => {
187
- const schema = new StringSchema({ maxGraphemes: 5 })
188
+ const schema = string({ maxGraphemes: 5 })
188
189
 
189
190
  it('accepts strings meeting maximum graphemes', () => {
190
191
  const result = schema.safeParse('hello')
@@ -213,7 +214,7 @@ describe('StringSchema', () => {
213
214
  })
214
215
 
215
216
  describe('combined grapheme constraints', () => {
216
- const schema = new StringSchema({ minGraphemes: 2, maxGraphemes: 5 })
217
+ const schema = string({ minGraphemes: 2, maxGraphemes: 5 })
217
218
 
218
219
  it('accepts strings within grapheme range', () => {
219
220
  const result = schema.safeParse('hello')
@@ -242,7 +243,7 @@ describe('StringSchema', () => {
242
243
  })
243
244
 
244
245
  describe('format: datetime', () => {
245
- const schema = new StringSchema({ format: 'datetime' })
246
+ const schema = string({ format: 'datetime' })
246
247
 
247
248
  it('accepts valid ISO datetime strings', () => {
248
249
  const result = schema.safeParse('2023-12-25T12:00:00Z')
@@ -266,7 +267,7 @@ describe('StringSchema', () => {
266
267
  })
267
268
 
268
269
  describe('format: uri', () => {
269
- const schema = new StringSchema({ format: 'uri' })
270
+ const schema = string({ format: 'uri' })
270
271
 
271
272
  it('accepts valid HTTP URIs', () => {
272
273
  const result = schema.safeParse('https://example.com')
@@ -295,7 +296,7 @@ describe('StringSchema', () => {
295
296
  })
296
297
 
297
298
  describe('format: at-uri', () => {
298
- const schema = new StringSchema({ format: 'at-uri' })
299
+ const schema = string({ format: 'at-uri' })
299
300
 
300
301
  it('accepts valid AT URI', () => {
301
302
  const result = schema.safeParse(
@@ -316,7 +317,7 @@ describe('StringSchema', () => {
316
317
  })
317
318
 
318
319
  describe('format: did', () => {
319
- const schema = new StringSchema({ format: 'did' })
320
+ const schema = string({ format: 'did' })
320
321
 
321
322
  it('accepts valid DID with plc method', () => {
322
323
  const result = schema.safeParse('did:plc:abc123')
@@ -340,7 +341,7 @@ describe('StringSchema', () => {
340
341
  })
341
342
 
342
343
  describe('format: handle', () => {
343
- const schema = new StringSchema({ format: 'handle' })
344
+ const schema = string({ format: 'handle' })
344
345
 
345
346
  it('accepts valid handle', () => {
346
347
  const result = schema.safeParse('user.bsky.social')
@@ -364,7 +365,7 @@ describe('StringSchema', () => {
364
365
  })
365
366
 
366
367
  describe('format: at-identifier', () => {
367
- const schema = new StringSchema({ format: 'at-identifier' })
368
+ const schema = string({ format: 'at-identifier' })
368
369
 
369
370
  it('accepts valid DID as at-identifier', () => {
370
371
  const result = schema.safeParse('did:plc:abc123')
@@ -383,7 +384,7 @@ describe('StringSchema', () => {
383
384
  })
384
385
 
385
386
  describe('format: nsid', () => {
386
- const schema = new StringSchema({ format: 'nsid' })
387
+ const schema = string({ format: 'nsid' })
387
388
 
388
389
  it('accepts valid NSID', () => {
389
390
  const result = schema.safeParse('app.bsky.feed.post')
@@ -407,7 +408,7 @@ describe('StringSchema', () => {
407
408
  })
408
409
 
409
410
  describe('format: cid', () => {
410
- const schema = new StringSchema({ format: 'cid' })
411
+ const schema = string({ format: 'cid' })
411
412
 
412
413
  it('accepts valid CID v1', () => {
413
414
  const result = schema.safeParse(
@@ -428,7 +429,7 @@ describe('StringSchema', () => {
428
429
  })
429
430
 
430
431
  describe('format: language', () => {
431
- const schema = new StringSchema({ format: 'language' })
432
+ const schema = string({ format: 'language' })
432
433
 
433
434
  it('accepts valid BCP 47 language code', () => {
434
435
  const result = schema.safeParse('en')
@@ -452,7 +453,7 @@ describe('StringSchema', () => {
452
453
  })
453
454
 
454
455
  describe('format: tid', () => {
455
- const schema = new StringSchema({ format: 'tid' })
456
+ const schema = string({ format: 'tid' })
456
457
 
457
458
  it('accepts valid TID', () => {
458
459
  const result = schema.safeParse('3jzfcijpj2z2a')
@@ -471,7 +472,7 @@ describe('StringSchema', () => {
471
472
  })
472
473
 
473
474
  describe('format: record-key', () => {
474
- const schema = new StringSchema({ format: 'record-key' })
475
+ const schema = string({ format: 'record-key' })
475
476
 
476
477
  it('accepts valid record key', () => {
477
478
  const result = schema.safeParse('3jzfcijpj2z2a')
@@ -495,7 +496,7 @@ describe('StringSchema', () => {
495
496
  })
496
497
 
497
498
  describe('type coercion', () => {
498
- const schema = new StringSchema({})
499
+ const schema = string()
499
500
 
500
501
  it('coerces Date objects to ISO strings', () => {
501
502
  const date = new Date('2023-12-25T12:00:00Z')
@@ -531,17 +532,16 @@ describe('StringSchema', () => {
531
532
  })
532
533
 
533
534
  it('coerces TokenSchema instances to strings', () => {
534
- const token = new TokenSchema('mytoken')
535
- const result = schema.safeParse(token)
535
+ const result = schema.safeParse(token('my.to.ken', 'main'))
536
536
  expect(result.success).toBe(true)
537
537
  if (result.success) {
538
- expect(result.value).toBe('mytoken')
538
+ expect(result.value).toBe('my.to.ken')
539
539
  }
540
540
  })
541
541
  })
542
542
 
543
543
  describe('combined constraints and format', () => {
544
- const schema = new StringSchema({
544
+ const schema = string({
545
545
  format: 'handle',
546
546
  minLength: 5,
547
547
  maxLength: 50,
@@ -572,39 +572,39 @@ describe('StringSchema', () => {
572
572
 
573
573
  describe('edge cases', () => {
574
574
  it('handles strings with special characters', () => {
575
- const schema = new StringSchema({})
575
+ const schema = string()
576
576
  const result = schema.safeParse('hello\nworld\ttab')
577
577
  expect(result.success).toBe(true)
578
578
  })
579
579
 
580
580
  it('handles strings with unicode characters', () => {
581
- const schema = new StringSchema({})
581
+ const schema = string()
582
582
  const result = schema.safeParse('Hello 世界 🌍')
583
583
  expect(result.success).toBe(true)
584
584
  })
585
585
 
586
586
  it('handles very long strings', () => {
587
- const schema = new StringSchema({ maxLength: 10000 })
587
+ const schema = string({ maxLength: 10000 })
588
588
  const longString = 'a'.repeat(10000)
589
589
  const result = schema.safeParse(longString)
590
590
  expect(result.success).toBe(true)
591
591
  })
592
592
 
593
593
  it('rejects very long strings exceeding maxLength', () => {
594
- const schema = new StringSchema({ maxLength: 100 })
594
+ const schema = string({ maxLength: 100 })
595
595
  const longString = 'a'.repeat(101)
596
596
  const result = schema.safeParse(longString)
597
597
  expect(result.success).toBe(false)
598
598
  })
599
599
 
600
600
  it('handles zero as minLength', () => {
601
- const schema = new StringSchema({ minLength: 0 })
601
+ const schema = string({ minLength: 0 })
602
602
  const result = schema.safeParse('')
603
603
  expect(result.success).toBe(true)
604
604
  })
605
605
 
606
606
  it('handles complex emoji sequences', () => {
607
- const schema = new StringSchema({ maxGraphemes: 5 })
607
+ const schema = string({ maxGraphemes: 5 })
608
608
  // Family emoji is a single grapheme cluster
609
609
  const result = schema.safeParse('👨‍👩‍👧‍👦')
610
610
  expect(result.success).toBe(true)
@@ -3,14 +3,13 @@ import {
3
3
  InferStringFormat,
4
4
  Schema,
5
5
  StringFormat,
6
- ValidationResult,
7
- ValidatorContext,
8
- assertStringFormat,
6
+ ValidationContext,
7
+ isStringFormat,
9
8
  } from '../core.js'
9
+ import { memoizedOptions } from '../util/memoize.js'
10
10
  import { TokenSchema } from './token.js'
11
11
 
12
12
  export type StringSchemaOptions = {
13
- default?: string
14
13
  format?: StringFormat
15
14
  minLength?: number
16
15
  maxLength?: number
@@ -18,26 +17,18 @@ export type StringSchemaOptions = {
18
17
  maxGraphemes?: number
19
18
  }
20
19
 
21
- export type StringSchemaOutput<Options> =
22
- //
23
- Options extends { format: infer F extends StringFormat }
20
+ export class StringSchema<
21
+ const TOptions extends StringSchemaOptions = StringSchemaOptions,
22
+ > extends Schema<
23
+ TOptions extends { format: infer F extends StringFormat }
24
24
  ? InferStringFormat<F>
25
25
  : string
26
-
27
- export class StringSchema<
28
- const Options extends StringSchemaOptions = any,
29
- > extends Schema<StringSchemaOutput<Options>> {
30
- constructor(readonly options: Options) {
26
+ > {
27
+ constructor(readonly options?: TOptions) {
31
28
  super()
32
29
  }
33
30
 
34
- validateInContext(
35
- // @NOTE validation will be applied on the default value as well
36
- input: unknown = this.options.default,
37
- ctx: ValidatorContext,
38
- ): ValidationResult<StringSchemaOutput<Options>> {
39
- const { options } = this
40
-
31
+ validateInContext(input: unknown, ctx: ValidationContext) {
41
32
  const str = coerceToString(input)
42
33
  if (str == null) {
43
34
  return ctx.issueInvalidType(input, 'string')
@@ -45,14 +36,14 @@ export class StringSchema<
45
36
 
46
37
  let lazyUtf8Len: number
47
38
 
48
- const { minLength } = options
39
+ const minLength = this.options?.minLength
49
40
  if (minLength != null) {
50
41
  if ((lazyUtf8Len ??= utf8Len(str)) < minLength) {
51
42
  return ctx.issueTooSmall(str, 'string', minLength, lazyUtf8Len)
52
43
  }
53
44
  }
54
45
 
55
- const { maxLength } = options
46
+ const maxLength = this.options?.maxLength
56
47
  if (maxLength != null) {
57
48
  // Optimization: we can avoid computing the UTF-8 length if the maximum
58
49
  // possible length, in bytes, of the input JS string is smaller than the
@@ -66,7 +57,7 @@ export class StringSchema<
66
57
 
67
58
  let lazyGraphLen: number
68
59
 
69
- const { minGraphemes } = options
60
+ const minGraphemes = this.options?.minGraphemes
70
61
  if (minGraphemes != null) {
71
62
  // Optimization: avoid counting graphemes if the length check already fails
72
63
  if (str.length < minGraphemes) {
@@ -76,25 +67,19 @@ export class StringSchema<
76
67
  }
77
68
  }
78
69
 
79
- const { maxGraphemes } = options
70
+ const maxGraphemes = this.options?.maxGraphemes
80
71
  if (maxGraphemes != null) {
81
72
  if ((lazyGraphLen ??= graphemeLen(str)) > maxGraphemes) {
82
73
  return ctx.issueTooBig(str, 'grapheme', maxGraphemes, lazyGraphLen)
83
74
  }
84
75
  }
85
76
 
86
- if (options.format !== undefined) {
87
- try {
88
- // @TODO optimize to avoid throw cost (requires re-writing utilities
89
- // from @atproto/syntax)
90
- assertStringFormat(str, options.format)
91
- } catch (err) {
92
- const message = err instanceof Error ? err.message : undefined
93
- return ctx.issueInvalidFormat(str, options.format, message)
94
- }
77
+ const format = this.options?.format
78
+ if (format != null && !isStringFormat(str, format)) {
79
+ return ctx.issueInvalidFormat(str, format)
95
80
  }
96
81
 
97
- return ctx.success(str as StringSchemaOutput<Options>)
82
+ return ctx.success(str)
98
83
  }
99
84
  }
100
85
 
@@ -137,3 +122,9 @@ export function coerceToString(input: unknown): string | null {
137
122
  return null
138
123
  }
139
124
  }
125
+
126
+ export const string = /*#__PURE__*/ memoizedOptions(function <
127
+ const O extends StringSchemaOptions = NonNullable<unknown>,
128
+ >(options?: StringSchemaOptions & O) {
129
+ return new StringSchema<O>(options)
130
+ })