@zapier/zapier-sdk 0.107.1 → 0.108.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.
@@ -1309,6 +1309,16 @@ function emitOnce(onEvent, event) {
1309
1309
  var { logDeprecation, resetDeprecationWarnings } = kitcore.createDeprecationLogger("zapier-sdk");
1310
1310
 
1311
1311
  // src/utils/url-utils.ts
1312
+ function withSearchParams({
1313
+ url,
1314
+ searchParams
1315
+ }) {
1316
+ const result = new URL(url);
1317
+ for (const [key, value] of Object.entries(searchParams ?? {})) {
1318
+ result.searchParams.set(key, value);
1319
+ }
1320
+ return result.toString();
1321
+ }
1312
1322
  function getZapierBaseUrl(baseUrl) {
1313
1323
  if (!baseUrl) {
1314
1324
  return void 0;
@@ -2153,6 +2163,88 @@ function parseDeprecationDate(value) {
2153
2163
  if (!match) return void 0;
2154
2164
  return Number(match[1]) * 1e3;
2155
2165
  }
2166
+ var RELAY_ERROR_HEADER = "x-relay-error";
2167
+ var RELAY_TIMEOUT_HEADER = "x-relay-extended-timeout";
2168
+ var SDK_CORRELATION_HEADER = "zapier-correlation-id";
2169
+ var SDK_MAX_TIME_HEADER = "x-zapier-sdk-max-time";
2170
+ var RELAY_NATIVE_TIMEOUT_SECONDS = 30;
2171
+ var RELAY_TIMEOUT_TIERS = [
2172
+ { seconds: 60, headerValue: "1m" },
2173
+ { seconds: 180, headerValue: "3m" },
2174
+ { seconds: 300, headerValue: "5m" },
2175
+ { seconds: 600, headerValue: "10m" }
2176
+ ];
2177
+ function transformRelayRequestHeaders({
2178
+ headers,
2179
+ debugLog
2180
+ }) {
2181
+ applyTimeoutTierPolicy({ headers, debugLog });
2182
+ applyCorrelationIdPolicy({ headers });
2183
+ return headers;
2184
+ }
2185
+ function applyCorrelationIdPolicy({ headers }) {
2186
+ const correlationId = headers.get(SDK_CORRELATION_HEADER);
2187
+ if (correlationId !== null && !isValidRelayCorrelationId(correlationId)) {
2188
+ headers.delete(SDK_CORRELATION_HEADER);
2189
+ }
2190
+ }
2191
+ function applyTimeoutTierPolicy({
2192
+ headers,
2193
+ debugLog
2194
+ }) {
2195
+ const sdkMaxTimeSeconds = headers.get(SDK_MAX_TIME_HEADER);
2196
+ headers.delete(SDK_MAX_TIME_HEADER);
2197
+ if (headers.has(RELAY_TIMEOUT_HEADER) || sdkMaxTimeSeconds === null) {
2198
+ return;
2199
+ }
2200
+ const seconds = parsePositiveInteger(sdkMaxTimeSeconds);
2201
+ if (seconds === void 0 || seconds <= RELAY_NATIVE_TIMEOUT_SECONDS) {
2202
+ return;
2203
+ }
2204
+ const match = RELAY_TIMEOUT_TIERS.find(
2205
+ (candidate) => candidate.seconds >= seconds
2206
+ );
2207
+ const tier = match ?? RELAY_TIMEOUT_TIERS[RELAY_TIMEOUT_TIERS.length - 1];
2208
+ if (!match) {
2209
+ debugLog?.(
2210
+ `Relay timeout of ${seconds}s exceeds the largest tier; capping to ${tier.headerValue}`
2211
+ );
2212
+ }
2213
+ headers.set(RELAY_TIMEOUT_HEADER, tier.headerValue);
2214
+ }
2215
+ function transformRelayResponseHeaders({
2216
+ headers
2217
+ }) {
2218
+ const relayError = headers.get(RELAY_ERROR_HEADER);
2219
+ if (!relayError?.startsWith("Authentication Template Failure")) {
2220
+ return headers;
2221
+ }
2222
+ const templateMatch = relayError.match(
2223
+ /Unable to find template for ([^@]+)@/
2224
+ );
2225
+ if (!templateMatch?.[1]) {
2226
+ return headers;
2227
+ }
2228
+ const appName = templateMatch[1].replace(/CLIAPI$/, "").replace(/([a-z])([A-Z])/g, "$1 $2").trim();
2229
+ if (!appName) {
2230
+ return headers;
2231
+ }
2232
+ headers.set(
2233
+ RELAY_ERROR_HEADER,
2234
+ `${appName} does not support direct HTTP requests. Use zapier.apps.{appKey} or zapier.runAction() to use Actions built for this app.`
2235
+ );
2236
+ return headers;
2237
+ }
2238
+ var RelayCorrelationIdSchema = zod.z.uuid();
2239
+ function isValidRelayCorrelationId(value) {
2240
+ return RelayCorrelationIdSchema.safeParse(value).success;
2241
+ }
2242
+ function parsePositiveInteger(value) {
2243
+ if (!/^[1-9]\d*$/.test(value)) {
2244
+ return void 0;
2245
+ }
2246
+ return Number(value);
2247
+ }
2156
2248
 
2157
2249
  // src/api/routing/config.ts
2158
2250
  var pathConfig = {
@@ -2160,7 +2252,11 @@ var pathConfig = {
2160
2252
  "/relay": {
2161
2253
  authHeader: "X-Relay-Authorization",
2162
2254
  pathPrefix: "/api/v0/sdk/relay",
2163
- omitDeprecationMessaging: true
2255
+ omitDeprecationMessaging: true,
2256
+ override: {
2257
+ transformRequestHeaders: transformRelayRequestHeaders,
2258
+ transformResponseHeaders: transformRelayResponseHeaders
2259
+ }
2164
2260
  },
2165
2261
  // The concrete gateway form of the relay route. Callers that pass the
2166
2262
  // already-prefixed path reach the same third-party upstreams, so it must
@@ -2237,6 +2333,15 @@ var pathConfig = {
2237
2333
  "/agentic-management": {
2238
2334
  authHeader: "Authorization",
2239
2335
  subdomain: "api"
2336
+ },
2337
+ // e.g. /vfs/v1/files -> https://api.zapier.com/vfs/v1/files
2338
+ // The VFS API is registered on the Public API Gateway and has no sdkapi
2339
+ // proxy route, so it goes straight to the gateway. Its governance metadata
2340
+ // rewrites /vfs/v1/... to the backend's /api/v1/fs/..., which is why no
2341
+ // pathPrefix is applied here.
2342
+ "/vfs": {
2343
+ authHeader: "Authorization",
2344
+ subdomain: "api"
2240
2345
  }
2241
2346
  };
2242
2347
 
@@ -2435,10 +2540,13 @@ function resolveOverrideRoute({
2435
2540
  }) {
2436
2541
  const { matchedPrefix, routeConfig, override } = overrideMatch;
2437
2542
  const directPath = deriveDirectPath({ path, matchedPrefix, routeConfig });
2543
+ const overrideConfig = routeConfig.override?.enabled === false ? void 0 : routeConfig.override;
2438
2544
  return {
2439
2545
  url: buildRouteOverrideUrl({ origin: override.origin, path: directPath }),
2440
2546
  pathConfig: routeConfig,
2441
- canSendDeprecationMessaging: false
2547
+ canSendDeprecationMessaging: false,
2548
+ ...overrideConfig?.transformRequestHeaders ? { transformRequestHeaders: overrideConfig.transformRequestHeaders } : {},
2549
+ ...overrideConfig?.transformResponseHeaders ? { transformResponseHeaders: overrideConfig.transformResponseHeaders } : {}
2442
2550
  };
2443
2551
  }
2444
2552
  function deriveDirectPath({
@@ -2618,6 +2726,89 @@ function isRouteOverrideEnabled({
2618
2726
  return routeConfig.override?.enabled !== false;
2619
2727
  }
2620
2728
 
2729
+ // src/api/routing/headers/request.ts
2730
+ function applyRequestHeaderTransform({
2731
+ headers,
2732
+ resolvedRoute,
2733
+ debugLog
2734
+ }) {
2735
+ return resolvedRoute?.transformRequestHeaders?.({ headers, debugLog }) ?? headers;
2736
+ }
2737
+
2738
+ // src/api/routing/headers/response.ts
2739
+ var NULL_BODY_RESPONSE_STATUSES = /* @__PURE__ */ new Set([204, 205, 304]);
2740
+ function applyResponseHeaderTransform({
2741
+ response,
2742
+ resolvedRoute
2743
+ }) {
2744
+ const transformHeaders = resolvedRoute?.transformResponseHeaders;
2745
+ if (!transformHeaders) {
2746
+ return response;
2747
+ }
2748
+ if (!Number.isInteger(response.status) || response.status < 200 || response.status > 599 || response.bodyUsed || response.body?.locked) {
2749
+ return response;
2750
+ }
2751
+ const originalHeaders = response.headers;
2752
+ if (!originalHeaders) {
2753
+ return response;
2754
+ }
2755
+ const transformedHeaders = transformHeaders({
2756
+ headers: new Headers(originalHeaders)
2757
+ });
2758
+ if (areHeadersEqual({ first: originalHeaders, second: transformedHeaders })) {
2759
+ return response;
2760
+ }
2761
+ const transformedResponse = new Response(
2762
+ NULL_BODY_RESPONSE_STATUSES.has(response.status) ? null : response.body,
2763
+ {
2764
+ status: response.status,
2765
+ statusText: response.statusText,
2766
+ headers: transformedHeaders
2767
+ }
2768
+ );
2769
+ return preserveResponseMetadata({
2770
+ response: transformedResponse,
2771
+ source: response
2772
+ });
2773
+ }
2774
+ function preserveResponseMetadata({
2775
+ response,
2776
+ source
2777
+ }) {
2778
+ const nativeClone = response.clone;
2779
+ Object.defineProperties(response, {
2780
+ clone: {
2781
+ configurable: true,
2782
+ writable: true,
2783
+ value: function clone() {
2784
+ const clonedResponse = nativeClone.call(this);
2785
+ return this === response ? preserveResponseMetadata({ response: clonedResponse, source }) : clonedResponse;
2786
+ }
2787
+ },
2788
+ ...sourceMetadataDescriptors(source)
2789
+ });
2790
+ return response;
2791
+ }
2792
+ function sourceMetadataDescriptors(source) {
2793
+ const descriptors = {};
2794
+ for (const key of ["redirected", "type", "url"]) {
2795
+ if (source[key] !== void 0) {
2796
+ descriptors[key] = { configurable: true, value: source[key] };
2797
+ }
2798
+ }
2799
+ return descriptors;
2800
+ }
2801
+ function areHeadersEqual({
2802
+ first,
2803
+ second
2804
+ }) {
2805
+ const firstEntries = [...first.entries()];
2806
+ const secondEntries = [...second.entries()];
2807
+ return firstEntries.length === secondEntries.length && firstEntries.every(
2808
+ ([key, value], index) => secondEntries[index]?.[0] === key && secondEntries[index]?.[1] === value
2809
+ );
2810
+ }
2811
+
2621
2812
  // src/api/routing/index.ts
2622
2813
  var routingConfig = createRoutingConfig({ pathConfig });
2623
2814
  function parseRoutingOptions({
@@ -2685,7 +2876,7 @@ function logRouteOverride({
2685
2876
  }
2686
2877
 
2687
2878
  // src/sdk-version.ts
2688
- var SDK_VERSION = (typeof process !== "undefined" && process.env ? "0.107.1" : void 0) || "unknown";
2879
+ var SDK_VERSION = (typeof process !== "undefined" && process.env ? "0.108.0" : void 0) || "unknown";
2689
2880
 
2690
2881
  // src/utils/open-url.ts
2691
2882
  var nodePrefix = "node:";
@@ -2825,14 +3016,14 @@ var ZapierApiClient = class {
2825
3016
  * auth/retry pipeline instead of reaching for `this.options.fetch`
2826
3017
  * directly and drifting.
2827
3018
  */
2828
- this.rawFetchUrl = async (url, init, pathConfig2) => {
3019
+ this.rawFetchUrl = async (url, init, resolvedRoute) => {
2829
3020
  const { [CORRELATION_CALL_ID]: callId, ...fetchInit } = init ?? {};
2830
3021
  if (fetchInit.body && (isPlainObject(fetchInit.body) || Array.isArray(fetchInit.body))) {
2831
3022
  fetchInit.body = JSON.stringify(fetchInit.body);
2832
3023
  }
2833
3024
  const builtHeaders = await this.buildHeaders(
2834
3025
  fetchInit,
2835
- pathConfig2
3026
+ resolvedRoute?.pathConfig
2836
3027
  );
2837
3028
  const inputHeaders = new Headers(fetchInit.headers ?? {});
2838
3029
  const mergedHeaders = new Headers();
@@ -2843,6 +3034,11 @@ var ZapierApiClient = class {
2843
3034
  mergedHeaders.set(key, value);
2844
3035
  });
2845
3036
  this.applyTelemetryHeaders({ headers: mergedHeaders, callId });
3037
+ const outboundHeaders = applyRequestHeaderTransform({
3038
+ headers: mergedHeaders,
3039
+ resolvedRoute,
3040
+ debugLog: this.options.debugLog
3041
+ });
2846
3042
  const {
2847
3043
  authRequired: _authRequired,
2848
3044
  requiredScopes: _requiredScopes,
@@ -2855,11 +3051,11 @@ var ZapierApiClient = class {
2855
3051
  const request = {
2856
3052
  ...wireInit,
2857
3053
  url,
2858
- headers: Object.fromEntries(mergedHeaders)
3054
+ headers: Object.fromEntries(outboundHeaders)
2859
3055
  };
2860
3056
  const response = await this.options.sendHttpRequest(request);
2861
3057
  if (response.status !== 429) {
2862
- return response;
3058
+ return applyResponseHeaderTransform({ response, resolvedRoute });
2863
3059
  }
2864
3060
  throw new ZapierRateLimitError(
2865
3061
  await this.readRateLimitErrorMessage(response),
@@ -2929,10 +3125,10 @@ var ZapierApiClient = class {
2929
3125
  */
2930
3126
  this.rawFetch = async (path, init) => {
2931
3127
  validateSdkPath(path);
2932
- const { url, pathConfig: pathConfig2 } = this.buildUrl(path, init?.searchParams);
3128
+ const { url, resolvedRoute } = this.buildUrl(path, init?.searchParams);
2933
3129
  return this.withSemaphore(
2934
3130
  { url, method: init?.method ?? "GET", signal: init?.signal },
2935
- () => this.rawFetchUrl(url, init, pathConfig2)
3131
+ () => this.rawFetchUrl(url, init, resolvedRoute)
2936
3132
  );
2937
3133
  };
2938
3134
  this.runApprovalFetchLoop = async ({
@@ -3342,22 +3538,12 @@ var ZapierApiClient = class {
3342
3538
  debugLog: this.options.debugLog
3343
3539
  });
3344
3540
  }
3345
- // Helper to build full URLs and return routing info
3346
3541
  buildUrl(path, searchParams) {
3347
- const {
3348
- url,
3349
- pathConfig: config,
3350
- canSendDeprecationMessaging
3351
- } = this.applyPathConfiguration(path);
3352
- if (searchParams) {
3353
- Object.entries(searchParams).forEach(([key, value]) => {
3354
- url.searchParams.set(key, value);
3355
- });
3356
- }
3542
+ const resolvedRoute = this.applyPathConfiguration(path);
3357
3543
  return {
3358
- url: url.toString(),
3359
- pathConfig: config,
3360
- canSendDeprecationMessaging
3544
+ url: withSearchParams({ url: resolvedRoute.url, searchParams }),
3545
+ resolvedRoute,
3546
+ canSendDeprecationMessaging: resolvedRoute.canSendDeprecationMessaging
3361
3547
  };
3362
3548
  }
3363
3549
  // Helper to build headers
@@ -1308,6 +1308,16 @@ function emitOnce(onEvent, event) {
1308
1308
  var { logDeprecation, resetDeprecationWarnings } = createDeprecationLogger("zapier-sdk");
1309
1309
 
1310
1310
  // src/utils/url-utils.ts
1311
+ function withSearchParams({
1312
+ url,
1313
+ searchParams
1314
+ }) {
1315
+ const result = new URL(url);
1316
+ for (const [key, value] of Object.entries(searchParams ?? {})) {
1317
+ result.searchParams.set(key, value);
1318
+ }
1319
+ return result.toString();
1320
+ }
1311
1321
  function getZapierBaseUrl(baseUrl) {
1312
1322
  if (!baseUrl) {
1313
1323
  return void 0;
@@ -2152,6 +2162,88 @@ function parseDeprecationDate(value) {
2152
2162
  if (!match) return void 0;
2153
2163
  return Number(match[1]) * 1e3;
2154
2164
  }
2165
+ var RELAY_ERROR_HEADER = "x-relay-error";
2166
+ var RELAY_TIMEOUT_HEADER = "x-relay-extended-timeout";
2167
+ var SDK_CORRELATION_HEADER = "zapier-correlation-id";
2168
+ var SDK_MAX_TIME_HEADER = "x-zapier-sdk-max-time";
2169
+ var RELAY_NATIVE_TIMEOUT_SECONDS = 30;
2170
+ var RELAY_TIMEOUT_TIERS = [
2171
+ { seconds: 60, headerValue: "1m" },
2172
+ { seconds: 180, headerValue: "3m" },
2173
+ { seconds: 300, headerValue: "5m" },
2174
+ { seconds: 600, headerValue: "10m" }
2175
+ ];
2176
+ function transformRelayRequestHeaders({
2177
+ headers,
2178
+ debugLog
2179
+ }) {
2180
+ applyTimeoutTierPolicy({ headers, debugLog });
2181
+ applyCorrelationIdPolicy({ headers });
2182
+ return headers;
2183
+ }
2184
+ function applyCorrelationIdPolicy({ headers }) {
2185
+ const correlationId = headers.get(SDK_CORRELATION_HEADER);
2186
+ if (correlationId !== null && !isValidRelayCorrelationId(correlationId)) {
2187
+ headers.delete(SDK_CORRELATION_HEADER);
2188
+ }
2189
+ }
2190
+ function applyTimeoutTierPolicy({
2191
+ headers,
2192
+ debugLog
2193
+ }) {
2194
+ const sdkMaxTimeSeconds = headers.get(SDK_MAX_TIME_HEADER);
2195
+ headers.delete(SDK_MAX_TIME_HEADER);
2196
+ if (headers.has(RELAY_TIMEOUT_HEADER) || sdkMaxTimeSeconds === null) {
2197
+ return;
2198
+ }
2199
+ const seconds = parsePositiveInteger(sdkMaxTimeSeconds);
2200
+ if (seconds === void 0 || seconds <= RELAY_NATIVE_TIMEOUT_SECONDS) {
2201
+ return;
2202
+ }
2203
+ const match = RELAY_TIMEOUT_TIERS.find(
2204
+ (candidate) => candidate.seconds >= seconds
2205
+ );
2206
+ const tier = match ?? RELAY_TIMEOUT_TIERS[RELAY_TIMEOUT_TIERS.length - 1];
2207
+ if (!match) {
2208
+ debugLog?.(
2209
+ `Relay timeout of ${seconds}s exceeds the largest tier; capping to ${tier.headerValue}`
2210
+ );
2211
+ }
2212
+ headers.set(RELAY_TIMEOUT_HEADER, tier.headerValue);
2213
+ }
2214
+ function transformRelayResponseHeaders({
2215
+ headers
2216
+ }) {
2217
+ const relayError = headers.get(RELAY_ERROR_HEADER);
2218
+ if (!relayError?.startsWith("Authentication Template Failure")) {
2219
+ return headers;
2220
+ }
2221
+ const templateMatch = relayError.match(
2222
+ /Unable to find template for ([^@]+)@/
2223
+ );
2224
+ if (!templateMatch?.[1]) {
2225
+ return headers;
2226
+ }
2227
+ const appName = templateMatch[1].replace(/CLIAPI$/, "").replace(/([a-z])([A-Z])/g, "$1 $2").trim();
2228
+ if (!appName) {
2229
+ return headers;
2230
+ }
2231
+ headers.set(
2232
+ RELAY_ERROR_HEADER,
2233
+ `${appName} does not support direct HTTP requests. Use zapier.apps.{appKey} or zapier.runAction() to use Actions built for this app.`
2234
+ );
2235
+ return headers;
2236
+ }
2237
+ var RelayCorrelationIdSchema = z.uuid();
2238
+ function isValidRelayCorrelationId(value) {
2239
+ return RelayCorrelationIdSchema.safeParse(value).success;
2240
+ }
2241
+ function parsePositiveInteger(value) {
2242
+ if (!/^[1-9]\d*$/.test(value)) {
2243
+ return void 0;
2244
+ }
2245
+ return Number(value);
2246
+ }
2155
2247
 
2156
2248
  // src/api/routing/config.ts
2157
2249
  var pathConfig = {
@@ -2159,7 +2251,11 @@ var pathConfig = {
2159
2251
  "/relay": {
2160
2252
  authHeader: "X-Relay-Authorization",
2161
2253
  pathPrefix: "/api/v0/sdk/relay",
2162
- omitDeprecationMessaging: true
2254
+ omitDeprecationMessaging: true,
2255
+ override: {
2256
+ transformRequestHeaders: transformRelayRequestHeaders,
2257
+ transformResponseHeaders: transformRelayResponseHeaders
2258
+ }
2163
2259
  },
2164
2260
  // The concrete gateway form of the relay route. Callers that pass the
2165
2261
  // already-prefixed path reach the same third-party upstreams, so it must
@@ -2236,6 +2332,15 @@ var pathConfig = {
2236
2332
  "/agentic-management": {
2237
2333
  authHeader: "Authorization",
2238
2334
  subdomain: "api"
2335
+ },
2336
+ // e.g. /vfs/v1/files -> https://api.zapier.com/vfs/v1/files
2337
+ // The VFS API is registered on the Public API Gateway and has no sdkapi
2338
+ // proxy route, so it goes straight to the gateway. Its governance metadata
2339
+ // rewrites /vfs/v1/... to the backend's /api/v1/fs/..., which is why no
2340
+ // pathPrefix is applied here.
2341
+ "/vfs": {
2342
+ authHeader: "Authorization",
2343
+ subdomain: "api"
2239
2344
  }
2240
2345
  };
2241
2346
 
@@ -2434,10 +2539,13 @@ function resolveOverrideRoute({
2434
2539
  }) {
2435
2540
  const { matchedPrefix, routeConfig, override } = overrideMatch;
2436
2541
  const directPath = deriveDirectPath({ path, matchedPrefix, routeConfig });
2542
+ const overrideConfig = routeConfig.override?.enabled === false ? void 0 : routeConfig.override;
2437
2543
  return {
2438
2544
  url: buildRouteOverrideUrl({ origin: override.origin, path: directPath }),
2439
2545
  pathConfig: routeConfig,
2440
- canSendDeprecationMessaging: false
2546
+ canSendDeprecationMessaging: false,
2547
+ ...overrideConfig?.transformRequestHeaders ? { transformRequestHeaders: overrideConfig.transformRequestHeaders } : {},
2548
+ ...overrideConfig?.transformResponseHeaders ? { transformResponseHeaders: overrideConfig.transformResponseHeaders } : {}
2441
2549
  };
2442
2550
  }
2443
2551
  function deriveDirectPath({
@@ -2617,6 +2725,89 @@ function isRouteOverrideEnabled({
2617
2725
  return routeConfig.override?.enabled !== false;
2618
2726
  }
2619
2727
 
2728
+ // src/api/routing/headers/request.ts
2729
+ function applyRequestHeaderTransform({
2730
+ headers,
2731
+ resolvedRoute,
2732
+ debugLog
2733
+ }) {
2734
+ return resolvedRoute?.transformRequestHeaders?.({ headers, debugLog }) ?? headers;
2735
+ }
2736
+
2737
+ // src/api/routing/headers/response.ts
2738
+ var NULL_BODY_RESPONSE_STATUSES = /* @__PURE__ */ new Set([204, 205, 304]);
2739
+ function applyResponseHeaderTransform({
2740
+ response,
2741
+ resolvedRoute
2742
+ }) {
2743
+ const transformHeaders = resolvedRoute?.transformResponseHeaders;
2744
+ if (!transformHeaders) {
2745
+ return response;
2746
+ }
2747
+ if (!Number.isInteger(response.status) || response.status < 200 || response.status > 599 || response.bodyUsed || response.body?.locked) {
2748
+ return response;
2749
+ }
2750
+ const originalHeaders = response.headers;
2751
+ if (!originalHeaders) {
2752
+ return response;
2753
+ }
2754
+ const transformedHeaders = transformHeaders({
2755
+ headers: new Headers(originalHeaders)
2756
+ });
2757
+ if (areHeadersEqual({ first: originalHeaders, second: transformedHeaders })) {
2758
+ return response;
2759
+ }
2760
+ const transformedResponse = new Response(
2761
+ NULL_BODY_RESPONSE_STATUSES.has(response.status) ? null : response.body,
2762
+ {
2763
+ status: response.status,
2764
+ statusText: response.statusText,
2765
+ headers: transformedHeaders
2766
+ }
2767
+ );
2768
+ return preserveResponseMetadata({
2769
+ response: transformedResponse,
2770
+ source: response
2771
+ });
2772
+ }
2773
+ function preserveResponseMetadata({
2774
+ response,
2775
+ source
2776
+ }) {
2777
+ const nativeClone = response.clone;
2778
+ Object.defineProperties(response, {
2779
+ clone: {
2780
+ configurable: true,
2781
+ writable: true,
2782
+ value: function clone() {
2783
+ const clonedResponse = nativeClone.call(this);
2784
+ return this === response ? preserveResponseMetadata({ response: clonedResponse, source }) : clonedResponse;
2785
+ }
2786
+ },
2787
+ ...sourceMetadataDescriptors(source)
2788
+ });
2789
+ return response;
2790
+ }
2791
+ function sourceMetadataDescriptors(source) {
2792
+ const descriptors = {};
2793
+ for (const key of ["redirected", "type", "url"]) {
2794
+ if (source[key] !== void 0) {
2795
+ descriptors[key] = { configurable: true, value: source[key] };
2796
+ }
2797
+ }
2798
+ return descriptors;
2799
+ }
2800
+ function areHeadersEqual({
2801
+ first,
2802
+ second
2803
+ }) {
2804
+ const firstEntries = [...first.entries()];
2805
+ const secondEntries = [...second.entries()];
2806
+ return firstEntries.length === secondEntries.length && firstEntries.every(
2807
+ ([key, value], index) => secondEntries[index]?.[0] === key && secondEntries[index]?.[1] === value
2808
+ );
2809
+ }
2810
+
2620
2811
  // src/api/routing/index.ts
2621
2812
  var routingConfig = createRoutingConfig({ pathConfig });
2622
2813
  function parseRoutingOptions({
@@ -2684,7 +2875,7 @@ function logRouteOverride({
2684
2875
  }
2685
2876
 
2686
2877
  // src/sdk-version.ts
2687
- var SDK_VERSION = (typeof process !== "undefined" && process.env ? "0.107.1" : void 0) || "unknown";
2878
+ var SDK_VERSION = (typeof process !== "undefined" && process.env ? "0.108.0" : void 0) || "unknown";
2688
2879
 
2689
2880
  // src/utils/open-url.ts
2690
2881
  var nodePrefix = "node:";
@@ -2824,14 +3015,14 @@ var ZapierApiClient = class {
2824
3015
  * auth/retry pipeline instead of reaching for `this.options.fetch`
2825
3016
  * directly and drifting.
2826
3017
  */
2827
- this.rawFetchUrl = async (url, init, pathConfig2) => {
3018
+ this.rawFetchUrl = async (url, init, resolvedRoute) => {
2828
3019
  const { [CORRELATION_CALL_ID]: callId, ...fetchInit } = init ?? {};
2829
3020
  if (fetchInit.body && (isPlainObject(fetchInit.body) || Array.isArray(fetchInit.body))) {
2830
3021
  fetchInit.body = JSON.stringify(fetchInit.body);
2831
3022
  }
2832
3023
  const builtHeaders = await this.buildHeaders(
2833
3024
  fetchInit,
2834
- pathConfig2
3025
+ resolvedRoute?.pathConfig
2835
3026
  );
2836
3027
  const inputHeaders = new Headers(fetchInit.headers ?? {});
2837
3028
  const mergedHeaders = new Headers();
@@ -2842,6 +3033,11 @@ var ZapierApiClient = class {
2842
3033
  mergedHeaders.set(key, value);
2843
3034
  });
2844
3035
  this.applyTelemetryHeaders({ headers: mergedHeaders, callId });
3036
+ const outboundHeaders = applyRequestHeaderTransform({
3037
+ headers: mergedHeaders,
3038
+ resolvedRoute,
3039
+ debugLog: this.options.debugLog
3040
+ });
2845
3041
  const {
2846
3042
  authRequired: _authRequired,
2847
3043
  requiredScopes: _requiredScopes,
@@ -2854,11 +3050,11 @@ var ZapierApiClient = class {
2854
3050
  const request = {
2855
3051
  ...wireInit,
2856
3052
  url,
2857
- headers: Object.fromEntries(mergedHeaders)
3053
+ headers: Object.fromEntries(outboundHeaders)
2858
3054
  };
2859
3055
  const response = await this.options.sendHttpRequest(request);
2860
3056
  if (response.status !== 429) {
2861
- return response;
3057
+ return applyResponseHeaderTransform({ response, resolvedRoute });
2862
3058
  }
2863
3059
  throw new ZapierRateLimitError(
2864
3060
  await this.readRateLimitErrorMessage(response),
@@ -2928,10 +3124,10 @@ var ZapierApiClient = class {
2928
3124
  */
2929
3125
  this.rawFetch = async (path, init) => {
2930
3126
  validateSdkPath(path);
2931
- const { url, pathConfig: pathConfig2 } = this.buildUrl(path, init?.searchParams);
3127
+ const { url, resolvedRoute } = this.buildUrl(path, init?.searchParams);
2932
3128
  return this.withSemaphore(
2933
3129
  { url, method: init?.method ?? "GET", signal: init?.signal },
2934
- () => this.rawFetchUrl(url, init, pathConfig2)
3130
+ () => this.rawFetchUrl(url, init, resolvedRoute)
2935
3131
  );
2936
3132
  };
2937
3133
  this.runApprovalFetchLoop = async ({
@@ -3341,22 +3537,12 @@ var ZapierApiClient = class {
3341
3537
  debugLog: this.options.debugLog
3342
3538
  });
3343
3539
  }
3344
- // Helper to build full URLs and return routing info
3345
3540
  buildUrl(path, searchParams) {
3346
- const {
3347
- url,
3348
- pathConfig: config,
3349
- canSendDeprecationMessaging
3350
- } = this.applyPathConfiguration(path);
3351
- if (searchParams) {
3352
- Object.entries(searchParams).forEach(([key, value]) => {
3353
- url.searchParams.set(key, value);
3354
- });
3355
- }
3541
+ const resolvedRoute = this.applyPathConfiguration(path);
3356
3542
  return {
3357
- url: url.toString(),
3358
- pathConfig: config,
3359
- canSendDeprecationMessaging
3543
+ url: withSearchParams({ url: resolvedRoute.url, searchParams }),
3544
+ resolvedRoute,
3545
+ canSendDeprecationMessaging: resolvedRoute.canSendDeprecationMessaging
3360
3546
  };
3361
3547
  }
3362
3548
  // Helper to build headers