@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
@@ -4,6 +4,15 @@ var chunkPD72MVTX_cjs = require('./chunk-PD72MVTX.cjs');
4
4
 
5
5
  // src/parser/utils.ts
6
6
  var hexToBytes = (hex) => {
7
+ if (hex.length === 0) {
8
+ return new Uint8Array(0);
9
+ }
10
+ if (hex.length % 2 !== 0) {
11
+ throw new Error("Hex string must have even length");
12
+ }
13
+ if (!/^[0-9a-fA-F]+$/.test(hex)) {
14
+ throw new Error(`Invalid hex character in: ${hex}`);
15
+ }
7
16
  const bytes = hex.match(/.{1,2}/g);
8
17
  if (!bytes) return new Uint8Array(0);
9
18
  return new Uint8Array(bytes.map((byte) => parseInt(byte, 16)));
@@ -235,9 +244,8 @@ function hasDuplicates(items) {
235
244
 
236
245
  // src/parser/composables/useCborInteger.ts
237
246
  function useCborInteger() {
238
- const parseInteger = (hexString, options) => {
239
- const buffer = hexToBytes(hexString);
240
- const initialByte = readByte(buffer, 0);
247
+ const parseIntegerFromBuffer = (buffer, offset, options) => {
248
+ const initialByte = readByte(buffer, offset);
241
249
  const { majorType, additionalInfo } = extractCborHeader(initialByte);
242
250
  let rawValue;
243
251
  let bytesRead;
@@ -245,16 +253,16 @@ function useCborInteger() {
245
253
  rawValue = additionalInfo;
246
254
  bytesRead = 1;
247
255
  } else if (additionalInfo === 24) {
248
- rawValue = readByte(buffer, 1);
256
+ rawValue = readByte(buffer, offset + 1);
249
257
  bytesRead = 2;
250
258
  } else if (additionalInfo === 25) {
251
- rawValue = readUint(buffer, 1, 2);
259
+ rawValue = readUint(buffer, offset + 1, 2);
252
260
  bytesRead = 3;
253
261
  } else if (additionalInfo === 26) {
254
- rawValue = readUint(buffer, 1, 4);
262
+ rawValue = readUint(buffer, offset + 1, 4);
255
263
  bytesRead = 5;
256
264
  } else if (additionalInfo === 27) {
257
- const bigValue = readBigUint(buffer, 1, 8);
265
+ const bigValue = readBigUint(buffer, offset + 1, 8);
258
266
  if (bigValue <= BigInt(Number.MAX_SAFE_INTEGER)) {
259
267
  rawValue = Number(bigValue);
260
268
  } else {
@@ -294,8 +302,13 @@ function useCborInteger() {
294
302
  bytesRead
295
303
  };
296
304
  };
305
+ const parseInteger = (hexString, options) => {
306
+ const buffer = hexToBytes(hexString);
307
+ return parseIntegerFromBuffer(buffer, 0, options);
308
+ };
297
309
  return {
298
- parseInteger
310
+ parseInteger,
311
+ parseIntegerFromBuffer
299
312
  };
300
313
  }
301
314
 
@@ -704,29 +717,34 @@ function useCborFloat() {
704
717
  return {
705
718
  parse,
706
719
  parseFloat,
707
- parseSimple
720
+ parseSimple,
721
+ parseFromBuffer
708
722
  };
709
723
  }
710
724
 
711
725
  // src/parser/composables/useCborTag.ts
712
726
  function useCborTag() {
713
- const { parseInteger } = useCborInteger();
727
+ const { parseIntegerFromBuffer } = useCborInteger();
714
728
  const { parseByteString, parseTextString } = useCborString();
715
- const { parseFloat, parseSimple } = useCborFloat();
729
+ const { parseFromBuffer: parseFloatOrSimpleFromBuffer } = useCborFloat();
730
+ let parseStartTime = 0;
716
731
  const parseItem = (buffer, offset, options, tagDepth = 0, collectionDepth = 0) => {
732
+ if (parseStartTime > 0 && options?.limits?.maxParseTime) {
733
+ const elapsed = Date.now() - parseStartTime;
734
+ if (elapsed > options.limits.maxParseTime) {
735
+ throw new Error(`Parse timeout: exceeded ${options.limits.maxParseTime}ms limit`);
736
+ }
737
+ }
717
738
  if (offset >= buffer.length) {
718
739
  throw new Error(`Unexpected end of buffer at offset ${offset}`);
719
740
  }
720
741
  const initialByte = readByte(buffer, offset);
721
- const { majorType, additionalInfo } = extractCborHeader(initialByte);
742
+ const { majorType } = extractCborHeader(initialByte);
722
743
  switch (majorType) {
723
744
  case 0:
724
745
  // Unsigned integer
725
- case 1: {
726
- const intHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
727
- const result = parseInteger(intHex, options);
728
- return { value: result.value, bytesRead: result.bytesRead };
729
- }
746
+ case 1:
747
+ return parseIntegerFromBuffer(buffer, offset, options);
730
748
  case 2:
731
749
  return parseByteString(buffer, offset, options);
732
750
  case 3:
@@ -737,16 +755,8 @@ function useCborTag() {
737
755
  return parseMapInternal(buffer, offset, options, collectionDepth);
738
756
  case 6:
739
757
  return parseTagFromBuffer(buffer, offset, options, tagDepth);
740
- case 7: {
741
- const simpleHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
742
- if (additionalInfo >= 25 && additionalInfo <= 27) {
743
- const result = parseFloat(simpleHex, options);
744
- return { value: result.value, bytesRead: result.bytesRead };
745
- } else {
746
- const result = parseSimple(simpleHex, options);
747
- return { value: result.value, bytesRead: result.bytesRead };
748
- }
749
- }
758
+ case 7:
759
+ return parseFloatOrSimpleFromBuffer(buffer, offset, options);
750
760
  default:
751
761
  throw new Error(`Unknown major type: ${majorType}`);
752
762
  }
@@ -1010,11 +1020,11 @@ function useCborTag() {
1010
1020
  if (value.length !== 2) {
1011
1021
  throw new Error(`Tag 4 (decimal fraction) array must have exactly 2 elements [exponent, mantissa], got ${value.length}`);
1012
1022
  }
1013
- if (typeof value[0] !== "number" && typeof value[0] !== "bigint") {
1014
- throw new Error(`Tag 4 (decimal fraction) exponent must be an integer, got ${typeof value[0]}`);
1023
+ if (typeof value[0] !== "number" && typeof value[0] !== "bigint" || typeof value[0] === "number" && !Number.isInteger(value[0])) {
1024
+ throw new Error(`Tag 4 (decimal fraction) exponent must be an integer, got ${value[0]}`);
1015
1025
  }
1016
- if (typeof value[1] !== "number" && typeof value[1] !== "bigint") {
1017
- throw new Error(`Tag 4 (decimal fraction) mantissa must be an integer, got ${typeof value[1]}`);
1026
+ if (typeof value[1] !== "number" && typeof value[1] !== "bigint" || typeof value[1] === "number" && !Number.isInteger(value[1])) {
1027
+ throw new Error(`Tag 4 (decimal fraction) mantissa must be an integer, got ${value[1]}`);
1018
1028
  }
1019
1029
  break;
1020
1030
  case 5:
@@ -1025,11 +1035,11 @@ function useCborTag() {
1025
1035
  if (value.length !== 2) {
1026
1036
  throw new Error(`Tag 5 (bigfloat) array must have exactly 2 elements [exponent, mantissa], got ${value.length}`);
1027
1037
  }
1028
- if (typeof value[0] !== "number" && typeof value[0] !== "bigint") {
1029
- throw new Error(`Tag 5 (bigfloat) exponent must be an integer, got ${typeof value[0]}`);
1038
+ if (typeof value[0] !== "number" && typeof value[0] !== "bigint" || typeof value[0] === "number" && !Number.isInteger(value[0])) {
1039
+ throw new Error(`Tag 5 (bigfloat) exponent must be an integer, got ${value[0]}`);
1030
1040
  }
1031
- if (typeof value[1] !== "number" && typeof value[1] !== "bigint") {
1032
- throw new Error(`Tag 5 (bigfloat) mantissa must be an integer, got ${typeof value[1]}`);
1041
+ if (typeof value[1] !== "number" && typeof value[1] !== "bigint" || typeof value[1] === "number" && !Number.isInteger(value[1])) {
1042
+ throw new Error(`Tag 5 (bigfloat) mantissa must be an integer, got ${value[1]}`);
1033
1043
  }
1034
1044
  break;
1035
1045
  case 32:
@@ -1233,12 +1243,25 @@ function useCborTag() {
1233
1243
  const parseTag = (hexString, options) => {
1234
1244
  const cleanHex = hexString.replace(/\s+/g, "");
1235
1245
  const buffer = hexToBytes(cleanHex);
1236
- return parseTagFromBuffer(buffer, 0, options);
1246
+ const isTopLevel = parseStartTime === 0;
1247
+ if (isTopLevel && options?.limits?.maxParseTime) {
1248
+ parseStartTime = Date.now();
1249
+ }
1250
+ try {
1251
+ return parseTagFromBuffer(buffer, 0, options);
1252
+ } finally {
1253
+ if (isTopLevel) {
1254
+ parseStartTime = 0;
1255
+ }
1256
+ }
1237
1257
  };
1238
1258
  const parse = parseTag;
1239
1259
  return {
1240
1260
  parseTag,
1241
- parse
1261
+ parse,
1262
+ parseTagFromBuffer,
1263
+ validateTagSemantics,
1264
+ decodePlutusConstructor
1242
1265
  };
1243
1266
  }
1244
1267
 
@@ -1297,17 +1320,18 @@ var logger = {
1297
1320
 
1298
1321
  // src/parser/composables/useCborCollection.ts
1299
1322
  function useCborCollection() {
1300
- const { parseInteger } = useCborInteger();
1323
+ const { parseIntegerFromBuffer } = useCborInteger();
1301
1324
  const { parseByteString, parseTextString } = useCborString();
1302
- const { parse: parseFloatOrSimple } = useCborFloat();
1303
- const { parseTag } = useCborTag();
1304
- const convertKeyToString = (key) => {
1305
- if (key instanceof Uint8Array) {
1306
- return bytesToHex(key);
1307
- }
1308
- return String(key);
1309
- };
1325
+ const { parseFromBuffer: parseFloatOrSimpleFromBuffer } = useCborFloat();
1326
+ const { parseTagFromBuffer } = useCborTag();
1327
+ let parseStartTime = 0;
1310
1328
  const parseItem = (buffer, offset, options, depth = 0) => {
1329
+ if (parseStartTime > 0 && options?.limits?.maxParseTime) {
1330
+ const elapsed = Date.now() - parseStartTime;
1331
+ if (elapsed > options.limits.maxParseTime) {
1332
+ throw new Error(`Parse timeout: exceeded ${options.limits.maxParseTime}ms limit`);
1333
+ }
1334
+ }
1311
1335
  if (offset >= buffer.length) {
1312
1336
  throw new Error(`Unexpected end of buffer at offset ${offset}`);
1313
1337
  }
@@ -1316,11 +1340,8 @@ function useCborCollection() {
1316
1340
  switch (majorType) {
1317
1341
  case 0:
1318
1342
  // Unsigned integer
1319
- case 1: {
1320
- const intHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1321
- const result = parseInteger(intHex, options);
1322
- return { value: result.value, bytesRead: result.bytesRead };
1323
- }
1343
+ case 1:
1344
+ return parseIntegerFromBuffer(buffer, offset, options);
1324
1345
  case 2:
1325
1346
  return parseByteString(buffer, offset, options);
1326
1347
  case 3:
@@ -1329,16 +1350,10 @@ function useCborCollection() {
1329
1350
  return parseArrayFromBuffer(buffer, offset, options, depth);
1330
1351
  case 5:
1331
1352
  return parseMapFromBuffer(buffer, offset, options, depth);
1332
- case 6: {
1333
- const tagHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1334
- const result = parseTag(tagHex, options);
1335
- return { value: result.value, bytesRead: result.bytesRead };
1336
- }
1337
- case 7: {
1338
- const floatHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1339
- const result = parseFloatOrSimple(floatHex, options);
1340
- return { value: result.value, bytesRead: result.bytesRead };
1341
- }
1353
+ case 6:
1354
+ return parseTagFromBuffer(buffer, offset, options);
1355
+ case 7:
1356
+ return parseFloatOrSimpleFromBuffer(buffer, offset, options);
1342
1357
  default:
1343
1358
  throw new Error(`Unknown major type: ${majorType}`);
1344
1359
  }
@@ -1480,7 +1495,7 @@ function useCborCollection() {
1480
1495
  }
1481
1496
  const valueResult = parseItem(buffer, currentOffset, options, depth + 1);
1482
1497
  currentOffset += valueResult.bytesRead;
1483
- const keyString = convertKeyToString(keyResult.value);
1498
+ const keyString = serializeValueForComparison(keyResult.value);
1484
1499
  if (seenKeys.has(keyString)) {
1485
1500
  const mode = options?.dupMapKeyMode || "allow";
1486
1501
  if (mode === "reject") {
@@ -1518,7 +1533,7 @@ function useCborCollection() {
1518
1533
  }
1519
1534
  const valueResult = parseItem(buffer, currentOffset, options, depth + 1);
1520
1535
  currentOffset += valueResult.bytesRead;
1521
- const keyString = convertKeyToString(keyResult.value);
1536
+ const keyString = serializeValueForComparison(keyResult.value);
1522
1537
  if (seenKeys.has(keyString)) {
1523
1538
  const mode = options?.dupMapKeyMode || "allow";
1524
1539
  if (mode === "reject") {
@@ -1558,12 +1573,26 @@ function useCborCollection() {
1558
1573
  const parseArray = (hexString, options) => {
1559
1574
  const cleanHex = hexString.replace(/\s+/g, "");
1560
1575
  const buffer = hexToBytes(cleanHex);
1561
- return parseArrayFromBuffer(buffer, 0, options, 0);
1576
+ if (options?.limits?.maxParseTime) {
1577
+ parseStartTime = Date.now();
1578
+ }
1579
+ try {
1580
+ return parseArrayFromBuffer(buffer, 0, options, 0);
1581
+ } finally {
1582
+ parseStartTime = 0;
1583
+ }
1562
1584
  };
1563
1585
  const parseMap = (hexString, options) => {
1564
1586
  const cleanHex = hexString.replace(/\s+/g, "");
1565
1587
  const buffer = hexToBytes(cleanHex);
1566
- return parseMapFromBuffer(buffer, 0, options, 0);
1588
+ if (options?.limits?.maxParseTime) {
1589
+ parseStartTime = Date.now();
1590
+ }
1591
+ try {
1592
+ return parseMapFromBuffer(buffer, 0, options, 0);
1593
+ } finally {
1594
+ parseStartTime = 0;
1595
+ }
1567
1596
  };
1568
1597
  return {
1569
1598
  parseArray,
@@ -1607,13 +1636,23 @@ function useCborParser() {
1607
1636
  throw new Error(`Parse timeout: exceeded ${ctx.options.limits.maxParseTime}ms limit (elapsed: ${elapsed}ms)`);
1608
1637
  }
1609
1638
  };
1610
- const { parseInteger } = useCborInteger();
1611
- const { parseString } = useCborString();
1639
+ const { parseInteger, parseIntegerFromBuffer: integerFromBuffer } = useCborInteger();
1640
+ const { parseString, parseByteString: byteStringFromBuffer, parseTextString: textStringFromBuffer } = useCborString();
1612
1641
  const { parseArray, parseMap } = useCborCollection();
1613
- const { parseTag } = useCborTag();
1614
- const { parse: parseFloatOrSimple } = useCborFloat();
1615
- const parse = (hexString, options) => {
1616
- const cleanHex = hexString.replace(/\s+/g, "");
1642
+ const { parseTag, validateTagSemantics, decodePlutusConstructor } = useCborTag();
1643
+ const { parse: parseFloatOrSimple, parseFromBuffer: floatOrSimpleFromBuffer } = useCborFloat();
1644
+ const parse = (input, options) => {
1645
+ const mergedOptions = mergeOptions(options);
1646
+ if (input instanceof Uint8Array) {
1647
+ if (input.length === 0) {
1648
+ throw new Error("Empty input");
1649
+ }
1650
+ if (mergedOptions.limits?.maxInputSize && input.length > mergedOptions.limits.maxInputSize) {
1651
+ throw new Error(`Input size ${input.length} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
1652
+ }
1653
+ return dispatchFromBuffer(input, 0, mergedOptions);
1654
+ }
1655
+ const cleanHex = input.replace(/\s+/g, "");
1617
1656
  if (!cleanHex || cleanHex.length === 0) {
1618
1657
  throw new Error("Empty hex string");
1619
1658
  }
@@ -1623,7 +1662,6 @@ function useCborParser() {
1623
1662
  if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
1624
1663
  throw new Error(`Invalid hex character in: ${cleanHex}`);
1625
1664
  }
1626
- const mergedOptions = mergeOptions(options);
1627
1665
  const inputSize = cleanHex.length / 2;
1628
1666
  if (mergedOptions.limits?.maxInputSize && inputSize > mergedOptions.limits.maxInputSize) {
1629
1667
  throw new Error(`Input size ${inputSize} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
@@ -1652,26 +1690,40 @@ function useCborParser() {
1652
1690
  throw new Error(`Unknown major type: ${majorType}`);
1653
1691
  }
1654
1692
  };
1655
- const parseWithSourceMap = (hexString, options) => {
1656
- const cleanHex = hexString.replace(/\s+/g, "");
1657
- if (!cleanHex || cleanHex.length === 0) {
1658
- throw new Error("Empty hex string");
1659
- }
1660
- if (cleanHex.length % 2 !== 0) {
1661
- throw new Error("Hex string must have even length");
1662
- }
1663
- if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
1664
- throw new Error(`Invalid hex character in: ${cleanHex}`);
1665
- }
1693
+ const parseWithSourceMap = (input, options) => {
1666
1694
  const mergedOptions = mergeOptions(options);
1667
- const inputSize = cleanHex.length / 2;
1668
- if (mergedOptions.limits?.maxInputSize && inputSize > mergedOptions.limits.maxInputSize) {
1669
- throw new Error(`Input size ${inputSize} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
1695
+ let buffer;
1696
+ if (input instanceof Uint8Array) {
1697
+ if (input.length === 0) {
1698
+ throw new Error("Empty input");
1699
+ }
1700
+ if (mergedOptions.limits?.maxInputSize && input.length > mergedOptions.limits.maxInputSize) {
1701
+ throw new Error(`Input size ${input.length} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
1702
+ }
1703
+ buffer = input;
1704
+ } else {
1705
+ const cleanHex = input.replace(/\s+/g, "");
1706
+ if (!cleanHex || cleanHex.length === 0) {
1707
+ throw new Error("Empty hex string");
1708
+ }
1709
+ if (cleanHex.length % 2 !== 0) {
1710
+ throw new Error("Hex string must have even length");
1711
+ }
1712
+ if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
1713
+ throw new Error(`Invalid hex character in: ${cleanHex}`);
1714
+ }
1715
+ const inputSize = cleanHex.length / 2;
1716
+ if (mergedOptions.limits?.maxInputSize && inputSize > mergedOptions.limits.maxInputSize) {
1717
+ throw new Error(`Input size ${inputSize} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
1718
+ }
1719
+ buffer = hexToBytes(cleanHex);
1670
1720
  }
1671
- const buffer = hexToBytes(cleanHex);
1672
1721
  const sourceMap = [];
1673
1722
  const ctx = {
1674
1723
  buffer,
1724
+ offset: 0,
1725
+ sourceMap,
1726
+ currentDepth: 0,
1675
1727
  startTime: Date.now(),
1676
1728
  bytesAllocated: 0,
1677
1729
  options: mergedOptions
@@ -1814,19 +1866,57 @@ function useCborParser() {
1814
1866
  }
1815
1867
  return result;
1816
1868
  };
1869
+ const dispatchFromBuffer = (buffer, offset, options) => {
1870
+ const initialByte = readByte(buffer, offset);
1871
+ const { majorType } = extractCborHeader(initialByte);
1872
+ switch (majorType) {
1873
+ case 0:
1874
+ // Unsigned integer
1875
+ case 1:
1876
+ return integerFromBuffer(buffer, offset, options);
1877
+ case 2:
1878
+ return byteStringFromBuffer(buffer, offset, options);
1879
+ case 3:
1880
+ return textStringFromBuffer(buffer, offset, options);
1881
+ case 4: {
1882
+ const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1883
+ return parseArray(hexString, options);
1884
+ }
1885
+ case 5: {
1886
+ const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1887
+ return parseMap(hexString, options);
1888
+ }
1889
+ case 6: {
1890
+ const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1891
+ return parseTag(hexString, options);
1892
+ }
1893
+ case 7:
1894
+ return floatOrSimpleFromBuffer(buffer, offset, options);
1895
+ default:
1896
+ throw new Error(`Unknown major type: ${majorType}`);
1897
+ }
1898
+ };
1817
1899
  const parseIntegerFromBuffer = (buffer, offset, options) => {
1818
- const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1819
- return parseInteger(hexString, options);
1900
+ return integerFromBuffer(buffer, offset, options);
1820
1901
  };
1821
1902
  const parseStringFromBuffer = (buffer, offset, options) => {
1822
- const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1823
- return parseString(hexString, options);
1903
+ const initialByte = readByte(buffer, offset);
1904
+ const { majorType } = extractCborHeader(initialByte);
1905
+ if (majorType === 2) {
1906
+ return byteStringFromBuffer(buffer, offset, options);
1907
+ }
1908
+ return textStringFromBuffer(buffer, offset, options);
1824
1909
  };
1825
1910
  const parseFloatFromBuffer = (buffer, offset, options) => {
1826
- const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1827
- return parseFloatOrSimple(hexString, options);
1911
+ return floatOrSimpleFromBuffer(buffer, offset, options);
1828
1912
  };
1829
1913
  const parseArrayWithMap = (ctx, offset, path, sourceMap) => {
1914
+ const previousDepth = ctx.currentDepth ?? 0;
1915
+ const maxDepth = ctx.options?.limits?.maxDepth;
1916
+ if (maxDepth !== void 0 && previousDepth >= maxDepth) {
1917
+ throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
1918
+ }
1919
+ ctx.currentDepth = previousDepth + 1;
1830
1920
  const startOffset = offset;
1831
1921
  const initialByte = readByte(ctx.buffer, offset);
1832
1922
  const { additionalInfo } = extractCborHeader(initialByte);
@@ -1853,6 +1943,10 @@ function useCborParser() {
1853
1943
  length = Number(bigLength);
1854
1944
  currentOffset += 8;
1855
1945
  } else if (additionalInfo === 31) {
1946
+ const isIndefiniteAllowed = ctx.options?.allowIndefinite ?? !(ctx.options?.validateCanonical || ctx.options?.strict);
1947
+ if (!isIndefiniteAllowed) {
1948
+ throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
1949
+ }
1856
1950
  isIndefinite = true;
1857
1951
  length = 0;
1858
1952
  } else {
@@ -1869,49 +1963,70 @@ function useCborParser() {
1869
1963
  isHeader: true,
1870
1964
  headerEnd
1871
1965
  });
1872
- const childPaths = [];
1873
- if (isIndefinite) {
1874
- let index = 0;
1875
- while (currentOffset < ctx.buffer.length) {
1876
- const nextByte = readByte(ctx.buffer, currentOffset);
1877
- if (nextByte === 255) {
1878
- currentOffset++;
1879
- break;
1966
+ try {
1967
+ const childPaths = [];
1968
+ if (isIndefinite) {
1969
+ let index = 0;
1970
+ let foundBreak = false;
1971
+ while (currentOffset < ctx.buffer.length) {
1972
+ const nextByte = readByte(ctx.buffer, currentOffset);
1973
+ if (nextByte === 255) {
1974
+ currentOffset++;
1975
+ foundBreak = true;
1976
+ break;
1977
+ }
1978
+ if (ctx.options?.limits?.maxArrayLength && index >= ctx.options.limits.maxArrayLength) {
1979
+ throw new Error(`Array length exceeds limit of ${ctx.options.limits.maxArrayLength}`);
1980
+ }
1981
+ const elementPath = `${path}[${index}]`;
1982
+ childPaths.push(elementPath);
1983
+ const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
1984
+ items.push(elementResult.value);
1985
+ currentOffset += elementResult.bytesRead;
1986
+ const elementEntry = sourceMap.find((e) => e.path === elementPath);
1987
+ if (elementEntry) {
1988
+ elementEntry.parent = path;
1989
+ }
1990
+ index++;
1880
1991
  }
1881
- const elementPath = `${path}[${index}]`;
1882
- childPaths.push(elementPath);
1883
- const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
1884
- items.push(elementResult.value);
1885
- currentOffset += elementResult.bytesRead;
1886
- const elementEntry = sourceMap.find((e) => e.path === elementPath);
1887
- if (elementEntry) {
1888
- elementEntry.parent = path;
1992
+ if (!foundBreak) {
1993
+ throw new Error("Indefinite-length array missing break code (0xFF)");
1889
1994
  }
1890
- index++;
1891
- }
1892
- } else {
1893
- for (let i = 0; i < length; i++) {
1894
- const elementPath = `${path}[${i}]`;
1895
- childPaths.push(elementPath);
1896
- const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
1897
- items.push(elementResult.value);
1898
- currentOffset += elementResult.bytesRead;
1899
- const elementEntry = sourceMap.find((e) => e.path === elementPath);
1900
- if (elementEntry) {
1901
- elementEntry.parent = path;
1995
+ } else {
1996
+ if (ctx.options?.limits?.maxArrayLength && length > ctx.options.limits.maxArrayLength) {
1997
+ throw new Error(`Array length ${length} exceeds limit of ${ctx.options.limits.maxArrayLength}`);
1998
+ }
1999
+ for (let i = 0; i < length; i++) {
2000
+ const elementPath = `${path}[${i}]`;
2001
+ childPaths.push(elementPath);
2002
+ const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
2003
+ items.push(elementResult.value);
2004
+ currentOffset += elementResult.bytesRead;
2005
+ const elementEntry = sourceMap.find((e) => e.path === elementPath);
2006
+ if (elementEntry) {
2007
+ elementEntry.parent = path;
2008
+ }
1902
2009
  }
1903
2010
  }
2011
+ const bytesRead = currentOffset - offset;
2012
+ if (childPaths.length > 0 && sourceMap[arrayEntryIndex]) {
2013
+ sourceMap[arrayEntryIndex].children = childPaths;
2014
+ }
2015
+ return {
2016
+ value: items,
2017
+ bytesRead
2018
+ };
2019
+ } finally {
2020
+ ctx.currentDepth = previousDepth;
1904
2021
  }
1905
- const bytesRead = currentOffset - offset;
1906
- if (childPaths.length > 0 && sourceMap[arrayEntryIndex]) {
1907
- sourceMap[arrayEntryIndex].children = childPaths;
1908
- }
1909
- return {
1910
- value: items,
1911
- bytesRead
1912
- };
1913
2022
  };
1914
2023
  const parseMapWithMap = (ctx, offset, path, sourceMap) => {
2024
+ const previousDepth = ctx.currentDepth ?? 0;
2025
+ const maxDepth = ctx.options?.limits?.maxDepth;
2026
+ if (maxDepth !== void 0 && previousDepth >= maxDepth) {
2027
+ throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
2028
+ }
2029
+ ctx.currentDepth = previousDepth + 1;
1915
2030
  const startOffset = offset;
1916
2031
  const initialByte = readByte(ctx.buffer, offset);
1917
2032
  const { additionalInfo } = extractCborHeader(initialByte);
@@ -1938,6 +2053,10 @@ function useCborParser() {
1938
2053
  length = Number(bigLength);
1939
2054
  currentOffset += 8;
1940
2055
  } else if (additionalInfo === 31) {
2056
+ const isIndefiniteAllowed = ctx.options?.allowIndefinite ?? !(ctx.options?.validateCanonical || ctx.options?.strict);
2057
+ if (!isIndefiniteAllowed) {
2058
+ throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
2059
+ }
1941
2060
  isIndefinite = true;
1942
2061
  length = 0;
1943
2062
  } else {
@@ -1954,72 +2073,91 @@ function useCborParser() {
1954
2073
  isHeader: true,
1955
2074
  headerEnd
1956
2075
  });
1957
- const childPaths = [];
1958
- const seenKeys = /* @__PURE__ */ new Set();
1959
- if (isIndefinite) {
1960
- while (currentOffset < ctx.buffer.length) {
1961
- const nextByte = readByte(ctx.buffer, currentOffset);
1962
- if (nextByte === 255) {
1963
- currentOffset++;
1964
- break;
1965
- }
1966
- const keyPath = `${path}${path ? "." : ""}#key`;
1967
- const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
1968
- currentOffset += keyResult.bytesRead;
1969
- const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
1970
- if (seenKeys.has(keyString)) {
1971
- const mode = ctx.options?.dupMapKeyMode || "allow";
1972
- if (mode === "reject") {
1973
- throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
1974
- } else if (mode === "warn") {
1975
- logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2076
+ try {
2077
+ const childPaths = [];
2078
+ const seenKeys = /* @__PURE__ */ new Set();
2079
+ if (isIndefinite) {
2080
+ let count = 0;
2081
+ let foundBreak = false;
2082
+ while (currentOffset < ctx.buffer.length) {
2083
+ const nextByte = readByte(ctx.buffer, currentOffset);
2084
+ if (nextByte === 255) {
2085
+ currentOffset++;
2086
+ foundBreak = true;
2087
+ break;
2088
+ }
2089
+ if (ctx.options?.limits?.maxMapSize && count >= ctx.options.limits.maxMapSize) {
2090
+ throw new Error(`Map size exceeds limit of ${ctx.options.limits.maxMapSize}`);
2091
+ }
2092
+ const keyPath = `${path}${path ? "." : ""}#key`;
2093
+ const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
2094
+ currentOffset += keyResult.bytesRead;
2095
+ const keyForDupCheck = serializeValueForComparison(keyResult.value);
2096
+ const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
2097
+ if (seenKeys.has(keyForDupCheck)) {
2098
+ const mode = ctx.options?.dupMapKeyMode || "allow";
2099
+ if (mode === "reject") {
2100
+ throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2101
+ } else if (mode === "warn") {
2102
+ logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2103
+ }
1976
2104
  }
2105
+ seenKeys.add(keyForDupCheck);
2106
+ const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
2107
+ childPaths.push(valuePath);
2108
+ const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
2109
+ map.set(keyResult.value, valueResult.value);
2110
+ currentOffset += valueResult.bytesRead;
2111
+ const valueEntry = sourceMap.find((e) => e.path === valuePath);
2112
+ if (valueEntry) {
2113
+ valueEntry.parent = path;
2114
+ }
2115
+ count++;
1977
2116
  }
1978
- seenKeys.add(keyString);
1979
- const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
1980
- childPaths.push(valuePath);
1981
- const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
1982
- map.set(keyResult.value, valueResult.value);
1983
- currentOffset += valueResult.bytesRead;
1984
- const valueEntry = sourceMap.find((e) => e.path === valuePath);
1985
- if (valueEntry) {
1986
- valueEntry.parent = path;
2117
+ if (!foundBreak) {
2118
+ throw new Error("Indefinite-length map missing break code (0xFF)");
1987
2119
  }
1988
- }
1989
- } else {
1990
- for (let i = 0; i < length; i++) {
1991
- const keyPath = `${path}${path ? "." : ""}#key${i}`;
1992
- const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
1993
- currentOffset += keyResult.bytesRead;
1994
- const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
1995
- if (seenKeys.has(keyString)) {
1996
- const mode = ctx.options?.dupMapKeyMode || "allow";
1997
- if (mode === "reject") {
1998
- throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
1999
- } else if (mode === "warn") {
2000
- logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2120
+ } else {
2121
+ if (ctx.options?.limits?.maxMapSize && length > ctx.options.limits.maxMapSize) {
2122
+ throw new Error(`Map size ${length} exceeds limit of ${ctx.options.limits.maxMapSize}`);
2123
+ }
2124
+ for (let i = 0; i < length; i++) {
2125
+ const keyPath = `${path}${path ? "." : ""}#key${i}`;
2126
+ const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
2127
+ currentOffset += keyResult.bytesRead;
2128
+ const keyForDupCheck = serializeValueForComparison(keyResult.value);
2129
+ const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
2130
+ if (seenKeys.has(keyForDupCheck)) {
2131
+ const mode = ctx.options?.dupMapKeyMode || "allow";
2132
+ if (mode === "reject") {
2133
+ throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2134
+ } else if (mode === "warn") {
2135
+ logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2136
+ }
2137
+ }
2138
+ seenKeys.add(keyForDupCheck);
2139
+ const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
2140
+ childPaths.push(valuePath);
2141
+ const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
2142
+ map.set(keyResult.value, valueResult.value);
2143
+ currentOffset += valueResult.bytesRead;
2144
+ const valueEntry = sourceMap.find((e) => e.path === valuePath);
2145
+ if (valueEntry) {
2146
+ valueEntry.parent = path;
2001
2147
  }
2002
- }
2003
- seenKeys.add(keyString);
2004
- const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
2005
- childPaths.push(valuePath);
2006
- const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
2007
- map.set(keyResult.value, valueResult.value);
2008
- currentOffset += valueResult.bytesRead;
2009
- const valueEntry = sourceMap.find((e) => e.path === valuePath);
2010
- if (valueEntry) {
2011
- valueEntry.parent = path;
2012
2148
  }
2013
2149
  }
2150
+ const bytesRead = currentOffset - offset;
2151
+ if (sourceMap[mapEntryIndex]) {
2152
+ sourceMap[mapEntryIndex].children = childPaths;
2153
+ }
2154
+ return {
2155
+ value: map,
2156
+ bytesRead
2157
+ };
2158
+ } finally {
2159
+ ctx.currentDepth = previousDepth;
2014
2160
  }
2015
- const bytesRead = currentOffset - offset;
2016
- if (sourceMap[mapEntryIndex]) {
2017
- sourceMap[mapEntryIndex].children = childPaths;
2018
- }
2019
- return {
2020
- value: map,
2021
- bytesRead
2022
- };
2023
2161
  };
2024
2162
  const parseTagNumberHelper = (buffer, offset, ai) => {
2025
2163
  if (ai < 24) {
@@ -2078,10 +2216,29 @@ function useCborParser() {
2078
2216
  if (valueEntry) {
2079
2217
  valueEntry.parent = path;
2080
2218
  }
2081
- const hexString = Array.from(ctx.buffer.slice(startOffset, currentOffset)).map((b) => b.toString(16).padStart(2, "0")).join("");
2082
- const tagResult = parseTag(hexString, ctx.options);
2219
+ let finalValue = valueResult.value;
2220
+ if ((tagNumber === 2 || tagNumber === 3) && finalValue instanceof Uint8Array) {
2221
+ const maxBignumBytes = ctx.options?.limits?.maxBignumBytes ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxBignumBytes;
2222
+ if (finalValue.length > maxBignumBytes) {
2223
+ throw new Error(
2224
+ `Bignum (tag ${tagNumber}) size ${finalValue.length} bytes exceeds limit of ${maxBignumBytes} bytes`
2225
+ );
2226
+ }
2227
+ let bigintValue = 0n;
2228
+ for (let i = 0; i < finalValue.length; i++) {
2229
+ bigintValue = bigintValue << 8n | BigInt(finalValue[i]);
2230
+ }
2231
+ finalValue = tagNumber === 2 ? bigintValue : -1n - bigintValue;
2232
+ }
2233
+ validateTagSemantics(tagNumber, finalValue, ctx.options);
2234
+ const plutusConstr = decodePlutusConstructor(tagNumber, finalValue);
2235
+ const taggedValue = {
2236
+ tag: tagNumber,
2237
+ value: finalValue,
2238
+ ...plutusConstr && { plutus: plutusConstr }
2239
+ };
2083
2240
  return {
2084
- value: tagResult.value,
2241
+ value: taggedValue,
2085
2242
  bytesRead: currentOffset - startOffset
2086
2243
  };
2087
2244
  };
@@ -2096,28 +2253,42 @@ function useCborParser() {
2096
2253
  if (ai < 20) return `Simple Value ${ai}`;
2097
2254
  return "Simple Value";
2098
2255
  };
2099
- const parseSequence = (hexString, options) => {
2100
- const cleanHex = hexString.replace(/\s+/g, "");
2101
- if (!cleanHex || cleanHex.length === 0) {
2102
- return [];
2103
- }
2104
- if (cleanHex.length % 2 !== 0) {
2105
- throw new Error("Hex string must have even length");
2106
- }
2107
- if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
2108
- throw new Error(`Invalid hex character in: ${cleanHex}`);
2109
- }
2256
+ const parseSequence = (input, options) => {
2110
2257
  const mergedOptions = mergeOptions(options);
2111
- const buffer = hexToBytes(cleanHex);
2258
+ let buffer;
2259
+ if (input instanceof Uint8Array) {
2260
+ if (input.length === 0) {
2261
+ return [];
2262
+ }
2263
+ buffer = input;
2264
+ } else {
2265
+ const cleanHex = input.replace(/\s+/g, "");
2266
+ if (!cleanHex || cleanHex.length === 0) {
2267
+ return [];
2268
+ }
2269
+ if (cleanHex.length % 2 !== 0) {
2270
+ throw new Error("Hex string must have even length");
2271
+ }
2272
+ if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
2273
+ throw new Error(`Invalid hex character in: ${cleanHex}`);
2274
+ }
2275
+ buffer = hexToBytes(cleanHex);
2276
+ }
2112
2277
  const results = [];
2113
2278
  let offset = 0;
2279
+ const sequenceStartTime = mergedOptions.limits?.maxParseTime ? Date.now() : 0;
2114
2280
  while (offset < buffer.length) {
2281
+ if (sequenceStartTime > 0 && mergedOptions.limits?.maxParseTime) {
2282
+ const elapsed = Date.now() - sequenceStartTime;
2283
+ if (elapsed > mergedOptions.limits.maxParseTime) {
2284
+ throw new Error(`Parse timeout: exceeded ${mergedOptions.limits.maxParseTime}ms limit`);
2285
+ }
2286
+ }
2115
2287
  const byte = readByte(buffer, offset);
2116
2288
  if (byte === 255) {
2117
2289
  throw new Error(`Unexpected break code (0xff) at offset ${offset} - not inside indefinite-length item`);
2118
2290
  }
2119
- const remainingHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
2120
- const result = parse(remainingHex, mergedOptions);
2291
+ const result = dispatchFromBuffer(buffer, offset, mergedOptions);
2121
2292
  results.push(result.value);
2122
2293
  offset += result.bytesRead;
2123
2294
  }
@@ -2178,5 +2349,5 @@ exports.useCborParser = useCborParser;
2178
2349
  exports.useCborString = useCborString;
2179
2350
  exports.useCborTag = useCborTag;
2180
2351
  exports.validateUtf8Strict = validateUtf8Strict;
2181
- //# sourceMappingURL=chunk-PTWN7K3Y.cjs.map
2182
- //# sourceMappingURL=chunk-PTWN7K3Y.cjs.map
2352
+ //# sourceMappingURL=chunk-RVG2BY32.cjs.map
2353
+ //# sourceMappingURL=chunk-RVG2BY32.cjs.map