@hasna/connectors 1.3.29 → 1.3.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@ Open-source connector platform for enabling, authenticating, and running API con
8
8
  ## Install
9
9
 
10
10
  ```bash
11
- npm install -g @hasna/connectors
11
+ bun install -g @hasna/connectors
12
12
  ```
13
13
 
14
14
  ## What It Is
@@ -56,6 +56,16 @@ connectors-mcp
56
56
  connectors-serve
57
57
  ```
58
58
 
59
+ The local REST API is served by the one-product runtime at
60
+ `http://localhost:9876`. Use `@hasna/connectors-sdk` with
61
+ `ConnectorsClient` or `LocalConnectorsClient` for this local
62
+ `connectors-serve` API.
63
+
64
+ Hosted SaaS products should use `HostedConnectorsClient` from
65
+ `@hasna/connectors-sdk`. The hosted client talks to a platform
66
+ `/api/v1` endpoint with bearer API keys and does not require local connector
67
+ installs or individual connector packages.
68
+
59
69
  ## Project Layout
60
70
 
61
71
  Project-local enablement is lightweight:
package/bin/index.js CHANGED
@@ -1909,7 +1909,7 @@ var package_default;
1909
1909
  var init_package = __esm(() => {
1910
1910
  package_default = {
1911
1911
  name: "@hasna/connectors",
1912
- version: "1.3.29",
1912
+ version: "1.3.30",
1913
1913
  description: "Open source connector library - Install API connectors with a single command",
1914
1914
  type: "module",
1915
1915
  bin: {
@@ -7017,22 +7017,58 @@ async function requestJson(profile, path, params, options = {}) {
7017
7017
  if (value !== undefined && value !== null && value !== "")
7018
7018
  url.searchParams.append(key, String(value));
7019
7019
  }
7020
- const response = await fetch(url, {
7021
- method: options.method ?? "GET",
7022
- headers: {
7023
- Authorization: `Bearer ${token}`,
7024
- Accept: "application/json",
7025
- ...options.body ? { "Content-Type": "application/json" } : {}
7026
- },
7027
- body: options.body ? JSON.stringify(options.body) : undefined
7028
- });
7029
- const text = await response.text();
7030
- const data = text ? JSON.parse(text) : {};
7031
- if (!response.ok) {
7020
+ let lastError;
7021
+ for (let attempt = 0;attempt <= MAX_GMAIL_RETRIES; attempt++) {
7022
+ let response;
7023
+ try {
7024
+ response = await fetch(url, {
7025
+ method: options.method ?? "GET",
7026
+ headers: {
7027
+ Authorization: `Bearer ${token}`,
7028
+ Accept: "application/json",
7029
+ ...options.body ? { "Content-Type": "application/json" } : {}
7030
+ },
7031
+ body: options.body ? JSON.stringify(options.body) : undefined
7032
+ });
7033
+ } catch (error2) {
7034
+ lastError = error2 instanceof Error ? error2.message : String(error2);
7035
+ if (attempt >= MAX_GMAIL_RETRIES)
7036
+ throw error2;
7037
+ await sleep(gmailBackoffDelayMs(attempt));
7038
+ continue;
7039
+ }
7040
+ const text = await response.text();
7041
+ const data = text ? JSON.parse(text) : {};
7042
+ if (response.ok)
7043
+ return data;
7032
7044
  const error = data;
7033
- throw new Error(`Gmail request failed (${response.status}): ${error.error?.message ?? response.statusText}`);
7045
+ lastError = error.error?.message ?? response.statusText;
7046
+ if (!isRetryableGmailResponse(response.status, error) || attempt >= MAX_GMAIL_RETRIES) {
7047
+ throw new Error(`Gmail request failed (${response.status}): ${lastError}`);
7048
+ }
7049
+ await sleep(gmailBackoffDelayMs(attempt, response.headers.get("retry-after")));
7034
7050
  }
7035
- return data;
7051
+ throw new Error(`Gmail request failed: ${lastError ?? "unknown error"}`);
7052
+ }
7053
+ function isRetryableGmailResponse(status, error) {
7054
+ if ([429, 500, 502, 503, 504].includes(status))
7055
+ return true;
7056
+ const reasons = error.error?.errors?.map((entry) => entry.reason) ?? [];
7057
+ return reasons.some((reason) => reason === "rateLimitExceeded" || reason === "userRateLimitExceeded");
7058
+ }
7059
+ function gmailBackoffDelayMs(attempt, retryAfter = null) {
7060
+ if (retryAfter) {
7061
+ const seconds = Number(retryAfter);
7062
+ if (Number.isFinite(seconds) && seconds >= 0)
7063
+ return seconds * 1000;
7064
+ }
7065
+ const base = Number(process.env.CONNECTORS_GMAIL_RETRY_BASE_MS ?? "1000");
7066
+ const baseMs = Number.isFinite(base) && base >= 0 ? base : 1000;
7067
+ const jitterMs = Math.floor(Math.random() * 1000);
7068
+ return Math.min(2 ** attempt * baseMs + jitterMs, 64000);
7069
+ }
7070
+ function sleep(ms) {
7071
+ return new Promise((resolve) => setTimeout(resolve, ms));
7036
7072
  }
7037
7073
  async function getValidAccessToken(profile) {
7038
7074
  if (process.env.GMAIL_ACCESS_TOKEN)
@@ -7210,7 +7246,7 @@ ${input.body}`;
7210
7246
  function safeFilename(filename) {
7211
7247
  return basename(filename.replace(/[\u00A0\u2000-\u200B\u202F\u205F\u3000]/g, " ")).replace(/[\/\\]/g, "_");
7212
7248
  }
7213
- var GMAIL_API_BASE = "https://gmail.googleapis.com/gmail/v1", TOKEN_URL = "https://oauth2.googleapis.com/token", REFRESH_BUFFER_MS, listMessagesSchema, messageIdSchema, readMessageSchema, attachmentListSchema, attachmentDownloadSchema, historyListSchema, replySchema, gmailConnector;
7249
+ var GMAIL_API_BASE = "https://gmail.googleapis.com/gmail/v1", TOKEN_URL = "https://oauth2.googleapis.com/token", REFRESH_BUFFER_MS, MAX_GMAIL_RETRIES = 5, listMessagesSchema, messageIdSchema, readMessageSchema, attachmentListSchema, attachmentDownloadSchema, historyListSchema, replySchema, gmailConnector;
7214
7250
  var init_gmail = __esm(() => {
7215
7251
  init_zod();
7216
7252
  init_connector();
package/bin/mcp.js CHANGED
@@ -11542,22 +11542,58 @@ async function requestJson(profile, path, params, options = {}) {
11542
11542
  if (value !== undefined && value !== null && value !== "")
11543
11543
  url.searchParams.append(key, String(value));
11544
11544
  }
11545
- const response = await fetch(url, {
11546
- method: options.method ?? "GET",
11547
- headers: {
11548
- Authorization: `Bearer ${token}`,
11549
- Accept: "application/json",
11550
- ...options.body ? { "Content-Type": "application/json" } : {}
11551
- },
11552
- body: options.body ? JSON.stringify(options.body) : undefined
11553
- });
11554
- const text = await response.text();
11555
- const data = text ? JSON.parse(text) : {};
11556
- if (!response.ok) {
11545
+ let lastError;
11546
+ for (let attempt = 0;attempt <= MAX_GMAIL_RETRIES; attempt++) {
11547
+ let response;
11548
+ try {
11549
+ response = await fetch(url, {
11550
+ method: options.method ?? "GET",
11551
+ headers: {
11552
+ Authorization: `Bearer ${token}`,
11553
+ Accept: "application/json",
11554
+ ...options.body ? { "Content-Type": "application/json" } : {}
11555
+ },
11556
+ body: options.body ? JSON.stringify(options.body) : undefined
11557
+ });
11558
+ } catch (error3) {
11559
+ lastError = error3 instanceof Error ? error3.message : String(error3);
11560
+ if (attempt >= MAX_GMAIL_RETRIES)
11561
+ throw error3;
11562
+ await sleep(gmailBackoffDelayMs(attempt));
11563
+ continue;
11564
+ }
11565
+ const text = await response.text();
11566
+ const data = text ? JSON.parse(text) : {};
11567
+ if (response.ok)
11568
+ return data;
11557
11569
  const error2 = data;
11558
- throw new Error(`Gmail request failed (${response.status}): ${error2.error?.message ?? response.statusText}`);
11570
+ lastError = error2.error?.message ?? response.statusText;
11571
+ if (!isRetryableGmailResponse(response.status, error2) || attempt >= MAX_GMAIL_RETRIES) {
11572
+ throw new Error(`Gmail request failed (${response.status}): ${lastError}`);
11573
+ }
11574
+ await sleep(gmailBackoffDelayMs(attempt, response.headers.get("retry-after")));
11575
+ }
11576
+ throw new Error(`Gmail request failed: ${lastError ?? "unknown error"}`);
11577
+ }
11578
+ function isRetryableGmailResponse(status, error2) {
11579
+ if ([429, 500, 502, 503, 504].includes(status))
11580
+ return true;
11581
+ const reasons = error2.error?.errors?.map((entry) => entry.reason) ?? [];
11582
+ return reasons.some((reason) => reason === "rateLimitExceeded" || reason === "userRateLimitExceeded");
11583
+ }
11584
+ function gmailBackoffDelayMs(attempt, retryAfter = null) {
11585
+ if (retryAfter) {
11586
+ const seconds = Number(retryAfter);
11587
+ if (Number.isFinite(seconds) && seconds >= 0)
11588
+ return seconds * 1000;
11559
11589
  }
11560
- return data;
11590
+ const base = Number(process.env.CONNECTORS_GMAIL_RETRY_BASE_MS ?? "1000");
11591
+ const baseMs = Number.isFinite(base) && base >= 0 ? base : 1000;
11592
+ const jitterMs = Math.floor(Math.random() * 1000);
11593
+ return Math.min(2 ** attempt * baseMs + jitterMs, 64000);
11594
+ }
11595
+ function sleep(ms) {
11596
+ return new Promise((resolve) => setTimeout(resolve, ms));
11561
11597
  }
11562
11598
  async function getValidAccessToken(profile) {
11563
11599
  if (process.env.GMAIL_ACCESS_TOKEN)
@@ -11735,7 +11771,7 @@ ${input.body}`;
11735
11771
  function safeFilename(filename) {
11736
11772
  return basename(filename.replace(/[\u00A0\u2000-\u200B\u202F\u205F\u3000]/g, " ")).replace(/[\/\\]/g, "_");
11737
11773
  }
11738
- var GMAIL_API_BASE = "https://gmail.googleapis.com/gmail/v1", TOKEN_URL = "https://oauth2.googleapis.com/token", REFRESH_BUFFER_MS, listMessagesSchema, messageIdSchema, readMessageSchema, attachmentListSchema, attachmentDownloadSchema, historyListSchema, replySchema, gmailConnector;
11774
+ var GMAIL_API_BASE = "https://gmail.googleapis.com/gmail/v1", TOKEN_URL = "https://oauth2.googleapis.com/token", REFRESH_BUFFER_MS, MAX_GMAIL_RETRIES = 5, listMessagesSchema, messageIdSchema, readMessageSchema, attachmentListSchema, attachmentDownloadSchema, historyListSchema, replySchema, gmailConnector;
11739
11775
  var init_gmail = __esm(() => {
11740
11776
  init_zod();
11741
11777
  init_connector();
@@ -12240,7 +12276,7 @@ var package_default;
12240
12276
  var init_package = __esm(() => {
12241
12277
  package_default = {
12242
12278
  name: "@hasna/connectors",
12243
- version: "1.3.29",
12279
+ version: "1.3.30",
12244
12280
  description: "Open source connector library - Install API connectors with a single command",
12245
12281
  type: "module",
12246
12282
  bin: {
package/bin/serve.js CHANGED
@@ -15200,6 +15200,7 @@ import { basename, join as join9 } from "path";
15200
15200
  var GMAIL_API_BASE = "https://gmail.googleapis.com/gmail/v1";
15201
15201
  var TOKEN_URL = "https://oauth2.googleapis.com/token";
15202
15202
  var REFRESH_BUFFER_MS = 5 * 60 * 1000;
15203
+ var MAX_GMAIL_RETRIES = 5;
15203
15204
  var listMessagesSchema = exports_external2.object({
15204
15205
  max: exports_external2.coerce.number().int().positive().max(500).optional(),
15205
15206
  maxResults: exports_external2.coerce.number().int().positive().max(500).optional(),
@@ -15430,22 +15431,58 @@ async function requestJson(profile, path, params, options = {}) {
15430
15431
  if (value !== undefined && value !== null && value !== "")
15431
15432
  url.searchParams.append(key, String(value));
15432
15433
  }
15433
- const response = await fetch(url, {
15434
- method: options.method ?? "GET",
15435
- headers: {
15436
- Authorization: `Bearer ${token}`,
15437
- Accept: "application/json",
15438
- ...options.body ? { "Content-Type": "application/json" } : {}
15439
- },
15440
- body: options.body ? JSON.stringify(options.body) : undefined
15441
- });
15442
- const text = await response.text();
15443
- const data = text ? JSON.parse(text) : {};
15444
- if (!response.ok) {
15434
+ let lastError;
15435
+ for (let attempt = 0;attempt <= MAX_GMAIL_RETRIES; attempt++) {
15436
+ let response;
15437
+ try {
15438
+ response = await fetch(url, {
15439
+ method: options.method ?? "GET",
15440
+ headers: {
15441
+ Authorization: `Bearer ${token}`,
15442
+ Accept: "application/json",
15443
+ ...options.body ? { "Content-Type": "application/json" } : {}
15444
+ },
15445
+ body: options.body ? JSON.stringify(options.body) : undefined
15446
+ });
15447
+ } catch (error2) {
15448
+ lastError = error2 instanceof Error ? error2.message : String(error2);
15449
+ if (attempt >= MAX_GMAIL_RETRIES)
15450
+ throw error2;
15451
+ await sleep(gmailBackoffDelayMs(attempt));
15452
+ continue;
15453
+ }
15454
+ const text = await response.text();
15455
+ const data = text ? JSON.parse(text) : {};
15456
+ if (response.ok)
15457
+ return data;
15445
15458
  const error = data;
15446
- throw new Error(`Gmail request failed (${response.status}): ${error.error?.message ?? response.statusText}`);
15459
+ lastError = error.error?.message ?? response.statusText;
15460
+ if (!isRetryableGmailResponse(response.status, error) || attempt >= MAX_GMAIL_RETRIES) {
15461
+ throw new Error(`Gmail request failed (${response.status}): ${lastError}`);
15462
+ }
15463
+ await sleep(gmailBackoffDelayMs(attempt, response.headers.get("retry-after")));
15447
15464
  }
15448
- return data;
15465
+ throw new Error(`Gmail request failed: ${lastError ?? "unknown error"}`);
15466
+ }
15467
+ function isRetryableGmailResponse(status, error) {
15468
+ if ([429, 500, 502, 503, 504].includes(status))
15469
+ return true;
15470
+ const reasons = error.error?.errors?.map((entry) => entry.reason) ?? [];
15471
+ return reasons.some((reason) => reason === "rateLimitExceeded" || reason === "userRateLimitExceeded");
15472
+ }
15473
+ function gmailBackoffDelayMs(attempt, retryAfter = null) {
15474
+ if (retryAfter) {
15475
+ const seconds = Number(retryAfter);
15476
+ if (Number.isFinite(seconds) && seconds >= 0)
15477
+ return seconds * 1000;
15478
+ }
15479
+ const base = Number(process.env.CONNECTORS_GMAIL_RETRY_BASE_MS ?? "1000");
15480
+ const baseMs = Number.isFinite(base) && base >= 0 ? base : 1000;
15481
+ const jitterMs = Math.floor(Math.random() * 1000);
15482
+ return Math.min(2 ** attempt * baseMs + jitterMs, 64000);
15483
+ }
15484
+ function sleep(ms) {
15485
+ return new Promise((resolve) => setTimeout(resolve, ms));
15449
15486
  }
15450
15487
  async function getValidAccessToken(profile) {
15451
15488
  if (process.env.GMAIL_ACCESS_TOKEN)
@@ -15953,7 +15990,7 @@ function extractGoogleError(body) {
15953
15990
  // package.json
15954
15991
  var package_default = {
15955
15992
  name: "@hasna/connectors",
15956
- version: "1.3.29",
15993
+ version: "1.3.30",
15957
15994
  description: "Open source connector library - Install API connectors with a single command",
15958
15995
  type: "module",
15959
15996
  bin: {
package/dist/index.js CHANGED
@@ -4974,6 +4974,7 @@ import { basename, join as join2 } from "path";
4974
4974
  var GMAIL_API_BASE = "https://gmail.googleapis.com/gmail/v1";
4975
4975
  var TOKEN_URL = "https://oauth2.googleapis.com/token";
4976
4976
  var REFRESH_BUFFER_MS = 5 * 60 * 1000;
4977
+ var MAX_GMAIL_RETRIES = 5;
4977
4978
  var listMessagesSchema = exports_external.object({
4978
4979
  max: exports_external.coerce.number().int().positive().max(500).optional(),
4979
4980
  maxResults: exports_external.coerce.number().int().positive().max(500).optional(),
@@ -5204,22 +5205,58 @@ async function requestJson(profile, path, params, options = {}) {
5204
5205
  if (value !== undefined && value !== null && value !== "")
5205
5206
  url.searchParams.append(key, String(value));
5206
5207
  }
5207
- const response = await fetch(url, {
5208
- method: options.method ?? "GET",
5209
- headers: {
5210
- Authorization: `Bearer ${token}`,
5211
- Accept: "application/json",
5212
- ...options.body ? { "Content-Type": "application/json" } : {}
5213
- },
5214
- body: options.body ? JSON.stringify(options.body) : undefined
5215
- });
5216
- const text = await response.text();
5217
- const data = text ? JSON.parse(text) : {};
5218
- if (!response.ok) {
5208
+ let lastError;
5209
+ for (let attempt = 0;attempt <= MAX_GMAIL_RETRIES; attempt++) {
5210
+ let response;
5211
+ try {
5212
+ response = await fetch(url, {
5213
+ method: options.method ?? "GET",
5214
+ headers: {
5215
+ Authorization: `Bearer ${token}`,
5216
+ Accept: "application/json",
5217
+ ...options.body ? { "Content-Type": "application/json" } : {}
5218
+ },
5219
+ body: options.body ? JSON.stringify(options.body) : undefined
5220
+ });
5221
+ } catch (error2) {
5222
+ lastError = error2 instanceof Error ? error2.message : String(error2);
5223
+ if (attempt >= MAX_GMAIL_RETRIES)
5224
+ throw error2;
5225
+ await sleep(gmailBackoffDelayMs(attempt));
5226
+ continue;
5227
+ }
5228
+ const text = await response.text();
5229
+ const data = text ? JSON.parse(text) : {};
5230
+ if (response.ok)
5231
+ return data;
5219
5232
  const error = data;
5220
- throw new Error(`Gmail request failed (${response.status}): ${error.error?.message ?? response.statusText}`);
5233
+ lastError = error.error?.message ?? response.statusText;
5234
+ if (!isRetryableGmailResponse(response.status, error) || attempt >= MAX_GMAIL_RETRIES) {
5235
+ throw new Error(`Gmail request failed (${response.status}): ${lastError}`);
5236
+ }
5237
+ await sleep(gmailBackoffDelayMs(attempt, response.headers.get("retry-after")));
5221
5238
  }
5222
- return data;
5239
+ throw new Error(`Gmail request failed: ${lastError ?? "unknown error"}`);
5240
+ }
5241
+ function isRetryableGmailResponse(status, error) {
5242
+ if ([429, 500, 502, 503, 504].includes(status))
5243
+ return true;
5244
+ const reasons = error.error?.errors?.map((entry) => entry.reason) ?? [];
5245
+ return reasons.some((reason) => reason === "rateLimitExceeded" || reason === "userRateLimitExceeded");
5246
+ }
5247
+ function gmailBackoffDelayMs(attempt, retryAfter = null) {
5248
+ if (retryAfter) {
5249
+ const seconds = Number(retryAfter);
5250
+ if (Number.isFinite(seconds) && seconds >= 0)
5251
+ return seconds * 1000;
5252
+ }
5253
+ const base = Number(process.env.CONNECTORS_GMAIL_RETRY_BASE_MS ?? "1000");
5254
+ const baseMs = Number.isFinite(base) && base >= 0 ? base : 1000;
5255
+ const jitterMs = Math.floor(Math.random() * 1000);
5256
+ return Math.min(2 ** attempt * baseMs + jitterMs, 64000);
5257
+ }
5258
+ function sleep(ms) {
5259
+ return new Promise((resolve) => setTimeout(resolve, ms));
5223
5260
  }
5224
5261
  async function getValidAccessToken(profile) {
5225
5262
  if (process.env.GMAIL_ACCESS_TOKEN)
@@ -5727,7 +5764,7 @@ function extractGoogleError(body) {
5727
5764
  // package.json
5728
5765
  var package_default = {
5729
5766
  name: "@hasna/connectors",
5730
- version: "1.3.29",
5767
+ version: "1.3.30",
5731
5768
  description: "Open source connector library - Install API connectors with a single command",
5732
5769
  type: "module",
5733
5770
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/connectors",
3
- "version": "1.3.29",
3
+ "version": "1.3.30",
4
4
  "description": "Open source connector library - Install API connectors with a single command",
5
5
  "type": "module",
6
6
  "bin": {