@procedures/s3 0.0.0-stage → 0.2.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.
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +886 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/module.d.ts +22 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +173 -0
- package/dist/module.js.map +1 -0
- package/dist/procedures/s3/delete.d.ts +26 -0
- package/dist/procedures/s3/delete.d.ts.map +1 -0
- package/dist/procedures/s3/delete.js +40 -0
- package/dist/procedures/s3/delete.js.map +1 -0
- package/dist/procedures/s3/download.d.ts +36 -0
- package/dist/procedures/s3/download.d.ts.map +1 -0
- package/dist/procedures/s3/download.js +143 -0
- package/dist/procedures/s3/download.js.map +1 -0
- package/dist/procedures/s3/index.d.ts +13 -0
- package/dist/procedures/s3/index.d.ts.map +1 -0
- package/dist/procedures/s3/index.js +13 -0
- package/dist/procedures/s3/index.js.map +1 -0
- package/dist/procedures/s3/list-all.d.ts +24 -0
- package/dist/procedures/s3/list-all.d.ts.map +1 -0
- package/dist/procedures/s3/list-all.js +57 -0
- package/dist/procedures/s3/list-all.js.map +1 -0
- package/dist/procedures/s3/list.d.ts +28 -0
- package/dist/procedures/s3/list.d.ts.map +1 -0
- package/dist/procedures/s3/list.js +56 -0
- package/dist/procedures/s3/list.js.map +1 -0
- package/dist/procedures/s3/multipart-abort.d.ts +21 -0
- package/dist/procedures/s3/multipart-abort.d.ts.map +1 -0
- package/dist/procedures/s3/multipart-abort.js +33 -0
- package/dist/procedures/s3/multipart-abort.js.map +1 -0
- package/dist/procedures/s3/multipart-complete.d.ts +26 -0
- package/dist/procedures/s3/multipart-complete.d.ts.map +1 -0
- package/dist/procedures/s3/multipart-complete.js +51 -0
- package/dist/procedures/s3/multipart-complete.js.map +1 -0
- package/dist/procedures/s3/multipart-init.d.ts +24 -0
- package/dist/procedures/s3/multipart-init.d.ts.map +1 -0
- package/dist/procedures/s3/multipart-init.js +42 -0
- package/dist/procedures/s3/multipart-init.js.map +1 -0
- package/dist/procedures/s3/multipart-upload.d.ts +24 -0
- package/dist/procedures/s3/multipart-upload.d.ts.map +1 -0
- package/dist/procedures/s3/multipart-upload.js +44 -0
- package/dist/procedures/s3/multipart-upload.js.map +1 -0
- package/dist/procedures/s3/upload.d.ts +30 -0
- package/dist/procedures/s3/upload.d.ts.map +1 -0
- package/dist/procedures/s3/upload.js +50 -0
- package/dist/procedures/s3/upload.js.map +1 -0
- package/dist/register.d.ts +7 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +9 -0
- package/dist/register.js.map +1 -0
- package/dist/s3-client.d.ts +38 -0
- package/dist/s3-client.d.ts.map +1 -0
- package/dist/s3-client.js +83 -0
- package/dist/s3-client.js.map +1 -0
- package/dist/types.d.ts +198 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +197 -0
- package/dist/types.js.map +1 -0
- package/package.json +80 -3
- package/src/index.ts +72 -0
- package/src/module.ts +234 -0
- package/src/procedures/s3/delete.ts +45 -0
- package/src/procedures/s3/download.ts +155 -0
- package/src/procedures/s3/index.ts +13 -0
- package/src/procedures/s3/list-all.ts +63 -0
- package/src/procedures/s3/list.ts +63 -0
- package/src/procedures/s3/multipart-abort.ts +40 -0
- package/src/procedures/s3/multipart-complete.ts +58 -0
- package/src/procedures/s3/multipart-init.ts +50 -0
- package/src/procedures/s3/multipart-upload.ts +53 -0
- package/src/procedures/s3/upload.ts +56 -0
- package/src/register.ts +11 -0
- package/src/s3-client.ts +94 -0
- package/src/types.ts +425 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* s3.multipart.init procedure
|
|
3
|
+
*
|
|
4
|
+
* Initialize a multipart upload for large files.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { CreateMultipartUploadCommand } from "@aws-sdk/client-s3";
|
|
8
|
+
import { getS3Client } from "../../s3-client.js";
|
|
9
|
+
import type { S3MultipartInitInput, S3MultipartInitOutput } from "../../types.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Initialize multipart upload
|
|
13
|
+
*
|
|
14
|
+
* Use this for files larger than 5MB. Workflow:
|
|
15
|
+
* 1. s3.multipart.init - get uploadId
|
|
16
|
+
* 2. s3.multipart.upload - upload each part (5MB min, except last)
|
|
17
|
+
* 3. s3.multipart.complete - finalize with part ETags
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const init = await client.call(["s3", "multipart", "init"], {
|
|
21
|
+
* bucket: "my-bucket",
|
|
22
|
+
* key: "large-file.tar.gz",
|
|
23
|
+
* contentType: "application/gzip",
|
|
24
|
+
* });
|
|
25
|
+
* console.log(init.uploadId); // Use for subsequent part uploads
|
|
26
|
+
*/
|
|
27
|
+
export async function s3MultipartInit(
|
|
28
|
+
input: S3MultipartInitInput
|
|
29
|
+
): Promise<S3MultipartInitOutput> {
|
|
30
|
+
const client = getS3Client();
|
|
31
|
+
|
|
32
|
+
const command = new CreateMultipartUploadCommand({
|
|
33
|
+
Bucket: input.bucket,
|
|
34
|
+
Key: input.key,
|
|
35
|
+
ContentType: input.contentType,
|
|
36
|
+
Metadata: input.metadata,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const response = await client.send(command);
|
|
40
|
+
|
|
41
|
+
if (!response.UploadId) {
|
|
42
|
+
throw new Error("Failed to initialize multipart upload: no UploadId returned");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
uploadId: response.UploadId,
|
|
47
|
+
bucket: input.bucket,
|
|
48
|
+
key: input.key,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* s3.multipart.upload procedure
|
|
3
|
+
*
|
|
4
|
+
* Upload a part in a multipart upload.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UploadPartCommand } from "@aws-sdk/client-s3";
|
|
8
|
+
import { getS3Client } from "../../s3-client.js";
|
|
9
|
+
import type { S3MultipartUploadInput, S3MultipartUploadOutput } from "../../types.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Upload a single part in multipart upload
|
|
13
|
+
*
|
|
14
|
+
* Parts must be at least 5MB (except the last part).
|
|
15
|
+
* Part numbers range from 1 to 10000.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* const part1 = await client.call(["s3", "multipart", "upload"], {
|
|
19
|
+
* bucket: "my-bucket",
|
|
20
|
+
* key: "large-file.tar.gz",
|
|
21
|
+
* uploadId: "abc123",
|
|
22
|
+
* partNumber: 1,
|
|
23
|
+
* body: base64EncodedChunk1,
|
|
24
|
+
* });
|
|
25
|
+
* // Store part1.etag for complete call
|
|
26
|
+
*/
|
|
27
|
+
export async function s3MultipartUpload(
|
|
28
|
+
input: S3MultipartUploadInput
|
|
29
|
+
): Promise<S3MultipartUploadOutput> {
|
|
30
|
+
const client = getS3Client();
|
|
31
|
+
|
|
32
|
+
// Body is always base64 encoded for multipart
|
|
33
|
+
const body = Buffer.from(input.body, "base64");
|
|
34
|
+
|
|
35
|
+
const command = new UploadPartCommand({
|
|
36
|
+
Bucket: input.bucket,
|
|
37
|
+
Key: input.key,
|
|
38
|
+
UploadId: input.uploadId,
|
|
39
|
+
PartNumber: input.partNumber,
|
|
40
|
+
Body: body,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const response = await client.send(command);
|
|
44
|
+
|
|
45
|
+
if (!response.ETag) {
|
|
46
|
+
throw new Error(`Failed to upload part ${input.partNumber}: no ETag returned`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
etag: response.ETag.replace(/"/g, ""),
|
|
51
|
+
partNumber: input.partNumber,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* s3.upload procedure
|
|
3
|
+
*
|
|
4
|
+
* Upload content to S3 bucket.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { PutObjectCommand } from "@aws-sdk/client-s3";
|
|
8
|
+
import { getS3Client } from "../../s3-client.js";
|
|
9
|
+
import type { S3UploadInput, S3UploadOutput } from "../../types.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Upload content to S3
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Upload text content
|
|
16
|
+
* await client.call(["s3", "upload"], {
|
|
17
|
+
* bucket: "my-bucket",
|
|
18
|
+
* key: "path/to/file.json",
|
|
19
|
+
* body: JSON.stringify({ data: "value" }),
|
|
20
|
+
* contentType: "application/json",
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // Upload base64 binary
|
|
25
|
+
* await client.call(["s3", "upload"], {
|
|
26
|
+
* bucket: "my-bucket",
|
|
27
|
+
* key: "path/to/image.png",
|
|
28
|
+
* body: base64EncodedData,
|
|
29
|
+
* base64: true,
|
|
30
|
+
* contentType: "image/png",
|
|
31
|
+
* });
|
|
32
|
+
*/
|
|
33
|
+
export async function s3Upload(input: S3UploadInput): Promise<S3UploadOutput> {
|
|
34
|
+
const client = getS3Client();
|
|
35
|
+
|
|
36
|
+
// Convert body to Buffer if base64
|
|
37
|
+
const body = input.base64
|
|
38
|
+
? Buffer.from(input.body, "base64")
|
|
39
|
+
: input.body;
|
|
40
|
+
|
|
41
|
+
const command = new PutObjectCommand({
|
|
42
|
+
Bucket: input.bucket,
|
|
43
|
+
Key: input.key,
|
|
44
|
+
Body: body,
|
|
45
|
+
ContentType: input.contentType,
|
|
46
|
+
Metadata: input.metadata,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const response = await client.send(command);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
etag: response.ETag?.replace(/"/g, "") || "",
|
|
53
|
+
versionId: response.VersionId,
|
|
54
|
+
location: `s3://${input.bucket}/${input.key}`,
|
|
55
|
+
};
|
|
56
|
+
}
|
package/src/register.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Importing this file registers the procedures of @procedures/s3, and of the modules that it
|
|
3
|
+
* requires, in the global registry: `import "@procedures/s3/register"`. The package root and the
|
|
4
|
+
* `./module` entry register nothing (ARCHITECTURE-PROPOSALS-2026-10, P9).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { registerS3Procedures } from "./module.js";
|
|
8
|
+
|
|
9
|
+
export * from "./module.js";
|
|
10
|
+
|
|
11
|
+
registerS3Procedures();
|
package/src/s3-client.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S3 Client Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates an S3 client. Static credentials are read from environment variables
|
|
5
|
+
* when present; otherwise the AWS SDK's default credential provider chain
|
|
6
|
+
* (SSO, IAM roles/IMDS, shared config profiles, env, etc.) resolves them.
|
|
7
|
+
* NEVER hardcode credentials - always use environment variables or an IAM role.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { S3Client } from "@aws-sdk/client-s3";
|
|
11
|
+
import type { S3Config } from "./types.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get S3 configuration from environment variables.
|
|
15
|
+
*
|
|
16
|
+
* Static credentials are optional: when `AWS_ACCESS_KEY_ID` /
|
|
17
|
+
* `AWS_SECRET_ACCESS_KEY` are absent, the returned config carries no
|
|
18
|
+
* credentials and the AWS SDK default provider chain is used instead.
|
|
19
|
+
* `AWS_SESSION_TOKEN` is included when present (STS/SSO temporary creds).
|
|
20
|
+
*/
|
|
21
|
+
export function getS3Config(): S3Config {
|
|
22
|
+
const accessKeyId = process.env["AWS_ACCESS_KEY_ID"];
|
|
23
|
+
const secretAccessKey = process.env["AWS_SECRET_ACCESS_KEY"];
|
|
24
|
+
const sessionToken = process.env["AWS_SESSION_TOKEN"];
|
|
25
|
+
const region = process.env["AWS_REGION"] || "us-east-1";
|
|
26
|
+
const endpoint = process.env["S3_ENDPOINT"];
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
region,
|
|
30
|
+
endpoint,
|
|
31
|
+
accessKeyId,
|
|
32
|
+
secretAccessKey,
|
|
33
|
+
sessionToken,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create an S3 client.
|
|
39
|
+
*
|
|
40
|
+
* Explicit credentials are passed ONLY when both `AWS_ACCESS_KEY_ID` and
|
|
41
|
+
* `AWS_SECRET_ACCESS_KEY` are set (including `AWS_SESSION_TOKEN` when present).
|
|
42
|
+
* Otherwise `credentials` is omitted so the AWS SDK default credential
|
|
43
|
+
* provider chain resolves SSO / IAM-role / profile credentials.
|
|
44
|
+
*/
|
|
45
|
+
export function createS3Client(): S3Client {
|
|
46
|
+
const config = getS3Config();
|
|
47
|
+
|
|
48
|
+
const clientConfig: ConstructorParameters<typeof S3Client>[0] = {
|
|
49
|
+
region: config.region,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Only supply static credentials when the env keys actually exist; otherwise
|
|
53
|
+
// let the SDK resolve credentials from its default chain (SSO/IMDS/profile).
|
|
54
|
+
if (config.accessKeyId && config.secretAccessKey) {
|
|
55
|
+
clientConfig.credentials = {
|
|
56
|
+
accessKeyId: config.accessKeyId,
|
|
57
|
+
secretAccessKey: config.secretAccessKey,
|
|
58
|
+
// Session token is required for STS/SSO temporary credentials.
|
|
59
|
+
...(config.sessionToken ? { sessionToken: config.sessionToken } : {}),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Custom endpoint for MinIO, LocalStack, etc.
|
|
64
|
+
if (config.endpoint) {
|
|
65
|
+
clientConfig.endpoint = config.endpoint;
|
|
66
|
+
clientConfig.forcePathStyle = true; // Required for non-AWS S3
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return new S3Client(clientConfig);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Singleton client instance (lazy initialized)
|
|
73
|
+
let s3ClientInstance: S3Client | null = null;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get singleton S3 client instance
|
|
77
|
+
* Creates client on first call, reuses on subsequent calls
|
|
78
|
+
*/
|
|
79
|
+
export function getS3Client(): S3Client {
|
|
80
|
+
if (!s3ClientInstance) {
|
|
81
|
+
s3ClientInstance = createS3Client();
|
|
82
|
+
}
|
|
83
|
+
return s3ClientInstance;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Reset the singleton client (useful for testing or credential refresh)
|
|
88
|
+
*/
|
|
89
|
+
export function resetS3Client(): void {
|
|
90
|
+
if (s3ClientInstance) {
|
|
91
|
+
s3ClientInstance.destroy();
|
|
92
|
+
s3ClientInstance = null;
|
|
93
|
+
}
|
|
94
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for client-s3 procedures
|
|
3
|
+
*
|
|
4
|
+
* AWS S3 operations: upload, download, list, delete, multipart upload
|
|
5
|
+
* Credentials are loaded from environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// S3 Client Configuration (from environment)
|
|
12
|
+
// =============================================================================
|
|
13
|
+
|
|
14
|
+
export interface S3Config {
|
|
15
|
+
/** AWS region (from AWS_REGION env var, defaults to us-east-1) */
|
|
16
|
+
region: string;
|
|
17
|
+
/** Optional custom endpoint (for MinIO, LocalStack, etc.) */
|
|
18
|
+
endpoint?: string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Access key ID (from AWS_ACCESS_KEY_ID env var).
|
|
21
|
+
* Optional: when absent, the AWS SDK default credential provider chain
|
|
22
|
+
* (SSO, IAM roles/IMDS, shared config profiles, etc.) is used instead.
|
|
23
|
+
*/
|
|
24
|
+
accessKeyId?: string | undefined;
|
|
25
|
+
/** Secret access key (from AWS_SECRET_ACCESS_KEY env var). Optional; see accessKeyId. */
|
|
26
|
+
secretAccessKey?: string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Session token (from AWS_SESSION_TOKEN env var) for STS/SSO temporary
|
|
29
|
+
* credentials. Present only alongside a temporary access key/secret pair.
|
|
30
|
+
*/
|
|
31
|
+
sessionToken?: string | undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// =============================================================================
|
|
35
|
+
// s3.upload Types
|
|
36
|
+
// =============================================================================
|
|
37
|
+
|
|
38
|
+
export const S3UploadInputSchema: z.ZodObject<{
|
|
39
|
+
bucket: z.ZodString;
|
|
40
|
+
key: z.ZodString;
|
|
41
|
+
body: z.ZodString;
|
|
42
|
+
contentType: z.ZodOptional<z.ZodString>;
|
|
43
|
+
base64: z.ZodOptional<z.ZodBoolean>;
|
|
44
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
45
|
+
}> = z.object({
|
|
46
|
+
/** S3 bucket name */
|
|
47
|
+
bucket: z.string().describe("S3 bucket name"),
|
|
48
|
+
/** Object key (path in bucket) */
|
|
49
|
+
key: z.string().describe("Object key (path in bucket)"),
|
|
50
|
+
/** Content to upload - string or base64 encoded binary */
|
|
51
|
+
body: z.string().describe("Content to upload - string or base64 encoded binary"),
|
|
52
|
+
/** Content type (e.g., "application/json", "text/plain") */
|
|
53
|
+
contentType: z.string().optional().describe("Content type (e.g., \"application/json\", \"text/plain\")"),
|
|
54
|
+
/** If true, body is base64 encoded binary data */
|
|
55
|
+
base64: z.boolean().optional().describe("If true, body is base64 encoded binary data"),
|
|
56
|
+
/** Optional metadata key-value pairs */
|
|
57
|
+
metadata: z.record(z.string(), z.string()).optional().describe("Optional metadata key-value pairs"),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export type S3UploadInput = z.infer<typeof S3UploadInputSchema>;
|
|
61
|
+
|
|
62
|
+
export interface S3UploadOutput {
|
|
63
|
+
/** ETag of uploaded object */
|
|
64
|
+
etag: string;
|
|
65
|
+
/** Version ID if bucket versioning is enabled */
|
|
66
|
+
versionId?: string | undefined;
|
|
67
|
+
/** Full S3 URI */
|
|
68
|
+
location: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// =============================================================================
|
|
72
|
+
// s3.download Types
|
|
73
|
+
// =============================================================================
|
|
74
|
+
|
|
75
|
+
export const S3DownloadInputSchema: z.ZodObject<{
|
|
76
|
+
bucket: z.ZodString;
|
|
77
|
+
key: z.ZodString;
|
|
78
|
+
encoding: z.ZodOptional<z.ZodString>;
|
|
79
|
+
versionId: z.ZodOptional<z.ZodString>;
|
|
80
|
+
destPath: z.ZodOptional<z.ZodString>;
|
|
81
|
+
maxBytes: z.ZodOptional<z.ZodNumber>;
|
|
82
|
+
}> = z.object({
|
|
83
|
+
/** S3 bucket name */
|
|
84
|
+
bucket: z.string().describe("S3 bucket name"),
|
|
85
|
+
/** Object key (path in bucket) */
|
|
86
|
+
key: z.string().describe("Object key (path in bucket)"),
|
|
87
|
+
/** Encoding for text content (utf8, ascii, etc.) - omit for base64 binary */
|
|
88
|
+
encoding: z.string().optional().describe("Encoding for text content (utf8, ascii, etc.) - omit for base64 binary"),
|
|
89
|
+
/** Specific version to download */
|
|
90
|
+
versionId: z.string().optional().describe("Specific version to download"),
|
|
91
|
+
/** Write the object to this file instead of returning it (streams to disk: no size limit, no base64) */
|
|
92
|
+
destPath: z.string().optional().describe("Write the object to this file instead of returning it (streams to disk: no size limit, no base64)"),
|
|
93
|
+
/** Largest object to load into memory when destPath is not set (default: 64 MiB) */
|
|
94
|
+
maxBytes: z.number().int().positive().optional().describe("Largest object to load into memory when destPath is not set (default: 64 MiB)"),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export type S3DownloadInput = z.infer<typeof S3DownloadInputSchema>;
|
|
98
|
+
|
|
99
|
+
export interface S3DownloadOutput {
|
|
100
|
+
/** Object content (string if encoding specified, base64 otherwise); empty when destPath was given */
|
|
101
|
+
body: string;
|
|
102
|
+
/** The file the object was written to, when destPath was given */
|
|
103
|
+
path?: string | undefined;
|
|
104
|
+
/** Content length in bytes */
|
|
105
|
+
contentLength: number;
|
|
106
|
+
/** Content type */
|
|
107
|
+
contentType?: string | undefined;
|
|
108
|
+
/** ETag of object */
|
|
109
|
+
etag?: string | undefined;
|
|
110
|
+
/** Last modified date as ISO string */
|
|
111
|
+
lastModified?: string | undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// =============================================================================
|
|
115
|
+
// s3.list Types
|
|
116
|
+
// =============================================================================
|
|
117
|
+
|
|
118
|
+
export const S3ListInputSchema: z.ZodObject<{
|
|
119
|
+
bucket: z.ZodString;
|
|
120
|
+
prefix: z.ZodOptional<z.ZodString>;
|
|
121
|
+
maxKeys: z.ZodOptional<z.ZodNumber>;
|
|
122
|
+
continuationToken: z.ZodOptional<z.ZodString>;
|
|
123
|
+
delimiter: z.ZodOptional<z.ZodString>;
|
|
124
|
+
}> = z.object({
|
|
125
|
+
/** S3 bucket name */
|
|
126
|
+
bucket: z.string().describe("S3 bucket name"),
|
|
127
|
+
/** Filter objects by prefix */
|
|
128
|
+
prefix: z.string().optional().describe("Filter objects by prefix"),
|
|
129
|
+
/** Maximum number of keys to return (default 1000) */
|
|
130
|
+
maxKeys: z.number().optional().describe("Maximum number of keys to return (default 1000)"),
|
|
131
|
+
/** Continuation token for pagination */
|
|
132
|
+
continuationToken: z.string().optional().describe("Continuation token for pagination"),
|
|
133
|
+
/** Delimiter for hierarchical listing (e.g., "/") */
|
|
134
|
+
delimiter: z.string().optional().describe("Delimiter for hierarchical listing (e.g., \"/\")"),
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
export type S3ListInput = z.infer<typeof S3ListInputSchema>;
|
|
138
|
+
|
|
139
|
+
export interface S3ListObject {
|
|
140
|
+
/** Object key */
|
|
141
|
+
key: string;
|
|
142
|
+
/** Size in bytes */
|
|
143
|
+
size: number;
|
|
144
|
+
/** Last modified date as ISO string */
|
|
145
|
+
lastModified: string;
|
|
146
|
+
/** ETag */
|
|
147
|
+
etag: string;
|
|
148
|
+
/** Storage class */
|
|
149
|
+
storageClass?: string | undefined;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface S3ListOutput {
|
|
153
|
+
/** List of objects */
|
|
154
|
+
contents: S3ListObject[];
|
|
155
|
+
/** Common prefixes (when using delimiter) */
|
|
156
|
+
commonPrefixes: string[];
|
|
157
|
+
/** Whether there are more results */
|
|
158
|
+
isTruncated: boolean;
|
|
159
|
+
/** Token for next page of results */
|
|
160
|
+
nextContinuationToken?: string | undefined;
|
|
161
|
+
/** Number of keys returned */
|
|
162
|
+
keyCount: number;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// =============================================================================
|
|
166
|
+
// s3.listAll Types
|
|
167
|
+
// =============================================================================
|
|
168
|
+
|
|
169
|
+
export const S3ListAllInputSchema: z.ZodObject<{
|
|
170
|
+
bucket: z.ZodString;
|
|
171
|
+
prefix: z.ZodOptional<z.ZodString>;
|
|
172
|
+
maxKeys: z.ZodOptional<z.ZodNumber>;
|
|
173
|
+
delimiter: z.ZodOptional<z.ZodString>;
|
|
174
|
+
}> = z.object({
|
|
175
|
+
/** S3 bucket name */
|
|
176
|
+
bucket: z.string().describe("S3 bucket name"),
|
|
177
|
+
/** Filter objects by prefix */
|
|
178
|
+
prefix: z.string().optional().describe("Filter objects by prefix"),
|
|
179
|
+
/** Page size for each underlying request (default/max 1000); paging is automatic */
|
|
180
|
+
maxKeys: z.number().optional().describe("Page size for each underlying request (default/max 1000); paging is automatic"),
|
|
181
|
+
/** Delimiter for hierarchical listing (e.g., "/") */
|
|
182
|
+
delimiter: z.string().optional().describe("Delimiter for hierarchical listing (e.g., \"/\")"),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
export type S3ListAllInput = z.infer<typeof S3ListAllInputSchema>;
|
|
186
|
+
|
|
187
|
+
export interface S3ListAllOutput {
|
|
188
|
+
/** All objects across every page (not capped at 1000) */
|
|
189
|
+
contents: S3ListObject[];
|
|
190
|
+
/** Deduplicated common prefixes across all pages (when using delimiter) */
|
|
191
|
+
commonPrefixes: string[];
|
|
192
|
+
/** Total number of keys returned */
|
|
193
|
+
keyCount: number;
|
|
194
|
+
/** Number of underlying ListObjectsV2 requests made */
|
|
195
|
+
pageCount: number;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// =============================================================================
|
|
199
|
+
// s3.delete Types
|
|
200
|
+
// =============================================================================
|
|
201
|
+
|
|
202
|
+
export const S3DeleteInputSchema: z.ZodObject<{
|
|
203
|
+
bucket: z.ZodString;
|
|
204
|
+
key: z.ZodString;
|
|
205
|
+
versionId: z.ZodOptional<z.ZodString>;
|
|
206
|
+
}> = z.object({
|
|
207
|
+
/** S3 bucket name */
|
|
208
|
+
bucket: z.string().describe("S3 bucket name"),
|
|
209
|
+
/** Object key (path in bucket) */
|
|
210
|
+
key: z.string().describe("Object key (path in bucket)"),
|
|
211
|
+
/** Specific version to delete */
|
|
212
|
+
versionId: z.string().optional().describe("Specific version to delete"),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
export type S3DeleteInput = z.infer<typeof S3DeleteInputSchema>;
|
|
216
|
+
|
|
217
|
+
export interface S3DeleteOutput {
|
|
218
|
+
/** Whether deletion was successful */
|
|
219
|
+
deleted: boolean;
|
|
220
|
+
/** Delete marker if versioning enabled */
|
|
221
|
+
deleteMarker?: boolean | undefined;
|
|
222
|
+
/** Version ID of delete marker */
|
|
223
|
+
versionId?: string | undefined;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// =============================================================================
|
|
227
|
+
// s3.multipart.init Types
|
|
228
|
+
// =============================================================================
|
|
229
|
+
|
|
230
|
+
export const S3MultipartInitInputSchema: z.ZodObject<{
|
|
231
|
+
bucket: z.ZodString;
|
|
232
|
+
key: z.ZodString;
|
|
233
|
+
contentType: z.ZodOptional<z.ZodString>;
|
|
234
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
235
|
+
}> = z.object({
|
|
236
|
+
/** S3 bucket name */
|
|
237
|
+
bucket: z.string().describe("S3 bucket name"),
|
|
238
|
+
/** Object key (path in bucket) */
|
|
239
|
+
key: z.string().describe("Object key (path in bucket)"),
|
|
240
|
+
/** Content type */
|
|
241
|
+
contentType: z.string().optional().describe("Content type"),
|
|
242
|
+
/** Optional metadata */
|
|
243
|
+
metadata: z.record(z.string(), z.string()).optional().describe("Optional metadata"),
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
export type S3MultipartInitInput = z.infer<typeof S3MultipartInitInputSchema>;
|
|
247
|
+
|
|
248
|
+
export interface S3MultipartInitOutput {
|
|
249
|
+
/** Upload ID for subsequent part uploads */
|
|
250
|
+
uploadId: string;
|
|
251
|
+
/** Bucket name */
|
|
252
|
+
bucket: string;
|
|
253
|
+
/** Object key */
|
|
254
|
+
key: string;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// =============================================================================
|
|
258
|
+
// s3.multipart.upload Types
|
|
259
|
+
// =============================================================================
|
|
260
|
+
|
|
261
|
+
export const S3MultipartUploadInputSchema: z.ZodObject<{
|
|
262
|
+
bucket: z.ZodString;
|
|
263
|
+
key: z.ZodString;
|
|
264
|
+
uploadId: z.ZodString;
|
|
265
|
+
partNumber: z.ZodNumber;
|
|
266
|
+
body: z.ZodString;
|
|
267
|
+
}> = z.object({
|
|
268
|
+
/** S3 bucket name */
|
|
269
|
+
bucket: z.string().describe("S3 bucket name"),
|
|
270
|
+
/** Object key */
|
|
271
|
+
key: z.string().describe("Object key"),
|
|
272
|
+
/** Upload ID from init */
|
|
273
|
+
uploadId: z.string().describe("Upload ID from init"),
|
|
274
|
+
/** Part number (1-10000) */
|
|
275
|
+
partNumber: z.number().min(1).max(10000).describe("Part number (1-10000)"),
|
|
276
|
+
/** Part content - base64 encoded */
|
|
277
|
+
body: z.string().describe("Part content - base64 encoded"),
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
export type S3MultipartUploadInput = z.infer<typeof S3MultipartUploadInputSchema>;
|
|
281
|
+
|
|
282
|
+
export interface S3MultipartUploadOutput {
|
|
283
|
+
/** ETag of uploaded part */
|
|
284
|
+
etag: string;
|
|
285
|
+
/** Part number */
|
|
286
|
+
partNumber: number;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// =============================================================================
|
|
290
|
+
// s3.multipart.complete Types
|
|
291
|
+
// =============================================================================
|
|
292
|
+
|
|
293
|
+
export const S3MultipartCompleteInputSchema: z.ZodObject<{
|
|
294
|
+
bucket: z.ZodString;
|
|
295
|
+
key: z.ZodString;
|
|
296
|
+
uploadId: z.ZodString;
|
|
297
|
+
parts: z.ZodArray<z.ZodObject<{
|
|
298
|
+
etag: z.ZodString;
|
|
299
|
+
partNumber: z.ZodNumber;
|
|
300
|
+
}>>;
|
|
301
|
+
}> = z.object({
|
|
302
|
+
/** S3 bucket name */
|
|
303
|
+
bucket: z.string().describe("S3 bucket name"),
|
|
304
|
+
/** Object key */
|
|
305
|
+
key: z.string().describe("Object key"),
|
|
306
|
+
/** Upload ID from init */
|
|
307
|
+
uploadId: z.string().describe("Upload ID from init"),
|
|
308
|
+
/** Parts to complete (etag and partNumber) */
|
|
309
|
+
parts: z.array(
|
|
310
|
+
z.object({
|
|
311
|
+
etag: z.string(),
|
|
312
|
+
partNumber: z.number(),
|
|
313
|
+
})
|
|
314
|
+
).describe("Parts to complete (etag and partNumber)"),
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
export type S3MultipartCompleteInput = z.infer<typeof S3MultipartCompleteInputSchema>;
|
|
318
|
+
|
|
319
|
+
export interface S3MultipartCompleteOutput {
|
|
320
|
+
/** Location URL of completed object */
|
|
321
|
+
location: string;
|
|
322
|
+
/** Bucket name */
|
|
323
|
+
bucket: string;
|
|
324
|
+
/** Object key */
|
|
325
|
+
key: string;
|
|
326
|
+
/** ETag of completed object */
|
|
327
|
+
etag: string;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// =============================================================================
|
|
331
|
+
// s3.multipart.abort Types
|
|
332
|
+
// =============================================================================
|
|
333
|
+
|
|
334
|
+
export const S3MultipartAbortInputSchema: z.ZodObject<{
|
|
335
|
+
bucket: z.ZodString;
|
|
336
|
+
key: z.ZodString;
|
|
337
|
+
uploadId: z.ZodString;
|
|
338
|
+
}> = z.object({
|
|
339
|
+
/** S3 bucket name */
|
|
340
|
+
bucket: z.string().describe("S3 bucket name"),
|
|
341
|
+
/** Object key */
|
|
342
|
+
key: z.string().describe("Object key"),
|
|
343
|
+
/** Upload ID to abort */
|
|
344
|
+
uploadId: z.string().describe("Upload ID to abort"),
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
export type S3MultipartAbortInput = z.infer<typeof S3MultipartAbortInputSchema>;
|
|
348
|
+
|
|
349
|
+
export interface S3MultipartAbortOutput {
|
|
350
|
+
/** Whether abort was successful */
|
|
351
|
+
aborted: boolean;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// =============================================================================
|
|
355
|
+
// Output schemas (roadmap 2.1)
|
|
356
|
+
// =============================================================================
|
|
357
|
+
|
|
358
|
+
// Loose objects: a field that a schema does not name stays in the result (a strict schema would
|
|
359
|
+
// remove it when the output is validated).
|
|
360
|
+
|
|
361
|
+
export const S3UploadOutputSchema: z.ZodType<S3UploadOutput> = z.looseObject({
|
|
362
|
+
etag: z.string().describe("The ETag of the object, without quotes"),
|
|
363
|
+
versionId: z.string().optional().describe("The version id, when the bucket has versioning"),
|
|
364
|
+
location: z.string().describe("The S3 URI of the object (s3://bucket/key)"),
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
export const S3DownloadOutputSchema: z.ZodType<S3DownloadOutput> = z.looseObject({
|
|
368
|
+
body: z.string().describe("The content: text with an encoding, base64 without one, empty with destPath"),
|
|
369
|
+
path: z.string().optional().describe("The file that holds the object, with destPath"),
|
|
370
|
+
contentLength: z.number().describe("The size of the object, in bytes"),
|
|
371
|
+
contentType: z.string().optional().describe("The content type"),
|
|
372
|
+
etag: z.string().optional().describe("The ETag of the object, without quotes"),
|
|
373
|
+
lastModified: z.string().optional().describe("The time of the last change (ISO 8601)"),
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const listObject = z.looseObject({
|
|
377
|
+
key: z.string().describe("The object key"),
|
|
378
|
+
size: z.number().describe("The size, in bytes"),
|
|
379
|
+
lastModified: z.string().describe("The time of the last change (ISO 8601)"),
|
|
380
|
+
etag: z.string().describe("The ETag, without quotes"),
|
|
381
|
+
storageClass: z.string().optional().describe("The storage class"),
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
export const S3ListOutputSchema: z.ZodType<S3ListOutput> = z.looseObject({
|
|
385
|
+
contents: z.array(listObject).describe("The objects of the page"),
|
|
386
|
+
commonPrefixes: z.array(z.string()).describe("The common prefixes (with a delimiter)"),
|
|
387
|
+
isTruncated: z.boolean().describe("True when more pages exist"),
|
|
388
|
+
nextContinuationToken: z.string().optional().describe("The token of the next page"),
|
|
389
|
+
keyCount: z.number().describe("The number of keys on the page"),
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
export const S3ListAllOutputSchema: z.ZodType<S3ListAllOutput> = z.looseObject({
|
|
393
|
+
contents: z.array(listObject).describe("The objects of all pages"),
|
|
394
|
+
commonPrefixes: z.array(z.string()).describe("The common prefixes of all pages (with a delimiter)"),
|
|
395
|
+
keyCount: z.number().describe("The number of keys"),
|
|
396
|
+
pageCount: z.number().describe("The number of list requests"),
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
export const S3DeleteOutputSchema: z.ZodType<S3DeleteOutput> = z.looseObject({
|
|
400
|
+
deleted: z.boolean().describe("True when the delete succeeded"),
|
|
401
|
+
deleteMarker: z.boolean().optional().describe("True when versioning made a delete marker"),
|
|
402
|
+
versionId: z.string().optional().describe("The version id of the delete marker"),
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
export const S3MultipartInitOutputSchema: z.ZodType<S3MultipartInitOutput> = z.looseObject({
|
|
406
|
+
uploadId: z.string().describe("The upload id: the part uploads and complete take it"),
|
|
407
|
+
bucket: z.string().describe("The bucket"),
|
|
408
|
+
key: z.string().describe("The object key"),
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
export const S3MultipartUploadOutputSchema: z.ZodType<S3MultipartUploadOutput> = z.looseObject({
|
|
412
|
+
etag: z.string().describe("The ETag of the part, without quotes: complete takes it"),
|
|
413
|
+
partNumber: z.number().describe("The part number"),
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
export const S3MultipartCompleteOutputSchema: z.ZodType<S3MultipartCompleteOutput> = z.looseObject({
|
|
417
|
+
location: z.string().describe("The location of the object"),
|
|
418
|
+
bucket: z.string().describe("The bucket"),
|
|
419
|
+
key: z.string().describe("The object key"),
|
|
420
|
+
etag: z.string().describe("The ETag of the object, without quotes"),
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
export const S3MultipartAbortOutputSchema: z.ZodType<S3MultipartAbortOutput> = z.looseObject({
|
|
424
|
+
aborted: z.boolean().describe("True when the upload was aborted"),
|
|
425
|
+
});
|