Skip to content

Commit fccf725

Browse files
committed
Merge branch 'main' into dan/custom-code-exchange
2 parents 357456c + 8951348 commit fccf725

File tree

5 files changed

+179
-16
lines changed

5 files changed

+179
-16
lines changed

packages/nylas-connect/CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
# @nylas/connect
22

3+
## 0.1.0
4+
5+
### Minor Changes
6+
7+
- 356ac8f: Add automatic API URL version suffix handling
8+
9+
The NylasConnect client now automatically appends `/v3` to API URLs that don't already have a version suffix. This ensures all API calls use versioned endpoints while preserving any explicitly set versions.
10+
11+
**Features:**
12+
- Automatically appends `/v3` to API URLs without version suffixes
13+
- Preserves existing version suffixes (e.g., `/v1`, `/v2`, `/v10`)
14+
- Handles trailing slashes correctly
15+
- Works with custom API URLs and regional endpoints
16+
17+
**Examples:**
18+
- `https://api.us.nylas.com``https://api.us.nylas.com/v3`
19+
- `https://api.us.nylas.com/v2``https://api.us.nylas.com/v2` (unchanged)
20+
- `https://custom.api.com``https://custom.api.com/v3`
21+
22+
This change is backward compatible and doesn't affect existing functionality.
23+
324
## 0.0.3
425

526
### Patch Changes

packages/nylas-connect/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@nylas/connect",
3-
"version": "0.0.3",
3+
"version": "0.1.0",
44
"description": "Modern, lightweight Nylas connection library with PKCE support",
55
"main": "dist/index.js",
66
"module": "dist/index.js",
@@ -50,7 +50,7 @@
5050
"devDependencies": {
5151
"@types/node": "^20.11.13",
5252
"@vitest/coverage-v8": "^2.1.9",
53-
"happy-dom": "^13.10.1",
53+
"happy-dom": "^15.10.2",
5454
"typescript": "^5.3.3",
5555
"vite": "^5.0.10",
5656
"vite-plugin-dts": "^3.7.0",

packages/nylas-connect/src/connect-client.test.ts

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1164,7 +1164,7 @@ describe("NylasConnect (custom code exchange)", () => {
11641164
expect(result.accessToken).toBe("builtin_access_token");
11651165
expect(result.grantId).toBe("builtin_grant_123");
11661166
expect(fetch).toHaveBeenCalledWith(
1167-
"https://api.us.nylas.com/connect/token",
1167+
"https://api.us.nylas.com/v3/connect/token",
11681168
expect.objectContaining({
11691169
method: "POST",
11701170
headers: { "Content-Type": "application/x-www-form-urlencoded" },
@@ -1187,3 +1187,129 @@ describe("NylasConnect (custom code exchange)", () => {
11871187
return [base64url(header), base64url(payload), "signature"].join(".");
11881188
}
11891189
});
1190+
1191+
describe("NylasConnect (API URL normalization)", () => {
1192+
const clientId = "client_123";
1193+
const redirectUri = "https://app.example/callback";
1194+
1195+
beforeEach(() => {
1196+
localStorage.clear();
1197+
vi.restoreAllMocks();
1198+
});
1199+
1200+
it("should append /v3 to default API URL when no version is present", async () => {
1201+
const auth = new NylasConnect({
1202+
clientId,
1203+
redirectUri,
1204+
apiUrl: "https://api.us.nylas.com",
1205+
});
1206+
1207+
const { url } = await auth.getAuthUrl();
1208+
expect(url).toContain("https://api.us.nylas.com/v3/connect/auth");
1209+
});
1210+
1211+
it("should append /v3 to custom API URL when no version is present", async () => {
1212+
const auth = new NylasConnect({
1213+
clientId,
1214+
redirectUri,
1215+
apiUrl: "https://custom.api.com",
1216+
});
1217+
1218+
const { url } = await auth.getAuthUrl();
1219+
expect(url).toContain("https://custom.api.com/v3/connect/auth");
1220+
});
1221+
1222+
it("should preserve existing version suffix (v2)", async () => {
1223+
const auth = new NylasConnect({
1224+
clientId,
1225+
redirectUri,
1226+
apiUrl: "https://api.us.nylas.com/v2",
1227+
});
1228+
1229+
const { url } = await auth.getAuthUrl();
1230+
expect(url).toContain("https://api.us.nylas.com/v2/connect/auth");
1231+
expect(url).not.toContain("/v3");
1232+
});
1233+
1234+
it("should preserve existing version suffix (v1)", async () => {
1235+
const auth = new NylasConnect({
1236+
clientId,
1237+
redirectUri,
1238+
apiUrl: "https://api.us.nylas.com/v1",
1239+
});
1240+
1241+
const { url } = await auth.getAuthUrl();
1242+
expect(url).toContain("https://api.us.nylas.com/v1/connect/auth");
1243+
expect(url).not.toContain("/v3");
1244+
});
1245+
1246+
it("should handle trailing slashes correctly", async () => {
1247+
const auth = new NylasConnect({
1248+
clientId,
1249+
redirectUri,
1250+
apiUrl: "https://api.us.nylas.com/",
1251+
});
1252+
1253+
const { url } = await auth.getAuthUrl();
1254+
expect(url).toContain("https://api.us.nylas.com/v3/connect/auth");
1255+
expect(url).not.toContain("//v3"); // Should not have double slashes
1256+
});
1257+
1258+
it("should handle multiple trailing slashes correctly", async () => {
1259+
const auth = new NylasConnect({
1260+
clientId,
1261+
redirectUri,
1262+
apiUrl: "https://api.us.nylas.com///",
1263+
});
1264+
1265+
const { url } = await auth.getAuthUrl();
1266+
expect(url).toContain("https://api.us.nylas.com/v3/connect/auth");
1267+
expect(url).not.toContain("//v3"); // Should not have double slashes
1268+
});
1269+
1270+
it("should preserve version with trailing slashes", async () => {
1271+
const auth = new NylasConnect({
1272+
clientId,
1273+
redirectUri,
1274+
apiUrl: "https://api.us.nylas.com/v2/",
1275+
});
1276+
1277+
const { url } = await auth.getAuthUrl();
1278+
expect(url).toContain("https://api.us.nylas.com/v2/connect/auth");
1279+
expect(url).not.toContain("/v3");
1280+
});
1281+
1282+
it("should append /v3 to default URL when no apiUrl is provided", async () => {
1283+
const auth = new NylasConnect({
1284+
clientId,
1285+
redirectUri,
1286+
// No apiUrl provided - should use default
1287+
});
1288+
1289+
const { url } = await auth.getAuthUrl();
1290+
expect(url).toContain("https://api.us.nylas.com/v3/connect/auth");
1291+
});
1292+
1293+
it("should handle EU API URL correctly", async () => {
1294+
const auth = new NylasConnect({
1295+
clientId,
1296+
redirectUri,
1297+
apiUrl: "https://api.eu.nylas.com",
1298+
});
1299+
1300+
const { url } = await auth.getAuthUrl();
1301+
expect(url).toContain("https://api.eu.nylas.com/v3/connect/auth");
1302+
});
1303+
1304+
it("should work with higher version numbers", async () => {
1305+
const auth = new NylasConnect({
1306+
clientId,
1307+
redirectUri,
1308+
apiUrl: "https://api.us.nylas.com/v10",
1309+
});
1310+
1311+
const { url } = await auth.getAuthUrl();
1312+
expect(url).toContain("https://api.us.nylas.com/v10/connect/auth");
1313+
expect(url).not.toContain("/v3");
1314+
});
1315+
});

packages/nylas-connect/src/connect-client.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,35 @@ export class NylasConnect {
9393
});
9494
}
9595

96+
/**
97+
* Normalize API URL to ensure it has a version suffix
98+
*/
99+
private normalizeApiUrl(apiUrl: string): string {
100+
// Remove trailing slashes
101+
const cleanUrl = apiUrl.replace(/\/+$/, "");
102+
// Check if URL already has a version suffix (e.g., /v3, /v2, etc.)
103+
const versionPattern = /\/v\d+$/;
104+
if (versionPattern.test(cleanUrl)) {
105+
return cleanUrl;
106+
}
107+
// Append /v3 if no version suffix is present
108+
return `${cleanUrl}/v3`;
109+
}
110+
96111
/**
97112
* Resolve configuration with environment variables and smart defaults
98113
*/
99114
private resolveConfig(config: ConnectConfig): ConnectConfig {
100115
const environment = this.detectEnvironment(config.environment);
116+
const baseApiUrl = config.apiUrl || "https://api.us.nylas.com";
101117

102118
return {
103119
clientId: config.clientId || this.getEnvVar("NYLAS_CLIENT_ID"),
104120
redirectUri:
105121
config.redirectUri ||
106122
this.getEnvVar("NYLAS_REDIRECT_URI") ||
107123
this.detectRedirectUri(),
108-
apiUrl: config.apiUrl || "https://api.us.nylas.com",
124+
apiUrl: this.normalizeApiUrl(baseApiUrl),
109125
environment,
110126
defaultScopes: config.defaultScopes,
111127
debug: config.debug ?? environment === "development",

pnpm-lock.yaml

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)