@marcuspuchalla/nachos 0.1.1 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/dist/{chunk-ZRPJUEIZ.js → chunk-5IWW5H47.js} +546 -227
- package/dist/chunk-5IWW5H47.js.map +1 -0
- package/dist/{chunk-2HBCILJS.cjs → chunk-RVG2BY32.cjs} +545 -226
- package/dist/chunk-RVG2BY32.cjs.map +1 -0
- package/dist/{chunk-2FUTHZQQ.cjs → chunk-S4RXO6IB.cjs} +244 -166
- package/dist/chunk-S4RXO6IB.cjs.map +1 -0
- package/dist/{chunk-7CFYWHS6.js → chunk-UMAX5MX5.js} +244 -166
- package/dist/chunk-UMAX5MX5.js.map +1 -0
- package/dist/encoder/index.cjs +13 -13
- package/dist/encoder/index.d.cts +2 -2
- package/dist/encoder/index.d.ts +2 -2
- package/dist/encoder/index.js +1 -1
- package/dist/index.cjs +32 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -19
- package/dist/index.d.ts +28 -19
- package/dist/index.js +16 -16
- package/dist/index.js.map +1 -1
- package/dist/metafile-cjs.json +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/parser/index.cjs +14 -14
- package/dist/parser/index.d.cts +3 -1
- package/dist/parser/index.d.ts +3 -1
- package/dist/parser/index.js +1 -1
- package/dist/{useCborSimpleEncoder-TVxzNJ_9.d.ts → useCborSimpleEncoder-BoKEmjP9.d.ts} +0 -2
- package/dist/{useCborSimpleEncoder-ButVU988.d.cts → useCborSimpleEncoder-C_OHxoB8.d.cts} +0 -2
- package/dist/{useCborTag-B_iaShG6.d.ts → useCborTag-BD6Sqp7p.d.ts} +11 -6
- package/dist/{useCborTag-BfTIV8HM.d.cts → useCborTag-QpZR-Er2.d.cts} +11 -6
- package/package.json +1 -1
- package/src/__tests__/public-api.test.ts +153 -0
- package/src/__tests__/roundtrip.test.ts +701 -0
- package/src/encoder/__tests__/cbor-collection-encoder.test.ts +129 -5
- package/src/encoder/__tests__/cbor-encoder-errors.test.ts +847 -0
- package/src/encoder/__tests__/cbor-simple-encoder.test.ts +126 -0
- package/src/encoder/__tests__/cbor-string-encoder.test.ts +14 -0
- package/src/encoder/composables/useCborCollectionEncoder.ts +56 -23
- package/src/encoder/composables/useCborEncoder.ts +27 -1
- package/src/encoder/composables/useCborSimpleEncoder.ts +40 -8
- package/src/encoder/composables/useCborStringEncoder.ts +23 -10
- package/src/encoder/types.ts +0 -2
- package/src/index.ts +29 -20
- package/src/parser/__tests__/buffer-native-parsing.test.ts +338 -0
- package/src/parser/__tests__/cbor-float-errors.test.ts +41 -0
- package/src/parser/__tests__/cbor-map-duplicate-keys.test.ts +97 -7
- package/src/parser/__tests__/cbor-security-dos-protection.test.ts +166 -33
- package/src/parser/__tests__/cbor-standard-tags.test.ts +104 -7
- package/src/parser/__tests__/cbor-string-errors.test.ts +4 -4
- package/src/parser/__tests__/cbor-tag-errors.test.ts +1 -1
- package/src/parser/__tests__/cbor-tag-reparse-fix.test.ts +268 -0
- package/src/parser/composables/useCborCollection.ts +45 -42
- package/src/parser/composables/useCborFloat.ts +95 -9
- package/src/parser/composables/useCborInteger.ts +24 -10
- package/src/parser/composables/useCborParser.ts +387 -216
- package/src/parser/composables/useCborString.ts +22 -4
- package/src/parser/composables/useCborTag.ts +149 -53
- package/src/parser/utils.ts +11 -0
- package/dist/chunk-2FUTHZQQ.cjs.map +0 -1
- package/dist/chunk-2HBCILJS.cjs.map +0 -1
- package/dist/chunk-7CFYWHS6.js.map +0 -1
- package/dist/chunk-ZRPJUEIZ.js.map +0 -1
- package/src/encoder/composables/#useCborTagEncoder.ts# +0 -158
|
@@ -4,6 +4,15 @@ var chunkPD72MVTX_cjs = require('./chunk-PD72MVTX.cjs');
|
|
|
4
4
|
|
|
5
5
|
// src/parser/utils.ts
|
|
6
6
|
var hexToBytes = (hex) => {
|
|
7
|
+
if (hex.length === 0) {
|
|
8
|
+
return new Uint8Array(0);
|
|
9
|
+
}
|
|
10
|
+
if (hex.length % 2 !== 0) {
|
|
11
|
+
throw new Error("Hex string must have even length");
|
|
12
|
+
}
|
|
13
|
+
if (!/^[0-9a-fA-F]+$/.test(hex)) {
|
|
14
|
+
throw new Error(`Invalid hex character in: ${hex}`);
|
|
15
|
+
}
|
|
7
16
|
const bytes = hex.match(/.{1,2}/g);
|
|
8
17
|
if (!bytes) return new Uint8Array(0);
|
|
9
18
|
return new Uint8Array(bytes.map((byte) => parseInt(byte, 16)));
|
|
@@ -235,9 +244,8 @@ function hasDuplicates(items) {
|
|
|
235
244
|
|
|
236
245
|
// src/parser/composables/useCborInteger.ts
|
|
237
246
|
function useCborInteger() {
|
|
238
|
-
const
|
|
239
|
-
const
|
|
240
|
-
const initialByte = readByte(buffer, 0);
|
|
247
|
+
const parseIntegerFromBuffer = (buffer, offset, options) => {
|
|
248
|
+
const initialByte = readByte(buffer, offset);
|
|
241
249
|
const { majorType, additionalInfo } = extractCborHeader(initialByte);
|
|
242
250
|
let rawValue;
|
|
243
251
|
let bytesRead;
|
|
@@ -245,16 +253,16 @@ function useCborInteger() {
|
|
|
245
253
|
rawValue = additionalInfo;
|
|
246
254
|
bytesRead = 1;
|
|
247
255
|
} else if (additionalInfo === 24) {
|
|
248
|
-
rawValue = readByte(buffer, 1);
|
|
256
|
+
rawValue = readByte(buffer, offset + 1);
|
|
249
257
|
bytesRead = 2;
|
|
250
258
|
} else if (additionalInfo === 25) {
|
|
251
|
-
rawValue = readUint(buffer, 1, 2);
|
|
259
|
+
rawValue = readUint(buffer, offset + 1, 2);
|
|
252
260
|
bytesRead = 3;
|
|
253
261
|
} else if (additionalInfo === 26) {
|
|
254
|
-
rawValue = readUint(buffer, 1, 4);
|
|
262
|
+
rawValue = readUint(buffer, offset + 1, 4);
|
|
255
263
|
bytesRead = 5;
|
|
256
264
|
} else if (additionalInfo === 27) {
|
|
257
|
-
const bigValue = readBigUint(buffer, 1, 8);
|
|
265
|
+
const bigValue = readBigUint(buffer, offset + 1, 8);
|
|
258
266
|
if (bigValue <= BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
259
267
|
rawValue = Number(bigValue);
|
|
260
268
|
} else {
|
|
@@ -294,8 +302,13 @@ function useCborInteger() {
|
|
|
294
302
|
bytesRead
|
|
295
303
|
};
|
|
296
304
|
};
|
|
305
|
+
const parseInteger = (hexString, options) => {
|
|
306
|
+
const buffer = hexToBytes(hexString);
|
|
307
|
+
return parseIntegerFromBuffer(buffer, 0, options);
|
|
308
|
+
};
|
|
297
309
|
return {
|
|
298
|
-
parseInteger
|
|
310
|
+
parseInteger,
|
|
311
|
+
parseIntegerFromBuffer
|
|
299
312
|
};
|
|
300
313
|
}
|
|
301
314
|
|
|
@@ -363,6 +376,14 @@ function useCborString() {
|
|
|
363
376
|
currentOffset++;
|
|
364
377
|
break;
|
|
365
378
|
}
|
|
379
|
+
const chunkInitialByte = readByte(buffer, currentOffset);
|
|
380
|
+
const chunkHeader = extractCborHeader(chunkInitialByte);
|
|
381
|
+
if (chunkHeader.majorType !== 2) {
|
|
382
|
+
throw new Error(`Indefinite byte string chunks must be byte strings (major type 2), got ${chunkHeader.majorType}`);
|
|
383
|
+
}
|
|
384
|
+
if (chunkHeader.additionalInfo === 31) {
|
|
385
|
+
throw new Error("Indefinite byte string chunks must be definite-length (RFC 8949 Section 3.2.3)");
|
|
386
|
+
}
|
|
366
387
|
let chunkResult;
|
|
367
388
|
try {
|
|
368
389
|
chunkResult = parseByteString(buffer, currentOffset, options);
|
|
@@ -426,6 +447,14 @@ function useCborString() {
|
|
|
426
447
|
currentOffset++;
|
|
427
448
|
break;
|
|
428
449
|
}
|
|
450
|
+
const chunkInitialByte = readByte(buffer, currentOffset);
|
|
451
|
+
const chunkHeader = extractCborHeader(chunkInitialByte);
|
|
452
|
+
if (chunkHeader.majorType !== 3) {
|
|
453
|
+
throw new Error(`Indefinite text string chunks must be text strings (major type 3), got ${chunkHeader.majorType}`);
|
|
454
|
+
}
|
|
455
|
+
if (chunkHeader.additionalInfo === 31) {
|
|
456
|
+
throw new Error("Indefinite text string chunks must be definite-length (RFC 8949 Section 3.2.3)");
|
|
457
|
+
}
|
|
429
458
|
let chunkResult;
|
|
430
459
|
try {
|
|
431
460
|
chunkResult = parseTextString(buffer, currentOffset, options);
|
|
@@ -508,6 +537,45 @@ function useCborFloat() {
|
|
|
508
537
|
}
|
|
509
538
|
return (sign === 0 ? 1 : -1) * Math.pow(2, exponent - 15) * (1 + fraction / 1024);
|
|
510
539
|
};
|
|
540
|
+
const fitsInFloat16 = (value) => {
|
|
541
|
+
if (Number.isNaN(value) || value === Infinity || value === -Infinity) return true;
|
|
542
|
+
if (Object.is(value, 0) || Object.is(value, -0)) return true;
|
|
543
|
+
const abs = Math.abs(value);
|
|
544
|
+
if (abs > 65504) return false;
|
|
545
|
+
if (abs < 5960464477539063e-23) return false;
|
|
546
|
+
const sign = value < 0 ? 1 : 0;
|
|
547
|
+
const buf = new ArrayBuffer(8);
|
|
548
|
+
const view = new DataView(buf);
|
|
549
|
+
view.setFloat64(0, abs, false);
|
|
550
|
+
const bits64 = view.getBigUint64(0, false);
|
|
551
|
+
const exp64 = Number(bits64 >> 52n & 0x7ffn) - 1023;
|
|
552
|
+
const mant64 = Number(bits64 & 0xfffffffffffffn);
|
|
553
|
+
let exp16;
|
|
554
|
+
let mant16;
|
|
555
|
+
if (exp64 < -14) {
|
|
556
|
+
exp16 = 0;
|
|
557
|
+
const shift = -14 - exp64;
|
|
558
|
+
mant16 = (1 << 10 | mant64 >> 42) >> shift;
|
|
559
|
+
} else if (exp64 > 15) {
|
|
560
|
+
return false;
|
|
561
|
+
} else {
|
|
562
|
+
exp16 = exp64 + 15;
|
|
563
|
+
mant16 = mant64 >> 42;
|
|
564
|
+
}
|
|
565
|
+
const float16Bits = sign << 15 | exp16 << 10 | mant16;
|
|
566
|
+
const s = (float16Bits & 32768) >> 15;
|
|
567
|
+
const e = (float16Bits & 31744) >> 10;
|
|
568
|
+
const f = float16Bits & 1023;
|
|
569
|
+
let reconstructed;
|
|
570
|
+
if (e === 0) {
|
|
571
|
+
reconstructed = (s === 0 ? 1 : -1) * Math.pow(2, -14) * (f / 1024);
|
|
572
|
+
} else if (e === 31) {
|
|
573
|
+
reconstructed = f === 0 ? s === 0 ? Infinity : -Infinity : NaN;
|
|
574
|
+
} else {
|
|
575
|
+
reconstructed = (s === 0 ? 1 : -1) * Math.pow(2, e - 15) * (1 + f / 1024);
|
|
576
|
+
}
|
|
577
|
+
return reconstructed === value;
|
|
578
|
+
};
|
|
511
579
|
const parseSimpleFromBuffer = (buffer, offset) => {
|
|
512
580
|
const initialByte = readByte(buffer, offset);
|
|
513
581
|
const { majorType, additionalInfo } = extractCborHeader(initialByte);
|
|
@@ -558,7 +626,7 @@ function useCborFloat() {
|
|
|
558
626
|
throw new Error(`Invalid additional info: ${additionalInfo}`);
|
|
559
627
|
}
|
|
560
628
|
};
|
|
561
|
-
const parseFloatFromBuffer = (buffer, offset) => {
|
|
629
|
+
const parseFloatFromBuffer = (buffer, offset, options) => {
|
|
562
630
|
const initialByte = readByte(buffer, offset);
|
|
563
631
|
const { majorType, additionalInfo } = extractCborHeader(initialByte);
|
|
564
632
|
if (majorType !== 7) {
|
|
@@ -569,6 +637,16 @@ function useCborFloat() {
|
|
|
569
637
|
if (offset + 2 >= buffer.length) {
|
|
570
638
|
throw new Error("Unexpected end of buffer while reading Float16");
|
|
571
639
|
}
|
|
640
|
+
if (options?.validateCanonical) {
|
|
641
|
+
const byte1 = readByte(buffer, offset + 1);
|
|
642
|
+
const byte2 = readByte(buffer, offset + 2);
|
|
643
|
+
const bits = byte1 << 8 | byte2;
|
|
644
|
+
const exp = bits >> 10 & 31;
|
|
645
|
+
const mant = bits & 1023;
|
|
646
|
+
if (exp === 31 && mant !== 0 && bits !== 32256) {
|
|
647
|
+
throw new Error("Non-canonical NaN encoding: use 0xf97e00");
|
|
648
|
+
}
|
|
649
|
+
}
|
|
572
650
|
const value = float16ToFloat64(buffer, offset + 1);
|
|
573
651
|
return { value, bytesRead: 3 };
|
|
574
652
|
}
|
|
@@ -578,6 +656,14 @@ function useCborFloat() {
|
|
|
578
656
|
}
|
|
579
657
|
const dataView = new DataView(buffer.buffer, buffer.byteOffset + offset + 1, 4);
|
|
580
658
|
const value = dataView.getFloat32(0, false);
|
|
659
|
+
if (options?.validateCanonical) {
|
|
660
|
+
if (Number.isNaN(value)) {
|
|
661
|
+
throw new Error("Non-canonical NaN encoding: NaN must use float16 0xf97e00");
|
|
662
|
+
}
|
|
663
|
+
if (fitsInFloat16(value)) {
|
|
664
|
+
throw new Error("Non-canonical float32: value fits in float16, use shortest encoding");
|
|
665
|
+
}
|
|
666
|
+
}
|
|
581
667
|
return { value, bytesRead: 5 };
|
|
582
668
|
}
|
|
583
669
|
case 27: {
|
|
@@ -586,20 +672,32 @@ function useCborFloat() {
|
|
|
586
672
|
}
|
|
587
673
|
const dataView = new DataView(buffer.buffer, buffer.byteOffset + offset + 1, 8);
|
|
588
674
|
const value = dataView.getFloat64(0, false);
|
|
675
|
+
if (options?.validateCanonical) {
|
|
676
|
+
if (Number.isNaN(value)) {
|
|
677
|
+
throw new Error("Non-canonical NaN encoding: NaN must use float16 0xf97e00");
|
|
678
|
+
}
|
|
679
|
+
if (fitsInFloat16(value)) {
|
|
680
|
+
throw new Error("Non-canonical float64: value fits in float16/float32, use shortest encoding");
|
|
681
|
+
}
|
|
682
|
+
const f32 = Math.fround(value);
|
|
683
|
+
if (f32 === value || Object.is(value, -0) && Object.is(f32, -0)) {
|
|
684
|
+
throw new Error("Non-canonical float64: value fits in float16/float32, use shortest encoding");
|
|
685
|
+
}
|
|
686
|
+
}
|
|
589
687
|
return { value, bytesRead: 9 };
|
|
590
688
|
}
|
|
591
689
|
default:
|
|
592
690
|
throw new Error(`Additional info ${additionalInfo} is not a float type`);
|
|
593
691
|
}
|
|
594
692
|
};
|
|
595
|
-
const parseFromBuffer = (buffer, offset) => {
|
|
693
|
+
const parseFromBuffer = (buffer, offset, options) => {
|
|
596
694
|
const initialByte = readByte(buffer, offset);
|
|
597
695
|
const { majorType, additionalInfo } = extractCborHeader(initialByte);
|
|
598
696
|
if (majorType !== 7) {
|
|
599
697
|
throw new Error(`Expected major type 7 (simple/float), got ${majorType}`);
|
|
600
698
|
}
|
|
601
699
|
if (additionalInfo === 25 || additionalInfo === 26 || additionalInfo === 27) {
|
|
602
|
-
return parseFloatFromBuffer(buffer, offset);
|
|
700
|
+
return parseFloatFromBuffer(buffer, offset, options);
|
|
603
701
|
} else {
|
|
604
702
|
return parseSimpleFromBuffer(buffer, offset);
|
|
605
703
|
}
|
|
@@ -608,70 +706,75 @@ function useCborFloat() {
|
|
|
608
706
|
const buffer = hexToBytes(hexString);
|
|
609
707
|
return parseSimpleFromBuffer(buffer, 0);
|
|
610
708
|
};
|
|
611
|
-
const parseFloat = (hexString,
|
|
709
|
+
const parseFloat = (hexString, options) => {
|
|
612
710
|
const buffer = hexToBytes(hexString);
|
|
613
|
-
return parseFloatFromBuffer(buffer, 0);
|
|
711
|
+
return parseFloatFromBuffer(buffer, 0, options);
|
|
614
712
|
};
|
|
615
|
-
const parse = (hexString,
|
|
713
|
+
const parse = (hexString, options) => {
|
|
616
714
|
const buffer = hexToBytes(hexString);
|
|
617
|
-
return parseFromBuffer(buffer, 0);
|
|
715
|
+
return parseFromBuffer(buffer, 0, options);
|
|
618
716
|
};
|
|
619
717
|
return {
|
|
620
718
|
parse,
|
|
621
719
|
parseFloat,
|
|
622
|
-
parseSimple
|
|
720
|
+
parseSimple,
|
|
721
|
+
parseFromBuffer
|
|
623
722
|
};
|
|
624
723
|
}
|
|
625
724
|
|
|
626
725
|
// src/parser/composables/useCborTag.ts
|
|
627
726
|
function useCborTag() {
|
|
628
|
-
const {
|
|
727
|
+
const { parseIntegerFromBuffer } = useCborInteger();
|
|
629
728
|
const { parseByteString, parseTextString } = useCborString();
|
|
630
|
-
const {
|
|
631
|
-
|
|
729
|
+
const { parseFromBuffer: parseFloatOrSimpleFromBuffer } = useCborFloat();
|
|
730
|
+
let parseStartTime = 0;
|
|
731
|
+
const parseItem = (buffer, offset, options, tagDepth = 0, collectionDepth = 0) => {
|
|
732
|
+
if (parseStartTime > 0 && options?.limits?.maxParseTime) {
|
|
733
|
+
const elapsed = Date.now() - parseStartTime;
|
|
734
|
+
if (elapsed > options.limits.maxParseTime) {
|
|
735
|
+
throw new Error(`Parse timeout: exceeded ${options.limits.maxParseTime}ms limit`);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
632
738
|
if (offset >= buffer.length) {
|
|
633
739
|
throw new Error(`Unexpected end of buffer at offset ${offset}`);
|
|
634
740
|
}
|
|
635
741
|
const initialByte = readByte(buffer, offset);
|
|
636
|
-
const { majorType
|
|
742
|
+
const { majorType } = extractCborHeader(initialByte);
|
|
637
743
|
switch (majorType) {
|
|
638
744
|
case 0:
|
|
639
745
|
// Unsigned integer
|
|
640
|
-
case 1:
|
|
641
|
-
|
|
642
|
-
const result = parseInteger(intHex, options);
|
|
643
|
-
return { value: result.value, bytesRead: result.bytesRead };
|
|
644
|
-
}
|
|
746
|
+
case 1:
|
|
747
|
+
return parseIntegerFromBuffer(buffer, offset, options);
|
|
645
748
|
case 2:
|
|
646
749
|
return parseByteString(buffer, offset, options);
|
|
647
750
|
case 3:
|
|
648
751
|
return parseTextString(buffer, offset, options);
|
|
649
752
|
case 4:
|
|
650
|
-
return parseArrayInternal(buffer, offset, options);
|
|
753
|
+
return parseArrayInternal(buffer, offset, options, collectionDepth);
|
|
651
754
|
case 5:
|
|
652
|
-
return parseMapInternal(buffer, offset, options);
|
|
755
|
+
return parseMapInternal(buffer, offset, options, collectionDepth);
|
|
653
756
|
case 6:
|
|
654
757
|
return parseTagFromBuffer(buffer, offset, options, tagDepth);
|
|
655
|
-
case 7:
|
|
656
|
-
|
|
657
|
-
if (additionalInfo >= 25 && additionalInfo <= 27) {
|
|
658
|
-
const result = parseFloat(simpleHex, options);
|
|
659
|
-
return { value: result.value, bytesRead: result.bytesRead };
|
|
660
|
-
} else {
|
|
661
|
-
const result = parseSimple(simpleHex, options);
|
|
662
|
-
return { value: result.value, bytesRead: result.bytesRead };
|
|
663
|
-
}
|
|
664
|
-
}
|
|
758
|
+
case 7:
|
|
759
|
+
return parseFloatOrSimpleFromBuffer(buffer, offset, options);
|
|
665
760
|
default:
|
|
666
761
|
throw new Error(`Unknown major type: ${majorType}`);
|
|
667
762
|
}
|
|
668
763
|
};
|
|
669
|
-
const parseArrayInternal = (buffer, offset, options) => {
|
|
764
|
+
const parseArrayInternal = (buffer, offset, options, depth = 0) => {
|
|
670
765
|
const initialByte = readByte(buffer, offset);
|
|
671
766
|
const { majorType, additionalInfo } = extractCborHeader(initialByte);
|
|
672
767
|
if (majorType !== 4) {
|
|
673
768
|
throw new Error(`Expected major type 4 (array), got ${majorType}`);
|
|
674
769
|
}
|
|
770
|
+
const isIndefiniteAllowed = options?.allowIndefinite ?? !(options?.validateCanonical || options?.strict);
|
|
771
|
+
if (additionalInfo === 31 && !isIndefiniteAllowed) {
|
|
772
|
+
throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
|
|
773
|
+
}
|
|
774
|
+
const maxDepth = options?.limits?.maxDepth ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxDepth;
|
|
775
|
+
if (depth >= maxDepth) {
|
|
776
|
+
throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
|
|
777
|
+
}
|
|
675
778
|
let length;
|
|
676
779
|
let bytesConsumed;
|
|
677
780
|
if (additionalInfo < 24) {
|
|
@@ -699,23 +802,39 @@ function useCborTag() {
|
|
|
699
802
|
} else {
|
|
700
803
|
throw new Error(`Invalid additional info for array: ${additionalInfo}`);
|
|
701
804
|
}
|
|
805
|
+
if (length !== null && options?.limits?.maxArrayLength && length > options.limits.maxArrayLength) {
|
|
806
|
+
throw new Error(`Array length ${length} exceeds limit of ${options.limits.maxArrayLength}`);
|
|
807
|
+
}
|
|
702
808
|
let currentOffset = offset + 1 + bytesConsumed;
|
|
703
809
|
const items = [];
|
|
704
810
|
if (length === null) {
|
|
811
|
+
let index = 0;
|
|
812
|
+
let foundBreak = false;
|
|
705
813
|
while (currentOffset < buffer.length) {
|
|
706
814
|
const nextByte = readByte(buffer, currentOffset);
|
|
707
815
|
if (nextByte === 255) {
|
|
708
816
|
currentOffset++;
|
|
817
|
+
foundBreak = true;
|
|
709
818
|
break;
|
|
710
819
|
}
|
|
711
|
-
|
|
820
|
+
if (options?.limits?.maxArrayLength && index >= options.limits.maxArrayLength) {
|
|
821
|
+
throw new Error(`Array length exceeds limit of ${options.limits.maxArrayLength}`);
|
|
822
|
+
}
|
|
823
|
+
const itemResult = parseItem(buffer, currentOffset, options, 0, depth + 1);
|
|
712
824
|
items.push(itemResult.value);
|
|
713
825
|
currentOffset += itemResult.bytesRead;
|
|
826
|
+
index++;
|
|
827
|
+
}
|
|
828
|
+
if (!foundBreak) {
|
|
829
|
+
throw new Error("Indefinite-length array missing break code (0xFF)");
|
|
714
830
|
}
|
|
715
831
|
items[chunkPD72MVTX_cjs.INDEFINITE_SYMBOL] = true;
|
|
716
832
|
} else {
|
|
717
833
|
for (let i = 0; i < length; i++) {
|
|
718
|
-
|
|
834
|
+
if (currentOffset >= buffer.length) {
|
|
835
|
+
throw new Error(`Unexpected end of buffer while parsing array element ${i}/${length}`);
|
|
836
|
+
}
|
|
837
|
+
const itemResult = parseItem(buffer, currentOffset, options, 0, depth + 1);
|
|
719
838
|
items.push(itemResult.value);
|
|
720
839
|
currentOffset += itemResult.bytesRead;
|
|
721
840
|
}
|
|
@@ -725,12 +844,20 @@ function useCborTag() {
|
|
|
725
844
|
bytesRead: currentOffset - offset
|
|
726
845
|
};
|
|
727
846
|
};
|
|
728
|
-
const parseMapInternal = (buffer, offset, options) => {
|
|
847
|
+
const parseMapInternal = (buffer, offset, options, depth = 0) => {
|
|
729
848
|
const initialByte = readByte(buffer, offset);
|
|
730
849
|
const { majorType, additionalInfo } = extractCborHeader(initialByte);
|
|
731
850
|
if (majorType !== 5) {
|
|
732
851
|
throw new Error(`Expected major type 5 (map), got ${majorType}`);
|
|
733
852
|
}
|
|
853
|
+
const isIndefiniteAllowed = options?.allowIndefinite ?? !(options?.validateCanonical || options?.strict);
|
|
854
|
+
if (additionalInfo === 31 && !isIndefiniteAllowed) {
|
|
855
|
+
throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
|
|
856
|
+
}
|
|
857
|
+
const maxDepth = options?.limits?.maxDepth ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxDepth;
|
|
858
|
+
if (depth >= maxDepth) {
|
|
859
|
+
throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
|
|
860
|
+
}
|
|
734
861
|
let length;
|
|
735
862
|
let bytesConsumed;
|
|
736
863
|
if (additionalInfo < 24) {
|
|
@@ -758,27 +885,43 @@ function useCborTag() {
|
|
|
758
885
|
} else {
|
|
759
886
|
throw new Error(`Invalid additional info for map: ${additionalInfo}`);
|
|
760
887
|
}
|
|
888
|
+
if (length !== null && options?.limits?.maxMapSize && length > options.limits.maxMapSize) {
|
|
889
|
+
throw new Error(`Map size ${length} exceeds limit of ${options.limits.maxMapSize}`);
|
|
890
|
+
}
|
|
761
891
|
let currentOffset = offset + 1 + bytesConsumed;
|
|
762
892
|
const map = /* @__PURE__ */ new Map();
|
|
763
893
|
if (length === null) {
|
|
894
|
+
let index = 0;
|
|
895
|
+
let foundBreak = false;
|
|
764
896
|
while (currentOffset < buffer.length) {
|
|
765
897
|
const nextByte = readByte(buffer, currentOffset);
|
|
766
898
|
if (nextByte === 255) {
|
|
767
899
|
currentOffset++;
|
|
900
|
+
foundBreak = true;
|
|
768
901
|
break;
|
|
769
902
|
}
|
|
770
|
-
|
|
903
|
+
if (options?.limits?.maxMapSize && index >= options.limits.maxMapSize) {
|
|
904
|
+
throw new Error(`Map size exceeds limit of ${options.limits.maxMapSize}`);
|
|
905
|
+
}
|
|
906
|
+
const keyResult = parseItem(buffer, currentOffset, options, 0, depth + 1);
|
|
771
907
|
currentOffset += keyResult.bytesRead;
|
|
772
|
-
const valueResult = parseItem(buffer, currentOffset, options);
|
|
908
|
+
const valueResult = parseItem(buffer, currentOffset, options, 0, depth + 1);
|
|
773
909
|
currentOffset += valueResult.bytesRead;
|
|
774
910
|
map.set(keyResult.value, valueResult.value);
|
|
911
|
+
index++;
|
|
912
|
+
}
|
|
913
|
+
if (!foundBreak) {
|
|
914
|
+
throw new Error("Indefinite-length map missing break code (0xFF)");
|
|
775
915
|
}
|
|
776
916
|
map[chunkPD72MVTX_cjs.INDEFINITE_SYMBOL] = true;
|
|
777
917
|
} else {
|
|
778
918
|
for (let i = 0; i < length; i++) {
|
|
779
|
-
|
|
919
|
+
if (currentOffset >= buffer.length) {
|
|
920
|
+
throw new Error(`Unexpected end of buffer while parsing map entry ${i}/${length}`);
|
|
921
|
+
}
|
|
922
|
+
const keyResult = parseItem(buffer, currentOffset, options, 0, depth + 1);
|
|
780
923
|
currentOffset += keyResult.bytesRead;
|
|
781
|
-
const valueResult = parseItem(buffer, currentOffset, options);
|
|
924
|
+
const valueResult = parseItem(buffer, currentOffset, options, 0, depth + 1);
|
|
782
925
|
currentOffset += valueResult.bytesRead;
|
|
783
926
|
map.set(keyResult.value, valueResult.value);
|
|
784
927
|
}
|
|
@@ -828,6 +971,13 @@ function useCborTag() {
|
|
|
828
971
|
}
|
|
829
972
|
return false;
|
|
830
973
|
};
|
|
974
|
+
const isByteString = (value) => {
|
|
975
|
+
if (value instanceof Uint8Array) return true;
|
|
976
|
+
if (value && typeof value === "object" && "type" in value && value.type === "cbor-byte-string") {
|
|
977
|
+
return true;
|
|
978
|
+
}
|
|
979
|
+
return false;
|
|
980
|
+
};
|
|
831
981
|
const getTextStringValue = (value) => {
|
|
832
982
|
if (typeof value === "string") return value;
|
|
833
983
|
if (value && typeof value === "object" && "text" in value) {
|
|
@@ -854,6 +1004,14 @@ function useCborTag() {
|
|
|
854
1004
|
throw new Error(`Tag 1 (epoch time) must contain a number (integer or float), got ${typeof value}`);
|
|
855
1005
|
}
|
|
856
1006
|
break;
|
|
1007
|
+
case 2:
|
|
1008
|
+
// Positive bignum
|
|
1009
|
+
case 3:
|
|
1010
|
+
if (!shouldValidateStandard) break;
|
|
1011
|
+
if (!isByteString(value)) {
|
|
1012
|
+
throw new Error(`Tag ${tagNumber} (bignum) must contain a byte string, got ${typeof value}`);
|
|
1013
|
+
}
|
|
1014
|
+
break;
|
|
857
1015
|
case 4:
|
|
858
1016
|
if (!shouldValidateStandard) break;
|
|
859
1017
|
if (!Array.isArray(value)) {
|
|
@@ -862,11 +1020,11 @@ function useCborTag() {
|
|
|
862
1020
|
if (value.length !== 2) {
|
|
863
1021
|
throw new Error(`Tag 4 (decimal fraction) array must have exactly 2 elements [exponent, mantissa], got ${value.length}`);
|
|
864
1022
|
}
|
|
865
|
-
if (typeof value[0] !== "number" && typeof value[0] !== "bigint") {
|
|
866
|
-
throw new Error(`Tag 4 (decimal fraction) exponent must be an integer, got ${
|
|
1023
|
+
if (typeof value[0] !== "number" && typeof value[0] !== "bigint" || typeof value[0] === "number" && !Number.isInteger(value[0])) {
|
|
1024
|
+
throw new Error(`Tag 4 (decimal fraction) exponent must be an integer, got ${value[0]}`);
|
|
867
1025
|
}
|
|
868
|
-
if (typeof value[1] !== "number" && typeof value[1] !== "bigint") {
|
|
869
|
-
throw new Error(`Tag 4 (decimal fraction) mantissa must be an integer, got ${
|
|
1026
|
+
if (typeof value[1] !== "number" && typeof value[1] !== "bigint" || typeof value[1] === "number" && !Number.isInteger(value[1])) {
|
|
1027
|
+
throw new Error(`Tag 4 (decimal fraction) mantissa must be an integer, got ${value[1]}`);
|
|
870
1028
|
}
|
|
871
1029
|
break;
|
|
872
1030
|
case 5:
|
|
@@ -877,11 +1035,11 @@ function useCborTag() {
|
|
|
877
1035
|
if (value.length !== 2) {
|
|
878
1036
|
throw new Error(`Tag 5 (bigfloat) array must have exactly 2 elements [exponent, mantissa], got ${value.length}`);
|
|
879
1037
|
}
|
|
880
|
-
if (typeof value[0] !== "number" && typeof value[0] !== "bigint") {
|
|
881
|
-
throw new Error(`Tag 5 (bigfloat) exponent must be an integer, got ${
|
|
1038
|
+
if (typeof value[0] !== "number" && typeof value[0] !== "bigint" || typeof value[0] === "number" && !Number.isInteger(value[0])) {
|
|
1039
|
+
throw new Error(`Tag 5 (bigfloat) exponent must be an integer, got ${value[0]}`);
|
|
882
1040
|
}
|
|
883
|
-
if (typeof value[1] !== "number" && typeof value[1] !== "bigint") {
|
|
884
|
-
throw new Error(`Tag 5 (bigfloat) mantissa must be an integer, got ${
|
|
1041
|
+
if (typeof value[1] !== "number" && typeof value[1] !== "bigint" || typeof value[1] === "number" && !Number.isInteger(value[1])) {
|
|
1042
|
+
throw new Error(`Tag 5 (bigfloat) mantissa must be an integer, got ${value[1]}`);
|
|
885
1043
|
}
|
|
886
1044
|
break;
|
|
887
1045
|
case 32:
|
|
@@ -1041,7 +1199,7 @@ function useCborTag() {
|
|
|
1041
1199
|
if (majorType !== 6) {
|
|
1042
1200
|
throw new Error(`Expected major type 6 (tag), got ${majorType}`);
|
|
1043
1201
|
}
|
|
1044
|
-
const maxTagDepth = options?.limits?.maxTagDepth ??
|
|
1202
|
+
const maxTagDepth = options?.limits?.maxTagDepth ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxTagDepth;
|
|
1045
1203
|
if (tagDepth >= maxTagDepth) {
|
|
1046
1204
|
throw new Error(`Tag nesting depth ${tagDepth} exceeds limit of ${maxTagDepth}`);
|
|
1047
1205
|
}
|
|
@@ -1085,12 +1243,25 @@ function useCborTag() {
|
|
|
1085
1243
|
const parseTag = (hexString, options) => {
|
|
1086
1244
|
const cleanHex = hexString.replace(/\s+/g, "");
|
|
1087
1245
|
const buffer = hexToBytes(cleanHex);
|
|
1088
|
-
|
|
1246
|
+
const isTopLevel = parseStartTime === 0;
|
|
1247
|
+
if (isTopLevel && options?.limits?.maxParseTime) {
|
|
1248
|
+
parseStartTime = Date.now();
|
|
1249
|
+
}
|
|
1250
|
+
try {
|
|
1251
|
+
return parseTagFromBuffer(buffer, 0, options);
|
|
1252
|
+
} finally {
|
|
1253
|
+
if (isTopLevel) {
|
|
1254
|
+
parseStartTime = 0;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1089
1257
|
};
|
|
1090
1258
|
const parse = parseTag;
|
|
1091
1259
|
return {
|
|
1092
1260
|
parseTag,
|
|
1093
|
-
parse
|
|
1261
|
+
parse,
|
|
1262
|
+
parseTagFromBuffer,
|
|
1263
|
+
validateTagSemantics,
|
|
1264
|
+
decodePlutusConstructor
|
|
1094
1265
|
};
|
|
1095
1266
|
}
|
|
1096
1267
|
|
|
@@ -1149,17 +1320,18 @@ var logger = {
|
|
|
1149
1320
|
|
|
1150
1321
|
// src/parser/composables/useCborCollection.ts
|
|
1151
1322
|
function useCborCollection() {
|
|
1152
|
-
const {
|
|
1323
|
+
const { parseIntegerFromBuffer } = useCborInteger();
|
|
1153
1324
|
const { parseByteString, parseTextString } = useCborString();
|
|
1154
|
-
const {
|
|
1155
|
-
const {
|
|
1156
|
-
|
|
1157
|
-
if (key instanceof Uint8Array) {
|
|
1158
|
-
return bytesToHex(key);
|
|
1159
|
-
}
|
|
1160
|
-
return String(key);
|
|
1161
|
-
};
|
|
1325
|
+
const { parseFromBuffer: parseFloatOrSimpleFromBuffer } = useCborFloat();
|
|
1326
|
+
const { parseTagFromBuffer } = useCborTag();
|
|
1327
|
+
let parseStartTime = 0;
|
|
1162
1328
|
const parseItem = (buffer, offset, options, depth = 0) => {
|
|
1329
|
+
if (parseStartTime > 0 && options?.limits?.maxParseTime) {
|
|
1330
|
+
const elapsed = Date.now() - parseStartTime;
|
|
1331
|
+
if (elapsed > options.limits.maxParseTime) {
|
|
1332
|
+
throw new Error(`Parse timeout: exceeded ${options.limits.maxParseTime}ms limit`);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1163
1335
|
if (offset >= buffer.length) {
|
|
1164
1336
|
throw new Error(`Unexpected end of buffer at offset ${offset}`);
|
|
1165
1337
|
}
|
|
@@ -1168,11 +1340,8 @@ function useCborCollection() {
|
|
|
1168
1340
|
switch (majorType) {
|
|
1169
1341
|
case 0:
|
|
1170
1342
|
// Unsigned integer
|
|
1171
|
-
case 1:
|
|
1172
|
-
|
|
1173
|
-
const result = parseInteger(intHex, options);
|
|
1174
|
-
return { value: result.value, bytesRead: result.bytesRead };
|
|
1175
|
-
}
|
|
1343
|
+
case 1:
|
|
1344
|
+
return parseIntegerFromBuffer(buffer, offset, options);
|
|
1176
1345
|
case 2:
|
|
1177
1346
|
return parseByteString(buffer, offset, options);
|
|
1178
1347
|
case 3:
|
|
@@ -1181,16 +1350,10 @@ function useCborCollection() {
|
|
|
1181
1350
|
return parseArrayFromBuffer(buffer, offset, options, depth);
|
|
1182
1351
|
case 5:
|
|
1183
1352
|
return parseMapFromBuffer(buffer, offset, options, depth);
|
|
1184
|
-
case 6:
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
return
|
|
1188
|
-
}
|
|
1189
|
-
case 7: {
|
|
1190
|
-
const floatHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1191
|
-
const result = parseFloatOrSimple(floatHex, options);
|
|
1192
|
-
return { value: result.value, bytesRead: result.bytesRead };
|
|
1193
|
-
}
|
|
1353
|
+
case 6:
|
|
1354
|
+
return parseTagFromBuffer(buffer, offset, options);
|
|
1355
|
+
case 7:
|
|
1356
|
+
return parseFloatOrSimpleFromBuffer(buffer, offset, options);
|
|
1194
1357
|
default:
|
|
1195
1358
|
throw new Error(`Unknown major type: ${majorType}`);
|
|
1196
1359
|
}
|
|
@@ -1332,7 +1495,7 @@ function useCborCollection() {
|
|
|
1332
1495
|
}
|
|
1333
1496
|
const valueResult = parseItem(buffer, currentOffset, options, depth + 1);
|
|
1334
1497
|
currentOffset += valueResult.bytesRead;
|
|
1335
|
-
const keyString =
|
|
1498
|
+
const keyString = serializeValueForComparison(keyResult.value);
|
|
1336
1499
|
if (seenKeys.has(keyString)) {
|
|
1337
1500
|
const mode = options?.dupMapKeyMode || "allow";
|
|
1338
1501
|
if (mode === "reject") {
|
|
@@ -1370,7 +1533,7 @@ function useCborCollection() {
|
|
|
1370
1533
|
}
|
|
1371
1534
|
const valueResult = parseItem(buffer, currentOffset, options, depth + 1);
|
|
1372
1535
|
currentOffset += valueResult.bytesRead;
|
|
1373
|
-
const keyString =
|
|
1536
|
+
const keyString = serializeValueForComparison(keyResult.value);
|
|
1374
1537
|
if (seenKeys.has(keyString)) {
|
|
1375
1538
|
const mode = options?.dupMapKeyMode || "allow";
|
|
1376
1539
|
if (mode === "reject") {
|
|
@@ -1410,12 +1573,26 @@ function useCborCollection() {
|
|
|
1410
1573
|
const parseArray = (hexString, options) => {
|
|
1411
1574
|
const cleanHex = hexString.replace(/\s+/g, "");
|
|
1412
1575
|
const buffer = hexToBytes(cleanHex);
|
|
1413
|
-
|
|
1576
|
+
if (options?.limits?.maxParseTime) {
|
|
1577
|
+
parseStartTime = Date.now();
|
|
1578
|
+
}
|
|
1579
|
+
try {
|
|
1580
|
+
return parseArrayFromBuffer(buffer, 0, options, 0);
|
|
1581
|
+
} finally {
|
|
1582
|
+
parseStartTime = 0;
|
|
1583
|
+
}
|
|
1414
1584
|
};
|
|
1415
1585
|
const parseMap = (hexString, options) => {
|
|
1416
1586
|
const cleanHex = hexString.replace(/\s+/g, "");
|
|
1417
1587
|
const buffer = hexToBytes(cleanHex);
|
|
1418
|
-
|
|
1588
|
+
if (options?.limits?.maxParseTime) {
|
|
1589
|
+
parseStartTime = Date.now();
|
|
1590
|
+
}
|
|
1591
|
+
try {
|
|
1592
|
+
return parseMapFromBuffer(buffer, 0, options, 0);
|
|
1593
|
+
} finally {
|
|
1594
|
+
parseStartTime = 0;
|
|
1595
|
+
}
|
|
1419
1596
|
};
|
|
1420
1597
|
return {
|
|
1421
1598
|
parseArray,
|
|
@@ -1459,13 +1636,23 @@ function useCborParser() {
|
|
|
1459
1636
|
throw new Error(`Parse timeout: exceeded ${ctx.options.limits.maxParseTime}ms limit (elapsed: ${elapsed}ms)`);
|
|
1460
1637
|
}
|
|
1461
1638
|
};
|
|
1462
|
-
const { parseInteger } = useCborInteger();
|
|
1463
|
-
const { parseString } = useCborString();
|
|
1639
|
+
const { parseInteger, parseIntegerFromBuffer: integerFromBuffer } = useCborInteger();
|
|
1640
|
+
const { parseString, parseByteString: byteStringFromBuffer, parseTextString: textStringFromBuffer } = useCborString();
|
|
1464
1641
|
const { parseArray, parseMap } = useCborCollection();
|
|
1465
|
-
const { parseTag } = useCborTag();
|
|
1466
|
-
const { parse: parseFloatOrSimple } = useCborFloat();
|
|
1467
|
-
const parse = (
|
|
1468
|
-
const
|
|
1642
|
+
const { parseTag, validateTagSemantics, decodePlutusConstructor } = useCborTag();
|
|
1643
|
+
const { parse: parseFloatOrSimple, parseFromBuffer: floatOrSimpleFromBuffer } = useCborFloat();
|
|
1644
|
+
const parse = (input, options) => {
|
|
1645
|
+
const mergedOptions = mergeOptions(options);
|
|
1646
|
+
if (input instanceof Uint8Array) {
|
|
1647
|
+
if (input.length === 0) {
|
|
1648
|
+
throw new Error("Empty input");
|
|
1649
|
+
}
|
|
1650
|
+
if (mergedOptions.limits?.maxInputSize && input.length > mergedOptions.limits.maxInputSize) {
|
|
1651
|
+
throw new Error(`Input size ${input.length} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
|
|
1652
|
+
}
|
|
1653
|
+
return dispatchFromBuffer(input, 0, mergedOptions);
|
|
1654
|
+
}
|
|
1655
|
+
const cleanHex = input.replace(/\s+/g, "");
|
|
1469
1656
|
if (!cleanHex || cleanHex.length === 0) {
|
|
1470
1657
|
throw new Error("Empty hex string");
|
|
1471
1658
|
}
|
|
@@ -1475,7 +1662,6 @@ function useCborParser() {
|
|
|
1475
1662
|
if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
|
|
1476
1663
|
throw new Error(`Invalid hex character in: ${cleanHex}`);
|
|
1477
1664
|
}
|
|
1478
|
-
const mergedOptions = mergeOptions(options);
|
|
1479
1665
|
const inputSize = cleanHex.length / 2;
|
|
1480
1666
|
if (mergedOptions.limits?.maxInputSize && inputSize > mergedOptions.limits.maxInputSize) {
|
|
1481
1667
|
throw new Error(`Input size ${inputSize} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
|
|
@@ -1504,26 +1690,40 @@ function useCborParser() {
|
|
|
1504
1690
|
throw new Error(`Unknown major type: ${majorType}`);
|
|
1505
1691
|
}
|
|
1506
1692
|
};
|
|
1507
|
-
const parseWithSourceMap = (
|
|
1508
|
-
const cleanHex = hexString.replace(/\s+/g, "");
|
|
1509
|
-
if (!cleanHex || cleanHex.length === 0) {
|
|
1510
|
-
throw new Error("Empty hex string");
|
|
1511
|
-
}
|
|
1512
|
-
if (cleanHex.length % 2 !== 0) {
|
|
1513
|
-
throw new Error("Hex string must have even length");
|
|
1514
|
-
}
|
|
1515
|
-
if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
|
|
1516
|
-
throw new Error(`Invalid hex character in: ${cleanHex}`);
|
|
1517
|
-
}
|
|
1693
|
+
const parseWithSourceMap = (input, options) => {
|
|
1518
1694
|
const mergedOptions = mergeOptions(options);
|
|
1519
|
-
|
|
1520
|
-
if (
|
|
1521
|
-
|
|
1695
|
+
let buffer;
|
|
1696
|
+
if (input instanceof Uint8Array) {
|
|
1697
|
+
if (input.length === 0) {
|
|
1698
|
+
throw new Error("Empty input");
|
|
1699
|
+
}
|
|
1700
|
+
if (mergedOptions.limits?.maxInputSize && input.length > mergedOptions.limits.maxInputSize) {
|
|
1701
|
+
throw new Error(`Input size ${input.length} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
|
|
1702
|
+
}
|
|
1703
|
+
buffer = input;
|
|
1704
|
+
} else {
|
|
1705
|
+
const cleanHex = input.replace(/\s+/g, "");
|
|
1706
|
+
if (!cleanHex || cleanHex.length === 0) {
|
|
1707
|
+
throw new Error("Empty hex string");
|
|
1708
|
+
}
|
|
1709
|
+
if (cleanHex.length % 2 !== 0) {
|
|
1710
|
+
throw new Error("Hex string must have even length");
|
|
1711
|
+
}
|
|
1712
|
+
if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
|
|
1713
|
+
throw new Error(`Invalid hex character in: ${cleanHex}`);
|
|
1714
|
+
}
|
|
1715
|
+
const inputSize = cleanHex.length / 2;
|
|
1716
|
+
if (mergedOptions.limits?.maxInputSize && inputSize > mergedOptions.limits.maxInputSize) {
|
|
1717
|
+
throw new Error(`Input size ${inputSize} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
|
|
1718
|
+
}
|
|
1719
|
+
buffer = hexToBytes(cleanHex);
|
|
1522
1720
|
}
|
|
1523
|
-
const buffer = hexToBytes(cleanHex);
|
|
1524
1721
|
const sourceMap = [];
|
|
1525
1722
|
const ctx = {
|
|
1526
1723
|
buffer,
|
|
1724
|
+
offset: 0,
|
|
1725
|
+
sourceMap,
|
|
1726
|
+
currentDepth: 0,
|
|
1527
1727
|
startTime: Date.now(),
|
|
1528
1728
|
bytesAllocated: 0,
|
|
1529
1729
|
options: mergedOptions
|
|
@@ -1666,19 +1866,57 @@ function useCborParser() {
|
|
|
1666
1866
|
}
|
|
1667
1867
|
return result;
|
|
1668
1868
|
};
|
|
1869
|
+
const dispatchFromBuffer = (buffer, offset, options) => {
|
|
1870
|
+
const initialByte = readByte(buffer, offset);
|
|
1871
|
+
const { majorType } = extractCborHeader(initialByte);
|
|
1872
|
+
switch (majorType) {
|
|
1873
|
+
case 0:
|
|
1874
|
+
// Unsigned integer
|
|
1875
|
+
case 1:
|
|
1876
|
+
return integerFromBuffer(buffer, offset, options);
|
|
1877
|
+
case 2:
|
|
1878
|
+
return byteStringFromBuffer(buffer, offset, options);
|
|
1879
|
+
case 3:
|
|
1880
|
+
return textStringFromBuffer(buffer, offset, options);
|
|
1881
|
+
case 4: {
|
|
1882
|
+
const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1883
|
+
return parseArray(hexString, options);
|
|
1884
|
+
}
|
|
1885
|
+
case 5: {
|
|
1886
|
+
const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1887
|
+
return parseMap(hexString, options);
|
|
1888
|
+
}
|
|
1889
|
+
case 6: {
|
|
1890
|
+
const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1891
|
+
return parseTag(hexString, options);
|
|
1892
|
+
}
|
|
1893
|
+
case 7:
|
|
1894
|
+
return floatOrSimpleFromBuffer(buffer, offset, options);
|
|
1895
|
+
default:
|
|
1896
|
+
throw new Error(`Unknown major type: ${majorType}`);
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1669
1899
|
const parseIntegerFromBuffer = (buffer, offset, options) => {
|
|
1670
|
-
|
|
1671
|
-
return parseInteger(hexString, options);
|
|
1900
|
+
return integerFromBuffer(buffer, offset, options);
|
|
1672
1901
|
};
|
|
1673
1902
|
const parseStringFromBuffer = (buffer, offset, options) => {
|
|
1674
|
-
const
|
|
1675
|
-
|
|
1903
|
+
const initialByte = readByte(buffer, offset);
|
|
1904
|
+
const { majorType } = extractCborHeader(initialByte);
|
|
1905
|
+
if (majorType === 2) {
|
|
1906
|
+
return byteStringFromBuffer(buffer, offset, options);
|
|
1907
|
+
}
|
|
1908
|
+
return textStringFromBuffer(buffer, offset, options);
|
|
1676
1909
|
};
|
|
1677
1910
|
const parseFloatFromBuffer = (buffer, offset, options) => {
|
|
1678
|
-
|
|
1679
|
-
return parseFloatOrSimple(hexString, options);
|
|
1911
|
+
return floatOrSimpleFromBuffer(buffer, offset, options);
|
|
1680
1912
|
};
|
|
1681
1913
|
const parseArrayWithMap = (ctx, offset, path, sourceMap) => {
|
|
1914
|
+
const previousDepth = ctx.currentDepth ?? 0;
|
|
1915
|
+
const maxDepth = ctx.options?.limits?.maxDepth;
|
|
1916
|
+
if (maxDepth !== void 0 && previousDepth >= maxDepth) {
|
|
1917
|
+
throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
|
|
1918
|
+
}
|
|
1919
|
+
ctx.currentDepth = previousDepth + 1;
|
|
1682
1920
|
const startOffset = offset;
|
|
1683
1921
|
const initialByte = readByte(ctx.buffer, offset);
|
|
1684
1922
|
const { additionalInfo } = extractCborHeader(initialByte);
|
|
@@ -1705,6 +1943,10 @@ function useCborParser() {
|
|
|
1705
1943
|
length = Number(bigLength);
|
|
1706
1944
|
currentOffset += 8;
|
|
1707
1945
|
} else if (additionalInfo === 31) {
|
|
1946
|
+
const isIndefiniteAllowed = ctx.options?.allowIndefinite ?? !(ctx.options?.validateCanonical || ctx.options?.strict);
|
|
1947
|
+
if (!isIndefiniteAllowed) {
|
|
1948
|
+
throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
|
|
1949
|
+
}
|
|
1708
1950
|
isIndefinite = true;
|
|
1709
1951
|
length = 0;
|
|
1710
1952
|
} else {
|
|
@@ -1721,49 +1963,70 @@ function useCborParser() {
|
|
|
1721
1963
|
isHeader: true,
|
|
1722
1964
|
headerEnd
|
|
1723
1965
|
});
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
currentOffset
|
|
1731
|
-
|
|
1966
|
+
try {
|
|
1967
|
+
const childPaths = [];
|
|
1968
|
+
if (isIndefinite) {
|
|
1969
|
+
let index = 0;
|
|
1970
|
+
let foundBreak = false;
|
|
1971
|
+
while (currentOffset < ctx.buffer.length) {
|
|
1972
|
+
const nextByte = readByte(ctx.buffer, currentOffset);
|
|
1973
|
+
if (nextByte === 255) {
|
|
1974
|
+
currentOffset++;
|
|
1975
|
+
foundBreak = true;
|
|
1976
|
+
break;
|
|
1977
|
+
}
|
|
1978
|
+
if (ctx.options?.limits?.maxArrayLength && index >= ctx.options.limits.maxArrayLength) {
|
|
1979
|
+
throw new Error(`Array length exceeds limit of ${ctx.options.limits.maxArrayLength}`);
|
|
1980
|
+
}
|
|
1981
|
+
const elementPath = `${path}[${index}]`;
|
|
1982
|
+
childPaths.push(elementPath);
|
|
1983
|
+
const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
|
|
1984
|
+
items.push(elementResult.value);
|
|
1985
|
+
currentOffset += elementResult.bytesRead;
|
|
1986
|
+
const elementEntry = sourceMap.find((e) => e.path === elementPath);
|
|
1987
|
+
if (elementEntry) {
|
|
1988
|
+
elementEntry.parent = path;
|
|
1989
|
+
}
|
|
1990
|
+
index++;
|
|
1732
1991
|
}
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
|
|
1736
|
-
items.push(elementResult.value);
|
|
1737
|
-
currentOffset += elementResult.bytesRead;
|
|
1738
|
-
const elementEntry = sourceMap.find((e) => e.path === elementPath);
|
|
1739
|
-
if (elementEntry) {
|
|
1740
|
-
elementEntry.parent = path;
|
|
1992
|
+
if (!foundBreak) {
|
|
1993
|
+
throw new Error("Indefinite-length array missing break code (0xFF)");
|
|
1741
1994
|
}
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
elementEntry
|
|
1995
|
+
} else {
|
|
1996
|
+
if (ctx.options?.limits?.maxArrayLength && length > ctx.options.limits.maxArrayLength) {
|
|
1997
|
+
throw new Error(`Array length ${length} exceeds limit of ${ctx.options.limits.maxArrayLength}`);
|
|
1998
|
+
}
|
|
1999
|
+
for (let i = 0; i < length; i++) {
|
|
2000
|
+
const elementPath = `${path}[${i}]`;
|
|
2001
|
+
childPaths.push(elementPath);
|
|
2002
|
+
const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
|
|
2003
|
+
items.push(elementResult.value);
|
|
2004
|
+
currentOffset += elementResult.bytesRead;
|
|
2005
|
+
const elementEntry = sourceMap.find((e) => e.path === elementPath);
|
|
2006
|
+
if (elementEntry) {
|
|
2007
|
+
elementEntry.parent = path;
|
|
2008
|
+
}
|
|
1754
2009
|
}
|
|
1755
2010
|
}
|
|
2011
|
+
const bytesRead = currentOffset - offset;
|
|
2012
|
+
if (childPaths.length > 0 && sourceMap[arrayEntryIndex]) {
|
|
2013
|
+
sourceMap[arrayEntryIndex].children = childPaths;
|
|
2014
|
+
}
|
|
2015
|
+
return {
|
|
2016
|
+
value: items,
|
|
2017
|
+
bytesRead
|
|
2018
|
+
};
|
|
2019
|
+
} finally {
|
|
2020
|
+
ctx.currentDepth = previousDepth;
|
|
1756
2021
|
}
|
|
1757
|
-
const bytesRead = currentOffset - offset;
|
|
1758
|
-
if (childPaths.length > 0 && sourceMap[arrayEntryIndex]) {
|
|
1759
|
-
sourceMap[arrayEntryIndex].children = childPaths;
|
|
1760
|
-
}
|
|
1761
|
-
return {
|
|
1762
|
-
value: items,
|
|
1763
|
-
bytesRead
|
|
1764
|
-
};
|
|
1765
2022
|
};
|
|
1766
2023
|
const parseMapWithMap = (ctx, offset, path, sourceMap) => {
|
|
2024
|
+
const previousDepth = ctx.currentDepth ?? 0;
|
|
2025
|
+
const maxDepth = ctx.options?.limits?.maxDepth;
|
|
2026
|
+
if (maxDepth !== void 0 && previousDepth >= maxDepth) {
|
|
2027
|
+
throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
|
|
2028
|
+
}
|
|
2029
|
+
ctx.currentDepth = previousDepth + 1;
|
|
1767
2030
|
const startOffset = offset;
|
|
1768
2031
|
const initialByte = readByte(ctx.buffer, offset);
|
|
1769
2032
|
const { additionalInfo } = extractCborHeader(initialByte);
|
|
@@ -1790,6 +2053,10 @@ function useCborParser() {
|
|
|
1790
2053
|
length = Number(bigLength);
|
|
1791
2054
|
currentOffset += 8;
|
|
1792
2055
|
} else if (additionalInfo === 31) {
|
|
2056
|
+
const isIndefiniteAllowed = ctx.options?.allowIndefinite ?? !(ctx.options?.validateCanonical || ctx.options?.strict);
|
|
2057
|
+
if (!isIndefiniteAllowed) {
|
|
2058
|
+
throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
|
|
2059
|
+
}
|
|
1793
2060
|
isIndefinite = true;
|
|
1794
2061
|
length = 0;
|
|
1795
2062
|
} else {
|
|
@@ -1806,72 +2073,91 @@ function useCborParser() {
|
|
|
1806
2073
|
isHeader: true,
|
|
1807
2074
|
headerEnd
|
|
1808
2075
|
});
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
2076
|
+
try {
|
|
2077
|
+
const childPaths = [];
|
|
2078
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
2079
|
+
if (isIndefinite) {
|
|
2080
|
+
let count = 0;
|
|
2081
|
+
let foundBreak = false;
|
|
2082
|
+
while (currentOffset < ctx.buffer.length) {
|
|
2083
|
+
const nextByte = readByte(ctx.buffer, currentOffset);
|
|
2084
|
+
if (nextByte === 255) {
|
|
2085
|
+
currentOffset++;
|
|
2086
|
+
foundBreak = true;
|
|
2087
|
+
break;
|
|
2088
|
+
}
|
|
2089
|
+
if (ctx.options?.limits?.maxMapSize && count >= ctx.options.limits.maxMapSize) {
|
|
2090
|
+
throw new Error(`Map size exceeds limit of ${ctx.options.limits.maxMapSize}`);
|
|
2091
|
+
}
|
|
2092
|
+
const keyPath = `${path}${path ? "." : ""}#key`;
|
|
2093
|
+
const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
|
|
2094
|
+
currentOffset += keyResult.bytesRead;
|
|
2095
|
+
const keyForDupCheck = serializeValueForComparison(keyResult.value);
|
|
2096
|
+
const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
|
|
2097
|
+
if (seenKeys.has(keyForDupCheck)) {
|
|
2098
|
+
const mode = ctx.options?.dupMapKeyMode || "allow";
|
|
2099
|
+
if (mode === "reject") {
|
|
2100
|
+
throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
|
|
2101
|
+
} else if (mode === "warn") {
|
|
2102
|
+
logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
seenKeys.add(keyForDupCheck);
|
|
2106
|
+
const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
|
|
2107
|
+
childPaths.push(valuePath);
|
|
2108
|
+
const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
|
|
2109
|
+
map.set(keyResult.value, valueResult.value);
|
|
2110
|
+
currentOffset += valueResult.bytesRead;
|
|
2111
|
+
const valueEntry = sourceMap.find((e) => e.path === valuePath);
|
|
2112
|
+
if (valueEntry) {
|
|
2113
|
+
valueEntry.parent = path;
|
|
1828
2114
|
}
|
|
2115
|
+
count++;
|
|
1829
2116
|
}
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
childPaths.push(valuePath);
|
|
1833
|
-
const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
|
|
1834
|
-
map.set(keyResult.value, valueResult.value);
|
|
1835
|
-
currentOffset += valueResult.bytesRead;
|
|
1836
|
-
const valueEntry = sourceMap.find((e) => e.path === valuePath);
|
|
1837
|
-
if (valueEntry) {
|
|
1838
|
-
valueEntry.parent = path;
|
|
2117
|
+
if (!foundBreak) {
|
|
2118
|
+
throw new Error("Indefinite-length map missing break code (0xFF)");
|
|
1839
2119
|
}
|
|
1840
|
-
}
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
const keyPath = `${path}${path ? "." : ""}#key${i}`;
|
|
1844
|
-
const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
|
|
1845
|
-
currentOffset += keyResult.bytesRead;
|
|
1846
|
-
const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
|
|
1847
|
-
if (seenKeys.has(keyString)) {
|
|
1848
|
-
const mode = ctx.options?.dupMapKeyMode || "allow";
|
|
1849
|
-
if (mode === "reject") {
|
|
1850
|
-
throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
|
|
1851
|
-
} else if (mode === "warn") {
|
|
1852
|
-
logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
|
|
1853
|
-
}
|
|
2120
|
+
} else {
|
|
2121
|
+
if (ctx.options?.limits?.maxMapSize && length > ctx.options.limits.maxMapSize) {
|
|
2122
|
+
throw new Error(`Map size ${length} exceeds limit of ${ctx.options.limits.maxMapSize}`);
|
|
1854
2123
|
}
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
2124
|
+
for (let i = 0; i < length; i++) {
|
|
2125
|
+
const keyPath = `${path}${path ? "." : ""}#key${i}`;
|
|
2126
|
+
const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
|
|
2127
|
+
currentOffset += keyResult.bytesRead;
|
|
2128
|
+
const keyForDupCheck = serializeValueForComparison(keyResult.value);
|
|
2129
|
+
const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
|
|
2130
|
+
if (seenKeys.has(keyForDupCheck)) {
|
|
2131
|
+
const mode = ctx.options?.dupMapKeyMode || "allow";
|
|
2132
|
+
if (mode === "reject") {
|
|
2133
|
+
throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
|
|
2134
|
+
} else if (mode === "warn") {
|
|
2135
|
+
logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
seenKeys.add(keyForDupCheck);
|
|
2139
|
+
const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
|
|
2140
|
+
childPaths.push(valuePath);
|
|
2141
|
+
const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
|
|
2142
|
+
map.set(keyResult.value, valueResult.value);
|
|
2143
|
+
currentOffset += valueResult.bytesRead;
|
|
2144
|
+
const valueEntry = sourceMap.find((e) => e.path === valuePath);
|
|
2145
|
+
if (valueEntry) {
|
|
2146
|
+
valueEntry.parent = path;
|
|
2147
|
+
}
|
|
1864
2148
|
}
|
|
1865
2149
|
}
|
|
2150
|
+
const bytesRead = currentOffset - offset;
|
|
2151
|
+
if (sourceMap[mapEntryIndex]) {
|
|
2152
|
+
sourceMap[mapEntryIndex].children = childPaths;
|
|
2153
|
+
}
|
|
2154
|
+
return {
|
|
2155
|
+
value: map,
|
|
2156
|
+
bytesRead
|
|
2157
|
+
};
|
|
2158
|
+
} finally {
|
|
2159
|
+
ctx.currentDepth = previousDepth;
|
|
1866
2160
|
}
|
|
1867
|
-
const bytesRead = currentOffset - offset;
|
|
1868
|
-
if (sourceMap[mapEntryIndex]) {
|
|
1869
|
-
sourceMap[mapEntryIndex].children = childPaths;
|
|
1870
|
-
}
|
|
1871
|
-
return {
|
|
1872
|
-
value: map,
|
|
1873
|
-
bytesRead
|
|
1874
|
-
};
|
|
1875
2161
|
};
|
|
1876
2162
|
const parseTagNumberHelper = (buffer, offset, ai) => {
|
|
1877
2163
|
if (ai < 24) {
|
|
@@ -1930,10 +2216,29 @@ function useCborParser() {
|
|
|
1930
2216
|
if (valueEntry) {
|
|
1931
2217
|
valueEntry.parent = path;
|
|
1932
2218
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
2219
|
+
let finalValue = valueResult.value;
|
|
2220
|
+
if ((tagNumber === 2 || tagNumber === 3) && finalValue instanceof Uint8Array) {
|
|
2221
|
+
const maxBignumBytes = ctx.options?.limits?.maxBignumBytes ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxBignumBytes;
|
|
2222
|
+
if (finalValue.length > maxBignumBytes) {
|
|
2223
|
+
throw new Error(
|
|
2224
|
+
`Bignum (tag ${tagNumber}) size ${finalValue.length} bytes exceeds limit of ${maxBignumBytes} bytes`
|
|
2225
|
+
);
|
|
2226
|
+
}
|
|
2227
|
+
let bigintValue = 0n;
|
|
2228
|
+
for (let i = 0; i < finalValue.length; i++) {
|
|
2229
|
+
bigintValue = bigintValue << 8n | BigInt(finalValue[i]);
|
|
2230
|
+
}
|
|
2231
|
+
finalValue = tagNumber === 2 ? bigintValue : -1n - bigintValue;
|
|
2232
|
+
}
|
|
2233
|
+
validateTagSemantics(tagNumber, finalValue, ctx.options);
|
|
2234
|
+
const plutusConstr = decodePlutusConstructor(tagNumber, finalValue);
|
|
2235
|
+
const taggedValue = {
|
|
2236
|
+
tag: tagNumber,
|
|
2237
|
+
value: finalValue,
|
|
2238
|
+
...plutusConstr && { plutus: plutusConstr }
|
|
2239
|
+
};
|
|
1935
2240
|
return {
|
|
1936
|
-
value:
|
|
2241
|
+
value: taggedValue,
|
|
1937
2242
|
bytesRead: currentOffset - startOffset
|
|
1938
2243
|
};
|
|
1939
2244
|
};
|
|
@@ -1948,28 +2253,42 @@ function useCborParser() {
|
|
|
1948
2253
|
if (ai < 20) return `Simple Value ${ai}`;
|
|
1949
2254
|
return "Simple Value";
|
|
1950
2255
|
};
|
|
1951
|
-
const parseSequence = (
|
|
1952
|
-
const cleanHex = hexString.replace(/\s+/g, "");
|
|
1953
|
-
if (!cleanHex || cleanHex.length === 0) {
|
|
1954
|
-
return [];
|
|
1955
|
-
}
|
|
1956
|
-
if (cleanHex.length % 2 !== 0) {
|
|
1957
|
-
throw new Error("Hex string must have even length");
|
|
1958
|
-
}
|
|
1959
|
-
if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
|
|
1960
|
-
throw new Error(`Invalid hex character in: ${cleanHex}`);
|
|
1961
|
-
}
|
|
2256
|
+
const parseSequence = (input, options) => {
|
|
1962
2257
|
const mergedOptions = mergeOptions(options);
|
|
1963
|
-
|
|
2258
|
+
let buffer;
|
|
2259
|
+
if (input instanceof Uint8Array) {
|
|
2260
|
+
if (input.length === 0) {
|
|
2261
|
+
return [];
|
|
2262
|
+
}
|
|
2263
|
+
buffer = input;
|
|
2264
|
+
} else {
|
|
2265
|
+
const cleanHex = input.replace(/\s+/g, "");
|
|
2266
|
+
if (!cleanHex || cleanHex.length === 0) {
|
|
2267
|
+
return [];
|
|
2268
|
+
}
|
|
2269
|
+
if (cleanHex.length % 2 !== 0) {
|
|
2270
|
+
throw new Error("Hex string must have even length");
|
|
2271
|
+
}
|
|
2272
|
+
if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
|
|
2273
|
+
throw new Error(`Invalid hex character in: ${cleanHex}`);
|
|
2274
|
+
}
|
|
2275
|
+
buffer = hexToBytes(cleanHex);
|
|
2276
|
+
}
|
|
1964
2277
|
const results = [];
|
|
1965
2278
|
let offset = 0;
|
|
2279
|
+
const sequenceStartTime = mergedOptions.limits?.maxParseTime ? Date.now() : 0;
|
|
1966
2280
|
while (offset < buffer.length) {
|
|
2281
|
+
if (sequenceStartTime > 0 && mergedOptions.limits?.maxParseTime) {
|
|
2282
|
+
const elapsed = Date.now() - sequenceStartTime;
|
|
2283
|
+
if (elapsed > mergedOptions.limits.maxParseTime) {
|
|
2284
|
+
throw new Error(`Parse timeout: exceeded ${mergedOptions.limits.maxParseTime}ms limit`);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
1967
2287
|
const byte = readByte(buffer, offset);
|
|
1968
2288
|
if (byte === 255) {
|
|
1969
2289
|
throw new Error(`Unexpected break code (0xff) at offset ${offset} - not inside indefinite-length item`);
|
|
1970
2290
|
}
|
|
1971
|
-
const
|
|
1972
|
-
const result = parse(remainingHex, mergedOptions);
|
|
2291
|
+
const result = dispatchFromBuffer(buffer, offset, mergedOptions);
|
|
1973
2292
|
results.push(result.value);
|
|
1974
2293
|
offset += result.bytesRead;
|
|
1975
2294
|
}
|
|
@@ -2030,5 +2349,5 @@ exports.useCborParser = useCborParser;
|
|
|
2030
2349
|
exports.useCborString = useCborString;
|
|
2031
2350
|
exports.useCborTag = useCborTag;
|
|
2032
2351
|
exports.validateUtf8Strict = validateUtf8Strict;
|
|
2033
|
-
//# sourceMappingURL=chunk-
|
|
2034
|
-
//# sourceMappingURL=chunk-
|
|
2352
|
+
//# sourceMappingURL=chunk-RVG2BY32.cjs.map
|
|
2353
|
+
//# sourceMappingURL=chunk-RVG2BY32.cjs.map
|