@gravito/ion 3.0.1 → 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 CHANGED
@@ -1,15 +1,19 @@
1
- # 🛰️ Orbit Inertia
1
+ # 🛰️ Orbit Inertia (Ion)
2
2
 
3
- > Inertia.js adapter for Gravito. Build modern monoliths with React/Vue.
3
+ > Inertia.js v2 adapter for Gravito. Build modern monoliths with React/Vue/Svelte.
4
4
 
5
- **Orbit Inertia** allows you to build single-page apps using classic server-side routing and controllers. It acts as the glue between Gravito (Photon) and your frontend framework.
5
+ **Orbit Inertia** (@gravito/ion) is a high-performance adapter implementing the **Inertia.js v2 protocol** for Gravito. It allows you to build single-page apps using classic server-side routing and controllers, acting as the "glue" between Gravito (Photon) and your frontend framework, eliminating the need for a separate REST/GraphQL API.
6
6
 
7
- ## ✨ Features
7
+ ## ✨ Key Features
8
8
 
9
- - **Server-Side Routing**: Use Gravito's elegant routing and controllers.
10
- - **Client-Side Rendering**: Build your UI with React, Vue, or Svelte.
11
- - **No API required**: Pass data directly from controllers to components as props.
12
- - **SEO Ready**: Compatible with SSR (Server-Side Rendering) patterns since we control the initial page load.
9
+ - **🚀 Modern Monolith Architecture**: Combine the productivity of server-side routing with the interactivity of SPA frameworks.
10
+ - **🛠️ Zero API Development**: Pass data directly from controllers to components as typed props—no more managing endpoints or manual serialization.
11
+ - **⚡ High-Performance Rendering**: Built-in multi-layer caching, version caching (60s TTL), and component metadata optimization.
12
+ - **🛡️ Native Type Safety**: Full TypeScript support with generics for props, ensuring end-to-end type safety from server to client.
13
+ - **🔗 Ecosystem Integration**: Seamlessly works with `OrbitPrism` for root templates and Gravito's session/auth modules.
14
+ - **🔍 SEO & SSR Friendly**: Designed for modern web requirements, supporting Server-Side Rendering patterns for optimal visibility.
15
+ - **🎨 Multi-Framework Support**: Official support for **React**, **Vue**, and **Svelte**.
16
+ - **✨ Inertia v2 Protocol**: Full support for deferred props, merge strategies, error bags, and CSRF protection.
13
17
 
14
18
  ## 📦 Installation
15
19
 
@@ -17,85 +21,172 @@
17
21
  bun add @gravito/ion
18
22
  ```
19
23
 
20
- ## 🚀 Usage
24
+ ## 🚀 Quick Start
21
25
 
22
26
  ### 1. Register the Orbit
23
27
 
24
- In your `bootstrap.ts`:
28
+ In your application bootstrap:
25
29
 
26
30
  ```typescript
27
31
  import { OrbitIon } from '@gravito/ion';
28
- import { OrbitPrism } from '@gravito/prism'; // Required for root template
32
+ import { OrbitPrism } from '@gravito/prism'; // Required for the base HTML template
29
33
 
30
34
  const config = defineConfig({
31
35
  orbits: [OrbitPrism, OrbitIon],
32
36
  });
33
37
  ```
34
38
 
35
- ### 2. Create the Root Template
39
+ ### 2. Configure the Root Template
36
40
 
37
- By default, Inertia looks for `src/views/app.html`. This is the "shell" of your application.
41
+ By default, Ion looks for `src/views/app.html`. Use the `{{{ page }}}` placeholder to inject the Inertia data:
38
42
 
39
43
  ```html
40
44
  <!DOCTYPE html>
41
45
  <html>
42
46
  <head>
43
47
  <meta charset="utf-8" />
44
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
45
- <!-- Include your compiled assets -->
46
- <script type="module" src="/static/build/assets/index.js"></script>
47
- <link rel="stylesheet" href="/static/build/assets/index.css">
48
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
49
+ <script type="module" src="/static/assets/app.js"></script>
50
+ <link rel="stylesheet" href="/static/assets/app.css">
48
51
  </head>
49
52
  <body>
50
- <!-- The data-page attribute is crucial for Inertia -->
51
53
  <div id="app" data-page='{{{ page }}}'></div>
52
54
  </body>
53
55
  </html>
54
56
  ```
55
57
 
56
- ### 3. Return Responses
58
+ ### 3. Return Responses from Controllers
57
59
 
58
- In your controllers, simply use `inertia.render()`:
60
+ Use the `InertiaService` provided in the context:
59
61
 
60
62
  ```typescript
61
63
  import { Context } from '@gravito/photon';
62
64
  import { InertiaService } from '@gravito/ion';
63
65
 
64
- export class HomeController {
66
+ export class DashboardController {
65
67
  index = async (c: Context) => {
66
68
  const inertia = c.get('inertia') as InertiaService;
67
69
 
68
- return inertia.render('Home', {
69
- user: 'Carl',
70
- stats: { visits: 100 }
70
+ return inertia.render('Dashboard/Index', {
71
+ user: c.get('user'),
72
+ stats: { activeOrders: 5 }
71
73
  });
72
74
  };
73
75
  }
74
76
  ```
75
77
 
76
- The `'Home'` string corresponds to your frontend component path (e.g., `src/client/pages/Home.tsx`).
78
+ ## 🔧 Inertia v2 Protocol Features
77
79
 
78
- ## 🔧 Client-Side Setup (React Example)
80
+ ### Deferred Props (Lazy Loading)
81
+ Skip initial render, load props separately post-render:
79
82
 
80
- You need to set up your client entry point (e.g., `src/client/app.tsx`):
83
+ ```typescript
84
+ inertia.render('Dashboard', {
85
+ user: { id: 1, name: 'Carl' }, // Initial
86
+ stats: InertiaService.defer(() => fetchStats(), 'heavy'), // Deferred
87
+ notifications: InertiaService.defer(() => fetchNotifications(), 'notifications')
88
+ });
89
+ ```
90
+
91
+ ### Merge Strategies (Partial Reloads)
92
+ Control how props merge during partial reloads:
93
+
94
+ ```typescript
95
+ inertia.render('Products/List', {
96
+ items: InertiaService.prepend([newProduct]), // Add to start
97
+ filters: InertiaService.deepMerge({ status: 'active' }), // Recursive merge
98
+ config: InertiaService.merge({ sortBy: 'name' }) // Shallow merge
99
+ });
100
+ ```
101
+
102
+ ### Error Bags (Form Validation)
103
+ Organize validation errors by category:
104
+
105
+ ```typescript
106
+ inertia.withErrors({
107
+ email: 'Email is required',
108
+ password: 'Must be 8+ characters'
109
+ }, 'login'); // Named bag
110
+
111
+ inertia.withErrors({
112
+ line_1: 'Invalid CSV format'
113
+ }, 'import');
114
+ ```
115
+
116
+ ### Smart Redirects
117
+ Automatic 409 response for Inertia requests, 302 for regular requests:
118
+
119
+ ```typescript
120
+ if (!user) {
121
+ return inertia.location('/login'); // Smart redirect
122
+ }
123
+ ```
124
+
125
+ ### History Control
126
+ ```typescript
127
+ inertia.encryptHistory(true); // Disable back button
128
+ inertia.clearHistory(); // Clear history after load
129
+ ```
81
130
 
82
- ```tsx
83
- import { createInertiaApp } from '@inertiajs/react';
84
- import { createRoot } from 'react-dom/client';
131
+ ### CSRF Protection
132
+ Automatic XSRF-TOKEN cookie generation (Axios-compatible):
85
133
 
86
- createInertiaApp({
87
- resolve: name => {
88
- const pages = import.meta.glob('./pages/**/*.tsx', { eager: true });
89
- return pages[`./pages/${name}.tsx`];
90
- },
91
- setup({ el, App, props }) {
92
- createRoot(el).render(<App {...props} />);
93
- },
134
+ ```typescript
135
+ const ion = new OrbitIon({
136
+ csrf: {
137
+ enabled: true,
138
+ cookieName: 'XSRF-TOKEN' // Axios reads this automatically
139
+ }
94
140
  });
95
141
  ```
96
142
 
97
- See `templates/inertia-react` for a complete working example with Vite.
143
+ ## 🔧 Advanced Features
144
+
145
+ ### Shared Props
146
+ Automatically share data with every Inertia response (e.g., auth user, flash messages):
147
+
148
+ ```typescript
149
+ inertia.share('auth', { user: 'Carl' });
150
+ ```
151
+
152
+ ### Partial Reloads
153
+ Ion supports Inertia's partial reloads with smart merge strategies, allowing the client to request only specific data to save bandwidth.
154
+
155
+ ### Method Chaining
156
+ All methods support fluent interface:
157
+
158
+ ```typescript
159
+ return await inertia
160
+ .encryptHistory()
161
+ .clearHistory()
162
+ .withErrors({ email: 'Invalid' })
163
+ .render('SecurePage', props);
164
+ ```
165
+
166
+ ### Manual Serialization Control
167
+ Customize how your data is converted to JSON for the client:
168
+
169
+ ```typescript
170
+ inertia.render('ProductDetail', {
171
+ product: product.toShortArray() // Explicit control
172
+ });
173
+ ```
174
+
175
+ ## 🛡️ Performance & Reliability
176
+
177
+ ### Benchmarks (Internal)
178
+ | Operation | Latency |
179
+ |-----------|---------|
180
+ | Response Generation | < 0.2ms |
181
+ | Template Injection | < 0.1ms |
182
+ | Props Serialization | Optimized LRU Caching |
183
+
184
+ ### Error Codes
185
+ Ion provides detailed error types via `InertiaErrorCodes`:
186
+ - `CONFIG_VIEW_SERVICE_MISSING`: Ensure `OrbitPrism` is loaded.
187
+ - `SERIALIZATION_FAILED`: Circular dependencies detected in props.
188
+ - `TEMPLATE_RENDER_FAILED`: The base HTML template could not be found or parsed.
98
189
 
99
190
  ## 📝 License
100
191
 
101
- MIT
192
+ MIT © Carl Lee
package/README.zh-TW.md CHANGED
@@ -1,36 +1,192 @@
1
- # Orbit Inertia
1
+ # 🛰️ Orbit Inertia (Ion)
2
2
 
3
- > Gravito 的 Inertia.js 轉接器,可用 React/Vue 建立現代化單體應用。
3
+ > Gravito 的 Inertia.js v2 轉接器,用於建立現代化單體應用 (Modern Monolith)。支援 ReactVue 與 Svelte。
4
4
 
5
- ## 安裝
5
+ **Orbit Inertia** (@gravito/ion) 是一個高效能的轉接器,實現 **Inertia.js v2 協議**,讓您能夠使用傳統的伺服器端路由與控制器來建立單頁應用程式 (SPA)。它扮演了 Gravito (Photon) 與前端框架之間的「膠水」,消除了開發獨立 REST 或 GraphQL API 的需求。
6
+
7
+ ## ✨ 核心特性
8
+
9
+ - **🚀 現代化單體架構**:結合伺服器端開發的高效率與 SPA 框架的高互動性。
10
+ - **🛠️ 零 API 開發**:直接將資料從控制器傳遞到組件作為具備型別的 Props,不再需要管理 API 端點或手動處理序列化。
11
+ - **⚡ 高效能渲染**:內建多層快取、版本快取 (60 秒 TTL) 與組件元資料最佳化,確保毫秒級的極低開銷。
12
+ - **🛡️ 原生型別安全**:完整的 TypeScript 支援,透過 Generics 確保從伺服器到前端的端到端型別安全。
13
+ - **🔗 生態系整合**:與 `OrbitPrism` (模板引擎) 及 Gravito 的 Session/Auth 模組完美協作。
14
+ - **🔍 SEO 與 SSR 友善**:專為現代 Web 需求設計,支援伺服器端渲染 (SSR) 模式以優化搜尋引擎可見度。
15
+ - **🎨 多框架支援**:官方支援 **React**、**Vue** 與 **Svelte**。
16
+ - **✨ Inertia v2 協議**:完整支援延遲 Props、合併策略、錯誤包與 CSRF 防護。
17
+
18
+ ## 📦 安裝
6
19
 
7
20
  ```bash
8
21
  bun add @gravito/ion
9
22
  ```
10
23
 
11
- ## 快速開始
24
+ ## 🚀 快速上手
25
+
26
+ ### 1. 註冊 Orbit
27
+
28
+ 在應用程式啟動程式碼中:
12
29
 
13
30
  ```typescript
14
- import { OrbitIon } from '@gravito/ion'
15
- import { OrbitPrism } from '@gravito/prism'
31
+ import { OrbitIon } from '@gravito/ion';
32
+ import { OrbitPrism } from '@gravito/prism'; // 渲染基礎 HTML 模板所需
16
33
 
17
34
  const config = defineConfig({
18
35
  orbits: [OrbitPrism, OrbitIon],
19
- })
36
+ });
20
37
  ```
21
38
 
39
+ ### 2. 設定基礎模板
40
+
41
+ 預設情況下,Ion 會尋找 `src/views/app.html`。使用 `{{{ page }}}` 佔位符來注入 Inertia 的資料:
42
+
43
+ ```html
44
+ <!DOCTYPE html>
45
+ <html>
46
+ <head>
47
+ <meta charset="utf-8" />
48
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
49
+ <script type="module" src="/static/assets/app.js"></script>
50
+ <link rel="stylesheet" href="/static/assets/app.css">
51
+ </head>
52
+ <body>
53
+ <div id="app" data-page='{{{ page }}}'></div>
54
+ </body>
55
+ </html>
56
+ ```
57
+
58
+ ### 3. 從控制器回傳回應
59
+
60
+ 使用 Context 中提供的 `InertiaService`:
61
+
22
62
  ```typescript
23
- import { Context } from '@gravito/photon'
24
- import { InertiaService } from '@gravito/ion'
63
+ import { Context } from '@gravito/photon';
64
+ import { InertiaService } from '@gravito/ion';
25
65
 
26
- export class HomeController {
66
+ export class DashboardController {
27
67
  index = async (c: Context) => {
28
- const inertia = c.get('inertia') as InertiaService
68
+ const inertia = c.get('inertia') as InertiaService;
69
+
70
+ return inertia.render('Dashboard/Index', {
71
+ user: c.get('user'),
72
+ stats: { activeOrders: 5 }
73
+ });
74
+ };
75
+ }
76
+ ```
29
77
 
30
- return inertia.render('Home', {
31
- user: 'Carl',
32
- stats: { visits: 100 }
33
- })
34
- }
78
+ ## 🔧 Inertia v2 協議功能
79
+
80
+ ### 延遲 Props (Deferred Props)
81
+ 跳過初始渲染,在後續操作中單獨載入 Props:
82
+
83
+ ```typescript
84
+ inertia.render('Dashboard', {
85
+ user: { id: 1, name: 'Carl' }, // 初始載入
86
+ stats: InertiaService.defer(() => fetchStats(), 'heavy'), // 延遲載入
87
+ notifications: InertiaService.defer(() => fetchNotifications(), 'notifications')
88
+ });
89
+ ```
90
+
91
+ ### 合併策略 (Merge Strategies)
92
+ 控制局部重新載入時 Props 如何合併:
93
+
94
+ ```typescript
95
+ inertia.render('Products/List', {
96
+ items: InertiaService.prepend([newProduct]), // 加到開頭
97
+ filters: InertiaService.deepMerge({ status: 'active' }), // 遞迴合併
98
+ config: InertiaService.merge({ sortBy: 'name' }) // 淺合併
99
+ });
100
+ ```
101
+
102
+ ### 錯誤包 (Error Bags)
103
+ 按類別組織表單驗證錯誤:
104
+
105
+ ```typescript
106
+ inertia.withErrors({
107
+ email: '電郵為必填',
108
+ password: '必須為 8 個字元以上'
109
+ }, 'login'); // 命名包
110
+
111
+ inertia.withErrors({
112
+ line_1: '無效的 CSV 格式'
113
+ }, 'import');
114
+ ```
115
+
116
+ ### 智慧重定向 (Smart Redirects)
117
+ 自動為 Inertia 請求回傳 409,普通請求回傳 302:
118
+
119
+ ```typescript
120
+ if (!user) {
121
+ return inertia.location('/login'); // 智慧重定向
35
122
  }
36
123
  ```
124
+
125
+ ### 瀏覽歷史控制 (History Control)
126
+ ```typescript
127
+ inertia.encryptHistory(true); // 停用返回按鈕
128
+ inertia.clearHistory(); // 載入後清除歷史
129
+ ```
130
+
131
+ ### CSRF 防護
132
+ 自動產生 XSRF-TOKEN Cookie (與 Axios 相容):
133
+
134
+ ```typescript
135
+ const ion = new OrbitIon({
136
+ csrf: {
137
+ enabled: true,
138
+ cookieName: 'XSRF-TOKEN' // Axios 會自動讀取
139
+ }
140
+ });
141
+ ```
142
+
143
+ ## 🔧 進階功能
144
+
145
+ ### 共享 Props (Shared Props)
146
+ 自動在每個 Inertia 回應中共享資料 (例如:當前用戶、快閃訊息):
147
+
148
+ ```typescript
149
+ inertia.share('auth', { user: 'Carl' });
150
+ ```
151
+
152
+ ### 局部重新載入 (Partial Reloads)
153
+ Ion 支援 Inertia 的局部重載機制,搭配智慧合併策略,允許客戶端僅請求特定資料以節省頻寬。
154
+
155
+ ### 方法鏈式調用 (Method Chaining)
156
+ 所有方法都支援流暢介面:
157
+
158
+ ```typescript
159
+ return await inertia
160
+ .encryptHistory()
161
+ .clearHistory()
162
+ .withErrors({ email: '無效' })
163
+ .render('SecurePage', props);
164
+ ```
165
+
166
+ ### 手動序列化控制
167
+ 自定義資料如何轉換為 JSON 傳遞給客戶端:
168
+
169
+ ```typescript
170
+ inertia.render('ProductDetail', {
171
+ product: product.toShortArray() // 顯式控制
172
+ });
173
+ ```
174
+
175
+ ## 🛡️ 效能與可靠性
176
+
177
+ ### 基準測試 (內部測試)
178
+ | 操作 | 延遲 (Latency) |
179
+ |-----------|---------|
180
+ | 回應生成 | < 0.2ms |
181
+ | 模板注入 | < 0.1ms |
182
+ | Props 序列化 | 優化後的 LRU 快取 |
183
+
184
+ ### 錯誤代碼
185
+ Ion 透過 `InertiaErrorCodes` 提供詳細的錯誤類型:
186
+ - `CONFIG_VIEW_SERVICE_MISSING`:請確保已載入 `OrbitPrism`。
187
+ - `SERIALIZATION_FAILED`:在 Props 中偵測到循環依賴。
188
+ - `TEMPLATE_RENDER_FAILED`:找不到或無法解析基礎 HTML 模板。
189
+
190
+ ## 📝 授權
191
+
192
+ MIT © Carl Lee