@better-s3/server 3.1045.2 → 3.1047.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.
Files changed (33) hide show
  1. package/README.md +6 -2
  2. package/dist/adapters/next.js +54 -20
  3. package/dist/adapters/next.js.map +1 -1
  4. package/dist/api.d.ts +20 -3
  5. package/dist/handlers/multipart/list-parts.d.ts +2 -0
  6. package/dist/helpers/index.d.ts +0 -1
  7. package/dist/helpers/index.js +1 -9
  8. package/dist/helpers/index.js.map +1 -1
  9. package/dist/helpers/server/index.js +9 -3
  10. package/dist/helpers/server/index.js.map +1 -1
  11. package/dist/helpers/server/resolve-object-acl.d.ts +3 -3
  12. package/dist/index.d.ts +2 -1
  13. package/dist/index.js +65 -21
  14. package/dist/index.js.map +1 -1
  15. package/dist/types/delete-hook-context.d.ts +5 -0
  16. package/dist/types/download-hook-context.d.ts +6 -0
  17. package/dist/types/download-success-context.d.ts +5 -0
  18. package/dist/types/hook-context.d.ts +3 -0
  19. package/dist/types/index.d.ts +15 -0
  20. package/dist/types/multipart-complete-success-context.d.ts +13 -0
  21. package/dist/types/multipart-hook-context.d.ts +7 -0
  22. package/dist/types/multipart-init-success-context.d.ts +8 -0
  23. package/dist/types/multipart-list-success-context.d.ts +11 -0
  24. package/dist/types/s3-handler-config.d.ts +66 -0
  25. package/dist/types/s3-handler.d.ts +1 -0
  26. package/dist/types/s3-handlers.d.ts +16 -0
  27. package/dist/types/s3-route-handler-config.d.ts +4 -0
  28. package/dist/types/upload-complete-context.d.ts +15 -0
  29. package/dist/types/upload-hook-context.d.ts +10 -0
  30. package/dist/types/upload-success-context.d.ts +5 -0
  31. package/dist/types.d.ts +1 -214
  32. package/package.json +5 -5
  33. package/dist/helpers/build-public-url.d.ts +0 -13
@@ -0,0 +1,66 @@
1
+ import type { S3Client } from "@aws-sdk/client-s3";
2
+ import type { HookContext } from "./hook-context";
3
+ import type { UploadHookContext } from "./upload-hook-context";
4
+ import type { UploadSuccessContext } from "./upload-success-context";
5
+ import type { UploadCompleteContext } from "./upload-complete-context";
6
+ import type { DownloadHookContext } from "./download-hook-context";
7
+ import type { DownloadSuccessContext } from "./download-success-context";
8
+ import type { DeleteHookContext } from "./delete-hook-context";
9
+ import type { MultipartHookContext } from "./multipart-hook-context";
10
+ import type { MultipartInitSuccessContext } from "./multipart-init-success-context";
11
+ import type { MultipartCompleteSuccessContext } from "./multipart-complete-success-context";
12
+ import type { MultipartListSuccessContext } from "./multipart-list-success-context";
13
+ export type S3HandlerConfig = {
14
+ s3: S3Client;
15
+ defaultBucket: string;
16
+ /**
17
+ * When enabled, confirmation handlers call GetObjectAcl to infer whether
18
+ * the object is public-read or private.
19
+ *
20
+ * Keep disabled unless you explicitly need ACL inference in responses/hooks,
21
+ * because it adds one extra S3 request per upload confirmation.
22
+ *
23
+ * @default false
24
+ */
25
+ resolveObjectAcl?: boolean;
26
+ guard?: (context: HookContext) => Promise<void> | void;
27
+ upload?: {
28
+ enabled?: boolean;
29
+ method?: "post" | "put";
30
+ requireFileSize?: boolean;
31
+ presignGuard?: (context: UploadHookContext) => Promise<void> | void;
32
+ onPresigned?: (context: UploadSuccessContext) => Promise<void> | void;
33
+ confirmGuard?: (context: HookContext & {
34
+ key: string;
35
+ bucket: string;
36
+ }) => Promise<void> | void;
37
+ onUploadConfirmed?: (context: UploadCompleteContext) => Promise<void> | void;
38
+ };
39
+ download?: {
40
+ enabled?: boolean;
41
+ presignGuard?: (context: DownloadHookContext) => Promise<void> | void;
42
+ onPresigned?: (context: DownloadSuccessContext) => Promise<void> | void;
43
+ };
44
+ delete?: {
45
+ enabled?: boolean;
46
+ deleteGuard?: (context: DeleteHookContext) => Promise<void> | void;
47
+ onDeleted?: (context: DeleteHookContext) => Promise<void> | void;
48
+ };
49
+ multipart?: {
50
+ enabled?: boolean;
51
+ requireFileSize?: boolean;
52
+ initGuard?: (context: MultipartHookContext) => Promise<void> | void;
53
+ partGuard?: (context: MultipartHookContext) => Promise<void> | void;
54
+ completeGuard?: (context: MultipartHookContext) => Promise<void> | void;
55
+ abortGuard?: (context: MultipartHookContext) => Promise<void> | void;
56
+ listGuard?: (context: MultipartHookContext & {
57
+ uploadId: string;
58
+ }) => Promise<void> | void;
59
+ onInit?: (context: MultipartInitSuccessContext) => Promise<void> | void;
60
+ onComplete?: (context: MultipartCompleteSuccessContext) => Promise<void> | void;
61
+ onAbort?: (context: MultipartHookContext & {
62
+ uploadId: string;
63
+ }) => Promise<void> | void;
64
+ onList?: (context: MultipartListSuccessContext) => Promise<void> | void;
65
+ };
66
+ };
@@ -0,0 +1 @@
1
+ export type S3Handler = (request: Request) => Promise<Response>;
@@ -0,0 +1,16 @@
1
+ import type { S3Handler } from "./s3-handler";
2
+ export type S3Handlers = {
3
+ presign: {
4
+ upload: S3Handler;
5
+ confirm: S3Handler;
6
+ download: S3Handler;
7
+ };
8
+ multipart: {
9
+ init: S3Handler;
10
+ part: S3Handler;
11
+ complete: S3Handler;
12
+ abort: S3Handler;
13
+ listParts: S3Handler;
14
+ };
15
+ delete: S3Handler;
16
+ };
@@ -0,0 +1,4 @@
1
+ import type { S3HandlerConfig } from "./s3-handler-config";
2
+ export type S3RouteHandlerConfig = S3HandlerConfig & {
3
+ basePath: string;
4
+ };
@@ -0,0 +1,15 @@
1
+ import type { HookContext } from "./hook-context";
2
+ /** contentLength and eTag are verified by S3 via HeadObject. */
3
+ export type UploadCompleteContext = HookContext & {
4
+ key: string;
5
+ bucket: string;
6
+ contentType?: string;
7
+ contentLength: number;
8
+ eTag?: string;
9
+ metadata?: Record<string, string>;
10
+ /** Resolved ACL. Omitted when ACL lookup is disabled or unsupported. */
11
+ acl?: "private" | "public-read";
12
+ fileName?: string;
13
+ versionId?: string;
14
+ lastModified?: string;
15
+ };
@@ -0,0 +1,10 @@
1
+ import type { HookContext } from "./hook-context";
2
+ /** Values are client-declared and not verified by S3. */
3
+ export type UploadHookContext = HookContext & {
4
+ key: string;
5
+ bucket: string;
6
+ contentType?: string;
7
+ fileSize?: number;
8
+ metadata?: Record<string, string>;
9
+ acl?: "private" | "public-read";
10
+ };
@@ -0,0 +1,5 @@
1
+ import type { UploadHookContext } from "./upload-hook-context";
2
+ export type UploadSuccessContext = UploadHookContext & {
3
+ url: string;
4
+ expiresIn: number;
5
+ };
package/dist/types.d.ts CHANGED
@@ -1,214 +1 @@
1
- import type { S3Client } from "@aws-sdk/client-s3";
2
- export type HookContext = {
3
- request: Request;
4
- };
5
- /** Values are **client-declared** — not verified by S3. */
6
- export type UploadHookContext = HookContext & {
7
- key: string;
8
- bucket: string;
9
- /** Client-declared MIME type. */
10
- contentType?: string;
11
- /** Client-declared file size in bytes. Can be spoofed — use for pre-checks only. */
12
- fileSize?: number;
13
- metadata?: Record<string, string>;
14
- acl?: "private" | "public-read";
15
- };
16
- export type UploadSuccessContext = UploadHookContext & {
17
- url: string;
18
- expiresIn: number;
19
- };
20
- /** `contentLength` and `eTag` are **verified by S3** via `HeadObject`. */
21
- export type UploadCompleteContext = HookContext & {
22
- key: string;
23
- bucket: string;
24
- /** MIME type from HeadObject. */
25
- contentType?: string;
26
- /** Verified file size in bytes from HeadObject. */
27
- contentLength: number;
28
- /** ETag from HeadObject. */
29
- eTag?: string;
30
- metadata?: Record<string, string>;
31
- acl: "private" | "public-read";
32
- /** Display file name parsed from the object's Content-Disposition header. */
33
- fileName?: string;
34
- /** Public URL of the object. Only present when `acl` is `public-read` and `publicUrlBase` is configured. */
35
- publicUrl?: string;
36
- /** Object version ID. Only present when bucket versioning is enabled. */
37
- versionId?: string;
38
- /** Last-modified timestamp from HeadObject (ISO 8601 string). */
39
- lastModified?: string;
40
- };
41
- export type DownloadHookContext = HookContext & {
42
- key: string;
43
- bucket: string;
44
- fileName?: string;
45
- };
46
- export type DownloadSuccessContext = DownloadHookContext & {
47
- url: string;
48
- expiresIn: number;
49
- };
50
- export type DeleteHookContext = HookContext & {
51
- key: string;
52
- bucket: string;
53
- };
54
- export type MultipartHookContext = HookContext & {
55
- key: string;
56
- bucket: string;
57
- /**
58
- * Declared byte size of the file — available during `init` only.
59
- * Undefined during `part`, `complete`, and `abort` operations.
60
- */
61
- fileSize?: number;
62
- };
63
- export type MultipartInitSuccessContext = MultipartHookContext & {
64
- uploadId: string;
65
- contentType?: string;
66
- /** Declared byte size of the file (as provided by the client). */
67
- fileSize?: number;
68
- metadata?: Record<string, string>;
69
- acl?: "private" | "public-read";
70
- };
71
- export type MultipartCompleteSuccessContext = MultipartHookContext & {
72
- uploadId: string;
73
- contentLength: number;
74
- contentType?: string;
75
- eTag?: string;
76
- metadata: Record<string, string>;
77
- acl: "private" | "public-read";
78
- /** Display file name parsed from the object's Content-Disposition header. */
79
- fileName?: string;
80
- /** Public URL of the object. Only present when `acl` is `public-read` and `publicUrlBase` is configured. */
81
- publicUrl?: string;
82
- /** Object version ID. Only present when bucket versioning is enabled. */
83
- versionId?: string;
84
- /** Last-modified timestamp from HeadObject (ISO 8601 string). */
85
- lastModified?: string;
86
- };
87
- export type S3HandlerConfig = {
88
- /** The S3 client instance used to communicate with your bucket. */
89
- s3: S3Client;
90
- /** Default bucket used when no `bucket` is specified in the request payload. */
91
- defaultBucket: string;
92
- /**
93
- * Resolves the public base URL for a bucket. The returned string is combined
94
- * with the object key to produce `publicUrl` on upload/multipart responses
95
- * when the object ACL is `public-read`. Return `undefined` for unknown buckets
96
- * or omit entirely if all objects are private.
97
- *
98
- * ```ts
99
- * // Single bucket (path-style or virtual-hosted)
100
- * resolvePublicUrl: (_bucket) => "https://cdn.example.com"
101
- *
102
- * // Multiple buckets — each mapped to its own domain
103
- * resolvePublicUrl: (bucket) =>
104
- * ({ avatars: "https://cdn.example.com", media: "https://media.example.com" })[bucket]
105
- * ```
106
- */
107
- resolvePublicUrl?: (bucket: string) => string | undefined;
108
- /**
109
- * Runs before every request. Return normally to allow, throw to reject.
110
- * Attach `status` to the thrown error to set the HTTP response code (default `403`).
111
- */
112
- guard?: (context: HookContext) => Promise<void> | void;
113
- upload?: {
114
- /** Enable the upload endpoints. @default false */
115
- enabled?: boolean;
116
- /**
117
- * Presign method for simple (non-multipart) uploads.
118
- * Use `"put"` for S3-compatible providers that do not support presigned POST (e.g. Cloudflare R2).
119
- * @default "post"
120
- */
121
- method?: "post" | "put";
122
- /**
123
- * Reject presign requests that do not include `fileSize`.
124
- *
125
- * When `true` and `method` is `"put"`, the handler returns `400` if the
126
- * client omits `fileSize`. This guarantees that every generated presigned
127
- * PUT URL has `Content-Length` locked into its HMAC signature — preventing
128
- * a caller from uploading a file of a different size with the same URL.
129
- *
130
- * Has no effect when `method` is `"post"` because presigned POST already
131
- * enforces size via the `content-length-range` policy condition.
132
- *
133
- * @default false
134
- */
135
- requireFileSize?: boolean;
136
- /** Runs before presigned URL generation. Values are **client-declared**. */
137
- presignGuard?: (context: UploadHookContext) => Promise<void> | void;
138
- /** Fires after presigned URL is issued. File not yet uploaded. */
139
- onPresigned?: (context: UploadSuccessContext) => Promise<void> | void;
140
- /** Runs before upload confirmation. */
141
- confirmGuard?: (context: HookContext & {
142
- key: string;
143
- bucket: string;
144
- }) => Promise<void> | void;
145
- /** Fires after upload confirmation. Values are **verified by S3** via `HeadObject`. */
146
- onUploadConfirmed?: (context: UploadCompleteContext) => Promise<void> | void;
147
- };
148
- download?: {
149
- /** Enable the download endpoint. @default false */
150
- enabled?: boolean;
151
- /** Runs before presigned GET URL generation. Values are **client-declared**. */
152
- presignGuard?: (context: DownloadHookContext) => Promise<void> | void;
153
- /** Fires after presigned GET URL is issued. */
154
- onPresigned?: (context: DownloadSuccessContext) => Promise<void> | void;
155
- };
156
- delete?: {
157
- /** Enable the delete endpoint. @default false */
158
- enabled?: boolean;
159
- /** Runs before object deletion. Values are **client-declared**. */
160
- deleteGuard?: (context: DeleteHookContext) => Promise<void> | void;
161
- /** Fires after object is successfully deleted. */
162
- onDeleted?: (context: DeleteHookContext) => Promise<void> | void;
163
- };
164
- multipart?: {
165
- /** Enable all multipart upload endpoints. @default false */
166
- enabled?: boolean;
167
- /**
168
- * Reject multipart init requests that do not include `fileSize`.
169
- *
170
- * When `true`, the handler returns `400` if the client omits `fileSize`
171
- * on the init call. Because the react client derives each part's byte
172
- * count from `file.size` (which is always known), this transitively
173
- * ensures that every `UploadPart` presigned URL has `Content-Length`
174
- * locked into its HMAC signature.
175
- *
176
- * @default false
177
- */
178
- requireFileSize?: boolean;
179
- /** Runs before `CreateMultipartUpload`. Values are **client-declared**. */
180
- initGuard?: (context: MultipartHookContext) => Promise<void> | void;
181
- /** Runs before `UploadPart` presign. */
182
- partGuard?: (context: MultipartHookContext) => Promise<void> | void;
183
- /** Runs before `CompleteMultipartUpload`. */
184
- completeGuard?: (context: MultipartHookContext) => Promise<void> | void;
185
- /** Runs before `AbortMultipartUpload`. */
186
- abortGuard?: (context: MultipartHookContext) => Promise<void> | void;
187
- /** Fires after `CreateMultipartUpload`. */
188
- onInit?: (context: MultipartInitSuccessContext) => Promise<void> | void;
189
- /** Fires after `CompleteMultipartUpload`. Values are **verified by S3** via `HeadObject`. */
190
- onComplete?: (context: MultipartCompleteSuccessContext) => Promise<void> | void;
191
- /** Fires after `AbortMultipartUpload`. */
192
- onAbort?: (context: MultipartHookContext & {
193
- uploadId: string;
194
- }) => Promise<void> | void;
195
- };
196
- };
197
- export type S3RouteHandlerConfig = S3HandlerConfig & {
198
- basePath: string;
199
- };
200
- export type S3Handler = (request: Request) => Promise<Response>;
201
- export type S3Handlers = {
202
- presign: {
203
- upload: S3Handler;
204
- confirm: S3Handler;
205
- download: S3Handler;
206
- };
207
- multipart: {
208
- init: S3Handler;
209
- part: S3Handler;
210
- complete: S3Handler;
211
- abort: S3Handler;
212
- };
213
- delete: S3Handler;
214
- };
1
+ export * from "./types/index";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-s3/server",
3
- "version": "3.1045.2",
3
+ "version": "3.1047.0",
4
4
  "description": "Framework-agnostic S3 server handlers — presigned uploads, downloads, deletes, and multipart operations",
5
5
  "keywords": [
6
6
  "s3",
@@ -46,17 +46,17 @@
46
46
  "dist"
47
47
  ],
48
48
  "dependencies": {
49
- "@aws-sdk/s3-presigned-post": "^3.1045.0",
50
- "@aws-sdk/s3-request-presigner": "^3.1045.0"
49
+ "@aws-sdk/s3-presigned-post": "^3.1055.0",
50
+ "@aws-sdk/s3-request-presigner": "^3.1055.0"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "@aws-sdk/client-s3": ">=3"
54
54
  },
55
55
  "devDependencies": {
56
- "@aws-sdk/client-s3": "^3.1045.0",
56
+ "@aws-sdk/client-s3": "^3.1055.0",
57
57
  "tsc-alias": "^1.8.17",
58
58
  "tsup": "^8.5.1",
59
- "typescript": "^6.0.3"
59
+ "typescript": "6.0.3"
60
60
  },
61
61
  "publishConfig": {
62
62
  "access": "public"
@@ -1,13 +0,0 @@
1
- /**
2
- * Builds the public URL for an S3 object.
3
- *
4
- * Returns `undefined` when:
5
- * - the object is `"private"`,
6
- * - `publicUrlBase` is not provided, or
7
- * - `publicUrlBase` returns `undefined` for the given bucket.
8
- *
9
- * @param publicUrlBase - A function that returns the base URL for a given bucket,
10
- * or `undefined` when no public URL is available for that bucket.
11
- * Matches the `resolvePublicUrl` field of `S3HandlerConfig`.
12
- */
13
- export declare function buildPublicUrl(publicUrlBase: ((bucket: string) => string | undefined) | undefined, bucket: string, key: string, acl: "public-read" | "private"): string | undefined;