Skip to content
Merged
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@
"author": "",
"license": "MIT",
"dependencies": {
"@aws-sdk/rds-signer": "^3.1001.0",
"@azure/identity": "^4.8.0",
"@iarna/toml": "^2.2.5",
"@modelcontextprotocol/sdk": "^1.25.1",
"dotenv": "^16.4.7",
Expand All @@ -51,6 +49,8 @@
"zod": "^3.24.2"
},
"optionalDependencies": {
"@aws-sdk/rds-signer": "^3.1001.0",
"@azure/identity": "^4.8.0",
"better-sqlite3": "^11.9.0",
"mariadb": "^3.4.0",
"mssql": "^11.0.1",
Expand Down
13 changes: 12 additions & 1 deletion src/connectors/sqlserver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
ExecuteOptions,
ConnectorConfig,
} from "../interface.js";
import { DefaultAzureCredential } from "@azure/identity";
import { isDriverNotInstalled } from "../../utils/module-loader.js";
import { SafeURL } from "../../utils/safe-url.js";
import { obfuscateDSNPassword } from "../../utils/dsn-obfuscate.js";
import { SQLRowLimiter } from "../../utils/sql-row-limiter.js";
Expand Down Expand Up @@ -94,6 +94,17 @@ export class SQLServerDSNParser implements DSNParser {
// Handle authentication types
switch (options.authentication) {
case "azure-active-directory-access-token": {
let DefaultAzureCredential: typeof import("@azure/identity")["DefaultAzureCredential"];
try {
({ DefaultAzureCredential } = await import("@azure/identity"));
} catch (importError) {
if (isDriverNotInstalled(importError, "@azure/identity")) {
throw new Error(
'Azure AD authentication requires the "@azure/identity" package. Install it with: pnpm add @azure/identity'
);
}
throw importError;
}
try {
const credential = new DefaultAzureCredential();
const token = await credential.getToken("https://database.windows.net/");
Comment thread
tianzhou marked this conversation as resolved.
Expand Down
22 changes: 22 additions & 0 deletions src/utils/__tests__/aws-rds-signer-missing.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { describe, it, expect } from 'vitest';
import { isDriverNotInstalled } from '../module-loader.js';

describe('isDriverNotInstalled with scoped packages', () => {
it('should match ERR_MODULE_NOT_FOUND for @aws-sdk/rds-signer', () => {
const err = new Error(
"Cannot find package '@aws-sdk/rds-signer' imported from /fake/path"
);
(err as NodeJS.ErrnoException).code = 'ERR_MODULE_NOT_FOUND';

expect(isDriverNotInstalled(err, '@aws-sdk/rds-signer')).toBe(true);
});

it('should not match unrelated ERR_MODULE_NOT_FOUND errors', () => {
const err = new Error(
"Cannot find package 'some-other-pkg' imported from /fake/path"
);
(err as NodeJS.ErrnoException).code = 'ERR_MODULE_NOT_FOUND';

expect(isDriverNotInstalled(err, '@aws-sdk/rds-signer')).toBe(false);
});
});
14 changes: 13 additions & 1 deletion src/utils/aws-rds-signer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Signer } from "@aws-sdk/rds-signer";
import { isDriverNotInstalled } from "./module-loader.js";

export interface RdsAuthTokenParams {
hostname: string;
Expand All @@ -13,6 +13,18 @@ export interface RdsAuthTokenParams {
* (AWS CLI profile, env vars, instance role, etc.).
*/
export async function generateRdsAuthToken(params: RdsAuthTokenParams): Promise<string> {
let Signer: typeof import("@aws-sdk/rds-signer")["Signer"];
try {
({ Signer } = await import("@aws-sdk/rds-signer"));
} catch (error) {
if (isDriverNotInstalled(error, "@aws-sdk/rds-signer")) {
throw new Error(
'AWS IAM authentication requires the "@aws-sdk/rds-signer" package. Install it with: pnpm add @aws-sdk/rds-signer'
);
}
throw error;
}

const signer = new Signer({
Comment thread
tianzhou marked this conversation as resolved.
hostname: params.hostname,
port: params.port,
Expand Down
11 changes: 7 additions & 4 deletions tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ export default defineConfig({
dts: true,
clean: true,
outDir: 'dist',
// Database drivers are optionalDependencies loaded at runtime via dynamic
// import(). They must be external so tsup does not bundle their CJS code
// into ESM chunks (which causes "Dynamic require of X is not supported").
external: ['pg', 'mysql2', 'mariadb', 'mssql', 'better-sqlite3'],
// Optional runtime-loaded dependencies (database drivers and cloud auth
// packages) are declared as optionalDependencies and loaded via dynamic
// import(). Database drivers must be external so tsup does not bundle their
// CJS code into ESM chunks (which causes "Dynamic require of X is not
// supported"). Cloud auth packages are externalized to keep their large
// dependency trees out of the bundle.
external: ['pg', 'mysql2', 'mariadb', 'mssql', 'better-sqlite3', '@aws-sdk/rds-signer', '@azure/identity'],
// Copy the employee-sqlite demo data to dist
async onSuccess() {
// Create target directory
Expand Down
Loading