mongodb 6.7.0-dev.20240608.sha.0655c730 → 6.7.0-dev.20240614.sha.3ed6a2ad

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 (96) hide show
  1. package/lib/bson.js.map +1 -1
  2. package/lib/client-side-encryption/auto_encrypter.js +8 -61
  3. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  4. package/lib/client-side-encryption/client_encryption.js +5 -5
  5. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  6. package/lib/client-side-encryption/providers/index.js.map +1 -1
  7. package/lib/client-side-encryption/state_machine.js +15 -11
  8. package/lib/client-side-encryption/state_machine.js.map +1 -1
  9. package/lib/cmap/connection.js +22 -20
  10. package/lib/cmap/connection.js.map +1 -1
  11. package/lib/cmap/wire_protocol/on_demand/document.js +8 -5
  12. package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -1
  13. package/lib/cmap/wire_protocol/responses.js +116 -40
  14. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  15. package/lib/constants.js +9 -1
  16. package/lib/constants.js.map +1 -1
  17. package/lib/cursor/abstract_cursor.js +24 -60
  18. package/lib/cursor/abstract_cursor.js.map +1 -1
  19. package/lib/cursor/aggregation_cursor.js +2 -3
  20. package/lib/cursor/aggregation_cursor.js.map +1 -1
  21. package/lib/cursor/change_stream_cursor.js +6 -8
  22. package/lib/cursor/change_stream_cursor.js.map +1 -1
  23. package/lib/cursor/find_cursor.js +5 -17
  24. package/lib/cursor/find_cursor.js.map +1 -1
  25. package/lib/cursor/list_collections_cursor.js +0 -1
  26. package/lib/cursor/list_collections_cursor.js.map +1 -1
  27. package/lib/cursor/list_indexes_cursor.js +0 -1
  28. package/lib/cursor/list_indexes_cursor.js.map +1 -1
  29. package/lib/cursor/run_command_cursor.js +4 -6
  30. package/lib/cursor/run_command_cursor.js.map +1 -1
  31. package/lib/error.js +6 -21
  32. package/lib/error.js.map +1 -1
  33. package/lib/index.js.map +1 -1
  34. package/lib/operations/aggregate.js +2 -2
  35. package/lib/operations/aggregate.js.map +1 -1
  36. package/lib/operations/bulk_write.js +1 -2
  37. package/lib/operations/bulk_write.js.map +1 -1
  38. package/lib/operations/command.js +2 -3
  39. package/lib/operations/command.js.map +1 -1
  40. package/lib/operations/count_documents.js +1 -7
  41. package/lib/operations/count_documents.js.map +1 -1
  42. package/lib/operations/execute_operation.js.map +1 -1
  43. package/lib/operations/find.js +2 -1
  44. package/lib/operations/find.js.map +1 -1
  45. package/lib/operations/get_more.js +1 -1
  46. package/lib/operations/get_more.js.map +1 -1
  47. package/lib/operations/indexes.js +2 -1
  48. package/lib/operations/indexes.js.map +1 -1
  49. package/lib/operations/list_collections.js +2 -1
  50. package/lib/operations/list_collections.js.map +1 -1
  51. package/lib/operations/run_command.js +1 -1
  52. package/lib/operations/run_command.js.map +1 -1
  53. package/lib/operations/update.js +2 -1
  54. package/lib/operations/update.js.map +1 -1
  55. package/lib/sdam/server.js +7 -2
  56. package/lib/sdam/server.js.map +1 -1
  57. package/lib/utils.js +45 -1
  58. package/lib/utils.js.map +1 -1
  59. package/lib/write_concern.js +17 -1
  60. package/lib/write_concern.js.map +1 -1
  61. package/mongodb.d.ts +120 -85
  62. package/package.json +1 -1
  63. package/src/bson.ts +1 -0
  64. package/src/client-side-encryption/auto_encrypter.ts +9 -70
  65. package/src/client-side-encryption/client_encryption.ts +6 -6
  66. package/src/client-side-encryption/providers/index.ts +120 -92
  67. package/src/client-side-encryption/state_machine.ts +22 -18
  68. package/src/cmap/connection.ts +46 -50
  69. package/src/cmap/wire_protocol/on_demand/document.ts +13 -6
  70. package/src/cmap/wire_protocol/responses.ts +140 -45
  71. package/src/constants.ts +9 -0
  72. package/src/cursor/abstract_cursor.ts +51 -71
  73. package/src/cursor/aggregation_cursor.ts +13 -12
  74. package/src/cursor/change_stream_cursor.ts +20 -34
  75. package/src/cursor/find_cursor.ts +17 -25
  76. package/src/cursor/list_collections_cursor.ts +3 -4
  77. package/src/cursor/list_indexes_cursor.ts +3 -4
  78. package/src/cursor/run_command_cursor.ts +13 -19
  79. package/src/error.ts +16 -28
  80. package/src/index.ts +12 -8
  81. package/src/operations/aggregate.ts +12 -5
  82. package/src/operations/bulk_write.ts +1 -2
  83. package/src/operations/command.ts +17 -3
  84. package/src/operations/count_documents.ts +7 -11
  85. package/src/operations/delete.ts +2 -2
  86. package/src/operations/execute_operation.ts +0 -13
  87. package/src/operations/find.ts +7 -3
  88. package/src/operations/find_and_modify.ts +1 -1
  89. package/src/operations/get_more.ts +6 -10
  90. package/src/operations/indexes.ts +7 -3
  91. package/src/operations/list_collections.ts +8 -3
  92. package/src/operations/run_command.ts +16 -6
  93. package/src/operations/update.ts +2 -1
  94. package/src/sdam/server.ts +7 -2
  95. package/src/utils.ts +52 -2
  96. package/src/write_concern.ts +18 -0
@@ -4,124 +4,152 @@ import { loadGCPCredentials } from './gcp';
4
4
 
5
5
  /**
6
6
  * @public
7
+ *
8
+ * A data key provider. Allowed values:
9
+ *
10
+ * - aws, gcp, local, kmip or azure
11
+ * - (`mongodb-client-encryption>=6.0.1` only) a named key, in the form of:
12
+ * `aws:<name>`, `gcp:<name>`, `local:<name>`, `kmip:<name>`, `azure:<name>`
13
+ * where `name` is an alphanumeric string, underscores allowed.
7
14
  */
8
- export type ClientEncryptionDataKeyProvider = 'aws' | 'azure' | 'gcp' | 'local' | 'kmip';
15
+ export type ClientEncryptionDataKeyProvider = string;
16
+
17
+ /** @public */
18
+ export interface AWSKMSProviderConfiguration {
19
+ /**
20
+ * The access key used for the AWS KMS provider
21
+ */
22
+ accessKeyId: string;
23
+
24
+ /**
25
+ * The secret access key used for the AWS KMS provider
26
+ */
27
+ secretAccessKey: string;
28
+
29
+ /**
30
+ * An optional AWS session token that will be used as the
31
+ * X-Amz-Security-Token header for AWS requests.
32
+ */
33
+ sessionToken?: string;
34
+ }
35
+
36
+ /** @public */
37
+ export interface LocalKMSProviderConfiguration {
38
+ /**
39
+ * The master key used to encrypt/decrypt data keys.
40
+ * A 96-byte long Buffer or base64 encoded string.
41
+ */
42
+ key: Buffer | string;
43
+ }
44
+
45
+ /** @public */
46
+ export interface KMIPKMSProviderConfiguration {
47
+ /**
48
+ * The output endpoint string.
49
+ * The endpoint consists of a hostname and port separated by a colon.
50
+ * E.g. "example.com:123". A port is always present.
51
+ */
52
+ endpoint?: string;
53
+ }
54
+
55
+ /** @public */
56
+ export type AzureKMSProviderConfiguration =
57
+ | {
58
+ /**
59
+ * The tenant ID identifies the organization for the account
60
+ */
61
+ tenantId: string;
62
+
63
+ /**
64
+ * The client ID to authenticate a registered application
65
+ */
66
+ clientId: string;
67
+
68
+ /**
69
+ * The client secret to authenticate a registered application
70
+ */
71
+ clientSecret: string;
72
+
73
+ /**
74
+ * If present, a host with optional port. E.g. "example.com" or "example.com:443".
75
+ * This is optional, and only needed if customer is using a non-commercial Azure instance
76
+ * (e.g. a government or China account, which use different URLs).
77
+ * Defaults to "login.microsoftonline.com"
78
+ */
79
+ identityPlatformEndpoint?: string | undefined;
80
+ }
81
+ | {
82
+ /**
83
+ * If present, an access token to authenticate with Azure.
84
+ */
85
+ accessToken: string;
86
+ };
87
+
88
+ /** @public */
89
+ export type GCPKMSProviderConfiguration =
90
+ | {
91
+ /**
92
+ * The service account email to authenticate
93
+ */
94
+ email: string;
95
+
96
+ /**
97
+ * A PKCS#8 encrypted key. This can either be a base64 string or a binary representation
98
+ */
99
+ privateKey: string | Buffer;
100
+
101
+ /**
102
+ * If present, a host with optional port. E.g. "example.com" or "example.com:443".
103
+ * Defaults to "oauth2.googleapis.com"
104
+ */
105
+ endpoint?: string | undefined;
106
+ }
107
+ | {
108
+ /**
109
+ * If present, an access token to authenticate with GCP.
110
+ */
111
+ accessToken: string;
112
+ };
9
113
 
10
114
  /**
11
115
  * @public
12
116
  * Configuration options that are used by specific KMS providers during key generation, encryption, and decryption.
117
+ *
118
+ * Named KMS providers _are not supported_ for automatic KMS credential fetching.
13
119
  */
14
120
  export interface KMSProviders {
15
121
  /**
16
122
  * Configuration options for using 'aws' as your KMS provider
17
123
  */
18
- aws?:
19
- | {
20
- /**
21
- * The access key used for the AWS KMS provider
22
- */
23
- accessKeyId: string;
24
-
25
- /**
26
- * The secret access key used for the AWS KMS provider
27
- */
28
- secretAccessKey: string;
29
-
30
- /**
31
- * An optional AWS session token that will be used as the
32
- * X-Amz-Security-Token header for AWS requests.
33
- */
34
- sessionToken?: string;
35
- }
36
- | Record<string, never>;
124
+ aws?: AWSKMSProviderConfiguration | Record<string, never>;
37
125
 
38
126
  /**
39
127
  * Configuration options for using 'local' as your KMS provider
40
128
  */
41
- local?: {
42
- /**
43
- * The master key used to encrypt/decrypt data keys.
44
- * A 96-byte long Buffer or base64 encoded string.
45
- */
46
- key: Buffer | string;
47
- };
129
+ local?: LocalKMSProviderConfiguration;
48
130
 
49
131
  /**
50
132
  * Configuration options for using 'kmip' as your KMS provider
51
133
  */
52
- kmip?: {
53
- /**
54
- * The output endpoint string.
55
- * The endpoint consists of a hostname and port separated by a colon.
56
- * E.g. "example.com:123". A port is always present.
57
- */
58
- endpoint?: string;
59
- };
134
+ kmip?: KMIPKMSProviderConfiguration;
60
135
 
61
136
  /**
62
137
  * Configuration options for using 'azure' as your KMS provider
63
138
  */
64
- azure?:
65
- | {
66
- /**
67
- * The tenant ID identifies the organization for the account
68
- */
69
- tenantId: string;
70
-
71
- /**
72
- * The client ID to authenticate a registered application
73
- */
74
- clientId: string;
75
-
76
- /**
77
- * The client secret to authenticate a registered application
78
- */
79
- clientSecret: string;
80
-
81
- /**
82
- * If present, a host with optional port. E.g. "example.com" or "example.com:443".
83
- * This is optional, and only needed if customer is using a non-commercial Azure instance
84
- * (e.g. a government or China account, which use different URLs).
85
- * Defaults to "login.microsoftonline.com"
86
- */
87
- identityPlatformEndpoint?: string | undefined;
88
- }
89
- | {
90
- /**
91
- * If present, an access token to authenticate with Azure.
92
- */
93
- accessToken: string;
94
- }
95
- | Record<string, never>;
139
+ azure?: AzureKMSProviderConfiguration | Record<string, never>;
96
140
 
97
141
  /**
98
142
  * Configuration options for using 'gcp' as your KMS provider
99
143
  */
100
- gcp?:
101
- | {
102
- /**
103
- * The service account email to authenticate
104
- */
105
- email: string;
106
-
107
- /**
108
- * A PKCS#8 encrypted key. This can either be a base64 string or a binary representation
109
- */
110
- privateKey: string | Buffer;
111
-
112
- /**
113
- * If present, a host with optional port. E.g. "example.com" or "example.com:443".
114
- * Defaults to "oauth2.googleapis.com"
115
- */
116
- endpoint?: string | undefined;
117
- }
118
- | {
119
- /**
120
- * If present, an access token to authenticate with GCP.
121
- */
122
- accessToken: string;
123
- }
124
- | Record<string, never>;
144
+ gcp?: GCPKMSProviderConfiguration | Record<string, never>;
145
+
146
+ [key: string]:
147
+ | AWSKMSProviderConfiguration
148
+ | LocalKMSProviderConfiguration
149
+ | KMIPKMSProviderConfiguration
150
+ | AzureKMSProviderConfiguration
151
+ | GCPKMSProviderConfiguration
152
+ | undefined;
125
153
  }
126
154
 
127
155
  /**
@@ -17,7 +17,7 @@ import { BufferPool, MongoDBCollectionNamespace, promiseWithResolvers } from '..
17
17
  import { type DataKey } from './client_encryption';
18
18
  import { MongoCryptError } from './errors';
19
19
  import { type MongocryptdManager } from './mongocryptd_manager';
20
- import { type ClientEncryptionDataKeyProvider, type KMSProviders } from './providers';
20
+ import { type KMSProviders } from './providers';
21
21
 
22
22
  let socks: SocksLib | null = null;
23
23
  function loadSocks(): SocksLib {
@@ -110,8 +110,23 @@ export type CSFLEKMSTlsOptions = {
110
110
  kmip?: ClientEncryptionTlsOptions;
111
111
  local?: ClientEncryptionTlsOptions;
112
112
  azure?: ClientEncryptionTlsOptions;
113
+
114
+ [key: string]: ClientEncryptionTlsOptions | undefined;
113
115
  };
114
116
 
117
+ /**
118
+ * This is kind of a hack. For `rewrapManyDataKey`, we have tests that
119
+ * guarantee that when there are no matching keys, `rewrapManyDataKey` returns
120
+ * nothing. We also have tests for auto encryption that guarantee for `encrypt`
121
+ * we return an error when there are no matching keys. This error is generated in
122
+ * subsequent iterations of the state machine.
123
+ * Some apis (`encrypt`) throw if there are no filter matches and others (`rewrapManyDataKey`)
124
+ * do not. We set the result manually here, and let the state machine continue. `libmongocrypt`
125
+ * will inform us if we need to error by setting the state to `MONGOCRYPT_CTX_ERROR` but
126
+ * otherwise we'll return `{ v: [] }`.
127
+ */
128
+ let EMPTY_V;
129
+
115
130
  /**
116
131
  * @internal
117
132
  *
@@ -154,16 +169,13 @@ export class StateMachine {
154
169
  /**
155
170
  * Executes the state machine according to the specification
156
171
  */
157
- async execute<T extends Document>(
158
- executor: StateMachineExecutable,
159
- context: MongoCryptContext
160
- ): Promise<T> {
172
+ async execute(executor: StateMachineExecutable, context: MongoCryptContext): Promise<Uint8Array> {
161
173
  const keyVaultNamespace = executor._keyVaultNamespace;
162
174
  const keyVaultClient = executor._keyVaultClient;
163
175
  const metaDataClient = executor._metaDataClient;
164
176
  const mongocryptdClient = executor._mongocryptdClient;
165
177
  const mongocryptdManager = executor._mongocryptdManager;
166
- let result: T | null = null;
178
+ let result: Uint8Array | null = null;
167
179
 
168
180
  while (context.state !== MONGOCRYPT_CTX_DONE && context.state !== MONGOCRYPT_CTX_ERROR) {
169
181
  debug(`[context#${context.id}] ${stateToString.get(context.state) || context.state}`);
@@ -211,16 +223,8 @@ export class StateMachine {
211
223
  const keys = await this.fetchKeys(keyVaultClient, keyVaultNamespace, filter);
212
224
 
213
225
  if (keys.length === 0) {
214
- // This is kind of a hack. For `rewrapManyDataKey`, we have tests that
215
- // guarantee that when there are no matching keys, `rewrapManyDataKey` returns
216
- // nothing. We also have tests for auto encryption that guarantee for `encrypt`
217
- // we return an error when there are no matching keys. This error is generated in
218
- // subsequent iterations of the state machine.
219
- // Some apis (`encrypt`) throw if there are no filter matches and others (`rewrapManyDataKey`)
220
- // do not. We set the result manually here, and let the state machine continue. `libmongocrypt`
221
- // will inform us if we need to error by setting the state to `MONGOCRYPT_CTX_ERROR` but
222
- // otherwise we'll return `{ v: [] }`.
223
- result = { v: [] } as any as T;
226
+ // See docs on EMPTY_V
227
+ result = EMPTY_V ??= serialize({ v: [] });
224
228
  }
225
229
  for await (const key of keys) {
226
230
  context.addMongoOperationResponse(serialize(key));
@@ -252,7 +256,7 @@ export class StateMachine {
252
256
  const message = context.status.message || 'Finalization error';
253
257
  throw new MongoCryptError(message);
254
258
  }
255
- result = deserialize(finalizedContext, this.options) as T;
259
+ result = finalizedContext;
256
260
  break;
257
261
  }
258
262
 
@@ -319,7 +323,7 @@ export class StateMachine {
319
323
 
320
324
  const tlsOptions = this.options.tlsOptions;
321
325
  if (tlsOptions) {
322
- const kmsProvider = request.kmsProvider as ClientEncryptionDataKeyProvider;
326
+ const kmsProvider = request.kmsProvider;
323
327
  const providerTlsOptions = tlsOptions[kmsProvider];
324
328
  if (providerTlsOptions) {
325
329
  const error = this.validateTlsOptions(kmsProvider, providerTlsOptions);
@@ -1,14 +1,15 @@
1
1
  import { type Readable, Transform, type TransformCallback } from 'stream';
2
2
  import { clearTimeout, setTimeout } from 'timers';
3
3
 
4
- import type { BSONSerializeOptions, Document, ObjectId } from '../bson';
5
- import type { AutoEncrypter } from '../client-side-encryption/auto_encrypter';
4
+ import { type BSONSerializeOptions, deserialize, type Document, type ObjectId } from '../bson';
5
+ import { type AutoEncrypter } from '../client-side-encryption/auto_encrypter';
6
6
  import {
7
7
  CLOSE,
8
8
  CLUSTER_TIME_RECEIVED,
9
9
  COMMAND_FAILED,
10
10
  COMMAND_STARTED,
11
11
  COMMAND_SUCCEEDED,
12
+ kDecorateResult,
12
13
  PINNED,
13
14
  UNPINNED
14
15
  } from '../constants';
@@ -19,8 +20,7 @@ import {
19
20
  MongoNetworkTimeoutError,
20
21
  MongoParseError,
21
22
  MongoServerError,
22
- MongoUnexpectedServerResponseError,
23
- MongoWriteConcernError
23
+ MongoUnexpectedServerResponseError
24
24
  } from '../error';
25
25
  import type { ServerApi, SupportedNodeConnectionOptions } from '../mongo_client';
26
26
  import { type MongoClientAuthProviders } from '../mongo_client_auth_providers';
@@ -33,6 +33,7 @@ import {
33
33
  BufferPool,
34
34
  calculateDurationInMs,
35
35
  type Callback,
36
+ decorateDecryptionResult,
36
37
  HostAddress,
37
38
  maxWireVersion,
38
39
  type MongoDBNamespace,
@@ -63,7 +64,7 @@ import { StreamDescription, type StreamDescriptionOptions } from './stream_descr
63
64
  import { type CompressorName, decompressResponse } from './wire_protocol/compression';
64
65
  import { onData } from './wire_protocol/on_data';
65
66
  import {
66
- isErrorResponse,
67
+ CursorResponse,
67
68
  MongoDBResponse,
68
69
  type MongoDBResponseConstructor
69
70
  } from './wire_protocol/responses';
@@ -448,12 +449,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
448
449
  this.socket.setTimeout(0);
449
450
  const bson = response.parse();
450
451
 
451
- const document =
452
- responseType == null
453
- ? new MongoDBResponse(bson)
454
- : isErrorResponse(bson)
455
- ? new MongoDBResponse(bson)
456
- : new responseType(bson);
452
+ const document = (responseType ?? MongoDBResponse).make(bson);
457
453
 
458
454
  yield document;
459
455
  this.throwIfAborted();
@@ -517,12 +513,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
517
513
  this.emit(Connection.CLUSTER_TIME_RECEIVED, document.$clusterTime);
518
514
  }
519
515
 
520
- if (document.has('writeConcernError')) {
521
- object ??= document.toObject(bsonOptions);
522
- throw new MongoWriteConcernError(object.writeConcernError, object);
523
- }
524
-
525
- if (document.isError) {
516
+ if (document.ok === 0) {
526
517
  throw new MongoServerError((object ??= document.toObject(bsonOptions)));
527
518
  }
528
519
 
@@ -552,40 +543,25 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
552
543
  }
553
544
  } catch (error) {
554
545
  if (this.shouldEmitAndLogCommand) {
555
- if (error.name === 'MongoWriteConcernError') {
556
- this.emitAndLogCommand(
557
- this.monitorCommands,
558
- Connection.COMMAND_SUCCEEDED,
559
- message.databaseName,
560
- this.established,
561
- new CommandSucceededEvent(
562
- this,
563
- message,
564
- options.noResponse ? undefined : (object ??= document?.toObject(bsonOptions)),
565
- started,
566
- this.description.serverConnectionId
567
- )
568
- );
569
- } else {
570
- this.emitAndLogCommand(
571
- this.monitorCommands,
572
- Connection.COMMAND_FAILED,
573
- message.databaseName,
574
- this.established,
575
- new CommandFailedEvent(
576
- this,
577
- message,
578
- error,
579
- started,
580
- this.description.serverConnectionId
581
- )
582
- );
583
- }
546
+ this.emitAndLogCommand(
547
+ this.monitorCommands,
548
+ Connection.COMMAND_FAILED,
549
+ message.databaseName,
550
+ this.established,
551
+ new CommandFailedEvent(this, message, error, started, this.description.serverConnectionId)
552
+ );
584
553
  }
585
554
  throw error;
586
555
  }
587
556
  }
588
557
 
558
+ public async command<T extends MongoDBResponseConstructor>(
559
+ ns: MongoDBNamespace,
560
+ command: Document,
561
+ options: CommandOptions | undefined,
562
+ responseType: T
563
+ ): Promise<InstanceType<T>>;
564
+
589
565
  public async command<T extends MongoDBResponseConstructor>(
590
566
  ns: MongoDBNamespace,
591
567
  command: Document,
@@ -749,7 +725,7 @@ export class CryptoConnection extends Connection {
749
725
  ns: MongoDBNamespace,
750
726
  cmd: Document,
751
727
  options?: CommandOptions,
752
- _responseType?: T | undefined
728
+ responseType?: T | undefined
753
729
  ): Promise<Document> {
754
730
  const { autoEncrypter } = this;
755
731
  if (!autoEncrypter) {
@@ -763,7 +739,7 @@ export class CryptoConnection extends Connection {
763
739
  const serverWireVersion = maxWireVersion(this);
764
740
  if (serverWireVersion === 0) {
765
741
  // This means the initial handshake hasn't happened yet
766
- return await super.command<T>(ns, cmd, options, undefined);
742
+ return await super.command<T>(ns, cmd, options, responseType);
767
743
  }
768
744
 
769
745
  if (serverWireVersion < 8) {
@@ -797,8 +773,28 @@ export class CryptoConnection extends Connection {
797
773
  }
798
774
  }
799
775
 
800
- const response = await super.command<T>(ns, encrypted, options, undefined);
776
+ const encryptedResponse = await super.command(
777
+ ns,
778
+ encrypted,
779
+ options,
780
+ // Eventually we want to require `responseType` which means we would satisfy `T` as the return type.
781
+ // In the meantime, we want encryptedResponse to always be _at least_ a MongoDBResponse if not a more specific subclass
782
+ // So that we can ensure we have access to the on-demand APIs for decorate response
783
+ responseType ?? MongoDBResponse
784
+ );
785
+
786
+ const result = await autoEncrypter.decrypt(encryptedResponse.toBytes(), options);
787
+
788
+ const decryptedResponse = responseType?.make(result) ?? deserialize(result, options);
789
+
790
+ if (autoEncrypter[kDecorateResult]) {
791
+ if (responseType == null) {
792
+ decorateDecryptionResult(decryptedResponse, encryptedResponse.toObject(), true);
793
+ } else if (decryptedResponse instanceof CursorResponse) {
794
+ decryptedResponse.encryptedResponse = encryptedResponse;
795
+ }
796
+ }
801
797
 
802
- return await autoEncrypter.decrypt(response, options);
798
+ return decryptedResponse;
803
799
  }
804
800
  }
@@ -66,9 +66,11 @@ export class OnDemandDocument {
66
66
  /** The start of the document */
67
67
  private readonly offset = 0,
68
68
  /** If this is an embedded document, indicates if this was a BSON array */
69
- public readonly isArray = false
69
+ public readonly isArray = false,
70
+ /** If elements was already calculated */
71
+ elements?: BSONElement[]
70
72
  ) {
71
- this.elements = parseToElementsToArray(this.bson, offset);
73
+ this.elements = elements ?? parseToElementsToArray(this.bson, offset);
72
74
  }
73
75
 
74
76
  /** Only supports basic latin strings */
@@ -78,8 +80,13 @@ export class OnDemandDocument {
78
80
 
79
81
  if (name.length !== nameLength) return false;
80
82
 
81
- for (let i = 0; i < name.length; i++) {
82
- if (this.bson[nameOffset + i] !== name.charCodeAt(i)) return false;
83
+ const nameEnd = nameOffset + nameLength;
84
+ for (
85
+ let byteIndex = nameOffset, charIndex = 0;
86
+ charIndex < name.length && byteIndex < nameEnd;
87
+ charIndex++, byteIndex++
88
+ ) {
89
+ if (this.bson[byteIndex] !== name.charCodeAt(charIndex)) return false;
83
90
  }
84
91
 
85
92
  return true;
@@ -125,7 +132,7 @@ export class OnDemandDocument {
125
132
  const element = this.elements[index];
126
133
 
127
134
  // skip this element if it has already been associated with a name
128
- if (!this.indexFound[index] && this.isElementName(name, element)) {
135
+ if (!(index in this.indexFound) && this.isElementName(name, element)) {
129
136
  const cachedElement = { element, value: undefined };
130
137
  this.cache[name] = cachedElement;
131
138
  this.indexFound[index] = true;
@@ -247,7 +254,7 @@ export class OnDemandDocument {
247
254
  public get<const T extends keyof JSTypeOf>(
248
255
  name: string | number,
249
256
  as: T,
250
- required?: false | undefined
257
+ required?: boolean | undefined
251
258
  ): JSTypeOf[T] | null;
252
259
 
253
260
  /** `required` will make `get` throw if name does not exist or is null/undefined */