@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,146 @@
1
+ /**
2
+ * Extreme Edge Cases for 100% Coverage
3
+ * Tests for the last few uncovered lines
4
+ */
5
+
6
+ import { describe, it, expect } from 'vitest'
7
+ import { useCborInteger } from '../composables/useCborInteger'
8
+ import { useCborString } from '../composables/useCborString'
9
+
10
+ describe('Extreme Edge Cases', () => {
11
+ describe('useCborInteger - Line 81 coverage', () => {
12
+ it('should keep BigInt when converting negative BigInt that stays outside safe range', () => {
13
+ const { parseInteger } = useCborInteger()
14
+
15
+ // Test the else branch on line 83: when negValue as BigInt stays as BigInt
16
+ // We need a value where the rawValue is BigInt AND the result is BigInt
17
+ // -1 - rawValue where rawValue > MAX_SAFE_INTEGER
18
+ // This will keep it as BigInt
19
+
20
+ // Use a value just above MAX_SAFE_INTEGER
21
+ // MAX_SAFE_INTEGER is 9007199254740991 (0x001FFFFFFFFFFFFF)
22
+ // We want rawValue = 9007199254740992 (0x0020000000000000)
23
+ // Result: -1 - 9007199254740992 = -9007199254740993 (outside safe range)
24
+
25
+ const result = parseInteger('3b0020000000000000')
26
+ expect(typeof result.value).toBe('bigint')
27
+ expect(result.value).toBe(-9007199254740993n)
28
+ })
29
+
30
+ it('should convert large BigInt negative to number when it fits', () => {
31
+ const { parseInteger } = useCborInteger()
32
+
33
+ // Test line 80-81: when negValue >= MIN_SAFE_INTEGER and converts to Number
34
+ // MIN_SAFE_INTEGER = -9007199254740991
35
+ // We want: -1 - rawValue >= -9007199254740991
36
+ // So: rawValue <= 9007199254740990
37
+
38
+ // Use rawValue = 9007199254740990 (just at the boundary)
39
+ // -1 - 9007199254740990 = -9007199254740991 (exactly MIN_SAFE_INTEGER)
40
+ const result = parseInteger('3b001ffffffffffffe')
41
+
42
+ // Should be converted to Number
43
+ expect(typeof result.value).toBe('number')
44
+ expect(result.value).toBe(-9007199254740991)
45
+ })
46
+ })
47
+
48
+ describe('useCborString - Indefinite edge cases', () => {
49
+ it('should handle edge case with indefinite string chunks', () => {
50
+ const { parseByteString } = useCborString()
51
+
52
+ // Test indefinite byte string with empty chunks
53
+ // 5f (indefinite) + 40 (empty chunk) + 40 (empty chunk) + ff (break)
54
+ const buffer = new Uint8Array([0x5f, 0x40, 0x40, 0xff])
55
+ const result = parseByteString(buffer, 0)
56
+
57
+ // Parser returns structured object with chunk metadata for round-tripping
58
+ expect(result.value).toMatchObject({
59
+ type: 'cbor-byte-string',
60
+ bytes: new Uint8Array([]),
61
+ chunks: [new Uint8Array([]), new Uint8Array([])]
62
+ })
63
+ expect(result.bytesRead).toBe(4)
64
+ })
65
+
66
+ it('should handle indefinite text string with empty chunks', () => {
67
+ const { parseTextString } = useCborString()
68
+
69
+ // Test indefinite text string with empty chunks
70
+ // 7f (indefinite) + 60 (empty chunk) + 60 (empty chunk) + ff (break)
71
+ const buffer = new Uint8Array([0x7f, 0x60, 0x60, 0xff])
72
+ const result = parseTextString(buffer, 0)
73
+
74
+ // Parser returns structured object with chunk metadata for round-tripping
75
+ expect(result.value).toMatchObject({
76
+ type: 'cbor-text-string',
77
+ text: '',
78
+ chunks: ['', '']
79
+ })
80
+ expect(result.bytesRead).toBe(4)
81
+ })
82
+ })
83
+
84
+ describe('Length encoding edge cases', () => {
85
+ it('should use all length encoding paths', () => {
86
+ const { parseByteString } = useCborString()
87
+
88
+ // Direct encoding (0-23): 23 bytes
89
+ const buffer1 = new Uint8Array([0x57, ...new Array(23).fill(0)])
90
+ const result1 = parseByteString(buffer1, 0)
91
+ expect(result1.value.length).toBe(23)
92
+
93
+ // 1-byte length (AI 24): 100 bytes
94
+ const buffer2 = new Uint8Array([0x58, 100, ...new Array(100).fill(0)])
95
+ const result2 = parseByteString(buffer2, 0)
96
+ expect(result2.value.length).toBe(100)
97
+
98
+ // 2-byte length (AI 25): 256 bytes
99
+ const buffer3 = new Uint8Array([0x59, 0x01, 0x00, ...new Array(256).fill(0)])
100
+ const result3 = parseByteString(buffer3, 0)
101
+ expect(result3.value.length).toBe(256)
102
+
103
+ // 4-byte length (AI 26): 300 bytes
104
+ const buffer4 = new Uint8Array([0x5a, 0x00, 0x00, 0x01, 0x2c, ...new Array(300).fill(0)])
105
+ const result4 = parseByteString(buffer4, 0)
106
+ expect(result4.value.length).toBe(300)
107
+
108
+ // 8-byte length (AI 27): 500 bytes
109
+ const buffer5 = new Uint8Array([0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4, ...new Array(500).fill(0)])
110
+ const result5 = parseByteString(buffer5, 0)
111
+ expect(result5.value.length).toBe(500)
112
+ })
113
+ })
114
+
115
+ describe('Boundary value testing', () => {
116
+ it('should handle maximum safe integer boundaries', () => {
117
+ const { parseInteger } = useCborInteger()
118
+
119
+ // Maximum safe positive integer: 2^53 - 1 = 9007199254740991
120
+ const result1 = parseInteger('1b001fffffffffffff')
121
+ expect(typeof result1.value).toBe('number')
122
+ expect(result1.value).toBe(9007199254740991)
123
+
124
+ // Just beyond max safe integer: 2^53 = 9007199254740992
125
+ const result2 = parseInteger('1b0020000000000000')
126
+ expect(typeof result2.value).toBe('bigint')
127
+ expect(result2.value).toBe(9007199254740992n)
128
+ })
129
+
130
+ it('should handle minimum safe integer boundary for negatives', () => {
131
+ const { parseInteger } = useCborInteger()
132
+
133
+ // Minimum safe negative integer: -(2^53 - 1) = -9007199254740991
134
+ // Encoding: -1 - N, so N = 9007199254740990
135
+ const result1 = parseInteger('3b001ffffffffffffe')
136
+ expect(typeof result1.value).toBe('number')
137
+ expect(result1.value).toBe(-9007199254740991)
138
+
139
+ // Just beyond min safe integer: -9007199254740992
140
+ // Encoding: -1 - N, so N = 9007199254740991
141
+ const result2 = parseInteger('3b001fffffffffffff')
142
+ expect(typeof result2.value).toBe('bigint')
143
+ expect(result2.value).toBe(-9007199254740992n)
144
+ })
145
+ })
146
+ })
@@ -0,0 +1,256 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { PathBuilder } from '../utils/pathBuilder'
3
+
4
+ describe('PathBuilder', () => {
5
+ describe('basic path creation', () => {
6
+ it('should create root path', () => {
7
+ expect(PathBuilder.root()).toBe('')
8
+ })
9
+
10
+ it('should create array index paths', () => {
11
+ expect(PathBuilder.arrayIndex('', 0)).toBe('[0]')
12
+ expect(PathBuilder.arrayIndex('', 1)).toBe('[1]')
13
+ expect(PathBuilder.arrayIndex('[0]', 2)).toBe('[0][2]')
14
+ })
15
+
16
+ it('should create nested array paths', () => {
17
+ const path1 = PathBuilder.arrayIndex('', 0)
18
+ const path2 = PathBuilder.arrayIndex(path1, 1)
19
+ const path3 = PathBuilder.arrayIndex(path2, 2)
20
+
21
+ expect(path1).toBe('[0]')
22
+ expect(path2).toBe('[0][1]')
23
+ expect(path3).toBe('[0][1][2]')
24
+ })
25
+
26
+ it('should create map key paths', () => {
27
+ expect(PathBuilder.mapKey('', 'amount')).toBe('.amount')
28
+ expect(PathBuilder.mapKey('[0]', 'txHash')).toBe('[0].txHash')
29
+ expect(PathBuilder.mapKey('[0].data', 'value')).toBe('[0].data.value')
30
+ })
31
+
32
+ it('should escape special characters in keys', () => {
33
+ expect(PathBuilder.mapKey('', 'key.with.dots')).toBe('.key\\.with\\.dots')
34
+ expect(PathBuilder.mapKey('', 'key[0]')).toBe('.key\\[0\\]')
35
+ expect(PathBuilder.mapKey('', 'back\\slash')).toBe('.back\\\\slash')
36
+ })
37
+
38
+ it('should create map key index paths', () => {
39
+ expect(PathBuilder.mapKeyIndex('', 0)).toBe('[#key:0]')
40
+ expect(PathBuilder.mapKeyIndex('[0]', 1)).toBe('[0][#key:1]')
41
+ })
42
+ })
43
+
44
+ describe('special markers', () => {
45
+ it('should create header paths', () => {
46
+ expect(PathBuilder.header('')).toBe('[#header]')
47
+ expect(PathBuilder.header('[0]')).toBe('[0][#header]')
48
+ expect(PathBuilder.header('[0].data')).toBe('[0].data[#header]')
49
+ })
50
+
51
+ it('should create content paths', () => {
52
+ expect(PathBuilder.content('')).toBe('[#content]')
53
+ expect(PathBuilder.content('[0]')).toBe('[0][#content]')
54
+ expect(PathBuilder.content('[0][1]')).toBe('[0][1][#content]')
55
+ })
56
+
57
+ it('should create tag value paths', () => {
58
+ expect(PathBuilder.tagValue('')).toBe('[#value]')
59
+ expect(PathBuilder.tagValue('[0]')).toBe('[0][#value]')
60
+ })
61
+
62
+ it('should normalize paths by removing markers', () => {
63
+ expect(PathBuilder.normalize('[0][#header]')).toBe('[0]')
64
+ expect(PathBuilder.normalize('[0][#content]')).toBe('[0]')
65
+ expect(PathBuilder.normalize('[0][#value]')).toBe('[0]')
66
+ expect(PathBuilder.normalize('[0].data[#header]')).toBe('[0].data')
67
+ expect(PathBuilder.normalize('[0]')).toBe('[0]') // no marker
68
+ })
69
+
70
+ it('should check for header paths', () => {
71
+ expect(PathBuilder.isHeader('[0][#header]')).toBe(true)
72
+ expect(PathBuilder.isHeader('[0][#content]')).toBe(false)
73
+ expect(PathBuilder.isHeader('[0]')).toBe(false)
74
+ })
75
+
76
+ it('should check for content paths', () => {
77
+ expect(PathBuilder.isContent('[0][#content]')).toBe(true)
78
+ expect(PathBuilder.isContent('[0][#header]')).toBe(false)
79
+ expect(PathBuilder.isContent('[0]')).toBe(false)
80
+ })
81
+
82
+ it('should check for tag value paths', () => {
83
+ expect(PathBuilder.isTagValue('[0][#value]')).toBe(true)
84
+ expect(PathBuilder.isTagValue('[0][#header]')).toBe(false)
85
+ expect(PathBuilder.isTagValue('[0]')).toBe(false)
86
+ })
87
+
88
+ it('should check for any marker', () => {
89
+ expect(PathBuilder.hasMarker('[0][#header]')).toBe(true)
90
+ expect(PathBuilder.hasMarker('[0][#content]')).toBe(true)
91
+ expect(PathBuilder.hasMarker('[0][#value]')).toBe(true)
92
+ expect(PathBuilder.hasMarker('[0]')).toBe(false)
93
+ })
94
+ })
95
+
96
+ describe('path traversal', () => {
97
+ it('should get parent path', () => {
98
+ expect(PathBuilder.getParent('[0]')).toBe('')
99
+ expect(PathBuilder.getParent('[0][1]')).toBe('[0]')
100
+ expect(PathBuilder.getParent('[0].data')).toBe('[0]')
101
+ expect(PathBuilder.getParent('.field')).toBe('')
102
+ expect(PathBuilder.getParent('')).toBe(null)
103
+ })
104
+
105
+ it('should get parent path from marker paths', () => {
106
+ expect(PathBuilder.getParent('[0][#header]')).toBe('')
107
+ expect(PathBuilder.getParent('[0][1][#content]')).toBe('[0]')
108
+ })
109
+
110
+ it('should calculate path depth', () => {
111
+ expect(PathBuilder.getDepth('')).toBe(0)
112
+ expect(PathBuilder.getDepth('[0]')).toBe(1)
113
+ expect(PathBuilder.getDepth('[0][1]')).toBe(2)
114
+ expect(PathBuilder.getDepth('[0].data')).toBe(2)
115
+ expect(PathBuilder.getDepth('[0][1].field.nested')).toBe(4)
116
+ })
117
+
118
+ it('should calculate depth ignoring markers', () => {
119
+ expect(PathBuilder.getDepth('[0][#header]')).toBe(1)
120
+ expect(PathBuilder.getDepth('[0][1][#content]')).toBe(2)
121
+ })
122
+ })
123
+
124
+ describe('path parsing and building', () => {
125
+ it('should parse simple paths', () => {
126
+ expect(PathBuilder.parse('[0]')).toEqual([
127
+ { type: 'index', value: 0 }
128
+ ])
129
+
130
+ expect(PathBuilder.parse('.field')).toEqual([
131
+ { type: 'key', value: 'field' }
132
+ ])
133
+ })
134
+
135
+ it('should parse nested paths', () => {
136
+ expect(PathBuilder.parse('[0][1]')).toEqual([
137
+ { type: 'index', value: 0 },
138
+ { type: 'index', value: 1 }
139
+ ])
140
+
141
+ expect(PathBuilder.parse('[0].data.value')).toEqual([
142
+ { type: 'index', value: 0 },
143
+ { type: 'key', value: 'data' },
144
+ { type: 'key', value: 'value' }
145
+ ])
146
+ })
147
+
148
+ it('should parse paths with markers', () => {
149
+ expect(PathBuilder.parse('[0][#header]')).toEqual([
150
+ { type: 'index', value: 0 },
151
+ { type: 'marker', value: 'header' }
152
+ ])
153
+
154
+ expect(PathBuilder.parse('[0][1][#content]')).toEqual([
155
+ { type: 'index', value: 0 },
156
+ { type: 'index', value: 1 },
157
+ { type: 'marker', value: 'content' }
158
+ ])
159
+ })
160
+
161
+ it('should build paths from segments', () => {
162
+ expect(PathBuilder.build([
163
+ { type: 'index', value: 0 }
164
+ ])).toBe('[0]')
165
+
166
+ expect(PathBuilder.build([
167
+ { type: 'index', value: 0 },
168
+ { type: 'key', value: 'data' },
169
+ { type: 'marker', value: 'header' }
170
+ ])).toBe('[0].data[#header]')
171
+ })
172
+
173
+ it('should roundtrip parse and build', () => {
174
+ const paths = [
175
+ '[0]',
176
+ '[0][1]',
177
+ '.field',
178
+ '[0].data',
179
+ '[0][1].nested.value',
180
+ '[0][#header]',
181
+ '[0].data[#content]'
182
+ ]
183
+
184
+ for (const path of paths) {
185
+ const segments = PathBuilder.parse(path)
186
+ const rebuilt = PathBuilder.build(segments)
187
+ expect(rebuilt).toBe(path)
188
+ }
189
+ })
190
+ })
191
+
192
+ describe('path utilities', () => {
193
+ it('should join paths', () => {
194
+ expect(PathBuilder.join('', '[0]')).toBe('[0]')
195
+ expect(PathBuilder.join('[0]', '[1]')).toBe('[0][1]')
196
+ expect(PathBuilder.join('[0]', '.data')).toBe('[0].data')
197
+ expect(PathBuilder.join('', '', '[0]', '')).toBe('[0]')
198
+ })
199
+
200
+ it('should check descendant relationships', () => {
201
+ expect(PathBuilder.isDescendantOf('[0][1]', '[0]')).toBe(true)
202
+ expect(PathBuilder.isDescendantOf('[0].data', '[0]')).toBe(true)
203
+ expect(PathBuilder.isDescendantOf('[0][1][2]', '[0]')).toBe(true)
204
+ expect(PathBuilder.isDescendantOf('[1]', '[0]')).toBe(false)
205
+ expect(PathBuilder.isDescendantOf('[0]', '[0]')).toBe(false) // same path
206
+ expect(PathBuilder.isDescendantOf('[0]', '')).toBe(true) // root ancestor
207
+ })
208
+
209
+ it('should handle marker paths in descendant check', () => {
210
+ expect(PathBuilder.isDescendantOf('[0][#header]', '')).toBe(true)
211
+ expect(PathBuilder.isDescendantOf('[0][1][#content]', '[0]')).toBe(true)
212
+ })
213
+ })
214
+
215
+ describe('real-world CBOR paths', () => {
216
+ it('should handle Cardano UTXO paths', () => {
217
+ // Typical UTXO: [[txHash, index], amount]
218
+ const utxoPath = PathBuilder.arrayIndex('', 0)
219
+ const txHashPath = PathBuilder.arrayIndex(utxoPath, 0)
220
+ const indexPath = PathBuilder.arrayIndex(utxoPath, 1)
221
+
222
+ expect(utxoPath).toBe('[0]')
223
+ expect(txHashPath).toBe('[0][0]')
224
+ expect(indexPath).toBe('[0][1]')
225
+
226
+ // With header/content for byte string
227
+ const txHashHeader = PathBuilder.header(txHashPath)
228
+ const txHashContent = PathBuilder.content(txHashPath)
229
+
230
+ expect(txHashHeader).toBe('[0][0][#header]')
231
+ expect(txHashContent).toBe('[0][0][#content]')
232
+ expect(PathBuilder.normalize(txHashHeader)).toBe('[0][0]')
233
+ expect(PathBuilder.normalize(txHashContent)).toBe('[0][0]')
234
+ })
235
+
236
+ it('should handle map with string keys', () => {
237
+ // {amount: 1000000, fee: 200000}
238
+ const amountPath = PathBuilder.mapKey('', 'amount')
239
+ const feePath = PathBuilder.mapKey('', 'fee')
240
+
241
+ expect(amountPath).toBe('.amount')
242
+ expect(feePath).toBe('.fee')
243
+ })
244
+
245
+ it('should handle nested Cardano transaction structure', () => {
246
+ // tx.inputs[0].txHash
247
+ const inputsPath = PathBuilder.mapKey('', 'inputs')
248
+ const firstInputPath = PathBuilder.arrayIndex(inputsPath, 0)
249
+ const txHashPath = PathBuilder.mapKey(firstInputPath, 'txHash')
250
+
251
+ expect(inputsPath).toBe('.inputs')
252
+ expect(firstInputPath).toBe('.inputs[0]')
253
+ expect(txHashPath).toBe('.inputs[0].txHash')
254
+ })
255
+ })
256
+ })