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/README.md
CHANGED
|
@@ -186,8 +186,10 @@ The following options properties can be provided to the Packr or Unpackr constru
|
|
|
186
186
|
* `largeBigIntToFloat` - If a bigint needs to be encoded that is larger than will fit in 64-bit integers, it will be encoded as a float-64 (otherwise will throw a RangeError).
|
|
187
187
|
* `useBigIntExtension` - If a bigint needs to be encoded that is larger than will fit in 64-bit integers, it will be encoded using a custom extension that supports up to about 1000-bits of integer precision.
|
|
188
188
|
* `encodeUndefinedAsNil` - Encodes a value of `undefined` as a MessagePack `nil`, the same as a `null`.
|
|
189
|
-
* `int64AsType` - This will decode uint64 and int64 numbers as the specified type. The type can be `bigint` (default), `number`, `string`, or `auto` (where range [-2^53...2^53] is represented by number and everything else by a bigint).
|
|
189
|
+
* `int64AsType` - This will decode uint64 and int64 numbers as the specified type. The type can be `bigint` (default), `number`, `string`, or `auto` (where range [-2^53...2^53] is represented by number and everything else by a bigint).
|
|
190
|
+
* `skipValues` - This can be an array of property values that will indicate properties that should be skipped when serializing objects. For example, to mimic `JSON.stringify`'s behavior of skipping properties with a value of `undefined`, you can provide `skipValues: [undefined]`. Note, that this will only apply to serializing objects as standard MessagePack maps, not to records. Also, the array is checked by calling the `include` method, so you can provide an object with an `includes` if you want a custom function to skip values.
|
|
190
191
|
* `onInvalidDate` - This can be provided as function that will be called when an invalid date is provided. The function can throw an error, or return a value that will be encoded in place of the invalid date. If not provided, an invalid date will be encoded as an invalid timestamp (which decodes with msgpackr back to an invalid date).
|
|
192
|
+
* `writeFunction` - This can be provided as function that will be called when a function is encountered. The function can throw an error, or return a value that will be encoded in place of the function. If not provided, a function will be encoded as undefined (similar to `JSON.stringify`).
|
|
191
193
|
* `mapAsEmptyObject` - Encodes JS `Map`s as empty objects (for back-compat with older libraries).
|
|
192
194
|
* `setAsEmptyObject` - Encodes JS `Set`s as empty objects (for back-compat with older libraries).
|
|
193
195
|
|
package/dist/index-no-eval.cjs
CHANGED
|
@@ -912,8 +912,10 @@
|
|
|
912
912
|
}
|
|
913
913
|
|
|
914
914
|
function asSafeString(property) {
|
|
915
|
+
// protect against expensive (DoS) string conversions
|
|
915
916
|
if (typeof property === 'string') return property;
|
|
916
|
-
if (typeof property === 'number') return property.toString();
|
|
917
|
+
if (typeof property === 'number' || typeof property === 'boolean' || typeof property === 'bigint') return property.toString();
|
|
918
|
+
if (property == null) return property + '';
|
|
917
919
|
throw new Error('Invalid property type for record', typeof property);
|
|
918
920
|
}
|
|
919
921
|
// the registration of the record definition extension (as "r")
|
|
@@ -953,7 +955,7 @@
|
|
|
953
955
|
let errors = { Error, TypeError, ReferenceError };
|
|
954
956
|
currentExtensions[0x65] = () => {
|
|
955
957
|
let data = read();
|
|
956
|
-
return (errors[data[0]] || Error)(data[1])
|
|
958
|
+
return (errors[data[0]] || Error)(data[1], { cause: data[2] })
|
|
957
959
|
};
|
|
958
960
|
|
|
959
961
|
currentExtensions[0x69] = (data) => {
|
|
@@ -997,8 +999,15 @@
|
|
|
997
999
|
currentExtensions[0x74] = (data) => {
|
|
998
1000
|
let typeCode = data[0];
|
|
999
1001
|
let typedArrayName = typedArrays[typeCode];
|
|
1000
|
-
if (!typedArrayName)
|
|
1002
|
+
if (!typedArrayName) {
|
|
1003
|
+
if (typeCode === 16) {
|
|
1004
|
+
let ab = new ArrayBuffer(data.length - 1);
|
|
1005
|
+
let u8 = new Uint8Array(ab);
|
|
1006
|
+
u8.set(data.subarray(1));
|
|
1007
|
+
return ab;
|
|
1008
|
+
}
|
|
1001
1009
|
throw new Error('Could not find typed array for code ' + typeCode)
|
|
1010
|
+
}
|
|
1002
1011
|
// we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
|
|
1003
1012
|
return new glbl[typedArrayName](Uint8Array.prototype.slice.call(data, 1).buffer)
|
|
1004
1013
|
};
|
|
@@ -1158,7 +1167,7 @@
|
|
|
1158
1167
|
if (!this.structures && options.useRecords != false)
|
|
1159
1168
|
this.structures = [];
|
|
1160
1169
|
// two byte record ids for shared structures
|
|
1161
|
-
let useTwoByteRecords = maxSharedStructures > 32 || (maxOwnStructures + maxSharedStructures > 64);
|
|
1170
|
+
let useTwoByteRecords = maxSharedStructures > 32 || (maxOwnStructures + maxSharedStructures > 64);
|
|
1162
1171
|
let sharedLimitId = maxSharedStructures + 0x40;
|
|
1163
1172
|
let maxStructureId = maxSharedStructures + maxOwnStructures + 0x40;
|
|
1164
1173
|
if (maxStructureId > 8256) {
|
|
@@ -1176,7 +1185,7 @@
|
|
|
1176
1185
|
}
|
|
1177
1186
|
safeEnd = target.length - 10;
|
|
1178
1187
|
if (safeEnd - position < 0x800) {
|
|
1179
|
-
// don't start too close to the end,
|
|
1188
|
+
// don't start too close to the end,
|
|
1180
1189
|
target = new ByteArrayAllocate(target.length);
|
|
1181
1190
|
targetView = target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length));
|
|
1182
1191
|
safeEnd = target.length - 10;
|
|
@@ -1294,10 +1303,14 @@
|
|
|
1294
1303
|
return packr.pack(value, encodeOptions)
|
|
1295
1304
|
}
|
|
1296
1305
|
packr.lastNamedStructuresLength = sharedLength;
|
|
1306
|
+
// don't keep large buffers around
|
|
1307
|
+
if (target.length > 0x40000000) target = null;
|
|
1297
1308
|
return returnBuffer
|
|
1298
1309
|
}
|
|
1299
1310
|
}
|
|
1300
1311
|
}
|
|
1312
|
+
// don't keep large buffers around, they take too much memory and cause problems (limit at 1GB)
|
|
1313
|
+
if (target.length > 0x40000000) target = null;
|
|
1301
1314
|
if (encodeOptions & RESET_BUFFER_MODE)
|
|
1302
1315
|
position = start;
|
|
1303
1316
|
}
|
|
@@ -1515,12 +1528,12 @@
|
|
|
1515
1528
|
targetView.setUint32(position, referee.id);
|
|
1516
1529
|
position += 4;
|
|
1517
1530
|
return
|
|
1518
|
-
} else
|
|
1531
|
+
} else
|
|
1519
1532
|
referenceMap.set(value, { offset: position - start });
|
|
1520
1533
|
}
|
|
1521
1534
|
let constructor = value.constructor;
|
|
1522
1535
|
if (constructor === Object) {
|
|
1523
|
-
writeObject(value
|
|
1536
|
+
writeObject(value);
|
|
1524
1537
|
} else if (constructor === Array) {
|
|
1525
1538
|
packArray(value);
|
|
1526
1539
|
} else if (constructor === Map) {
|
|
@@ -1543,7 +1556,7 @@
|
|
|
1543
1556
|
pack(entryValue);
|
|
1544
1557
|
}
|
|
1545
1558
|
}
|
|
1546
|
-
} else {
|
|
1559
|
+
} else {
|
|
1547
1560
|
for (let i = 0, l = extensions.length; i < l; i++) {
|
|
1548
1561
|
let extensionClass = extensionClasses[i];
|
|
1549
1562
|
if (value instanceof extensionClass) {
|
|
@@ -1611,13 +1624,13 @@
|
|
|
1611
1624
|
if (json !== value)
|
|
1612
1625
|
return pack(json)
|
|
1613
1626
|
}
|
|
1614
|
-
|
|
1627
|
+
|
|
1615
1628
|
// if there is a writeFunction, use it, otherwise just encode as undefined
|
|
1616
1629
|
if (type === 'function')
|
|
1617
1630
|
return pack(this.writeFunction && this.writeFunction(value));
|
|
1618
|
-
|
|
1619
|
-
// no extension found, write as object
|
|
1620
|
-
writeObject(value
|
|
1631
|
+
|
|
1632
|
+
// no extension found, write as plain object
|
|
1633
|
+
writeObject(value);
|
|
1621
1634
|
}
|
|
1622
1635
|
}
|
|
1623
1636
|
}
|
|
@@ -1673,9 +1686,19 @@
|
|
|
1673
1686
|
}
|
|
1674
1687
|
};
|
|
1675
1688
|
|
|
1676
|
-
const writePlainObject = (this.variableMapSize || this.coercibleKeyAsNumber) ? (object) => {
|
|
1689
|
+
const writePlainObject = (this.variableMapSize || this.coercibleKeyAsNumber || this.skipValues) ? (object) => {
|
|
1677
1690
|
// this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
|
|
1678
|
-
let keys
|
|
1691
|
+
let keys;
|
|
1692
|
+
if (this.skipValues) {
|
|
1693
|
+
keys = [];
|
|
1694
|
+
for (let key in object) {
|
|
1695
|
+
if ((typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) &&
|
|
1696
|
+
!this.skipValues.includes(object[key]))
|
|
1697
|
+
keys.push(key);
|
|
1698
|
+
}
|
|
1699
|
+
} else {
|
|
1700
|
+
keys = Object.keys(object);
|
|
1701
|
+
}
|
|
1679
1702
|
let length = keys.length;
|
|
1680
1703
|
if (length < 0x10) {
|
|
1681
1704
|
target[position++] = 0x80 | length;
|
|
@@ -1704,13 +1727,13 @@
|
|
|
1704
1727
|
}
|
|
1705
1728
|
}
|
|
1706
1729
|
} :
|
|
1707
|
-
(object
|
|
1730
|
+
(object) => {
|
|
1708
1731
|
target[position++] = 0xde; // always using map 16, so we can preallocate and set the length afterwards
|
|
1709
1732
|
let objectOffset = position - start;
|
|
1710
1733
|
position += 2;
|
|
1711
1734
|
let size = 0;
|
|
1712
1735
|
for (let key in object) {
|
|
1713
|
-
if (
|
|
1736
|
+
if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
|
|
1714
1737
|
pack(key);
|
|
1715
1738
|
pack(object[key]);
|
|
1716
1739
|
size++;
|
|
@@ -1722,12 +1745,12 @@
|
|
|
1722
1745
|
|
|
1723
1746
|
const writeRecord = this.useRecords === false ? writePlainObject :
|
|
1724
1747
|
(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)
|
|
1725
|
-
(object
|
|
1748
|
+
(object) => {
|
|
1726
1749
|
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
|
|
1727
1750
|
let objectOffset = position++ - start;
|
|
1728
1751
|
let wroteKeys;
|
|
1729
1752
|
for (let key in object) {
|
|
1730
|
-
if (
|
|
1753
|
+
if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
|
|
1731
1754
|
nextTransition = transition[key];
|
|
1732
1755
|
if (nextTransition)
|
|
1733
1756
|
transition = nextTransition;
|
|
@@ -1766,10 +1789,10 @@
|
|
|
1766
1789
|
insertNewRecord(transition, Object.keys(object), objectOffset, 0);
|
|
1767
1790
|
}
|
|
1768
1791
|
} :
|
|
1769
|
-
(object
|
|
1792
|
+
(object) => {
|
|
1770
1793
|
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
|
|
1771
1794
|
let newTransitions = 0;
|
|
1772
|
-
for (let key in object) if (
|
|
1795
|
+
for (let key in object) if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
|
|
1773
1796
|
nextTransition = transition[key];
|
|
1774
1797
|
if (!nextTransition) {
|
|
1775
1798
|
nextTransition = transition[key] = Object.create(null);
|
|
@@ -1789,16 +1812,16 @@
|
|
|
1789
1812
|
}
|
|
1790
1813
|
// now write the values
|
|
1791
1814
|
for (let key in object)
|
|
1792
|
-
if (
|
|
1815
|
+
if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
|
|
1793
1816
|
pack(object[key]);
|
|
1794
1817
|
}
|
|
1795
1818
|
};
|
|
1796
1819
|
|
|
1797
|
-
//
|
|
1820
|
+
// create reference to useRecords if useRecords is a function
|
|
1798
1821
|
const checkUseRecords = typeof this.useRecords == 'function' && this.useRecords;
|
|
1799
|
-
|
|
1800
|
-
const writeObject = checkUseRecords ? (object
|
|
1801
|
-
checkUseRecords(object) ? writeRecord(object
|
|
1822
|
+
|
|
1823
|
+
const writeObject = checkUseRecords ? (object) => {
|
|
1824
|
+
checkUseRecords(object) ? writeRecord(object) : writePlainObject(object);
|
|
1802
1825
|
} : writeRecord;
|
|
1803
1826
|
|
|
1804
1827
|
const makeRoom = (end) => {
|
|
@@ -1903,7 +1926,7 @@
|
|
|
1903
1926
|
target[insertionOffset + start] = keysTarget[0];
|
|
1904
1927
|
}
|
|
1905
1928
|
};
|
|
1906
|
-
const writeStruct = (object
|
|
1929
|
+
const writeStruct = (object) => {
|
|
1907
1930
|
let newPosition = writeStructSlots(object, target, start, position, structures, makeRoom, (value, newPosition, notifySharedUpdate) => {
|
|
1908
1931
|
if (notifySharedUpdate)
|
|
1909
1932
|
return hasSharedUpdate = true;
|
|
@@ -1917,16 +1940,22 @@
|
|
|
1917
1940
|
return position;
|
|
1918
1941
|
}, this);
|
|
1919
1942
|
if (newPosition === 0) // bail and go to a msgpack object
|
|
1920
|
-
return writeObject(object
|
|
1943
|
+
return writeObject(object);
|
|
1921
1944
|
position = newPosition;
|
|
1922
1945
|
};
|
|
1923
1946
|
}
|
|
1924
1947
|
useBuffer(buffer) {
|
|
1925
1948
|
// this means we are finished using our own buffer and we can write over it safely
|
|
1926
1949
|
target = buffer;
|
|
1927
|
-
|
|
1950
|
+
target.dataView || (target.dataView = new DataView(target.buffer, target.byteOffset, target.byteLength));
|
|
1928
1951
|
position = 0;
|
|
1929
1952
|
}
|
|
1953
|
+
set position (value) {
|
|
1954
|
+
position = value;
|
|
1955
|
+
}
|
|
1956
|
+
get position() {
|
|
1957
|
+
return position;
|
|
1958
|
+
}
|
|
1930
1959
|
clearSharedData() {
|
|
1931
1960
|
if (this.structures)
|
|
1932
1961
|
this.structures = [];
|
|
@@ -1995,7 +2024,7 @@
|
|
|
1995
2024
|
target[position++] = 0x65; // 'e' for error
|
|
1996
2025
|
target[position++] = 0;
|
|
1997
2026
|
}
|
|
1998
|
-
pack([ error.name, error.message ]);
|
|
2027
|
+
pack([ error.name, error.message, error.cause ]);
|
|
1999
2028
|
}
|
|
2000
2029
|
}, {
|
|
2001
2030
|
pack(regex, allocateForWrite, pack) {
|
|
@@ -2048,6 +2077,7 @@
|
|
|
2048
2077
|
}
|
|
2049
2078
|
target[position++] = 0x74; // "t" for typed array
|
|
2050
2079
|
target[position++] = type;
|
|
2080
|
+
if (!typedArray.buffer) typedArray = new Uint8Array(typedArray);
|
|
2051
2081
|
target.set(new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength), position);
|
|
2052
2082
|
}
|
|
2053
2083
|
function writeBuffer(buffer, allocateForWrite) {
|
|
@@ -2271,6 +2301,8 @@
|
|
|
2271
2301
|
exports.FLOAT32_OPTIONS = FLOAT32_OPTIONS;
|
|
2272
2302
|
exports.NEVER = NEVER;
|
|
2273
2303
|
exports.Packr = Packr;
|
|
2304
|
+
exports.RESERVE_START_SPACE = RESERVE_START_SPACE;
|
|
2305
|
+
exports.RESET_BUFFER_MODE = RESET_BUFFER_MODE;
|
|
2274
2306
|
exports.REUSE_BUFFER_MODE = REUSE_BUFFER_MODE;
|
|
2275
2307
|
exports.Unpackr = Unpackr;
|
|
2276
2308
|
exports.addExtension = addExtension;
|