@cruxjs/app 0.0.4 β 0.0.6
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 +488 -312
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
10
|
<div align="center">
|
|
11
|
-
<img src="https://img.shields.io/badge/v-0.0.
|
|
11
|
+
<img src="https://img.shields.io/badge/v-0.0.6-black"/>
|
|
12
12
|
<img src="https://img.shields.io/badge/π₯-@cruxjs-black"/>
|
|
13
13
|
<br>
|
|
14
14
|
<img src="https://img.shields.io/github/issues/cruxjs-org/app?style=flat" alt="Github Repo Issues" />
|
|
@@ -24,39 +24,65 @@
|
|
|
24
24
|
|
|
25
25
|
- ## Quick Start π₯
|
|
26
26
|
|
|
27
|
-
> **
|
|
27
|
+
> **_CruxJS is a full-stack framework orchestrator built on [@minejs](https://github.com/minejs-org) that empowers developers to build modern web applications with **zero configuration**. It orchestrates without dictatingβproviding a plugin-based architecture where you control the flow._**
|
|
28
28
|
|
|
29
29
|
- ### Setup
|
|
30
30
|
|
|
31
|
-
> install [`hmm`](https://github.com/minejs-org/hmm)
|
|
31
|
+
> First, install [`hmm`](https://github.com/minejs-org/hmm) β the package manager for the CruxJS ecosystem.
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
+
# Install CruxJS App
|
|
34
35
|
hmm i @cruxjs/app
|
|
35
|
-
```
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
# Or install with plugins
|
|
38
|
+
hmm i @cruxjs/app @cruxplug/spa
|
|
39
|
+
```
|
|
38
40
|
|
|
39
|
-
- ### Usage
|
|
41
|
+
- ### Basic Usage
|
|
40
42
|
|
|
41
43
|
```typescript
|
|
42
44
|
import { createApp, type AppConfig } from '@cruxjs/app';
|
|
43
45
|
|
|
44
|
-
// Define your application
|
|
45
46
|
const config: AppConfig = {
|
|
46
47
|
debug: true,
|
|
48
|
+
|
|
49
|
+
// Server configuration
|
|
47
50
|
server: {
|
|
48
51
|
port: 3000,
|
|
49
52
|
host: 'localhost'
|
|
50
53
|
},
|
|
54
|
+
|
|
55
|
+
// Client build configuration (bundles with Bun)
|
|
51
56
|
client: {
|
|
52
57
|
entry: './src/client/browser.tsx',
|
|
53
|
-
output: './dist/
|
|
54
|
-
|
|
58
|
+
output: './src/shared/static/dist/js',
|
|
59
|
+
minify: true,
|
|
60
|
+
sourcemap: false
|
|
55
61
|
},
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
|
|
63
|
+
// UI library (installs from npm and exports min.css)
|
|
64
|
+
ui: {
|
|
65
|
+
package: '@mineui/core',
|
|
66
|
+
output: './src/shared/static/dist/css'
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// Custom styles (compiles SCSS/CSS)
|
|
70
|
+
style: {
|
|
71
|
+
entry: './src/client/ui/style/index.scss',
|
|
72
|
+
output: './src/shared/static/dist/css/extra.css',
|
|
73
|
+
minify: true,
|
|
74
|
+
sourcemap: false
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// Static files serving
|
|
78
|
+
static: {
|
|
79
|
+
path: '/static',
|
|
80
|
+
directory: './src/shared/static',
|
|
81
|
+
maxAge: 3600
|
|
58
82
|
},
|
|
59
|
-
|
|
83
|
+
|
|
84
|
+
// Plugins (SPA, API routes, databases, etc.)
|
|
85
|
+
plugins: [/* your plugins */]
|
|
60
86
|
};
|
|
61
87
|
|
|
62
88
|
// Create and run the app
|
|
@@ -66,189 +92,493 @@
|
|
|
66
92
|
|
|
67
93
|
<br>
|
|
68
94
|
|
|
69
|
-
|
|
95
|
+
- ## Complete Example π
|
|
70
96
|
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
import type { RouteDefinition } from '@cruxjs/app';
|
|
74
|
-
|
|
75
|
-
export const routes: RouteDefinition[] = [
|
|
76
|
-
{
|
|
77
|
-
method: 'GET',
|
|
78
|
-
path: '/api/users',
|
|
79
|
-
handler: async (ctx) => {
|
|
80
|
-
return ctx.json({ users: [] });
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
method: 'POST',
|
|
85
|
-
path: '/api/users',
|
|
86
|
-
handler: async (ctx) => {
|
|
87
|
-
const data = await ctx.request.json();
|
|
88
|
-
// Your logic here
|
|
89
|
-
return ctx.json({ created: true }, { status: 201 });
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
];
|
|
93
|
-
```
|
|
97
|
+
### Directory Structure
|
|
94
98
|
|
|
95
|
-
|
|
99
|
+
```
|
|
100
|
+
src/
|
|
101
|
+
βββ index.ts # Entry point (app config + setup)
|
|
102
|
+
βββ client/
|
|
103
|
+
β βββ browser.tsx # Client entry
|
|
104
|
+
β βββ ui/
|
|
105
|
+
β βββ App.tsx
|
|
106
|
+
β βββ pages/ # Page components
|
|
107
|
+
β βββ style/
|
|
108
|
+
β βββ index.scss # Custom styles
|
|
109
|
+
βββ server/
|
|
110
|
+
β βββ api/
|
|
111
|
+
β β βββ index.ts # API routes
|
|
112
|
+
β βββ schema.ts # Database schemas
|
|
113
|
+
βββ shared/
|
|
114
|
+
βββ static/ # Static assets (generated files here)
|
|
115
|
+
```
|
|
96
116
|
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
import { mount } from '@minejsx/runtime';
|
|
100
|
-
import { App } from './ui/App';
|
|
101
|
-
import { ClientManager } from '@cruxjs/client';
|
|
102
|
-
import { HomePage } from './ui/pages/HomePage';
|
|
103
|
-
import { NotFoundPage } from './ui/pages/NotFoundPage';
|
|
104
|
-
|
|
105
|
-
// ClientManager is a pure management layer
|
|
106
|
-
// You provide route components and error pages
|
|
107
|
-
const clientManager = new ClientManager({
|
|
108
|
-
debug: true,
|
|
109
|
-
routes: {
|
|
110
|
-
'/': HomePage,
|
|
111
|
-
'/about': AboutPage,
|
|
112
|
-
},
|
|
113
|
-
notFoundComponent: NotFoundPage,
|
|
114
|
-
errorComponent: ErrorPage
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Mount and run
|
|
118
|
-
(async () => {
|
|
119
|
-
await clientManager.boot();
|
|
120
|
-
mount(<App />, '#app');
|
|
121
|
-
await clientManager.ready('#app-main');
|
|
122
|
-
})();
|
|
123
|
-
```
|
|
117
|
+
### 1. Server Entry Point
|
|
124
118
|
|
|
125
|
-
|
|
119
|
+
**src/index.ts**
|
|
120
|
+
```typescript
|
|
121
|
+
import { createApp, type AppConfig } from '@cruxjs/app';
|
|
122
|
+
import { serverSPA } from '@cruxplug/spa';
|
|
123
|
+
|
|
124
|
+
const spaPlugin = serverSPA({
|
|
125
|
+
baseUrl: 'http://localhost:3000',
|
|
126
|
+
clientEntry: './src/client/browser.tsx',
|
|
127
|
+
clientScriptPath: ['/static/dist/js/browser.js'],
|
|
128
|
+
clientStylePath: ['/static/dist/css/min.css', '/static/dist/css/extra.css'],
|
|
129
|
+
|
|
130
|
+
// SEO & E-E-A-T configuration
|
|
131
|
+
author: 'Your Team',
|
|
132
|
+
authorUrl: 'https://example.com/about',
|
|
133
|
+
defaultDescription: 'Your app description',
|
|
134
|
+
defaultKeywords: ['key', 'words'],
|
|
135
|
+
|
|
136
|
+
enableAutoNotFound: true,
|
|
137
|
+
pages: [
|
|
138
|
+
{
|
|
139
|
+
title: 'Home',
|
|
140
|
+
path: '/',
|
|
141
|
+
description: 'Welcome',
|
|
142
|
+
keywords: ['home']
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
errorPages: [
|
|
146
|
+
{
|
|
147
|
+
statusCode: 404,
|
|
148
|
+
title: '404 - Not Found',
|
|
149
|
+
path: '/404'
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const config: AppConfig = {
|
|
155
|
+
debug: true,
|
|
156
|
+
|
|
157
|
+
server: {
|
|
158
|
+
port: 3000,
|
|
159
|
+
host: 'localhost',
|
|
160
|
+
logging: { level: 'info', pretty: true }
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
// Build configuration
|
|
164
|
+
client: {
|
|
165
|
+
entry: './src/client/browser.tsx',
|
|
166
|
+
output: './src/shared/static/dist/js',
|
|
167
|
+
minify: true
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
ui: {
|
|
171
|
+
package: '@mineui/core',
|
|
172
|
+
output: './src/shared/static/dist/css'
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
style: {
|
|
176
|
+
entry: './src/client/ui/style/index.scss',
|
|
177
|
+
output: './src/shared/static/dist/css/extra.css',
|
|
178
|
+
minify: true
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
static: {
|
|
182
|
+
path: '/static',
|
|
183
|
+
directory: './src/shared/static',
|
|
184
|
+
maxAge: 3600
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
plugins: [spaPlugin]
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const app = createApp(config);
|
|
191
|
+
await app.start();
|
|
192
|
+
```
|
|
126
193
|
|
|
127
|
-
|
|
128
|
-
import { serverSPA } from '@cruxplug/spa';
|
|
129
|
-
|
|
130
|
-
const spaPlugin = serverSPA({
|
|
131
|
-
baseUrl: 'http://localhost:3000',
|
|
132
|
-
clientEntry: './src/client/browser.tsx',
|
|
133
|
-
clientScriptPath: '/static/dist/js/browser.js',
|
|
134
|
-
pages: [
|
|
135
|
-
{
|
|
136
|
-
title: 'Home',
|
|
137
|
-
path: '/',
|
|
138
|
-
description: 'Welcome to my app',
|
|
139
|
-
keywords: ['app', 'home']
|
|
140
|
-
}
|
|
141
|
-
],
|
|
142
|
-
errorPages: [
|
|
143
|
-
{
|
|
144
|
-
statusCode: 404,
|
|
145
|
-
title: '404 - Not Found',
|
|
146
|
-
path: '/404'
|
|
147
|
-
}
|
|
148
|
-
]
|
|
149
|
-
});
|
|
194
|
+
### 2. Client Entry Point
|
|
150
195
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
196
|
+
**src/client/browser.tsx**
|
|
197
|
+
```typescript
|
|
198
|
+
import { mount } from '@minejs/jsx';
|
|
199
|
+
import { App } from './ui/App';
|
|
200
|
+
import { ClientManager } from '@cruxjs/client';
|
|
201
|
+
import { HomePage } from './ui/pages/HomePage';
|
|
202
|
+
import { NotFoundPage } from './ui/pages/NotFoundPage';
|
|
203
|
+
|
|
204
|
+
const clientManager = new ClientManager({
|
|
205
|
+
debug: true,
|
|
206
|
+
routes: {
|
|
207
|
+
'/': HomePage,
|
|
208
|
+
'/about': AboutPage,
|
|
209
|
+
// ...more routes
|
|
210
|
+
},
|
|
211
|
+
notFoundComponent: NotFoundPage,
|
|
212
|
+
errorComponent: ErrorPage
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
(async () => {
|
|
216
|
+
await clientManager.boot();
|
|
217
|
+
mount(<App />, '#app');
|
|
218
|
+
await clientManager.ready('#app-main');
|
|
219
|
+
})();
|
|
220
|
+
```
|
|
156
221
|
|
|
157
|
-
|
|
222
|
+
### 3. Define API Routes
|
|
158
223
|
|
|
159
|
-
|
|
224
|
+
**src/server/api/index.ts**
|
|
225
|
+
```typescript
|
|
226
|
+
import type { RouteDefinition } from '@cruxjs/app';
|
|
227
|
+
|
|
228
|
+
export const routes: RouteDefinition[] = [
|
|
229
|
+
{
|
|
230
|
+
method: 'GET',
|
|
231
|
+
path: '/api/users',
|
|
232
|
+
handler: async (ctx) => {
|
|
233
|
+
return ctx.json({ users: [] });
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
method: 'POST',
|
|
238
|
+
path: '/api/users',
|
|
239
|
+
handler: async (ctx) => {
|
|
240
|
+
const data = await ctx.request.json();
|
|
241
|
+
return ctx.json({ created: true }, { status: 201 });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
];
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 4. Add Custom Styles
|
|
160
248
|
|
|
161
|
-
|
|
249
|
+
**src/client/ui/style/index.scss**
|
|
250
|
+
```scss
|
|
251
|
+
// Import UI library variables (if available)
|
|
252
|
+
@import '@mineui/core/scss/variables';
|
|
253
|
+
|
|
254
|
+
// Custom styles
|
|
255
|
+
body {
|
|
256
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
257
|
+
margin: 0;
|
|
258
|
+
padding: 0;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.app {
|
|
262
|
+
display: flex;
|
|
263
|
+
flex-direction: column;
|
|
264
|
+
min-height: 100vh;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// More custom styles...
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
- ## How It Works ποΈ
|
|
271
|
+
|
|
272
|
+
CruxJS orchestrates your full-stack application through **4 lifecycle phases**:
|
|
162
273
|
|
|
163
274
|
- #### **Phase 0: REGISTER**
|
|
164
|
-
|
|
275
|
+
Plugins register and declare their capabilities (routes, schemas, middleware).
|
|
165
276
|
|
|
166
277
|
- #### **Phase 1: AWAKE**
|
|
167
|
-
|
|
278
|
+
- Client is built using Bun's bundler
|
|
279
|
+
- UI library (e.g., @mineui/core) is installed and CSS is extracted
|
|
280
|
+
- Custom SCSS/CSS is compiled using Dart Sass
|
|
281
|
+
- Databases are initialized with plugin schemas
|
|
282
|
+
- i18n is set up
|
|
168
283
|
|
|
169
|
-
**
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
});
|
|
284
|
+
**Output structure:**
|
|
285
|
+
```
|
|
286
|
+
src/shared/static/dist/
|
|
287
|
+
βββ js/
|
|
288
|
+
β βββ browser.js (bundled client)
|
|
289
|
+
βββ css/
|
|
290
|
+
βββ min.css (UI library CSS)
|
|
291
|
+
βββ extra.css (compiled custom styles)
|
|
178
292
|
```
|
|
179
293
|
|
|
180
294
|
- #### **Phase 2: START**
|
|
181
|
-
|
|
295
|
+
- Server is created with @minejs/server
|
|
296
|
+
- Routes are merged (user routes + plugin routes)
|
|
297
|
+
- Middleware stack is built
|
|
298
|
+
- Static files handler is configured
|
|
182
299
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
300
|
+
- #### **Phase 3: READY**
|
|
301
|
+
- Server is listening and ready to handle requests
|
|
302
|
+
- Plugins are fully operational
|
|
303
|
+
|
|
304
|
+
<br>
|
|
305
|
+
|
|
306
|
+
- ## Building Your App π οΈ
|
|
307
|
+
|
|
308
|
+
### Client Build (JavaScript)
|
|
309
|
+
```typescript
|
|
310
|
+
client: {
|
|
311
|
+
entry: './src/client/browser.tsx',
|
|
312
|
+
output: './src/shared/static/dist/js',
|
|
313
|
+
target: 'browser', // or 'bun'
|
|
314
|
+
minify: true, // Minify in production
|
|
315
|
+
sourcemap: false, // Source maps in development
|
|
316
|
+
external: [] // Dependencies to exclude
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
Uses Bun's bundler to create optimized JavaScript bundles.
|
|
320
|
+
|
|
321
|
+
### UI Library Build (CSS from npm)
|
|
322
|
+
```typescript
|
|
323
|
+
ui: {
|
|
324
|
+
package: '@mineui/core', // Any npm package
|
|
325
|
+
output: './src/shared/static/dist/css'
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
- Installs the UI package from npm
|
|
329
|
+
- Extracts `dist/mineui.css` and copies it as `min.css`
|
|
330
|
+
- Perfect for pre-built component libraries
|
|
331
|
+
|
|
332
|
+
### Styles Build (SCSS to CSS)
|
|
333
|
+
```typescript
|
|
334
|
+
style: {
|
|
335
|
+
entry: './src/client/ui/style/index.scss',
|
|
336
|
+
output: './src/shared/static/dist/css/extra.css',
|
|
337
|
+
minify: true,
|
|
338
|
+
sourcemap: false
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
- Compiles SCSS/CSS using Dart Sass (pure CSS output)
|
|
342
|
+
- Minifies output in production
|
|
343
|
+
- No JavaScript wrappersβjust clean CSS files
|
|
344
|
+
|
|
345
|
+
### All Together
|
|
346
|
+
The SPA plugin links everything:
|
|
347
|
+
```typescript
|
|
348
|
+
import { serverSPA } from '@cruxplug/spa';
|
|
349
|
+
|
|
350
|
+
const spaPlugin = serverSPA({
|
|
351
|
+
baseUrl: 'http://localhost:3000',
|
|
352
|
+
clientEntry: './src/client/browser.tsx',
|
|
353
|
+
clientScriptPath: ['/static/dist/js/browser.js'],
|
|
354
|
+
clientStylePath: [
|
|
355
|
+
'/static/dist/css/min.css', // UI library
|
|
356
|
+
'/static/dist/css/extra.css' // Custom styles
|
|
357
|
+
],
|
|
358
|
+
// ...SEO/E-E-A-T configuration
|
|
359
|
+
});
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
<br>
|
|
363
|
+
|
|
364
|
+
- ## Lifecycle Hooks π
|
|
365
|
+
|
|
366
|
+
Control your application at each phase:
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
createApp(config, {
|
|
370
|
+
onAwake: async (ctx) => {
|
|
371
|
+
// Databases ready, client built, plugins initialized
|
|
372
|
+
// Perfect for: seed data, cache warming
|
|
373
|
+
const db = ctx.databases.get('default');
|
|
374
|
+
await db.query('INSERT INTO ...');
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
onStart: async (ctx) => {
|
|
378
|
+
// Server created, routes merged, middleware ready
|
|
379
|
+
// Perfect for: logging setup, route inspection
|
|
380
|
+
console.log(`${ctx.routes.length} routes registered`);
|
|
381
|
+
},
|
|
382
|
+
|
|
383
|
+
onReady: async (ctx) => {
|
|
384
|
+
// Server listening and ready
|
|
385
|
+
// Perfect for: external service notifications
|
|
386
|
+
const url = `http://${ctx.config.server.host}:${ctx.config.server.port}`;
|
|
387
|
+
console.log(`π Live at ${url}`);
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
onFinish: async (ctx) => {
|
|
391
|
+
// Graceful shutdown
|
|
392
|
+
// Perfect for: cleanup, connection closing
|
|
393
|
+
},
|
|
394
|
+
|
|
395
|
+
onError: async (ctx, phase, error) => {
|
|
396
|
+
// Error handling across all phases
|
|
397
|
+
console.error(`Error in ${phase}:`, error);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
<br>
|
|
403
|
+
|
|
404
|
+
- ## Plugins π
|
|
405
|
+
|
|
406
|
+
Extend CruxJS with plugins. They integrate seamlessly into the lifecycle:
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
import type { CruxPlugin } from '@cruxjs/base';
|
|
410
|
+
|
|
411
|
+
const myPlugin: CruxPlugin = {
|
|
412
|
+
name: 'my-plugin',
|
|
413
|
+
version: '1.0.0',
|
|
414
|
+
|
|
415
|
+
// Provide routes
|
|
416
|
+
routes: [
|
|
417
|
+
{
|
|
418
|
+
method: 'GET',
|
|
419
|
+
path: '/plugin/status',
|
|
420
|
+
handler: async (ctx) => ctx.json({ status: 'ok' })
|
|
188
421
|
}
|
|
189
|
-
|
|
422
|
+
],
|
|
423
|
+
|
|
424
|
+
// Define database schemas
|
|
425
|
+
schemas: [
|
|
426
|
+
{
|
|
427
|
+
name: 'my_table',
|
|
428
|
+
columns: [
|
|
429
|
+
{ name: 'id', type: 'INTEGER PRIMARY KEY' },
|
|
430
|
+
{ name: 'data', type: 'TEXT' }
|
|
431
|
+
]
|
|
432
|
+
}
|
|
433
|
+
],
|
|
434
|
+
|
|
435
|
+
// Register middleware
|
|
436
|
+
middleware: {
|
|
437
|
+
'my-middleware': async (ctx, next) => {
|
|
438
|
+
console.log('Before request');
|
|
439
|
+
const result = await next();
|
|
440
|
+
console.log('After request');
|
|
441
|
+
return result;
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
|
|
445
|
+
// Hook into lifecycle
|
|
446
|
+
hooks: {
|
|
447
|
+
onAwake: async (ctx) => console.log('Plugin awake'),
|
|
448
|
+
onStart: async (ctx) => console.log('Plugin start'),
|
|
449
|
+
onReady: async (ctx) => console.log('Plugin ready'),
|
|
450
|
+
onShutdown: async (ctx) => console.log('Plugin shutdown')
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
// Use in config
|
|
455
|
+
const config: AppConfig = {
|
|
456
|
+
plugins: [myPlugin]
|
|
457
|
+
};
|
|
458
|
+
```
|
|
190
459
|
|
|
191
|
-
|
|
192
|
-
|
|
460
|
+
**Popular Plugins:**
|
|
461
|
+
- [@cruxplug/spa](https://github.com/cruxplug-org/spa) β Single Page Application with SEO/E-E-A-T
|
|
462
|
+
- More coming soon...
|
|
193
463
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
464
|
+
<br>
|
|
465
|
+
|
|
466
|
+
- ## Database Support π
|
|
467
|
+
|
|
468
|
+
Configure one or multiple databases:
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
const config: AppConfig = {
|
|
472
|
+
database: [
|
|
473
|
+
{
|
|
474
|
+
name: 'primary',
|
|
475
|
+
connection: './data/primary.db',
|
|
476
|
+
schema: './src/schemas/primary.ts'
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
name: 'cache',
|
|
480
|
+
connection: './data/cache.db',
|
|
481
|
+
schema: './src/schemas/cache.ts'
|
|
199
482
|
}
|
|
200
|
-
|
|
483
|
+
]
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
// Access in lifecycle hooks or plugins
|
|
487
|
+
const app = createApp(config);
|
|
488
|
+
const primaryDb = app.getContext().databases.get('primary');
|
|
489
|
+
const cacheDb = app.getContext().databases.get('cache');
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
<br>
|
|
493
|
+
|
|
494
|
+
- ## Internationalization (i18n) π
|
|
495
|
+
|
|
496
|
+
Built-in multi-language support:
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
const config: AppConfig = {
|
|
500
|
+
i18n: {
|
|
501
|
+
defaultLanguage: 'en',
|
|
502
|
+
supportedLanguages: ['en', 'ar', 'fr', 'es'],
|
|
503
|
+
basePath: './src/shared/static/i18n',
|
|
504
|
+
fileExtension: 'json'
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
**Directory structure:**
|
|
510
|
+
```
|
|
511
|
+
src/shared/static/i18n/
|
|
512
|
+
βββ en.json
|
|
513
|
+
βββ ar.json
|
|
514
|
+
βββ fr.json
|
|
515
|
+
βββ es.json
|
|
516
|
+
```
|
|
201
517
|
|
|
202
518
|
<br>
|
|
203
519
|
|
|
204
520
|
- ## Configuration π₯
|
|
205
521
|
|
|
206
|
-
### AppConfig
|
|
522
|
+
### AppConfig Interface
|
|
207
523
|
|
|
208
524
|
```typescript
|
|
209
525
|
interface AppConfig {
|
|
210
|
-
//
|
|
526
|
+
// Enable debug mode (affects minification, sourcemaps)
|
|
211
527
|
debug?: boolean;
|
|
212
528
|
|
|
213
529
|
// Server configuration
|
|
214
530
|
server?: {
|
|
215
531
|
port?: number; // Default: 3000
|
|
216
532
|
host?: string; // Default: 'localhost'
|
|
217
|
-
logging?:
|
|
533
|
+
logging?: {
|
|
534
|
+
level?: 'debug' | 'info' | 'warn' | 'error';
|
|
535
|
+
pretty?: boolean;
|
|
536
|
+
};
|
|
218
537
|
};
|
|
219
538
|
|
|
220
|
-
// Client build
|
|
539
|
+
// Client build (Bun bundler)
|
|
221
540
|
client?: {
|
|
222
|
-
entry: string; //
|
|
541
|
+
entry: string; // Entry point (e.g., './src/client/browser.tsx')
|
|
223
542
|
output: string; // Output directory
|
|
224
|
-
target?: 'browser' | 'bun';
|
|
225
|
-
minify?: boolean;
|
|
226
|
-
sourcemap?: boolean;
|
|
227
|
-
external?: string[];
|
|
543
|
+
target?: 'browser' | 'bun';
|
|
544
|
+
minify?: boolean; // Default: !debug
|
|
545
|
+
sourcemap?: boolean; // Default: debug
|
|
546
|
+
external?: string[]; // Don't bundle these
|
|
228
547
|
};
|
|
229
548
|
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
|
|
549
|
+
// UI Library (npm package)
|
|
550
|
+
ui?: {
|
|
551
|
+
package: string; // Package name (e.g., '@mineui/core')
|
|
552
|
+
output: string; // CSS output directory
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
// Custom styles (SCSS/CSS)
|
|
556
|
+
style?: {
|
|
557
|
+
entry: string; // Entry SCSS/CSS file
|
|
558
|
+
output: string; // CSS output file (e.g., 'extra.css')
|
|
559
|
+
minify?: boolean; // Default: !debug
|
|
560
|
+
sourcemap?: boolean; // Default: debug
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
// Static files
|
|
564
|
+
static?: {
|
|
565
|
+
path: string; // Web path (e.g., '/static')
|
|
566
|
+
directory: string; // Disk directory
|
|
567
|
+
maxAge?: number; // Cache duration in seconds
|
|
568
|
+
index?: string[]; // Default files
|
|
233
569
|
};
|
|
234
570
|
|
|
235
571
|
// Database configuration
|
|
236
572
|
database?: DatabaseConfig | DatabaseConfig[];
|
|
237
573
|
|
|
238
|
-
// Internationalization
|
|
574
|
+
// Internationalization
|
|
239
575
|
i18n?: {
|
|
240
576
|
defaultLanguage: string;
|
|
241
577
|
supportedLanguages: string[];
|
|
242
578
|
basePath?: string;
|
|
243
|
-
fileExtension?: string;
|
|
579
|
+
fileExtension?: string; // Default: 'json'
|
|
244
580
|
};
|
|
245
581
|
|
|
246
|
-
// Static files serving
|
|
247
|
-
static?: StaticConfig | StaticConfig[];
|
|
248
|
-
|
|
249
|
-
// Security configuration
|
|
250
|
-
security?: SecurityConfig;
|
|
251
|
-
|
|
252
582
|
// Inline routes
|
|
253
583
|
routes?: RouteDefinition[];
|
|
254
584
|
|
|
@@ -262,186 +592,32 @@
|
|
|
262
592
|
|
|
263
593
|
<br>
|
|
264
594
|
|
|
595
|
+
<br>
|
|
596
|
+
|
|
265
597
|
- ## API Reference π₯
|
|
266
598
|
|
|
267
599
|
### Core Function
|
|
268
600
|
|
|
269
|
-
- #### `createApp(config: AppConfig, hooks?: LifecycleHooks): AppInstance`
|
|
270
|
-
> Creates and returns an application instance.
|
|
271
|
-
|
|
272
|
-
**Parameters:**
|
|
273
|
-
- `config` - Application configuration
|
|
274
|
-
- `hooks` (optional) - Lifecycle hooks
|
|
275
|
-
|
|
276
|
-
**Returns:** AppInstance with `start()`, `stop()`, `restart()` methods
|
|
277
|
-
|
|
278
|
-
**Example:**
|
|
279
|
-
```typescript
|
|
280
|
-
const app = createApp({
|
|
281
|
-
debug: true,
|
|
282
|
-
server: { port: 3000 }
|
|
283
|
-
}, {
|
|
284
|
-
onAwake: async (ctx) => console.log('awake'),
|
|
285
|
-
onStart: async (ctx) => console.log('start'),
|
|
286
|
-
onReady: async (ctx) => console.log('ready'),
|
|
287
|
-
onFinish: async (ctx) => console.log('done'),
|
|
288
|
-
onError: async (ctx, phase, error) => console.error(error)
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
await app.start();
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### AppInstance
|
|
295
|
-
|
|
296
|
-
- #### `app.start(): Promise<void>`
|
|
297
|
-
> Starts the application through all lifecycle phases (REGISTER β AWAKE β START β READY).
|
|
298
|
-
|
|
299
|
-
- #### `app.stop(): Promise<void>`
|
|
300
|
-
> Stops the server and cleans up all resources.
|
|
301
|
-
|
|
302
|
-
- #### `app.restart(): Promise<void>`
|
|
303
|
-
> Restarts the application (stops then starts).
|
|
304
|
-
|
|
305
|
-
- #### `app.getContext(): LifecycleContext`
|
|
306
|
-
> Returns the current lifecycle context with databases, plugins, and configuration.
|
|
307
|
-
|
|
308
|
-
- #### `app.getMiddleware(name: string): AppMiddleware | undefined`
|
|
309
|
-
> Retrieves a registered middleware by name.
|
|
310
|
-
|
|
311
|
-
### LifecycleHooks
|
|
312
|
-
|
|
313
601
|
```typescript
|
|
314
|
-
|
|
315
|
-
onConfig?(config: AppConfig): AppConfig;
|
|
316
|
-
onAwake?(ctx: LifecycleContext): Promise<void>;
|
|
317
|
-
onStart?(ctx: LifecycleContext): Promise<void>;
|
|
318
|
-
onReady?(ctx: LifecycleContext): Promise<void>;
|
|
319
|
-
onFinish?(ctx: LifecycleContext): Promise<void>;
|
|
320
|
-
onError?(ctx: LifecycleContext, phase: string, error: Error): Promise<void>;
|
|
321
|
-
}
|
|
322
|
-
```
|
|
602
|
+
import { createApp, type AppConfig } from '@cruxjs/app';
|
|
323
603
|
|
|
324
|
-
|
|
604
|
+
const app = createApp(config, {
|
|
605
|
+
onAwake: async (ctx) => { /* ... */ },
|
|
606
|
+
onStart: async (ctx) => { /* ... */ },
|
|
607
|
+
onReady: async (ctx) => { /* ... */ },
|
|
608
|
+
onFinish: async (ctx) => { /* ... */ },
|
|
609
|
+
onError: async (ctx, phase, error) => { /* ... */ }
|
|
610
|
+
});
|
|
325
611
|
|
|
326
|
-
|
|
327
|
-
interface LifecycleContext {
|
|
328
|
-
config: AppConfig;
|
|
329
|
-
server: ServerInstance | null;
|
|
330
|
-
databases: Map<string, DB>;
|
|
331
|
-
plugins: CruxPlugin[];
|
|
332
|
-
clientBuild?: any;
|
|
333
|
-
}
|
|
612
|
+
await app.start();
|
|
334
613
|
```
|
|
335
614
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
- ## Advanced Features π
|
|
339
|
-
|
|
340
|
-
- #### Plugin System
|
|
341
|
-
> CruxJS uses a plugin-based architecture. Plugins can:
|
|
342
|
-
- Provide routes
|
|
343
|
-
- Define database schemas
|
|
344
|
-
- Register middleware
|
|
345
|
-
- Hook into lifecycle events
|
|
346
|
-
|
|
347
|
-
**Example - Creating a Plugin:**
|
|
348
|
-
```typescript
|
|
349
|
-
import type { CruxPlugin } from '@cruxjs/base';
|
|
350
|
-
|
|
351
|
-
const myPlugin: CruxPlugin = {
|
|
352
|
-
name: 'my-plugin',
|
|
353
|
-
version: '1.0.0',
|
|
354
|
-
|
|
355
|
-
register: async (app) => {
|
|
356
|
-
console.log('Plugin registered');
|
|
357
|
-
},
|
|
358
|
-
|
|
359
|
-
routes: [
|
|
360
|
-
{
|
|
361
|
-
method: 'GET',
|
|
362
|
-
path: '/my-plugin/status',
|
|
363
|
-
handler: async (ctx) => ctx.json({ status: 'ok' })
|
|
364
|
-
}
|
|
365
|
-
],
|
|
366
|
-
|
|
367
|
-
schemas: [
|
|
368
|
-
{
|
|
369
|
-
name: 'my_table',
|
|
370
|
-
columns: [
|
|
371
|
-
{ name: 'id', type: 'INTEGER PRIMARY KEY' },
|
|
372
|
-
{ name: 'data', type: 'TEXT' }
|
|
373
|
-
]
|
|
374
|
-
}
|
|
375
|
-
],
|
|
376
|
-
|
|
377
|
-
middleware: {
|
|
378
|
-
'my-middleware': async (ctx, next) => {
|
|
379
|
-
console.log('Custom middleware');
|
|
380
|
-
return next();
|
|
381
|
-
}
|
|
382
|
-
},
|
|
383
|
-
|
|
384
|
-
hooks: {
|
|
385
|
-
onAwake: async (ctx) => console.log('Plugin awake'),
|
|
386
|
-
onStart: async (ctx) => console.log('Plugin start'),
|
|
387
|
-
onReady: async (ctx) => console.log('Plugin ready'),
|
|
388
|
-
onShutdown: async (ctx) => console.log('Plugin shutdown')
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
- #### Multiple Databases
|
|
394
|
-
> Support for multiple database connections:
|
|
395
|
-
```typescript
|
|
396
|
-
const config: AppConfig = {
|
|
397
|
-
database: [
|
|
398
|
-
{
|
|
399
|
-
name: 'primary',
|
|
400
|
-
connection: './data/primary.db',
|
|
401
|
-
schema: './src/schemas/primary.ts'
|
|
402
|
-
},
|
|
403
|
-
{
|
|
404
|
-
name: 'cache',
|
|
405
|
-
connection: './data/cache.db',
|
|
406
|
-
schema: './src/schemas/cache.ts'
|
|
407
|
-
}
|
|
408
|
-
]
|
|
409
|
-
};
|
|
615
|
+
### AppInstance Methods
|
|
410
616
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const primaryDb = app.databases.get('primary');
|
|
416
|
-
const cacheDb = app.databases.get('cache');
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
- #### Client Building
|
|
420
|
-
> Automatic client bundle generation using Bun:
|
|
421
|
-
```typescript
|
|
422
|
-
const config: AppConfig = {
|
|
423
|
-
client: {
|
|
424
|
-
entry: './src/client/browser.tsx',
|
|
425
|
-
output: './dist/client',
|
|
426
|
-
minify: true,
|
|
427
|
-
sourcemap: false,
|
|
428
|
-
external: ['@minejsx/runtime'] // Don't bundle, rely on external
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
- #### i18n Integration
|
|
434
|
-
> Built-in internationalization support:
|
|
435
|
-
```typescript
|
|
436
|
-
const config: AppConfig = {
|
|
437
|
-
i18n: {
|
|
438
|
-
defaultLanguage: 'en',
|
|
439
|
-
supportedLanguages: ['en', 'ar', 'fr'],
|
|
440
|
-
basePath: './src/shared/static/i18n',
|
|
441
|
-
fileExtension: 'json'
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
```
|
|
617
|
+
- `app.start()` β Start through all lifecycle phases
|
|
618
|
+
- `app.stop()` β Stop server and cleanup
|
|
619
|
+
- `app.restart()` β Restart the application
|
|
620
|
+
- `app.getContext()` β Get current lifecycle context
|
|
445
621
|
|
|
446
622
|
<!-- βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ -->
|
|
447
623
|
|