@relayfile/adapter-core 0.3.37 → 0.3.38

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 (37) hide show
  1. package/dist/src/alias-lifecycle.d.ts +73 -0
  2. package/dist/src/alias-lifecycle.d.ts.map +1 -0
  3. package/dist/src/alias-lifecycle.js +90 -0
  4. package/dist/src/alias-lifecycle.js.map +1 -0
  5. package/dist/src/http/index.d.ts +3 -0
  6. package/dist/src/http/index.d.ts.map +1 -0
  7. package/dist/src/http/index.js +2 -0
  8. package/dist/src/http/index.js.map +1 -0
  9. package/dist/src/http/retry.d.ts +165 -0
  10. package/dist/src/http/retry.d.ts.map +1 -0
  11. package/dist/src/http/retry.js +330 -0
  12. package/dist/src/http/retry.js.map +1 -0
  13. package/dist/src/http/retry.test.d.ts +2 -0
  14. package/dist/src/http/retry.test.d.ts.map +1 -0
  15. package/dist/src/http/retry.test.js +382 -0
  16. package/dist/src/http/retry.test.js.map +1 -0
  17. package/dist/src/index.d.ts +2 -0
  18. package/dist/src/index.d.ts.map +1 -1
  19. package/dist/src/index.js +2 -0
  20. package/dist/src/index.js.map +1 -1
  21. package/dist/src/ingest/shared.d.ts.map +1 -1
  22. package/dist/src/ingest/shared.js +2 -1
  23. package/dist/src/ingest/shared.js.map +1 -1
  24. package/dist/src/runtime/file-native-router.test.js +15 -0
  25. package/dist/src/runtime/file-native-router.test.js.map +1 -1
  26. package/dist/src/runtime/schema-adapter.d.ts.map +1 -1
  27. package/dist/src/runtime/schema-adapter.js +3 -2
  28. package/dist/src/runtime/schema-adapter.js.map +1 -1
  29. package/dist/src/writeback-paths/catalog.generated.d.ts +4 -0
  30. package/dist/src/writeback-paths/catalog.generated.d.ts.map +1 -1
  31. package/dist/src/writeback-paths/catalog.generated.js +6 -0
  32. package/dist/src/writeback-paths/catalog.generated.js.map +1 -1
  33. package/dist/tests/alias-lifecycle/alias-lifecycle.test.d.ts +2 -0
  34. package/dist/tests/alias-lifecycle/alias-lifecycle.test.d.ts.map +1 -0
  35. package/dist/tests/alias-lifecycle/alias-lifecycle.test.js +104 -0
  36. package/dist/tests/alias-lifecycle/alias-lifecycle.test.js.map +1 -0
  37. package/package.json +6 -1
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Stale alias lifecycle cleanup (issue #106).
3
+ *
4
+ * Adapters mirror provider records to a canonical path plus lookup aliases
5
+ * (`by-title/…`, `by-name/…`). Aliases duplicate the canonical bytes
6
+ * verbatim, and the title-independent `by-id` alias is rewritten on every
7
+ * ingest — so the bytes sitting at the `by-id` alias path *before* a
8
+ * re-ingest are a faithful snapshot of the previous record version. That
9
+ * snapshot is the prior state consumed here: callers read it before
10
+ * overwriting, derive the candidate alias paths the previous version may
11
+ * occupy (base + collision variants), and this helper deletes every
12
+ * candidate that still holds the previous record bytes.
13
+ *
14
+ * Content equality is the ownership proof: a candidate is deleted only when
15
+ * its bytes match `previousContent`. This guarantees we never delete
16
+ * (a) an alias owned by a different record that happens to share a slug
17
+ * (collision case), (b) the freshly written alias for the current version
18
+ * (its bytes are the new content), or (c) anything when the previous
19
+ * version was never mirrored.
20
+ *
21
+ * Relayfile contract: a title change is an alias cleanup, never a record
22
+ * deletion. Callers must only pass alias paths as candidates — canonical
23
+ * record paths are never deleted by this helper's callers.
24
+ *
25
+ * See also `PriorAliasReader` in `emit-auxiliary` — the same "by-id alias
26
+ * as prior-state anchor" convention for adapters built on the auxiliary
27
+ * emitter client. This module is the IO-agnostic deletion side, usable
28
+ * with both `RelayFileClientLike` and duck-typed `VfsLike` backends.
29
+ */
30
+ export interface StaleAliasCleanupIo {
31
+ /** Returns file content, or `undefined` when missing/unreadable. */
32
+ readFile(path: string): Promise<string | undefined> | string | undefined;
33
+ /** Deletes the file at `path`. */
34
+ deleteFile(path: string): Promise<unknown> | unknown;
35
+ }
36
+ export interface StaleAliasCleanupInput {
37
+ /**
38
+ * Bytes of the record as previously mirrored — read from a
39
+ * title-independent alias (e.g. `by-id/…`) before it was overwritten.
40
+ */
41
+ previousContent: string;
42
+ /**
43
+ * Alias paths the previous record version may occupy. Derive from the
44
+ * previous title/name (base + collision variants).
45
+ */
46
+ candidatePaths: readonly string[];
47
+ /**
48
+ * Alias paths belonging to the current record version. Never deleted,
49
+ * even when a candidate path matches.
50
+ */
51
+ keepPaths?: readonly string[];
52
+ }
53
+ export interface StaleAliasCleanupResult {
54
+ deletedPaths: string[];
55
+ errors: Array<{
56
+ path: string;
57
+ error: string;
58
+ }>;
59
+ }
60
+ /**
61
+ * Deletes stale alias files left behind when a record's alias-relevant
62
+ * field (title/name) changed between ingests. Failures are captured per
63
+ * path and never thrown — a leftover stale alias is benign, while failing
64
+ * the surrounding ingest is not.
65
+ */
66
+ export declare function cleanupStaleAliases(io: StaleAliasCleanupIo, input: StaleAliasCleanupInput): Promise<StaleAliasCleanupResult>;
67
+ /**
68
+ * Extracts a top-level string field from mirrored JSON record content.
69
+ * Returns `undefined` when the content is not parseable JSON or the field
70
+ * is missing/non-string — callers treat that as "no prior alias to clean".
71
+ */
72
+ export declare function readAliasKeyFromContent(content: string, ...fieldPath: readonly string[]): string | undefined;
73
+ //# sourceMappingURL=alias-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alias-lifecycle.d.ts","sourceRoot":"","sources":["../../src/alias-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,GAAG,SAAS,CAAC;IACzE,kCAAkC;IAClC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CACtD;AAED,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,mBAAmB,EACvB,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,uBAAuB,CAAC,CAgClC;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAgB5G"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Stale alias lifecycle cleanup (issue #106).
3
+ *
4
+ * Adapters mirror provider records to a canonical path plus lookup aliases
5
+ * (`by-title/…`, `by-name/…`). Aliases duplicate the canonical bytes
6
+ * verbatim, and the title-independent `by-id` alias is rewritten on every
7
+ * ingest — so the bytes sitting at the `by-id` alias path *before* a
8
+ * re-ingest are a faithful snapshot of the previous record version. That
9
+ * snapshot is the prior state consumed here: callers read it before
10
+ * overwriting, derive the candidate alias paths the previous version may
11
+ * occupy (base + collision variants), and this helper deletes every
12
+ * candidate that still holds the previous record bytes.
13
+ *
14
+ * Content equality is the ownership proof: a candidate is deleted only when
15
+ * its bytes match `previousContent`. This guarantees we never delete
16
+ * (a) an alias owned by a different record that happens to share a slug
17
+ * (collision case), (b) the freshly written alias for the current version
18
+ * (its bytes are the new content), or (c) anything when the previous
19
+ * version was never mirrored.
20
+ *
21
+ * Relayfile contract: a title change is an alias cleanup, never a record
22
+ * deletion. Callers must only pass alias paths as candidates — canonical
23
+ * record paths are never deleted by this helper's callers.
24
+ *
25
+ * See also `PriorAliasReader` in `emit-auxiliary` — the same "by-id alias
26
+ * as prior-state anchor" convention for adapters built on the auxiliary
27
+ * emitter client. This module is the IO-agnostic deletion side, usable
28
+ * with both `RelayFileClientLike` and duck-typed `VfsLike` backends.
29
+ */
30
+ /**
31
+ * Deletes stale alias files left behind when a record's alias-relevant
32
+ * field (title/name) changed between ingests. Failures are captured per
33
+ * path and never thrown — a leftover stale alias is benign, while failing
34
+ * the surrounding ingest is not.
35
+ */
36
+ export async function cleanupStaleAliases(io, input) {
37
+ const result = { deletedPaths: [], errors: [] };
38
+ const keep = new Set(input.keepPaths ?? []);
39
+ const seen = new Set();
40
+ for (const candidate of input.candidatePaths) {
41
+ if (!candidate || keep.has(candidate) || seen.has(candidate)) {
42
+ continue;
43
+ }
44
+ seen.add(candidate);
45
+ let existing;
46
+ try {
47
+ existing = await io.readFile(candidate);
48
+ }
49
+ catch (error) {
50
+ result.errors.push({ path: candidate, error: formatError(error) });
51
+ continue;
52
+ }
53
+ if (existing === undefined || existing !== input.previousContent) {
54
+ continue;
55
+ }
56
+ try {
57
+ await io.deleteFile(candidate);
58
+ result.deletedPaths.push(candidate);
59
+ }
60
+ catch (error) {
61
+ result.errors.push({ path: candidate, error: formatError(error) });
62
+ }
63
+ }
64
+ return result;
65
+ }
66
+ /**
67
+ * Extracts a top-level string field from mirrored JSON record content.
68
+ * Returns `undefined` when the content is not parseable JSON or the field
69
+ * is missing/non-string — callers treat that as "no prior alias to clean".
70
+ */
71
+ export function readAliasKeyFromContent(content, ...fieldPath) {
72
+ let value;
73
+ try {
74
+ value = JSON.parse(content);
75
+ }
76
+ catch {
77
+ return undefined;
78
+ }
79
+ for (const field of fieldPath) {
80
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
81
+ return undefined;
82
+ }
83
+ value = value[field];
84
+ }
85
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
86
+ }
87
+ function formatError(error) {
88
+ return error instanceof Error ? error.message : String(error);
89
+ }
90
+ //# sourceMappingURL=alias-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alias-lifecycle.js","sourceRoot":"","sources":["../../src/alias-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAgCH;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,EAAuB,EACvB,KAA6B;IAE7B,MAAM,MAAM,GAA4B,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACzE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QAC7C,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7D,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAEpB,IAAI,QAA4B,CAAC;QACjC,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACnE,SAAS;QACX,CAAC;QAED,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,KAAK,CAAC,eAAe,EAAE,CAAC;YACjE,SAAS;QACX,CAAC;QAED,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;YAC/B,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAe,EAAE,GAAG,SAA4B;IACtF,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACxE,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,KAAK,GAAI,KAAiC,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3E,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { RetryExhaustedError, executeWithRetry, fetchWithRetry, isTransientNetworkError, parseRetryAfterMs, withProxyRetry, } from './retry.js';
2
+ export type { FetchRetryOptions, ProxyCapable, ProxyResponseLike, RetryOptions, RetryRequestInit, RetryResponseLike, } from './retry.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/http/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,uBAAuB,EACvB,iBAAiB,EACjB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { RetryExhaustedError, executeWithRetry, fetchWithRetry, isTransientNetworkError, parseRetryAfterMs, withProxyRetry, } from './retry.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/http/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,uBAAuB,EACvB,iBAAiB,EACjB,cAAc,GACf,MAAM,YAAY,CAAC"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Shared HTTP rate-limit / retry helpers.
3
+ *
4
+ * Adapters in this repo talk to providers through one of two transports:
5
+ *
6
+ * 1. A `fetch`-shaped client (Notion's raw token path, the X search client).
7
+ * Wrap those call sites with {@link fetchWithRetry}.
8
+ * 2. A relayfile `ConnectionProvider.proxy()` call. Wrap the provider with
9
+ * {@link withProxyRetry} at the call site:
10
+ *
11
+ * const response = await withProxyRetry(provider).proxy({ ... });
12
+ *
13
+ * Both honor `Retry-After` (delta-seconds and HTTP-date forms), retry on 429,
14
+ * 5xx, and transient network errors with exponential backoff plus jitter, and
15
+ * never retry non-idempotent requests (POST/PATCH) unless the caller opts in
16
+ * via `retryNonIdempotent: true`.
17
+ *
18
+ * Exhaustion semantics:
19
+ * - If the final attempt produced an HTTP response, that response is returned
20
+ * so existing status handling in adapters keeps working unchanged. Callers
21
+ * that want a typed failure instead can pass
22
+ * `throwOnExhaustedRetryableStatus: true` to receive a
23
+ * {@link RetryExhaustedError}.
24
+ * - If the final attempt failed with a transient network error, a
25
+ * {@link RetryExhaustedError} is thrown with the underlying error as
26
+ * `cause`.
27
+ */
28
+ export interface RetryOptions {
29
+ /** Total attempts, including the first one. Default 3. */
30
+ maxAttempts?: number;
31
+ /** Give up once this much wall-clock time has been spent. Default 30s. */
32
+ maxElapsedMs?: number;
33
+ /** Base delay before the first retry. Default 250ms. */
34
+ initialDelayMs?: number;
35
+ /** Upper bound for a single computed backoff delay. Default 10s. */
36
+ maxDelayMs?: number;
37
+ /** Exponential growth factor. Default 2. */
38
+ backoffFactor?: number;
39
+ /**
40
+ * Retry POST/PATCH requests too. Default false: only idempotent methods
41
+ * (GET, HEAD, OPTIONS, PUT, DELETE, TRACE) are retried.
42
+ */
43
+ retryNonIdempotent?: boolean;
44
+ /** Status classifier. Default: 429 or any 5xx is retryable. */
45
+ isRetryableStatus?: (status: number) => boolean;
46
+ /** Error classifier. Default: {@link isTransientNetworkError}. */
47
+ isRetryableError?: (error: unknown) => boolean;
48
+ /**
49
+ * Throw a {@link RetryExhaustedError} instead of returning the final
50
+ * response when attempts run out while the response is still retryable
51
+ * (e.g. a persistent 429). Default false.
52
+ */
53
+ throwOnExhaustedRetryableStatus?: boolean;
54
+ /** Abort retries (and in-flight sleeps) when this signal fires. */
55
+ signal?: AbortSignal;
56
+ /** Injectable sleep, for tests. */
57
+ sleep?: (ms: number) => Promise<void>;
58
+ /** Injectable jitter source, for tests. Must return [0, 1). */
59
+ random?: () => number;
60
+ /** Injectable clock, for tests. Returns epoch milliseconds. */
61
+ now?: () => number;
62
+ }
63
+ /** Typed error surfaced when retries are exhausted. */
64
+ export declare class RetryExhaustedError extends Error {
65
+ readonly code = "RETRY_EXHAUSTED";
66
+ /** Number of attempts that were made. */
67
+ readonly attempts: number;
68
+ /** Wall-clock time spent across all attempts, in milliseconds. */
69
+ readonly elapsedMs: number;
70
+ /** Status of the final response, when the final attempt got one. */
71
+ readonly lastStatus?: number;
72
+ constructor(message: string, details: {
73
+ attempts: number;
74
+ elapsedMs: number;
75
+ lastStatus?: number;
76
+ cause?: unknown;
77
+ });
78
+ }
79
+ /** True for errors that look like transient network failures worth retrying. */
80
+ export declare function isTransientNetworkError(error: unknown): boolean;
81
+ /**
82
+ * Parse a `Retry-After` header value: either delta-seconds or an HTTP-date.
83
+ * Returns the wait in milliseconds, or `undefined` when unparseable.
84
+ */
85
+ export declare function parseRetryAfterMs(value: string | null | undefined, nowMs: number): number | undefined;
86
+ type HeadersLike = {
87
+ get(name: string): string | null;
88
+ } | Record<string, string | string[] | undefined>;
89
+ interface AttemptInspection {
90
+ retryable: boolean;
91
+ status: number;
92
+ retryAfterMs?: number;
93
+ }
94
+ /**
95
+ * Core retry loop shared by {@link fetchWithRetry} and {@link withProxyRetry}.
96
+ *
97
+ * `attempt` performs one request; `inspect` classifies its result. Thrown
98
+ * errors are retried only when `isRetryableError` says so and the request is
99
+ * idempotent (or `retryNonIdempotent` is set).
100
+ */
101
+ export declare function executeWithRetry<R>(args: {
102
+ attempt: () => Promise<R>;
103
+ inspect: (result: R) => AttemptInspection;
104
+ idempotent: boolean;
105
+ describe: string;
106
+ options: RetryOptions;
107
+ }): Promise<R>;
108
+ /** Minimal response surface required by {@link fetchWithRetry}. */
109
+ export interface RetryResponseLike {
110
+ status: number;
111
+ headers?: HeadersLike;
112
+ }
113
+ /** Permissive `RequestInit` so any fetch implementation can be wrapped. */
114
+ export interface RetryRequestInit {
115
+ method?: string;
116
+ signal?: AbortSignal | null;
117
+ [key: string]: unknown;
118
+ }
119
+ export interface FetchRetryOptions<R extends RetryResponseLike> extends RetryOptions {
120
+ /** Fetch implementation. Defaults to `globalThis.fetch`. */
121
+ fetch?: (input: string | URL, init?: RetryRequestInit) => Promise<R>;
122
+ /**
123
+ * Force the idempotency classification instead of deriving it from
124
+ * `init.method`. Useful for POST endpoints that are semantically reads.
125
+ */
126
+ idempotent?: boolean;
127
+ }
128
+ /**
129
+ * `fetch` with rate-limit aware retries.
130
+ *
131
+ * Retries 429/5xx responses (honoring `Retry-After`) and transient network
132
+ * errors with exponential backoff plus jitter. Non-idempotent methods
133
+ * (POST/PATCH) are never retried unless the caller opts in via
134
+ * `retryNonIdempotent` or `idempotent: true`.
135
+ *
136
+ * Returns the final response even when it is still an error status, unless
137
+ * `throwOnExhaustedRetryableStatus` is set; network-error exhaustion throws a
138
+ * {@link RetryExhaustedError}.
139
+ */
140
+ export declare function fetchWithRetry<R extends RetryResponseLike = Response>(input: string | URL, init?: RetryRequestInit, options?: FetchRetryOptions<R>): Promise<R>;
141
+ /** Minimal proxy response surface required by {@link withProxyRetry}. */
142
+ export interface ProxyResponseLike {
143
+ status: number;
144
+ headers?: Record<string, string>;
145
+ }
146
+ /**
147
+ * Minimal provider surface required by {@link withProxyRetry}. The `never`
148
+ * parameter type makes any concrete `proxy(request)` method assignable here
149
+ * (method parameters are checked bivariantly).
150
+ */
151
+ export interface ProxyCapable {
152
+ proxy(request: never): Promise<ProxyResponseLike>;
153
+ }
154
+ /**
155
+ * Wrap a `ConnectionProvider`-shaped object so `proxy()` retries 429/5xx
156
+ * responses (honoring `Retry-After`) and transient network errors. The
157
+ * wrapper preserves the provider's full type, so call sites change from
158
+ * `provider.proxy({...})` to `withProxyRetry(provider).proxy({...})`.
159
+ *
160
+ * POST/PATCH requests pass through without retries unless
161
+ * `retryNonIdempotent` is set.
162
+ */
163
+ export declare function withProxyRetry<P extends ProxyCapable>(provider: P, options?: RetryOptions): P;
164
+ export {};
165
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../../src/http/retry.ts"],"names":[],"mappings":"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"}