@zapier/zapier-sdk 0.94.1 → 0.96.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.
@@ -1541,6 +1541,11 @@ function declareOptionalProperty(config) {
1541
1541
  // import binding is still typed `TValue | undefined` from the descriptor.
1542
1542
  };
1543
1543
  }
1544
+ function declareDefault({
1545
+ plugin
1546
+ }) {
1547
+ return { ...plugin, defaultSource: plugin };
1548
+ }
1544
1549
  function defineHook(config) {
1545
1550
  const deps = normalizeImports(config.imports);
1546
1551
  return {
@@ -4210,6 +4215,321 @@ function createCorePlugin(options) {
4210
4215
  }
4211
4216
  });
4212
4217
  }
4218
+ function describeUnclaimedConnection(connection) {
4219
+ const delimiterIndex = connection.indexOf(":");
4220
+ if (delimiterIndex === -1) {
4221
+ return 'no auth provider claimed this connection, and it carries no "scheme:" prefix';
4222
+ }
4223
+ return `no auth provider claimed the "${connection.slice(0, delimiterIndex)}" connection scheme`;
4224
+ }
4225
+ var authorizeHttpRequestPlugin = defineMethod({
4226
+ name: "authorizeHttpRequest",
4227
+ namespace: "kitcore",
4228
+ inputSchema: zod.z.custom(),
4229
+ skipInputValidation: true,
4230
+ run: async ({ input }) => {
4231
+ const { connection } = input.request;
4232
+ if (connection != null) {
4233
+ throw createCoreError({
4234
+ code: CoreErrorCode.Unknown,
4235
+ message: `authorizeHttpRequest: ${describeUnclaimedConnection(connection)}, so the request was not sent.`
4236
+ });
4237
+ }
4238
+ return input.request;
4239
+ }
4240
+ });
4241
+ function toFetchInput(request) {
4242
+ const init = { ...request };
4243
+ const { url } = request;
4244
+ delete init.url;
4245
+ delete init.connection;
4246
+ return { url, init };
4247
+ }
4248
+ var dispatchHttpRequestPlugin = defineMethod({
4249
+ name: "dispatchHttpRequest",
4250
+ namespace: "kitcore",
4251
+ inputSchema: zod.z.custom(),
4252
+ skipInputValidation: true,
4253
+ run: async ({ input }) => {
4254
+ const { url, init } = toFetchInput(input.request);
4255
+ return fetch(url, init);
4256
+ }
4257
+ });
4258
+ var prepareHttpRequestPlugin = defineMethod({
4259
+ name: "prepareHttpRequest",
4260
+ namespace: "kitcore",
4261
+ inputSchema: zod.z.custom(),
4262
+ skipInputValidation: true,
4263
+ run: async ({ input }) => input.request
4264
+ });
4265
+ var receiveHttpResponsePlugin = defineMethod({
4266
+ name: "receiveHttpResponse",
4267
+ namespace: "kitcore",
4268
+ inputSchema: zod.z.custom(),
4269
+ skipInputValidation: true,
4270
+ run: async ({ input }) => input.response
4271
+ });
4272
+ var attemptHttpRequestPlugin = defineMethod({
4273
+ name: "attemptHttpRequest",
4274
+ namespace: "kitcore",
4275
+ imports: [
4276
+ declareDefault({ plugin: prepareHttpRequestPlugin }),
4277
+ declareDefault({ plugin: authorizeHttpRequestPlugin }),
4278
+ declareDefault({ plugin: dispatchHttpRequestPlugin }),
4279
+ declareDefault({ plugin: receiveHttpResponsePlugin })
4280
+ ],
4281
+ inputSchema: zod.z.custom(),
4282
+ skipInputValidation: true,
4283
+ run: async ({ input, imports }) => {
4284
+ const { attempt } = input;
4285
+ const preparedRequest = await imports.prepareHttpRequest({
4286
+ request: input.request,
4287
+ attempt
4288
+ });
4289
+ const authorizedRequest = await imports.authorizeHttpRequest({
4290
+ request: preparedRequest,
4291
+ attempt
4292
+ });
4293
+ const response = await imports.dispatchHttpRequest({
4294
+ request: authorizedRequest,
4295
+ attempt
4296
+ });
4297
+ return imports.receiveHttpResponse({
4298
+ request: authorizedRequest,
4299
+ response,
4300
+ attempt
4301
+ });
4302
+ }
4303
+ });
4304
+ function isReplayableBody(body) {
4305
+ if (body == null || typeof body !== "object") return true;
4306
+ return typeof Blob !== "undefined" && body instanceof Blob || // File extends Blob
4307
+ typeof FormData !== "undefined" && body instanceof FormData || typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams || body instanceof ArrayBuffer || ArrayBuffer.isView(body);
4308
+ }
4309
+ function withStringUrl(request) {
4310
+ return typeof request.url === "string" ? request : { ...request, url: String(request.url) };
4311
+ }
4312
+ var initializeHttpRequestPlugin = defineMethod({
4313
+ name: "initializeHttpRequest",
4314
+ namespace: "kitcore",
4315
+ inputSchema: zod.z.custom(),
4316
+ skipInputValidation: true,
4317
+ run: async ({ input }) => {
4318
+ const request = withStringUrl(input.request);
4319
+ return {
4320
+ ...input.operation,
4321
+ request,
4322
+ replayable: isReplayableBody(request.body)
4323
+ };
4324
+ }
4325
+ });
4326
+ var RETRY_HTTP_REQUEST_OPTIONS_ID = "kitcore/retryHttpRequestOptions";
4327
+ var retryHttpRequestOptionsPluginRef = declareOptionalProperty({ id: RETRY_HTTP_REQUEST_OPTIONS_ID });
4328
+ var DEFAULT_MAX_ATTEMPTS = 3;
4329
+ var DEFAULT_MAX_DELAY_MILLISECONDS = 6e4;
4330
+ var DEFAULT_RETRY_STATUSES = [429, 500, 502, 503, 504];
4331
+ var DEFAULT_NON_IDEMPOTENT_RETRY_STATUSES = [429];
4332
+ var DEFAULT_IDEMPOTENT_METHODS = [
4333
+ "GET",
4334
+ "HEAD",
4335
+ "PUT",
4336
+ "DELETE",
4337
+ "OPTIONS",
4338
+ "TRACE"
4339
+ ];
4340
+ var BASE_BACKOFF_MILLISECONDS = 1e3;
4341
+ var JITTER_FACTOR = 0.5;
4342
+ function directedDelayMilliseconds(response) {
4343
+ const retryAfter = response.headers.get("retry-after");
4344
+ if (retryAfter) {
4345
+ const seconds = Number.parseInt(retryAfter, 10);
4346
+ if (!Number.isNaN(seconds)) return Math.max(0, seconds * 1e3);
4347
+ const date = Date.parse(retryAfter);
4348
+ if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
4349
+ }
4350
+ const reset = response.headers.get("x-ratelimit-reset");
4351
+ if (reset) {
4352
+ const resetSeconds = Number.parseInt(reset, 10);
4353
+ if (!Number.isNaN(resetSeconds)) {
4354
+ return Math.max(0, resetSeconds * 1e3 - Date.now());
4355
+ }
4356
+ }
4357
+ return void 0;
4358
+ }
4359
+ function backoffMilliseconds(attemptNumber) {
4360
+ const base = BASE_BACKOFF_MILLISECONDS * 2 ** (attemptNumber - 1);
4361
+ return base + Math.random() * JITTER_FACTOR * base;
4362
+ }
4363
+ function abortReason(signal) {
4364
+ const reason = signal?.reason;
4365
+ return reason ?? new Error("The request was aborted.");
4366
+ }
4367
+ function sleep(milliseconds, signal) {
4368
+ return new Promise((resolve2, reject) => {
4369
+ const timer = setTimeout(finish, milliseconds);
4370
+ function finish() {
4371
+ clearTimeout(timer);
4372
+ signal?.removeEventListener("abort", cancel);
4373
+ resolve2();
4374
+ }
4375
+ function cancel() {
4376
+ clearTimeout(timer);
4377
+ reject(abortReason(signal));
4378
+ }
4379
+ if (signal?.aborted) return cancel();
4380
+ signal?.addEventListener("abort", cancel, { once: true });
4381
+ });
4382
+ }
4383
+ function isIdempotent(request, idempotentMethods) {
4384
+ const method = (request.method ?? "GET").toUpperCase();
4385
+ return idempotentMethods.includes(method);
4386
+ }
4387
+ defineHook({
4388
+ name: "retryHttpRequest",
4389
+ imports: [attemptHttpRequestPlugin, retryHttpRequestOptionsPluginRef],
4390
+ wrap: {
4391
+ attemptHttpRequest: async ({ input, imports, next }) => {
4392
+ const options = imports.retryHttpRequestOptions ?? {};
4393
+ const maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
4394
+ const maxDelayMilliseconds = options.maxDelayMilliseconds ?? DEFAULT_MAX_DELAY_MILLISECONDS;
4395
+ const idempotentMethods = options.idempotentMethods ?? DEFAULT_IDEMPOTENT_METHODS;
4396
+ const statuses = isIdempotent(input.request, idempotentMethods) ? options.retryStatuses ?? DEFAULT_RETRY_STATUSES : options.nonIdempotentRetryStatuses ?? DEFAULT_NON_IDEMPOTENT_RETRY_STATUSES;
4397
+ for (let attemptNumber = 1; ; attemptNumber++) {
4398
+ const attempt = {
4399
+ ...input.attempt,
4400
+ attemptNumber,
4401
+ // Per-attempt scratch: a cross-stage handoff from a previous attempt
4402
+ // describes a request that is no longer in flight. `operation` rides
4403
+ // through unchanged, so the id and the caller's original request are
4404
+ // the same for every attempt.
4405
+ state: {}
4406
+ };
4407
+ let response;
4408
+ try {
4409
+ response = await next({ ...input, attempt });
4410
+ } catch (error) {
4411
+ if (!options.retryOnError || !canRetry(
4412
+ attemptNumber,
4413
+ maxAttempts,
4414
+ input.attempt.operation.replayable
4415
+ )) {
4416
+ throw error;
4417
+ }
4418
+ await sleep(
4419
+ Math.min(backoffMilliseconds(attemptNumber), maxDelayMilliseconds),
4420
+ attempt.signal
4421
+ );
4422
+ continue;
4423
+ }
4424
+ if (!statuses.includes(response.status) || !canRetry(
4425
+ attemptNumber,
4426
+ maxAttempts,
4427
+ input.attempt.operation.replayable
4428
+ )) {
4429
+ return response;
4430
+ }
4431
+ const directed = directedDelayMilliseconds(response);
4432
+ if (directed != null && directed > maxDelayMilliseconds)
4433
+ return response;
4434
+ const delay = Math.min(
4435
+ directed ?? backoffMilliseconds(attemptNumber),
4436
+ maxDelayMilliseconds
4437
+ );
4438
+ await response.body?.cancel().catch(() => {
4439
+ });
4440
+ await sleep(delay, attempt.signal);
4441
+ }
4442
+ }
4443
+ }
4444
+ });
4445
+ function canRetry(attemptNumber, maxAttempts, replayable) {
4446
+ return attemptNumber < maxAttempts && replayable;
4447
+ }
4448
+ function createOperationId() {
4449
+ return globalThis.crypto?.randomUUID?.() ?? `http-${Date.now()}`;
4450
+ }
4451
+ var sendHttpRequestPlugin = defineMethod({
4452
+ name: "sendHttpRequest",
4453
+ namespace: "kitcore",
4454
+ imports: [
4455
+ declareDefault({ plugin: initializeHttpRequestPlugin }),
4456
+ declareDefault({ plugin: attemptHttpRequestPlugin })
4457
+ ],
4458
+ inputSchema: zod.z.custom(),
4459
+ skipInputValidation: true,
4460
+ run: async ({ input, imports }) => {
4461
+ const start2 = {
4462
+ operationId: createOperationId(),
4463
+ signal: input.signal
4464
+ };
4465
+ const operation = await imports.initializeHttpRequest({
4466
+ request: input,
4467
+ operation: start2
4468
+ });
4469
+ return imports.attemptHttpRequest({
4470
+ request: operation.request,
4471
+ attempt: {
4472
+ attemptNumber: 1,
4473
+ operation,
4474
+ signal: operation.signal,
4475
+ state: {}
4476
+ }
4477
+ });
4478
+ }
4479
+ });
4480
+ defineMethod({
4481
+ name: "fetch",
4482
+ namespace: "kitcore",
4483
+ imports: [declareDefault({ plugin: sendHttpRequestPlugin })],
4484
+ positional: ["url", "init"],
4485
+ inputSchema: zod.z.custom(),
4486
+ skipInputValidation: true,
4487
+ run: ({ input, imports }) => {
4488
+ const { url, init } = input;
4489
+ return imports.sendHttpRequest({ url, ...init });
4490
+ }
4491
+ });
4492
+ var defaultConnectionSchemePlugin = defineMethod({
4493
+ name: "defaultConnectionScheme",
4494
+ namespace: "kitcore",
4495
+ inputSchema: zod.z.custom(),
4496
+ skipInputValidation: true,
4497
+ run: () => void 0
4498
+ });
4499
+ defineMethod({
4500
+ name: "normalizeConnection",
4501
+ namespace: "kitcore",
4502
+ imports: [declareDefault({ plugin: defaultConnectionSchemePlugin })],
4503
+ inputSchema: zod.z.custom(),
4504
+ skipInputValidation: true,
4505
+ run: ({ input, imports }) => {
4506
+ const { connection } = input;
4507
+ if (connection == null) {
4508
+ return void 0;
4509
+ }
4510
+ const delimiterIndex = connection.indexOf(":");
4511
+ if (delimiterIndex !== -1) {
4512
+ return {
4513
+ connection,
4514
+ scheme: connection.slice(0, delimiterIndex),
4515
+ value: connection.slice(delimiterIndex + 1)
4516
+ };
4517
+ }
4518
+ const scheme = imports.defaultConnectionScheme({ connection });
4519
+ return {
4520
+ connection,
4521
+ scheme,
4522
+ value: connection
4523
+ };
4524
+ }
4525
+ });
4526
+ defineMethod({
4527
+ name: "resolveConnection",
4528
+ namespace: "kitcore",
4529
+ inputSchema: zod.z.custom(),
4530
+ skipInputValidation: true,
4531
+ run: ({ input }) => input.connection
4532
+ });
4213
4533
 
4214
4534
  // src/constants.ts
4215
4535
  var ZAPIER_BASE_URL = globalThis.process?.env?.ZAPIER_BASE_URL || "https://zapier.com";
@@ -4914,9 +5234,9 @@ function createDebugFetch(options) {
4914
5234
  var MAX_CONSECUTIVE_ERRORS = 3;
4915
5235
  var BASE_ERROR_BACKOFF_MILLISECONDS = 1e3;
4916
5236
  var BASE_EXPONENTIAL_BACKOFF_MILLISECONDS = 1e3;
4917
- var JITTER_FACTOR = 0.5;
5237
+ var JITTER_FACTOR2 = 0.5;
4918
5238
  function calculateErrorBackoffMs(baseInterval, errorCount) {
4919
- const jitter = Math.random() * JITTER_FACTOR * baseInterval;
5239
+ const jitter = Math.random() * JITTER_FACTOR2 * baseInterval;
4920
5240
  const errorBackoff = Math.min(
4921
5241
  BASE_ERROR_BACKOFF_MILLISECONDS * (errorCount / 2),
4922
5242
  baseInterval * 2
@@ -4926,10 +5246,10 @@ function calculateErrorBackoffMs(baseInterval, errorCount) {
4926
5246
  }
4927
5247
  function calculateExponentialBackoffMs(attempt, baseDelayMs = BASE_EXPONENTIAL_BACKOFF_MILLISECONDS) {
4928
5248
  const baseDelay = baseDelayMs * Math.pow(2, attempt - 1);
4929
- const jitter = Math.random() * JITTER_FACTOR * baseDelay;
5249
+ const jitter = Math.random() * JITTER_FACTOR2 * baseDelay;
4930
5250
  return Math.floor(baseDelay + jitter);
4931
5251
  }
4932
- function sleep(ms, signal) {
5252
+ function sleep2(ms, signal) {
4933
5253
  if (!signal) {
4934
5254
  return new Promise((resolve2) => setTimeout(resolve2, ms));
4935
5255
  }
@@ -5141,7 +5461,7 @@ async function pollUntilComplete(options) {
5141
5461
  let attempts = 0;
5142
5462
  let errorCount = 0;
5143
5463
  if (initialDelay > 0) {
5144
- await sleep(initialDelay, signal);
5464
+ await sleep2(initialDelay, signal);
5145
5465
  if (signal?.aborted) throw makeAbortError();
5146
5466
  }
5147
5467
  while (true) {
@@ -5159,7 +5479,7 @@ async function pollUntilComplete(options) {
5159
5479
  const interval = getPollingInterval(elapsedTime);
5160
5480
  const waitTime = calculateErrorBackoffMs(interval, errorCount);
5161
5481
  const cappedWaitTime = maxPollingIntervalMs === void 0 ? waitTime : Math.min(waitTime, maxPollingIntervalMs);
5162
- await sleep(cappedWaitTime, signal);
5482
+ await sleep2(cappedWaitTime, signal);
5163
5483
  if (signal?.aborted) throw makeAbortError();
5164
5484
  }
5165
5485
  attempts++;
@@ -6194,7 +6514,7 @@ function parseDeprecationDate(value) {
6194
6514
  }
6195
6515
 
6196
6516
  // src/sdk-version.ts
6197
- var SDK_VERSION = (typeof process !== "undefined" && process.env ? "0.94.1" : void 0) || "unknown";
6517
+ var SDK_VERSION = (typeof process !== "undefined" && process.env ? "0.96.0" : void 0) || "unknown";
6198
6518
 
6199
6519
  // src/utils/open-url.ts
6200
6520
  var nodePrefix = "node:";
@@ -6303,7 +6623,11 @@ var PollApprovalResponseSchema = zod.z.object({
6303
6623
  status: ApprovalStatusSchema,
6304
6624
  approval_id: zod.z.string().optional(),
6305
6625
  mode: ApprovalModeSchema.optional(),
6306
- reason: zod.z.string().optional()
6626
+ reason: zod.z.string().optional(),
6627
+ // Present when an auto-mode agent escalated the approval to a human reviewer.
6628
+ // The SDK uses review_status to detect escalation and approval_url to open the browser.
6629
+ review_status: zod.z.enum(["in_review", "escalated"]).optional(),
6630
+ approval_url: zod.z.string().optional()
6307
6631
  });
6308
6632
  var APPROVAL_MAX_POLLING_INTERVAL_MILLISECONDS = 5e3;
6309
6633
  function validateSdkPath(path) {
@@ -6424,6 +6748,15 @@ var pathConfig = {
6424
6748
  "/code-substrate-workflows": {
6425
6749
  authHeader: "Authorization",
6426
6750
  pathPrefix: "/api/v0/sdk/code-substrate-workflows"
6751
+ },
6752
+ // e.g. /forms/v0/forms -> https://api.zapier.com/forms/v0/forms
6753
+ // The Forms API is registered on the Public API Gateway and has no sdkapi
6754
+ // proxy route, so it goes straight to the gateway. Its governance metadata
6755
+ // rewrites /forms/v0/... to the backend's /api/forms/v0/..., which is why no
6756
+ // pathPrefix is applied here.
6757
+ "/forms": {
6758
+ authHeader: "Authorization",
6759
+ subdomain: "api"
6427
6760
  }
6428
6761
  };
6429
6762
  var ZapierApiClient = class {
@@ -6486,7 +6819,7 @@ var ZapierApiClient = class {
6486
6819
  method: init?.method ?? "GET",
6487
6820
  rateLimit: rateLimitInfo
6488
6821
  });
6489
- await sleep(delayMs, init?.signal ?? void 0);
6822
+ await sleep2(delayMs, init?.signal ?? void 0);
6490
6823
  }
6491
6824
  };
6492
6825
  /**
@@ -6964,7 +7297,8 @@ var ZapierApiClient = class {
6964
7297
  const zapierBaseUrl = getZapierBaseUrl(this.options.baseUrl);
6965
7298
  if (zapierBaseUrl === this.options.baseUrl.replace(/\/$/, "")) {
6966
7299
  const originalBaseUrl = new URL(this.options.baseUrl);
6967
- const finalBaseUrl = `https://sdkapi.${originalBaseUrl.hostname}`;
7300
+ const subdomain = routingMatch?.config.subdomain ?? "sdkapi";
7301
+ const finalBaseUrl = `https://${subdomain}.${originalBaseUrl.hostname}`;
6968
7302
  const url2 = new URL(finalPath, finalBaseUrl);
6969
7303
  return buildResult(url2);
6970
7304
  }
@@ -7313,6 +7647,7 @@ var ZapierApiClient = class {
7313
7647
  await openApproval(approval.approval_url);
7314
7648
  }
7315
7649
  const timeoutMs = this.options.approvalTimeoutMilliseconds ?? this.options.approvalTimeoutMs ?? DEFAULT_APPROVAL_TIMEOUT_MILLISECONDS;
7650
+ const approvalDeadline = Date.now() + timeoutMs;
7316
7651
  let streamAbortController;
7317
7652
  let streamPromise;
7318
7653
  let removeStreamAbortListener;
@@ -7334,75 +7669,87 @@ var ZapierApiClient = class {
7334
7669
  emitEvent: (type, payload) => this.emitEvent(type, payload)
7335
7670
  });
7336
7671
  }
7337
- let rawPollResult;
7338
- try {
7339
- rawPollResult = await pollUntilComplete({
7340
- // poll_url is an absolute URL supplied by the server, so we use
7341
- // rawFetchUrl directly (skipping path resolution) but still share
7342
- // auth + interactive-header + 429-retry with the rest of the SDK.
7343
- // Each individual poll request goes through the concurrency
7344
- // semaphore — but we deliberately do not hold a slot across the
7345
- // sleep between polls or across the human-approval wait.
7346
- fetchPoll: () => this.withSemaphore(
7347
- { url: approval.poll_url, method: "GET", signal },
7348
- () => this.rawFetchUrl(approval.poll_url, {
7349
- method: "GET",
7350
- headers: { Accept: "application/json" },
7351
- signal
7352
- })
7353
- ),
7354
- timeoutMs,
7355
- maxPollingIntervalMs: APPROVAL_MAX_POLLING_INTERVAL_MILLISECONDS,
7356
- signal,
7357
- isPending: (body2) => {
7358
- const parsed = PollApprovalResponseSchema.safeParse(body2);
7359
- return parsed.success && parsed.data.status === "pending_approval";
7360
- }
7361
- });
7362
- } catch (err) {
7363
- if (!isZapierTimeoutError(err)) {
7364
- this.emitEvent("approval:error", {
7365
- approvalId: approval.id,
7366
- message: err instanceof Error ? err.message : String(err)
7672
+ const fetchApprovalPoll = () => this.withSemaphore(
7673
+ { url: approval.poll_url, method: "GET", signal },
7674
+ () => this.rawFetchUrl(approval.poll_url, {
7675
+ method: "GET",
7676
+ headers: { Accept: "application/json" },
7677
+ signal
7678
+ })
7679
+ );
7680
+ const pollApprovalUntilComplete = async (isPending, deadlineMs = approvalDeadline) => {
7681
+ try {
7682
+ return await pollUntilComplete({
7683
+ fetchPoll: fetchApprovalPoll,
7684
+ timeoutMs: Math.max(1, deadlineMs - Date.now()),
7685
+ maxPollingIntervalMs: APPROVAL_MAX_POLLING_INTERVAL_MILLISECONDS,
7686
+ signal,
7687
+ isPending
7367
7688
  });
7368
- throw err;
7689
+ } catch (err) {
7690
+ if (!isZapierTimeoutError(err)) {
7691
+ this.emitEvent("approval:error", {
7692
+ approvalId: approval.id,
7693
+ message: err instanceof Error ? err.message : String(err)
7694
+ });
7695
+ throw err;
7696
+ }
7697
+ this.emitEvent("approval:timeout", { approvalId: approval.id });
7698
+ throw new ZapierApprovalError(
7699
+ `Approval timed out after ${timeoutMs / 1e3} seconds`,
7700
+ {
7701
+ approvalId: approval.id,
7702
+ approvalUrl: approval.approval_url,
7703
+ pollUrl: approval.poll_url,
7704
+ streamUrl: approval.stream_url,
7705
+ status: "timeout",
7706
+ cause: err
7707
+ }
7708
+ );
7369
7709
  }
7370
- this.emitEvent("approval:timeout", {
7371
- approvalId: approval.id
7710
+ };
7711
+ let rawPollResult;
7712
+ try {
7713
+ rawPollResult = await pollApprovalUntilComplete((body2) => {
7714
+ const parsed = PollApprovalResponseSchema.safeParse(body2);
7715
+ if (!parsed.success) return false;
7716
+ if (parsed.data.review_status === "escalated") return false;
7717
+ return parsed.data.status === "pending_approval";
7372
7718
  });
7373
- throw new ZapierApprovalError(
7374
- `Approval timed out after ${timeoutMs / 1e3} seconds`,
7375
- {
7376
- approvalId: approval.id,
7377
- approvalUrl: approval.approval_url,
7378
- pollUrl: approval.poll_url,
7379
- streamUrl: approval.stream_url,
7380
- status: "timeout",
7381
- cause: err
7382
- }
7383
- );
7384
7719
  } finally {
7385
7720
  removeStreamAbortListener?.();
7386
7721
  streamAbortController?.abort();
7387
7722
  await streamPromise;
7388
7723
  }
7389
- const pollParse = PollApprovalResponseSchema.safeParse(rawPollResult);
7390
- if (!pollParse.success) {
7391
- const bodyPreview = typeof rawPollResult === "string" ? rawPollResult : JSON.stringify(rawPollResult);
7392
- this.emitEvent("approval:error", {
7724
+ const parsePollResult = (raw) => {
7725
+ const parsed = PollApprovalResponseSchema.safeParse(raw);
7726
+ if (!parsed.success) {
7727
+ const bodyPreview = typeof raw === "string" ? raw : JSON.stringify(raw);
7728
+ this.emitEvent("approval:error", {
7729
+ approvalId: approval.id,
7730
+ message: `Failed to parse approval poll response: ${bodyPreview}`
7731
+ });
7732
+ throw new ZapierApiError(
7733
+ `Failed to parse approval poll response: ${bodyPreview}`,
7734
+ { statusCode: 0, cause: parsed.error, response: raw }
7735
+ );
7736
+ }
7737
+ return parsed.data;
7738
+ };
7739
+ const pollResult = parsePollResult(rawPollResult);
7740
+ if (pollResult.review_status === "escalated") {
7741
+ const escalationUrl = pollResult.approval_url ?? approval.approval_url;
7742
+ this.emitEvent("approval:escalated", {
7393
7743
  approvalId: approval.id,
7394
- message: `Failed to parse approval poll response: ${bodyPreview}`
7744
+ approvalUrl: escalationUrl
7395
7745
  });
7396
- throw new ZapierApiError(
7397
- `Failed to parse approval poll response: ${bodyPreview}`,
7398
- {
7399
- statusCode: 0,
7400
- cause: pollParse.error,
7401
- response: rawPollResult
7402
- }
7403
- );
7746
+ await openApproval(escalationUrl);
7747
+ const humanRaw = await pollApprovalUntilComplete((body2) => {
7748
+ const parsed = PollApprovalResponseSchema.safeParse(body2);
7749
+ return parsed.success && parsed.data.status === "pending_approval";
7750
+ });
7751
+ Object.assign(pollResult, parsePollResult(humanRaw));
7404
7752
  }
7405
- const pollResult = pollParse.data;
7406
7753
  if (pollResult.status === "denied") {
7407
7754
  this.emitEvent("approval:denied", {
7408
7755
  approvalId: approval.id,
@@ -8356,7 +8703,7 @@ function buildAbortHandle({
8356
8703
  return combineAbortSignals({ handles });
8357
8704
  }
8358
8705
  var FetchInitSdkValidationSchema = zod.z.looseObject(FetchInitZapierFieldsSchema.shape).optional();
8359
- var fetchPlugin = defineMethod({
8706
+ var fetchPlugin2 = defineMethod({
8360
8707
  name: "fetch",
8361
8708
  imports: [coreOptionsPluginRef, connectionsPluginRef, apiPluginRef],
8362
8709
  output: "raw",
@@ -10688,7 +11035,7 @@ function createAppsProxy(imports) {
10688
11035
  }
10689
11036
  var appsPlugin = defineProperty({
10690
11037
  name: "apps",
10691
- imports: [fetchPlugin, runActionPlugin],
11038
+ imports: [fetchPlugin2, runActionPlugin],
10692
11039
  // Build the proxy once from the imported methods (stable callables).
10693
11040
  setup: ({ imports }) => (
10694
11041
  // Cast: ZapierSdkApps is augmented by user-generated .d.ts files to give
@@ -12042,7 +12389,7 @@ var RelayFetchSchema = RelayRequestSchema;
12042
12389
  // src/plugins/request/index.ts
12043
12390
  var requestPlugin = defineMethod({
12044
12391
  name: "request",
12045
- imports: [fetchPlugin, coreOptionsPluginRef],
12392
+ imports: [fetchPlugin2, coreOptionsPluginRef],
12046
12393
  output: "raw",
12047
12394
  inputSchema: RelayRequestSchema,
12048
12395
  skipInputValidation: true,
@@ -13867,7 +14214,7 @@ async function drainRunner({
13867
14214
  `[zapier-sdk] Retrying drain for inbox ${inboxId} (attempt ${errorAttempts}, retry in ${delay}ms)${httpPart}: ${errorMessage(error)}`
13868
14215
  );
13869
14216
  }
13870
- await sleep(delay, signal);
14217
+ await sleep2(delay, signal);
13871
14218
  if (signal.aborted) return { kind: "aborted" };
13872
14219
  drainRequest.request();
13873
14220
  continue;
@@ -13934,7 +14281,7 @@ async function sseLoop({
13934
14281
  }
13935
14282
  attempt = 0;
13936
14283
  if (signal.aborted) return;
13937
- await sleep(safetyDrainMs, signal);
14284
+ await sleep2(safetyDrainMs, signal);
13938
14285
  continue;
13939
14286
  }
13940
14287
  if (connected) attempt = 0;
@@ -13954,7 +14301,7 @@ async function sseLoop({
13954
14301
  `[zapier-sdk] Reconnecting real-time wake-ups for inbox ${inboxId} (attempt ${attempt}, retry in ${delay}ms)${httpPart}: ${errorMsg}`
13955
14302
  );
13956
14303
  }
13957
- await sleep(delay, signal);
14304
+ await sleep2(delay, signal);
13958
14305
  }
13959
14306
  }
13960
14307
  async function safetyTimerLoop({
@@ -13963,7 +14310,7 @@ async function safetyTimerLoop({
13963
14310
  signal
13964
14311
  }) {
13965
14312
  while (!signal.aborted) {
13966
- await sleep(safetyDrainMs, signal);
14313
+ await sleep2(safetyDrainMs, signal);
13967
14314
  if (signal.aborted) return;
13968
14315
  drainRequest.request();
13969
14316
  }
@@ -15845,7 +16192,7 @@ var zapierSdkPlugin = definePlugin({
15845
16192
  createActionRunPlugin,
15846
16193
  getActionRunPlugin,
15847
16194
  runActionPlugin,
15848
- fetchPlugin,
16195
+ fetchPlugin2,
15849
16196
  requestPlugin,
15850
16197
  appsPlugin,
15851
16198
  // Triggers. `list*` plugins are exported before their `get*` / mutation
@@ -15929,7 +16276,7 @@ async function batch(tasks, options = {}) {
15929
16276
  try {
15930
16277
  let result;
15931
16278
  if (taskTimeoutMs !== void 0) {
15932
- const timeoutPromise = sleep(taskTimeoutMs).then(() => {
16279
+ const timeoutPromise = sleep2(taskTimeoutMs).then(() => {
15933
16280
  throw new ZapierTimeoutError(
15934
16281
  `Task timed out after ${taskTimeoutMs}ms`
15935
16282
  );
@@ -15944,7 +16291,7 @@ async function batch(tasks, options = {}) {
15944
16291
  const isTimeout = isZapierTimeoutError(error);
15945
16292
  if (retry && !isTimeout && newErrorCount < MAX_CONSECUTIVE_ERRORS) {
15946
16293
  const waitTime = calculateErrorBackoffMs(1e3, newErrorCount);
15947
- await sleep(waitTime);
16294
+ await sleep2(waitTime);
15948
16295
  taskQueue.push({
15949
16296
  index,
15950
16297
  task,
@@ -15967,7 +16314,7 @@ async function batch(tasks, options = {}) {
15967
16314
  if (!taskState) break;
15968
16315
  await executeTask(taskState);
15969
16316
  if (taskQueue.length > 0 && batchDelay > 0) {
15970
- await sleep(batchDelay);
16317
+ await sleep2(batchDelay);
15971
16318
  }
15972
16319
  }
15973
16320
  }
@@ -15976,7 +16323,7 @@ async function batch(tasks, options = {}) {
15976
16323
  for (let i = 0; i < workerCount; i++) {
15977
16324
  workers.push(worker());
15978
16325
  if (i < workerCount - 1 && batchDelay > 0) {
15979
- await sleep(batchDelay / 10);
16326
+ await sleep2(batchDelay / 10);
15980
16327
  }
15981
16328
  }
15982
16329
  await Promise.all(workers);
@@ -16211,7 +16558,7 @@ exports.eventEmissionHookPlugin = eventEmissionHookPlugin;
16211
16558
  exports.eventEmissionPlugin = eventEmissionPlugin;
16212
16559
  exports.eventEmissionPluginRef = eventEmissionPluginRef;
16213
16560
  exports.extractErrorDetail = extractErrorDetail;
16214
- exports.fetchPlugin = fetchPlugin;
16561
+ exports.fetchPlugin = fetchPlugin2;
16215
16562
  exports.findFirstConnectionPlugin = findFirstConnectionPlugin;
16216
16563
  exports.findManifestEntry = findManifestEntry;
16217
16564
  exports.findUniqueConnectionPlugin = findUniqueConnectionPlugin;