msgpackr 1.10.1 → 1.11.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.
package/dist/test.js CHANGED
@@ -963,8 +963,10 @@
963
963
  }
964
964
 
965
965
  function asSafeString(property) {
966
+ // protect against expensive (DoS) string conversions
966
967
  if (typeof property === 'string') return property;
967
- if (typeof property === 'number') return property.toString();
968
+ if (typeof property === 'number' || typeof property === 'boolean' || typeof property === 'bigint') return property.toString();
969
+ if (property == null) return property + '';
968
970
  throw new Error('Invalid property type for record', typeof property);
969
971
  }
970
972
  // the registration of the record definition extension (as "r")
@@ -1004,7 +1006,7 @@
1004
1006
  let errors = { Error, TypeError, ReferenceError };
1005
1007
  currentExtensions[0x65] = () => {
1006
1008
  let data = read();
1007
- return (errors[data[0]] || Error)(data[1])
1009
+ return (errors[data[0]] || Error)(data[1], { cause: data[2] })
1008
1010
  };
1009
1011
 
1010
1012
  currentExtensions[0x69] = (data) => {
@@ -1048,8 +1050,15 @@
1048
1050
  currentExtensions[0x74] = (data) => {
1049
1051
  let typeCode = data[0];
1050
1052
  let typedArrayName = typedArrays[typeCode];
1051
- if (!typedArrayName)
1053
+ 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
+ }
1052
1060
  throw new Error('Could not find typed array for code ' + typeCode)
1061
+ }
1053
1062
  // we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
1054
1063
  return new glbl[typedArrayName](Uint8Array.prototype.slice.call(data, 1).buffer)
1055
1064
  };
@@ -1219,7 +1228,7 @@
1219
1228
  if (!this.structures && options.useRecords != false)
1220
1229
  this.structures = [];
1221
1230
  // two byte record ids for shared structures
1222
- let useTwoByteRecords = maxSharedStructures > 32 || (maxOwnStructures + maxSharedStructures > 64);
1231
+ let useTwoByteRecords = maxSharedStructures > 32 || (maxOwnStructures + maxSharedStructures > 64);
1223
1232
  let sharedLimitId = maxSharedStructures + 0x40;
1224
1233
  let maxStructureId = maxSharedStructures + maxOwnStructures + 0x40;
1225
1234
  if (maxStructureId > 8256) {
@@ -1237,7 +1246,7 @@
1237
1246
  }
1238
1247
  safeEnd = target.length - 10;
1239
1248
  if (safeEnd - position < 0x800) {
1240
- // don't start too close to the end,
1249
+ // don't start too close to the end,
1241
1250
  target = new ByteArrayAllocate(target.length);
1242
1251
  targetView = target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length));
1243
1252
  safeEnd = target.length - 10;
@@ -1355,10 +1364,14 @@
1355
1364
  return packr.pack(value, encodeOptions)
1356
1365
  }
1357
1366
  packr.lastNamedStructuresLength = sharedLength;
1367
+ // don't keep large buffers around
1368
+ if (target.length > 0x40000000) target = null;
1358
1369
  return returnBuffer
1359
1370
  }
1360
1371
  }
1361
1372
  }
1373
+ // don't keep large buffers around, they take too much memory and cause problems (limit at 1GB)
1374
+ if (target.length > 0x40000000) target = null;
1362
1375
  if (encodeOptions & RESET_BUFFER_MODE)
1363
1376
  position = start;
1364
1377
  }
@@ -1576,12 +1589,12 @@
1576
1589
  targetView.setUint32(position, referee.id);
1577
1590
  position += 4;
1578
1591
  return
1579
- } else
1592
+ } else
1580
1593
  referenceMap.set(value, { offset: position - start });
1581
1594
  }
1582
1595
  let constructor = value.constructor;
1583
1596
  if (constructor === Object) {
1584
- writeObject(value, true);
1597
+ writeObject(value);
1585
1598
  } else if (constructor === Array) {
1586
1599
  packArray(value);
1587
1600
  } else if (constructor === Map) {
@@ -1604,7 +1617,7 @@
1604
1617
  pack(entryValue);
1605
1618
  }
1606
1619
  }
1607
- } else {
1620
+ } else {
1608
1621
  for (let i = 0, l = extensions.length; i < l; i++) {
1609
1622
  let extensionClass = extensionClasses[i];
1610
1623
  if (value instanceof extensionClass) {
@@ -1672,13 +1685,13 @@
1672
1685
  if (json !== value)
1673
1686
  return pack(json)
1674
1687
  }
1675
-
1688
+
1676
1689
  // if there is a writeFunction, use it, otherwise just encode as undefined
1677
1690
  if (type === 'function')
1678
1691
  return pack(this.writeFunction && this.writeFunction(value));
1679
-
1680
- // no extension found, write as object
1681
- writeObject(value, !value.hasOwnProperty); // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks
1692
+
1693
+ // no extension found, write as plain object
1694
+ writeObject(value);
1682
1695
  }
1683
1696
  }
1684
1697
  }
@@ -1734,9 +1747,19 @@
1734
1747
  }
1735
1748
  };
1736
1749
 
1737
- const writePlainObject = (this.variableMapSize || this.coercibleKeyAsNumber) ? (object) => {
1750
+ const writePlainObject = (this.variableMapSize || this.coercibleKeyAsNumber || this.skipValues) ? (object) => {
1738
1751
  // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
1739
- let keys = Object.keys(object);
1752
+ let keys;
1753
+ if (this.skipValues) {
1754
+ keys = [];
1755
+ for (let key in object) {
1756
+ if ((typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) &&
1757
+ !this.skipValues.includes(object[key]))
1758
+ keys.push(key);
1759
+ }
1760
+ } else {
1761
+ keys = Object.keys(object);
1762
+ }
1740
1763
  let length = keys.length;
1741
1764
  if (length < 0x10) {
1742
1765
  target[position++] = 0x80 | length;
@@ -1765,13 +1788,13 @@
1765
1788
  }
1766
1789
  }
1767
1790
  } :
1768
- (object, safePrototype) => {
1791
+ (object) => {
1769
1792
  target[position++] = 0xde; // always using map 16, so we can preallocate and set the length afterwards
1770
1793
  let objectOffset = position - start;
1771
1794
  position += 2;
1772
1795
  let size = 0;
1773
1796
  for (let key in object) {
1774
- if (safePrototype || object.hasOwnProperty(key)) {
1797
+ if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1775
1798
  pack(key);
1776
1799
  pack(object[key]);
1777
1800
  size++;
@@ -1783,12 +1806,12 @@
1783
1806
 
1784
1807
  const writeRecord = this.useRecords === false ? writePlainObject :
1785
1808
  (options.progressiveRecords && !useTwoByteRecords) ? // this is about 2% faster for highly stable structures, since it only requires one for-in loop (but much more expensive when new structure needs to be written)
1786
- (object, safePrototype) => {
1809
+ (object) => {
1787
1810
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1788
1811
  let objectOffset = position++ - start;
1789
1812
  let wroteKeys;
1790
1813
  for (let key in object) {
1791
- if (safePrototype || object.hasOwnProperty(key)) {
1814
+ if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1792
1815
  nextTransition = transition[key];
1793
1816
  if (nextTransition)
1794
1817
  transition = nextTransition;
@@ -1827,10 +1850,10 @@
1827
1850
  insertNewRecord(transition, Object.keys(object), objectOffset, 0);
1828
1851
  }
1829
1852
  } :
1830
- (object, safePrototype) => {
1853
+ (object) => {
1831
1854
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1832
1855
  let newTransitions = 0;
1833
- for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1856
+ for (let key in object) if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1834
1857
  nextTransition = transition[key];
1835
1858
  if (!nextTransition) {
1836
1859
  nextTransition = transition[key] = Object.create(null);
@@ -1850,16 +1873,16 @@
1850
1873
  }
1851
1874
  // now write the values
1852
1875
  for (let key in object)
1853
- if (safePrototype || object.hasOwnProperty(key)) {
1876
+ if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1854
1877
  pack(object[key]);
1855
1878
  }
1856
1879
  };
1857
1880
 
1858
- // craete reference to useRecords if useRecords is a function
1881
+ // create reference to useRecords if useRecords is a function
1859
1882
  const checkUseRecords = typeof this.useRecords == 'function' && this.useRecords;
1860
-
1861
- const writeObject = checkUseRecords ? (object, safePrototype) => {
1862
- checkUseRecords(object) ? writeRecord(object,safePrototype) : writePlainObject(object,safePrototype);
1883
+
1884
+ const writeObject = checkUseRecords ? (object) => {
1885
+ checkUseRecords(object) ? writeRecord(object) : writePlainObject(object);
1863
1886
  } : writeRecord;
1864
1887
 
1865
1888
  const makeRoom = (end) => {
@@ -1964,7 +1987,7 @@
1964
1987
  target[insertionOffset + start] = keysTarget[0];
1965
1988
  }
1966
1989
  };
1967
- const writeStruct = (object, safePrototype) => {
1990
+ const writeStruct = (object) => {
1968
1991
  let newPosition = writeStructSlots(object, target, start, position, structures, makeRoom, (value, newPosition, notifySharedUpdate) => {
1969
1992
  if (notifySharedUpdate)
1970
1993
  return hasSharedUpdate = true;
@@ -1978,16 +2001,22 @@
1978
2001
  return position;
1979
2002
  }, this);
1980
2003
  if (newPosition === 0) // bail and go to a msgpack object
1981
- return writeObject(object, true);
2004
+ return writeObject(object);
1982
2005
  position = newPosition;
1983
2006
  };
1984
2007
  }
1985
2008
  useBuffer(buffer) {
1986
2009
  // this means we are finished using our own buffer and we can write over it safely
1987
2010
  target = buffer;
1988
- targetView = new DataView(target.buffer, target.byteOffset, target.byteLength);
2011
+ target.dataView || (target.dataView = new DataView(target.buffer, target.byteOffset, target.byteLength));
1989
2012
  position = 0;
1990
2013
  }
2014
+ set position (value) {
2015
+ position = value;
2016
+ }
2017
+ get position() {
2018
+ return position;
2019
+ }
1991
2020
  clearSharedData() {
1992
2021
  if (this.structures)
1993
2022
  this.structures = [];
@@ -2056,7 +2085,7 @@
2056
2085
  target[position++] = 0x65; // 'e' for error
2057
2086
  target[position++] = 0;
2058
2087
  }
2059
- pack([ error.name, error.message ]);
2088
+ pack([ error.name, error.message, error.cause ]);
2060
2089
  }
2061
2090
  }, {
2062
2091
  pack(regex, allocateForWrite, pack) {
@@ -2109,6 +2138,7 @@
2109
2138
  }
2110
2139
  target[position++] = 0x74; // "t" for typed array
2111
2140
  target[position++] = type;
2141
+ if (!typedArray.buffer) typedArray = new Uint8Array(typedArray);
2112
2142
  target.set(new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength), position);
2113
2143
  }
2114
2144
  function writeBuffer(buffer, allocateForWrite) {
@@ -2701,6 +2731,8 @@
2701
2731
  src = Uint8Array.prototype.slice.call(src, position, srcEnd);
2702
2732
  srcEnd -= position;
2703
2733
  position = 0;
2734
+ if (!unpackr.getStructures)
2735
+ throw new Error(`Reference to shared structure ${recordId} without getStructures method`);
2704
2736
  unpackr._mergeStructures(unpackr.getStructures());
2705
2737
  if (!unpackr.typedStructs)
2706
2738
  throw new Error('Could not find any shared typed structures');
@@ -3711,13 +3743,35 @@
3711
3743
  }
3712
3744
  });
3713
3745
 
3746
+ test('moreTyesp: Error with causes', function() {
3747
+ const object = {
3748
+ error: new Error('test'),
3749
+ errorWithCause: new Error('test-1', { cause: new Error('test-2')}),
3750
+ };
3751
+ const packr = new Packr({
3752
+ moreTypes: true,
3753
+ });
3754
+
3755
+ const serialized = packr.pack(object);
3756
+ const deserialized = packr.unpack(serialized);
3757
+ assert.equal(deserialized.error.message, object.error.message);
3758
+ assert.equal(deserialized.error.cause, object.error.cause);
3759
+ assert.equal(deserialized.errorWithCause.message, object.errorWithCause.message);
3760
+ assert.equal(deserialized.errorWithCause.cause.message, object.errorWithCause.cause.message);
3761
+ assert.equal(deserialized.errorWithCause.cause.cause, object.errorWithCause.cause.cause);
3762
+ });
3763
+
3714
3764
  test('structured cloning: self reference', function() {
3715
3765
  let object = {
3716
3766
  test: 'string',
3717
3767
  children: [
3718
3768
  { name: 'child' }
3719
- ]
3769
+ ],
3770
+ value: new ArrayBuffer(10)
3720
3771
  };
3772
+ let u8 = new Uint8Array(object.value);
3773
+ u8[0] = 1;
3774
+ u8[1] = 2;
3721
3775
  object.self = object;
3722
3776
  object.children[1] = object;
3723
3777
  object.children[2] = object.children[0];
@@ -3733,6 +3787,10 @@
3733
3787
  assert.equal(deserialized.children[1], deserialized);
3734
3788
  assert.equal(deserialized.children[0], deserialized.children[2]);
3735
3789
  assert.equal(deserialized.children, deserialized.childrenAgain);
3790
+ assert.equal(deserialized.value.constructor.name, 'ArrayBuffer');
3791
+ u8 = new Uint8Array(deserialized.value);
3792
+ assert.equal(u8[0], 1);
3793
+ assert.equal(u8[1], 2);
3736
3794
  });
3737
3795
 
3738
3796
  test('structured cloning: types', function() {
@@ -3813,6 +3871,13 @@
3813
3871
  assert.deepEqual(deserialized, data);
3814
3872
  });
3815
3873
 
3874
+ test('object with __proto__', function(){
3875
+ const data = { foo: 'bar', __proto__: { isAdmin: true } };
3876
+ var serialized = pack(data);
3877
+ var deserialized = unpack(serialized);
3878
+ assert.deepEqual(deserialized, { foo: 'bar' });
3879
+ });
3880
+
3816
3881
  test('separate instances', function() {
3817
3882
  const packr = new Packr({
3818
3883
  structures: [['m', 'e'], ['action', 'share']]
@@ -4200,6 +4265,54 @@
4200
4265
  const deserialized = unpack(serialized);
4201
4266
  assert.deepStrictEqual(deserialized, { someData: [1, 2, 3, 4] });
4202
4267
  });
4268
+ test('skip values', function () {
4269
+ var data = {
4270
+ data: [
4271
+ { a: 1, name: 'one', type: 'odd', isOdd: true },
4272
+ { a: 2, name: 'two', type: 'even', isOdd: undefined },
4273
+ { a: 3, name: 'three', type: 'odd', isOdd: true },
4274
+ { a: 4, name: 'four', type: 'even', isOdd: null},
4275
+ { a: 5, name: 'five', type: 'odd', isOdd: true },
4276
+ { a: 6, name: 'six', type: 'even', isOdd: null }
4277
+ ],
4278
+ description: 'some names',
4279
+ types: ['odd', 'even'],
4280
+ convertEnumToNum: [
4281
+ { prop: 'test' },
4282
+ { prop: 'test' },
4283
+ { prop: 'test' },
4284
+ { prop: 1 },
4285
+ { prop: 2 },
4286
+ { prop: [undefined, null] },
4287
+ { prop: null }
4288
+ ]
4289
+ };
4290
+ var expected = {
4291
+ data: [
4292
+ { a: 1, name: 'one', type: 'odd', isOdd: true },
4293
+ { a: 2, name: 'two', type: 'even' },
4294
+ { a: 3, name: 'three', type: 'odd', isOdd: true },
4295
+ { a: 4, name: 'four', type: 'even', },
4296
+ { a: 5, name: 'five', type: 'odd', isOdd: true },
4297
+ { a: 6, name: 'six', type: 'even' }
4298
+ ],
4299
+ description: 'some names',
4300
+ types: ['odd', 'even'],
4301
+ convertEnumToNum: [
4302
+ { prop: 'test' },
4303
+ { prop: 'test' },
4304
+ { prop: 'test' },
4305
+ { prop: 1 },
4306
+ { prop: 2 },
4307
+ { prop: [undefined, null] },
4308
+ {}
4309
+ ]
4310
+ };
4311
+ let packr = new Packr({ useRecords: false, skipValues: [undefined, null] });
4312
+ var serialized = packr.pack(data);
4313
+ var deserialized = packr.unpack(serialized);
4314
+ assert.deepEqual(deserialized, expected);
4315
+ });
4203
4316
  });
4204
4317
  suite('msgpackr performance tests', function(){
4205
4318
  test('performance JSON.parse', function() {