@atproto/lex-schema 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/core/$type.d.ts +2 -2
  3. package/dist/core/$type.d.ts.map +1 -1
  4. package/dist/core/$type.js.map +1 -1
  5. package/dist/core/record-key.d.ts +1 -1
  6. package/dist/core/record-key.d.ts.map +1 -1
  7. package/dist/core/record-key.js.map +1 -1
  8. package/dist/core/schema.d.ts +3 -2
  9. package/dist/core/schema.d.ts.map +1 -1
  10. package/dist/core/schema.js +1 -1
  11. package/dist/core/schema.js.map +1 -1
  12. package/dist/core/standard-schema.d.ts +2 -2
  13. package/dist/core/standard-schema.d.ts.map +1 -1
  14. package/dist/core/standard-schema.js.map +1 -1
  15. package/dist/core/string-format.d.ts +2 -2
  16. package/dist/core/string-format.d.ts.map +1 -1
  17. package/dist/core/string-format.js.map +1 -1
  18. package/dist/core/validation-error.d.ts +1 -1
  19. package/dist/core/validation-error.d.ts.map +1 -1
  20. package/dist/core/validation-error.js +1 -1
  21. package/dist/core/validation-error.js.map +1 -1
  22. package/dist/core/validator.d.ts +1 -1
  23. package/dist/core/validator.d.ts.map +1 -1
  24. package/dist/core/validator.js +1 -1
  25. package/dist/core/validator.js.map +1 -1
  26. package/dist/helpers.d.ts +2 -2
  27. package/dist/helpers.d.ts.map +1 -1
  28. package/dist/helpers.js +2 -2
  29. package/dist/helpers.js.map +1 -1
  30. package/dist/schema/array.d.ts +1 -1
  31. package/dist/schema/array.d.ts.map +1 -1
  32. package/dist/schema/array.js +1 -1
  33. package/dist/schema/array.js.map +1 -1
  34. package/dist/schema/blob.d.ts +1 -1
  35. package/dist/schema/blob.d.ts.map +1 -1
  36. package/dist/schema/blob.js +2 -2
  37. package/dist/schema/blob.js.map +1 -1
  38. package/dist/schema/boolean.js +1 -1
  39. package/dist/schema/boolean.js.map +1 -1
  40. package/dist/schema/bytes.js +1 -1
  41. package/dist/schema/bytes.js.map +1 -1
  42. package/dist/schema/cid.d.ts +1 -1
  43. package/dist/schema/cid.d.ts.map +1 -1
  44. package/dist/schema/cid.js +3 -3
  45. package/dist/schema/cid.js.map +1 -1
  46. package/dist/schema/custom.js +1 -1
  47. package/dist/schema/custom.js.map +1 -1
  48. package/dist/schema/dict.d.ts +1 -1
  49. package/dist/schema/dict.d.ts.map +1 -1
  50. package/dist/schema/dict.js +1 -1
  51. package/dist/schema/dict.js.map +1 -1
  52. package/dist/schema/discriminated-union.d.ts +1 -1
  53. package/dist/schema/discriminated-union.d.ts.map +1 -1
  54. package/dist/schema/discriminated-union.js +2 -1
  55. package/dist/schema/discriminated-union.js.map +1 -1
  56. package/dist/schema/enum.js +1 -1
  57. package/dist/schema/enum.js.map +1 -1
  58. package/dist/schema/integer.js +1 -1
  59. package/dist/schema/integer.js.map +1 -1
  60. package/dist/schema/intersection.d.ts +1 -1
  61. package/dist/schema/intersection.d.ts.map +1 -1
  62. package/dist/schema/intersection.js +3 -1
  63. package/dist/schema/intersection.js.map +1 -1
  64. package/dist/schema/lex-map.d.ts +1 -1
  65. package/dist/schema/lex-map.d.ts.map +1 -1
  66. package/dist/schema/lex-map.js +1 -1
  67. package/dist/schema/lex-map.js.map +1 -1
  68. package/dist/schema/lex-value.d.ts +1 -1
  69. package/dist/schema/lex-value.d.ts.map +1 -1
  70. package/dist/schema/lex-value.js +1 -1
  71. package/dist/schema/lex-value.js.map +1 -1
  72. package/dist/schema/literal.js +1 -1
  73. package/dist/schema/literal.js.map +1 -1
  74. package/dist/schema/never.js +1 -1
  75. package/dist/schema/never.js.map +1 -1
  76. package/dist/schema/null.js +1 -1
  77. package/dist/schema/null.js.map +1 -1
  78. package/dist/schema/nullable.d.ts +1 -1
  79. package/dist/schema/nullable.d.ts.map +1 -1
  80. package/dist/schema/nullable.js +1 -1
  81. package/dist/schema/nullable.js.map +1 -1
  82. package/dist/schema/object.d.ts +2 -1
  83. package/dist/schema/object.d.ts.map +1 -1
  84. package/dist/schema/object.js +1 -1
  85. package/dist/schema/object.js.map +1 -1
  86. package/dist/schema/optional.d.ts +2 -1
  87. package/dist/schema/optional.d.ts.map +1 -1
  88. package/dist/schema/optional.js +2 -1
  89. package/dist/schema/optional.js.map +1 -1
  90. package/dist/schema/params.d.ts +1 -1
  91. package/dist/schema/params.d.ts.map +1 -1
  92. package/dist/schema/params.js +1 -1
  93. package/dist/schema/params.js.map +1 -1
  94. package/dist/schema/payload.d.ts +3 -2
  95. package/dist/schema/payload.d.ts.map +1 -1
  96. package/dist/schema/payload.js +2 -1
  97. package/dist/schema/payload.js.map +1 -1
  98. package/dist/schema/permission-set.d.ts +1 -1
  99. package/dist/schema/permission-set.d.ts.map +1 -1
  100. package/dist/schema/permission-set.js +1 -0
  101. package/dist/schema/permission-set.js.map +1 -1
  102. package/dist/schema/permission.d.ts +1 -1
  103. package/dist/schema/permission.d.ts.map +1 -1
  104. package/dist/schema/permission.js.map +1 -1
  105. package/dist/schema/procedure.d.ts +1 -1
  106. package/dist/schema/procedure.d.ts.map +1 -1
  107. package/dist/schema/procedure.js +2 -0
  108. package/dist/schema/procedure.js.map +1 -1
  109. package/dist/schema/query.d.ts +1 -1
  110. package/dist/schema/query.d.ts.map +1 -1
  111. package/dist/schema/query.js +2 -0
  112. package/dist/schema/query.js.map +1 -1
  113. package/dist/schema/record.d.ts +2 -2
  114. package/dist/schema/record.d.ts.map +1 -1
  115. package/dist/schema/record.js +1 -1
  116. package/dist/schema/record.js.map +1 -1
  117. package/dist/schema/ref.d.ts +1 -1
  118. package/dist/schema/ref.d.ts.map +1 -1
  119. package/dist/schema/ref.js +1 -1
  120. package/dist/schema/ref.js.map +1 -1
  121. package/dist/schema/refine.d.ts +2 -2
  122. package/dist/schema/refine.d.ts.map +1 -1
  123. package/dist/schema/refine.js +1 -1
  124. package/dist/schema/refine.js.map +1 -1
  125. package/dist/schema/regexp.js +1 -1
  126. package/dist/schema/regexp.js.map +1 -1
  127. package/dist/schema/string.d.ts +2 -2
  128. package/dist/schema/string.d.ts.map +1 -1
  129. package/dist/schema/string.js +1 -1
  130. package/dist/schema/string.js.map +1 -1
  131. package/dist/schema/subscription.d.ts +3 -2
  132. package/dist/schema/subscription.d.ts.map +1 -1
  133. package/dist/schema/subscription.js +2 -0
  134. package/dist/schema/subscription.js.map +1 -1
  135. package/dist/schema/token.d.ts +1 -1
  136. package/dist/schema/token.d.ts.map +1 -1
  137. package/dist/schema/token.js +1 -1
  138. package/dist/schema/token.js.map +1 -1
  139. package/dist/schema/typed-object.d.ts +2 -2
  140. package/dist/schema/typed-object.d.ts.map +1 -1
  141. package/dist/schema/typed-object.js +1 -1
  142. package/dist/schema/typed-object.js.map +1 -1
  143. package/dist/schema/typed-ref.d.ts +1 -1
  144. package/dist/schema/typed-ref.d.ts.map +1 -1
  145. package/dist/schema/typed-ref.js +1 -1
  146. package/dist/schema/typed-ref.js.map +1 -1
  147. package/dist/schema/typed-union.d.ts +1 -1
  148. package/dist/schema/typed-union.d.ts.map +1 -1
  149. package/dist/schema/typed-union.js +3 -1
  150. package/dist/schema/typed-union.js.map +1 -1
  151. package/dist/schema/union.d.ts +1 -1
  152. package/dist/schema/union.d.ts.map +1 -1
  153. package/dist/schema/union.js +1 -1
  154. package/dist/schema/union.js.map +1 -1
  155. package/dist/schema/unknown.js +1 -1
  156. package/dist/schema/unknown.js.map +1 -1
  157. package/dist/schema/with-default.d.ts +1 -1
  158. package/dist/schema/with-default.d.ts.map +1 -1
  159. package/dist/schema/with-default.js +1 -1
  160. package/dist/schema/with-default.js.map +1 -1
  161. package/package.json +6 -10
  162. package/src/core/$type.test.ts +0 -24
  163. package/src/core/$type.ts +0 -199
  164. package/src/core/record-key.ts +0 -85
  165. package/src/core/result.ts +0 -15
  166. package/src/core/schema.ts +0 -412
  167. package/src/core/standard-schema.test.ts +0 -124
  168. package/src/core/standard-schema.ts +0 -31
  169. package/src/core/string-format.ts +0 -411
  170. package/src/core/types.ts +0 -120
  171. package/src/core/validation-error.ts +0 -134
  172. package/src/core/validation-issue.ts +0 -340
  173. package/src/core/validator.ts +0 -636
  174. package/src/core.ts +0 -9
  175. package/src/external.ts +0 -3
  176. package/src/helpers.test.ts +0 -694
  177. package/src/helpers.ts +0 -222
  178. package/src/index.ts +0 -3
  179. package/src/schema/array.test.ts +0 -251
  180. package/src/schema/array.ts +0 -126
  181. package/src/schema/blob.test.ts +0 -733
  182. package/src/schema/blob.ts +0 -150
  183. package/src/schema/boolean.test.ts +0 -118
  184. package/src/schema/boolean.ts +0 -46
  185. package/src/schema/bytes.test.ts +0 -227
  186. package/src/schema/bytes.ts +0 -81
  187. package/src/schema/cid.test.ts +0 -125
  188. package/src/schema/cid.ts +0 -69
  189. package/src/schema/custom.test.ts +0 -414
  190. package/src/schema/custom.ts +0 -106
  191. package/src/schema/dict.test.ts +0 -181
  192. package/src/schema/dict.ts +0 -122
  193. package/src/schema/discriminated-union.test.ts +0 -676
  194. package/src/schema/discriminated-union.ts +0 -196
  195. package/src/schema/enum.test.ts +0 -398
  196. package/src/schema/enum.ts +0 -77
  197. package/src/schema/integer.test.ts +0 -314
  198. package/src/schema/integer.ts +0 -86
  199. package/src/schema/intersection.test.ts +0 -33
  200. package/src/schema/intersection.ts +0 -113
  201. package/src/schema/lex-map.test.ts +0 -593
  202. package/src/schema/lex-map.ts +0 -63
  203. package/src/schema/lex-value.test.ts +0 -81
  204. package/src/schema/lex-value.ts +0 -86
  205. package/src/schema/literal.test.ts +0 -533
  206. package/src/schema/literal.ts +0 -70
  207. package/src/schema/never.test.ts +0 -175
  208. package/src/schema/never.ts +0 -56
  209. package/src/schema/null.test.ts +0 -80
  210. package/src/schema/null.ts +0 -49
  211. package/src/schema/nullable.test.ts +0 -470
  212. package/src/schema/nullable.ts +0 -74
  213. package/src/schema/object.test.ts +0 -69
  214. package/src/schema/object.ts +0 -136
  215. package/src/schema/optional.test.ts +0 -479
  216. package/src/schema/optional.ts +0 -92
  217. package/src/schema/params.test.ts +0 -1118
  218. package/src/schema/params.ts +0 -371
  219. package/src/schema/payload.test.ts +0 -340
  220. package/src/schema/payload.ts +0 -204
  221. package/src/schema/permission-set.test.ts +0 -613
  222. package/src/schema/permission-set.ts +0 -86
  223. package/src/schema/permission.test.ts +0 -537
  224. package/src/schema/permission.ts +0 -63
  225. package/src/schema/procedure.test.ts +0 -324
  226. package/src/schema/procedure.ts +0 -98
  227. package/src/schema/query.test.ts +0 -348
  228. package/src/schema/query.ts +0 -86
  229. package/src/schema/record.test.ts +0 -812
  230. package/src/schema/record.ts +0 -217
  231. package/src/schema/ref.test.ts +0 -349
  232. package/src/schema/ref.ts +0 -103
  233. package/src/schema/refine.test.ts +0 -579
  234. package/src/schema/refine.ts +0 -153
  235. package/src/schema/regexp.test.ts +0 -577
  236. package/src/schema/regexp.ts +0 -82
  237. package/src/schema/string.test.ts +0 -773
  238. package/src/schema/string.ts +0 -229
  239. package/src/schema/subscription.test.ts +0 -499
  240. package/src/schema/subscription.ts +0 -108
  241. package/src/schema/token.test.ts +0 -152
  242. package/src/schema/token.ts +0 -103
  243. package/src/schema/typed-object.test.ts +0 -745
  244. package/src/schema/typed-object.ts +0 -181
  245. package/src/schema/typed-ref.test.ts +0 -796
  246. package/src/schema/typed-ref.ts +0 -126
  247. package/src/schema/typed-union.test.ts +0 -355
  248. package/src/schema/typed-union.ts +0 -130
  249. package/src/schema/union.test.ts +0 -191
  250. package/src/schema/union.ts +0 -89
  251. package/src/schema/unknown.test.ts +0 -313
  252. package/src/schema/unknown.ts +0 -47
  253. package/src/schema/with-default.ts +0 -81
  254. package/src/schema.ts +0 -43
  255. package/src/util/array-agg.test.ts +0 -42
  256. package/src/util/array-agg.ts +0 -44
  257. package/src/util/assertion-util.ts +0 -1
  258. package/src/util/if-any.ts +0 -3
  259. package/src/util/lazy-property.ts +0 -14
  260. package/src/util/memoize.ts +0 -37
  261. package/tsconfig.build.json +0 -12
  262. package/tsconfig.json +0 -7
  263. package/tsconfig.tests.json +0 -8
@@ -1,812 +0,0 @@
1
- import { describe, expect, expectTypeOf, it } from 'vitest'
2
- import { DidString, Infer, Unknown$Type, Unknown$TypedObject } from '../core.js'
3
- import { integer } from './integer.js'
4
- import { object } from './object.js'
5
- import { optional } from './optional.js'
6
- import { record } from './record.js'
7
- import { string } from './string.js'
8
-
9
- describe('RecordSchema', () => {
10
- describe('basic validation', () => {
11
- const schema = record(
12
- 'any',
13
- 'app.bsky.feed.post',
14
- object({
15
- text: string(),
16
- }),
17
- )
18
-
19
- it('validates record with correct $type', () => {
20
- const result = schema.safeParse({
21
- $type: 'app.bsky.feed.post',
22
- text: 'Hello world',
23
- })
24
- expect(result.success).toBe(true)
25
- if (result.success) {
26
- expect(result.value.$type).toBe('app.bsky.feed.post')
27
- expect(result.value.text).toBe('Hello world')
28
- }
29
- })
30
-
31
- it('rejects record with incorrect $type', () => {
32
- const result = schema.safeParse({
33
- $type: 'app.bsky.feed.like',
34
- text: 'Hello world',
35
- })
36
- expect(result.success).toBe(false)
37
- })
38
-
39
- it('rejects record missing $type', () => {
40
- const result = schema.safeParse({
41
- text: 'Hello world',
42
- })
43
- expect(result.success).toBe(false)
44
- })
45
-
46
- it('rejects non-objects', () => {
47
- const result = schema.safeParse('not an object')
48
- expect(result.success).toBe(false)
49
- })
50
-
51
- it('rejects null', () => {
52
- const result = schema.safeParse(null)
53
- expect(result.success).toBe(false)
54
- })
55
-
56
- it('rejects undefined', () => {
57
- const result = schema.safeParse(undefined)
58
- expect(result.success).toBe(false)
59
- })
60
-
61
- it('rejects arrays', () => {
62
- const result = schema.safeParse([{ $type: 'app.bsky.feed.post' }])
63
- expect(result.success).toBe(false)
64
- })
65
- })
66
-
67
- describe('isTypeOf method', () => {
68
- const schema = record(
69
- 'any',
70
- 'app.bsky.feed.post',
71
- object({
72
- text: string(),
73
- }),
74
- )
75
- type Schema = Infer<typeof schema>
76
-
77
- it('returns true for matching $type', () => {
78
- const result = schema.isTypeOf({ $type: 'app.bsky.feed.post' })
79
- expect(result).toBe(true)
80
- })
81
-
82
- it('returns false for non-matching $type', () => {
83
- const result = schema.isTypeOf({ $type: 'app.bsky.feed.like' })
84
- expect(result).toBe(false)
85
- })
86
-
87
- it('returns false for missing $type', () => {
88
- const result = schema.isTypeOf({})
89
- expect(result).toBe(false)
90
- })
91
-
92
- it('returns false for undefined $type', () => {
93
- const result = schema.isTypeOf({ $type: undefined })
94
- expect(result).toBe(false)
95
- })
96
-
97
- it('returns false for null $type', () => {
98
- const result = schema.isTypeOf({ $type: null })
99
- expect(result).toBe(false)
100
- })
101
-
102
- it('properly discriminates Unknown$TypeObject', () => {
103
- function foo(value: Unknown$TypedObject | Schema) {
104
- if (schema.isTypeOf(value)) {
105
- void value.text
106
- } else {
107
- // @ts-expect-error
108
- void value.text
109
- }
110
- }
111
-
112
- foo({
113
- $type: 'app.bsky.feed.post',
114
- text: 'aze',
115
- // @ts-expect-error
116
- unknownProperty: 'should not be allowed !',
117
- })
118
-
119
- foo({
120
- $type: 'blah' as Unknown$Type,
121
- // @ts-expect-error
122
- unknownProperty: 'should not be allowed !',
123
- })
124
- })
125
- })
126
-
127
- describe('build() method', () => {
128
- const schema = record(
129
- 'any',
130
- 'io.example.record',
131
- object({
132
- actor: string({ format: 'did' }),
133
- text: optional(string()),
134
- }),
135
- )
136
-
137
- it('adds $type to input', () => {
138
- const result = schema.build({
139
- actor: 'did:foo:bar',
140
- text: 'Hello',
141
- })
142
- expect(result).toStrictEqual({
143
- $type: 'io.example.record',
144
- actor: 'did:foo:bar',
145
- text: 'Hello',
146
- })
147
- expectTypeOf(result).toEqualTypeOf<{
148
- $type: 'io.example.record'
149
- actor: DidString
150
- text?: string
151
- }>()
152
- })
153
-
154
- it('causes a type error for invalid values', () => {
155
- const result = schema.build({
156
- // @ts-expect-error
157
- actor: 3,
158
- text: 'Hello',
159
- })
160
- expectTypeOf(result).toEqualTypeOf<{
161
- $type: 'io.example.record'
162
- actor: DidString
163
- text?: string
164
- }>()
165
- })
166
-
167
- it('preserves existing properties', () => {
168
- const result = schema.build({
169
- actor: 'did:foo:bar',
170
- // @ts-expect-error
171
- extra: 'value',
172
- })
173
- expect(result).toStrictEqual({
174
- $type: 'io.example.record',
175
- actor: 'did:foo:bar',
176
- extra: 'value',
177
- })
178
- expectTypeOf(result).toEqualTypeOf<{
179
- actor: `did:${string}:${string}`
180
- text?: string | undefined
181
- $type: 'io.example.record'
182
- }>()
183
- })
184
-
185
- it('overwrites existing $type', () => {
186
- const result = schema.build({
187
- // @ts-expect-error
188
- $type: 'wrong.type',
189
- actor: 'did:foo:bar',
190
- })
191
- expect(result).toStrictEqual({
192
- $type: 'io.example.record',
193
- actor: 'did:foo:bar',
194
- })
195
- expectTypeOf(result).toEqualTypeOf<{
196
- $type: 'io.example.record'
197
- actor: DidString
198
- text?: string
199
- }>()
200
- })
201
-
202
- describe('build() does not validate', () => {
203
- const schema = record(
204
- 'any',
205
- 'io.example.record',
206
- object({
207
- actor: string({ format: 'did' }),
208
- count: integer(),
209
- }),
210
- )
211
-
212
- it('does not throw for invalid data', () => {
213
- const result = schema.build({
214
- // @ts-expect-error
215
- actor: 'not-a-did',
216
- count: 123,
217
- })
218
-
219
- expect(result.$type).toBe('io.example.record')
220
- expect(result.actor).toBe('not-a-did')
221
- expect(result.count).toBe(123)
222
- })
223
-
224
- it('does not throw for invalid types', () => {
225
- const result = schema.build({
226
- actor: 'did:plc:abc123',
227
- // @ts-expect-error
228
- count: 'not-a-number',
229
- })
230
-
231
- expect(result.$type).toBe('io.example.record')
232
- expect(result.count).toBe('not-a-number')
233
- })
234
-
235
- it('does not throw for missing required fields', () => {
236
- // @ts-expect-error
237
- const result = schema.build({})
238
-
239
- expect(result.$type).toBe('io.example.record')
240
- expect(result.actor).toBeUndefined()
241
- expect(result.count).toBeUndefined()
242
- })
243
-
244
- it('does not throw for extra fields', () => {
245
- const result = schema.build({
246
- actor: 'did:plc:abc123',
247
- count: 42,
248
- // @ts-expect-error
249
- extra: 'unexpected',
250
- })
251
-
252
- expect(result.$type).toBe('io.example.record')
253
-
254
- // @ts-expect-error
255
- expect(result.extra).toBe('unexpected')
256
- })
257
-
258
- it('parse() still validates after build()', () => {
259
- const built = schema.build({
260
- // @ts-expect-error
261
- actor: 'not-a-did',
262
- count: 123,
263
- })
264
-
265
- expect(() => schema.parse(built)).toThrow('Invalid DID')
266
- })
267
-
268
- it('safeParse() can detect validation errors after build()', () => {
269
- const built = schema.build({
270
- // @ts-expect-error
271
- actor: 'not-a-did',
272
- count: 123,
273
- })
274
-
275
- const result = schema.safeParse(built)
276
- expect(result.success).toBe(false)
277
- })
278
- })
279
- })
280
-
281
- describe('key type: any', () => {
282
- const schema = record(
283
- 'any',
284
- 'app.bsky.feed.post',
285
- object({
286
- text: string(),
287
- }),
288
- )
289
-
290
- it('validates record keys', () => {
291
- const result = schema.keySchema.safeParse('anyStringKey')
292
- expect(result.success).toBe(true)
293
- })
294
-
295
- it('validates alphanumeric keys', () => {
296
- const result = schema.keySchema.safeParse('key123')
297
- expect(result.success).toBe(true)
298
- })
299
-
300
- it('validates keys with special characters', () => {
301
- const result = schema.keySchema.safeParse('key-with-dashes')
302
- expect(result.success).toBe(true)
303
- })
304
-
305
- it('rejects empty strings', () => {
306
- const result = schema.keySchema.safeParse('')
307
- expect(result.success).toBe(false)
308
- })
309
-
310
- it('rejects non-strings', () => {
311
- const result = schema.keySchema.safeParse(123)
312
- expect(result.success).toBe(false)
313
- })
314
- })
315
-
316
- describe('key type: tid', () => {
317
- const schema = record(
318
- 'tid',
319
- 'app.bsky.feed.post',
320
- object({
321
- text: string(),
322
- }),
323
- )
324
-
325
- it('validates valid TID', () => {
326
- const result = schema.keySchema.safeParse('3jzfcijpj2z2a')
327
- expect(result.success).toBe(true)
328
- })
329
-
330
- it('rejects invalid TID format', () => {
331
- const result = schema.keySchema.safeParse('not-a-tid')
332
- expect(result.success).toBe(false)
333
- })
334
-
335
- it('rejects TID with invalid characters', () => {
336
- const result = schema.keySchema.safeParse('3jzfcijpj2z2!')
337
- expect(result.success).toBe(false)
338
- })
339
-
340
- it('rejects empty strings', () => {
341
- const result = schema.keySchema.safeParse('')
342
- expect(result.success).toBe(false)
343
- })
344
-
345
- it('rejects regular strings', () => {
346
- const result = schema.keySchema.safeParse('regularString')
347
- expect(result.success).toBe(false)
348
- })
349
- })
350
-
351
- describe('key type: nsid', () => {
352
- const schema = record(
353
- 'nsid',
354
- 'app.bsky.feed.post',
355
- object({
356
- text: string(),
357
- }),
358
- )
359
-
360
- it('validates valid NSID', () => {
361
- const result = schema.keySchema.safeParse('app.bsky.feed.post')
362
- expect(result.success).toBe(true)
363
- })
364
-
365
- it('validates NSID with multiple segments', () => {
366
- const result = schema.keySchema.safeParse(
367
- 'com.example.app.feature.action',
368
- )
369
- expect(result.success).toBe(true)
370
- })
371
-
372
- it('rejects invalid NSID format', () => {
373
- const result = schema.keySchema.safeParse('not-an-nsid')
374
- expect(result.success).toBe(false)
375
- })
376
-
377
- it('rejects NSID with invalid characters', () => {
378
- const result = schema.keySchema.safeParse('app.bsky.feed!')
379
- expect(result.success).toBe(false)
380
- })
381
-
382
- it('rejects empty strings', () => {
383
- const result = schema.keySchema.safeParse('')
384
- expect(result.success).toBe(false)
385
- })
386
- })
387
-
388
- describe('key type: literal', () => {
389
- describe('literal:self', () => {
390
- const schema = record(
391
- 'literal:self',
392
- 'app.bsky.feed.post',
393
- object({
394
- text: string(),
395
- }),
396
- )
397
-
398
- it('validates exact literal "self"', () => {
399
- const result = schema.keySchema.safeParse('self')
400
- expect(result.success).toBe(true)
401
- })
402
-
403
- it('rejects non-matching strings', () => {
404
- const result = schema.keySchema.safeParse('other')
405
- expect(result.success).toBe(false)
406
- })
407
-
408
- it('rejects case variations', () => {
409
- const result = schema.keySchema.safeParse('Self')
410
- expect(result.success).toBe(false)
411
- })
412
-
413
- it('rejects empty strings', () => {
414
- const result = schema.keySchema.safeParse('')
415
- expect(result.success).toBe(false)
416
- })
417
- })
418
-
419
- describe('literal:customKey', () => {
420
- const schema = record(
421
- 'literal:customKey',
422
- 'app.bsky.feed.post',
423
- object({
424
- text: string(),
425
- }),
426
- )
427
-
428
- it('validates exact literal match', () => {
429
- const result = schema.keySchema.safeParse('customKey')
430
- expect(result.success).toBe(true)
431
- })
432
-
433
- it('rejects non-matching strings', () => {
434
- const result = schema.keySchema.safeParse('otherKey')
435
- expect(result.success).toBe(false)
436
- })
437
-
438
- it('rejects partial matches', () => {
439
- const result = schema.keySchema.safeParse('custom')
440
- expect(result.success).toBe(false)
441
- })
442
- })
443
- })
444
-
445
- describe('$type with hash fragment', () => {
446
- const schema = record(
447
- 'any',
448
- 'app.bsky.feed.post#main',
449
- object({
450
- text: string(),
451
- }),
452
- )
453
-
454
- it('validates record with correct $type including hash', () => {
455
- const result = schema.safeParse({
456
- $type: 'app.bsky.feed.post#main',
457
- text: 'Hello world',
458
- })
459
- expect(result.success).toBe(true)
460
- })
461
-
462
- it('rejects record with $type without hash', () => {
463
- const result = schema.safeParse({
464
- $type: 'app.bsky.feed.post',
465
- text: 'Hello world',
466
- })
467
- expect(result.success).toBe(false)
468
- })
469
-
470
- it('rejects record with different hash fragment', () => {
471
- const result = schema.safeParse({
472
- $type: 'app.bsky.feed.post#other',
473
- text: 'Hello world',
474
- })
475
- expect(result.success).toBe(false)
476
- })
477
- })
478
-
479
- describe('complex nested schema', () => {
480
- const schema = record(
481
- 'any',
482
- 'app.bsky.feed.post',
483
- object({
484
- text: string({ maxLength: 300 }),
485
- createdAt: string({ format: 'datetime' }),
486
- }),
487
- )
488
-
489
- it('validates complex record with all constraints', () => {
490
- const result = schema.safeParse({
491
- $type: 'app.bsky.feed.post',
492
- text: 'Hello world',
493
- createdAt: '2023-12-25T12:00:00Z',
494
- })
495
- expect(result.success).toBe(true)
496
- })
497
-
498
- it('rejects when nested field violates constraints', () => {
499
- const result = schema.safeParse({
500
- $type: 'app.bsky.feed.post',
501
- text: 'a'.repeat(301),
502
- createdAt: '2023-12-25T12:00:00Z',
503
- })
504
- expect(result.success).toBe(false)
505
- })
506
-
507
- it('rejects when datetime format is invalid', () => {
508
- const result = schema.safeParse({
509
- $type: 'app.bsky.feed.post',
510
- text: 'Hello world',
511
- createdAt: 'not-a-date',
512
- })
513
- expect(result.success).toBe(false)
514
- })
515
- })
516
-
517
- describe('edge cases', () => {
518
- const schema = record(
519
- 'any',
520
- 'app.bsky.feed.post',
521
- object({
522
- text: string(),
523
- }),
524
- )
525
-
526
- it('handles $type as number', () => {
527
- const result = schema.safeParse({
528
- $type: 123,
529
- text: 'Hello world',
530
- })
531
- expect(result.success).toBe(false)
532
- })
533
-
534
- it('handles $type as boolean', () => {
535
- const result = schema.safeParse({
536
- $type: true,
537
- text: 'Hello world',
538
- })
539
- expect(result.success).toBe(false)
540
- })
541
-
542
- it('handles $type as object', () => {
543
- const result = schema.safeParse({
544
- $type: { value: 'app.bsky.feed.post' },
545
- text: 'Hello world',
546
- })
547
- expect(result.success).toBe(false)
548
- })
549
-
550
- it('handles $type as array', () => {
551
- const result = schema.safeParse({
552
- $type: ['app.bsky.feed.post'],
553
- text: 'Hello world',
554
- })
555
- expect(result.success).toBe(false)
556
- })
557
-
558
- it('preserves extra properties not in schema', () => {
559
- const input = {
560
- $type: 'app.bsky.feed.post',
561
- text: 'Hello world',
562
- extra: 'value',
563
- another: 123,
564
- }
565
-
566
- const result = schema.safeParse(input)
567
- expect(result.success).toBe(true)
568
- if (result.success) {
569
- // @ts-expect-error
570
- expect(result.value.extra).toBe('value')
571
- // @ts-expect-error
572
- expect(result.value.another).toBe(123)
573
- }
574
- })
575
-
576
- it('handles empty object', () => {
577
- const result = schema.safeParse({})
578
- expect(result.success).toBe(false)
579
- })
580
-
581
- it('handles deeply nested structures', () => {
582
- const complexSchema = record(
583
- 'any',
584
- 'app.bsky.complex',
585
- object({
586
- nested: object({
587
- deep: object({
588
- value: string(),
589
- }),
590
- }),
591
- }),
592
- )
593
-
594
- const result = complexSchema.safeParse({
595
- $type: 'app.bsky.complex',
596
- nested: {
597
- deep: {
598
- value: 'test',
599
- },
600
- },
601
- })
602
- expect(result.success).toBe(true)
603
- })
604
- })
605
-
606
- describe('$isTypeOf method', () => {
607
- const schema = record(
608
- 'any',
609
- 'app.bsky.feed.post',
610
- object({
611
- text: string(),
612
- }),
613
- )
614
-
615
- it('returns true for matching $type', () => {
616
- const result = schema.$isTypeOf({ $type: 'app.bsky.feed.post' })
617
- expect(result).toBe(true)
618
- })
619
-
620
- it('returns false for non-matching $type', () => {
621
- const result = schema.$isTypeOf({ $type: 'app.bsky.feed.like' })
622
- expect(result).toBe(false)
623
- })
624
- })
625
-
626
- describe('$build method', () => {
627
- const schema = record(
628
- 'any',
629
- 'app.bsky.feed.post',
630
- object({
631
- text: string(),
632
- }),
633
- )
634
-
635
- it('adds correct $type to input', () => {
636
- const result = schema.$build({ text: 'Hello world' })
637
- expect(result.$type).toBe('app.bsky.feed.post')
638
- expect(result.text).toBe('Hello world')
639
- })
640
- })
641
-
642
- describe('validation with missing required fields', () => {
643
- const schema = record(
644
- 'any',
645
- 'app.bsky.feed.post',
646
- object({
647
- text: string(),
648
- author: string(),
649
- }),
650
- )
651
-
652
- it('rejects when required field is missing', () => {
653
- const result = schema.safeParse({
654
- $type: 'app.bsky.feed.post',
655
- text: 'Hello world',
656
- })
657
- expect(result.success).toBe(false)
658
- })
659
-
660
- it('validates when all required fields are present', () => {
661
- const result = schema.safeParse({
662
- $type: 'app.bsky.feed.post',
663
- text: 'Hello world',
664
- author: 'did:plc:123',
665
- })
666
- expect(result.success).toBe(true)
667
- })
668
- })
669
-
670
- describe('different record key types', () => {
671
- it('constructs with key type "any"', () => {
672
- const schema = record('any', 'app.bsky.test', object({}))
673
- expect(schema.key).toBe('any')
674
- expect(schema.keySchema).toBeDefined()
675
- })
676
-
677
- it('constructs with key type "tid"', () => {
678
- const schema = record('tid', 'app.bsky.test', object({}))
679
- expect(schema.key).toBe('tid')
680
- expect(schema.keySchema).toBeDefined()
681
- })
682
-
683
- it('constructs with key type "nsid"', () => {
684
- const schema = record('nsid', 'app.bsky.test', object({}))
685
- expect(schema.key).toBe('nsid')
686
- expect(schema.keySchema).toBeDefined()
687
- expect(schema.keySchema.safeParse('app.bsky.post').success).toBe(true)
688
- expect(schema.keySchema.safeParse('invalid-nsid').success).toBe(false)
689
- })
690
-
691
- it('constructs with literal key type', () => {
692
- const schema = record('literal:custom', 'app.bsky.test', object({}))
693
- expect(schema.key).toBe('literal:custom')
694
- expect(schema.keySchema).toBeDefined()
695
- // Applies default value in parse mode
696
- expect(schema.keySchema.parse(undefined)).toBe('custom')
697
- expect(schema.keySchema.safeParse('not-custom').success).toBe(false)
698
- })
699
- })
700
-
701
- describe('validation with undefined vs missing fields', () => {
702
- const schema = record(
703
- 'any',
704
- 'app.bsky.feed.post',
705
- object({
706
- text: string(),
707
- }),
708
- )
709
-
710
- it('rejects when required field is explicitly undefined', () => {
711
- const result = schema.safeParse({
712
- $type: 'app.bsky.feed.post',
713
- text: undefined,
714
- })
715
- expect(result.success).toBe(false)
716
- })
717
-
718
- it('rejects when $type is explicitly undefined', () => {
719
- const result = schema.safeParse({
720
- $type: undefined,
721
- text: 'Hello world',
722
- })
723
- expect(result.success).toBe(false)
724
- })
725
-
726
- it('rejects when $type is null', () => {
727
- const result = schema.safeParse({
728
- $type: null,
729
- text: 'Hello world',
730
- })
731
- expect(result.success).toBe(false)
732
- })
733
- })
734
-
735
- describe('record with empty $type string', () => {
736
- it('rejects empty $type string', () => {
737
- const schema = record(
738
- 'any',
739
- 'app.bsky.feed.post',
740
- object({
741
- text: string(),
742
- }),
743
- )
744
-
745
- const result = schema.safeParse({
746
- $type: '',
747
- text: 'Hello world',
748
- })
749
- expect(result.success).toBe(false)
750
- })
751
- })
752
-
753
- describe('special characters in $type', () => {
754
- it('validates $type with dots', () => {
755
- const schema = record(
756
- 'any',
757
- 'app.bsky.feed.post',
758
- object({
759
- text: string(),
760
- }),
761
- )
762
-
763
- const result = schema.safeParse({
764
- $type: 'app.bsky.feed.post',
765
- text: 'Hello world',
766
- })
767
- expect(result.success).toBe(true)
768
- })
769
-
770
- it('validates $type with hash and alphanumeric fragment', () => {
771
- const schema = record(
772
- 'any',
773
- 'app.bsky.feed.post#reply123',
774
- object({
775
- text: string(),
776
- }),
777
- )
778
-
779
- const result = schema.safeParse({
780
- $type: 'app.bsky.feed.post#reply123',
781
- text: 'Hello world',
782
- })
783
- expect(result.success).toBe(true)
784
- })
785
- })
786
-
787
- describe('case sensitivity', () => {
788
- const schema = record(
789
- 'any',
790
- 'app.bsky.feed.post',
791
- object({
792
- text: string(),
793
- }),
794
- )
795
-
796
- it('rejects $type with different case', () => {
797
- const result = schema.safeParse({
798
- $type: 'App.Bsky.Feed.Post',
799
- text: 'Hello world',
800
- })
801
- expect(result.success).toBe(false)
802
- })
803
-
804
- it('rejects $type with uppercase', () => {
805
- const result = schema.safeParse({
806
- $type: 'APP.BSKY.FEED.POST',
807
- text: 'Hello world',
808
- })
809
- expect(result.success).toBe(false)
810
- })
811
- })
812
- })