@gravito/nebula 3.0.1 → 4.1.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 CHANGED
@@ -1,8 +1,23 @@
1
1
  # @gravito/nebula
2
2
 
3
- > The Standard Storage Orbit for Galaxy Architecture.
3
+ > The Standard Storage Orbit for Galaxy Architecture. Lightweight, multi-disk, and pluggable.
4
4
 
5
- Provides an abstraction layer for file storage, with a built-in Local Disk provider.
5
+ [![npm version](https://img.shields.io/npm/v/@gravito/nebula.svg)](https://www.npmjs.com/package/@gravito/nebula)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
8
+ [![Bun](https://img.shields.io/badge/Bun-1.0+-black.svg)](https://bun.sh/)
9
+
10
+ **@gravito/nebula** provides a unified file storage abstraction layer for Gravito applications. Built on the **Orbit** pattern, it acts as a bridge between the core micro-kernel and various storage backends (Local, S3, Memory, etc.), supporting multi-disk configurations and high extensibility via hooks.
11
+
12
+ ## ✨ Features
13
+
14
+ - 🪐 **Orbit Integration** - Seamlessly plugs into the PlanetCore micro-kernel.
15
+ - 💽 **Multi-Disk Support** - Manage multiple storage backends (disks) within a single application.
16
+ - 🔌 **Pluggable Drivers** - Built-in support for `local`, `memory`, and `null` drivers, with easy `custom` driver implementation.
17
+ - 🪝 **Powerful Hooks** - Intercept and modify storage operations (upload, delete, etc.) using Gravito's async Hook system.
18
+ - 🏢 **Enterprise Ready** - Automatic service registration in the IoC container and context-aware middleware.
19
+ - 🧪 **Test Friendly** - Includes a `MemoryStore` for fast, zero-side-effect unit testing.
20
+ - 🚀 **Modern** - Built for **Bun** with native TypeScript support.
6
21
 
7
22
  ## 📦 Installation
8
23
 
@@ -10,7 +25,9 @@ Provides an abstraction layer for file storage, with a built-in Local Disk provi
10
25
  bun add @gravito/nebula
11
26
  ```
12
27
 
13
- ## 🚀 Usage
28
+ ## 🚀 Quick Start
29
+
30
+ ### 1. Initialize with PlanetCore
14
31
 
15
32
  ```typescript
16
33
  import { PlanetCore } from '@gravito/core';
@@ -18,30 +35,142 @@ import orbitStorage from '@gravito/nebula';
18
35
 
19
36
  const core = new PlanetCore();
20
37
 
21
- // Initialize Storage Orbit (Local)
38
+ // Quick install with options
22
39
  const storage = orbitStorage(core, {
23
- local: {
24
- root: './uploads',
25
- baseUrl: '/uploads'
26
- },
27
- exposeAs: 'storage' // Access via c.get('storage')
40
+ default: 'local',
41
+ disks: {
42
+ local: {
43
+ driver: 'local',
44
+ root: './uploads',
45
+ baseUrl: '/uploads'
46
+ }
47
+ }
28
48
  });
49
+ ```
29
50
 
30
- // Use in routes
51
+ ### 2. Use in Routes (Middleware)
52
+
53
+ Nebula automatically injects the `storage` manager into the request context:
54
+
55
+ ```typescript
31
56
  core.app.post('/upload', async (c) => {
32
57
  const body = await c.req.parseBody();
33
58
  const file = body['file'];
34
59
 
35
60
  if (file instanceof File) {
36
- await storage.put(file.name, file);
37
- return c.json({ url: storage.getUrl(file.name) });
61
+ const storage = c.get('storage'); // Resolved from context
62
+ await storage.put(`avatars/${file.name}`, file);
63
+
64
+ return c.json({
65
+ success: true,
66
+ url: storage.getUrl(`avatars/${file.name}`)
67
+ });
38
68
  }
69
+
39
70
  return c.text('No file uploaded', 400);
40
71
  });
41
72
  ```
42
73
 
74
+ ## 🔧 Multi-Disk Configuration
75
+
76
+ You can define multiple disks and switch between them at runtime:
77
+
78
+ ```typescript
79
+ const storage = orbitStorage(core, {
80
+ default: 'local',
81
+ disks: {
82
+ local: { driver: 'local', root: './uploads', baseUrl: '/uploads' },
83
+ temp: { driver: 'memory' },
84
+ s3: {
85
+ driver: 'custom',
86
+ store: new S3Store({ bucket: 'my-bucket' })
87
+ }
88
+ }
89
+ });
90
+
91
+ // Uses 'local' (default)
92
+ await storage.put('hello.txt', 'world');
93
+
94
+ // Uses 's3' disk explicitly
95
+ await storage.disk('s3').put('backup.zip', data);
96
+ ```
97
+
98
+ ## 📖 API Reference
99
+
100
+ ### `StorageManager`
101
+
102
+ The central hub accessed via `c.get('storage')` or returned by `orbitStorage()`.
103
+
104
+ - **`disk(name?: string)`**: Access a specific disk repository.
105
+ - **`put(key, data)`**: Store content (Default disk).
106
+ - **`get(key)`**: Retrieve content as a `Blob` (Default disk).
107
+ - **`delete(key)`**: Remove a file (Default disk).
108
+ - **`exists(key)`**: Check file existence (Default disk).
109
+ - **`copy(from, to)`**: Copy a file (Default disk).
110
+ - **`move(from, to)`**: Move/Rename a file (Default disk).
111
+ - **`getUrl(key)`**: Get public URL (Default disk).
112
+ - **`getSignedUrl(key, expires)`**: Get temporary signed URL (Default disk).
113
+ - **`getMetadata(key)`**: Get file metadata (size, mimeType, etc.).
114
+ - **`list(prefix?)`**: List files as an async iterable.
115
+
116
+ ### `StorageRepository`
117
+
118
+ Returned by `storage.disk('name')`. Implements the same storage methods as above but scoped to that specific disk.
119
+
43
120
  ## 🪝 Hooks
44
121
 
45
- - `storage:init` - Fired when initialized.
46
- - `storage:upload` - (Filter) Modify data before upload.
47
- - `storage:uploaded` - (Action) Triggered after successful upload.
122
+ Nebula triggers various hooks during its lifecycle:
123
+
124
+ | Hook | Type | Context | Description |
125
+ |------|------|---------|-------------|
126
+ | `storage:init` | Action | `{ manager }` | Fired on initialization |
127
+ | `storage:upload` | Filter | `data, { key }` | Modify data before it's saved |
128
+ | `storage:uploaded`| Action | `{ key }` | Fired after successful save |
129
+ | `storage:hit` | Action | `{ key }` | Fired when file is found/retrieved |
130
+ | `storage:miss` | Action | `{ key }` | Fired when file is not found |
131
+ | `storage:deleted` | Action | `{ key }` | Fired after file deletion |
132
+
133
+ ### Example: Image Resizing
134
+
135
+ ```typescript
136
+ core.hooks.addFilter('storage:upload', async (data, context) => {
137
+ if (context.key.match(/\.(jpg|png)$/)) {
138
+ return await myImageProcessor.resize(data, 800);
139
+ }
140
+ return data;
141
+ });
142
+ ```
143
+
144
+ ## 🔌 Custom Drivers
145
+
146
+ Implement the `StorageStore` interface to create your own backend:
147
+
148
+ ```typescript
149
+ import type { StorageStore, StorageMetadata } from '@gravito/nebula';
150
+
151
+ class MyCustomStore implements StorageStore {
152
+ async put(key: string, data: Blob | Buffer | string): Promise<void> { /* ... */ }
153
+ async get(key: string): Promise<Blob | null> { /* ... */ }
154
+ async delete(key: string): Promise<boolean> { /* ... */ }
155
+ async exists(key: string): Promise<boolean> { /* ... */ }
156
+ getUrl(key: string): string { /* ... */ }
157
+ // ... implement other methods
158
+ }
159
+ ```
160
+
161
+ ## 🔄 Migration from v3.x
162
+
163
+ Nebula v4.0 introduces the **Manager** pattern for multi-disk support.
164
+
165
+ - **Breaking**: The configuration structure has moved under a `disks` property.
166
+ - **Compatibility**: The old flat configuration format is still supported but will trigger a deprecation warning.
167
+ - **Types**: `StorageProvider` is now `StorageStore`, and `LocalStorageProvider` is now `LocalStore`.
168
+
169
+ ## 🤝 Contributing
170
+
171
+ Contributions, issues and feature requests are welcome!
172
+ Feel free to check the [issues page](https://github.com/gravito-framework/gravito/issues).
173
+
174
+ ## 📝 License
175
+
176
+ MIT © [Carl Lee](https://github.com/gravito-framework/gravito)
package/README.zh-TW.md CHANGED
@@ -1,37 +1,176 @@
1
1
  # @gravito/nebula
2
2
 
3
- > Gravito 的標準儲存 Orbit,提供檔案儲存抽象層。
3
+ > Galaxy 架構的標準儲存 Orbit。輕量、多磁碟支援且可插拔。
4
4
 
5
- ## 安裝
5
+ [![npm version](https://img.shields.io/npm/v/@gravito/nebula.svg)](https://www.npmjs.com/package/@gravito/nebula)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
8
+ [![Bun](https://img.shields.io/badge/Bun-1.0+-black.svg)](https://bun.sh/)
9
+
10
+ **@gravito/nebula** 為 Gravito 應用程式提供統一的檔案儲存抽象層。基於 **Orbit** 模式,它充當核心微核心與各種儲存後端(本地、S3、記憶體等)之間的橋樑,支援多磁碟配置並透過 Hook 系統提供高度可擴展性。
11
+
12
+ ## ✨ 特性
13
+
14
+ - 🪐 **Orbit 整合** - 無縫插入 PlanetCore 微核心。
15
+ - 💽 **多磁碟支援** - 在單個應用程式中管理多個儲存後端(磁碟)。
16
+ - 🔌 **可插拔驅動** - 內建支援 `local`、`memory` 與 `null` 驅動,並支援輕鬆實作 `custom` 驅動。
17
+ - 🪝 **強大的 Hooks** - 使用 Gravito 的異步 Hook 系統攔截並修改儲存操作(上傳、刪除等)。
18
+ - 🏢 **企業級設計** - 在 IoC 容器中自動註冊服務,並提供 Context 感知的中間件。
19
+ - 🧪 **測試友善** - 包含 `MemoryStore`,用於快速、無副作用的單元測試。
20
+ - 🚀 **現代化** - 專為 **Bun** 構建,原生支援 TypeScript。
21
+
22
+ ## 📦 安裝
6
23
 
7
24
  ```bash
8
25
  bun add @gravito/nebula
9
26
  ```
10
27
 
11
- ## 快速開始
28
+ ## 🚀 快速上手
29
+
30
+ ### 1. 初始化與 PlanetCore 整合
12
31
 
13
32
  ```typescript
14
- import { PlanetCore } from '@gravito/core'
15
- import orbitStorage from '@gravito/nebula'
33
+ import { PlanetCore } from '@gravito/core';
34
+ import orbitStorage from '@gravito/nebula';
16
35
 
17
- const core = new PlanetCore()
36
+ const core = new PlanetCore();
18
37
 
38
+ // 使用選項快速安裝
19
39
  const storage = orbitStorage(core, {
20
- local: {
21
- root: './uploads',
22
- baseUrl: '/uploads'
23
- },
24
- exposeAs: 'storage'
25
- })
40
+ default: 'local',
41
+ disks: {
42
+ local: {
43
+ driver: 'local',
44
+ root: './uploads',
45
+ baseUrl: '/uploads'
46
+ }
47
+ }
48
+ });
49
+ ```
26
50
 
27
- core.app.post('/upload', async (c) => {
28
- const body = await c.req.parseBody()
29
- const file = body['file']
51
+ ### 2. 在路由中使用(中間件)
52
+
53
+ Nebula 會自動將 `storage` 管理器注入請求上下文(Context):
30
54
 
55
+ ```typescript
56
+ core.app.post('/upload', async (c) => {
57
+ const body = await c.req.parseBody();
58
+ const file = body['file'];
59
+
31
60
  if (file instanceof File) {
32
- await storage.put(file.name, file)
33
- return c.json({ url: storage.getUrl(file.name) })
61
+ const storage = c.get('storage'); // 從上下文中解析
62
+ await storage.put(`avatars/${file.name}`, file);
63
+
64
+ return c.json({
65
+ success: true,
66
+ url: storage.getUrl(`avatars/${file.name}`)
67
+ });
68
+ }
69
+
70
+ return c.text('No file uploaded', 400);
71
+ });
72
+ ```
73
+
74
+ ## 🔧 多磁碟配置
75
+
76
+ 您可以定義多個磁碟並在運行時切換:
77
+
78
+ ```typescript
79
+ const storage = orbitStorage(core, {
80
+ default: 'local',
81
+ disks: {
82
+ local: { driver: 'local', root: './uploads', baseUrl: '/uploads' },
83
+ temp: { driver: 'memory' },
84
+ s3: {
85
+ driver: 'custom',
86
+ store: new S3Store({ bucket: 'my-bucket' })
87
+ }
34
88
  }
35
- return c.text('No file uploaded', 400)
36
- })
89
+ });
90
+
91
+ // 使用 'local' (預設)
92
+ await storage.put('hello.txt', 'world');
93
+
94
+ // 明確使用 's3' 磁碟
95
+ await storage.disk('s3').put('backup.zip', data);
37
96
  ```
97
+
98
+ ## 📖 API 參考
99
+
100
+ ### `StorageManager`
101
+
102
+ 透過 `c.get('storage')` 存取或由 `orbitStorage()` 回傳的中央管理器。
103
+
104
+ - **`disk(name?: string)`**: 存取特定的磁碟倉庫。
105
+ - **`put(key, data)`**: 儲存內容(預設磁碟)。
106
+ - **`get(key)`**: 取得內容為 `Blob`(預設磁碟)。
107
+ - **`delete(key)`**: 刪除檔案(預設磁碟)。
108
+ - **`exists(key)`**: 檢查檔案是否存在(預設磁碟)。
109
+ - **`copy(from, to)`**: 複製檔案(預設磁碟)。
110
+ - **`move(from, to)`**: 移動/重新命名檔案(預設磁碟)。
111
+ - **`getUrl(key)`**: 取得公開 URL(預設磁碟)。
112
+ - **`getSignedUrl(key, expires)`**: 取得臨時簽名 URL(預設磁碟)。
113
+ - **`getMetadata(key)`**: 取得檔案元數據(大小、MIME 類型等)。
114
+ - **`list(prefix?)`**: 列出檔案(異步叠代器)。
115
+
116
+ ### `StorageRepository`
117
+
118
+ 由 `storage.disk('name')` 回傳。實作與上述相同的儲存方法,但範圍限定於該特定磁碟。
119
+
120
+ ## 🪝 Hooks
121
+
122
+ Nebula 在其生命週期中觸發各種 Hook:
123
+
124
+ | Hook | 類型 | 上下文 | 描述 |
125
+ |------|------|---------|-------------|
126
+ | `storage:init` | Action | `{ manager }` | 初始化時觸發 |
127
+ | `storage:upload` | Filter | `data, { key }` | 在儲存前修改資料 |
128
+ | `storage:uploaded`| Action | `{ key }` | 儲存成功後觸發 |
129
+ | `storage:hit` | Action | `{ key }` | 找到/取得檔案時觸發 |
130
+ | `storage:miss` | Action | `{ key }` | 找不到檔案時觸發 |
131
+ | `storage:deleted` | Action | `{ key }` | 檔案刪除後觸發 |
132
+
133
+ ### 範例:圖片縮放
134
+
135
+ ```typescript
136
+ core.hooks.addFilter('storage:upload', async (data, context) => {
137
+ if (context.key.match(/\.(jpg|png)$/)) {
138
+ return await myImageProcessor.resize(data, 800);
139
+ }
140
+ return data;
141
+ });
142
+ ```
143
+
144
+ ## 🔌 自定義驅動
145
+
146
+ 實作 `StorageStore` 介面來建立您自己的後端:
147
+
148
+ ```typescript
149
+ import type { StorageStore, StorageMetadata } from '@gravito/nebula';
150
+
151
+ class MyCustomStore implements StorageStore {
152
+ async put(key: string, data: Blob | Buffer | string): Promise<void> { /* ... */ }
153
+ async get(key: string): Promise<Blob | null> { /* ... */ }
154
+ async delete(key: string): Promise<boolean> { /* ... */ }
155
+ async exists(key: string): Promise<boolean> { /* ... */ }
156
+ getUrl(key: string): string { /* ... */ }
157
+ // ... 實作其他方法
158
+ }
159
+ ```
160
+
161
+ ## 🔄 從 v3.x 遷移
162
+
163
+ Nebula v4.0 引入了 **Manager** 模式以支援多磁碟。
164
+
165
+ - **破壞性變更**:配置結構已移至 `disks` 屬性下。
166
+ - **相容性**:舊的扁平配置格式仍然支援,但會觸發棄用警告。
167
+ - **類型變更**:`StorageProvider` 現已更名為 `StorageStore`,`LocalStorageProvider` 更名為 `LocalStore`。
168
+
169
+ ## 🤝 貢獻
170
+
171
+ 歡迎提交貢獻、問題與功能請求!
172
+ 請隨時查看 [Issues 頁面](https://github.com/gravito-framework/gravito/issues)。
173
+
174
+ ## 📝 授權
175
+
176
+ MIT © [Carl Lee](https://github.com/gravito-framework/gravito)