@37signals/basecamp 0.11.0 → 0.12.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.
Files changed (80) hide show
  1. package/dist/client.d.ts +14 -2
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +200 -162
  4. package/dist/client.js.map +1 -1
  5. package/dist/errors.d.ts +1 -1
  6. package/dist/errors.js +1 -1
  7. package/dist/generated/metadata.d.ts.map +1 -1
  8. package/dist/generated/metadata.js +172 -1
  9. package/dist/generated/metadata.js.map +1 -1
  10. package/dist/generated/metadata.ts +172 -1
  11. package/dist/generated/openapi-stripped.json +2074 -134
  12. package/dist/generated/path-mapping.d.ts.map +1 -1
  13. package/dist/generated/path-mapping.js +13 -0
  14. package/dist/generated/path-mapping.js.map +1 -1
  15. package/dist/generated/path-mapping.ts +13 -0
  16. package/dist/generated/schema.d.ts +1361 -25
  17. package/dist/generated/services/bookmarks.d.ts +76 -0
  18. package/dist/generated/services/bookmarks.d.ts.map +1 -0
  19. package/dist/generated/services/bookmarks.js +114 -0
  20. package/dist/generated/services/bookmarks.js.map +1 -0
  21. package/dist/generated/services/bookmarks.ts +158 -0
  22. package/dist/generated/services/calendars.d.ts +47 -0
  23. package/dist/generated/services/calendars.d.ts.map +1 -0
  24. package/dist/generated/services/calendars.js +73 -0
  25. package/dist/generated/services/calendars.js.map +1 -0
  26. package/dist/generated/services/calendars.ts +102 -0
  27. package/dist/generated/services/drafts.d.ts +38 -0
  28. package/dist/generated/services/drafts.d.ts.map +1 -0
  29. package/dist/generated/services/drafts.js +40 -0
  30. package/dist/generated/services/drafts.js.map +1 -0
  31. package/dist/generated/services/drafts.ts +67 -0
  32. package/dist/generated/services/everything.d.ts +69 -2
  33. package/dist/generated/services/everything.d.ts.map +1 -1
  34. package/dist/generated/services/everything.js +23 -13
  35. package/dist/generated/services/everything.js.map +1 -1
  36. package/dist/generated/services/everything.ts +86 -11
  37. package/dist/generated/services/index.d.ts +5 -1
  38. package/dist/generated/services/index.d.ts.map +1 -1
  39. package/dist/generated/services/index.js +5 -1
  40. package/dist/generated/services/index.js.map +1 -1
  41. package/dist/generated/services/index.ts +5 -1
  42. package/dist/generated/services/my-assignments.d.ts +52 -0
  43. package/dist/generated/services/my-assignments.d.ts.map +1 -1
  44. package/dist/generated/services/my-assignments.js +71 -0
  45. package/dist/generated/services/my-assignments.js.map +1 -1
  46. package/dist/generated/services/my-assignments.ts +104 -0
  47. package/dist/generated/services/my-notes.d.ts +45 -0
  48. package/dist/generated/services/my-notes.d.ts.map +1 -0
  49. package/dist/generated/services/my-notes.js +62 -0
  50. package/dist/generated/services/my-notes.js.map +1 -0
  51. package/dist/generated/services/my-notes.ts +92 -0
  52. package/dist/generated/services/todos.d.ts +33 -0
  53. package/dist/generated/services/todos.d.ts.map +1 -1
  54. package/dist/generated/services/todos.js +46 -0
  55. package/dist/generated/services/todos.js.map +1 -1
  56. package/dist/generated/services/todos.ts +71 -0
  57. package/dist/hooks/otel.d.ts +1 -1
  58. package/dist/hooks/otel.js +1 -1
  59. package/dist/index.d.ts +6 -2
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +5 -1
  62. package/dist/index.js.map +1 -1
  63. package/dist/oauth/discovery.d.ts.map +1 -1
  64. package/dist/oauth/discovery.js +1 -0
  65. package/dist/oauth/discovery.js.map +1 -1
  66. package/dist/pagination.d.ts +1 -1
  67. package/dist/pagination.js +1 -1
  68. package/package.json +1 -1
  69. package/src/generated/metadata.ts +172 -1
  70. package/src/generated/openapi-stripped.json +2074 -134
  71. package/src/generated/path-mapping.ts +13 -0
  72. package/src/generated/schema.d.ts +1361 -25
  73. package/src/generated/services/bookmarks.ts +158 -0
  74. package/src/generated/services/calendars.ts +102 -0
  75. package/src/generated/services/drafts.ts +67 -0
  76. package/src/generated/services/everything.ts +86 -11
  77. package/src/generated/services/index.ts +5 -1
  78. package/src/generated/services/my-assignments.ts +104 -0
  79. package/src/generated/services/my-notes.ts +92 -0
  80. package/src/generated/services/todos.ts +71 -0
package/dist/client.d.ts CHANGED
@@ -33,6 +33,10 @@ import { ClientApprovalsService } from "./generated/services/client-approvals.js
33
33
  import { ClientCorrespondencesService } from "./generated/services/client-correspondences.js";
34
34
  import { ClientRepliesService } from "./generated/services/client-replies.js";
35
35
  import { WebhooksService } from "./generated/services/webhooks.js";
36
+ import { BookmarksService } from "./generated/services/bookmarks.js";
37
+ import { DraftsService } from "./generated/services/drafts.js";
38
+ import { CalendarsService } from "./generated/services/calendars.js";
39
+ import { MyNotesService } from "./generated/services/my-notes.js";
36
40
  import { SubscriptionsService } from "./generated/services/subscriptions.js";
37
41
  import { AttachmentsService } from "./generated/services/attachments.js";
38
42
  import { VaultsService } from "./generated/services/vaults.js";
@@ -119,6 +123,14 @@ export interface BasecampClient extends RawClient {
119
123
  readonly clientReplies: ClientRepliesService;
120
124
  /** Webhooks service - create, update, delete webhooks */
121
125
  readonly webhooks: WebhooksService;
126
+ /** Bookmarks service - the current user's personal bookmarks */
127
+ readonly bookmarks: BookmarksService;
128
+ /** Drafts service - the current user's unpublished drafts */
129
+ readonly drafts: DraftsService;
130
+ /** Calendars service - per-account calendars (show + update) */
131
+ readonly calendars: CalendarsService;
132
+ /** MyNotes service - the current user's scratchpad note */
133
+ readonly myNotes: MyNotesService;
122
134
  /** Subscriptions service - manage notification subscriptions */
123
135
  readonly subscriptions: SubscriptionsService;
124
136
  /** Attachments service - upload files for embedding in rich text */
@@ -200,8 +212,8 @@ export interface BasecampClientOptions {
200
212
  /** Maximum pages to follow during auto-pagination (defaults to 10,000) */
201
213
  maxPages?: number;
202
214
  }
203
- export declare const VERSION = "0.11.0";
204
- export declare const API_VERSION = "2026-07-30";
215
+ export declare const VERSION = "0.12.0";
216
+ export declare const API_VERSION = "2026-07-31";
205
217
  /**
206
218
  * Creates a type-safe Basecamp API client with built-in middleware for:
207
219
  * - Authentication (Bearer token)
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -38,6 +38,10 @@ import { ClientApprovalsService } from "./generated/services/client-approvals.js
38
38
  import { ClientCorrespondencesService } from "./generated/services/client-correspondences.js";
39
39
  import { ClientRepliesService } from "./generated/services/client-replies.js";
40
40
  import { WebhooksService } from "./generated/services/webhooks.js";
41
+ import { BookmarksService } from "./generated/services/bookmarks.js";
42
+ import { DraftsService } from "./generated/services/drafts.js";
43
+ import { CalendarsService } from "./generated/services/calendars.js";
44
+ import { MyNotesService } from "./generated/services/my-notes.js";
41
45
  import { SubscriptionsService } from "./generated/services/subscriptions.js";
42
46
  import { AttachmentsService } from "./generated/services/attachments.js";
43
47
  import { VaultsService } from "./generated/services/vaults.js";
@@ -72,8 +76,8 @@ import { AuthorizationService } from "./services/authorization.js";
72
76
  * a TimeoutOverflowWarning rather than honored.
73
77
  */
74
78
  const MAX_TIMEOUT_MS = 2_147_483_647;
75
- export const VERSION = "0.11.0";
76
- export const API_VERSION = "2026-07-30";
79
+ export const VERSION = "0.12.0";
80
+ export const API_VERSION = "2026-07-31";
77
81
  const DEFAULT_USER_AGENT = `basecamp-sdk-ts/${VERSION} (api:${API_VERSION})`;
78
82
  /**
79
83
  * Creates a type-safe Basecamp API client with built-in middleware for:
@@ -130,28 +134,31 @@ export function createBasecampClient(options) {
130
134
  throw new BasecampError("usage", `Invalid base URL: ${baseUrl}`);
131
135
  }
132
136
  }
133
- const client = createClient({ baseUrl });
134
- // Apply middleware in order: auth, lifecycle, cache, retry.
137
+ // One lifecycle, shared between the retrying fetch and the lifecycle
138
+ // middleware: the fetch owns every attempt's start and each abandoned
139
+ // attempt's end; the middleware finalizes the terminal outcome.
140
+ const lifecycle = new RequestLifecycle(hooks);
141
+ // The retry loop lives BENEATH the middleware chain, as the client's custom
142
+ // fetch: openapi-fetch calls it exactly once per logical request, after every
143
+ // onRequest middleware and before every onError/onResponse. Installed
144
+ // unconditionally — with retry disabled it degenerates to a single attempt —
145
+ // so hook emission is single-sourced regardless of config.
146
+ const client = createClient({
147
+ baseUrl,
148
+ fetch: createRetryingFetch(lifecycle, authStrategy, enableRetry),
149
+ });
150
+ // Apply middleware in order: auth, lifecycle, cache.
135
151
  // onRequest runs in this order; onResponse and onError run in reverse, so the
136
- // retry middleware sees a response first and the lifecycle middleware last.
152
+ // lifecycle middleware finalizes the terminal attempt only after the cache
153
+ // middleware has had its chance to transform the response.
137
154
  client.use(createAuthMiddleware(authStrategy, userAgent, requestTimeoutMs, baseUrl));
138
- // One lifecycle, shared with the retry middleware: the retry loop lives inside a
139
- // single middleware callback, so per-attempt hooks have to be emitted from there
140
- // rather than by a middleware that is only visited once per request.
141
- const lifecycle = new RequestLifecycle(hooks);
142
155
  // Registered even when no hooks are configured. It emits nothing in that case
143
- // (every call is optional-chained), but it still owns releasing per-request
144
- // state — and the retry middleware records an attempt whether or not anyone is
145
- // listening, so skipping this would strand one map entry per retried request.
156
+ // (every call is optional-chained), but registering it unconditionally keeps
157
+ // the finalize path identical whether or not anyone is listening.
146
158
  client.use(createLifecycleMiddleware(lifecycle));
147
159
  if (enableCache) {
148
160
  client.use(createCacheMiddleware());
149
161
  }
150
- // Registered last so that on the reverse-order response pass it runs first,
151
- // before the cache and lifecycle middleware see the response.
152
- if (enableRetry) {
153
- client.use(createRetryMiddleware(lifecycle, authStrategy));
154
- }
155
162
  // Create enhanced client with additional properties
156
163
  const enhancedClient = client;
157
164
  Object.defineProperty(enhancedClient, "raw", {
@@ -237,6 +244,10 @@ export function createBasecampClient(options) {
237
244
  defineService("clientCorrespondences", () => new ClientCorrespondencesService(client, hooks, fetchPage, maxPages));
238
245
  defineService("clientReplies", () => new ClientRepliesService(client, hooks, fetchPage, maxPages));
239
246
  defineService("webhooks", () => new WebhooksService(client, hooks, fetchPage, maxPages));
247
+ defineService("bookmarks", () => new BookmarksService(client, hooks, fetchPage, maxPages));
248
+ defineService("drafts", () => new DraftsService(client, hooks, fetchPage, maxPages));
249
+ defineService("calendars", () => new CalendarsService(client, hooks, fetchPage, maxPages));
250
+ defineService("myNotes", () => new MyNotesService(client, hooks, fetchPage, maxPages));
240
251
  defineService("subscriptions", () => new SubscriptionsService(client, hooks, fetchPage, maxPages));
241
252
  defineService("attachments", () => new AttachmentsService(client, hooks, fetchPage, maxPages));
242
253
  defineService("vaults", () => new VaultsService(client, hooks, fetchPage, maxPages));
@@ -301,45 +312,41 @@ function createAuthMiddleware(authStrategy, userAgent, requestTimeoutMs, baseUrl
301
312
  };
302
313
  }
303
314
  /**
304
- * Owns the onRequestStart / onRequestEnd / onRetry lifecycle for every attempt of
305
- * every in-flight request, keyed by openapi-fetch's per-request `id`.
315
+ * Owns the onRequestStart / onRequestEnd / onRetry lifecycle for every attempt
316
+ * of every in-flight request, keyed by the final Request object openapi-fetch
317
+ * hands to its custom fetch.
306
318
  *
307
- * The id is minted once per logical request (one client.GET/POST/... call) and is
308
- * passed unchanged to onRequest, onResponse and onError, so it survives a
309
- * middleware replacing the Request object. It deliberately does NOT distinguish
310
- * attempts — per-attempt data lives nested under it here — and it never goes on
311
- * the wire, which is why this replaces the old `X-SDK-Request-Id` and
312
- * `X-Request-Id` correlation headers. (`X-Request-Id` in particular is a
313
- * spec-reserved BC3 *response* header; sending an SDK-internal value under that
314
- * name was a collision.)
319
+ * openapi-fetch passes that same object to onResponse and onError, so identity
320
+ * holds from the retrying fetch (which begins every attempt) through to the
321
+ * lifecycle middleware (which finalizes the terminal one). Nothing ever goes on
322
+ * the wire — this keying replaced the old `X-SDK-Request-Id` and `X-Request-Id`
323
+ * correlation headers. (`X-Request-Id` in particular is a spec-reserved BC3
324
+ * *response* header; sending an SDK-internal value under that name was a
325
+ * collision.)
315
326
  *
316
- * Shared with the retry middleware because the retry loop lives inside
317
- * one middleware callback: openapi-fetch visits each middleware once per request,
318
- * so per-attempt hooks cannot be emitted by a separate middleware.
327
+ * A WeakMap makes the release bookkeeping structural: once a request settles
328
+ * and its Request is unreachable, the state is collectable — there is no
329
+ * release() to forget.
319
330
  */
320
331
  class RequestLifecycle {
321
332
  hooks;
322
- states = new Map();
333
+ states = new WeakMap();
323
334
  constructor(hooks) {
324
335
  this.hooks = hooks;
325
336
  }
326
337
  /** Begin an attempt and fire onRequestStart for it. */
327
- begin(id, method, url, attempt) {
328
- this.states.set(id, { startTime: performance.now(), attempt, finalized: false });
338
+ begin(request, method, url, attempt) {
339
+ this.states.set(request, { startTime: performance.now(), attempt, finalized: false });
329
340
  this.emit(() => this.hooks?.onRequestStart?.({ method, url, attempt }));
330
341
  }
331
- /** The attempt currently in flight, or 1 if we have no record. */
332
- attemptOf(id) {
333
- return this.states.get(id)?.attempt ?? 1;
334
- }
335
342
  /**
336
343
  * Fire onRequestEnd for the in-flight attempt, exactly once.
337
344
  *
338
345
  * A network failure or timeout carries statusCode 0 and the originating error,
339
346
  * matching the multipart transport in services/base.ts and SPEC section 7.
340
347
  */
341
- finalize(id, method, url, outcome) {
342
- const state = this.states.get(id);
348
+ finalize(request, method, url, outcome) {
349
+ const state = this.states.get(request);
343
350
  if (!state || state.finalized)
344
351
  return;
345
352
  state.finalized = true;
@@ -359,13 +366,9 @@ class RequestLifecycle {
359
366
  * just FAILED, while the standalone argument is the UPCOMING attempt. Go,
360
367
  * Python, Ruby and Kotlin all pass (failed, failed + 1).
361
368
  */
362
- retrying(id, method, url, failedAttempt, error, delayMs) {
369
+ retrying(method, url, failedAttempt, error, delayMs) {
363
370
  this.emit(() => this.hooks?.onRetry?.({ method, url, attempt: failedAttempt }, failedAttempt + 1, error, delayMs));
364
371
  }
365
- /** Drop all state for a logical request. Safe to call more than once. */
366
- release(id) {
367
- this.states.delete(id);
368
- }
369
372
  emit(fn) {
370
373
  try {
371
374
  fn();
@@ -377,37 +380,32 @@ class RequestLifecycle {
377
380
  }
378
381
  function createLifecycleMiddleware(lifecycle) {
379
382
  return {
380
- async onRequest({ request, id }) {
381
- lifecycle.begin(id, request.method, request.url, 1);
382
- return request;
383
- },
384
- async onResponse({ request, response, id }) {
385
- // The authoritative end for whichever attempt is in flight: attempt 1 for a
386
- // single-attempt request, or attempt 2 after a retry — the retry middleware
387
- // deliberately does not finalize a successful attempt, so that the cache
388
- // middleware (which runs between the two) can transform the response first.
389
- // finalize stays idempotent as a backstop.
383
+ async onResponse({ request, response }) {
384
+ // The authoritative end for the terminal attempt: the retrying fetch
385
+ // begins every attempt but deliberately does not finalize the response it
386
+ // returns, so that this pass can record the outcome the cache middleware
387
+ // has since transformed (a 304 rewritten into a cached 200). finalize
388
+ // stays idempotent as a backstop.
390
389
  //
391
390
  // fromCache means "served out of the ETag cache", and only the header the
392
391
  // cache middleware sets proves that. A bare 304 does NOT: it reaches here
393
392
  // when the cache is disabled, or is enabled but holds no entry for this key,
394
393
  // and in both cases the caller's own conditional request went to the server.
395
394
  const fromCache = response.headers.get("X-From-Cache") === "1";
396
- lifecycle.finalize(id, request.method, request.url, {
395
+ lifecycle.finalize(request, request.method, request.url, {
397
396
  statusCode: response.status,
398
397
  fromCache,
399
398
  });
400
- lifecycle.release(id);
401
399
  return response;
402
400
  },
403
- async onError({ request, error, id }) {
404
- // openapi-fetch skips onResponse entirely when the initial fetch rejects,
405
- // so this is the only place a network error or timeout can be observed.
406
- lifecycle.finalize(id, request.method, request.url, {
401
+ async onError({ request, error }) {
402
+ // A terminal throw from the retrying fetch — a network error, a timeout,
403
+ // or a retry's auth refresh — skips onResponse entirely, so this is the
404
+ // only place it can be observed.
405
+ lifecycle.finalize(request, request.method, request.url, {
407
406
  statusCode: 0,
408
407
  error: error instanceof Error ? error : new Error(String(error)),
409
408
  });
410
- lifecycle.release(id);
411
409
  // Returning nothing preserves the original error's identity and rethrows it.
412
410
  return undefined;
413
411
  },
@@ -679,44 +677,105 @@ function getRetryConfigForRequest(method, url) {
679
677
  }
680
678
  return DEFAULT_RETRY_CONFIG;
681
679
  }
682
- function createRetryMiddleware(lifecycle, authStrategy) {
683
- // Serialized request bodies, keyed by openapi-fetch's per-request id, because
684
- // Request.body can only be read once and the retry needs to replay it. Keyed on
685
- // the id rather than method+url+timestamp: two concurrent mutations to the same
686
- // URL used to collide on one slot and replay each other's bytes.
687
- const bodyCache = new Map();
688
- return {
689
- async onRequest({ request, id }) {
690
- // For methods that may have a body, clone it before the initial fetch
691
- // so we can use it for retries. Request.body can only be consumed once.
692
- const method = request.method.toUpperCase();
693
- if (method === "POST" || method === "PUT" || method === "PATCH") {
694
- if (request.body) {
695
- // Clone the body before it gets consumed
696
- const cloned = request.clone();
697
- bodyCache.set(id, await cloned.arrayBuffer());
698
- }
699
- else {
700
- bodyCache.set(id, null);
701
- }
680
+ /**
681
+ * The retry loop, installed beneath the middleware chain as the client's custom
682
+ * fetch (createClient's `fetch` option). openapi-fetch calls it exactly once
683
+ * per logical request, after every onRequest middleware and before every
684
+ * onError/onResponse — so the loop runs to the operation's full declared
685
+ * maxAttempts, and no attempt ever leaves the chain, because the chain sits
686
+ * above the loop.
687
+ *
688
+ * Division of labor with the lifecycle middleware: the loop begins EVERY
689
+ * attempt and finalizes the attempts it abandons (before the backoff sleep);
690
+ * the terminal outcome — the response it returns, or the error it throws — is
691
+ * deliberately NOT finalized here. It flows up through the cache middleware,
692
+ * which may rewrite a 304 into a cached 200 + X-From-Cache, to the lifecycle
693
+ * middleware, which records the post-transform result. Finalizing in the loop
694
+ * would freeze the pre-transformation status, and the idempotent finalize
695
+ * would keep the hooks pass from correcting it.
696
+ *
697
+ * With retry disabled the loop degenerates to a single attempt but still owns
698
+ * hook emission, so start/end events are single-sourced regardless of config.
699
+ */
700
+ function createRetryingFetch(lifecycle, authStrategy, enableRetry) {
701
+ return async (request) => {
702
+ const { method, url } = request;
703
+ const retryConfig = getRetryConfigForRequest(method, url);
704
+ const maxAttempts = enableRetry ? retryConfig.maxAttempts : 1;
705
+ // Serialize the body once, before the first send, because Request.body is
706
+ // a stream that can only be consumed once and a retry needs to replay it.
707
+ // clone() tees the stream, so attempt 1 sends the original untouched and
708
+ // replays read from this buffer. Requests that can never retry — including
709
+ // every non-idempotent POST, via NO_RETRY_CONFIG — skip the buffering.
710
+ const upperMethod = method.toUpperCase();
711
+ let bodyBuffer = null;
712
+ if (maxAttempts > 1 &&
713
+ (upperMethod === "POST" || upperMethod === "PUT" || upperMethod === "PATCH") &&
714
+ request.body) {
715
+ bodyBuffer = await request.clone().arrayBuffer();
716
+ }
717
+ let attempt = 1;
718
+ let attemptRequest = request;
719
+ lifecycle.begin(request, method, url, attempt);
720
+ for (;;) {
721
+ if (attempt > 1) {
722
+ // Rebuild from the original request: the headers carry everything the
723
+ // onRequest middleware attached (auth, User-Agent, If-None-Match), and
724
+ // the signal keeps the caller's cancellation and the per-request
725
+ // timeout — one budget shared by all attempts and backoffs.
726
+ attemptRequest = new Request(url, {
727
+ method,
728
+ headers: new Headers(request.headers),
729
+ body: bodyBuffer,
730
+ signal: request.signal,
731
+ });
732
+ // Refresh auth (the token may have rotated since the last attempt).
733
+ // The attempt is already begun, so a throwing refresh lands on a live
734
+ // attempt and propagates to the lifecycle middleware's onError.
735
+ await authStrategy.authenticate(attemptRequest.headers);
702
736
  }
703
- return request;
704
- },
705
- async onResponse({ request, response, id }) {
706
- const { method, url } = request;
707
- // Get operation-specific retry config from metadata
708
- const retryConfig = getRetryConfigForRequest(method, url);
709
- // Not retrying: let the lifecycle middleware end the attempt, and drop the body.
710
- if (!retryConfig.retryOn.includes(response.status)) {
711
- bodyCache.delete(id);
712
- return response;
737
+ let response;
738
+ try {
739
+ // globalThis.fetch resolved per attempt rather than captured at client
740
+ // creation, so test interceptors that patch it (MSW) are honored.
741
+ response = await globalThis.fetch(attemptRequest);
713
742
  }
714
- const failedAttempt = lifecycle.attemptOf(id);
715
- // maxAttempts is a total attempt count, so attempt N is terminal when it
716
- // equals the cap. TypeScript additionally chains at most one retry, since
717
- // the retry below leaves the middleware chain (SPEC waiver 2B.1).
718
- if (failedAttempt >= retryConfig.maxAttempts) {
719
- bodyCache.delete(id);
743
+ catch (error) {
744
+ // An abort is terminal no matter what the budget says: a caller
745
+ // cancellation must not re-send, and the per-request timeout is one
746
+ // budget shared by every attempt and backoff — once it fires, a retry
747
+ // would instantly re-reject. Terminal errors are rethrown as-is so
748
+ // their identity survives to the lifecycle middleware's onError (and
749
+ // to the caller).
750
+ //
751
+ // The signal is the authoritative abort test: a caller can abort with
752
+ // a CUSTOM reason — AbortController.abort(reason) — and fetch then
753
+ // rejects with that reason, not a DOMException named AbortError. The
754
+ // DOMException check remains for abort-shaped rejections that arrive
755
+ // without an aborted request signal.
756
+ const isAbort = request.signal?.aborted === true ||
757
+ (error instanceof DOMException &&
758
+ (error.name === "AbortError" || error.name === "TimeoutError"));
759
+ if (isAbort || attempt >= maxAttempts) {
760
+ throw error;
761
+ }
762
+ // Network-error retry rides the same per-operation gate as status
763
+ // retry: a non-idempotent POST resolves NO_RETRY_CONFIG (maxAttempts
764
+ // 1), so it is rethrown above after its single attempt.
765
+ const cause = error instanceof Error ? error : new Error(String(error));
766
+ const delay = calculateBackoffDelay(retryConfig, attempt - 1);
767
+ lifecycle.finalize(request, method, url, { statusCode: 0, error: cause });
768
+ lifecycle.retrying(method, url, attempt, cause, delay);
769
+ await sleep(delay, request.signal);
770
+ // Same placement rationale as the status path below.
771
+ attempt += 1;
772
+ lifecycle.begin(request, method, url, attempt);
773
+ continue;
774
+ }
775
+ // Terminal: a status outside the operation's declared retryOn set, or a
776
+ // spent budget — maxAttempts is a total attempt count, so attempt N is
777
+ // terminal when it equals the cap.
778
+ if (!retryConfig.retryOn.includes(response.status) || attempt >= maxAttempts) {
720
779
  return response;
721
780
  }
722
781
  // For 429, respect Retry-After; otherwise back off.
@@ -727,71 +786,29 @@ function createRetryMiddleware(lifecycle, authStrategy) {
727
786
  delay = retryAfterSeconds * 1000;
728
787
  }
729
788
  else {
730
- delay = calculateBackoffDelay(retryConfig, failedAttempt - 1);
789
+ delay = calculateBackoffDelay(retryConfig, attempt - 1);
731
790
  }
732
791
  const statusError = new Error(`HTTP ${response.status}: ${response.statusText || "Request failed"}`);
733
- try {
734
- // End the failed attempt before sleeping, so a slow backoff cannot leave
735
- // an attempt open, then announce the upcoming one.
736
- lifecycle.finalize(id, method, url, { statusCode: response.status });
737
- lifecycle.retrying(id, method, url, failedAttempt, statusError, delay);
738
- // This response is being discarded, so release its stream before we sleep
739
- // rather than leaving it open across the backoff — otherwise a throttled
740
- // client holds a connection per in-flight retry and cannot reuse any of
741
- // them. The multipart transport in services/base.ts already does this.
742
- // Errors are ignored: the body may already be consumed or closed.
743
- void response.body?.cancel().catch(() => { });
744
- await sleep(delay);
745
- // The raw fetch below bypasses the middleware chain, so no downstream
746
- // middleware sees this attempt begin — start it here.
747
- //
748
- // Started after the backoff but before any work that can throw. After, so
749
- // the attempt's duration measures the request rather than the sleep;
750
- // before, so that if the auth refresh or the fetch throws, the catch has a
751
- // live attempt to finalize. Starting it later would let onRetry announce
752
- // attempt 2 and then never account for it.
753
- lifecycle.begin(id, method, url, failedAttempt + 1);
754
- const body = bodyCache.get(id) ?? null;
755
- const retryRequest = new Request(url, {
756
- method,
757
- headers: new Headers(request.headers),
758
- body,
759
- signal: request.signal,
760
- });
761
- // Refresh auth header for retry (token may have been refreshed since initial request)
762
- if (authStrategy) {
763
- await authStrategy.authenticate(retryRequest.headers);
764
- }
765
- // Deliberately NOT finalized here. The returned response still flows
766
- // through the cache middleware, which may rewrite a 304 into a cached 200;
767
- // finalizing now would freeze the pre-transformation status and
768
- // fromCache: false, and an idempotent finalize means the hooks pass could
769
- // not correct it. The lifecycle middleware ends this attempt instead, reading
770
- // attempt 2 from the state begun above.
771
- return await fetch(retryRequest);
772
- }
773
- catch (error) {
774
- // openapi-fetch routes only the INITIAL fetch through onError, so anything
775
- // thrown in here — the retry fetch, the auth refresh, the sleep — would
776
- // otherwise escape with the attempt still open and its state stranded.
777
- lifecycle.finalize(id, method, url, {
778
- statusCode: 0,
779
- error: error instanceof Error ? error : new Error(String(error)),
780
- });
781
- lifecycle.release(id);
782
- // Rethrow the original value so its identity survives.
783
- throw error;
784
- }
785
- finally {
786
- bodyCache.delete(id);
787
- }
788
- },
789
- async onError({ id }) {
790
- // The initial fetch rejected, so onResponse never ran and the cached body
791
- // would leak. The lifecycle middleware owns ending the attempt.
792
- bodyCache.delete(id);
793
- return undefined;
794
- },
792
+ // End the failed attempt before sleeping, so a slow backoff cannot leave
793
+ // an attempt open, then announce the upcoming one.
794
+ lifecycle.finalize(request, method, url, { statusCode: response.status });
795
+ lifecycle.retrying(method, url, attempt, statusError, delay);
796
+ // This response is being discarded, so release its stream before we sleep
797
+ // rather than leaving it open across the backoff — otherwise a throttled
798
+ // client holds a connection per in-flight retry and cannot reuse any of
799
+ // them. The multipart transport in services/base.ts already does this.
800
+ // Errors are ignored: the body may already be consumed or closed.
801
+ void response.body?.cancel().catch(() => { });
802
+ await sleep(delay, request.signal);
803
+ // Begun after the backoff but before any work that can throw. After, so
804
+ // the attempt's duration measures the request rather than the sleep;
805
+ // before, so that if the auth refresh or the fetch throws, the lifecycle
806
+ // middleware's onError still finds a live attempt to finalize. Starting
807
+ // it later would let onRetry announce an attempt and then never account
808
+ // for it.
809
+ attempt += 1;
810
+ lifecycle.begin(request, method, url, attempt);
811
+ }
795
812
  };
796
813
  }
797
814
  function calculateBackoffDelay(config, attempt) {
@@ -812,8 +829,29 @@ function calculateBackoffDelay(config, attempt) {
812
829
  const jitter = Math.random() * MAX_JITTER_MS;
813
830
  return delay + jitter;
814
831
  }
815
- function sleep(ms) {
816
- return new Promise((resolve) => setTimeout(resolve, ms));
832
+ /**
833
+ * Signal-aware sleep for backoff waits: resolves after `ms`, or rejects with
834
+ * the signal's abort reason the moment it fires. Without this, a caller abort
835
+ * or the request-timeout budget expiring during a backoff would leave the
836
+ * request pending for the full delay and then start another attempt — begin,
837
+ * auth refresh, fetch — against an already-aborted signal.
838
+ */
839
+ function sleep(ms, signal) {
840
+ return new Promise((resolve, reject) => {
841
+ if (signal?.aborted) {
842
+ reject(signal.reason);
843
+ return;
844
+ }
845
+ const onAbort = () => {
846
+ clearTimeout(timer);
847
+ reject(signal.reason);
848
+ };
849
+ const timer = setTimeout(() => {
850
+ signal?.removeEventListener("abort", onAbort);
851
+ resolve();
852
+ }, ms);
853
+ signal?.addEventListener("abort", onAbort, { once: true });
854
+ });
817
855
  }
818
856
  // =============================================================================
819
857
  // Pagination Helper