@allegria/aws-file-manager 0.0.1 → 1.0.2
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 +157 -18
- package/dist/cjs/aws-file-manager.d.ts +318 -0
- package/dist/cjs/aws-file-manager.js +422 -0
- package/dist/cjs/aws-file-manager.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/examples/01-setup.d.ts +14 -0
- package/dist/examples/01-setup.js +42 -0
- package/dist/examples/01-setup.js.map +1 -0
- package/dist/examples/02-upload-multer.d.ts +13 -0
- package/dist/examples/02-upload-multer.js +63 -0
- package/dist/examples/02-upload-multer.js.map +1 -0
- package/dist/examples/03-upload-web.d.ts +12 -0
- package/dist/examples/03-upload-web.js +38 -0
- package/dist/examples/03-upload-web.js.map +1 -0
- package/dist/examples/04-signed-urls.d.ts +12 -0
- package/dist/examples/04-signed-urls.js +48 -0
- package/dist/examples/04-signed-urls.js.map +1 -0
- package/dist/examples/05-download.d.ts +14 -0
- package/dist/examples/05-download.js +53 -0
- package/dist/examples/05-download.js.map +1 -0
- package/dist/examples/06-delete.d.ts +17 -0
- package/dist/examples/06-delete.js +44 -0
- package/dist/examples/06-delete.js.map +1 -0
- package/dist/examples/07-copy.d.ts +14 -0
- package/dist/examples/07-copy.js +35 -0
- package/dist/examples/07-copy.js.map +1 -0
- package/dist/examples/08-list-paginated.d.ts +16 -0
- package/dist/examples/08-list-paginated.js +60 -0
- package/dist/examples/08-list-paginated.js.map +1 -0
- package/dist/examples/09-exists.d.ts +13 -0
- package/dist/examples/09-exists.js +32 -0
- package/dist/examples/09-exists.js.map +1 -0
- package/dist/lib/aws-file-manager.d.ts +262 -102
- package/dist/lib/aws-file-manager.js +353 -118
- package/dist/lib/aws-file-manager.js.map +1 -1
- package/dist/tests/aws-file-manager.test.d.ts +1 -0
- package/dist/tests/aws-file-manager.test.js +359 -0
- package/dist/tests/aws-file-manager.test.js.map +1 -0
- package/examples/01-setup.ts +49 -0
- package/examples/02-upload-multer.ts +82 -0
- package/examples/03-upload-web.ts +46 -0
- package/examples/04-signed-urls.ts +55 -0
- package/examples/05-download.ts +64 -0
- package/examples/06-delete.ts +53 -0
- package/examples/07-copy.ts +47 -0
- package/examples/08-list-paginated.ts +71 -0
- package/examples/09-exists.ts +39 -0
- package/package.json +36 -9
|
@@ -1,158 +1,318 @@
|
|
|
1
1
|
import { S3Client, StorageClass } from "@aws-sdk/client-s3";
|
|
2
2
|
import type { Readable } from "stream";
|
|
3
3
|
/**
|
|
4
|
-
* Configuration options for
|
|
4
|
+
* Configuration options for AwsFileManager.
|
|
5
|
+
* Credentials are optional — when omitted, the SDK resolves them from the
|
|
6
|
+
* environment (IAM role, AWS_ACCESS_KEY_ID, ~/.aws/credentials, etc.).
|
|
7
|
+
* Prefer environment/IAM resolution in production.
|
|
5
8
|
*/
|
|
6
9
|
export interface AwsFileManagerConfig {
|
|
7
|
-
/** AWS region */
|
|
10
|
+
/** AWS region, e.g. 'us-east-1' */
|
|
8
11
|
region: string;
|
|
9
12
|
/** S3 bucket name */
|
|
10
13
|
bucketName: string;
|
|
11
|
-
/** AWS access key ID
|
|
14
|
+
/** AWS access key ID — omit to rely on environment/IAM resolution */
|
|
12
15
|
accessKeyId?: string;
|
|
13
|
-
/** AWS secret access key
|
|
16
|
+
/** AWS secret access key — omit to rely on environment/IAM resolution */
|
|
14
17
|
secretAccessKey?: string;
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Base path prefix prepended to every key (no leading/trailing slashes).
|
|
20
|
+
* Useful for namespacing all writes to a sub-folder, e.g. 'uploads'.
|
|
21
|
+
*/
|
|
18
22
|
basePath?: string;
|
|
19
|
-
/** URL
|
|
23
|
+
/** Signed URL TTL in seconds (default: 3600) */
|
|
20
24
|
urlExpirationSeconds?: number;
|
|
21
|
-
/** Default storage class
|
|
25
|
+
/** Default S3 storage class (default: INTELLIGENT_TIERING) */
|
|
22
26
|
storageClass?: StorageClass;
|
|
23
27
|
}
|
|
24
28
|
/**
|
|
25
|
-
*
|
|
29
|
+
* A normalised file input that works for both server-side (multer Buffer) and
|
|
30
|
+
* browser-side (Web API File) callers. Use `fromMullerFile()` or
|
|
31
|
+
* `fromWebFile()` helpers to construct one, or build it directly.
|
|
26
32
|
*/
|
|
33
|
+
export interface FileInput {
|
|
34
|
+
/** Raw file bytes */
|
|
35
|
+
buffer: Buffer;
|
|
36
|
+
/** Original filename as provided by the uploader */
|
|
37
|
+
originalName: string;
|
|
38
|
+
/** MIME type, e.g. 'image/jpeg' */
|
|
39
|
+
mimeType: string;
|
|
40
|
+
/** File size in bytes */
|
|
41
|
+
size: number;
|
|
42
|
+
}
|
|
43
|
+
/** Options that control how a single file is stored in S3. */
|
|
27
44
|
export interface UploadOptions {
|
|
28
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Full S3 key (path + filename) for this object, relative to basePath.
|
|
47
|
+
* When provided, `folder` and `generateUniqueFileName` are ignored.
|
|
48
|
+
* Prefer this in pipeline usage where the calling code owns key generation.
|
|
49
|
+
*/
|
|
50
|
+
key?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Folder path appended to basePath (no leading/trailing slashes).
|
|
53
|
+
* Used when `key` is not provided.
|
|
54
|
+
*/
|
|
29
55
|
folder?: string;
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/** Custom file name */
|
|
56
|
+
/**
|
|
57
|
+
* Custom filename to use instead of originalName.
|
|
58
|
+
* Used when `key` is not provided.
|
|
59
|
+
*/
|
|
35
60
|
fileName?: string;
|
|
36
|
-
|
|
37
|
-
|
|
61
|
+
/**
|
|
62
|
+
* When true and `key` is not provided, replaces the filename with a UUID
|
|
63
|
+
* while preserving the extension. Prevents collisions under concurrent
|
|
64
|
+
* uploads.
|
|
65
|
+
*/
|
|
66
|
+
generateUniqueFileName?: boolean;
|
|
67
|
+
/** Override the detected MIME type */
|
|
68
|
+
contentType?: string;
|
|
69
|
+
/** Arbitrary string metadata stored alongside the S3 object */
|
|
38
70
|
metadata?: Record<string, string>;
|
|
39
|
-
/**
|
|
71
|
+
/** Per-upload storage class override */
|
|
40
72
|
storageClass?: StorageClass;
|
|
41
73
|
}
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
*/
|
|
45
|
-
export interface FindOptions {
|
|
46
|
-
/** Folder path to search in (appended to basePath) */
|
|
74
|
+
/** Options for listing objects in a folder */
|
|
75
|
+
export interface ListOptions {
|
|
76
|
+
/** Folder path appended to basePath */
|
|
47
77
|
folder?: string;
|
|
48
|
-
/**
|
|
78
|
+
/** Additional prefix filter within the folder */
|
|
79
|
+
prefix?: string;
|
|
80
|
+
/** Maximum number of keys to return (1–1000, default: 1000) */
|
|
49
81
|
maxResults?: number;
|
|
50
|
-
/** Continuation token for pagination */
|
|
82
|
+
/** Continuation token from a previous list call for pagination */
|
|
51
83
|
continuationToken?: string;
|
|
52
|
-
/** Filter by file prefix */
|
|
53
|
-
prefix?: string;
|
|
54
|
-
/** Generate signed URLs for the files */
|
|
55
|
-
generateUrls?: boolean;
|
|
56
84
|
}
|
|
57
|
-
/**
|
|
58
|
-
|
|
59
|
-
*/
|
|
60
|
-
|
|
61
|
-
/**
|
|
85
|
+
/** Options for generating a signed URL for an existing key */
|
|
86
|
+
export interface SignedUrlOptions {
|
|
87
|
+
/** TTL override in seconds. Falls back to instance default. */
|
|
88
|
+
expiresIn?: number;
|
|
89
|
+
/**
|
|
90
|
+
* Sets Content-Disposition on the response.
|
|
91
|
+
* 'inline' — browser renders the file (images, PDFs).
|
|
92
|
+
* 'attachment' — browser downloads the file.
|
|
93
|
+
*/
|
|
94
|
+
disposition?: "inline" | "attachment";
|
|
95
|
+
/** Original filename to suggest in Content-Disposition (for attachments) */
|
|
96
|
+
fileName?: string;
|
|
97
|
+
}
|
|
98
|
+
/** Metadata returned after a successful upload */
|
|
99
|
+
export interface UploadResult {
|
|
100
|
+
/** Full S3 key for this object — store this in your database */
|
|
62
101
|
key: string;
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
contentType?: string;
|
|
73
|
-
/** The ETag from S3 */
|
|
102
|
+
/** Original filename as provided by the uploader */
|
|
103
|
+
originalName: string;
|
|
104
|
+
/** Filename used in S3 (may differ from originalName if UUID was generated) */
|
|
105
|
+
storedName: string;
|
|
106
|
+
/** Stored file size in bytes */
|
|
107
|
+
size: number;
|
|
108
|
+
/** MIME type */
|
|
109
|
+
contentType: string;
|
|
110
|
+
/** S3 ETag (without surrounding quotes) */
|
|
74
111
|
etag?: string;
|
|
75
|
-
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
112
|
+
/** Storage class applied */
|
|
113
|
+
storageClass: string;
|
|
114
|
+
}
|
|
115
|
+
/** A single entry returned by list() */
|
|
116
|
+
export interface ListEntry {
|
|
117
|
+
/** Full S3 key */
|
|
118
|
+
key: string;
|
|
119
|
+
/** Filename portion of the key */
|
|
120
|
+
fileName: string;
|
|
121
|
+
/** Object size in bytes */
|
|
122
|
+
size: number;
|
|
123
|
+
/** Last modified timestamp */
|
|
124
|
+
lastModified: Date;
|
|
125
|
+
/** Storage class */
|
|
80
126
|
storageClass?: string;
|
|
127
|
+
/** ETag (without surrounding quotes) */
|
|
128
|
+
etag?: string;
|
|
81
129
|
}
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
/** List of files found */
|
|
87
|
-
files: FileResult[];
|
|
88
|
-
/** Continuation token for pagination */
|
|
130
|
+
/** Paginated result from list() */
|
|
131
|
+
export interface ListResult {
|
|
132
|
+
entries: ListEntry[];
|
|
133
|
+
/** Pass this to the next list() call to fetch the next page */
|
|
89
134
|
continuationToken?: string;
|
|
90
|
-
/**
|
|
135
|
+
/** True when there are more results beyond this page */
|
|
91
136
|
hasMore: boolean;
|
|
92
137
|
}
|
|
93
138
|
export type DownloadMode = "buffer" | "stream";
|
|
94
139
|
export type DownloadResult<T extends DownloadMode> = T extends "buffer" ? {
|
|
95
140
|
buffer: Buffer;
|
|
96
|
-
metadata:
|
|
141
|
+
metadata: ObjectMetadata;
|
|
97
142
|
} : {
|
|
98
143
|
stream: Readable;
|
|
99
|
-
metadata:
|
|
144
|
+
metadata: ObjectMetadata;
|
|
100
145
|
};
|
|
146
|
+
export interface ObjectMetadata {
|
|
147
|
+
contentType?: string;
|
|
148
|
+
contentLength?: number;
|
|
149
|
+
lastModified?: Date;
|
|
150
|
+
metadata?: Record<string, string>;
|
|
151
|
+
storageClass?: string;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Adapts a multer file (Express.Multer.File) to FileInput.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* // In an Express route with multer:
|
|
158
|
+
* const fileInput = fromMulterFile(req.file);
|
|
159
|
+
*/
|
|
160
|
+
export declare function fromMulterFile(multerFile: {
|
|
161
|
+
buffer: Buffer;
|
|
162
|
+
originalname: string;
|
|
163
|
+
mimetype: string;
|
|
164
|
+
size: number;
|
|
165
|
+
}): FileInput;
|
|
101
166
|
/**
|
|
102
|
-
*
|
|
167
|
+
* Adapts a Web API File (browser / Next.js App Router FormData) to FileInput.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* // In a Next.js App Router route handler:
|
|
171
|
+
* const formData = await request.formData();
|
|
172
|
+
* const fileInput = await fromWebFile(formData.get('file') as File);
|
|
103
173
|
*/
|
|
104
|
-
export
|
|
105
|
-
|
|
106
|
-
private
|
|
107
|
-
private
|
|
108
|
-
private basePath;
|
|
109
|
-
private urlExpirationSeconds;
|
|
110
|
-
private storageClass;
|
|
111
|
-
static DEFAULTS: {
|
|
112
|
-
storageClass: "INTELLIGENT_TIERING";
|
|
113
|
-
urlExpirationSeconds:
|
|
174
|
+
export declare function fromWebFile(webFile: File): Promise<FileInput>;
|
|
175
|
+
export declare class AwsFileManager {
|
|
176
|
+
private readonly s3;
|
|
177
|
+
private readonly bucketName;
|
|
178
|
+
private readonly basePath;
|
|
179
|
+
private readonly urlExpirationSeconds;
|
|
180
|
+
private readonly storageClass;
|
|
181
|
+
static readonly DEFAULTS: {
|
|
182
|
+
readonly storageClass: "INTELLIGENT_TIERING";
|
|
183
|
+
readonly urlExpirationSeconds: 3600;
|
|
114
184
|
};
|
|
185
|
+
constructor(config: AwsFileManagerConfig);
|
|
115
186
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
187
|
+
* Uploads a file to S3 and returns storage metadata (no URL).
|
|
188
|
+
*
|
|
189
|
+
* The returned `key` is what you persist to your database. Generate signed
|
|
190
|
+
* URLs on demand via `getSignedUrl()` — never store them, as they expire.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* // In a pipeline step:
|
|
194
|
+
* const result = await fileManager.upload(ctx.fileInput, { key: ctx.s3Key });
|
|
195
|
+
* ctx.uploadResult = result;
|
|
118
196
|
*/
|
|
119
|
-
|
|
197
|
+
upload(file: FileInput, options?: UploadOptions): Promise<UploadResult>;
|
|
198
|
+
/**
|
|
199
|
+
* Generates a short-lived signed URL for a private S3 object.
|
|
200
|
+
*
|
|
201
|
+
* Call this at request time; never store the returned URL. Signed URLs
|
|
202
|
+
* are credentials — treat them accordingly.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* const url = await fileManager.getSignedUrl(file.s3Key, {
|
|
206
|
+
* disposition: 'inline',
|
|
207
|
+
* fileName: file.originalName,
|
|
208
|
+
* });
|
|
209
|
+
*/
|
|
210
|
+
getSignedUrl(key: string, options?: SignedUrlOptions): Promise<string>;
|
|
211
|
+
/**
|
|
212
|
+
* Downloads an S3 object as a Buffer or a Node.js Readable stream.
|
|
213
|
+
*
|
|
214
|
+
* Use `'stream'` (default) when piping to a response or writing to disk.
|
|
215
|
+
* Use `'buffer'` when you need the full bytes in memory (e.g. for processing).
|
|
216
|
+
*
|
|
217
|
+
* Returns `null` when the object does not exist.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* const result = await fileManager.download(key, 'buffer');
|
|
221
|
+
* if (result) {
|
|
222
|
+
* const { buffer, metadata } = result;
|
|
223
|
+
* }
|
|
224
|
+
*/
|
|
225
|
+
download<T extends DownloadMode = "stream">(key: string, mode?: T): Promise<DownloadResult<T> | null>;
|
|
226
|
+
/**
|
|
227
|
+
* Permanently deletes an object from S3.
|
|
228
|
+
*
|
|
229
|
+
* Note: this only removes the S3 object. Your database record should be
|
|
230
|
+
* soft-deleted first, and this called as a cleanup step after the DB
|
|
231
|
+
* transaction commits — so a crash mid-way leaves a recoverable orphan
|
|
232
|
+
* rather than a missing file with an intact DB row.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* await fileManager.delete(file.s3Key);
|
|
236
|
+
*/
|
|
237
|
+
delete(key: string): Promise<void>;
|
|
120
238
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* @
|
|
239
|
+
* Deletes multiple objects in a single call.
|
|
240
|
+
* Silently skips keys that do not exist.
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* await fileManager.deleteMany([original.s3Key, thumbnail.s3Key]);
|
|
125
244
|
*/
|
|
126
|
-
|
|
245
|
+
deleteMany(keys: string[]): Promise<void>;
|
|
127
246
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
247
|
+
* Copies an object within the same bucket.
|
|
248
|
+
*
|
|
249
|
+
* Useful when duplicating files across entities (e.g. cloning a note)
|
|
250
|
+
* or reorganising keys without re-uploading bytes.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* await fileManager.copy(sourceFile.s3Key, newKey);
|
|
132
254
|
*/
|
|
133
|
-
|
|
255
|
+
copy(sourceKey: string, destinationKey: string, options?: {
|
|
256
|
+
storageClass?: StorageClass;
|
|
257
|
+
metadata?: Record<string, string>;
|
|
258
|
+
}): Promise<void>;
|
|
134
259
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
260
|
+
* Lists objects under a folder prefix.
|
|
261
|
+
*
|
|
262
|
+
* Results are paginated. Pass the returned `continuationToken` back
|
|
263
|
+
* into subsequent calls to walk through large result sets.
|
|
264
|
+
*
|
|
265
|
+
* Primarily intended for the server-side storage reconciliation job
|
|
266
|
+
* (nightly scan to reconcile DB byte counts against S3 actuals).
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* let token: string | undefined;
|
|
270
|
+
* do {
|
|
271
|
+
* const result = await fileManager.list({ folder: 'acme-corp', continuationToken: token });
|
|
272
|
+
* processBatch(result.entries);
|
|
273
|
+
* token = result.continuationToken;
|
|
274
|
+
* } while (result.hasMore);
|
|
139
275
|
*/
|
|
140
|
-
|
|
276
|
+
list(options?: ListOptions): Promise<ListResult>;
|
|
141
277
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* @
|
|
278
|
+
* Returns true if an object exists at the given key.
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* if (!(await fileManager.exists(key))) {
|
|
282
|
+
* throw new Error('Referenced file is missing from storage');
|
|
283
|
+
* }
|
|
145
284
|
*/
|
|
146
|
-
|
|
147
|
-
buffer: Buffer;
|
|
148
|
-
metadata: Record<string, any>;
|
|
149
|
-
} | {
|
|
150
|
-
stream: Readable;
|
|
151
|
-
metadata: Record<string, any>;
|
|
152
|
-
} | null>;
|
|
285
|
+
exists(key: string): Promise<boolean>;
|
|
153
286
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
287
|
+
* Returns the underlying S3Client for operations not covered by this class.
|
|
288
|
+
* Use sparingly — prefer adding methods here so the abstraction stays coherent.
|
|
156
289
|
*/
|
|
157
290
|
getS3Client(): S3Client;
|
|
291
|
+
/**
|
|
292
|
+
* Resolves the final S3 key and stored filename from upload options.
|
|
293
|
+
*
|
|
294
|
+
* Priority order for the key:
|
|
295
|
+
* 1. options.key (explicit full key — pipeline usage)
|
|
296
|
+
* 2. basePath + folder + fileName (if provided)
|
|
297
|
+
* 3. basePath + folder + UUID-based name (if generateUniqueFileName)
|
|
298
|
+
* 4. basePath + folder + originalName (fallback)
|
|
299
|
+
*/
|
|
300
|
+
private resolveKey;
|
|
301
|
+
/**
|
|
302
|
+
* Builds a full S3 key from basePath, optional folder, and filename.
|
|
303
|
+
*/
|
|
304
|
+
private buildFullKey;
|
|
305
|
+
/**
|
|
306
|
+
* Builds a prefix string for list operations.
|
|
307
|
+
*/
|
|
308
|
+
private buildPrefix;
|
|
309
|
+
/**
|
|
310
|
+
* Generates a UUID-based filename, preserving the original extension.
|
|
311
|
+
* UUIDs are collision-proof under any upload concurrency.
|
|
312
|
+
*/
|
|
313
|
+
private makeUniqueFileName;
|
|
314
|
+
/**
|
|
315
|
+
* Builds a Content-Disposition header value from SignedUrlOptions.
|
|
316
|
+
*/
|
|
317
|
+
private buildContentDisposition;
|
|
158
318
|
}
|