@contract-kit/provider-storage-s3 1.0.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 +28 -0
- package/README.md +141 -0
- package/dist/index.d.ts +148 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +644 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
- package/src/index.ts +906 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# @contract-kit/provider-storage-s3
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 408ceb9: Add an S3-compatible storage provider with `createS3Storage`,
|
|
8
|
+
`createS3StorageProvider`, and `s3StorageProvider` for AWS S3, Cloudflare R2,
|
|
9
|
+
MinIO, and similar object stores.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [55f651c]
|
|
14
|
+
- Updated dependencies [e73a554]
|
|
15
|
+
- Updated dependencies [481e8ca]
|
|
16
|
+
- Updated dependencies [58d5528]
|
|
17
|
+
- Updated dependencies [c8b51ea]
|
|
18
|
+
- Updated dependencies [c5829fb]
|
|
19
|
+
- Updated dependencies [56ac09f]
|
|
20
|
+
- Updated dependencies [01eccbc]
|
|
21
|
+
- @contract-kit/devtools@1.0.0
|
|
22
|
+
- @contract-kit/ports@1.0.0
|
|
23
|
+
|
|
24
|
+
## 0.1.10
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Initial S3-compatible storage provider package.
|
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# @contract-kit/provider-storage-s3
|
|
2
|
+
|
|
3
|
+
S3-compatible object storage provider for Contract Kit.
|
|
4
|
+
|
|
5
|
+
The provider installs the app-facing `ctx.ports.storage` port. Use it for
|
|
6
|
+
production object storage on AWS S3, Cloudflare R2, MinIO, Backblaze B2,
|
|
7
|
+
DigitalOcean Spaces, or another S3-compatible backend.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun add @contract-kit/provider-storage-s3
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Provider setup
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { s3StorageProvider } from "@contract-kit/provider-storage-s3";
|
|
19
|
+
import { createServer } from "@contract-kit/server";
|
|
20
|
+
|
|
21
|
+
const server = await createServer({
|
|
22
|
+
ports: basePorts,
|
|
23
|
+
providers: [s3StorageProvider],
|
|
24
|
+
createContext: ({ ports }) => ({ ports }),
|
|
25
|
+
routes,
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Environment variables:
|
|
30
|
+
|
|
31
|
+
| Variable | Description |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| `STORAGE_S3_BUCKET` | Bucket name. |
|
|
34
|
+
| `STORAGE_S3_REGION` | Region. Defaults to `us-east-1`. Use `auto` for Cloudflare R2. |
|
|
35
|
+
| `STORAGE_S3_ENDPOINT` | Optional S3-compatible endpoint. Required for R2, MinIO, Spaces, B2, and similar services. |
|
|
36
|
+
| `STORAGE_S3_ACCESS_KEY_ID` | Optional static access key. |
|
|
37
|
+
| `STORAGE_S3_SECRET_ACCESS_KEY` | Optional static secret key. |
|
|
38
|
+
| `STORAGE_S3_SESSION_TOKEN` | Optional static session token. |
|
|
39
|
+
| `STORAGE_S3_PUBLIC_BASE_URL` | Optional base URL returned by `publicUrl(...)` for public objects. |
|
|
40
|
+
| `STORAGE_S3_FORCE_PATH_STYLE` | Optional `true` or `false` path-style addressing toggle. |
|
|
41
|
+
| `STORAGE_S3_KEY_PREFIX` | Optional prefix for every object key written by this app. |
|
|
42
|
+
|
|
43
|
+
## AWS S3
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
STORAGE_S3_BUCKET=my-app-assets
|
|
47
|
+
STORAGE_S3_REGION=us-east-1
|
|
48
|
+
STORAGE_S3_PUBLIC_BASE_URL=https://cdn.example.com
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
When credentials are omitted, the AWS SDK uses its normal credential provider
|
|
52
|
+
chain.
|
|
53
|
+
|
|
54
|
+
## Cloudflare R2
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
STORAGE_S3_BUCKET=my-app-assets
|
|
58
|
+
STORAGE_S3_REGION=auto
|
|
59
|
+
STORAGE_S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
|
|
60
|
+
STORAGE_S3_ACCESS_KEY_ID=...
|
|
61
|
+
STORAGE_S3_SECRET_ACCESS_KEY=...
|
|
62
|
+
STORAGE_S3_PUBLIC_BASE_URL=https://assets.example.com
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
R2 is S3-compatible, but not every S3 feature exists on every compatible
|
|
66
|
+
service. This provider only relies on object `put`, `get`, `head`, and
|
|
67
|
+
`delete`.
|
|
68
|
+
|
|
69
|
+
## Direct port factory
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { createS3Storage } from "@contract-kit/provider-storage-s3";
|
|
73
|
+
|
|
74
|
+
const storage = createS3Storage({
|
|
75
|
+
bucket: "my-app-assets",
|
|
76
|
+
region: "auto",
|
|
77
|
+
endpoint: "https://<account-id>.r2.cloudflarestorage.com",
|
|
78
|
+
credentials: {
|
|
79
|
+
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
|
|
80
|
+
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
|
|
81
|
+
},
|
|
82
|
+
publicBaseUrl: "https://assets.example.com",
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The same `StoragePort` works with local files, memory tests, and
|
|
87
|
+
S3-compatible object stores:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
await ctx.ports.storage.put("avatars/user_123.png", avatarBytes, {
|
|
91
|
+
contentType: "image/png",
|
|
92
|
+
visibility: "public",
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const object = await ctx.ports.storage.get("avatars/user_123.png");
|
|
96
|
+
const url = await ctx.ports.storage.publicUrl("avatars/user_123.png");
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Visibility
|
|
100
|
+
|
|
101
|
+
`visibility` is stored as reserved object metadata so `publicUrl(...)` can
|
|
102
|
+
return URLs only for objects written with `visibility: "public"`. The provider
|
|
103
|
+
does not set S3 ACLs. Configure bucket policies, R2 public buckets, or a CDN
|
|
104
|
+
outside the provider when objects should be publicly reachable.
|
|
105
|
+
|
|
106
|
+
The reserved metadata key is `ck-visibility`. It is hidden from
|
|
107
|
+
`StorageObject.metadata`.
|
|
108
|
+
|
|
109
|
+
## Escape hatch
|
|
110
|
+
|
|
111
|
+
The provider also installs `ctx.ports.s3Storage` for S3-specific operations that
|
|
112
|
+
do not belong in `StoragePort`:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { ListObjectsV2Command } from "@aws-sdk/client-s3";
|
|
116
|
+
|
|
117
|
+
const s3Key = ctx.ports.s3Storage.objectKey("exports/report.csv");
|
|
118
|
+
const s3Prefix = ctx.ports.s3Storage.objectPrefix("exports");
|
|
119
|
+
|
|
120
|
+
await ctx.ports.s3Storage.client.send(
|
|
121
|
+
new ListObjectsV2Command({
|
|
122
|
+
Bucket: ctx.ports.s3Storage.bucket,
|
|
123
|
+
Prefix: s3Prefix,
|
|
124
|
+
}),
|
|
125
|
+
);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Use `objectKey(...)` when direct S3 calls need to address objects written
|
|
129
|
+
through `ctx.ports.storage`. Use `objectPrefix(...)` for list operations. Both
|
|
130
|
+
helpers apply the configured `STORAGE_S3_KEY_PREFIX`.
|
|
131
|
+
|
|
132
|
+
## Devtools
|
|
133
|
+
|
|
134
|
+
When `ctx.ports.devtools` is installed, the provider records storage
|
|
135
|
+
operations under the `storage` watcher. Events include operation name, key,
|
|
136
|
+
bucket, duration, object size, visibility, and whether a lookup hit. Object
|
|
137
|
+
bodies and metadata values are never recorded.
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { S3Client, type S3ClientConfig } from "@aws-sdk/client-s3";
|
|
2
|
+
import { type StoragePort } from "@contract-kit/ports";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
export type { StorageBody, StorageMetadata, StorageObject, StorageObjectBody, StoragePort, StorageVisibility, } from "@contract-kit/ports";
|
|
5
|
+
declare const S3StorageConfigSchema: z.ZodObject<{
|
|
6
|
+
BUCKET: z.ZodString;
|
|
7
|
+
REGION: z.ZodDefault<z.ZodString>;
|
|
8
|
+
ENDPOINT: z.ZodOptional<z.ZodString>;
|
|
9
|
+
ACCESS_KEY_ID: z.ZodOptional<z.ZodString>;
|
|
10
|
+
SECRET_ACCESS_KEY: z.ZodOptional<z.ZodString>;
|
|
11
|
+
SESSION_TOKEN: z.ZodOptional<z.ZodString>;
|
|
12
|
+
PUBLIC_BASE_URL: z.ZodOptional<z.ZodString>;
|
|
13
|
+
FORCE_PATH_STYLE: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
14
|
+
true: "true";
|
|
15
|
+
false: "false";
|
|
16
|
+
}>, z.ZodTransform<boolean, "true" | "false">>>;
|
|
17
|
+
KEY_PREFIX: z.ZodOptional<z.ZodString>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
export type S3StorageConfig = z.infer<typeof S3StorageConfigSchema>;
|
|
20
|
+
export type S3StorageClient = Pick<S3Client, "send">;
|
|
21
|
+
export interface S3StorageOptions {
|
|
22
|
+
/**
|
|
23
|
+
* S3 bucket name.
|
|
24
|
+
*/
|
|
25
|
+
bucket: string;
|
|
26
|
+
/**
|
|
27
|
+
* S3-compatible client. Omit to create an AWS SDK S3Client from config.
|
|
28
|
+
*/
|
|
29
|
+
client?: S3StorageClient;
|
|
30
|
+
/**
|
|
31
|
+
* Region used when creating the default S3Client.
|
|
32
|
+
*
|
|
33
|
+
* @default "us-east-1"
|
|
34
|
+
*/
|
|
35
|
+
region?: string;
|
|
36
|
+
/**
|
|
37
|
+
* S3-compatible endpoint. Required for R2, MinIO, Spaces, B2, and most
|
|
38
|
+
* non-AWS object stores.
|
|
39
|
+
*/
|
|
40
|
+
endpoint?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Static credentials used when creating the default S3Client.
|
|
43
|
+
*/
|
|
44
|
+
credentials?: {
|
|
45
|
+
accessKeyId: string;
|
|
46
|
+
secretAccessKey: string;
|
|
47
|
+
sessionToken?: string;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Use path-style bucket addressing when required by the object store.
|
|
51
|
+
*/
|
|
52
|
+
forcePathStyle?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Prefix all object keys before sending them to S3.
|
|
55
|
+
*/
|
|
56
|
+
keyPrefix?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Base URL used by `publicUrl(...)` for public objects.
|
|
59
|
+
*/
|
|
60
|
+
publicBaseUrl?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Additional AWS SDK S3Client config.
|
|
63
|
+
*/
|
|
64
|
+
clientConfig?: Omit<S3ClientConfig, "region" | "endpoint" | "credentials" | "forcePathStyle">;
|
|
65
|
+
/**
|
|
66
|
+
* Optional devtools target. The provider passes existing app ports here
|
|
67
|
+
* automatically; direct factory users can pass a devtools port explicitly.
|
|
68
|
+
*/
|
|
69
|
+
devtools?: unknown;
|
|
70
|
+
}
|
|
71
|
+
export interface S3StorageProviderOptions extends Omit<S3StorageOptions, "bucket" | "client" | "devtools"> {
|
|
72
|
+
/**
|
|
73
|
+
* Provider name. Defaults to "storage-s3".
|
|
74
|
+
*/
|
|
75
|
+
name?: string;
|
|
76
|
+
/**
|
|
77
|
+
* Default bucket used when STORAGE_S3_BUCKET is not set.
|
|
78
|
+
*/
|
|
79
|
+
bucket?: string;
|
|
80
|
+
/**
|
|
81
|
+
* Optional client factory for tests or custom S3 clients.
|
|
82
|
+
*/
|
|
83
|
+
createClient?: (config: S3StorageConfig) => S3StorageClient;
|
|
84
|
+
}
|
|
85
|
+
export declare function createS3Storage(options: S3StorageOptions): StoragePort;
|
|
86
|
+
export interface S3StorageProviderPorts {
|
|
87
|
+
storage: StoragePort;
|
|
88
|
+
s3Storage: {
|
|
89
|
+
client: S3StorageClient;
|
|
90
|
+
bucket: string;
|
|
91
|
+
keyPrefix: string;
|
|
92
|
+
objectKey(key: string): string;
|
|
93
|
+
objectPrefix(prefix: string): string;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export declare function createS3StorageProvider(options?: S3StorageProviderOptions): import("@contract-kit/ports").ServiceProvider<unknown, z.ZodObject<{
|
|
97
|
+
ACCESS_KEY_ID: z.ZodOptional<z.ZodString>;
|
|
98
|
+
SECRET_ACCESS_KEY: z.ZodOptional<z.ZodString>;
|
|
99
|
+
SESSION_TOKEN: z.ZodOptional<z.ZodString>;
|
|
100
|
+
BUCKET: z.ZodString | z.ZodDefault<z.ZodString>;
|
|
101
|
+
REGION: z.ZodDefault<z.ZodString>;
|
|
102
|
+
ENDPOINT: z.ZodDefault<z.ZodString> | z.ZodOptional<z.ZodString>;
|
|
103
|
+
PUBLIC_BASE_URL: z.ZodDefault<z.ZodString> | z.ZodOptional<z.ZodString>;
|
|
104
|
+
FORCE_PATH_STYLE: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
105
|
+
true: "true";
|
|
106
|
+
false: "false";
|
|
107
|
+
}>, z.ZodTransform<boolean, "true" | "false">>> | z.ZodDefault<z.ZodPipe<z.ZodEnum<{
|
|
108
|
+
true: "true";
|
|
109
|
+
false: "false";
|
|
110
|
+
}>, z.ZodTransform<boolean, "true" | "false">>>;
|
|
111
|
+
KEY_PREFIX: z.ZodDefault<z.ZodString> | z.ZodOptional<z.ZodString>;
|
|
112
|
+
}, z.core.$strip>, {
|
|
113
|
+
storage: StoragePort;
|
|
114
|
+
s3Storage: {
|
|
115
|
+
client: S3StorageClient;
|
|
116
|
+
bucket: string;
|
|
117
|
+
keyPrefix: string;
|
|
118
|
+
objectKey(key: string): string;
|
|
119
|
+
objectPrefix(prefix: string): string;
|
|
120
|
+
};
|
|
121
|
+
}>;
|
|
122
|
+
export declare const s3StorageProvider: import("@contract-kit/ports").ServiceProvider<unknown, z.ZodObject<{
|
|
123
|
+
ACCESS_KEY_ID: z.ZodOptional<z.ZodString>;
|
|
124
|
+
SECRET_ACCESS_KEY: z.ZodOptional<z.ZodString>;
|
|
125
|
+
SESSION_TOKEN: z.ZodOptional<z.ZodString>;
|
|
126
|
+
BUCKET: z.ZodString | z.ZodDefault<z.ZodString>;
|
|
127
|
+
REGION: z.ZodDefault<z.ZodString>;
|
|
128
|
+
ENDPOINT: z.ZodDefault<z.ZodString> | z.ZodOptional<z.ZodString>;
|
|
129
|
+
PUBLIC_BASE_URL: z.ZodDefault<z.ZodString> | z.ZodOptional<z.ZodString>;
|
|
130
|
+
FORCE_PATH_STYLE: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
131
|
+
true: "true";
|
|
132
|
+
false: "false";
|
|
133
|
+
}>, z.ZodTransform<boolean, "true" | "false">>> | z.ZodDefault<z.ZodPipe<z.ZodEnum<{
|
|
134
|
+
true: "true";
|
|
135
|
+
false: "false";
|
|
136
|
+
}>, z.ZodTransform<boolean, "true" | "false">>>;
|
|
137
|
+
KEY_PREFIX: z.ZodDefault<z.ZodString> | z.ZodOptional<z.ZodString>;
|
|
138
|
+
}, z.core.$strip>, {
|
|
139
|
+
storage: StoragePort;
|
|
140
|
+
s3Storage: {
|
|
141
|
+
client: S3StorageClient;
|
|
142
|
+
bucket: string;
|
|
143
|
+
keyPrefix: string;
|
|
144
|
+
objectKey(key: string): string;
|
|
145
|
+
objectPrefix(prefix: string): string;
|
|
146
|
+
};
|
|
147
|
+
}>;
|
|
148
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,QAAQ,EACR,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAML,KAAK,WAAW,EAEjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,YAAY,EACV,WAAW,EACX,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAQ7B,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;iBAUzB,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAErD,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IAEzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,WAAW,CAAC,EAAE;QACZ,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IAEF;;OAEG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,YAAY,CAAC,EAAE,IAAI,CACjB,cAAc,EACd,QAAQ,GAAG,UAAU,GAAG,aAAa,GAAG,gBAAgB,CACzD,CAAC;IAEF;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,wBACf,SAAQ,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;IAChE;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,eAAe,CAAC;CAC7D;AAuWD,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,WAAW,CAgTtE;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE;QACT,MAAM,EAAE,eAAe,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;QAC/B,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;KACtC,CAAC;CACH;AAED,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,wBAA6B;;;;;;;;;;;;;;;;;;;;;;;;;GA+FvC;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;EAA4B,CAAC"}
|