msgpackr 1.11.2 → 1.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.cjs CHANGED
@@ -971,7 +971,10 @@ function asSafeString(property) {
971
971
  if (typeof property === 'string') return property;
972
972
  if (typeof property === 'number' || typeof property === 'boolean' || typeof property === 'bigint') return property.toString();
973
973
  if (property == null) return property + '';
974
- throw new Error('Invalid property type for record', typeof property);
974
+ if (currentUnpackr.allowArraysInMapKeys && Array.isArray(property) && property.flat().every(item => ['string', 'number', 'boolean', 'bigint'].includes(typeof item))) {
975
+ return property.flat().toString();
976
+ }
977
+ throw new Error(`Invalid property type for record: ${typeof property}`);
975
978
  }
976
979
  // the registration of the record definition extension (as "r")
977
980
  const recordDefinition = (id, highByte) => {
@@ -996,21 +999,47 @@ const recordDefinition = (id, highByte) => {
996
999
  currentExtensions[0] = () => {}; // notepack defines extension 0 to mean undefined, so use that as the default here
997
1000
  currentExtensions[0].noBuffer = true;
998
1001
 
999
- currentExtensions[0x42] = (data) => {
1000
- // decode bigint
1001
- let length = data.length;
1002
- let value = BigInt(data[0] & 0x80 ? data[0] - 0x100 : data[0]);
1003
- for (let i = 1; i < length; i++) {
1004
- value <<= BigInt(8);
1005
- value += BigInt(data[i]);
1002
+ currentExtensions[0x42] = data => {
1003
+ let headLength = (data.byteLength % 8) || 8;
1004
+ let head = BigInt(data[0] & 0x80 ? data[0] - 0x100 : data[0]);
1005
+ for (let i = 1; i < headLength; i++) {
1006
+ head <<= BigInt(8);
1007
+ head += BigInt(data[i]);
1008
+ }
1009
+ if (data.byteLength !== headLength) {
1010
+ let view = new DataView(data.buffer, data.byteOffset, data.byteLength);
1011
+ let decode = (start, end) => {
1012
+ let length = end - start;
1013
+ if (length <= 40) {
1014
+ let out = view.getBigUint64(start);
1015
+ for (let i = start + 8; i < end; i += 8) {
1016
+ out <<= BigInt(64n);
1017
+ out |= view.getBigUint64(i);
1018
+ }
1019
+ return out
1020
+ }
1021
+ // if (length === 8) return view.getBigUint64(start)
1022
+ let middle = start + (length >> 4 << 3);
1023
+ let left = decode(start, middle);
1024
+ let right = decode(middle, end);
1025
+ return (left << BigInt((end - middle) * 8)) | right
1026
+ };
1027
+ head = (head << BigInt((view.byteLength - headLength) * 8)) | decode(headLength, view.byteLength);
1006
1028
  }
1007
- return value;
1029
+ return head
1008
1030
  };
1009
1031
 
1010
- let errors = { Error, TypeError, ReferenceError };
1032
+ let errors = {
1033
+ Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError, AggregateError: typeof AggregateError === 'function' ? AggregateError : null,
1034
+ };
1011
1035
  currentExtensions[0x65] = () => {
1012
1036
  let data = read();
1013
- return (errors[data[0]] || Error)(data[1], { cause: data[2] })
1037
+ if (!errors[data[0]]) {
1038
+ let error = Error(data[1], { cause: data[2] });
1039
+ error.name = data[0];
1040
+ return error
1041
+ }
1042
+ return errors[data[0]](data[1], { cause: data[2] })
1014
1043
  };
1015
1044
 
1016
1045
  currentExtensions[0x69] = (data) => {
@@ -1021,20 +1050,33 @@ currentExtensions[0x69] = (data) => {
1021
1050
  referenceMap = new Map();
1022
1051
  let token = src[position$1];
1023
1052
  let target;
1024
- // TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
1025
- // ahead past references to record structure definitions
1053
+ // TODO: handle any other types that can cycle and make the code more robust if there are other extensions
1026
1054
  if (token >= 0x90 && token < 0xa0 || token == 0xdc || token == 0xdd)
1027
1055
  target = [];
1056
+ else if (token >= 0x80 && token < 0x90 || token == 0xde || token == 0xdf)
1057
+ target = new Map();
1058
+ else if ((token >= 0xc7 && token <= 0xc9 || token >= 0xd4 && token <= 0xd8) && src[position$1 + 1] === 0x73)
1059
+ target = new Set();
1028
1060
  else
1029
1061
  target = {};
1030
1062
 
1031
1063
  let refEntry = { target }; // a placeholder object
1032
1064
  referenceMap.set(id, refEntry);
1033
1065
  let targetProperties = read(); // read the next value as the target object to id
1034
- if (refEntry.used) // there is a cycle, so we have to assign properties to original target
1035
- return Object.assign(target, targetProperties)
1036
- refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
1037
- return targetProperties // no cycle, can just use the returned read object
1066
+ if (!refEntry.used) {
1067
+ // no cycle, can just use the returned read object
1068
+ return refEntry.target = targetProperties // replace the placeholder with the real one
1069
+ } else {
1070
+ // there is a cycle, so we have to assign properties to original target
1071
+ Object.assign(target, targetProperties);
1072
+ }
1073
+
1074
+ // copy over map/set entries if we're able to
1075
+ if (target instanceof Map)
1076
+ for (let [k, v] of targetProperties.entries()) target.set(k, v);
1077
+ if (target instanceof Set)
1078
+ for (let i of Array.from(targetProperties)) target.add(i);
1079
+ return target
1038
1080
  };
1039
1081
 
1040
1082
  currentExtensions[0x70] = (data) => {
@@ -1053,18 +1095,16 @@ const typedArrays = ['Int8','Uint8','Uint8Clamped','Int16','Uint16','Int32','Uin
1053
1095
  let glbl = typeof globalThis === 'object' ? globalThis : window;
1054
1096
  currentExtensions[0x74] = (data) => {
1055
1097
  let typeCode = data[0];
1098
+ // we always have to slice to get a new ArrayBuffer that is aligned
1099
+ let buffer = Uint8Array.prototype.slice.call(data, 1).buffer;
1100
+
1056
1101
  let typedArrayName = typedArrays[typeCode];
1057
1102
  if (!typedArrayName) {
1058
- if (typeCode === 16) {
1059
- let ab = new ArrayBuffer(data.length - 1);
1060
- let u8 = new Uint8Array(ab);
1061
- u8.set(data.subarray(1));
1062
- return ab;
1063
- }
1103
+ if (typeCode === 16) return buffer
1104
+ if (typeCode === 17) return new DataView(buffer)
1064
1105
  throw new Error('Could not find typed array for code ' + typeCode)
1065
1106
  }
1066
- // we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
1067
- return new glbl[typedArrayName](Uint8Array.prototype.slice.call(data, 1).buffer)
1107
+ return new glbl[typedArrayName](buffer)
1068
1108
  };
1069
1109
  currentExtensions[0x78] = () => {
1070
1110
  let data = read();
@@ -1092,13 +1132,13 @@ currentExtensions[0xff] = (data) => {
1092
1132
  return new Date(
1093
1133
  ((data[0] << 22) + (data[1] << 14) + (data[2] << 6) + (data[3] >> 2)) / 1000000 +
1094
1134
  ((data[3] & 0x3) * 0x100000000 + data[4] * 0x1000000 + (data[5] << 16) + (data[6] << 8) + data[7]) * 1000)
1095
- else if (data.length == 12)// TODO: Implement support for negative
1135
+ else if (data.length == 12)
1096
1136
  return new Date(
1097
1137
  ((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]) / 1000000 +
1098
1138
  (((data[4] & 0x80) ? -0x1000000000000 : 0) + data[6] * 0x10000000000 + data[7] * 0x100000000 + data[8] * 0x1000000 + (data[9] << 16) + (data[10] << 8) + data[11]) * 1000)
1099
1139
  else
1100
1140
  return new Date('invalid')
1101
- }; // notepack defines extension 0 to mean undefined, so use that as the default here
1141
+ };
1102
1142
  // registration of bulk record definition?
1103
1143
  // currentExtensions[0x52] = () =>
1104
1144
 
@@ -1703,11 +1743,11 @@ class Packr extends Unpackr {
1703
1743
  } else if (type === 'boolean') {
1704
1744
  target[position++] = value ? 0xc3 : 0xc2;
1705
1745
  } else if (type === 'bigint') {
1706
- if (value < (BigInt(1)<<BigInt(63)) && value >= -(BigInt(1)<<BigInt(63))) {
1746
+ if (value < 0x8000000000000000 && value >= -0x8000000000000000) {
1707
1747
  // use a signed int as long as it fits
1708
1748
  target[position++] = 0xd3;
1709
1749
  targetView.setBigInt64(position, value);
1710
- } else if (value < (BigInt(1)<<BigInt(64)) && value > 0) {
1750
+ } else if (value < 0x10000000000000000 && value > 0) {
1711
1751
  // if we can fit an unsigned int, use that
1712
1752
  target[position++] = 0xcf;
1713
1753
  targetView.setBigUint64(position, value);
@@ -1718,22 +1758,46 @@ class Packr extends Unpackr {
1718
1758
  targetView.setFloat64(position, Number(value));
1719
1759
  } else if (this.largeBigIntToString) {
1720
1760
  return pack(value.toString());
1721
- } else if (this.useBigIntExtension && value < BigInt(2)**BigInt(1023) && value > -(BigInt(2)**BigInt(1023))) {
1722
- target[position++] = 0xc7;
1723
- position++;
1724
- target[position++] = 0x42; // "B" for BigInt
1725
- let bytes = [];
1726
- let alignedSign;
1727
- do {
1728
- let byte = value & BigInt(0xff);
1729
- alignedSign = (byte & BigInt(0x80)) === (value < BigInt(0) ? BigInt(0x80) : BigInt(0));
1730
- bytes.push(byte);
1731
- value >>= BigInt(8);
1732
- } while (!((value === BigInt(0) || value === BigInt(-1)) && alignedSign));
1733
- target[position-2] = bytes.length;
1734
- for (let i = bytes.length; i > 0;) {
1735
- target[position++] = Number(bytes[--i]);
1761
+ } else if (this.useBigIntExtension || this.moreTypes) {
1762
+ let empty = value < 0 ? BigInt(-1) : BigInt(0);
1763
+
1764
+ let array;
1765
+ if (value >> BigInt(0x10000) === empty) {
1766
+ let mask = BigInt(0x10000000000000000) - BigInt(1); // literal would overflow
1767
+ let chunks = [];
1768
+ do {
1769
+ chunks.push(value & mask);
1770
+ value >>= BigInt(64);
1771
+ } while (value !== empty)
1772
+
1773
+ array = new Uint8Array(new BigUint64Array(chunks).buffer);
1774
+ array.reverse();
1775
+ } else {
1776
+ let invert = value < 0;
1777
+ let string = (invert ? ~value : value).toString(16);
1778
+ if (string.length % 2) {
1779
+ string = '0' + string;
1780
+ } else if (parseInt(string.charAt(0), 16) >= 8) {
1781
+ string = '00' + string;
1782
+ }
1783
+
1784
+ if (hasNodeBuffer$1) {
1785
+ array = Buffer.from(string, 'hex');
1786
+ } else {
1787
+ array = new Uint8Array(string.length / 2);
1788
+ for (let i = 0; i < array.length; i++) {
1789
+ array[i] = parseInt(string.slice(i * 2, i * 2 + 2), 16);
1790
+ }
1791
+ }
1792
+
1793
+ if (invert) {
1794
+ for (let i = 0; i < array.length; i++) array[i] = ~array[i];
1795
+ }
1736
1796
  }
1797
+
1798
+ if (array.length + position > safeEnd)
1799
+ makeRoom(array.length + position);
1800
+ position = writeExtensionData(array, target, position, 0x42);
1737
1801
  return
1738
1802
  } else {
1739
1803
  throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, use' +
@@ -2037,7 +2101,7 @@ class Packr extends Unpackr {
2037
2101
  }
2038
2102
  }
2039
2103
 
2040
- extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, C1Type ];
2104
+ extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, DataView, C1Type ];
2041
2105
  extensions = [{
2042
2106
  pack(date, allocateForWrite, pack) {
2043
2107
  let seconds = date.getTime() / 1000;
@@ -2124,6 +2188,13 @@ extensions = [{
2124
2188
  else
2125
2189
  writeBuffer(typedArray, allocateForWrite);
2126
2190
  }
2191
+ }, {
2192
+ pack(arrayBuffer, allocateForWrite) {
2193
+ if (this.moreTypes)
2194
+ writeExtBuffer(arrayBuffer, 0x11, allocateForWrite);
2195
+ else
2196
+ writeBuffer(hasNodeBuffer$1 ? Buffer.from(arrayBuffer) : new Uint8Array(arrayBuffer), allocateForWrite);
2197
+ }
2127
2198
  }, {
2128
2199
  pack(c1, allocateForWrite) { // specific 0xC1 object
2129
2200
  let { target, position} = allocateForWrite(1);
@@ -2756,10 +2827,14 @@ function readStruct(src, position, srcEnd, unpackr) {
2756
2827
  throw new Error('Could not find typed structure ' + recordId);
2757
2828
  }
2758
2829
  var construct = structure.construct;
2830
+ var fullConstruct = structure.fullConstruct;
2759
2831
  if (!construct) {
2760
2832
  construct = structure.construct = function LazyObject() {
2761
2833
  };
2762
- var prototype = construct.prototype;
2834
+ fullConstruct = structure.fullConstruct = function LoadedObject() {
2835
+ };
2836
+ fullConstruct.prototype = unpackr.structPrototype ?? {};
2837
+ var prototype = construct.prototype = unpackr.structPrototype ? Object.create(unpackr.structPrototype) : {};
2763
2838
  let properties = [];
2764
2839
  let currentOffset = 0;
2765
2840
  let lastRefProperty;
@@ -2960,12 +3035,12 @@ function readStruct(src, position, srcEnd, unpackr) {
2960
3035
  Object.defineProperty(prototype, property.key, { get: withSource(property.get), enumerable: true });
2961
3036
  let valueFunction = 'v' + i++;
2962
3037
  args.push(valueFunction);
2963
- objectLiteralProperties.push('[' + JSON.stringify(property.key) + ']:' + valueFunction + '(s)');
3038
+ objectLiteralProperties.push('o[' + JSON.stringify(property.key) + ']=' + valueFunction + '(s)');
2964
3039
  }
2965
3040
  if (hasInheritedProperties) {
2966
3041
  objectLiteralProperties.push('__proto__:this');
2967
3042
  }
2968
- let toObject = (new Function(...args, 'return function(s){return{' + objectLiteralProperties.join(',') + '}}')).apply(null, properties.map(prop => prop.get));
3043
+ let toObject = (new Function(...args, 'var c=this;return function(s){var o=new c();' + objectLiteralProperties.join(';') + ';return o;}')).apply(fullConstruct, properties.map(prop => prop.get));
2969
3044
  Object.defineProperty(prototype, 'toJSON', {
2970
3045
  value(omitUnderscoredProperties) {
2971
3046
  return toObject.call(this, this[sourceSymbol]);