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/README.md +3 -1
- package/dist/index-no-eval.cjs +61 -29
- package/dist/index-no-eval.cjs.map +1 -1
- package/dist/index-no-eval.min.js +1 -1
- package/dist/index-no-eval.min.js.map +1 -1
- package/dist/index.js +61 -29
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/node.cjs +61 -29
- package/dist/node.cjs.map +1 -1
- package/dist/test.js +143 -30
- package/dist/test.js.map +1 -1
- package/dist/unpack-no-eval.cjs +12 -3
- package/dist/unpack-no-eval.cjs.map +1 -1
- package/index.d.cts +19 -9
- package/index.d.ts +19 -9
- package/index.js +1 -1
- package/pack.js +47 -32
- package/package.json +1 -1
- package/struct.js +2 -0
- package/unpack.js +12 -3
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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 (
|
|
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
|
|
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 (
|
|
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 (
|
|
1876
|
+
if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
|
|
1854
1877
|
pack(object[key]);
|
|
1855
1878
|
}
|
|
1856
1879
|
};
|
|
1857
1880
|
|
|
1858
|
-
//
|
|
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
|
|
1862
|
-
checkUseRecords(object) ? writeRecord(object
|
|
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
|
|
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
|
|
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
|
-
|
|
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() {
|