msgpackr 1.11.2 → 1.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/test.js CHANGED
@@ -967,7 +967,10 @@
967
967
  if (typeof property === 'string') return property;
968
968
  if (typeof property === 'number' || typeof property === 'boolean' || typeof property === 'bigint') return property.toString();
969
969
  if (property == null) return property + '';
970
- throw new Error('Invalid property type for record', typeof property);
970
+ if (currentUnpackr.allowArraysInMapKeys && Array.isArray(property) && property.flat().every(item => ['string', 'number', 'boolean', 'bigint'].includes(typeof item))) {
971
+ return property.flat().toString();
972
+ }
973
+ throw new Error(`Invalid property type for record: ${typeof property}`);
971
974
  }
972
975
  // the registration of the record definition extension (as "r")
973
976
  const recordDefinition = (id, highByte) => {
@@ -1017,20 +1020,33 @@
1017
1020
  referenceMap = new Map();
1018
1021
  let token = src[position$1];
1019
1022
  let target;
1020
- // TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
1021
- // ahead past references to record structure definitions
1023
+ // TODO: handle any other types that can cycle and make the code more robust if there are other extensions
1022
1024
  if (token >= 0x90 && token < 0xa0 || token == 0xdc || token == 0xdd)
1023
1025
  target = [];
1026
+ else if (token >= 0x80 && token < 0x90 || token == 0xde || token == 0xdf)
1027
+ target = new Map();
1028
+ else if ((token >= 0xc7 && token <= 0xc9 || token >= 0xd4 && token <= 0xd8) && src[position$1 + 1] === 0x73)
1029
+ target = new Set();
1024
1030
  else
1025
1031
  target = {};
1026
1032
 
1027
1033
  let refEntry = { target }; // a placeholder object
1028
1034
  referenceMap.set(id, refEntry);
1029
1035
  let targetProperties = read(); // read the next value as the target object to id
1030
- if (refEntry.used) // there is a cycle, so we have to assign properties to original target
1031
- return Object.assign(target, targetProperties)
1032
- refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
1033
- return targetProperties // no cycle, can just use the returned read object
1036
+ if (!refEntry.used) {
1037
+ // no cycle, can just use the returned read object
1038
+ return refEntry.target = targetProperties // replace the placeholder with the real one
1039
+ } else {
1040
+ // there is a cycle, so we have to assign properties to original target
1041
+ Object.assign(target, targetProperties);
1042
+ }
1043
+
1044
+ // copy over map/set entries if we're able to
1045
+ if (target instanceof Map)
1046
+ for (let [k, v] of targetProperties.entries()) target.set(k, v);
1047
+ if (target instanceof Set)
1048
+ for (let i of Array.from(targetProperties)) target.add(i);
1049
+ return target
1034
1050
  };
1035
1051
 
1036
1052
  currentExtensions[0x70] = (data) => {
@@ -1049,18 +1065,16 @@
1049
1065
  let glbl = typeof globalThis === 'object' ? globalThis : window;
1050
1066
  currentExtensions[0x74] = (data) => {
1051
1067
  let typeCode = data[0];
1068
+ // we always have to slice to get a new ArrayBuffer that is aligned
1069
+ let buffer = Uint8Array.prototype.slice.call(data, 1).buffer;
1070
+
1052
1071
  let typedArrayName = typedArrays[typeCode];
1053
1072
  if (!typedArrayName) {
1054
- if (typeCode === 16) {
1055
- let ab = new ArrayBuffer(data.length - 1);
1056
- let u8 = new Uint8Array(ab);
1057
- u8.set(data.subarray(1));
1058
- return ab;
1059
- }
1073
+ if (typeCode === 16) return buffer
1074
+ if (typeCode === 17) return new DataView(buffer)
1060
1075
  throw new Error('Could not find typed array for code ' + typeCode)
1061
1076
  }
1062
- // we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
1063
- return new glbl[typedArrayName](Uint8Array.prototype.slice.call(data, 1).buffer)
1077
+ return new glbl[typedArrayName](buffer)
1064
1078
  };
1065
1079
  currentExtensions[0x78] = () => {
1066
1080
  let data = read();
@@ -1088,13 +1102,13 @@
1088
1102
  return new Date(
1089
1103
  ((data[0] << 22) + (data[1] << 14) + (data[2] << 6) + (data[3] >> 2)) / 1000000 +
1090
1104
  ((data[3] & 0x3) * 0x100000000 + data[4] * 0x1000000 + (data[5] << 16) + (data[6] << 8) + data[7]) * 1000)
1091
- else if (data.length == 12)// TODO: Implement support for negative
1105
+ else if (data.length == 12)
1092
1106
  return new Date(
1093
1107
  ((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]) / 1000000 +
1094
1108
  (((data[4] & 0x80) ? -0x1000000000000 : 0) + data[6] * 0x10000000000 + data[7] * 0x100000000 + data[8] * 0x1000000 + (data[9] << 16) + (data[10] << 8) + data[11]) * 1000)
1095
1109
  else
1096
1110
  return new Date('invalid')
1097
- }; // notepack defines extension 0 to mean undefined, so use that as the default here
1111
+ };
1098
1112
  // registration of bulk record definition?
1099
1113
  // currentExtensions[0x52] = () =>
1100
1114
 
@@ -1698,11 +1712,11 @@
1698
1712
  } else if (type === 'boolean') {
1699
1713
  target[position++] = value ? 0xc3 : 0xc2;
1700
1714
  } else if (type === 'bigint') {
1701
- if (value < (BigInt(1)<<BigInt(63)) && value >= -(BigInt(1)<<BigInt(63))) {
1715
+ if (value < 0x8000000000000000 && value >= -0x8000000000000000) {
1702
1716
  // use a signed int as long as it fits
1703
1717
  target[position++] = 0xd3;
1704
1718
  targetView.setBigInt64(position, value);
1705
- } else if (value < (BigInt(1)<<BigInt(64)) && value > 0) {
1719
+ } else if (value < 0x10000000000000000 && value > 0) {
1706
1720
  // if we can fit an unsigned int, use that
1707
1721
  target[position++] = 0xcf;
1708
1722
  targetView.setBigUint64(position, value);
@@ -1713,7 +1727,7 @@
1713
1727
  targetView.setFloat64(position, Number(value));
1714
1728
  } else if (this.largeBigIntToString) {
1715
1729
  return pack(value.toString());
1716
- } else if (this.useBigIntExtension && value < BigInt(2)**BigInt(1023) && value > -(BigInt(2)**BigInt(1023))) {
1730
+ } else if ((this.useBigIntExtension || this.moreTypes) && value < BigInt(2)**BigInt(1023) && value > -(BigInt(2)**BigInt(1023))) {
1717
1731
  target[position++] = 0xc7;
1718
1732
  position++;
1719
1733
  target[position++] = 0x42; // "B" for BigInt
@@ -2032,7 +2046,7 @@
2032
2046
  }
2033
2047
  };
2034
2048
 
2035
- extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, C1Type ];
2049
+ extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, DataView, C1Type ];
2036
2050
  extensions = [{
2037
2051
  pack(date, allocateForWrite, pack) {
2038
2052
  let seconds = date.getTime() / 1000;
@@ -2119,6 +2133,13 @@
2119
2133
  else
2120
2134
  writeBuffer(typedArray, allocateForWrite);
2121
2135
  }
2136
+ }, {
2137
+ pack(arrayBuffer, allocateForWrite) {
2138
+ if (this.moreTypes)
2139
+ writeExtBuffer(arrayBuffer, 0x11, allocateForWrite);
2140
+ else
2141
+ writeBuffer(hasNodeBuffer$1 ? Buffer.from(arrayBuffer) : new Uint8Array(arrayBuffer), allocateForWrite);
2142
+ }
2122
2143
  }, {
2123
2144
  pack(c1, allocateForWrite) { // specific 0xC1 object
2124
2145
  let { target, position} = allocateForWrite(1);
@@ -2749,10 +2770,14 @@
2749
2770
  throw new Error('Could not find typed structure ' + recordId);
2750
2771
  }
2751
2772
  var construct = structure.construct;
2773
+ var fullConstruct = structure.fullConstruct;
2752
2774
  if (!construct) {
2753
2775
  construct = structure.construct = function LazyObject() {
2754
2776
  };
2755
- var prototype = construct.prototype;
2777
+ fullConstruct = structure.fullConstruct = function LoadedObject() {
2778
+ };
2779
+ fullConstruct.prototype = unpackr.structPrototype ?? {};
2780
+ var prototype = construct.prototype = unpackr.structPrototype ? Object.create(unpackr.structPrototype) : {};
2756
2781
  let properties = [];
2757
2782
  let currentOffset = 0;
2758
2783
  let lastRefProperty;
@@ -2953,12 +2978,12 @@
2953
2978
  Object.defineProperty(prototype, property.key, { get: withSource(property.get), enumerable: true });
2954
2979
  let valueFunction = 'v' + i++;
2955
2980
  args.push(valueFunction);
2956
- objectLiteralProperties.push('[' + JSON.stringify(property.key) + ']:' + valueFunction + '(s)');
2981
+ objectLiteralProperties.push('o[' + JSON.stringify(property.key) + ']=' + valueFunction + '(s)');
2957
2982
  }
2958
2983
  if (hasInheritedProperties) {
2959
2984
  objectLiteralProperties.push('__proto__:this');
2960
2985
  }
2961
- let toObject = (new Function(...args, 'return function(s){return{' + objectLiteralProperties.join(',') + '}}')).apply(null, properties.map(prop => prop.get));
2986
+ 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));
2962
2987
  Object.defineProperty(prototype, 'toJSON', {
2963
2988
  value(omitUnderscoredProperties) {
2964
2989
  return toObject.call(this, this[sourceSymbol]);
@@ -3434,7 +3459,10 @@
3434
3459
  d: -352523523642364364364264264264264264262642642n,
3435
3460
  e: 0xffffffffffffffffffffffffffn,
3436
3461
  f: -0xffffffffffffffffffffffffffn,
3462
+ o: -12345678901234567890n,
3463
+ array: [],
3437
3464
  };
3465
+
3438
3466
  let serialized = packr.pack(data);
3439
3467
  let deserialized = packr.unpack(serialized);
3440
3468
  assert.deepEqual(data, deserialized);
@@ -3750,7 +3778,7 @@
3750
3778
  }
3751
3779
  });
3752
3780
 
3753
- test('moreTyesp: Error with causes', function() {
3781
+ test('moreTypes: Error with causes', function() {
3754
3782
  const object = {
3755
3783
  error: new Error('test'),
3756
3784
  errorWithCause: new Error('test-1', { cause: new Error('test-2')}),
@@ -3800,6 +3828,39 @@
3800
3828
  assert.equal(u8[1], 2);
3801
3829
  });
3802
3830
 
3831
+ test('structured cloning: self reference with more types', function() {
3832
+ let set = new Set();
3833
+ set.add(['hello', 1, 2, { map: new Map([[set, set], ['a', 'b']]) }]);
3834
+
3835
+ let packr = new Packr({
3836
+ moreTypes: true,
3837
+ structuredClone: true,
3838
+ });
3839
+ let serialized = packr.pack(set);
3840
+ let deserialized = packr.unpack(serialized);
3841
+ assert.equal(deserialized.constructor.name, 'Set');
3842
+ let map = Array.from(deserialized)[0][3].map;
3843
+ assert.equal(map.get(deserialized), deserialized);
3844
+
3845
+ let sizeTestMap = new Map();
3846
+ for (let i = 0; i < 50; i++) {
3847
+ sizeTestMap.set(i || sizeTestMap, sizeTestMap);
3848
+ let deserialized = packr.unpack(packr.pack(sizeTestMap));
3849
+ assert.equal(deserialized.size, i + 1);
3850
+ assert(deserialized.has(deserialized));
3851
+ assert(deserialized.has(i || deserialized));
3852
+ }
3853
+
3854
+ let sizeTestSet = new Set();
3855
+ for (let i = 0; i < 50; i++) {
3856
+ sizeTestSet.add(i || sizeTestSet);
3857
+ let deserialized = packr.unpack(packr.pack(sizeTestSet));
3858
+ assert.equal(deserialized.size, i + 1);
3859
+ assert(deserialized.has(deserialized));
3860
+ assert(deserialized.has(i || deserialized));
3861
+ }
3862
+ });
3863
+
3803
3864
  test('structured cloning: types', function() {
3804
3865
  let b = typeof Buffer != 'undefined' ? Buffer.alloc(20) : new Uint8Array(20);
3805
3866
  let fa = new Float32Array(b.buffer, 8, 2);
@@ -3810,7 +3871,9 @@
3810
3871
  set: new Set(['a', 'b']),
3811
3872
  regexp: /test/gi,
3812
3873
  float32Array: fa,
3813
- uint16Array: new Uint16Array([3,4])
3874
+ uint16Array: new Uint16Array([3, 4]),
3875
+ arrayBuffer: new Uint8Array([0xde, 0xad]).buffer,
3876
+ dataView: new DataView(new Uint8Array([0xbe, 0xef]).buffer),
3814
3877
  };
3815
3878
  let packr = new Packr({
3816
3879
  moreTypes: true,
@@ -3827,6 +3890,10 @@
3827
3890
  assert.equal(deserialized.uint16Array.constructor.name, 'Uint16Array');
3828
3891
  assert.equal(deserialized.uint16Array[0], 3);
3829
3892
  assert.equal(deserialized.uint16Array[1], 4);
3893
+ assert.equal(deserialized.arrayBuffer.constructor.name, 'ArrayBuffer');
3894
+ assert.equal(new DataView(deserialized.arrayBuffer).getUint16(), 0xdead);
3895
+ assert.equal(deserialized.dataView.constructor.name, 'DataView');
3896
+ assert.equal(deserialized.dataView.getUint16(), 0xbeef);
3830
3897
  });
3831
3898
  test('big bundledStrings', function() {
3832
3899
  const MSGPACK_OPTIONS = {bundleStrings: true};
@@ -4245,6 +4312,18 @@
4245
4312
  assert.deepEqual(deserialized, data);
4246
4313
  });
4247
4314
 
4315
+ test('arrays in map keys', function() {
4316
+ const msgpackr = new Packr({ mapsAsObjects: true, allowArraysInMapKeys: true });
4317
+
4318
+ const map = new Map();
4319
+ map.set([1, 2, 3], 1);
4320
+ map.set([1, 2, ['foo', 3.14]], 2);
4321
+
4322
+ const packed = msgpackr.pack(map);
4323
+ const unpacked = msgpackr.unpack(packed);
4324
+ assert.deepEqual(unpacked, { '1,2,3': 1, '1,2,foo,3.14': 2 });
4325
+ });
4326
+
4248
4327
  test('utf16 causing expansion', function() {
4249
4328
  this.timeout(10000);
4250
4329
  let data = {fixstr: 'ᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝ', str8:'ᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝ'};