manage-storage 0.0.3 → 0.0.4
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/README.md +77 -17
- package/dist/index.d.ts +60 -6
- package/dist/index.js +72 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,6 @@ Universal cloud storage manager supporting AWS S3, Cloudflare R2, and Backblaze
|
|
|
13
13
|
- **Lightweight**: Built on aws-lite (not the bloated AWS SDK) for faster cold starts
|
|
14
14
|
- **Simple API**: Single function interface for all storage operations
|
|
15
15
|
- **No File System**: Returns data directly - perfect for serverless/edge environments
|
|
16
|
-
- **TypeScript Ready**: Full type support with comprehensive type definitions
|
|
17
16
|
|
|
18
17
|
## Installation
|
|
19
18
|
|
|
@@ -44,6 +43,18 @@ const data = await manageStorage("download", {
|
|
|
44
43
|
// List all files
|
|
45
44
|
const files = await manageStorage("list");
|
|
46
45
|
|
|
46
|
+
// Copy a file
|
|
47
|
+
await manageStorage("copy", {
|
|
48
|
+
key: "documents/report.pdf",
|
|
49
|
+
destinationKey: "documents/report-backup.pdf",
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Rename a file (copy + delete)
|
|
53
|
+
await manageStorage("rename", {
|
|
54
|
+
key: "documents/old-name.pdf",
|
|
55
|
+
destinationKey: "documents/new-name.pdf",
|
|
56
|
+
});
|
|
57
|
+
|
|
47
58
|
// Delete a file
|
|
48
59
|
await manageStorage("delete", {
|
|
49
60
|
key: "documents/report.pdf",
|
|
@@ -90,16 +101,17 @@ Performs storage operations on your configured cloud provider.
|
|
|
90
101
|
|
|
91
102
|
#### Parameters
|
|
92
103
|
|
|
93
|
-
- **action** `string` - The operation to perform: `'upload'`, `'download'`, `'delete'`, `'list'`, or `'
|
|
104
|
+
- **action** `string` - The operation to perform: `'upload'`, `'download'`, `'delete'`, `'list'`, `'deleteAll'`, `'copy'`, or `'rename'`
|
|
94
105
|
- **options** `object` - Operation-specific options
|
|
95
106
|
|
|
96
107
|
#### Options
|
|
97
108
|
|
|
98
|
-
| Option
|
|
99
|
-
|
|
|
100
|
-
| `key`
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
109
|
+
| Option | Type | Required | Description |
|
|
110
|
+
| ---------------- | ------------------------ | ----------------------------------- | ---------------------------------------------------- |
|
|
111
|
+
| `key` | `string` | Yes (except for `list`/`deleteAll`) | The object key/path |
|
|
112
|
+
| `destinationKey` | `string` | Yes (for `copy`/`rename`) | The destination key/path for copy/rename operations |
|
|
113
|
+
| `body` | `string\|Buffer\|Stream` | Yes (for `upload`) | The file content to upload |
|
|
114
|
+
| `provider` | `'s3'\|'r2'\|'b2'` | No | Force a specific provider (auto-detected if omitted) |
|
|
103
115
|
|
|
104
116
|
## Usage Examples
|
|
105
117
|
|
|
@@ -156,7 +168,39 @@ const notes = files.filter((key) => key.startsWith("notes/"));
|
|
|
156
168
|
console.log(notes); // ['notes/memo.txt']
|
|
157
169
|
```
|
|
158
170
|
|
|
159
|
-
### 4.
|
|
171
|
+
### 4. Copy Files
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
// Copy a file to a new location
|
|
175
|
+
await manageStorage("copy", {
|
|
176
|
+
key: "documents/report.pdf",
|
|
177
|
+
destinationKey: "documents/backup/report-2024.pdf",
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Create a backup
|
|
181
|
+
await manageStorage("copy", {
|
|
182
|
+
key: "config/settings.json",
|
|
183
|
+
destinationKey: "config/settings.backup.json",
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 5. Rename Files
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
// Rename a file (performs copy + delete)
|
|
191
|
+
await manageStorage("rename", {
|
|
192
|
+
key: "old-filename.txt",
|
|
193
|
+
destinationKey: "new-filename.txt",
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Move to a different folder
|
|
197
|
+
await manageStorage("rename", {
|
|
198
|
+
key: "temp/draft.md",
|
|
199
|
+
destinationKey: "published/article.md",
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### 6. Delete Files
|
|
160
204
|
|
|
161
205
|
```javascript
|
|
162
206
|
// Delete a single file
|
|
@@ -169,7 +213,7 @@ const result = await manageStorage("deleteAll");
|
|
|
169
213
|
console.log(`Deleted ${result.count} files`);
|
|
170
214
|
```
|
|
171
215
|
|
|
172
|
-
###
|
|
216
|
+
### 7. Force a Specific Provider
|
|
173
217
|
|
|
174
218
|
```javascript
|
|
175
219
|
// Use R2 even if other providers are configured
|
|
@@ -187,7 +231,7 @@ await manageStorage("upload", {
|
|
|
187
231
|
});
|
|
188
232
|
```
|
|
189
233
|
|
|
190
|
-
###
|
|
234
|
+
### 8. Runtime Configuration (Override Environment Variables)
|
|
191
235
|
|
|
192
236
|
```javascript
|
|
193
237
|
// Pass credentials at runtime instead of using env vars
|
|
@@ -242,7 +286,7 @@ export async function GET(req) {
|
|
|
242
286
|
|
|
243
287
|
```javascript
|
|
244
288
|
import express from "express";
|
|
245
|
-
import { manageStorage } from "
|
|
289
|
+
import { manageStorage } from "manage-storage";
|
|
246
290
|
|
|
247
291
|
const app = express();
|
|
248
292
|
app.use(express.json());
|
|
@@ -290,7 +334,7 @@ app.listen(3000, () => console.log("Server running on port 3000"));
|
|
|
290
334
|
### Cloudflare Workers
|
|
291
335
|
|
|
292
336
|
```javascript
|
|
293
|
-
import { manageStorage } from "
|
|
337
|
+
import { manageStorage } from "manage-storage";
|
|
294
338
|
|
|
295
339
|
export default {
|
|
296
340
|
async fetch(request, env) {
|
|
@@ -383,6 +427,26 @@ const contents = await Promise.all(
|
|
|
383
427
|
}
|
|
384
428
|
```
|
|
385
429
|
|
|
430
|
+
### Copy
|
|
431
|
+
|
|
432
|
+
```javascript
|
|
433
|
+
{
|
|
434
|
+
success: true,
|
|
435
|
+
sourceKey: 'documents/report.pdf',
|
|
436
|
+
destinationKey: 'documents/backup/report-2024.pdf'
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Rename
|
|
441
|
+
|
|
442
|
+
```javascript
|
|
443
|
+
{
|
|
444
|
+
success: true,
|
|
445
|
+
oldKey: 'old-filename.txt',
|
|
446
|
+
newKey: 'new-filename.txt'
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
386
450
|
## Why aws-lite?
|
|
387
451
|
|
|
388
452
|
This library uses [@aws-lite](https://aws-lite.org/) instead of the official AWS SDK because:
|
|
@@ -393,11 +457,7 @@ This library uses [@aws-lite](https://aws-lite.org/) instead of the official AWS
|
|
|
393
457
|
- **Modern API**: Clean, promise-based interface
|
|
394
458
|
- **No dependencies overhead**: Minimal dependency tree
|
|
395
459
|
|
|
396
|
-
# Cloud Object Storage Comparison
|
|
397
|
-
|
|
398
|
-
Google Cloud Storage (GCS) joins Backblaze B2, Cloudflare R2, and AWS S3 as a hyperscaler option with [strong multi-region support](https://cloud.google.com/storage/pricing), multiple storage classes, and [deep integration with Google Cloud services](https://cloud.google.com/storage/pricing). These providers all [offer S3-compatible object storage](https://www.backblaze.com/cloud-storage/pricing) but differ significantly in [pricing models](https://www.backblaze.com/cloud-storage/pricing), especially storage costs, egress fees, and ecosystem fit.
|
|
399
|
-
|
|
400
|
-
## Updated pricing snapshot ("hot"/Standard storage)
|
|
460
|
+
# Cloud Object Storage Comparison
|
|
401
461
|
|
|
402
462
|
| Service | Storage price (/TB-month) | Egress to internet | API ops (Class A/B per 1K, approx) | Minimum duration | Notes |
|
|
403
463
|
| --------------------------------------------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------ |
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Storage operation action type
|
|
3
3
|
*/
|
|
4
|
-
export declare type Action = "upload" | "download" | "delete" | "list" | "deleteAll";
|
|
4
|
+
export declare type Action = "upload" | "download" | "delete" | "list" | "deleteAll" | "copy" | "rename";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Result from copy operation
|
|
8
|
+
*/
|
|
9
|
+
export declare interface CopyResult {
|
|
10
|
+
/** Whether the copy was successful */
|
|
11
|
+
success: boolean;
|
|
12
|
+
/** The source object key */
|
|
13
|
+
sourceKey: string;
|
|
14
|
+
/** The destination object key */
|
|
15
|
+
destinationKey: string;
|
|
16
|
+
[key: string]: any;
|
|
17
|
+
}
|
|
5
18
|
|
|
6
19
|
/**
|
|
7
20
|
* Result from deleteAll operation
|
|
@@ -63,6 +76,20 @@ export declare interface DeleteResult {
|
|
|
63
76
|
* });
|
|
64
77
|
*
|
|
65
78
|
* @example
|
|
79
|
+
* // Copy a file
|
|
80
|
+
* await manageStorage('copy', {
|
|
81
|
+
* key: 'documents/report.pdf',
|
|
82
|
+
* destinationKey: 'documents/report-copy.pdf'
|
|
83
|
+
* });
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // Rename a file (copy + delete)
|
|
87
|
+
* await manageStorage('rename', {
|
|
88
|
+
* key: 'documents/old-name.pdf',
|
|
89
|
+
* destinationKey: 'documents/new-name.pdf'
|
|
90
|
+
* });
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
66
93
|
* // Force a specific provider with runtime credentials
|
|
67
94
|
* await manageStorage('upload', {
|
|
68
95
|
* key: 'test.txt',
|
|
@@ -74,34 +101,61 @@ export declare interface DeleteResult {
|
|
|
74
101
|
* BUCKET_URL: 'https://account.r2.cloudflarestorage.com'
|
|
75
102
|
* });
|
|
76
103
|
*/
|
|
77
|
-
|
|
104
|
+
declare function manageStorage(action: "upload", options: StorageOptions & {
|
|
78
105
|
key: string;
|
|
79
106
|
body: string | Buffer | ReadableStream;
|
|
80
107
|
}): Promise<UploadResult>;
|
|
81
108
|
|
|
82
|
-
|
|
109
|
+
declare function manageStorage(action: "download", options: StorageOptions & {
|
|
83
110
|
key: string;
|
|
84
111
|
}): Promise<string>;
|
|
85
112
|
|
|
86
|
-
|
|
113
|
+
declare function manageStorage(action: "delete", options: StorageOptions & {
|
|
87
114
|
key: string;
|
|
88
115
|
}): Promise<DeleteResult>;
|
|
89
116
|
|
|
90
|
-
|
|
117
|
+
declare function manageStorage(action: "list", options?: StorageOptions): Promise<string[]>;
|
|
118
|
+
|
|
119
|
+
declare function manageStorage(action: "deleteAll", options?: StorageOptions): Promise<DeleteAllResult>;
|
|
120
|
+
|
|
121
|
+
declare function manageStorage(action: "copy", options: StorageOptions & {
|
|
122
|
+
key: string;
|
|
123
|
+
destinationKey: string;
|
|
124
|
+
}): Promise<CopyResult>;
|
|
91
125
|
|
|
92
|
-
|
|
126
|
+
declare function manageStorage(action: "rename", options: StorageOptions & {
|
|
127
|
+
key: string;
|
|
128
|
+
destinationKey: string;
|
|
129
|
+
}): Promise<RenameResult>;
|
|
130
|
+
export default manageStorage;
|
|
131
|
+
export { manageStorage }
|
|
93
132
|
|
|
94
133
|
/**
|
|
95
134
|
* Cloud storage provider type
|
|
96
135
|
*/
|
|
97
136
|
export declare type Provider = "s3" | "r2" | "b2";
|
|
98
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Result from rename operation
|
|
140
|
+
*/
|
|
141
|
+
export declare interface RenameResult {
|
|
142
|
+
/** Whether the rename was successful */
|
|
143
|
+
success: boolean;
|
|
144
|
+
/** The old object key */
|
|
145
|
+
oldKey: string;
|
|
146
|
+
/** The new object key */
|
|
147
|
+
newKey: string;
|
|
148
|
+
[key: string]: any;
|
|
149
|
+
}
|
|
150
|
+
|
|
99
151
|
/**
|
|
100
152
|
* Options for storage operations
|
|
101
153
|
*/
|
|
102
154
|
export declare interface StorageOptions {
|
|
103
155
|
/** The object key/path (required for upload, download, delete) */
|
|
104
156
|
key?: string;
|
|
157
|
+
/** The destination key/path (required for copy, rename) */
|
|
158
|
+
destinationKey?: string;
|
|
105
159
|
/** The file content to upload (required for upload) */
|
|
106
160
|
body?: string | Buffer | ReadableStream;
|
|
107
161
|
/** Force a specific cloud provider (auto-detected if omitted) */
|
package/dist/index.js
CHANGED
|
@@ -1,79 +1,105 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import { config as
|
|
4
|
-
|
|
5
|
-
async function
|
|
6
|
-
var _,
|
|
7
|
-
const { key: e, body:
|
|
8
|
-
Object.entries(
|
|
9
|
-
), t =
|
|
1
|
+
import b from "@aws-lite/client";
|
|
2
|
+
import k from "@aws-lite/s3";
|
|
3
|
+
import { config as w } from "dotenv";
|
|
4
|
+
w();
|
|
5
|
+
async function U(r, s = {}) {
|
|
6
|
+
var _, C;
|
|
7
|
+
const { key: e, body: a, destinationKey: o, provider: K, ...d } = s, u = K || B(), y = Object.fromEntries(
|
|
8
|
+
Object.entries(d).map(([n, l]) => [`${u.toUpperCase()}_${n}`, l])
|
|
9
|
+
), t = f(u, y);
|
|
10
10
|
if (!t.bucket || !t.accessKeyId || !t.secretAccessKey)
|
|
11
|
-
throw new Error(`Missing credentials for ${
|
|
12
|
-
const
|
|
11
|
+
throw new Error(`Missing credentials for ${u}`);
|
|
12
|
+
const c = await b({
|
|
13
13
|
region: t.region,
|
|
14
14
|
endpoint: t.endpoint,
|
|
15
15
|
accessKeyId: t.accessKeyId,
|
|
16
16
|
secretAccessKey: t.secretAccessKey,
|
|
17
|
-
plugins: [
|
|
17
|
+
plugins: [k]
|
|
18
18
|
});
|
|
19
19
|
try {
|
|
20
|
-
switch (
|
|
20
|
+
switch (r) {
|
|
21
21
|
case "upload":
|
|
22
|
-
const n = await
|
|
22
|
+
const n = await c.s3.PutObject({
|
|
23
23
|
Bucket: t.bucket,
|
|
24
24
|
Key: e,
|
|
25
|
-
Body:
|
|
25
|
+
Body: a
|
|
26
26
|
});
|
|
27
|
-
return
|
|
27
|
+
return { success: !0, key: e, ...n };
|
|
28
28
|
case "download":
|
|
29
|
-
|
|
29
|
+
return (await c.s3.GetObject({
|
|
30
30
|
Bucket: t.bucket,
|
|
31
31
|
Key: e
|
|
32
|
-
});
|
|
33
|
-
return console.log(`Downloaded ${e} from ${c}`), a.Body;
|
|
32
|
+
})).Body;
|
|
34
33
|
case "delete":
|
|
35
|
-
const
|
|
34
|
+
const p = await c.s3.DeleteObject({
|
|
36
35
|
Bucket: t.bucket,
|
|
37
36
|
Key: e
|
|
38
37
|
});
|
|
39
|
-
return
|
|
38
|
+
return { success: !0, key: e, ...p };
|
|
40
39
|
case "list":
|
|
41
|
-
|
|
40
|
+
return ((_ = (await c.s3.ListObjectsV2({
|
|
42
41
|
Bucket: t.bucket
|
|
43
|
-
})).Contents) == null ? void 0 : _.map((
|
|
44
|
-
return console.log(C.join(`
|
|
45
|
-
`)), C;
|
|
42
|
+
})).Contents) == null ? void 0 : _.map((E) => E.Key)) || [];
|
|
46
43
|
case "deleteAll":
|
|
47
|
-
const
|
|
44
|
+
const i = await c.s3.ListObjectsV2({
|
|
48
45
|
Bucket: t.bucket
|
|
49
46
|
});
|
|
50
|
-
if ((
|
|
51
|
-
const
|
|
47
|
+
if ((C = i.Contents) != null && C.length) {
|
|
48
|
+
const E = await c.s3.DeleteObjects({
|
|
52
49
|
Bucket: t.bucket,
|
|
53
50
|
Delete: {
|
|
54
|
-
Objects:
|
|
51
|
+
Objects: i.Contents.map((A) => ({
|
|
52
|
+
Key: A.Key
|
|
53
|
+
}))
|
|
55
54
|
}
|
|
56
55
|
});
|
|
57
|
-
return
|
|
58
|
-
`Deleted all ${l.Contents.length} files from ${c}`
|
|
59
|
-
), {
|
|
56
|
+
return {
|
|
60
57
|
success: !0,
|
|
61
|
-
count:
|
|
62
|
-
...
|
|
58
|
+
count: i.Contents.length,
|
|
59
|
+
...E
|
|
63
60
|
};
|
|
64
61
|
} else
|
|
65
|
-
return
|
|
62
|
+
return { success: !0, count: 0 };
|
|
63
|
+
case "copy":
|
|
64
|
+
if (!o)
|
|
65
|
+
throw new Error("destinationKey is required for copy operation");
|
|
66
|
+
const S = await c.s3.CopyObject({
|
|
67
|
+
Bucket: t.bucket,
|
|
68
|
+
CopySource: `${t.bucket}/${e}`,
|
|
69
|
+
Key: o
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
success: !0,
|
|
73
|
+
sourceKey: e,
|
|
74
|
+
destinationKey: o,
|
|
75
|
+
...S
|
|
76
|
+
};
|
|
77
|
+
case "rename":
|
|
78
|
+
if (!o)
|
|
79
|
+
throw new Error("destinationKey is required for rename operation");
|
|
80
|
+
return await c.s3.CopyObject({
|
|
81
|
+
Bucket: t.bucket,
|
|
82
|
+
CopySource: `${t.bucket}/${e}`,
|
|
83
|
+
Key: o
|
|
84
|
+
}), await c.s3.DeleteObject({
|
|
85
|
+
Bucket: t.bucket,
|
|
86
|
+
Key: e
|
|
87
|
+
}), {
|
|
88
|
+
success: !0,
|
|
89
|
+
oldKey: e,
|
|
90
|
+
newKey: o
|
|
91
|
+
};
|
|
66
92
|
default:
|
|
67
93
|
throw new Error(
|
|
68
|
-
"Invalid action: upload, download, delete, list, deleteAll"
|
|
94
|
+
"Invalid action: upload, download, delete, list, deleteAll, copy, rename"
|
|
69
95
|
);
|
|
70
96
|
}
|
|
71
97
|
} catch (n) {
|
|
72
|
-
throw console.error(`Error in ${
|
|
98
|
+
throw console.error(`Error in ${u}:`, n.message), n;
|
|
73
99
|
}
|
|
74
100
|
}
|
|
75
|
-
function
|
|
76
|
-
const
|
|
101
|
+
function B() {
|
|
102
|
+
const r = {
|
|
77
103
|
r2: [
|
|
78
104
|
"R2_BUCKET_NAME",
|
|
79
105
|
"R2_ACCESS_KEY_ID",
|
|
@@ -93,15 +119,15 @@ function b() {
|
|
|
93
119
|
"S3_BUCKET_URL"
|
|
94
120
|
]
|
|
95
121
|
};
|
|
96
|
-
for (const [s, e] of Object.entries(
|
|
97
|
-
if (e.every((
|
|
122
|
+
for (const [s, e] of Object.entries(r))
|
|
123
|
+
if (e.every((a) => process.env[a]))
|
|
98
124
|
return s;
|
|
99
125
|
return "r2";
|
|
100
126
|
}
|
|
101
|
-
function
|
|
102
|
-
const e =
|
|
127
|
+
function f(r, s = {}) {
|
|
128
|
+
const e = r.toUpperCase() + "_";
|
|
103
129
|
return {
|
|
104
|
-
region:
|
|
130
|
+
region: r === "s3" ? s.awsRegion || process.env.S3_REGION || "us-east-1" : "auto",
|
|
105
131
|
endpoint: s[`${e}BUCKET_URL`] || process.env[`${e}BUCKET_URL`] || "",
|
|
106
132
|
bucket: s[`${e}BUCKET_NAME`] || process.env[`${e}BUCKET_NAME`] || "",
|
|
107
133
|
accessKeyId: s[`${e}ACCESS_KEY_ID`] || s[`${e}APPLICATION_KEY_ID`] || process.env[`${e}ACCESS_KEY_ID`] || process.env[`${e}APPLICATION_KEY_ID`] || "",
|
|
@@ -109,5 +135,6 @@ function g(o, s = {}) {
|
|
|
109
135
|
};
|
|
110
136
|
}
|
|
111
137
|
export {
|
|
112
|
-
|
|
138
|
+
U as default,
|
|
139
|
+
U as manageStorage
|
|
113
140
|
};
|
package/package.json
CHANGED