@velajs/storage 0.3.0 → 0.3.2
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/CHANGELOG.md +6 -1
- package/dist/body-BMUcBeQ0.js +93 -0
- package/dist/body-BMUcBeQ0.js.map +1 -0
- package/dist/client/index.d.ts +49 -46
- package/dist/client/index.js +207 -228
- package/dist/client/index.js.map +1 -0
- package/dist/drivers/memory/index.d.ts +17 -13
- package/dist/drivers/memory/index.js +190 -197
- package/dist/drivers/memory/index.js.map +1 -0
- package/dist/drivers/r2/index.d.ts +7 -4
- package/dist/drivers/r2/index.js +162 -160
- package/dist/drivers/r2/index.js.map +1 -0
- package/dist/drivers/r2-http/index.d.ts +23 -19
- package/dist/drivers/r2-http/index.js +52 -45
- package/dist/drivers/r2-http/index.js.map +1 -0
- package/dist/drivers/s3/index.d.ts +79 -5
- package/dist/drivers/s3/index.js +2 -3
- package/dist/index.d.ts +271 -17
- package/dist/index.js +1078 -16
- package/dist/index.js.map +1 -0
- package/dist/middleware/index.d.ts +177 -15
- package/dist/middleware/index.js +553 -8
- package/dist/middleware/index.js.map +1 -0
- package/dist/protocol.types-C5PPBD12.d.ts +80 -0
- package/dist/r2.types-Bw4Ft83t.d.ts +71 -0
- package/dist/retry-DXtO6R9D.js +119 -0
- package/dist/retry-DXtO6R9D.js.map +1 -0
- package/dist/s3-DrE9NrnD.js +557 -0
- package/dist/s3-DrE9NrnD.js.map +1 -0
- package/dist/storage.error-DVCOkJhc.js +49 -0
- package/dist/storage.error-DVCOkJhc.js.map +1 -0
- package/dist/storage.types-BJN7Yp1J.d.ts +288 -0
- package/dist/storagesdk/index.d.ts +73 -69
- package/dist/storagesdk/index.js +117 -130
- package/dist/storagesdk/index.js.map +1 -0
- package/dist/stored-file-2Yl07eRc.js +105 -0
- package/dist/stored-file-2Yl07eRc.js.map +1 -0
- package/dist/testing/index.d.ts +12 -8
- package/dist/testing/index.js +36 -53
- package/dist/testing/index.js.map +1 -0
- package/package.json +62 -48
- package/dist/base64.d.ts +0 -8
- package/dist/base64.js +0 -25
- package/dist/decorators/inject-storage.decorator.d.ts +0 -17
- package/dist/decorators/inject-storage.decorator.js +0 -21
- package/dist/drivers/r2/r2.types.d.ts +0 -67
- package/dist/drivers/r2/r2.types.js +0 -5
- package/dist/drivers/s3/s3-client.d.ts +0 -33
- package/dist/drivers/s3/s3-client.js +0 -154
- package/dist/drivers/s3/s3.driver.d.ts +0 -4
- package/dist/drivers/s3/s3.driver.js +0 -404
- package/dist/drivers/s3/s3.types.d.ts +0 -28
- package/dist/drivers/s3/s3.types.js +0 -1
- package/dist/drivers/s3/xml.d.ts +0 -6
- package/dist/drivers/s3/xml.js +0 -52
- package/dist/http/authorizer.types.d.ts +0 -59
- package/dist/http/authorizer.types.js +0 -1
- package/dist/http/http-helpers.d.ts +0 -22
- package/dist/http/http-helpers.js +0 -80
- package/dist/http/protocol.types.d.ts +0 -76
- package/dist/http/protocol.types.js +0 -1
- package/dist/internal/body.d.ts +0 -18
- package/dist/internal/body.js +0 -90
- package/dist/internal/retry.d.ts +0 -41
- package/dist/internal/retry.js +0 -127
- package/dist/internal/stored-file.d.ts +0 -27
- package/dist/internal/stored-file.js +0 -114
- package/dist/middleware/cache.d.ts +0 -50
- package/dist/middleware/cache.js +0 -101
- package/dist/middleware/compose.d.ts +0 -12
- package/dist/middleware/compose.js +0 -11
- package/dist/middleware/compression.d.ts +0 -16
- package/dist/middleware/compression.js +0 -101
- package/dist/middleware/encryption.d.ts +0 -14
- package/dist/middleware/encryption.js +0 -134
- package/dist/middleware/failover.d.ts +0 -18
- package/dist/middleware/failover.js +0 -52
- package/dist/middleware/retry.d.ts +0 -14
- package/dist/middleware/retry.js +0 -47
- package/dist/middleware/versioning.d.ts +0 -36
- package/dist/middleware/versioning.js +0 -89
- package/dist/middleware/wrap.d.ts +0 -11
- package/dist/middleware/wrap.js +0 -46
- package/dist/object-key.d.ts +0 -17
- package/dist/object-key.js +0 -42
- package/dist/storage.controller.d.ts +0 -21
- package/dist/storage.controller.js +0 -380
- package/dist/storage.error.d.ts +0 -28
- package/dist/storage.error.js +0 -46
- package/dist/storage.facade.d.ts +0 -47
- package/dist/storage.facade.js +0 -443
- package/dist/storage.module.d.ts +0 -46
- package/dist/storage.module.js +0 -113
- package/dist/storage.service.d.ts +0 -42
- package/dist/storage.service.js +0 -86
- package/dist/storage.tokens.d.ts +0 -13
- package/dist/storage.tokens.js +0 -26
- package/dist/storage.types.d.ts +0 -245
- package/dist/storage.types.js +0 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { A as UploadResult, b as SignedUpload, j as UploadedPart } from "./storage.types-BJN7Yp1J.js";
|
|
2
|
+
//#region src/http/protocol.types.d.ts
|
|
3
|
+
interface SignUploadRequest {
|
|
4
|
+
key: string;
|
|
5
|
+
contentType?: string;
|
|
6
|
+
size?: number;
|
|
7
|
+
metadata?: Record<string, string>;
|
|
8
|
+
expiresIn?: number;
|
|
9
|
+
}
|
|
10
|
+
interface MultipartCreateRequest {
|
|
11
|
+
key: string;
|
|
12
|
+
contentType?: string;
|
|
13
|
+
metadata?: Record<string, string>;
|
|
14
|
+
partSize?: number;
|
|
15
|
+
}
|
|
16
|
+
interface SignPartRequest {
|
|
17
|
+
key: string;
|
|
18
|
+
uploadId: string;
|
|
19
|
+
partNumber: number;
|
|
20
|
+
}
|
|
21
|
+
interface MultipartCompleteRequest {
|
|
22
|
+
key: string;
|
|
23
|
+
uploadId: string;
|
|
24
|
+
parts: UploadedPart[];
|
|
25
|
+
}
|
|
26
|
+
interface MultipartAbortRequest {
|
|
27
|
+
key: string;
|
|
28
|
+
uploadId: string;
|
|
29
|
+
}
|
|
30
|
+
interface DeleteRequest {
|
|
31
|
+
keys: string[];
|
|
32
|
+
}
|
|
33
|
+
interface SignUploadResponse {
|
|
34
|
+
key: string;
|
|
35
|
+
upload: SignedUpload;
|
|
36
|
+
}
|
|
37
|
+
interface MultipartCreateResponse {
|
|
38
|
+
key: string;
|
|
39
|
+
uploadId: string;
|
|
40
|
+
partSize: number;
|
|
41
|
+
}
|
|
42
|
+
interface SignPartResponse {
|
|
43
|
+
partNumber: number;
|
|
44
|
+
method: 'PUT';
|
|
45
|
+
url: string;
|
|
46
|
+
headers?: Record<string, string>;
|
|
47
|
+
}
|
|
48
|
+
type MultipartCompleteResponse = UploadResult;
|
|
49
|
+
interface OkResponse {
|
|
50
|
+
ok: true;
|
|
51
|
+
}
|
|
52
|
+
/** JSON-safe {@link StoredFile} projection (no body accessors). */
|
|
53
|
+
interface StoredFileMeta {
|
|
54
|
+
key: string;
|
|
55
|
+
name: string;
|
|
56
|
+
size: number;
|
|
57
|
+
type: string;
|
|
58
|
+
lastModified?: number;
|
|
59
|
+
etag?: string;
|
|
60
|
+
metadata?: Record<string, string>;
|
|
61
|
+
}
|
|
62
|
+
interface ListResponse {
|
|
63
|
+
items: StoredFileMeta[];
|
|
64
|
+
prefixes?: string[];
|
|
65
|
+
cursor?: string;
|
|
66
|
+
}
|
|
67
|
+
interface SignDownloadResponse {
|
|
68
|
+
url: string;
|
|
69
|
+
expiresAt: number;
|
|
70
|
+
}
|
|
71
|
+
type StorageWireErrorCode = 'forbidden' | 'invalid_key' | 'invalid_request' | 'not_found' | 'conflict' | 'rate_limited' | 'capability_unsupported' | 'upstream_error';
|
|
72
|
+
interface StorageErrorBody {
|
|
73
|
+
error: {
|
|
74
|
+
code: StorageWireErrorCode;
|
|
75
|
+
message: string;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
export { MultipartCompleteResponse as a, OkResponse as c, SignPartResponse as d, SignUploadRequest as f, StoredFileMeta as g, StorageWireErrorCode as h, MultipartCompleteRequest as i, SignDownloadResponse as l, StorageErrorBody as m, ListResponse as n, MultipartCreateRequest as o, SignUploadResponse as p, MultipartAbortRequest as r, MultipartCreateResponse as s, DeleteRequest as t, SignPartRequest as u };
|
|
80
|
+
//# sourceMappingURL=protocol.types-C5PPBD12.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//#region src/drivers/r2/r2.types.d.ts
|
|
2
|
+
type R2PutValue = ReadableStream | ArrayBuffer | ArrayBufferView | string | Blob | null;
|
|
3
|
+
interface R2HttpMetadataLike {
|
|
4
|
+
contentType?: string;
|
|
5
|
+
cacheControl?: string;
|
|
6
|
+
}
|
|
7
|
+
interface R2PutOptionsLike {
|
|
8
|
+
httpMetadata?: R2HttpMetadataLike;
|
|
9
|
+
customMetadata?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
interface R2GetOptionsLike {
|
|
12
|
+
range?: {
|
|
13
|
+
offset?: number;
|
|
14
|
+
length?: number;
|
|
15
|
+
suffix?: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
interface R2ListOptionsLike {
|
|
19
|
+
prefix?: string;
|
|
20
|
+
cursor?: string;
|
|
21
|
+
limit?: number;
|
|
22
|
+
delimiter?: string;
|
|
23
|
+
}
|
|
24
|
+
interface R2ObjectLike {
|
|
25
|
+
key: string;
|
|
26
|
+
size: number;
|
|
27
|
+
etag: string;
|
|
28
|
+
uploaded: Date;
|
|
29
|
+
httpMetadata?: R2HttpMetadataLike;
|
|
30
|
+
customMetadata?: Record<string, string>;
|
|
31
|
+
}
|
|
32
|
+
interface R2ObjectBodyLike extends R2ObjectLike {
|
|
33
|
+
body: ReadableStream;
|
|
34
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
35
|
+
}
|
|
36
|
+
interface R2ListResultLike {
|
|
37
|
+
objects: R2ObjectLike[];
|
|
38
|
+
truncated: boolean;
|
|
39
|
+
cursor?: string;
|
|
40
|
+
delimitedPrefixes: string[];
|
|
41
|
+
}
|
|
42
|
+
interface R2UploadedPartLike {
|
|
43
|
+
partNumber: number;
|
|
44
|
+
etag: string;
|
|
45
|
+
}
|
|
46
|
+
interface R2MultipartUploadLike {
|
|
47
|
+
readonly uploadId: string;
|
|
48
|
+
uploadPart(partNumber: number, value: R2PutValue): Promise<R2UploadedPartLike>;
|
|
49
|
+
complete(parts: R2UploadedPartLike[]): Promise<R2ObjectLike>;
|
|
50
|
+
abort(): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
interface R2BucketLike {
|
|
53
|
+
put(key: string, value: R2PutValue, options?: R2PutOptionsLike): Promise<R2ObjectLike | null>;
|
|
54
|
+
get(key: string, options?: R2GetOptionsLike): Promise<R2ObjectBodyLike | null>;
|
|
55
|
+
head(key: string): Promise<R2ObjectLike | null>;
|
|
56
|
+
delete(keys: string | string[]): Promise<void>;
|
|
57
|
+
list(options?: R2ListOptionsLike): Promise<R2ListResultLike>;
|
|
58
|
+
createMultipartUpload(key: string, options?: R2PutOptionsLike): Promise<R2MultipartUploadLike>;
|
|
59
|
+
resumeMultipartUpload?(key: string, uploadId: string): R2MultipartUploadLike;
|
|
60
|
+
}
|
|
61
|
+
interface R2DriverOptions {
|
|
62
|
+
/** The Workers `R2Bucket` binding (e.g. `env.MY_BUCKET` or `R2Service.bucket`). */
|
|
63
|
+
bucket: R2BucketLike;
|
|
64
|
+
/** Public origin (r2.dev subdomain / custom domain) so `url()` can return a link. */
|
|
65
|
+
publicBaseUrl?: string;
|
|
66
|
+
/** Friendly driver name (defaults to `r2`). */
|
|
67
|
+
name?: string;
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { R2ObjectLike as a, R2ObjectBodyLike as i, R2DriverOptions as n, R2MultipartUploadLike as r, R2BucketLike as t };
|
|
71
|
+
//# sourceMappingURL=r2.types-Bw4Ft83t.d.ts.map
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { n as isAbort, t as StorageError } from "./storage.error-DVCOkJhc.js";
|
|
2
|
+
//#region src/internal/retry.ts
|
|
3
|
+
/**
|
|
4
|
+
* Combine multiple abort signals into one. The result aborts as soon as any
|
|
5
|
+
* input aborts, adopting that input's reason. Listeners are cleaned up on
|
|
6
|
+
* first abort. `undefined` inputs are ignored.
|
|
7
|
+
*/
|
|
8
|
+
function anySignal(signals) {
|
|
9
|
+
const controller = new AbortController();
|
|
10
|
+
const live = signals.filter((s) => s != null);
|
|
11
|
+
const onAbort = (reason) => {
|
|
12
|
+
cleanup();
|
|
13
|
+
if (!controller.signal.aborted) controller.abort(reason);
|
|
14
|
+
};
|
|
15
|
+
const cleanup = () => {
|
|
16
|
+
for (const s of live) s.removeEventListener("abort", handlers.get(s));
|
|
17
|
+
};
|
|
18
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
19
|
+
for (const s of live) {
|
|
20
|
+
if (s.aborted) {
|
|
21
|
+
controller.abort(s.reason);
|
|
22
|
+
return controller.signal;
|
|
23
|
+
}
|
|
24
|
+
const h = () => onAbort(s.reason);
|
|
25
|
+
handlers.set(s, h);
|
|
26
|
+
s.addEventListener("abort", h, { once: true });
|
|
27
|
+
}
|
|
28
|
+
return controller.signal;
|
|
29
|
+
}
|
|
30
|
+
/** A controller that aborts after `ms`; call `clear()` to cancel the timer. */
|
|
31
|
+
function timeoutController(ms) {
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
const timer = setTimeout(() => {
|
|
34
|
+
controller.abort(new StorageError("Timeout", `operation timed out after ${ms}ms`));
|
|
35
|
+
}, ms);
|
|
36
|
+
return {
|
|
37
|
+
controller,
|
|
38
|
+
clear: () => clearTimeout(timer)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Resolve after `ms`, or reject with `Aborted` if `signal` fires first. */
|
|
42
|
+
function sleep(ms, signal) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
if (signal?.aborted) {
|
|
45
|
+
reject(new StorageError("Aborted", "operation aborted"));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const timer = setTimeout(() => {
|
|
49
|
+
signal?.removeEventListener("abort", onAbort);
|
|
50
|
+
resolve();
|
|
51
|
+
}, ms);
|
|
52
|
+
const onAbort = () => {
|
|
53
|
+
clearTimeout(timer);
|
|
54
|
+
reject(new StorageError("Aborted", "operation aborted"));
|
|
55
|
+
};
|
|
56
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const DEFAULT_BASE_MS = 100;
|
|
60
|
+
const DEFAULT_CAP_MS = 2e3;
|
|
61
|
+
function defaultBackoff({ attempt }) {
|
|
62
|
+
return Math.min(DEFAULT_CAP_MS, DEFAULT_BASE_MS * 2 ** (attempt - 1)) + Math.floor(Math.random() * DEFAULT_BASE_MS);
|
|
63
|
+
}
|
|
64
|
+
function normalizeRetry(retries) {
|
|
65
|
+
if (retries == null) return {
|
|
66
|
+
max: 0,
|
|
67
|
+
backoff: defaultBackoff
|
|
68
|
+
};
|
|
69
|
+
if (typeof retries === "number") return {
|
|
70
|
+
max: Math.max(0, retries),
|
|
71
|
+
backoff: defaultBackoff
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
max: Math.max(0, retries.max),
|
|
75
|
+
backoff: retries.backoff ?? defaultBackoff
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Run `fn` with per-attempt timeout, caller-abort propagation, and
|
|
80
|
+
* retryable-error backoff. `fn` receives the combined per-attempt signal.
|
|
81
|
+
*
|
|
82
|
+
* - Caller-abort (`opts.signal`) → `Aborted`, never retried.
|
|
83
|
+
* - Per-attempt timeout → `Timeout` (retryable by default, within budget).
|
|
84
|
+
* - Only errors with `error.retryable` are retried, up to `retries.max`.
|
|
85
|
+
*/
|
|
86
|
+
async function runWithRetry(fn, opts, onRetry) {
|
|
87
|
+
const { max, backoff } = normalizeRetry(opts.retries);
|
|
88
|
+
const useTimeout = typeof opts.timeout === "number" && opts.timeout > 0;
|
|
89
|
+
let attempt = 0;
|
|
90
|
+
for (;;) {
|
|
91
|
+
if (opts.signal?.aborted) throw new StorageError("Aborted", "operation aborted");
|
|
92
|
+
const to = useTimeout ? timeoutController(opts.timeout) : void 0;
|
|
93
|
+
const signal = to ? anySignal([opts.signal, to.controller.signal]) : opts.signal;
|
|
94
|
+
try {
|
|
95
|
+
return await fn(signal);
|
|
96
|
+
} catch (e) {
|
|
97
|
+
if (opts.signal?.aborted) throw new StorageError("Aborted", "operation aborted", { cause: e });
|
|
98
|
+
const error = to?.controller.signal.aborted && isAbort(e) ? new StorageError("Timeout", `operation timed out after ${opts.timeout}ms`, { cause: e }) : StorageError.wrap(e);
|
|
99
|
+
if (attempt >= max || !error.retryable) throw error;
|
|
100
|
+
attempt += 1;
|
|
101
|
+
const delayMs = Math.max(0, backoff({
|
|
102
|
+
attempt,
|
|
103
|
+
error
|
|
104
|
+
}));
|
|
105
|
+
onRetry?.({
|
|
106
|
+
attempt,
|
|
107
|
+
delayMs,
|
|
108
|
+
error
|
|
109
|
+
});
|
|
110
|
+
await sleep(delayMs, opts.signal);
|
|
111
|
+
} finally {
|
|
112
|
+
to?.clear();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { runWithRetry as n, normalizeRetry as t };
|
|
118
|
+
|
|
119
|
+
//# sourceMappingURL=retry-DXtO6R9D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-DXtO6R9D.js","names":[],"sources":["../src/internal/retry.ts"],"sourcesContent":["import { StorageError, isAbort } from '../storage.error';\nimport type { RetryOptions } from '../storage.types';\n\n// Retry/timeout/abort plumbing. Deliberately does NOT use `AbortSignal.any`\n// (only added in Node 20.3 — `engines.node >= 20` allows 20.0–20.2) nor\n// `AbortSignal.timeout`; both are re-implemented from `AbortController` +\n// `setTimeout` so the package runs on every target from 20.0 up.\n\n/**\n * Combine multiple abort signals into one. The result aborts as soon as any\n * input aborts, adopting that input's reason. Listeners are cleaned up on\n * first abort. `undefined` inputs are ignored.\n */\nexport function anySignal(signals: Array<AbortSignal | undefined>): AbortSignal {\n const controller = new AbortController();\n const live = signals.filter((s): s is AbortSignal => s != null);\n\n const onAbort = (reason: unknown) => {\n cleanup();\n if (!controller.signal.aborted) controller.abort(reason);\n };\n const cleanup = () => {\n for (const s of live) s.removeEventListener('abort', handlers.get(s)!);\n };\n const handlers = new Map<AbortSignal, () => void>();\n\n for (const s of live) {\n if (s.aborted) {\n controller.abort(s.reason);\n return controller.signal;\n }\n const h = () => onAbort(s.reason);\n handlers.set(s, h);\n s.addEventListener('abort', h, { once: true });\n }\n return controller.signal;\n}\n\n/** A controller that aborts after `ms`; call `clear()` to cancel the timer. */\nexport function timeoutController(ms: number): { controller: AbortController; clear: () => void } {\n const controller = new AbortController();\n const timer = setTimeout(() => {\n controller.abort(new StorageError('Timeout', `operation timed out after ${ms}ms`));\n }, ms);\n return { controller, clear: () => clearTimeout(timer) };\n}\n\n/** Resolve after `ms`, or reject with `Aborted` if `signal` fires first. */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new StorageError('Aborted', 'operation aborted'));\n return;\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(new StorageError('Aborted', 'operation aborted'));\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\nexport interface NormalizedRetry {\n max: number;\n backoff: (ctx: { attempt: number; error: StorageError }) => number;\n}\n\nconst DEFAULT_BASE_MS = 100;\nconst DEFAULT_CAP_MS = 2000;\n\nfunction defaultBackoff({ attempt }: { attempt: number }): number {\n const expo = Math.min(DEFAULT_CAP_MS, DEFAULT_BASE_MS * 2 ** (attempt - 1));\n return expo + Math.floor(Math.random() * DEFAULT_BASE_MS); // full-ish jitter\n}\n\nexport function normalizeRetry(retries: RetryOptions | undefined): NormalizedRetry {\n if (retries == null) return { max: 0, backoff: defaultBackoff };\n if (typeof retries === 'number') return { max: Math.max(0, retries), backoff: defaultBackoff };\n return { max: Math.max(0, retries.max), backoff: retries.backoff ?? defaultBackoff };\n}\n\nexport interface RunOptions {\n retries?: RetryOptions;\n timeout?: number;\n signal?: AbortSignal;\n}\n\n/**\n * Run `fn` with per-attempt timeout, caller-abort propagation, and\n * retryable-error backoff. `fn` receives the combined per-attempt signal.\n *\n * - Caller-abort (`opts.signal`) → `Aborted`, never retried.\n * - Per-attempt timeout → `Timeout` (retryable by default, within budget).\n * - Only errors with `error.retryable` are retried, up to `retries.max`.\n */\nexport async function runWithRetry<T>(\n fn: (signal: AbortSignal | undefined) => Promise<T>,\n opts: RunOptions,\n onRetry?: (info: { attempt: number; delayMs: number; error: StorageError }) => void,\n): Promise<T> {\n const { max, backoff } = normalizeRetry(opts.retries);\n const useTimeout = typeof opts.timeout === 'number' && opts.timeout > 0;\n\n let attempt = 0;\n for (;;) {\n if (opts.signal?.aborted) throw new StorageError('Aborted', 'operation aborted');\n\n const to = useTimeout ? timeoutController(opts.timeout!) : undefined;\n const signal = to ? anySignal([opts.signal, to.controller.signal]) : opts.signal;\n\n try {\n return await fn(signal);\n } catch (e) {\n // Hard caller cancellation: never retried.\n if (opts.signal?.aborted)\n throw new StorageError('Aborted', 'operation aborted', { cause: e });\n\n const error =\n to?.controller.signal.aborted && isAbort(e)\n ? new StorageError('Timeout', `operation timed out after ${opts.timeout}ms`, { cause: e })\n : StorageError.wrap(e);\n\n if (attempt >= max || !error.retryable) throw error;\n\n attempt += 1;\n const delayMs = Math.max(0, backoff({ attempt, error }));\n onRetry?.({ attempt, delayMs, error });\n await sleep(delayMs, opts.signal);\n } finally {\n to?.clear();\n }\n }\n}\n"],"mappings":"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"}
|