@zapier/zapier-sdk 0.107.0 → 0.107.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @zapier/zapier-sdk
2
2
 
3
+ ## 0.107.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 48996ac: _This release contains no user-facing changes._
8
+
9
+ ## 0.107.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 274d411: `getConnection` now:
14
+ - Raises a `ZapierValidationError` (`ZAPIER_VALIDATION_ERROR`) when the response
15
+ does not match the connection schema; previously some malformed responses
16
+ surfaced as `ZapierApiError`.
17
+ - Preserves error status codes and messages. Previously, some 4xx messages were
18
+ replaced with generic text and some 5xx responses were reported as generic
19
+ 502 errors.
20
+
21
+ Output is unchanged for conforming responses.
22
+
3
23
  ## 0.107.0
4
24
 
5
25
  ### Minor Changes
@@ -4,7 +4,7 @@ import { withPositional, declareOptionalProperty, defineProperty, createDeprecat
4
4
  export { CONTEXT, CORE_ERROR_SYMBOL, CORE_OPTIONS_ID, CORE_SIGNAL_SYMBOL, CoreCancelledSignal, CoreDisposeError, CoreErrorCode, CoreSignal, addPlugin, composePlugins, createController, createCorePlugin, createFunction, createPaginatedFunction, createPaginatedPluginMethod, createPluginMethod, createPluginStack, createSdk, declareMethod, declareOptionalProperty, declarePlugin, declareProperty, defineFormatter, defineLegacyMerge, defineMethod, defineMethodOverride, defineOverride, definePlugin, defineProperty, defineResolver, disposeSdk, fromFunctionPlugin, getContext, getCoreErrorCause, getCoreErrorCode, getNegatable, getRegistryPlugin, isCoreCancelledSignal, isCoreError, isCoreSignal, isPositional, omitExports, resolvePlugin, runInMethodScope, runWithTelemetryContext, selectExports, toSnakeCase, toTitleCase } from '@zapier/kitcore';
5
5
  import { buildHttpRequestContext, buildActionRunContext } from '@zapier/policy-context';
6
6
  import { ListAppsQuerySchema, AppItemSchema as AppItemSchema$1 } from '@zapier/zapier-sdk-core/v0/schemas/apps';
7
- import { ListConnectionsQuerySchema as ListConnectionsQuerySchema$1, ConnectionItemSchema } from '@zapier/zapier-sdk-core/v0/schemas/connections';
7
+ import { ListConnectionsQuerySchema as ListConnectionsQuerySchema$1, ConnectionSchema, ConnectionItemSchema } from '@zapier/zapier-sdk-core/v0/schemas/connections';
8
8
  import { ListClientCredentialsQuerySchema as ListClientCredentialsQuerySchema$1, ClientCredentialsItemSchema as ClientCredentialsItemSchema$1, CreateClientCredentialsRequestSchema, ClientCredentialsCreatedItemSchema as ClientCredentialsCreatedItemSchema$1 } from '@zapier/zapier-sdk-core/v0/schemas/client-credentials';
9
9
 
10
10
  // src/constants.ts
@@ -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
@@ -2434,10 +2530,13 @@ function resolveOverrideRoute({
2434
2530
  }) {
2435
2531
  const { matchedPrefix, routeConfig, override } = overrideMatch;
2436
2532
  const directPath = deriveDirectPath({ path, matchedPrefix, routeConfig });
2533
+ const overrideConfig = routeConfig.override?.enabled === false ? void 0 : routeConfig.override;
2437
2534
  return {
2438
2535
  url: buildRouteOverrideUrl({ origin: override.origin, path: directPath }),
2439
2536
  pathConfig: routeConfig,
2440
- canSendDeprecationMessaging: false
2537
+ canSendDeprecationMessaging: false,
2538
+ ...overrideConfig?.transformRequestHeaders ? { transformRequestHeaders: overrideConfig.transformRequestHeaders } : {},
2539
+ ...overrideConfig?.transformResponseHeaders ? { transformResponseHeaders: overrideConfig.transformResponseHeaders } : {}
2441
2540
  };
2442
2541
  }
2443
2542
  function deriveDirectPath({
@@ -2617,6 +2716,89 @@ function isRouteOverrideEnabled({
2617
2716
  return routeConfig.override?.enabled !== false;
2618
2717
  }
2619
2718
 
2719
+ // src/api/routing/headers/request.ts
2720
+ function applyRequestHeaderTransform({
2721
+ headers,
2722
+ resolvedRoute,
2723
+ debugLog
2724
+ }) {
2725
+ return resolvedRoute?.transformRequestHeaders?.({ headers, debugLog }) ?? headers;
2726
+ }
2727
+
2728
+ // src/api/routing/headers/response.ts
2729
+ var NULL_BODY_RESPONSE_STATUSES = /* @__PURE__ */ new Set([204, 205, 304]);
2730
+ function applyResponseHeaderTransform({
2731
+ response,
2732
+ resolvedRoute
2733
+ }) {
2734
+ const transformHeaders = resolvedRoute?.transformResponseHeaders;
2735
+ if (!transformHeaders) {
2736
+ return response;
2737
+ }
2738
+ if (!Number.isInteger(response.status) || response.status < 200 || response.status > 599 || response.bodyUsed || response.body?.locked) {
2739
+ return response;
2740
+ }
2741
+ const originalHeaders = response.headers;
2742
+ if (!originalHeaders) {
2743
+ return response;
2744
+ }
2745
+ const transformedHeaders = transformHeaders({
2746
+ headers: new Headers(originalHeaders)
2747
+ });
2748
+ if (areHeadersEqual({ first: originalHeaders, second: transformedHeaders })) {
2749
+ return response;
2750
+ }
2751
+ const transformedResponse = new Response(
2752
+ NULL_BODY_RESPONSE_STATUSES.has(response.status) ? null : response.body,
2753
+ {
2754
+ status: response.status,
2755
+ statusText: response.statusText,
2756
+ headers: transformedHeaders
2757
+ }
2758
+ );
2759
+ return preserveResponseMetadata({
2760
+ response: transformedResponse,
2761
+ source: response
2762
+ });
2763
+ }
2764
+ function preserveResponseMetadata({
2765
+ response,
2766
+ source
2767
+ }) {
2768
+ const nativeClone = response.clone;
2769
+ Object.defineProperties(response, {
2770
+ clone: {
2771
+ configurable: true,
2772
+ writable: true,
2773
+ value: function clone() {
2774
+ const clonedResponse = nativeClone.call(this);
2775
+ return this === response ? preserveResponseMetadata({ response: clonedResponse, source }) : clonedResponse;
2776
+ }
2777
+ },
2778
+ ...sourceMetadataDescriptors(source)
2779
+ });
2780
+ return response;
2781
+ }
2782
+ function sourceMetadataDescriptors(source) {
2783
+ const descriptors = {};
2784
+ for (const key of ["redirected", "type", "url"]) {
2785
+ if (source[key] !== void 0) {
2786
+ descriptors[key] = { configurable: true, value: source[key] };
2787
+ }
2788
+ }
2789
+ return descriptors;
2790
+ }
2791
+ function areHeadersEqual({
2792
+ first,
2793
+ second
2794
+ }) {
2795
+ const firstEntries = [...first.entries()];
2796
+ const secondEntries = [...second.entries()];
2797
+ return firstEntries.length === secondEntries.length && firstEntries.every(
2798
+ ([key, value], index) => secondEntries[index]?.[0] === key && secondEntries[index]?.[1] === value
2799
+ );
2800
+ }
2801
+
2620
2802
  // src/api/routing/index.ts
2621
2803
  var routingConfig = createRoutingConfig({ pathConfig });
2622
2804
  function parseRoutingOptions({
@@ -2684,7 +2866,7 @@ function logRouteOverride({
2684
2866
  }
2685
2867
 
2686
2868
  // src/sdk-version.ts
2687
- var SDK_VERSION = (typeof process !== "undefined" && process.env ? "0.107.0" : void 0) || "unknown";
2869
+ var SDK_VERSION = (typeof process !== "undefined" && process.env ? "0.107.2" : void 0) || "unknown";
2688
2870
 
2689
2871
  // src/utils/open-url.ts
2690
2872
  var nodePrefix = "node:";
@@ -2824,14 +3006,14 @@ var ZapierApiClient = class {
2824
3006
  * auth/retry pipeline instead of reaching for `this.options.fetch`
2825
3007
  * directly and drifting.
2826
3008
  */
2827
- this.rawFetchUrl = async (url, init, pathConfig2) => {
3009
+ this.rawFetchUrl = async (url, init, resolvedRoute) => {
2828
3010
  const { [CORRELATION_CALL_ID]: callId, ...fetchInit } = init ?? {};
2829
3011
  if (fetchInit.body && (isPlainObject(fetchInit.body) || Array.isArray(fetchInit.body))) {
2830
3012
  fetchInit.body = JSON.stringify(fetchInit.body);
2831
3013
  }
2832
3014
  const builtHeaders = await this.buildHeaders(
2833
3015
  fetchInit,
2834
- pathConfig2
3016
+ resolvedRoute?.pathConfig
2835
3017
  );
2836
3018
  const inputHeaders = new Headers(fetchInit.headers ?? {});
2837
3019
  const mergedHeaders = new Headers();
@@ -2842,6 +3024,11 @@ var ZapierApiClient = class {
2842
3024
  mergedHeaders.set(key, value);
2843
3025
  });
2844
3026
  this.applyTelemetryHeaders({ headers: mergedHeaders, callId });
3027
+ const outboundHeaders = applyRequestHeaderTransform({
3028
+ headers: mergedHeaders,
3029
+ resolvedRoute,
3030
+ debugLog: this.options.debugLog
3031
+ });
2845
3032
  const {
2846
3033
  authRequired: _authRequired,
2847
3034
  requiredScopes: _requiredScopes,
@@ -2854,11 +3041,11 @@ var ZapierApiClient = class {
2854
3041
  const request = {
2855
3042
  ...wireInit,
2856
3043
  url,
2857
- headers: Object.fromEntries(mergedHeaders)
3044
+ headers: Object.fromEntries(outboundHeaders)
2858
3045
  };
2859
3046
  const response = await this.options.sendHttpRequest(request);
2860
3047
  if (response.status !== 429) {
2861
- return response;
3048
+ return applyResponseHeaderTransform({ response, resolvedRoute });
2862
3049
  }
2863
3050
  throw new ZapierRateLimitError(
2864
3051
  await this.readRateLimitErrorMessage(response),
@@ -2928,10 +3115,10 @@ var ZapierApiClient = class {
2928
3115
  */
2929
3116
  this.rawFetch = async (path, init) => {
2930
3117
  validateSdkPath(path);
2931
- const { url, pathConfig: pathConfig2 } = this.buildUrl(path, init?.searchParams);
3118
+ const { url, resolvedRoute } = this.buildUrl(path, init?.searchParams);
2932
3119
  return this.withSemaphore(
2933
3120
  { url, method: init?.method ?? "GET", signal: init?.signal },
2934
- () => this.rawFetchUrl(url, init, pathConfig2)
3121
+ () => this.rawFetchUrl(url, init, resolvedRoute)
2935
3122
  );
2936
3123
  };
2937
3124
  this.runApprovalFetchLoop = async ({
@@ -3341,22 +3528,12 @@ var ZapierApiClient = class {
3341
3528
  debugLog: this.options.debugLog
3342
3529
  });
3343
3530
  }
3344
- // Helper to build full URLs and return routing info
3345
3531
  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
- }
3532
+ const resolvedRoute = this.applyPathConfiguration(path);
3356
3533
  return {
3357
- url: url.toString(),
3358
- pathConfig: config,
3359
- canSendDeprecationMessaging
3534
+ url: withSearchParams({ url: resolvedRoute.url, searchParams }),
3535
+ resolvedRoute,
3536
+ canSendDeprecationMessaging: resolvedRoute.canSendDeprecationMessaging
3360
3537
  };
3361
3538
  }
3362
3539
  // Helper to build headers
@@ -7977,6 +8154,13 @@ var ListConnectionsQuerySchema = ListConnectionsQuerySchema$1.omit({
7977
8154
  // SDK specific property for pagination/iterable helpers
7978
8155
  cursor: z.string().optional().describe("Cursor to start from")
7979
8156
  }).describe("List available connections with optional filtering");
8157
+ ConnectionSchema.extend({
8158
+ is_stale: z.boolean().optional(),
8159
+ is_shared: z.boolean().optional(),
8160
+ members: z.array(z.record(z.string(), z.any())).optional(),
8161
+ customuser_id: z.number().nullable().optional(),
8162
+ customuser_public_id: z.string().nullable().optional()
8163
+ });
7980
8164
  function formatConnectionItem(item) {
7981
8165
  const details = [];
7982
8166
  const appKey = item.app_key ?? "unknown";
@@ -8288,6 +8472,76 @@ var getAppPlugin = defineMethod({
8288
8472
  throw new ZapierAppNotFoundError("App not found", { appKey });
8289
8473
  }
8290
8474
  });
8475
+
8476
+ // src/normalizers/shared.ts
8477
+ function fastifyToString(value) {
8478
+ if (value === void 0) {
8479
+ return void 0;
8480
+ }
8481
+ if (typeof value === "string") {
8482
+ return value;
8483
+ }
8484
+ if (value === null) {
8485
+ return "";
8486
+ }
8487
+ if (value instanceof Date) {
8488
+ return value.toISOString();
8489
+ }
8490
+ if (value instanceof RegExp) {
8491
+ return value.source;
8492
+ }
8493
+ try {
8494
+ return String(value.toString());
8495
+ } catch {
8496
+ return "[unserializable]";
8497
+ }
8498
+ }
8499
+
8500
+ // src/normalizers/connection.ts
8501
+ function normalizeConnectionItem({
8502
+ connection,
8503
+ appKey: providedAppKey,
8504
+ appVersion: providedAppVersion,
8505
+ adaptError
8506
+ }) {
8507
+ let appKey = providedAppKey;
8508
+ let appVersion = providedAppVersion;
8509
+ if (connection.selected_api && typeof connection.selected_api === "string") {
8510
+ const [extractedAppKey, extractedVersion] = splitVersionedKey(
8511
+ connection.selected_api
8512
+ );
8513
+ if (!appKey) {
8514
+ appKey = extractedAppKey;
8515
+ }
8516
+ if (!appVersion) {
8517
+ appVersion = extractedVersion;
8518
+ }
8519
+ }
8520
+ const {
8521
+ selected_api: selectedApi,
8522
+ customuser_id: profileId,
8523
+ id,
8524
+ account_id: accountId,
8525
+ ...restOfConnection
8526
+ } = connection;
8527
+ const normalized = {
8528
+ ...restOfConnection,
8529
+ id: String(id),
8530
+ account_id: String(accountId),
8531
+ implementation_id: selectedApi,
8532
+ title: connection.title || connection.label || void 0,
8533
+ is_stale: fastifyToString(connection.is_stale),
8534
+ is_expired: fastifyToString(connection.is_stale),
8535
+ is_shared: fastifyToString(connection.is_shared),
8536
+ members: fastifyToString(connection.members),
8537
+ customuser_public_id: fastifyToString(connection.customuser_public_id),
8538
+ expired_at: connection.marked_stale_at,
8539
+ app_key: appKey,
8540
+ app_version: appVersion,
8541
+ profile_id: profileId != null ? String(profileId) : void 0
8542
+ };
8543
+ return createValidator(ConnectionItemSchema, { adaptError })(normalized);
8544
+ }
8291
8545
  var GetConnectionDescription = "Get details for a specific connection";
8292
8546
  var GetConnectionSchema = z.object({
8293
8547
  connection: ConnectionPropertySchema
@@ -8309,7 +8563,7 @@ var GetConnectionInputSchema = z.union([
8309
8563
  // src/plugins/getConnection/index.ts
8310
8564
  var getConnectionPlugin = defineMethod({
8311
8565
  name: "getConnection",
8312
- imports: [apiPluginRef],
8566
+ imports: [apiPluginRef, coreOptionsPluginRef],
8313
8567
  categories: ["connection"],
8314
8568
  itemType: "Connection",
8315
8569
  inputSchema: GetConnectionInputSchema,
@@ -8321,8 +8575,8 @@ var getConnectionPlugin = defineMethod({
8321
8575
  run: async ({ imports, input }) => {
8322
8576
  const api = imports.api;
8323
8577
  const resolvedConnectionId = "connection" in input ? input.connection : "connectionId" in input ? input.connectionId : input.authenticationId;
8324
- const response = await api.get(
8325
- `/api/v0/connections/${encodeURIComponent(String(resolvedConnectionId))}`,
8578
+ const raw = await api.get(
8579
+ `/zapier/api/v4/authentications/${encodeURIComponent(String(resolvedConnectionId))}/`,
8326
8580
  {
8327
8581
  resource: {
8328
8582
  type: "connection",
@@ -8330,7 +8584,14 @@ var getConnectionPlugin = defineMethod({
8330
8584
  }
8331
8585
  }
8332
8586
  );
8333
- return { data: transformConnectionItem(response.data) };
8587
+ return {
8588
+ data: transformConnectionItem(
8589
+ normalizeConnectionItem({
8590
+ connection: raw,
8591
+ adaptError: imports.coreOptions?.adaptError
8592
+ })
8593
+ )
8594
+ };
8334
8595
  }
8335
8596
  });
8336
8597