@nexusts/upload 0.9.5 → 0.9.7
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/dist/index.js +51 -81
- package/dist/index.js.map +4 -4
- package/dist/upload.service.d.ts +3 -14
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9,11 +9,6 @@ var __legacyDecorateClassTS = function(decorators, target, key, desc) {
|
|
|
9
9
|
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
10
10
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
11
11
|
};
|
|
12
|
-
var __legacyDecorateParamTS = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
13
|
-
var __legacyMetadataTS = (k, v) => {
|
|
14
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
|
|
15
|
-
return Reflect.metadata(k, v);
|
|
16
|
-
};
|
|
17
12
|
|
|
18
13
|
// packages/upload/src/types.ts
|
|
19
14
|
import { METADATA_KEY } from "@nexusts/core";
|
|
@@ -35,58 +30,57 @@ var UPLOAD_META = "nexus:upload:options";
|
|
|
35
30
|
import { Inject, Injectable } from "@nexusts/core";
|
|
36
31
|
class UploadService {
|
|
37
32
|
static TOKEN = Symbol.for("nexus:UploadService");
|
|
38
|
-
#config;
|
|
39
33
|
#driveResolver = null;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
34
|
+
_cfg = null;
|
|
35
|
+
get config() {
|
|
36
|
+
if (!this._cfg) {
|
|
37
|
+
const c = this._config ?? {};
|
|
38
|
+
this._cfg = {
|
|
39
|
+
maxFileSize: c.maxFileSize ?? 10 * 1024 * 1024,
|
|
40
|
+
maxFiles: c.maxFiles ?? 5,
|
|
41
|
+
allowedMimeTypes: c.allowedMimeTypes ?? ["*"],
|
|
42
|
+
storage: c.storage ?? "memory",
|
|
43
|
+
driveToken: c.driveToken ?? "",
|
|
44
|
+
drivePrefix: c.drivePrefix ?? "",
|
|
45
|
+
preserveFilename: c.preserveFilename ?? false
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return this._cfg;
|
|
50
49
|
}
|
|
51
50
|
bindDriveResolver(fn) {
|
|
52
51
|
this.#driveResolver = fn;
|
|
53
52
|
}
|
|
54
53
|
getConfig() {
|
|
55
|
-
return { ...this
|
|
54
|
+
return { ...this.config };
|
|
56
55
|
}
|
|
57
56
|
async parseAndStore(c, fields) {
|
|
57
|
+
const cfg = this.config;
|
|
58
58
|
const body = await this.#parseBody(c);
|
|
59
59
|
const stored = {};
|
|
60
60
|
for (const spec of fields) {
|
|
61
61
|
const value = body[spec.fieldName];
|
|
62
62
|
const files = this.#extractFiles(value);
|
|
63
63
|
if (files.length === 0) {
|
|
64
|
-
if (spec.required)
|
|
64
|
+
if (spec.required)
|
|
65
65
|
throw new UploadError("MISSING_FIELD", spec.fieldName, `Missing required field "${spec.fieldName}".`);
|
|
66
|
-
}
|
|
67
66
|
continue;
|
|
68
67
|
}
|
|
69
|
-
if (files.length > spec.maxFiles)
|
|
68
|
+
if (files.length > spec.maxFiles)
|
|
70
69
|
throw new UploadError("TOO_MANY_FILES", spec.fieldName, `Field "${spec.fieldName}" accepts at most ${spec.maxFiles} files (got ${files.length}).`);
|
|
71
|
-
|
|
72
|
-
for (const f of files) {
|
|
70
|
+
for (const f of files)
|
|
73
71
|
this.#validate(f, spec.fieldName);
|
|
74
|
-
}
|
|
75
72
|
stored[spec.fieldName] = files.length === 1 ? files[0] : files;
|
|
76
73
|
}
|
|
77
74
|
c.set(UPLOAD_STORAGE_KEY, stored);
|
|
78
|
-
if (
|
|
79
|
-
const drive = this.#driveResolver(
|
|
75
|
+
if (cfg.driveToken && this.#driveResolver) {
|
|
76
|
+
const drive = this.#driveResolver(cfg.driveToken);
|
|
80
77
|
if (drive?.put) {
|
|
81
|
-
for (const
|
|
78
|
+
for (const entry of Object.values(stored)) {
|
|
82
79
|
const list = Array.isArray(entry) ? entry : [entry];
|
|
83
80
|
for (const file of list) {
|
|
84
|
-
const filename =
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
contentType: file.contentType
|
|
88
|
-
});
|
|
89
|
-
file.storedKey = key;
|
|
81
|
+
const filename = cfg.preserveFilename ? file.filename : this.#newFilename(file);
|
|
82
|
+
await drive.put(`${cfg.drivePrefix}/${filename}`, file.buffer, { contentType: file.contentType });
|
|
83
|
+
file.storedKey = `${cfg.drivePrefix}/${filename}`;
|
|
90
84
|
}
|
|
91
85
|
}
|
|
92
86
|
}
|
|
@@ -97,9 +91,7 @@ class UploadService {
|
|
|
97
91
|
if (!stored)
|
|
98
92
|
return;
|
|
99
93
|
const v = stored[fieldName];
|
|
100
|
-
|
|
101
|
-
return v[0];
|
|
102
|
-
return v;
|
|
94
|
+
return Array.isArray(v) ? v[0] : v;
|
|
103
95
|
}
|
|
104
96
|
getAll(c, fieldName) {
|
|
105
97
|
const stored = c.get(UPLOAD_STORAGE_KEY);
|
|
@@ -115,9 +107,8 @@ class UploadService {
|
|
|
115
107
|
async#parseBody(c) {
|
|
116
108
|
const req = c.req?.raw ?? c.req;
|
|
117
109
|
const ct = req.headers?.get?.("content-type") ?? "";
|
|
118
|
-
if (!ct.startsWith("multipart/form-data"))
|
|
110
|
+
if (!ct.startsWith("multipart/form-data"))
|
|
119
111
|
return {};
|
|
120
|
-
}
|
|
121
112
|
try {
|
|
122
113
|
return await c.req.parseBody({ all: true });
|
|
123
114
|
} catch (err) {
|
|
@@ -128,66 +119,46 @@ class UploadService {
|
|
|
128
119
|
if (value === undefined || value === null)
|
|
129
120
|
return [];
|
|
130
121
|
const list = Array.isArray(value) ? value : [value];
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
continue;
|
|
140
|
-
const filename = file.filename ?? file.name ?? "upload";
|
|
141
|
-
const contentType = file.type ?? "application/octet-stream";
|
|
142
|
-
out.push({
|
|
143
|
-
fieldName: file.name,
|
|
144
|
-
filename,
|
|
145
|
-
contentType,
|
|
146
|
-
encoding: "7bit",
|
|
147
|
-
buffer: Buffer.alloc(0),
|
|
148
|
-
size: file.size ?? 0
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
return out;
|
|
122
|
+
return list.filter((v) => typeof v === "object" && typeof v.arrayBuffer === "function").map((file) => ({
|
|
123
|
+
fieldName: file.name,
|
|
124
|
+
filename: file.filename ?? file.name ?? "upload",
|
|
125
|
+
contentType: file.type ?? "application/octet-stream",
|
|
126
|
+
encoding: "7bit",
|
|
127
|
+
buffer: Buffer.alloc(0),
|
|
128
|
+
size: file.size ?? 0
|
|
129
|
+
}));
|
|
152
130
|
}
|
|
153
131
|
#validate(file, fieldName) {
|
|
154
|
-
if (file.size > this
|
|
155
|
-
throw new UploadError("FILE_TOO_LARGE", fieldName, `File "${file.filename}" is ${file.size} bytes; max is ${this
|
|
156
|
-
|
|
157
|
-
if (!this.#mimeAllowed(file.contentType)) {
|
|
132
|
+
if (file.size > this.config.maxFileSize)
|
|
133
|
+
throw new UploadError("FILE_TOO_LARGE", fieldName, `File "${file.filename}" is ${file.size} bytes; max is ${this.config.maxFileSize}.`);
|
|
134
|
+
if (!this.#mimeAllowed(file.contentType))
|
|
158
135
|
throw new UploadError("MIME_NOT_ALLOWED", fieldName, `File "${file.filename}" has type "${file.contentType}"; not in the allow list.`);
|
|
159
|
-
}
|
|
160
136
|
}
|
|
161
137
|
#mimeAllowed(mime) {
|
|
162
|
-
for (const pat of this
|
|
138
|
+
for (const pat of this.config.allowedMimeTypes) {
|
|
163
139
|
if (pat === "*")
|
|
164
140
|
return true;
|
|
165
141
|
if (pat === mime)
|
|
166
142
|
return true;
|
|
167
|
-
if (pat.endsWith("/*"))
|
|
168
|
-
|
|
169
|
-
if (mime.startsWith(prefix))
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
143
|
+
if (pat.endsWith("/*") && mime.startsWith(pat.slice(0, -2)))
|
|
144
|
+
return true;
|
|
172
145
|
}
|
|
173
146
|
return false;
|
|
174
147
|
}
|
|
175
148
|
#newFilename(file) {
|
|
176
149
|
const ext = file.filename.includes(".") ? file.filename.slice(file.filename.lastIndexOf(".")) : "";
|
|
177
|
-
|
|
178
|
-
const rand = Math.random().toString(36).slice(2, 8);
|
|
179
|
-
return `${stamp}-${rand}${ext}`;
|
|
150
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}${ext}`;
|
|
180
151
|
}
|
|
181
152
|
}
|
|
153
|
+
__legacyDecorateClassTS([
|
|
154
|
+
Inject("UPLOAD_CONFIG")
|
|
155
|
+
], UploadService.prototype, "_config", undefined);
|
|
182
156
|
UploadService = __legacyDecorateClassTS([
|
|
183
|
-
Injectable()
|
|
184
|
-
__legacyDecorateParamTS(0, Inject("UPLOAD_CONFIG")),
|
|
185
|
-
__legacyMetadataTS("design:paramtypes", [
|
|
186
|
-
typeof UploadConfig === "undefined" ? Object : UploadConfig
|
|
187
|
-
])
|
|
157
|
+
Injectable()
|
|
188
158
|
], UploadService);
|
|
189
159
|
// packages/upload/src/upload.module.ts
|
|
190
160
|
import { Module } from "@nexusts/core";
|
|
161
|
+
import { safeGetMeta } from "@nexusts/core/di/safe-reflect";
|
|
191
162
|
|
|
192
163
|
// packages/upload/src/upload.middleware.ts
|
|
193
164
|
function uploadMiddleware(svc) {
|
|
@@ -211,7 +182,6 @@ function uploadMiddleware(svc) {
|
|
|
211
182
|
}
|
|
212
183
|
|
|
213
184
|
// packages/upload/src/upload.module.ts
|
|
214
|
-
import { safeGetMeta } from "@nexusts/core/di/safe-reflect";
|
|
215
185
|
class UploadModule {
|
|
216
186
|
static forRoot(config = {}) {
|
|
217
187
|
class ConfiguredUploadModule {
|
|
@@ -264,7 +234,7 @@ UploadModule = __legacyDecorateClassTS([
|
|
|
264
234
|
})
|
|
265
235
|
], UploadModule);
|
|
266
236
|
// packages/upload/src/decorators/upload.ts
|
|
267
|
-
import { safeGetMeta as safeGetMeta2, safeDefineMeta
|
|
237
|
+
import { safeGetMeta as safeGetMeta2, safeDefineMeta } from "@nexusts/core/di/safe-reflect";
|
|
268
238
|
var DEFAULT_NAME = "__upload__";
|
|
269
239
|
function Upload(name = DEFAULT_NAME, options = {}) {
|
|
270
240
|
return function(target, context) {
|
|
@@ -286,7 +256,7 @@ function Upload(name = DEFAULT_NAME, options = {}) {
|
|
|
286
256
|
const cls = target?.constructor ?? target;
|
|
287
257
|
const existing = safeGetMeta2(UPLOAD_META, cls, propertyKey) ?? [];
|
|
288
258
|
existing.push({ name, options });
|
|
289
|
-
|
|
259
|
+
safeDefineMeta(UPLOAD_META, existing, cls, propertyKey);
|
|
290
260
|
};
|
|
291
261
|
}
|
|
292
262
|
// packages/upload/src/decorators/uploaded-file.ts
|
|
@@ -326,5 +296,5 @@ export {
|
|
|
326
296
|
METADATA_KEY
|
|
327
297
|
};
|
|
328
298
|
|
|
329
|
-
//# debugId=
|
|
299
|
+
//# debugId=E3F4C4BF2780A4AB64756E2164756E21
|
|
330
300
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
"sources": ["../src/types.ts", "../src/upload.service.ts", "../src/upload.module.ts", "../src/upload.middleware.ts", "../src/decorators/upload.ts", "../src/decorators/uploaded-file.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * `@nexusts/upload` — file upload helper.\n *\n * @Module({\n * imports: [\n * UploadModule.forRoot({\n * maxFileSize: 10 * 1024 * 1024, // 10MB per file\n * maxFiles: 5,\n * allowedMimeTypes: ['image/*', 'application/pdf'],\n * storage: 'memory', // or 'drive' (uses nexus/drive)\n * }),\n * ],\n * })\n *\n * @Post('/avatars')\n * @Upload('avatar') // form field name\n * async upload(@UploadedFile('avatar') file: UploadedFile) {\n * return { size: file.size, type: file.contentType };\n * }\n *\n * @Post('/photos')\n * async multi(@UploadedFiles('photos') files: UploadedFile[]) {\n * return files.length;\n * }\n */\n\nimport { METADATA_KEY } from \"@nexusts/core\";\nimport { safeGetMeta, safeDefineMeta, safeHasMeta } from \"@nexusts/core/di/safe-reflect\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A file that has been parsed from `multipart/form-data`.\n *\n * The framework reads the entire body into memory (with a hard cap\n * enforced by `maxFileSize`) and exposes it as a `Buffer`. For very\n * large files (gigabytes), use a streaming approach instead — the\n * middleware leaves `stream` populated when the file is too large\n * to buffer.\n */\nexport interface UploadedFile {\n\t/** Form field name (e.g. \"avatar\", \"photos\"). */\n\tfieldName: string;\n\t/** Filename from the client (e.g. \"my-photo.png\"). */\n\tfilename: string;\n\t/** MIME type from the client. */\n\tcontentType: string;\n\t/** Encoding (typically '7bit'). */\n\tencoding: string;\n\t/** File content. */\n\tbuffer: Buffer;\n\t/** Convenience: same as `buffer.length`. */\n\tsize: number;\n}\n\n/** Top-level config. */\nexport interface UploadConfig {\n\t/** Max bytes per file. Default: 10 MB. */\n\tmaxFileSize?: number;\n\t/** Max number of files per request. Default: 5. */\n\tmaxFiles?: number;\n\t/**\n\t * Allowed MIME types. Supports `*` wildcards:\n\t * 'image/*' — any image type\n\t * 'application/pdf'\n\t * 'video/mp4'\n\t * Default: '*' (any).\n\t */\n\tallowedMimeTypes?: string[];\n\t/**\n\t * Where parsed files are stored in memory. Default: 'memory'.\n\t * The decorator reads from this storage on each access.\n\t */\n\tstorage?: \"memory\";\n\t/**\n\t * When set, parsed files are also pushed to the configured\n\t * `@nexusts/drive` storage under this prefix. The drive is\n\t * resolved by the DI token string.\n\t */\n\tdriveToken?: string;\n\tdrivePrefix?: string;\n\t/** When using drive storage: keep the original filename. Default: false (UUID). */\n\tpreserveFilename?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Decorator payload\n// ---------------------------------------------------------------------------\n\nexport interface UploadOptions {\n\t/** Form field name. Default: property name. */\n\tname?: string;\n\t/** Per-field max size (overrides the global default). */\n\tmaxSize?: number;\n\t/** Per-field MIME-type filter. */\n\tmimeTypes?: string[];\n\t/** Per-field max files (for multi-file). Default: 1. */\n\tmaxFiles?: number;\n\t/** Whether the field is required. Default: true. */\n\trequired?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Validation errors\n// ---------------------------------------------------------------------------\n\nexport class UploadError extends Error {\n\treadonly status = 400;\n\treadonly code: string;\n\treadonly field: string;\n\tconstructor(code: string, field: string, message: string) {\n\t\tsuper(message);\n\t\tthis.code = code;\n\t\tthis.field = field;\n\t\tthis.name = \"UploadError\";\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Middleware storage key (per-request)\n// ---------------------------------------------------------------------------\n\n/** Key under which the multipart middleware stores parsed files. */\nexport const UPLOAD_STORAGE_KEY = \"nexus:upload:files\";\n\n/** Internal metadata key (decorator). */\nexport const UPLOAD_META = \"nexus:upload:options\";\n\nexport { METADATA_KEY };\n",
|
|
6
|
-
"/**\n * `UploadService` — parses `multipart/form-data` requests and exposes\n * the parsed files to controller methods via decorators.\n
|
|
7
|
-
"/**\n * `UploadModule` — drop-in file upload handling.\n *\n * @Module({\n * imports: [\n * UploadModule.forRoot({\n * maxFileSize: 10 * 1024 * 1024,\n * allowedMimeTypes: ['image/*', 'application/pdf'],\n * storage: 'memory',\n * }),\n * ],\n * })\n *\n * After the framework router is built, call `UploadModule.mount(app, svc)`\n * to install the multipart middleware. The middleware looks at\n * the route's metadata (set by `@Upload('fieldName')`) to know\n * which fields to parse.\n */\nimport { Module } from \"@nexusts/core\";\nimport { UploadService } from \"./upload.service.js\";\nimport
|
|
6
|
+
"/**\n * `UploadService` — parses `multipart/form-data` requests and exposes\n * the parsed files to controller methods via decorators.\n */\nimport { Inject, Injectable } from \"@nexusts/core\";\nimport {\n\tUPLOAD_STORAGE_KEY,\n\tUploadError,\n\ttype UploadConfig,\n\ttype UploadedFile,\n} from \"./types.js\";\n\ninterface ParsedFileEntry {\n\tname: string;\n\tfilename?: string;\n\ttype?: string;\n\tsize?: number;\n\tarrayBuffer(): Promise<ArrayBuffer>;\n}\n\ntype ParsedBodyValue = string | ParsedFileEntry | Array<string | ParsedFileEntry> | undefined;\n\n@Injectable()\nexport class UploadService {\n\tstatic readonly TOKEN = Symbol.for(\"nexus:UploadService\");\n\n\t@Inject(\"UPLOAD_CONFIG\") declare private _config: UploadConfig;\n\t#driveResolver: ((token: string) => any) | null = null;\n\tprivate _cfg: Required<UploadConfig> | null = null;\n\n\tprivate get config(): Required<UploadConfig> {\n\t\tif (!this._cfg) {\n\t\t\tconst c = this._config ?? {};\n\t\t\tthis._cfg = {\n\t\t\t\tmaxFileSize: c.maxFileSize ?? 10 * 1024 * 1024,\n\t\t\t\tmaxFiles: c.maxFiles ?? 5,\n\t\t\t\tallowedMimeTypes: c.allowedMimeTypes ?? [\"*\"],\n\t\t\t\tstorage: (c.storage as \"memory\") ?? \"memory\",\n\t\t\t\tdriveToken: c.driveToken ?? \"\",\n\t\t\t\tdrivePrefix: c.drivePrefix ?? \"\",\n\t\t\t\tpreserveFilename: c.preserveFilename ?? false,\n\t\t\t};\n\t\t}\n\t\treturn this._cfg;\n\t}\n\n\tbindDriveResolver(fn: (token: string) => any): void {\n\t\tthis.#driveResolver = fn;\n\t}\n\n\tgetConfig(): Required<UploadConfig> {\n\t\treturn { ...this.config };\n\t}\n\n\tasync parseAndStore(c: any, fields: Array<{ fieldName: string; maxFiles: number; required: boolean }>): Promise<void> {\n\t\tconst cfg = this.config;\n\t\tconst body = await this.#parseBody(c);\n\t\tconst stored: Record<string, UploadedFile | UploadedFile[]> = {};\n\n\t\tfor (const spec of fields) {\n\t\t\tconst value = body[spec.fieldName];\n\t\t\tconst files = this.#extractFiles(value);\n\t\t\tif (files.length === 0) {\n\t\t\t\tif (spec.required) throw new UploadError(\"MISSING_FIELD\", spec.fieldName, `Missing required field \"${spec.fieldName}\".`);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (files.length > spec.maxFiles) throw new UploadError(\"TOO_MANY_FILES\", spec.fieldName, `Field \"${spec.fieldName}\" accepts at most ${spec.maxFiles} files (got ${files.length}).`);\n\t\t\tfor (const f of files) this.#validate(f, spec.fieldName);\n\t\t\tstored[spec.fieldName] = files.length === 1 ? files[0]! : files;\n\t\t}\n\t\tc.set(UPLOAD_STORAGE_KEY, stored);\n\t\tif (cfg.driveToken && this.#driveResolver) {\n\t\t\tconst drive = this.#driveResolver(cfg.driveToken);\n\t\t\tif (drive?.put) {\n\t\t\t\tfor (const entry of Object.values(stored)) {\n\t\t\t\t\tconst list = Array.isArray(entry) ? entry : [entry];\n\t\t\t\t\tfor (const file of list) {\n\t\t\t\t\t\tconst filename = cfg.preserveFilename ? file.filename : this.#newFilename(file);\n\t\t\t\t\t\tawait drive.put(`${cfg.drivePrefix}/${filename}`, file.buffer, { contentType: file.contentType });\n\t\t\t\t\t\t(file as any).storedKey = `${cfg.drivePrefix}/${filename}`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tget(c: any, fieldName: string): UploadedFile | undefined {\n\t\tconst stored: Record<string, UploadedFile | UploadedFile[]> | undefined = c.get(UPLOAD_STORAGE_KEY);\n\t\tif (!stored) return undefined;\n\t\tconst v = stored[fieldName];\n\t\treturn Array.isArray(v) ? v[0] : v;\n\t}\n\n\tgetAll(c: any, fieldName: string): UploadedFile[] {\n\t\tconst stored: Record<string, UploadedFile | UploadedFile[]> | undefined = c.get(UPLOAD_STORAGE_KEY);\n\t\tif (!stored) return [];\n\t\tconst v = stored[fieldName];\n\t\tif (Array.isArray(v)) return v;\n\t\tif (v) return [v];\n\t\treturn [];\n\t}\n\n\tasync #parseBody(c: any): Promise<Record<string, ParsedBodyValue>> {\n\t\tconst req = c.req?.raw ?? c.req;\n\t\tconst ct = (req.headers?.get?.(\"content-type\") ?? \"\") as string;\n\t\tif (!ct.startsWith(\"multipart/form-data\")) return {};\n\t\ttry {\n\t\t\treturn (await c.req.parseBody({ all: true })) as Record<string, ParsedBodyValue>;\n\t\t} catch (err) {\n\t\t\tthrow new UploadError(\"BAD_MULTIPART\", \"*\", `Failed to parse multipart body: ${(err as Error).message}`);\n\t\t}\n\t}\n\n\t#extractFiles(value: ParsedBodyValue): UploadedFile[] {\n\t\tif (value === undefined || value === null) return [];\n\t\tconst list = Array.isArray(value) ? value : [value];\n\t\treturn list\n\t\t\t.filter((v): v is ParsedFileEntry => typeof v === \"object\" && typeof (v as any).arrayBuffer === \"function\")\n\t\t\t.map((file) => ({\n\t\t\t\tfieldName: file.name,\n\t\t\t\tfilename: (file as any).filename ?? file.name ?? \"upload\",\n\t\t\t\tcontentType: file.type ?? \"application/octet-stream\",\n\t\t\t\tencoding: \"7bit\",\n\t\t\t\tbuffer: Buffer.alloc(0),\n\t\t\t\tsize: file.size ?? 0,\n\t\t\t}));\n\t}\n\n\t#validate(file: UploadedFile, fieldName: string): void {\n\t\tif (file.size > this.config.maxFileSize) throw new UploadError(\"FILE_TOO_LARGE\", fieldName, `File \"${file.filename}\" is ${file.size} bytes; max is ${this.config.maxFileSize}.`);\n\t\tif (!this.#mimeAllowed(file.contentType)) throw new UploadError(\"MIME_NOT_ALLOWED\", fieldName, `File \"${file.filename}\" has type \"${file.contentType}\"; not in the allow list.`);\n\t}\n\n\t#mimeAllowed(mime: string): boolean {\n\t\tfor (const pat of this.config.allowedMimeTypes) {\n\t\t\tif (pat === \"*\") return true;\n\t\t\tif (pat === mime) return true;\n\t\t\tif (pat.endsWith(\"/*\") && mime.startsWith(pat.slice(0, -2))) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t#newFilename(file: UploadedFile): string {\n\t\tconst ext = file.filename.includes(\".\") ? file.filename.slice(file.filename.lastIndexOf(\".\")) : \"\";\n\t\treturn `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}${ext}`;\n\t}\n}\n",
|
|
7
|
+
"/**\n * `UploadModule` — drop-in file upload handling.\n *\n * @Module({\n * imports: [\n * UploadModule.forRoot({\n * maxFileSize: 10 * 1024 * 1024,\n * allowedMimeTypes: ['image/*', 'application/pdf'],\n * storage: 'memory',\n * }),\n * ],\n * })\n *\n * After the framework router is built, call `UploadModule.mount(app, svc)`\n * to install the multipart middleware. The middleware looks at\n * the route's metadata (set by `@Upload('fieldName')`) to know\n * which fields to parse.\n */\nimport { Module } from \"@nexusts/core\";\nimport { UploadService } from \"./upload.service.js\";\nimport { safeGetMeta } from \"@nexusts/core/di/safe-reflect\";\nimport type { UploadConfig } from \"./types.js\";\nimport { uploadMiddleware } from \"./upload.middleware.js\";\n\n@Module({\n\tproviders: [\n\t\tUploadService,\n\t\t{ provide: UploadService.TOKEN, useExisting: UploadService },\n\t],\n\texports: [UploadService, UploadService.TOKEN],\n})\nexport class UploadModule {\n\tstatic forRoot(config: UploadConfig = {}) {\n\t\t@Module({\n\t\t\tproviders: [\n\t\t\t\tUploadService,\n\t\t\t\t{ provide: UploadService.TOKEN, useExisting: UploadService },\n\t\t\t\t{ provide: \"UPLOAD_CONFIG\", useValue: config },\n\t\t\t],\n\t\t\texports: [UploadService, UploadService.TOKEN],\n\t\t})\n\t\tclass ConfiguredUploadModule {}\n\t\tObject.defineProperty(ConfiguredUploadModule, \"name\", {\n\t\t\tvalue: \"ConfiguredUploadModule\",\n\t\t});\n\t\treturn ConfiguredUploadModule;\n\t}\n\n\t/**\n\t * Install the multipart middleware on the Hono app. Walk the\n\t * route table, collect every `@Upload` decorator, and emit a\n\t * middleware that knows which fields to parse.\n\t */\n\tstatic mount(app: any, svc: UploadService, routes: any[] = []): void {\n\t\tconst fieldSet = new Map<string, { maxFiles: number; required: boolean }>();\n\t\tfor (const r of routes) {\n\t\t\tconst metas = (safeGetMeta(\"nexus:upload:options\", r.target.constructor, r.propertyKey) ?? []) as Array<{ name: string; options: any }>;\n\t\t\tfor (const m of metas) {\n\t\t\t\tconst existing = fieldSet.get(m.name);\n\t\t\t\tconst maxFiles = m.options?.maxFiles ?? 1;\n\t\t\t\tconst required = m.options?.required ?? true;\n\t\t\t\tif (!existing || maxFiles > existing.maxFiles) {\n\t\t\t\t\tfieldSet.set(m.name, { maxFiles, required: existing?.required ?? required });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Per-route middleware: we capture the fields for that route in\n\t\t// a closure. Hono supports per-route middleware via `app.use(path, mw)`.\n\t\tapp.use(\"/**\", async (c: any, next: () => Promise<any>) => {\n\t\t\tconst fields: Array<{ fieldName: string; maxFiles: number; required: boolean }> = [];\n\t\t\tfor (const [name, info] of fieldSet.entries()) {\n\t\t\t\tfields.push({ fieldName: name, ...info });\n\t\t\t}\n\t\t\tc.set(\"uploadFields\", fields);\n\t\t\treturn uploadMiddleware(svc)(c, next);\n\t\t});\n\t}\n}",
|
|
8
8
|
"/**\n * Hono middleware that parses multipart/form-data and stores the\n * requested fields on the Hono context. Routes that need\n * `@UploadedFile` opt in by:\n *\n * 1. Calling `uploadMiddleware(fieldSpecs)` to register the fields\n * they expect.\n * 2. Or, listing the field names in the `@Upload(...)` decorator\n * metadata, which the `UploadModule` reads to wire up the\n * middleware at boot.\n *\n * For convenience, `UploadModule.mountAll()` walks the route table\n * and installs the middleware on every controller method that has\n * an `@Upload(...)` decorator.\n */\nimport { UploadError } from \"./types.js\";\n\nexport interface FieldSpec {\n\tfieldName: string;\n\tmaxFiles: number;\n\trequired: boolean;\n}\n\n/**\n * Middleware factory. Returns a Hono middleware that:\n * 1. Skips non-multipart requests.\n * 2. Asks the UploadService to parse and store the given fields.\n * 3. On `UploadError`, returns a 400 response with the error code.\n */\nexport function uploadMiddleware(svc: {\n\tparseAndStore(c: any, fields: FieldSpec[]): Promise<void>;\n}) {\n\treturn async (c: any, next: () => Promise<any>) => {\n\t\tconst ct = (c.req?.raw?.headers?.get?.(\"content-type\") ?? \"\") as string;\n\t\tif (!ct.startsWith(\"multipart/form-data\")) return next();\n\t\t// The fields are read off the route at boot. By the time this\n\t\t// middleware runs, `c.var` should have the route spec.\n\t\tconst fields: FieldSpec[] = (c.get?.(\"uploadFields\") as FieldSpec[] | undefined) ?? [];\n\t\tif (fields.length === 0) return next();\n\t\ttry {\n\t\t\tawait svc.parseAndStore(c, fields);\n\t\t\treturn next();\n\t\t} catch (err) {\n\t\t\tif (err instanceof UploadError) {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{ error: err.message, code: err.code, field: err.field },\n\t\t\t\t\terr.status as any,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t};\n}\n",
|
|
9
9
|
"/**\n * `@Upload('fieldName', opts?)` — declare that a controller method\n * expects one or more uploaded files in `multipart/form-data`.\n *\n * @Post('/avatars')\n * @Upload('avatar')\n * async upload(ctx: Context) {\n * const file = ctx.uploadedFile('avatar');\n * ...\n * }\n *\n * @Post('/photos')\n * @Upload('photos', { maxFiles: 10 })\n * async multi(ctx: Context) {\n * const files = ctx.uploadedFiles('photos');\n * ...\n * }\n *\n * Standard decorator mode (TC39):\n * The decorator stores metadata via `context.metadata` and copies\n * it to the class constructor via `__nexus_meta__`.\n *\n * Legacy decorator mode (experimentalDecorators):\n * Uses the old `safeDefineMeta` path with `reflect-metadata` or the\n * framework's internal Map fallback.\n */\nimport { UPLOAD_META, type UploadOptions } from \"../types.js\";\nimport { safeGetMeta, safeDefineMeta } from \"@nexusts/core/di/safe-reflect\";\n\n/** Default name when the decorator is applied without arguments. */\nconst DEFAULT_NAME = \"__upload__\";\n\nexport function Upload(name: string = DEFAULT_NAME, options: UploadOptions = {}): any {\n\treturn function (this: any, target: any, context?: any): void {\n\t\t// ── Standard decorator mode (TC39) ──\n\t\tif (context?.kind === \"method\" && context?.metadata) {\n\t\t\tconst cls = typeof target === \"function\" ? target : target?.constructor;\n\t\t\tif (!cls) return;\n\t\t\tconst key = typeof context.name === \"symbol\" ? context.name : String(context.name);\n\t\t\tconst existing: Array<{ name: string; options: UploadOptions }> =\n\t\t\t\tcontext.metadata[UPLOAD_META]?.[key] ?? [];\n\t\t\texisting.push({ name, options });\n\t\t\tif (!context.metadata[UPLOAD_META]) context.metadata[UPLOAD_META] = {};\n\t\t\tcontext.metadata[UPLOAD_META][key] = existing;\n\t\t\treturn;\n\t\t}\n\n\t\t// ── Legacy decorator mode ──\n\t\tconst propertyKey = typeof context === \"string\" || typeof context === \"symbol\"\n\t\t\t? context\n\t\t\t: (arguments[1] as string | symbol | undefined);\n\t\tif (!propertyKey) return;\n\t\tconst cls = (target as any)?.constructor ?? target;\n\t\tconst existing: Array<{ name: string; options: UploadOptions }> =\n\t\t\tsafeGetMeta(UPLOAD_META, cls, propertyKey) ?? [];\n\t\texisting.push({ name, options });\n\t\tsafeDefineMeta(UPLOAD_META, existing, cls, propertyKey);\n\t};\n}\n",
|
|
10
10
|
"/**\n * `@UploadedFile('fieldName')` — inject a single uploaded file into\n * the controller method.\n *\n * The framework pulls the parsed file from the Hono context (which\n * the upload middleware populated). If the field is missing and the\n * `@Upload` decorator declared it required, the middleware has\n * already returned a 400 — by the time we run, the file is either\n * present or the request is non-multipart.\n */\nimport { createParamDecorator } from \"@nexusts/core\";\nimport { UPLOAD_STORAGE_KEY } from \"../types.js\";\n\nexport const UploadedFile = (fieldName?: string) =>\n\tcreateParamDecorator(0x80 as any /* USER */, fieldName); // placeholder token\n\n// ---------------------------------------------------------------------\n// The decorator above is a placeholder for the framework's param\n// decorator factory. The actual pull-from-context logic is wired by\n// the framework's param resolver at boot. We re-export a helper\n// that, given a Hono context, returns the file.\n// ---------------------------------------------------------------------\n\n/**\n * Resolve a `@UploadedFile(name)` call against a Hono context. The\n * framework invokes this from its param pipeline. Application\n * code typically does not call this directly — use the decorator.\n */\nexport function getUploadedFile(c: any, fieldName: string) {\n\tconst stored = c.get?.(UPLOAD_STORAGE_KEY) ?? c.var?.[UPLOAD_STORAGE_KEY];\n\tif (!stored) return undefined;\n\tconst v = stored[fieldName];\n\tif (Array.isArray(v)) return v[0];\n\treturn v;\n}\n\n/** Resolve a `@UploadedFiles(name)` call. */\nexport function getUploadedFiles(c: any, fieldName: string) {\n\tconst stored = c.get?.(UPLOAD_STORAGE_KEY) ?? c.var?.[UPLOAD_STORAGE_KEY];\n\tif (!stored) return [];\n\tconst v = stored[fieldName];\n\tif (Array.isArray(v)) return v;\n\tif (v) return [v];\n\treturn [];\n}"
|
|
11
11
|
],
|
|
12
|
-
"mappings": "
|
|
13
|
-
"debugId": "
|
|
12
|
+
"mappings": "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",
|
|
13
|
+
"debugId": "E3F4C4BF2780A4AB64756E2164756E21",
|
|
14
14
|
"names": []
|
|
15
15
|
}
|
package/dist/upload.service.d.ts
CHANGED
|
@@ -1,28 +1,17 @@
|
|
|
1
1
|
import { type UploadConfig, type UploadedFile } from "./types.js";
|
|
2
2
|
export declare class UploadService {
|
|
3
3
|
#private;
|
|
4
|
-
/** DI token. */
|
|
5
4
|
static readonly TOKEN: unique symbol;
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
private _config;
|
|
6
|
+
private _cfg;
|
|
7
|
+
private get config();
|
|
8
8
|
bindDriveResolver(fn: (token: string) => any): void;
|
|
9
|
-
/** The config this service was constructed with. */
|
|
10
9
|
getConfig(): Required<UploadConfig>;
|
|
11
|
-
/**
|
|
12
|
-
* Parse the request body and store the requested fields on the
|
|
13
|
-
* Hono context. Called by `uploadMiddleware`.
|
|
14
|
-
*/
|
|
15
10
|
parseAndStore(c: any, fields: Array<{
|
|
16
11
|
fieldName: string;
|
|
17
12
|
maxFiles: number;
|
|
18
13
|
required: boolean;
|
|
19
14
|
}>): Promise<void>;
|
|
20
|
-
/**
|
|
21
|
-
* Read a single file from the stored body. Used by `@UploadedFile`.
|
|
22
|
-
*/
|
|
23
15
|
get(c: any, fieldName: string): UploadedFile | undefined;
|
|
24
|
-
/**
|
|
25
|
-
* Read multiple files from the stored body. Used by `@UploadedFiles`.
|
|
26
|
-
*/
|
|
27
16
|
getAll(c: any, fieldName: string): UploadedFile[];
|
|
28
17
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexusts/upload",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.7",
|
|
4
4
|
"description": "Multipart file upload with validation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
],
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@nexusts/core": "^0.9.
|
|
29
|
+
"@nexusts/core": "^0.9.7"
|
|
30
30
|
},
|
|
31
31
|
"repository": {
|
|
32
32
|
"type": "git",
|