bson 4.7.0 → 5.0.0-alpha.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/bson.d.ts +207 -259
- package/lib/bson.bundle.js +4033 -0
- package/lib/bson.bundle.js.map +1 -0
- package/lib/bson.cjs +4028 -0
- package/lib/bson.cjs.map +1 -0
- package/lib/bson.mjs +4002 -0
- package/lib/bson.mjs.map +1 -0
- package/package.json +49 -62
- package/src/binary.ts +63 -52
- package/src/bson.ts +27 -108
- package/src/code.ts +24 -14
- package/src/constants.ts +28 -0
- package/src/db_ref.ts +13 -8
- package/src/decimal128.ts +31 -25
- package/src/double.ts +7 -5
- package/src/error.ts +0 -2
- package/src/extended_json.ts +148 -148
- package/src/index.ts +19 -0
- package/src/int_32.ts +7 -5
- package/src/long.ts +16 -16
- package/src/max_key.ts +6 -6
- package/src/min_key.ts +6 -6
- package/src/objectid.ts +41 -74
- package/src/parser/calculate_size.ts +39 -63
- package/src/parser/deserializer.ts +41 -112
- package/src/parser/serializer.ts +234 -341
- package/src/parser/utils.ts +1 -99
- package/src/regexp.ts +16 -5
- package/src/symbol.ts +7 -5
- package/src/timestamp.ts +62 -27
- package/src/utils/byte_utils.ts +61 -0
- package/src/utils/node_byte_utils.ts +141 -0
- package/src/utils/web_byte_utils.ts +190 -0
- package/src/uuid_utils.ts +15 -15
- package/bower.json +0 -26
- package/dist/bson.browser.esm.js +0 -7470
- package/dist/bson.browser.esm.js.map +0 -1
- package/dist/bson.browser.umd.js +0 -7537
- package/dist/bson.browser.umd.js.map +0 -1
- package/dist/bson.bundle.js +0 -7536
- package/dist/bson.bundle.js.map +0 -1
- package/dist/bson.esm.js +0 -5436
- package/dist/bson.esm.js.map +0 -1
- package/lib/binary.js +0 -426
- package/lib/binary.js.map +0 -1
- package/lib/bson.js +0 -251
- package/lib/bson.js.map +0 -1
- package/lib/code.js +0 -46
- package/lib/code.js.map +0 -1
- package/lib/constants.js +0 -82
- package/lib/constants.js.map +0 -1
- package/lib/db_ref.js +0 -97
- package/lib/db_ref.js.map +0 -1
- package/lib/decimal128.js +0 -669
- package/lib/decimal128.js.map +0 -1
- package/lib/double.js +0 -76
- package/lib/double.js.map +0 -1
- package/lib/ensure_buffer.js +0 -25
- package/lib/ensure_buffer.js.map +0 -1
- package/lib/error.js +0 -55
- package/lib/error.js.map +0 -1
- package/lib/extended_json.js +0 -390
- package/lib/extended_json.js.map +0 -1
- package/lib/int_32.js +0 -58
- package/lib/int_32.js.map +0 -1
- package/lib/long.js +0 -900
- package/lib/long.js.map +0 -1
- package/lib/map.js +0 -123
- package/lib/map.js.map +0 -1
- package/lib/max_key.js +0 -33
- package/lib/max_key.js.map +0 -1
- package/lib/min_key.js +0 -33
- package/lib/min_key.js.map +0 -1
- package/lib/objectid.js +0 -299
- package/lib/objectid.js.map +0 -1
- package/lib/parser/calculate_size.js +0 -194
- package/lib/parser/calculate_size.js.map +0 -1
- package/lib/parser/deserializer.js +0 -665
- package/lib/parser/deserializer.js.map +0 -1
- package/lib/parser/serializer.js +0 -867
- package/lib/parser/serializer.js.map +0 -1
- package/lib/parser/utils.js +0 -115
- package/lib/parser/utils.js.map +0 -1
- package/lib/regexp.js +0 -74
- package/lib/regexp.js.map +0 -1
- package/lib/symbol.js +0 -48
- package/lib/symbol.js.map +0 -1
- package/lib/timestamp.js +0 -102
- package/lib/timestamp.js.map +0 -1
- package/lib/utils/global.js +0 -18
- package/lib/utils/global.js.map +0 -1
- package/lib/uuid_utils.js +0 -35
- package/lib/uuid_utils.js.map +0 -1
- package/lib/validate_utf8.js +0 -47
- package/lib/validate_utf8.js.map +0 -1
- package/src/ensure_buffer.ts +0 -27
- package/src/map.ts +0 -119
- package/src/utils/global.ts +0 -22
package/src/db_ref.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Document } from './bson';
|
|
2
2
|
import type { EJSONOptions } from './extended_json';
|
|
3
3
|
import type { ObjectId } from './objectid';
|
|
4
|
-
import { isObjectLike } from './parser/utils';
|
|
5
4
|
|
|
6
5
|
/** @public */
|
|
7
6
|
export interface DBRefLike {
|
|
@@ -13,10 +12,14 @@ export interface DBRefLike {
|
|
|
13
12
|
/** @internal */
|
|
14
13
|
export function isDBRefLike(value: unknown): value is DBRefLike {
|
|
15
14
|
return (
|
|
16
|
-
|
|
15
|
+
value != null &&
|
|
16
|
+
typeof value === 'object' &&
|
|
17
|
+
'$id' in value &&
|
|
17
18
|
value.$id != null &&
|
|
19
|
+
'$ref' in value &&
|
|
18
20
|
typeof value.$ref === 'string' &&
|
|
19
|
-
|
|
21
|
+
// If '$db' is defined it MUST be a string, otherwise it should be absent
|
|
22
|
+
(!('$db' in value) || ('$db' in value && typeof value.$db === 'string'))
|
|
20
23
|
);
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -26,7 +29,13 @@ export function isDBRefLike(value: unknown): value is DBRefLike {
|
|
|
26
29
|
* @category BSONType
|
|
27
30
|
*/
|
|
28
31
|
export class DBRef {
|
|
29
|
-
_bsontype
|
|
32
|
+
get _bsontype(): 'DBRef' {
|
|
33
|
+
return 'DBRef';
|
|
34
|
+
}
|
|
35
|
+
/** @internal */
|
|
36
|
+
get [Symbol.for('@@mdb.bson.version')](): 5 {
|
|
37
|
+
return 5;
|
|
38
|
+
}
|
|
30
39
|
|
|
31
40
|
collection!: string;
|
|
32
41
|
oid!: ObjectId;
|
|
@@ -39,8 +48,6 @@ export class DBRef {
|
|
|
39
48
|
* @param db - optional db name, if omitted the reference is local to the current db.
|
|
40
49
|
*/
|
|
41
50
|
constructor(collection: string, oid: ObjectId, db?: string, fields?: Document) {
|
|
42
|
-
if (!(this instanceof DBRef)) return new DBRef(collection, oid, db, fields);
|
|
43
|
-
|
|
44
51
|
// check if namespace has been provided
|
|
45
52
|
const parts = collection.split('.');
|
|
46
53
|
if (parts.length === 2) {
|
|
@@ -120,5 +127,3 @@ export class DBRef {
|
|
|
120
127
|
})`;
|
|
121
128
|
}
|
|
122
129
|
}
|
|
123
|
-
|
|
124
|
-
Object.defineProperty(DBRef.prototype, '_bsontype', { value: 'DBRef' });
|
package/src/decimal128.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Buffer } from 'buffer';
|
|
2
1
|
import { BSONTypeError } from './error';
|
|
3
2
|
import { Long } from './long';
|
|
4
3
|
import { isUint8Array } from './parser/utils';
|
|
4
|
+
import { ByteUtils } from './utils/byte_utils';
|
|
5
5
|
|
|
6
6
|
const PARSE_STRING_REGEXP = /^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/;
|
|
7
7
|
const PARSE_INF_REGEXP = /^(\+|-)?(Infinity|inf)$/i;
|
|
@@ -13,16 +13,22 @@ const EXPONENT_BIAS = 6176;
|
|
|
13
13
|
const MAX_DIGITS = 34;
|
|
14
14
|
|
|
15
15
|
// Nan value bits as 32 bit values (due to lack of longs)
|
|
16
|
-
const NAN_BUFFER =
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const NAN_BUFFER = ByteUtils.fromNumberArray(
|
|
17
|
+
[
|
|
18
|
+
0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
19
|
+
].reverse()
|
|
20
|
+
);
|
|
19
21
|
// Infinity value bits 32 bit values (due to lack of longs)
|
|
20
|
-
const INF_NEGATIVE_BUFFER =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const INF_NEGATIVE_BUFFER = ByteUtils.fromNumberArray(
|
|
23
|
+
[
|
|
24
|
+
0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
25
|
+
].reverse()
|
|
26
|
+
);
|
|
27
|
+
const INF_POSITIVE_BUFFER = ByteUtils.fromNumberArray(
|
|
28
|
+
[
|
|
29
|
+
0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
30
|
+
].reverse()
|
|
31
|
+
);
|
|
26
32
|
|
|
27
33
|
const EXPONENT_REGEX = /^([-+])?(\d+)?$/;
|
|
28
34
|
|
|
@@ -121,17 +127,21 @@ export interface Decimal128Extended {
|
|
|
121
127
|
* @category BSONType
|
|
122
128
|
*/
|
|
123
129
|
export class Decimal128 {
|
|
124
|
-
_bsontype
|
|
130
|
+
get _bsontype(): 'Decimal128' {
|
|
131
|
+
return 'Decimal128';
|
|
132
|
+
}
|
|
133
|
+
/** @internal */
|
|
134
|
+
get [Symbol.for('@@mdb.bson.version')](): 5 {
|
|
135
|
+
return 5;
|
|
136
|
+
}
|
|
125
137
|
|
|
126
|
-
readonly bytes!:
|
|
138
|
+
readonly bytes!: Uint8Array;
|
|
127
139
|
|
|
128
140
|
/**
|
|
129
141
|
* @param bytes - a buffer containing the raw Decimal128 bytes in little endian order,
|
|
130
142
|
* or a string representation as returned by .toString()
|
|
131
143
|
*/
|
|
132
|
-
constructor(bytes:
|
|
133
|
-
if (!(this instanceof Decimal128)) return new Decimal128(bytes);
|
|
134
|
-
|
|
144
|
+
constructor(bytes: Uint8Array | string) {
|
|
135
145
|
if (typeof bytes === 'string') {
|
|
136
146
|
this.bytes = Decimal128.fromString(bytes).bytes;
|
|
137
147
|
} else if (isUint8Array(bytes)) {
|
|
@@ -239,9 +249,9 @@ export class Decimal128 {
|
|
|
239
249
|
// Check if user passed Infinity or NaN
|
|
240
250
|
if (!isDigit(representation[index]) && representation[index] !== '.') {
|
|
241
251
|
if (representation[index] === 'i' || representation[index] === 'I') {
|
|
242
|
-
return new Decimal128(
|
|
252
|
+
return new Decimal128(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER);
|
|
243
253
|
} else if (representation[index] === 'N') {
|
|
244
|
-
return new Decimal128(
|
|
254
|
+
return new Decimal128(NAN_BUFFER);
|
|
245
255
|
}
|
|
246
256
|
}
|
|
247
257
|
|
|
@@ -285,7 +295,7 @@ export class Decimal128 {
|
|
|
285
295
|
const match = representation.substr(++index).match(EXPONENT_REGEX);
|
|
286
296
|
|
|
287
297
|
// No digits read
|
|
288
|
-
if (!match || !match[2]) return new Decimal128(
|
|
298
|
+
if (!match || !match[2]) return new Decimal128(NAN_BUFFER);
|
|
289
299
|
|
|
290
300
|
// Get exponent
|
|
291
301
|
exponent = parseInt(match[0], 10);
|
|
@@ -295,7 +305,7 @@ export class Decimal128 {
|
|
|
295
305
|
}
|
|
296
306
|
|
|
297
307
|
// Return not a number
|
|
298
|
-
if (representation[index]) return new Decimal128(
|
|
308
|
+
if (representation[index]) return new Decimal128(NAN_BUFFER);
|
|
299
309
|
|
|
300
310
|
// Done reading input
|
|
301
311
|
// Find first non-zero digit in digits
|
|
@@ -423,9 +433,7 @@ export class Decimal128 {
|
|
|
423
433
|
exponent = exponent + 1;
|
|
424
434
|
digits[dIdx] = 1;
|
|
425
435
|
} else {
|
|
426
|
-
return new Decimal128(
|
|
427
|
-
Buffer.from(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER)
|
|
428
|
-
);
|
|
436
|
+
return new Decimal128(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER);
|
|
429
437
|
}
|
|
430
438
|
}
|
|
431
439
|
}
|
|
@@ -503,7 +511,7 @@ export class Decimal128 {
|
|
|
503
511
|
}
|
|
504
512
|
|
|
505
513
|
// Encode into a buffer
|
|
506
|
-
const buffer =
|
|
514
|
+
const buffer = ByteUtils.allocate(16);
|
|
507
515
|
index = 0;
|
|
508
516
|
|
|
509
517
|
// Encode the low 64 bits of the decimal
|
|
@@ -769,5 +777,3 @@ export class Decimal128 {
|
|
|
769
777
|
return `new Decimal128("${this.toString()}")`;
|
|
770
778
|
}
|
|
771
779
|
}
|
|
772
|
-
|
|
773
|
-
Object.defineProperty(Decimal128.prototype, '_bsontype', { value: 'Decimal128' });
|
package/src/double.ts
CHANGED
|
@@ -11,7 +11,13 @@ export interface DoubleExtended {
|
|
|
11
11
|
* @category BSONType
|
|
12
12
|
*/
|
|
13
13
|
export class Double {
|
|
14
|
-
_bsontype
|
|
14
|
+
get _bsontype(): 'Double' {
|
|
15
|
+
return 'Double';
|
|
16
|
+
}
|
|
17
|
+
/** @internal */
|
|
18
|
+
get [Symbol.for('@@mdb.bson.version')](): 5 {
|
|
19
|
+
return 5;
|
|
20
|
+
}
|
|
15
21
|
|
|
16
22
|
value!: number;
|
|
17
23
|
/**
|
|
@@ -20,8 +26,6 @@ export class Double {
|
|
|
20
26
|
* @param value - the number we want to represent as a double.
|
|
21
27
|
*/
|
|
22
28
|
constructor(value: number) {
|
|
23
|
-
if (!(this instanceof Double)) return new Double(value);
|
|
24
|
-
|
|
25
29
|
if ((value as unknown) instanceof Number) {
|
|
26
30
|
value = value.valueOf();
|
|
27
31
|
}
|
|
@@ -87,5 +91,3 @@ export class Double {
|
|
|
87
91
|
return `new Double(${eJSON.$numberDouble})`;
|
|
88
92
|
}
|
|
89
93
|
}
|
|
90
|
-
|
|
91
|
-
Object.defineProperty(Double.prototype, '_bsontype', { value: 'Double' });
|
package/src/error.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
export class BSONError extends Error {
|
|
3
3
|
constructor(message: string) {
|
|
4
4
|
super(message);
|
|
5
|
-
Object.setPrototypeOf(this, BSONError.prototype);
|
|
6
5
|
}
|
|
7
6
|
|
|
8
7
|
get name(): string {
|
|
@@ -14,7 +13,6 @@ export class BSONError extends Error {
|
|
|
14
13
|
export class BSONTypeError extends TypeError {
|
|
15
14
|
constructor(message: string) {
|
|
16
15
|
super(message);
|
|
17
|
-
Object.setPrototypeOf(this, BSONTypeError.prototype);
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
get name(): string {
|
package/src/extended_json.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Binary } from './binary';
|
|
2
2
|
import type { Document } from './bson';
|
|
3
3
|
import { Code } from './code';
|
|
4
|
+
import { BSON_INT32_MAX, BSON_INT32_MIN, BSON_INT64_MAX, BSON_INT64_MIN } from './constants';
|
|
4
5
|
import { DBRef, isDBRefLike } from './db_ref';
|
|
5
6
|
import { Decimal128 } from './decimal128';
|
|
6
7
|
import { Double } from './double';
|
|
@@ -10,13 +11,18 @@ import { Long } from './long';
|
|
|
10
11
|
import { MaxKey } from './max_key';
|
|
11
12
|
import { MinKey } from './min_key';
|
|
12
13
|
import { ObjectId } from './objectid';
|
|
13
|
-
import { isDate,
|
|
14
|
+
import { isDate, isRegExp } from './parser/utils';
|
|
14
15
|
import { BSONRegExp } from './regexp';
|
|
15
16
|
import { BSONSymbol } from './symbol';
|
|
16
17
|
import { Timestamp } from './timestamp';
|
|
17
18
|
|
|
18
19
|
/** @public */
|
|
19
|
-
export type EJSONOptions =
|
|
20
|
+
export type EJSONOptions = {
|
|
21
|
+
/** Output using the Extended JSON v1 spec */
|
|
22
|
+
legacy?: boolean;
|
|
23
|
+
/** Enable Extended JSON's `relaxed` mode, which attempts to return native JS types where possible, rather than BSON types */
|
|
24
|
+
relaxed?: boolean;
|
|
25
|
+
};
|
|
20
26
|
|
|
21
27
|
/** @internal */
|
|
22
28
|
type BSONType =
|
|
@@ -34,20 +40,15 @@ type BSONType =
|
|
|
34
40
|
| BSONSymbol
|
|
35
41
|
| Timestamp;
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
function isBSONType(value: unknown): value is BSONType {
|
|
38
44
|
return (
|
|
39
|
-
|
|
45
|
+
value != null &&
|
|
46
|
+
typeof value === 'object' &&
|
|
47
|
+
'_bsontype' in value &&
|
|
48
|
+
typeof value._bsontype === 'string'
|
|
40
49
|
);
|
|
41
50
|
}
|
|
42
51
|
|
|
43
|
-
// INT32 boundaries
|
|
44
|
-
const BSON_INT32_MAX = 0x7fffffff;
|
|
45
|
-
const BSON_INT32_MIN = -0x80000000;
|
|
46
|
-
// INT64 boundaries
|
|
47
|
-
// const BSON_INT64_MAX = 0x7fffffffffffffff; // TODO(NODE-4377): This number cannot be precisely represented in JS
|
|
48
|
-
const BSON_INT64_MAX = 0x8000000000000000;
|
|
49
|
-
const BSON_INT64_MIN = -0x8000000000000000;
|
|
50
|
-
|
|
51
52
|
// all the types where we don't need to do any special processing and can just pass the EJSON
|
|
52
53
|
//straight to type.fromExtendedJSON
|
|
53
54
|
const keysToCodecs = {
|
|
@@ -67,17 +68,21 @@ const keysToCodecs = {
|
|
|
67
68
|
} as const;
|
|
68
69
|
|
|
69
70
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
-
function deserializeValue(value: any, options:
|
|
71
|
+
function deserializeValue(value: any, options: EJSONOptions = {}) {
|
|
71
72
|
if (typeof value === 'number') {
|
|
72
73
|
if (options.relaxed || options.legacy) {
|
|
73
74
|
return value;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
if (Number.isInteger(value) && !Object.is(value, -0)) {
|
|
78
|
+
// interpret as being of the smallest BSON integer type that can represent the number exactly
|
|
79
|
+
if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
|
|
80
|
+
return new Int32(value);
|
|
81
|
+
}
|
|
82
|
+
if (value >= BSON_INT64_MIN && value <= BSON_INT64_MAX) {
|
|
83
|
+
// TODO(NODE-4377): EJSON js number handling diverges from BSON
|
|
84
|
+
return Long.fromNumber(value);
|
|
85
|
+
}
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
// If the number is a non-integer or out of integer range, should interpret as BSON Double.
|
|
@@ -142,7 +147,7 @@ function deserializeValue(value: any, options: EJSON.Options = {}) {
|
|
|
142
147
|
return value;
|
|
143
148
|
}
|
|
144
149
|
|
|
145
|
-
type EJSONSerializeOptions =
|
|
150
|
+
type EJSONSerializeOptions = EJSONOptions & {
|
|
146
151
|
seenObjects: { obj: unknown; propertyName: string }[];
|
|
147
152
|
};
|
|
148
153
|
|
|
@@ -216,16 +221,17 @@ function serializeValue(value: any, options: EJSONSerializeOptions): any {
|
|
|
216
221
|
}
|
|
217
222
|
|
|
218
223
|
if (typeof value === 'number' && (!options.relaxed || !isFinite(value))) {
|
|
219
|
-
|
|
220
|
-
if (Math.floor(value) === value) {
|
|
221
|
-
const int32Range = value >= BSON_INT32_MIN && value <= BSON_INT32_MAX,
|
|
222
|
-
int64Range = value >= BSON_INT64_MIN && value <= BSON_INT64_MAX;
|
|
223
|
-
|
|
224
|
+
if (Number.isInteger(value) && !Object.is(value, -0)) {
|
|
224
225
|
// interpret as being of the smallest BSON integer type that can represent the number exactly
|
|
225
|
-
if (
|
|
226
|
-
|
|
226
|
+
if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
|
|
227
|
+
return { $numberInt: value.toString() };
|
|
228
|
+
}
|
|
229
|
+
if (value >= BSON_INT64_MIN && value <= BSON_INT64_MAX) {
|
|
230
|
+
// TODO(NODE-4377): EJSON js number handling diverges from BSON
|
|
231
|
+
return { $numberLong: value.toString() };
|
|
232
|
+
}
|
|
227
233
|
}
|
|
228
|
-
return { $numberDouble: value.toString() };
|
|
234
|
+
return { $numberDouble: Object.is(value, -0) ? '-0.0' : value.toString() };
|
|
229
235
|
}
|
|
230
236
|
|
|
231
237
|
if (value instanceof RegExp || isRegExp(value)) {
|
|
@@ -267,10 +273,9 @@ const BSON_TYPE_MAPPINGS = {
|
|
|
267
273
|
),
|
|
268
274
|
MaxKey: () => new MaxKey(),
|
|
269
275
|
MinKey: () => new MinKey(),
|
|
270
|
-
|
|
271
|
-
ObjectId: (o: ObjectId) => new ObjectId(o), // support 4.0.0/4.0.1 before _bsontype was reverted back to ObjectID
|
|
276
|
+
ObjectId: (o: ObjectId) => new ObjectId(o),
|
|
272
277
|
BSONRegExp: (o: BSONRegExp) => new BSONRegExp(o.pattern, o.options),
|
|
273
|
-
|
|
278
|
+
BSONSymbol: (o: BSONSymbol) => new BSONSymbol(o.value),
|
|
274
279
|
Timestamp: (o: Timestamp) => Timestamp.fromBits(o.low, o.high)
|
|
275
280
|
} as const;
|
|
276
281
|
|
|
@@ -282,7 +287,7 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) {
|
|
|
282
287
|
if (typeof bsontype === 'undefined') {
|
|
283
288
|
// It's a regular object. Recursively serialize its property values.
|
|
284
289
|
const _doc: Document = {};
|
|
285
|
-
for (const name
|
|
290
|
+
for (const name of Object.keys(doc)) {
|
|
286
291
|
options.seenObjects.push({ propertyName: name, obj: null });
|
|
287
292
|
try {
|
|
288
293
|
const value = serializeValue(doc[name], options);
|
|
@@ -301,6 +306,13 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) {
|
|
|
301
306
|
}
|
|
302
307
|
}
|
|
303
308
|
return _doc;
|
|
309
|
+
} else if (
|
|
310
|
+
doc != null &&
|
|
311
|
+
typeof doc === 'object' &&
|
|
312
|
+
typeof doc._bsontype === 'string' &&
|
|
313
|
+
doc[Symbol.for('@@mdb.bson.version')] == null
|
|
314
|
+
) {
|
|
315
|
+
throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later');
|
|
304
316
|
} else if (isBSONType(doc)) {
|
|
305
317
|
// the "document" is really just a BSON type object
|
|
306
318
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -336,127 +348,115 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) {
|
|
|
336
348
|
}
|
|
337
349
|
|
|
338
350
|
/**
|
|
339
|
-
*
|
|
340
|
-
*
|
|
351
|
+
* Parse an Extended JSON string, constructing the JavaScript value or object described by that
|
|
352
|
+
* string.
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* ```js
|
|
356
|
+
* const { EJSON } = require('bson');
|
|
357
|
+
* const text = '{ "int32": { "$numberInt": "10" } }';
|
|
358
|
+
*
|
|
359
|
+
* // prints { int32: { [String: '10'] _bsontype: 'Int32', value: '10' } }
|
|
360
|
+
* console.log(EJSON.parse(text, { relaxed: false }));
|
|
361
|
+
*
|
|
362
|
+
* // prints { int32: 10 }
|
|
363
|
+
* console.log(EJSON.parse(text));
|
|
364
|
+
* ```
|
|
341
365
|
*/
|
|
342
|
-
//
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
/** Enable Extended JSON's `relaxed` mode, which attempts to return native JS types where possible, rather than BSON types */
|
|
350
|
-
relaxed?: boolean;
|
|
351
|
-
/**
|
|
352
|
-
* Disable Extended JSON's `relaxed` mode, which attempts to return BSON types where possible, rather than native JS types
|
|
353
|
-
* @deprecated Please use the relaxed property instead
|
|
354
|
-
*/
|
|
355
|
-
strict?: boolean;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Parse an Extended JSON string, constructing the JavaScript value or object described by that
|
|
360
|
-
* string.
|
|
361
|
-
*
|
|
362
|
-
* @example
|
|
363
|
-
* ```js
|
|
364
|
-
* const { EJSON } = require('bson');
|
|
365
|
-
* const text = '{ "int32": { "$numberInt": "10" } }';
|
|
366
|
-
*
|
|
367
|
-
* // prints { int32: { [String: '10'] _bsontype: 'Int32', value: '10' } }
|
|
368
|
-
* console.log(EJSON.parse(text, { relaxed: false }));
|
|
369
|
-
*
|
|
370
|
-
* // prints { int32: 10 }
|
|
371
|
-
* console.log(EJSON.parse(text));
|
|
372
|
-
* ```
|
|
373
|
-
*/
|
|
374
|
-
export function parse(text: string, options?: EJSON.Options): SerializableTypes {
|
|
375
|
-
const finalOptions = Object.assign({}, { relaxed: true, legacy: false }, options);
|
|
376
|
-
|
|
377
|
-
// relaxed implies not strict
|
|
378
|
-
if (typeof finalOptions.relaxed === 'boolean') finalOptions.strict = !finalOptions.relaxed;
|
|
379
|
-
if (typeof finalOptions.strict === 'boolean') finalOptions.relaxed = !finalOptions.strict;
|
|
380
|
-
|
|
381
|
-
return JSON.parse(text, (key, value) => {
|
|
382
|
-
if (key.indexOf('\x00') !== -1) {
|
|
383
|
-
throw new BSONError(
|
|
384
|
-
`BSON Document field names cannot contain null bytes, found: ${JSON.stringify(key)}`
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
return deserializeValue(value, finalOptions);
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
export type JSONPrimitive = string | number | boolean | null;
|
|
392
|
-
export type SerializableTypes = Document | Array<JSONPrimitive | Document> | JSONPrimitive;
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Converts a BSON document to an Extended JSON string, optionally replacing values if a replacer
|
|
396
|
-
* function is specified or optionally including only the specified properties if a replacer array
|
|
397
|
-
* is specified.
|
|
398
|
-
*
|
|
399
|
-
* @param value - The value to convert to extended JSON
|
|
400
|
-
* @param replacer - A function that alters the behavior of the stringification process, or an array of String and Number objects that serve as a whitelist for selecting/filtering the properties of the value object to be included in the JSON string. If this value is null or not provided, all properties of the object are included in the resulting JSON string
|
|
401
|
-
* @param space - A String or Number object that's used to insert white space into the output JSON string for readability purposes.
|
|
402
|
-
* @param options - Optional settings
|
|
403
|
-
*
|
|
404
|
-
* @example
|
|
405
|
-
* ```js
|
|
406
|
-
* const { EJSON } = require('bson');
|
|
407
|
-
* const Int32 = require('mongodb').Int32;
|
|
408
|
-
* const doc = { int32: new Int32(10) };
|
|
409
|
-
*
|
|
410
|
-
* // prints '{"int32":{"$numberInt":"10"}}'
|
|
411
|
-
* console.log(EJSON.stringify(doc, { relaxed: false }));
|
|
412
|
-
*
|
|
413
|
-
* // prints '{"int32":10}'
|
|
414
|
-
* console.log(EJSON.stringify(doc));
|
|
415
|
-
* ```
|
|
416
|
-
*/
|
|
417
|
-
export function stringify(
|
|
418
|
-
value: SerializableTypes,
|
|
419
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
420
|
-
replacer?: (number | string)[] | ((this: any, key: string, value: any) => any) | EJSON.Options,
|
|
421
|
-
space?: string | number,
|
|
422
|
-
options?: EJSON.Options
|
|
423
|
-
): string {
|
|
424
|
-
if (space != null && typeof space === 'object') {
|
|
425
|
-
options = space;
|
|
426
|
-
space = 0;
|
|
427
|
-
}
|
|
428
|
-
if (replacer != null && typeof replacer === 'object' && !Array.isArray(replacer)) {
|
|
429
|
-
options = replacer;
|
|
430
|
-
replacer = undefined;
|
|
431
|
-
space = 0;
|
|
366
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
367
|
+
function parse(text: string, options?: EJSONOptions): any {
|
|
368
|
+
return JSON.parse(text, (key, value) => {
|
|
369
|
+
if (key.indexOf('\x00') !== -1) {
|
|
370
|
+
throw new BSONError(
|
|
371
|
+
`BSON Document field names cannot contain null bytes, found: ${JSON.stringify(key)}`
|
|
372
|
+
);
|
|
432
373
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
374
|
+
return deserializeValue(value, { relaxed: true, legacy: false, ...options });
|
|
375
|
+
});
|
|
376
|
+
}
|
|
436
377
|
|
|
437
|
-
|
|
438
|
-
|
|
378
|
+
/**
|
|
379
|
+
* Converts a BSON document to an Extended JSON string, optionally replacing values if a replacer
|
|
380
|
+
* function is specified or optionally including only the specified properties if a replacer array
|
|
381
|
+
* is specified.
|
|
382
|
+
*
|
|
383
|
+
* @param value - The value to convert to extended JSON
|
|
384
|
+
* @param replacer - A function that alters the behavior of the stringification process, or an array of String and Number objects that serve as a whitelist for selecting/filtering the properties of the value object to be included in the JSON string. If this value is null or not provided, all properties of the object are included in the resulting JSON string
|
|
385
|
+
* @param space - A String or Number object that's used to insert white space into the output JSON string for readability purposes.
|
|
386
|
+
* @param options - Optional settings
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```js
|
|
390
|
+
* const { EJSON } = require('bson');
|
|
391
|
+
* const Int32 = require('mongodb').Int32;
|
|
392
|
+
* const doc = { int32: new Int32(10) };
|
|
393
|
+
*
|
|
394
|
+
* // prints '{"int32":{"$numberInt":"10"}}'
|
|
395
|
+
* console.log(EJSON.stringify(doc, { relaxed: false }));
|
|
396
|
+
*
|
|
397
|
+
* // prints '{"int32":10}'
|
|
398
|
+
* console.log(EJSON.stringify(doc));
|
|
399
|
+
* ```
|
|
400
|
+
*/
|
|
401
|
+
function stringify(
|
|
402
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
403
|
+
value: any,
|
|
404
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
405
|
+
replacer?: (number | string)[] | ((this: any, key: string, value: any) => any) | EJSONOptions,
|
|
406
|
+
space?: string | number,
|
|
407
|
+
options?: EJSONOptions
|
|
408
|
+
): string {
|
|
409
|
+
if (space != null && typeof space === 'object') {
|
|
410
|
+
options = space;
|
|
411
|
+
space = 0;
|
|
439
412
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
* @param value - The object to serialize
|
|
445
|
-
* @param options - Optional settings passed to the `stringify` function
|
|
446
|
-
*/
|
|
447
|
-
export function serialize(value: SerializableTypes, options?: EJSON.Options): Document {
|
|
448
|
-
options = options || {};
|
|
449
|
-
return JSON.parse(stringify(value, options));
|
|
413
|
+
if (replacer != null && typeof replacer === 'object' && !Array.isArray(replacer)) {
|
|
414
|
+
options = replacer;
|
|
415
|
+
replacer = undefined;
|
|
416
|
+
space = 0;
|
|
450
417
|
}
|
|
418
|
+
const serializeOptions = Object.assign({ relaxed: true, legacy: false }, options, {
|
|
419
|
+
seenObjects: [{ propertyName: '(root)', obj: null }]
|
|
420
|
+
});
|
|
451
421
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
*
|
|
455
|
-
* @param ejson - The Extended JSON object to deserialize
|
|
456
|
-
* @param options - Optional settings passed to the parse method
|
|
457
|
-
*/
|
|
458
|
-
export function deserialize(ejson: Document, options?: EJSON.Options): SerializableTypes {
|
|
459
|
-
options = options || {};
|
|
460
|
-
return parse(JSON.stringify(ejson), options);
|
|
461
|
-
}
|
|
422
|
+
const doc = serializeValue(value, serializeOptions);
|
|
423
|
+
return JSON.stringify(doc, replacer as Parameters<JSON['stringify']>[1], space);
|
|
462
424
|
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Serializes an object to an Extended JSON string, and reparse it as a JavaScript object.
|
|
428
|
+
*
|
|
429
|
+
* @param value - The object to serialize
|
|
430
|
+
* @param options - Optional settings passed to the `stringify` function
|
|
431
|
+
*/
|
|
432
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
433
|
+
function EJSONserialize(value: any, options?: EJSONOptions): Document {
|
|
434
|
+
options = options || {};
|
|
435
|
+
return JSON.parse(stringify(value, options));
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Deserializes an Extended JSON object into a plain JavaScript object with native/BSON types
|
|
440
|
+
*
|
|
441
|
+
* @param ejson - The Extended JSON object to deserialize
|
|
442
|
+
* @param options - Optional settings passed to the parse method
|
|
443
|
+
*/
|
|
444
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
445
|
+
function EJSONdeserialize(ejson: Document, options?: EJSONOptions): any {
|
|
446
|
+
options = options || {};
|
|
447
|
+
return parse(JSON.stringify(ejson), options);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** @public */
|
|
451
|
+
const EJSON: {
|
|
452
|
+
parse: typeof parse;
|
|
453
|
+
stringify: typeof stringify;
|
|
454
|
+
serialize: typeof EJSONserialize;
|
|
455
|
+
deserialize: typeof EJSONdeserialize;
|
|
456
|
+
} = Object.create(null);
|
|
457
|
+
EJSON.parse = parse;
|
|
458
|
+
EJSON.stringify = stringify;
|
|
459
|
+
EJSON.serialize = EJSONserialize;
|
|
460
|
+
EJSON.deserialize = EJSONdeserialize;
|
|
461
|
+
Object.freeze(EJSON);
|
|
462
|
+
export { EJSON };
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as BSON from './bson';
|
|
2
|
+
|
|
3
|
+
// Export all named properties from BSON to support
|
|
4
|
+
// import { ObjectId, serialize } from 'bson';
|
|
5
|
+
// const { ObjectId, serialize } = require('bson');
|
|
6
|
+
export * from './bson';
|
|
7
|
+
|
|
8
|
+
// Export BSON as a namespace to support:
|
|
9
|
+
// import { BSON } from 'bson';
|
|
10
|
+
// const { BSON } = require('bson');
|
|
11
|
+
export { BSON };
|
|
12
|
+
|
|
13
|
+
// BSON does **NOT** have a default export
|
|
14
|
+
|
|
15
|
+
// The following will crash in es module environments
|
|
16
|
+
// import BSON from 'bson';
|
|
17
|
+
|
|
18
|
+
// The following will work as expected, BSON as a namespace of all the APIs (BSON.ObjectId, BSON.serialize)
|
|
19
|
+
// const BSON = require('bson');
|
package/src/int_32.ts
CHANGED
|
@@ -11,7 +11,13 @@ export interface Int32Extended {
|
|
|
11
11
|
* @category BSONType
|
|
12
12
|
*/
|
|
13
13
|
export class Int32 {
|
|
14
|
-
_bsontype
|
|
14
|
+
get _bsontype(): 'Int32' {
|
|
15
|
+
return 'Int32';
|
|
16
|
+
}
|
|
17
|
+
/** @internal */
|
|
18
|
+
get [Symbol.for('@@mdb.bson.version')](): 5 {
|
|
19
|
+
return 5;
|
|
20
|
+
}
|
|
15
21
|
|
|
16
22
|
value!: number;
|
|
17
23
|
/**
|
|
@@ -20,8 +26,6 @@ export class Int32 {
|
|
|
20
26
|
* @param value - the number we want to represent as an int32.
|
|
21
27
|
*/
|
|
22
28
|
constructor(value: number | string) {
|
|
23
|
-
if (!(this instanceof Int32)) return new Int32(value);
|
|
24
|
-
|
|
25
29
|
if ((value as unknown) instanceof Number) {
|
|
26
30
|
value = value.valueOf();
|
|
27
31
|
}
|
|
@@ -66,5 +70,3 @@ export class Int32 {
|
|
|
66
70
|
return `new Int32(${this.valueOf()})`;
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
|
-
|
|
70
|
-
Object.defineProperty(Int32.prototype, '_bsontype', { value: 'Int32' });
|