mongodb 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.
Files changed (207) hide show
  1. package/README.md +9 -9
  2. package/lib/admin.js +9 -9
  3. package/lib/admin.js.map +1 -1
  4. package/lib/bson.js +33 -22
  5. package/lib/bson.js.map +1 -1
  6. package/lib/bulk/common.js +13 -12
  7. package/lib/bulk/common.js.map +1 -1
  8. package/lib/change_stream.js +28 -18
  9. package/lib/change_stream.js.map +1 -1
  10. package/lib/client-side-encryption/auto_encrypter.js +2 -2
  11. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  12. package/lib/client-side-encryption/client_encryption.js +6 -6
  13. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  14. package/lib/client-side-encryption/providers/aws.js +13 -10
  15. package/lib/client-side-encryption/providers/aws.js.map +1 -1
  16. package/lib/client-side-encryption/providers/azure.js +6 -3
  17. package/lib/client-side-encryption/providers/azure.js.map +1 -1
  18. package/lib/cmap/auth/aws_temporary_credentials.js +140 -0
  19. package/lib/cmap/auth/aws_temporary_credentials.js.map +1 -0
  20. package/lib/cmap/auth/gssapi.js +7 -6
  21. package/lib/cmap/auth/gssapi.js.map +1 -1
  22. package/lib/cmap/auth/mongodb_aws.js +8 -101
  23. package/lib/cmap/auth/mongodb_aws.js.map +1 -1
  24. package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js +1 -1
  25. package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js.map +1 -1
  26. package/lib/cmap/auth/mongodb_oidc/callback_lock_cache.js +2 -1
  27. package/lib/cmap/auth/mongodb_oidc/callback_lock_cache.js.map +1 -1
  28. package/lib/cmap/auth/mongodb_oidc/service_workflow.js +1 -1
  29. package/lib/cmap/auth/mongodb_oidc/service_workflow.js.map +1 -1
  30. package/lib/cmap/auth/scram.js +2 -2
  31. package/lib/cmap/auth/scram.js.map +1 -1
  32. package/lib/cmap/commands.js +24 -111
  33. package/lib/cmap/commands.js.map +1 -1
  34. package/lib/cmap/connect.js +4 -4
  35. package/lib/cmap/connect.js.map +1 -1
  36. package/lib/cmap/connection.js +61 -36
  37. package/lib/cmap/connection.js.map +1 -1
  38. package/lib/cmap/connection_pool.js +22 -13
  39. package/lib/cmap/connection_pool.js.map +1 -1
  40. package/lib/cmap/handshake/client_metadata.js +2 -2
  41. package/lib/cmap/handshake/client_metadata.js.map +1 -1
  42. package/lib/cmap/wire_protocol/compression.js +8 -8
  43. package/lib/cmap/wire_protocol/compression.js.map +1 -1
  44. package/lib/cmap/wire_protocol/on_demand/document.js +218 -0
  45. package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -0
  46. package/lib/cmap/wire_protocol/responses.js +184 -0
  47. package/lib/cmap/wire_protocol/responses.js.map +1 -0
  48. package/lib/collection.js +42 -38
  49. package/lib/collection.js.map +1 -1
  50. package/lib/connection_string.js +4 -6
  51. package/lib/connection_string.js.map +1 -1
  52. package/lib/cursor/abstract_cursor.js +76 -43
  53. package/lib/cursor/abstract_cursor.js.map +1 -1
  54. package/lib/cursor/aggregation_cursor.js +16 -33
  55. package/lib/cursor/aggregation_cursor.js.map +1 -1
  56. package/lib/cursor/find_cursor.js +36 -18
  57. package/lib/cursor/find_cursor.js.map +1 -1
  58. package/lib/cursor/run_command_cursor.js +3 -2
  59. package/lib/cursor/run_command_cursor.js.map +1 -1
  60. package/lib/db.js +15 -19
  61. package/lib/db.js.map +1 -1
  62. package/lib/deps.js +31 -26
  63. package/lib/deps.js.map +1 -1
  64. package/lib/encrypter.js +14 -5
  65. package/lib/encrypter.js.map +1 -1
  66. package/lib/error.js +4 -3
  67. package/lib/error.js.map +1 -1
  68. package/lib/gridfs/download.js +19 -14
  69. package/lib/gridfs/download.js.map +1 -1
  70. package/lib/gridfs/index.js.map +1 -1
  71. package/lib/gridfs/upload.js +6 -1
  72. package/lib/gridfs/upload.js.map +1 -1
  73. package/lib/index.js.map +1 -1
  74. package/lib/mongo_client.js +11 -7
  75. package/lib/mongo_client.js.map +1 -1
  76. package/lib/mongo_logger.js +3 -0
  77. package/lib/mongo_logger.js.map +1 -1
  78. package/lib/operations/aggregate.js +2 -1
  79. package/lib/operations/aggregate.js.map +1 -1
  80. package/lib/operations/command.js +1 -1
  81. package/lib/operations/command.js.map +1 -1
  82. package/lib/operations/create_collection.js +1 -1
  83. package/lib/operations/create_collection.js.map +1 -1
  84. package/lib/operations/delete.js +4 -3
  85. package/lib/operations/delete.js.map +1 -1
  86. package/lib/operations/drop.js +1 -1
  87. package/lib/operations/drop.js.map +1 -1
  88. package/lib/operations/execute_operation.js +23 -8
  89. package/lib/operations/execute_operation.js.map +1 -1
  90. package/lib/operations/find.js +4 -4
  91. package/lib/operations/find.js.map +1 -1
  92. package/lib/operations/get_more.js +2 -1
  93. package/lib/operations/get_more.js.map +1 -1
  94. package/lib/operations/indexes.js +29 -121
  95. package/lib/operations/indexes.js.map +1 -1
  96. package/lib/operations/insert.js +3 -3
  97. package/lib/operations/insert.js.map +1 -1
  98. package/lib/operations/kill_cursors.js +3 -1
  99. package/lib/operations/kill_cursors.js.map +1 -1
  100. package/lib/operations/list_collections.js +1 -1
  101. package/lib/operations/list_collections.js.map +1 -1
  102. package/lib/operations/list_databases.js +1 -1
  103. package/lib/operations/list_databases.js.map +1 -1
  104. package/lib/operations/operation.js.map +1 -1
  105. package/lib/operations/run_command.js +4 -2
  106. package/lib/operations/run_command.js.map +1 -1
  107. package/lib/operations/search_indexes/create.js.map +1 -1
  108. package/lib/operations/stats.js +1 -1
  109. package/lib/operations/stats.js.map +1 -1
  110. package/lib/operations/update.js +1 -1
  111. package/lib/operations/update.js.map +1 -1
  112. package/lib/sdam/common.js.map +1 -1
  113. package/lib/sdam/monitor.js +139 -42
  114. package/lib/sdam/monitor.js.map +1 -1
  115. package/lib/sdam/server.js +5 -15
  116. package/lib/sdam/server.js.map +1 -1
  117. package/lib/sdam/server_description.js +1 -0
  118. package/lib/sdam/server_description.js.map +1 -1
  119. package/lib/sdam/server_selection.js +1 -1
  120. package/lib/sdam/server_selection.js.map +1 -1
  121. package/lib/sdam/srv_polling.js +2 -1
  122. package/lib/sdam/srv_polling.js.map +1 -1
  123. package/lib/sdam/topology.js +67 -54
  124. package/lib/sdam/topology.js.map +1 -1
  125. package/lib/sdam/topology_description.js +10 -0
  126. package/lib/sdam/topology_description.js.map +1 -1
  127. package/lib/sessions.js +133 -93
  128. package/lib/sessions.js.map +1 -1
  129. package/lib/timeout.js +77 -0
  130. package/lib/timeout.js.map +1 -0
  131. package/lib/utils.js +61 -28
  132. package/lib/utils.js.map +1 -1
  133. package/mongodb.d.ts +150 -38
  134. package/package.json +16 -13
  135. package/src/admin.ts +9 -9
  136. package/src/bson.ts +14 -0
  137. package/src/bulk/common.ts +3 -2
  138. package/src/change_stream.ts +39 -30
  139. package/src/client-side-encryption/auto_encrypter.ts +2 -2
  140. package/src/client-side-encryption/client_encryption.ts +6 -6
  141. package/src/client-side-encryption/providers/aws.ts +17 -10
  142. package/src/client-side-encryption/providers/azure.ts +5 -3
  143. package/src/cmap/auth/aws_temporary_credentials.ts +169 -0
  144. package/src/cmap/auth/gssapi.ts +9 -11
  145. package/src/cmap/auth/mongodb_aws.ts +19 -126
  146. package/src/cmap/auth/mongodb_oidc/aws_service_workflow.ts +1 -1
  147. package/src/cmap/auth/mongodb_oidc/callback_lock_cache.ts +2 -1
  148. package/src/cmap/auth/mongodb_oidc/service_workflow.ts +1 -1
  149. package/src/cmap/auth/scram.ts +2 -2
  150. package/src/cmap/commands.ts +28 -132
  151. package/src/cmap/connect.ts +4 -4
  152. package/src/cmap/connection.ts +107 -43
  153. package/src/cmap/connection_pool.ts +32 -29
  154. package/src/cmap/handshake/client_metadata.ts +2 -5
  155. package/src/cmap/wire_protocol/compression.ts +11 -13
  156. package/src/cmap/wire_protocol/on_demand/document.ts +338 -0
  157. package/src/cmap/wire_protocol/responses.ts +237 -0
  158. package/src/collection.ts +87 -58
  159. package/src/connection_string.ts +9 -7
  160. package/src/cursor/abstract_cursor.ts +102 -38
  161. package/src/cursor/aggregation_cursor.ts +32 -34
  162. package/src/cursor/find_cursor.ts +33 -21
  163. package/src/cursor/list_search_indexes_cursor.ts +1 -1
  164. package/src/cursor/run_command_cursor.ts +3 -2
  165. package/src/db.ts +42 -21
  166. package/src/deps.ts +52 -40
  167. package/src/encrypter.ts +14 -5
  168. package/src/error.ts +9 -3
  169. package/src/gridfs/download.ts +19 -31
  170. package/src/gridfs/index.ts +2 -0
  171. package/src/gridfs/upload.ts +11 -8
  172. package/src/index.ts +13 -5
  173. package/src/mongo_client.ts +21 -15
  174. package/src/mongo_logger.ts +3 -0
  175. package/src/mongo_types.ts +1 -1
  176. package/src/operations/aggregate.ts +2 -1
  177. package/src/operations/command.ts +1 -1
  178. package/src/operations/create_collection.ts +7 -2
  179. package/src/operations/delete.ts +4 -3
  180. package/src/operations/drop.ts +1 -1
  181. package/src/operations/execute_operation.ts +29 -10
  182. package/src/operations/find.ts +13 -14
  183. package/src/operations/get_more.ts +9 -1
  184. package/src/operations/indexes.ts +103 -176
  185. package/src/operations/insert.ts +2 -2
  186. package/src/operations/kill_cursors.ts +3 -2
  187. package/src/operations/list_collections.ts +5 -1
  188. package/src/operations/list_databases.ts +1 -1
  189. package/src/operations/operation.ts +3 -0
  190. package/src/operations/run_command.ts +6 -4
  191. package/src/operations/search_indexes/create.ts +4 -1
  192. package/src/operations/stats.ts +1 -1
  193. package/src/operations/update.ts +7 -7
  194. package/src/sdam/common.ts +8 -2
  195. package/src/sdam/monitor.ts +178 -61
  196. package/src/sdam/server.ts +27 -20
  197. package/src/sdam/server_description.ts +8 -3
  198. package/src/sdam/server_selection.ts +2 -3
  199. package/src/sdam/srv_polling.ts +3 -2
  200. package/src/sdam/topology.ts +114 -117
  201. package/src/sdam/topology_description.ts +14 -4
  202. package/src/sessions.ts +168 -148
  203. package/src/timeout.ts +96 -0
  204. package/src/utils.ts +85 -32
  205. package/lib/operations/common_functions.js +0 -38
  206. package/lib/operations/common_functions.js.map +0 -1
  207. package/src/operations/common_functions.ts +0 -79
@@ -0,0 +1,338 @@
1
+ import {
2
+ Binary,
3
+ BSON,
4
+ type BSONElement,
5
+ BSONError,
6
+ type BSONSerializeOptions,
7
+ BSONType,
8
+ getBigInt64LE,
9
+ getFloat64LE,
10
+ getInt32LE,
11
+ ObjectId,
12
+ parseToElementsToArray,
13
+ Timestamp,
14
+ toUTF8
15
+ } from '../../../bson';
16
+
17
+ // eslint-disable-next-line no-restricted-syntax
18
+ const enum BSONElementOffset {
19
+ type = 0,
20
+ nameOffset = 1,
21
+ nameLength = 2,
22
+ offset = 3,
23
+ length = 4
24
+ }
25
+
26
+ /** @internal */
27
+ export type JSTypeOf = {
28
+ [BSONType.null]: null;
29
+ [BSONType.undefined]: null;
30
+ [BSONType.double]: number;
31
+ [BSONType.int]: number;
32
+ [BSONType.long]: bigint;
33
+ [BSONType.timestamp]: Timestamp;
34
+ [BSONType.binData]: Binary;
35
+ [BSONType.bool]: boolean;
36
+ [BSONType.objectId]: ObjectId;
37
+ [BSONType.string]: string;
38
+ [BSONType.date]: Date;
39
+ [BSONType.object]: OnDemandDocument;
40
+ [BSONType.array]: OnDemandDocument;
41
+ };
42
+
43
+ /** @internal */
44
+ type CachedBSONElement = { element: BSONElement; value: any | undefined };
45
+
46
+ /** @internal */
47
+ export class OnDemandDocument {
48
+ /**
49
+ * Maps JS strings to elements and jsValues for speeding up subsequent lookups.
50
+ * - If `false` then name does not exist in the BSON document
51
+ * - If `CachedBSONElement` instance name exists
52
+ * - If `cache[name].value == null` jsValue has not yet been parsed
53
+ * - Null/Undefined values do not get cached because they are zero-length values.
54
+ */
55
+ private readonly cache: Record<string, CachedBSONElement | false | undefined> =
56
+ Object.create(null);
57
+ /** Caches the index of elements that have been named */
58
+ private readonly indexFound: Record<number, boolean> = Object.create(null);
59
+
60
+ /** All bson elements in this document */
61
+ private readonly elements: ReadonlyArray<BSONElement>;
62
+
63
+ constructor(
64
+ /** BSON bytes, this document begins at offset */
65
+ protected readonly bson: Uint8Array,
66
+ /** The start of the document */
67
+ private readonly offset = 0,
68
+ /** If this is an embedded document, indicates if this was a BSON array */
69
+ public readonly isArray = false
70
+ ) {
71
+ this.elements = parseToElementsToArray(this.bson, offset);
72
+ }
73
+
74
+ /** Only supports basic latin strings */
75
+ private isElementName(name: string, element: BSONElement): boolean {
76
+ const nameLength = element[BSONElementOffset.nameLength];
77
+ const nameOffset = element[BSONElementOffset.nameOffset];
78
+
79
+ if (name.length !== nameLength) return false;
80
+
81
+ for (let i = 0; i < name.length; i++) {
82
+ if (this.bson[nameOffset + i] !== name.charCodeAt(i)) return false;
83
+ }
84
+
85
+ return true;
86
+ }
87
+
88
+ /**
89
+ * Seeks into the elements array for an element matching the given name.
90
+ *
91
+ * @remarks
92
+ * Caching:
93
+ * - Caches the existence of a property making subsequent look ups for non-existent properties return immediately
94
+ * - Caches names mapped to elements to avoid reiterating the array and comparing the name again
95
+ * - Caches the index at which an element has been found to prevent rechecking against elements already determined to belong to another name
96
+ *
97
+ * @param name - a basic latin string name of a BSON element
98
+ * @returns
99
+ */
100
+ private getElement(name: string | number): CachedBSONElement | null {
101
+ const cachedElement = this.cache[name];
102
+ if (cachedElement === false) return null;
103
+
104
+ if (cachedElement != null) {
105
+ return cachedElement;
106
+ }
107
+
108
+ if (typeof name === 'number') {
109
+ if (this.isArray) {
110
+ if (name < this.elements.length) {
111
+ const element = this.elements[name];
112
+ const cachedElement = { element, value: undefined };
113
+ this.cache[name] = cachedElement;
114
+ this.indexFound[name] = true;
115
+ return cachedElement;
116
+ } else {
117
+ return null;
118
+ }
119
+ } else {
120
+ return null;
121
+ }
122
+ }
123
+
124
+ for (let index = 0; index < this.elements.length; index++) {
125
+ const element = this.elements[index];
126
+
127
+ // skip this element if it has already been associated with a name
128
+ if (!this.indexFound[index] && this.isElementName(name, element)) {
129
+ const cachedElement = { element, value: undefined };
130
+ this.cache[name] = cachedElement;
131
+ this.indexFound[index] = true;
132
+ return cachedElement;
133
+ }
134
+ }
135
+
136
+ this.cache[name] = false;
137
+ return null;
138
+ }
139
+
140
+ /**
141
+ * Translates BSON bytes into a javascript value. Checking `as` against the BSON element's type
142
+ * this methods returns the small subset of BSON types that the driver needs to function.
143
+ *
144
+ * @remarks
145
+ * - BSONType.null and BSONType.undefined always return null
146
+ * - If the type requested does not match this returns null
147
+ *
148
+ * @param element - The element to revive to a javascript value
149
+ * @param as - A type byte expected to be returned
150
+ */
151
+ private toJSValue<T extends keyof JSTypeOf>(element: BSONElement, as: T): JSTypeOf[T];
152
+ private toJSValue(element: BSONElement, as: keyof JSTypeOf): any {
153
+ const type = element[BSONElementOffset.type];
154
+ const offset = element[BSONElementOffset.offset];
155
+ const length = element[BSONElementOffset.length];
156
+
157
+ if (as !== type) {
158
+ return null;
159
+ }
160
+
161
+ switch (as) {
162
+ case BSONType.null:
163
+ case BSONType.undefined:
164
+ return null;
165
+ case BSONType.double:
166
+ return getFloat64LE(this.bson, offset);
167
+ case BSONType.int:
168
+ return getInt32LE(this.bson, offset);
169
+ case BSONType.long:
170
+ return getBigInt64LE(this.bson, offset);
171
+ case BSONType.bool:
172
+ return Boolean(this.bson[offset]);
173
+ case BSONType.objectId:
174
+ return new ObjectId(this.bson.subarray(offset, offset + 12));
175
+ case BSONType.timestamp:
176
+ return new Timestamp(getBigInt64LE(this.bson, offset));
177
+ case BSONType.string:
178
+ return toUTF8(this.bson, offset + 4, offset + length - 1, false);
179
+ case BSONType.binData: {
180
+ const totalBinarySize = getInt32LE(this.bson, offset);
181
+ const subType = this.bson[offset + 4];
182
+
183
+ if (subType === 2) {
184
+ const subType2BinarySize = getInt32LE(this.bson, offset + 1 + 4);
185
+ if (subType2BinarySize < 0)
186
+ throw new BSONError('Negative binary type element size found for subtype 0x02');
187
+ if (subType2BinarySize > totalBinarySize - 4)
188
+ throw new BSONError('Binary type with subtype 0x02 contains too long binary size');
189
+ if (subType2BinarySize < totalBinarySize - 4)
190
+ throw new BSONError('Binary type with subtype 0x02 contains too short binary size');
191
+ return new Binary(
192
+ this.bson.subarray(offset + 1 + 4 + 4, offset + 1 + 4 + 4 + subType2BinarySize),
193
+ 2
194
+ );
195
+ }
196
+
197
+ return new Binary(
198
+ this.bson.subarray(offset + 1 + 4, offset + 1 + 4 + totalBinarySize),
199
+ subType
200
+ );
201
+ }
202
+ case BSONType.date:
203
+ // Pretend this is correct.
204
+ return new Date(Number(getBigInt64LE(this.bson, offset)));
205
+
206
+ case BSONType.object:
207
+ return new OnDemandDocument(this.bson, offset);
208
+ case BSONType.array:
209
+ return new OnDemandDocument(this.bson, offset, true);
210
+
211
+ default:
212
+ throw new BSONError(`Unsupported BSON type: ${as}`);
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Returns the number of elements in this BSON document
218
+ */
219
+ public size() {
220
+ return this.elements.length;
221
+ }
222
+
223
+ /**
224
+ * Checks for the existence of an element by name.
225
+ *
226
+ * @remarks
227
+ * Uses `getElement` with the expectation that will populate caches such that a `has` call
228
+ * followed by a `getElement` call will not repeat the cost paid by the first look up.
229
+ *
230
+ * @param name - element name
231
+ */
232
+ public has(name: string): boolean {
233
+ const cachedElement = this.cache[name];
234
+ if (cachedElement === false) return false;
235
+ if (cachedElement != null) return true;
236
+ return this.getElement(name) != null;
237
+ }
238
+
239
+ /**
240
+ * Turns BSON element with `name` into a javascript value.
241
+ *
242
+ * @typeParam T - must be one of the supported BSON types determined by `JSTypeOf` this will determine the return type of this function.
243
+ * @param name - the element name
244
+ * @param as - the bson type expected
245
+ * @param required - whether or not the element is expected to exist, if true this function will throw if it is not present
246
+ */
247
+ public get<const T extends keyof JSTypeOf>(
248
+ name: string | number,
249
+ as: T,
250
+ required?: false | undefined
251
+ ): JSTypeOf[T] | null;
252
+
253
+ /** `required` will make `get` throw if name does not exist or is null/undefined */
254
+ public get<const T extends keyof JSTypeOf>(
255
+ name: string | number,
256
+ as: T,
257
+ required: true
258
+ ): JSTypeOf[T];
259
+
260
+ public get<const T extends keyof JSTypeOf>(
261
+ name: string | number,
262
+ as: T,
263
+ required?: boolean
264
+ ): JSTypeOf[T] | null {
265
+ const element = this.getElement(name);
266
+ if (element == null) {
267
+ if (required === true) {
268
+ throw new BSONError(`BSON element "${name}" is missing`);
269
+ } else {
270
+ return null;
271
+ }
272
+ }
273
+
274
+ if (element.value == null) {
275
+ const value = this.toJSValue(element.element, as);
276
+ if (value == null) {
277
+ if (required === true) {
278
+ throw new BSONError(`BSON element "${name}" is missing`);
279
+ } else {
280
+ return null;
281
+ }
282
+ }
283
+ // It is important to never store null
284
+ element.value = value;
285
+ }
286
+
287
+ return element.value;
288
+ }
289
+
290
+ /**
291
+ * Supports returning int, double, long, and bool as javascript numbers
292
+ *
293
+ * @remarks
294
+ * **NOTE:**
295
+ * - Use this _only_ when you believe the potential precision loss of an int64 is acceptable
296
+ * - This method does not cache the result as Longs or booleans would be stored incorrectly
297
+ *
298
+ * @param name - element name
299
+ * @param required - throws if name does not exist
300
+ */
301
+ public getNumber<const Req extends boolean = false>(
302
+ name: string,
303
+ required?: Req
304
+ ): Req extends true ? number : number | null;
305
+ public getNumber(name: string, required: boolean): number | null {
306
+ const maybeBool = this.get(name, BSONType.bool);
307
+ const bool = maybeBool == null ? null : maybeBool ? 1 : 0;
308
+
309
+ const maybeLong = this.get(name, BSONType.long);
310
+ const long = maybeLong == null ? null : Number(maybeLong);
311
+
312
+ const result = bool ?? long ?? this.get(name, BSONType.int) ?? this.get(name, BSONType.double);
313
+
314
+ if (required === true && result == null) {
315
+ throw new BSONError(`BSON element "${name}" is missing`);
316
+ }
317
+
318
+ return result;
319
+ }
320
+
321
+ /**
322
+ * Deserialize this object, DOES NOT cache result so avoid multiple invocations
323
+ * @param options - BSON deserialization options
324
+ */
325
+ public toObject(options?: BSONSerializeOptions): Record<string, any> {
326
+ return BSON.deserialize(this.bson, {
327
+ ...options,
328
+ index: this.offset,
329
+ allowObjectSmallerThanBufferSize: true
330
+ });
331
+ }
332
+
333
+ /** Returns this document's bytes only */
334
+ toBytes() {
335
+ const size = getInt32LE(this.bson, this.offset);
336
+ return this.bson.subarray(this.offset, this.offset + size);
337
+ }
338
+ }
@@ -0,0 +1,237 @@
1
+ import {
2
+ type BSONSerializeOptions,
3
+ BSONType,
4
+ type Document,
5
+ Long,
6
+ parseToElementsToArray,
7
+ type Timestamp
8
+ } from '../../bson';
9
+ import { MongoUnexpectedServerResponseError } from '../../error';
10
+ import { type ClusterTime } from '../../sdam/common';
11
+ import { type MongoDBNamespace, ns } from '../../utils';
12
+ import { OnDemandDocument } from './on_demand/document';
13
+
14
+ // eslint-disable-next-line no-restricted-syntax
15
+ const enum BSONElementOffset {
16
+ type = 0,
17
+ nameOffset = 1,
18
+ nameLength = 2,
19
+ offset = 3,
20
+ length = 4
21
+ }
22
+ /**
23
+ * Accepts a BSON payload and checks for na "ok: 0" element.
24
+ * This utility is intended to prevent calling response class constructors
25
+ * that expect the result to be a success and demand certain properties to exist.
26
+ *
27
+ * For example, a cursor response always expects a cursor embedded document.
28
+ * In order to write the class such that the properties reflect that assertion (non-null)
29
+ * we cannot invoke the subclass constructor if the BSON represents an error.
30
+ *
31
+ * @param bytes - BSON document returned from the server
32
+ */
33
+ export function isErrorResponse(bson: Uint8Array): boolean {
34
+ const elements = parseToElementsToArray(bson, 0);
35
+ for (let eIdx = 0; eIdx < elements.length; eIdx++) {
36
+ const element = elements[eIdx];
37
+
38
+ if (element[BSONElementOffset.nameLength] === 2) {
39
+ const nameOffset = element[BSONElementOffset.nameOffset];
40
+
41
+ // 111 == "o", 107 == "k"
42
+ if (bson[nameOffset] === 111 && bson[nameOffset + 1] === 107) {
43
+ const valueOffset = element[BSONElementOffset.offset];
44
+ const valueLength = element[BSONElementOffset.length];
45
+
46
+ // If any byte in the length of the ok number (works for any type) is non zero,
47
+ // then it is considered "ok: 1"
48
+ for (let i = valueOffset; i < valueOffset + valueLength; i++) {
49
+ if (bson[i] !== 0x00) return false;
50
+ }
51
+
52
+ return true;
53
+ }
54
+ }
55
+ }
56
+
57
+ return true;
58
+ }
59
+
60
+ /** @internal */
61
+ export type MongoDBResponseConstructor = {
62
+ new (bson: Uint8Array, offset?: number, isArray?: boolean): MongoDBResponse;
63
+ };
64
+
65
+ /** @internal */
66
+ export class MongoDBResponse extends OnDemandDocument {
67
+ static is(value: unknown): value is MongoDBResponse {
68
+ return value instanceof MongoDBResponse;
69
+ }
70
+
71
+ // {ok:1}
72
+ static empty = new MongoDBResponse(new Uint8Array([13, 0, 0, 0, 16, 111, 107, 0, 1, 0, 0, 0, 0]));
73
+
74
+ /** Indicates this document is a server error */
75
+ public get isError() {
76
+ let isError = this.ok === 0;
77
+ isError ||= this.has('errmsg');
78
+ isError ||= this.has('code');
79
+ isError ||= this.has('$err'); // The '$err' field is used in OP_REPLY responses
80
+ return isError;
81
+ }
82
+
83
+ /**
84
+ * Drivers can safely assume that the `recoveryToken` field is always a BSON document but drivers MUST NOT modify the
85
+ * contents of the document.
86
+ */
87
+ get recoveryToken(): Document | null {
88
+ return (
89
+ this.get('recoveryToken', BSONType.object)?.toObject({
90
+ promoteValues: false,
91
+ promoteLongs: false,
92
+ promoteBuffers: false
93
+ }) ?? null
94
+ );
95
+ }
96
+
97
+ /**
98
+ * The server creates a cursor in response to a snapshot find/aggregate command and reports atClusterTime within the cursor field in the response.
99
+ * For the distinct command the server adds a top-level atClusterTime field to the response.
100
+ * The atClusterTime field represents the timestamp of the read and is guaranteed to be majority committed.
101
+ */
102
+ public get atClusterTime(): Timestamp | null {
103
+ return (
104
+ this.get('cursor', BSONType.object)?.get('atClusterTime', BSONType.timestamp) ??
105
+ this.get('atClusterTime', BSONType.timestamp)
106
+ );
107
+ }
108
+
109
+ public get operationTime(): Timestamp | null {
110
+ return this.get('operationTime', BSONType.timestamp);
111
+ }
112
+
113
+ public get ok(): 0 | 1 {
114
+ return this.getNumber('ok') ? 1 : 0;
115
+ }
116
+
117
+ public get $err(): string | null {
118
+ return this.get('$err', BSONType.string);
119
+ }
120
+
121
+ public get errmsg(): string | null {
122
+ return this.get('errmsg', BSONType.string);
123
+ }
124
+
125
+ public get code(): number | null {
126
+ return this.getNumber('code');
127
+ }
128
+
129
+ private clusterTime?: ClusterTime | null;
130
+ public get $clusterTime(): ClusterTime | null {
131
+ if (!('clusterTime' in this)) {
132
+ const clusterTimeDoc = this.get('$clusterTime', BSONType.object);
133
+ if (clusterTimeDoc == null) {
134
+ this.clusterTime = null;
135
+ return null;
136
+ }
137
+ const clusterTime = clusterTimeDoc.get('clusterTime', BSONType.timestamp, true);
138
+ const signature = clusterTimeDoc.get('signature', BSONType.object)?.toObject();
139
+ // @ts-expect-error: `signature` is incorrectly typed. It is public API.
140
+ this.clusterTime = { clusterTime, signature };
141
+ }
142
+ return this.clusterTime ?? null;
143
+ }
144
+
145
+ public override toObject(options?: BSONSerializeOptions): Record<string, any> {
146
+ const exactBSONOptions = {
147
+ useBigInt64: options?.useBigInt64,
148
+ promoteLongs: options?.promoteLongs,
149
+ promoteValues: options?.promoteValues,
150
+ promoteBuffers: options?.promoteBuffers,
151
+ bsonRegExp: options?.bsonRegExp,
152
+ raw: options?.raw ?? false,
153
+ fieldsAsRaw: options?.fieldsAsRaw ?? {},
154
+ validation: this.parseBsonSerializationOptions(options)
155
+ };
156
+ return super.toObject(exactBSONOptions);
157
+ }
158
+
159
+ private parseBsonSerializationOptions(options?: { enableUtf8Validation?: boolean }): {
160
+ utf8: { writeErrors: false } | false;
161
+ } {
162
+ const enableUtf8Validation = options?.enableUtf8Validation;
163
+ if (enableUtf8Validation === false) {
164
+ return { utf8: false };
165
+ }
166
+ return { utf8: { writeErrors: false } };
167
+ }
168
+ }
169
+
170
+ /** @internal */
171
+ export class CursorResponse extends MongoDBResponse {
172
+ /**
173
+ * This supports a feature of the FindCursor.
174
+ * It is an optimization to avoid an extra getMore when the limit has been reached
175
+ */
176
+ static emptyGetMore = { id: new Long(0), length: 0, shift: () => null };
177
+
178
+ static override is(value: unknown): value is CursorResponse {
179
+ return value instanceof CursorResponse || value === CursorResponse.emptyGetMore;
180
+ }
181
+
182
+ public id: Long;
183
+ public ns: MongoDBNamespace | null = null;
184
+ public batchSize = 0;
185
+
186
+ private batch: OnDemandDocument;
187
+ private iterated = 0;
188
+
189
+ constructor(bytes: Uint8Array, offset?: number, isArray?: boolean) {
190
+ super(bytes, offset, isArray);
191
+
192
+ const cursor = this.get('cursor', BSONType.object, true);
193
+
194
+ const id = cursor.get('id', BSONType.long, true);
195
+ this.id = new Long(Number(id & 0xffff_ffffn), Number((id >> 32n) & 0xffff_ffffn));
196
+
197
+ const namespace = cursor.get('ns', BSONType.string);
198
+ if (namespace != null) this.ns = ns(namespace);
199
+
200
+ if (cursor.has('firstBatch')) this.batch = cursor.get('firstBatch', BSONType.array, true);
201
+ else if (cursor.has('nextBatch')) this.batch = cursor.get('nextBatch', BSONType.array, true);
202
+ else throw new MongoUnexpectedServerResponseError('Cursor document did not contain a batch');
203
+
204
+ this.batchSize = this.batch.size();
205
+ }
206
+
207
+ get length() {
208
+ return Math.max(this.batchSize - this.iterated, 0);
209
+ }
210
+
211
+ shift(options?: BSONSerializeOptions): any {
212
+ if (this.iterated >= this.batchSize) {
213
+ return null;
214
+ }
215
+
216
+ const result = this.batch.get(this.iterated, BSONType.object, true) ?? null;
217
+ this.iterated += 1;
218
+
219
+ if (options?.raw) {
220
+ return result.toBytes();
221
+ } else {
222
+ return result.toObject(options);
223
+ }
224
+ }
225
+
226
+ clear() {
227
+ this.iterated = this.batchSize;
228
+ }
229
+
230
+ pushMany() {
231
+ throw new Error('pushMany Unsupported method');
232
+ }
233
+
234
+ push() {
235
+ throw new Error('push Unsupported method');
236
+ }
237
+ }