@transferx/adapter-s3 0.1.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.
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @module R2Adapter
3
+ *
4
+ * Cloudflare R2 multipart upload adapter for TransferX.
5
+ *
6
+ * R2 is S3-compatible but requires three non-standard settings:
7
+ *
8
+ * 1. Custom endpoint: `https://<accountId>.r2.cloudflarestorage.com`
9
+ * 2. forcePathStyle: `true` — avoids virtual-hosted DNS issues with R2
10
+ * 3. Region: `"auto"` — R2 ignores region; AWS SDK requires a value
11
+ *
12
+ * All multipart upload semantics (5 MiB min part size, ETag tracking,
13
+ * ListParts-based resume, sorted CompleteMultipartUpload) are inherited
14
+ * from S3Adapter.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { R2Adapter } from '@transferx/adapter-s3';
19
+ *
20
+ * const adapter = new R2Adapter({
21
+ * accountId: process.env.CF_ACCOUNT_ID!,
22
+ * bucket: 'my-r2-bucket',
23
+ * credentials: {
24
+ * accessKeyId: process.env.R2_ACCESS_KEY_ID!,
25
+ * secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
26
+ * },
27
+ * });
28
+ * ```
29
+ */
30
+ import type { S3Client } from "@aws-sdk/client-s3";
31
+ import type { LogLevel } from "@transferx/core";
32
+ import { S3Adapter } from "./S3Adapter.js";
33
+ export interface R2AdapterOptions {
34
+ /**
35
+ * Cloudflare account ID.
36
+ * Found in the Cloudflare dashboard URL: `dash.cloudflare.com/<accountId>`.
37
+ */
38
+ accountId: string;
39
+ /** R2 bucket name. */
40
+ bucket: string;
41
+ /**
42
+ * R2 API credentials. Generate from Cloudflare Dashboard → R2 → Manage API tokens.
43
+ * These are **never** logged.
44
+ */
45
+ credentials: {
46
+ /** R2 Access Key ID. */
47
+ accessKeyId: string;
48
+ /** R2 Secret Access Key. */
49
+ secretAccessKey: string;
50
+ };
51
+ /**
52
+ * Per-request HTTP timeout in milliseconds.
53
+ * Default: `120_000` (2 minutes).
54
+ */
55
+ timeoutMs?: number;
56
+ /**
57
+ * Optional structured-log callback. Wire to the engine's EventBus for
58
+ * end-to-end observability.
59
+ */
60
+ onLog?: (level: LogLevel, message: string, context?: Record<string, unknown>) => void;
61
+ /**
62
+ * Inject a pre-built `S3Client` instance.
63
+ * **For testing only.**
64
+ */
65
+ client?: S3Client;
66
+ }
67
+ /**
68
+ * R2Adapter delegates all work to S3Adapter, injecting the R2-specific
69
+ * connection settings in the constructor. No R2-specific overrides are needed
70
+ * beyond those three config values.
71
+ */
72
+ export declare class R2Adapter extends S3Adapter {
73
+ constructor(opts: R2AdapterOptions);
74
+ }
75
+ //# sourceMappingURL=R2Adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"R2Adapter.d.ts","sourceRoot":"","sources":["../src/R2Adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAyB,MAAM,gBAAgB,CAAC;AAIlE,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,WAAW,EAAE;QACX,wBAAwB;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,4BAA4B;QAC5B,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,CACN,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC9B,IAAI,CAAC;IACV;;;OAGG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAID;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,SAAS;gBAC1B,IAAI,EAAE,gBAAgB;CAoBnC"}
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ /**
3
+ * @module R2Adapter
4
+ *
5
+ * Cloudflare R2 multipart upload adapter for TransferX.
6
+ *
7
+ * R2 is S3-compatible but requires three non-standard settings:
8
+ *
9
+ * 1. Custom endpoint: `https://<accountId>.r2.cloudflarestorage.com`
10
+ * 2. forcePathStyle: `true` — avoids virtual-hosted DNS issues with R2
11
+ * 3. Region: `"auto"` — R2 ignores region; AWS SDK requires a value
12
+ *
13
+ * All multipart upload semantics (5 MiB min part size, ETag tracking,
14
+ * ListParts-based resume, sorted CompleteMultipartUpload) are inherited
15
+ * from S3Adapter.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { R2Adapter } from '@transferx/adapter-s3';
20
+ *
21
+ * const adapter = new R2Adapter({
22
+ * accountId: process.env.CF_ACCOUNT_ID!,
23
+ * bucket: 'my-r2-bucket',
24
+ * credentials: {
25
+ * accessKeyId: process.env.R2_ACCESS_KEY_ID!,
26
+ * secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
27
+ * },
28
+ * });
29
+ * ```
30
+ */
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.R2Adapter = void 0;
33
+ const S3Adapter_js_1 = require("./S3Adapter.js");
34
+ // ── Adapter ───────────────────────────────────────────────────────────────────
35
+ /**
36
+ * R2Adapter delegates all work to S3Adapter, injecting the R2-specific
37
+ * connection settings in the constructor. No R2-specific overrides are needed
38
+ * beyond those three config values.
39
+ */
40
+ class R2Adapter extends S3Adapter_js_1.S3Adapter {
41
+ constructor(opts) {
42
+ const s3Opts = {
43
+ bucket: opts.bucket,
44
+ // R2 does not enforce region routing but the AWS SDK requires a value.
45
+ region: "auto",
46
+ // Construct the R2 account-specific endpoint.
47
+ endpoint: `https://${opts.accountId}.r2.cloudflarestorage.com`,
48
+ // Path-style addressing is required to avoid virtual-hosted DNS issues.
49
+ forcePathStyle: true,
50
+ credentials: {
51
+ accessKeyId: opts.credentials.accessKeyId,
52
+ secretAccessKey: opts.credentials.secretAccessKey,
53
+ },
54
+ // Forward optional fields only when present (exactOptionalPropertyTypes).
55
+ ...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),
56
+ ...(opts.onLog !== undefined ? { onLog: opts.onLog } : {}),
57
+ ...(opts.client !== undefined ? { client: opts.client } : {}),
58
+ };
59
+ super(s3Opts);
60
+ }
61
+ }
62
+ exports.R2Adapter = R2Adapter;
63
+ //# sourceMappingURL=R2Adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"R2Adapter.js","sourceRoot":"","sources":["../src/R2Adapter.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;;;AAIH,iDAAkE;AA2ClE,iFAAiF;AAEjF;;;;GAIG;AACH,MAAa,SAAU,SAAQ,wBAAS;IACtC,YAAY,IAAsB;QAChC,MAAM,MAAM,GAAqB;YAC/B,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,uEAAuE;YACvE,MAAM,EAAE,MAAM;YACd,8CAA8C;YAC9C,QAAQ,EAAE,WAAW,IAAI,CAAC,SAAS,2BAA2B;YAC9D,wEAAwE;YACxE,cAAc,EAAE,IAAI;YACpB,WAAW,EAAE;gBACX,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,WAAW;gBACzC,eAAe,EAAE,IAAI,CAAC,WAAW,CAAC,eAAe;aAClD;YACD,0EAA0E;YAC1E,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACtE,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9D,CAAC;QACF,KAAK,CAAC,MAAM,CAAC,CAAC;IAChB,CAAC;CACF;AArBD,8BAqBC"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * @module S3Adapter
3
+ *
4
+ * AWS S3 (and S3-compatible) multipart upload adapter for TransferX.
5
+ *
6
+ * Multipart upload lifecycle:
7
+ *
8
+ * 1. CreateMultipartUpload → UploadId (stored as session.providerSessionId)
9
+ * 2. UploadPart × N → ETag (stored as chunk.providerToken, verbatim
10
+ * with surrounding quotes, e.g. `"abc123"`)
11
+ * 3. CompleteMultipartUpload → finalises the object
12
+ * (abort path: AbortMultipartUpload)
13
+ *
14
+ * Resume support:
15
+ * getRemoteState() calls ListParts (with pagination) to discover already-
16
+ * uploaded parts, so the engine can skip re-uploading them on resume.
17
+ *
18
+ * Cloudflare R2 / S3-compatible stores:
19
+ * Pass `endpoint` + `forcePathStyle: true` to target any S3-compatible API.
20
+ * See R2Adapter for a purpose-built Cloudflare R2 wrapper.
21
+ *
22
+ * Security notes:
23
+ * - Credentials are passed to the AWS SDK and NEVER logged or stored in
24
+ * instance fields accessible outside this module.
25
+ * - The AWS SDK's built-in retry mechanism is disabled (maxAttempts: 1) so
26
+ * that TransferX's retry engine is the single source of truth.
27
+ *
28
+ * ETag contract:
29
+ * S3 returns ETags surrounded by double-quotes (e.g. `"d8e8fca2dc0f896f"`).
30
+ * These are stored verbatim as chunk.providerToken and passed back verbatim
31
+ * in CompleteMultipartUpload — the AWS SDK serialises them correctly.
32
+ *
33
+ * Minimum part size:
34
+ * AWS S3 requires each non-final part to be ≥ 5 MiB. TransferX's default
35
+ * chunkSize (10 MiB) satisfies this. The engine enforces chunkSize ≥ 5 MiB
36
+ * through config validation; this adapter does not re-validate.
37
+ */
38
+ import { S3Client } from "@aws-sdk/client-s3";
39
+ import type { ITransferAdapter, ChunkUploadResult, RemoteUploadState, LogLevel } from "@transferx/core";
40
+ import type { TransferSession, ChunkMeta } from "@transferx/core";
41
+ export interface S3AdapterOptions {
42
+ /** Target S3 bucket name. */
43
+ bucket: string;
44
+ /**
45
+ * AWS region (e.g. `"us-east-1"`).
46
+ * Ignored when `endpoint` is set and the provider does not require a region
47
+ * (e.g. Cloudflare R2 — use `"auto"` or omit).
48
+ * Defaults to `"us-east-1"`.
49
+ */
50
+ region?: string;
51
+ /**
52
+ * AWS credentials. These are passed directly to the AWS SDK and never logged.
53
+ * For Cloudflare R2: use the Access Key ID and Secret from an R2 API token.
54
+ */
55
+ credentials: {
56
+ /** AWS Access Key ID (or R2 Access Key). */
57
+ accessKeyId: string;
58
+ /** AWS Secret Access Key (or R2 Secret). */
59
+ secretAccessKey: string;
60
+ /** AWS STS session token for temporary credentials. Omit for R2. */
61
+ sessionToken?: string;
62
+ };
63
+ /**
64
+ * Custom endpoint URL for S3-compatible storage providers.
65
+ * Required for Cloudflare R2:
66
+ * `https://<account-id>.r2.cloudflarestorage.com`
67
+ */
68
+ endpoint?: string;
69
+ /**
70
+ * Use path-style URLs (`/bucket/key`) instead of virtual-hosted style
71
+ * (`bucket.s3.amazonaws.com/key`).
72
+ * Required for Cloudflare R2 and most non-AWS S3-compatible stores.
73
+ * Default: `false`.
74
+ */
75
+ forcePathStyle?: boolean;
76
+ /**
77
+ * Per-request HTTP timeout in milliseconds. Any request that stalls beyond
78
+ * this threshold is aborted and throws a retryable `network` error.
79
+ * Default: `120_000` (2 minutes).
80
+ */
81
+ timeoutMs?: number;
82
+ /**
83
+ * Optional structured-log callback for observability.
84
+ * `createS3Engine()` (if using the SDK façade) wires this automatically to
85
+ * the shared EventBus; for manual use, pass `bus.emit` directly.
86
+ *
87
+ * @example
88
+ * const adapter = new S3Adapter({
89
+ * ...,
90
+ * onLog: (level, msg, ctx) => bus.emit({ type: 'log', level, message: msg, ...ctx && { context: ctx } }),
91
+ * });
92
+ */
93
+ onLog?: (level: LogLevel, message: string, context?: Record<string, unknown>) => void;
94
+ /**
95
+ * Inject a pre-built `S3Client` instance.
96
+ * **For testing only** — pass a mock client to avoid real AWS calls.
97
+ * When provided, all other connection options (`region`, `endpoint`, etc.)
98
+ * are ignored.
99
+ */
100
+ client?: S3Client;
101
+ }
102
+ export declare class S3Adapter implements ITransferAdapter {
103
+ /** @internal exposed as protected so R2Adapter can subclass. */
104
+ protected readonly _client: S3Client;
105
+ private readonly _bucket;
106
+ private readonly _timeoutMs;
107
+ private readonly _onLog;
108
+ constructor(opts: S3AdapterOptions);
109
+ initTransfer(session: TransferSession): Promise<string>;
110
+ uploadChunk(session: TransferSession, chunk: ChunkMeta, data: Uint8Array, _sha256Hex: string): Promise<ChunkUploadResult>;
111
+ completeTransfer(session: TransferSession, chunks: ChunkMeta[]): Promise<void>;
112
+ abortTransfer(session: TransferSession): Promise<void>;
113
+ getRemoteState(session: TransferSession): Promise<RemoteUploadState>;
114
+ /**
115
+ * Send an S3 command with a per-request AbortController timeout and unified
116
+ * error mapping. The return type is cast to `T` — callers know the shape of
117
+ * each command's output.
118
+ *
119
+ * The `command` parameter is typed as `unknown` here because AWS SDK v3
120
+ * Command classes are generic and contravariant — assigning a specific
121
+ * `CreateMultipartUploadCommand` to `Command<ServiceInputTypes, ...>` fails
122
+ * TypeScript's structural check due to middlewareStack overloads. Each call
123
+ * site already supplies the correctly-typed Command, so the cast is safe.
124
+ */
125
+ private _send;
126
+ /**
127
+ * Normalise any error thrown by the AWS SDK into a typed TransferError so the
128
+ * engine can make correct retry/abort decisions.
129
+ */
130
+ private _mapError;
131
+ }
132
+ //# sourceMappingURL=S3Adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"S3Adapter.d.ts","sourceRoot":"","sources":["../src/S3Adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EACL,QAAQ,EAMT,MAAM,oBAAoB,CAAC;AAS5B,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,QAAQ,EACT,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIlE,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,EAAE;QACX,4CAA4C;QAC5C,WAAW,EAAE,MAAM,CAAC;QACpB,4CAA4C;QAC5C,eAAe,EAAE,MAAM,CAAC;QACxB,oEAAoE;QACpE,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,CACN,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC9B,IAAI,CAAC;IACV;;;;;OAKG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAYD,qBAAa,SAAU,YAAW,gBAAgB;IAChD,gEAAgE;IAChE,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;gBAEvC,IAAI,EAAE,gBAAgB;IAuC5B,YAAY,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC;IAyBvD,WAAW,CACf,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,UAAU,EAKhB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,iBAAiB,CAAC;IAuCvB,gBAAgB,CACpB,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,SAAS,EAAE,GAClB,OAAO,CAAC,IAAI,CAAC;IAgCV,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IActD,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAiD1E;;;;;;;;;;OAUG;YACW,KAAK;IAqBnB;;;OAGG;IACH,OAAO,CAAC,SAAS;CAkDlB"}
@@ -0,0 +1,271 @@
1
+ "use strict";
2
+ /**
3
+ * @module S3Adapter
4
+ *
5
+ * AWS S3 (and S3-compatible) multipart upload adapter for TransferX.
6
+ *
7
+ * Multipart upload lifecycle:
8
+ *
9
+ * 1. CreateMultipartUpload → UploadId (stored as session.providerSessionId)
10
+ * 2. UploadPart × N → ETag (stored as chunk.providerToken, verbatim
11
+ * with surrounding quotes, e.g. `"abc123"`)
12
+ * 3. CompleteMultipartUpload → finalises the object
13
+ * (abort path: AbortMultipartUpload)
14
+ *
15
+ * Resume support:
16
+ * getRemoteState() calls ListParts (with pagination) to discover already-
17
+ * uploaded parts, so the engine can skip re-uploading them on resume.
18
+ *
19
+ * Cloudflare R2 / S3-compatible stores:
20
+ * Pass `endpoint` + `forcePathStyle: true` to target any S3-compatible API.
21
+ * See R2Adapter for a purpose-built Cloudflare R2 wrapper.
22
+ *
23
+ * Security notes:
24
+ * - Credentials are passed to the AWS SDK and NEVER logged or stored in
25
+ * instance fields accessible outside this module.
26
+ * - The AWS SDK's built-in retry mechanism is disabled (maxAttempts: 1) so
27
+ * that TransferX's retry engine is the single source of truth.
28
+ *
29
+ * ETag contract:
30
+ * S3 returns ETags surrounded by double-quotes (e.g. `"d8e8fca2dc0f896f"`).
31
+ * These are stored verbatim as chunk.providerToken and passed back verbatim
32
+ * in CompleteMultipartUpload — the AWS SDK serialises them correctly.
33
+ *
34
+ * Minimum part size:
35
+ * AWS S3 requires each non-final part to be ≥ 5 MiB. TransferX's default
36
+ * chunkSize (10 MiB) satisfies this. The engine enforces chunkSize ≥ 5 MiB
37
+ * through config validation; this adapter does not re-validate.
38
+ */
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.S3Adapter = void 0;
41
+ const client_s3_1 = require("@aws-sdk/client-s3");
42
+ const core_1 = require("@transferx/core");
43
+ // ── Adapter ───────────────────────────────────────────────────────────────────
44
+ class S3Adapter {
45
+ /** @internal exposed as protected so R2Adapter can subclass. */
46
+ _client;
47
+ _bucket;
48
+ _timeoutMs;
49
+ _onLog;
50
+ constructor(opts) {
51
+ this._bucket = opts.bucket;
52
+ this._timeoutMs = opts.timeoutMs ?? 120_000;
53
+ this._onLog = opts.onLog;
54
+ if (opts.client) {
55
+ // Injected client — used in tests; skip constructing a real one.
56
+ this._client = opts.client;
57
+ }
58
+ else {
59
+ // Disable SDK-level retries — TransferX owns the retry loop.
60
+ const clientConfig = {
61
+ maxAttempts: 1,
62
+ region: opts.region ?? "us-east-1",
63
+ credentials: {
64
+ accessKeyId: opts.credentials.accessKeyId,
65
+ secretAccessKey: opts.credentials.secretAccessKey,
66
+ // Conditionally include sessionToken — exactOptionalPropertyTypes safe.
67
+ ...(opts.credentials.sessionToken !== undefined
68
+ ? { sessionToken: opts.credentials.sessionToken }
69
+ : {}),
70
+ },
71
+ };
72
+ // Only set endpoint / forcePathStyle when explicitly provided. Setting
73
+ // either to undefined would trigger AWS SDK default-resolution paths
74
+ // that conflict with some S3-compatible providers.
75
+ if (opts.endpoint !== undefined) {
76
+ clientConfig.endpoint = opts.endpoint;
77
+ }
78
+ if (opts.forcePathStyle === true) {
79
+ clientConfig.forcePathStyle = true;
80
+ }
81
+ this._client = new client_s3_1.S3Client(clientConfig);
82
+ }
83
+ }
84
+ // ── ITransferAdapter ───────────────────────────────────────────────────────
85
+ async initTransfer(session) {
86
+ const cmd = new client_s3_1.CreateMultipartUploadCommand({
87
+ Bucket: this._bucket,
88
+ Key: session.targetKey,
89
+ ContentType: session.file.mimeType || "application/octet-stream",
90
+ });
91
+ const output = await this._send(cmd, "initTransfer");
92
+ const uploadId = output.UploadId;
93
+ if (!uploadId) {
94
+ throw new core_1.TransferError("S3 CreateMultipartUpload did not return an UploadId", "serverError");
95
+ }
96
+ this._onLog?.("info", "[S3] Multipart upload initiated", {
97
+ sessionId: session.id,
98
+ uploadId,
99
+ });
100
+ return uploadId;
101
+ }
102
+ async uploadChunk(session, chunk, data,
103
+ // sha256Hex intentionally unused: the engine handles local integrity checks.
104
+ // S3 computes its own ETag (MD5) for server-side integrity verification.
105
+ // Attaching ChecksumSHA256 would require coordinating ChecksumAlgorithm
106
+ // on both CreateMultipartUpload and UploadPart — a future enhancement.
107
+ _sha256Hex) {
108
+ if (!session.providerSessionId) {
109
+ throw new core_1.TransferError("providerSessionId is missing — initTransfer must be called first", "fatal");
110
+ }
111
+ // S3 part numbers are 1-based (range: 1–10,000).
112
+ const partNumber = chunk.index + 1;
113
+ const cmd = new client_s3_1.UploadPartCommand({
114
+ Bucket: this._bucket,
115
+ Key: session.targetKey,
116
+ UploadId: session.providerSessionId,
117
+ PartNumber: partNumber,
118
+ Body: data,
119
+ });
120
+ const output = await this._send(cmd, "uploadChunk", chunk.index);
121
+ const etag = output.ETag;
122
+ if (!etag) {
123
+ throw new core_1.TransferError(`S3 UploadPart for part ${partNumber} returned no ETag`, "serverError");
124
+ }
125
+ // Store ETag verbatim — AWS returns it with surrounding double-quotes
126
+ // (e.g. `"d8e8fca2dc0f896fd7cb4cb0031ba249"`). CompleteMultipartUpload
127
+ // requires ETags in the same format, so we must NOT strip the quotes here.
128
+ return { providerToken: etag };
129
+ }
130
+ async completeTransfer(session, chunks) {
131
+ if (!session.providerSessionId) {
132
+ throw new core_1.TransferError("providerSessionId missing in completeTransfer", "fatal");
133
+ }
134
+ // S3 requires Parts sorted ascending by PartNumber; do so defensively even
135
+ // if the engine already delivers them in order.
136
+ const sortedParts = [...chunks]
137
+ .sort((a, b) => a.index - b.index)
138
+ .map((c) => ({
139
+ PartNumber: c.index + 1,
140
+ ETag: c.providerToken ?? "",
141
+ }));
142
+ const cmd = new client_s3_1.CompleteMultipartUploadCommand({
143
+ Bucket: this._bucket,
144
+ Key: session.targetKey,
145
+ UploadId: session.providerSessionId,
146
+ MultipartUpload: { Parts: sortedParts },
147
+ });
148
+ await this._send(cmd, "completeTransfer");
149
+ this._onLog?.("info", "[S3] Multipart upload completed", {
150
+ sessionId: session.id,
151
+ partCount: sortedParts.length,
152
+ });
153
+ }
154
+ async abortTransfer(session) {
155
+ if (!session.providerSessionId)
156
+ return;
157
+ try {
158
+ const cmd = new client_s3_1.AbortMultipartUploadCommand({
159
+ Bucket: this._bucket,
160
+ Key: session.targetKey,
161
+ UploadId: session.providerSessionId,
162
+ });
163
+ await this._send(cmd, "abortTransfer");
164
+ }
165
+ catch {
166
+ // Best-effort — swallow errors so cancel/fail never blocks the engine.
167
+ }
168
+ }
169
+ async getRemoteState(session) {
170
+ if (!session.providerSessionId) {
171
+ return { uploadedParts: [] };
172
+ }
173
+ const uploadedParts = [];
174
+ // S3 paginates ListParts via PartNumberMarker / NextPartNumberMarker.
175
+ // IsTruncated === true means there are more pages.
176
+ let partNumberMarker;
177
+ do {
178
+ const cmdInput = {
179
+ Bucket: this._bucket,
180
+ Key: session.targetKey,
181
+ UploadId: session.providerSessionId,
182
+ MaxParts: 1000,
183
+ };
184
+ if (partNumberMarker !== undefined) {
185
+ // ListPartsCommandInput.PartNumberMarker is typed as string in the
186
+ // AWS SDK even though S3's API uses an integer marker value.
187
+ cmdInput.PartNumberMarker = String(partNumberMarker);
188
+ }
189
+ const output = await this._send(new client_s3_1.ListPartsCommand(cmdInput), "getRemoteState");
190
+ for (const part of output.Parts ?? []) {
191
+ if (part.PartNumber != null && part.ETag) {
192
+ uploadedParts.push({
193
+ partNumber: part.PartNumber,
194
+ providerToken: part.ETag,
195
+ });
196
+ }
197
+ }
198
+ partNumberMarker =
199
+ output.IsTruncated === true && output.NextPartNumberMarker != null
200
+ ? output.NextPartNumberMarker
201
+ : undefined;
202
+ } while (partNumberMarker !== undefined);
203
+ return { uploadedParts };
204
+ }
205
+ // ── Private helpers ────────────────────────────────────────────────────────
206
+ /**
207
+ * Send an S3 command with a per-request AbortController timeout and unified
208
+ * error mapping. The return type is cast to `T` — callers know the shape of
209
+ * each command's output.
210
+ *
211
+ * The `command` parameter is typed as `unknown` here because AWS SDK v3
212
+ * Command classes are generic and contravariant — assigning a specific
213
+ * `CreateMultipartUploadCommand` to `Command<ServiceInputTypes, ...>` fails
214
+ * TypeScript's structural check due to middlewareStack overloads. Each call
215
+ * site already supplies the correctly-typed Command, so the cast is safe.
216
+ */
217
+ async _send(command, op, chunkIndex) {
218
+ const controller = new AbortController();
219
+ const timer = setTimeout(() => controller.abort(), this._timeoutMs);
220
+ try {
221
+ return (await this._client.send(command, { abortSignal: controller.signal }));
222
+ }
223
+ catch (err) {
224
+ throw this._mapError(err, op, chunkIndex);
225
+ }
226
+ finally {
227
+ clearTimeout(timer);
228
+ }
229
+ }
230
+ /**
231
+ * Normalise any error thrown by the AWS SDK into a typed TransferError so the
232
+ * engine can make correct retry/abort decisions.
233
+ */
234
+ _mapError(err, op, chunkIndex) {
235
+ // AbortController fired → request timed out.
236
+ if (err instanceof Error && err.name === "AbortError") {
237
+ return (0, core_1.networkError)(`[S3] ${op} timed out after ${this._timeoutMs}ms`, err, chunkIndex);
238
+ }
239
+ // AWS SDK v3 service exceptions carry $metadata.httpStatusCode.
240
+ // Duck-type to avoid importing the (internal) base exception class.
241
+ const sdkErr = err;
242
+ const status = sdkErr.$metadata?.httpStatusCode ?? 0;
243
+ const msg = err instanceof Error ? err.message : String(err);
244
+ const retryAfterHeader = sdkErr.$response?.headers?.["retry-after"];
245
+ if (status === 401 || status === 403) {
246
+ this._onLog?.("warn", `[S3] ${op} authorisation error (HTTP ${status})`, {
247
+ chunkIndex,
248
+ });
249
+ return (0, core_1.authError)(`S3 ${status}: ${msg}`);
250
+ }
251
+ if (status === 429) {
252
+ const retryAfterMs = retryAfterHeader
253
+ ? parseInt(retryAfterHeader, 10) * 1000
254
+ : undefined;
255
+ return (0, core_1.rateLimitError)(retryAfterMs);
256
+ }
257
+ if (status >= 500) {
258
+ return (0, core_1.serverError)(status, msg);
259
+ }
260
+ if (status >= 400) {
261
+ return (0, core_1.clientError)(status, msg);
262
+ }
263
+ // No HTTP status — treat as a transient network failure.
264
+ if (err instanceof Error) {
265
+ return (0, core_1.networkError)(`[S3] ${op} request failed: ${err.message}`, err, chunkIndex);
266
+ }
267
+ return new core_1.TransferError(String(err), "unknown", err, chunkIndex);
268
+ }
269
+ }
270
+ exports.S3Adapter = S3Adapter;
271
+ //# sourceMappingURL=S3Adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"S3Adapter.js","sourceRoot":"","sources":["../src/S3Adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { S3Adapter } from "./S3Adapter.js";
2
+ export type { S3AdapterOptions } from "./S3Adapter.js";
3
+ export { R2Adapter } from "./R2Adapter.js";
4
+ export type { R2AdapterOptions } from "./R2Adapter.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.R2Adapter = exports.S3Adapter = void 0;
4
+ var S3Adapter_js_1 = require("./S3Adapter.js");
5
+ Object.defineProperty(exports, "S3Adapter", { enumerable: true, get: function () { return S3Adapter_js_1.S3Adapter; } });
6
+ var R2Adapter_js_1 = require("./R2Adapter.js");
7
+ Object.defineProperty(exports, "R2Adapter", { enumerable: true, get: function () { return R2Adapter_js_1.R2Adapter; } });
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,+CAA2C;AAAlC,yGAAA,SAAS,OAAA;AAGlB,+CAA2C;AAAlC,yGAAA,SAAS,OAAA"}
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@transferx/adapter-s3",
3
+ "version": "0.1.0",
4
+ "description": "TransferX adapters for AWS S3 and Cloudflare R2 multipart uploads",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc -p tsconfig.json",
12
+ "typecheck": "tsc --noEmit"
13
+ },
14
+ "dependencies": {
15
+ "@aws-sdk/client-s3": "^3.0.0",
16
+ "@transferx/core": "0.1.0"
17
+ }
18
+ }