@marcuspuchalla/nachos 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/dist/{chunk-PTWN7K3Y.cjs → chunk-3Z45RBZP.cjs} +469 -244
  3. package/dist/chunk-3Z45RBZP.cjs.map +1 -0
  4. package/dist/{chunk-2MTLSQ7E.js → chunk-EDXZTSIA.js} +224 -166
  5. package/dist/chunk-EDXZTSIA.js.map +1 -0
  6. package/dist/{chunk-R62CQQNI.cjs → chunk-HMUA5KLG.cjs} +239 -181
  7. package/dist/chunk-HMUA5KLG.cjs.map +1 -0
  8. package/dist/{chunk-ZDZ2B5PE.js → chunk-JESIF5IF.js} +7 -3
  9. package/dist/chunk-JESIF5IF.js.map +1 -0
  10. package/dist/{chunk-5A5T56JB.js → chunk-LWNWC2O7.js} +442 -217
  11. package/dist/chunk-LWNWC2O7.js.map +1 -0
  12. package/dist/{chunk-PD72MVTX.cjs → chunk-P6A2OOIY.cjs} +7 -3
  13. package/dist/chunk-P6A2OOIY.cjs.map +1 -0
  14. package/dist/encoder/index.cjs +14 -14
  15. package/dist/encoder/index.d.cts +5 -4
  16. package/dist/encoder/index.d.ts +5 -4
  17. package/dist/encoder/index.js +2 -2
  18. package/dist/index.cjs +58 -39
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +40 -21
  21. package/dist/index.d.ts +40 -21
  22. package/dist/index.js +37 -17
  23. package/dist/index.js.map +1 -1
  24. package/dist/metafile-cjs.json +1 -1
  25. package/dist/metafile-esm.json +1 -1
  26. package/dist/parser/index.cjs +21 -21
  27. package/dist/parser/index.d.cts +4 -2
  28. package/dist/parser/index.d.ts +4 -2
  29. package/dist/parser/index.js +2 -2
  30. package/dist/{types-DvNlfbKB.d.cts → types-eG2qalpr.d.cts} +27 -1
  31. package/dist/{types-DvNlfbKB.d.ts → types-eG2qalpr.d.ts} +27 -1
  32. package/dist/{useCborSimpleEncoder-TVxzNJ_9.d.ts → useCborSimpleEncoder-CamvS-_N.d.ts} +7 -3
  33. package/dist/{useCborSimpleEncoder-ButVU988.d.cts → useCborSimpleEncoder-DXgPx62d.d.cts} +7 -3
  34. package/dist/{useCborTag-xV2Pz2VY.d.ts → useCborTag-D4d7xG3-.d.cts} +9 -4
  35. package/dist/{useCborTag-Cs1CZuXZ.d.cts → useCborTag-TYst1KR6.d.ts} +9 -4
  36. package/package.json +1 -1
  37. package/src/__tests__/audit-fixes.test.ts +141 -0
  38. package/src/__tests__/public-api.test.ts +153 -0
  39. package/src/__tests__/roundtrip.test.ts +5 -6
  40. package/src/encoder/__tests__/cbor-collection-encoder.test.ts +103 -5
  41. package/src/encoder/__tests__/cbor-encoder-errors.test.ts +40 -5
  42. package/src/encoder/__tests__/cbor-simple-encoder.test.ts +126 -0
  43. package/src/encoder/composables/useCborCollectionEncoder.ts +30 -26
  44. package/src/encoder/composables/useCborEncoder.ts +40 -0
  45. package/src/encoder/composables/useCborSimpleEncoder.ts +40 -9
  46. package/src/encoder/types.ts +9 -4
  47. package/src/encoder/utils.ts +33 -1
  48. package/src/index.ts +39 -20
  49. package/src/parser/__tests__/buffer-native-parsing.test.ts +338 -0
  50. package/src/parser/__tests__/cbor-map-duplicate-keys.test.ts +97 -7
  51. package/src/parser/__tests__/cbor-security-dos-protection.test.ts +164 -31
  52. package/src/parser/__tests__/cbor-standard-tags.test.ts +75 -7
  53. package/src/parser/__tests__/cbor-tag-reparse-fix.test.ts +268 -0
  54. package/src/parser/__tests__/utils-errors.test.ts +11 -3
  55. package/src/parser/composables/useCborCollection.ts +51 -45
  56. package/src/parser/composables/useCborDiagnostic.ts +28 -0
  57. package/src/parser/composables/useCborFloat.ts +2 -1
  58. package/src/parser/composables/useCborInteger.ts +24 -10
  59. package/src/parser/composables/useCborParser.ts +448 -208
  60. package/src/parser/composables/useCborTag.ts +53 -38
  61. package/src/parser/types.ts +32 -1
  62. package/src/parser/utils.ts +52 -0
  63. package/dist/chunk-2MTLSQ7E.js.map +0 -1
  64. package/dist/chunk-5A5T56JB.js.map +0 -1
  65. package/dist/chunk-PD72MVTX.cjs.map +0 -1
  66. package/dist/chunk-PTWN7K3Y.cjs.map +0 -1
  67. package/dist/chunk-R62CQQNI.cjs.map +0 -1
  68. package/dist/chunk-ZDZ2B5PE.js.map +0 -1
@@ -1,9 +1,18 @@
1
1
  'use strict';
2
2
 
3
- var chunkPD72MVTX_cjs = require('./chunk-PD72MVTX.cjs');
3
+ var chunkP6A2OOIY_cjs = require('./chunk-P6A2OOIY.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)));
@@ -32,6 +41,9 @@ var readUint = (buffer, offset, length) => {
32
41
  for (let i = 0; i < length; i++) {
33
42
  result = result * 256 + readByte(buffer, offset + i);
34
43
  }
44
+ if (result > Number.MAX_SAFE_INTEGER) {
45
+ throw new Error(`Value at offset ${offset} (${length} bytes) exceeds MAX_SAFE_INTEGER; use readBigUint`);
46
+ }
35
47
  return result;
36
48
  };
37
49
  var readBigUint = (buffer, offset, length) => {
@@ -198,6 +210,23 @@ function compareBytes(a, b) {
198
210
  }
199
211
  return 0;
200
212
  }
213
+ function compareBytesLexicographic(a, b) {
214
+ if (!a || !b) {
215
+ throw new Error("compareBytesLexicographic: arguments cannot be null or undefined");
216
+ }
217
+ const min = Math.min(a.length, b.length);
218
+ for (let i = 0; i < min; i++) {
219
+ const byteA = a[i];
220
+ const byteB = b[i];
221
+ if (byteA !== byteB) {
222
+ return byteA - byteB;
223
+ }
224
+ }
225
+ return a.length - b.length;
226
+ }
227
+ function compareMapKeys(a, b, order = "length-first") {
228
+ return order === "bytewise" ? compareBytesLexicographic(a, b) : compareBytes(a, b);
229
+ }
201
230
  function serializeValueForComparison(value) {
202
231
  if (value === null) return "null";
203
232
  if (value === void 0) return "undefined";
@@ -235,9 +264,8 @@ function hasDuplicates(items) {
235
264
 
236
265
  // src/parser/composables/useCborInteger.ts
237
266
  function useCborInteger() {
238
- const parseInteger = (hexString, options) => {
239
- const buffer = hexToBytes(hexString);
240
- const initialByte = readByte(buffer, 0);
267
+ const parseIntegerFromBuffer = (buffer, offset, options) => {
268
+ const initialByte = readByte(buffer, offset);
241
269
  const { majorType, additionalInfo } = extractCborHeader(initialByte);
242
270
  let rawValue;
243
271
  let bytesRead;
@@ -245,16 +273,16 @@ function useCborInteger() {
245
273
  rawValue = additionalInfo;
246
274
  bytesRead = 1;
247
275
  } else if (additionalInfo === 24) {
248
- rawValue = readByte(buffer, 1);
276
+ rawValue = readByte(buffer, offset + 1);
249
277
  bytesRead = 2;
250
278
  } else if (additionalInfo === 25) {
251
- rawValue = readUint(buffer, 1, 2);
279
+ rawValue = readUint(buffer, offset + 1, 2);
252
280
  bytesRead = 3;
253
281
  } else if (additionalInfo === 26) {
254
- rawValue = readUint(buffer, 1, 4);
282
+ rawValue = readUint(buffer, offset + 1, 4);
255
283
  bytesRead = 5;
256
284
  } else if (additionalInfo === 27) {
257
- const bigValue = readBigUint(buffer, 1, 8);
285
+ const bigValue = readBigUint(buffer, offset + 1, 8);
258
286
  if (bigValue <= BigInt(Number.MAX_SAFE_INTEGER)) {
259
287
  rawValue = Number(bigValue);
260
288
  } else {
@@ -294,15 +322,20 @@ function useCborInteger() {
294
322
  bytesRead
295
323
  };
296
324
  };
325
+ const parseInteger = (hexString, options) => {
326
+ const buffer = hexToBytes(hexString);
327
+ return parseIntegerFromBuffer(buffer, 0, options);
328
+ };
297
329
  return {
298
- parseInteger
330
+ parseInteger,
331
+ parseIntegerFromBuffer
299
332
  };
300
333
  }
301
334
 
302
335
  // src/parser/composables/useCborString.ts
303
336
  function useCborString() {
304
- const { create: createByteString } = chunkPD72MVTX_cjs.useCborByteString();
305
- const { create: createTextString } = chunkPD72MVTX_cjs.useCborTextString();
337
+ const { create: createByteString } = chunkP6A2OOIY_cjs.useCborByteString();
338
+ const { create: createTextString } = chunkP6A2OOIY_cjs.useCborTextString();
306
339
  const parseLength = (buffer, offset, ai, options) => {
307
340
  if (ai < 24) {
308
341
  return { length: ai, bytesConsumed: 0 };
@@ -704,29 +737,34 @@ function useCborFloat() {
704
737
  return {
705
738
  parse,
706
739
  parseFloat,
707
- parseSimple
740
+ parseSimple,
741
+ parseFromBuffer
708
742
  };
709
743
  }
710
744
 
711
745
  // src/parser/composables/useCborTag.ts
712
746
  function useCborTag() {
713
- const { parseInteger } = useCborInteger();
747
+ const { parseIntegerFromBuffer } = useCborInteger();
714
748
  const { parseByteString, parseTextString } = useCborString();
715
- const { parseFloat, parseSimple } = useCborFloat();
749
+ const { parseFromBuffer: parseFloatOrSimpleFromBuffer } = useCborFloat();
750
+ let parseStartTime = 0;
716
751
  const parseItem = (buffer, offset, options, tagDepth = 0, collectionDepth = 0) => {
752
+ if (parseStartTime > 0 && options?.limits?.maxParseTime) {
753
+ const elapsed = Date.now() - parseStartTime;
754
+ if (elapsed > options.limits.maxParseTime) {
755
+ throw new Error(`Parse timeout: exceeded ${options.limits.maxParseTime}ms limit`);
756
+ }
757
+ }
717
758
  if (offset >= buffer.length) {
718
759
  throw new Error(`Unexpected end of buffer at offset ${offset}`);
719
760
  }
720
761
  const initialByte = readByte(buffer, offset);
721
- const { majorType, additionalInfo } = extractCborHeader(initialByte);
762
+ const { majorType } = extractCborHeader(initialByte);
722
763
  switch (majorType) {
723
764
  case 0:
724
765
  // 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
- }
766
+ case 1:
767
+ return parseIntegerFromBuffer(buffer, offset, options);
730
768
  case 2:
731
769
  return parseByteString(buffer, offset, options);
732
770
  case 3:
@@ -737,16 +775,8 @@ function useCborTag() {
737
775
  return parseMapInternal(buffer, offset, options, collectionDepth);
738
776
  case 6:
739
777
  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
- }
778
+ case 7:
779
+ return parseFloatOrSimpleFromBuffer(buffer, offset, options);
750
780
  default:
751
781
  throw new Error(`Unknown major type: ${majorType}`);
752
782
  }
@@ -761,7 +791,7 @@ function useCborTag() {
761
791
  if (additionalInfo === 31 && !isIndefiniteAllowed) {
762
792
  throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
763
793
  }
764
- const maxDepth = options?.limits?.maxDepth ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxDepth;
794
+ const maxDepth = options?.limits?.maxDepth ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxDepth;
765
795
  if (depth >= maxDepth) {
766
796
  throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
767
797
  }
@@ -818,7 +848,7 @@ function useCborTag() {
818
848
  if (!foundBreak) {
819
849
  throw new Error("Indefinite-length array missing break code (0xFF)");
820
850
  }
821
- items[chunkPD72MVTX_cjs.INDEFINITE_SYMBOL] = true;
851
+ items[chunkP6A2OOIY_cjs.INDEFINITE_SYMBOL] = true;
822
852
  } else {
823
853
  for (let i = 0; i < length; i++) {
824
854
  if (currentOffset >= buffer.length) {
@@ -844,7 +874,7 @@ function useCborTag() {
844
874
  if (additionalInfo === 31 && !isIndefiniteAllowed) {
845
875
  throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
846
876
  }
847
- const maxDepth = options?.limits?.maxDepth ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxDepth;
877
+ const maxDepth = options?.limits?.maxDepth ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxDepth;
848
878
  if (depth >= maxDepth) {
849
879
  throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
850
880
  }
@@ -903,7 +933,7 @@ function useCborTag() {
903
933
  if (!foundBreak) {
904
934
  throw new Error("Indefinite-length map missing break code (0xFF)");
905
935
  }
906
- map[chunkPD72MVTX_cjs.INDEFINITE_SYMBOL] = true;
936
+ map[chunkP6A2OOIY_cjs.INDEFINITE_SYMBOL] = true;
907
937
  } else {
908
938
  for (let i = 0; i < length; i++) {
909
939
  if (currentOffset >= buffer.length) {
@@ -1010,11 +1040,11 @@ function useCborTag() {
1010
1040
  if (value.length !== 2) {
1011
1041
  throw new Error(`Tag 4 (decimal fraction) array must have exactly 2 elements [exponent, mantissa], got ${value.length}`);
1012
1042
  }
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]}`);
1043
+ if (typeof value[0] !== "number" && typeof value[0] !== "bigint" || typeof value[0] === "number" && !Number.isInteger(value[0])) {
1044
+ throw new Error(`Tag 4 (decimal fraction) exponent must be an integer, got ${value[0]}`);
1015
1045
  }
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]}`);
1046
+ if (typeof value[1] !== "number" && typeof value[1] !== "bigint" || typeof value[1] === "number" && !Number.isInteger(value[1])) {
1047
+ throw new Error(`Tag 4 (decimal fraction) mantissa must be an integer, got ${value[1]}`);
1018
1048
  }
1019
1049
  break;
1020
1050
  case 5:
@@ -1025,11 +1055,11 @@ function useCborTag() {
1025
1055
  if (value.length !== 2) {
1026
1056
  throw new Error(`Tag 5 (bigfloat) array must have exactly 2 elements [exponent, mantissa], got ${value.length}`);
1027
1057
  }
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]}`);
1058
+ if (typeof value[0] !== "number" && typeof value[0] !== "bigint" || typeof value[0] === "number" && !Number.isInteger(value[0])) {
1059
+ throw new Error(`Tag 5 (bigfloat) exponent must be an integer, got ${value[0]}`);
1030
1060
  }
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]}`);
1061
+ if (typeof value[1] !== "number" && typeof value[1] !== "bigint" || typeof value[1] === "number" && !Number.isInteger(value[1])) {
1062
+ throw new Error(`Tag 5 (bigfloat) mantissa must be an integer, got ${value[1]}`);
1033
1063
  }
1034
1064
  break;
1035
1065
  case 32:
@@ -1189,11 +1219,14 @@ function useCborTag() {
1189
1219
  if (majorType !== 6) {
1190
1220
  throw new Error(`Expected major type 6 (tag), got ${majorType}`);
1191
1221
  }
1192
- const maxTagDepth = options?.limits?.maxTagDepth ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxTagDepth;
1222
+ const maxTagDepth = options?.limits?.maxTagDepth ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxTagDepth;
1193
1223
  if (tagDepth >= maxTagDepth) {
1194
1224
  throw new Error(`Tag nesting depth ${tagDepth} exceeds limit of ${maxTagDepth}`);
1195
1225
  }
1196
1226
  const { tagNumber, bytesConsumed } = parseTagNumber(buffer, offset + 1, additionalInfo);
1227
+ if (options?.validateCanonical) {
1228
+ validateCanonicalInteger(tagNumber, additionalInfo);
1229
+ }
1197
1230
  let currentOffset = offset + 1 + bytesConsumed;
1198
1231
  if (currentOffset >= buffer.length) {
1199
1232
  throw new Error(`Unexpected end of buffer after tag ${tagNumber}`);
@@ -1233,12 +1266,25 @@ function useCborTag() {
1233
1266
  const parseTag = (hexString, options) => {
1234
1267
  const cleanHex = hexString.replace(/\s+/g, "");
1235
1268
  const buffer = hexToBytes(cleanHex);
1236
- return parseTagFromBuffer(buffer, 0, options);
1269
+ const isTopLevel = parseStartTime === 0;
1270
+ if (isTopLevel && options?.limits?.maxParseTime) {
1271
+ parseStartTime = Date.now();
1272
+ }
1273
+ try {
1274
+ return parseTagFromBuffer(buffer, 0, options);
1275
+ } finally {
1276
+ if (isTopLevel) {
1277
+ parseStartTime = 0;
1278
+ }
1279
+ }
1237
1280
  };
1238
1281
  const parse = parseTag;
1239
1282
  return {
1240
1283
  parseTag,
1241
- parse
1284
+ parse,
1285
+ parseTagFromBuffer,
1286
+ validateTagSemantics,
1287
+ decodePlutusConstructor
1242
1288
  };
1243
1289
  }
1244
1290
 
@@ -1297,17 +1343,18 @@ var logger = {
1297
1343
 
1298
1344
  // src/parser/composables/useCborCollection.ts
1299
1345
  function useCborCollection() {
1300
- const { parseInteger } = useCborInteger();
1346
+ const { parseIntegerFromBuffer } = useCborInteger();
1301
1347
  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
- };
1348
+ const { parseFromBuffer: parseFloatOrSimpleFromBuffer } = useCborFloat();
1349
+ const { parseTagFromBuffer } = useCborTag();
1350
+ let parseStartTime = 0;
1310
1351
  const parseItem = (buffer, offset, options, depth = 0) => {
1352
+ if (parseStartTime > 0 && options?.limits?.maxParseTime) {
1353
+ const elapsed = Date.now() - parseStartTime;
1354
+ if (elapsed > options.limits.maxParseTime) {
1355
+ throw new Error(`Parse timeout: exceeded ${options.limits.maxParseTime}ms limit`);
1356
+ }
1357
+ }
1311
1358
  if (offset >= buffer.length) {
1312
1359
  throw new Error(`Unexpected end of buffer at offset ${offset}`);
1313
1360
  }
@@ -1316,11 +1363,8 @@ function useCborCollection() {
1316
1363
  switch (majorType) {
1317
1364
  case 0:
1318
1365
  // 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
- }
1366
+ case 1:
1367
+ return parseIntegerFromBuffer(buffer, offset, options);
1324
1368
  case 2:
1325
1369
  return parseByteString(buffer, offset, options);
1326
1370
  case 3:
@@ -1329,16 +1373,10 @@ function useCborCollection() {
1329
1373
  return parseArrayFromBuffer(buffer, offset, options, depth);
1330
1374
  case 5:
1331
1375
  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
- }
1376
+ case 6:
1377
+ return parseTagFromBuffer(buffer, offset, options);
1378
+ case 7:
1379
+ return parseFloatOrSimpleFromBuffer(buffer, offset, options);
1342
1380
  default:
1343
1381
  throw new Error(`Unknown major type: ${majorType}`);
1344
1382
  }
@@ -1417,7 +1455,7 @@ function useCborCollection() {
1417
1455
  if (!foundBreak) {
1418
1456
  throw new Error("Indefinite-length array missing break code (0xFF)");
1419
1457
  }
1420
- items[chunkPD72MVTX_cjs.INDEFINITE_SYMBOL] = true;
1458
+ items[chunkP6A2OOIY_cjs.INDEFINITE_SYMBOL] = true;
1421
1459
  } else {
1422
1460
  if (options?.limits?.maxArrayLength && length > options.limits.maxArrayLength) {
1423
1461
  throw new Error(`Array length ${length} exceeds limit of ${options.limits.maxArrayLength}`);
@@ -1480,7 +1518,7 @@ function useCborCollection() {
1480
1518
  }
1481
1519
  const valueResult = parseItem(buffer, currentOffset, options, depth + 1);
1482
1520
  currentOffset += valueResult.bytesRead;
1483
- const keyString = convertKeyToString(keyResult.value);
1521
+ const keyString = serializeValueForComparison(keyResult.value);
1484
1522
  if (seenKeys.has(keyString)) {
1485
1523
  const mode = options?.dupMapKeyMode || "allow";
1486
1524
  if (mode === "reject") {
@@ -1497,7 +1535,7 @@ function useCborCollection() {
1497
1535
  if (!foundBreak) {
1498
1536
  throw new Error("Indefinite-length map missing break code (0xFF)");
1499
1537
  }
1500
- map[chunkPD72MVTX_cjs.INDEFINITE_SYMBOL] = true;
1538
+ map[chunkP6A2OOIY_cjs.INDEFINITE_SYMBOL] = true;
1501
1539
  } else {
1502
1540
  if (options?.limits?.maxMapSize && length > options.limits.maxMapSize) {
1503
1541
  throw new Error(`Map size ${length} exceeds limit of ${options.limits.maxMapSize}`);
@@ -1518,7 +1556,7 @@ function useCborCollection() {
1518
1556
  }
1519
1557
  const valueResult = parseItem(buffer, currentOffset, options, depth + 1);
1520
1558
  currentOffset += valueResult.bytesRead;
1521
- const keyString = convertKeyToString(keyResult.value);
1559
+ const keyString = serializeValueForComparison(keyResult.value);
1522
1560
  if (seenKeys.has(keyString)) {
1523
1561
  const mode = options?.dupMapKeyMode || "allow";
1524
1562
  if (mode === "reject") {
@@ -1532,16 +1570,17 @@ function useCborCollection() {
1532
1570
  allEntries.push([keyResult.value, valueResult.value]);
1533
1571
  }
1534
1572
  }
1535
- map[chunkPD72MVTX_cjs.ALL_ENTRIES_SYMBOL] = allEntries;
1573
+ map[chunkP6A2OOIY_cjs.ALL_ENTRIES_SYMBOL] = allEntries;
1536
1574
  if (options?.validateCanonical && keyBytes.length > 1) {
1575
+ const keyOrder = options?.mapKeyOrder ?? "length-first";
1537
1576
  for (let i = 1; i < keyBytes.length; i++) {
1538
1577
  const prevKey = keyBytes[i - 1];
1539
1578
  const currKey = keyBytes[i];
1540
1579
  if (prevKey && currKey) {
1541
- const cmp = compareBytes(prevKey, currKey);
1580
+ const cmp = compareMapKeys(prevKey, currKey, keyOrder);
1542
1581
  if (cmp > 0) {
1543
1582
  throw new Error(
1544
- `Map keys are not in canonical order: key at index ${i} should come before key at index ${i - 1}`
1583
+ `Map keys are not in canonical order (${keyOrder}): key at index ${i} should come before key at index ${i - 1}`
1545
1584
  );
1546
1585
  }
1547
1586
  if (cmp === 0) {
@@ -1558,12 +1597,26 @@ function useCborCollection() {
1558
1597
  const parseArray = (hexString, options) => {
1559
1598
  const cleanHex = hexString.replace(/\s+/g, "");
1560
1599
  const buffer = hexToBytes(cleanHex);
1561
- return parseArrayFromBuffer(buffer, 0, options, 0);
1600
+ if (options?.limits?.maxParseTime) {
1601
+ parseStartTime = Date.now();
1602
+ }
1603
+ try {
1604
+ return parseArrayFromBuffer(buffer, 0, options, 0);
1605
+ } finally {
1606
+ parseStartTime = 0;
1607
+ }
1562
1608
  };
1563
1609
  const parseMap = (hexString, options) => {
1564
1610
  const cleanHex = hexString.replace(/\s+/g, "");
1565
1611
  const buffer = hexToBytes(cleanHex);
1566
- return parseMapFromBuffer(buffer, 0, options, 0);
1612
+ if (options?.limits?.maxParseTime) {
1613
+ parseStartTime = Date.now();
1614
+ }
1615
+ try {
1616
+ return parseMapFromBuffer(buffer, 0, options, 0);
1617
+ } finally {
1618
+ parseStartTime = 0;
1619
+ }
1567
1620
  };
1568
1621
  return {
1569
1622
  parseArray,
@@ -1574,29 +1627,32 @@ function useCborCollection() {
1574
1627
  // src/parser/composables/useCborParser.ts
1575
1628
  function useCborParser() {
1576
1629
  const mergeOptions = (options) => {
1577
- if (!options) return chunkPD72MVTX_cjs.DEFAULT_OPTIONS;
1630
+ if (!options) return chunkP6A2OOIY_cjs.DEFAULT_OPTIONS;
1578
1631
  const isCanonical = options.validateCanonical ?? (options.strict ? true : false);
1579
1632
  return {
1580
- strict: options.strict ?? chunkPD72MVTX_cjs.DEFAULT_OPTIONS.strict,
1633
+ strict: options.strict ?? chunkP6A2OOIY_cjs.DEFAULT_OPTIONS.strict,
1581
1634
  validateCanonical: isCanonical,
1582
1635
  // RFC 8949 Section 4.2: Deterministic encoding MUST NOT use indefinite-length
1583
- allowIndefinite: options.allowIndefinite ?? (isCanonical || options.strict ? false : chunkPD72MVTX_cjs.DEFAULT_OPTIONS.allowIndefinite),
1636
+ allowIndefinite: options.allowIndefinite ?? (isCanonical || options.strict ? false : chunkP6A2OOIY_cjs.DEFAULT_OPTIONS.allowIndefinite),
1584
1637
  // Auto-enable duplicate key rejection for canonical or strict mode
1585
- dupMapKeyMode: options.dupMapKeyMode ?? (isCanonical || options.strict ? "reject" : chunkPD72MVTX_cjs.DEFAULT_OPTIONS.dupMapKeyMode),
1586
- validateUtf8Strict: options.validateUtf8Strict ?? (options.strict ? true : chunkPD72MVTX_cjs.DEFAULT_OPTIONS.validateUtf8Strict),
1587
- validateSetUniqueness: options.validateSetUniqueness ?? (options.strict ? true : chunkPD72MVTX_cjs.DEFAULT_OPTIONS.validateSetUniqueness),
1588
- validateTagSemantics: options.validateTagSemantics ?? (options.strict ? true : chunkPD72MVTX_cjs.DEFAULT_OPTIONS.validateTagSemantics),
1589
- validatePlutusSemantics: options.validatePlutusSemantics ?? (options.strict ? true : chunkPD72MVTX_cjs.DEFAULT_OPTIONS.validatePlutusSemantics),
1638
+ dupMapKeyMode: options.dupMapKeyMode ?? (isCanonical || options.strict ? "reject" : chunkP6A2OOIY_cjs.DEFAULT_OPTIONS.dupMapKeyMode),
1639
+ validateUtf8Strict: options.validateUtf8Strict ?? (options.strict ? true : chunkP6A2OOIY_cjs.DEFAULT_OPTIONS.validateUtf8Strict),
1640
+ validateSetUniqueness: options.validateSetUniqueness ?? (options.strict ? true : chunkP6A2OOIY_cjs.DEFAULT_OPTIONS.validateSetUniqueness),
1641
+ validateTagSemantics: options.validateTagSemantics ?? (options.strict ? true : chunkP6A2OOIY_cjs.DEFAULT_OPTIONS.validateTagSemantics),
1642
+ validatePlutusSemantics: options.validatePlutusSemantics ?? (options.strict ? true : chunkP6A2OOIY_cjs.DEFAULT_OPTIONS.validatePlutusSemantics),
1643
+ mapKeyOrder: options.mapKeyOrder ?? chunkP6A2OOIY_cjs.DEFAULT_OPTIONS.mapKeyOrder,
1644
+ // Strict mode rejects trailing data after the top-level item (well-formedness).
1645
+ allowTrailingData: options.allowTrailingData ?? (options.strict ? false : chunkP6A2OOIY_cjs.DEFAULT_OPTIONS.allowTrailingData),
1590
1646
  limits: {
1591
- maxInputSize: options.limits?.maxInputSize ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxInputSize,
1592
- maxOutputSize: options.limits?.maxOutputSize ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxOutputSize,
1593
- maxStringLength: options.limits?.maxStringLength ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxStringLength,
1594
- maxArrayLength: options.limits?.maxArrayLength ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxArrayLength,
1595
- maxMapSize: options.limits?.maxMapSize ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxMapSize,
1596
- maxDepth: options.limits?.maxDepth ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxDepth,
1597
- maxTagDepth: options.limits?.maxTagDepth ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxTagDepth,
1598
- maxBignumBytes: options.limits?.maxBignumBytes ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxBignumBytes,
1599
- maxParseTime: options.limits?.maxParseTime ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxParseTime
1647
+ maxInputSize: options.limits?.maxInputSize ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxInputSize,
1648
+ maxOutputSize: options.limits?.maxOutputSize ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxOutputSize,
1649
+ maxStringLength: options.limits?.maxStringLength ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxStringLength,
1650
+ maxArrayLength: options.limits?.maxArrayLength ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxArrayLength,
1651
+ maxMapSize: options.limits?.maxMapSize ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxMapSize,
1652
+ maxDepth: options.limits?.maxDepth ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxDepth,
1653
+ maxTagDepth: options.limits?.maxTagDepth ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxTagDepth,
1654
+ maxBignumBytes: options.limits?.maxBignumBytes ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxBignumBytes,
1655
+ maxParseTime: options.limits?.maxParseTime ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxParseTime
1600
1656
  }
1601
1657
  };
1602
1658
  };
@@ -1607,13 +1663,25 @@ function useCborParser() {
1607
1663
  throw new Error(`Parse timeout: exceeded ${ctx.options.limits.maxParseTime}ms limit (elapsed: ${elapsed}ms)`);
1608
1664
  }
1609
1665
  };
1610
- const { parseInteger } = useCborInteger();
1611
- const { parseString } = useCborString();
1666
+ const { parseInteger, parseIntegerFromBuffer: integerFromBuffer } = useCborInteger();
1667
+ const { parseString, parseByteString: byteStringFromBuffer, parseTextString: textStringFromBuffer } = useCborString();
1612
1668
  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, "");
1669
+ const { parseTag, validateTagSemantics, decodePlutusConstructor } = useCborTag();
1670
+ const { parse: parseFloatOrSimple, parseFromBuffer: floatOrSimpleFromBuffer } = useCborFloat();
1671
+ const parse = (input, options) => {
1672
+ const mergedOptions = mergeOptions(options);
1673
+ if (input instanceof Uint8Array) {
1674
+ if (input.length === 0) {
1675
+ throw new Error("Empty input");
1676
+ }
1677
+ if (mergedOptions.limits?.maxInputSize && input.length > mergedOptions.limits.maxInputSize) {
1678
+ throw new Error(`Input size ${input.length} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
1679
+ }
1680
+ const bufResult = dispatchFromBuffer(input, 0, mergedOptions);
1681
+ checkTrailingData(bufResult.bytesRead, input.length, mergedOptions);
1682
+ return bufResult;
1683
+ }
1684
+ const cleanHex = input.replace(/\s+/g, "");
1617
1685
  if (!cleanHex || cleanHex.length === 0) {
1618
1686
  throw new Error("Empty hex string");
1619
1687
  }
@@ -1623,7 +1691,6 @@ function useCborParser() {
1623
1691
  if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
1624
1692
  throw new Error(`Invalid hex character in: ${cleanHex}`);
1625
1693
  }
1626
- const mergedOptions = mergeOptions(options);
1627
1694
  const inputSize = cleanHex.length / 2;
1628
1695
  if (mergedOptions.limits?.maxInputSize && inputSize > mergedOptions.limits.maxInputSize) {
1629
1696
  throw new Error(`Input size ${inputSize} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
@@ -1631,47 +1698,77 @@ function useCborParser() {
1631
1698
  const buffer = hexToBytes(cleanHex);
1632
1699
  const initialByte = readByte(buffer, 0);
1633
1700
  const { majorType } = extractCborHeader(initialByte);
1701
+ let result;
1634
1702
  switch (majorType) {
1635
1703
  case 0:
1636
1704
  // Unsigned integer
1637
1705
  case 1:
1638
- return parseInteger(cleanHex, mergedOptions);
1706
+ result = parseInteger(cleanHex, mergedOptions);
1707
+ break;
1639
1708
  case 2:
1640
1709
  // Byte string
1641
1710
  case 3:
1642
- return parseString(cleanHex, mergedOptions);
1711
+ result = parseString(cleanHex, mergedOptions);
1712
+ break;
1643
1713
  case 4:
1644
- return parseArray(cleanHex, mergedOptions);
1714
+ result = parseArray(cleanHex, mergedOptions);
1715
+ break;
1645
1716
  case 5:
1646
- return parseMap(cleanHex, mergedOptions);
1717
+ result = parseMap(cleanHex, mergedOptions);
1718
+ break;
1647
1719
  case 6:
1648
- return parseTag(cleanHex, mergedOptions);
1720
+ result = parseTag(cleanHex, mergedOptions);
1721
+ break;
1649
1722
  case 7:
1650
- return parseFloatOrSimple(cleanHex, mergedOptions);
1723
+ result = parseFloatOrSimple(cleanHex, mergedOptions);
1724
+ break;
1651
1725
  default:
1652
1726
  throw new Error(`Unknown major type: ${majorType}`);
1653
1727
  }
1728
+ checkTrailingData(result.bytesRead, buffer.length, mergedOptions);
1729
+ return result;
1654
1730
  };
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}`);
1731
+ const checkTrailingData = (bytesRead, totalLength, opts) => {
1732
+ if (!opts.allowTrailingData && bytesRead < totalLength) {
1733
+ throw new Error(
1734
+ `Trailing data: ${totalLength - bytesRead} byte(s) remain after the top-level CBOR item (bytesRead=${bytesRead}, length=${totalLength}). Use parseSequence to decode multiple items.`
1735
+ );
1665
1736
  }
1737
+ };
1738
+ const parseWithSourceMap = (input, options) => {
1666
1739
  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`);
1740
+ let buffer;
1741
+ if (input instanceof Uint8Array) {
1742
+ if (input.length === 0) {
1743
+ throw new Error("Empty input");
1744
+ }
1745
+ if (mergedOptions.limits?.maxInputSize && input.length > mergedOptions.limits.maxInputSize) {
1746
+ throw new Error(`Input size ${input.length} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
1747
+ }
1748
+ buffer = input;
1749
+ } else {
1750
+ const cleanHex = input.replace(/\s+/g, "");
1751
+ if (!cleanHex || cleanHex.length === 0) {
1752
+ throw new Error("Empty hex string");
1753
+ }
1754
+ if (cleanHex.length % 2 !== 0) {
1755
+ throw new Error("Hex string must have even length");
1756
+ }
1757
+ if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
1758
+ throw new Error(`Invalid hex character in: ${cleanHex}`);
1759
+ }
1760
+ const inputSize = cleanHex.length / 2;
1761
+ if (mergedOptions.limits?.maxInputSize && inputSize > mergedOptions.limits.maxInputSize) {
1762
+ throw new Error(`Input size ${inputSize} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
1763
+ }
1764
+ buffer = hexToBytes(cleanHex);
1670
1765
  }
1671
- const buffer = hexToBytes(cleanHex);
1672
1766
  const sourceMap = [];
1673
1767
  const ctx = {
1674
1768
  buffer,
1769
+ offset: 0,
1770
+ sourceMap,
1771
+ currentDepth: 0,
1675
1772
  startTime: Date.now(),
1676
1773
  bytesAllocated: 0,
1677
1774
  options: mergedOptions
@@ -1814,19 +1911,57 @@ function useCborParser() {
1814
1911
  }
1815
1912
  return result;
1816
1913
  };
1914
+ const dispatchFromBuffer = (buffer, offset, options) => {
1915
+ const initialByte = readByte(buffer, offset);
1916
+ const { majorType } = extractCborHeader(initialByte);
1917
+ switch (majorType) {
1918
+ case 0:
1919
+ // Unsigned integer
1920
+ case 1:
1921
+ return integerFromBuffer(buffer, offset, options);
1922
+ case 2:
1923
+ return byteStringFromBuffer(buffer, offset, options);
1924
+ case 3:
1925
+ return textStringFromBuffer(buffer, offset, options);
1926
+ case 4: {
1927
+ const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1928
+ return parseArray(hexString, options);
1929
+ }
1930
+ case 5: {
1931
+ const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1932
+ return parseMap(hexString, options);
1933
+ }
1934
+ case 6: {
1935
+ const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1936
+ return parseTag(hexString, options);
1937
+ }
1938
+ case 7:
1939
+ return floatOrSimpleFromBuffer(buffer, offset, options);
1940
+ default:
1941
+ throw new Error(`Unknown major type: ${majorType}`);
1942
+ }
1943
+ };
1817
1944
  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);
1945
+ return integerFromBuffer(buffer, offset, options);
1820
1946
  };
1821
1947
  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);
1948
+ const initialByte = readByte(buffer, offset);
1949
+ const { majorType } = extractCborHeader(initialByte);
1950
+ if (majorType === 2) {
1951
+ return byteStringFromBuffer(buffer, offset, options);
1952
+ }
1953
+ return textStringFromBuffer(buffer, offset, options);
1824
1954
  };
1825
1955
  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);
1956
+ return floatOrSimpleFromBuffer(buffer, offset, options);
1828
1957
  };
1829
1958
  const parseArrayWithMap = (ctx, offset, path, sourceMap) => {
1959
+ const previousDepth = ctx.currentDepth ?? 0;
1960
+ const maxDepth = ctx.options?.limits?.maxDepth;
1961
+ if (maxDepth !== void 0 && previousDepth >= maxDepth) {
1962
+ throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
1963
+ }
1964
+ ctx.currentDepth = previousDepth + 1;
1830
1965
  const startOffset = offset;
1831
1966
  const initialByte = readByte(ctx.buffer, offset);
1832
1967
  const { additionalInfo } = extractCborHeader(initialByte);
@@ -1853,6 +1988,10 @@ function useCborParser() {
1853
1988
  length = Number(bigLength);
1854
1989
  currentOffset += 8;
1855
1990
  } else if (additionalInfo === 31) {
1991
+ const isIndefiniteAllowed = ctx.options?.allowIndefinite ?? !(ctx.options?.validateCanonical || ctx.options?.strict);
1992
+ if (!isIndefiniteAllowed) {
1993
+ throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
1994
+ }
1856
1995
  isIndefinite = true;
1857
1996
  length = 0;
1858
1997
  } else {
@@ -1869,49 +2008,70 @@ function useCborParser() {
1869
2008
  isHeader: true,
1870
2009
  headerEnd
1871
2010
  });
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;
2011
+ try {
2012
+ const childPaths = [];
2013
+ if (isIndefinite) {
2014
+ let index = 0;
2015
+ let foundBreak = false;
2016
+ while (currentOffset < ctx.buffer.length) {
2017
+ const nextByte = readByte(ctx.buffer, currentOffset);
2018
+ if (nextByte === 255) {
2019
+ currentOffset++;
2020
+ foundBreak = true;
2021
+ break;
2022
+ }
2023
+ if (ctx.options?.limits?.maxArrayLength && index >= ctx.options.limits.maxArrayLength) {
2024
+ throw new Error(`Array length exceeds limit of ${ctx.options.limits.maxArrayLength}`);
2025
+ }
2026
+ const elementPath = `${path}[${index}]`;
2027
+ childPaths.push(elementPath);
2028
+ const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
2029
+ items.push(elementResult.value);
2030
+ currentOffset += elementResult.bytesRead;
2031
+ const elementEntry = sourceMap.find((e) => e.path === elementPath);
2032
+ if (elementEntry) {
2033
+ elementEntry.parent = path;
2034
+ }
2035
+ index++;
1880
2036
  }
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;
2037
+ if (!foundBreak) {
2038
+ throw new Error("Indefinite-length array missing break code (0xFF)");
1889
2039
  }
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;
2040
+ } else {
2041
+ if (ctx.options?.limits?.maxArrayLength && length > ctx.options.limits.maxArrayLength) {
2042
+ throw new Error(`Array length ${length} exceeds limit of ${ctx.options.limits.maxArrayLength}`);
2043
+ }
2044
+ for (let i = 0; i < length; i++) {
2045
+ const elementPath = `${path}[${i}]`;
2046
+ childPaths.push(elementPath);
2047
+ const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
2048
+ items.push(elementResult.value);
2049
+ currentOffset += elementResult.bytesRead;
2050
+ const elementEntry = sourceMap.find((e) => e.path === elementPath);
2051
+ if (elementEntry) {
2052
+ elementEntry.parent = path;
2053
+ }
1902
2054
  }
1903
2055
  }
2056
+ const bytesRead = currentOffset - offset;
2057
+ if (childPaths.length > 0 && sourceMap[arrayEntryIndex]) {
2058
+ sourceMap[arrayEntryIndex].children = childPaths;
2059
+ }
2060
+ return {
2061
+ value: items,
2062
+ bytesRead
2063
+ };
2064
+ } finally {
2065
+ ctx.currentDepth = previousDepth;
1904
2066
  }
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
2067
  };
1914
2068
  const parseMapWithMap = (ctx, offset, path, sourceMap) => {
2069
+ const previousDepth = ctx.currentDepth ?? 0;
2070
+ const maxDepth = ctx.options?.limits?.maxDepth;
2071
+ if (maxDepth !== void 0 && previousDepth >= maxDepth) {
2072
+ throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
2073
+ }
2074
+ ctx.currentDepth = previousDepth + 1;
1915
2075
  const startOffset = offset;
1916
2076
  const initialByte = readByte(ctx.buffer, offset);
1917
2077
  const { additionalInfo } = extractCborHeader(initialByte);
@@ -1938,6 +2098,10 @@ function useCborParser() {
1938
2098
  length = Number(bigLength);
1939
2099
  currentOffset += 8;
1940
2100
  } else if (additionalInfo === 31) {
2101
+ const isIndefiniteAllowed = ctx.options?.allowIndefinite ?? !(ctx.options?.validateCanonical || ctx.options?.strict);
2102
+ if (!isIndefiniteAllowed) {
2103
+ throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
2104
+ }
1941
2105
  isIndefinite = true;
1942
2106
  length = 0;
1943
2107
  } else {
@@ -1954,72 +2118,91 @@ function useCborParser() {
1954
2118
  isHeader: true,
1955
2119
  headerEnd
1956
2120
  });
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}`);
2121
+ try {
2122
+ const childPaths = [];
2123
+ const seenKeys = /* @__PURE__ */ new Set();
2124
+ if (isIndefinite) {
2125
+ let count = 0;
2126
+ let foundBreak = false;
2127
+ while (currentOffset < ctx.buffer.length) {
2128
+ const nextByte = readByte(ctx.buffer, currentOffset);
2129
+ if (nextByte === 255) {
2130
+ currentOffset++;
2131
+ foundBreak = true;
2132
+ break;
2133
+ }
2134
+ if (ctx.options?.limits?.maxMapSize && count >= ctx.options.limits.maxMapSize) {
2135
+ throw new Error(`Map size exceeds limit of ${ctx.options.limits.maxMapSize}`);
2136
+ }
2137
+ const keyPath = `${path}${path ? "." : ""}#key`;
2138
+ const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
2139
+ currentOffset += keyResult.bytesRead;
2140
+ const keyForDupCheck = serializeValueForComparison(keyResult.value);
2141
+ const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
2142
+ if (seenKeys.has(keyForDupCheck)) {
2143
+ const mode = ctx.options?.dupMapKeyMode || "allow";
2144
+ if (mode === "reject") {
2145
+ throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2146
+ } else if (mode === "warn") {
2147
+ logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2148
+ }
1976
2149
  }
2150
+ seenKeys.add(keyForDupCheck);
2151
+ const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
2152
+ childPaths.push(valuePath);
2153
+ const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
2154
+ map.set(keyResult.value, valueResult.value);
2155
+ currentOffset += valueResult.bytesRead;
2156
+ const valueEntry = sourceMap.find((e) => e.path === valuePath);
2157
+ if (valueEntry) {
2158
+ valueEntry.parent = path;
2159
+ }
2160
+ count++;
1977
2161
  }
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;
2162
+ if (!foundBreak) {
2163
+ throw new Error("Indefinite-length map missing break code (0xFF)");
1987
2164
  }
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}`);
2165
+ } else {
2166
+ if (ctx.options?.limits?.maxMapSize && length > ctx.options.limits.maxMapSize) {
2167
+ throw new Error(`Map size ${length} exceeds limit of ${ctx.options.limits.maxMapSize}`);
2168
+ }
2169
+ for (let i = 0; i < length; i++) {
2170
+ const keyPath = `${path}${path ? "." : ""}#key${i}`;
2171
+ const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
2172
+ currentOffset += keyResult.bytesRead;
2173
+ const keyForDupCheck = serializeValueForComparison(keyResult.value);
2174
+ const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
2175
+ if (seenKeys.has(keyForDupCheck)) {
2176
+ const mode = ctx.options?.dupMapKeyMode || "allow";
2177
+ if (mode === "reject") {
2178
+ throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2179
+ } else if (mode === "warn") {
2180
+ logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2181
+ }
2182
+ }
2183
+ seenKeys.add(keyForDupCheck);
2184
+ const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
2185
+ childPaths.push(valuePath);
2186
+ const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
2187
+ map.set(keyResult.value, valueResult.value);
2188
+ currentOffset += valueResult.bytesRead;
2189
+ const valueEntry = sourceMap.find((e) => e.path === valuePath);
2190
+ if (valueEntry) {
2191
+ valueEntry.parent = path;
2001
2192
  }
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
2193
  }
2013
2194
  }
2195
+ const bytesRead = currentOffset - offset;
2196
+ if (sourceMap[mapEntryIndex]) {
2197
+ sourceMap[mapEntryIndex].children = childPaths;
2198
+ }
2199
+ return {
2200
+ value: map,
2201
+ bytesRead
2202
+ };
2203
+ } finally {
2204
+ ctx.currentDepth = previousDepth;
2014
2205
  }
2015
- const bytesRead = currentOffset - offset;
2016
- if (sourceMap[mapEntryIndex]) {
2017
- sourceMap[mapEntryIndex].children = childPaths;
2018
- }
2019
- return {
2020
- value: map,
2021
- bytesRead
2022
- };
2023
2206
  };
2024
2207
  const parseTagNumberHelper = (buffer, offset, ai) => {
2025
2208
  if (ai < 24) {
@@ -2047,6 +2230,12 @@ function useCborParser() {
2047
2230
  }
2048
2231
  };
2049
2232
  const parseTagWithMap = (ctx, offset, path, sourceMap) => {
2233
+ const previousTagDepth = ctx.currentTagDepth ?? 0;
2234
+ const maxTagDepth = ctx.options?.limits?.maxTagDepth ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxTagDepth;
2235
+ if (previousTagDepth >= maxTagDepth) {
2236
+ throw new Error(`Tag nesting depth ${previousTagDepth} exceeds limit of ${maxTagDepth}`);
2237
+ }
2238
+ ctx.currentTagDepth = previousTagDepth + 1;
2050
2239
  const startOffset = offset;
2051
2240
  const initialByte = readByte(ctx.buffer, offset);
2052
2241
  const { additionalInfo } = extractCborHeader(initialByte);
@@ -2055,6 +2244,9 @@ function useCborParser() {
2055
2244
  offset + 1,
2056
2245
  additionalInfo
2057
2246
  );
2247
+ if (ctx.options?.validateCanonical) {
2248
+ validateCanonicalInteger(tagNumber, additionalInfo);
2249
+ }
2058
2250
  let currentOffset = offset + 1 + bytesConsumed;
2059
2251
  const headerEnd = currentOffset;
2060
2252
  const tagEntryIndex = sourceMap.length;
@@ -2078,10 +2270,30 @@ function useCborParser() {
2078
2270
  if (valueEntry) {
2079
2271
  valueEntry.parent = path;
2080
2272
  }
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);
2273
+ let finalValue = valueResult.value;
2274
+ if ((tagNumber === 2 || tagNumber === 3) && finalValue instanceof Uint8Array) {
2275
+ const maxBignumBytes = ctx.options?.limits?.maxBignumBytes ?? chunkP6A2OOIY_cjs.DEFAULT_LIMITS.maxBignumBytes;
2276
+ if (finalValue.length > maxBignumBytes) {
2277
+ throw new Error(
2278
+ `Bignum (tag ${tagNumber}) size ${finalValue.length} bytes exceeds limit of ${maxBignumBytes} bytes`
2279
+ );
2280
+ }
2281
+ let bigintValue = 0n;
2282
+ for (let i = 0; i < finalValue.length; i++) {
2283
+ bigintValue = bigintValue << 8n | BigInt(finalValue[i]);
2284
+ }
2285
+ finalValue = tagNumber === 2 ? bigintValue : -1n - bigintValue;
2286
+ }
2287
+ validateTagSemantics(tagNumber, finalValue, ctx.options);
2288
+ const plutusConstr = decodePlutusConstructor(tagNumber, finalValue);
2289
+ const taggedValue = {
2290
+ tag: tagNumber,
2291
+ value: finalValue,
2292
+ ...plutusConstr && { plutus: plutusConstr }
2293
+ };
2294
+ ctx.currentTagDepth = previousTagDepth;
2083
2295
  return {
2084
- value: tagResult.value,
2296
+ value: taggedValue,
2085
2297
  bytesRead: currentOffset - startOffset
2086
2298
  };
2087
2299
  };
@@ -2096,28 +2308,42 @@ function useCborParser() {
2096
2308
  if (ai < 20) return `Simple Value ${ai}`;
2097
2309
  return "Simple Value";
2098
2310
  };
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
- }
2311
+ const parseSequence = (input, options) => {
2110
2312
  const mergedOptions = mergeOptions(options);
2111
- const buffer = hexToBytes(cleanHex);
2313
+ let buffer;
2314
+ if (input instanceof Uint8Array) {
2315
+ if (input.length === 0) {
2316
+ return [];
2317
+ }
2318
+ buffer = input;
2319
+ } else {
2320
+ const cleanHex = input.replace(/\s+/g, "");
2321
+ if (!cleanHex || cleanHex.length === 0) {
2322
+ return [];
2323
+ }
2324
+ if (cleanHex.length % 2 !== 0) {
2325
+ throw new Error("Hex string must have even length");
2326
+ }
2327
+ if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
2328
+ throw new Error(`Invalid hex character in: ${cleanHex}`);
2329
+ }
2330
+ buffer = hexToBytes(cleanHex);
2331
+ }
2112
2332
  const results = [];
2113
2333
  let offset = 0;
2334
+ const sequenceStartTime = mergedOptions.limits?.maxParseTime ? Date.now() : 0;
2114
2335
  while (offset < buffer.length) {
2336
+ if (sequenceStartTime > 0 && mergedOptions.limits?.maxParseTime) {
2337
+ const elapsed = Date.now() - sequenceStartTime;
2338
+ if (elapsed > mergedOptions.limits.maxParseTime) {
2339
+ throw new Error(`Parse timeout: exceeded ${mergedOptions.limits.maxParseTime}ms limit`);
2340
+ }
2341
+ }
2115
2342
  const byte = readByte(buffer, offset);
2116
2343
  if (byte === 255) {
2117
2344
  throw new Error(`Unexpected break code (0xff) at offset ${offset} - not inside indefinite-length item`);
2118
2345
  }
2119
- const remainingHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
2120
- const result = parse(remainingHex, mergedOptions);
2346
+ const result = dispatchFromBuffer(buffer, offset, mergedOptions);
2121
2347
  results.push(result.value);
2122
2348
  offset += result.bytesRead;
2123
2349
  }
@@ -2144,8 +2370,7 @@ function useCborParser() {
2144
2370
  if (byte === 255) {
2145
2371
  throw new Error(`Unexpected break code (0xff) at offset ${offset}`);
2146
2372
  }
2147
- const remainingHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
2148
- const result = parseWithSourceMap(remainingHex, mergedOptions);
2373
+ const result = parseWithSourceMap(buffer.subarray(offset), mergedOptions);
2149
2374
  const adjustedSourceMap = result.sourceMap.map((entry) => ({
2150
2375
  ...entry,
2151
2376
  start: entry.start + offset,
@@ -2178,5 +2403,5 @@ exports.useCborParser = useCborParser;
2178
2403
  exports.useCborString = useCborString;
2179
2404
  exports.useCborTag = useCborTag;
2180
2405
  exports.validateUtf8Strict = validateUtf8Strict;
2181
- //# sourceMappingURL=chunk-PTWN7K3Y.cjs.map
2182
- //# sourceMappingURL=chunk-PTWN7K3Y.cjs.map
2406
+ //# sourceMappingURL=chunk-3Z45RBZP.cjs.map
2407
+ //# sourceMappingURL=chunk-3Z45RBZP.cjs.map