bson 4.2.2 → 4.4.1
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/HISTORY.md +44 -0
- package/bower.json +1 -1
- package/bson.d.ts +77 -6
- package/dist/bson.browser.esm.js +2116 -1292
- package/dist/bson.browser.esm.js.map +1 -1
- package/dist/bson.browser.umd.js +2172 -1347
- package/dist/bson.browser.umd.js.map +1 -1
- package/dist/bson.bundle.js +2174 -1347
- package/dist/bson.bundle.js.map +1 -1
- package/dist/bson.esm.js +2061 -1241
- package/dist/bson.esm.js.map +1 -1
- package/lib/binary.js +71 -60
- package/lib/binary.js.map +1 -1
- package/lib/bson.js +60 -52
- package/lib/bson.js.map +1 -1
- package/lib/code.js +18 -15
- package/lib/code.js.map +1 -1
- package/lib/db_ref.js +40 -30
- package/lib/db_ref.js.map +1 -1
- package/lib/decimal128.js +135 -124
- package/lib/decimal128.js.map +1 -1
- package/lib/double.js +24 -21
- package/lib/double.js.map +1 -1
- package/lib/ensure_buffer.js +4 -7
- package/lib/ensure_buffer.js.map +1 -1
- package/lib/extended_json.js +113 -71
- package/lib/extended_json.js.map +1 -1
- package/lib/float_parser.js +21 -21
- package/lib/float_parser.js.map +1 -1
- package/lib/int_32.js +19 -16
- package/lib/int_32.js.map +1 -1
- package/lib/long.js +248 -230
- package/lib/long.js.map +1 -1
- package/lib/map.js +54 -45
- package/lib/map.js.map +1 -1
- package/lib/max_key.js +16 -11
- package/lib/max_key.js.map +1 -1
- package/lib/min_key.js +16 -11
- package/lib/min_key.js.map +1 -1
- package/lib/objectid.js +96 -93
- package/lib/objectid.js.map +1 -1
- package/lib/parser/calculate_size.js +17 -15
- package/lib/parser/calculate_size.js.map +1 -1
- package/lib/parser/deserializer.js +135 -123
- package/lib/parser/deserializer.js.map +1 -1
- package/lib/parser/serializer.js +108 -88
- package/lib/parser/serializer.js.map +1 -1
- package/lib/parser/utils.js +47 -25
- package/lib/parser/utils.js.map +1 -1
- package/lib/regexp.js +16 -15
- package/lib/regexp.js.map +1 -1
- package/lib/symbol.js +21 -18
- package/lib/symbol.js.map +1 -1
- package/lib/timestamp.js +50 -27
- package/lib/timestamp.js.map +1 -1
- package/lib/uuid.js +173 -42
- package/lib/uuid.js.map +1 -1
- package/lib/uuid_utils.js +34 -0
- package/lib/uuid_utils.js.map +1 -0
- package/lib/validate_utf8.js +12 -12
- package/lib/validate_utf8.js.map +1 -1
- package/package.json +5 -4
- package/src/binary.ts +20 -6
- package/src/bson.ts +3 -0
- package/src/code.ts +6 -2
- package/src/db_ref.ts +14 -5
- package/src/decimal128.ts +14 -5
- package/src/double.ts +4 -2
- package/src/ensure_buffer.ts +7 -7
- package/src/extended_json.ts +64 -16
- package/src/int_32.ts +4 -2
- package/src/long.ts +22 -8
- package/src/max_key.ts +5 -1
- package/src/min_key.ts +5 -1
- package/src/objectid.ts +15 -20
- package/src/parser/calculate_size.ts +8 -11
- package/src/parser/deserializer.ts +46 -36
- package/src/parser/serializer.ts +13 -12
- package/src/parser/utils.ts +49 -17
- package/src/regexp.ts +5 -5
- package/src/symbol.ts +4 -2
- package/src/timestamp.ts +6 -1
- package/src/uuid.ts +192 -40
- package/src/uuid_utils.ts +31 -0
package/src/extended_json.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Binary } from './binary';
|
|
2
2
|
import type { Document } from './bson';
|
|
3
3
|
import { Code } from './code';
|
|
4
|
-
import { DBRef } from './db_ref';
|
|
4
|
+
import { DBRef, isDBRefLike } from './db_ref';
|
|
5
5
|
import { Decimal128 } from './decimal128';
|
|
6
6
|
import { Double } from './double';
|
|
7
7
|
import { Int32 } from './int_32';
|
|
@@ -9,7 +9,7 @@ import { Long } from './long';
|
|
|
9
9
|
import { MaxKey } from './max_key';
|
|
10
10
|
import { MinKey } from './min_key';
|
|
11
11
|
import { ObjectId } from './objectid';
|
|
12
|
-
import { isObjectLike } from './parser/utils';
|
|
12
|
+
import { isDate, isObjectLike, isRegExp } from './parser/utils';
|
|
13
13
|
import { BSONRegExp } from './regexp';
|
|
14
14
|
import { BSONSymbol } from './symbol';
|
|
15
15
|
import { Timestamp } from './timestamp';
|
|
@@ -120,7 +120,7 @@ function deserializeValue(value: any, options: EJSON.Options = {}) {
|
|
|
120
120
|
return Code.fromExtendedJSON(value);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
if (value
|
|
123
|
+
if (isDBRefLike(value) || value.$dbPointer) {
|
|
124
124
|
const v = value.$ref ? value : value.$dbPointer;
|
|
125
125
|
|
|
126
126
|
// we run into this in a "degenerate EJSON" case (with $id and $ref order flipped)
|
|
@@ -140,9 +140,20 @@ function deserializeValue(value: any, options: EJSON.Options = {}) {
|
|
|
140
140
|
return value;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
type EJSONSerializeOptions = EJSON.Options & {
|
|
144
|
+
seenObjects: { obj: unknown; propertyName: string }[];
|
|
145
|
+
};
|
|
146
|
+
|
|
143
147
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
144
|
-
function serializeArray(array: any[], options:
|
|
145
|
-
return array.map((v: unknown) =>
|
|
148
|
+
function serializeArray(array: any[], options: EJSONSerializeOptions): any[] {
|
|
149
|
+
return array.map((v: unknown, index: number) => {
|
|
150
|
+
options.seenObjects.push({ propertyName: `index ${index}`, obj: null });
|
|
151
|
+
try {
|
|
152
|
+
return serializeValue(v, options);
|
|
153
|
+
} finally {
|
|
154
|
+
options.seenObjects.pop();
|
|
155
|
+
}
|
|
156
|
+
});
|
|
146
157
|
}
|
|
147
158
|
|
|
148
159
|
function getISOString(date: Date) {
|
|
@@ -152,12 +163,42 @@ function getISOString(date: Date) {
|
|
|
152
163
|
}
|
|
153
164
|
|
|
154
165
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
155
|
-
function serializeValue(value: any, options:
|
|
166
|
+
function serializeValue(value: any, options: EJSONSerializeOptions): any {
|
|
167
|
+
if ((typeof value === 'object' || typeof value === 'function') && value !== null) {
|
|
168
|
+
const index = options.seenObjects.findIndex(entry => entry.obj === value);
|
|
169
|
+
if (index !== -1) {
|
|
170
|
+
const props = options.seenObjects.map(entry => entry.propertyName);
|
|
171
|
+
const leadingPart = props
|
|
172
|
+
.slice(0, index)
|
|
173
|
+
.map(prop => `${prop} -> `)
|
|
174
|
+
.join('');
|
|
175
|
+
const alreadySeen = props[index];
|
|
176
|
+
const circularPart =
|
|
177
|
+
' -> ' +
|
|
178
|
+
props
|
|
179
|
+
.slice(index + 1, props.length - 1)
|
|
180
|
+
.map(prop => `${prop} -> `)
|
|
181
|
+
.join('');
|
|
182
|
+
const current = props[props.length - 1];
|
|
183
|
+
const leadingSpace = ' '.repeat(leadingPart.length + alreadySeen.length / 2);
|
|
184
|
+
const dashes = '-'.repeat(
|
|
185
|
+
circularPart.length + (alreadySeen.length + current.length) / 2 - 1
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
throw new TypeError(
|
|
189
|
+
'Converting circular structure to EJSON:\n' +
|
|
190
|
+
` ${leadingPart}${alreadySeen}${circularPart}${current}\n` +
|
|
191
|
+
` ${leadingSpace}\\${dashes}/`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
options.seenObjects[options.seenObjects.length - 1].obj = value;
|
|
195
|
+
}
|
|
196
|
+
|
|
156
197
|
if (Array.isArray(value)) return serializeArray(value, options);
|
|
157
198
|
|
|
158
199
|
if (value === undefined) return null;
|
|
159
200
|
|
|
160
|
-
if (value instanceof Date) {
|
|
201
|
+
if (value instanceof Date || isDate(value)) {
|
|
161
202
|
const dateNum = value.getTime(),
|
|
162
203
|
// is it in year range 1970-9999?
|
|
163
204
|
inRange = dateNum > -1 && dateNum < 253402318800000;
|
|
@@ -172,7 +213,7 @@ function serializeValue(value: any, options: EJSON.Options): any {
|
|
|
172
213
|
: { $date: { $numberLong: value.getTime().toString() } };
|
|
173
214
|
}
|
|
174
215
|
|
|
175
|
-
if (typeof value === 'number' && !options.relaxed) {
|
|
216
|
+
if (typeof value === 'number' && (!options.relaxed || !isFinite(value))) {
|
|
176
217
|
// it's an integer
|
|
177
218
|
if (Math.floor(value) === value) {
|
|
178
219
|
const int32Range = value >= BSON_INT32_MIN && value <= BSON_INT32_MAX,
|
|
@@ -185,7 +226,7 @@ function serializeValue(value: any, options: EJSON.Options): any {
|
|
|
185
226
|
return { $numberDouble: value.toString() };
|
|
186
227
|
}
|
|
187
228
|
|
|
188
|
-
if (value instanceof RegExp) {
|
|
229
|
+
if (value instanceof RegExp || isRegExp(value)) {
|
|
189
230
|
let flags = value.flags;
|
|
190
231
|
if (flags === undefined) {
|
|
191
232
|
const match = value.toString().match(/[gimuy]*$/);
|
|
@@ -232,7 +273,7 @@ const BSON_TYPE_MAPPINGS = {
|
|
|
232
273
|
} as const;
|
|
233
274
|
|
|
234
275
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
235
|
-
function serializeDocument(doc: any, options:
|
|
276
|
+
function serializeDocument(doc: any, options: EJSONSerializeOptions) {
|
|
236
277
|
if (doc == null || typeof doc !== 'object') throw new Error('not an object instance');
|
|
237
278
|
|
|
238
279
|
const bsontype: BSONType['_bsontype'] = doc._bsontype;
|
|
@@ -240,7 +281,12 @@ function serializeDocument(doc: any, options: EJSON.Options) {
|
|
|
240
281
|
// It's a regular object. Recursively serialize its property values.
|
|
241
282
|
const _doc: Document = {};
|
|
242
283
|
for (const name in doc) {
|
|
243
|
-
|
|
284
|
+
options.seenObjects.push({ propertyName: name, obj: null });
|
|
285
|
+
try {
|
|
286
|
+
_doc[name] = serializeValue(doc[name], options);
|
|
287
|
+
} finally {
|
|
288
|
+
options.seenObjects.pop();
|
|
289
|
+
}
|
|
244
290
|
}
|
|
245
291
|
return _doc;
|
|
246
292
|
} else if (isBSONType(doc)) {
|
|
@@ -264,10 +310,10 @@ function serializeDocument(doc: any, options: EJSON.Options) {
|
|
|
264
310
|
outDoc = new Code(outDoc.code, serializeValue(outDoc.scope, options));
|
|
265
311
|
} else if (bsontype === 'DBRef' && outDoc.oid) {
|
|
266
312
|
outDoc = new DBRef(
|
|
267
|
-
outDoc.collection,
|
|
313
|
+
serializeValue(outDoc.collection, options),
|
|
268
314
|
serializeValue(outDoc.oid, options),
|
|
269
|
-
outDoc.db,
|
|
270
|
-
outDoc.fields
|
|
315
|
+
serializeValue(outDoc.db, options),
|
|
316
|
+
serializeValue(outDoc.fields, options)
|
|
271
317
|
);
|
|
272
318
|
}
|
|
273
319
|
|
|
@@ -365,9 +411,11 @@ export namespace EJSON {
|
|
|
365
411
|
replacer = undefined;
|
|
366
412
|
space = 0;
|
|
367
413
|
}
|
|
368
|
-
|
|
414
|
+
const serializeOptions = Object.assign({ relaxed: true, legacy: false }, options, {
|
|
415
|
+
seenObjects: [{ propertyName: '(root)', obj: null }]
|
|
416
|
+
});
|
|
369
417
|
|
|
370
|
-
const doc = serializeValue(value,
|
|
418
|
+
const doc = serializeValue(value, serializeOptions);
|
|
371
419
|
return JSON.stringify(doc, replacer as Parameters<JSON['stringify']>[1], space);
|
|
372
420
|
}
|
|
373
421
|
|
package/src/int_32.ts
CHANGED
|
@@ -12,13 +12,15 @@ export interface Int32Extended {
|
|
|
12
12
|
export class Int32 {
|
|
13
13
|
_bsontype!: 'Int32';
|
|
14
14
|
|
|
15
|
-
value
|
|
15
|
+
value!: number;
|
|
16
16
|
/**
|
|
17
17
|
* Create an Int32 type
|
|
18
18
|
*
|
|
19
19
|
* @param value - the number we want to represent as an int32.
|
|
20
20
|
*/
|
|
21
21
|
constructor(value: number | string) {
|
|
22
|
+
if (!(this instanceof Int32)) return new Int32(value);
|
|
23
|
+
|
|
22
24
|
if ((value as unknown) instanceof Number) {
|
|
23
25
|
value = value.valueOf();
|
|
24
26
|
}
|
|
@@ -57,7 +59,7 @@ export class Int32 {
|
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
inspect(): string {
|
|
60
|
-
return `Int32(${this.valueOf()})`;
|
|
62
|
+
return `new Int32(${this.valueOf()})`;
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
|
package/src/long.ts
CHANGED
|
@@ -82,29 +82,43 @@ export class Long {
|
|
|
82
82
|
/**
|
|
83
83
|
* The high 32 bits as a signed value.
|
|
84
84
|
*/
|
|
85
|
-
high
|
|
85
|
+
high!: number;
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
88
|
* The low 32 bits as a signed value.
|
|
89
89
|
*/
|
|
90
|
-
low
|
|
90
|
+
low!: number;
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* Whether unsigned or not.
|
|
94
94
|
*/
|
|
95
|
-
unsigned
|
|
95
|
+
unsigned!: boolean;
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* Constructs a 64 bit two's-complement integer, given its low and high 32 bit values as *signed* integers.
|
|
99
99
|
* See the from* functions below for more convenient ways of constructing Longs.
|
|
100
|
+
*
|
|
101
|
+
* Acceptable signatures are:
|
|
102
|
+
* - Long(low, high, unsigned?)
|
|
103
|
+
* - Long(bigint, unsigned?)
|
|
104
|
+
* - Long(string, unsigned?)
|
|
105
|
+
*
|
|
100
106
|
* @param low - The low (signed) 32 bits of the long
|
|
101
107
|
* @param high - The high (signed) 32 bits of the long
|
|
102
108
|
* @param unsigned - Whether unsigned or not, defaults to signed
|
|
103
109
|
*/
|
|
104
|
-
constructor(low = 0, high
|
|
105
|
-
this
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
constructor(low: number | bigint | string = 0, high?: number | boolean, unsigned?: boolean) {
|
|
111
|
+
if (!(this instanceof Long)) return new Long(low, high, unsigned);
|
|
112
|
+
|
|
113
|
+
if (typeof low === 'bigint') {
|
|
114
|
+
Object.assign(this, Long.fromBigInt(low, !!high));
|
|
115
|
+
} else if (typeof low === 'string') {
|
|
116
|
+
Object.assign(this, Long.fromString(low, !!high));
|
|
117
|
+
} else {
|
|
118
|
+
this.low = low | 0;
|
|
119
|
+
this.high = (high as number) | 0;
|
|
120
|
+
this.unsigned = !!unsigned;
|
|
121
|
+
}
|
|
108
122
|
|
|
109
123
|
Object.defineProperty(this, '__isLong__', {
|
|
110
124
|
value: true,
|
|
@@ -992,7 +1006,7 @@ export class Long {
|
|
|
992
1006
|
}
|
|
993
1007
|
|
|
994
1008
|
inspect(): string {
|
|
995
|
-
return `Long("${this.toString()}")`;
|
|
1009
|
+
return `new Long("${this.toString()}"${this.unsigned ? ', true' : ''})`;
|
|
996
1010
|
}
|
|
997
1011
|
}
|
|
998
1012
|
|
package/src/max_key.ts
CHANGED
|
@@ -10,6 +10,10 @@ export interface MaxKeyExtended {
|
|
|
10
10
|
export class MaxKey {
|
|
11
11
|
_bsontype!: 'MaxKey';
|
|
12
12
|
|
|
13
|
+
constructor() {
|
|
14
|
+
if (!(this instanceof MaxKey)) return new MaxKey();
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
/** @internal */
|
|
14
18
|
toExtendedJSON(): MaxKeyExtended {
|
|
15
19
|
return { $maxKey: 1 };
|
|
@@ -26,7 +30,7 @@ export class MaxKey {
|
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
inspect(): string {
|
|
29
|
-
return 'MaxKey()';
|
|
33
|
+
return 'new MaxKey()';
|
|
30
34
|
}
|
|
31
35
|
}
|
|
32
36
|
|
package/src/min_key.ts
CHANGED
|
@@ -10,6 +10,10 @@ export interface MinKeyExtended {
|
|
|
10
10
|
export class MinKey {
|
|
11
11
|
_bsontype!: 'MinKey';
|
|
12
12
|
|
|
13
|
+
constructor() {
|
|
14
|
+
if (!(this instanceof MinKey)) return new MinKey();
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
/** @internal */
|
|
14
18
|
toExtendedJSON(): MinKeyExtended {
|
|
15
19
|
return { $minKey: 1 };
|
|
@@ -26,7 +30,7 @@ export class MinKey {
|
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
inspect(): string {
|
|
29
|
-
return 'MinKey()';
|
|
33
|
+
return 'new MinKey()';
|
|
30
34
|
}
|
|
31
35
|
}
|
|
32
36
|
|
package/src/objectid.ts
CHANGED
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
2
|
import { ensureBuffer } from './ensure_buffer';
|
|
3
|
-
import { deprecate, randomBytes } from './parser/utils';
|
|
4
|
-
|
|
5
|
-
// constants
|
|
6
|
-
const PROCESS_UNIQUE = randomBytes(5);
|
|
3
|
+
import { deprecate, isUint8Array, randomBytes } from './parser/utils';
|
|
7
4
|
|
|
8
5
|
// Regular expression that checks for hex value
|
|
9
6
|
const checkForHexRegExp = new RegExp('^[0-9a-fA-F]{24}$');
|
|
10
7
|
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
for (let i = 0; i < 256; i++) {
|
|
14
|
-
hexTable[i] = (i <= 15 ? '0' : '') + i.toString(16);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Lookup tables
|
|
18
|
-
const decodeLookup: number[] = [];
|
|
19
|
-
let i = 0;
|
|
20
|
-
while (i < 10) decodeLookup[0x30 + i] = i++;
|
|
21
|
-
while (i < 16) decodeLookup[0x41 - 10 + i] = decodeLookup[0x61 - 10 + i] = i++;
|
|
8
|
+
// Unique sequence for the current process (initialized on first use)
|
|
9
|
+
let PROCESS_UNIQUE: Uint8Array | null = null;
|
|
22
10
|
|
|
23
11
|
/** @public */
|
|
24
12
|
export interface ObjectIdLike {
|
|
@@ -57,6 +45,8 @@ export class ObjectId {
|
|
|
57
45
|
* @param id - Can be a 24 character hex string, 12 byte binary Buffer, or a number.
|
|
58
46
|
*/
|
|
59
47
|
constructor(id?: string | Buffer | number | ObjectIdLike | ObjectId) {
|
|
48
|
+
if (!(this instanceof ObjectId)) return new ObjectId(id);
|
|
49
|
+
|
|
60
50
|
// Duck-typing to support ObjectId from different npm packages
|
|
61
51
|
if (id instanceof ObjectId) {
|
|
62
52
|
this[kId] = id.id;
|
|
@@ -174,6 +164,11 @@ export class ObjectId {
|
|
|
174
164
|
// 4-byte timestamp
|
|
175
165
|
buffer.writeUInt32BE(time, 0);
|
|
176
166
|
|
|
167
|
+
// set PROCESS_UNIQUE if yet not initialized
|
|
168
|
+
if (PROCESS_UNIQUE === null) {
|
|
169
|
+
PROCESS_UNIQUE = randomBytes(5);
|
|
170
|
+
}
|
|
171
|
+
|
|
177
172
|
// 5-byte process unique
|
|
178
173
|
buffer[4] = PROCESS_UNIQUE[0];
|
|
179
174
|
buffer[5] = PROCESS_UNIQUE[1];
|
|
@@ -227,9 +222,9 @@ export class ObjectId {
|
|
|
227
222
|
typeof otherId === 'string' &&
|
|
228
223
|
ObjectId.isValid(otherId) &&
|
|
229
224
|
otherId.length === 12 &&
|
|
230
|
-
this.id
|
|
225
|
+
isUint8Array(this.id)
|
|
231
226
|
) {
|
|
232
|
-
return otherId ===
|
|
227
|
+
return otherId === Buffer.prototype.toString.call(this.id, 'latin1');
|
|
233
228
|
}
|
|
234
229
|
|
|
235
230
|
if (typeof otherId === 'string' && ObjectId.isValid(otherId) && otherId.length === 24) {
|
|
@@ -298,7 +293,7 @@ export class ObjectId {
|
|
|
298
293
|
*
|
|
299
294
|
* @param id - ObjectId instance to validate.
|
|
300
295
|
*/
|
|
301
|
-
static isValid(id: number | string | ObjectId |
|
|
296
|
+
static isValid(id: number | string | ObjectId | Uint8Array | ObjectIdLike): boolean {
|
|
302
297
|
if (id == null) return false;
|
|
303
298
|
|
|
304
299
|
if (typeof id === 'number') {
|
|
@@ -313,7 +308,7 @@ export class ObjectId {
|
|
|
313
308
|
return true;
|
|
314
309
|
}
|
|
315
310
|
|
|
316
|
-
if (id
|
|
311
|
+
if (isUint8Array(id) && id.length === 12) {
|
|
317
312
|
return true;
|
|
318
313
|
}
|
|
319
314
|
|
|
@@ -350,7 +345,7 @@ export class ObjectId {
|
|
|
350
345
|
}
|
|
351
346
|
|
|
352
347
|
inspect(): string {
|
|
353
|
-
return `ObjectId("${this.toHexString()}")`;
|
|
348
|
+
return `new ObjectId("${this.toHexString()}")`;
|
|
354
349
|
}
|
|
355
350
|
}
|
|
356
351
|
|
|
@@ -2,7 +2,7 @@ import { Buffer } from 'buffer';
|
|
|
2
2
|
import { Binary } from '../binary';
|
|
3
3
|
import type { Document } from '../bson';
|
|
4
4
|
import * as constants from '../constants';
|
|
5
|
-
import { isDate, normalizedFunctionString } from './utils';
|
|
5
|
+
import { isAnyArrayBuffer, isDate, isRegExp, normalizedFunctionString } from './utils';
|
|
6
6
|
|
|
7
7
|
export function calculateObjectSize(
|
|
8
8
|
object: Document,
|
|
@@ -83,7 +83,11 @@ function calculateElement(
|
|
|
83
83
|
return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (12 + 1);
|
|
84
84
|
} else if (value instanceof Date || isDate(value)) {
|
|
85
85
|
return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (8 + 1);
|
|
86
|
-
} else if (
|
|
86
|
+
} else if (
|
|
87
|
+
ArrayBuffer.isView(value) ||
|
|
88
|
+
value instanceof ArrayBuffer ||
|
|
89
|
+
isAnyArrayBuffer(value)
|
|
90
|
+
) {
|
|
87
91
|
return (
|
|
88
92
|
(name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (1 + 4 + 1) + value.byteLength
|
|
89
93
|
);
|
|
@@ -156,10 +160,7 @@ function calculateElement(
|
|
|
156
160
|
1 +
|
|
157
161
|
calculateObjectSize(ordered_values, serializeFunctions, ignoreUndefined)
|
|
158
162
|
);
|
|
159
|
-
} else if (
|
|
160
|
-
value instanceof RegExp ||
|
|
161
|
-
Object.prototype.toString.call(value) === '[object RegExp]'
|
|
162
|
-
) {
|
|
163
|
+
} else if (value instanceof RegExp || isRegExp(value)) {
|
|
163
164
|
return (
|
|
164
165
|
(name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
|
|
165
166
|
1 +
|
|
@@ -188,11 +189,7 @@ function calculateElement(
|
|
|
188
189
|
}
|
|
189
190
|
case 'function':
|
|
190
191
|
// WTF for 0.4.X where typeof /someregexp/ === 'function'
|
|
191
|
-
if (
|
|
192
|
-
value instanceof RegExp ||
|
|
193
|
-
Object.prototype.toString.call(value) === '[object RegExp]' ||
|
|
194
|
-
String.call(value) === '[object RegExp]'
|
|
195
|
-
) {
|
|
192
|
+
if (value instanceof RegExp || isRegExp(value) || String.call(value) === '[object RegExp]') {
|
|
196
193
|
return (
|
|
197
194
|
(name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
|
|
198
195
|
1 +
|
|
@@ -152,6 +152,7 @@ function deserializeObject(
|
|
|
152
152
|
// If are at the end of the buffer there is a problem with the document
|
|
153
153
|
if (i >= buffer.byteLength) throw new Error('Bad BSON Document: illegal CString');
|
|
154
154
|
const name = isArray ? arrayIndex++ : buffer.toString('utf8', index, i);
|
|
155
|
+
let value;
|
|
155
156
|
|
|
156
157
|
index = i + 1;
|
|
157
158
|
|
|
@@ -172,30 +173,29 @@ function deserializeObject(
|
|
|
172
173
|
throw new Error('Invalid UTF-8 string in BSON document');
|
|
173
174
|
}
|
|
174
175
|
|
|
175
|
-
|
|
176
|
+
value = buffer.toString('utf8', index, index + stringSize - 1);
|
|
176
177
|
|
|
177
|
-
object[name] = s;
|
|
178
178
|
index = index + stringSize;
|
|
179
179
|
} else if (elementType === constants.BSON_DATA_OID) {
|
|
180
180
|
const oid = Buffer.alloc(12);
|
|
181
181
|
buffer.copy(oid, 0, index, index + 12);
|
|
182
|
-
|
|
182
|
+
value = new ObjectId(oid);
|
|
183
183
|
index = index + 12;
|
|
184
184
|
} else if (elementType === constants.BSON_DATA_INT && promoteValues === false) {
|
|
185
|
-
|
|
185
|
+
value = new Int32(
|
|
186
186
|
buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24)
|
|
187
187
|
);
|
|
188
188
|
} else if (elementType === constants.BSON_DATA_INT) {
|
|
189
|
-
|
|
189
|
+
value =
|
|
190
190
|
buffer[index++] |
|
|
191
191
|
(buffer[index++] << 8) |
|
|
192
192
|
(buffer[index++] << 16) |
|
|
193
193
|
(buffer[index++] << 24);
|
|
194
194
|
} else if (elementType === constants.BSON_DATA_NUMBER && promoteValues === false) {
|
|
195
|
-
|
|
195
|
+
value = new Double(buffer.readDoubleLE(index));
|
|
196
196
|
index = index + 8;
|
|
197
197
|
} else if (elementType === constants.BSON_DATA_NUMBER) {
|
|
198
|
-
|
|
198
|
+
value = buffer.readDoubleLE(index);
|
|
199
199
|
index = index + 8;
|
|
200
200
|
} else if (elementType === constants.BSON_DATA_DATE) {
|
|
201
201
|
const lowBits =
|
|
@@ -208,10 +208,10 @@ function deserializeObject(
|
|
|
208
208
|
(buffer[index++] << 8) |
|
|
209
209
|
(buffer[index++] << 16) |
|
|
210
210
|
(buffer[index++] << 24);
|
|
211
|
-
|
|
211
|
+
value = new Date(new Long(lowBits, highBits).toNumber());
|
|
212
212
|
} else if (elementType === constants.BSON_DATA_BOOLEAN) {
|
|
213
213
|
if (buffer[index] !== 0 && buffer[index] !== 1) throw new Error('illegal boolean type value');
|
|
214
|
-
|
|
214
|
+
value = buffer[index++] === 1;
|
|
215
215
|
} else if (elementType === constants.BSON_DATA_OBJECT) {
|
|
216
216
|
const _index = index;
|
|
217
217
|
const objectSize =
|
|
@@ -224,9 +224,9 @@ function deserializeObject(
|
|
|
224
224
|
|
|
225
225
|
// We have a raw value
|
|
226
226
|
if (raw) {
|
|
227
|
-
|
|
227
|
+
value = buffer.slice(index, index + objectSize);
|
|
228
228
|
} else {
|
|
229
|
-
|
|
229
|
+
value = deserializeObject(buffer, _index, options, false);
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
index = index + objectSize;
|
|
@@ -253,15 +253,15 @@ function deserializeObject(
|
|
|
253
253
|
arrayOptions['raw'] = true;
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
|
|
256
|
+
value = deserializeObject(buffer, _index, arrayOptions, true);
|
|
257
257
|
index = index + objectSize;
|
|
258
258
|
|
|
259
259
|
if (buffer[index - 1] !== 0) throw new Error('invalid array terminator byte');
|
|
260
260
|
if (index !== stopIndex) throw new Error('corrupted array bson');
|
|
261
261
|
} else if (elementType === constants.BSON_DATA_UNDEFINED) {
|
|
262
|
-
|
|
262
|
+
value = undefined;
|
|
263
263
|
} else if (elementType === constants.BSON_DATA_NULL) {
|
|
264
|
-
|
|
264
|
+
value = null;
|
|
265
265
|
} else if (elementType === constants.BSON_DATA_LONG) {
|
|
266
266
|
// Unpack the low and high bits
|
|
267
267
|
const lowBits =
|
|
@@ -277,12 +277,12 @@ function deserializeObject(
|
|
|
277
277
|
const long = new Long(lowBits, highBits);
|
|
278
278
|
// Promote the long if possible
|
|
279
279
|
if (promoteLongs && promoteValues === true) {
|
|
280
|
-
|
|
280
|
+
value =
|
|
281
281
|
long.lessThanOrEqual(JS_INT_MAX_LONG) && long.greaterThanOrEqual(JS_INT_MIN_LONG)
|
|
282
282
|
? long.toNumber()
|
|
283
283
|
: long;
|
|
284
284
|
} else {
|
|
285
|
-
|
|
285
|
+
value = long;
|
|
286
286
|
}
|
|
287
287
|
} else if (elementType === constants.BSON_DATA_DECIMAL128) {
|
|
288
288
|
// Buffer to contain the decimal bytes
|
|
@@ -295,9 +295,9 @@ function deserializeObject(
|
|
|
295
295
|
const decimal128 = new Decimal128(bytes) as Decimal128 | { toObject(): unknown };
|
|
296
296
|
// If we have an alternative mapper use that
|
|
297
297
|
if ('toObject' in decimal128 && typeof decimal128.toObject === 'function') {
|
|
298
|
-
|
|
298
|
+
value = decimal128.toObject();
|
|
299
299
|
} else {
|
|
300
|
-
|
|
300
|
+
value = decimal128;
|
|
301
301
|
}
|
|
302
302
|
} else if (elementType === constants.BSON_DATA_BINARY) {
|
|
303
303
|
let binarySize =
|
|
@@ -333,9 +333,9 @@ function deserializeObject(
|
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
if (promoteBuffers && promoteValues) {
|
|
336
|
-
|
|
336
|
+
value = buffer.slice(index, index + binarySize);
|
|
337
337
|
} else {
|
|
338
|
-
|
|
338
|
+
value = new Binary(buffer.slice(index, index + binarySize), subType);
|
|
339
339
|
}
|
|
340
340
|
} else {
|
|
341
341
|
const _buffer = Buffer.alloc(binarySize);
|
|
@@ -360,9 +360,9 @@ function deserializeObject(
|
|
|
360
360
|
}
|
|
361
361
|
|
|
362
362
|
if (promoteBuffers && promoteValues) {
|
|
363
|
-
|
|
363
|
+
value = _buffer;
|
|
364
364
|
} else {
|
|
365
|
-
|
|
365
|
+
value = new Binary(_buffer, subType);
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
368
|
|
|
@@ -412,7 +412,7 @@ function deserializeObject(
|
|
|
412
412
|
}
|
|
413
413
|
}
|
|
414
414
|
|
|
415
|
-
|
|
415
|
+
value = new RegExp(source, optionsArray.join(''));
|
|
416
416
|
} else if (elementType === constants.BSON_DATA_REGEXP && bsonRegExp === true) {
|
|
417
417
|
// Get the start search index
|
|
418
418
|
i = index;
|
|
@@ -439,7 +439,7 @@ function deserializeObject(
|
|
|
439
439
|
index = i + 1;
|
|
440
440
|
|
|
441
441
|
// Set the object
|
|
442
|
-
|
|
442
|
+
value = new BSONRegExp(source, regExpOptions);
|
|
443
443
|
} else if (elementType === constants.BSON_DATA_SYMBOL) {
|
|
444
444
|
const stringSize =
|
|
445
445
|
buffer[index++] |
|
|
@@ -453,7 +453,7 @@ function deserializeObject(
|
|
|
453
453
|
)
|
|
454
454
|
throw new Error('bad string length in bson');
|
|
455
455
|
const symbol = buffer.toString('utf8', index, index + stringSize - 1);
|
|
456
|
-
|
|
456
|
+
value = promoteValues ? symbol : new BSONSymbol(symbol);
|
|
457
457
|
index = index + stringSize;
|
|
458
458
|
} else if (elementType === constants.BSON_DATA_TIMESTAMP) {
|
|
459
459
|
const lowBits =
|
|
@@ -467,11 +467,11 @@ function deserializeObject(
|
|
|
467
467
|
(buffer[index++] << 16) |
|
|
468
468
|
(buffer[index++] << 24);
|
|
469
469
|
|
|
470
|
-
|
|
470
|
+
value = new Timestamp(lowBits, highBits);
|
|
471
471
|
} else if (elementType === constants.BSON_DATA_MIN_KEY) {
|
|
472
|
-
|
|
472
|
+
value = new MinKey();
|
|
473
473
|
} else if (elementType === constants.BSON_DATA_MAX_KEY) {
|
|
474
|
-
|
|
474
|
+
value = new MaxKey();
|
|
475
475
|
} else if (elementType === constants.BSON_DATA_CODE) {
|
|
476
476
|
const stringSize =
|
|
477
477
|
buffer[index++] |
|
|
@@ -491,12 +491,12 @@ function deserializeObject(
|
|
|
491
491
|
// If we have cache enabled let's look for the md5 of the function in the cache
|
|
492
492
|
if (cacheFunctions) {
|
|
493
493
|
// Got to do this to avoid V8 deoptimizing the call due to finding eval
|
|
494
|
-
|
|
494
|
+
value = isolateEval(functionString, functionCache, object);
|
|
495
495
|
} else {
|
|
496
|
-
|
|
496
|
+
value = isolateEval(functionString);
|
|
497
497
|
}
|
|
498
498
|
} else {
|
|
499
|
-
|
|
499
|
+
value = new Code(functionString);
|
|
500
500
|
}
|
|
501
501
|
|
|
502
502
|
// Update parse index position
|
|
@@ -559,14 +559,14 @@ function deserializeObject(
|
|
|
559
559
|
// If we have cache enabled let's look for the md5 of the function in the cache
|
|
560
560
|
if (cacheFunctions) {
|
|
561
561
|
// Got to do this to avoid V8 deoptimizing the call due to finding eval
|
|
562
|
-
|
|
562
|
+
value = isolateEval(functionString, functionCache, object);
|
|
563
563
|
} else {
|
|
564
|
-
|
|
564
|
+
value = isolateEval(functionString);
|
|
565
565
|
}
|
|
566
566
|
|
|
567
|
-
|
|
567
|
+
value.scope = scopeObject;
|
|
568
568
|
} else {
|
|
569
|
-
|
|
569
|
+
value = new Code(functionString, scopeObject);
|
|
570
570
|
}
|
|
571
571
|
} else if (elementType === constants.BSON_DATA_DBPOINTER) {
|
|
572
572
|
// Get the code string size
|
|
@@ -599,12 +599,22 @@ function deserializeObject(
|
|
|
599
599
|
index = index + 12;
|
|
600
600
|
|
|
601
601
|
// Upgrade to DBRef type
|
|
602
|
-
|
|
602
|
+
value = new DBRef(namespace, oid);
|
|
603
603
|
} else {
|
|
604
604
|
throw new Error(
|
|
605
605
|
'Detected unknown BSON type ' + elementType.toString(16) + ' for fieldname "' + name + '"'
|
|
606
606
|
);
|
|
607
607
|
}
|
|
608
|
+
if (name === '__proto__') {
|
|
609
|
+
Object.defineProperty(object, name, {
|
|
610
|
+
value,
|
|
611
|
+
writable: true,
|
|
612
|
+
enumerable: true,
|
|
613
|
+
configurable: true
|
|
614
|
+
});
|
|
615
|
+
} else {
|
|
616
|
+
object[name] = value;
|
|
617
|
+
}
|
|
608
618
|
}
|
|
609
619
|
|
|
610
620
|
// Check if the deserialization was against a valid array/object
|