@marcuspuchalla/nachos 0.1.3 → 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.
Files changed (55) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/{chunk-5A5T56JB.js → chunk-5IWW5H47.js} +378 -207
  3. package/dist/chunk-5IWW5H47.js.map +1 -0
  4. package/dist/{chunk-PTWN7K3Y.cjs → chunk-RVG2BY32.cjs} +378 -207
  5. package/dist/chunk-RVG2BY32.cjs.map +1 -0
  6. package/dist/{chunk-R62CQQNI.cjs → chunk-S4RXO6IB.cjs} +195 -165
  7. package/dist/chunk-S4RXO6IB.cjs.map +1 -0
  8. package/dist/{chunk-2MTLSQ7E.js → chunk-UMAX5MX5.js} +195 -165
  9. package/dist/chunk-UMAX5MX5.js.map +1 -0
  10. package/dist/encoder/index.cjs +13 -13
  11. package/dist/encoder/index.d.cts +2 -2
  12. package/dist/encoder/index.d.ts +2 -2
  13. package/dist/encoder/index.js +1 -1
  14. package/dist/index.cjs +32 -32
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +28 -19
  17. package/dist/index.d.ts +28 -19
  18. package/dist/index.js +16 -16
  19. package/dist/index.js.map +1 -1
  20. package/dist/metafile-cjs.json +1 -1
  21. package/dist/metafile-esm.json +1 -1
  22. package/dist/parser/index.cjs +14 -14
  23. package/dist/parser/index.d.cts +3 -1
  24. package/dist/parser/index.d.ts +3 -1
  25. package/dist/parser/index.js +1 -1
  26. package/dist/{useCborSimpleEncoder-TVxzNJ_9.d.ts → useCborSimpleEncoder-BoKEmjP9.d.ts} +0 -2
  27. package/dist/{useCborSimpleEncoder-ButVU988.d.cts → useCborSimpleEncoder-C_OHxoB8.d.cts} +0 -2
  28. package/dist/{useCborTag-Cs1CZuXZ.d.cts → useCborTag-BD6Sqp7p.d.ts} +9 -4
  29. package/dist/{useCborTag-xV2Pz2VY.d.ts → useCborTag-QpZR-Er2.d.cts} +9 -4
  30. package/package.json +1 -1
  31. package/src/__tests__/public-api.test.ts +153 -0
  32. package/src/__tests__/roundtrip.test.ts +5 -6
  33. package/src/encoder/__tests__/cbor-collection-encoder.test.ts +103 -5
  34. package/src/encoder/__tests__/cbor-encoder-errors.test.ts +40 -5
  35. package/src/encoder/__tests__/cbor-simple-encoder.test.ts +126 -0
  36. package/src/encoder/composables/useCborCollectionEncoder.ts +28 -25
  37. package/src/encoder/composables/useCborEncoder.ts +21 -0
  38. package/src/encoder/composables/useCborSimpleEncoder.ts +34 -7
  39. package/src/encoder/types.ts +0 -2
  40. package/src/index.ts +29 -20
  41. package/src/parser/__tests__/buffer-native-parsing.test.ts +338 -0
  42. package/src/parser/__tests__/cbor-map-duplicate-keys.test.ts +97 -7
  43. package/src/parser/__tests__/cbor-security-dos-protection.test.ts +164 -31
  44. package/src/parser/__tests__/cbor-standard-tags.test.ts +75 -7
  45. package/src/parser/__tests__/cbor-tag-reparse-fix.test.ts +268 -0
  46. package/src/parser/composables/useCborCollection.ts +45 -42
  47. package/src/parser/composables/useCborFloat.ts +2 -1
  48. package/src/parser/composables/useCborInteger.ts +24 -10
  49. package/src/parser/composables/useCborParser.ts +387 -197
  50. package/src/parser/composables/useCborTag.ts +45 -37
  51. package/src/parser/utils.ts +11 -0
  52. package/dist/chunk-2MTLSQ7E.js.map +0 -1
  53. package/dist/chunk-5A5T56JB.js.map +0 -1
  54. package/dist/chunk-PTWN7K3Y.cjs.map +0 -1
  55. package/dist/chunk-R62CQQNI.cjs.map +0 -1
@@ -2,6 +2,15 @@ import { useCborByteString, useCborTextString, DEFAULT_LIMITS, INDEFINITE_SYMBOL
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 parseInteger = (hexString, options) => {
237
- const buffer = hexToBytes(hexString);
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
 
@@ -702,29 +715,34 @@ function useCborFloat() {
702
715
  return {
703
716
  parse,
704
717
  parseFloat,
705
- parseSimple
718
+ parseSimple,
719
+ parseFromBuffer
706
720
  };
707
721
  }
708
722
 
709
723
  // src/parser/composables/useCborTag.ts
710
724
  function useCborTag() {
711
- const { parseInteger } = useCborInteger();
725
+ const { parseIntegerFromBuffer } = useCborInteger();
712
726
  const { parseByteString, parseTextString } = useCborString();
713
- const { parseFloat, parseSimple } = useCborFloat();
727
+ const { parseFromBuffer: parseFloatOrSimpleFromBuffer } = useCborFloat();
728
+ let parseStartTime = 0;
714
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
+ }
715
736
  if (offset >= buffer.length) {
716
737
  throw new Error(`Unexpected end of buffer at offset ${offset}`);
717
738
  }
718
739
  const initialByte = readByte(buffer, offset);
719
- const { majorType, additionalInfo } = extractCborHeader(initialByte);
740
+ const { majorType } = extractCborHeader(initialByte);
720
741
  switch (majorType) {
721
742
  case 0:
722
743
  // Unsigned integer
723
- case 1: {
724
- const intHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
725
- const result = parseInteger(intHex, options);
726
- return { value: result.value, bytesRead: result.bytesRead };
727
- }
744
+ case 1:
745
+ return parseIntegerFromBuffer(buffer, offset, options);
728
746
  case 2:
729
747
  return parseByteString(buffer, offset, options);
730
748
  case 3:
@@ -735,16 +753,8 @@ function useCborTag() {
735
753
  return parseMapInternal(buffer, offset, options, collectionDepth);
736
754
  case 6:
737
755
  return parseTagFromBuffer(buffer, offset, options, tagDepth);
738
- case 7: {
739
- const simpleHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
740
- if (additionalInfo >= 25 && additionalInfo <= 27) {
741
- const result = parseFloat(simpleHex, options);
742
- return { value: result.value, bytesRead: result.bytesRead };
743
- } else {
744
- const result = parseSimple(simpleHex, options);
745
- return { value: result.value, bytesRead: result.bytesRead };
746
- }
747
- }
756
+ case 7:
757
+ return parseFloatOrSimpleFromBuffer(buffer, offset, options);
748
758
  default:
749
759
  throw new Error(`Unknown major type: ${majorType}`);
750
760
  }
@@ -1008,11 +1018,11 @@ function useCborTag() {
1008
1018
  if (value.length !== 2) {
1009
1019
  throw new Error(`Tag 4 (decimal fraction) array must have exactly 2 elements [exponent, mantissa], got ${value.length}`);
1010
1020
  }
1011
- if (typeof value[0] !== "number" && typeof value[0] !== "bigint") {
1012
- throw new Error(`Tag 4 (decimal fraction) exponent must be an integer, got ${typeof value[0]}`);
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]}`);
1013
1023
  }
1014
- if (typeof value[1] !== "number" && typeof value[1] !== "bigint") {
1015
- throw new Error(`Tag 4 (decimal fraction) mantissa must be an integer, got ${typeof value[1]}`);
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]}`);
1016
1026
  }
1017
1027
  break;
1018
1028
  case 5:
@@ -1023,11 +1033,11 @@ function useCborTag() {
1023
1033
  if (value.length !== 2) {
1024
1034
  throw new Error(`Tag 5 (bigfloat) array must have exactly 2 elements [exponent, mantissa], got ${value.length}`);
1025
1035
  }
1026
- if (typeof value[0] !== "number" && typeof value[0] !== "bigint") {
1027
- throw new Error(`Tag 5 (bigfloat) exponent must be an integer, got ${typeof value[0]}`);
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]}`);
1028
1038
  }
1029
- if (typeof value[1] !== "number" && typeof value[1] !== "bigint") {
1030
- throw new Error(`Tag 5 (bigfloat) mantissa must be an integer, got ${typeof value[1]}`);
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]}`);
1031
1041
  }
1032
1042
  break;
1033
1043
  case 32:
@@ -1231,12 +1241,25 @@ function useCborTag() {
1231
1241
  const parseTag = (hexString, options) => {
1232
1242
  const cleanHex = hexString.replace(/\s+/g, "");
1233
1243
  const buffer = hexToBytes(cleanHex);
1234
- return parseTagFromBuffer(buffer, 0, options);
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
+ }
1235
1255
  };
1236
1256
  const parse = parseTag;
1237
1257
  return {
1238
1258
  parseTag,
1239
- parse
1259
+ parse,
1260
+ parseTagFromBuffer,
1261
+ validateTagSemantics,
1262
+ decodePlutusConstructor
1240
1263
  };
1241
1264
  }
1242
1265
 
@@ -1295,17 +1318,18 @@ var logger = {
1295
1318
 
1296
1319
  // src/parser/composables/useCborCollection.ts
1297
1320
  function useCborCollection() {
1298
- const { parseInteger } = useCborInteger();
1321
+ const { parseIntegerFromBuffer } = useCborInteger();
1299
1322
  const { parseByteString, parseTextString } = useCborString();
1300
- const { parse: parseFloatOrSimple } = useCborFloat();
1301
- const { parseTag } = useCborTag();
1302
- const convertKeyToString = (key) => {
1303
- if (key instanceof Uint8Array) {
1304
- return bytesToHex(key);
1305
- }
1306
- return String(key);
1307
- };
1323
+ const { parseFromBuffer: parseFloatOrSimpleFromBuffer } = useCborFloat();
1324
+ const { parseTagFromBuffer } = useCborTag();
1325
+ let parseStartTime = 0;
1308
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
+ }
1309
1333
  if (offset >= buffer.length) {
1310
1334
  throw new Error(`Unexpected end of buffer at offset ${offset}`);
1311
1335
  }
@@ -1314,11 +1338,8 @@ function useCborCollection() {
1314
1338
  switch (majorType) {
1315
1339
  case 0:
1316
1340
  // Unsigned integer
1317
- case 1: {
1318
- const intHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1319
- const result = parseInteger(intHex, options);
1320
- return { value: result.value, bytesRead: result.bytesRead };
1321
- }
1341
+ case 1:
1342
+ return parseIntegerFromBuffer(buffer, offset, options);
1322
1343
  case 2:
1323
1344
  return parseByteString(buffer, offset, options);
1324
1345
  case 3:
@@ -1327,16 +1348,10 @@ function useCborCollection() {
1327
1348
  return parseArrayFromBuffer(buffer, offset, options, depth);
1328
1349
  case 5:
1329
1350
  return parseMapFromBuffer(buffer, offset, options, depth);
1330
- case 6: {
1331
- const tagHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1332
- const result = parseTag(tagHex, options);
1333
- return { value: result.value, bytesRead: result.bytesRead };
1334
- }
1335
- case 7: {
1336
- const floatHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1337
- const result = parseFloatOrSimple(floatHex, options);
1338
- return { value: result.value, bytesRead: result.bytesRead };
1339
- }
1351
+ case 6:
1352
+ return parseTagFromBuffer(buffer, offset, options);
1353
+ case 7:
1354
+ return parseFloatOrSimpleFromBuffer(buffer, offset, options);
1340
1355
  default:
1341
1356
  throw new Error(`Unknown major type: ${majorType}`);
1342
1357
  }
@@ -1478,7 +1493,7 @@ function useCborCollection() {
1478
1493
  }
1479
1494
  const valueResult = parseItem(buffer, currentOffset, options, depth + 1);
1480
1495
  currentOffset += valueResult.bytesRead;
1481
- const keyString = convertKeyToString(keyResult.value);
1496
+ const keyString = serializeValueForComparison(keyResult.value);
1482
1497
  if (seenKeys.has(keyString)) {
1483
1498
  const mode = options?.dupMapKeyMode || "allow";
1484
1499
  if (mode === "reject") {
@@ -1516,7 +1531,7 @@ function useCborCollection() {
1516
1531
  }
1517
1532
  const valueResult = parseItem(buffer, currentOffset, options, depth + 1);
1518
1533
  currentOffset += valueResult.bytesRead;
1519
- const keyString = convertKeyToString(keyResult.value);
1534
+ const keyString = serializeValueForComparison(keyResult.value);
1520
1535
  if (seenKeys.has(keyString)) {
1521
1536
  const mode = options?.dupMapKeyMode || "allow";
1522
1537
  if (mode === "reject") {
@@ -1556,12 +1571,26 @@ function useCborCollection() {
1556
1571
  const parseArray = (hexString, options) => {
1557
1572
  const cleanHex = hexString.replace(/\s+/g, "");
1558
1573
  const buffer = hexToBytes(cleanHex);
1559
- return parseArrayFromBuffer(buffer, 0, options, 0);
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
+ }
1560
1582
  };
1561
1583
  const parseMap = (hexString, options) => {
1562
1584
  const cleanHex = hexString.replace(/\s+/g, "");
1563
1585
  const buffer = hexToBytes(cleanHex);
1564
- return parseMapFromBuffer(buffer, 0, options, 0);
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
+ }
1565
1594
  };
1566
1595
  return {
1567
1596
  parseArray,
@@ -1605,13 +1634,23 @@ function useCborParser() {
1605
1634
  throw new Error(`Parse timeout: exceeded ${ctx.options.limits.maxParseTime}ms limit (elapsed: ${elapsed}ms)`);
1606
1635
  }
1607
1636
  };
1608
- const { parseInteger } = useCborInteger();
1609
- const { parseString } = useCborString();
1637
+ const { parseInteger, parseIntegerFromBuffer: integerFromBuffer } = useCborInteger();
1638
+ const { parseString, parseByteString: byteStringFromBuffer, parseTextString: textStringFromBuffer } = useCborString();
1610
1639
  const { parseArray, parseMap } = useCborCollection();
1611
- const { parseTag } = useCborTag();
1612
- const { parse: parseFloatOrSimple } = useCborFloat();
1613
- const parse = (hexString, options) => {
1614
- const cleanHex = hexString.replace(/\s+/g, "");
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, "");
1615
1654
  if (!cleanHex || cleanHex.length === 0) {
1616
1655
  throw new Error("Empty hex string");
1617
1656
  }
@@ -1621,7 +1660,6 @@ function useCborParser() {
1621
1660
  if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
1622
1661
  throw new Error(`Invalid hex character in: ${cleanHex}`);
1623
1662
  }
1624
- const mergedOptions = mergeOptions(options);
1625
1663
  const inputSize = cleanHex.length / 2;
1626
1664
  if (mergedOptions.limits?.maxInputSize && inputSize > mergedOptions.limits.maxInputSize) {
1627
1665
  throw new Error(`Input size ${inputSize} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
@@ -1650,26 +1688,40 @@ function useCborParser() {
1650
1688
  throw new Error(`Unknown major type: ${majorType}`);
1651
1689
  }
1652
1690
  };
1653
- const parseWithSourceMap = (hexString, options) => {
1654
- const cleanHex = hexString.replace(/\s+/g, "");
1655
- if (!cleanHex || cleanHex.length === 0) {
1656
- throw new Error("Empty hex string");
1657
- }
1658
- if (cleanHex.length % 2 !== 0) {
1659
- throw new Error("Hex string must have even length");
1660
- }
1661
- if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
1662
- throw new Error(`Invalid hex character in: ${cleanHex}`);
1663
- }
1691
+ const parseWithSourceMap = (input, options) => {
1664
1692
  const mergedOptions = mergeOptions(options);
1665
- const inputSize = cleanHex.length / 2;
1666
- if (mergedOptions.limits?.maxInputSize && inputSize > mergedOptions.limits.maxInputSize) {
1667
- throw new Error(`Input size ${inputSize} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
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);
1668
1718
  }
1669
- const buffer = hexToBytes(cleanHex);
1670
1719
  const sourceMap = [];
1671
1720
  const ctx = {
1672
1721
  buffer,
1722
+ offset: 0,
1723
+ sourceMap,
1724
+ currentDepth: 0,
1673
1725
  startTime: Date.now(),
1674
1726
  bytesAllocated: 0,
1675
1727
  options: mergedOptions
@@ -1812,19 +1864,57 @@ function useCborParser() {
1812
1864
  }
1813
1865
  return result;
1814
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
+ };
1815
1897
  const parseIntegerFromBuffer = (buffer, offset, options) => {
1816
- const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1817
- return parseInteger(hexString, options);
1898
+ return integerFromBuffer(buffer, offset, options);
1818
1899
  };
1819
1900
  const parseStringFromBuffer = (buffer, offset, options) => {
1820
- const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1821
- return parseString(hexString, options);
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);
1822
1907
  };
1823
1908
  const parseFloatFromBuffer = (buffer, offset, options) => {
1824
- const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1825
- return parseFloatOrSimple(hexString, options);
1909
+ return floatOrSimpleFromBuffer(buffer, offset, options);
1826
1910
  };
1827
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;
1828
1918
  const startOffset = offset;
1829
1919
  const initialByte = readByte(ctx.buffer, offset);
1830
1920
  const { additionalInfo } = extractCborHeader(initialByte);
@@ -1851,6 +1941,10 @@ function useCborParser() {
1851
1941
  length = Number(bigLength);
1852
1942
  currentOffset += 8;
1853
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
+ }
1854
1948
  isIndefinite = true;
1855
1949
  length = 0;
1856
1950
  } else {
@@ -1867,49 +1961,70 @@ function useCborParser() {
1867
1961
  isHeader: true,
1868
1962
  headerEnd
1869
1963
  });
1870
- const childPaths = [];
1871
- if (isIndefinite) {
1872
- let index = 0;
1873
- while (currentOffset < ctx.buffer.length) {
1874
- const nextByte = readByte(ctx.buffer, currentOffset);
1875
- if (nextByte === 255) {
1876
- currentOffset++;
1877
- break;
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++;
1878
1989
  }
1879
- const elementPath = `${path}[${index}]`;
1880
- childPaths.push(elementPath);
1881
- const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
1882
- items.push(elementResult.value);
1883
- currentOffset += elementResult.bytesRead;
1884
- const elementEntry = sourceMap.find((e) => e.path === elementPath);
1885
- if (elementEntry) {
1886
- elementEntry.parent = path;
1990
+ if (!foundBreak) {
1991
+ throw new Error("Indefinite-length array missing break code (0xFF)");
1887
1992
  }
1888
- index++;
1889
- }
1890
- } else {
1891
- for (let i = 0; i < length; i++) {
1892
- const elementPath = `${path}[${i}]`;
1893
- childPaths.push(elementPath);
1894
- const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
1895
- items.push(elementResult.value);
1896
- currentOffset += elementResult.bytesRead;
1897
- const elementEntry = sourceMap.find((e) => e.path === elementPath);
1898
- if (elementEntry) {
1899
- elementEntry.parent = path;
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
+ }
1900
2007
  }
1901
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;
1902
2019
  }
1903
- const bytesRead = currentOffset - offset;
1904
- if (childPaths.length > 0 && sourceMap[arrayEntryIndex]) {
1905
- sourceMap[arrayEntryIndex].children = childPaths;
1906
- }
1907
- return {
1908
- value: items,
1909
- bytesRead
1910
- };
1911
2020
  };
1912
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;
1913
2028
  const startOffset = offset;
1914
2029
  const initialByte = readByte(ctx.buffer, offset);
1915
2030
  const { additionalInfo } = extractCborHeader(initialByte);
@@ -1936,6 +2051,10 @@ function useCborParser() {
1936
2051
  length = Number(bigLength);
1937
2052
  currentOffset += 8;
1938
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
+ }
1939
2058
  isIndefinite = true;
1940
2059
  length = 0;
1941
2060
  } else {
@@ -1952,72 +2071,91 @@ function useCborParser() {
1952
2071
  isHeader: true,
1953
2072
  headerEnd
1954
2073
  });
1955
- const childPaths = [];
1956
- const seenKeys = /* @__PURE__ */ new Set();
1957
- if (isIndefinite) {
1958
- while (currentOffset < ctx.buffer.length) {
1959
- const nextByte = readByte(ctx.buffer, currentOffset);
1960
- if (nextByte === 255) {
1961
- currentOffset++;
1962
- break;
1963
- }
1964
- const keyPath = `${path}${path ? "." : ""}#key`;
1965
- const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
1966
- currentOffset += keyResult.bytesRead;
1967
- const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
1968
- if (seenKeys.has(keyString)) {
1969
- const mode = ctx.options?.dupMapKeyMode || "allow";
1970
- if (mode === "reject") {
1971
- throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
1972
- } else if (mode === "warn") {
1973
- logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
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
+ }
1974
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;
2112
+ }
2113
+ count++;
1975
2114
  }
1976
- seenKeys.add(keyString);
1977
- const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
1978
- childPaths.push(valuePath);
1979
- const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
1980
- map.set(keyResult.value, valueResult.value);
1981
- currentOffset += valueResult.bytesRead;
1982
- const valueEntry = sourceMap.find((e) => e.path === valuePath);
1983
- if (valueEntry) {
1984
- valueEntry.parent = path;
2115
+ if (!foundBreak) {
2116
+ throw new Error("Indefinite-length map missing break code (0xFF)");
1985
2117
  }
1986
- }
1987
- } else {
1988
- for (let i = 0; i < length; i++) {
1989
- const keyPath = `${path}${path ? "." : ""}#key${i}`;
1990
- const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
1991
- currentOffset += keyResult.bytesRead;
1992
- const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
1993
- if (seenKeys.has(keyString)) {
1994
- const mode = ctx.options?.dupMapKeyMode || "allow";
1995
- if (mode === "reject") {
1996
- throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
1997
- } else if (mode === "warn") {
1998
- logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
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}`);
2121
+ }
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;
1999
2145
  }
2000
- }
2001
- seenKeys.add(keyString);
2002
- const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
2003
- childPaths.push(valuePath);
2004
- const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
2005
- map.set(keyResult.value, valueResult.value);
2006
- currentOffset += valueResult.bytesRead;
2007
- const valueEntry = sourceMap.find((e) => e.path === valuePath);
2008
- if (valueEntry) {
2009
- valueEntry.parent = path;
2010
2146
  }
2011
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;
2012
2158
  }
2013
- const bytesRead = currentOffset - offset;
2014
- if (sourceMap[mapEntryIndex]) {
2015
- sourceMap[mapEntryIndex].children = childPaths;
2016
- }
2017
- return {
2018
- value: map,
2019
- bytesRead
2020
- };
2021
2159
  };
2022
2160
  const parseTagNumberHelper = (buffer, offset, ai) => {
2023
2161
  if (ai < 24) {
@@ -2076,10 +2214,29 @@ function useCborParser() {
2076
2214
  if (valueEntry) {
2077
2215
  valueEntry.parent = path;
2078
2216
  }
2079
- const hexString = Array.from(ctx.buffer.slice(startOffset, currentOffset)).map((b) => b.toString(16).padStart(2, "0")).join("");
2080
- const tagResult = parseTag(hexString, ctx.options);
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
+ };
2081
2238
  return {
2082
- value: tagResult.value,
2239
+ value: taggedValue,
2083
2240
  bytesRead: currentOffset - startOffset
2084
2241
  };
2085
2242
  };
@@ -2094,28 +2251,42 @@ function useCborParser() {
2094
2251
  if (ai < 20) return `Simple Value ${ai}`;
2095
2252
  return "Simple Value";
2096
2253
  };
2097
- const parseSequence = (hexString, options) => {
2098
- const cleanHex = hexString.replace(/\s+/g, "");
2099
- if (!cleanHex || cleanHex.length === 0) {
2100
- return [];
2101
- }
2102
- if (cleanHex.length % 2 !== 0) {
2103
- throw new Error("Hex string must have even length");
2104
- }
2105
- if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
2106
- throw new Error(`Invalid hex character in: ${cleanHex}`);
2107
- }
2254
+ const parseSequence = (input, options) => {
2108
2255
  const mergedOptions = mergeOptions(options);
2109
- const buffer = hexToBytes(cleanHex);
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
+ }
2110
2275
  const results = [];
2111
2276
  let offset = 0;
2277
+ const sequenceStartTime = mergedOptions.limits?.maxParseTime ? Date.now() : 0;
2112
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
+ }
2113
2285
  const byte = readByte(buffer, offset);
2114
2286
  if (byte === 255) {
2115
2287
  throw new Error(`Unexpected break code (0xff) at offset ${offset} - not inside indefinite-length item`);
2116
2288
  }
2117
- const remainingHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
2118
- const result = parse(remainingHex, mergedOptions);
2289
+ const result = dispatchFromBuffer(buffer, offset, mergedOptions);
2119
2290
  results.push(result.value);
2120
2291
  offset += result.bytesRead;
2121
2292
  }
@@ -2164,5 +2335,5 @@ function useCborParser() {
2164
2335
  }
2165
2336
 
2166
2337
  export { bytesToHex, extractCborHeader, hexToBytes, readBigUint, readByte, readUint, useCborCollection, useCborFloat, useCborInteger, useCborParser, useCborString, useCborTag, validateUtf8Strict };
2167
- //# sourceMappingURL=chunk-5A5T56JB.js.map
2168
- //# sourceMappingURL=chunk-5A5T56JB.js.map
2338
+ //# sourceMappingURL=chunk-5IWW5H47.js.map
2339
+ //# sourceMappingURL=chunk-5IWW5H47.js.map