bun-gridfs-storage 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aissam Irhir
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,478 @@
1
+ # bun-gridfs-storage
2
+
3
+ A **Multer storage engine** for **GridFS** that works seamlessly with both **Bun** and **Node.js**.
4
+
5
+ This package provides a production-ready, type-safe implementation for storing uploaded files in MongoDB GridFS using Multer. Unlike the original `multer-gridfs-storage`, this package is optimized for Bun's runtime while maintaining full Node.js compatibility.
6
+
7
+ ## Features
8
+
9
+ - ✅ **Bun & Node.js compatible** - Works with both runtimes
10
+ - ✅ **TypeScript first** - Full type safety with strict mode
11
+ - ✅ **Event-driven** - Monitor upload progress and errors
12
+ - ✅ **Flexible configuration** - Custom filenames, metadata, and bucket names
13
+ - ✅ **Promise support** - Deferred database connections
14
+ - ✅ **Stream handling** - Optimized for Bun's stream implementation
15
+ - ✅ **Production ready** - Based on battle-tested implementation
16
+
17
+ ## Installation
18
+
19
+ ### Bun
20
+
21
+ ```bash
22
+ bun add bun-gridfs-storage multer mongodb mongoose
23
+ ```
24
+
25
+ ### npm
26
+
27
+ ```bash
28
+ npm install bun-gridfs-storage multer mongodb mongoose
29
+ ```
30
+
31
+ ### Yarn
32
+
33
+ ```bash
34
+ yarn add bun-gridfs-storage multer mongodb mongoose
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ### Basic Usage
40
+
41
+ ```typescript
42
+ import { BunGridFSStorage } from 'bun-gridfs-storage';
43
+ import multer from 'multer';
44
+ import mongoose from 'mongoose';
45
+
46
+ // Connect to MongoDB
47
+ await mongoose.connect('mongodb://localhost:27017/mydb');
48
+
49
+ // Create storage engine
50
+ const storage = new BunGridFSStorage({
51
+ db: mongoose.connection.db,
52
+ file: (req, file) => ({
53
+ filename: `${Date.now()}-${file.originalname}`,
54
+ bucketName: 'uploads',
55
+ }),
56
+ });
57
+
58
+ // Create multer instance
59
+ const upload = multer({ storage });
60
+
61
+ // Use in Express route
62
+ app.post('/upload', upload.single('file'), (req, res) => {
63
+ res.json({ file: req.file });
64
+ });
65
+ ```
66
+
67
+ ### Advanced Usage with Metadata
68
+
69
+ ```typescript
70
+ const storage = new BunGridFSStorage({
71
+ db: mongoose.connection.db,
72
+ file: async (req, file) => {
73
+ // Generate unique filename
74
+ const uid = crypto.randomUUID();
75
+
76
+ return {
77
+ filename: `${uid}-${file.originalname}`,
78
+ bucketName: 'uploads',
79
+ chunkSize: 255 * 1024, // 255KB chunks
80
+ metadata: {
81
+ userId: req.user.id,
82
+ uploadDate: new Date(),
83
+ originalName: file.originalname,
84
+ mimeType: file.mimetype,
85
+ },
86
+ contentType: file.mimetype,
87
+ };
88
+ },
89
+ });
90
+
91
+ // Listen to events
92
+ storage.on('connection', (db) => {
93
+ console.log('GridFS storage connected');
94
+ });
95
+
96
+ storage.on('file', (file) => {
97
+ console.log('File uploaded:', file.filename, 'Size:', file.size);
98
+ });
99
+
100
+ storage.on('streamError', (error, fileConfig) => {
101
+ console.error('Upload error:', error, fileConfig);
102
+ });
103
+ ```
104
+
105
+ ### Deferred Connection
106
+
107
+ ```typescript
108
+ // Storage can be created before database connection
109
+ const storage = new BunGridFSStorage({
110
+ db: getMongooseDb(), // Returns Promise<Db>
111
+ file: (req, file) => ({
112
+ filename: file.originalname,
113
+ bucketName: 'uploads',
114
+ }),
115
+ });
116
+
117
+ async function getMongooseDb() {
118
+ if (mongoose.connection.readyState === 1) {
119
+ return mongoose.connection.db;
120
+ }
121
+
122
+ return new Promise((resolve, reject) => {
123
+ mongoose.connection.once('open', () => {
124
+ resolve(mongoose.connection.db);
125
+ });
126
+
127
+ mongoose.connection.once('error', (err) => {
128
+ reject(err);
129
+ });
130
+ });
131
+ }
132
+ ```
133
+
134
+ ## API Documentation
135
+
136
+ ### Constructor
137
+
138
+ ```typescript
139
+ new BunGridFSStorage(options: BunGridFSStorageOptions)
140
+ ```
141
+
142
+ #### Options
143
+
144
+ | Option | Type | Required | Description |
145
+ |--------|------|----------|-------------|
146
+ | `db` | `Promise<Db>` \| `Db` | Yes | MongoDB database connection (can be a promise) |
147
+ | `file` | `FileConfigCallback` | No | Callback to configure file storage |
148
+
149
+ #### FileConfigCallback
150
+
151
+ ```typescript
152
+ type FileConfigCallback = (
153
+ req: Request,
154
+ file: Express.Multer.File
155
+ ) => Promise<FileConfig> | FileConfig;
156
+ ```
157
+
158
+ #### FileConfig
159
+
160
+ | Property | Type | Required | Description |
161
+ |----------|------|----------|-------------|
162
+ | `filename` | `string` | Yes | Filename to use in GridFS |
163
+ | `bucketName` | `string` | Yes | GridFS bucket name |
164
+ | `chunkSize` | `number` | No | Chunk size in bytes (default: 255KB) |
165
+ | `metadata` | `Record<string, any>` | No | Custom metadata to store |
166
+ | `contentType` | `string` | No | Content type (defaults to file mimetype) |
167
+
168
+ ### Methods
169
+
170
+ #### `isReady(): boolean`
171
+
172
+ Check if the storage engine is connected and ready.
173
+
174
+ ```typescript
175
+ if (storage.isReady()) {
176
+ console.log('Storage is ready');
177
+ }
178
+ ```
179
+
180
+ #### `getBucket(): GridFSBucket | null`
181
+
182
+ Get the underlying GridFSBucket instance.
183
+
184
+ ```typescript
185
+ const bucket = storage.getBucket();
186
+ if (bucket) {
187
+ // Use bucket directly for advanced operations
188
+ const downloadStream = bucket.openDownloadStreamByName('myfile.txt');
189
+ }
190
+ ```
191
+
192
+ #### `setDefaultBucketName(name: string): void`
193
+
194
+ Set the default bucket name.
195
+
196
+ ```typescript
197
+ storage.setDefaultBucketName('my-bucket');
198
+ ```
199
+
200
+ #### `setDefaultChunkSize(size: number): void`
201
+
202
+ Set the default chunk size in bytes.
203
+
204
+ ```typescript
205
+ storage.setDefaultChunkSize(512 * 1024); // 512KB
206
+ ```
207
+
208
+ ### Events
209
+
210
+ The storage engine extends `EventEmitter` and emits the following events:
211
+
212
+ #### `connection`
213
+
214
+ Emitted when database connection is established.
215
+
216
+ ```typescript
217
+ storage.on('connection', (db: Db) => {
218
+ console.log('Connected to database');
219
+ });
220
+ ```
221
+
222
+ #### `file`
223
+
224
+ Emitted when a file is successfully uploaded.
225
+
226
+ ```typescript
227
+ storage.on('file', (file: GridFSFile) => {
228
+ console.log('Uploaded:', file.filename, 'ID:', file.id);
229
+ });
230
+ ```
231
+
232
+ #### `streamError`
233
+
234
+ Emitted when a stream error occurs during upload.
235
+
236
+ ```typescript
237
+ storage.on('streamError', (error: Error, fileConfig: Partial<FileConfig>) => {
238
+ console.error('Upload failed:', error.message, fileConfig);
239
+ });
240
+ ```
241
+
242
+ #### `connectionFailed`
243
+
244
+ Emitted when database connection fails.
245
+
246
+ ```typescript
247
+ storage.on('connectionFailed', (error: Error) => {
248
+ console.error('Connection failed:', error.message);
249
+ });
250
+ ```
251
+
252
+ ## TypeScript Support
253
+
254
+ Full TypeScript definitions are included. Import types as needed:
255
+
256
+ ```typescript
257
+ import type {
258
+ GridFSFile,
259
+ FileConfig,
260
+ FileConfigCallback,
261
+ BunGridFSStorageOptions,
262
+ BunGridFSStorageEvents,
263
+ } from 'bun-gridfs-storage';
264
+ ```
265
+
266
+ ### GridFSFile Interface
267
+
268
+ ```typescript
269
+ interface GridFSFile {
270
+ id: ObjectId; // MongoDB ObjectId
271
+ filename: string; // Stored filename
272
+ originalname: string; // Original filename
273
+ encoding: string; // File encoding
274
+ mimetype: string; // MIME type
275
+ size: number; // File size in bytes
276
+ bucketName: string; // Bucket name
277
+ metadata?: Record<string, any>; // Custom metadata
278
+ contentType?: string; // Content type
279
+ uploadDate?: Date; // Upload timestamp
280
+ }
281
+ ```
282
+
283
+ ## Examples
284
+
285
+ ### Express.js with File Upload
286
+
287
+ ```typescript
288
+ import express from 'express';
289
+ import multer from 'multer';
290
+ import mongoose from 'mongoose';
291
+ import { BunGridFSStorage } from 'bun-gridfs-storage';
292
+
293
+ const app = express();
294
+
295
+ // Connect to MongoDB
296
+ await mongoose.connect('mongodb://localhost:27017/myapp');
297
+
298
+ // Create storage
299
+ const storage = new BunGridFSStorage({
300
+ db: mongoose.connection.db,
301
+ file: (req, file) => ({
302
+ filename: `${Date.now()}-${file.originalname}`,
303
+ bucketName: 'uploads',
304
+ metadata: {
305
+ userId: req.user?.id,
306
+ uploadedAt: new Date(),
307
+ },
308
+ }),
309
+ });
310
+
311
+ const upload = multer({
312
+ storage,
313
+ limits: { fileSize: 5 * 1024 * 1024 }, // 5MB limit
314
+ });
315
+
316
+ // Single file upload
317
+ app.post('/upload', upload.single('file'), (req, res) => {
318
+ res.json({
319
+ message: 'File uploaded successfully',
320
+ file: req.file,
321
+ });
322
+ });
323
+
324
+ // Multiple files upload
325
+ app.post('/upload-multiple', upload.array('files', 5), (req, res) => {
326
+ res.json({
327
+ message: 'Files uploaded successfully',
328
+ files: req.files,
329
+ });
330
+ });
331
+
332
+ app.listen(3000, () => {
333
+ console.log('Server running on port 3000');
334
+ });
335
+ ```
336
+
337
+ ### File Download
338
+
339
+ ```typescript
340
+ app.get('/download/:filename', async (req, res) => {
341
+ const bucket = storage.getBucket();
342
+
343
+ if (!bucket) {
344
+ return res.status(500).send('Storage not ready');
345
+ }
346
+
347
+ try {
348
+ const downloadStream = bucket.openDownloadStreamByName(req.params.filename);
349
+
350
+ downloadStream.on('error', (error) => {
351
+ res.status(404).send('File not found');
352
+ });
353
+
354
+ downloadStream.pipe(res);
355
+ } catch (error) {
356
+ res.status(500).send('Download failed');
357
+ }
358
+ });
359
+ ```
360
+
361
+ ### File Deletion
362
+
363
+ ```typescript
364
+ app.delete('/files/:id', async (req, res) => {
365
+ const bucket = storage.getBucket();
366
+
367
+ if (!bucket) {
368
+ return res.status(500).send('Storage not ready');
369
+ }
370
+
371
+ try {
372
+ await bucket.delete(new ObjectId(req.params.id));
373
+ res.json({ message: 'File deleted successfully' });
374
+ } catch (error) {
375
+ res.status(500).send('Deletion failed');
376
+ }
377
+ });
378
+ ```
379
+
380
+ ## Why This Package?
381
+
382
+ ### Bun Compatibility
383
+
384
+ The original `multer-gridfs-storage` package has compatibility issues with Bun's runtime. This package:
385
+
386
+ - Uses Bun-optimized stream handling
387
+ - Properly handles EventEmitter in Bun
388
+ - Tested with Bun's test runner
389
+ - Maintains Node.js compatibility
390
+
391
+ ### Comparison with multer-gridfs-storage
392
+
393
+ | Feature | bun-gridfs-storage | multer-gridfs-storage |
394
+ |---------|-------------------|----------------------|
395
+ | Bun Support | ✅ | ❌ |
396
+ | Node.js Support | ✅ | ✅ |
397
+ | TypeScript | ✅ Full types | ⚠️ Partial |
398
+ | Modern API | ✅ Promise-based | ⚠️ Callback-based |
399
+ | Events | ✅ 4 events | ✅ Multiple events |
400
+ | Maintenance | ✅ Active | ⚠️ Limited |
401
+
402
+ ## Development
403
+
404
+ ### Build
405
+
406
+ ```bash
407
+ bun run build
408
+ ```
409
+
410
+ This will generate:
411
+ - `dist/index.js` - CommonJS build
412
+ - `dist/index.mjs` - ESM build
413
+ - `dist/index.d.ts` - TypeScript declarations
414
+
415
+ ### Test
416
+
417
+ ```bash
418
+ bun test
419
+ ```
420
+
421
+ Run tests with watch mode:
422
+
423
+ ```bash
424
+ bun test --watch
425
+ ```
426
+
427
+ ### Clean
428
+
429
+ ```bash
430
+ bun run clean
431
+ ```
432
+
433
+ ## Requirements
434
+
435
+ - **Node.js** >= 18.0.0 OR **Bun** >= 1.0.0
436
+ - **MongoDB** >= 6.0.0
437
+ - **Mongoose** >= 8.0.0
438
+ - **Multer** >= 1.4.0
439
+
440
+ ## License
441
+
442
+ MIT License - see [LICENSE](LICENSE) file for details
443
+
444
+ ## Author
445
+
446
+ **Aissam Irhir** <aissamirhir@gmail.com>
447
+
448
+ ## Contributing
449
+
450
+ Contributions are welcome! Please feel free to submit a Pull Request.
451
+
452
+ 1. Fork the repository
453
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
454
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
455
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
456
+ 5. Open a Pull Request
457
+
458
+ ## Changelog
459
+
460
+ ### 1.0.0 (2025-01-XX)
461
+
462
+ - Initial release
463
+ - Bun and Node.js support
464
+ - Full TypeScript support
465
+ - Event-driven architecture
466
+ - Custom file configuration
467
+ - Deferred connection support
468
+
469
+ ## Support
470
+
471
+ If you encounter any issues or have questions:
472
+
473
+ - Open an issue on [GitHub](https://github.com/aissamirhir/bun-gridfs-storage/issues)
474
+ - Check existing issues for solutions
475
+
476
+ ## Acknowledgments
477
+
478
+ This package is inspired by and based on the battle-tested implementation used in production environments. Special thanks to the MongoDB and Multer communities.
@@ -0,0 +1,11 @@
1
+ /**
2
+ * bun-gridfs-storage - A Multer storage engine for GridFS
3
+ * Works with both Bun and Node.js
4
+ *
5
+ * @module bun-gridfs-storage
6
+ * @author Aissam Irhir <aissamirhir@gmail.com>
7
+ * @license MIT
8
+ */
9
+ export { BunGridFSStorage } from "./storage";
10
+ export type { GridFSFile, FileConfig, FileConfigCallback, BunGridFSStorageOptions, BunGridFSStorageEvents, MulterFile, } from "./types";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,YAAY,EACV,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,uBAAuB,EACvB,sBAAsB,EACtB,UAAU,GACX,MAAM,SAAS,CAAC"}