@allegria/aws-file-manager 1.0.1 → 1.0.3

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.
Files changed (38) hide show
  1. package/dist/cjs/aws-file-manager.d.ts +318 -0
  2. package/dist/cjs/aws-file-manager.js +426 -0
  3. package/dist/cjs/aws-file-manager.js.map +1 -0
  4. package/dist/cjs/package.json +3 -0
  5. package/dist/lib/aws-file-manager.d.ts +1 -1
  6. package/dist/lib/aws-file-manager.js +5 -1
  7. package/dist/lib/aws-file-manager.js.map +1 -1
  8. package/package.json +27 -9
  9. package/dist/examples/01-setup.d.ts +0 -14
  10. package/dist/examples/01-setup.js +0 -42
  11. package/dist/examples/01-setup.js.map +0 -1
  12. package/dist/examples/02-upload-multer.d.ts +0 -13
  13. package/dist/examples/02-upload-multer.js +0 -63
  14. package/dist/examples/02-upload-multer.js.map +0 -1
  15. package/dist/examples/03-upload-web.d.ts +0 -12
  16. package/dist/examples/03-upload-web.js +0 -38
  17. package/dist/examples/03-upload-web.js.map +0 -1
  18. package/dist/examples/04-signed-urls.d.ts +0 -12
  19. package/dist/examples/04-signed-urls.js +0 -48
  20. package/dist/examples/04-signed-urls.js.map +0 -1
  21. package/dist/examples/05-download.d.ts +0 -14
  22. package/dist/examples/05-download.js +0 -53
  23. package/dist/examples/05-download.js.map +0 -1
  24. package/dist/examples/06-delete.d.ts +0 -17
  25. package/dist/examples/06-delete.js +0 -44
  26. package/dist/examples/06-delete.js.map +0 -1
  27. package/dist/examples/07-copy.d.ts +0 -14
  28. package/dist/examples/07-copy.js +0 -35
  29. package/dist/examples/07-copy.js.map +0 -1
  30. package/dist/examples/08-list-paginated.d.ts +0 -16
  31. package/dist/examples/08-list-paginated.js +0 -60
  32. package/dist/examples/08-list-paginated.js.map +0 -1
  33. package/dist/examples/09-exists.d.ts +0 -13
  34. package/dist/examples/09-exists.js +0 -32
  35. package/dist/examples/09-exists.js.map +0 -1
  36. package/dist/tests/aws-file-manager.test.d.ts +0 -1
  37. package/dist/tests/aws-file-manager.test.js +0 -359
  38. package/dist/tests/aws-file-manager.test.js.map +0 -1
@@ -1,63 +0,0 @@
1
- /**
2
- * 02-upload-multer.ts
3
- *
4
- * Uploading files from an Express route that uses multer for multipart parsing.
5
- *
6
- * Key points:
7
- * - Use `fromMulterFile()` to normalise multer's file shape into FileInput.
8
- * - `upload()` returns an UploadResult with the S3 key — persist this key
9
- * to your database, NOT a signed URL (URLs expire).
10
- * - Generate a signed URL on demand at request time via `getSignedUrl()`.
11
- */
12
- import express from "express";
13
- import multer from "multer";
14
- import { AwsFileManager, fromMulterFile, } from "@lib/aws-file-manager";
15
- const app = express();
16
- const upload = multer({ storage: multer.memoryStorage() });
17
- const fileManager = new AwsFileManager({
18
- region: process.env.AWS_REGION ?? "us-east-1",
19
- bucketName: process.env.S3_BUCKET_NAME,
20
- basePath: "uploads",
21
- });
22
- // POST /upload — accepts a single file field named 'file'
23
- app.post("/upload", upload.single("file"), async (req, res) => {
24
- if (!req.file) {
25
- return res.status(400).json({ error: "No file provided" });
26
- }
27
- // Normalise multer's file into a FileInput
28
- const fileInput = fromMulterFile(req.file);
29
- // Upload to S3 with a UUID-based filename to prevent collisions
30
- const result = await fileManager.upload(fileInput, {
31
- folder: "avatars",
32
- generateUniqueFileName: true,
33
- metadata: {
34
- uploadedBy: req.body.userId ?? "anonymous",
35
- },
36
- });
37
- // Persist result.key to your database — never store the signed URL
38
- // await db.files.create({ s3Key: result.key, ... });
39
- // Return a short-lived signed URL for the client to use immediately
40
- const url = await fileManager.getSignedUrl(result.key, {
41
- disposition: "inline",
42
- });
43
- return res.json({
44
- key: result.key,
45
- url,
46
- size: result.size,
47
- contentType: result.contentType,
48
- });
49
- });
50
- // POST /upload-many — accepts up to 5 files in a 'files' field
51
- app.post("/upload-many", upload.array("files", 5), async (req, res) => {
52
- const files = req.files;
53
- if (!files?.length) {
54
- return res.status(400).json({ error: "No files provided" });
55
- }
56
- const results = await Promise.all(files.map((file) => fileManager.upload(fromMulterFile(file), {
57
- folder: "documents",
58
- generateUniqueFileName: true,
59
- })));
60
- return res.json(results.map((r) => ({ key: r.key, size: r.size })));
61
- });
62
- export { app };
63
- //# sourceMappingURL=02-upload-multer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"02-upload-multer.js","sourceRoot":"","sources":["../../examples/02-upload-multer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,cAAc,GACf,MAAM,uBAAuB,CAAC;AAE/B,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AACtB,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;AAE3D,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;IACvC,QAAQ,EAAE,SAAS;CACpB,CAAC,CAAC;AAEH,0DAA0D;AAC1D,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC5D,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,2CAA2C;IAC3C,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE3C,gEAAgE;IAChE,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,SAAS,EAAE;QACjD,MAAM,EAAE,SAAS;QACjB,sBAAsB,EAAE,IAAI;QAC5B,QAAQ,EAAE;YACR,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,WAAW;SAC3C;KACF,CAAC,CAAC;IAEH,mEAAmE;IACnE,qDAAqD;IAErD,oEAAoE;IACpE,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE;QACrD,WAAW,EAAE,QAAQ;KACtB,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC,IAAI,CAAC;QACd,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,GAAG;QACH,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+DAA+D;AAC/D,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACpE,MAAM,KAAK,GAAG,GAAG,CAAC,KAA8B,CAAC;IACjD,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACjB,WAAW,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE;QACvC,MAAM,EAAE,WAAW;QACnB,sBAAsB,EAAE,IAAI;KAC7B,CAAC,CACH,CACF,CAAC;IAEF,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACtE,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,GAAG,EAAE,CAAC"}
@@ -1,12 +0,0 @@
1
- /**
2
- * 03-upload-web.ts
3
- *
4
- * Uploading files from a Next.js App Router route handler (or any environment
5
- * where you receive a Web API `File` object from FormData).
6
- *
7
- * Key points:
8
- * - `fromWebFile()` is async because it calls `file.arrayBuffer()`.
9
- * - Everything else is the same as the multer path.
10
- */
11
- import { NextRequest } from "next/server";
12
- export declare function POST(request: NextRequest): Promise<any>;
@@ -1,38 +0,0 @@
1
- /**
2
- * 03-upload-web.ts
3
- *
4
- * Uploading files from a Next.js App Router route handler (or any environment
5
- * where you receive a Web API `File` object from FormData).
6
- *
7
- * Key points:
8
- * - `fromWebFile()` is async because it calls `file.arrayBuffer()`.
9
- * - Everything else is the same as the multer path.
10
- */
11
- import { NextResponse } from "next/server";
12
- import { AwsFileManager, fromWebFile } from "@lib/aws-file-manager";
13
- const fileManager = new AwsFileManager({
14
- region: process.env.AWS_REGION ?? "us-east-1",
15
- bucketName: process.env.S3_BUCKET_NAME,
16
- basePath: "uploads",
17
- });
18
- // app/api/upload/route.ts
19
- export async function POST(request) {
20
- const formData = await request.formData();
21
- const file = formData.get("file");
22
- if (!(file instanceof File)) {
23
- return NextResponse.json({ error: "No file provided" }, { status: 400 });
24
- }
25
- // Normalise the Web API File into a FileInput
26
- const fileInput = await fromWebFile(file);
27
- const result = await fileManager.upload(fileInput, {
28
- folder: "profile-photos",
29
- generateUniqueFileName: true,
30
- });
31
- // Persist result.key to your database
32
- // await db.users.update({ id: userId, avatarKey: result.key });
33
- const url = await fileManager.getSignedUrl(result.key, {
34
- disposition: "inline",
35
- });
36
- return NextResponse.json({ key: result.key, url });
37
- }
38
- //# sourceMappingURL=03-upload-web.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"03-upload-web.js","sourceRoot":"","sources":["../../examples/03-upload-web.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpE,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;IACvC,QAAQ,EAAE,SAAS;CACpB,CAAC,CAAC;AAEH,0BAA0B;AAC1B,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAoB;IAC7C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAElC,IAAI,CAAC,CAAC,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,8CAA8C;IAC9C,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;IAE1C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,SAAS,EAAE;QACjD,MAAM,EAAE,gBAAgB;QACxB,sBAAsB,EAAE,IAAI;KAC7B,CAAC,CAAC;IAEH,sCAAsC;IACtC,gEAAgE;IAEhE,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE;QACrD,WAAW,EAAE,QAAQ;KACtB,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;AACrD,CAAC"}
@@ -1,12 +0,0 @@
1
- /**
2
- * 04-signed-urls.ts
3
- *
4
- * Generating signed URLs for private S3 objects.
5
- *
6
- * Key points:
7
- * - Call `getSignedUrl()` at request time — never store the returned URL.
8
- * - Use `disposition: 'inline'` for images and PDFs rendered in the browser.
9
- * - Use `disposition: 'attachment'` to force a file download with a filename.
10
- * - Signed URLs are credentials — treat them accordingly (HTTPS only, short TTL).
11
- */
12
- export {};
@@ -1,48 +0,0 @@
1
- /**
2
- * 04-signed-urls.ts
3
- *
4
- * Generating signed URLs for private S3 objects.
5
- *
6
- * Key points:
7
- * - Call `getSignedUrl()` at request time — never store the returned URL.
8
- * - Use `disposition: 'inline'` for images and PDFs rendered in the browser.
9
- * - Use `disposition: 'attachment'` to force a file download with a filename.
10
- * - Signed URLs are credentials — treat them accordingly (HTTPS only, short TTL).
11
- */
12
- import { AwsFileManager } from "@lib/aws-file-manager";
13
- const fileManager = new AwsFileManager({
14
- region: process.env.AWS_REGION ?? "us-east-1",
15
- bucketName: process.env.S3_BUCKET_NAME,
16
- urlExpirationSeconds: 3600, // 1 hour default
17
- });
18
- async function examples() {
19
- const s3Key = "uploads/avatars/f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg";
20
- // ─── Inline (browser renders the file) ───────────────────────────────────
21
- const inlineUrl = await fileManager.getSignedUrl(s3Key, {
22
- disposition: "inline",
23
- });
24
- console.log("Inline URL:", inlineUrl);
25
- // → https://my-bucket.s3.amazonaws.com/uploads/avatars/...?X-Amz-Signature=...
26
- // ─── Attachment (forced download with a suggested filename) ───────────────
27
- const downloadUrl = await fileManager.getSignedUrl(s3Key, {
28
- disposition: "attachment",
29
- fileName: "my-photo.jpg", // Suggested filename in Content-Disposition
30
- });
31
- console.log("Download URL:", downloadUrl);
32
- // ─── Custom TTL ───────────────────────────────────────────────────────────
33
- // Override the instance default for a short-lived share link
34
- const shortLivedUrl = await fileManager.getSignedUrl(s3Key, {
35
- disposition: "inline",
36
- expiresIn: 300, // 5 minutes
37
- });
38
- console.log("Short-lived URL:", shortLivedUrl);
39
- // ─── Serve from an API route ──────────────────────────────────────────────
40
- // Typical pattern: look up the key from your DB, then redirect to the URL.
41
- //
42
- // GET /api/files/:id
43
- // const file = await db.files.findById(req.params.id);
44
- // const url = await fileManager.getSignedUrl(file.s3Key, { disposition: 'inline' });
45
- // return res.redirect(302, url);
46
- }
47
- examples().catch(console.error);
48
- //# sourceMappingURL=04-signed-urls.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"04-signed-urls.js","sourceRoot":"","sources":["../../examples/04-signed-urls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;IACvC,oBAAoB,EAAE,IAAI,EAAE,iBAAiB;CAC9C,CAAC,CAAC;AAEH,KAAK,UAAU,QAAQ;IACrB,MAAM,KAAK,GAAG,0DAA0D,CAAC;IAEzE,4EAA4E;IAC5E,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE;QACtD,WAAW,EAAE,QAAQ;KACtB,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IACtC,+EAA+E;IAE/E,6EAA6E;IAC7E,MAAM,WAAW,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE;QACxD,WAAW,EAAE,YAAY;QACzB,QAAQ,EAAE,cAAc,EAAE,4CAA4C;KACvE,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;IAE1C,6EAA6E;IAC7E,6DAA6D;IAC7D,MAAM,aAAa,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE;QAC1D,WAAW,EAAE,QAAQ;QACrB,SAAS,EAAE,GAAG,EAAE,YAAY;KAC7B,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;IAE/C,6EAA6E;IAC7E,2EAA2E;IAC3E,EAAE;IACF,qBAAqB;IACrB,uDAAuD;IACvD,qFAAqF;IACrF,iCAAiC;AACnC,CAAC;AAED,QAAQ,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC"}
@@ -1,14 +0,0 @@
1
- /**
2
- * 05-download.ts
3
- *
4
- * Downloading S3 objects as a Buffer or a Node.js Readable stream.
5
- *
6
- * Key points:
7
- * - `download()` returns `null` when the object does not exist (no throw).
8
- * - Use `'stream'` (default) when piping to an HTTP response or writing to disk.
9
- * - Use `'buffer'` when you need the full bytes in memory (e.g. image processing).
10
- */
11
- declare const app: import("express-serve-static-core").Express;
12
- declare function processImage(s3Key: string): Promise<void>;
13
- declare function saveToDisk(s3Key: string, outputPath: string): Promise<void>;
14
- export { app, processImage, saveToDisk };
@@ -1,53 +0,0 @@
1
- /**
2
- * 05-download.ts
3
- *
4
- * Downloading S3 objects as a Buffer or a Node.js Readable stream.
5
- *
6
- * Key points:
7
- * - `download()` returns `null` when the object does not exist (no throw).
8
- * - Use `'stream'` (default) when piping to an HTTP response or writing to disk.
9
- * - Use `'buffer'` when you need the full bytes in memory (e.g. image processing).
10
- */
11
- import { createWriteStream } from "fs";
12
- import { pipeline } from "stream/promises";
13
- import express from "express";
14
- import { AwsFileManager } from "@lib/aws-file-manager";
15
- const fileManager = new AwsFileManager({
16
- region: process.env.AWS_REGION ?? "us-east-1",
17
- bucketName: process.env.S3_BUCKET_NAME,
18
- });
19
- const app = express();
20
- // ─── Stream directly to HTTP response ────────────────────────────────────────
21
- app.get("/files/:key(*)", async (req, res) => {
22
- const result = await fileManager.download(req.params.key, "stream");
23
- if (!result) {
24
- return res.status(404).json({ error: "File not found" });
25
- }
26
- const { stream, metadata } = result;
27
- if (metadata.contentType)
28
- res.setHeader("Content-Type", metadata.contentType);
29
- if (metadata.contentLength)
30
- res.setHeader("Content-Length", metadata.contentLength);
31
- stream.pipe(res);
32
- });
33
- // ─── Download to a Buffer (for in-memory processing) ─────────────────────────
34
- async function processImage(s3Key) {
35
- const result = await fileManager.download(s3Key, "buffer");
36
- if (!result) {
37
- throw new Error(`File not found: ${s3Key}`);
38
- }
39
- const { buffer, metadata } = result;
40
- console.log(`Downloaded ${buffer.length} bytes, type: ${metadata.contentType}`);
41
- // Pass the buffer to an image processing library, compression pipeline, etc.
42
- // const compressed = await sharp(buffer).jpeg({ quality: 80 }).toBuffer();
43
- }
44
- // ─── Stream to disk ───────────────────────────────────────────────────────────
45
- async function saveToDisk(s3Key, outputPath) {
46
- const result = await fileManager.download(s3Key, "stream");
47
- if (!result)
48
- throw new Error(`File not found: ${s3Key}`);
49
- await pipeline(result.stream, createWriteStream(outputPath));
50
- console.log(`Saved to ${outputPath}`);
51
- }
52
- export { app, processImage, saveToDisk };
53
- //# sourceMappingURL=05-download.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"05-download.js","sourceRoot":"","sources":["../../examples/05-download.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,IAAI,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;CACxC,CAAC,CAAC;AAEH,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,gFAAgF;AAChF,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC3C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAEpE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAEpC,IAAI,QAAQ,CAAC,WAAW;QAAE,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC9E,IAAI,QAAQ,CAAC,aAAa;QAAE,GAAG,CAAC,SAAS,CAAC,gBAAgB,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;IAEpF,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnB,CAAC,CAAC,CAAC;AAEH,gFAAgF;AAChF,KAAK,UAAU,YAAY,CAAC,KAAa;IACvC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAE3D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,MAAM,iBAAiB,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IAEhF,6EAA6E;IAC7E,2EAA2E;AAC7E,CAAC;AAED,iFAAiF;AACjF,KAAK,UAAU,UAAU,CAAC,KAAa,EAAE,UAAkB;IACzD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC3D,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,EAAE,CAAC,CAAC;IAEzD,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,YAAY,UAAU,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC"}
@@ -1,17 +0,0 @@
1
- /**
2
- * 06-delete.ts
3
- *
4
- * Deleting one or many S3 objects.
5
- *
6
- * Key points:
7
- * - `delete()` is idempotent — calling it on a missing key does not throw.
8
- * - Always soft-delete your DB record first, then call `delete()` as cleanup.
9
- * A crash between the two steps leaves a recoverable S3 orphan rather than
10
- * a missing file with an intact DB row.
11
- * - `deleteMany()` is for removing a file and all its variants in one call.
12
- * It chunks automatically at 1 000 keys to respect the S3 limit.
13
- */
14
- declare function deleteFile(s3Key: string): Promise<void>;
15
- declare function deleteWithVariants(originalKey: string, thumbnailKey: string, compressedKey: string): Promise<void>;
16
- declare function deleteOrphans(orphanKeys: string[]): Promise<void>;
17
- export { deleteFile, deleteWithVariants, deleteOrphans };
@@ -1,44 +0,0 @@
1
- /**
2
- * 06-delete.ts
3
- *
4
- * Deleting one or many S3 objects.
5
- *
6
- * Key points:
7
- * - `delete()` is idempotent — calling it on a missing key does not throw.
8
- * - Always soft-delete your DB record first, then call `delete()` as cleanup.
9
- * A crash between the two steps leaves a recoverable S3 orphan rather than
10
- * a missing file with an intact DB row.
11
- * - `deleteMany()` is for removing a file and all its variants in one call.
12
- * It chunks automatically at 1 000 keys to respect the S3 limit.
13
- */
14
- import { AwsFileManager } from "@lib/aws-file-manager";
15
- const fileManager = new AwsFileManager({
16
- region: process.env.AWS_REGION ?? "us-east-1",
17
- bucketName: process.env.S3_BUCKET_NAME,
18
- });
19
- // ─── Delete a single file ─────────────────────────────────────────────────────
20
- async function deleteFile(s3Key) {
21
- // 1. Soft-delete the DB record first
22
- // await db.files.update({ id: fileId, deletedAt: new Date() });
23
- // 2. Remove the S3 object as a cleanup step
24
- await fileManager.delete(s3Key);
25
- console.log(`Deleted: ${s3Key}`);
26
- }
27
- // ─── Delete a file and all its variants ──────────────────────────────────────
28
- // After uploading, your pipeline may generate additional variants (thumbnails,
29
- // compressed copies). Store all their keys and delete them together.
30
- async function deleteWithVariants(originalKey, thumbnailKey, compressedKey) {
31
- await fileManager.deleteMany([originalKey, thumbnailKey, compressedKey]);
32
- console.log("Deleted original + all variants");
33
- }
34
- // ─── Batch delete from a reconciliation job ───────────────────────────────────
35
- // If you have a list of orphaned keys (e.g. found by `list()` but absent from
36
- // your DB), delete them all at once. `deleteMany` handles chunking internally.
37
- async function deleteOrphans(orphanKeys) {
38
- if (orphanKeys.length === 0)
39
- return;
40
- await fileManager.deleteMany(orphanKeys);
41
- console.log(`Deleted ${orphanKeys.length} orphaned objects`);
42
- }
43
- export { deleteFile, deleteWithVariants, deleteOrphans };
44
- //# sourceMappingURL=06-delete.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"06-delete.js","sourceRoot":"","sources":["../../examples/06-delete.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;CACxC,CAAC,CAAC;AAEH,iFAAiF;AACjF,KAAK,UAAU,UAAU,CAAC,KAAa;IACrC,qCAAqC;IACrC,gEAAgE;IAEhE,4CAA4C;IAC5C,MAAM,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,gFAAgF;AAChF,+EAA+E;AAC/E,qEAAqE;AACrE,KAAK,UAAU,kBAAkB,CAC/B,WAAmB,EACnB,YAAoB,EACpB,aAAqB;IAErB,MAAM,WAAW,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;AACjD,CAAC;AAED,iFAAiF;AACjF,8EAA8E;AAC9E,+EAA+E;AAC/E,KAAK,UAAU,aAAa,CAAC,UAAoB;IAC/C,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACpC,MAAM,WAAW,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,WAAW,UAAU,CAAC,MAAM,mBAAmB,CAAC,CAAC;AAC/D,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,aAAa,EAAE,CAAC"}
@@ -1,14 +0,0 @@
1
- /**
2
- * 07-copy.ts
3
- *
4
- * Copying an S3 object within the same bucket.
5
- *
6
- * Key points:
7
- * - `copy()` is a server-side operation — no bytes move through your server.
8
- * - Use it when duplicating entities that own files (e.g. cloning a template).
9
- * - Pass `metadata` to override the copied object's metadata; omit it to
10
- * inherit the source object's metadata (MetadataDirective=COPY).
11
- */
12
- declare function cloneDocument(sourceKey: string, targetOwnerId: string): Promise<string>;
13
- declare function moveObject(sourceKey: string, destKey: string): Promise<void>;
14
- export { cloneDocument, moveObject };
@@ -1,35 +0,0 @@
1
- /**
2
- * 07-copy.ts
3
- *
4
- * Copying an S3 object within the same bucket.
5
- *
6
- * Key points:
7
- * - `copy()` is a server-side operation — no bytes move through your server.
8
- * - Use it when duplicating entities that own files (e.g. cloning a template).
9
- * - Pass `metadata` to override the copied object's metadata; omit it to
10
- * inherit the source object's metadata (MetadataDirective=COPY).
11
- */
12
- import { AwsFileManager } from "@lib/aws-file-manager";
13
- import { randomUUID } from "crypto";
14
- const fileManager = new AwsFileManager({
15
- region: process.env.AWS_REGION ?? "us-east-1",
16
- bucketName: process.env.S3_BUCKET_NAME,
17
- basePath: "uploads",
18
- });
19
- // ─── Clone a template document ───────────────────────────────────────────────
20
- async function cloneDocument(sourceKey, targetOwnerId) {
21
- const ext = sourceKey.substring(sourceKey.lastIndexOf("."));
22
- const destKey = `uploads/${targetOwnerId}/${randomUUID()}${ext}`;
23
- await fileManager.copy(sourceKey, destKey);
24
- console.log(`Copied ${sourceKey} → ${destKey}`);
25
- return destKey;
26
- }
27
- // ─── Reorganise a key (rename/move) ──────────────────────────────────────────
28
- // S3 has no rename — copy to the new key, then delete the old one.
29
- async function moveObject(sourceKey, destKey) {
30
- await fileManager.copy(sourceKey, destKey);
31
- await fileManager.delete(sourceKey);
32
- console.log(`Moved ${sourceKey} → ${destKey}`);
33
- }
34
- export { cloneDocument, moveObject };
35
- //# sourceMappingURL=07-copy.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"07-copy.js","sourceRoot":"","sources":["../../examples/07-copy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;IACvC,QAAQ,EAAE,SAAS;CACpB,CAAC,CAAC;AAEH,gFAAgF;AAChF,KAAK,UAAU,aAAa,CAC1B,SAAiB,EACjB,aAAqB;IAErB,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,WAAW,aAAa,IAAI,UAAU,EAAE,GAAG,GAAG,EAAE,CAAC;IAEjE,MAAM,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAE3C,OAAO,CAAC,GAAG,CAAC,UAAU,SAAS,MAAM,OAAO,EAAE,CAAC,CAAC;IAChD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,gFAAgF;AAChF,mEAAmE;AACnE,KAAK,UAAU,UAAU,CACvB,SAAiB,EACjB,OAAe;IAEf,MAAM,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,SAAS,SAAS,MAAM,OAAO,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC"}
@@ -1,16 +0,0 @@
1
- /**
2
- * 08-list-paginated.ts
3
- *
4
- * Listing objects under a prefix, with pagination.
5
- *
6
- * Key points:
7
- * - Each call returns up to 1 000 entries (S3 maximum per request).
8
- * - `hasMore` and `continuationToken` drive the pagination loop.
9
- * - Primarily used for nightly storage reconciliation jobs that walk the
10
- * bucket and verify byte counts against the database.
11
- */
12
- import { ListEntry } from "@lib/aws-file-manager";
13
- declare function listAll(folder: string): Promise<ListEntry[]>;
14
- declare function reconcileStorage(folder: string): Promise<void>;
15
- declare function listByPrefix(folder: string, prefix: string): Promise<ListEntry[]>;
16
- export { listAll, reconcileStorage, listByPrefix };
@@ -1,60 +0,0 @@
1
- /**
2
- * 08-list-paginated.ts
3
- *
4
- * Listing objects under a prefix, with pagination.
5
- *
6
- * Key points:
7
- * - Each call returns up to 1 000 entries (S3 maximum per request).
8
- * - `hasMore` and `continuationToken` drive the pagination loop.
9
- * - Primarily used for nightly storage reconciliation jobs that walk the
10
- * bucket and verify byte counts against the database.
11
- */
12
- import { AwsFileManager } from "@lib/aws-file-manager";
13
- const fileManager = new AwsFileManager({
14
- region: process.env.AWS_REGION ?? "us-east-1",
15
- bucketName: process.env.S3_BUCKET_NAME,
16
- basePath: "uploads",
17
- });
18
- // ─── Collect all entries under a folder ──────────────────────────────────────
19
- async function listAll(folder) {
20
- const allEntries = [];
21
- let token;
22
- do {
23
- const result = await fileManager.list({
24
- folder,
25
- continuationToken: token,
26
- });
27
- allEntries.push(...result.entries);
28
- token = result.continuationToken;
29
- } while (token);
30
- console.log(`Found ${allEntries.length} objects in '${folder}'`);
31
- return allEntries;
32
- }
33
- // ─── Process large result sets in pages ──────────────────────────────────────
34
- // Avoid accumulating everything in memory when the bucket is very large.
35
- async function reconcileStorage(folder) {
36
- let token;
37
- let totalBytes = 0;
38
- let pageCount = 0;
39
- do {
40
- const result = await fileManager.list({
41
- folder,
42
- maxResults: 500, // smaller pages for memory-constrained jobs
43
- continuationToken: token,
44
- });
45
- pageCount++;
46
- for (const entry of result.entries) {
47
- totalBytes += entry.size;
48
- // Compare entry.key / entry.size against your database here
49
- }
50
- token = result.continuationToken;
51
- } while (token);
52
- console.log(`Reconciled ${pageCount} page(s), total: ${totalBytes} bytes`);
53
- }
54
- // ─── Filter by prefix within a folder ────────────────────────────────────────
55
- async function listByPrefix(folder, prefix) {
56
- const result = await fileManager.list({ folder, prefix });
57
- return result.entries;
58
- }
59
- export { listAll, reconcileStorage, listByPrefix };
60
- //# sourceMappingURL=08-list-paginated.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"08-list-paginated.js","sourceRoot":"","sources":["../../examples/08-list-paginated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAa,MAAM,uBAAuB,CAAC;AAElE,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;IACvC,QAAQ,EAAE,SAAS;CACpB,CAAC,CAAC;AAEH,gFAAgF;AAChF,KAAK,UAAU,OAAO,CAAC,MAAc;IACnC,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,IAAI,KAAyB,CAAC;IAE9B,GAAG,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC;YACpC,MAAM;YACN,iBAAiB,EAAE,KAAK;SACzB,CAAC,CAAC;QACH,UAAU,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QACnC,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACnC,CAAC,QAAQ,KAAK,EAAE;IAEhB,OAAO,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,MAAM,gBAAgB,MAAM,GAAG,CAAC,CAAC;IACjE,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,gFAAgF;AAChF,yEAAyE;AACzE,KAAK,UAAU,gBAAgB,CAAC,MAAc;IAC5C,IAAI,KAAyB,CAAC;IAC9B,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,GAAG,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC;YACpC,MAAM;YACN,UAAU,EAAE,GAAG,EAAE,4CAA4C;YAC7D,iBAAiB,EAAE,KAAK;SACzB,CAAC,CAAC;QAEH,SAAS,EAAE,CAAC;QACZ,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnC,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC;YACzB,4DAA4D;QAC9D,CAAC;QAED,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACnC,CAAC,QAAQ,KAAK,EAAE;IAEhB,OAAO,CAAC,GAAG,CAAC,cAAc,SAAS,oBAAoB,UAAU,QAAQ,CAAC,CAAC;AAC7E,CAAC;AAED,gFAAgF;AAChF,KAAK,UAAU,YAAY,CAAC,MAAc,EAAE,MAAc;IACxD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1D,OAAO,MAAM,CAAC,OAAO,CAAC;AACxB,CAAC;AAED,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC"}
@@ -1,13 +0,0 @@
1
- /**
2
- * 09-exists.ts
3
- *
4
- * Checking whether an S3 object exists at a given key.
5
- *
6
- * Key points:
7
- * - `exists()` returns `false` for missing keys; it does not throw.
8
- * - Useful as a lightweight data integrity check before an operation.
9
- * - Not a replacement for proper error handling — network errors still throw.
10
- */
11
- declare function assertFileExists(s3Key: string): Promise<void>;
12
- declare function uploadIfAbsent(s3Key: string, uploadFn: () => Promise<void>): Promise<void>;
13
- export { assertFileExists, uploadIfAbsent };
@@ -1,32 +0,0 @@
1
- /**
2
- * 09-exists.ts
3
- *
4
- * Checking whether an S3 object exists at a given key.
5
- *
6
- * Key points:
7
- * - `exists()` returns `false` for missing keys; it does not throw.
8
- * - Useful as a lightweight data integrity check before an operation.
9
- * - Not a replacement for proper error handling — network errors still throw.
10
- */
11
- import { AwsFileManager } from "@lib/aws-file-manager";
12
- const fileManager = new AwsFileManager({
13
- region: process.env.AWS_REGION ?? "us-east-1",
14
- bucketName: process.env.S3_BUCKET_NAME,
15
- });
16
- // ─── Guard before serving a file ─────────────────────────────────────────────
17
- async function assertFileExists(s3Key) {
18
- const found = await fileManager.exists(s3Key);
19
- if (!found) {
20
- throw new Error(`Storage integrity error: file missing at key "${s3Key}"`);
21
- }
22
- }
23
- // ─── Skip re-uploading if the file is already there ──────────────────────────
24
- async function uploadIfAbsent(s3Key, uploadFn) {
25
- if (await fileManager.exists(s3Key)) {
26
- console.log(`Skipping upload — key already exists: ${s3Key}`);
27
- return;
28
- }
29
- await uploadFn();
30
- }
31
- export { assertFileExists, uploadIfAbsent };
32
- //# sourceMappingURL=09-exists.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"09-exists.js","sourceRoot":"","sources":["../../examples/09-exists.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;CACxC,CAAC,CAAC;AAEH,gFAAgF;AAChF,KAAK,UAAU,gBAAgB,CAAC,KAAa;IAC3C,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,iDAAiD,KAAK,GAAG,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,KAAK,UAAU,cAAc,CAC3B,KAAa,EACb,QAA6B;IAE7B,IAAI,MAAM,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,yCAAyC,KAAK,EAAE,CAAC,CAAC;QAC9D,OAAO;IACT,CAAC;IACD,MAAM,QAAQ,EAAE,CAAC;AACnB,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAC"}
@@ -1 +0,0 @@
1
- export {};