@zapier/zapier-sdk 0.102.3 → 0.103.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.
@@ -1,6 +1,6 @@
1
1
  import { __require } from './chunk-Y6FXYEAI.mjs';
2
2
  import { z } from 'zod';
3
- import { withPositional, createDeprecationLogger, createAsyncContext, declareOptionalProperty, declareProperty, defineProperty, defineMethod, coreOptionsPluginRef, createValidator, defineFormatter, declareMethod, defineResolver, concatLists, openEnum, defineHook, definePlugin, getRegistryPlugin, paginate, toSnakeCase, isCoreError, toTitleCase, CORE_ERROR_SYMBOL, CoreErrorCode, CORE_SIGNAL_SYMBOL, isCoreSignal, createSdk, CORE_OPTIONS_ID } from '@zapier/kitcore';
3
+ import { withPositional, declareOptionalProperty, defineProperty, createDeprecationLogger, createAsyncContext, definePlugin, sendHttpRequestPlugin, declareProperty, defineMethod, coreOptionsPluginRef, createValidator, defineFormatter, declareMethod, defineResolver, concatLists, openEnum, defineHook, getRegistryPlugin, paginate, toSnakeCase, isCoreError, toTitleCase, createSdk, CORE_ERROR_SYMBOL, CoreErrorCode, CORE_SIGNAL_SYMBOL, isCoreSignal, CORE_OPTIONS_ID } from '@zapier/kitcore';
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';
@@ -1070,6 +1070,51 @@ function createSemaphore(maxPermits) {
1070
1070
  }
1071
1071
  };
1072
1072
  }
1073
+ var SDK_OPTIONS_ID = "zapier/sdkOptions";
1074
+ var sdkOptionsPluginRef = declareOptionalProperty({
1075
+ id: SDK_OPTIONS_ID
1076
+ });
1077
+
1078
+ // src/plugins/transport/options.ts
1079
+ function resolveTransportFetch({
1080
+ fetch: customFetch,
1081
+ debug = false
1082
+ }) {
1083
+ const originalFetch = customFetch ?? ((input, init) => globalThis.fetch(input, init));
1084
+ if (!debug) return originalFetch;
1085
+ return createDebugFetch({
1086
+ originalFetch,
1087
+ debugLog: createDebugLogger(debug)
1088
+ });
1089
+ }
1090
+
1091
+ // src/plugins/transport/http-fetch.ts
1092
+ var httpFetchPlugin = defineProperty({
1093
+ namespace: "kitcore",
1094
+ name: "httpFetch",
1095
+ imports: [sdkOptionsPluginRef],
1096
+ setup: ({ imports }) => resolveTransportFetch({
1097
+ fetch: imports.sdkOptions?.fetch,
1098
+ debug: imports.sdkOptions?.debug
1099
+ }),
1100
+ get: ({ state }) => state
1101
+ });
1102
+
1103
+ // src/plugins/transport/standalone.ts
1104
+ function createZapierSendHttpRequest({
1105
+ fetch: customFetch,
1106
+ debug
1107
+ }) {
1108
+ const sdk = createSdk(
1109
+ definePlugin({
1110
+ name: "zapierStandaloneHttpTransport",
1111
+ imports: [sendHttpRequestPlugin, httpFetchPlugin],
1112
+ exports: [sendHttpRequestPlugin]
1113
+ }),
1114
+ { configuration: { [SDK_OPTIONS_ID]: { fetch: customFetch, debug } } }
1115
+ );
1116
+ return sdk.sendHttpRequest;
1117
+ }
1073
1118
 
1074
1119
  // src/api/correlation.ts
1075
1120
  var CORRELATION_CALL_ID = Symbol(
@@ -1986,8 +2031,528 @@ function parseDeprecationDate(value) {
1986
2031
  return Number(match[1]) * 1e3;
1987
2032
  }
1988
2033
 
2034
+ // src/api/routing/config.ts
2035
+ var pathConfig = {
2036
+ // e.g. /relay -> https://sdkapi.zapier.com/api/v0/sdk/relay/...
2037
+ "/relay": {
2038
+ authHeader: "X-Relay-Authorization",
2039
+ pathPrefix: "/api/v0/sdk/relay",
2040
+ omitDeprecationMessaging: true
2041
+ },
2042
+ // The concrete gateway form of the relay route. Callers that pass the
2043
+ // already-prefixed path reach the same third-party upstreams, so it must
2044
+ // classify as relay too; without this entry it would match nothing and
2045
+ // sniff deprecation headers off a relay response.
2046
+ "/api/v0/sdk/relay": {
2047
+ override: { enabled: false },
2048
+ omitDeprecationMessaging: true
2049
+ },
2050
+ // Concrete sdkapi routes that do not live behind /api/v0/sdk/<service>.
2051
+ "/api/v0": {},
2052
+ // e.g. /zapier -> https://sdkapi.zapier.com/api/v0/sdk/zapier/...
2053
+ "/zapier": {
2054
+ authHeader: "Authorization",
2055
+ pathPrefix: "/api/v0/sdk/zapier"
2056
+ },
2057
+ // e.g. /tables -> https://sdkapi.zapier.com/api/v0/sdk/tables/...
2058
+ "/tables": {
2059
+ authHeader: "Authorization",
2060
+ pathPrefix: "/api/v0/sdk/tables"
2061
+ },
2062
+ // e.g. /trigger-inbox -> https://sdkapi.zapier.com/api/v0/sdk/trigger-inbox/...
2063
+ "/trigger-inbox": {
2064
+ authHeader: "Authorization",
2065
+ pathPrefix: "/api/v0/sdk/trigger-inbox"
2066
+ },
2067
+ // e.g. /sdkdurableapi -> https://sdkapi.zapier.com/api/v0/sdk/sdkdurableapi/...
2068
+ // sdkapi proxies to the sdkdurableapi backend.
2069
+ "/sdkdurableapi": {
2070
+ authHeader: "Authorization",
2071
+ pathPrefix: "/api/v0/sdk/sdkdurableapi"
2072
+ },
2073
+ // e.g. /durableworkflowzaps -> https://sdkapi.zapier.com/api/v0/sdk/durableworkflowzaps/...
2074
+ // sdkapi proxies to the durableworkflowzaps backend.
2075
+ "/durableworkflowzaps": {
2076
+ authHeader: "Authorization",
2077
+ pathPrefix: "/api/v0/sdk/durableworkflowzaps"
2078
+ },
2079
+ // e.g. /code-substrate-runner -> https://sdkapi.zapier.com/api/v0/sdk/code-substrate-runner/...
2080
+ // sdkapi proxies to the code-substrate-runner backend.
2081
+ "/code-substrate-runner": {
2082
+ authHeader: "Authorization",
2083
+ pathPrefix: "/api/v0/sdk/code-substrate-runner"
2084
+ },
2085
+ // e.g. /code-substrate-workflows -> https://sdkapi.zapier.com/api/v0/sdk/code-substrate-workflows/...
2086
+ // sdkapi proxies to the code-substrate-workflows backend.
2087
+ "/code-substrate-workflows": {
2088
+ authHeader: "Authorization",
2089
+ pathPrefix: "/api/v0/sdk/code-substrate-workflows"
2090
+ },
2091
+ // e.g. /code-substrate-analyzer/validations ->
2092
+ // https://api.zapier.com/code-substrate-analyzer/v0/validations
2093
+ "/code-substrate-analyzer": {
2094
+ authHeader: "Authorization",
2095
+ pathPrefix: "/code-substrate-analyzer/v0",
2096
+ subdomain: "api",
2097
+ override: { pathPrefixMode: "preserve" }
2098
+ },
2099
+ // e.g. /forms/v0/forms -> https://api.zapier.com/forms/v0/forms
2100
+ // The Forms API is registered on the Public API Gateway and has no sdkapi
2101
+ // proxy route, so it goes straight to the gateway. Its governance metadata
2102
+ // rewrites /forms/v0/... to the backend's /api/forms/v0/..., which is why no
2103
+ // pathPrefix is applied here.
2104
+ "/forms": {
2105
+ authHeader: "Authorization",
2106
+ subdomain: "api"
2107
+ }
2108
+ };
2109
+
2110
+ // src/api/routing/default.ts
2111
+ function resolveDefaultRoute({
2112
+ path,
2113
+ baseUrl
2114
+ }) {
2115
+ const matchingPathEntries = findPathConfigEntries({ path });
2116
+ const routingMatch = matchingPathEntries[0];
2117
+ let finalPath = path;
2118
+ if (routingMatch?.config.pathPrefix) {
2119
+ const pathWithoutPrefix = path.slice(routingMatch.configPath.length) || "/";
2120
+ finalPath = `${routingMatch.config.pathPrefix}${pathWithoutPrefix}`;
2121
+ }
2122
+ const zapierBaseUrl = getZapierBaseUrl(baseUrl);
2123
+ if (zapierBaseUrl === baseUrl.replace(/\/$/, "")) {
2124
+ const originalBaseUrl = new URL(baseUrl);
2125
+ const subdomain = routingMatch?.config.subdomain ?? "sdkapi";
2126
+ const finalBaseUrl = `https://${subdomain}.${originalBaseUrl.hostname}`;
2127
+ return buildRouteResult({
2128
+ url: new URL(finalPath, finalBaseUrl),
2129
+ matchingPathEntries,
2130
+ routingMatch
2131
+ });
2132
+ }
2133
+ const parsedBaseUrl = new URL(baseUrl);
2134
+ const basePath = parsedBaseUrl.pathname.replace(/\/$/, "");
2135
+ return buildRouteResult({
2136
+ url: new URL(basePath + finalPath, parsedBaseUrl.origin),
2137
+ matchingPathEntries,
2138
+ routingMatch
2139
+ });
2140
+ }
2141
+ function buildRouteResult({
2142
+ url,
2143
+ matchingPathEntries,
2144
+ routingMatch
2145
+ }) {
2146
+ const resolvedPathEntries = findPathConfigEntries({
2147
+ path: url.pathname,
2148
+ matchResolvedGatewayPath: true
2149
+ });
2150
+ const deprecationPathMatches = [
2151
+ ...matchingPathEntries,
2152
+ ...resolvedPathEntries
2153
+ ];
2154
+ const canSendDeprecationMessaging = deprecationPathMatches.length > 0 && deprecationPathMatches.every(
2155
+ ({ config }) => config.omitDeprecationMessaging !== true
2156
+ );
2157
+ return {
2158
+ url,
2159
+ pathConfig: routingMatch?.config,
2160
+ canSendDeprecationMessaging
2161
+ };
2162
+ }
2163
+ function findPathConfigEntries({
2164
+ path,
2165
+ matchResolvedGatewayPath = false
2166
+ }) {
2167
+ const pathSegments = path.split("/").filter(Boolean);
2168
+ return Object.entries(pathConfig).filter(([configPath, config]) => {
2169
+ if (!matchResolvedGatewayPath) {
2170
+ return path === configPath || path.startsWith(`${configPath}/`);
2171
+ }
2172
+ const prefixSegments = (config.pathPrefix ?? configPath).split("/").filter(Boolean);
2173
+ return pathSegments.some(
2174
+ (_, startIndex) => prefixSegments.every(
2175
+ (segment, offset) => pathSegments[startIndex + offset] === segment
2176
+ )
2177
+ );
2178
+ }).map(([configPath, config]) => ({ configPath, config }));
2179
+ }
2180
+
2181
+ // src/api/routing/overrides.ts
2182
+ var ROUTE_ORIGIN_ENV_PREFIX = "ZAPIER_ROUTE_ORIGIN_";
2183
+ function createRoutingConfig({
2184
+ pathConfig: pathConfig2
2185
+ }) {
2186
+ const routeByEnvName = new Map(
2187
+ Object.entries(pathConfig2).filter(([, routeConfig]) => isRouteOverrideEnabled({ routeConfig })).map(([route]) => [encodeRouteOriginEnvName({ route }), route])
2188
+ );
2189
+ return { pathConfig: pathConfig2, routeByEnvName };
2190
+ }
2191
+ function encodeRouteOriginEnvName({ route }) {
2192
+ return `${ROUTE_ORIGIN_ENV_PREFIX}${route.slice(1).split("/").map((segment) => segment.replace(/-/g, "_").toUpperCase()).join("__")}`;
2193
+ }
2194
+ function createRouteOverrideEntry({
2195
+ route,
2196
+ source,
2197
+ sourceReference,
2198
+ origin
2199
+ }) {
2200
+ return {
2201
+ route,
2202
+ source,
2203
+ sourceReference,
2204
+ origin: parseRouteOverrideOrigin({
2205
+ origin,
2206
+ sourceLabel: describeRouteOverrideSource({ source, sourceReference })
2207
+ })
2208
+ };
2209
+ }
2210
+ function describeRouteOverrideSource({
2211
+ source,
2212
+ sourceReference
2213
+ }) {
2214
+ return source === "environment-variable" ? `environment variable ${sourceReference}` : `routeOverrides[${JSON.stringify(sourceReference)}]`;
2215
+ }
2216
+ function readOptionRouteOverrideEntries({
2217
+ routeOverrides,
2218
+ routingConfig: routingConfig2
2219
+ }) {
2220
+ const entries = [];
2221
+ if (!routeOverrides) {
2222
+ return entries;
2223
+ }
2224
+ for (const [route, origin] of Object.entries(routeOverrides)) {
2225
+ assertSupportedRouteOverride({ route, routingConfig: routingConfig2 });
2226
+ entries.push(
2227
+ createRouteOverrideEntry({
2228
+ route,
2229
+ source: "sdk-option",
2230
+ sourceReference: route,
2231
+ origin
2232
+ })
2233
+ );
2234
+ }
2235
+ return entries;
2236
+ }
2237
+ function buildRouteOverrideMap({
2238
+ environmentEntries,
2239
+ optionEntries
2240
+ }) {
2241
+ const routeOverrideMap = /* @__PURE__ */ new Map();
2242
+ for (const entry of environmentEntries) {
2243
+ routeOverrideMap.set(entry.route, entry);
2244
+ }
2245
+ for (const entry of optionEntries) {
2246
+ routeOverrideMap.set(entry.route, entry);
2247
+ }
2248
+ return routeOverrideMap;
2249
+ }
2250
+ function collectRouteOverrideSources({
2251
+ routingOptions,
2252
+ environment,
2253
+ routingConfig: routingConfig2
2254
+ }) {
2255
+ try {
2256
+ const optionEntries = routingOptions?.routeOverrideEntries ?? [];
2257
+ const environmentEntries = readEnvironmentRouteOverrideEntries({
2258
+ environment,
2259
+ routingConfig: routingConfig2
2260
+ });
2261
+ if (optionEntries.length === 0 && environmentEntries.length === 0) {
2262
+ return void 0;
2263
+ }
2264
+ return { optionEntries, environmentEntries };
2265
+ } catch (error) {
2266
+ if (isZapierError(error) && error.code === "ZAPIER_CONFIGURATION_ERROR") {
2267
+ throw error;
2268
+ }
2269
+ return void 0;
2270
+ }
2271
+ }
2272
+ function findRouteOverrideMatch({
2273
+ path,
2274
+ routeOverrideMap,
2275
+ routingConfig: routingConfig2
2276
+ }) {
2277
+ let bestMatch;
2278
+ const { pathname } = splitPathReference({ path });
2279
+ for (const override of routeOverrideMap.values()) {
2280
+ const { route } = override;
2281
+ for (const candidate of getRouteCandidates({
2282
+ route,
2283
+ override,
2284
+ routingConfig: routingConfig2
2285
+ })) {
2286
+ if (!pathnameMatchesPrefix({
2287
+ pathname,
2288
+ prefix: candidate.matchedPrefix
2289
+ })) {
2290
+ continue;
2291
+ }
2292
+ if (!bestMatch || compareRouteOverrideMatches(candidate, bestMatch) < 0) {
2293
+ bestMatch = candidate;
2294
+ }
2295
+ }
2296
+ }
2297
+ return bestMatch;
2298
+ }
2299
+ function resolveOverrideRoute({
2300
+ overrideMatch,
2301
+ path
2302
+ }) {
2303
+ const { matchedPrefix, routeConfig, override } = overrideMatch;
2304
+ const directPath = deriveDirectPath({ path, matchedPrefix, routeConfig });
2305
+ return {
2306
+ url: buildRouteOverrideUrl({ origin: override.origin, path: directPath }),
2307
+ pathConfig: routeConfig,
2308
+ canSendDeprecationMessaging: false
2309
+ };
2310
+ }
2311
+ function deriveDirectPath({
2312
+ path,
2313
+ matchedPrefix,
2314
+ routeConfig
2315
+ }) {
2316
+ const { pathPrefix } = routeConfig;
2317
+ if (!pathPrefix) {
2318
+ return path;
2319
+ }
2320
+ const remainingPath = path.slice(matchedPrefix.length) || "/";
2321
+ const override = routeConfig.override;
2322
+ const pathPrefixMode = override?.enabled === false ? "strip" : override?.pathPrefixMode ?? "strip";
2323
+ return pathPrefixMode === "preserve" ? `${pathPrefix}${remainingPath}` : remainingPath;
2324
+ }
2325
+ function readEnvironmentRouteOverrideEntries({
2326
+ environment,
2327
+ routingConfig: routingConfig2
2328
+ }) {
2329
+ const entries = [];
2330
+ if (!environment) {
2331
+ return entries;
2332
+ }
2333
+ for (const [envName, route] of routingConfig2.routeByEnvName) {
2334
+ let value;
2335
+ try {
2336
+ if (!Object.prototype.hasOwnProperty.call(environment, envName)) {
2337
+ continue;
2338
+ }
2339
+ value = environment[envName];
2340
+ } catch {
2341
+ continue;
2342
+ }
2343
+ if (isUnsetEnvironmentValue(value)) {
2344
+ continue;
2345
+ }
2346
+ entries.push(
2347
+ createRouteOverrideEntry({
2348
+ route,
2349
+ source: "environment-variable",
2350
+ sourceReference: envName,
2351
+ origin: value
2352
+ })
2353
+ );
2354
+ }
2355
+ return entries;
2356
+ }
2357
+ function isUnsetEnvironmentValue(value) {
2358
+ if (value === void 0 || value === null) {
2359
+ return true;
2360
+ }
2361
+ return typeof value === "string" && value.trim() === "";
2362
+ }
2363
+ function assertSupportedRouteOverride({
2364
+ route,
2365
+ routingConfig: routingConfig2
2366
+ }) {
2367
+ if (!Object.prototype.hasOwnProperty.call(routingConfig2.pathConfig, route)) {
2368
+ throw new ZapierConfigurationError(
2369
+ `Route override is not supported for ${route}`,
2370
+ { configType: "routeOverrides" }
2371
+ );
2372
+ }
2373
+ const routeConfig = routingConfig2.pathConfig[route];
2374
+ if (!isRouteOverrideEnabled({ routeConfig })) {
2375
+ throw new ZapierConfigurationError(
2376
+ `Route override is disabled for ${route}`,
2377
+ { configType: "routeOverrides" }
2378
+ );
2379
+ }
2380
+ }
2381
+ function parseRouteOverrideOrigin({
2382
+ origin,
2383
+ sourceLabel
2384
+ }) {
2385
+ if (typeof origin !== "string") {
2386
+ throw new ZapierConfigurationError(
2387
+ `Invalid route override origin for ${sourceLabel}`,
2388
+ { configType: "routeOverrides" }
2389
+ );
2390
+ }
2391
+ let url;
2392
+ try {
2393
+ url = new URL(origin);
2394
+ } catch {
2395
+ throw new ZapierConfigurationError(
2396
+ `Invalid route override origin for ${sourceLabel}`,
2397
+ { configType: "routeOverrides" }
2398
+ );
2399
+ }
2400
+ const includesOnlyOrigin = url.username === "" && url.password === "" && url.pathname === "/" && url.search === "" && url.hash === "";
2401
+ if (!includesOnlyOrigin) {
2402
+ throw new ZapierConfigurationError(
2403
+ `Invalid route override origin for ${sourceLabel}`,
2404
+ { configType: "routeOverrides" }
2405
+ );
2406
+ }
2407
+ if (url.protocol === "https:") {
2408
+ return url.origin;
2409
+ }
2410
+ if (url.protocol === "http:" && isLoopbackHost(url.hostname)) {
2411
+ return url.origin;
2412
+ }
2413
+ throw new ZapierConfigurationError(
2414
+ `Invalid route override origin for ${sourceLabel}: expected https, or http on a loopback host`,
2415
+ { configType: "routeOverrides" }
2416
+ );
2417
+ }
2418
+ function isLoopbackHost(hostname) {
2419
+ return hostname === "localhost" || hostname === "[::1]" || // pii:allow
2420
+ // RFC 6761 reserves .localhost names for loopback.
2421
+ hostname.endsWith(".localhost") || /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname);
2422
+ }
2423
+ function compareRouteOverrideMatches(a, b) {
2424
+ return b.matchedPrefix.length - a.matchedPrefix.length || Number(b.isExactRouteSpelling) - Number(a.isExactRouteSpelling);
2425
+ }
2426
+ function getRouteCandidates({
2427
+ route,
2428
+ override,
2429
+ routingConfig: routingConfig2
2430
+ }) {
2431
+ const routeConfig = routingConfig2.pathConfig[route];
2432
+ if (!routeConfig || !isRouteOverrideEnabled({ routeConfig })) {
2433
+ return [];
2434
+ }
2435
+ const candidates = [
2436
+ {
2437
+ route,
2438
+ matchedPrefix: route,
2439
+ isExactRouteSpelling: true,
2440
+ routeConfig,
2441
+ override
2442
+ }
2443
+ ];
2444
+ if (routeConfig.pathPrefix && routeConfig.pathPrefix !== route) {
2445
+ candidates.push({
2446
+ route,
2447
+ matchedPrefix: routeConfig.pathPrefix,
2448
+ isExactRouteSpelling: false,
2449
+ routeConfig,
2450
+ override
2451
+ });
2452
+ }
2453
+ return candidates;
2454
+ }
2455
+ function buildRouteOverrideUrl({
2456
+ origin,
2457
+ path
2458
+ }) {
2459
+ const url = new URL(origin);
2460
+ const { pathname, search, hash } = splitPathReference({ path });
2461
+ url.pathname = pathname;
2462
+ url.search = search;
2463
+ url.hash = hash;
2464
+ return url;
2465
+ }
2466
+ function splitPathReference({ path }) {
2467
+ const hashStart = path.indexOf("#");
2468
+ const pathWithoutHash = hashStart === -1 ? path : path.slice(0, hashStart);
2469
+ const searchStart = pathWithoutHash.indexOf("?");
2470
+ return {
2471
+ pathname: searchStart === -1 ? pathWithoutHash : pathWithoutHash.slice(0, searchStart),
2472
+ search: searchStart === -1 ? "" : pathWithoutHash.slice(searchStart),
2473
+ hash: hashStart === -1 ? "" : path.slice(hashStart)
2474
+ };
2475
+ }
2476
+ function pathnameMatchesPrefix({
2477
+ pathname,
2478
+ prefix
2479
+ }) {
2480
+ return pathname === prefix || pathname.startsWith(`${prefix}/`);
2481
+ }
2482
+ function isRouteOverrideEnabled({
2483
+ routeConfig
2484
+ }) {
2485
+ return routeConfig.override?.enabled !== false;
2486
+ }
2487
+
2488
+ // src/api/routing/index.ts
2489
+ var routingConfig = createRoutingConfig({ pathConfig });
2490
+ function parseRoutingOptions({
2491
+ options
2492
+ }) {
2493
+ try {
2494
+ return {
2495
+ routeOverrideEntries: readOptionRouteOverrideEntries({
2496
+ routeOverrides: options.routeOverrides,
2497
+ routingConfig
2498
+ })
2499
+ };
2500
+ } catch (error) {
2501
+ if (isZapierError(error) && error.code === "ZAPIER_CONFIGURATION_ERROR") {
2502
+ throw error;
2503
+ }
2504
+ throw new ZapierConfigurationError("Failed to read SDK routing options", {
2505
+ configType: "routeOverrides",
2506
+ cause: error
2507
+ });
2508
+ }
2509
+ }
2510
+ function resolveRoute({
2511
+ path,
2512
+ baseUrl,
2513
+ routingOptions = { routeOverrideEntries: [] },
2514
+ environment,
2515
+ debugLog
2516
+ }) {
2517
+ const overrideSources = collectRouteOverrideSources({
2518
+ routingOptions,
2519
+ environment,
2520
+ routingConfig
2521
+ });
2522
+ if (!overrideSources) {
2523
+ return resolveDefaultRoute({ path, baseUrl });
2524
+ }
2525
+ const routeOverrideMap = buildRouteOverrideMap(overrideSources);
2526
+ const overrideMatch = findRouteOverrideMatch({
2527
+ path,
2528
+ routeOverrideMap,
2529
+ routingConfig
2530
+ });
2531
+ if (overrideMatch) {
2532
+ const resolved = resolveOverrideRoute({ overrideMatch, path });
2533
+ logRouteOverride({ path, resolved, overrideMatch, debugLog });
2534
+ return resolved;
2535
+ }
2536
+ return resolveDefaultRoute({ path, baseUrl });
2537
+ }
2538
+ function logRouteOverride({
2539
+ path,
2540
+ resolved,
2541
+ overrideMatch,
2542
+ debugLog
2543
+ }) {
2544
+ if (!debugLog) {
2545
+ return;
2546
+ }
2547
+ const { route, override } = overrideMatch;
2548
+ const source = describeRouteOverrideSource(override);
2549
+ debugLog(
2550
+ `Route override: ${path} -> ${resolved.url.toString()} (route ${route} via ${source})`
2551
+ );
2552
+ }
2553
+
1989
2554
  // src/sdk-version.ts
1990
- var SDK_VERSION = (typeof process !== "undefined" && process.env ? "0.102.3" : void 0) || "unknown";
2555
+ var SDK_VERSION = (typeof process !== "undefined" && process.env ? "0.103.0" : void 0) || "unknown";
1991
2556
 
1992
2557
  // src/utils/open-url.ts
1993
2558
  var nodePrefix = "node:";
@@ -2111,23 +2676,6 @@ function validateSdkPath(path) {
2111
2676
  );
2112
2677
  }
2113
2678
  }
2114
- function findPathConfigEntries({
2115
- path,
2116
- matchResolvedGatewayPath = false
2117
- }) {
2118
- const pathSegments = path.split("/").filter(Boolean);
2119
- return Object.entries(pathConfig).filter(([configPath, config]) => {
2120
- if (!matchResolvedGatewayPath) {
2121
- return path === configPath || path.startsWith(`${configPath}/`);
2122
- }
2123
- const prefixSegments = (config.pathPrefix ?? configPath).split("/").filter(Boolean);
2124
- return pathSegments.some(
2125
- (_, startIndex) => prefixSegments.every(
2126
- (segment, offset) => pathSegments[startIndex + offset] === segment
2127
- )
2128
- );
2129
- }).map(([configPath, config]) => ({ configPath, config }));
2130
- }
2131
2679
  function parseRateLimitHeaders(response) {
2132
2680
  const info = {};
2133
2681
  const retryAfter = response.headers.get("retry-after");
@@ -2169,78 +2717,6 @@ function parseRateLimitHeaders(response) {
2169
2717
  }
2170
2718
  return info;
2171
2719
  }
2172
- var pathConfig = {
2173
- // e.g. /relay -> https://sdkapi.zapier.com/api/v0/sdk/relay/...
2174
- "/relay": {
2175
- authHeader: "X-Relay-Authorization",
2176
- pathPrefix: "/api/v0/sdk/relay",
2177
- omitDeprecationMessaging: true
2178
- },
2179
- // The concrete gateway form of the relay route. Callers that pass the
2180
- // already-prefixed path reach the same third-party upstreams, so it must
2181
- // classify as relay too; without this entry it would match nothing and
2182
- // sniff deprecation headers off a relay response.
2183
- "/api/v0/sdk/relay": {
2184
- omitDeprecationMessaging: true
2185
- },
2186
- // Concrete sdkapi routes that do not live behind /api/v0/sdk/<service>.
2187
- "/api/v0": {},
2188
- // e.g. /zapier -> https://sdkapi.zapier.com/api/v0/sdk/zapier/...
2189
- "/zapier": {
2190
- authHeader: "Authorization",
2191
- pathPrefix: "/api/v0/sdk/zapier"
2192
- },
2193
- // e.g. /tables -> https://sdkapi.zapier.com/api/v0/sdk/tables/...
2194
- "/tables": {
2195
- authHeader: "Authorization",
2196
- pathPrefix: "/api/v0/sdk/tables"
2197
- },
2198
- // e.g. /trigger-inbox -> https://sdkapi.zapier.com/api/v0/sdk/trigger-inbox/...
2199
- "/trigger-inbox": {
2200
- authHeader: "Authorization",
2201
- pathPrefix: "/api/v0/sdk/trigger-inbox"
2202
- },
2203
- // e.g. /sdkdurableapi -> https://sdkapi.zapier.com/api/v0/sdk/sdkdurableapi/...
2204
- // sdkapi proxies to the sdkdurableapi backend.
2205
- "/sdkdurableapi": {
2206
- authHeader: "Authorization",
2207
- pathPrefix: "/api/v0/sdk/sdkdurableapi"
2208
- },
2209
- // e.g. /durableworkflowzaps -> https://sdkapi.zapier.com/api/v0/sdk/durableworkflowzaps/...
2210
- // sdkapi proxies to the durableworkflowzaps backend.
2211
- "/durableworkflowzaps": {
2212
- authHeader: "Authorization",
2213
- pathPrefix: "/api/v0/sdk/durableworkflowzaps"
2214
- },
2215
- // e.g. /code-substrate-runner -> https://sdkapi.zapier.com/api/v0/sdk/code-substrate-runner/...
2216
- // sdkapi proxies to the code-substrate-runner backend.
2217
- "/code-substrate-runner": {
2218
- authHeader: "Authorization",
2219
- pathPrefix: "/api/v0/sdk/code-substrate-runner"
2220
- },
2221
- // e.g. /code-substrate-workflows -> https://sdkapi.zapier.com/api/v0/sdk/code-substrate-workflows/...
2222
- // sdkapi proxies to the code-substrate-workflows backend.
2223
- "/code-substrate-workflows": {
2224
- authHeader: "Authorization",
2225
- pathPrefix: "/api/v0/sdk/code-substrate-workflows"
2226
- },
2227
- // e.g. /code-substrate-analyzer/validations ->
2228
- // https://api.zapier.com/code-substrate-analyzer/v0/validations
2229
- "/code-substrate-analyzer": {
2230
- authHeader: "Authorization",
2231
- pathPrefix: "/code-substrate-analyzer/v0",
2232
- subdomain: "api"
2233
- },
2234
- // e.g. /forms/v0/forms -> https://api.zapier.com/forms/v0/forms
2235
- // The Forms API is registered on the Public API Gateway and has no sdkapi
2236
- // proxy route, so it goes straight to the gateway. Its governance metadata
2237
- // rewrites /forms/v0/... to the backend's /api/forms/v0/..., which is why no
2238
- // pathPrefix is applied here.
2239
- "/forms": {
2240
- authHeader: "Authorization",
2241
- subdomain: "api"
2242
- }
2243
- };
2244
2720
  var ZapierApiClient = class {
2245
2721
  constructor(options) {
2246
2722
  this.options = options;
@@ -2275,11 +2751,23 @@ var ZapierApiClient = class {
2275
2751
  mergedHeaders.set(key, value);
2276
2752
  });
2277
2753
  this.applyTelemetryHeaders({ headers: mergedHeaders, callId });
2754
+ const {
2755
+ authRequired: _authRequired,
2756
+ requiredScopes: _requiredScopes,
2757
+ searchParams: _searchParams,
2758
+ approvalContext: _approvalContext,
2759
+ customErrorHandler: _customErrorHandler,
2760
+ resource: _resource,
2761
+ ...wireInit
2762
+ } = fetchInit;
2278
2763
  let retries = 0;
2279
2764
  while (true) {
2280
- const response = await this.options.fetch(url, {
2281
- ...fetchInit,
2282
- headers: mergedHeaders
2765
+ const response = await this.options.sendHttpRequest({
2766
+ ...wireInit,
2767
+ // Set the resolved URL and merged headers after the spread so caller
2768
+ // values cannot override them.
2769
+ url,
2770
+ headers: Object.fromEntries(mergedHeaders)
2283
2771
  });
2284
2772
  if (response.status !== 429) {
2285
2773
  return response;
@@ -2773,43 +3261,13 @@ var ZapierApiClient = class {
2773
3261
  }
2774
3262
  // Apply any special routing logic for configured paths.
2775
3263
  applyPathConfiguration(path) {
2776
- const matchingPathEntries = findPathConfigEntries({ path });
2777
- const routingMatch = matchingPathEntries[0];
2778
- const buildResult = (url2) => {
2779
- const resolvedPathEntries = findPathConfigEntries({
2780
- path: url2.pathname,
2781
- matchResolvedGatewayPath: true
2782
- });
2783
- const deprecationPathMatches = [
2784
- ...matchingPathEntries,
2785
- ...resolvedPathEntries
2786
- ];
2787
- const canSendDeprecationMessaging = deprecationPathMatches.length > 0 && deprecationPathMatches.every(
2788
- ({ config }) => config.omitDeprecationMessaging !== true
2789
- );
2790
- return {
2791
- url: url2,
2792
- pathConfig: routingMatch?.config,
2793
- canSendDeprecationMessaging
2794
- };
2795
- };
2796
- let finalPath = path;
2797
- if (routingMatch?.config.pathPrefix) {
2798
- const pathWithoutPrefix = path.slice(routingMatch.configPath.length) || "/";
2799
- finalPath = `${routingMatch.config.pathPrefix}${pathWithoutPrefix}`;
2800
- }
2801
- const zapierBaseUrl = getZapierBaseUrl(this.options.baseUrl);
2802
- if (zapierBaseUrl === this.options.baseUrl.replace(/\/$/, "")) {
2803
- const originalBaseUrl = new URL(this.options.baseUrl);
2804
- const subdomain = routingMatch?.config.subdomain ?? "sdkapi";
2805
- const finalBaseUrl = `https://${subdomain}.${originalBaseUrl.hostname}`;
2806
- const url2 = new URL(finalPath, finalBaseUrl);
2807
- return buildResult(url2);
2808
- }
2809
- const baseUrl = new URL(this.options.baseUrl);
2810
- const basePath = baseUrl.pathname.replace(/\/$/, "");
2811
- const url = new URL(basePath + finalPath, baseUrl.origin);
2812
- return buildResult(url);
3264
+ return resolveRoute({
3265
+ path,
3266
+ baseUrl: this.options.baseUrl,
3267
+ routingOptions: this.options.routingOptions,
3268
+ environment: globalThis.process?.env,
3269
+ debugLog: this.options.debugLog
3270
+ });
2813
3271
  }
2814
3272
  // Helper to build full URLs and return routing info
2815
3273
  buildUrl(path, searchParams) {
@@ -3338,12 +3796,20 @@ var ZapierApiClient = class {
3338
3796
  };
3339
3797
  var createZapierApi = (options) => {
3340
3798
  const { debug = false, fetch: originalFetch = globalThis.fetch } = options;
3799
+ const routingOptions = parseRoutingOptions({ options });
3341
3800
  const debugLog = createDebugLogger(debug);
3342
3801
  const debugFetch = createDebugFetch({ originalFetch, debugLog });
3343
3802
  return new ZapierApiClient({
3344
3803
  ...options,
3345
3804
  debug,
3346
- fetch: debugFetch
3805
+ // The credential-exchange transport. Debug-wrapped here rather than in the
3806
+ // pipeline because `resolveAuthToken` is issued outside it.
3807
+ fetch: debugFetch,
3808
+ // Built from the caller's own `fetch`, not `debugFetch`: the pipeline wraps
3809
+ // for debug itself, and passing the wrapped one would log twice.
3810
+ sendHttpRequest: options.sendHttpRequest ?? createZapierSendHttpRequest({ fetch: options.fetch, debug }),
3811
+ debugLog,
3812
+ routingOptions
3347
3813
  });
3348
3814
  };
3349
3815
 
@@ -3380,9 +3846,11 @@ function getOrCreateApiClient(config) {
3380
3846
  callerPackage
3381
3847
  });
3382
3848
  }
3383
- var SDK_OPTIONS_ID = "zapier/sdkOptions";
3384
- var sdkOptionsPluginRef = declareOptionalProperty({
3385
- id: SDK_OPTIONS_ID
3849
+ var zapierHttpTransportPlugin = definePlugin({
3850
+ namespace: "zapier",
3851
+ name: "httpTransport",
3852
+ imports: [sendHttpRequestPlugin, httpFetchPlugin],
3853
+ exports: [sendHttpRequestPlugin]
3386
3854
  });
3387
3855
 
3388
3856
  // src/plugins/api/index.ts
@@ -3410,7 +3878,9 @@ function withCorrelationId({
3410
3878
  var apiPlugin = defineProperty({
3411
3879
  namespace: "zapier",
3412
3880
  name: "api",
3413
- imports: [sdkOptionsPluginRef],
3881
+ // Import the aggregate so it also provides `kitcore/httpFetch`; importing
3882
+ // `sendHttpRequestPlugin` alone would bypass `sdkOptions.fetch`.
3883
+ imports: [sdkOptionsPluginRef, zapierHttpTransportPlugin],
3414
3884
  setup: ({ imports }) => {
3415
3885
  const {
3416
3886
  fetch: customFetch = globalThis.fetch,
@@ -3428,7 +3898,8 @@ var apiPlugin = defineProperty({
3428
3898
  maxApprovalRetries,
3429
3899
  approvalMode,
3430
3900
  openAutoModeApprovalsInBrowser,
3431
- callerPackage
3901
+ callerPackage,
3902
+ routeOverrides
3432
3903
  } = imports.sdkOptions ?? {};
3433
3904
  return createZapierApi({
3434
3905
  baseUrl,
@@ -3444,7 +3915,11 @@ var apiPlugin = defineProperty({
3444
3915
  maxApprovalRetries,
3445
3916
  approvalMode,
3446
3917
  openAutoModeApprovalsInBrowser,
3447
- callerPackage
3918
+ callerPackage,
3919
+ routeOverrides,
3920
+ // Inject the graph-composed transport so host `dispatchHttpRequest` wraps
3921
+ // apply.
3922
+ sendHttpRequest: imports.sendHttpRequest
3448
3923
  });
3449
3924
  },
3450
3925
  get: ({ state, callContext }) => callContext?.callId ? withCorrelationId({ client: state, callId: callContext.callId }) : state
@@ -11836,6 +12311,7 @@ var BaseSdkOptionsSchema = z.object({
11836
12311
  ),
11837
12312
  debug: z.boolean().optional().describe("Enable debug logging."),
11838
12313
  baseUrl: z.string().optional().describe("Base URL for Zapier API endpoints.").meta({ valueHint: "url" }),
12314
+ routeOverrides: z.record(z.string(), z.string()).optional().describe("Maps SDK route prefixes to direct origins.").meta({ internal: true }),
11839
12315
  trackingBaseUrl: z.string().optional().describe("Base URL for Zapier tracking endpoints.").meta({ valueHint: "url" }),
11840
12316
  /**
11841
12317
  * Maximum number of retries for rate-limited requests (429 responses).