@cruxjs/app 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Maysara Elshewehy (https://github.com/maysara-elshewehy)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,460 @@
1
+ <!-- ╔══════════════════════════════ BEG ══════════════════════════════╗ -->
2
+
3
+ <br>
4
+ <div align="center">
5
+ <p>
6
+ <img src="./assets/img/logo.png" alt="logo" style="" height="60" />
7
+ </p>
8
+ </div>
9
+
10
+ <div align="center">
11
+ <img src="https://img.shields.io/badge/v-0.0.1-black"/>
12
+ <img src="https://img.shields.io/badge/🔥-@cruxjs-black"/>
13
+ <br>
14
+ <img src="https://img.shields.io/github/issues/cruxjs-/app?style=flat" alt="Github Repo Issues" />
15
+ <img src="https://img.shields.io/github/stars/cruxjs-/app?style=social" alt="GitHub Repo stars" />
16
+ </div>
17
+ <br>
18
+
19
+ <!-- ╚═════════════════════════════════════════════════════════════════╝ -->
20
+
21
+
22
+
23
+ <!-- ╔══════════════════════════════ DOC ══════════════════════════════╗ -->
24
+
25
+ - ## Quick Start 🔥
26
+
27
+ > **_Full-stack framework orchestrator for building modern web applications. Zero configuration. Plugin-based architecture._**
28
+
29
+ - ### Setup
30
+
31
+ > install [`hmm`](https://github.com/minejs-org/hmm) first.
32
+
33
+ ```bash
34
+ hmm i @cruxjs/app
35
+ ```
36
+
37
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
38
+
39
+ - ### Usage
40
+
41
+ ```typescript
42
+ import { createApp, type AppConfig } from '@cruxjs/app';
43
+
44
+ // Define your application
45
+ const config: AppConfig = {
46
+ debug: true,
47
+ server: {
48
+ port: 3000,
49
+ host: 'localhost'
50
+ },
51
+ client: {
52
+ entry: './src/client/browser.tsx',
53
+ output: './dist/client',
54
+ target: 'browser'
55
+ },
56
+ api: {
57
+ directory: './src/server/api'
58
+ },
59
+ plugins: [] // Add plugins here
60
+ };
61
+
62
+ // Create and run the app
63
+ const app = createApp(config);
64
+ await app.start();
65
+ ```
66
+
67
+ <br>
68
+
69
+ - ### 1. Define Routes
70
+
71
+ **src/server/api/index.ts**
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
+ ```
94
+
95
+ - ### 2. Create Client Entry
96
+
97
+ **src/client/browser.tsx**
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
+ ```
124
+
125
+ - ### 3. Add Plugins
126
+
127
+ ```typescript
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
+ });
150
+
151
+ const config: AppConfig = {
152
+ // ... other config
153
+ plugins: [spaPlugin]
154
+ };
155
+ ```
156
+
157
+ <br>
158
+
159
+ - ## Lifecycle 🔥
160
+
161
+ CruxJS applications go through **4 phases**:
162
+
163
+ - #### **Phase 0: REGISTER**
164
+ > Plugins are registered and can hook into the application lifecycle.
165
+
166
+ - #### **Phase 1: AWAKE**
167
+ > Client is built, databases are initialized, i18n is setup, plugins initialize their schemas.
168
+
169
+ **Hooks available:**
170
+ ```typescript
171
+ createApp(config, {
172
+ onAwake: async (ctx) => {
173
+ console.log('App is awake, databases ready');
174
+ const db = ctx.databases.get('default');
175
+ // Setup your initial data
176
+ }
177
+ });
178
+ ```
179
+
180
+ - #### **Phase 2: START**
181
+ > Server is created, routes are merged (user + plugin routes), middleware stack is built.
182
+
183
+ **Hooks available:**
184
+ ```typescript
185
+ onStart: async (ctx) => {
186
+ console.log('Server created, routes ready');
187
+ console.log('Number of routes:', ctx.routes.length);
188
+ }
189
+ ```
190
+
191
+ - #### **Phase 3: READY**
192
+ > Server is listening and ready to handle requests.
193
+
194
+ **Hooks available:**
195
+ ```typescript
196
+ onReady: async (ctx) => {
197
+ const url = `http://${ctx.config.server.host}:${ctx.config.server.port}`;
198
+ console.log(`🚀 Server ready at ${url}`);
199
+ }
200
+ ```
201
+
202
+ <br>
203
+
204
+ - ## Configuration 🔥
205
+
206
+ ### AppConfig
207
+
208
+ ```typescript
209
+ interface AppConfig {
210
+ // Debug mode
211
+ debug?: boolean;
212
+
213
+ // Server configuration
214
+ server?: {
215
+ port?: number; // Default: 3000
216
+ host?: string; // Default: 'localhost'
217
+ logging?: LoggingConfig;
218
+ };
219
+
220
+ // Client build configuration
221
+ client?: {
222
+ entry: string; // Client entry point (e.g., './src/client/browser.tsx')
223
+ output: string; // Output directory
224
+ target?: 'browser' | 'bun'; // Default: 'browser'
225
+ minify?: boolean; // Default: !debug
226
+ sourcemap?: boolean; // Default: debug
227
+ external?: string[]; // External dependencies to exclude from bundle
228
+ };
229
+
230
+ // API routes configuration
231
+ api?: {
232
+ directory: string; // Directory to scan for route files
233
+ };
234
+
235
+ // Database configuration
236
+ database?: DatabaseConfig | DatabaseConfig[];
237
+
238
+ // Internationalization configuration
239
+ i18n?: {
240
+ defaultLanguage: string;
241
+ supportedLanguages: string[];
242
+ basePath?: string;
243
+ fileExtension?: string; // Default: 'json'
244
+ };
245
+
246
+ // Static files serving
247
+ static?: StaticConfig | StaticConfig[];
248
+
249
+ // Security configuration
250
+ security?: SecurityConfig;
251
+
252
+ // Inline routes
253
+ routes?: RouteDefinition[];
254
+
255
+ // Plugins
256
+ plugins?: CruxPlugin[];
257
+
258
+ // Custom middleware
259
+ middlewares?: Record<string, AppMiddleware>;
260
+ }
261
+ ```
262
+
263
+ <br>
264
+
265
+ - ## API Reference 🔥
266
+
267
+ ### Core Function
268
+
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
+ ```typescript
314
+ interface LifecycleHooks {
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
+ ```
323
+
324
+ ### LifecycleContext
325
+
326
+ ```typescript
327
+ interface LifecycleContext {
328
+ config: AppConfig;
329
+ server: ServerInstance | null;
330
+ databases: Map<string, DB>;
331
+ plugins: CruxPlugin[];
332
+ clientBuild?: any;
333
+ }
334
+ ```
335
+
336
+ <br>
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
+ };
410
+
411
+ const app = createApp(config);
412
+ await app.start();
413
+
414
+ // Access databases
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
+ ```
445
+
446
+ <!-- ╚═════════════════════════════════════════════════════════════════╝ -->
447
+
448
+
449
+
450
+ <!-- ╔══════════════════════════════ END ══════════════════════════════╗ -->
451
+
452
+ <br>
453
+
454
+ ---
455
+
456
+ <div align="center">
457
+ <a href="https://github.com/maysara-elshewehy"><img src="https://img.shields.io/badge/by-Maysara-black"/></a>
458
+ </div>
459
+
460
+ <!-- ╚═════════════════════════════════════════════════════════════════╝ -->
package/dist/index.cjs ADDED
@@ -0,0 +1,3 @@
1
+ 'use strict';var base=require('@cruxjs/base'),server=require('@minejs/server'),db=require('@minejs/db'),i18n=require('@minejs/i18n');async function F(r,a){if(!r.client)return null;a.info("Building client...");try{let e=await Bun.build({entrypoints:[r.client.entry],outdir:r.client.output,target:r.client.target||"browser",minify:r.client.minify??!r.debug,sourcemap:r.client.sourcemap??r.debug?"inline":"none",external:r.client.external||[]});if(!e.success)throw new Error("Build failed");let t=e.outputs.map(o=>o.path);return a.success(`Client built \u2192 ${t.join(", ")}`),{success:!0,outputs:t}}catch(e){throw a.error("Failed to build client",e),e}}async function T(r,a,e){let t=new Map;if(!r.database&&a.length===0)return e.info("No database config, skipping setup"),t;let o=r.database?Array.isArray(r.database)?r.database:[r.database]:[];e.info("Setting up databases...");for(let i of o){let u=i.name||"default",l=new db.DB(i.connection);if(i.schema)try{let c=await import(i.schema),d=c.default||c.schema;if(Array.isArray(d))for(let n of d)l.defineSchema(n);}catch(c){throw e.error(`Failed to load schema: ${i.schema}`,c),c}for(let c of a)l.defineSchema(c);t.set(u,l),e.success(`Database '${u}' ready`);}if(t.size===0&&a.length>0){let i=new db.DB(":memory:");for(let u of a)i.defineSchema(u);t.set("default",i),e.success("Database 'default' ready (in-memory for plugins)");}return t}async function I(r,a){if(r.i18n){a.info("Setting up i18n...");try{await i18n.setupAuto({defaultLanguage:r.i18n.defaultLanguage,supportedLanguages:r.i18n.supportedLanguages,basePath:r.i18n.basePath,fileExtension:r.i18n.fileExtension||"json"}),a.success(`i18n ready \u2192 ${r.i18n.supportedLanguages.join(", ")}`);}catch(e){throw a.error("Failed to setup i18n",e),e}}}async function j(r,a){if(!r.api)return [];a.info(`Loading routes from ${r.api.directory}...`);let e=[];try{let t=await P(r.api.directory);for(let o of t)try{let i=await import(o),u=i.default||i.routes;Array.isArray(u)&&e.push(...u);}catch(i){a.error(`Failed to load routes from ${o}`,i);}return a.success(`Routes loaded \u2192 ${e.length} routes`),e}catch(t){return a.error("Failed to scan route directory",t),[]}}async function P(r){let a=[];try{let e=await Array.fromAsync(new Bun.Glob("**/*.{ts,js}").scan(r));for(let t of e)a.push(`${r}/${t}`);}catch{}return a}function q(r,a){let e=a?.onConfig?a.onConfig(r):r,t=new base.Logger(e.debug),o=new base.PluginRegistry(t),i=new base.ResourceMerger(t),u=new Map,l=new Map,c=null,d=null,n={config:e,databases:u,plugins:[]},h={config:e,server:null,databases:u,plugins:[],middlewares:l,start:async()=>{},stop:async()=>{},restart:async()=>{},getContext:()=>n,getMiddleware:s=>l.get(s)};async function E(){if(!e.plugins||e.plugins.length===0){t.info("No plugins to register");return}t.phase("REGISTER");for(let p of e.plugins)await o.register(p,h);n.plugins=o.getAll(),o.collectMiddlewares().forEach((p,f)=>l.set(f,p));}async function R(){t.phase("AWAKE");try{d=await F(e,t),n.clientBuild=d;let s=o.collectSchemas(),p=await T(e,s,t);u.clear(),p.forEach((f,w)=>u.set(w,f)),await I(e,t),await o.callHook("onAwake",n),await a?.onAwake?.(n);}catch(s){throw await a?.onError?.(n,"AWAKE",s),s}}async function S(){t.phase("START");try{let s=await j(e,t),p=o.collectRoutes(),w=[...e.routes||[],...s],C=i.mergeRoutes(w,p),L=e.static?Array.isArray(e.static)?e.static:[e.static]:[],$=o.collectStatic(),m=i.mergeStatic(L,$);t.info("Creating server...");let A;for(let g of n.plugins){let y=g;if(y.__spaErrorHandler){A=y.__spaErrorHandler,console.log(`[CruxJS] \u2713 Error handler collected from: ${g.name}`);break}}c=server.server({port:e.server?.port||3e3,hostname:e.server?.host||"localhost",logging:e.server?.logging,routes:C,static:m.length>0?m:void 0,security:e.security,middlewares:e.middlewares,i18n:e.i18n?{defaultLanguage:e.i18n.defaultLanguage,supportedLanguages:e.i18n.supportedLanguages}:void 0,onError:A}),u.forEach((g,y)=>{c.db.set(y,g);}),n.server=c,h.server=c,t.success(`Server created \u2192 ${e.server?.host||"localhost"}:${e.server?.port||3e3}`),await o.callHook("onStart",n),await a?.onStart?.(n);}catch(s){throw await a?.onError?.(n,"START",s),s}}async function v(){t.phase("READY");try{await c.start(),t.success(`Server running on http://${e.server?.host||"localhost"}:${e.server?.port||3e3}`),await o.callHook("onReady",n),await a?.onReady?.(n);}catch(s){throw await a?.onError?.(n,"READY",s),s}}return {config:e,server:c,databases:u,plugins:n.plugins,middlewares:l,async start(){await E(),await R(),await S(),await v();},async stop(){t.info("Stopping server...");try{c&&await c.stop(),u.forEach((s,p)=>{s.close(),t.info(`Database '${p}' closed`);}),await o.callHook("onShutdown",n),await a?.onFinish?.(n),t.success("Server stopped");}catch(s){throw t.error("Failed to stop server",s),s}},async restart(){await this.stop(),await this.start();},getContext(){return n},getMiddleware(s){return l.get(s)}}}
2
+ exports.createApp=q;Object.keys(base).forEach(function(k){if(k!=='default'&&!Object.prototype.hasOwnProperty.call(exports,k))Object.defineProperty(exports,k,{enumerable:true,get:function(){return base[k]}})});//# sourceMappingURL=index.cjs.map
3
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["buildClient","config","logger","result","outputs","err","setupDatabases","additionalSchemas","databases","dbConfigs","dbConfig","name","db","DB","schemaModule","schema","table","setupI18n","setupAuto","loadRoutes","routes","files","scanDirectory","file","module","exported","dir","entries","entry","createApp","userConfig","hooks","Logger","pluginRegistry","PluginRegistry","resourceMerger","ResourceMerger","middlewares","serverInstance","clientBuild","ctx","partialApp","phaseRegister","plugin","mw","phaseAwake","pluginSchemas","dbs","phaseStart","userApiRoutes","pluginRoutes","allUserRoutes","mergedRoutes","userStatic","pluginStatic","mergedStatic","errorHandler","pluginWithHandler","createServer","phaseReady"],"mappings":"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","file":"index.cjs","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { AppConfig, LifecycleContext, LifecycleHooks, AppInstance, RouteDefinition, AppMiddleware, Logger, PluginRegistry, ResourceMerger } from '@cruxjs/base';\r\n import { server as createServer } from '@minejs/server';\r\n import { DB } from '@minejs/db';\r\n import { setupAuto } from '@minejs/i18n';\r\n import type { TableSchema } from '@minejs/db';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n /**\r\n * Builds the client bundle using Bun's bundler\r\n * \r\n * @param {AppConfig} config - The application configuration containing client build settings\r\n * @param {Logger} logger - Logger instance for logging build progress and errors\r\n * @returns {Promise<{success: boolean, outputs: string[]} | null>} Build result with output paths, or null if no client config\r\n * @throws {Error} If the build process fails\r\n * \r\n * @example\r\n * const result = await buildClient(config, logger);\r\n * if (result?.success) {\r\n * console.log('Built to:', result.outputs);\r\n * }\r\n */\r\n async function buildClient(config: AppConfig, logger: Logger) {\r\n if (!config.client) return null;\r\n\r\n logger.info('Building client...');\r\n\r\n try {\r\n const result = await Bun.build({\r\n entrypoints: [config.client.entry],\r\n outdir: config.client.output,\r\n target: config.client.target || 'browser',\r\n minify: config.client.minify ?? !config.debug,\r\n sourcemap: config.client.sourcemap ?? config.debug ? 'inline' : 'none',\r\n external: config.client.external || []\r\n });\r\n\r\n if (!result.success) throw new Error('Build failed');\r\n\r\n const outputs = result.outputs.map(o => o.path);\r\n logger.success(`Client built → ${outputs.join(', ')}`);\r\n\r\n return { success: true, outputs };\r\n } catch (err) {\r\n logger.error('Failed to build client', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Initializes and configures database connections\r\n * \r\n * Handles:\r\n * - Multiple database instances (primary, cache, etc.)\r\n * - User-defined schemas from config\r\n * - Plugin schemas from plugins\r\n * - In-memory databases for plugin-only scenarios\r\n * \r\n * @param {AppConfig} config - Application configuration with database settings\r\n * @param {TableSchema[]} additionalSchemas - Database schemas provided by plugins\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<Map<string, DB>>} Map of database instances by name\r\n * @throws {Error} If schema loading or database initialization fails\r\n * \r\n * @example\r\n * const databases = await setupDatabases(config, pluginSchemas, logger);\r\n * const db = databases.get('primary');\r\n * const result = db.query('SELECT * FROM users');\r\n */\r\n async function setupDatabases(\r\n config: AppConfig,\r\n additionalSchemas: TableSchema[],\r\n logger: Logger\r\n ) {\r\n const databases = new Map<string, DB>();\r\n\r\n if (!config.database && additionalSchemas.length === 0) {\r\n logger.info('No database config, skipping setup');\r\n return databases;\r\n }\r\n\r\n const dbConfigs = config.database\r\n ? Array.isArray(config.database)\r\n ? config.database\r\n : [config.database]\r\n : [];\r\n\r\n logger.info('Setting up databases...');\r\n\r\n for (const dbConfig of dbConfigs) {\r\n const name = dbConfig.name || 'default';\r\n const db = new DB(dbConfig.connection);\r\n\r\n // Load user schema\r\n if (dbConfig.schema) {\r\n try {\r\n const schemaModule = await import(dbConfig.schema);\r\n const schema = schemaModule.default || schemaModule.schema;\r\n\r\n if (Array.isArray(schema)) {\r\n for (const table of schema) {\r\n db.defineSchema(table);\r\n }\r\n }\r\n } catch (err) {\r\n logger.error(`Failed to load schema: ${dbConfig.schema}`, err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // Load plugin schemas for this database\r\n for (const schema of additionalSchemas) {\r\n db.defineSchema(schema);\r\n }\r\n\r\n databases.set(name, db);\r\n logger.success(`Database '${name}' ready`);\r\n }\r\n\r\n // If no user database but plugins need one, create default\r\n if (databases.size === 0 && additionalSchemas.length > 0) {\r\n const db = new DB(':memory:');\r\n\r\n for (const schema of additionalSchemas) {\r\n db.defineSchema(schema);\r\n }\r\n\r\n databases.set('default', db);\r\n logger.success(`Database 'default' ready (in-memory for plugins)`);\r\n }\r\n\r\n return databases;\r\n }\r\n\r\n /**\r\n * Initializes internationalization (i18n) support\r\n * \r\n * Loads language files and configures the i18n system based on:\r\n * - Default language\r\n * - Supported languages\r\n * - Base path for translation files\r\n * - File extension (json, cjson, etc.)\r\n * \r\n * @param {AppConfig} config - Application configuration with i18n settings\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<void>}\r\n * @throws {Error} If i18n setup or language file loading fails\r\n * \r\n * @example\r\n * await setupI18n({\r\n * i18n: {\r\n * defaultLanguage: 'en',\r\n * supportedLanguages: ['en', 'ar'],\r\n * basePath: './src/i18n'\r\n * }\r\n * }, logger);\r\n */\r\n async function setupI18n(config: AppConfig, logger: Logger) {\r\n if (!config.i18n) return;\r\n\r\n logger.info('Setting up i18n...');\r\n\r\n try {\r\n await setupAuto({\r\n defaultLanguage: config.i18n.defaultLanguage,\r\n supportedLanguages: config.i18n.supportedLanguages,\r\n basePath: config.i18n.basePath,\r\n fileExtension: config.i18n.fileExtension || 'json'\r\n });\r\n\r\n logger.success(`i18n ready → ${config.i18n.supportedLanguages.join(', ')}`);\r\n } catch (err) {\r\n logger.error('Failed to setup i18n', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Dynamically loads route definitions from API directory\r\n * \r\n * Scans the specified directory for route files and imports them.\r\n * Each route file should export either:\r\n * - `routes` property with RouteDefinition array\r\n * - Default export with RouteDefinition array\r\n * \r\n * @param {AppConfig} config - Application configuration with api.directory\r\n * @param {Logger} logger - Logger instance for logging scan progress\r\n * @returns {Promise<RouteDefinition[]>} Array of loaded route definitions\r\n * \r\n * @example\r\n * const routes = await loadRoutes({\r\n * api: { directory: './src/server/api' }\r\n * }, logger);\r\n * console.log(`Loaded ${routes.length} routes`);\r\n */\r\n async function loadRoutes(config: AppConfig, logger: Logger): Promise<RouteDefinition[]> {\r\n if (!config.api) return [];\r\n\r\n logger.info(`Loading routes from ${config.api.directory}...`);\r\n\r\n const routes: RouteDefinition[] = [];\r\n\r\n try {\r\n const files = await scanDirectory(config.api.directory);\r\n\r\n for (const file of files) {\r\n try {\r\n const module = await import(file);\r\n const exported = module.default || module.routes;\r\n\r\n if (Array.isArray(exported)) {\r\n routes.push(...exported);\r\n }\r\n } catch (err) {\r\n logger.error(`Failed to load routes from ${file}`, err as Error);\r\n }\r\n }\r\n\r\n logger.success(`Routes loaded → ${routes.length} routes`);\r\n return routes;\r\n } catch (err) {\r\n logger.error('Failed to scan route directory', err as Error);\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Scans a directory for TypeScript/JavaScript files\r\n * \r\n * Uses Bun's Glob API to recursively find all .ts and .js files in a directory.\r\n * Gracefully handles non-existent directories (returns empty array).\r\n * \r\n * @param {string} dir - Directory path to scan\r\n * @returns {Promise<string[]>} Array of absolute file paths found\r\n * \r\n * @example\r\n * const files = await scanDirectory('./src/server/api');\r\n * // Returns: ['./src/server/api/users.ts', './src/server/api/posts.ts']\r\n */\r\n async function scanDirectory(dir: string): Promise<string[]> {\r\n const files: string[] = [];\r\n\r\n try {\r\n const entries = await Array.fromAsync(\r\n new Bun.Glob('**/*.{ts,js}').scan(dir)\r\n );\r\n\r\n for (const entry of entries) {\r\n files.push(`${dir}/${entry}`);\r\n }\r\n } catch {\r\n // Directory doesn't exist\r\n }\r\n\r\n return files;\r\n }\r\n\r\n /**\r\n * Creates a CruxJS application instance with full lifecycle management\r\n * \r\n * This is the main entry point for building a CruxJS application. It:\r\n * 1. Registers plugins (Phase 0)\r\n * 2. Builds client, initializes databases, setups i18n (Phase 1: AWAKE)\r\n * 3. Creates server, merges routes and middleware (Phase 2: START)\r\n * 4. Starts the server and enables request handling (Phase 3: READY)\r\n * \r\n * Phases execute sequentially when `app.start()` is called.\r\n * \r\n * @param {AppConfig} userConfig - Application configuration object\r\n * @param {LifecycleHooks} [hooks] - Optional lifecycle event handlers\r\n * @returns {AppInstance} Application instance with control methods\r\n * \r\n * @throws {Error} Will throw if any lifecycle phase fails (unless caught in onError hook)\r\n * \r\n * @example\r\n * // Basic usage\r\n * const app = createApp({\r\n * debug: true,\r\n * server: { port: 3000 },\r\n * api: { directory: './src/api' },\r\n * plugins: [spaPlugin]\r\n * });\r\n * await app.start();\r\n * \r\n * @example\r\n * // With lifecycle hooks\r\n * const app = createApp(config, {\r\n * onAwake: async (ctx) => {\r\n * console.log('⏰ App awoken, databases ready');\r\n * },\r\n * onReady: async (ctx) => {\r\n * console.log('✅ Server ready:', ctx.server.getURL());\r\n * },\r\n * onError: async (ctx, phase, error) => {\r\n * console.error(`Error in ${phase}:`, error.message);\r\n * }\r\n * });\r\n * \r\n * @example\r\n * // With cleanup\r\n * const app = createApp(config);\r\n * await app.start();\r\n * // ... server is running\r\n * await app.stop(); // Cleanup\r\n */\r\n export function createApp(\r\n userConfig: AppConfig,\r\n hooks?: LifecycleHooks\r\n ): AppInstance {\r\n // Apply config hook\r\n const config: AppConfig = hooks?.onConfig\r\n ? hooks.onConfig(userConfig) as AppConfig\r\n : userConfig;\r\n\r\n const logger = new Logger(config.debug);\r\n const pluginRegistry = new PluginRegistry(logger);\r\n const resourceMerger = new ResourceMerger(logger);\r\n\r\n const databases = new Map<string, DB>();\r\n const middlewares = new Map<string, AppMiddleware>();\r\n let serverInstance: any = null;\r\n let clientBuild: any = null;\r\n\r\n const ctx: LifecycleContext = {\r\n config,\r\n databases,\r\n plugins: []\r\n };\r\n\r\n // Create partial app instance for plugin registration\r\n const partialApp: AppInstance = {\r\n config,\r\n server: null,\r\n databases,\r\n plugins: [],\r\n middlewares,\r\n start: async () => { },\r\n stop: async () => { },\r\n restart: async () => { },\r\n getContext: () => ctx,\r\n getMiddleware: (name: string) => middlewares.get(name)\r\n };\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 0: Plugin Registration\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseRegister() {\r\n if (!config.plugins || config.plugins.length === 0) {\r\n logger.info('No plugins to register');\r\n return;\r\n }\r\n\r\n logger.phase('REGISTER');\r\n\r\n for (const plugin of config.plugins) {\r\n await pluginRegistry.register(plugin, partialApp);\r\n }\r\n\r\n ctx.plugins = pluginRegistry.getAll();\r\n\r\n // Collect plugin middlewares\r\n const pluginMiddlewares = pluginRegistry.collectMiddlewares();\r\n pluginMiddlewares.forEach((mw, name) => middlewares.set(name, mw));\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 1: AWAKE\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseAwake() {\r\n logger.phase('AWAKE');\r\n\r\n try {\r\n // Build client\r\n clientBuild = await buildClient(config, logger);\r\n ctx.clientBuild = clientBuild;\r\n\r\n // Collect plugin schemas\r\n const pluginSchemas = pluginRegistry.collectSchemas();\r\n\r\n // Setup databases\r\n const dbs = await setupDatabases(config, pluginSchemas, logger);\r\n databases.clear();\r\n dbs.forEach((db, name) => databases.set(name, db));\r\n\r\n // Setup i18n\r\n await setupI18n(config, logger);\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onAwake', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onAwake?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'AWAKE', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 2: START\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseStart() {\r\n logger.phase('START');\r\n\r\n try {\r\n // Load user routes\r\n const userApiRoutes = await loadRoutes(config, logger);\r\n\r\n // Collect plugin routes\r\n const pluginRoutes = pluginRegistry.collectRoutes();\r\n\r\n // Merge user routes + plugin routes\r\n const userRoutes = config.routes || [];\r\n const allUserRoutes = [...userRoutes, ...userApiRoutes];\r\n const mergedRoutes = resourceMerger.mergeRoutes(allUserRoutes, pluginRoutes);\r\n\r\n // Collect static configs\r\n const userStatic = config.static\r\n ? Array.isArray(config.static)\r\n ? config.static\r\n : [config.static]\r\n : [];\r\n const pluginStatic = pluginRegistry.collectStatic();\r\n const mergedStatic = resourceMerger.mergeStatic(userStatic, pluginStatic);\r\n\r\n logger.info('Creating server...');\r\n\r\n // Collect error handlers from plugins (especially SPA plugin)\r\n let errorHandler: ((statusCode: number, path: string) => Response) | undefined;\r\n for (const plugin of ctx.plugins) {\r\n const pluginWithHandler = plugin as any;\r\n if (pluginWithHandler.__spaErrorHandler) {\r\n errorHandler = pluginWithHandler.__spaErrorHandler;\r\n console.log(`[CruxJS] ✓ Error handler collected from: ${plugin.name}`);\r\n break; // Use first error handler found\r\n }\r\n }\r\n\r\n // Create server\r\n serverInstance = createServer({\r\n port: config.server?.port || 3000,\r\n hostname: config.server?.host || 'localhost',\r\n logging: config.server?.logging,\r\n routes: mergedRoutes,\r\n static: mergedStatic.length > 0 ? mergedStatic : undefined,\r\n security: config.security,\r\n middlewares: config.middlewares,\r\n i18n: config.i18n\r\n ? {\r\n defaultLanguage: config.i18n.defaultLanguage,\r\n supportedLanguages: config.i18n.supportedLanguages\r\n }\r\n : undefined,\r\n onError: errorHandler\r\n });\r\n\r\n // Inject databases\r\n databases.forEach((db, name) => {\r\n serverInstance.db.set(name, db);\r\n });\r\n\r\n ctx.server = serverInstance;\r\n partialApp.server = serverInstance;\r\n\r\n logger.success(\r\n `Server created → ${config.server?.host || 'localhost'}:${config.server?.port || 3000}`\r\n );\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onStart', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onStart?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'START', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 3: READY\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseReady() {\r\n logger.phase('READY');\r\n\r\n try {\r\n await serverInstance.start();\r\n\r\n logger.success(\r\n `Server running on http://${config.server?.host || 'localhost'}:${config.server?.port || 3000}`\r\n );\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onReady', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onReady?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'READY', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // App Instance\r\n // ─────────────────────────────────────────────────────────\r\n\r\n return {\r\n config,\r\n server: serverInstance,\r\n databases,\r\n plugins: ctx.plugins,\r\n middlewares,\r\n\r\n async start() {\r\n await phaseRegister();\r\n await phaseAwake();\r\n await phaseStart();\r\n await phaseReady();\r\n },\r\n\r\n async stop() {\r\n logger.info('Stopping server...');\r\n\r\n try {\r\n if (serverInstance) {\r\n await serverInstance.stop();\r\n }\r\n\r\n databases.forEach((db, name) => {\r\n db.close();\r\n logger.info(`Database '${name}' closed`);\r\n });\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onShutdown', ctx);\r\n\r\n await hooks?.onFinish?.(ctx);\r\n\r\n logger.success('Server stopped');\r\n } catch (err) {\r\n logger.error('Failed to stop server', err as Error);\r\n throw err;\r\n }\r\n },\r\n\r\n async restart() {\r\n await this.stop();\r\n await this.start();\r\n },\r\n\r\n getContext() {\r\n return ctx;\r\n },\r\n\r\n getMiddleware(name: string) {\r\n return middlewares.get(name);\r\n }\r\n };\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ ════ ════════════════════════════════════════╗\r\n\r\n export * from '@cruxjs/base';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝"]}
@@ -0,0 +1,54 @@
1
+ import { AppConfig, LifecycleHooks, AppInstance } from '@cruxjs/base';
2
+ export * from '@cruxjs/base';
3
+
4
+ /**
5
+ * Creates a CruxJS application instance with full lifecycle management
6
+ *
7
+ * This is the main entry point for building a CruxJS application. It:
8
+ * 1. Registers plugins (Phase 0)
9
+ * 2. Builds client, initializes databases, setups i18n (Phase 1: AWAKE)
10
+ * 3. Creates server, merges routes and middleware (Phase 2: START)
11
+ * 4. Starts the server and enables request handling (Phase 3: READY)
12
+ *
13
+ * Phases execute sequentially when `app.start()` is called.
14
+ *
15
+ * @param {AppConfig} userConfig - Application configuration object
16
+ * @param {LifecycleHooks} [hooks] - Optional lifecycle event handlers
17
+ * @returns {AppInstance} Application instance with control methods
18
+ *
19
+ * @throws {Error} Will throw if any lifecycle phase fails (unless caught in onError hook)
20
+ *
21
+ * @example
22
+ * // Basic usage
23
+ * const app = createApp({
24
+ * debug: true,
25
+ * server: { port: 3000 },
26
+ * api: { directory: './src/api' },
27
+ * plugins: [spaPlugin]
28
+ * });
29
+ * await app.start();
30
+ *
31
+ * @example
32
+ * // With lifecycle hooks
33
+ * const app = createApp(config, {
34
+ * onAwake: async (ctx) => {
35
+ * console.log('⏰ App awoken, databases ready');
36
+ * },
37
+ * onReady: async (ctx) => {
38
+ * console.log('✅ Server ready:', ctx.server.getURL());
39
+ * },
40
+ * onError: async (ctx, phase, error) => {
41
+ * console.error(`Error in ${phase}:`, error.message);
42
+ * }
43
+ * });
44
+ *
45
+ * @example
46
+ * // With cleanup
47
+ * const app = createApp(config);
48
+ * await app.start();
49
+ * // ... server is running
50
+ * await app.stop(); // Cleanup
51
+ */
52
+ declare function createApp(userConfig: AppConfig, hooks?: LifecycleHooks): AppInstance;
53
+
54
+ export { createApp };
@@ -0,0 +1,54 @@
1
+ import { AppConfig, LifecycleHooks, AppInstance } from '@cruxjs/base';
2
+ export * from '@cruxjs/base';
3
+
4
+ /**
5
+ * Creates a CruxJS application instance with full lifecycle management
6
+ *
7
+ * This is the main entry point for building a CruxJS application. It:
8
+ * 1. Registers plugins (Phase 0)
9
+ * 2. Builds client, initializes databases, setups i18n (Phase 1: AWAKE)
10
+ * 3. Creates server, merges routes and middleware (Phase 2: START)
11
+ * 4. Starts the server and enables request handling (Phase 3: READY)
12
+ *
13
+ * Phases execute sequentially when `app.start()` is called.
14
+ *
15
+ * @param {AppConfig} userConfig - Application configuration object
16
+ * @param {LifecycleHooks} [hooks] - Optional lifecycle event handlers
17
+ * @returns {AppInstance} Application instance with control methods
18
+ *
19
+ * @throws {Error} Will throw if any lifecycle phase fails (unless caught in onError hook)
20
+ *
21
+ * @example
22
+ * // Basic usage
23
+ * const app = createApp({
24
+ * debug: true,
25
+ * server: { port: 3000 },
26
+ * api: { directory: './src/api' },
27
+ * plugins: [spaPlugin]
28
+ * });
29
+ * await app.start();
30
+ *
31
+ * @example
32
+ * // With lifecycle hooks
33
+ * const app = createApp(config, {
34
+ * onAwake: async (ctx) => {
35
+ * console.log('⏰ App awoken, databases ready');
36
+ * },
37
+ * onReady: async (ctx) => {
38
+ * console.log('✅ Server ready:', ctx.server.getURL());
39
+ * },
40
+ * onError: async (ctx, phase, error) => {
41
+ * console.error(`Error in ${phase}:`, error.message);
42
+ * }
43
+ * });
44
+ *
45
+ * @example
46
+ * // With cleanup
47
+ * const app = createApp(config);
48
+ * await app.start();
49
+ * // ... server is running
50
+ * await app.stop(); // Cleanup
51
+ */
52
+ declare function createApp(userConfig: AppConfig, hooks?: LifecycleHooks): AppInstance;
53
+
54
+ export { createApp };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import {Logger,PluginRegistry,ResourceMerger}from'@cruxjs/base';export*from'@cruxjs/base';import {server}from'@minejs/server';import {DB}from'@minejs/db';import {setupAuto}from'@minejs/i18n';async function F(r,a){if(!r.client)return null;a.info("Building client...");try{let e=await Bun.build({entrypoints:[r.client.entry],outdir:r.client.output,target:r.client.target||"browser",minify:r.client.minify??!r.debug,sourcemap:r.client.sourcemap??r.debug?"inline":"none",external:r.client.external||[]});if(!e.success)throw new Error("Build failed");let t=e.outputs.map(o=>o.path);return a.success(`Client built \u2192 ${t.join(", ")}`),{success:!0,outputs:t}}catch(e){throw a.error("Failed to build client",e),e}}async function T(r,a,e){let t=new Map;if(!r.database&&a.length===0)return e.info("No database config, skipping setup"),t;let o=r.database?Array.isArray(r.database)?r.database:[r.database]:[];e.info("Setting up databases...");for(let i of o){let u=i.name||"default",l=new DB(i.connection);if(i.schema)try{let c=await import(i.schema),d=c.default||c.schema;if(Array.isArray(d))for(let n of d)l.defineSchema(n);}catch(c){throw e.error(`Failed to load schema: ${i.schema}`,c),c}for(let c of a)l.defineSchema(c);t.set(u,l),e.success(`Database '${u}' ready`);}if(t.size===0&&a.length>0){let i=new DB(":memory:");for(let u of a)i.defineSchema(u);t.set("default",i),e.success("Database 'default' ready (in-memory for plugins)");}return t}async function I(r,a){if(r.i18n){a.info("Setting up i18n...");try{await setupAuto({defaultLanguage:r.i18n.defaultLanguage,supportedLanguages:r.i18n.supportedLanguages,basePath:r.i18n.basePath,fileExtension:r.i18n.fileExtension||"json"}),a.success(`i18n ready \u2192 ${r.i18n.supportedLanguages.join(", ")}`);}catch(e){throw a.error("Failed to setup i18n",e),e}}}async function j(r,a){if(!r.api)return [];a.info(`Loading routes from ${r.api.directory}...`);let e=[];try{let t=await P(r.api.directory);for(let o of t)try{let i=await import(o),u=i.default||i.routes;Array.isArray(u)&&e.push(...u);}catch(i){a.error(`Failed to load routes from ${o}`,i);}return a.success(`Routes loaded \u2192 ${e.length} routes`),e}catch(t){return a.error("Failed to scan route directory",t),[]}}async function P(r){let a=[];try{let e=await Array.fromAsync(new Bun.Glob("**/*.{ts,js}").scan(r));for(let t of e)a.push(`${r}/${t}`);}catch{}return a}function q(r,a){let e=a?.onConfig?a.onConfig(r):r,t=new Logger(e.debug),o=new PluginRegistry(t),i=new ResourceMerger(t),u=new Map,l=new Map,c=null,d=null,n={config:e,databases:u,plugins:[]},h={config:e,server:null,databases:u,plugins:[],middlewares:l,start:async()=>{},stop:async()=>{},restart:async()=>{},getContext:()=>n,getMiddleware:s=>l.get(s)};async function E(){if(!e.plugins||e.plugins.length===0){t.info("No plugins to register");return}t.phase("REGISTER");for(let p of e.plugins)await o.register(p,h);n.plugins=o.getAll(),o.collectMiddlewares().forEach((p,f)=>l.set(f,p));}async function R(){t.phase("AWAKE");try{d=await F(e,t),n.clientBuild=d;let s=o.collectSchemas(),p=await T(e,s,t);u.clear(),p.forEach((f,w)=>u.set(w,f)),await I(e,t),await o.callHook("onAwake",n),await a?.onAwake?.(n);}catch(s){throw await a?.onError?.(n,"AWAKE",s),s}}async function S(){t.phase("START");try{let s=await j(e,t),p=o.collectRoutes(),w=[...e.routes||[],...s],C=i.mergeRoutes(w,p),L=e.static?Array.isArray(e.static)?e.static:[e.static]:[],$=o.collectStatic(),m=i.mergeStatic(L,$);t.info("Creating server...");let A;for(let g of n.plugins){let y=g;if(y.__spaErrorHandler){A=y.__spaErrorHandler,console.log(`[CruxJS] \u2713 Error handler collected from: ${g.name}`);break}}c=server({port:e.server?.port||3e3,hostname:e.server?.host||"localhost",logging:e.server?.logging,routes:C,static:m.length>0?m:void 0,security:e.security,middlewares:e.middlewares,i18n:e.i18n?{defaultLanguage:e.i18n.defaultLanguage,supportedLanguages:e.i18n.supportedLanguages}:void 0,onError:A}),u.forEach((g,y)=>{c.db.set(y,g);}),n.server=c,h.server=c,t.success(`Server created \u2192 ${e.server?.host||"localhost"}:${e.server?.port||3e3}`),await o.callHook("onStart",n),await a?.onStart?.(n);}catch(s){throw await a?.onError?.(n,"START",s),s}}async function v(){t.phase("READY");try{await c.start(),t.success(`Server running on http://${e.server?.host||"localhost"}:${e.server?.port||3e3}`),await o.callHook("onReady",n),await a?.onReady?.(n);}catch(s){throw await a?.onError?.(n,"READY",s),s}}return {config:e,server:c,databases:u,plugins:n.plugins,middlewares:l,async start(){await E(),await R(),await S(),await v();},async stop(){t.info("Stopping server...");try{c&&await c.stop(),u.forEach((s,p)=>{s.close(),t.info(`Database '${p}' closed`);}),await o.callHook("onShutdown",n),await a?.onFinish?.(n),t.success("Server stopped");}catch(s){throw t.error("Failed to stop server",s),s}},async restart(){await this.stop(),await this.start();},getContext(){return n},getMiddleware(s){return l.get(s)}}}
2
+ export{q as createApp};//# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["buildClient","config","logger","result","outputs","err","setupDatabases","additionalSchemas","databases","dbConfigs","dbConfig","name","db","DB","schemaModule","schema","table","setupI18n","setupAuto","loadRoutes","routes","files","scanDirectory","file","module","exported","dir","entries","entry","createApp","userConfig","hooks","Logger","pluginRegistry","PluginRegistry","resourceMerger","ResourceMerger","middlewares","serverInstance","clientBuild","ctx","partialApp","phaseRegister","plugin","mw","phaseAwake","pluginSchemas","dbs","phaseStart","userApiRoutes","pluginRoutes","allUserRoutes","mergedRoutes","userStatic","pluginStatic","mergedStatic","errorHandler","pluginWithHandler","createServer","phaseReady"],"mappings":"+LAmCI,eAAeA,CAAAA,CAAYC,CAAAA,CAAmBC,CAAAA,CAAgB,CAC1D,GAAI,CAACD,CAAAA,CAAO,MAAA,CAAQ,OAAO,IAAA,CAE3BC,CAAAA,CAAO,IAAA,CAAK,oBAAoB,CAAA,CAEhC,GAAI,CACA,IAAMC,CAAAA,CAAS,MAAM,GAAA,CAAI,KAAA,CAAM,CAC3B,WAAA,CAAa,CAACF,CAAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CACjC,MAAA,CAAQA,CAAAA,CAAO,MAAA,CAAO,MAAA,CACtB,MAAA,CAAQA,CAAAA,CAAO,MAAA,CAAO,MAAA,EAAU,SAAA,CAChC,MAAA,CAAQA,EAAO,MAAA,CAAO,MAAA,EAAU,CAACA,CAAAA,CAAO,KAAA,CACxC,SAAA,CAAWA,CAAAA,CAAO,MAAA,CAAO,SAAA,EAAaA,CAAAA,CAAO,KAAA,CAAQ,QAAA,CAAW,MAAA,CAChE,QAAA,CAAUA,CAAAA,CAAO,MAAA,CAAO,QAAA,EAAY,EACxC,CAAC,CAAA,CAED,GAAI,CAACE,CAAAA,CAAO,OAAA,CAAS,MAAM,IAAI,KAAA,CAAM,cAAc,CAAA,CAEnD,IAAMC,CAAAA,CAAUD,EAAO,OAAA,CAAQ,GAAA,CAAI,CAAA,EAAK,CAAA,CAAE,IAAI,CAAA,CAC9C,OAAAD,CAAAA,CAAO,OAAA,CAAQ,CAAA,oBAAA,EAAkBE,CAAAA,CAAQ,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA,CAE9C,CAAE,OAAA,CAAS,CAAA,CAAA,CAAM,OAAA,CAAAA,CAAQ,CACpC,CAAA,MAASC,CAAAA,CAAK,CACV,MAAAH,CAAAA,CAAO,KAAA,CAAM,wBAAA,CAA0BG,CAAY,CAAA,CAC7CA,CACV,CACJ,CAsBA,eAAeC,CAAAA,CACXL,CAAAA,CACAM,CAAAA,CACAL,CAAAA,CACF,CACE,IAAMM,CAAAA,CAAY,IAAI,GAAA,CAEtB,GAAI,CAACP,CAAAA,CAAO,QAAA,EAAYM,CAAAA,CAAkB,MAAA,GAAW,CAAA,CACjD,OAAAL,CAAAA,CAAO,IAAA,CAAK,oCAAoC,CAAA,CACzCM,CAAAA,CAGX,IAAMC,CAAAA,CAAYR,CAAAA,CAAO,QAAA,CACnB,KAAA,CAAM,OAAA,CAAQA,CAAAA,CAAO,QAAQ,CAAA,CACzBA,CAAAA,CAAO,QAAA,CACP,CAACA,CAAAA,CAAO,QAAQ,CAAA,CACpB,EAAC,CAEPC,CAAAA,CAAO,IAAA,CAAK,yBAAyB,CAAA,CAErC,IAAA,IAAWQ,CAAAA,IAAYD,CAAAA,CAAW,CAC9B,IAAME,CAAAA,CAAOD,CAAAA,CAAS,IAAA,EAAQ,SAAA,CACxBE,CAAAA,CAAK,IAAIC,EAAAA,CAAGH,CAAAA,CAAS,UAAU,CAAA,CAGrC,GAAIA,CAAAA,CAAS,MAAA,CACT,GAAI,CACA,IAAMI,CAAAA,CAAe,MAAM,OAAOJ,CAAAA,CAAS,MAAA,CAAA,CACrCK,CAAAA,CAASD,CAAAA,CAAa,OAAA,EAAWA,CAAAA,CAAa,MAAA,CAEpD,GAAI,KAAA,CAAM,OAAA,CAAQC,CAAM,CAAA,CACpB,IAAA,IAAWC,CAAAA,IAASD,CAAAA,CAChBH,CAAAA,CAAG,YAAA,CAAaI,CAAK,EAGjC,CAAA,MAASX,CAAAA,CAAK,CACV,MAAAH,CAAAA,CAAO,KAAA,CAAM,CAAA,uBAAA,EAA0BQ,CAAAA,CAAS,MAAM,GAAIL,CAAY,CAAA,CAChEA,CACV,CAIJ,IAAA,IAAWU,CAAAA,IAAUR,CAAAA,CACjBK,CAAAA,CAAG,YAAA,CAAaG,CAAM,CAAA,CAG1BP,CAAAA,CAAU,GAAA,CAAIG,CAAAA,CAAMC,CAAE,CAAA,CACtBV,CAAAA,CAAO,OAAA,CAAQ,CAAA,UAAA,EAAaS,CAAI,CAAA,OAAA,CAAS,EAC7C,CAGA,GAAIH,CAAAA,CAAU,IAAA,GAAS,CAAA,EAAKD,CAAAA,CAAkB,MAAA,CAAS,CAAA,CAAG,CACtD,IAAMK,EAAK,IAAIC,EAAAA,CAAG,UAAU,CAAA,CAE5B,IAAA,IAAWE,CAAAA,IAAUR,CAAAA,CACjBK,CAAAA,CAAG,YAAA,CAAaG,CAAM,CAAA,CAG1BP,CAAAA,CAAU,GAAA,CAAI,SAAA,CAAWI,CAAE,CAAA,CAC3BV,CAAAA,CAAO,OAAA,CAAQ,kDAAkD,EACrE,CAEA,OAAOM,CACX,CAyBA,eAAeS,CAAAA,CAAUhB,CAAAA,CAAmBC,CAAAA,CAAgB,CACxD,GAAKD,CAAAA,CAAO,IAAA,CAEZ,CAAAC,CAAAA,CAAO,IAAA,CAAK,oBAAoB,CAAA,CAEhC,GAAI,CACA,MAAMgB,SAAAA,CAAU,CACZ,eAAA,CAAiBjB,CAAAA,CAAO,IAAA,CAAK,eAAA,CAC7B,kBAAA,CAAoBA,CAAAA,CAAO,IAAA,CAAK,kBAAA,CAChC,QAAA,CAAUA,CAAAA,CAAO,IAAA,CAAK,QAAA,CACtB,aAAA,CAAeA,CAAAA,CAAO,IAAA,CAAK,aAAA,EAAiB,MAChD,CAAC,CAAA,CAEDC,CAAAA,CAAO,OAAA,CAAQ,CAAA,kBAAA,EAAgBD,EAAO,IAAA,CAAK,kBAAA,CAAmB,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,EAC9E,CAAA,MAASI,CAAAA,CAAK,CACV,MAAAH,CAAAA,CAAO,KAAA,CAAM,sBAAA,CAAwBG,CAAY,CAAA,CAC3CA,CACV,CAAA,CACJ,CAoBA,eAAec,CAAAA,CAAWlB,CAAAA,CAAmBC,CAAAA,CAA4C,CACrF,GAAI,CAACD,CAAAA,CAAO,GAAA,CAAK,OAAO,EAAC,CAEzBC,CAAAA,CAAO,KAAK,CAAA,oBAAA,EAAuBD,CAAAA,CAAO,GAAA,CAAI,SAAS,CAAA,GAAA,CAAK,CAAA,CAE5D,IAAMmB,CAAAA,CAA4B,EAAC,CAEnC,GAAI,CACA,IAAMC,CAAAA,CAAQ,MAAMC,CAAAA,CAAcrB,CAAAA,CAAO,GAAA,CAAI,SAAS,CAAA,CAEtD,IAAA,IAAWsB,CAAAA,IAAQF,CAAAA,CACf,GAAI,CACA,IAAMG,CAAAA,CAAS,MAAM,OAAOD,CAAAA,CAAAA,CACtBE,CAAAA,CAAWD,CAAAA,CAAO,SAAWA,CAAAA,CAAO,MAAA,CAEtC,KAAA,CAAM,OAAA,CAAQC,CAAQ,CAAA,EACtBL,CAAAA,CAAO,IAAA,CAAK,GAAGK,CAAQ,EAE/B,CAAA,MAASpB,CAAAA,CAAK,CACVH,CAAAA,CAAO,KAAA,CAAM,CAAA,2BAAA,EAA8BqB,CAAI,CAAA,CAAA,CAAIlB,CAAY,EACnE,CAGJ,OAAAH,CAAAA,CAAO,OAAA,CAAQ,CAAA,qBAAA,EAAmBkB,CAAAA,CAAO,MAAM,CAAA,OAAA,CAAS,CAAA,CACjDA,CACX,CAAA,MAASf,EAAK,CACV,OAAAH,CAAAA,CAAO,KAAA,CAAM,gCAAA,CAAkCG,CAAY,CAAA,CACpD,EACX,CACJ,CAeA,eAAeiB,CAAAA,CAAcI,CAAAA,CAAgC,CACzD,IAAML,CAAAA,CAAkB,EAAC,CAEzB,GAAI,CACA,IAAMM,CAAAA,CAAU,MAAM,KAAA,CAAM,SAAA,CACxB,IAAI,GAAA,CAAI,IAAA,CAAK,cAAc,CAAA,CAAE,IAAA,CAAKD,CAAG,CACzC,CAAA,CAEA,IAAA,IAAWE,CAAAA,IAASD,CAAAA,CAChBN,CAAAA,CAAM,IAAA,CAAK,CAAA,EAAGK,CAAG,CAAA,CAAA,EAAIE,CAAK,CAAA,CAAE,EAEpC,CAAA,KAAQ,CAER,CAEA,OAAOP,CACX,CAkDO,SAASQ,CAAAA,CACZC,CAAAA,CACAC,CAAAA,CACW,CAEX,IAAM9B,CAAAA,CAAoB8B,CAAAA,EAAO,QAAA,CAC3BA,CAAAA,CAAM,QAAA,CAASD,CAAU,CAAA,CACzBA,EAEA5B,CAAAA,CAAS,IAAI8B,MAAAA,CAAO/B,CAAAA,CAAO,KAAK,CAAA,CAChCgC,CAAAA,CAAiB,IAAIC,cAAAA,CAAehC,CAAM,CAAA,CAC1CiC,CAAAA,CAAiB,IAAIC,cAAAA,CAAelC,CAAM,CAAA,CAE1CM,CAAAA,CAAY,IAAI,GAAA,CAChB6B,CAAAA,CAAc,IAAI,GAAA,CACpBC,CAAAA,CAAsB,IAAA,CACtBC,CAAAA,CAAmB,IAAA,CAEjBC,CAAAA,CAAwB,CAC1B,MAAA,CAAAvC,CAAAA,CACA,SAAA,CAAAO,CAAAA,CACA,QAAS,EACb,CAAA,CAGMiC,CAAAA,CAA0B,CAC5B,MAAA,CAAAxC,CAAAA,CACA,MAAA,CAAQ,IAAA,CACR,SAAA,CAAAO,CAAAA,CACA,OAAA,CAAS,EAAC,CACV,WAAA,CAAA6B,CAAAA,CACA,KAAA,CAAO,SAAY,CAAE,CAAA,CACrB,IAAA,CAAM,SAAY,CAAE,CAAA,CACpB,OAAA,CAAS,SAAY,CAAE,CAAA,CACvB,UAAA,CAAY,IAAMG,CAAAA,CAClB,aAAA,CAAgB7B,CAAAA,EAAiB0B,CAAAA,CAAY,GAAA,CAAI1B,CAAI,CACzD,CAAA,CAMA,eAAe+B,CAAAA,EAAgB,CAC3B,GAAI,CAACzC,CAAAA,CAAO,OAAA,EAAWA,CAAAA,CAAO,OAAA,CAAQ,MAAA,GAAW,CAAA,CAAG,CAChDC,CAAAA,CAAO,IAAA,CAAK,wBAAwB,CAAA,CACpC,MACJ,CAEAA,CAAAA,CAAO,KAAA,CAAM,UAAU,CAAA,CAEvB,IAAA,IAAWyC,CAAAA,IAAU1C,CAAAA,CAAO,OAAA,CACxB,MAAMgC,CAAAA,CAAe,QAAA,CAASU,CAAAA,CAAQF,CAAU,CAAA,CAGpDD,CAAAA,CAAI,OAAA,CAAUP,CAAAA,CAAe,MAAA,EAAO,CAGVA,CAAAA,CAAe,kBAAA,EAAmB,CAC1C,OAAA,CAAQ,CAACW,CAAAA,CAAIjC,CAAAA,GAAS0B,CAAAA,CAAY,GAAA,CAAI1B,CAAAA,CAAMiC,CAAE,CAAC,EACrE,CAMA,eAAeC,CAAAA,EAAa,CACxB3C,CAAAA,CAAO,KAAA,CAAM,OAAO,CAAA,CAEpB,GAAI,CAEAqC,CAAAA,CAAc,MAAMvC,CAAAA,CAAYC,CAAAA,CAAQC,CAAM,CAAA,CAC9CsC,CAAAA,CAAI,WAAA,CAAcD,CAAAA,CAGlB,IAAMO,CAAAA,CAAgBb,CAAAA,CAAe,cAAA,EAAe,CAG9Cc,CAAAA,CAAM,MAAMzC,CAAAA,CAAeL,CAAAA,CAAQ6C,CAAAA,CAAe5C,CAAM,CAAA,CAC9DM,CAAAA,CAAU,KAAA,EAAM,CAChBuC,CAAAA,CAAI,OAAA,CAAQ,CAACnC,CAAAA,CAAID,CAAAA,GAASH,CAAAA,CAAU,GAAA,CAAIG,CAAAA,CAAMC,CAAE,CAAC,CAAA,CAGjD,MAAMK,CAAAA,CAAUhB,CAAAA,CAAQC,CAAM,CAAA,CAG9B,MAAM+B,CAAAA,CAAe,QAAA,CAAS,SAAA,CAAWO,CAAG,CAAA,CAG5C,MAAMT,CAAAA,EAAO,OAAA,GAAUS,CAAG,EAC9B,CAAA,MAASnC,CAAAA,CAAK,CACV,MAAA,MAAM0B,CAAAA,EAAO,OAAA,GAAUS,CAAAA,CAAK,OAAA,CAASnC,CAAY,CAAA,CAC3CA,CACV,CACJ,CAMA,eAAe2C,GAAa,CACxB9C,CAAAA,CAAO,KAAA,CAAM,OAAO,CAAA,CAEpB,GAAI,CAEA,IAAM+C,CAAAA,CAAgB,MAAM9B,CAAAA,CAAWlB,CAAAA,CAAQC,CAAM,CAAA,CAG/CgD,CAAAA,CAAejB,CAAAA,CAAe,aAAA,EAAc,CAI5CkB,CAAAA,CAAgB,CAAC,GADJlD,CAAAA,CAAO,MAAA,EAAU,EAAC,CACC,GAAGgD,CAAa,CAAA,CAChDG,CAAAA,CAAejB,CAAAA,CAAe,WAAA,CAAYgB,CAAAA,CAAeD,CAAY,CAAA,CAGrEG,CAAAA,CAAapD,CAAAA,CAAO,MAAA,CACpB,KAAA,CAAM,OAAA,CAAQA,CAAAA,CAAO,MAAM,CAAA,CACvBA,CAAAA,CAAO,MAAA,CACP,CAACA,CAAAA,CAAO,MAAM,CAAA,CAClB,EAAC,CACDqD,CAAAA,CAAerB,CAAAA,CAAe,aAAA,EAAc,CAC5CsB,CAAAA,CAAepB,CAAAA,CAAe,WAAA,CAAYkB,CAAAA,CAAYC,CAAY,CAAA,CAExEpD,CAAAA,CAAO,IAAA,CAAK,oBAAoB,CAAA,CAGhC,IAAIsD,EACJ,IAAA,IAAWb,CAAAA,IAAUH,CAAAA,CAAI,OAAA,CAAS,CAC9B,IAAMiB,CAAAA,CAAoBd,CAAAA,CAC1B,GAAIc,CAAAA,CAAkB,iBAAA,CAAmB,CACrCD,CAAAA,CAAeC,CAAAA,CAAkB,iBAAA,CACjC,OAAA,CAAQ,GAAA,CAAI,CAAA,8CAAA,EAA4Cd,CAAAA,CAAO,IAAI,CAAA,CAAE,CAAA,CACrE,KACJ,CACJ,CAGAL,CAAAA,CAAiBoB,MAAAA,CAAa,CAC1B,IAAA,CAAMzD,CAAAA,CAAO,MAAA,EAAQ,IAAA,EAAQ,IAC7B,QAAA,CAAUA,CAAAA,CAAO,MAAA,EAAQ,IAAA,EAAQ,WAAA,CACjC,OAAA,CAASA,CAAAA,CAAO,MAAA,EAAQ,OAAA,CACxB,MAAA,CAAQmD,CAAAA,CACR,MAAA,CAAQG,CAAAA,CAAa,MAAA,CAAS,CAAA,CAAIA,CAAAA,CAAe,KAAA,CAAA,CACjD,QAAA,CAAUtD,CAAAA,CAAO,QAAA,CACjB,WAAA,CAAaA,CAAAA,CAAO,WAAA,CACpB,IAAA,CAAMA,CAAAA,CAAO,IAAA,CACP,CACE,eAAA,CAAiBA,CAAAA,CAAO,IAAA,CAAK,eAAA,CAC7B,kBAAA,CAAoBA,CAAAA,CAAO,IAAA,CAAK,kBACpC,CAAA,CACE,KAAA,CAAA,CACN,OAAA,CAASuD,CACb,CAAC,CAAA,CAGDhD,CAAAA,CAAU,OAAA,CAAQ,CAACI,CAAAA,CAAID,CAAAA,GAAS,CAC5B2B,CAAAA,CAAe,EAAA,CAAG,GAAA,CAAI3B,CAAAA,CAAMC,CAAE,EAClC,CAAC,CAAA,CAED4B,CAAAA,CAAI,MAAA,CAASF,CAAAA,CACbG,CAAAA,CAAW,MAAA,CAASH,CAAAA,CAEpBpC,CAAAA,CAAO,OAAA,CACH,CAAA,sBAAA,EAAoBD,EAAO,MAAA,EAAQ,IAAA,EAAQ,WAAW,CAAA,CAAA,EAAIA,CAAAA,CAAO,MAAA,EAAQ,IAAA,EAAQ,GAAI,CAAA,CACzF,CAAA,CAGA,MAAMgC,CAAAA,CAAe,QAAA,CAAS,SAAA,CAAWO,CAAG,CAAA,CAG5C,MAAMT,CAAAA,EAAO,OAAA,GAAUS,CAAG,EAC9B,CAAA,MAASnC,CAAAA,CAAK,CACV,MAAA,MAAM0B,CAAAA,EAAO,OAAA,GAAUS,CAAAA,CAAK,OAAA,CAASnC,CAAY,CAAA,CAC3CA,CACV,CACJ,CAMA,eAAesD,CAAAA,EAAa,CACxBzD,CAAAA,CAAO,KAAA,CAAM,OAAO,CAAA,CAEpB,GAAI,CACA,MAAMoC,CAAAA,CAAe,KAAA,EAAM,CAE3BpC,CAAAA,CAAO,OAAA,CACH,CAAA,yBAAA,EAA4BD,CAAAA,CAAO,MAAA,EAAQ,IAAA,EAAQ,WAAW,CAAA,CAAA,EAAIA,CAAAA,CAAO,MAAA,EAAQ,IAAA,EAAQ,GAAI,CAAA,CACjG,CAAA,CAGA,MAAMgC,CAAAA,CAAe,QAAA,CAAS,SAAA,CAAWO,CAAG,CAAA,CAG5C,MAAMT,CAAAA,EAAO,OAAA,GAAUS,CAAG,EAC9B,CAAA,MAASnC,CAAAA,CAAK,CACV,MAAA,MAAM0B,CAAAA,EAAO,OAAA,GAAUS,CAAAA,CAAK,OAAA,CAASnC,CAAY,CAAA,CAC3CA,CACV,CACJ,CAMA,OAAO,CACH,MAAA,CAAAJ,CAAAA,CACA,MAAA,CAAQqC,CAAAA,CACR,SAAA,CAAA9B,CAAAA,CACA,OAAA,CAASgC,CAAAA,CAAI,OAAA,CACb,WAAA,CAAAH,CAAAA,CAEA,MAAM,KAAA,EAAQ,CACV,MAAMK,CAAAA,EAAc,CACpB,MAAMG,CAAAA,EAAW,CACjB,MAAMG,CAAAA,EAAW,CACjB,MAAMW,CAAAA,GACV,CAAA,CAEA,MAAM,IAAA,EAAO,CACTzD,CAAAA,CAAO,IAAA,CAAK,oBAAoB,CAAA,CAEhC,GAAI,CACIoC,CAAAA,EACA,MAAMA,CAAAA,CAAe,IAAA,EAAK,CAG9B9B,CAAAA,CAAU,OAAA,CAAQ,CAACI,EAAID,CAAAA,GAAS,CAC5BC,CAAAA,CAAG,KAAA,EAAM,CACTV,CAAAA,CAAO,IAAA,CAAK,CAAA,UAAA,EAAaS,CAAI,CAAA,QAAA,CAAU,EAC3C,CAAC,CAAA,CAGD,MAAMsB,CAAAA,CAAe,QAAA,CAAS,YAAA,CAAcO,CAAG,CAAA,CAE/C,MAAMT,CAAAA,EAAO,QAAA,GAAWS,CAAG,CAAA,CAE3BtC,CAAAA,CAAO,OAAA,CAAQ,gBAAgB,EACnC,CAAA,MAASG,CAAAA,CAAK,CACV,MAAAH,EAAO,KAAA,CAAM,uBAAA,CAAyBG,CAAY,CAAA,CAC5CA,CACV,CACJ,CAAA,CAEA,MAAM,OAAA,EAAU,CACZ,MAAM,IAAA,CAAK,IAAA,EAAK,CAChB,MAAM,IAAA,CAAK,KAAA,GACf,CAAA,CAEA,UAAA,EAAa,CACT,OAAOmC,CACX,CAAA,CAEA,aAAA,CAAc7B,CAAAA,CAAc,CACxB,OAAO0B,CAAAA,CAAY,GAAA,CAAI1B,CAAI,CAC/B,CACJ,CACJ","file":"index.js","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { AppConfig, LifecycleContext, LifecycleHooks, AppInstance, RouteDefinition, AppMiddleware, Logger, PluginRegistry, ResourceMerger } from '@cruxjs/base';\r\n import { server as createServer } from '@minejs/server';\r\n import { DB } from '@minejs/db';\r\n import { setupAuto } from '@minejs/i18n';\r\n import type { TableSchema } from '@minejs/db';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n /**\r\n * Builds the client bundle using Bun's bundler\r\n * \r\n * @param {AppConfig} config - The application configuration containing client build settings\r\n * @param {Logger} logger - Logger instance for logging build progress and errors\r\n * @returns {Promise<{success: boolean, outputs: string[]} | null>} Build result with output paths, or null if no client config\r\n * @throws {Error} If the build process fails\r\n * \r\n * @example\r\n * const result = await buildClient(config, logger);\r\n * if (result?.success) {\r\n * console.log('Built to:', result.outputs);\r\n * }\r\n */\r\n async function buildClient(config: AppConfig, logger: Logger) {\r\n if (!config.client) return null;\r\n\r\n logger.info('Building client...');\r\n\r\n try {\r\n const result = await Bun.build({\r\n entrypoints: [config.client.entry],\r\n outdir: config.client.output,\r\n target: config.client.target || 'browser',\r\n minify: config.client.minify ?? !config.debug,\r\n sourcemap: config.client.sourcemap ?? config.debug ? 'inline' : 'none',\r\n external: config.client.external || []\r\n });\r\n\r\n if (!result.success) throw new Error('Build failed');\r\n\r\n const outputs = result.outputs.map(o => o.path);\r\n logger.success(`Client built → ${outputs.join(', ')}`);\r\n\r\n return { success: true, outputs };\r\n } catch (err) {\r\n logger.error('Failed to build client', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Initializes and configures database connections\r\n * \r\n * Handles:\r\n * - Multiple database instances (primary, cache, etc.)\r\n * - User-defined schemas from config\r\n * - Plugin schemas from plugins\r\n * - In-memory databases for plugin-only scenarios\r\n * \r\n * @param {AppConfig} config - Application configuration with database settings\r\n * @param {TableSchema[]} additionalSchemas - Database schemas provided by plugins\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<Map<string, DB>>} Map of database instances by name\r\n * @throws {Error} If schema loading or database initialization fails\r\n * \r\n * @example\r\n * const databases = await setupDatabases(config, pluginSchemas, logger);\r\n * const db = databases.get('primary');\r\n * const result = db.query('SELECT * FROM users');\r\n */\r\n async function setupDatabases(\r\n config: AppConfig,\r\n additionalSchemas: TableSchema[],\r\n logger: Logger\r\n ) {\r\n const databases = new Map<string, DB>();\r\n\r\n if (!config.database && additionalSchemas.length === 0) {\r\n logger.info('No database config, skipping setup');\r\n return databases;\r\n }\r\n\r\n const dbConfigs = config.database\r\n ? Array.isArray(config.database)\r\n ? config.database\r\n : [config.database]\r\n : [];\r\n\r\n logger.info('Setting up databases...');\r\n\r\n for (const dbConfig of dbConfigs) {\r\n const name = dbConfig.name || 'default';\r\n const db = new DB(dbConfig.connection);\r\n\r\n // Load user schema\r\n if (dbConfig.schema) {\r\n try {\r\n const schemaModule = await import(dbConfig.schema);\r\n const schema = schemaModule.default || schemaModule.schema;\r\n\r\n if (Array.isArray(schema)) {\r\n for (const table of schema) {\r\n db.defineSchema(table);\r\n }\r\n }\r\n } catch (err) {\r\n logger.error(`Failed to load schema: ${dbConfig.schema}`, err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // Load plugin schemas for this database\r\n for (const schema of additionalSchemas) {\r\n db.defineSchema(schema);\r\n }\r\n\r\n databases.set(name, db);\r\n logger.success(`Database '${name}' ready`);\r\n }\r\n\r\n // If no user database but plugins need one, create default\r\n if (databases.size === 0 && additionalSchemas.length > 0) {\r\n const db = new DB(':memory:');\r\n\r\n for (const schema of additionalSchemas) {\r\n db.defineSchema(schema);\r\n }\r\n\r\n databases.set('default', db);\r\n logger.success(`Database 'default' ready (in-memory for plugins)`);\r\n }\r\n\r\n return databases;\r\n }\r\n\r\n /**\r\n * Initializes internationalization (i18n) support\r\n * \r\n * Loads language files and configures the i18n system based on:\r\n * - Default language\r\n * - Supported languages\r\n * - Base path for translation files\r\n * - File extension (json, cjson, etc.)\r\n * \r\n * @param {AppConfig} config - Application configuration with i18n settings\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<void>}\r\n * @throws {Error} If i18n setup or language file loading fails\r\n * \r\n * @example\r\n * await setupI18n({\r\n * i18n: {\r\n * defaultLanguage: 'en',\r\n * supportedLanguages: ['en', 'ar'],\r\n * basePath: './src/i18n'\r\n * }\r\n * }, logger);\r\n */\r\n async function setupI18n(config: AppConfig, logger: Logger) {\r\n if (!config.i18n) return;\r\n\r\n logger.info('Setting up i18n...');\r\n\r\n try {\r\n await setupAuto({\r\n defaultLanguage: config.i18n.defaultLanguage,\r\n supportedLanguages: config.i18n.supportedLanguages,\r\n basePath: config.i18n.basePath,\r\n fileExtension: config.i18n.fileExtension || 'json'\r\n });\r\n\r\n logger.success(`i18n ready → ${config.i18n.supportedLanguages.join(', ')}`);\r\n } catch (err) {\r\n logger.error('Failed to setup i18n', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Dynamically loads route definitions from API directory\r\n * \r\n * Scans the specified directory for route files and imports them.\r\n * Each route file should export either:\r\n * - `routes` property with RouteDefinition array\r\n * - Default export with RouteDefinition array\r\n * \r\n * @param {AppConfig} config - Application configuration with api.directory\r\n * @param {Logger} logger - Logger instance for logging scan progress\r\n * @returns {Promise<RouteDefinition[]>} Array of loaded route definitions\r\n * \r\n * @example\r\n * const routes = await loadRoutes({\r\n * api: { directory: './src/server/api' }\r\n * }, logger);\r\n * console.log(`Loaded ${routes.length} routes`);\r\n */\r\n async function loadRoutes(config: AppConfig, logger: Logger): Promise<RouteDefinition[]> {\r\n if (!config.api) return [];\r\n\r\n logger.info(`Loading routes from ${config.api.directory}...`);\r\n\r\n const routes: RouteDefinition[] = [];\r\n\r\n try {\r\n const files = await scanDirectory(config.api.directory);\r\n\r\n for (const file of files) {\r\n try {\r\n const module = await import(file);\r\n const exported = module.default || module.routes;\r\n\r\n if (Array.isArray(exported)) {\r\n routes.push(...exported);\r\n }\r\n } catch (err) {\r\n logger.error(`Failed to load routes from ${file}`, err as Error);\r\n }\r\n }\r\n\r\n logger.success(`Routes loaded → ${routes.length} routes`);\r\n return routes;\r\n } catch (err) {\r\n logger.error('Failed to scan route directory', err as Error);\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Scans a directory for TypeScript/JavaScript files\r\n * \r\n * Uses Bun's Glob API to recursively find all .ts and .js files in a directory.\r\n * Gracefully handles non-existent directories (returns empty array).\r\n * \r\n * @param {string} dir - Directory path to scan\r\n * @returns {Promise<string[]>} Array of absolute file paths found\r\n * \r\n * @example\r\n * const files = await scanDirectory('./src/server/api');\r\n * // Returns: ['./src/server/api/users.ts', './src/server/api/posts.ts']\r\n */\r\n async function scanDirectory(dir: string): Promise<string[]> {\r\n const files: string[] = [];\r\n\r\n try {\r\n const entries = await Array.fromAsync(\r\n new Bun.Glob('**/*.{ts,js}').scan(dir)\r\n );\r\n\r\n for (const entry of entries) {\r\n files.push(`${dir}/${entry}`);\r\n }\r\n } catch {\r\n // Directory doesn't exist\r\n }\r\n\r\n return files;\r\n }\r\n\r\n /**\r\n * Creates a CruxJS application instance with full lifecycle management\r\n * \r\n * This is the main entry point for building a CruxJS application. It:\r\n * 1. Registers plugins (Phase 0)\r\n * 2. Builds client, initializes databases, setups i18n (Phase 1: AWAKE)\r\n * 3. Creates server, merges routes and middleware (Phase 2: START)\r\n * 4. Starts the server and enables request handling (Phase 3: READY)\r\n * \r\n * Phases execute sequentially when `app.start()` is called.\r\n * \r\n * @param {AppConfig} userConfig - Application configuration object\r\n * @param {LifecycleHooks} [hooks] - Optional lifecycle event handlers\r\n * @returns {AppInstance} Application instance with control methods\r\n * \r\n * @throws {Error} Will throw if any lifecycle phase fails (unless caught in onError hook)\r\n * \r\n * @example\r\n * // Basic usage\r\n * const app = createApp({\r\n * debug: true,\r\n * server: { port: 3000 },\r\n * api: { directory: './src/api' },\r\n * plugins: [spaPlugin]\r\n * });\r\n * await app.start();\r\n * \r\n * @example\r\n * // With lifecycle hooks\r\n * const app = createApp(config, {\r\n * onAwake: async (ctx) => {\r\n * console.log('⏰ App awoken, databases ready');\r\n * },\r\n * onReady: async (ctx) => {\r\n * console.log('✅ Server ready:', ctx.server.getURL());\r\n * },\r\n * onError: async (ctx, phase, error) => {\r\n * console.error(`Error in ${phase}:`, error.message);\r\n * }\r\n * });\r\n * \r\n * @example\r\n * // With cleanup\r\n * const app = createApp(config);\r\n * await app.start();\r\n * // ... server is running\r\n * await app.stop(); // Cleanup\r\n */\r\n export function createApp(\r\n userConfig: AppConfig,\r\n hooks?: LifecycleHooks\r\n ): AppInstance {\r\n // Apply config hook\r\n const config: AppConfig = hooks?.onConfig\r\n ? hooks.onConfig(userConfig) as AppConfig\r\n : userConfig;\r\n\r\n const logger = new Logger(config.debug);\r\n const pluginRegistry = new PluginRegistry(logger);\r\n const resourceMerger = new ResourceMerger(logger);\r\n\r\n const databases = new Map<string, DB>();\r\n const middlewares = new Map<string, AppMiddleware>();\r\n let serverInstance: any = null;\r\n let clientBuild: any = null;\r\n\r\n const ctx: LifecycleContext = {\r\n config,\r\n databases,\r\n plugins: []\r\n };\r\n\r\n // Create partial app instance for plugin registration\r\n const partialApp: AppInstance = {\r\n config,\r\n server: null,\r\n databases,\r\n plugins: [],\r\n middlewares,\r\n start: async () => { },\r\n stop: async () => { },\r\n restart: async () => { },\r\n getContext: () => ctx,\r\n getMiddleware: (name: string) => middlewares.get(name)\r\n };\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 0: Plugin Registration\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseRegister() {\r\n if (!config.plugins || config.plugins.length === 0) {\r\n logger.info('No plugins to register');\r\n return;\r\n }\r\n\r\n logger.phase('REGISTER');\r\n\r\n for (const plugin of config.plugins) {\r\n await pluginRegistry.register(plugin, partialApp);\r\n }\r\n\r\n ctx.plugins = pluginRegistry.getAll();\r\n\r\n // Collect plugin middlewares\r\n const pluginMiddlewares = pluginRegistry.collectMiddlewares();\r\n pluginMiddlewares.forEach((mw, name) => middlewares.set(name, mw));\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 1: AWAKE\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseAwake() {\r\n logger.phase('AWAKE');\r\n\r\n try {\r\n // Build client\r\n clientBuild = await buildClient(config, logger);\r\n ctx.clientBuild = clientBuild;\r\n\r\n // Collect plugin schemas\r\n const pluginSchemas = pluginRegistry.collectSchemas();\r\n\r\n // Setup databases\r\n const dbs = await setupDatabases(config, pluginSchemas, logger);\r\n databases.clear();\r\n dbs.forEach((db, name) => databases.set(name, db));\r\n\r\n // Setup i18n\r\n await setupI18n(config, logger);\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onAwake', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onAwake?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'AWAKE', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 2: START\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseStart() {\r\n logger.phase('START');\r\n\r\n try {\r\n // Load user routes\r\n const userApiRoutes = await loadRoutes(config, logger);\r\n\r\n // Collect plugin routes\r\n const pluginRoutes = pluginRegistry.collectRoutes();\r\n\r\n // Merge user routes + plugin routes\r\n const userRoutes = config.routes || [];\r\n const allUserRoutes = [...userRoutes, ...userApiRoutes];\r\n const mergedRoutes = resourceMerger.mergeRoutes(allUserRoutes, pluginRoutes);\r\n\r\n // Collect static configs\r\n const userStatic = config.static\r\n ? Array.isArray(config.static)\r\n ? config.static\r\n : [config.static]\r\n : [];\r\n const pluginStatic = pluginRegistry.collectStatic();\r\n const mergedStatic = resourceMerger.mergeStatic(userStatic, pluginStatic);\r\n\r\n logger.info('Creating server...');\r\n\r\n // Collect error handlers from plugins (especially SPA plugin)\r\n let errorHandler: ((statusCode: number, path: string) => Response) | undefined;\r\n for (const plugin of ctx.plugins) {\r\n const pluginWithHandler = plugin as any;\r\n if (pluginWithHandler.__spaErrorHandler) {\r\n errorHandler = pluginWithHandler.__spaErrorHandler;\r\n console.log(`[CruxJS] ✓ Error handler collected from: ${plugin.name}`);\r\n break; // Use first error handler found\r\n }\r\n }\r\n\r\n // Create server\r\n serverInstance = createServer({\r\n port: config.server?.port || 3000,\r\n hostname: config.server?.host || 'localhost',\r\n logging: config.server?.logging,\r\n routes: mergedRoutes,\r\n static: mergedStatic.length > 0 ? mergedStatic : undefined,\r\n security: config.security,\r\n middlewares: config.middlewares,\r\n i18n: config.i18n\r\n ? {\r\n defaultLanguage: config.i18n.defaultLanguage,\r\n supportedLanguages: config.i18n.supportedLanguages\r\n }\r\n : undefined,\r\n onError: errorHandler\r\n });\r\n\r\n // Inject databases\r\n databases.forEach((db, name) => {\r\n serverInstance.db.set(name, db);\r\n });\r\n\r\n ctx.server = serverInstance;\r\n partialApp.server = serverInstance;\r\n\r\n logger.success(\r\n `Server created → ${config.server?.host || 'localhost'}:${config.server?.port || 3000}`\r\n );\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onStart', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onStart?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'START', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 3: READY\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseReady() {\r\n logger.phase('READY');\r\n\r\n try {\r\n await serverInstance.start();\r\n\r\n logger.success(\r\n `Server running on http://${config.server?.host || 'localhost'}:${config.server?.port || 3000}`\r\n );\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onReady', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onReady?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'READY', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // App Instance\r\n // ─────────────────────────────────────────────────────────\r\n\r\n return {\r\n config,\r\n server: serverInstance,\r\n databases,\r\n plugins: ctx.plugins,\r\n middlewares,\r\n\r\n async start() {\r\n await phaseRegister();\r\n await phaseAwake();\r\n await phaseStart();\r\n await phaseReady();\r\n },\r\n\r\n async stop() {\r\n logger.info('Stopping server...');\r\n\r\n try {\r\n if (serverInstance) {\r\n await serverInstance.stop();\r\n }\r\n\r\n databases.forEach((db, name) => {\r\n db.close();\r\n logger.info(`Database '${name}' closed`);\r\n });\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onShutdown', ctx);\r\n\r\n await hooks?.onFinish?.(ctx);\r\n\r\n logger.success('Server stopped');\r\n } catch (err) {\r\n logger.error('Failed to stop server', err as Error);\r\n throw err;\r\n }\r\n },\r\n\r\n async restart() {\r\n await this.stop();\r\n await this.start();\r\n },\r\n\r\n getContext() {\r\n return ctx;\r\n },\r\n\r\n getMiddleware(name: string) {\r\n return middlewares.get(name);\r\n }\r\n };\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ ════ ════════════════════════════════════════╗\r\n\r\n export * from '@cruxjs/base';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝"]}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@cruxjs/app",
3
+ "version": "0.0.1",
4
+ "description": "Full-stack framework orchestrator for building modern web applications. Zero configuration. Plugin-based architecture.",
5
+ "keywords": ["cruxjs", "app"],
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/cruxjs-org/app#readme",
8
+ "bugs": {
9
+ "url": "https://github.com/cruxjs-org/app/issues"
10
+ },
11
+ "author": {
12
+ "name": "Maysara",
13
+ "email": "maysara.elshewehy@gmail.com",
14
+ "url": "https://github.com/maysara-elshewehy"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/cruxjs-org/app.git"
19
+ },
20
+ "type": "module",
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "files": ["dist"],
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js",
28
+ "require": "./dist/index.js"
29
+ }
30
+ },
31
+ "scripts": {
32
+ "build": "tsup",
33
+ "lint": "eslint src --ext .ts",
34
+ "test": "bun test"
35
+ },
36
+ "engines": {
37
+ "bun": ">=1.3.3"
38
+ },
39
+ "peerDependencies": {
40
+ "bun": "^1.3.3"
41
+ },
42
+ "dependencies": {
43
+ "@cruxjs/base": "^0.0.3",
44
+ "@minejs/db": "^0.0.3",
45
+ "@minejs/i18n": "^0.0.3",
46
+ "@minejs/server": "^0.0.5"
47
+ },
48
+ "devDependencies": {
49
+ "@eslint/js": "^9.39.2",
50
+ "@minejsx/runtime": "^0.0.5",
51
+ "@stylistic/eslint-plugin": "^5.6.1",
52
+ "@types/bun": "^1.3.5",
53
+ "@types/node": "^20.19.27",
54
+ "bun-plugin-dts": "^0.3.0",
55
+ "bun-types": "^1.3.5",
56
+ "ts-node": "^10.9.2",
57
+ "tsup": "^8.5.1",
58
+ "typescript": "^5.9.3",
59
+ "typescript-eslint": "^8.52.0"
60
+ }
61
+ }