@gravito/nebula 3.0.0 → 4.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/README.md +329 -21
- package/dist/index.cjs +364 -89
- package/dist/index.d.cts +240 -49
- package/dist/index.d.ts +240 -49
- package/dist/index.js +358 -92
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> The Standard Storage Orbit for Galaxy Architecture.
|
|
4
4
|
|
|
5
|
-
Provides
|
|
5
|
+
Provides a unified file storage abstraction layer with multi-disk support and pluggable backends.
|
|
6
6
|
|
|
7
7
|
## 📦 Installation
|
|
8
8
|
|
|
@@ -10,38 +10,346 @@ Provides an abstraction layer for file storage, with a built-in Local Disk provi
|
|
|
10
10
|
bun add @gravito/nebula
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
## 🚀
|
|
13
|
+
## 🚀 Quick Start
|
|
14
|
+
|
|
15
|
+
### Basic Usage
|
|
14
16
|
|
|
15
17
|
```typescript
|
|
16
|
-
import { PlanetCore } from '@gravito/core'
|
|
17
|
-
import orbitStorage from '@gravito/nebula'
|
|
18
|
+
import { PlanetCore } from '@gravito/core'
|
|
19
|
+
import orbitStorage from '@gravito/nebula'
|
|
18
20
|
|
|
19
|
-
const core = new PlanetCore()
|
|
21
|
+
const core = new PlanetCore()
|
|
20
22
|
|
|
21
|
-
// Initialize Storage Orbit (Local)
|
|
22
23
|
const storage = orbitStorage(core, {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
default: 'local',
|
|
25
|
+
disks: {
|
|
26
|
+
local: {
|
|
27
|
+
driver: 'local',
|
|
28
|
+
root: './uploads',
|
|
29
|
+
baseUrl: '/uploads'
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
})
|
|
29
33
|
|
|
30
34
|
// Use in routes
|
|
31
35
|
core.app.post('/upload', async (c) => {
|
|
32
|
-
const body = await c.req.parseBody()
|
|
33
|
-
const file = body['file']
|
|
36
|
+
const body = await c.req.parseBody()
|
|
37
|
+
const file = body['file']
|
|
34
38
|
|
|
35
39
|
if (file instanceof File) {
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
const storage = c.get('storage')
|
|
41
|
+
await storage.put(file.name, file)
|
|
42
|
+
return c.json({ url: storage.getUrl(file.name) })
|
|
38
43
|
}
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
|
|
45
|
+
return c.text('No file uploaded', 400)
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 🔧 Multi-Disk Configuration
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const storage = orbitStorage(core, {
|
|
55
|
+
default: 'local',
|
|
56
|
+
disks: {
|
|
57
|
+
// Local disk
|
|
58
|
+
local: {
|
|
59
|
+
driver: 'local',
|
|
60
|
+
root: './uploads',
|
|
61
|
+
baseUrl: '/uploads'
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// Memory disk (for testing)
|
|
65
|
+
temp: {
|
|
66
|
+
driver: 'memory'
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// Null disk (no-op)
|
|
70
|
+
null: {
|
|
71
|
+
driver: 'null'
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Custom disk (e.g., S3)
|
|
75
|
+
s3: {
|
|
76
|
+
driver: 'custom',
|
|
77
|
+
store: new S3Store({
|
|
78
|
+
bucket: 'my-bucket',
|
|
79
|
+
region: 'us-east-1'
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
// Use default disk
|
|
86
|
+
await storage.put('file.txt', 'content')
|
|
87
|
+
|
|
88
|
+
// Use specific disk
|
|
89
|
+
await storage.disk('s3').put('important.pdf', pdfData)
|
|
90
|
+
await storage.disk('temp').put('cache.json', jsonData)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 📖 API Reference
|
|
96
|
+
|
|
97
|
+
### StorageManager
|
|
98
|
+
|
|
99
|
+
The main storage manager returned by `orbitStorage()` or `c.get('storage')`.
|
|
100
|
+
|
|
101
|
+
#### Methods
|
|
102
|
+
|
|
103
|
+
##### `disk(name?: string): StorageRepository`
|
|
104
|
+
|
|
105
|
+
Get a specific disk repository. If `name` is not provided, returns the default disk.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const local = storage.disk('local')
|
|
109
|
+
const s3 = storage.disk('s3')
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
##### `put(key: string, data: Blob | Buffer | string): Promise<void>`
|
|
113
|
+
|
|
114
|
+
Store a file (using default disk).
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
await storage.put('file.txt', 'Hello World')
|
|
118
|
+
await storage.put('image.png', imageBlob)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
##### `get(key: string): Promise<Blob | null>`
|
|
122
|
+
|
|
123
|
+
Retrieve a file (using default disk).
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const data = await storage.get('file.txt')
|
|
127
|
+
if (data) {
|
|
128
|
+
console.log(await data.text())
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
##### `delete(key: string): Promise<boolean>`
|
|
133
|
+
|
|
134
|
+
Delete a file (using default disk). Returns `true` if deleted, `false` if file didn't exist.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const deleted = await storage.delete('old-file.txt')
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
##### `exists(key: string): Promise<boolean>` 🆕
|
|
141
|
+
|
|
142
|
+
Check if a file exists (using default disk).
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
if (await storage.exists('config.json')) {
|
|
146
|
+
// File exists
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
##### `copy(from: string, to: string): Promise<void>` 🆕
|
|
151
|
+
|
|
152
|
+
Copy a file (using default disk).
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
await storage.copy('original.txt', 'backup.txt')
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
##### `move(from: string, to: string): Promise<void>` 🆕
|
|
159
|
+
|
|
160
|
+
Move/rename a file (using default disk).
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
await storage.move('temp.txt', 'final.txt')
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
##### `getMetadata(key: string): Promise<StorageMetadata | null>` 🆕
|
|
167
|
+
|
|
168
|
+
Get file metadata (using default disk).
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const meta = await storage.getMetadata('file.pdf')
|
|
172
|
+
if (meta) {
|
|
173
|
+
console.log(meta.size, meta.mimeType, meta.lastModified)
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
##### `getUrl(key: string): string`
|
|
178
|
+
|
|
179
|
+
Get the public URL for a file (using default disk).
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const url = storage.getUrl('avatar.jpg')
|
|
183
|
+
// "/uploads/avatar.jpg"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
##### `getSignedUrl(key: string, expiresIn: number): Promise<string>` 🆕
|
|
187
|
+
|
|
188
|
+
Get a signed URL with expiration (if supported by the driver).
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Generate a URL that expires in 1 hour
|
|
192
|
+
const signedUrl = await storage.disk('s3').getSignedUrl('private.pdf', 3600)
|
|
41
193
|
```
|
|
42
194
|
|
|
195
|
+
##### `list(prefix?: string): AsyncIterable<StorageItem>` 🆕
|
|
196
|
+
|
|
197
|
+
List files in a directory (if supported by the driver).
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
for await (const item of storage.list('uploads/')) {
|
|
201
|
+
console.log(item.key, item.size, item.lastModified)
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
43
207
|
## 🪝 Hooks
|
|
44
208
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
209
|
+
Nebula integrates with Gravito's hook system for extensibility.
|
|
210
|
+
|
|
211
|
+
| Hook | Type | Parameters | Description |
|
|
212
|
+
|------|------|------------|-------------|
|
|
213
|
+
| `storage:init` | Action | `{ manager: StorageManager }` | Fired when storage is initialized |
|
|
214
|
+
| `storage:upload` | Filter | `data: Blob/Buffer/string, { key: string }` | Modify data before upload |
|
|
215
|
+
| `storage:uploaded` | Action | `{ key: string }` | Triggered after successful upload |
|
|
216
|
+
| `storage:hit` | Action | `{ key: string }` | File retrieved successfully |
|
|
217
|
+
| `storage:miss` | Action | `{ key: string }` | File not found |
|
|
218
|
+
| `storage:deleted` | Action | `{ key: string }` | File deleted |
|
|
219
|
+
| `storage:copied` 🆕 | Action | `{ from: string, to: string }` | File copied |
|
|
220
|
+
| `storage:moved` 🆕 | Action | `{ from: string, to: string }` | File moved |
|
|
221
|
+
|
|
222
|
+
### Example: Auto-resize Images on Upload
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
core.hooks.addFilter('storage:upload', async (data, context) => {
|
|
226
|
+
if (context.key.endsWith('.jpg') || context.key.endsWith('.png')) {
|
|
227
|
+
// Resize image using sharp, etc.
|
|
228
|
+
return await resizeImage(data, { width: 1920 })
|
|
229
|
+
}
|
|
230
|
+
return data
|
|
231
|
+
})
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Example: Log All Uploads
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
core.hooks.addAction('storage:uploaded', async (context) => {
|
|
238
|
+
core.logger.info(`File uploaded: ${context.key}`)
|
|
239
|
+
})
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 🔌 Custom Storage Drivers
|
|
245
|
+
|
|
246
|
+
Implement the `StorageStore` interface to create custom drivers.
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
import type { StorageStore, StorageMetadata } from '@gravito/nebula'
|
|
250
|
+
|
|
251
|
+
class S3Store implements StorageStore {
|
|
252
|
+
async put(key: string, data: Blob | Buffer | string): Promise<void> {
|
|
253
|
+
// Upload to S3
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async get(key: string): Promise<Blob | null> {
|
|
257
|
+
// Download from S3
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async delete(key: string): Promise<boolean> {
|
|
261
|
+
// Delete from S3
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async exists(key: string): Promise<boolean> {
|
|
265
|
+
// Check if exists in S3
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async copy(from: string, to: string): Promise<void> {
|
|
269
|
+
// S3 copy operation
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async move(from: string, to: string): Promise<void> {
|
|
273
|
+
await this.copy(from, to)
|
|
274
|
+
await this.delete(from)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async getMetadata(key: string): Promise<StorageMetadata | null> {
|
|
278
|
+
// Get S3 object metadata
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
getUrl(key: string): string {
|
|
282
|
+
return `https://my-bucket.s3.amazonaws.com/${key}`
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async getSignedUrl(key: string, expiresIn: number): Promise<string> {
|
|
286
|
+
// Generate pre-signed URL
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Use it
|
|
291
|
+
const storage = orbitStorage(core, {
|
|
292
|
+
disks: {
|
|
293
|
+
s3: {
|
|
294
|
+
driver: 'custom',
|
|
295
|
+
store: new S3Store({ bucket: 'my-bucket' })
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
})
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## 🔄 Migration from v3.x
|
|
304
|
+
|
|
305
|
+
### Configuration Changes
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
// v3.x (Old)
|
|
309
|
+
orbitStorage(core, {
|
|
310
|
+
local: { root: './uploads', baseUrl: '/uploads' },
|
|
311
|
+
exposeAs: 'storage'
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
// v4.0 (New)
|
|
315
|
+
orbitStorage(core, {
|
|
316
|
+
default: 'local',
|
|
317
|
+
disks: {
|
|
318
|
+
local: { driver: 'local', root: './uploads', baseUrl: '/uploads' }
|
|
319
|
+
},
|
|
320
|
+
exposeAs: 'storage'
|
|
321
|
+
})
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Note**: The old format is still supported for backward compatibility but is deprecated.
|
|
325
|
+
|
|
326
|
+
### Return Value Changes
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
// v3.x - Returns wrapped provider
|
|
330
|
+
const storage = orbitStorage(core, { ... })
|
|
331
|
+
await storage.put('file.txt', data)
|
|
332
|
+
|
|
333
|
+
// v4.0 - Returns StorageManager
|
|
334
|
+
const storage = orbitStorage(core, { ... })
|
|
335
|
+
await storage.put('file.txt', data) // Same API!
|
|
336
|
+
await storage.disk('s3').put('file.txt', data) // New!
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
The API is backward compatible, but v4.0 adds multi-disk support via `disk()`.
|
|
340
|
+
|
|
341
|
+
### Type Changes
|
|
342
|
+
|
|
343
|
+
| v3.x | v4.0 |
|
|
344
|
+
|------|------|
|
|
345
|
+
| `StorageProvider` | `StorageStore` |
|
|
346
|
+
| `LocalStorageProvider` | `LocalStore` |
|
|
347
|
+
| `OrbitStorageOptions` | `OrbitNebulaOptions` |
|
|
348
|
+
|
|
349
|
+
Old type names are still exported with `@deprecated` warnings.
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## 📝 License
|
|
354
|
+
|
|
355
|
+
MIT © [Carl Lee](https://github.com/gravito-framework/gravito)
|