@atproto/lex-schema 0.0.2 → 0.0.4

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 +75 -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
@@ -1,3 +1,4 @@
1
+ import { NsidString } from '../core.js'
1
2
  import { Infer } from '../validation.js'
2
3
  import { ObjectSchema } from './object.js'
3
4
  import { ParamsSchema } from './params.js'
@@ -19,17 +20,21 @@ export type InferSubscriptionMessage<S extends Subscription> =
19
20
  : unknown
20
21
 
21
22
  export class Subscription<
22
- N extends string = any,
23
- P extends ParamsSchema = any,
24
- M extends undefined | RefSchema | TypedUnionSchema | ObjectSchema = any,
25
- E extends undefined | readonly string[] = any,
23
+ TNsid extends NsidString = any,
24
+ TParameters extends ParamsSchema = any,
25
+ TMessage extends
26
+ | undefined
27
+ | RefSchema
28
+ | TypedUnionSchema
29
+ | ObjectSchema = any,
30
+ TErrors extends undefined | readonly string[] = any,
26
31
  > {
27
32
  readonly type = 'subscription' as const
28
33
 
29
34
  constructor(
30
- readonly nsid: N,
31
- readonly parameters: P,
32
- readonly message: M,
33
- readonly errors: E,
35
+ readonly nsid: TNsid,
36
+ readonly parameters: TParameters,
37
+ readonly message: TMessage,
38
+ readonly errors: TErrors,
34
39
  ) {}
35
40
  }
@@ -0,0 +1,428 @@
1
+ import { TokenSchema } from './token.js'
2
+
3
+ describe('TokenSchema', () => {
4
+ describe('basic validation', () => {
5
+ const schema = new TokenSchema('mytoken')
6
+
7
+ it('validates exact token match', () => {
8
+ const result = schema.safeParse('mytoken')
9
+ expect(result.success).toBe(true)
10
+ if (result.success) {
11
+ expect(result.value).toBe('mytoken')
12
+ }
13
+ })
14
+
15
+ it('rejects different strings', () => {
16
+ const result = schema.safeParse('othertoken')
17
+ expect(result.success).toBe(false)
18
+ })
19
+
20
+ it('rejects similar strings with different case', () => {
21
+ const result = schema.safeParse('MyToken')
22
+ expect(result.success).toBe(false)
23
+ })
24
+
25
+ it('rejects partial matches', () => {
26
+ const result = schema.safeParse('my')
27
+ expect(result.success).toBe(false)
28
+ })
29
+
30
+ it('rejects empty string', () => {
31
+ const result = schema.safeParse('')
32
+ expect(result.success).toBe(false)
33
+ })
34
+ })
35
+
36
+ describe('TokenSchema instance validation', () => {
37
+ const schema = new TokenSchema('mytoken')
38
+
39
+ it('accepts TokenSchema instance with same value', () => {
40
+ const otherInstance = new TokenSchema('mytoken')
41
+ const result = schema.safeParse(otherInstance)
42
+ expect(result.success).toBe(true)
43
+ if (result.success) {
44
+ expect(result.value).toBe('mytoken')
45
+ }
46
+ })
47
+
48
+ it('rejects TokenSchema instance with different value', () => {
49
+ const otherInstance = new TokenSchema('othertoken')
50
+ const result = schema.safeParse(otherInstance)
51
+ expect(result.success).toBe(false)
52
+ })
53
+
54
+ it('accepts itself as input', () => {
55
+ const result = schema.safeParse(schema)
56
+ expect(result.success).toBe(true)
57
+ if (result.success) {
58
+ expect(result.value).toBe('mytoken')
59
+ }
60
+ })
61
+ })
62
+
63
+ describe('type validation', () => {
64
+ const schema = new TokenSchema('mytoken')
65
+
66
+ it('rejects null', () => {
67
+ const result = schema.safeParse(null)
68
+ expect(result.success).toBe(false)
69
+ })
70
+
71
+ it('rejects undefined', () => {
72
+ const result = schema.safeParse(undefined)
73
+ expect(result.success).toBe(false)
74
+ })
75
+
76
+ it('rejects numbers', () => {
77
+ const result = schema.safeParse(123)
78
+ expect(result.success).toBe(false)
79
+ })
80
+
81
+ it('rejects booleans', () => {
82
+ const result = schema.safeParse(true)
83
+ expect(result.success).toBe(false)
84
+ })
85
+
86
+ it('rejects objects', () => {
87
+ const result = schema.safeParse({ token: 'mytoken' })
88
+ expect(result.success).toBe(false)
89
+ })
90
+
91
+ it('rejects arrays', () => {
92
+ const result = schema.safeParse(['mytoken'])
93
+ expect(result.success).toBe(false)
94
+ })
95
+
96
+ it('rejects String objects', () => {
97
+ const result = schema.safeParse(new String('mytoken'))
98
+ expect(result.success).toBe(false)
99
+ })
100
+ })
101
+
102
+ describe('special token values', () => {
103
+ it('validates token with spaces', () => {
104
+ const schema = new TokenSchema('my token')
105
+ expect(schema.safeParse('my token').success).toBe(true)
106
+ })
107
+
108
+ it('validates token with special characters', () => {
109
+ const schema = new TokenSchema('token-with-dashes')
110
+ expect(schema.safeParse('token-with-dashes').success).toBe(true)
111
+ })
112
+
113
+ it('validates token with underscores', () => {
114
+ const schema = new TokenSchema('token_with_underscores')
115
+ expect(schema.safeParse('token_with_underscores').success).toBe(true)
116
+ })
117
+
118
+ it('validates token with dots', () => {
119
+ const schema = new TokenSchema('token.with.dots')
120
+ expect(schema.safeParse('token.with.dots').success).toBe(true)
121
+ })
122
+
123
+ it('validates token with numbers', () => {
124
+ const schema = new TokenSchema('token123')
125
+ expect(schema.safeParse('token123').success).toBe(true)
126
+ })
127
+
128
+ it('validates token with unicode characters', () => {
129
+ const schema = new TokenSchema('token世界')
130
+ expect(schema.safeParse('token世界').success).toBe(true)
131
+ })
132
+
133
+ it('validates token with emoji', () => {
134
+ const schema = new TokenSchema('token🚀')
135
+ expect(schema.safeParse('token🚀').success).toBe(true)
136
+ })
137
+
138
+ it('validates token with newlines', () => {
139
+ const schema = new TokenSchema('token\nwith\nnewlines')
140
+ expect(schema.safeParse('token\nwith\nnewlines').success).toBe(true)
141
+ expect(schema.safeParse('token with newlines').success).toBe(false)
142
+ })
143
+
144
+ it('validates token with tabs', () => {
145
+ const schema = new TokenSchema('token\twith\ttabs')
146
+ expect(schema.safeParse('token\twith\ttabs').success).toBe(true)
147
+ })
148
+
149
+ it('validates very long token', () => {
150
+ const longToken = 'token'.repeat(200)
151
+ const schema = new TokenSchema(longToken)
152
+ expect(schema.safeParse(longToken).success).toBe(true)
153
+ expect(schema.safeParse(longToken + 'x').success).toBe(false)
154
+ })
155
+ })
156
+
157
+ describe('empty token', () => {
158
+ const schema = new TokenSchema('')
159
+
160
+ it('validates empty string', () => {
161
+ const result = schema.safeParse('')
162
+ expect(result.success).toBe(true)
163
+ if (result.success) {
164
+ expect(result.value).toBe('')
165
+ }
166
+ })
167
+
168
+ it('rejects non-empty strings', () => {
169
+ const result = schema.safeParse('anything')
170
+ expect(result.success).toBe(false)
171
+ })
172
+
173
+ it('rejects null', () => {
174
+ const result = schema.safeParse(null)
175
+ expect(result.success).toBe(false)
176
+ })
177
+
178
+ it('rejects undefined', () => {
179
+ const result = schema.safeParse(undefined)
180
+ expect(result.success).toBe(false)
181
+ })
182
+
183
+ it('accepts TokenSchema instance with empty value', () => {
184
+ const otherInstance = new TokenSchema('')
185
+ const result = schema.safeParse(otherInstance)
186
+ expect(result.success).toBe(true)
187
+ if (result.success) {
188
+ expect(result.value).toBe('')
189
+ }
190
+ })
191
+ })
192
+
193
+ describe('whitespace handling', () => {
194
+ it('treats whitespace as significant', () => {
195
+ const schema = new TokenSchema(' token ')
196
+ expect(schema.safeParse(' token ').success).toBe(true)
197
+ expect(schema.safeParse('token').success).toBe(false)
198
+ expect(schema.safeParse(' token').success).toBe(false)
199
+ expect(schema.safeParse('token ').success).toBe(false)
200
+ })
201
+
202
+ it('distinguishes between different whitespace', () => {
203
+ const schema = new TokenSchema('token ')
204
+ expect(schema.safeParse('token ').success).toBe(true)
205
+ expect(schema.safeParse('token').success).toBe(false)
206
+ expect(schema.safeParse('token ').success).toBe(false)
207
+ })
208
+ })
209
+
210
+ describe('case sensitivity', () => {
211
+ const schema = new TokenSchema('Token')
212
+
213
+ it('is case sensitive', () => {
214
+ expect(schema.safeParse('Token').success).toBe(true)
215
+ expect(schema.safeParse('token').success).toBe(false)
216
+ expect(schema.safeParse('TOKEN').success).toBe(false)
217
+ expect(schema.safeParse('ToKeN').success).toBe(false)
218
+ })
219
+
220
+ it('handles case-sensitive TokenSchema instances', () => {
221
+ expect(schema.safeParse(new TokenSchema('Token')).success).toBe(true)
222
+ expect(schema.safeParse(new TokenSchema('token')).success).toBe(false)
223
+ })
224
+ })
225
+
226
+ describe('common token patterns', () => {
227
+ it('validates namespace-style tokens', () => {
228
+ const schema = new TokenSchema('com.example.record')
229
+ expect(schema.safeParse('com.example.record').success).toBe(true)
230
+ })
231
+
232
+ it('validates URL-like tokens', () => {
233
+ const schema = new TokenSchema('https://example.com/path')
234
+ expect(schema.safeParse('https://example.com/path').success).toBe(true)
235
+ })
236
+
237
+ it('validates version-like tokens', () => {
238
+ const schema = new TokenSchema('v1.0.0')
239
+ expect(schema.safeParse('v1.0.0').success).toBe(true)
240
+ })
241
+
242
+ it('validates hash-like tokens', () => {
243
+ const schema = new TokenSchema('#token')
244
+ expect(schema.safeParse('#token').success).toBe(true)
245
+ })
246
+
247
+ it('validates type discriminator tokens', () => {
248
+ const schema = new TokenSchema('#post')
249
+ expect(schema.safeParse('#post').success).toBe(true)
250
+ expect(schema.safeParse('#comment').success).toBe(false)
251
+ })
252
+ })
253
+
254
+ describe('serialization methods', () => {
255
+ const schema = new TokenSchema('mytoken')
256
+
257
+ it('toJSON returns the token value', () => {
258
+ expect(schema.toJSON()).toBe('mytoken')
259
+ })
260
+
261
+ it('toString returns the token value', () => {
262
+ expect(schema.toString()).toBe('mytoken')
263
+ })
264
+
265
+ it('toJSON and toString return the same value', () => {
266
+ expect(schema.toJSON()).toBe(schema.toString())
267
+ })
268
+
269
+ it('handles empty token serialization', () => {
270
+ const emptySchema = new TokenSchema('')
271
+ expect(emptySchema.toJSON()).toBe('')
272
+ expect(emptySchema.toString()).toBe('')
273
+ })
274
+
275
+ it('handles special characters in serialization', () => {
276
+ const specialSchema = new TokenSchema('token\nwith\tspecial')
277
+ expect(specialSchema.toJSON()).toBe('token\nwith\tspecial')
278
+ expect(specialSchema.toString()).toBe('token\nwith\tspecial')
279
+ })
280
+
281
+ it('serializes to primitive string in JSON', () => {
282
+ const schema = new TokenSchema('mytoken')
283
+ const obj = { token: schema }
284
+ expect(JSON.stringify(obj)).toBe('{"token":"mytoken"}')
285
+ })
286
+ })
287
+
288
+ describe('value property', () => {
289
+ const schema = new TokenSchema('mytoken')
290
+
291
+ it('exposes the token value through serialization', () => {
292
+ // value is protected, so we verify it through toString/toJSON
293
+ expect(schema.toString()).toBe('mytoken')
294
+ expect(schema.toJSON()).toBe('mytoken')
295
+ })
296
+ })
297
+
298
+ describe('multiple token instances', () => {
299
+ const schema1 = new TokenSchema('token1')
300
+ const schema2 = new TokenSchema('token2')
301
+ const schema3 = new TokenSchema('token1')
302
+
303
+ it('different instances with same value validate each other', () => {
304
+ expect(schema1.safeParse(schema3).success).toBe(true)
305
+ expect(schema3.safeParse(schema1).success).toBe(true)
306
+ })
307
+
308
+ it('different instances with different values reject each other', () => {
309
+ expect(schema1.safeParse(schema2).success).toBe(false)
310
+ expect(schema2.safeParse(schema1).success).toBe(false)
311
+ })
312
+
313
+ it('validates string values correctly', () => {
314
+ expect(schema1.safeParse('token1').success).toBe(true)
315
+ expect(schema1.safeParse('token2').success).toBe(false)
316
+ expect(schema2.safeParse('token2').success).toBe(true)
317
+ expect(schema2.safeParse('token1').success).toBe(false)
318
+ })
319
+ })
320
+
321
+ describe('type safety', () => {
322
+ it('preserves token type in TypeScript', () => {
323
+ const schema = new TokenSchema('specific' as const)
324
+ const result = schema.safeParse('specific')
325
+ expect(result.success).toBe(true)
326
+ if (result.success) {
327
+ // TypeScript should infer result.value as the literal type 'specific'
328
+ expect(result.value).toBe('specific')
329
+ }
330
+ })
331
+
332
+ it('handles generic token types', () => {
333
+ const schema: TokenSchema<'a' | 'b'> = new TokenSchema('a')
334
+ expect(schema.safeParse('a').success).toBe(true)
335
+ })
336
+ })
337
+
338
+ describe('edge cases', () => {
339
+ it('handles tokens that look like numbers', () => {
340
+ const schema = new TokenSchema('123')
341
+ expect(schema.safeParse('123').success).toBe(true)
342
+ expect(schema.safeParse(123).success).toBe(false)
343
+ })
344
+
345
+ it('handles tokens that look like booleans', () => {
346
+ const schema = new TokenSchema('true')
347
+ expect(schema.safeParse('true').success).toBe(true)
348
+ expect(schema.safeParse(true).success).toBe(false)
349
+ })
350
+
351
+ it('handles tokens that look like null', () => {
352
+ const schema = new TokenSchema('null')
353
+ expect(schema.safeParse('null').success).toBe(true)
354
+ expect(schema.safeParse(null).success).toBe(false)
355
+ })
356
+
357
+ it('handles tokens that look like undefined', () => {
358
+ const schema = new TokenSchema('undefined')
359
+ expect(schema.safeParse('undefined').success).toBe(true)
360
+ expect(schema.safeParse(undefined).success).toBe(false)
361
+ })
362
+
363
+ it('handles tokens with only whitespace', () => {
364
+ const schema = new TokenSchema(' ')
365
+ expect(schema.safeParse(' ').success).toBe(true)
366
+ expect(schema.safeParse(' ').success).toBe(false)
367
+ expect(schema.safeParse(' ').success).toBe(false)
368
+ })
369
+
370
+ it('handles single character tokens', () => {
371
+ const schema = new TokenSchema('x')
372
+ expect(schema.safeParse('x').success).toBe(true)
373
+ expect(schema.safeParse('X').success).toBe(false)
374
+ expect(schema.safeParse('').success).toBe(false)
375
+ })
376
+
377
+ it('rejects symbols', () => {
378
+ const schema = new TokenSchema('mytoken')
379
+ const result = schema.safeParse(Symbol('mytoken'))
380
+ expect(result.success).toBe(false)
381
+ })
382
+
383
+ it('rejects functions', () => {
384
+ const schema = new TokenSchema('mytoken')
385
+ const result = schema.safeParse(() => 'mytoken')
386
+ expect(result.success).toBe(false)
387
+ })
388
+
389
+ it('rejects dates', () => {
390
+ const schema = new TokenSchema('mytoken')
391
+ const result = schema.safeParse(new Date())
392
+ expect(result.success).toBe(false)
393
+ })
394
+
395
+ it('rejects regular expressions', () => {
396
+ const schema = new TokenSchema('mytoken')
397
+ const result = schema.safeParse(/mytoken/)
398
+ expect(result.success).toBe(false)
399
+ })
400
+ })
401
+
402
+ describe('comparison with similar schemas', () => {
403
+ it('behaves like a string literal validator', () => {
404
+ const schema = new TokenSchema('literal')
405
+
406
+ // Should only accept the exact string
407
+ expect(schema.safeParse('literal').success).toBe(true)
408
+ expect(schema.safeParse('other').success).toBe(false)
409
+ expect(schema.safeParse('').success).toBe(false)
410
+ })
411
+
412
+ it('differs from enum by accepting only one value', () => {
413
+ const schema = new TokenSchema('option1')
414
+
415
+ // Only accepts the single token value
416
+ expect(schema.safeParse('option1').success).toBe(true)
417
+ expect(schema.safeParse('option2').success).toBe(false)
418
+ })
419
+
420
+ it('differs from string schema by accepting only specific value', () => {
421
+ const schema = new TokenSchema('specific')
422
+
423
+ // Only accepts the exact token
424
+ expect(schema.safeParse('specific').success).toBe(true)
425
+ expect(schema.safeParse('any other string').success).toBe(false)
426
+ })
427
+ })
428
+ })
@@ -1,13 +1,11 @@
1
- import { ValidationResult, Validator, ValidatorContext } from '../validation.js'
2
-
3
- export class TokenSchema<V extends string = any> extends Validator<V> {
4
- readonly lexiconType = 'token' as const
1
+ import { Schema, ValidationResult, ValidatorContext } from '../validation.js'
5
2
 
3
+ export class TokenSchema<V extends string = any> extends Schema<V> {
6
4
  constructor(protected readonly value: V) {
7
5
  super()
8
6
  }
9
7
 
10
- override validateInContext(
8
+ validateInContext(
11
9
  input: unknown,
12
10
  ctx: ValidatorContext,
13
11
  ): ValidationResult<V> {