msgpackr 1.11.1 → 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/README.md +1 -0
- package/dist/index-no-eval.cjs +117 -46
- 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 +117 -46
- 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 +124 -49
- package/dist/node.cjs.map +1 -1
- package/dist/test.js +207 -59
- package/dist/test.js.map +1 -1
- package/dist/unpack-no-eval.cjs +67 -27
- package/dist/unpack-no-eval.cjs.map +1 -1
- package/index.d.cts +1 -0
- package/index.d.ts +1 -0
- package/pack.js +50 -19
- package/package.json +5 -1
- package/struct.js +7 -3
- package/unpack.js +67 -27
package/README.md
CHANGED
|
@@ -193,6 +193,7 @@ The following options properties can be provided to the Packr or Unpackr constru
|
|
|
193
193
|
* `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`).
|
|
194
194
|
* `mapAsEmptyObject` - Encodes JS `Map`s as empty objects (for back-compat with older libraries).
|
|
195
195
|
* `setAsEmptyObject` - Encodes JS `Set`s as empty objects (for back-compat with older libraries).
|
|
196
|
+
* `allowArraysInMapKeys` - Allows arrays to be used as keys in Maps, as long as all elements are strings, numbers, booleans, or bigints. When enabled, such arrays are flattened and converted to a string representation.
|
|
196
197
|
|
|
197
198
|
### 32-bit Float Options
|
|
198
199
|
By default all non-integer numbers are serialized as 64-bit float (double). This is fast, and ensures maximum precision. However, often real-world data doesn't not need 64-bits of precision, and using 32-bit encoding can be much more space efficient. There are several options that provide more efficient encodings. Using the decimal rounding options for encoding and decoding provides lossless storage of common decimal representations like 7.99, in more efficient 32-bit format (rather than 64-bit). The `useFloat32` property has several possible options, available from the module as constants:
|
package/dist/index-no-eval.cjs
CHANGED
|
@@ -916,7 +916,10 @@
|
|
|
916
916
|
if (typeof property === 'string') return property;
|
|
917
917
|
if (typeof property === 'number' || typeof property === 'boolean' || typeof property === 'bigint') return property.toString();
|
|
918
918
|
if (property == null) return property + '';
|
|
919
|
-
|
|
919
|
+
if (currentUnpackr.allowArraysInMapKeys && Array.isArray(property) && property.flat().every(item => ['string', 'number', 'boolean', 'bigint'].includes(typeof item))) {
|
|
920
|
+
return property.flat().toString();
|
|
921
|
+
}
|
|
922
|
+
throw new Error(`Invalid property type for record: ${typeof property}`);
|
|
920
923
|
}
|
|
921
924
|
// the registration of the record definition extension (as "r")
|
|
922
925
|
const recordDefinition = (id, highByte) => {
|
|
@@ -941,21 +944,47 @@
|
|
|
941
944
|
currentExtensions[0] = () => {}; // notepack defines extension 0 to mean undefined, so use that as the default here
|
|
942
945
|
currentExtensions[0].noBuffer = true;
|
|
943
946
|
|
|
944
|
-
currentExtensions[0x42] =
|
|
945
|
-
|
|
946
|
-
let
|
|
947
|
-
let
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
value += BigInt(data[i]);
|
|
947
|
+
currentExtensions[0x42] = data => {
|
|
948
|
+
let headLength = (data.byteLength % 8) || 8;
|
|
949
|
+
let head = BigInt(data[0] & 0x80 ? data[0] - 0x100 : data[0]);
|
|
950
|
+
for (let i = 1; i < headLength; i++) {
|
|
951
|
+
head <<= BigInt(8);
|
|
952
|
+
head += BigInt(data[i]);
|
|
951
953
|
}
|
|
952
|
-
|
|
954
|
+
if (data.byteLength !== headLength) {
|
|
955
|
+
let view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
956
|
+
let decode = (start, end) => {
|
|
957
|
+
let length = end - start;
|
|
958
|
+
if (length <= 40) {
|
|
959
|
+
let out = view.getBigUint64(start);
|
|
960
|
+
for (let i = start + 8; i < end; i += 8) {
|
|
961
|
+
out <<= BigInt(64n);
|
|
962
|
+
out |= view.getBigUint64(i);
|
|
963
|
+
}
|
|
964
|
+
return out
|
|
965
|
+
}
|
|
966
|
+
// if (length === 8) return view.getBigUint64(start)
|
|
967
|
+
let middle = start + (length >> 4 << 3);
|
|
968
|
+
let left = decode(start, middle);
|
|
969
|
+
let right = decode(middle, end);
|
|
970
|
+
return (left << BigInt((end - middle) * 8)) | right
|
|
971
|
+
};
|
|
972
|
+
head = (head << BigInt((view.byteLength - headLength) * 8)) | decode(headLength, view.byteLength);
|
|
973
|
+
}
|
|
974
|
+
return head
|
|
953
975
|
};
|
|
954
976
|
|
|
955
|
-
let errors = {
|
|
977
|
+
let errors = {
|
|
978
|
+
Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError, AggregateError: typeof AggregateError === 'function' ? AggregateError : null,
|
|
979
|
+
};
|
|
956
980
|
currentExtensions[0x65] = () => {
|
|
957
981
|
let data = read();
|
|
958
|
-
|
|
982
|
+
if (!errors[data[0]]) {
|
|
983
|
+
let error = Error(data[1], { cause: data[2] });
|
|
984
|
+
error.name = data[0];
|
|
985
|
+
return error
|
|
986
|
+
}
|
|
987
|
+
return errors[data[0]](data[1], { cause: data[2] })
|
|
959
988
|
};
|
|
960
989
|
|
|
961
990
|
currentExtensions[0x69] = (data) => {
|
|
@@ -966,20 +995,33 @@
|
|
|
966
995
|
referenceMap = new Map();
|
|
967
996
|
let token = src[position$1];
|
|
968
997
|
let target;
|
|
969
|
-
// TODO: handle
|
|
970
|
-
// ahead past references to record structure definitions
|
|
998
|
+
// TODO: handle any other types that can cycle and make the code more robust if there are other extensions
|
|
971
999
|
if (token >= 0x90 && token < 0xa0 || token == 0xdc || token == 0xdd)
|
|
972
1000
|
target = [];
|
|
1001
|
+
else if (token >= 0x80 && token < 0x90 || token == 0xde || token == 0xdf)
|
|
1002
|
+
target = new Map();
|
|
1003
|
+
else if ((token >= 0xc7 && token <= 0xc9 || token >= 0xd4 && token <= 0xd8) && src[position$1 + 1] === 0x73)
|
|
1004
|
+
target = new Set();
|
|
973
1005
|
else
|
|
974
1006
|
target = {};
|
|
975
1007
|
|
|
976
1008
|
let refEntry = { target }; // a placeholder object
|
|
977
1009
|
referenceMap.set(id, refEntry);
|
|
978
1010
|
let targetProperties = read(); // read the next value as the target object to id
|
|
979
|
-
if (refEntry.used)
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1011
|
+
if (!refEntry.used) {
|
|
1012
|
+
// no cycle, can just use the returned read object
|
|
1013
|
+
return refEntry.target = targetProperties // replace the placeholder with the real one
|
|
1014
|
+
} else {
|
|
1015
|
+
// there is a cycle, so we have to assign properties to original target
|
|
1016
|
+
Object.assign(target, targetProperties);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// copy over map/set entries if we're able to
|
|
1020
|
+
if (target instanceof Map)
|
|
1021
|
+
for (let [k, v] of targetProperties.entries()) target.set(k, v);
|
|
1022
|
+
if (target instanceof Set)
|
|
1023
|
+
for (let i of Array.from(targetProperties)) target.add(i);
|
|
1024
|
+
return target
|
|
983
1025
|
};
|
|
984
1026
|
|
|
985
1027
|
currentExtensions[0x70] = (data) => {
|
|
@@ -998,18 +1040,16 @@
|
|
|
998
1040
|
let glbl = typeof globalThis === 'object' ? globalThis : window;
|
|
999
1041
|
currentExtensions[0x74] = (data) => {
|
|
1000
1042
|
let typeCode = data[0];
|
|
1043
|
+
// we always have to slice to get a new ArrayBuffer that is aligned
|
|
1044
|
+
let buffer = Uint8Array.prototype.slice.call(data, 1).buffer;
|
|
1045
|
+
|
|
1001
1046
|
let typedArrayName = typedArrays[typeCode];
|
|
1002
1047
|
if (!typedArrayName) {
|
|
1003
|
-
if (typeCode === 16)
|
|
1004
|
-
|
|
1005
|
-
let u8 = new Uint8Array(ab);
|
|
1006
|
-
u8.set(data.subarray(1));
|
|
1007
|
-
return ab;
|
|
1008
|
-
}
|
|
1048
|
+
if (typeCode === 16) return buffer
|
|
1049
|
+
if (typeCode === 17) return new DataView(buffer)
|
|
1009
1050
|
throw new Error('Could not find typed array for code ' + typeCode)
|
|
1010
1051
|
}
|
|
1011
|
-
|
|
1012
|
-
return new glbl[typedArrayName](Uint8Array.prototype.slice.call(data, 1).buffer)
|
|
1052
|
+
return new glbl[typedArrayName](buffer)
|
|
1013
1053
|
};
|
|
1014
1054
|
currentExtensions[0x78] = () => {
|
|
1015
1055
|
let data = read();
|
|
@@ -1037,13 +1077,13 @@
|
|
|
1037
1077
|
return new Date(
|
|
1038
1078
|
((data[0] << 22) + (data[1] << 14) + (data[2] << 6) + (data[3] >> 2)) / 1000000 +
|
|
1039
1079
|
((data[3] & 0x3) * 0x100000000 + data[4] * 0x1000000 + (data[5] << 16) + (data[6] << 8) + data[7]) * 1000)
|
|
1040
|
-
else if (data.length == 12)
|
|
1080
|
+
else if (data.length == 12)
|
|
1041
1081
|
return new Date(
|
|
1042
1082
|
((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]) / 1000000 +
|
|
1043
1083
|
(((data[4] & 0x80) ? -0x1000000000000 : 0) + data[6] * 0x10000000000 + data[7] * 0x100000000 + data[8] * 0x1000000 + (data[9] << 16) + (data[10] << 8) + data[11]) * 1000)
|
|
1044
1084
|
else
|
|
1045
1085
|
return new Date('invalid')
|
|
1046
|
-
};
|
|
1086
|
+
};
|
|
1047
1087
|
// registration of bulk record definition?
|
|
1048
1088
|
// currentExtensions[0x52] = () =>
|
|
1049
1089
|
|
|
@@ -1637,11 +1677,11 @@
|
|
|
1637
1677
|
} else if (type === 'boolean') {
|
|
1638
1678
|
target[position++] = value ? 0xc3 : 0xc2;
|
|
1639
1679
|
} else if (type === 'bigint') {
|
|
1640
|
-
if (value <
|
|
1680
|
+
if (value < 0x8000000000000000 && value >= -0x8000000000000000) {
|
|
1641
1681
|
// use a signed int as long as it fits
|
|
1642
1682
|
target[position++] = 0xd3;
|
|
1643
1683
|
targetView.setBigInt64(position, value);
|
|
1644
|
-
} else if (value <
|
|
1684
|
+
} else if (value < 0x10000000000000000 && value > 0) {
|
|
1645
1685
|
// if we can fit an unsigned int, use that
|
|
1646
1686
|
target[position++] = 0xcf;
|
|
1647
1687
|
targetView.setBigUint64(position, value);
|
|
@@ -1652,22 +1692,46 @@
|
|
|
1652
1692
|
targetView.setFloat64(position, Number(value));
|
|
1653
1693
|
} else if (this.largeBigIntToString) {
|
|
1654
1694
|
return pack(value.toString());
|
|
1655
|
-
} else if (this.useBigIntExtension
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
value
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1695
|
+
} else if (this.useBigIntExtension || this.moreTypes) {
|
|
1696
|
+
let empty = value < 0 ? BigInt(-1) : BigInt(0);
|
|
1697
|
+
|
|
1698
|
+
let array;
|
|
1699
|
+
if (value >> BigInt(0x10000) === empty) {
|
|
1700
|
+
let mask = BigInt(0x10000000000000000) - BigInt(1); // literal would overflow
|
|
1701
|
+
let chunks = [];
|
|
1702
|
+
do {
|
|
1703
|
+
chunks.push(value & mask);
|
|
1704
|
+
value >>= BigInt(64);
|
|
1705
|
+
} while (value !== empty)
|
|
1706
|
+
|
|
1707
|
+
array = new Uint8Array(new BigUint64Array(chunks).buffer);
|
|
1708
|
+
array.reverse();
|
|
1709
|
+
} else {
|
|
1710
|
+
let invert = value < 0;
|
|
1711
|
+
let string = (invert ? ~value : value).toString(16);
|
|
1712
|
+
if (string.length % 2) {
|
|
1713
|
+
string = '0' + string;
|
|
1714
|
+
} else if (parseInt(string.charAt(0), 16) >= 8) {
|
|
1715
|
+
string = '00' + string;
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
if (hasNodeBuffer) {
|
|
1719
|
+
array = Buffer.from(string, 'hex');
|
|
1720
|
+
} else {
|
|
1721
|
+
array = new Uint8Array(string.length / 2);
|
|
1722
|
+
for (let i = 0; i < array.length; i++) {
|
|
1723
|
+
array[i] = parseInt(string.slice(i * 2, i * 2 + 2), 16);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
if (invert) {
|
|
1728
|
+
for (let i = 0; i < array.length; i++) array[i] = ~array[i];
|
|
1729
|
+
}
|
|
1670
1730
|
}
|
|
1731
|
+
|
|
1732
|
+
if (array.length + position > safeEnd)
|
|
1733
|
+
makeRoom(array.length + position);
|
|
1734
|
+
position = writeExtensionData(array, target, position, 0x42);
|
|
1671
1735
|
return
|
|
1672
1736
|
} else {
|
|
1673
1737
|
throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, use' +
|
|
@@ -1971,7 +2035,7 @@
|
|
|
1971
2035
|
}
|
|
1972
2036
|
}
|
|
1973
2037
|
|
|
1974
|
-
extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, C1Type ];
|
|
2038
|
+
extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, DataView, C1Type ];
|
|
1975
2039
|
extensions = [{
|
|
1976
2040
|
pack(date, allocateForWrite, pack) {
|
|
1977
2041
|
let seconds = date.getTime() / 1000;
|
|
@@ -2058,6 +2122,13 @@
|
|
|
2058
2122
|
else
|
|
2059
2123
|
writeBuffer(typedArray, allocateForWrite);
|
|
2060
2124
|
}
|
|
2125
|
+
}, {
|
|
2126
|
+
pack(arrayBuffer, allocateForWrite) {
|
|
2127
|
+
if (this.moreTypes)
|
|
2128
|
+
writeExtBuffer(arrayBuffer, 0x11, allocateForWrite);
|
|
2129
|
+
else
|
|
2130
|
+
writeBuffer(hasNodeBuffer ? Buffer.from(arrayBuffer) : new Uint8Array(arrayBuffer), allocateForWrite);
|
|
2131
|
+
}
|
|
2061
2132
|
}, {
|
|
2062
2133
|
pack(c1, allocateForWrite) { // specific 0xC1 object
|
|
2063
2134
|
let { target, position} = allocateForWrite(1);
|
|
@@ -2100,7 +2171,7 @@
|
|
|
2100
2171
|
target[position++] = length >> 8;
|
|
2101
2172
|
target[position++] = length & 0xff;
|
|
2102
2173
|
} else {
|
|
2103
|
-
|
|
2174
|
+
var { target, position, targetView } = allocateForWrite(length + 5);
|
|
2104
2175
|
target[position++] = 0xc6;
|
|
2105
2176
|
targetView.setUint32(position, length);
|
|
2106
2177
|
position += 4;
|