bson 6.4.1 → 6.5.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/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "vendor"
15
15
  ],
16
16
  "types": "bson.d.ts",
17
- "version": "6.4.1",
17
+ "version": "6.5.1",
18
18
  "author": {
19
19
  "name": "The MongoDB NodeJS Team",
20
20
  "email": "dbx-node@mongodb.com"
package/src/binary.ts CHANGED
@@ -56,6 +56,8 @@ export class Binary extends BSONValue {
56
56
  static readonly SUBTYPE_ENCRYPTED = 6;
57
57
  /** Column BSON type */
58
58
  static readonly SUBTYPE_COLUMN = 7;
59
+ /** Sensitive BSON type */
60
+ static readonly SUBTYPE_SENSITIVE = 8;
59
61
  /** User BSON type */
60
62
  static readonly SUBTYPE_USER_DEFINED = 128;
61
63
 
package/src/bson.ts CHANGED
@@ -54,6 +54,7 @@ export { BSONValue } from './bson_value';
54
54
  export { BSONError, BSONVersionError, BSONRuntimeError } from './error';
55
55
  export { BSONType } from './constants';
56
56
  export { EJSON } from './extended_json';
57
+ export { onDemand } from './parser/on_demand/index';
57
58
 
58
59
  /** @public */
59
60
  export interface Document {
package/src/constants.ts CHANGED
@@ -109,6 +109,9 @@ export const BSON_BINARY_SUBTYPE_ENCRYPTED = 6;
109
109
  /** Column BSON type @internal */
110
110
  export const BSON_BINARY_SUBTYPE_COLUMN = 7;
111
111
 
112
+ /** Sensitive BSON type @internal */
113
+ export const BSON_BINARY_SUBTYPE_SENSITIVE = 8;
114
+
112
115
  /** Binary User Defined Type @internal */
113
116
  export const BSON_BINARY_SUBTYPE_USER_DEFINED = 128;
114
117
 
package/src/error.ts CHANGED
@@ -81,3 +81,25 @@ export class BSONRuntimeError extends BSONError {
81
81
  super(message);
82
82
  }
83
83
  }
84
+
85
+ /**
86
+ * @public
87
+ * @category Error
88
+ *
89
+ * @experimental
90
+ *
91
+ * An error generated when BSON bytes are invalid.
92
+ * Reports the offset the parser was able to reach before encountering the error.
93
+ */
94
+ export class BSONOffsetError extends BSONError {
95
+ public get name(): 'BSONOffsetError' {
96
+ return 'BSONOffsetError';
97
+ }
98
+
99
+ public offset: number;
100
+
101
+ constructor(message: string, offset: number) {
102
+ super(`${message}. offset: ${offset}`);
103
+ this.offset = offset;
104
+ }
105
+ }
@@ -0,0 +1,28 @@
1
+ import { type BSONError, BSONOffsetError } from '../../error';
2
+ import { type BSONElement, parseToElements } from './parse_to_elements';
3
+ /**
4
+ * @experimental
5
+ * @public
6
+ *
7
+ * A new set of BSON APIs that are currently experimental and not intended for production use.
8
+ */
9
+ export type OnDemand = {
10
+ BSONOffsetError: {
11
+ new (message: string, offset: number): BSONOffsetError;
12
+ isBSONError(value: unknown): value is BSONError;
13
+ };
14
+ parseToElements: (this: void, bytes: Uint8Array, startOffset?: number) => Iterable<BSONElement>;
15
+ };
16
+
17
+ /**
18
+ * @experimental
19
+ * @public
20
+ */
21
+ const onDemand: OnDemand = Object.create(null);
22
+
23
+ onDemand.parseToElements = parseToElements;
24
+ onDemand.BSONOffsetError = BSONOffsetError;
25
+
26
+ Object.freeze(onDemand);
27
+
28
+ export { onDemand };
@@ -0,0 +1,174 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-enum-comparison */
2
+ import { BSONOffsetError } from '../../error';
3
+
4
+ /**
5
+ * @internal
6
+ *
7
+ * @remarks
8
+ * - This enum is const so the code we produce will inline the numbers
9
+ * - `minKey` is set to 255 so unsigned comparisons succeed
10
+ * - Modify with caution, double check the bundle contains literals
11
+ */
12
+ const enum t {
13
+ double = 1,
14
+ string = 2,
15
+ object = 3,
16
+ array = 4,
17
+ binData = 5,
18
+ undefined = 6,
19
+ objectId = 7,
20
+ bool = 8,
21
+ date = 9,
22
+ null = 10,
23
+ regex = 11,
24
+ dbPointer = 12,
25
+ javascript = 13,
26
+ symbol = 14,
27
+ javascriptWithScope = 15,
28
+ int = 16,
29
+ timestamp = 17,
30
+ long = 18,
31
+ decimal = 19,
32
+ minKey = 255,
33
+ maxKey = 127
34
+ }
35
+
36
+ /**
37
+ * @public
38
+ * @experimental
39
+ */
40
+ export type BSONElement = [
41
+ type: number,
42
+ nameOffset: number,
43
+ nameLength: number,
44
+ offset: number,
45
+ length: number
46
+ ];
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);
52
+ }
53
+ return (
54
+ source[offset] |
55
+ (source[offset + 1] << 8) |
56
+ (source[offset + 2] << 16) |
57
+ (source[offset + 3] << 24)
58
+ );
59
+ }
60
+
61
+ /**
62
+ * Searches for null terminator of a BSON element's value (Never the document null terminator)
63
+ * **Does not** bounds check since this should **ONLY** be used within parseToElements which has asserted that `bytes` ends with a `0x00`.
64
+ * So this will at most iterate to the document's terminator and error if that is the offset reached.
65
+ */
66
+ function findNull(bytes: Uint8Array, offset: number): number {
67
+ let nullTerminatorOffset = offset;
68
+
69
+ for (; bytes[nullTerminatorOffset] !== 0x00; nullTerminatorOffset++);
70
+
71
+ if (nullTerminatorOffset === bytes.length - 1) {
72
+ // We reached the null terminator of the document, not a value's
73
+ throw new BSONOffsetError('Null terminator not found', offset);
74
+ }
75
+
76
+ return nullTerminatorOffset;
77
+ }
78
+
79
+ /**
80
+ * @public
81
+ * @experimental
82
+ */
83
+ export function parseToElements(bytes: Uint8Array, startOffset = 0): Iterable<BSONElement> {
84
+ if (bytes.length < 5) {
85
+ throw new BSONOffsetError(
86
+ `Input must be at least 5 bytes, got ${bytes.length} bytes`,
87
+ startOffset
88
+ );
89
+ }
90
+
91
+ const documentSize = getSize(bytes, startOffset);
92
+
93
+ if (documentSize > bytes.length - startOffset) {
94
+ throw new BSONOffsetError(
95
+ `Parsed documentSize (${documentSize} bytes) does not match input length (${bytes.length} bytes)`,
96
+ startOffset
97
+ );
98
+ }
99
+
100
+ if (bytes[startOffset + documentSize - 1] !== 0x00) {
101
+ throw new BSONOffsetError('BSON documents must end in 0x00', startOffset + documentSize);
102
+ }
103
+
104
+ const elements: BSONElement[] = [];
105
+ let offset = startOffset + 4;
106
+
107
+ while (offset <= documentSize + startOffset) {
108
+ const type = bytes[offset];
109
+ offset += 1;
110
+
111
+ if (type === 0) {
112
+ if (offset - startOffset !== documentSize) {
113
+ throw new BSONOffsetError(`Invalid 0x00 type byte`, offset);
114
+ }
115
+ break;
116
+ }
117
+
118
+ const nameOffset = offset;
119
+ const nameLength = findNull(bytes, offset) - nameOffset;
120
+ offset += nameLength + 1;
121
+
122
+ let length: number;
123
+
124
+ if (type === t.double || type === t.long || type === t.date || type === t.timestamp) {
125
+ length = 8;
126
+ } else if (type === t.int) {
127
+ length = 4;
128
+ } else if (type === t.objectId) {
129
+ length = 12;
130
+ } else if (type === t.decimal) {
131
+ length = 16;
132
+ } else if (type === t.bool) {
133
+ length = 1;
134
+ } else if (type === t.null || type === t.undefined || type === t.maxKey || type === t.minKey) {
135
+ length = 0;
136
+ }
137
+ // Needs a size calculation
138
+ else if (type === t.regex) {
139
+ length = findNull(bytes, findNull(bytes, offset) + 1) + 1 - offset;
140
+ } else if (type === t.object || type === t.array || type === t.javascriptWithScope) {
141
+ length = getSize(bytes, offset);
142
+ } else if (
143
+ type === t.string ||
144
+ type === t.binData ||
145
+ type === t.dbPointer ||
146
+ type === t.javascript ||
147
+ type === t.symbol
148
+ ) {
149
+ length = getSize(bytes, offset) + 4;
150
+ if (type === t.binData) {
151
+ // binary subtype
152
+ length += 1;
153
+ }
154
+ if (type === t.dbPointer) {
155
+ // dbPointer's objectId
156
+ length += 12;
157
+ }
158
+ } else {
159
+ throw new BSONOffsetError(
160
+ `Invalid 0x${type.toString(16).padStart(2, '0')} type byte`,
161
+ offset
162
+ );
163
+ }
164
+
165
+ if (length > documentSize) {
166
+ throw new BSONOffsetError('value reports length larger than document', offset);
167
+ }
168
+
169
+ elements.push([type, nameOffset, nameLength, offset, length]);
170
+ offset += length;
171
+ }
172
+
173
+ return elements;
174
+ }