Skip to content

Commit d2ad07f

Browse files
feat(NODE-7385): add experimental os runtime adapter (#4851)
1 parent 00f3232 commit d2ad07f

File tree

15 files changed

+194
-45
lines changed

15 files changed

+194
-45
lines changed

.eslintrc.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@
276276
"patterns": [
277277
"**/../lib/**",
278278
"mongodb-mock-server",
279-
"node:*"
279+
"node:*",
280+
"os"
280281
],
281282
"paths": [
282283
{
@@ -327,4 +328,4 @@
327328
}
328329
}
329330
]
330-
}
331+
}

src/cmap/auth/gssapi.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as dns from 'dns';
2-
import * as os from 'os';
32

43
import { getKerberos, type Kerberos, type KerberosClient } from '../../deps';
54
import { MongoInvalidArgumentError, MongoMissingCredentialsError } from '../../error';
@@ -69,9 +68,13 @@ export class GSSAPI extends AuthProvider {
6968
}
7069
}
7170

72-
async function makeKerberosClient(authContext: AuthContext): Promise<KerberosClient> {
73-
const { hostAddress } = authContext.options;
74-
const { credentials } = authContext;
71+
async function makeKerberosClient({
72+
options: {
73+
hostAddress,
74+
runtime: { os }
75+
},
76+
credentials
77+
}: AuthContext): Promise<KerberosClient> {
7578
if (!hostAddress || typeof hostAddress.host !== 'string' || !credentials) {
7679
throw new MongoInvalidArgumentError(
7780
'Connection must have host and port and credentials defined.'

src/cmap/connection.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { type MongoClientAuthProviders } from '../mongo_client_auth_providers';
3535
import { MongoLoggableComponent, type MongoLogger, SeverityLevel } from '../mongo_logger';
3636
import { type Abortable, type CancellationToken, TypedEventEmitter } from '../mongo_types';
3737
import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
38+
import { type Runtime } from '../runtime_adapters';
3839
import { ServerType } from '../sdam/common';
3940
import { applySession, type ClientSession, updateSessionFromResponse } from '../sessions';
4041
import { type TimeoutContext, TimeoutError } from '../timeout';
@@ -143,6 +144,8 @@ export interface ConnectionOptions
143144
metadata: Promise<ClientMetadata>;
144145
/** @internal */
145146
mongoLogger?: MongoLogger | undefined;
147+
/** @internal */
148+
runtime: Runtime;
146149
}
147150

148151
/** @public */

src/cmap/handshake/client_metadata.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import * as os from 'os';
21
import * as process from 'process';
32

43
import { BSON, type Document, Int32, NumberUtils } from '../../bson';
@@ -96,7 +95,8 @@ export class LimitedSizeDocument {
9695
}
9796
}
9897

99-
type MakeClientMetadataOptions = Pick<MongoOptions, 'appName'>;
98+
type MakeClientMetadataOptions = Pick<MongoOptions, 'appName' | 'runtime'>;
99+
100100
/**
101101
* From the specs:
102102
* Implementors SHOULD cumulatively update fields in the following order until the document is under the size limit:
@@ -107,7 +107,7 @@ type MakeClientMetadataOptions = Pick<MongoOptions, 'appName'>;
107107
*/
108108
export async function makeClientMetadata(
109109
driverInfoList: DriverInfo[],
110-
{ appName = '' }: MakeClientMetadataOptions
110+
{ appName = '', runtime: { os } }: MakeClientMetadataOptions
111111
): Promise<ClientMetadata> {
112112
const metadataDocument = new LimitedSizeDocument(512);
113113

src/connection_string.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
import { MongoLoggableComponent, MongoLogger, SeverityLevel } from './mongo_logger';
2121
import { ReadConcern, type ReadConcernLevel } from './read_concern';
2222
import { ReadPreference, type ReadPreferenceMode } from './read_preference';
23+
import { resolveRuntimeAdapters } from './runtime_adapters';
2324
import { ServerMonitoringMode } from './sdam/monitor';
2425
import type { TagSet } from './sdam/server_description';
2526
import {
@@ -538,6 +539,8 @@ export function parseOptions(
538539
}
539540
);
540541

542+
mongoOptions.runtime = resolveRuntimeAdapters(options);
543+
541544
return mongoOptions;
542545
}
543546

@@ -1061,6 +1064,9 @@ export const OPTIONS = {
10611064
default: true,
10621065
type: 'boolean'
10631066
},
1067+
runtimeAdapters: {
1068+
type: 'record'
1069+
},
10641070
serializeFunctions: {
10651071
type: 'boolean'
10661072
},

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,7 @@ export type {
562562
ReadPreferenceLikeOptions,
563563
ReadPreferenceOptions
564564
} from './read_preference';
565+
export type { OsAdapter, Runtime, RuntimeAdapters } from './runtime_adapters';
565566
export type { ClusterTime } from './sdam/common';
566567
export type {
567568
Monitor,

src/mongo_client.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import { EndSessionsOperation } from './operations/end_sessions';
4646
import { executeOperation } from './operations/execute_operation';
4747
import type { ReadConcern, ReadConcernLevel, ReadConcernLike } from './read_concern';
4848
import { ReadPreference, type ReadPreferenceMode } from './read_preference';
49+
import { type Runtime, type RuntimeAdapters } from './runtime_adapters';
4950
import type { ServerMonitoringMode } from './sdam/monitor';
5051
import type { TagSet } from './sdam/server_description';
5152
import { DeprioritizedServers, readPreferenceServerSelector } from './sdam/server_selection';
@@ -318,6 +319,12 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
318319
connectionType?: typeof Connection;
319320
/** @internal */
320321
__skipPingOnConnect?: boolean;
322+
/**
323+
* @experimental
324+
*
325+
* If provided, any adapters provided will be used in place of the corresponding Node.js module.
326+
*/
327+
runtimeAdapters?: RuntimeAdapters;
321328
}
322329

323330
/** @public */
@@ -1152,4 +1159,7 @@ export interface MongoOptions
11521159
timeoutMS?: number;
11531160
/** @internal */
11541161
__skipPingOnConnect?: boolean;
1162+
1163+
/** @internal */
1164+
runtime: Runtime;
11551165
}

src/runtime_adapters.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/* eslint-disable no-restricted-imports, @typescript-eslint/no-require-imports */
2+
3+
// We squash the restricted import errors here because we are using type-only imports, which
4+
// do not impact the driver's actual runtime dependencies.
5+
// We also allow restricted imports in this file, because we expect this file to be the only place actually importing restricted Node APIs.
6+
7+
import type * as os from 'os';
8+
9+
import { type MongoClientOptions } from './mongo_client';
10+
11+
/**
12+
* @public
13+
* @experimental
14+
*
15+
* Represents the set of dependencies that the driver uses from the [Node.js OS module](https://nodejs.org/api/os.html).
16+
*/
17+
export type OsAdapter = Pick<typeof os, 'release' | 'platform' | 'arch' | 'type'>;
18+
19+
/**
20+
* @public
21+
* @experimental
22+
*
23+
* This type represents the set of dependencies that the driver needs from the Javascript runtime in order to function.
24+
*/
25+
export interface RuntimeAdapters {
26+
os?: OsAdapter;
27+
}
28+
29+
/**
30+
* @internal
31+
*
32+
* Represents a complete, parsed set of runtime adapters. After options parsing, all adapters
33+
* are always present (either using the user's provided adapter, or defaulting to the Node.js module).
34+
*/
35+
export interface Runtime {
36+
os: OsAdapter;
37+
}
38+
39+
/**
40+
* @internal
41+
*
42+
* Given a MongoClientOptions, this function resolves the set of runtime options, providing Nodejs implementations if
43+
* not provided by in `options`, and returns a `Runtime`.
44+
*/
45+
export function resolveRuntimeAdapters(options: MongoClientOptions): Runtime {
46+
return {
47+
os: options.runtimeAdapters?.os ?? require('os')
48+
};
49+
}

test/integration/connection-monitoring-and-pooling/connection.test.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { LEGACY_HELLO_COMMAND } from '../../../src/constants';
2222
import { Topology } from '../../../src/sdam/topology';
2323
import { HostAddress, ns } from '../../../src/utils';
2424
import * as mock from '../../tools/mongodb-mock/index';
25-
import { processTick, sleep } from '../../tools/utils';
25+
import { processTick, runtime, sleep } from '../../tools/utils';
2626
import { assert as test, setupDatabase } from '../shared';
2727

2828
const commonConnectOptions = {
@@ -49,7 +49,10 @@ describe('Connection', function () {
4949
...commonConnectOptions,
5050
connectionType: Connection,
5151
...this.configuration.options,
52-
metadata: makeClientMetadata([], {})
52+
metadata: makeClientMetadata([], {
53+
runtime
54+
}),
55+
runtime
5356
};
5457

5558
let conn;
@@ -71,7 +74,8 @@ describe('Connection', function () {
7174
connectionType: Connection,
7275
...this.configuration.options,
7376
monitorCommands: true,
74-
metadata: makeClientMetadata([], {})
77+
runtime,
78+
metadata: makeClientMetadata([], { runtime })
7579
};
7680

7781
let conn;
@@ -102,7 +106,10 @@ describe('Connection', function () {
102106
connectionType: Connection,
103107
...this.configuration.options,
104108
monitorCommands: true,
105-
metadata: makeClientMetadata([], {})
109+
runtime,
110+
metadata: makeClientMetadata([], {
111+
runtime
112+
})
106113
};
107114

108115
let conn;

test/tools/utils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ import {
1818
type HostAddress,
1919
MongoClient,
2020
type MongoClientOptions,
21+
type Runtime,
2122
type ServerApiVersion,
2223
type TopologyOptions
2324
} from '../../src';
2425
import { OP_MSG } from '../../src/cmap/wire_protocol/constants';
26+
import { resolveRuntimeAdapters } from '../../src/runtime_adapters';
2527
import { Topology } from '../../src/sdam/topology';
2628
import { processTimeMS } from '../../src/utils';
2729
import { type TestConfiguration } from './runner/config';
@@ -604,3 +606,8 @@ export function configureMongocryptdSpawnHooks(
604606
port
605607
};
606608
}
609+
610+
/**
611+
* A `Runtime` that resolves to entirely Nodejs modules, useful when tests must provide a default `runtime` object to an API.
612+
*/
613+
export const runtime: Runtime = resolveRuntimeAdapters({});

0 commit comments

Comments
 (0)