@getanyapi/sdk 0.9.7 → 0.9.9

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/dist/index.d.cts CHANGED
@@ -8,8 +8,11 @@ interface RunResult<T> {
8
8
  provider: "AnyAPI";
9
9
  /** Amount charged in USD for this call. */
10
10
  costUsd: number;
11
- /** Number of result rows returned (present on per-result SKUs). */
12
- items?: number;
11
+ /**
12
+ * Number of result rows returned. Always present on the wire: the gateway's field
13
+ * carries no `omitempty`, and a metadata-only replay preserves the original count.
14
+ */
15
+ items: number;
13
16
  /**
14
17
  * True when the gateway served a stored response for a repeated Idempotency-Key instead
15
18
  * of running the SKU again. A replay is not billed twice. Always present on the wire.
@@ -55,8 +58,11 @@ interface BareRunResult<T> {
55
58
  provider: "AnyAPI";
56
59
  /** Amount charged in USD for this call. */
57
60
  costUsd: number;
58
- /** Number of result rows returned (present on per-result SKUs). */
59
- items?: number;
61
+ /**
62
+ * Number of result rows returned. Always present on the wire: the gateway's field
63
+ * carries no `omitempty`, and a metadata-only replay preserves the original count.
64
+ */
65
+ items: number;
60
66
  /**
61
67
  * True when the gateway served a stored response for a repeated Idempotency-Key instead
62
68
  * of running the SKU again. A replay is not billed twice. Always present on the wire.
@@ -108,6 +114,8 @@ interface RequestOptions {
108
114
  signal?: AbortSignal;
109
115
  /** Override the client maxRetries for this call. */
110
116
  maxRetries?: number;
117
+ /** Override the client maxInProgressWaitMs for this call. */
118
+ maxInProgressWaitMs?: number;
111
119
  /** Override the generated Idempotency-Key for this billed POST. */
112
120
  idempotencyKey?: string;
113
121
  }
@@ -127,6 +135,13 @@ interface ClientOptions {
127
135
  timeoutMs?: number;
128
136
  /** Send Idempotency-Key on billed POSTs. Default "auto"; use "off" as a kill switch. */
129
137
  idempotency?: "auto" | "off";
138
+ /**
139
+ * Total milliseconds one run() call may block waiting out a 409 `idempotency_in_progress`
140
+ * (the gateway is still executing the run this key claims). Default 60000. Set 0 to
141
+ * surface that 409 immediately. This is a whole-call budget across every in-progress
142
+ * wait, separate from the 8000ms ordinary-backoff ceiling.
143
+ */
144
+ maxInProgressWaitMs?: number;
130
145
  }
131
146
  /**
132
147
  * Account profile returned by /v1/me (internal-only fields dropped). See SPEC 2.7.
@@ -162,7 +177,7 @@ interface DiscoveryPricing {
162
177
  failoverMaxUsd: number;
163
178
  }
164
179
  interface LaneHealth {
165
- window: "30d";
180
+ window: string;
166
181
  uptimePct: number;
167
182
  latencyP50Ms: number;
168
183
  requests: number;
@@ -183,6 +198,8 @@ interface CatalogEntry {
183
198
  lanes: DiscoveryLane[];
184
199
  heavy: boolean;
185
200
  tryEligible: boolean;
201
+ failover?: boolean;
202
+ excludesCallerDelay?: boolean;
186
203
  inputSchema?: Record<string, unknown>;
187
204
  outputSchema?: Record<string, unknown>;
188
205
  }
@@ -257,6 +274,7 @@ declare class AnyAPI$1 implements ClientCore {
257
274
  private readonly maxRetries;
258
275
  private readonly timeoutMs;
259
276
  private readonly idempotency;
277
+ private readonly maxInProgressWaitMs;
260
278
  /**
261
279
  * The network seam the generated per-platform namespaces target. The base client IS a
262
280
  * ClientCore (it implements `run`), so the generated subclass hands `this._core` to each
@@ -292,6 +310,7 @@ declare class AnyAPI$1 implements ClientCore {
292
310
  body?: string;
293
311
  timeoutMs: number;
294
312
  maxRetries: number;
313
+ maxInProgressWaitMs?: number;
295
314
  signal?: AbortSignal;
296
315
  idempotencyKey?: string;
297
316
  }): Promise<T>;
@@ -337,7 +356,10 @@ declare function paginate<Item, Page extends RunResult<unknown> | BareRunResult<
337
356
  declare class AnyAPIError extends Error {
338
357
  /** HTTP status code, or 0 for transport-level failures (connection/timeout). */
339
358
  readonly status: number;
340
- /** The x-request-id response header when present, else undefined. */
359
+ /**
360
+ * The gateway's X-Anyapi-Request-Id response header when present (falling back to a
361
+ * proxy-set x-request-id), else undefined. Quote it to AnyAPI support.
362
+ */
341
363
  readonly requestId?: string;
342
364
  /** Stable gateway error code when the JSON body includes one, else undefined. */
343
365
  readonly code?: string;
package/dist/index.d.ts CHANGED
@@ -8,8 +8,11 @@ interface RunResult<T> {
8
8
  provider: "AnyAPI";
9
9
  /** Amount charged in USD for this call. */
10
10
  costUsd: number;
11
- /** Number of result rows returned (present on per-result SKUs). */
12
- items?: number;
11
+ /**
12
+ * Number of result rows returned. Always present on the wire: the gateway's field
13
+ * carries no `omitempty`, and a metadata-only replay preserves the original count.
14
+ */
15
+ items: number;
13
16
  /**
14
17
  * True when the gateway served a stored response for a repeated Idempotency-Key instead
15
18
  * of running the SKU again. A replay is not billed twice. Always present on the wire.
@@ -55,8 +58,11 @@ interface BareRunResult<T> {
55
58
  provider: "AnyAPI";
56
59
  /** Amount charged in USD for this call. */
57
60
  costUsd: number;
58
- /** Number of result rows returned (present on per-result SKUs). */
59
- items?: number;
61
+ /**
62
+ * Number of result rows returned. Always present on the wire: the gateway's field
63
+ * carries no `omitempty`, and a metadata-only replay preserves the original count.
64
+ */
65
+ items: number;
60
66
  /**
61
67
  * True when the gateway served a stored response for a repeated Idempotency-Key instead
62
68
  * of running the SKU again. A replay is not billed twice. Always present on the wire.
@@ -108,6 +114,8 @@ interface RequestOptions {
108
114
  signal?: AbortSignal;
109
115
  /** Override the client maxRetries for this call. */
110
116
  maxRetries?: number;
117
+ /** Override the client maxInProgressWaitMs for this call. */
118
+ maxInProgressWaitMs?: number;
111
119
  /** Override the generated Idempotency-Key for this billed POST. */
112
120
  idempotencyKey?: string;
113
121
  }
@@ -127,6 +135,13 @@ interface ClientOptions {
127
135
  timeoutMs?: number;
128
136
  /** Send Idempotency-Key on billed POSTs. Default "auto"; use "off" as a kill switch. */
129
137
  idempotency?: "auto" | "off";
138
+ /**
139
+ * Total milliseconds one run() call may block waiting out a 409 `idempotency_in_progress`
140
+ * (the gateway is still executing the run this key claims). Default 60000. Set 0 to
141
+ * surface that 409 immediately. This is a whole-call budget across every in-progress
142
+ * wait, separate from the 8000ms ordinary-backoff ceiling.
143
+ */
144
+ maxInProgressWaitMs?: number;
130
145
  }
131
146
  /**
132
147
  * Account profile returned by /v1/me (internal-only fields dropped). See SPEC 2.7.
@@ -162,7 +177,7 @@ interface DiscoveryPricing {
162
177
  failoverMaxUsd: number;
163
178
  }
164
179
  interface LaneHealth {
165
- window: "30d";
180
+ window: string;
166
181
  uptimePct: number;
167
182
  latencyP50Ms: number;
168
183
  requests: number;
@@ -183,6 +198,8 @@ interface CatalogEntry {
183
198
  lanes: DiscoveryLane[];
184
199
  heavy: boolean;
185
200
  tryEligible: boolean;
201
+ failover?: boolean;
202
+ excludesCallerDelay?: boolean;
186
203
  inputSchema?: Record<string, unknown>;
187
204
  outputSchema?: Record<string, unknown>;
188
205
  }
@@ -257,6 +274,7 @@ declare class AnyAPI$1 implements ClientCore {
257
274
  private readonly maxRetries;
258
275
  private readonly timeoutMs;
259
276
  private readonly idempotency;
277
+ private readonly maxInProgressWaitMs;
260
278
  /**
261
279
  * The network seam the generated per-platform namespaces target. The base client IS a
262
280
  * ClientCore (it implements `run`), so the generated subclass hands `this._core` to each
@@ -292,6 +310,7 @@ declare class AnyAPI$1 implements ClientCore {
292
310
  body?: string;
293
311
  timeoutMs: number;
294
312
  maxRetries: number;
313
+ maxInProgressWaitMs?: number;
295
314
  signal?: AbortSignal;
296
315
  idempotencyKey?: string;
297
316
  }): Promise<T>;
@@ -337,7 +356,10 @@ declare function paginate<Item, Page extends RunResult<unknown> | BareRunResult<
337
356
  declare class AnyAPIError extends Error {
338
357
  /** HTTP status code, or 0 for transport-level failures (connection/timeout). */
339
358
  readonly status: number;
340
- /** The x-request-id response header when present, else undefined. */
359
+ /**
360
+ * The gateway's X-Anyapi-Request-Id response header when present (falling back to a
361
+ * proxy-set x-request-id), else undefined. Quote it to AnyAPI support.
362
+ */
341
363
  readonly requestId?: string;
342
364
  /** Stable gateway error code when the JSON body includes one, else undefined. */
343
365
  readonly code?: string;
package/dist/index.js CHANGED
@@ -2,7 +2,10 @@
2
2
  var AnyAPIError = class extends Error {
3
3
  /** HTTP status code, or 0 for transport-level failures (connection/timeout). */
4
4
  status;
5
- /** The x-request-id response header when present, else undefined. */
5
+ /**
6
+ * The gateway's X-Anyapi-Request-Id response header when present (falling back to a
7
+ * proxy-set x-request-id), else undefined. Quote it to AnyAPI support.
8
+ */
6
9
  requestId;
7
10
  /** Stable gateway error code when the JSON body includes one, else undefined. */
8
11
  code;
@@ -37,6 +40,14 @@ var ConnectionError = class extends AnyAPIError {
37
40
  };
38
41
  var TimeoutError = class extends AnyAPIError {
39
42
  };
43
+ var REQUEST_ID_HEADERS = ["x-anyapi-request-id", "x-request-id"];
44
+ function requestIdOf(headers) {
45
+ for (const name of REQUEST_ID_HEADERS) {
46
+ const value = headers.get(name);
47
+ if (value) return value;
48
+ }
49
+ return void 0;
50
+ }
40
51
  function errorFromStatus(status, message, requestId, code) {
41
52
  switch (status) {
42
53
  case 400:
@@ -108,17 +119,20 @@ var DEFAULT_BASE_URL = "https://api.getanyapi.com";
108
119
  function malformed(path) {
109
120
  throw new AnyAPIError(`malformed discovery response: ${path}`, 0);
110
121
  }
111
- function rejectInternalKeys(value, path) {
122
+ function rejectUnsafeDiscoveryFields(value, path) {
112
123
  if (Array.isArray(value)) {
113
124
  value.forEach(
114
- (item, index) => rejectInternalKeys(item, `${path}[${index}]`)
125
+ (item, index) => rejectUnsafeDiscoveryFields(item, `${path}[${index}]`)
115
126
  );
116
127
  return;
117
128
  }
118
129
  if (typeof value !== "object" || value === null) return;
119
130
  for (const [key, item] of Object.entries(value)) {
120
131
  if (key.toLowerCase().includes("credit")) malformed(`${path}.${key}`);
121
- rejectInternalKeys(item, `${path}.${key}`);
132
+ if (key === "provider" && item !== "AnyAPI") {
133
+ malformed(`${path}.${key}`);
134
+ }
135
+ rejectUnsafeDiscoveryFields(item, `${path}.${key}`);
122
136
  }
123
137
  }
124
138
  function record(value, path) {
@@ -127,12 +141,6 @@ function record(value, path) {
127
141
  }
128
142
  return value;
129
143
  }
130
- function exactKeys(raw, allowed, path) {
131
- const keys = new Set(allowed);
132
- for (const key of Object.keys(raw)) {
133
- if (!keys.has(key)) malformed(`${path}.${key}`);
134
- }
135
- }
136
144
  function stringField(raw, key, path) {
137
145
  const value = raw[key];
138
146
  if (typeof value !== "string") return malformed(`${path}.${key}`);
@@ -163,14 +171,12 @@ function parseOffer(value, path) {
163
171
  const unit = stringField(raw, "unit", path);
164
172
  const maxUsd = numberField(raw, "maxUsd", path);
165
173
  if (model === "flat") {
166
- exactKeys(raw, ["model", "unit", "maxUsd"], path);
167
- if (unit !== "request" || "baseUsd" in raw || "perUnitUsd" in raw) {
174
+ if (unit !== "request") {
168
175
  return malformed(path);
169
176
  }
170
177
  return { model, unit, maxUsd };
171
178
  }
172
179
  if (model === "linear") {
173
- exactKeys(raw, ["model", "unit", "baseUsd", "perUnitUsd", "maxUsd"], path);
174
180
  if (unit.length === 0) return malformed(`${path}.unit`);
175
181
  return {
176
182
  model,
@@ -184,7 +190,6 @@ function parseOffer(value, path) {
184
190
  }
185
191
  function parsePricing(value, path) {
186
192
  const raw = record(value, path);
187
- exactKeys(raw, ["from", "failoverMaxUsd"], path);
188
193
  return {
189
194
  from: parseOffer(raw["from"], `${path}.from`),
190
195
  failoverMaxUsd: numberField(raw, "failoverMaxUsd", path)
@@ -192,10 +197,8 @@ function parsePricing(value, path) {
192
197
  }
193
198
  function parseHealth(value, path) {
194
199
  const raw = record(value, path);
195
- exactKeys(raw, ["window", "uptimePct", "latencyP50Ms", "requests"], path);
196
- if (raw["window"] !== "30d") return malformed(`${path}.window`);
197
200
  return {
198
- window: "30d",
201
+ window: stringField(raw, "window", path),
199
202
  uptimePct: boundedNumberField(raw, "uptimePct", path, void 0, 100),
200
203
  latencyP50Ms: integerField(raw, "latencyP50Ms", path),
201
204
  requests: integerField(raw, "requests", path)
@@ -203,7 +206,6 @@ function parseHealth(value, path) {
203
206
  }
204
207
  function parseLane(value, path) {
205
208
  const raw = record(value, path);
206
- exactKeys(raw, ["pricing", "health"], path);
207
209
  const lane = {
208
210
  pricing: parseOffer(raw["pricing"], `${path}.pricing`)
209
211
  };
@@ -221,7 +223,6 @@ function parseSchema(value, path) {
221
223
  }
222
224
  function parseHighlight(value, path) {
223
225
  const raw = record(value, path);
224
- exactKeys(raw, ["path", "type", "why"], path);
225
226
  const field = {
226
227
  path: stringField(raw, "path", path),
227
228
  type: stringField(raw, "type", path)
@@ -242,28 +243,10 @@ function mapProfile(raw) {
242
243
  return profile;
243
244
  }
244
245
  function mapCatalogEntry(raw) {
245
- rejectInternalKeys(raw, "api");
246
+ rejectUnsafeDiscoveryFields(raw, "api");
246
247
  const value = record(raw, "api");
247
- exactKeys(
248
- value,
249
- [
250
- "id",
251
- "slug",
252
- "category",
253
- "name",
254
- "description",
255
- "provider",
256
- "pricing",
257
- "lanes",
258
- "heavy",
259
- "tryEligible",
260
- "inputSchema",
261
- "outputSchema"
262
- ],
263
- "api"
264
- );
265
248
  const lanesRaw = value["lanes"];
266
- if (!Array.isArray(lanesRaw) || lanesRaw.length === 0) {
249
+ if (!Array.isArray(lanesRaw)) {
267
250
  return malformed("api.lanes");
268
251
  }
269
252
  const entry = {
@@ -285,32 +268,24 @@ function mapCatalogEntry(raw) {
285
268
  }
286
269
  if (typeof value["tryEligible"] !== "boolean")
287
270
  return malformed("api.tryEligible");
271
+ if (value["failover"] !== void 0) {
272
+ if (typeof value["failover"] !== "boolean")
273
+ return malformed("api.failover");
274
+ entry.failover = value["failover"];
275
+ }
276
+ if (value["excludesCallerDelay"] !== void 0) {
277
+ if (typeof value["excludesCallerDelay"] !== "boolean")
278
+ return malformed("api.excludesCallerDelay");
279
+ entry.excludesCallerDelay = value["excludesCallerDelay"];
280
+ }
288
281
  if (value["inputSchema"] !== void 0) {
289
282
  entry.inputSchema = parseSchema(value["inputSchema"], "api.inputSchema");
290
283
  }
291
284
  if (value["outputSchema"] !== void 0) {
292
285
  entry.outputSchema = parseSchema(value["outputSchema"], "api.outputSchema");
293
286
  }
294
- if (!offersEqual(entry.pricing.from, entry.lanes[0].pricing)) {
295
- return malformed("api.pricing.from");
296
- }
297
- const failoverMaxUsd = Math.max(
298
- ...entry.lanes.map((lane) => lane.pricing.maxUsd)
299
- );
300
- if (entry.pricing.failoverMaxUsd !== failoverMaxUsd) {
301
- return malformed("api.pricing.failoverMaxUsd");
302
- }
303
287
  return entry;
304
288
  }
305
- function offersEqual(left, right) {
306
- if (left.model !== right.model || left.unit !== right.unit || left.maxUsd !== right.maxUsd) {
307
- return false;
308
- }
309
- if (left.model === "flat" || right.model === "flat") {
310
- return left.model === right.model;
311
- }
312
- return left.baseUsd === right.baseUsd && left.perUnitUsd === right.perUnitUsd;
313
- }
314
289
  function mapCatalogDetail(raw) {
315
290
  const entry = mapCatalogEntry(raw);
316
291
  if (entry.inputSchema === void 0) return malformed("api.inputSchema");
@@ -318,28 +293,13 @@ function mapCatalogDetail(raw) {
318
293
  return entry;
319
294
  }
320
295
  function mapCatalogList(raw) {
296
+ rejectUnsafeDiscoveryFields(raw, "catalog");
321
297
  const envelope = record(raw, "catalog");
322
- exactKeys(envelope, ["apis"], "catalog");
323
298
  if (!Array.isArray(envelope["apis"])) return malformed("catalog.apis");
324
299
  return envelope["apis"].map(mapCatalogEntry);
325
300
  }
326
301
  function mapSearchResult(value, path) {
327
302
  const raw = record(value, path);
328
- exactKeys(
329
- raw,
330
- [
331
- "slug",
332
- "platformId",
333
- "name",
334
- "description",
335
- "category",
336
- "provider",
337
- "pricing",
338
- "relevance",
339
- "highlightFields"
340
- ],
341
- path
342
- );
343
303
  const result = {
344
304
  slug: stringField(raw, "slug", path),
345
305
  platformId: stringField(raw, "platformId", path),
@@ -360,9 +320,8 @@ function mapSearchResult(value, path) {
360
320
  return result;
361
321
  }
362
322
  function mapCatalogSearch(raw) {
363
- rejectInternalKeys(raw, "search");
323
+ rejectUnsafeDiscoveryFields(raw, "search");
364
324
  const envelope = record(raw, "search");
365
- exactKeys(envelope, ["results", "total", "ranking"], "search");
366
325
  if (!Array.isArray(envelope["results"])) return malformed("search.results");
367
326
  const ranking = envelope["ranking"];
368
327
  if (ranking !== "semantic" && ranking !== "keyword")
@@ -407,7 +366,7 @@ async function agentSignup(options = {}) {
407
366
  0
408
367
  );
409
368
  }
410
- const requestId = response.headers.get("x-request-id") ?? void 0;
369
+ const requestId = requestIdOf(response.headers);
411
370
  const text = await response.text().catch(() => "");
412
371
  if (response.status !== 200) {
413
372
  let message = `request failed with status ${response.status}`;
@@ -439,6 +398,8 @@ var DEFAULT_TIMEOUT_MS = 6e4;
439
398
  var DEFAULT_MAX_RETRIES = 2;
440
399
  var RETRY_BASE_DELAY_MS = 500;
441
400
  var RETRY_MAX_DELAY_MS = 8e3;
401
+ var DEFAULT_MAX_IN_PROGRESS_WAIT_MS = 6e4;
402
+ var IDEMPOTENCY_IN_PROGRESS_CODE = "idempotency_in_progress";
442
403
  var PRE_SEND_NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
443
404
  "EADDRNOTAVAIL",
444
405
  "EAI_AGAIN",
@@ -482,7 +443,7 @@ function backoffDelay(attempt) {
482
443
  const factor = 0.5 + Math.random();
483
444
  return base * factor;
484
445
  }
485
- function parseRetryAfter(header) {
446
+ function parseRetryAfterMs(header) {
486
447
  if (header === null) {
487
448
  return void 0;
488
449
  }
@@ -492,13 +453,11 @@ function parseRetryAfter(header) {
492
453
  }
493
454
  const seconds = Number(trimmed);
494
455
  if (Number.isFinite(seconds)) {
495
- const ms = seconds * 1e3;
496
- return Math.min(Math.max(ms, 0), RETRY_MAX_DELAY_MS);
456
+ return Math.max(seconds * 1e3, 0);
497
457
  }
498
458
  const dateMs = Date.parse(trimmed);
499
459
  if (Number.isFinite(dateMs)) {
500
- const delta = dateMs - Date.now();
501
- return Math.min(Math.max(delta, 0), RETRY_MAX_DELAY_MS);
460
+ return Math.max(dateMs - Date.now(), 0);
502
461
  }
503
462
  return void 0;
504
463
  }
@@ -603,6 +562,7 @@ var AnyAPI = class {
603
562
  maxRetries;
604
563
  timeoutMs;
605
564
  idempotency;
565
+ maxInProgressWaitMs;
606
566
  /**
607
567
  * The network seam the generated per-platform namespaces target. The base client IS a
608
568
  * ClientCore (it implements `run`), so the generated subclass hands `this._core` to each
@@ -625,6 +585,7 @@ var AnyAPI = class {
625
585
  this.fetchImpl = resolvedFetch;
626
586
  this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
627
587
  this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
588
+ this.maxInProgressWaitMs = options.maxInProgressWaitMs ?? DEFAULT_MAX_IN_PROGRESS_WAIT_MS;
628
589
  const idempotency = options.idempotency ?? "auto";
629
590
  if (idempotency !== "auto" && idempotency !== "off") {
630
591
  throw new TypeError('idempotency must be "auto" or "off"');
@@ -645,6 +606,7 @@ var AnyAPI = class {
645
606
  body,
646
607
  timeoutMs: options?.timeoutMs ?? this.timeoutMs,
647
608
  maxRetries: options?.maxRetries ?? this.maxRetries,
609
+ maxInProgressWaitMs: options?.maxInProgressWaitMs ?? this.maxInProgressWaitMs,
648
610
  ...options?.idempotencyKey !== void 0 ? { idempotencyKey: options.idempotencyKey } : {},
649
611
  ...options?.signal ? { signal: options.signal } : {}
650
612
  }
@@ -717,6 +679,11 @@ var AnyAPI = class {
717
679
  headers["Idempotency-Key"] = key;
718
680
  }
719
681
  let attempt = 0;
682
+ let inProgressWaitedMs = 0;
683
+ const inProgressBudgetMs = Math.max(
684
+ opts.maxInProgressWaitMs ?? DEFAULT_MAX_IN_PROGRESS_WAIT_MS,
685
+ 0
686
+ );
720
687
  for (; ; ) {
721
688
  const { signal, timeoutSignal } = composeSignal(
722
689
  opts.timeoutMs,
@@ -750,7 +717,7 @@ var AnyAPI = class {
750
717
  }
751
718
  throw connErr;
752
719
  }
753
- const requestId = response.headers.get("x-request-id") ?? void 0;
720
+ const requestId = requestIdOf(response.headers);
754
721
  if (response.status === 200) {
755
722
  const text = await response.text();
756
723
  try {
@@ -765,13 +732,22 @@ var AnyAPI = class {
765
732
  }
766
733
  const body = await response.text().catch(() => "");
767
734
  const { message, code } = messageFromBody(body, response.status);
735
+ const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
768
736
  if (response.status === 429 && attempt < opts.maxRetries) {
769
- const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
770
- const delay = retryAfter ?? backoffDelay(attempt);
737
+ const delay = retryAfterMs !== void 0 ? Math.min(retryAfterMs, RETRY_MAX_DELAY_MS) : backoffDelay(attempt);
771
738
  await sleep(delay, opts.signal);
772
739
  attempt += 1;
773
740
  continue;
774
741
  }
742
+ if (billedPost && response.status === 409 && code === IDEMPOTENCY_IN_PROGRESS_CODE && attempt < opts.maxRetries) {
743
+ const delay = retryAfterMs ?? backoffDelay(attempt);
744
+ if (inProgressWaitedMs + delay <= inProgressBudgetMs) {
745
+ await sleep(delay, opts.signal);
746
+ inProgressWaitedMs += delay;
747
+ attempt += 1;
748
+ continue;
749
+ }
750
+ }
775
751
  throw errorFromStatus(response.status, message, requestId, code);
776
752
  }
777
753
  }