From 158aad22c1090cd2be4ac908800d986a30ada91f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Thu, 23 Oct 2025 16:29:50 +0200 Subject: [PATCH 1/9] Add a new identifyServerName function --- packages/mongodb-build-info/src/index.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/mongodb-build-info/src/index.ts b/packages/mongodb-build-info/src/index.ts index ed75e9b6..ae9d2423 100644 --- a/packages/mongodb-build-info/src/index.ts +++ b/packages/mongodb-build-info/src/index.ts @@ -1,5 +1,7 @@ import ConnectionString from 'mongodb-connection-string-url'; +type Document = Record; + const ATLAS_REGEX = /\.mongodb(-dev|-qa|-stage)?\.net$/i; const ATLAS_STREAM_REGEX = /^atlas-stream-.+/i; const LOCALHOST_REGEX = @@ -134,6 +136,28 @@ export function getGenuineMongoDB(uri: string): { }; } +export async function identifyServerName( + uri: string, + runCommand: (command: Document) => Promise, +): Promise { + const hostname = getHostnameFromUrl(uri); + if (hostname.match(COSMOS_DB_REGEX)) { + return 'cosmosdb'; + } + + if (hostname.match(DOCUMENT_DB_REGEX)) { + return 'documentdb'; + } + + const buildInfo = await runCommand({ buildInfo: 1 }); + + if ('ferretdb' in buildInfo) { + return 'ferretdb'; + } + + return 'mongodb'; +} + export function getBuildEnv(buildInfo: unknown): { serverOs: string | null; serverArch: string | null; From ccfd719db68196ac11508e5e1af8947a19ae3c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Thu, 23 Oct 2025 16:41:36 +0200 Subject: [PATCH 2/9] Add firestore regexp --- packages/mongodb-build-info/src/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/mongodb-build-info/src/index.ts b/packages/mongodb-build-info/src/index.ts index ae9d2423..4f9152fe 100644 --- a/packages/mongodb-build-info/src/index.ts +++ b/packages/mongodb-build-info/src/index.ts @@ -9,6 +9,7 @@ const LOCALHOST_REGEX = const DIGITAL_OCEAN_REGEX = /\.mongo\.ondigitalocean\.com$/i; const COSMOS_DB_REGEX = /\.cosmos\.azure\.com$/i; const DOCUMENT_DB_REGEX = /docdb(-elastic)?\.amazonaws\.com$/i; +const FIRESTORE_REGEX = /\.firestore.goog$/i; function isRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null; @@ -149,6 +150,10 @@ export async function identifyServerName( return 'documentdb'; } + if (hostname.match(FIRESTORE_REGEX)) { + return 'firestore'; + } + const buildInfo = await runCommand({ buildInfo: 1 }); if ('ferretdb' in buildInfo) { From b2eb32d6a3e0f9302e31d966240978aa675c25c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Fri, 24 Oct 2025 14:54:00 +0200 Subject: [PATCH 3/9] Add pg_documentdb --- packages/mongodb-build-info/src/index.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/mongodb-build-info/src/index.ts b/packages/mongodb-build-info/src/index.ts index 4f9152fe..1a04bedf 100644 --- a/packages/mongodb-build-info/src/index.ts +++ b/packages/mongodb-build-info/src/index.ts @@ -140,6 +140,7 @@ export function getGenuineMongoDB(uri: string): { export async function identifyServerName( uri: string, runCommand: (command: Document) => Promise, + adminCommand: (command: Document) => Promise, ): Promise { const hostname = getHostnameFromUrl(uri); if (hostname.match(COSMOS_DB_REGEX)) { @@ -160,6 +161,14 @@ export async function identifyServerName( return 'ferretdb'; } + try { + await adminCommand({ getParameter: 'foo' }); + } catch (error) { + if (error instanceof Error && /documentdb_api/.test(error.message)) { + return 'pg_documentdb'; + } + } + return 'mongodb'; } From 10f1273b3cf2d8743f318657adf309862327f1ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Fri, 24 Oct 2025 14:57:35 +0200 Subject: [PATCH 4/9] Deprecate `getGenuineMongoDB` --- packages/mongodb-build-info/src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/mongodb-build-info/src/index.ts b/packages/mongodb-build-info/src/index.ts index 1a04bedf..ea58643d 100644 --- a/packages/mongodb-build-info/src/index.ts +++ b/packages/mongodb-build-info/src/index.ts @@ -112,6 +112,9 @@ export function isDigitalOcean(uri: string): boolean { return !!getHostnameFromUrl(uri).match(DIGITAL_OCEAN_REGEX); } +/** + * @deprecated Use `identifyServerName` instead. + */ export function getGenuineMongoDB(uri: string): { isGenuine: boolean; serverName: string; From 9cbf143e15958cfcda231b22d7efeceffad31f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Fri, 24 Oct 2025 15:18:12 +0200 Subject: [PATCH 5/9] Add tests --- packages/mongodb-build-info/test/fixtures.ts | 4 ++ .../mongodb-build-info/test/index.spec.ts | 65 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/packages/mongodb-build-info/test/fixtures.ts b/packages/mongodb-build-info/test/fixtures.ts index 775e010e..006610c6 100644 --- a/packages/mongodb-build-info/test/fixtures.ts +++ b/packages/mongodb-build-info/test/fixtures.ts @@ -80,6 +80,10 @@ export const DOCUMENT_DB_URIS = [ 'mongodb://x:y@elastic-docdb-123456789.eu-central-1.docdb-elastic.amazonaws.com:27017', ]; +export const FIRESTORE_URIS = [ + 'mongodb://x:y@bbccdaf5-527a-4be5-9881-b7073e92002b.europe-north2.firestore.goog:443/test-db?loadBalanced=true&tls=true&authMechanism=SCRAM-SHA-256&retryWrites=false', +]; + export const COSMOSDB_BUILD_INFO = { _t: 'BuildInfoResponse', ok: 1, diff --git a/packages/mongodb-build-info/test/index.spec.ts b/packages/mongodb-build-info/test/index.spec.ts index ca5989c6..03a4af18 100644 --- a/packages/mongodb-build-info/test/index.spec.ts +++ b/packages/mongodb-build-info/test/index.spec.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; + import * as fixtures from './fixtures'; import { isAtlas, @@ -10,6 +11,7 @@ import { getBuildEnv, isEnterprise, getGenuineMongoDB, + identifyServerName, } from '../src/index'; describe('mongodb-build-info', function () { @@ -428,4 +430,67 @@ describe('mongodb-build-info', function () { expect(isGenuine.serverName).to.equal('mongodb'); }); }); + + context('identifyServerName', function () { + function fail() { + return Promise.reject(new Error('Should not be called')); + } + + it('reports CosmosDB', async function () { + for (const uri of fixtures.COSMOS_DB_URI) { + const result = await identifyServerName(uri, fail, fail); + expect(result).to.equal('cosmosdb'); + } + }); + + it('reports DocumentDB', async function () { + for (const uri of fixtures.DOCUMENT_DB_URIS) { + const result = await identifyServerName(uri, fail, fail); + expect(result).to.equal('documentdb'); + } + }); + + it('reports Firestore', async function () { + for (const uri of fixtures.FIRESTORE_URIS) { + const result = await identifyServerName(uri, fail, fail); + expect(result).to.equal('firestore'); + } + }); + + it('reports FerretDB', async function () { + const result = await identifyServerName( + '', + (req) => { + if ('buildInfo' in req) { + return Promise.resolve({ + ferretdb: {}, + }); + } else { + return Promise.resolve({}); + } + }, + fail, + ); + expect(result).to.equal('ferretdb'); + }); + + it('reports PG DocumentDB', async function () { + const result = await identifyServerName( + '', + () => Promise.resolve({}), + (req) => { + if ('getParameter' in req) { + return Promise.reject( + new Error( + 'function documentdb_api.get_parameter(boolean, boolean, text[]) does not exist', + ), + ); + } else { + return Promise.resolve({}); + } + }, + ); + expect(result).to.equal('pg_documentdb'); + }); + }); }); From 8e9b04a9d1304d52c802d690a2e9c0d4f92815be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Fri, 24 Oct 2025 22:17:33 +0200 Subject: [PATCH 6/9] Add debug calls --- package-lock.json | 2 ++ packages/mongodb-build-info/package.json | 1 + packages/mongodb-build-info/src/index.ts | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/package-lock.json b/package-lock.json index 96c0a173..9f44afd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30589,6 +30589,7 @@ "version": "1.7.4", "license": "Apache-2.0", "dependencies": { + "debug": "^4.4.0", "mongodb-connection-string-url": "^3.0.0" }, "devDependencies": { @@ -50739,6 +50740,7 @@ "@mongodb-js/tsconfig-devtools": "^1.0.4", "@types/mocha": "^9.1.1", "chai": "^4.5.0", + "debug": "^4.4.0", "depcheck": "^1.4.7", "eslint": "^7.25.0", "mocha": "^8.4.0", diff --git a/packages/mongodb-build-info/package.json b/packages/mongodb-build-info/package.json index 0c180a76..e804a73f 100644 --- a/packages/mongodb-build-info/package.json +++ b/packages/mongodb-build-info/package.json @@ -77,6 +77,7 @@ "typescript": "^5.0.4" }, "dependencies": { + "debug": "^4.4.0", "mongodb-connection-string-url": "^3.0.0" } } diff --git a/packages/mongodb-build-info/src/index.ts b/packages/mongodb-build-info/src/index.ts index ea58643d..3f21fb22 100644 --- a/packages/mongodb-build-info/src/index.ts +++ b/packages/mongodb-build-info/src/index.ts @@ -1,4 +1,7 @@ import ConnectionString from 'mongodb-connection-string-url'; +import { debug as createDebug } from 'debug'; + +const debug = createDebug('mongodb-build-info'); type Document = Record; @@ -167,6 +170,7 @@ export async function identifyServerName( try { await adminCommand({ getParameter: 'foo' }); } catch (error) { + debug('getParameter command failed %O', error); if (error instanceof Error && /documentdb_api/.test(error.message)) { return 'pg_documentdb'; } From cb1e7f7744f40e87e1a4a21b4e70ddbf189fccea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Fri, 24 Oct 2025 22:18:04 +0200 Subject: [PATCH 7/9] Fail gracefully if the buildInfo command fails --- packages/mongodb-build-info/src/index.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/mongodb-build-info/src/index.ts b/packages/mongodb-build-info/src/index.ts index 3f21fb22..7dbb0756 100644 --- a/packages/mongodb-build-info/src/index.ts +++ b/packages/mongodb-build-info/src/index.ts @@ -161,10 +161,15 @@ export async function identifyServerName( return 'firestore'; } - const buildInfo = await runCommand({ buildInfo: 1 }); + try { + const buildInfo = await adminCommand({ buildInfo: 1 }); - if ('ferretdb' in buildInfo) { - return 'ferretdb'; + if ('ferretdb' in buildInfo) { + return 'ferretdb'; + } + } catch (error) { + debug('buildInfo command failed %O', error); + return 'unknown'; } try { From 4d2ef76d305e8fe8e5ee26fe2a323672aa39b5aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Fri, 24 Oct 2025 22:20:15 +0200 Subject: [PATCH 8/9] Update signature of identifyServerName to take and options object and only adminCommand --- packages/mongodb-build-info/src/index.ts | 20 +++++++--- .../mongodb-build-info/test/index.spec.ts | 39 +++++++++++-------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/packages/mongodb-build-info/src/index.ts b/packages/mongodb-build-info/src/index.ts index 7dbb0756..9ff6176e 100644 --- a/packages/mongodb-build-info/src/index.ts +++ b/packages/mongodb-build-info/src/index.ts @@ -143,12 +143,20 @@ export function getGenuineMongoDB(uri: string): { }; } -export async function identifyServerName( - uri: string, - runCommand: (command: Document) => Promise, - adminCommand: (command: Document) => Promise, -): Promise { - const hostname = getHostnameFromUrl(uri); +type IdentifyServerNameOptions = { + connectionString: string; + adminCommand: (command: Document) => Promise; +}; + +/** + * Identify the server name based on connection string and server responses. + * @returns A name of the server, "unknown" if we fail to identify it. + */ +export async function identifyServerName({ + connectionString, + adminCommand, +}: IdentifyServerNameOptions): Promise { + const hostname = getHostnameFromUrl(connectionString); if (hostname.match(COSMOS_DB_REGEX)) { return 'cosmosdb'; } diff --git a/packages/mongodb-build-info/test/index.spec.ts b/packages/mongodb-build-info/test/index.spec.ts index 03a4af18..f206b143 100644 --- a/packages/mongodb-build-info/test/index.spec.ts +++ b/packages/mongodb-build-info/test/index.spec.ts @@ -437,30 +437,39 @@ describe('mongodb-build-info', function () { } it('reports CosmosDB', async function () { - for (const uri of fixtures.COSMOS_DB_URI) { - const result = await identifyServerName(uri, fail, fail); + for (const connectionString of fixtures.COSMOS_DB_URI) { + const result = await identifyServerName({ + connectionString, + adminCommand: fail, + }); expect(result).to.equal('cosmosdb'); } }); it('reports DocumentDB', async function () { - for (const uri of fixtures.DOCUMENT_DB_URIS) { - const result = await identifyServerName(uri, fail, fail); + for (const connectionString of fixtures.DOCUMENT_DB_URIS) { + const result = await identifyServerName({ + connectionString, + adminCommand: fail, + }); expect(result).to.equal('documentdb'); } }); it('reports Firestore', async function () { - for (const uri of fixtures.FIRESTORE_URIS) { - const result = await identifyServerName(uri, fail, fail); + for (const connectionString of fixtures.FIRESTORE_URIS) { + const result = await identifyServerName({ + connectionString, + adminCommand: fail, + }); expect(result).to.equal('firestore'); } }); it('reports FerretDB', async function () { - const result = await identifyServerName( - '', - (req) => { + const result = await identifyServerName({ + connectionString: '', + adminCommand(req) { if ('buildInfo' in req) { return Promise.resolve({ ferretdb: {}, @@ -469,16 +478,14 @@ describe('mongodb-build-info', function () { return Promise.resolve({}); } }, - fail, - ); + }); expect(result).to.equal('ferretdb'); }); it('reports PG DocumentDB', async function () { - const result = await identifyServerName( - '', - () => Promise.resolve({}), - (req) => { + const result = await identifyServerName({ + connectionString: '', + adminCommand(req) { if ('getParameter' in req) { return Promise.reject( new Error( @@ -489,7 +496,7 @@ describe('mongodb-build-info', function () { return Promise.resolve({}); } }, - ); + }); expect(result).to.equal('pg_documentdb'); }); }); From 6adc59d6d69ab108815aa27ce72eddf13909a0ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Mon, 27 Oct 2025 12:05:11 +0100 Subject: [PATCH 9/9] Refactored to call commands in parallel --- packages/mongodb-build-info/src/index.ts | 72 +++++++++++++++--------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/packages/mongodb-build-info/src/index.ts b/packages/mongodb-build-info/src/index.ts index 9ff6176e..dfde7a08 100644 --- a/packages/mongodb-build-info/src/index.ts +++ b/packages/mongodb-build-info/src/index.ts @@ -156,40 +156,58 @@ export async function identifyServerName({ connectionString, adminCommand, }: IdentifyServerNameOptions): Promise { - const hostname = getHostnameFromUrl(connectionString); - if (hostname.match(COSMOS_DB_REGEX)) { - return 'cosmosdb'; - } - - if (hostname.match(DOCUMENT_DB_REGEX)) { - return 'documentdb'; - } + try { + const hostname = getHostnameFromUrl(connectionString); + if (hostname.match(COSMOS_DB_REGEX)) { + return 'cosmosdb'; + } - if (hostname.match(FIRESTORE_REGEX)) { - return 'firestore'; - } + if (hostname.match(DOCUMENT_DB_REGEX)) { + return 'documentdb'; + } - try { - const buildInfo = await adminCommand({ buildInfo: 1 }); + if (hostname.match(FIRESTORE_REGEX)) { + return 'firestore'; + } - if ('ferretdb' in buildInfo) { - return 'ferretdb'; + const candidates = await Promise.all([ + adminCommand({ buildInfo: 1 }).then( + (response) => { + if ('ferretdb' in response) { + return ['ferretdb']; + } else { + return []; + } + }, + (error: unknown) => { + debug('buildInfo command failed %O', error); + return []; + }, + ), + adminCommand({ getParameter: 'foo' }).then( + // A successful response doesn't represent a signal + () => [], + (error: unknown) => { + if (error instanceof Error && /documentdb_api/.test(error.message)) { + return ['pg_documentdb']; + } else { + return []; + } + }, + ), + ]).then((results) => results.flat()); + + if (candidates.length === 0) { + return 'mongodb'; + } else if (candidates.length === 1) { + return candidates[0]; + } else { + return 'unknown'; } } catch (error) { - debug('buildInfo command failed %O', error); + debug('Failed to identify server name', error); return 'unknown'; } - - try { - await adminCommand({ getParameter: 'foo' }); - } catch (error) { - debug('getParameter command failed %O', error); - if (error instanceof Error && /documentdb_api/.test(error.message)) { - return 'pg_documentdb'; - } - } - - return 'mongodb'; } export function getBuildEnv(buildInfo: unknown): {