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