@marcuspuchalla/nachos 0.1.0

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 (100) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +674 -0
  3. package/README.md +345 -0
  4. package/dist/chunk-2FUTHZQQ.cjs +755 -0
  5. package/dist/chunk-2FUTHZQQ.cjs.map +1 -0
  6. package/dist/chunk-2HBCILJS.cjs +2034 -0
  7. package/dist/chunk-2HBCILJS.cjs.map +1 -0
  8. package/dist/chunk-7CFYWHS6.js +742 -0
  9. package/dist/chunk-7CFYWHS6.js.map +1 -0
  10. package/dist/chunk-PD72MVTX.cjs +160 -0
  11. package/dist/chunk-PD72MVTX.cjs.map +1 -0
  12. package/dist/chunk-ZDZ2B5PE.js +149 -0
  13. package/dist/chunk-ZDZ2B5PE.js.map +1 -0
  14. package/dist/chunk-ZRPJUEIZ.js +2020 -0
  15. package/dist/chunk-ZRPJUEIZ.js.map +1 -0
  16. package/dist/encoder/index.cjs +57 -0
  17. package/dist/encoder/index.cjs.map +1 -0
  18. package/dist/encoder/index.d.cts +72 -0
  19. package/dist/encoder/index.d.ts +72 -0
  20. package/dist/encoder/index.js +4 -0
  21. package/dist/encoder/index.js.map +1 -0
  22. package/dist/index.cjs +606 -0
  23. package/dist/index.cjs.map +1 -0
  24. package/dist/index.d.cts +494 -0
  25. package/dist/index.d.ts +494 -0
  26. package/dist/index.js +523 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/metafile-cjs.json +1 -0
  29. package/dist/metafile-esm.json +1 -0
  30. package/dist/parser/index.cjs +85 -0
  31. package/dist/parser/index.cjs.map +1 -0
  32. package/dist/parser/index.d.cts +72 -0
  33. package/dist/parser/index.d.ts +72 -0
  34. package/dist/parser/index.js +4 -0
  35. package/dist/parser/index.js.map +1 -0
  36. package/dist/types-DvNlfbKB.d.cts +301 -0
  37. package/dist/types-DvNlfbKB.d.ts +301 -0
  38. package/dist/useCborSimpleEncoder-ButVU988.d.cts +268 -0
  39. package/dist/useCborSimpleEncoder-TVxzNJ_9.d.ts +268 -0
  40. package/dist/useCborTag-B_iaShG6.d.ts +142 -0
  41. package/dist/useCborTag-BfTIV8HM.d.cts +142 -0
  42. package/package.json +102 -0
  43. package/src/__tests__/public-api.test.ts +326 -0
  44. package/src/encoder/__tests__/cbor-collection-encoder.test.ts +331 -0
  45. package/src/encoder/__tests__/cbor-integer-encoder.test.ts +283 -0
  46. package/src/encoder/__tests__/cbor-simple-encoder.test.ts +224 -0
  47. package/src/encoder/__tests__/cbor-string-encoder.test.ts +345 -0
  48. package/src/encoder/__tests__/cbor-tag-encoder.test.ts +565 -0
  49. package/src/encoder/composables/#useCborTagEncoder.ts# +158 -0
  50. package/src/encoder/composables/useCborCollectionEncoder.ts +424 -0
  51. package/src/encoder/composables/useCborEncoder.ts +203 -0
  52. package/src/encoder/composables/useCborIntegerEncoder.ts +188 -0
  53. package/src/encoder/composables/useCborSimpleEncoder.ts +266 -0
  54. package/src/encoder/composables/useCborStringEncoder.ts +266 -0
  55. package/src/encoder/composables/useCborTagEncoder.ts +158 -0
  56. package/src/encoder/index.ts +35 -0
  57. package/src/encoder/types.ts +88 -0
  58. package/src/encoder/utils.ts +80 -0
  59. package/src/index.ts +434 -0
  60. package/src/parser/__tests__/ast-tree-structure.test.ts +311 -0
  61. package/src/parser/__tests__/cbor-collection-errors.test.ts +296 -0
  62. package/src/parser/__tests__/cbor-collection.test.ts +369 -0
  63. package/src/parser/__tests__/cbor-deterministic-encoding.test.ts +432 -0
  64. package/src/parser/__tests__/cbor-diagnostic.test.ts +333 -0
  65. package/src/parser/__tests__/cbor-duplicate-keys.test.ts +235 -0
  66. package/src/parser/__tests__/cbor-float-errors.test.ts +222 -0
  67. package/src/parser/__tests__/cbor-float.test.ts +502 -0
  68. package/src/parser/__tests__/cbor-integer-errors.test.ts +139 -0
  69. package/src/parser/__tests__/cbor-integer.test.ts +200 -0
  70. package/src/parser/__tests__/cbor-map-duplicate-keys.test.ts +403 -0
  71. package/src/parser/__tests__/cbor-parser-errors.test.ts +126 -0
  72. package/src/parser/__tests__/cbor-security-dos-protection.test.ts +503 -0
  73. package/src/parser/__tests__/cbor-sequences.test.ts +150 -0
  74. package/src/parser/__tests__/cbor-source-map.test.ts +321 -0
  75. package/src/parser/__tests__/cbor-standard-tags.test.ts +340 -0
  76. package/src/parser/__tests__/cbor-string-errors.test.ts +227 -0
  77. package/src/parser/__tests__/cbor-string.test.ts +224 -0
  78. package/src/parser/__tests__/cbor-tag-advanced.test.ts +500 -0
  79. package/src/parser/__tests__/cbor-tag-errors.test.ts +447 -0
  80. package/src/parser/__tests__/cbor-tag-source-map.test.ts +360 -0
  81. package/src/parser/__tests__/cbor-tag.test.ts +684 -0
  82. package/src/parser/__tests__/extreme-edge-cases.test.ts +146 -0
  83. package/src/parser/__tests__/pathBuilder.test.ts +256 -0
  84. package/src/parser/__tests__/rfc-test-vectors.test.ts +607 -0
  85. package/src/parser/__tests__/security-limits.test.ts +248 -0
  86. package/src/parser/__tests__/utils-errors.test.ts +127 -0
  87. package/src/parser/composables/useCborCollection.ts +509 -0
  88. package/src/parser/composables/useCborDiagnostic.ts +381 -0
  89. package/src/parser/composables/useCborFloat.ts +256 -0
  90. package/src/parser/composables/useCborInteger.ts +114 -0
  91. package/src/parser/composables/useCborParser.ts +951 -0
  92. package/src/parser/composables/useCborString.ts +330 -0
  93. package/src/parser/composables/useCborStringTypes.ts +129 -0
  94. package/src/parser/composables/useCborTag.ts +739 -0
  95. package/src/parser/index.ts +56 -0
  96. package/src/parser/types.ts +371 -0
  97. package/src/parser/utils/pathBuilder.ts +259 -0
  98. package/src/parser/utils.ts +398 -0
  99. package/src/utils/__tests__/logger.test.ts +186 -0
  100. package/src/utils/logger.ts +96 -0
@@ -0,0 +1,447 @@
1
+ /**
2
+ * CBOR Tag Parser Error Handling Tests
3
+ * Tests all error cases and edge cases for 100% code coverage
4
+ */
5
+
6
+ import { describe, it, expect } from 'vitest'
7
+ import { useCborTag } from '../composables/useCborTag'
8
+
9
+ describe('useCborTag - Error Handling', () => {
10
+ describe('parseItem - Unexpected End of Buffer', () => {
11
+ it('should throw error when buffer ends before tagged value', () => {
12
+ const { parseTag } = useCborTag()
13
+
14
+ // Tag 0 with no value following
15
+ expect(() => parseTag('c0')).toThrow('Unexpected end of buffer after tag 0')
16
+ })
17
+
18
+ it('should throw error when nested structure is incomplete', () => {
19
+ const { parseTag } = useCborTag()
20
+
21
+ // Tag with array that's incomplete
22
+ // c1 (tag 1) + 82 (array of 2) + 01 (element 1) - missing element 2
23
+ expect(() => parseTag('c18201')).toThrow('Unexpected end of buffer at offset')
24
+ })
25
+ })
26
+
27
+ describe('parseItem - Unknown Major Type', () => {
28
+ it('should never have unknown major type (defensive check)', () => {
29
+ const { parseTag } = useCborTag()
30
+
31
+ // All major types 0-7 should be handled correctly in tagged values
32
+ // MT 0-7 are all valid, so this defensive code path is hard to trigger
33
+ // without mocking, but the code exists for safety
34
+
35
+ // Test that all major types work correctly when tagged
36
+ expect(() => parseTag('c000')).not.toThrow() // Tag(0)
37
+ expect(() => parseTag('c020')).not.toThrow() // Tag(-1)
38
+ expect(() => parseTag('c040')).not.toThrow() // Tag(h'')
39
+ expect(() => parseTag('c060')).not.toThrow() // Tag("")
40
+ expect(() => parseTag('c080')).not.toThrow() // Tag([])
41
+ expect(() => parseTag('c0a0')).not.toThrow() // Tag({})
42
+ expect(() => parseTag('c0c000')).not.toThrow() // Tag(Tag(0))
43
+ expect(() => parseTag('c0f4')).not.toThrow() // Tag(false)
44
+ })
45
+ })
46
+
47
+ describe('parseItem - Simple Values in Tags', () => {
48
+ it('should parse tag with boolean values', () => {
49
+ const { parseTag } = useCborTag()
50
+
51
+ // Tag with false
52
+ const result1 = parseTag('c0f4')
53
+ expect(result1.value).toEqual({ tag: 0, value: false })
54
+
55
+ // Tag with true
56
+ const result2 = parseTag('c0f5')
57
+ expect(result2.value).toEqual({ tag: 0, value: true })
58
+ })
59
+
60
+ it('should parse tag with null', () => {
61
+ const { parseTag } = useCborTag()
62
+
63
+ // Tag with null
64
+ const result = parseTag('c0f6')
65
+ expect(result.value).toEqual({ tag: 0, value: null })
66
+ })
67
+
68
+ it('should parse tag with undefined', () => {
69
+ const { parseTag } = useCborTag()
70
+
71
+ // Tag with undefined
72
+ const result = parseTag('c0f7')
73
+ expect(result.value).toEqual({ tag: 0, value: undefined })
74
+ })
75
+
76
+ it('should parse tag with unassigned simple values', () => {
77
+ const { parseTag } = useCborTag()
78
+
79
+ // Tag with simple(16)
80
+ const result = parseTag('c0f0')
81
+ expect(result.value).toEqual({ tag: 0, value: { simpleValue: 16 } })
82
+ })
83
+ })
84
+
85
+ describe('parseArrayInternal - Wrong Major Type', () => {
86
+ it('should throw error when expecting array but gets integer', () => {
87
+ const { parseTag } = useCborTag()
88
+
89
+ // This is hard to trigger directly, but we can test by ensuring
90
+ // arrays in tagged values work correctly
91
+ // Tag 0 with array should work
92
+ expect(() => parseTag('c080')).not.toThrow()
93
+ })
94
+ })
95
+
96
+ describe('parseArrayInternal - All Length Encodings', () => {
97
+ it('should parse tag with 1-byte array length (AI 24)', () => {
98
+ const { parseTag } = useCborTag()
99
+
100
+ // Tag 0 with array of 24 elements (AI 24)
101
+ // c0 (tag 0) + 9818 (array, 24 elements, 1-byte length) + 24x 00
102
+ const result = parseTag('c09818' + '00'.repeat(24))
103
+ expect(result.value.value).toHaveLength(24)
104
+ })
105
+
106
+ it('should parse tag with 2-byte array length (AI 25)', () => {
107
+ const { parseTag } = useCborTag()
108
+
109
+ // Tag 0 with array of 256 elements (AI 25)
110
+ // c0 (tag 0) + 990100 (array, 256 elements, 2-byte length) + 256x 00
111
+ const result = parseTag('c0990100' + '00'.repeat(256))
112
+ expect(result.value.value).toHaveLength(256)
113
+ })
114
+
115
+ it('should parse tag with 4-byte array length (AI 26)', () => {
116
+ const { parseTag } = useCborTag()
117
+
118
+ // Tag 0 with array of 1000 elements (AI 26)
119
+ // c0 (tag 0) + 9a000003e8 (array, 1000 elements, 4-byte length) + 1000x 00
120
+ const result = parseTag('c09a000003e8' + '00'.repeat(1000))
121
+ expect(result.value.value).toHaveLength(1000)
122
+ })
123
+
124
+ it('should parse tag with 8-byte array length (AI 27)', () => {
125
+ const { parseTag } = useCborTag()
126
+
127
+ // Tag 0 with array of 100 elements (AI 27, 8-byte length)
128
+ // c0 (tag 0) + 9b0000000000000064 (array, 100 elements, 8-byte length) + 100x 00
129
+ const result = parseTag('c09b0000000000000064' + '00'.repeat(100))
130
+ expect(result.value.value).toHaveLength(100)
131
+ })
132
+
133
+ it('should throw error for reserved AI 28 in array', () => {
134
+ const { parseTag } = useCborTag()
135
+
136
+ // Tag 0 with array using reserved AI 28
137
+ // c0 (tag 0) + 9c (array, AI 28 - reserved)
138
+ expect(() => parseTag('c09c')).toThrow('Invalid additional info for array: 28')
139
+ })
140
+
141
+ it('should throw error for reserved AI 29 in array', () => {
142
+ const { parseTag } = useCborTag()
143
+
144
+ // Tag 0 with array using reserved AI 29
145
+ expect(() => parseTag('c09d')).toThrow('Invalid additional info for array: 29')
146
+ })
147
+
148
+ it('should throw error for reserved AI 30 in array', () => {
149
+ const { parseTag } = useCborTag()
150
+
151
+ // Tag 0 with array using reserved AI 30
152
+ expect(() => parseTag('c09e')).toThrow('Invalid additional info for array: 30')
153
+ })
154
+ })
155
+
156
+ describe('parseArrayInternal - Indefinite Length', () => {
157
+ it('should parse tag with indefinite-length array', () => {
158
+ const { parseTag } = useCborTag()
159
+
160
+ // Tag 0 with indefinite array [_ 1, 2, 3]
161
+ // c0 (tag 0) + 9f (indefinite array) + 01 02 03 + ff (break)
162
+ const result = parseTag('c09f010203ff')
163
+ expect(Array.isArray(result.value.value)).toBe(true)
164
+ const arr = result.value.value as any[]
165
+ expect(arr[0]).toBe(1)
166
+ expect(arr[1]).toBe(2)
167
+ expect(arr[2]).toBe(3)
168
+ })
169
+
170
+ it('should parse tag with empty indefinite array', () => {
171
+ const { parseTag } = useCborTag()
172
+
173
+ // Tag 0 with empty indefinite array
174
+ // c0 (tag 0) + 9f (indefinite array) + ff (break)
175
+ const result = parseTag('c09fff')
176
+ expect(Array.isArray(result.value.value)).toBe(true)
177
+ expect(result.value.value).toHaveLength(0)
178
+ })
179
+
180
+ it('should parse nested indefinite arrays in tag', () => {
181
+ const { parseTag } = useCborTag()
182
+
183
+ // Tag 0 with nested indefinite arrays
184
+ // c0 (tag 0) + 9f (indefinite) + 9f 01 ff (nested [_ 1]) + ff (break)
185
+ const result = parseTag('c09f9f01ffff')
186
+ expect(Array.isArray(result.value.value)).toBe(true)
187
+ const arr = result.value.value as any[]
188
+ expect(arr).toHaveLength(1)
189
+ expect(Array.isArray(arr[0])).toBe(true)
190
+ expect(arr[0][0]).toBe(1)
191
+ })
192
+ })
193
+
194
+ describe('parseMapInternal - Wrong Major Type', () => {
195
+ it('should parse tag with map correctly', () => {
196
+ const { parseTag } = useCborTag()
197
+
198
+ // Tag 0 with map
199
+ expect(() => parseTag('c0a0')).not.toThrow()
200
+ })
201
+ })
202
+
203
+ describe('parseMapInternal - All Length Encodings', () => {
204
+ it('should parse tag with 1-byte map length (AI 24)', () => {
205
+ const { parseTag } = useCborTag()
206
+
207
+ // Tag 0 with map of 24 entries (AI 24)
208
+ // c0 (tag 0) + b818 (map, 24 entries, 1-byte length) + 24x unique key-value pairs
209
+ // Use keys 0-23 which are safe (direct encoding, no reserved values)
210
+ const entries = Array.from({ length: 24 }, (_, i) => {
211
+ // Use keys 0-23, values 0-23 (but different from key)
212
+ const key = i.toString(16).padStart(2, '0')
213
+ const value = ((i + 1) % 24).toString(16).padStart(2, '0')
214
+ return key + value
215
+ }).join('')
216
+ const result = parseTag('c0b818' + entries)
217
+ // Should have 24 entries (keys 0-23 are unique)
218
+ expect(result.value.value.size).toBe(24)
219
+ })
220
+
221
+ it('should parse tag with 2-byte map length (AI 25)', () => {
222
+ const { parseTag } = useCborTag()
223
+
224
+ // Tag 0 with map of 100 entries (AI 25)
225
+ // c0 (tag 0) + b90064 (map, 100 entries, 2-byte length) + 100x unique key-value pairs
226
+ // Use 1-byte length encoding for keys to avoid reserved AI values
227
+ const entries = Array.from({ length: 100 }, (_, i) => {
228
+ // Use AI 24 encoding: 1818 for value 24, 1819 for 25, etc.
229
+ const keyHex = '18' + (24 + i).toString(16).padStart(2, '0')
230
+ const valueHex = '18' + (124 + i).toString(16).padStart(2, '0')
231
+ return keyHex + valueHex
232
+ }).join('')
233
+ const result = parseTag('c0b90064' + entries)
234
+ expect(result.value.value.size).toBe(100)
235
+ })
236
+
237
+ it('should parse tag with 4-byte map length (AI 26)', () => {
238
+ const { parseTag } = useCborTag()
239
+
240
+ // Tag 0 with map of 50 entries (AI 26, 4-byte length)
241
+ // c0 (tag 0) + ba00000032 (map, 50 entries, 4-byte length) + 50x unique key-value pairs
242
+ const entries = Array.from({ length: 50 }, (_, i) => {
243
+ const keyHex = '18' + (30 + i).toString(16).padStart(2, '0')
244
+ const valueHex = '18' + (80 + i).toString(16).padStart(2, '0')
245
+ return keyHex + valueHex
246
+ }).join('')
247
+ const result = parseTag('c0ba00000032' + entries)
248
+ expect(result.value.value.size).toBe(50)
249
+ })
250
+
251
+ it('should parse tag with 8-byte map length (AI 27)', () => {
252
+ const { parseTag } = useCborTag()
253
+
254
+ // Tag 0 with map of 30 entries (AI 27, 8-byte length)
255
+ // c0 (tag 0) + bb000000000000001e (map, 30 entries, 8-byte length) + 30x unique key-value pairs
256
+ const entries = Array.from({ length: 30 }, (_, i) => {
257
+ const keyHex = '18' + (40 + i).toString(16).padStart(2, '0')
258
+ const valueHex = '18' + (70 + i).toString(16).padStart(2, '0')
259
+ return keyHex + valueHex
260
+ }).join('')
261
+ const result = parseTag('c0bb000000000000001e' + entries)
262
+ expect(result.value.value.size).toBe(30)
263
+ })
264
+
265
+ it('should throw error for reserved AI 28 in map', () => {
266
+ const { parseTag } = useCborTag()
267
+
268
+ // Tag 0 with map using reserved AI 28
269
+ // c0 (tag 0) + bc (map, AI 28 - reserved)
270
+ expect(() => parseTag('c0bc')).toThrow('Invalid additional info for map: 28')
271
+ })
272
+
273
+ it('should throw error for reserved AI 29 in map', () => {
274
+ const { parseTag } = useCborTag()
275
+
276
+ // Tag 0 with map using reserved AI 29
277
+ expect(() => parseTag('c0bd')).toThrow('Invalid additional info for map: 29')
278
+ })
279
+
280
+ it('should throw error for reserved AI 30 in map', () => {
281
+ const { parseTag } = useCborTag()
282
+
283
+ // Tag 0 with map using reserved AI 30
284
+ expect(() => parseTag('c0be')).toThrow('Invalid additional info for map: 30')
285
+ })
286
+ })
287
+
288
+ describe('parseMapInternal - Indefinite Length', () => {
289
+ it('should parse tag with indefinite-length map', () => {
290
+ const { parseTag } = useCborTag()
291
+
292
+ // Tag 0 with indefinite map {_ 1: 2, 3: 4}
293
+ // c0 (tag 0) + bf (indefinite map) + 01 02 03 04 + ff (break)
294
+ const result = parseTag('c0bf01020304ff')
295
+ expect(result.value.value).toEqual(new Map([[1, 2], [3, 4]]))
296
+ })
297
+
298
+ it('should parse tag with empty indefinite map', () => {
299
+ const { parseTag } = useCborTag()
300
+
301
+ // Tag 0 with empty indefinite map
302
+ // c0 (tag 0) + bf (indefinite map) + ff (break)
303
+ const result = parseTag('c0bfff')
304
+ expect(result.value.value).toEqual(new Map())
305
+ })
306
+
307
+ it('should parse nested indefinite maps in tag', () => {
308
+ const { parseTag } = useCborTag()
309
+
310
+ // Tag 0 with nested indefinite map
311
+ // c0 (tag 0) + bf (indefinite) + 01 (key) + bf 02 03 ff (nested map) + ff (break)
312
+ const result = parseTag('c0bf01bf0203ffff')
313
+ expect(result.value.value).toEqual(new Map([[1, new Map([[2, 3]])]]))
314
+ })
315
+ })
316
+
317
+ describe('parseTagNumber - Large Tag Numbers', () => {
318
+ it('should parse tag with 1-byte number (AI 24)', () => {
319
+ const { parseTag } = useCborTag()
320
+
321
+ // Tag 255 (largest 1-byte tag)
322
+ // d8ff (tag 255) + 00 (value)
323
+ const result = parseTag('d8ff00')
324
+ expect(result.value.tag).toBe(255)
325
+ })
326
+
327
+ it('should parse tag with 2-byte number (AI 25)', () => {
328
+ const { parseTag } = useCborTag()
329
+
330
+ // Tag 256 (smallest 2-byte tag)
331
+ // d90100 (tag 256) + 00 (value)
332
+ const result = parseTag('d9010000')
333
+ expect(result.value.tag).toBe(256)
334
+ })
335
+
336
+ it('should parse tag with 4-byte number (AI 26)', () => {
337
+ const { parseTag } = useCborTag()
338
+
339
+ // Tag 70000 (requires 4 bytes)
340
+ // da00011170 (tag 70000) + 00 (value)
341
+ const result = parseTag('da0001117000')
342
+ expect(result.value.tag).toBe(70000)
343
+ })
344
+
345
+ it('should parse tag with 8-byte number (AI 27)', () => {
346
+ const { parseTag } = useCborTag()
347
+
348
+ // Tag with 8-byte number (within safe integer range)
349
+ // db0000000000000064 (tag 100, 8-byte encoding) + 00 (value)
350
+ const result = parseTag('db000000000000006400')
351
+ expect(result.value.tag).toBe(100)
352
+ })
353
+
354
+ it('should throw error for tag number exceeding MAX_SAFE_INTEGER', () => {
355
+ const { parseTag } = useCborTag()
356
+
357
+ // Tag number larger than MAX_SAFE_INTEGER (2^53 - 1)
358
+ // db0020000000000000 (tag 2^53) + 00 (value)
359
+ expect(() => parseTag('db002000000000000000')).toThrow('Tag number')
360
+ expect(() => parseTag('db002000000000000000')).toThrow('exceeds maximum safe integer')
361
+ })
362
+
363
+ it('should throw error for reserved AI 28', () => {
364
+ const { parseTag } = useCborTag()
365
+
366
+ // MT 6, AI 28 - reserved
367
+ expect(() => parseTag('dc')).toThrow('Reserved additional info 28 for major type 6')
368
+ })
369
+
370
+ it('should throw error for reserved AI 29', () => {
371
+ const { parseTag } = useCborTag()
372
+
373
+ // MT 6, AI 29 - reserved
374
+ expect(() => parseTag('dd')).toThrow('Reserved additional info 29 for major type 6')
375
+ })
376
+
377
+ it('should throw error for reserved AI 30', () => {
378
+ const { parseTag } = useCborTag()
379
+
380
+ // MT 6, AI 30 - reserved
381
+ expect(() => parseTag('de')).toThrow('Reserved additional info 30 for major type 6')
382
+ })
383
+
384
+ it('should throw error for AI 31 (break marker invalid for tags)', () => {
385
+ const { parseTag } = useCborTag()
386
+
387
+ // MT 6, AI 31 - invalid for tags (break marker)
388
+ expect(() => parseTag('df')).toThrow('Invalid additional info 31 for tags')
389
+ })
390
+ })
391
+
392
+ describe('Nested Tags', () => {
393
+ it('should parse doubly-nested tags', () => {
394
+ const { parseTag } = useCborTag()
395
+
396
+ // Tag 0 containing Tag 1 containing 100
397
+ // c0 (tag 0) + c1 (tag 1) + 1864 (100)
398
+ const result = parseTag('c0c11864')
399
+ expect(result.value).toEqual({
400
+ tag: 0,
401
+ value: { tag: 1, value: 100 }
402
+ })
403
+ })
404
+
405
+ it('should parse triply-nested tags', () => {
406
+ const { parseTag } = useCborTag()
407
+
408
+ // Tag 0 containing Tag 1 containing Tag 2 containing 0
409
+ // c0 (tag 0) + c1 (tag 1) + c2 (tag 2) + 00 (0)
410
+ const result = parseTag('c0c1c200')
411
+ expect(result.value).toEqual({
412
+ tag: 0,
413
+ value: { tag: 1, value: { tag: 2, value: 0 } }
414
+ })
415
+ })
416
+ })
417
+
418
+ describe('parse() alias', () => {
419
+ it('should work identically to parseTag', () => {
420
+ const { parse, parseTag } = useCborTag()
421
+
422
+ const hexString = 'c11a514b67b0' // Tag 1 with epoch timestamp
423
+
424
+ const parseResult = parse(hexString)
425
+ const parseTagResult = parseTag(hexString)
426
+
427
+ expect(parseResult.value).toEqual(parseTagResult.value)
428
+ expect(parseResult.bytesRead).toBe(parseTagResult.bytesRead)
429
+ })
430
+ })
431
+
432
+ describe('Wrong Major Type for Tag', () => {
433
+ it('should throw error when major type is not 6', () => {
434
+ const { parseTag } = useCborTag()
435
+
436
+ // MT 0 (integer) is not a tag
437
+ expect(() => parseTag('00')).toThrow('Expected major type 6 (tag), got 0')
438
+ })
439
+
440
+ it('should throw error for MT 7 (simple value)', () => {
441
+ const { parseTag } = useCborTag()
442
+
443
+ // MT 7 (simple value) is not a tag
444
+ expect(() => parseTag('f4')).toThrow('Expected major type 6 (tag), got 7')
445
+ })
446
+ })
447
+ })