@zapier/zapier-sdk 0.50.0 → 0.52.0

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +2 -1
  3. package/dist/api/auth.d.ts +1 -6
  4. package/dist/api/auth.d.ts.map +1 -1
  5. package/dist/api/auth.js +34 -27
  6. package/dist/api/client.d.ts.map +1 -1
  7. package/dist/api/client.js +87 -9
  8. package/dist/api/concurrency.d.ts +28 -0
  9. package/dist/api/concurrency.d.ts.map +1 -0
  10. package/dist/api/concurrency.js +90 -0
  11. package/dist/api/index.d.ts +1 -1
  12. package/dist/api/index.d.ts.map +1 -1
  13. package/dist/api/index.js +1 -1
  14. package/dist/api/schemas.d.ts +3 -3
  15. package/dist/api/types.d.ts +6 -0
  16. package/dist/api/types.d.ts.map +1 -1
  17. package/dist/auth.d.ts +13 -2
  18. package/dist/auth.d.ts.map +1 -1
  19. package/dist/auth.js +95 -11
  20. package/dist/constants.d.ts +16 -0
  21. package/dist/constants.d.ts.map +1 -1
  22. package/dist/constants.js +29 -0
  23. package/dist/experimental.cjs +357 -34
  24. package/dist/experimental.d.mts +28 -28
  25. package/dist/experimental.d.ts +26 -26
  26. package/dist/experimental.mjs +353 -35
  27. package/dist/{index-BQ2ii0Bs.d.mts → index-DcdtPei-.d.mts} +132 -2
  28. package/dist/{index-BQ2ii0Bs.d.ts → index-DcdtPei-.d.ts} +132 -2
  29. package/dist/index.cjs +357 -34
  30. package/dist/index.d.mts +1 -1
  31. package/dist/index.d.ts +2 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +1 -0
  34. package/dist/index.mjs +353 -35
  35. package/dist/plugins/api/index.d.ts.map +1 -1
  36. package/dist/plugins/api/index.js +3 -2
  37. package/dist/plugins/apps/index.d.ts +2 -2
  38. package/dist/plugins/deprecated/inputFields.d.ts +18 -18
  39. package/dist/plugins/getAction/index.d.ts +6 -6
  40. package/dist/plugins/getAction/schemas.d.ts +4 -4
  41. package/dist/plugins/getActionInputFieldsSchema/index.d.ts +5 -5
  42. package/dist/plugins/getActionInputFieldsSchema/schemas.d.ts +4 -4
  43. package/dist/plugins/listActionInputFieldChoices/index.d.ts +5 -5
  44. package/dist/plugins/listActionInputFieldChoices/schemas.d.ts +4 -4
  45. package/dist/plugins/listActionInputFields/index.d.ts +5 -5
  46. package/dist/plugins/listActionInputFields/schemas.d.ts +4 -4
  47. package/dist/plugins/listActions/index.d.ts +3 -3
  48. package/dist/plugins/listActions/schemas.d.ts +4 -4
  49. package/dist/plugins/runAction/index.d.ts +5 -5
  50. package/dist/plugins/runAction/schemas.d.ts +4 -4
  51. package/dist/plugins/triggers/getTriggerInputFieldsSchema/index.d.ts +2 -2
  52. package/dist/plugins/triggers/listTriggerInputFieldChoices/index.d.ts +2 -2
  53. package/dist/plugins/triggers/listTriggerInputFields/index.d.ts +2 -2
  54. package/dist/schemas/Action.d.ts +1 -1
  55. package/dist/sdk.d.ts +52 -52
  56. package/dist/types/properties.d.ts +1 -1
  57. package/dist/types/sdk.d.ts +1 -0
  58. package/dist/types/sdk.d.ts.map +1 -1
  59. package/dist/types/sdk.js +25 -0
  60. package/dist/utils/telemetry.d.ts +11 -0
  61. package/dist/utils/telemetry.d.ts.map +1 -0
  62. package/dist/utils/telemetry.js +19 -0
  63. package/package.json +1 -1
@@ -148,6 +148,21 @@ function parseIntEnvVar(name) {
148
148
  }
149
149
  var ZAPIER_MAX_NETWORK_RETRIES = parseIntEnvVar("ZAPIER_MAX_NETWORK_RETRIES") ?? 3;
150
150
  var ZAPIER_MAX_NETWORK_RETRY_DELAY_MS = parseIntEnvVar("ZAPIER_MAX_NETWORK_RETRY_DELAY_MS") ?? 6e4;
151
+ var MAX_CONCURRENCY_LIMIT = 1e4;
152
+ function parseConcurrencyEnvVar(name) {
153
+ const value = globalThis.process?.env?.[name];
154
+ if (!value) return void 0;
155
+ if (value === "Infinity") return Infinity;
156
+ if (/^[1-9]\d*$/.test(value)) {
157
+ const parsed = parseInt(value, 10);
158
+ if (parsed <= MAX_CONCURRENCY_LIMIT) return parsed;
159
+ }
160
+ console.warn(
161
+ `[zapier-sdk] Invalid value for ${name}: "${value}" (expected positive integer 1-${MAX_CONCURRENCY_LIMIT} or "Infinity")`
162
+ );
163
+ return void 0;
164
+ }
165
+ var ZAPIER_MAX_CONCURRENT_REQUESTS = parseConcurrencyEnvVar("ZAPIER_MAX_CONCURRENT_REQUESTS") ?? 200;
151
166
  function getZapierApprovalMode() {
152
167
  const value = globalThis.process?.env?.ZAPIER_APPROVAL_MODE;
153
168
  if (value === "disabled" || value === "poll" || value === "throw")
@@ -1834,33 +1849,40 @@ function getAuthorizationHeader(token) {
1834
1849
  }
1835
1850
  return `Bearer ${token}`;
1836
1851
  }
1837
- function extractUserIdsFromJwt(token) {
1852
+ function readJwtPayload(token) {
1838
1853
  const parts = parseJwt(token);
1839
- if (!parts) {
1840
- return { customuser_id: null, account_id: null };
1841
- }
1854
+ if (!parts) return null;
1855
+ let payload;
1842
1856
  try {
1843
- const payload = JSON.parse(
1844
- Buffer.from(parts[1], "base64url").toString("utf-8")
1845
- );
1846
- let actualPayload = payload;
1847
- if (payload.sub_type === "service" && payload.njwt) {
1848
- const nestedParts = payload.njwt.split(".");
1849
- if (nestedParts.length === 3) {
1850
- actualPayload = JSON.parse(
1857
+ payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
1858
+ } catch {
1859
+ return null;
1860
+ }
1861
+ if (payload["sub_type"] === "service" && typeof payload["njwt"] === "string") {
1862
+ const nestedParts = parseJwt(payload["njwt"]);
1863
+ if (nestedParts) {
1864
+ try {
1865
+ return JSON.parse(
1851
1866
  Buffer.from(nestedParts[1], "base64url").toString("utf-8")
1852
1867
  );
1868
+ } catch {
1853
1869
  }
1854
1870
  }
1855
- const accountId = actualPayload["zap:acc"] != null ? parseInt(String(actualPayload["zap:acc"]), 10) : null;
1856
- const customUserId = actualPayload.sub_type === "customuser" && actualPayload.sub != null ? parseInt(String(actualPayload.sub), 10) : null;
1857
- return {
1858
- customuser_id: customUserId !== null && !isNaN(customUserId) ? customUserId : null,
1859
- account_id: accountId !== null && !isNaN(accountId) ? accountId : null
1860
- };
1861
- } catch {
1871
+ }
1872
+ return payload;
1873
+ }
1874
+ function extractUserIdsFromJwt(token) {
1875
+ const payload = readJwtPayload(token);
1876
+ if (!payload) {
1862
1877
  return { customuser_id: null, account_id: null };
1863
1878
  }
1879
+ const accRaw = payload["zap:acc"];
1880
+ const accountId = accRaw != null ? parseInt(String(accRaw), 10) : null;
1881
+ const customUserId = payload["sub_type"] === "customuser" && payload["sub"] != null ? parseInt(String(payload["sub"]), 10) : null;
1882
+ return {
1883
+ customuser_id: customUserId !== null && !isNaN(customUserId) ? customUserId : null,
1884
+ account_id: accountId !== null && !isNaN(accountId) ? accountId : null
1885
+ };
1864
1886
  }
1865
1887
 
1866
1888
  // src/api/debug.ts
@@ -2168,6 +2190,83 @@ async function pollUntilComplete(options) {
2168
2190
  }
2169
2191
  }
2170
2192
  }
2193
+
2194
+ // src/api/concurrency.ts
2195
+ var NO_OP_RELEASE = () => {
2196
+ };
2197
+ var NO_OP_SEMAPHORE = {
2198
+ acquire: async () => NO_OP_RELEASE,
2199
+ tryAcquire: () => NO_OP_RELEASE
2200
+ };
2201
+ function createSemaphore(maxPermits) {
2202
+ if (maxPermits === Infinity) {
2203
+ return NO_OP_SEMAPHORE;
2204
+ }
2205
+ if (!Number.isInteger(maxPermits) || maxPermits <= 0) {
2206
+ throw new Error(
2207
+ `maxPermits must be a positive integer or Infinity, got: ${maxPermits}`
2208
+ );
2209
+ }
2210
+ let permits = maxPermits;
2211
+ const waiters = [];
2212
+ const release = () => {
2213
+ const next = waiters.shift();
2214
+ if (next) {
2215
+ next.grant();
2216
+ } else {
2217
+ permits++;
2218
+ }
2219
+ };
2220
+ const makeReleaseOnce = () => {
2221
+ let released = false;
2222
+ return () => {
2223
+ if (released) return;
2224
+ released = true;
2225
+ release();
2226
+ };
2227
+ };
2228
+ return {
2229
+ tryAcquire() {
2230
+ if (permits > 0) {
2231
+ permits--;
2232
+ return makeReleaseOnce();
2233
+ }
2234
+ return null;
2235
+ },
2236
+ async acquire(signal) {
2237
+ if (signal?.aborted) {
2238
+ throw signal.reason ?? new DOMException("Aborted", "AbortError");
2239
+ }
2240
+ if (permits > 0) {
2241
+ permits--;
2242
+ return makeReleaseOnce();
2243
+ }
2244
+ return new Promise((resolve2, reject) => {
2245
+ const onAbort = () => {
2246
+ const idx = waiters.indexOf(waiter);
2247
+ if (idx !== -1) {
2248
+ waiters.splice(idx, 1);
2249
+ waiter.cancel(
2250
+ signal?.reason ?? new DOMException("Aborted", "AbortError")
2251
+ );
2252
+ }
2253
+ };
2254
+ const waiter = {
2255
+ grant: () => {
2256
+ signal?.removeEventListener("abort", onAbort);
2257
+ resolve2(makeReleaseOnce());
2258
+ },
2259
+ cancel: (reason) => {
2260
+ signal?.removeEventListener("abort", onAbort);
2261
+ reject(reason);
2262
+ }
2263
+ };
2264
+ signal?.addEventListener("abort", onAbort);
2265
+ waiters.push(waiter);
2266
+ });
2267
+ }
2268
+ };
2269
+ }
2171
2270
  var ClientCredentialsObjectSchema = z.object({
2172
2271
  type: z.enum(["client_credentials"]).optional().meta({ internal: true }),
2173
2272
  clientId: z.string().describe("OAuth client ID for authentication.").meta({ valueHint: "id" }),
@@ -2209,6 +2308,19 @@ function isCredentialsFunction(credentials) {
2209
2308
  return typeof credentials === "function";
2210
2309
  }
2211
2310
 
2311
+ // src/utils/telemetry.ts
2312
+ var emittedOnce = /* @__PURE__ */ new WeakMap();
2313
+ function emitOnce(onEvent, event) {
2314
+ if (!emittedOnce.has(onEvent)) {
2315
+ emittedOnce.set(onEvent, /* @__PURE__ */ new Set());
2316
+ }
2317
+ const fired = emittedOnce.get(onEvent);
2318
+ if (!fired.has(event.type)) {
2319
+ fired.add(event.type);
2320
+ onEvent(event);
2321
+ }
2322
+ }
2323
+
2212
2324
  // src/utils/url-utils.ts
2213
2325
  function getZapierBaseUrl(baseUrl) {
2214
2326
  if (!baseUrl) {
@@ -2431,8 +2543,10 @@ async function resolveCache(options) {
2431
2543
  if (cliLogin?.createCache) {
2432
2544
  try {
2433
2545
  const cache = cliLogin.createCache();
2434
- cachedDefaultCache = cache;
2435
- return cache;
2546
+ if (cache) {
2547
+ cachedDefaultCache = cache;
2548
+ return cache;
2549
+ }
2436
2550
  } catch {
2437
2551
  }
2438
2552
  }
@@ -2444,6 +2558,11 @@ function entryIsValid(entry) {
2444
2558
  if (entry.expiresAt === void 0) return true;
2445
2559
  return entry.expiresAt > Date.now() + TOKEN_EXPIRATION_BUFFER_MS;
2446
2560
  }
2561
+ async function readCachedToken(cacheKey, cache) {
2562
+ const cached = await cache.get(cacheKey);
2563
+ if (cached && entryIsValid(cached)) return cached.value;
2564
+ return void 0;
2565
+ }
2447
2566
  async function invalidateCachedToken(options) {
2448
2567
  const cacheKey = buildCacheKey(options);
2449
2568
  pendingExchanges.delete(cacheKey);
@@ -2557,11 +2676,76 @@ function isCliLoginAvailable() {
2557
2676
  if (cachedCliLogin === void 0) return void 0;
2558
2677
  return cachedCliLogin !== false;
2559
2678
  }
2679
+ function emitAuthResolved(onEvent, mechanism) {
2680
+ if (onEvent) {
2681
+ emitOnce(onEvent, {
2682
+ type: "auth_resolved",
2683
+ payload: { mechanism },
2684
+ timestamp: Date.now()
2685
+ });
2686
+ }
2687
+ }
2688
+ async function getActiveCredentialsFromCli(baseUrl) {
2689
+ const cliLogin = await getCliLogin();
2690
+ return cliLogin?.getActiveCredentials?.({ baseUrl });
2691
+ }
2692
+ async function getStoredClientCredentialsFromCli(baseUrl) {
2693
+ const cliLogin = await getCliLogin();
2694
+ return cliLogin?.getStoredClientCredentials?.({ baseUrl });
2695
+ }
2560
2696
  async function getTokenFromCliLogin(options) {
2561
2697
  const cliLogin = await getCliLogin();
2562
2698
  if (!cliLogin) return void 0;
2563
2699
  return await cliLogin.getToken(options);
2564
2700
  }
2701
+ async function tryStoredClientCredentialToken(options) {
2702
+ const activeCredential = await getActiveCredentialsFromCli(options.baseUrl);
2703
+ if (!activeCredential) return void 0;
2704
+ const resolvedBaseUrl = activeCredential.baseUrl || options.baseUrl || DEFAULT_AUTH_BASE_URL;
2705
+ const mergedScopes = mergeScopes(
2706
+ activeCredential.scopes.join(" "),
2707
+ options.requiredScopes
2708
+ );
2709
+ const cacheKey = buildCacheKey({
2710
+ clientId: activeCredential.clientId,
2711
+ scopes: mergedScopes,
2712
+ baseUrl: resolvedBaseUrl
2713
+ });
2714
+ const cache = await resolveCache(options);
2715
+ const pending = pendingExchanges.get(cacheKey);
2716
+ if (pending) return pending;
2717
+ const cached = await readCachedToken(cacheKey, cache);
2718
+ if (cached !== void 0) {
2719
+ if (options.debug)
2720
+ console.log(
2721
+ `[auth] Using cached token (clientId: ${activeCredential.clientId})`
2722
+ );
2723
+ emitAuthResolved(options.onEvent, "client_credentials");
2724
+ return cached;
2725
+ }
2726
+ const storedCredential = await getStoredClientCredentialsFromCli(resolvedBaseUrl);
2727
+ if (!storedCredential) {
2728
+ await invalidateCachedToken({
2729
+ clientId: activeCredential.clientId,
2730
+ scopes: activeCredential.scopes,
2731
+ baseUrl: resolvedBaseUrl,
2732
+ cache: options.cache
2733
+ });
2734
+ throw new ZapierAuthenticationError(
2735
+ `Stored client credential is missing its secret (clientId: ${activeCredential.clientId}). Run \`zapier-sdk login\` to recreate it.`
2736
+ );
2737
+ }
2738
+ if (options.debug)
2739
+ console.log(
2740
+ `[auth] Using stored client credential (clientId: ${storedCredential.clientId})`
2741
+ );
2742
+ const token = await resolveAuthTokenFromCredentials(
2743
+ storedCredential,
2744
+ options
2745
+ );
2746
+ emitAuthResolved(options.onEvent, "client_credentials");
2747
+ return token;
2748
+ }
2565
2749
  async function resolveAuthToken(options = {}) {
2566
2750
  const credentials = await resolveCredentials({
2567
2751
  credentials: options.credentials,
@@ -2571,14 +2755,24 @@ async function resolveAuthToken(options = {}) {
2571
2755
  if (credentials !== void 0) {
2572
2756
  return resolveAuthTokenFromCredentials(credentials, options);
2573
2757
  }
2574
- return getTokenFromCliLogin({
2758
+ const storedToken = await tryStoredClientCredentialToken(options);
2759
+ if (storedToken !== void 0) return storedToken;
2760
+ if (options.debug) {
2761
+ console.log("[auth] Using JWT (no stored client credential found)");
2762
+ }
2763
+ const jwtToken = await getTokenFromCliLogin({
2575
2764
  onEvent: options.onEvent,
2576
2765
  fetch: options.fetch,
2577
2766
  debug: options.debug
2578
2767
  });
2768
+ if (jwtToken !== void 0) {
2769
+ emitAuthResolved(options.onEvent, "jwt");
2770
+ }
2771
+ return jwtToken;
2579
2772
  }
2580
2773
  async function resolveAuthTokenFromCredentials(credentials, options) {
2581
2774
  if (typeof credentials === "string") {
2775
+ emitAuthResolved(options.onEvent, "token");
2582
2776
  return credentials;
2583
2777
  }
2584
2778
  if (isClientCredentials(credentials)) {
@@ -2591,15 +2785,25 @@ async function resolveAuthTokenFromCredentials(credentials, options) {
2591
2785
  baseUrl: resolvedBaseUrl
2592
2786
  });
2593
2787
  const cache = await resolveCache(options);
2594
- const cached = await cache.get(cacheKey);
2595
- if (cached && entryIsValid(cached)) {
2596
- return cached.value;
2788
+ const cached = await readCachedToken(cacheKey, cache);
2789
+ if (cached !== void 0) {
2790
+ if (options.debug) {
2791
+ console.log(`[auth] Using cached token (clientId: ${clientId})`);
2792
+ }
2793
+ return cached;
2597
2794
  }
2598
2795
  const pending = pendingExchanges.get(cacheKey);
2599
2796
  if (pending) return pending;
2600
2797
  const runLocked = async () => {
2601
- const recheck = await cache.get(cacheKey);
2602
- if (recheck && entryIsValid(recheck)) return recheck.value;
2798
+ const recheck = await readCachedToken(cacheKey, cache);
2799
+ if (recheck !== void 0) {
2800
+ if (options.debug) {
2801
+ console.log(
2802
+ `[auth] Using cached token (clientId: ${clientId}, locked recheck)`
2803
+ );
2804
+ }
2805
+ return recheck;
2806
+ }
2603
2807
  const { accessToken, expiresIn } = await exchangeClientCredentials({
2604
2808
  clientId: credentials.clientId,
2605
2809
  clientSecret: credentials.clientSecret,
@@ -2657,7 +2861,7 @@ async function invalidateCredentialsToken(options) {
2657
2861
  }
2658
2862
 
2659
2863
  // src/sdk-version.ts
2660
- var SDK_VERSION = (typeof process !== "undefined" && process.env ? "0.50.0" : void 0) || "unknown";
2864
+ var SDK_VERSION = (typeof process !== "undefined" && process.env ? "0.52.0" : void 0) || "unknown";
2661
2865
 
2662
2866
  // src/utils/open-url.ts
2663
2867
  var nodePrefix = "node:";
@@ -2867,9 +3071,61 @@ var ZapierApiClient = class {
2867
3071
  await sleep(delayMs, init?.signal ?? void 0);
2868
3072
  }
2869
3073
  };
3074
+ /**
3075
+ * Wrap an outbound HTTP call with the concurrency semaphore. Used by both
3076
+ * `rawFetch` (path-based) and the approval-poll path (absolute URL); each
3077
+ * caller acquires per-attempt, so 429 retry sleep is held but the gap
3078
+ * between approval polls and the human-approval wait are not.
3079
+ *
3080
+ * The release is registered in a finally that wraps the entire post-
3081
+ * acquire flow — including the `wait_end` event emission — so a throwing
3082
+ * `onEvent` handler can never leak a permit.
3083
+ *
3084
+ * Slot lifetime is intentionally tied to "fetch resolves" (headers
3085
+ * received), NOT to "response body fully consumed". WHATWG `fetch()`
3086
+ * resolves once headers are in; the body is still streaming. We rely on
3087
+ * that boundary so streaming responses (SSE, long-running chunked reads)
3088
+ * don't pin a permit for the lifetime of the stream — a single SSE
3089
+ * consumer would otherwise hold one of N slots for as long as the
3090
+ * connection stays open. Do not move the release into a path that awaits
3091
+ * body consumption (e.g. `parseResult` / `response.text()`); doing so
3092
+ * would silently break streaming consumers without failing any of the
3093
+ * short-request tests.
3094
+ */
3095
+ this.withSemaphore = async (context, fn) => {
3096
+ const fastRelease = this.semaphore.tryAcquire();
3097
+ let waitStart = null;
3098
+ let release = fastRelease;
3099
+ if (release === null) {
3100
+ waitStart = Date.now();
3101
+ this.emitEvent("api:concurrency_wait_start", {
3102
+ url: context.url,
3103
+ method: context.method
3104
+ });
3105
+ release = await this.semaphore.acquire(context.signal ?? void 0);
3106
+ }
3107
+ const acquiredRelease = release;
3108
+ try {
3109
+ if (waitStart !== null) {
3110
+ this.emitEvent("api:concurrency_wait_end", {
3111
+ url: context.url,
3112
+ method: context.method,
3113
+ waitedMs: Date.now() - waitStart
3114
+ });
3115
+ }
3116
+ return await fn();
3117
+ } finally {
3118
+ acquiredRelease();
3119
+ }
3120
+ };
2870
3121
  /**
2871
3122
  * Perform a request with auth, header merging, and rate-limit (429) retries.
2872
3123
  * Does NOT handle 403 approval_required — that's routed by `fetch`.
3124
+ *
3125
+ * Concurrency: a semaphore slot is held across the entire call, including
3126
+ * the 429 retry sleep inside `rawFetchUrl`. That keeps backpressure
3127
+ * coherent — when the server is rate-limiting us, we don't dump more
3128
+ * parallelism into the queue.
2873
3129
  */
2874
3130
  this.rawFetch = async (path, init) => {
2875
3131
  if (!path.startsWith("/")) {
@@ -2878,7 +3134,10 @@ var ZapierApiClient = class {
2878
3134
  );
2879
3135
  }
2880
3136
  const { url, pathConfig: pathConfig2 } = this.buildUrl(path, init?.searchParams);
2881
- return this.rawFetchUrl(url, init, pathConfig2);
3137
+ return this.withSemaphore(
3138
+ { url, method: init?.method ?? "GET", signal: init?.signal },
3139
+ () => this.rawFetchUrl(url, init, pathConfig2)
3140
+ );
2882
3141
  };
2883
3142
  /**
2884
3143
  * Approval-aware HTTP fetch.
@@ -2986,6 +3245,15 @@ var ZapierApiClient = class {
2986
3245
  };
2987
3246
  this.maxNetworkRetries = options.maxNetworkRetries ?? ZAPIER_MAX_NETWORK_RETRIES;
2988
3247
  this.maxNetworkRetryDelayMs = options.maxNetworkRetryDelayMs ?? ZAPIER_MAX_NETWORK_RETRY_DELAY_MS;
3248
+ const requested = options.maxConcurrentRequests;
3249
+ const limit = requested === void 0 || Number.isNaN(requested) ? ZAPIER_MAX_CONCURRENT_REQUESTS : requested;
3250
+ if (limit !== Infinity && (!Number.isInteger(limit) || limit < 1 || limit > MAX_CONCURRENCY_LIMIT)) {
3251
+ throw new ZapierConfigurationError(
3252
+ `Invalid maxConcurrentRequests: ${limit} (expected positive integer 1-${MAX_CONCURRENCY_LIMIT} or Infinity)`,
3253
+ { configType: "maxConcurrentRequests" }
3254
+ );
3255
+ }
3256
+ this.semaphore = createSemaphore(limit);
2989
3257
  }
2990
3258
  // Emit an event if onEvent handler is configured
2991
3259
  emitEvent(type, payload) {
@@ -3238,7 +3506,9 @@ var ZapierApiClient = class {
3238
3506
  if (data && typeof data === "object") {
3239
3507
  headers["Content-Type"] = "application/json";
3240
3508
  }
3241
- const wasMissingAuthToken = options.authRequired && await this.getAuthToken({ requiredScopes: options.requiredScopes }) == null;
3509
+ const wasMissingAuthToken = options.authRequired && await this.getAuthToken({
3510
+ requiredScopes: options.requiredScopes
3511
+ }) == null;
3242
3512
  const response = await this.fetch(path, {
3243
3513
  ...options,
3244
3514
  method,
@@ -3361,10 +3631,16 @@ var ZapierApiClient = class {
3361
3631
  // poll_url is an absolute URL supplied by the server, so we use
3362
3632
  // rawFetchUrl directly (skipping path resolution) but still share
3363
3633
  // auth + interactive-header + 429-retry with the rest of the SDK.
3364
- fetchPoll: () => this.rawFetchUrl(approval.poll_url, {
3365
- method: "GET",
3366
- headers: { Accept: "application/json" }
3367
- }),
3634
+ // Each individual poll request goes through the concurrency
3635
+ // semaphore — but we deliberately do not hold a slot across the
3636
+ // sleep between polls or across the human-approval wait.
3637
+ fetchPoll: () => this.withSemaphore(
3638
+ { url: approval.poll_url, method: "GET" },
3639
+ () => this.rawFetchUrl(approval.poll_url, {
3640
+ method: "GET",
3641
+ headers: { Accept: "application/json" }
3642
+ })
3643
+ ),
3368
3644
  timeoutMs,
3369
3645
  isPending: (body2) => {
3370
3646
  const parsed = PollApprovalResponseSchema.safeParse(body2);
@@ -3432,6 +3708,28 @@ var createZapierApi = (options) => {
3432
3708
  });
3433
3709
  };
3434
3710
 
3711
+ // src/api/index.ts
3712
+ function getOrCreateApiClient(config) {
3713
+ const {
3714
+ baseUrl = ZAPIER_BASE_URL,
3715
+ credentials,
3716
+ token,
3717
+ api: providedApi,
3718
+ debug = false,
3719
+ fetch: customFetch
3720
+ } = config;
3721
+ if (providedApi) {
3722
+ return providedApi;
3723
+ }
3724
+ return createZapierApi({
3725
+ baseUrl,
3726
+ credentials,
3727
+ token,
3728
+ debug,
3729
+ fetch: customFetch
3730
+ });
3731
+ }
3732
+
3435
3733
  // src/plugins/api/index.ts
3436
3734
  var apiPlugin = definePlugin(
3437
3735
  (sdk) => {
@@ -3444,6 +3742,7 @@ var apiPlugin = definePlugin(
3444
3742
  debug = false,
3445
3743
  maxNetworkRetries = ZAPIER_MAX_NETWORK_RETRIES,
3446
3744
  maxNetworkRetryDelayMs = ZAPIER_MAX_NETWORK_RETRY_DELAY_MS,
3745
+ maxConcurrentRequests = ZAPIER_MAX_CONCURRENT_REQUESTS,
3447
3746
  approvalTimeoutMs,
3448
3747
  maxApprovalRetries,
3449
3748
  approvalMode,
@@ -3458,6 +3757,7 @@ var apiPlugin = definePlugin(
3458
3757
  onEvent,
3459
3758
  maxNetworkRetries,
3460
3759
  maxNetworkRetryDelayMs,
3760
+ maxConcurrentRequests,
3461
3761
  approvalTimeoutMs,
3462
3762
  maxApprovalRetries,
3463
3763
  approvalMode,
@@ -10163,6 +10463,24 @@ var BaseSdkOptionsSchema = z.object({
10163
10463
  * Default is 60000 (60 seconds).
10164
10464
  */
10165
10465
  maxNetworkRetryDelayMs: z.number().optional().describe("Max delay in ms to wait for retry (default: 60000).").meta({ valueHint: "ms" }),
10466
+ /**
10467
+ * Maximum number of concurrent in-flight HTTP requests per client.
10468
+ * Requests beyond this limit queue in FIFO order until a slot frees.
10469
+ * Pass `Infinity` to disable. Default: 200.
10470
+ *
10471
+ * The description and meta are duplicated across the outer wrapper and
10472
+ * the inner numeric branch because the SDK and CLI doc generators walk
10473
+ * the schema differently — the SDK reader looks at wrappers only, while
10474
+ * the CLI reader recurses into union branches.
10475
+ */
10476
+ maxConcurrentRequests: z.union([
10477
+ z.number().int().min(1).max(MAX_CONCURRENCY_LIMIT).describe(
10478
+ `Max concurrent in-flight HTTP requests (default: 200, max: ${MAX_CONCURRENCY_LIMIT}).`
10479
+ ).meta({ valueHint: "count" }),
10480
+ z.literal(Infinity)
10481
+ ]).optional().describe(
10482
+ `Max concurrent in-flight HTTP requests (default: 200, max: ${MAX_CONCURRENCY_LIMIT}).`
10483
+ ).meta({ valueHint: "count" }),
10166
10484
  approvalTimeoutMs: z.number().optional().describe("Timeout in ms for approval polling. Default: 600000 (10 min).").meta({ valueHint: "ms" }),
10167
10485
  maxApprovalRetries: z.number().optional().describe(
10168
10486
  "Maximum number of sequential approval rounds per request (one per gating policy) before giving up. Default: 2."
@@ -10199,4 +10517,4 @@ function createZapierSdk2(options = {}) {
10199
10517
  return createSdk().addPlugin(createOptionsPlugin(options)).addPlugin(eventEmissionPlugin).addPlugin(apiPlugin).addPlugin(manifestPlugin).addPlugin(capabilitiesPlugin).addPlugin(connectionsPlugin).addPlugin(listAppsPlugin).addPlugin(getAppPlugin).addPlugin(listActionsPlugin).addPlugin(getActionPlugin).addPlugin(listActionInputFieldsPlugin).addPlugin(getActionInputFieldsSchemaPlugin).addPlugin(listActionInputFieldChoicesPlugin).addPlugin(listInputFieldsDeprecatedPlugin).addPlugin(getInputFieldsSchemaDeprecatedPlugin).addPlugin(listInputFieldChoicesDeprecatedPlugin).addPlugin(runActionPlugin).addPlugin(listConnectionsPlugin).addPlugin(getConnectionPlugin).addPlugin(findFirstConnectionPlugin).addPlugin(findUniqueConnectionPlugin).addPlugin(listAuthenticationsPlugin).addPlugin(getAuthenticationPlugin).addPlugin(findFirstAuthenticationPlugin).addPlugin(findUniqueAuthenticationPlugin).addPlugin(listClientCredentialsPlugin).addPlugin(createClientCredentialsPlugin).addPlugin(deleteClientCredentialsPlugin).addPlugin(fetchPlugin).addPlugin(requestPlugin).addPlugin(createTriggerInboxPlugin).addPlugin(ensureTriggerInboxPlugin).addPlugin(listTriggerInboxesPlugin).addPlugin(getTriggerInboxPlugin).addPlugin(updateTriggerInboxPlugin).addPlugin(deleteTriggerInboxPlugin).addPlugin(pauseTriggerInboxPlugin).addPlugin(resumeTriggerInboxPlugin).addPlugin(listTriggerInboxMessagesPlugin).addPlugin(leaseTriggerInboxMessagesPlugin).addPlugin(ackTriggerInboxMessagesPlugin).addPlugin(releaseTriggerInboxMessagesPlugin).addPlugin(drainTriggerInboxPlugin).addPlugin(watchTriggerInboxPlugin).addPlugin(listTriggerInputFieldsPlugin).addPlugin(listTriggerInputFieldChoicesPlugin).addPlugin(getTriggerInputFieldsSchemaPlugin).addPlugin(listTablesPlugin).addPlugin(getTablePlugin).addPlugin(deleteTablePlugin).addPlugin(createTablePlugin).addPlugin(listTableFieldsPlugin).addPlugin(createTableFieldsPlugin).addPlugin(deleteTableFieldsPlugin).addPlugin(getTableRecordPlugin).addPlugin(listTableRecordsPlugin).addPlugin(createTableRecordsPlugin).addPlugin(deleteTableRecordsPlugin).addPlugin(updateTableRecordsPlugin).addPlugin(appsPlugin).addPlugin(getProfilePlugin);
10200
10518
  }
10201
10519
 
10202
- export { ActionKeyPropertySchema, ActionPropertySchema, ActionTimeoutMsPropertySchema, ActionTypePropertySchema, AppKeyPropertySchema, AppPropertySchema, AppsPropertySchema, AuthenticationIdPropertySchema, BaseSdkOptionsSchema, CONTEXT_CACHE_MAX_SIZE, CONTEXT_CACHE_TTL_MS, ClientCredentialsObjectSchema, ConnectionEntrySchema, ConnectionIdPropertySchema, ConnectionPropertySchema, ConnectionsMapSchema, ConnectionsPropertySchema, CredentialsFunctionSchema, CredentialsObjectSchema, CredentialsSchema, DEFAULT_ACTION_TIMEOUT_MS, DEFAULT_APPROVAL_TIMEOUT_MS, DEFAULT_CONFIG_PATH, DEFAULT_MAX_APPROVAL_RETRIES, DEFAULT_PAGE_SIZE, DebugPropertySchema, FieldsPropertySchema, InputFieldPropertySchema, InputsPropertySchema, LeaseLimitPropertySchema, LeasePropertySchema, LeaseSecondsPropertySchema, LimitPropertySchema, MAX_PAGE_LIMIT, OffsetPropertySchema, OutputPropertySchema, ParamsPropertySchema, PkceCredentialsObjectSchema, RecordPropertySchema, RecordsPropertySchema, RelayFetchSchema, RelayRequestSchema, ResolvedCredentialsSchema, TablePropertySchema, TablesPropertySchema, TriggerInboxNamePropertySchema, TriggerInboxPropertySchema, ZAPIER_BASE_URL, ZAPIER_MAX_NETWORK_RETRIES, ZAPIER_MAX_NETWORK_RETRY_DELAY_MS, ZapierAbortDrainSignal, ZapierActionError, ZapierApiError, ZapierAppNotFoundError, ZapierApprovalError, ZapierAuthenticationError, ZapierBundleError, ZapierConfigurationError, ZapierConflictError, ZapierError, ZapierNotFoundError, ZapierRateLimitError, ZapierRelayError, ZapierReleaseTriggerMessageSignal, ZapierResourceNotFoundError, ZapierSignal, ZapierTimeoutError, ZapierUnknownError, ZapierValidationError, actionKeyResolver, actionTypeResolver, apiPlugin, appKeyResolver, appsPlugin, connectionIdGenericResolver as authenticationIdGenericResolver, connectionIdResolver as authenticationIdResolver, batch, buildApplicationLifecycleEvent, buildCapabilityMessage, buildErrorEvent, buildErrorEventWithContext, buildMethodCalledEvent, clearTokenCache, clientCredentialsNameResolver, clientIdResolver, composePlugins, connectionIdGenericResolver, connectionIdResolver, connectionsPlugin, createBaseEvent, createClientCredentialsPlugin, createFunction, createMemoryCache, createOptionsPlugin, createPaginatedPluginMethod, createPluginMethod, createSdk, createTableFieldsPlugin, createTablePlugin, createTableRecordsPlugin, createZapierSdk2 as createZapierSdk, createZapierSdkWithoutRegistry, definePlugin, deleteClientCredentialsPlugin, deleteTableFieldsPlugin, deleteTablePlugin, deleteTableRecordsPlugin, fetchPlugin, findFirstConnectionPlugin, findManifestEntry, findUniqueConnectionPlugin, formatErrorMessage, generateEventId, getActionInputFieldsSchemaPlugin, getActionPlugin, getAppPlugin, getBaseUrlFromCredentials, getCiPlatform, getClientIdFromCredentials, getConnectionPlugin, getCpuTime, getCurrentTimestamp, getMemoryUsage, getOsInfo, getPlatformVersions, getPreferredManifestEntryKey, getProfilePlugin, getReleaseId, getTablePlugin, getTableRecordPlugin, getTokenFromCliLogin, getZapierApprovalMode, getZapierSdkService, injectCliLogin, inputFieldKeyResolver, inputsAllOptionalResolver, inputsResolver, invalidateCachedToken, invalidateCredentialsToken, isCi, isCliLoginAvailable, isClientCredentials, isCredentialsFunction, isCredentialsObject, isPkceCredentials, isPositional, listActionInputFieldChoicesPlugin, listActionInputFieldsPlugin, listActionsPlugin, listAppsPlugin, listClientCredentialsPlugin, listConnectionsPlugin, listTableFieldsPlugin, listTableRecordsPlugin, listTablesPlugin, logDeprecation, manifestPlugin, readManifestFromFile, registryPlugin, requestPlugin, resetDeprecationWarnings, resolveAuthToken, resolveCredentials, resolveCredentialsFromEnv, runActionPlugin, runWithTelemetryContext, tableFieldIdsResolver, tableFieldsResolver, tableFiltersResolver, tableIdResolver, tableNameResolver, tableRecordIdResolver, tableRecordIdsResolver, tableRecordsResolver, tableSortResolver, tableUpdateRecordsResolver, toSnakeCase, toTitleCase, triggerInboxResolver, updateTableRecordsPlugin };
10520
+ export { ActionKeyPropertySchema, ActionPropertySchema, ActionTimeoutMsPropertySchema, ActionTypePropertySchema, AppKeyPropertySchema, AppPropertySchema, AppsPropertySchema, AuthenticationIdPropertySchema, BaseSdkOptionsSchema, CONTEXT_CACHE_MAX_SIZE, CONTEXT_CACHE_TTL_MS, ClientCredentialsObjectSchema, ConnectionEntrySchema, ConnectionIdPropertySchema, ConnectionPropertySchema, ConnectionsMapSchema, ConnectionsPropertySchema, CredentialsFunctionSchema, CredentialsObjectSchema, CredentialsSchema, DEFAULT_ACTION_TIMEOUT_MS, DEFAULT_APPROVAL_TIMEOUT_MS, DEFAULT_CONFIG_PATH, DEFAULT_MAX_APPROVAL_RETRIES, DEFAULT_PAGE_SIZE, DebugPropertySchema, FieldsPropertySchema, InputFieldPropertySchema, InputsPropertySchema, LeaseLimitPropertySchema, LeasePropertySchema, LeaseSecondsPropertySchema, LimitPropertySchema, MAX_CONCURRENCY_LIMIT, MAX_PAGE_LIMIT, OffsetPropertySchema, OutputPropertySchema, ParamsPropertySchema, PkceCredentialsObjectSchema, RecordPropertySchema, RecordsPropertySchema, RelayFetchSchema, RelayRequestSchema, ResolvedCredentialsSchema, TablePropertySchema, TablesPropertySchema, TriggerInboxNamePropertySchema, TriggerInboxPropertySchema, ZAPIER_BASE_URL, ZAPIER_MAX_CONCURRENT_REQUESTS, ZAPIER_MAX_NETWORK_RETRIES, ZAPIER_MAX_NETWORK_RETRY_DELAY_MS, ZapierAbortDrainSignal, ZapierActionError, ZapierApiError, ZapierAppNotFoundError, ZapierApprovalError, ZapierAuthenticationError, ZapierBundleError, ZapierConfigurationError, ZapierConflictError, ZapierError, ZapierNotFoundError, ZapierRateLimitError, ZapierRelayError, ZapierReleaseTriggerMessageSignal, ZapierResourceNotFoundError, ZapierSignal, ZapierTimeoutError, ZapierUnknownError, ZapierValidationError, actionKeyResolver, actionTypeResolver, apiPlugin, appKeyResolver, appsPlugin, connectionIdGenericResolver as authenticationIdGenericResolver, connectionIdResolver as authenticationIdResolver, batch, buildApplicationLifecycleEvent, buildCapabilityMessage, buildErrorEvent, buildErrorEventWithContext, buildMethodCalledEvent, clearTokenCache, clientCredentialsNameResolver, clientIdResolver, composePlugins, connectionIdGenericResolver, connectionIdResolver, connectionsPlugin, createBaseEvent, createClientCredentialsPlugin, createFunction, createMemoryCache, createOptionsPlugin, createPaginatedPluginMethod, createPluginMethod, createSdk, createTableFieldsPlugin, createTablePlugin, createTableRecordsPlugin, createZapierApi, createZapierSdk2 as createZapierSdk, createZapierSdkWithoutRegistry, definePlugin, deleteClientCredentialsPlugin, deleteTableFieldsPlugin, deleteTablePlugin, deleteTableRecordsPlugin, fetchPlugin, findFirstConnectionPlugin, findManifestEntry, findUniqueConnectionPlugin, formatErrorMessage, generateEventId, getActionInputFieldsSchemaPlugin, getActionPlugin, getAppPlugin, getBaseUrlFromCredentials, getCiPlatform, getClientIdFromCredentials, getConnectionPlugin, getCpuTime, getCurrentTimestamp, getMemoryUsage, getOrCreateApiClient, getOsInfo, getPlatformVersions, getPreferredManifestEntryKey, getProfilePlugin, getReleaseId, getTablePlugin, getTableRecordPlugin, getTokenFromCliLogin, getZapierApprovalMode, getZapierSdkService, injectCliLogin, inputFieldKeyResolver, inputsAllOptionalResolver, inputsResolver, invalidateCachedToken, invalidateCredentialsToken, isCi, isCliLoginAvailable, isClientCredentials, isCredentialsFunction, isCredentialsObject, isPkceCredentials, isPositional, listActionInputFieldChoicesPlugin, listActionInputFieldsPlugin, listActionsPlugin, listAppsPlugin, listClientCredentialsPlugin, listConnectionsPlugin, listTableFieldsPlugin, listTableRecordsPlugin, listTablesPlugin, logDeprecation, manifestPlugin, parseConcurrencyEnvVar, readManifestFromFile, registryPlugin, requestPlugin, resetDeprecationWarnings, resolveAuthToken, resolveCredentials, resolveCredentialsFromEnv, runActionPlugin, runWithTelemetryContext, tableFieldIdsResolver, tableFieldsResolver, tableFiltersResolver, tableIdResolver, tableNameResolver, tableRecordIdResolver, tableRecordIdsResolver, tableRecordsResolver, tableSortResolver, tableUpdateRecordsResolver, toSnakeCase, toTitleCase, triggerInboxResolver, updateTableRecordsPlugin };