manage-storage 0.0.62 → 0.0.63

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.
@@ -1,5 +1,10 @@
1
+ import { S3Client } from '@aws-sdk/client-s3';
2
+
1
3
  /**
2
- * Storage operation action type
4
+ * Storage operation action type.
5
+ *
6
+ * @deprecated Actions belong to the {@link manageStorage} function. Call the
7
+ * matching {@link StorageManager} method instead.
3
8
  */
4
9
  export declare type Action = "upload" | "download" | "delete" | "list" | "deleteAll" | "copy" | "rename";
5
10
 
@@ -39,96 +44,61 @@ export declare interface DeleteResult {
39
44
  }
40
45
 
41
46
  /**
42
- * Universal cloud storage manager supporting Amazon S3, Backblaze B2, and Cloudflare R2.
43
- * Automatically detects the configured provider from environment variables.
44
- *
45
- * @param action - The storage operation to perform
46
- * @param options - Operation-specific options
47
- * @returns
48
- * - upload: Returns {success: true, key: string}
49
- * - download: Returns the file content as a string
50
- * - delete: Returns {success: true, key: string}
51
- * - list: Returns array of object keys
52
- * - deleteAll: Returns {success: true, count: number}
53
- * @throws {Error} When credentials are missing or operation fails
54
- *
55
- * @example
56
- * // Upload a file
57
- * await manageStorage('upload', {
58
- * key: 'documents/report.pdf',
59
- * body: fileContent
60
- * });
47
+ * Which provider the environment is configured for, by looking for a complete
48
+ * set of credentials under each prefix in turn: Cloudflare, Backblaze, Amazon.
61
49
  *
62
- * @example
63
- * // Download a file
64
- * const data = await manageStorage('download', {
65
- * key: 'documents/report.pdf'
66
- * });
67
- *
68
- * @example
69
- * // List all files
70
- * const files = await manageStorage('list');
50
+ * @returns The detected provider, or `cloudflare` when none is fully configured
51
+ * (in which case the missing credentials are reported on the first call).
71
52
  *
72
53
  * @example
73
- * // Delete a file
74
- * await manageStorage('delete', {
75
- * key: 'documents/report.pdf'
76
- * });
54
+ * // With CLOUDFLARE_* set
55
+ * detectProvider(); // "cloudflare"
56
+ */
57
+ export declare function detectProvider(): Provider;
58
+
59
+ /**
60
+ * The original action-string API, kept working for already-published callers.
77
61
  *
78
- * @example
79
- * // Copy a file
80
- * await manageStorage('copy', {
81
- * key: 'documents/report.pdf',
82
- * destinationKey: 'documents/report-copy.pdf'
83
- * });
62
+ * @deprecated Use {@link StorageManager}. `manageStorage("upload", { key, body })`
63
+ * becomes `new StorageManager().upload(key, body)` — same requests, but the
64
+ * arguments each method needs are in its signature instead of an option bag
65
+ * whose required fields change per action.
84
66
  *
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
- * });
67
+ * @param action - `upload`, `download`, `delete`, `list`, `deleteAll`, `copy` or `rename`
68
+ * @param options - The key, body, destination and credential overrides
91
69
  *
92
70
  * @example
93
- * // Force a specific provider with runtime credentials
94
- * await manageStorage('upload', {
95
- * key: 'test.txt',
96
- * body: 'Hello!',
97
- * provider: 'cloudflare',
98
- * BUCKET_NAME: 'my-bucket',
99
- * ACCESS_KEY_ID: 'key-id',
100
- * SECRET_ACCESS_KEY: 'secret',
101
- * BUCKET_URL: 'https://account.r2.cloudflarestorage.com'
102
- * });
71
+ * // Deprecated:
72
+ * await manageStorage("upload", { key: "a.txt", body: "hi" });
73
+ * // Current:
74
+ * await new StorageManager().upload("a.txt", "hi");
103
75
  */
104
- declare function manageStorage(action: "upload", options: StorageOptions & {
76
+ export declare function manageStorage(action: "upload", options: StorageOptions & {
105
77
  key: string;
106
- body: string | Buffer | ReadableStream;
78
+ body: StorageBody;
107
79
  }): Promise<UploadResult>;
108
80
 
109
- declare function manageStorage(action: "download", options: StorageOptions & {
81
+ export declare function manageStorage(action: "download", options: StorageOptions & {
110
82
  key: string;
111
83
  }): Promise<string>;
112
84
 
113
- declare function manageStorage(action: "delete", options: StorageOptions & {
85
+ export declare function manageStorage(action: "delete", options: StorageOptions & {
114
86
  key: string;
115
87
  }): Promise<DeleteResult>;
116
88
 
117
- declare function manageStorage(action: "list", options?: StorageOptions): Promise<string[]>;
89
+ export declare function manageStorage(action: "list", options?: StorageOptions): Promise<string[]>;
118
90
 
119
- declare function manageStorage(action: "deleteAll", options?: StorageOptions): Promise<DeleteAllResult>;
91
+ export declare function manageStorage(action: "deleteAll", options?: StorageOptions): Promise<DeleteAllResult>;
120
92
 
121
- declare function manageStorage(action: "copy", options: StorageOptions & {
93
+ export declare function manageStorage(action: "copy", options: StorageOptions & {
122
94
  key: string;
123
95
  destinationKey: string;
124
96
  }): Promise<CopyResult>;
125
97
 
126
- declare function manageStorage(action: "rename", options: StorageOptions & {
98
+ export declare function manageStorage(action: "rename", options: StorageOptions & {
127
99
  key: string;
128
100
  destinationKey: string;
129
101
  }): Promise<RenameResult>;
130
- export default manageStorage;
131
- export { manageStorage }
132
102
 
133
103
  /**
134
104
  * Cloud storage provider type
@@ -149,7 +119,231 @@ export declare interface RenameResult {
149
119
  }
150
120
 
151
121
  /**
152
- * Options for storage operations
122
+ * Merge explicit config over the environment for one provider.
123
+ *
124
+ * @param config - Explicit values, each of which wins over its env var
125
+ * @returns Everything the S3 client needs, with missing pieces left empty for
126
+ * the caller to report on
127
+ *
128
+ * @example
129
+ * resolveConfig({ provider: "backblaze", bucket: "my-bucket" });
130
+ */
131
+ export declare function resolveConfig(config?: StorageConfig): ResolvedConfig;
132
+
133
+ /** The fully resolved connection details, after env vars and overrides merge. */
134
+ export declare interface ResolvedConfig {
135
+ provider: Provider;
136
+ region: string;
137
+ endpoint: string;
138
+ bucket: string;
139
+ accessKeyId: string;
140
+ secretAccessKey: string;
141
+ }
142
+
143
+ /** What an upload accepts as its payload. */
144
+ export declare type StorageBody = string | Uint8Array | Buffer | ReadableStream;
145
+
146
+ /**
147
+ * How a {@link StorageManager} reaches its bucket.
148
+ *
149
+ * Every field is optional: anything left out falls back to the `*_BUCKET_NAME`,
150
+ * `*_ACCESS_KEY_ID`, `*_SECRET_ACCESS_KEY` and `*_BUCKET_URL` environment
151
+ * variables for the resolved provider. Edge runtimes have no `process.env` to
152
+ * read, so there you pass all four.
153
+ */
154
+ export declare interface StorageConfig {
155
+ /** Which provider to talk to. Auto-detected from the environment if omitted. */
156
+ provider?: Provider;
157
+ /** Bucket name. Falls back to `<PROVIDER>_BUCKET_NAME`. */
158
+ bucket?: string;
159
+ /** Access key id. Falls back to `<PROVIDER>_ACCESS_KEY_ID`. */
160
+ accessKeyId?: string;
161
+ /** Secret access key. Falls back to `<PROVIDER>_SECRET_ACCESS_KEY`. */
162
+ secretAccessKey?: string;
163
+ /** S3 endpoint for the bucket. Falls back to `<PROVIDER>_BUCKET_URL`. */
164
+ endpoint?: string;
165
+ /** Region. Only S3 uses it; R2 and B2 are always `auto`. */
166
+ region?: string;
167
+ /**
168
+ * A pre-built client, for tests or for a client configured with retry,
169
+ * proxy or credential-provider options this class does not expose.
170
+ */
171
+ client?: S3Client;
172
+ }
173
+
174
+ /**
175
+ * One bucket, on Amazon S3, Cloudflare R2 or Backblaze B2, as an object with a
176
+ * method per operation.
177
+ *
178
+ * Credentials are resolved once, in the constructor, from the config you pass
179
+ * and the environment: `new StorageManager()` with the env vars set is the whole
180
+ * setup. The client is built lazily on the first call, so constructing a manager
181
+ * for a provider you end up not using costs nothing.
182
+ *
183
+ * @example
184
+ * // Env vars configure it; the provider is detected from which ones are set.
185
+ * const storage = new StorageManager();
186
+ *
187
+ * await storage.upload("notes/memo.txt", "Hello, World!");
188
+ * const text = await storage.download("notes/memo.txt");
189
+ * const keys = await storage.list("notes/");
190
+ * await storage.rename("notes/memo.txt", "archive/memo.txt");
191
+ * await storage.delete("archive/memo.txt");
192
+ *
193
+ * @example
194
+ * // Cloudflare Workers: no process.env, so pass the bindings in.
195
+ * const storage = new StorageManager({
196
+ * provider: "cloudflare",
197
+ * bucket: env.CLOUDFLARE_BUCKET_NAME,
198
+ * accessKeyId: env.CLOUDFLARE_ACCESS_KEY_ID,
199
+ * secretAccessKey: env.CLOUDFLARE_SECRET_ACCESS_KEY,
200
+ * endpoint: env.CLOUDFLARE_BUCKET_URL,
201
+ * });
202
+ */
203
+ declare class StorageManager_2 {
204
+ /** The resolved connection details: provider, bucket, endpoint, region. */
205
+ readonly config: ResolvedConfig;
206
+ private readonly injectedClient?;
207
+ private cachedClient?;
208
+ /**
209
+ * @param config - Connection details; anything omitted comes from the
210
+ * environment. Throws only when an operation runs without credentials, not
211
+ * here, so a manager can be constructed before the environment is loaded.
212
+ */
213
+ constructor(config?: StorageConfig);
214
+ /** Which provider this manager resolved to. */
215
+ get provider(): Provider;
216
+ /** Which bucket this manager reads and writes. */
217
+ get bucket(): string;
218
+ /**
219
+ * The S3 client, built on first use.
220
+ *
221
+ * The credential check lives here rather than in the constructor because
222
+ * `dotenv` is often loaded after the module graph is: failing at construction
223
+ * would break the common `new StorageManager()` at module scope.
224
+ */
225
+ private get client();
226
+ /**
227
+ * Send one command, tagging any failure with the provider and operation.
228
+ *
229
+ * The SDK's errors say what went wrong but not which bucket or provider they
230
+ * went wrong against, which is the first thing you need when two providers are
231
+ * configured.
232
+ */
233
+ private send;
234
+ /**
235
+ * Store bytes at a key, creating or replacing whatever was there.
236
+ *
237
+ * @param key - The object key/path
238
+ * @param body - String, Buffer, Uint8Array or stream to store
239
+ * @param options - `contentType` sets the stored MIME type, which is what a
240
+ * browser reads when the object is served from a public bucket
241
+ *
242
+ * @example
243
+ * await storage.upload("config/settings.json", JSON.stringify(settings), {
244
+ * contentType: "application/json",
245
+ * });
246
+ */
247
+ upload(key: string, body: StorageBody, options?: {
248
+ contentType?: string;
249
+ }): Promise<UploadResult>;
250
+ /**
251
+ * Read an object back as text.
252
+ *
253
+ * @param key - The object key/path
254
+ * @returns The object's content, decoded as UTF-8
255
+ * @throws When the key does not exist — there is no "missing" return value
256
+ *
257
+ * @example
258
+ * const settings = JSON.parse(await storage.download("config/settings.json"));
259
+ */
260
+ download(key: string): Promise<string>;
261
+ /**
262
+ * Read an object back as bytes, for anything that is not text.
263
+ *
264
+ * @param key - The object key/path
265
+ *
266
+ * @example
267
+ * const bytes = await storage.downloadBytes("images/logo.png");
268
+ */
269
+ downloadBytes(key: string): Promise<Uint8Array>;
270
+ /**
271
+ * Every key in the bucket, or every key under a prefix.
272
+ *
273
+ * Paginated: S3 returns at most 1000 keys per response, and this follows the
274
+ * continuation token to the end rather than silently returning the first page.
275
+ *
276
+ * @param prefix - Only keys starting with this string. Keys are flat — there
277
+ * are no folders — so `"documents/"` is just a prefix match.
278
+ *
279
+ * @example
280
+ * const invoices = await storage.list("invoices/2024/");
281
+ */
282
+ list(prefix?: string): Promise<string[]>;
283
+ /**
284
+ * Whether a key exists, without downloading it.
285
+ *
286
+ * @param key - The object key/path
287
+ *
288
+ * @example
289
+ * if (await storage.exists("config/settings.json")) { … }
290
+ */
291
+ exists(key: string): Promise<boolean>;
292
+ /**
293
+ * Duplicate an object to another key.
294
+ *
295
+ * @param key - The source key
296
+ * @param destinationKey - The key to copy it to
297
+ *
298
+ * @example
299
+ * await storage.copy("config/settings.json", "config/settings.backup.json");
300
+ */
301
+ copy(key: string, destinationKey: string): Promise<CopyResult>;
302
+ /**
303
+ * Move an object to another key: a copy, then a delete.
304
+ *
305
+ * Not atomic — S3 has no rename — so an interruption between the two can leave
306
+ * both keys in place. Nothing is deleted unless the copy succeeded.
307
+ *
308
+ * @param key - The current key
309
+ * @param destinationKey - The key to move it to
310
+ *
311
+ * @example
312
+ * await storage.rename("temp/draft.md", "published/article.md");
313
+ */
314
+ rename(key: string, destinationKey: string): Promise<RenameResult>;
315
+ /**
316
+ * Remove one object. Deleting a key that does not exist succeeds — that is how
317
+ * S3 behaves, and this does not paper over it with a lookup first.
318
+ *
319
+ * @param key - The object key/path
320
+ *
321
+ * @example
322
+ * await storage.delete("notes/memo.txt");
323
+ */
324
+ delete(key: string): Promise<DeleteResult>;
325
+ /**
326
+ * Empty the bucket, or everything under a prefix. Irreversible.
327
+ *
328
+ * Lists and deletes in batches of 1000 (the API's per-request limit) until
329
+ * nothing is left, so it finishes the job on a bucket of any size.
330
+ *
331
+ * @param prefix - Restrict the deletion to keys starting with this string.
332
+ * Omitting it deletes everything in the bucket.
333
+ *
334
+ * @example
335
+ * const { count } = await storage.deleteAll("temp/");
336
+ */
337
+ deleteAll(prefix?: string): Promise<DeleteAllResult>;
338
+ }
339
+ export { StorageManager_2 as StorageManager }
340
+ export default StorageManager_2;
341
+
342
+ /**
343
+ * Options for storage operations.
344
+ *
345
+ * @deprecated The option bag belongs to {@link manageStorage}. {@link StorageManager}
346
+ * takes credentials once in its constructor and the key as an argument.
153
347
  */
154
348
  export declare interface StorageOptions {
155
349
  /** The object key/path (required for upload, download, delete) */
@@ -157,7 +351,7 @@ export declare interface StorageOptions {
157
351
  /** The destination key/path (required for copy, rename) */
158
352
  destinationKey?: string;
159
353
  /** The file content to upload (required for upload) */
160
- body?: string | Buffer | ReadableStream;
354
+ body?: StorageBody;
161
355
  /** Force a specific cloud provider (auto-detected if omitted) */
162
356
  provider?: Provider;
163
357
  /** Override bucket name at runtime */
@@ -1 +1 @@
1
- import"dotenv";import{m as a,m as e}from"./manage-storage-CGRNU6uH.js";export{a as default,e as manageStorage};
1
+ import"dotenv";import{e as a,e,f as o,m as r,l as t}from"./manage-storage-De0Cb_cq.js";export{a as StorageManager,e as default,o as detectProvider,r as manageStorage,t as resolveConfig};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "manage-storage",
3
- "version": "0.0.62",
3
+ "version": "0.0.63",
4
4
  "description": "Storage manager supporting AWS S3, Backblaze B2, and Cloudflare R2 with automatic provider detection",
5
5
  "main": "./dist/manage-storage.js",
6
6
  "module": "./dist/manage-storage.js",
@@ -1 +0,0 @@
1
- import{t as e,f as t}from"./manage-storage-CGRNU6uH.js";import{E as r,a,H as s,I as n,M as i,c as o,S as l,d as c,b as m,e as d,g as y,h,i as u,r as S,j as f}from"./manage-storage-CGRNU6uH.js";class p{marshaller;serializer;deserializer;serdeContext;defaultContentType;constructor({marshaller:e,serializer:t,deserializer:r,serdeContext:a,defaultContentType:s}){this.marshaller=e,this.serializer=t,this.deserializer=r,this.serdeContext=a,this.defaultContentType=s}async serializeEventStream({eventStream:e,requestSchema:t,initialRequest:r}){const a=this.marshaller,s=t.getEventStreamMember(),n=t.getMemberSchema(s),i=this.serializer,o=this.defaultContentType,l=/* @__PURE__ */Symbol("initialRequestMarker"),c={async*[Symbol.asyncIterator](){if(r){const e={":event-type":{type:"string",value:"initial-request"},":message-type":{type:"string",value:"event"},":content-type":{type:"string",value:o}};i.write(t,r);const a=i.flush();yield{[l]:!0,headers:e,body:a}}for await(const t of e)yield t}};return a.serialize(c,e=>{if(e[l])return{headers:e.headers,body:e.body};let t="";for(const n in e)if("__type"!==n){t=n;break}const{additionalHeaders:r,body:a,eventType:s,explicitPayloadContentType:i}=this.writeEventBody(t,n,e);return{headers:{":event-type":{type:"string",value:s},":message-type":{type:"string",value:"event"},":content-type":{type:"string",value:i??o},...r},body:a}})}async deserializeEventStream({response:t,responseSchema:r,initialResponseContainer:a}){const s=this.marshaller,n=r.getEventStreamMember(),i=r.getMemberSchema(n).getMemberSchemas(),o=/* @__PURE__ */Symbol("initialResponseMarker"),l=s.deserialize(t.body,async t=>{let a="";for(const e in t)if("__type"!==e){a=e;break}const s=t[a].body;if("initial-response"===a){const e=await this.deserializer.read(r,s);return delete e[n],{[o]:!0,...e}}if(a in i){const r=i[a];if(r.isStructSchema()){const n={};let i=!1;for(const[o,l]of r.structIterator()){const{eventHeader:r,eventPayload:c}=l.getMergedTraits();if(i=i||Boolean(r||c),c)l.isBlobSchema()?n[o]=s:l.isStringSchema()?n[o]=(this.serdeContext?.utf8Encoder??e)(s):l.isStructSchema()&&(n[o]=await this.deserializer.read(l,s));else if(r){const e=t[a].headers[o]?.value;null!=e&&(l.isNumericSchema()?n[o]=e&&"object"==typeof e&&"bytes"in e?BigInt(e.toString()):Number(e):n[o]=e)}}if(i)return{[a]:n};if(0===s.byteLength)return{[a]:{}}}return{[a]:await this.deserializer.read(r,s)}}return{$unknown:t}}),c=l[Symbol.asyncIterator](),m=await c.next();if(m.done)return l;if(m.value?.[o]){if(!r)throw new Error("@smithy::core/protocols - initial-response event encountered in event stream but no response schema given.");for(const e in m.value)a[e]=m.value[e]}return{async*[Symbol.asyncIterator](){for(m?.value?.[o]||(yield m.value);;){const{done:e,value:t}=await c.next();if(e)break;yield t}}}}writeEventBody(e,r,a){const s=this.serializer;let n,i=e,o=null;const l={};if(r.getSchema()[4].includes(e)){const t=r.getMemberSchema(e);if(t.isStructSchema()){for(const[r,s]of t.structIterator()){const{eventHeader:t,eventPayload:n}=s.getMergedTraits();if(n)o=r;else if(t){const t=a[e][r];let n="binary";s.isNumericSchema()?n=(-2)**31<=t&&t<=2**31-1?"integer":"long":s.isTimestampSchema()?n="timestamp":s.isStringSchema()?n="string":s.isBooleanSchema()&&(n="boolean"),null!=t&&(l[r]={type:n,value:t},delete a[e][r])}}if(null!==o){const r=t.getMemberSchema(o);r.isBlobSchema()?n="application/octet-stream":r.isStringSchema()&&(n="text/plain"),s.write(r,a[e][o])}else s.write(t,a[e])}else{if(!t.isUnitSchema())throw new Error("@smithy/core/event-streams - non-struct member not supported in event stream union.");s.write(t,{})}}else{const[t,r]=a[e];i=t,s.write(15,r)}const c=s.flush()??new Uint8Array;return{body:"string"==typeof c?(this.serdeContext?.utf8Decoder??t)(c):c,eventType:i,explicitPayloadContentType:n,additionalHeaders:l}}}export{r as EventStreamCodec,a as EventStreamMarshaller,p as EventStreamSerde,s as HeaderMarshaller,n as Int64,i as MessageDecoderStream,o as MessageEncoderStream,l as SmithyMessageDecoderStream,c as SmithyMessageEncoderStream,m as UniversalEventStreamMarshaller,d as eventStreamSerdeProvider,y as getChunkedStream,h as getMessageUnmarshaller,u as iterableToReadableStream,S as readableStreamToIterable,f as resolveEventStreamSerdeConfig};