@devbro/neko-storage 0.1.7 → 0.1.9
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 +344 -48
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,56 +1,154 @@
|
|
|
1
1
|
# @devbro/neko-storage
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A unified, driver-based file storage abstraction library for Node.js and TypeScript. Store and manage files across multiple platforms using a consistent API.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @devbro/neko-storage
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- 🔌 **Multiple Storage Providers** - Local, AWS S3, GCP, Azure, FTP, SFTP
|
|
14
|
+
- 🎯 **Unified API** - Same interface for all storage providers
|
|
15
|
+
- 📦 **Multiple Formats** - Support for JSON, String, Buffer, and Stream
|
|
16
|
+
- 🔄 **Easy Migration** - Switch storage providers without changing your code
|
|
17
|
+
- 🛡️ **Type-Safe** - Full TypeScript support
|
|
18
|
+
- ⚡ **Async/Await** - Modern promise-based API
|
|
19
|
+
- 🔍 **Metadata Support** - Get file information (size, mime type, modified date)
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { Storage, LocalStorageProvider } from '@devbro/neko-storage';
|
|
25
|
+
|
|
26
|
+
// Create a storage instance
|
|
27
|
+
const provider = new LocalStorageProvider({
|
|
28
|
+
engine: 'local',
|
|
29
|
+
basePath: '/tmp/my-app-storage',
|
|
30
|
+
});
|
|
31
|
+
const storage = new Storage(provider);
|
|
32
|
+
|
|
33
|
+
// Store a file
|
|
34
|
+
await storage.put('documents/report.txt', 'Hello World');
|
|
35
|
+
|
|
36
|
+
// Check if file exists
|
|
37
|
+
const exists = await storage.exists('documents/report.txt'); // true
|
|
38
|
+
|
|
39
|
+
// Read the file
|
|
40
|
+
const content = await storage.getString('documents/report.txt'); // 'Hello World'
|
|
41
|
+
|
|
42
|
+
// Delete the file
|
|
43
|
+
await storage.delete('documents/report.txt');
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Core API
|
|
47
|
+
|
|
48
|
+
### Storing Files
|
|
49
|
+
|
|
50
|
+
Store content in various formats:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
// Store string content
|
|
54
|
+
await storage.put('path/to/file.txt', 'Hello World');
|
|
55
|
+
|
|
56
|
+
// Store JSON object
|
|
57
|
+
await storage.put('path/to/data.json', { name: 'John', age: 30 });
|
|
58
|
+
|
|
59
|
+
// Store Buffer
|
|
60
|
+
const buffer = Buffer.from('Binary data');
|
|
61
|
+
await storage.put('path/to/file.bin', buffer);
|
|
62
|
+
|
|
63
|
+
// Store from Stream
|
|
64
|
+
const stream = fs.createReadStream('/path/to/source.txt');
|
|
65
|
+
await storage.put('path/to/destination.txt', stream);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
> **Note**: If a file already exists at the specified path, it will be overwritten.
|
|
69
|
+
|
|
70
|
+
### Checking File Existence
|
|
6
71
|
|
|
7
72
|
```ts
|
|
8
|
-
|
|
73
|
+
const exists = await storage.exists('path/to/file.txt');
|
|
74
|
+
if (exists) {
|
|
75
|
+
console.log('File exists!');
|
|
76
|
+
}
|
|
77
|
+
```
|
|
9
78
|
|
|
10
|
-
|
|
79
|
+
### Reading Files
|
|
11
80
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
81
|
+
Retrieve files in different formats:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
// As JSON object
|
|
85
|
+
const data = await storage.getJson<{ name: string }>('config.json');
|
|
15
86
|
|
|
16
|
-
//
|
|
17
|
-
|
|
87
|
+
// As string
|
|
88
|
+
const text = await storage.getString('document.txt');
|
|
89
|
+
|
|
90
|
+
// As Buffer
|
|
91
|
+
const buffer = await storage.getBuffer('image.png');
|
|
92
|
+
|
|
93
|
+
// As Stream (for large files)
|
|
94
|
+
const stream = await storage.getStream('video.mp4');
|
|
95
|
+
stream.pipe(destination);
|
|
96
|
+
```
|
|
18
97
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
98
|
+
> **Note**: All read methods throw an error if the file doesn't exist.
|
|
99
|
+
|
|
100
|
+
### File Metadata
|
|
101
|
+
|
|
102
|
+
Get information about stored files:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const metadata = await storage.metadata('path/to/file.txt');
|
|
106
|
+
console.log(metadata);
|
|
107
|
+
/* Output:
|
|
22
108
|
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
109
|
+
size: 12345, // File size in bytes
|
|
110
|
+
mimeType: 'text/plain', // MIME type
|
|
111
|
+
lastModifiedDate: '2026-01-31' // Last modified date
|
|
26
112
|
}
|
|
27
113
|
*/
|
|
114
|
+
```
|
|
28
115
|
|
|
29
|
-
|
|
30
|
-
let file_content_as_Json = await storage.getJson('path/to/file/filename.ext');
|
|
31
|
-
let file_content_as_String = await storage.getString('path/to/file/filename.ext');
|
|
32
|
-
let file_content_as_Buffer = await storage.getBuffer('path/to/file/filename.ext');
|
|
33
|
-
let file_content_as_Stream = await storage.getStream('path/to/file/filename.ext');
|
|
116
|
+
> **Note**: Available metadata fields may vary depending on the storage provider.
|
|
34
117
|
|
|
118
|
+
### Deleting Files
|
|
35
119
|
|
|
36
|
-
|
|
37
|
-
|
|
120
|
+
```ts
|
|
121
|
+
const deleted = await storage.delete('path/to/file.txt');
|
|
122
|
+
console.log(deleted); // true if deleted, false if file didn't exist
|
|
38
123
|
```
|
|
39
124
|
|
|
40
|
-
##
|
|
125
|
+
## Storage Providers
|
|
41
126
|
|
|
42
|
-
### Local
|
|
127
|
+
### Local File System
|
|
43
128
|
|
|
44
|
-
Store files on the local
|
|
129
|
+
Store files on the local disk.
|
|
45
130
|
|
|
46
131
|
```ts
|
|
47
132
|
import { LocalStorageProvider, Storage } from '@devbro/neko-storage';
|
|
133
|
+
import path from 'path';
|
|
134
|
+
import os from 'os';
|
|
48
135
|
|
|
49
|
-
const
|
|
50
|
-
|
|
136
|
+
const provider = new LocalStorageProvider({
|
|
137
|
+
engine: 'local',
|
|
138
|
+
basePath: path.join(os.tmpdir(), 'my-app-storage'),
|
|
139
|
+
});
|
|
51
140
|
const storage = new Storage(provider);
|
|
52
141
|
```
|
|
53
142
|
|
|
143
|
+
**Configuration:**
|
|
144
|
+
|
|
145
|
+
- `engine`: `'local'`
|
|
146
|
+
- `basePath`: Absolute path to the storage directory
|
|
147
|
+
|
|
148
|
+
**Best for:** Development, testing, single-server deployments
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
54
152
|
### AWS S3
|
|
55
153
|
|
|
56
154
|
Store files in Amazon S3 buckets.
|
|
@@ -60,18 +158,30 @@ import { AWSS3StorageProvider, Storage } from '@devbro/neko-storage';
|
|
|
60
158
|
|
|
61
159
|
const provider = new AWSS3StorageProvider({
|
|
62
160
|
engine: 's3',
|
|
63
|
-
bucket: '
|
|
161
|
+
bucket: 'my-app-uploads',
|
|
64
162
|
s3Config: {
|
|
65
163
|
region: 'us-east-1',
|
|
66
164
|
credentials: {
|
|
67
|
-
accessKeyId:
|
|
68
|
-
secretAccessKey:
|
|
165
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
166
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
69
167
|
},
|
|
70
168
|
},
|
|
71
169
|
});
|
|
72
170
|
const storage = new Storage(provider);
|
|
73
171
|
```
|
|
74
172
|
|
|
173
|
+
**Configuration:**
|
|
174
|
+
|
|
175
|
+
- `engine`: `'s3'`
|
|
176
|
+
- `bucket`: S3 bucket name
|
|
177
|
+
- `s3Config`: AWS SDK configuration object
|
|
178
|
+
- `region`: AWS region
|
|
179
|
+
- `credentials`: Access credentials
|
|
180
|
+
|
|
181
|
+
**Best for:** Scalable cloud storage, CDN integration, high availability
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
75
185
|
### Google Cloud Storage (GCP)
|
|
76
186
|
|
|
77
187
|
Store files in Google Cloud Storage buckets.
|
|
@@ -81,20 +191,33 @@ import { GCPStorageProvider, Storage } from '@devbro/neko-storage';
|
|
|
81
191
|
|
|
82
192
|
const provider = new GCPStorageProvider({
|
|
83
193
|
engine: 'gcp',
|
|
84
|
-
bucket: '
|
|
194
|
+
bucket: 'my-app-uploads',
|
|
85
195
|
gcpConfig: {
|
|
86
|
-
projectId: '
|
|
196
|
+
projectId: 'my-project-id',
|
|
87
197
|
keyFilename: '/path/to/service-account-key.json',
|
|
88
|
-
//
|
|
89
|
-
// credentials:
|
|
198
|
+
// Alternative: Use credentials object
|
|
199
|
+
// credentials: require('./service-account-key.json'),
|
|
90
200
|
},
|
|
91
201
|
});
|
|
92
202
|
const storage = new Storage(provider);
|
|
93
203
|
```
|
|
94
204
|
|
|
205
|
+
**Configuration:**
|
|
206
|
+
|
|
207
|
+
- `engine`: `'gcp'`
|
|
208
|
+
- `bucket`: GCS bucket name
|
|
209
|
+
- `gcpConfig`: Google Cloud configuration
|
|
210
|
+
- `projectId`: GCP project ID
|
|
211
|
+
- `keyFilename`: Path to service account JSON key file
|
|
212
|
+
- `credentials`: Or provide credentials object directly
|
|
213
|
+
|
|
214
|
+
**Best for:** Google Cloud Platform ecosystem, global distribution
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
95
218
|
### Azure Blob Storage
|
|
96
219
|
|
|
97
|
-
Store files in Azure Blob Storage containers.
|
|
220
|
+
Store files in Microsoft Azure Blob Storage containers.
|
|
98
221
|
|
|
99
222
|
```ts
|
|
100
223
|
import { AzureBlobStorageProvider, Storage } from '@devbro/neko-storage';
|
|
@@ -102,14 +225,28 @@ import { AzureBlobStorageProvider, Storage } from '@devbro/neko-storage';
|
|
|
102
225
|
const provider = new AzureBlobStorageProvider({
|
|
103
226
|
engine: 'azure',
|
|
104
227
|
azureConfig: {
|
|
105
|
-
accountName: '
|
|
106
|
-
accountKey:
|
|
107
|
-
|
|
228
|
+
accountName: 'mystorageaccount',
|
|
229
|
+
accountKey: process.env.AZURE_STORAGE_KEY,
|
|
230
|
+
// Alternative: Use SAS token
|
|
231
|
+
// sasToken: process.env.AZURE_SAS_TOKEN,
|
|
232
|
+
containerName: 'uploads',
|
|
108
233
|
},
|
|
109
234
|
});
|
|
110
235
|
const storage = new Storage(provider);
|
|
111
236
|
```
|
|
112
237
|
|
|
238
|
+
**Configuration:**
|
|
239
|
+
|
|
240
|
+
- `engine`: `'azure'`
|
|
241
|
+
- `azureConfig`: Azure storage configuration
|
|
242
|
+
- `accountName`: Azure storage account name
|
|
243
|
+
- `accountKey`: Account access key (or use `sasToken`)
|
|
244
|
+
- `containerName`: Blob container name
|
|
245
|
+
|
|
246
|
+
**Best for:** Microsoft Azure ecosystem, enterprise applications
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
113
250
|
### FTP
|
|
114
251
|
|
|
115
252
|
Store files on FTP servers.
|
|
@@ -119,39 +256,198 @@ import { FTPStorageProvider, Storage } from '@devbro/neko-storage';
|
|
|
119
256
|
|
|
120
257
|
const provider = new FTPStorageProvider({
|
|
121
258
|
engine: 'ftp',
|
|
122
|
-
basePath: '/
|
|
259
|
+
basePath: '/uploads',
|
|
123
260
|
FTPStorageProviderConfig: {
|
|
124
261
|
host: 'ftp.example.com',
|
|
125
262
|
port: 21,
|
|
126
|
-
user:
|
|
127
|
-
password:
|
|
128
|
-
secure: false, // Set to true for FTPS
|
|
263
|
+
user: process.env.FTP_USER,
|
|
264
|
+
password: process.env.FTP_PASSWORD,
|
|
265
|
+
secure: false, // Set to true for FTPS (FTP over SSL/TLS)
|
|
129
266
|
},
|
|
130
267
|
});
|
|
131
268
|
const storage = new Storage(provider);
|
|
132
269
|
```
|
|
133
270
|
|
|
271
|
+
**Configuration:**
|
|
272
|
+
|
|
273
|
+
- `engine`: `'ftp'`
|
|
274
|
+
- `basePath`: Remote directory path
|
|
275
|
+
- `FTPStorageProviderConfig`: FTP connection settings
|
|
276
|
+
- `host`: FTP server hostname
|
|
277
|
+
- `port`: FTP port (default: 21)
|
|
278
|
+
- `user`: Username
|
|
279
|
+
- `password`: Password
|
|
280
|
+
- `secure`: Enable FTPS (default: false)
|
|
281
|
+
|
|
282
|
+
**Best for:** Legacy systems, shared hosting environments
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
134
286
|
### SFTP
|
|
135
287
|
|
|
136
288
|
Store files on SFTP servers via SSH.
|
|
137
289
|
|
|
138
290
|
```ts
|
|
139
291
|
import { SFTPStorageProvider, Storage } from '@devbro/neko-storage';
|
|
292
|
+
import fs from 'fs';
|
|
140
293
|
|
|
141
294
|
const provider = new SFTPStorageProvider({
|
|
142
295
|
engine: 'sftp',
|
|
143
|
-
basePath: '/
|
|
296
|
+
basePath: '/home/user/uploads',
|
|
144
297
|
SFTPStorageProviderConfig: {
|
|
145
298
|
host: 'sftp.example.com',
|
|
146
299
|
port: 22,
|
|
147
|
-
username:
|
|
148
|
-
|
|
149
|
-
|
|
300
|
+
username: process.env.SFTP_USER,
|
|
301
|
+
// Password authentication
|
|
302
|
+
password: process.env.SFTP_PASSWORD,
|
|
303
|
+
// Or use SSH key authentication
|
|
150
304
|
// privateKey: fs.readFileSync('/path/to/private-key'),
|
|
151
|
-
// passphrase: 'key-passphrase',
|
|
305
|
+
// passphrase: 'key-passphrase', // if key is encrypted
|
|
152
306
|
},
|
|
153
307
|
});
|
|
154
308
|
const storage = new Storage(provider);
|
|
155
309
|
```
|
|
156
310
|
|
|
157
|
-
|
|
311
|
+
**Configuration:**
|
|
312
|
+
|
|
313
|
+
- `engine`: `'sftp'`
|
|
314
|
+
- `basePath`: Remote directory path
|
|
315
|
+
- `SFTPStorageProviderConfig`: SSH/SFTP connection settings
|
|
316
|
+
- `host`: SFTP server hostname
|
|
317
|
+
- `port`: SSH port (default: 22)
|
|
318
|
+
- `username`: Username
|
|
319
|
+
- `password`: Password (or use `privateKey`)
|
|
320
|
+
- `privateKey`: SSH private key (Buffer or string)
|
|
321
|
+
- `passphrase`: Private key passphrase (if encrypted)
|
|
322
|
+
|
|
323
|
+
**Best for:** Secure file transfers, SSH-enabled servers
|
|
324
|
+
|
|
325
|
+
## Advanced Usage
|
|
326
|
+
|
|
327
|
+
### Environment-Based Provider Selection
|
|
328
|
+
|
|
329
|
+
Switch storage providers based on environment:
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
import { Storage, LocalStorageProvider, AWSS3StorageProvider } from '@devbro/neko-storage';
|
|
333
|
+
|
|
334
|
+
function createStorage(): Storage {
|
|
335
|
+
if (process.env.NODE_ENV === 'production') {
|
|
336
|
+
// Use S3 in production
|
|
337
|
+
return new Storage(
|
|
338
|
+
new AWSS3StorageProvider({
|
|
339
|
+
engine: 's3',
|
|
340
|
+
bucket: process.env.S3_BUCKET,
|
|
341
|
+
s3Config: {
|
|
342
|
+
region: process.env.AWS_REGION,
|
|
343
|
+
credentials: {
|
|
344
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
345
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
})
|
|
349
|
+
);
|
|
350
|
+
} else {
|
|
351
|
+
// Use local storage in development
|
|
352
|
+
return new Storage(
|
|
353
|
+
new LocalStorageProvider({
|
|
354
|
+
engine: 'local',
|
|
355
|
+
basePath: './storage',
|
|
356
|
+
})
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export const storage = createStorage();
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Handling Large Files with Streams
|
|
365
|
+
|
|
366
|
+
For large files, use streams to avoid memory issues:
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
import fs from 'fs';
|
|
370
|
+
|
|
371
|
+
// Upload large file
|
|
372
|
+
const uploadStream = fs.createReadStream('./large-video.mp4');
|
|
373
|
+
await storage.put('videos/upload.mp4', uploadStream);
|
|
374
|
+
|
|
375
|
+
// Download large file
|
|
376
|
+
const downloadStream = await storage.getStream('videos/upload.mp4');
|
|
377
|
+
const writeStream = fs.createWriteStream('./downloaded-video.mp4');
|
|
378
|
+
downloadStream.pipe(writeStream);
|
|
379
|
+
|
|
380
|
+
await new Promise((resolve, reject) => {
|
|
381
|
+
writeStream.on('finish', resolve);
|
|
382
|
+
writeStream.on('error', reject);
|
|
383
|
+
});
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Error Handling
|
|
387
|
+
|
|
388
|
+
```ts
|
|
389
|
+
try {
|
|
390
|
+
const content = await storage.getString('path/to/file.txt');
|
|
391
|
+
console.log(content);
|
|
392
|
+
} catch (error) {
|
|
393
|
+
if (error.code === 'ENOENT') {
|
|
394
|
+
console.error('File not found');
|
|
395
|
+
} else {
|
|
396
|
+
console.error('Error reading file:', error.message);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
## Best Practices
|
|
402
|
+
|
|
403
|
+
1. **Use Environment Variables** - Store credentials in environment variables, never in code
|
|
404
|
+
2. **Check Existence** - Use `exists()` before reading if the file might not exist
|
|
405
|
+
3. **Use Streams for Large Files** - Avoid loading large files into memory
|
|
406
|
+
4. **Handle Errors** - Always wrap storage operations in try-catch blocks
|
|
407
|
+
5. **Path Consistency** - Use forward slashes `/` in paths for all providers
|
|
408
|
+
6. **Provider Abstraction** - Design your app to work with any provider
|
|
409
|
+
|
|
410
|
+
## TypeScript Support
|
|
411
|
+
|
|
412
|
+
Full TypeScript definitions are included:
|
|
413
|
+
|
|
414
|
+
```ts
|
|
415
|
+
import { Storage, StorageProvider, FileMetadata } from '@devbro/neko-storage';
|
|
416
|
+
|
|
417
|
+
// Type-safe metadata
|
|
418
|
+
const metadata: FileMetadata = await storage.metadata('file.txt');
|
|
419
|
+
|
|
420
|
+
// Generic type support for JSON
|
|
421
|
+
interface UserConfig {
|
|
422
|
+
theme: string;
|
|
423
|
+
language: string;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const config = await storage.getJson<UserConfig>('config.json');
|
|
427
|
+
console.log(config.theme); // Type-safe!
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
## Contributing
|
|
431
|
+
|
|
432
|
+
We welcome contributions! If you need a storage provider that's not listed:
|
|
433
|
+
|
|
434
|
+
1. Open an issue to discuss the provider
|
|
435
|
+
2. Submit a pull request with implementation
|
|
436
|
+
3. Ensure tests are included
|
|
437
|
+
|
|
438
|
+
Popular providers we'd love to see:
|
|
439
|
+
|
|
440
|
+
- DigitalOcean Spaces
|
|
441
|
+
- Cloudflare R2
|
|
442
|
+
- MinIO
|
|
443
|
+
- WebDAV
|
|
444
|
+
|
|
445
|
+
## License
|
|
446
|
+
|
|
447
|
+
MIT
|
|
448
|
+
|
|
449
|
+
## Related Packages
|
|
450
|
+
|
|
451
|
+
- [@devbro/neko-cache](https://www.npmjs.com/package/@devbro/neko-cache) - Caching solution
|
|
452
|
+
- [@devbro/neko-config](https://www.npmjs.com/package/@devbro/neko-config) - Configuration management
|
|
453
|
+
- [@devbro/pashmak](https://www.npmjs.com/package/@devbro/pashmak) - Full-stack TypeScript framework
|