Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions packages/devtools-connect/src/fast-failure-connect.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,71 @@ describe('isFastFailureConnectionError', function () {
isFastFailureConnectionError(new Error('could not connect')),
).to.equal(false);
});

describe('isCompassSocketServiceError', function () {
class CompassSocketServiceError extends Error {
constructor(
msg: string,
public code: number,
) {
super(msg);
this.name = 'CompassSocketServiceError';
}
}

it('returns true for UNAUTHORIZED (3000)', function () {
const error = new CompassSocketServiceError('Unauthorized', 3000);
expect(isFastFailureConnectionError(error)).to.equal(true);
});

it('returns true for FORBIDDEN (3003)', function () {
const error = new CompassSocketServiceError('Forbidden', 3003);
expect(isFastFailureConnectionError(error)).to.equal(true);
});

it('returns true for NOT_FOUND (4004)', function () {
const error = new CompassSocketServiceError('Not found', 4004);
expect(isFastFailureConnectionError(error)).to.equal(true);
});

it('returns true for VIOLATED_POLICY (1008)', function () {
const error = new CompassSocketServiceError('Violated policy', 1008);
expect(isFastFailureConnectionError(error)).to.equal(true);
});

it('returns true for DO_NOT_TRY_AGAIN (4101)', function () {
const error = new CompassSocketServiceError('Do not try again', 4101);
expect(isFastFailureConnectionError(error)).to.equal(true);
});

it('returns false for CompassSocketServiceError with non-fail-fast code', function () {
const error = new CompassSocketServiceError('Some other error', 9999);
expect(isFastFailureConnectionError(error)).to.equal(false);
});

it('returns true when CompassSocketServiceError is the cause of MongoNetworkError', function () {
const cause = new CompassSocketServiceError('Unauthorized', 3000);
const error = new MongoNetworkError('Connection failed');
(error as any).cause = cause;
expect(isFastFailureConnectionError(error)).to.equal(true);
});

it('returns true when CompassSocketServiceError is nested deeply', function () {
const cause = new CompassSocketServiceError('Forbidden', 3003);
const wrappedError = new Error('Wrapped error');
(wrappedError as any).cause = cause;
const error = new MongoNetworkError('Connection failed');
(error as any).cause = wrappedError;
expect(isFastFailureConnectionError(error)).to.equal(true);
});

it('returns true when CompassSocketServiceError is in an AggregateError', function () {
const cause = new CompassSocketServiceError('Not found', 4004);
const aggregateError = new AggregateError(
[new Error('Other error'), cause],
'Multiple errors',
);
expect(isFastFailureConnectionError(aggregateError)).to.equal(true);
});
});
});
41 changes: 34 additions & 7 deletions packages/devtools-connect/src/fast-failure-connect.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,48 @@
// It probably makes sense to put this into its own package/repository once
// other tools start using it.

const NODE_SOCKET_NON_RETRY_CODES = [
'ECONNREFUSED',
'ENOTFOUND',
'ENETUNREACH',
'EINVAL',
];
const COMPASS_SOCKET_SERVICE_NON_RETRY_CODES = [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

COMPASS_SOCKET_SERVICE

Are we renaming CCS? 🙂

Copy link
Member

@Anemy Anemy Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use the acronym CSS 🙂 /s

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

( My not-so subtle move towards that direction........... 👀 ) happy to pick another name

3000, // UNAUTHORIZED
3003, // FORBIDDEN
4004, // NOT_FOUND
1008, // VIOLATED_POLICY
4101, // DO_NOT_TRY_AGAIN
];

const isCompassSocketServiceError = handleNestedErrors(
(error: Error & { code?: string | number }): boolean => {
if (error.name === 'CompassSocketServiceError') {
return (
typeof error.code === 'number' &&
COMPASS_SOCKET_SERVICE_NON_RETRY_CODES.includes(error.code)
);
}
return false;
},
);

function isFastFailureConnectionSingleError(
error: Error & { code?: string },
error: Error & { code?: string | number },
): boolean {
switch (error.name) {
case 'MongoNetworkError':
return /\b(ECONNREFUSED|ENOTFOUND|ENETUNREACH|EINVAL)\b/.test(
error.message,
);
return new RegExp(
String.raw`\b(${NODE_SOCKET_NON_RETRY_CODES.join('|')})\b`,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def overkill for me to hoist the node error codes but figured the pattern matched my new front end code stuff... this stuff is very unchanging so we can go back to not changing it. 😅

).test(error.message);
case 'MongoError':
return /The apiVersion parameter is required/.test(error.message);
default:
return (
['ECONNREFUSED', 'ENOTFOUND', 'ENETUNREACH', 'EINVAL'].includes(
error.code ?? '',
) || isPotentialTLSCertificateError(error)
(typeof error.code === 'string' &&
NODE_SOCKET_NON_RETRY_CODES.includes(error.code)) ||
isPotentialTLSCertificateError(error) ||
isCompassSocketServiceError(error)
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools-connect/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"extends": "@mongodb-js/tsconfig-devtools/tsconfig.common.json",
"compilerOptions": {
"target": "es2020",
"lib": ["es2020", "DOM"],
"lib": ["es2021", "DOM"],
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just cus AggregateError was missing in the test type check, I think this is also ok for the shipped code

"module": "commonjs",
"moduleResolution": "node"
}
Expand Down