@miguelmorales13/nestkit 0.4.0 → 0.5.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/README.md +42 -4
- package/dist/index.cjs +7 -7
- package/dist/index.js +9 -9
- package/dist/media/index.cjs +17 -9
- package/dist/media/index.d.ts +1 -1
- package/dist/media/index.js +19 -11
- package/dist/media/media.options.d.ts +1 -0
- package/dist/media/media.service.d.ts +0 -1
- package/dist/storage/adapters/bunny.storage.d.ts +34 -0
- package/dist/storage/adapters/local.storage.d.ts +31 -0
- package/dist/storage/adapters/s3.storage.d.ts +37 -0
- package/dist/storage/index.cjs +528 -0
- package/dist/storage/index.d.ts +8 -0
- package/dist/storage/index.js +528 -0
- package/dist/storage/storage.module.d.ts +47 -0
- package/dist/storage/storage.options.d.ts +79 -0
- package/dist/storage/storage.port.d.ts +136 -0
- package/package.json +22 -2
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
2
|
+
/**
|
|
3
|
+
* DI token for the storage adapter. Inject with `@Inject(STORAGE)`.
|
|
4
|
+
*
|
|
5
|
+
* `StoragePort` is the single provider-agnostic interface every adapter (local
|
|
6
|
+
* filesystem, S3/R2, Bunny.net, or a future one) implements. Consumers depend on
|
|
7
|
+
* this port and never on a concrete provider, so switching from R2 to Bunny (or
|
|
8
|
+
* back) is a one-line change in `StorageModule.forRoot(...)` — no call site moves.
|
|
9
|
+
*/
|
|
10
|
+
export declare const STORAGE: unique symbol;
|
|
11
|
+
/** Options for a write (`put`/`putStream`/`signedUploadUrl`). */
|
|
12
|
+
export interface PutOptions {
|
|
13
|
+
/** MIME type stored with the object (`image/webp`, `application/pdf`, `video/mp4`, …). */
|
|
14
|
+
contentType?: string;
|
|
15
|
+
/** `Cache-Control` header the object is served with (e.g. `public, max-age=31536000, immutable`). */
|
|
16
|
+
cacheControl?: string;
|
|
17
|
+
/** `Content-Disposition` (e.g. `attachment; filename="factura.pdf"`) for downloads. */
|
|
18
|
+
contentDisposition?: string;
|
|
19
|
+
/** Arbitrary metadata, stored where the provider supports it. */
|
|
20
|
+
metadata?: Record<string, string>;
|
|
21
|
+
/**
|
|
22
|
+
* Byte length of the payload. Optional for `put` (a Buffer knows its size) but
|
|
23
|
+
* some providers require it up front when streaming a body of unknown length.
|
|
24
|
+
*/
|
|
25
|
+
contentLength?: number;
|
|
26
|
+
}
|
|
27
|
+
/** What is known about a stored object. */
|
|
28
|
+
export interface ObjectInfo {
|
|
29
|
+
key: string;
|
|
30
|
+
/** Size in bytes. */
|
|
31
|
+
size: number;
|
|
32
|
+
contentType?: string;
|
|
33
|
+
lastModified?: Date;
|
|
34
|
+
etag?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A half-open-ish byte range, HTTP semantics: `start` and `end` are both
|
|
38
|
+
* INCLUSIVE. `{ start: 0, end: 1023 }` is the first 1024 bytes. `end` omitted
|
|
39
|
+
* means "to the end of the object". Used to serve video/audio seek requests.
|
|
40
|
+
*/
|
|
41
|
+
export interface ByteRange {
|
|
42
|
+
start: number;
|
|
43
|
+
end?: number;
|
|
44
|
+
}
|
|
45
|
+
/** The result of opening an object for reading as a stream. */
|
|
46
|
+
export interface ReadStream {
|
|
47
|
+
stream: Readable;
|
|
48
|
+
contentType?: string;
|
|
49
|
+
/** Bytes in THIS response (the range length when a range was requested). */
|
|
50
|
+
contentLength?: number;
|
|
51
|
+
/** Total size of the whole object, regardless of range. */
|
|
52
|
+
totalSize?: number;
|
|
53
|
+
/** Present only when a range was served: the `Content-Range` header value. */
|
|
54
|
+
contentRange?: string;
|
|
55
|
+
lastModified?: Date;
|
|
56
|
+
etag?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A short-lived target a browser (or any client) can upload to DIRECTLY, without
|
|
60
|
+
* the file passing through the API. Essential for large files (video): the API
|
|
61
|
+
* hands back this target, the client PUTs/POSTs the bytes straight to the
|
|
62
|
+
* provider, and the API is never a proxy for gigabytes.
|
|
63
|
+
*/
|
|
64
|
+
export interface SignedUploadTarget {
|
|
65
|
+
/** URL to upload to. */
|
|
66
|
+
url: string;
|
|
67
|
+
/** HTTP method to use. `PUT` for presigned-URL providers; `POST` for form-policy uploads. */
|
|
68
|
+
method: 'PUT' | 'POST';
|
|
69
|
+
/** Headers the client MUST send (e.g. `Content-Type`) for the signature to match. */
|
|
70
|
+
headers?: Record<string, string>;
|
|
71
|
+
/** For `POST` form uploads (S3 POST policy): fields to include in the multipart form before the file. */
|
|
72
|
+
fields?: Record<string, string>;
|
|
73
|
+
/** The key the object will land at. */
|
|
74
|
+
key: string;
|
|
75
|
+
/** When the target stops being valid. */
|
|
76
|
+
expiresAt: Date;
|
|
77
|
+
}
|
|
78
|
+
/** A page of `list()` results. */
|
|
79
|
+
export interface ListResult {
|
|
80
|
+
objects: ObjectInfo[];
|
|
81
|
+
/** Opaque cursor for the next page; absent when there are no more. */
|
|
82
|
+
cursor?: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Provider-agnostic file storage. Handles any file type — images, PDFs and other
|
|
86
|
+
* documents, video, audio, archives — the port makes no assumption about content.
|
|
87
|
+
* Adapters: local filesystem (dev), S3-compatible (AWS S3 and Cloudflare R2), and
|
|
88
|
+
* Bunny.net.
|
|
89
|
+
*
|
|
90
|
+
* Keys are forward-slash paths WITHOUT a leading slash, e.g.
|
|
91
|
+
* `tenant-123/videos/clip.mp4`. The caller composes keys; never build one from an
|
|
92
|
+
* untrusted filename (path traversal). Adapters store objects verbatim under the key.
|
|
93
|
+
*/
|
|
94
|
+
export interface StoragePort {
|
|
95
|
+
/** Store `data` at `key`, overwriting if it exists. Returns what is known about the stored object. */
|
|
96
|
+
put(key: string, data: Buffer | Uint8Array, options?: PutOptions): Promise<ObjectInfo>;
|
|
97
|
+
/**
|
|
98
|
+
* Store a readable stream at `key` (large uploads, video). The adapter uses
|
|
99
|
+
* multipart under the hood where the provider needs it, so the whole file never
|
|
100
|
+
* has to sit in memory.
|
|
101
|
+
*/
|
|
102
|
+
putStream(key: string, stream: Readable, options?: PutOptions): Promise<ObjectInfo>;
|
|
103
|
+
/** Read the whole object into memory. Prefer `getStream` for anything large. */
|
|
104
|
+
get(key: string): Promise<Buffer>;
|
|
105
|
+
/** Open the object as a stream, optionally a byte range (for seeking media). */
|
|
106
|
+
getStream(key: string, range?: ByteRange): Promise<ReadStream>;
|
|
107
|
+
/** Metadata about the object, or `null` if it does not exist. */
|
|
108
|
+
stat(key: string): Promise<ObjectInfo | null>;
|
|
109
|
+
/** Whether an object exists at `key`. */
|
|
110
|
+
exists(key: string): Promise<boolean>;
|
|
111
|
+
/** Delete the object. Idempotent: deleting a missing key does not throw. */
|
|
112
|
+
delete(key: string): Promise<void>;
|
|
113
|
+
/** List objects under `prefix`, paginated. */
|
|
114
|
+
list(prefix: string, options?: {
|
|
115
|
+
limit?: number;
|
|
116
|
+
cursor?: string;
|
|
117
|
+
}): Promise<ListResult>;
|
|
118
|
+
/**
|
|
119
|
+
* A short-lived URL to READ a private object. For a public bucket/zone prefer
|
|
120
|
+
* `publicUrl`. `ttlSeconds` defaults to one hour.
|
|
121
|
+
*/
|
|
122
|
+
signedReadUrl(key: string, ttlSeconds?: number): Promise<string>;
|
|
123
|
+
/**
|
|
124
|
+
* A short-lived target for a client to UPLOAD directly to the provider.
|
|
125
|
+
* Not every provider supports this the same way — see each adapter.
|
|
126
|
+
*/
|
|
127
|
+
signedUploadUrl(key: string, options?: PutOptions & {
|
|
128
|
+
ttlSeconds?: number;
|
|
129
|
+
}): Promise<SignedUploadTarget>;
|
|
130
|
+
/**
|
|
131
|
+
* The stable public URL for `key`, or `null` when the store is private (no
|
|
132
|
+
* public base URL / pull zone configured). For private stores, use
|
|
133
|
+
* `signedReadUrl` instead.
|
|
134
|
+
*/
|
|
135
|
+
publicUrl(key: string): string | null;
|
|
136
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miguelmorales13/nestkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -110,6 +110,11 @@
|
|
|
110
110
|
"types": "./dist/media/index.d.ts",
|
|
111
111
|
"import": "./dist/media/index.js",
|
|
112
112
|
"require": "./dist/media/index.cjs"
|
|
113
|
+
},
|
|
114
|
+
"./storage": {
|
|
115
|
+
"types": "./dist/storage/index.d.ts",
|
|
116
|
+
"import": "./dist/storage/index.js",
|
|
117
|
+
"require": "./dist/storage/index.cjs"
|
|
113
118
|
}
|
|
114
119
|
},
|
|
115
120
|
"scripts": {
|
|
@@ -131,9 +136,21 @@
|
|
|
131
136
|
"class-validator": "^0.15.0",
|
|
132
137
|
"class-transformer": "^0.5.1",
|
|
133
138
|
"resend": "^6.20.0",
|
|
134
|
-
"nodemailer": "^9.0.5"
|
|
139
|
+
"nodemailer": "^9.0.5",
|
|
140
|
+
"@aws-sdk/client-s3": "^3.0.0",
|
|
141
|
+
"@aws-sdk/s3-request-presigner": "^3.0.0",
|
|
142
|
+
"@aws-sdk/lib-storage": "^3.0.0"
|
|
135
143
|
},
|
|
136
144
|
"peerDependenciesMeta": {
|
|
145
|
+
"@aws-sdk/client-s3": {
|
|
146
|
+
"optional": true
|
|
147
|
+
},
|
|
148
|
+
"@aws-sdk/s3-request-presigner": {
|
|
149
|
+
"optional": true
|
|
150
|
+
},
|
|
151
|
+
"@aws-sdk/lib-storage": {
|
|
152
|
+
"optional": true
|
|
153
|
+
},
|
|
137
154
|
"pg": {
|
|
138
155
|
"optional": true
|
|
139
156
|
},
|
|
@@ -171,6 +188,9 @@
|
|
|
171
188
|
"jsonwebtoken": "^9.0.3"
|
|
172
189
|
},
|
|
173
190
|
"devDependencies": {
|
|
191
|
+
"@aws-sdk/client-s3": "^3.1121.0",
|
|
192
|
+
"@aws-sdk/lib-storage": "^3.1121.0",
|
|
193
|
+
"@aws-sdk/s3-request-presigner": "^3.1121.0",
|
|
174
194
|
"@nestjs/common": "^11.0.0",
|
|
175
195
|
"@nestjs/core": "^11.0.0",
|
|
176
196
|
"@nestjs/swagger": "^11.0.0",
|