@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,311 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { useCborParser } from '../composables/useCborParser'
3
+
4
+ /**
5
+ * AST Tree Structure Tests
6
+ *
7
+ * These tests verify that the source map produces proper AST structure where:
8
+ * - Each node shows only its header bytes (type + length encoding)
9
+ * - Content is separated from headers
10
+ * - Nested structures are properly represented
11
+ */
12
+ describe('AST Tree Structure', () => {
13
+ const { parseWithSourceMap } = useCborParser()
14
+
15
+ describe('Byte String AST Structure', () => {
16
+ it('should split byte string into header and content', () => {
17
+ // 54 = bytes(20)
18
+ // 736c6f6fc9942073e1b4892073e1b489c9a5ca87 = 20 bytes of data
19
+ const hex = '54736c6f6fc9942073e1b4892073e1b489c9a5ca87'
20
+ const result = parseWithSourceMap(hex)
21
+
22
+ expect(result.sourceMap).toHaveLength(2)
23
+
24
+ // Header entry (container)
25
+ const headerEntry = result.sourceMap[0]
26
+ expect(headerEntry.type).toBe('bytes(20)')
27
+ expect(headerEntry.start).toBe(0)
28
+ expect(headerEntry.end).toBe(1) // Only the "54" byte
29
+ expect(headerEntry.isHeader).toBe(true)
30
+ expect(headerEntry.headerEnd).toBe(1)
31
+ expect(headerEntry.contentPath).toBe('#content')
32
+ expect(headerEntry.children).toEqual(['#content']) // Header is a container
33
+
34
+ // Content entry (child of header)
35
+ const contentEntry = result.sourceMap[1]
36
+ expect(contentEntry.type).toBe('→ 20 bytes')
37
+ expect(contentEntry.start).toBe(1)
38
+ expect(contentEntry.end).toBe(21) // The 20 data bytes
39
+ expect(contentEntry.isContent).toBe(true)
40
+ expect(contentEntry.parent).toBe('') // Parent is the header
41
+ })
42
+
43
+ it('should handle empty byte string', () => {
44
+ // 40 = bytes(0)
45
+ const hex = '40'
46
+ const result = parseWithSourceMap(hex)
47
+
48
+ expect(result.sourceMap).toHaveLength(1)
49
+
50
+ // Only header, no content
51
+ const headerEntry = result.sourceMap[0]
52
+ expect(headerEntry.type).toBe('bytes(0)')
53
+ expect(headerEntry.start).toBe(0)
54
+ expect(headerEntry.end).toBe(1)
55
+ expect(headerEntry.isHeader).toBe(true)
56
+ expect(headerEntry.contentPath).toBeUndefined()
57
+ })
58
+ })
59
+
60
+ describe('Text String AST Structure', () => {
61
+ it('should split text string into header and content', () => {
62
+ // 64 = text(4)
63
+ // 49455446 = "IETF"
64
+ const hex = '6449455446'
65
+ const result = parseWithSourceMap(hex)
66
+
67
+ expect(result.sourceMap).toHaveLength(2)
68
+
69
+ // Header entry (container)
70
+ const headerEntry = result.sourceMap[0]
71
+ expect(headerEntry.type).toBe('text(4)')
72
+ expect(headerEntry.start).toBe(0)
73
+ expect(headerEntry.end).toBe(1) // Only the "64" byte
74
+ expect(headerEntry.isHeader).toBe(true)
75
+ expect(headerEntry.headerEnd).toBe(1)
76
+ expect(headerEntry.children).toEqual(['#content']) // Header is a container
77
+
78
+ // Content entry (child of header)
79
+ const contentEntry = result.sourceMap[1]
80
+ expect(contentEntry.type).toBe('→ "IETF"')
81
+ expect(contentEntry.start).toBe(1)
82
+ expect(contentEntry.end).toBe(5) // The 4 text bytes
83
+ expect(contentEntry.isContent).toBe(true)
84
+ expect(contentEntry.parent).toBe('') // Parent is the header
85
+ })
86
+ })
87
+
88
+ describe('Array AST Structure', () => {
89
+ it('should show array header separate from elements', () => {
90
+ // 82 = array(2)
91
+ // 01 = int 1
92
+ // 02 = int 2
93
+ const hex = '820102'
94
+ const result = parseWithSourceMap(hex)
95
+
96
+ // Should have: array header, element[0], element[1]
97
+ expect(result.sourceMap).toHaveLength(3)
98
+
99
+ // Array header
100
+ const arrayEntry = result.sourceMap[0]
101
+ expect(arrayEntry.type).toBe('array(2)')
102
+ expect(arrayEntry.start).toBe(0)
103
+ expect(arrayEntry.end).toBe(1) // Only the "82" byte
104
+ expect(arrayEntry.isHeader).toBe(true)
105
+ expect(arrayEntry.headerEnd).toBe(1)
106
+ expect(arrayEntry.children).toEqual(['[0]', '[1]'])
107
+
108
+ // First element
109
+ const elem0 = result.sourceMap[1]
110
+ expect(elem0.path).toBe('[0]')
111
+ expect(elem0.start).toBe(1)
112
+ expect(elem0.end).toBe(2) // The "01" byte
113
+ expect(elem0.parent).toBe('')
114
+
115
+ // Second element
116
+ const elem1 = result.sourceMap[2]
117
+ expect(elem1.path).toBe('[1]')
118
+ expect(elem1.start).toBe(2)
119
+ expect(elem1.end).toBe(3) // The "02" byte
120
+ expect(elem1.parent).toBe('')
121
+ })
122
+
123
+ it('should handle nested arrays with proper AST structure', () => {
124
+ // 82 = array(2)
125
+ // 82 = array(2)
126
+ // 58 20 = bytes(32)
127
+ // 00 = int 0
128
+ // 01 = int 1
129
+ const hex = '8282582048bd01d51e580cde15afa6d28f63d89c9137b93a910e5941192e26b129061067000001'
130
+ const result = parseWithSourceMap(hex)
131
+
132
+ // Outer array header
133
+ const outerArray = result.sourceMap[0]
134
+ expect(outerArray.type).toBe('array(2)')
135
+ expect(outerArray.start).toBe(0)
136
+ expect(outerArray.end).toBe(1) // Only first "82"
137
+ expect(outerArray.isHeader).toBe(true)
138
+
139
+ // Inner array header (first element of outer array)
140
+ const innerArray = result.sourceMap.find(e => e.path === '[0]' && e.type.startsWith('array'))
141
+ expect(innerArray).toBeDefined()
142
+ expect(innerArray!.type).toBe('array(2)')
143
+ expect(innerArray!.start).toBe(1)
144
+ expect(innerArray!.end).toBe(2) // Only second "82"
145
+ expect(innerArray!.isHeader).toBe(true)
146
+ expect(innerArray!.children).toEqual(['[0][0]', '[0][1]'])
147
+
148
+ // Byte string header inside inner array
149
+ const bytesHeader = result.sourceMap.find(e => e.path === '[0][0]' && e.isHeader === true)
150
+ expect(bytesHeader).toBeDefined()
151
+ expect(bytesHeader!.type).toBe('bytes(32)')
152
+ expect(bytesHeader!.start).toBe(2)
153
+ expect(bytesHeader!.end).toBe(4) // "58 20"
154
+ expect(bytesHeader!.isHeader).toBe(true)
155
+
156
+ // Byte string content
157
+ const bytesContent = result.sourceMap.find(e => e.path === '[0][0]#content')
158
+ expect(bytesContent).toBeDefined()
159
+ expect(bytesContent!.start).toBe(4)
160
+ expect(bytesContent!.end).toBe(36) // 32 bytes of data
161
+ expect(bytesContent!.isContent).toBe(true)
162
+
163
+ // Integer inside inner array
164
+ const intEntry = result.sourceMap.find(e => e.path === '[0][1]')
165
+ expect(intEntry).toBeDefined()
166
+ expect(intEntry!.start).toBe(36)
167
+ expect(intEntry!.end).toBe(37) // The "00" byte
168
+
169
+ // Second element of outer array
170
+ const secondElem = result.sourceMap.find(e => e.path === '[1]' && !e.type.startsWith('array'))
171
+ expect(secondElem).toBeDefined()
172
+ expect(secondElem!.start).toBe(37)
173
+ expect(secondElem!.end).toBe(38) // The "01" byte
174
+ })
175
+ })
176
+
177
+ describe('Map AST Structure', () => {
178
+ it('should show map header separate from key-value pairs', () => {
179
+ // a1 = map(1)
180
+ // 61 61 = text(1) "a"
181
+ // 01 = int 1
182
+ const hex = 'a16161 01'
183
+ const result = parseWithSourceMap(hex.replace(/\s/g, ''))
184
+
185
+ // Map header
186
+ const mapEntry = result.sourceMap[0]
187
+ expect(mapEntry.type).toBe('map(1)')
188
+ expect(mapEntry.start).toBe(0)
189
+ expect(mapEntry.end).toBe(1) // Only the "a1" byte
190
+ expect(mapEntry.isHeader).toBe(true)
191
+ expect(mapEntry.headerEnd).toBe(1)
192
+
193
+ // Should have children pointing to values (not keys)
194
+ expect(mapEntry.children).toBeDefined()
195
+ expect(mapEntry.children!.length).toBe(1)
196
+ })
197
+ })
198
+
199
+ describe('Tag AST Structure', () => {
200
+ it('should show tag header separate from tagged value', () => {
201
+ // c2 = tag(2) - bignum
202
+ // 41 ff = bytes(1) with value 0xff
203
+ const hex = 'c241ff'
204
+ const result = parseWithSourceMap(hex)
205
+
206
+ // Tag header
207
+ const tagEntry = result.sourceMap[0]
208
+ expect(tagEntry.type).toBe('tag(2)')
209
+ expect(tagEntry.start).toBe(0)
210
+ expect(tagEntry.end).toBe(1) // Only the "c2" byte
211
+ expect(tagEntry.isHeader).toBe(true)
212
+ expect(tagEntry.headerEnd).toBe(1)
213
+ expect(tagEntry.children).toEqual(['.value'])
214
+
215
+ // Tagged value - bytes header
216
+ const bytesHeader = result.sourceMap.find(e => e.path === '.value' && e.isHeader === true)
217
+ expect(bytesHeader).toBeDefined()
218
+ expect(bytesHeader!.type).toBe('bytes(1)')
219
+ expect(bytesHeader!.start).toBe(1)
220
+ expect(bytesHeader!.end).toBe(2) // The "41" byte
221
+ expect(bytesHeader!.parent).toBe('')
222
+
223
+ // Bytes content
224
+ const bytesContent = result.sourceMap.find(e => e.path === '.value#content')
225
+ expect(bytesContent).toBeDefined()
226
+ expect(bytesContent!.start).toBe(2)
227
+ expect(bytesContent!.end).toBe(3) // The "ff" byte
228
+ })
229
+
230
+ it('should handle tag 258 (Cardano set) with proper AST', () => {
231
+ // d9 0102 = tag(258)
232
+ // 81 = array(1)
233
+ // 01 = int 1
234
+ const hex = 'd901028101'
235
+ const result = parseWithSourceMap(hex)
236
+
237
+ // Tag header
238
+ const tagEntry = result.sourceMap[0]
239
+ expect(tagEntry.type).toBe('tag(258)')
240
+ expect(tagEntry.start).toBe(0)
241
+ expect(tagEntry.end).toBe(3) // "d9 01 02"
242
+ expect(tagEntry.isHeader).toBe(true)
243
+ expect(tagEntry.headerEnd).toBe(3)
244
+
245
+ // Array header (tagged value)
246
+ const arrayEntry = result.sourceMap.find(e => e.path === '.value' && e.type === 'array(1)')
247
+ expect(arrayEntry).toBeDefined()
248
+ expect(arrayEntry!.start).toBe(3)
249
+ expect(arrayEntry!.end).toBe(4) // Only the "81" byte
250
+ expect(arrayEntry!.isHeader).toBe(true)
251
+ expect(arrayEntry!.parent).toBe('')
252
+ })
253
+ })
254
+
255
+ describe('Complex Cardano Transaction Example', () => {
256
+ it('should produce proper AST for real Cardano UTXO', () => {
257
+ // This is the user's example from the conversation:
258
+ // 82 82 58 20 48bd... 00 82 58 39 00dae... 1a 000f4240
259
+ const hex = '8282582048bd01d51e580cde15afa6d28f63d89c9137b93a910e5941192e26b12906106700825839000dae074cac48222800da644971a35b68832abb40b619643efde77dc9db8ec58f9fa297093e286f81d37bea7154209064956254d5d4e2108d1a000f4240'
260
+ const result = parseWithSourceMap(hex)
261
+
262
+ // Outer array: 82 at offset 0
263
+ const outerArray = result.sourceMap[0]
264
+ expect(outerArray.type).toBe('array(2)')
265
+ expect(outerArray.start).toBe(0)
266
+ expect(outerArray.end).toBe(1) // Only first "82"
267
+ expect(outerArray.isHeader).toBe(true)
268
+
269
+ // First inner array: 82 at offset 1
270
+ const firstInnerArray = result.sourceMap.find(e => e.path === '[0]' && e.type === 'array(2)')
271
+ expect(firstInnerArray).toBeDefined()
272
+ expect(firstInnerArray!.start).toBe(1)
273
+ expect(firstInnerArray!.end).toBe(2) // Only second "82"
274
+ expect(firstInnerArray!.isHeader).toBe(true)
275
+
276
+ // Byte string header: 58 20 at offset 2-3
277
+ const bytesHeader = result.sourceMap.find(e => e.path === '[0][0]' && e.isHeader === true)
278
+ expect(bytesHeader).toBeDefined()
279
+ expect(bytesHeader!.type).toBe('bytes(32)')
280
+ expect(bytesHeader!.start).toBe(2)
281
+ expect(bytesHeader!.end).toBe(4) // "58 20"
282
+ expect(bytesHeader!.isHeader).toBe(true)
283
+
284
+ // Byte string content: 32 bytes starting at offset 4
285
+ const bytesContent = result.sourceMap.find(e => e.path === '[0][0]#content')
286
+ expect(bytesContent).toBeDefined()
287
+ expect(bytesContent!.start).toBe(4)
288
+ expect(bytesContent!.end).toBe(36) // 32 bytes
289
+ expect(bytesContent!.isContent).toBe(true)
290
+
291
+ // Integer: 00 at offset 36
292
+ const intEntry = result.sourceMap.find(e => e.path === '[0][1]')
293
+ expect(intEntry).toBeDefined()
294
+ expect(intEntry!.start).toBe(36)
295
+ expect(intEntry!.end).toBe(37)
296
+
297
+ // Second inner array: 82 at offset 37
298
+ const secondInnerArray = result.sourceMap.find(e => e.path === '[1]' && e.type === 'array(2)')
299
+ expect(secondInnerArray).toBeDefined()
300
+ expect(secondInnerArray!.start).toBe(37)
301
+ expect(secondInnerArray!.end).toBe(38) // Only the "82" byte
302
+ expect(secondInnerArray!.isHeader).toBe(true)
303
+
304
+ // Verify proper parent-child relationships
305
+ expect(firstInnerArray!.parent).toBe('')
306
+ expect(secondInnerArray!.parent).toBe('')
307
+ expect(bytesHeader!.parent).toBe('[0]')
308
+ expect(bytesContent!.parent).toBe('[0][0]')
309
+ })
310
+ })
311
+ })
@@ -0,0 +1,296 @@
1
+ /**
2
+ * CBOR Collection 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 { useCborCollection } from '../composables/useCborCollection'
8
+
9
+ describe('useCborCollection - Error Handling', () => {
10
+ describe('parseItem - Unexpected End of Buffer', () => {
11
+ it('should throw error when offset is at end of buffer', () => {
12
+ const { parseArray } = useCborCollection()
13
+
14
+ // Array with 2 elements, but only 1 element provided
15
+ // 82 (array of 2) + 01 (element 1) - missing element 2
16
+ expect(() => parseArray('8201')).toThrow('Unexpected end of buffer')
17
+ })
18
+
19
+ it('should throw error when buffer ends mid-element', () => {
20
+ const { parseArray } = useCborCollection()
21
+
22
+ // Array with nested array that's incomplete
23
+ // 82 (array of 2) + 01 (element 1) + 82 (nested array of 2, incomplete)
24
+ expect(() => parseArray('820182')).toThrow('Unexpected end of buffer')
25
+ })
26
+ })
27
+
28
+ describe('parseItem - Tag Parsing Support', () => {
29
+ it('should successfully parse array containing a tag', () => {
30
+ const { parseArray } = useCborCollection()
31
+
32
+ // Array containing a tag (now properly supported)
33
+ // 81 (array of 1) + c0 (tag 0) + 00 (value)
34
+ const result = parseArray('81c000')
35
+ expect(result.value).toBeInstanceOf(Array)
36
+ expect(result.value).toHaveLength(1)
37
+ expect(result.value[0].tag).toBe(0)
38
+ expect(result.value[0].value).toBe(0)
39
+ })
40
+
41
+ it('should successfully parse map with tag as value', () => {
42
+ const { parseMap } = useCborCollection()
43
+
44
+ // Map with tag as value (now properly supported)
45
+ // a1 (map of 1) + 01 (key) + c0 (tag 0) + 00 (value)
46
+ const result = parseMap('a101c000')
47
+ expect(result.value.get(1)).toBeDefined()
48
+ expect(result.value.get(1).tag).toBe(0)
49
+ expect(result.value.get(1).value).toBe(0)
50
+ })
51
+ })
52
+
53
+ describe('parseLength - All Encoding Sizes for Collections', () => {
54
+ it('should parse array with 4-byte length (AI 26)', () => {
55
+ const { parseArray } = useCborCollection()
56
+
57
+ // Array with 1000 elements (using 4-byte length)
58
+ // 9a (MT 4, AI 26) + 000003e8 (1000 in 4 bytes) + 1000x 00 (zeros)
59
+ const elements = '00'.repeat(1000)
60
+ const result = parseArray('9a000003e8' + elements)
61
+ expect(result.value.length).toBe(1000)
62
+ })
63
+
64
+ it('should parse array with 8-byte length (AI 27)', () => {
65
+ const { parseArray } = useCborCollection()
66
+
67
+ // Array with 100 elements (using 8-byte length)
68
+ // 9b (MT 4, AI 27) + 0000000000000064 (100 in 8 bytes) + 100x 00
69
+ const elements = '00'.repeat(100)
70
+ const result = parseArray('9b0000000000000064' + elements)
71
+ expect(result.value.length).toBe(100)
72
+ })
73
+
74
+ it('should parse map with 4-byte length (AI 26)', () => {
75
+ const { parseMap } = useCborCollection()
76
+
77
+ // Map with 100 entries (using 4-byte length)
78
+ // ba (MT 5, AI 26) + 00000064 (100 in 4 bytes) + 100x unique key-value pairs
79
+ // Use 1-byte encoding for keys to avoid reserved AI values (28-31)
80
+ const entries = Array.from({ length: 100 }, (_, i) => {
81
+ const keyHex = '18' + (24 + i).toString(16).padStart(2, '0')
82
+ const valueHex = '18' + (124 + i).toString(16).padStart(2, '0')
83
+ return keyHex + valueHex
84
+ }).join('')
85
+ const result = parseMap('ba00000064' + entries)
86
+ expect(result.value.size).toBe(100)
87
+ })
88
+
89
+ it('should parse map with 8-byte length (AI 27)', () => {
90
+ const { parseMap } = useCborCollection()
91
+
92
+ // Map with 50 entries (using 8-byte length)
93
+ // bb (MT 5, AI 27) + 0000000000000032 (50 in 8 bytes) + 50x unique key-value pairs
94
+ const entries = Array.from({ length: 50 }, (_, i) => {
95
+ const keyHex = '18' + (30 + i).toString(16).padStart(2, '0')
96
+ const valueHex = '18' + (80 + i).toString(16).padStart(2, '0')
97
+ return keyHex + valueHex
98
+ }).join('')
99
+ const result = parseMap('bb0000000000000032' + entries)
100
+ expect(result.value.size).toBe(50)
101
+ })
102
+ })
103
+
104
+ describe('parseLength - Invalid Additional Info', () => {
105
+ it('should throw error for array with reserved AI 28', () => {
106
+ const { parseArray } = useCborCollection()
107
+
108
+ // MT 4, AI 28 - reserved
109
+ expect(() => parseArray('9c')).toThrow('Invalid additional info: 28')
110
+ })
111
+
112
+ it('should throw error for array with reserved AI 29', () => {
113
+ const { parseArray } = useCborCollection()
114
+
115
+ // MT 4, AI 29 - reserved
116
+ expect(() => parseArray('9d')).toThrow('Invalid additional info: 29')
117
+ })
118
+
119
+ it('should throw error for array with reserved AI 30', () => {
120
+ const { parseArray } = useCborCollection()
121
+
122
+ // MT 4, AI 30 - reserved
123
+ expect(() => parseArray('9e')).toThrow('Invalid additional info: 30')
124
+ })
125
+
126
+ it('should throw error for map with reserved AI 28', () => {
127
+ const { parseMap } = useCborCollection()
128
+
129
+ // MT 5, AI 28 - reserved
130
+ expect(() => parseMap('bc')).toThrow('Invalid additional info: 28')
131
+ })
132
+
133
+ it('should throw error for map with reserved AI 29', () => {
134
+ const { parseMap } = useCborCollection()
135
+
136
+ // MT 5, AI 29 - reserved
137
+ expect(() => parseMap('bd')).toThrow('Invalid additional info: 29')
138
+ })
139
+
140
+ it('should throw error for map with reserved AI 30', () => {
141
+ const { parseMap } = useCborCollection()
142
+
143
+ // MT 5, AI 30 - reserved
144
+ expect(() => parseMap('be')).toThrow('Invalid additional info: 30')
145
+ })
146
+ })
147
+
148
+ describe('parseArray - Wrong Major Type', () => {
149
+ it('should throw error when major type is not 4', () => {
150
+ const { parseArray } = useCborCollection()
151
+
152
+ // MT 0 (integer) is not an array
153
+ expect(() => parseArray('00')).toThrow('Expected major type 4 (array), got 0')
154
+ })
155
+
156
+ it('should throw error for MT 5 (map)', () => {
157
+ const { parseArray } = useCborCollection()
158
+
159
+ // MT 5 (map) is not an array
160
+ expect(() => parseArray('a0')).toThrow('Expected major type 4 (array), got 5')
161
+ })
162
+ })
163
+
164
+ describe('parseMap - Wrong Major Type', () => {
165
+ it('should throw error when major type is not 5', () => {
166
+ const { parseMap } = useCborCollection()
167
+
168
+ // MT 0 (integer) is not a map
169
+ expect(() => parseMap('00')).toThrow('Expected major type 5 (map), got 0')
170
+ })
171
+
172
+ it('should throw error for MT 4 (array)', () => {
173
+ const { parseMap } = useCborCollection()
174
+
175
+ // MT 4 (array) is not a map
176
+ expect(() => parseMap('80')).toThrow('Expected major type 5 (map), got 4')
177
+ })
178
+ })
179
+
180
+ describe('parseArray - Insufficient Data', () => {
181
+ it('should throw error when array element is missing', () => {
182
+ const { parseArray } = useCborCollection()
183
+
184
+ // Array of 3, but only 2 elements
185
+ expect(() => parseArray('830102')).toThrow('Unexpected end of buffer while parsing array element 2/3')
186
+ })
187
+
188
+ it('should throw error when nested array is incomplete', () => {
189
+ const { parseArray } = useCborCollection()
190
+
191
+ // Array with incomplete nested structure
192
+ // 82 (array of 2) + 01 + 8301 (nested array of 3 with only 1 element)
193
+ expect(() => parseArray('8201830102')).toThrow('Unexpected end of buffer while parsing array element 2/3')
194
+ })
195
+ })
196
+
197
+ describe('parseMap - Insufficient Data', () => {
198
+ it('should throw error when map entry is missing', () => {
199
+ const { parseMap } = useCborCollection()
200
+
201
+ // Map of 2, but only 1 entry
202
+ expect(() => parseMap('a20102')).toThrow('Unexpected end of buffer while parsing map entry 1/2')
203
+ })
204
+
205
+ it('should throw error when map value is missing', () => {
206
+ const { parseMap } = useCborCollection()
207
+
208
+ // Map of 1, with key but no value
209
+ expect(() => parseMap('a101')).toThrow('Unexpected end of buffer while parsing map value for entry 0/1')
210
+ })
211
+
212
+ it('should throw error in indefinite map when value is missing after key', () => {
213
+ const { parseMap } = useCborCollection()
214
+
215
+ // Indefinite map with key but no value before break
216
+ // bf (indefinite map) + 01 (key) - missing value
217
+ expect(() => parseMap('bf01')).toThrow('Unexpected end of buffer while parsing map value')
218
+ })
219
+ })
220
+
221
+ describe('Indefinite-Length Collections', () => {
222
+ it('should parse indefinite array with proper break', () => {
223
+ const { parseArray } = useCborCollection()
224
+
225
+ // 9f (indefinite array) + 01 02 03 + ff (break)
226
+ const result = parseArray('9f010203ff')
227
+ // Use spread to get clean array without Symbol metadata
228
+ expect([...result.value]).toEqual([1, 2, 3])
229
+ })
230
+
231
+ it('should parse indefinite map with proper break', () => {
232
+ const { parseMap } = useCborCollection()
233
+
234
+ // bf (indefinite map) + 01 02 03 04 + ff (break)
235
+ const result = parseMap('bf01020304ff')
236
+ // Use spread to get clean map without Symbol metadata
237
+ expect(new Map([...result.value])).toEqual(new Map([[1, 2], [3, 4]]))
238
+ })
239
+
240
+ it('should parse nested indefinite structures', () => {
241
+ const { parseArray } = useCborCollection()
242
+
243
+ // 9f (indefinite array) + 9f 01 ff (nested indefinite array [1]) + ff (break)
244
+ const result = parseArray('9f9f01ffff')
245
+ // Use spread to get clean arrays without Symbol metadata
246
+ expect([...result.value].map(inner => [...inner])).toEqual([[1]])
247
+ })
248
+ })
249
+
250
+ describe('Complex Nested Structures', () => {
251
+ it('should parse deeply nested arrays', () => {
252
+ const { parseArray } = useCborCollection()
253
+
254
+ // [[[[1]]]]
255
+ const result = parseArray('81818181 01'.replace(/\s/g, ''))
256
+ expect(result.value).toEqual([[[[1]]]])
257
+ })
258
+
259
+ it('should parse map with array values', () => {
260
+ const { parseMap } = useCborCollection()
261
+
262
+ // {"a": [1, 2], "b": [3, 4]}
263
+ const result = parseMap('a261618201026162820304')
264
+ expect(result.value).toEqual(new Map([['a', [1, 2]], ['b', [3, 4]]]))
265
+ })
266
+
267
+ it('should parse array with map values', () => {
268
+ const { parseArray } = useCborCollection()
269
+
270
+ // [{"x": 1}, {"y": 2}]
271
+ const result = parseArray('82a16178 01a16179 02'.replace(/\s/g, ''))
272
+ expect(result.value).toEqual([new Map([['x', 1]]), new Map([['y', 2]])])
273
+ })
274
+ })
275
+
276
+ describe('All Major Types in Collections', () => {
277
+ it('should parse array containing all major types', () => {
278
+ const { parseArray } = useCborCollection()
279
+
280
+ // Array with: integer, negative, text string, byte string, array, map, simple
281
+ // [0, -1, "", h'', [], {}, false]
282
+ // 87 (array of 7) + 00 (0) + 20 (-1) + 60 ("") + 40 (h'') + 80 ([]) + a0 ({}) + f4 (false)
283
+ const result = parseArray('8700206040 80a0f4'.replace(/\s/g, ''))
284
+ expect(result.value).toEqual([0, -1, '', new Uint8Array([]), [], new Map(), false])
285
+ })
286
+
287
+ it('should handle map with various key types', () => {
288
+ const { parseMap } = useCborCollection()
289
+
290
+ // Map with integer and string keys
291
+ // {1: "a", "b": 2}
292
+ const result = parseMap('a2016161616202')
293
+ expect(result.value).toEqual(new Map([[1, 'a'], ['b', 2]]))
294
+ })
295
+ })
296
+ })