bson 6.5.0 → 6.6.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/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "vendor"
15
15
  ],
16
16
  "types": "bson.d.ts",
17
- "version": "6.5.0",
17
+ "version": "6.6.0",
18
18
  "author": {
19
19
  "name": "The MongoDB NodeJS Team",
20
20
  "email": "dbx-node@mongodb.com"
package/src/binary.ts CHANGED
@@ -186,15 +186,15 @@ export class Binary extends BSONValue {
186
186
  }
187
187
 
188
188
  toJSON(): string {
189
- return ByteUtils.toBase64(this.buffer);
189
+ return ByteUtils.toBase64(this.buffer.subarray(0, this.position));
190
190
  }
191
191
 
192
192
  toString(encoding?: 'hex' | 'base64' | 'utf8' | 'utf-8'): string {
193
- if (encoding === 'hex') return ByteUtils.toHex(this.buffer);
194
- if (encoding === 'base64') return ByteUtils.toBase64(this.buffer);
193
+ if (encoding === 'hex') return ByteUtils.toHex(this.buffer.subarray(0, this.position));
194
+ if (encoding === 'base64') return ByteUtils.toBase64(this.buffer.subarray(0, this.position));
195
195
  if (encoding === 'utf8' || encoding === 'utf-8')
196
- return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength, false);
197
- return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength, false);
196
+ return ByteUtils.toUTF8(this.buffer, 0, this.position, false);
197
+ return ByteUtils.toUTF8(this.buffer, 0, this.position, false);
198
198
  }
199
199
 
200
200
  /** @internal */
package/src/bson.ts CHANGED
@@ -51,10 +51,10 @@ export {
51
51
  Decimal128
52
52
  };
53
53
  export { BSONValue } from './bson_value';
54
- export { BSONError, BSONVersionError, BSONRuntimeError } from './error';
54
+ export { BSONError, BSONVersionError, BSONRuntimeError, BSONOffsetError } from './error';
55
55
  export { BSONType } from './constants';
56
56
  export { EJSON } from './extended_json';
57
- export { onDemand } from './parser/on_demand/index';
57
+ export { onDemand, type OnDemand } from './parser/on_demand/index';
58
58
 
59
59
  /** @public */
60
60
  export interface Document {
package/src/error.ts CHANGED
@@ -98,8 +98,8 @@ export class BSONOffsetError extends BSONError {
98
98
 
99
99
  public offset: number;
100
100
 
101
- constructor(message: string, offset: number) {
102
- super(`${message}. offset: ${offset}`);
101
+ constructor(message: string, offset: number, options?: { cause?: unknown }) {
102
+ super(`${message}. offset: ${offset}`, options);
103
103
  this.offset = offset;
104
104
  }
105
105
  }
@@ -1,4 +1,5 @@
1
- import { type BSONError, BSONOffsetError } from '../../error';
1
+ import { ByteUtils } from '../../utils/byte_utils';
2
+ import { NumberUtils } from '../../utils/number_utils';
2
3
  import { type BSONElement, parseToElements } from './parse_to_elements';
3
4
  /**
4
5
  * @experimental
@@ -7,11 +8,13 @@ import { type BSONElement, parseToElements } from './parse_to_elements';
7
8
  * A new set of BSON APIs that are currently experimental and not intended for production use.
8
9
  */
9
10
  export type OnDemand = {
10
- BSONOffsetError: {
11
- new (message: string, offset: number): BSONOffsetError;
12
- isBSONError(value: unknown): value is BSONError;
13
- };
14
11
  parseToElements: (this: void, bytes: Uint8Array, startOffset?: number) => Iterable<BSONElement>;
12
+ // Types
13
+ BSONElement: BSONElement;
14
+
15
+ // Utils
16
+ ByteUtils: ByteUtils;
17
+ NumberUtils: NumberUtils;
15
18
  };
16
19
 
17
20
  /**
@@ -21,7 +24,8 @@ export type OnDemand = {
21
24
  const onDemand: OnDemand = Object.create(null);
22
25
 
23
26
  onDemand.parseToElements = parseToElements;
24
- onDemand.BSONOffsetError = BSONOffsetError;
27
+ onDemand.ByteUtils = ByteUtils;
28
+ onDemand.NumberUtils = NumberUtils;
25
29
 
26
30
  Object.freeze(onDemand);
27
31
 
@@ -1,5 +1,5 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-enum-comparison */
2
1
  import { BSONOffsetError } from '../../error';
2
+ import { NumberUtils } from '../../utils/number_utils';
3
3
 
4
4
  /**
5
5
  * @internal
@@ -9,7 +9,7 @@ import { BSONOffsetError } from '../../error';
9
9
  * - `minKey` is set to 255 so unsigned comparisons succeed
10
10
  * - Modify with caution, double check the bundle contains literals
11
11
  */
12
- const enum t {
12
+ const enum BSONElementType {
13
13
  double = 1,
14
14
  string = 2,
15
15
  object = 3,
@@ -45,17 +45,12 @@ export type BSONElement = [
45
45
  length: number
46
46
  ];
47
47
 
48
- /** Parses a int32 little-endian at offset, throws if it is negative */
49
- function getSize(source: Uint8Array, offset: number): number {
50
- if (source[offset + 3] > 127) {
51
- throw new BSONOffsetError('BSON size cannot be negative', offset);
48
+ function getSize(source: Uint8Array, offset: number) {
49
+ try {
50
+ return NumberUtils.getNonnegativeInt32LE(source, offset);
51
+ } catch (cause) {
52
+ throw new BSONOffsetError('BSON size cannot be negative', offset, { cause });
52
53
  }
53
- return (
54
- source[offset] |
55
- (source[offset + 1] << 8) |
56
- (source[offset + 2] << 16) |
57
- (source[offset + 3] << 24)
58
- );
59
54
  }
60
55
 
61
56
  /**
@@ -80,7 +75,12 @@ function findNull(bytes: Uint8Array, offset: number): number {
80
75
  * @public
81
76
  * @experimental
82
77
  */
83
- export function parseToElements(bytes: Uint8Array, startOffset = 0): Iterable<BSONElement> {
78
+ export function parseToElements(
79
+ bytes: Uint8Array,
80
+ startOffset: number | null = 0
81
+ ): Iterable<BSONElement> {
82
+ startOffset ??= 0;
83
+
84
84
  if (bytes.length < 5) {
85
85
  throw new BSONOffsetError(
86
86
  `Input must be at least 5 bytes, got ${bytes.length} bytes`,
@@ -121,37 +121,51 @@ export function parseToElements(bytes: Uint8Array, startOffset = 0): Iterable<BS
121
121
 
122
122
  let length: number;
123
123
 
124
- if (type === t.double || type === t.long || type === t.date || type === t.timestamp) {
124
+ if (
125
+ type === BSONElementType.double ||
126
+ type === BSONElementType.long ||
127
+ type === BSONElementType.date ||
128
+ type === BSONElementType.timestamp
129
+ ) {
125
130
  length = 8;
126
- } else if (type === t.int) {
131
+ } else if (type === BSONElementType.int) {
127
132
  length = 4;
128
- } else if (type === t.objectId) {
133
+ } else if (type === BSONElementType.objectId) {
129
134
  length = 12;
130
- } else if (type === t.decimal) {
135
+ } else if (type === BSONElementType.decimal) {
131
136
  length = 16;
132
- } else if (type === t.bool) {
137
+ } else if (type === BSONElementType.bool) {
133
138
  length = 1;
134
- } else if (type === t.null || type === t.undefined || type === t.maxKey || type === t.minKey) {
139
+ } else if (
140
+ type === BSONElementType.null ||
141
+ type === BSONElementType.undefined ||
142
+ type === BSONElementType.maxKey ||
143
+ type === BSONElementType.minKey
144
+ ) {
135
145
  length = 0;
136
146
  }
137
147
  // Needs a size calculation
138
- else if (type === t.regex) {
148
+ else if (type === BSONElementType.regex) {
139
149
  length = findNull(bytes, findNull(bytes, offset) + 1) + 1 - offset;
140
- } else if (type === t.object || type === t.array || type === t.javascriptWithScope) {
150
+ } else if (
151
+ type === BSONElementType.object ||
152
+ type === BSONElementType.array ||
153
+ type === BSONElementType.javascriptWithScope
154
+ ) {
141
155
  length = getSize(bytes, offset);
142
156
  } else if (
143
- type === t.string ||
144
- type === t.binData ||
145
- type === t.dbPointer ||
146
- type === t.javascript ||
147
- type === t.symbol
157
+ type === BSONElementType.string ||
158
+ type === BSONElementType.binData ||
159
+ type === BSONElementType.dbPointer ||
160
+ type === BSONElementType.javascript ||
161
+ type === BSONElementType.symbol
148
162
  ) {
149
163
  length = getSize(bytes, offset) + 4;
150
- if (type === t.binData) {
164
+ if (type === BSONElementType.binData) {
151
165
  // binary subtype
152
166
  length += 1;
153
167
  }
154
- if (type === t.dbPointer) {
168
+ if (type === BSONElementType.dbPointer) {
155
169
  // dbPointer's objectId
156
170
  length += 12;
157
171
  }
@@ -1,10 +1,16 @@
1
1
  import { nodeJsByteUtils } from './node_byte_utils';
2
2
  import { webByteUtils } from './web_byte_utils';
3
3
 
4
- /** @internal */
4
+ /**
5
+ * @public
6
+ * @experimental
7
+ *
8
+ * A collection of functions that help work with data in a Uint8Array.
9
+ * ByteUtils is configured at load time to use Node.js or Web based APIs for the internal implementations.
10
+ */
5
11
  export type ByteUtils = {
6
12
  /** Transforms the input to an instance of Buffer if running on node, otherwise Uint8Array */
7
- toLocalBufferType(buffer: Uint8Array | ArrayBufferView | ArrayBuffer): Uint8Array;
13
+ toLocalBufferType: (buffer: Uint8Array | ArrayBufferView | ArrayBuffer) => Uint8Array;
8
14
  /** Create empty space of size */
9
15
  allocate: (size: number) => Uint8Array;
10
16
  /** Create empty space of size, use pooled memory when available */
@@ -30,9 +36,9 @@ export type ByteUtils = {
30
36
  /** Get the utf8 code unit count from a string if it were to be transformed to utf8 */
31
37
  utf8ByteLength: (input: string) => number;
32
38
  /** Encode UTF8 bytes generated from `source` string into `destination` at byteOffset. Returns the number of bytes encoded. */
33
- encodeUTF8Into(destination: Uint8Array, source: string, byteOffset: number): number;
39
+ encodeUTF8Into: (destination: Uint8Array, source: string, byteOffset: number) => number;
34
40
  /** Generate a Uint8Array filled with random bytes with byteLength */
35
- randomBytes(byteLength: number): Uint8Array;
41
+ randomBytes: (byteLength: number) => Uint8Array;
36
42
  };
37
43
 
38
44
  declare const Buffer: { new (): unknown; prototype?: { _isBuffer?: boolean } } | undefined;
@@ -6,12 +6,47 @@ FLOAT[0] = -1;
6
6
  // Big endian [191, 240, 0, 0, 0, 0, 0, 0]
7
7
  const isBigEndian = FLOAT_BYTES[7] === 0;
8
8
 
9
+ /**
10
+ * @experimental
11
+ * @public
12
+ *
13
+ * A collection of functions that get or set various numeric types and bit widths from a Uint8Array.
14
+ */
15
+ export type NumberUtils = {
16
+ /**
17
+ * Parses a signed int32 at offset. Throws a `RangeError` if value is negative.
18
+ */
19
+ getNonnegativeInt32LE: (source: Uint8Array, offset: number) => number;
20
+ getInt32LE: (source: Uint8Array, offset: number) => number;
21
+ getUint32LE: (source: Uint8Array, offset: number) => number;
22
+ getUint32BE: (source: Uint8Array, offset: number) => number;
23
+ getBigInt64LE: (source: Uint8Array, offset: number) => bigint;
24
+ getFloat64LE: (source: Uint8Array, offset: number) => number;
25
+ setInt32BE: (destination: Uint8Array, offset: number, value: number) => 4;
26
+ setInt32LE: (destination: Uint8Array, offset: number, value: number) => 4;
27
+ setBigInt64LE: (destination: Uint8Array, offset: number, value: bigint) => 8;
28
+ setFloat64LE: (destination: Uint8Array, offset: number, value: number) => 8;
29
+ };
30
+
9
31
  /**
10
32
  * Number parsing and serializing utilities.
11
33
  *
12
- * @internal
34
+ * @experimental
35
+ * @public
13
36
  */
14
- export const NumberUtils = {
37
+ export const NumberUtils: NumberUtils = {
38
+ getNonnegativeInt32LE(source: Uint8Array, offset: number): number {
39
+ if (source[offset + 3] > 127) {
40
+ throw new RangeError(`Size cannot be negative at offset: ${offset}`);
41
+ }
42
+ return (
43
+ source[offset] |
44
+ (source[offset + 1] << 8) |
45
+ (source[offset + 2] << 16) |
46
+ (source[offset + 3] << 24)
47
+ );
48
+ },
49
+
15
50
  /** Reads a little-endian 32-bit integer from source */
16
51
  getInt32LE(source: Uint8Array, offset: number): number {
17
52
  return (