clovie 0.1.29 → 0.1.32

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.
Files changed (32) hide show
  1. package/README.md +32 -0
  2. package/dist/{ExpressAdapter-DL0Y9BG2.js → ExpressAdapter-BsYIpF7A.js} +14 -5
  3. package/dist/ExpressAdapter-BsYIpF7A.js.map +1 -0
  4. package/dist/{LiveReload-CXR08AFg.js → LiveReload-ClrG9hAe.js} +8 -2
  5. package/dist/{LiveReload-CXR08AFg.js.map → LiveReload-ClrG9hAe.js.map} +1 -1
  6. package/dist/{Server-BR7ZqQBN.js → Server-GcEIC8Hj.js} +28 -5
  7. package/dist/Server-GcEIC8Hj.js.map +1 -0
  8. package/dist/cjs/{ExpressAdapter-De7bTuZu.cjs → ExpressAdapter--oUcRq-k.cjs} +11 -5
  9. package/dist/cjs/ExpressAdapter--oUcRq-k.cjs.map +1 -0
  10. package/dist/cjs/{LiveReload-TdgPIiCn.cjs → LiveReload-BvFz-bG3.cjs} +4 -3
  11. package/dist/cjs/{LiveReload-TdgPIiCn.cjs.map → LiveReload-BvFz-bG3.cjs.map} +1 -1
  12. package/dist/cjs/{Server-BVelCzau.cjs → Server-DkYaMJak.cjs} +28 -5
  13. package/dist/cjs/Server-DkYaMJak.cjs.map +1 -0
  14. package/dist/cjs/{createClovie-DdMTZ_dO.cjs → createClovie-CJ1h3Pfo.cjs} +60 -10
  15. package/dist/cjs/createClovie-CJ1h3Pfo.cjs.map +1 -0
  16. package/dist/cjs/{index-9MtAg70f.cjs → index-DU3_mYKB.cjs} +4 -4
  17. package/dist/cjs/{index-9MtAg70f.cjs.map → index-DU3_mYKB.cjs.map} +1 -1
  18. package/dist/cjs/index.cjs +3 -2
  19. package/dist/cjs/index.cjs.map +1 -1
  20. package/dist/{createClovie-LSdi1lXJ.js → createClovie-Dh7RbB5L.js} +60 -8
  21. package/dist/createClovie-Dh7RbB5L.js.map +1 -0
  22. package/dist/{index-CpXKm4Ga.js → index-D8ThZ_1b.js} +6 -2
  23. package/dist/{index-CpXKm4Ga.js.map → index-D8ThZ_1b.js.map} +1 -1
  24. package/dist/index.js +7 -1
  25. package/dist/index.js.map +1 -1
  26. package/package.json +1 -1
  27. package/dist/ExpressAdapter-DL0Y9BG2.js.map +0 -1
  28. package/dist/Server-BR7ZqQBN.js.map +0 -1
  29. package/dist/cjs/ExpressAdapter-De7bTuZu.cjs.map +0 -1
  30. package/dist/cjs/Server-BVelCzau.cjs.map +0 -1
  31. package/dist/cjs/createClovie-DdMTZ_dO.cjs.map +0 -1
  32. package/dist/createClovie-LSdi1lXJ.js.map +0 -1
package/README.md CHANGED
@@ -26,6 +26,7 @@ npm run dev
26
26
  - **🔄 Live Reload**: WebSocket-based live reload during development
27
27
  - **🗄️ Database Ready**: SQLite integration for server mode applications
28
28
  - **🛣️ Dynamic Routing**: Data-driven page generation and API endpoints
29
+ - **🧩 App Orchestration**: Build and serve external Vite/Webpack/Rollup/esbuild apps via kernel handlers
29
30
  - **🔧 Service Architecture**: Modular, extensible service-oriented design
30
31
 
31
32
  ## 🏗️ Architecture Overview
@@ -401,6 +402,37 @@ curl http://localhost:3000/api/protected/data
401
402
  curl -H "Authorization: Bearer your-token" http://localhost:3000/api/protected/data
402
403
  ```
403
404
 
405
+ ### 🧩 App Orchestration
406
+
407
+ Clovie can manage multiple frontend apps alongside your core project using kernel-level handlers, so no Express-specific middleware is required. Define apps in `clovie.config.js`, and Clovie will:
408
+
409
+ - Detect the build tool (Vite, Webpack, Rollup, esbuild) or use the specified `buildTool`
410
+ - Run builds during `clovie build` and `clovie serve`
411
+ - Launch tool-specific dev middleware during `clovie dev`
412
+ - Mount bundles or dev servers at configurable mount paths (e.g., `/admin`, `/studio`)
413
+
414
+ ```javascript
415
+ export default {
416
+ type: 'server',
417
+ apps: [
418
+ {
419
+ name: 'studio',
420
+ source: './apps/studio',
421
+ buildTool: 'vite',
422
+ buildOptions: {
423
+ watch: true,
424
+ build: { outDir: './apps/studio/dist' }
425
+ },
426
+ dev: {
427
+ mountPath: '/studio'
428
+ }
429
+ }
430
+ ]
431
+ };
432
+ ```
433
+
434
+ See [Apps Integration](docs/CONFIGURATION.md#apps-integration) for full examples covering Webpack, Rollup, and esbuild setups.
435
+
404
436
  ### 📊 Database Integration (Server Mode)
405
437
 
406
438
  Clovie includes built-in SQLite database support for server applications:
@@ -1,6 +1,6 @@
1
- import { A as AdapterInterface } from "./Server-BR7ZqQBN.js";
1
+ import { A as AdapterInterface } from "./Server-GcEIC8Hj.js";
2
2
 
3
- import "./createClovie-LSdi1lXJ.js";
3
+ import "./createClovie-Dh7RbB5L.js";
4
4
 
5
5
  import "stream";
6
6
 
@@ -26,10 +26,14 @@ import "node:os";
26
26
 
27
27
  import "node:tty";
28
28
 
29
- import "node:http";
29
+ import "fs/promises";
30
30
 
31
31
  import "child_process";
32
32
 
33
+ import "url";
34
+
35
+ import "node:http";
36
+
33
37
  import "util";
34
38
 
35
39
  class ExpressAdapter extends AdapterInterface {
@@ -54,10 +58,15 @@ class ExpressAdapter extends AdapterInterface {
54
58
  throw new Error(`Failed to apply middleware[${index}]: ${error.message}`);
55
59
  }
56
60
  }
57
- this.app.all("*", async (req, res) => {
61
+ }
62
+ registerKernelHandler() {
63
+ this.app && this.app.all("*", async (req, res) => {
58
64
  await this.handleRequest(req, res);
59
65
  });
60
66
  }
67
+ getExpressApp() {
68
+ return this.app;
69
+ }
61
70
  async start({port: port = 3e3, host: host = "0.0.0.0"} = {}) {
62
71
  return new Promise((resolve, reject) => {
63
72
  this.server = this.app.listen(port, host, err => {
@@ -97,4 +106,4 @@ class ExpressAdapter extends AdapterInterface {
97
106
  }
98
107
 
99
108
  export { ExpressAdapter };
100
- //# sourceMappingURL=ExpressAdapter-DL0Y9BG2.js.map
109
+ //# sourceMappingURL=ExpressAdapter-BsYIpF7A.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpressAdapter-BsYIpF7A.js","sources":["../lib/Server/adapters/ExpressAdapter.js"],"sourcesContent":["/**\n * Express adapter for Clovie with full middleware support\n */\nimport { AdapterInterface } from './AdapterInterface.js';\n\nexport class ExpressAdapter extends AdapterInterface {\n #connections = new Set();\n\n constructor(express = null) {\n super('express');\n this.express = express;\n this.app = null;\n }\n\n async initialize(kernel, opts = {}, log = null) {\n await super.initialize(kernel, opts, log);\n \n // Import Express\n if (!this.express) {\n try {\n this.express = await import('express');\n this.express = this.express.default;\n } catch (error) {\n throw new Error('Express not installed. Run: npm install express');\n }\n }\n\n // Create Express app\n this.app = this.express();\n\n // Apply default middleware (can be overridden by user middleware)\n if (!opts.middleware || !this.#hasBodyParser(opts.middleware)) {\n this.app.use(this.express.json());\n this.app.use(this.express.urlencoded({ extended: true }));\n }\n\n // Apply user-defined middleware\n if (opts.middleware && Array.isArray(opts.middleware)) {\n this.log?.info(`Applying ${opts.middleware.length} middleware functions`);\n \n for (const [index, middleware] of opts.middleware.entries()) {\n try {\n this.app.use(middleware);\n this.log?.debug(`Applied middleware ${index + 1}/${opts.middleware.length}`);\n } catch (error) {\n throw new Error(`Failed to apply middleware[${index}]: ${error.message}`);\n }\n }\n }\n\n // Additional middleware will be mounted by the server before kernel handler registration\n }\n\n registerKernelHandler() {\n if (!this.app) {\n return;\n }\n\n this.app.all('*', async (req, res) => {\n await this.handleRequest(req, res);\n });\n }\n\n getExpressApp() {\n return this.app;\n }\n\n async start({ port = 3000, host = '0.0.0.0' } = {}) {\n return new Promise((resolve, reject) => {\n this.server = this.app.listen(port, host, (err) => {\n if (err) {\n reject(err);\n } else {\n // Track connections for clean shutdown\n this.server.on('connection', (conn) => {\n this.#connections.add(conn);\n conn.on('close', () => {\n this.#connections.delete(conn);\n });\n });\n\n this.log?.info(`Express Server with middleware listening on http://${host}:${port}`);\n resolve(this.server);\n }\n });\n });\n }\n\n async stop() {\n if (this.server?.close) {\n // Destroy all active connections\n for (const conn of this.#connections) {\n conn.destroy();\n }\n this.#connections.clear();\n\n return new Promise((resolve) => {\n this.server.close(() => {\n this.log?.info('Express Server stopped');\n resolve();\n });\n });\n }\n }\n\n async handleRequest(req, res) {\n try {\n // Create context from Express request\n const ctx = this.createContext(req, res);\n \n // Delegate to kernel for route matching and handling\n const response = await this.kernel.handle(ctx);\n \n // Send response using shared method\n await this.sendResponse(res, response, req.method === 'HEAD');\n\n } catch (error) {\n await this.handleError(res, error);\n }\n }\n\n /**\n * Check if middleware array contains body parser\n * @private\n */\n #hasBodyParser(middlewareArray) {\n return middlewareArray.some(mw => {\n // Check for common body parser middleware names\n const name = mw.name?.toLowerCase() || mw.toString().toLowerCase();\n return name.includes('json') || name.includes('urlencoded') || name.includes('bodyparser');\n });\n }\n}\n"],"names":["ExpressAdapter","AdapterInterface","connections","Set","constructor","express","super","this","app","initialize","kernel","opts","log","import","default","error","Error","middleware","hasBodyParser","use","json","urlencoded","extended","Array","isArray","info","length","index","entries","debug","message","registerKernelHandler","all","async","req","res","handleRequest","getExpressApp","start","port","host","Promise","resolve","reject","server","listen","err","on","conn","add","delete","stop","close","destroy","clear","ctx","createContext","response","handle","sendResponse","method","handleError","middlewareArray","some","mw","name","toLowerCase","toString","includes"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKO,MAAMA,uBAAuBC;IAClCC,aAAe,IAAIC;IAEnB,WAAAC,CAAYC,UAAU;QACpBC,MAAM,YACNC,KAAKF,UAAUA,SACfE,KAAKC,MAAM;AACb;IAEA,gBAAMC,CAAWC,QAAQC,OAAO,CAAA,GAAIC,MAAM;QAIxC,UAHMN,MAAMG,WAAWC,QAAQC,MAAMC,OAGhCL,KAAKF,SACR;YACEE,KAAKF,gBAAgBQ,OAAO,YAC5BN,KAAKF,UAAUE,KAAKF,QAAQS;AAC9B,UAAE,OAAOC;YACP,MAAM,IAAIC,MAAM;AAClB;QAaF,IATAT,KAAKC,MAAMD,KAAKF,WAGXM,KAAKM,cAAeV,MAAKW,cAAeP,KAAKM,gBAChDV,KAAKC,IAAIW,IAAIZ,KAAKF,QAAQe;QAC1Bb,KAAKC,IAAIW,IAAIZ,KAAKF,QAAQgB,WAAW;YAAEC,WAAU;cAI/CX,KAAKM,cAAcM,MAAMC,QAAQb,KAAKM,aAAa;YACrDV,KAAKK,KAAKa,KAAK,YAAYd,KAAKM,WAAWS;YAE3C,KAAK,OAAOC,OAAOV,eAAeN,KAAKM,WAAWW,WAChD;gBACErB,KAAKC,IAAIW,IAAIF,aACbV,KAAKK,KAAKiB,MAAM,sBAAsBF,QAAQ,KAAKhB,KAAKM,WAAWS;AACrE,cAAE,OAAOX;gBACP,MAAM,IAAIC,MAAM,8BAA8BW,WAAWZ,MAAMe;AACjE;AAEJ;AAGF;IAEA,qBAAAC;QACOxB,KAAKC,OAIVD,KAAKC,IAAIwB,IAAI,KAAKC,OAAOC,KAAKC;kBACtB5B,KAAK6B,cAAcF,KAAKC;;AAElC;IAEA,aAAAE;QACE,OAAO9B,KAAKC;AACd;IAEA,WAAM8B,EAAMC,MAAEA,OAAO,KAAIC,MAAEA,OAAO,aAAc;QAC9C,OAAO,IAAIC,QAAQ,CAACC,SAASC;YAC3BpC,KAAKqC,SAASrC,KAAKC,IAAIqC,OAAON,MAAMC,MAAOM;gBACrCA,MACFH,OAAOG,QAGPvC,KAAKqC,OAAOG,GAAG,cAAeC;oBAC5BzC,MAAKL,YAAa+C,IAAID,OACtBA,KAAKD,GAAG,SAAS;wBACfxC,MAAKL,YAAagD,OAAOF;;oBAI7BzC,KAAKK,KAAKa,KAAK,sDAAsDe,QAAQD;gBAC7EG,QAAQnC,KAAKqC;;;AAIrB;IAEA,UAAMO;QACJ,IAAI5C,KAAKqC,QAAQQ,OAAO;YAEtB,KAAK,MAAMJ,QAAQzC,MAAKL,aACtB8C,KAAKK;YAIP,OAFA9C,MAAKL,YAAaoD,SAEX,IAAIb,QAASC;gBAClBnC,KAAKqC,OAAOQ,MAAM;oBAChB7C,KAAKK,KAAKa,KAAK,2BACfiB;;;AAGN;AACF;IAEA,mBAAMN,CAAcF,KAAKC;QACvB;YAEE,MAAMoB,MAAMhD,KAAKiD,cAActB,KAAKC,MAG9BsB,iBAAiBlD,KAAKG,OAAOgD,OAAOH;kBAGpChD,KAAKoD,aAAaxB,KAAKsB,UAAyB,WAAfvB,IAAI0B;AAE7C,UAAE,OAAO7C;kBACDR,KAAKsD,YAAY1B,KAAKpB;AAC9B;AACF;IAMA,cAAAG,CAAe4C;QACb,OAAOA,gBAAgBC,KAAKC;YAE1B,MAAMC,OAAOD,GAAGC,MAAMC,iBAAiBF,GAAGG,WAAWD;YACrD,OAAOD,KAAKG,SAAS,WAAWH,KAAKG,SAAS,iBAAiBH,KAAKG,SAAS;;AAEjF;;;"}
@@ -1,6 +1,6 @@
1
1
  import { Server } from "socket.io";
2
2
 
3
- import { S as ServiceProvider, q as queueMacrotask } from "./createClovie-LSdi1lXJ.js";
3
+ import { S as ServiceProvider, q as queueMacrotask } from "./createClovie-Dh7RbB5L.js";
4
4
 
5
5
  import "stream";
6
6
 
@@ -26,6 +26,12 @@ import "node:os";
26
26
 
27
27
  import "node:tty";
28
28
 
29
+ import "fs/promises";
30
+
31
+ import "child_process";
32
+
33
+ import "url";
34
+
29
35
  class LiveReload extends ServiceProvider {
30
36
  static manifest={
31
37
  name: "Clovie LiveReload",
@@ -92,4 +98,4 @@ class LiveReload extends ServiceProvider {
92
98
  }
93
99
 
94
100
  export { LiveReload };
95
- //# sourceMappingURL=LiveReload-CXR08AFg.js.map
101
+ //# sourceMappingURL=LiveReload-ClrG9hAe.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LiveReload-CXR08AFg.js","sources":["../lib/LiveReload.js"],"sourcesContent":["import { Server as SocketIOServer } from 'socket.io';\nimport { ServiceProvider } from '@brickworks/engine';\nimport { queueMacrotask } from './utils/tasks.js';\n\nexport class LiveReload extends ServiceProvider {\n static manifest = {\n name: 'Clovie LiveReload',\n namespace: 'liveReload',\n version: '1.0.0',\n };\n\n #io;\n #reloading = false;\n\n getter () {\n return {\n io: () => this.#io,\n }\n }\n\n actions() {\n return {\n initializeServer: async (server, opts) => {\n const log = this.useContext('log');\n if (opts.mode === 'development') {\n await this.#setupSocketIO(server, log);\n }\n },\n notifyReload: () => {\n if (this.#reloading) return;\n this.#reloading = true;\n \n queueMacrotask(() => {\n this.#reloading = false;\n if (this.#io) {\n this.#io.emit('reload')\n }\n });\n },\n injectLiveReloadScript: async (renderedContent, opts) => {\n const lastBodyIndex = renderedContent.lastIndexOf('</body>');\n if (lastBodyIndex !== -1) {\n const scriptConfig = { \n mode: opts.mode || 'development', \n port: opts.port || 3000 \n };\n \n try {\n // Import live reload script dynamically \n renderedContent = renderedContent.substring(0, lastBodyIndex) + \n this.#liveReloadScript(scriptConfig) + '\\n' + \n renderedContent.substring(lastBodyIndex);\n } catch (err) {\n console.warn('⚠️ Could not load live reload script:', err.message);\n }\n }\n return renderedContent;\n }\n }\n }\n\n async #setupSocketIO(server, log) {\n try {\n // Wait a bit for server to be fully initialized\n await new Promise(resolve => setTimeout(resolve, 100));\n\n log.debug('Setting up Socket.IO...');\n \n // Configure Socket.IO with proper CORS and options\n this.#io = new SocketIOServer(server, {\n cors: {\n origin: \"*\",\n methods: [\"GET\", \"POST\"]\n },\n transports: ['polling', 'websocket'],\n allowEIO3: true\n });\n\n this.#io.on('connection', (socket) => {\n log.debug(`Client connected: ${socket.id}`);\n \n socket.on('disconnect', (reason) => {\n log.debug(`Client disconnected: ${socket.id} - ${reason}`);\n });\n \n socket.on('error', (error) => {\n log.error('Socket error:', error);\n });\n });\n\n log.info('Socket.IO server ready');\n } catch (error) {\n log.error('Error setting up Socket.IO:', error);\n }\n }\n\n #liveReloadScript = (opts) => `<!-- Live Reload Script (Development Mode Only) -->\n<script src=\"https://cdn.socket.io/4.7.4/socket.io.min.js\"></script>\n<script>\n (function() {\n const opts = ${JSON.stringify(opts)};\n \n console.log('Initializing live reload with opts:', opts);\n \n // Configure Socket.IO client with proper options\n const socket = io({\n transports: ['polling', 'websocket'],\n timeout: 20000,\n forceNew: true\n });\n \n socket.on('reload', () => {\n console.log('Live reload triggered');\n window.location.reload();\n });\n \n socket.on('connect', () => {\n console.log('Connected to live reload server');\n });\n \n socket.on('disconnect', (reason) => {\n console.log('Disconnected from live reload server:', reason);\n });\n \n socket.on('connect_error', (error) => {\n console.error('Connection error:', error);\n console.log('Retrying connection in 3 seconds...');\n setTimeout(() => {\n socket.connect();\n }, 3000);\n });\n \n socket.on('error', (error) => {\n console.error('Socket error:', error);\n });\n \n // Add connection timeout\n setTimeout(() => {\n if (!socket.connected) {\n console.warn('Live reload connection timeout - server may not be running');\n }\n }, 5000);\n })();\n</script>`;\n}"],"names":["LiveReload","ServiceProvider","static","name","namespace","version","io","reloading","getter","this","actions","initializeServer","async","server","opts","log","useContext","mode","setupSocketIO","notifyReload","queueMacrotask","emit","injectLiveReloadScript","renderedContent","lastBodyIndex","lastIndexOf","scriptConfig","port","substring","liveReloadScript","err","console","warn","message","Promise","resolve","setTimeout","debug","SocketIOServer","cors","origin","methods","transports","allowEIO3","on","socket","id","reason","error","info","JSON","stringify"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIO,MAAMA,mBAAmBC;IAC9BC,gBAAkB;QAChBC,MAAM;QACNC,WAAW;QACXC,SAAS;;IAGXC;IACAC,YAAa;IAEb,MAAAC;QACE,OAAO;YACLF,IAAI,MAAMG,MAAKH;;AAEnB;IAEA,OAAAI;QACE,OAAO;YACLC,kBAAkBC,OAAOC,QAAQC;gBAC/B,MAAMC,MAAMN,KAAKO,WAAW;gBACV,kBAAdF,KAAKG,cACDR,MAAKS,cAAeL,QAAQE;;YAGtCI,cAAc;gBACRV,MAAKF,cACTE,MAAKF,aAAa,GAElBa,eAAe;oBACbX,MAAKF,aAAa,GACdE,MAAKH,MACPG,MAAKH,GAAIe,KAAK;;;YAIpBC,wBAAwBV,OAAOW,iBAAiBT;gBAC9C,MAAMU,gBAAgBD,gBAAgBE,YAAY;gBAClD,KAAsB,MAAlBD,eAAsB;oBACxB,MAAME,eAAe;wBACnBT,MAAMH,KAAKG,QAAQ;wBACnBU,MAAMb,KAAKa,QAAQ;;oBAGrB;wBAEEJ,kBAAkBA,gBAAgBK,UAAU,GAAGJ,iBAC/Bf,MAAKoB,iBAAkBH,gBAAgB,OACvCH,gBAAgBK,UAAUJ;AAC5C,sBAAE,OAAOM;wBACPC,QAAQC,KAAK,0CAA0CF,IAAIG;AAC7D;AACF;gBACA,OAAOV;;;AAGb;IAEA,oBAAML,CAAeL,QAAQE;QAC3B;kBAEQ,IAAImB,QAAQC,WAAWC,WAAWD,SAAS,OAEjDpB,IAAIsB,MAAM;YAGV5B,MAAKH,KAAM,IAAIgC,OAAezB,QAAQ;gBACpC0B,MAAM;oBACJC,QAAQ;oBACRC,SAAS,EAAC,OAAO;;gBAEnBC,YAAY,EAAC,WAAW;gBACxBC,YAAW;gBAGblC,MAAKH,GAAIsC,GAAG,cAAeC;gBACzB9B,IAAIsB,MAAM,qBAAqBQ,OAAOC,OAEtCD,OAAOD,GAAG,cAAeG;oBACvBhC,IAAIsB,MAAM,wBAAwBQ,OAAOC,QAAQC;oBAGnDF,OAAOD,GAAG,SAAUI;oBAClBjC,IAAIiC,MAAM,iBAAiBA;;gBAI/BjC,IAAIkC,KAAK;AACX,UAAE,OAAOD;YACPjC,IAAIiC,MAAM,+BAA+BA;AAC3C;AACF;IAEAnB,kBAAqBf,QAAS,iLAIboC,KAAKC,UAAUrC;;;"}
1
+ {"version":3,"file":"LiveReload-ClrG9hAe.js","sources":["../lib/LiveReload.js"],"sourcesContent":["import { Server as SocketIOServer } from 'socket.io';\nimport { ServiceProvider } from '@brickworks/engine';\nimport { queueMacrotask } from './utils/tasks.js';\n\nexport class LiveReload extends ServiceProvider {\n static manifest = {\n name: 'Clovie LiveReload',\n namespace: 'liveReload',\n version: '1.0.0',\n };\n\n #io;\n #reloading = false;\n\n getter () {\n return {\n io: () => this.#io,\n }\n }\n\n actions() {\n return {\n initializeServer: async (server, opts) => {\n const log = this.useContext('log');\n if (opts.mode === 'development') {\n await this.#setupSocketIO(server, log);\n }\n },\n notifyReload: () => {\n if (this.#reloading) return;\n this.#reloading = true;\n \n queueMacrotask(() => {\n this.#reloading = false;\n if (this.#io) {\n this.#io.emit('reload')\n }\n });\n },\n injectLiveReloadScript: async (renderedContent, opts) => {\n const lastBodyIndex = renderedContent.lastIndexOf('</body>');\n if (lastBodyIndex !== -1) {\n const scriptConfig = { \n mode: opts.mode || 'development', \n port: opts.port || 3000 \n };\n \n try {\n // Import live reload script dynamically \n renderedContent = renderedContent.substring(0, lastBodyIndex) + \n this.#liveReloadScript(scriptConfig) + '\\n' + \n renderedContent.substring(lastBodyIndex);\n } catch (err) {\n console.warn('⚠️ Could not load live reload script:', err.message);\n }\n }\n return renderedContent;\n }\n }\n }\n\n async #setupSocketIO(server, log) {\n try {\n // Wait a bit for server to be fully initialized\n await new Promise(resolve => setTimeout(resolve, 100));\n\n log.debug('Setting up Socket.IO...');\n \n // Configure Socket.IO with proper CORS and options\n this.#io = new SocketIOServer(server, {\n cors: {\n origin: \"*\",\n methods: [\"GET\", \"POST\"]\n },\n transports: ['polling', 'websocket'],\n allowEIO3: true\n });\n\n this.#io.on('connection', (socket) => {\n log.debug(`Client connected: ${socket.id}`);\n \n socket.on('disconnect', (reason) => {\n log.debug(`Client disconnected: ${socket.id} - ${reason}`);\n });\n \n socket.on('error', (error) => {\n log.error('Socket error:', error);\n });\n });\n\n log.info('Socket.IO server ready');\n } catch (error) {\n log.error('Error setting up Socket.IO:', error);\n }\n }\n\n #liveReloadScript = (opts) => `<!-- Live Reload Script (Development Mode Only) -->\n<script src=\"https://cdn.socket.io/4.7.4/socket.io.min.js\"></script>\n<script>\n (function() {\n const opts = ${JSON.stringify(opts)};\n \n console.log('Initializing live reload with opts:', opts);\n \n // Configure Socket.IO client with proper options\n const socket = io({\n transports: ['polling', 'websocket'],\n timeout: 20000,\n forceNew: true\n });\n \n socket.on('reload', () => {\n console.log('Live reload triggered');\n window.location.reload();\n });\n \n socket.on('connect', () => {\n console.log('Connected to live reload server');\n });\n \n socket.on('disconnect', (reason) => {\n console.log('Disconnected from live reload server:', reason);\n });\n \n socket.on('connect_error', (error) => {\n console.error('Connection error:', error);\n console.log('Retrying connection in 3 seconds...');\n setTimeout(() => {\n socket.connect();\n }, 3000);\n });\n \n socket.on('error', (error) => {\n console.error('Socket error:', error);\n });\n \n // Add connection timeout\n setTimeout(() => {\n if (!socket.connected) {\n console.warn('Live reload connection timeout - server may not be running');\n }\n }, 5000);\n })();\n</script>`;\n}"],"names":["LiveReload","ServiceProvider","static","name","namespace","version","io","reloading","getter","this","actions","initializeServer","async","server","opts","log","useContext","mode","setupSocketIO","notifyReload","queueMacrotask","emit","injectLiveReloadScript","renderedContent","lastBodyIndex","lastIndexOf","scriptConfig","port","substring","liveReloadScript","err","console","warn","message","Promise","resolve","setTimeout","debug","SocketIOServer","cors","origin","methods","transports","allowEIO3","on","socket","id","reason","error","info","JSON","stringify"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIO,MAAMA,mBAAmBC;IAC9BC,gBAAkB;QAChBC,MAAM;QACNC,WAAW;QACXC,SAAS;;IAGXC;IACAC,YAAa;IAEb,MAAAC;QACE,OAAO;YACLF,IAAI,MAAMG,MAAKH;;AAEnB;IAEA,OAAAI;QACE,OAAO;YACLC,kBAAkBC,OAAOC,QAAQC;gBAC/B,MAAMC,MAAMN,KAAKO,WAAW;gBACV,kBAAdF,KAAKG,cACDR,MAAKS,cAAeL,QAAQE;;YAGtCI,cAAc;gBACRV,MAAKF,cACTE,MAAKF,aAAa,GAElBa,eAAe;oBACbX,MAAKF,aAAa,GACdE,MAAKH,MACPG,MAAKH,GAAIe,KAAK;;;YAIpBC,wBAAwBV,OAAOW,iBAAiBT;gBAC9C,MAAMU,gBAAgBD,gBAAgBE,YAAY;gBAClD,KAAsB,MAAlBD,eAAsB;oBACxB,MAAME,eAAe;wBACnBT,MAAMH,KAAKG,QAAQ;wBACnBU,MAAMb,KAAKa,QAAQ;;oBAGrB;wBAEEJ,kBAAkBA,gBAAgBK,UAAU,GAAGJ,iBAC/Bf,MAAKoB,iBAAkBH,gBAAgB,OACvCH,gBAAgBK,UAAUJ;AAC5C,sBAAE,OAAOM;wBACPC,QAAQC,KAAK,0CAA0CF,IAAIG;AAC7D;AACF;gBACA,OAAOV;;;AAGb;IAEA,oBAAML,CAAeL,QAAQE;QAC3B;kBAEQ,IAAImB,QAAQC,WAAWC,WAAWD,SAAS,OAEjDpB,IAAIsB,MAAM;YAGV5B,MAAKH,KAAM,IAAIgC,OAAezB,QAAQ;gBACpC0B,MAAM;oBACJC,QAAQ;oBACRC,SAAS,EAAC,OAAO;;gBAEnBC,YAAY,EAAC,WAAW;gBACxBC,YAAW;gBAGblC,MAAKH,GAAIsC,GAAG,cAAeC;gBACzB9B,IAAIsB,MAAM,qBAAqBQ,OAAOC,OAEtCD,OAAOD,GAAG,cAAeG;oBACvBhC,IAAIsB,MAAM,wBAAwBQ,OAAOC,QAAQC;oBAGnDF,OAAOD,GAAG,SAAUI;oBAClBjC,IAAIiC,MAAM,iBAAiBA;;gBAI/BjC,IAAIkC,KAAK;AACX,UAAE,OAAOD;YACPjC,IAAIiC,MAAM,+BAA+BA;AAC3C;AACF;IAEAnB,kBAAqBf,QAAS,iLAIboC,KAAKC,UAAUrC;;;"}
@@ -1,4 +1,4 @@
1
- import { c as compileRoute, a as chalk, S as ServiceProvider } from "./createClovie-LSdi1lXJ.js";
1
+ import { c as compileRoute, a as chalk, S as ServiceProvider } from "./createClovie-Dh7RbB5L.js";
2
2
 
3
3
  import http from "node:http";
4
4
 
@@ -8,6 +8,7 @@ import { promisify } from "util";
8
8
 
9
9
  class Kernel {
10
10
  #routes=[];
11
+ #appHandlers=[];
11
12
  #hooks={
12
13
  onRequest: null,
13
14
  preHandler: null,
@@ -26,6 +27,13 @@ class Kernel {
26
27
  };
27
28
  this.#routes.push(compiledRoute);
28
29
  }
30
+ setAppHandlers(handlers = []) {
31
+ this.#appHandlers = Array.isArray(handlers) ? handlers : [];
32
+ }
33
+ async cleanupAppHandlers() {
34
+ for (const handler of this.#appHandlers) handler?.cleanup && await handler.cleanup();
35
+ this.#appHandlers = [];
36
+ }
29
37
  hooks(h) {
30
38
  Object.assign(this.#hooks, h);
31
39
  }
@@ -40,6 +48,16 @@ class Kernel {
40
48
  try {
41
49
  const onReqOut = await (this.#hooks.onRequest?.(ctx));
42
50
  if (onReqOut) return response = onReqOut, response;
51
+ for (const handler of this.#appHandlers) try {
52
+ if (!handler?.match || !handler?.handle) continue;
53
+ if (await handler.match(ctx.req)) {
54
+ const handlerResponse = await handler.handle(ctx);
55
+ if (handlerResponse) return response = handlerResponse, response;
56
+ }
57
+ } catch (handlerError) {
58
+ return response = await (this.#hooks.onError?.(ctx, handlerError)) ?? ctx.respond.text("Internal Server Error", 500),
59
+ response;
60
+ }
43
61
  let matchParams = null;
44
62
  const route = this.#routes.find(r => {
45
63
  if (r.method !== ctx.req.method) return !1;
@@ -169,7 +187,7 @@ class AdapterInterface {
169
187
  headers: {},
170
188
  body: ""
171
189
  };
172
- if (!res.headersSent) {
190
+ if ("handled" !== resp.type && !res.headersSent) {
173
191
  if (res.statusCode = resp.status ?? 200, resp.headers) for (const [k, v] of Object.entries(resp.headers)) res.setHeader(k.toLowerCase(), v);
174
192
  if (isHead) return res.end();
175
193
  switch (resp.type) {
@@ -246,6 +264,11 @@ class AdapterInterface {
246
264
  }
247
265
 
248
266
  class RespondHelpers {
267
+ handled() {
268
+ return {
269
+ type: "handled"
270
+ };
271
+ }
249
272
  json(data, status = 200, headers = {}) {
250
273
  return {
251
274
  type: "json",
@@ -438,10 +461,10 @@ var Server$1 = Object.freeze({
438
461
  })
439
462
  } ]), opts.outputDir && kernel.setStaticFallback(opts.outputDir, (ctx, outputDir) => this.#serveStaticFile(ctx, outputDir)),
440
463
  !this.#adapter) if ("express" === opts.adapter || opts.middleware?.length > 0) {
441
- const {ExpressAdapter: ExpressAdapter} = await import("./ExpressAdapter-DL0Y9BG2.js");
464
+ const {ExpressAdapter: ExpressAdapter} = await import("./ExpressAdapter-BsYIpF7A.js");
442
465
  this.#adapter = ExpressAdapter.create(), log.debug("Using Express adapter for middleware support");
443
466
  } else this.#adapter = HttpAdapter.create(), log.debug("Using HTTP adapter");
444
- await this.#adapter.initialize(kernel, opts, log);
467
+ await this.#adapter.initialize(kernel, opts, log), "function" == typeof this.#adapter.registerKernelHandler && this.#adapter.registerKernelHandler();
445
468
  const server = await this.#adapter.start({
446
469
  port: port,
447
470
  host: host
@@ -528,4 +551,4 @@ var Server$1 = Object.freeze({
528
551
  });
529
552
 
530
553
  export { AdapterInterface as A, Server$1 as S };
531
- //# sourceMappingURL=Server-BR7ZqQBN.js.map
554
+ //# sourceMappingURL=Server-GcEIC8Hj.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Server-GcEIC8Hj.js","sources":["../lib/Server/Kernel.js","../lib/Server/utils/httpParsing.js","../lib/Server/adapters/AdapterInterface.js","../lib/Server/adapters/HttpAdapter.js","../lib/Server/utils/serverReady.js","../lib/Server/Server.js"],"sourcesContent":["import { compileRoute } from './utils/routeMatch.js';\n\n/**\n * Kernel handles route registration and matching\n * Separates routing logic from HTTP protocol handling\n */\nexport class Kernel {\n #routes = []; // { method, path, match, handler, mode, revalidate, ... }\n #appHandlers = []; // [{ match, handle, cleanup? }]\n #hooks = { \n onRequest: null, \n preHandler: null, \n onSend: null, \n onError: null \n };\n #staticFallback = null;\n\n constructor() { \n }\n\n /**\n * Register multiple routes at once\n * @param {Array} rawRoutes - Array of route objects\n */\n registerRoutes(rawRoutes) {\n for (const r of rawRoutes) {\n this.registerRoute(r);\n }\n }\n\n /**\n * Register a single route\n * @param {Object} route - Route object with method, path, handler, etc.\n */\n registerRoute(route) {\n const compiledRoute = {\n ...route,\n match: compileRoute(route.path)\n };\n this.#routes.push(compiledRoute);\n }\n\n setAppHandlers(handlers = []) {\n this.#appHandlers = Array.isArray(handlers) ? handlers : [];\n }\n\n async cleanupAppHandlers() {\n for (const handler of this.#appHandlers) {\n if (handler?.cleanup) {\n await handler.cleanup();\n }\n }\n this.#appHandlers = [];\n }\n\n /**\n * Configure hooks\n * @param {Object} hooks - Hook functions\n */\n hooks(h) { \n Object.assign(this.#hooks, h); \n }\n\n /**\n * Set static file fallback handler\n * @param {string} outputDir - Directory to serve static files from\n * @param {Function} handler - Static file handler function\n */\n setStaticFallback(outputDir, handler) {\n this.#staticFallback = { outputDir, handler };\n }\n\n /**\n * Handle a request through the routing system\n * @param {Object} ctx - Request context\n * @returns {Object} Response object\n */\n async handle(ctx) {\n let response;\n try {\n // onRequest can short-circuit\n const onReqOut = await this.#hooks.onRequest?.(ctx);\n if (onReqOut) { response = onReqOut; return response; }\n\n // app handlers first\n for (const handler of this.#appHandlers) {\n try {\n if (!handler?.match || !handler?.handle) continue;\n if (await handler.match(ctx.req)) {\n const handlerResponse = await handler.handle(ctx);\n if (handlerResponse) {\n response = handlerResponse;\n return response;\n }\n }\n } catch (handlerError) {\n response = (await this.#hooks.onError?.(ctx, handlerError))\n ?? ctx.respond.text('Internal Server Error', 500);\n return response;\n }\n }\n\n // route match …\n let matchParams = null;\n const route = this.#routes.find(r => {\n if (r.method !== ctx.req.method) return false;\n const m = r.match(ctx.req.path);\n if (m) { matchParams = m; return true; }\n return false;\n });\n\n if (!route) {\n if (this.#staticFallback) {\n const staticResponse = await this.#staticFallback.handler(ctx, this.#staticFallback.outputDir);\n response = (staticResponse && staticResponse.status !== 404)\n ? staticResponse\n : ctx.respond.text('Not Found', 404);\n } else {\n response = ctx.respond.text('Not Found', 404);\n }\n return response;\n }\n\n ctx.req.params = matchParams || {};\n\n // preHandler can short-circuit\n const preOut = await this.#hooks.preHandler?.(ctx, route);\n if (preOut) { response = preOut; return response; }\n\n // route handler\n response = await route.handler(ctx);\n\n } catch (err) {\n response = (await this.#hooks.onError?.(ctx, err))\n ?? ctx.respond.text('Internal Server Error', 500);\n } finally {\n await this.#hooks.onSend?.(ctx, response);\n }\n return response ?? ctx.respond.text('', 204);\n}\n\n\n /**\n * Get all registered routes\n * @returns {Array} Array of registered routes\n */\n getRoutes() {\n return this.#routes.map(route => ({\n method: route.method,\n path: route.path,\n params: route.params || [],\n compiled: true\n }));\n }\n}\n","/**\n * HTTP parsing utilities for request body and query parameters\n */\n\n/**\n * Parse request body based on content type\n * @param {Object} req - Node.js request object\n * @returns {Object|null} Parsed body object or null\n */\nexport async function parseBody(req) {\n return new Promise((resolve) => {\n if (req.method === 'GET' || req.method === 'HEAD') {\n resolve(null);\n return;\n }\n\n const contentType = req.headers['content-type'] || '';\n let body = '';\n\n req.on('data', chunk => {\n body += chunk.toString();\n });\n\n req.on('end', () => {\n try {\n if (contentType.includes('application/json')) {\n resolve(body ? JSON.parse(body) : {});\n } else if (contentType.includes('application/x-www-form-urlencoded')) {\n resolve(parseUrlEncoded(body));\n } else if (contentType.includes('multipart/form-data')) {\n // For now, just return the raw body for multipart\n // Could be enhanced with proper multipart parsing\n resolve({ raw: body });\n } else {\n resolve(body || null);\n }\n } catch (error) {\n resolve(null);\n }\n });\n\n req.on('error', () => {\n resolve(null);\n });\n });\n}\n\n/**\n * Parse URL-encoded form data\n * @param {string} data - URL-encoded string\n * @returns {Object} Parsed object\n */\nfunction parseUrlEncoded(data) {\n const result = {};\n const pairs = data.split('&');\n \n for (const pair of pairs) {\n const [key, value] = pair.split('=');\n if (key) {\n const decodedKey = decodeURIComponent(key);\n const decodedValue = value ? decodeURIComponent(value) : '';\n \n // Handle arrays (key[] or key[0])\n if (decodedKey.endsWith('[]')) {\n const arrayKey = decodedKey.slice(0, -2);\n if (!result[arrayKey]) result[arrayKey] = [];\n result[arrayKey].push(decodedValue);\n } else {\n result[decodedKey] = decodedValue;\n }\n }\n }\n \n return result;\n}\n\n/**\n * Parse query parameters from URLSearchParams\n * @param {URLSearchParams} searchParams - URL search parameters\n * @returns {Object} Parsed query object with support for arrays\n */\nexport function parseQuery(searchParams) {\n const result = {};\n \n for (const [key, value] of searchParams.entries()) {\n // Handle array parameters (key[] or key[0])\n if (key.endsWith('[]') || /\\[\\d*\\]$/.test(key)) {\n const arrayKey = key.replace(/\\[\\d*\\]$/, '').replace('[]', '');\n if (!result[arrayKey]) result[arrayKey] = [];\n result[arrayKey].push(value);\n } else {\n result[key] = value;\n }\n }\n \n return result;\n}\n","/**\n * Clovie Adapter Interface\n * \n * This defines the contract that all HTTP server adapters must implement.\n * The Clovie kernel uses this interface to delegate HTTP handling to\n * any compatible adapter (Express, Fastify, Node HTTP, etc.)\n */\nimport { parseQuery } from '../utils/httpParsing.js';\n\n/**\n * Adapter Interface - All adapters must implement this\n */\nexport class AdapterInterface {\n static create(name) {\n return new this(name);\n }\n\n constructor(name) {\n this.name = name;\n this.server = null;\n this.kernel = null;\n this.log = null;\n this.hooks = null;\n }\n\n /**\n * Initialize the adapter with kernel and context\n * @param {Object} kernel - Clovie kernel instance\n * @param {Object} opts - Configuration options (including middleware)\n * @param {Object} log - Clovie logger\n */\n async initialize(kernel, opts = {}, log = null) {\n this.kernel = kernel;\n this.opts = opts;\n this.log = log;\n \n // Validate options\n if (opts.middleware && !Array.isArray(opts.middleware)) {\n throw new Error('opts.middleware must be an array');\n }\n }\n\n /**\n * Start the HTTP server\n * @param {Object} options - Server options (port, host, etc.)\n * @returns {Promise<Object>} - Server instance\n */\n async start(options) {\n throw new Error(`start() must be implemented by ${this.name} adapter`);\n }\n\n /**\n * Stop the HTTP server\n * @returns {Promise<void>}\n */\n async stop() {\n throw new Error(`stop() must be implemented by ${this.name} adapter`);\n }\n\n /**\n * Get the underlying HTTP server instance\n * Used by services like LiveReload for Socket.IO integration\n * @returns {Object|null} - HTTP server instance\n */\n getHttpServer() {\n return this.server;\n }\n\n /**\n * Check if the server is currently running\n * @returns {boolean}\n */\n isRunning() {\n return this.server !== null;\n }\n\n /**\n * Handle a request through the adapter\n * @param {Object} req - HTTP request\n * @param {Object} res - HTTP response\n * @returns {Promise<void>}\n */\n async handleRequest(req, res) {\n throw new Error(`handleRequest() must be implemented by ${this.name} adapter`);\n }\n\n /**\n * Create standardized context object from request\n * @param {Object} req - HTTP request\n * @param {Object} res - HTTP response\n * @returns {Object} - Standardized context object\n */\n createContext(req, res) {\n const url = new URL(req.url || req.originalUrl, `http://${req.headers.host}`);\n\n return {\n req: {\n method: req.method,\n url: url.toString(),\n path: url.pathname,\n headers: Object.fromEntries(\n Object.entries(req.headers).map(([k, v]) => [k.toLowerCase(), v])\n ),\n query: req.query || parseQuery(url.searchParams),\n body: req.body || null,\n raw: { req, res },\n params: {}\n },\n res,\n respond: new RespondHelpers()\n };\n }\n\n /**\n * Send response using standardized response format\n * @param {Object} res - HTTP response\n * @param {Object} response - Response object from route handler\n * @param {boolean} isHead - Whether this is a HEAD request\n * @returns {Promise<void>}\n */\n async sendResponse(res, response, isHead = false) {\n // Default to 204 if no response object was returned\n const resp = response ?? { type: 'text', status: 204, headers: {}, body: '' };\n\n if (resp.type === 'handled') {\n return;\n }\n\n if (res.headersSent) return; // defensive guard\n\n // Set status and headers (normalize keys)\n res.statusCode = resp.status ?? 200;\n if (resp.headers) {\n for (const [k, v] of Object.entries(resp.headers)) {\n res.setHeader(k.toLowerCase(), v);\n }\n }\n\n // Respect HEAD requests: send headers only\n if (isHead) {\n return res.end();\n }\n\n switch (resp.type) {\n case 'json': {\n if (!res.getHeader('content-type')) {\n res.setHeader('content-type', 'application/json; charset=utf-8');\n }\n return res.end(JSON.stringify(resp.body));\n }\n\n case 'text': {\n if (!res.getHeader('content-type')) {\n res.setHeader('content-type', 'text/plain; charset=utf-8');\n }\n return res.end(resp.body ?? '');\n }\n\n case 'html': {\n if (!res.getHeader('content-type')) {\n res.setHeader('content-type', 'text/html; charset=utf-8');\n }\n return res.end(resp.body ?? '');\n }\n\n case 'file': {\n return await this.serveFile(res, resp.path);\n }\n\n case 'stream': {\n const b = resp.body;\n if (!b) return res.end();\n // Node stream\n if (typeof b.pipe === 'function') return b.pipe(res);\n // AsyncIterable\n if (Symbol.asyncIterator in Object(b)) {\n for await (const chunk of b) {\n if (!res.write(chunk)) {\n await new Promise(r => res.once('drain', r));\n }\n }\n return res.end();\n }\n // Fallback\n return res.end(b);\n }\n\n default:\n return res.end('');\n }\n }\n\n /**\n * Serve static files with proper MIME types and streaming\n * @param {Object} res - HTTP response\n * @param {string} filePath - Path to file to serve\n * @returns {Promise<void>}\n */\n async serveFile(res, filePath) {\n const fs = await import('node:fs');\n const fsp = await import('node:fs/promises');\n const path = await import('node:path');\n const { pipeline } = await import('node:stream');\n const { promisify } = await import('node:util');\n \n const pipelineAsync = promisify(pipeline);\n\n const MIME = {\n '.html': 'text/html; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.js': 'application/javascript; charset=utf-8',\n '.mjs': 'application/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.eot': 'application/vnd.ms-fontobject'\n };\n\n try {\n const st = await fsp.stat(filePath);\n if (!st.isFile()) {\n res.statusCode = 404;\n res.setHeader('content-type', 'text/plain; charset=utf-8');\n return res.end('Not Found');\n }\n\n const ext = path.extname(filePath).toLowerCase();\n if (!res.getHeader('content-type')) {\n res.setHeader('content-type', MIME[ext] ?? 'application/octet-stream');\n }\n res.setHeader('content-length', String(st.size));\n\n const rs = fs.createReadStream(filePath);\n rs.on('error', () => {\n if (!res.headersSent) res.statusCode = 404;\n res.end('Not Found');\n });\n await pipelineAsync(rs, res); // backpressure-aware piping\n } catch {\n if (!res.headersSent) {\n res.statusCode = 404;\n res.setHeader('content-type', 'text/plain; charset=utf-8');\n }\n res.end('Not Found');\n }\n }\n\n /**\n * Handle request errors consistently\n * @param {Object} res - HTTP response\n * @param {Error} error - Error that occurred\n * @returns {Promise<void>}\n */\n async handleError(res, error) {\n this.log?.error?.('Request handling error:', error);\n if (!res.headersSent) {\n res.statusCode = 500;\n res.setHeader('content-type', 'text/plain; charset=utf-8');\n res.end('Internal Server Error');\n }\n }\n}\n\n/**\n * Route object structure that adapters receive from Clovie kernel\n */\nexport class ClovieRoute {\n constructor(method, path, handler, options = {}) {\n this.method = method.toUpperCase();\n this.path = path;\n this.handler = handler;\n this.options = options;\n this.params = options.params || [];\n }\n}\n\n/**\n * Hook object structure for lifecycle management\n */\nexport class ClovieHooks {\n constructor() {\n this.onRequest = null;\n this.preHandler = null;\n this.onSend = null;\n this.onError = null;\n }\n}\n\n/**\n * Context object passed to route handlers\n */\nexport class ClovieContext {\n constructor(req, res, state, stable) {\n this.req = req;\n this.res = res;\n this.state = state;\n this.stable = stable;\n this.respond = new RespondHelpers();\n }\n}\n\n/**\n * Response helpers for route handlers\n */\nexport class RespondHelpers {\n handled() {\n return { type: 'handled' };\n }\n\n json(data, status = 200, headers = {}) {\n return { type: 'json', status, headers, body: data };\n }\n\n text(data, status = 200, headers = {}) {\n return { type: 'text', status, headers, body: data };\n }\n\n html(data, status = 200, headers = {}) {\n return { type: 'html', status, headers, body: data };\n }\n\n file(path, status = 200, headers = {}) {\n return { type: 'file', status, headers, path };\n }\n\n stream(body, status = 200, headers = {}) {\n return { type: 'stream', status, headers, body };\n }\n\n redirect(location, status = 302, headers = {}) {\n return { type:'text', status, headers: { ...headers, location }, body: '' };\n }\n}\n","/**\n * HTTP Adapter for Node.js native HTTP server\n * Thin I/O shim: normalize req/res, call kernel, send response\n */\nimport http from 'node:http';\nimport { AdapterInterface } from './AdapterInterface.js';\nimport { parseBody } from '../utils/httpParsing.js';\n\nexport class HttpAdapter extends AdapterInterface {\n #connections = new Set();\n\n constructor() {\n super('http');\n }\n\n async initialize(kernel, opts = {}, log = null) {\n await super.initialize(kernel, opts, log);\n \n // Warn if middleware is configured for HTTP adapter\n if (opts.middleware?.length > 0) {\n log?.warn('Middleware configured but HTTP adapter selected. Consider using adapter: \"express\" for full middleware support.');\n log?.info('HTTP adapter will simulate middleware using hooks for basic compatibility.');\n }\n }\n\n async start({ port = 3000, host = '0.0.0.0' } = {}) {\n this.server = http.createServer(async (req, res) => {\n try {\n // Simulate middleware execution for HTTP adapter\n if (this.opts.middleware?.length > 0) {\n const success = await this.#simulateMiddleware(req, res, this.opts.middleware);\n if (!success) return; // Response was sent by middleware\n }\n\n // Parse body for HTTP adapter\n if (!req.body) {\n req.body = await parseBody(req);\n }\n\n const ctx = this.createContext(req, res);\n const out = await this.kernel.handle(ctx);\n await this.sendResponse(res, out, req.method === 'HEAD');\n } catch (error) {\n await this.handleError(res, error);\n }\n });\n\n // Track connections for clean shutdown\n this.server.on('connection', (conn) => {\n this.#connections.add(conn);\n conn.on('close', () => {\n this.#connections.delete(conn);\n });\n });\n\n await new Promise((resolve, reject) =>\n this.server.listen(port, host, err => (err ? reject(err) : resolve()))\n );\n\n this.log?.info?.(`HTTP Server listening on http://${host}:${port}`);\n return this.server;\n }\n\n async stop() {\n if (this.server) {\n // Destroy all active connections\n for (const conn of this.#connections) {\n conn.destroy();\n }\n this.#connections.clear();\n\n // Close the server\n await new Promise(resolve => this.server.close(() => resolve()));\n this.server = null;\n }\n }\n\n /**\n * Simulate Express middleware for HTTP adapter\n * Limited compatibility - not all Express middleware will work\n * @private\n */\n async #simulateMiddleware(req, res, middlewareArray) {\n for (const middleware of middlewareArray) {\n try {\n let nextCalled = false;\n const next = (err) => {\n if (err) throw err;\n nextCalled = true;\n };\n\n // Add Express-like methods to response\n if (!res.json) {\n res.json = (data) => {\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify(data));\n return false; // Indicates response was sent\n };\n }\n\n await middleware(req, res, next);\n \n // If next wasn't called and response was sent, stop processing\n if (!nextCalled && res.headersSent) {\n return false;\n }\n } catch (error) {\n this.log?.error(`Middleware simulation error: ${error.message}`);\n res.statusCode = 500;\n res.end('Internal Server Error');\n return false;\n }\n }\n return true; // Continue to kernel\n }\n\n}\n","import chalk from 'chalk';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\n\nconst execAsync = promisify(exec);\n\n/**\n * Display a pretty server ready message with clickable URL\n * @param {object} options - Server options\n * @param {number} options.port - Server port\n * @param {string} options.host - Server host\n * @param {string} options.mode - Server mode (development/production)\n * @param {object} logger - Logger instance\n */\nexport async function displayServerReady({ port, host, mode }, logger) {\n const protocol = mode === 'development' ? 'http' : 'https';\n const url = `${protocol}://${host === '0.0.0.0' ? 'localhost' : host}:${port}`;\n \n // Create a nice banner\n const banner = [\n '',\n chalk.bold.blue('╔══════════════════════════════════════════════════════════════╗'),\n chalk.bold.blue('║') + chalk.bold.white(' 🚀 Clovie Server Ready! 🚀 ') + chalk.bold.blue('║'),\n chalk.bold.blue('╠══════════════════════════════════════════════════════════════╣'),\n chalk.bold.blue('║') + chalk.white(` Mode: ${chalk.green(mode.toUpperCase())} `) + chalk.bold.blue('║'),\n chalk.bold.blue('║') + chalk.white(` URL: ${chalk.cyan.underline(url)} `) + chalk.bold.blue('║'),\n chalk.bold.blue('║') + chalk.white(` Host: ${chalk.yellow(host)}:${chalk.yellow(port)} `) + chalk.bold.blue('║'),\n chalk.bold.blue('╚══════════════════════════════════════════════════════════════╝'),\n ''\n ];\n\n // Display the banner\n banner.forEach(line => logger.info(line));\n\n // Try to open browser automatically in development mode\n if (mode === 'development') {\n try {\n await openBrowser(url);\n logger.info(chalk.green('🌐 Browser opened automatically'));\n } catch (error) {\n logger.debug('Could not open browser automatically:', error.message);\n }\n }\n\n // Display helpful commands\n const commands = [\n '',\n chalk.gray('💡 Quick Commands:'),\n chalk.gray(` • Press ${chalk.yellow('Ctrl+C')} to stop the server`),\n chalk.gray(` • Visit ${chalk.cyan(url)} to view your site`),\n chalk.gray(` • Check the console for live reload updates`),\n ''\n ];\n\n commands.forEach(line => logger.info(line));\n}\n\n/**\n * Open browser with the given URL\n * @param {string} url - URL to open\n */\nasync function openBrowser(url) {\n const platform = process.platform;\n let command;\n\n switch (platform) {\n case 'darwin': // macOS\n command = `open \"${url}\"`;\n break;\n case 'win32': // Windows\n command = `start \"${url}\"`;\n break;\n default: // Linux and others\n command = `xdg-open \"${url}\"`;\n break;\n }\n\n try {\n await execAsync(command);\n } catch (error) {\n throw new Error(`Failed to open browser: ${error.message}`);\n }\n}","import { ServiceProvider } from '@brickworks/engine';\nimport { Kernel } from './Kernel.js';\nimport { HttpAdapter } from './adapters/HttpAdapter.js';\nimport { displayServerReady } from './utils/serverReady.js';\n\nexport class Server extends ServiceProvider {\n static manifest = {\n name: 'Clovie Server',\n namespace: 'server',\n version: '1.0.0'\n };\n\n #routes = [];\n #adapter = null;\n #hooks = {\n onRequest: null,\n preHandler: null,\n onSend: null,\n onError: null\n };\n\n actions(useContext) { \n return {\n // Add a route\n add: (method, path, handler, meta = {}) => {\n this.#routes.push({\n method: method.toUpperCase(),\n path,\n handler,\n meta\n });\n },\n\n // Set server adapter\n useAdapter: (adapter) => {\n this.#adapter = adapter;\n return this;\n },\n\n // Configure hooks\n hooks: (hooks) => {\n this.#hooks = { ...this.#hooks, ...hooks };\n return this;\n },\n\n // Start listening server\n listen: async (opts = {}) => {\n const relay = useContext('relay');\n const port = opts.port || 3000;\n const host = opts.host || '0.0.0.0';\n const log = useContext('log');\n \n // Create kernel with services\n const kernel = new Kernel({\n state: useContext('state'),\n stable: useContext('stable'),\n // Add other services as needed for ISR/static/SSR\n });\n\n // Configure kernel hooks\n kernel.hooks(this.#hooks);\n\n // 1) Register app routes first\n kernel.registerRoutes(this.#routes);\n\n // 2) Add system routes (won't shadow user routes)\n kernel.registerRoutes([\n {\n method: 'GET',\n path: '/health',\n handler: (ctx) => ctx.respond.json({\n status: 'ok',\n timestamp: new Date().toISOString(),\n mode: opts.mode || 'production'\n })\n },\n {\n method: 'GET',\n path: '/api/info',\n handler: (ctx) => ctx.respond.json({\n name: 'Clovie',\n version: '1.0.0',\n mode: opts.mode || 'production',\n routes: this.#routes.length\n })\n }\n ]);\n\n // 3) Configure static file serving as 404 fallback\n if (opts.outputDir) {\n kernel.setStaticFallback(opts.outputDir, (ctx, outputDir) => {\n return this.#serveStaticFile(ctx, outputDir);\n });\n }\n\n // 4) Boot adapter with middleware support\n if (!this.#adapter) {\n // Choose adapter based on config\n if (opts.adapter === 'express' || opts.middleware?.length > 0) {\n const { ExpressAdapter } = await import('./adapters/ExpressAdapter.js');\n this.#adapter = ExpressAdapter.create();\n log.debug('Using Express adapter for middleware support');\n } else {\n this.#adapter = HttpAdapter.create();\n log.debug('Using HTTP adapter');\n }\n }\n\n // Pass configuration to adapter\n await this.#adapter.initialize(kernel, opts, log);\n\n if (typeof this.#adapter.registerKernelHandler === 'function') {\n this.#adapter.registerKernelHandler();\n }\n \n // Start the server and return the instance\n const server = await this.#adapter.start({ port, host });\n \n // Check for LiveReload service and initialize it if available\n try {\n const liveReload = useContext('liveReload');\n log.debug(`LiveReload service found: ${!!liveReload}, mode: ${opts.mode}`);\n if (liveReload && opts.mode === 'development') {\n log.info('Initializing LiveReload...');\n await liveReload.initializeServer(server, opts);\n log.info('LiveReload initialized successfully');\n }\n } catch (error) {\n log.debug('LiveReload not available:', error.message);\n }\n \n // Display pretty server ready message\n const actualPort = server.address()?.port || port;\n await displayServerReady({ \n port: actualPort, \n host, \n mode: opts.mode || 'production' \n }, log);\n \n // Broadcast server ready event\n relay.broadcast('server:ready', server);\n return server;\n },\n\n // Get all routes\n getRoutes: () => this.#routes,\n\n // Clear all routes (useful for testing)\n clearRoutes: () => {\n this.#routes = [];\n },\n\n // Stop the server\n stop: async () => {\n if (this.#adapter) {\n await this.#adapter.stop();\n }\n },\n\n // Check if server is running\n isRunning: () => this.#adapter && this.#adapter.isRunning(),\n\n // Get the underlying HTTP server (for Socket.IO integration)\n getHttpServer: () => {\n if (this.#adapter) {\n return this.#adapter.getHttpServer();\n }\n return null;\n }\n };\n }\n /**\n * Serve static files from the output directory\n * @private\n */\n async #serveStaticFile(context, outputDir) {\n const fs = await import('fs/promises');\n const path = await import('path');\n \n try {\n // Get the requested file path\n const requestPath = context.req.path === '/' ? '/index.html' : context.req.path;\n let filePath = path.join(outputDir, requestPath);\n let stats;\n \n try {\n // Try the path as-is first\n stats = await fs.stat(filePath);\n } catch (error) {\n // If it fails and has no extension, try adding .html\n const ext = path.extname(requestPath);\n if (!ext) {\n const htmlPath = path.join(outputDir, `${requestPath}.html`);\n try {\n stats = await fs.stat(htmlPath);\n filePath = htmlPath; // Use the .html version\n } catch (htmlError) {\n // Neither worked, return 404\n return context.respond.text('Not Found', 404);\n }\n } else {\n // Had an extension but file not found\n return context.respond.text('Not Found', 404);\n }\n }\n \n if (stats.isFile()) {\n // Determine content type\n const ext = path.extname(filePath);\n const contentType = this.#getContentType(ext);\n \n // Read and serve the file\n const content = await fs.readFile(filePath);\n \n // Serve with correct content type\n if (contentType === 'text/html') {\n return context.respond.html(content.toString(), 200);\n } else if (contentType === 'text/css') {\n return context.respond.text(content.toString(), 200, { 'Content-Type': 'text/css' });\n } else if (contentType === 'application/javascript') {\n return context.respond.text(content.toString(), 200, { 'Content-Type': 'application/javascript' });\n } else if (contentType.startsWith('text/')) {\n return context.respond.text(content.toString(), 200, { 'Content-Type': contentType });\n } else {\n // For binary files, send as file response\n return context.respond.file(filePath, 200, { 'Content-Type': contentType });\n }\n } else {\n return context.respond.text('Not Found', 404);\n }\n } catch (error) {\n return context.respond.text('Not Found', 404);\n }\n }\n\n /**\n * Get content type based on file extension\n * @private\n */\n #getContentType(ext) {\n const types = {\n '.html': 'text/html',\n '.css': 'text/css',\n '.js': 'application/javascript',\n '.json': 'application/json',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.eot': 'application/vnd.ms-fontobject'\n };\n \n return types[ext.toLowerCase()] || 'application/octet-stream';\n }\n}"],"names":["Kernel","routes","appHandlers","hooks","onRequest","preHandler","onSend","onError","staticFallback","constructor","registerRoutes","rawRoutes","r","this","registerRoute","route","compiledRoute","match","compileRoute","path","push","setAppHandlers","handlers","Array","isArray","cleanupAppHandlers","handler","cleanup","h","Object","assign","setStaticFallback","outputDir","handle","ctx","response","onReqOut","req","handlerResponse","handlerError","respond","text","matchParams","find","method","m","staticResponse","status","params","preOut","err","getRoutes","map","compiled","async","parseBody","Promise","resolve","contentType","headers","body","on","chunk","toString","includes","JSON","parse","data","result","pairs","split","pair","key","value","decodedKey","decodeURIComponent","decodedValue","endsWith","arrayKey","slice","parseUrlEncoded","raw","error","parseQuery","searchParams","entries","test","replace","AdapterInterface","create","name","server","kernel","log","initialize","opts","middleware","Error","start","options","stop","getHttpServer","isRunning","handleRequest","res","createContext","url","URL","originalUrl","host","pathname","fromEntries","k","v","toLowerCase","query","RespondHelpers","sendResponse","isHead","resp","type","headersSent","statusCode","setHeader","end","getHeader","stringify","serveFile","b","pipe","Symbol","asyncIterator","write","once","filePath","fs","import","fsp","pipeline","promisify","pipelineAsync","MIME","st","stat","isFile","ext","extname","String","size","rs","createReadStream","handleError","handled","json","html","file","stream","redirect","location","HttpAdapter","connections","Set","super","length","warn","info","port","http","createServer","simulateMiddleware","out","conn","add","delete","reject","listen","destroy","clear","close","middlewareArray","nextCalled","next","message","execAsync","exec","displayServerReady","mode","logger","chalk","bold","blue","white","green","toUpperCase","cyan","underline","yellow","forEach","line","command","process","platform","openBrowser","debug","gray","ServiceProvider","static","namespace","version","adapter","actions","useContext","meta","useAdapter","relay","state","stable","timestamp","Date","toISOString","serveStaticFile","ExpressAdapter","registerKernelHandler","liveReload","initializeServer","actualPort","address","broadcast","clearRoutes","context","requestPath","stats","join","htmlPath","htmlError","getContentType","content","readFile","startsWith"],"mappings":"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"}
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
 
3
- var Server = require("./Server-BVelCzau.cjs");
3
+ var Server = require("./Server-DkYaMJak.cjs");
4
4
 
5
- require("./createClovie-DdMTZ_dO.cjs"), require("stream"), require("module"), require("path"),
5
+ require("./createClovie-CJ1h3Pfo.cjs"), require("stream"), require("module"), require("path"),
6
6
  require("crypto"), require("fs"), require("istextorbinary"), require("chokidar"),
7
7
  require("readline"), require("events"), require("node:process"), require("node:os"),
8
- require("node:tty"), require("node:http"), require("child_process"), require("util");
8
+ require("node:tty"), require("fs/promises"), require("child_process"), require("url"),
9
+ require("node:http"), require("util");
9
10
 
10
11
  class ExpressAdapter extends Server.AdapterInterface {
11
12
  #connections=new Set;
@@ -29,10 +30,15 @@ class ExpressAdapter extends Server.AdapterInterface {
29
30
  throw new Error(`Failed to apply middleware[${index}]: ${error.message}`);
30
31
  }
31
32
  }
32
- this.app.all("*", async (req, res) => {
33
+ }
34
+ registerKernelHandler() {
35
+ this.app && this.app.all("*", async (req, res) => {
33
36
  await this.handleRequest(req, res);
34
37
  });
35
38
  }
39
+ getExpressApp() {
40
+ return this.app;
41
+ }
36
42
  async start({port: port = 3e3, host: host = "0.0.0.0"} = {}) {
37
43
  return new Promise((resolve, reject) => {
38
44
  this.server = this.app.listen(port, host, err => {
@@ -72,4 +78,4 @@ class ExpressAdapter extends Server.AdapterInterface {
72
78
  }
73
79
 
74
80
  exports.ExpressAdapter = ExpressAdapter;
75
- //# sourceMappingURL=ExpressAdapter-De7bTuZu.cjs.map
81
+ //# sourceMappingURL=ExpressAdapter--oUcRq-k.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpressAdapter--oUcRq-k.cjs","sources":["../../lib/Server/adapters/ExpressAdapter.js"],"sourcesContent":["/**\n * Express adapter for Clovie with full middleware support\n */\nimport { AdapterInterface } from './AdapterInterface.js';\n\nexport class ExpressAdapter extends AdapterInterface {\n #connections = new Set();\n\n constructor(express = null) {\n super('express');\n this.express = express;\n this.app = null;\n }\n\n async initialize(kernel, opts = {}, log = null) {\n await super.initialize(kernel, opts, log);\n \n // Import Express\n if (!this.express) {\n try {\n this.express = await import('express');\n this.express = this.express.default;\n } catch (error) {\n throw new Error('Express not installed. Run: npm install express');\n }\n }\n\n // Create Express app\n this.app = this.express();\n\n // Apply default middleware (can be overridden by user middleware)\n if (!opts.middleware || !this.#hasBodyParser(opts.middleware)) {\n this.app.use(this.express.json());\n this.app.use(this.express.urlencoded({ extended: true }));\n }\n\n // Apply user-defined middleware\n if (opts.middleware && Array.isArray(opts.middleware)) {\n this.log?.info(`Applying ${opts.middleware.length} middleware functions`);\n \n for (const [index, middleware] of opts.middleware.entries()) {\n try {\n this.app.use(middleware);\n this.log?.debug(`Applied middleware ${index + 1}/${opts.middleware.length}`);\n } catch (error) {\n throw new Error(`Failed to apply middleware[${index}]: ${error.message}`);\n }\n }\n }\n\n // Additional middleware will be mounted by the server before kernel handler registration\n }\n\n registerKernelHandler() {\n if (!this.app) {\n return;\n }\n\n this.app.all('*', async (req, res) => {\n await this.handleRequest(req, res);\n });\n }\n\n getExpressApp() {\n return this.app;\n }\n\n async start({ port = 3000, host = '0.0.0.0' } = {}) {\n return new Promise((resolve, reject) => {\n this.server = this.app.listen(port, host, (err) => {\n if (err) {\n reject(err);\n } else {\n // Track connections for clean shutdown\n this.server.on('connection', (conn) => {\n this.#connections.add(conn);\n conn.on('close', () => {\n this.#connections.delete(conn);\n });\n });\n\n this.log?.info(`Express Server with middleware listening on http://${host}:${port}`);\n resolve(this.server);\n }\n });\n });\n }\n\n async stop() {\n if (this.server?.close) {\n // Destroy all active connections\n for (const conn of this.#connections) {\n conn.destroy();\n }\n this.#connections.clear();\n\n return new Promise((resolve) => {\n this.server.close(() => {\n this.log?.info('Express Server stopped');\n resolve();\n });\n });\n }\n }\n\n async handleRequest(req, res) {\n try {\n // Create context from Express request\n const ctx = this.createContext(req, res);\n \n // Delegate to kernel for route matching and handling\n const response = await this.kernel.handle(ctx);\n \n // Send response using shared method\n await this.sendResponse(res, response, req.method === 'HEAD');\n\n } catch (error) {\n await this.handleError(res, error);\n }\n }\n\n /**\n * Check if middleware array contains body parser\n * @private\n */\n #hasBodyParser(middlewareArray) {\n return middlewareArray.some(mw => {\n // Check for common body parser middleware names\n const name = mw.name?.toLowerCase() || mw.toString().toLowerCase();\n return name.includes('json') || name.includes('urlencoded') || name.includes('bodyparser');\n });\n }\n}\n"],"names":["ExpressAdapter","AdapterInterface","connections","Set","constructor","express","super","this","app","initialize","kernel","opts","log","import","default","error","Error","middleware","hasBodyParser","use","json","urlencoded","extended","Array","isArray","info","length","index","entries","debug","message","registerKernelHandler","all","async","req","res","handleRequest","getExpressApp","start","port","host","Promise","resolve","reject","server","listen","err","on","conn","add","delete","stop","close","destroy","clear","ctx","createContext","response","handle","sendResponse","method","handleError","middlewareArray","some","mw","name","toLowerCase","toString","includes"],"mappings":";;;;;;;;;;AAKO,MAAMA,uBAAuBC,OAAAA;IAClCC,aAAe,IAAIC;IAEnB,WAAAC,CAAYC,UAAU;QACpBC,MAAM,YACNC,KAAKF,UAAUA,SACfE,KAAKC,MAAM;AACb;IAEA,gBAAMC,CAAWC,QAAQC,OAAO,CAAA,GAAIC,MAAM;QAIxC,UAHMN,MAAMG,WAAWC,QAAQC,MAAMC,OAGhCL,KAAKF,SACR;YACEE,KAAKF,gBAAgBQ,OAAO,YAC5BN,KAAKF,UAAUE,KAAKF,QAAQS;AAC9B,UAAE,OAAOC;YACP,MAAM,IAAIC,MAAM;AAClB;QAaF,IATAT,KAAKC,MAAMD,KAAKF,WAGXM,KAAKM,cAAeV,MAAKW,cAAeP,KAAKM,gBAChDV,KAAKC,IAAIW,IAAIZ,KAAKF,QAAQe;QAC1Bb,KAAKC,IAAIW,IAAIZ,KAAKF,QAAQgB,WAAW;YAAEC,WAAU;cAI/CX,KAAKM,cAAcM,MAAMC,QAAQb,KAAKM,aAAa;YACrDV,KAAKK,KAAKa,KAAK,YAAYd,KAAKM,WAAWS;YAE3C,KAAK,OAAOC,OAAOV,eAAeN,KAAKM,WAAWW,WAChD;gBACErB,KAAKC,IAAIW,IAAIF,aACbV,KAAKK,KAAKiB,MAAM,sBAAsBF,QAAQ,KAAKhB,KAAKM,WAAWS;AACrE,cAAE,OAAOX;gBACP,MAAM,IAAIC,MAAM,8BAA8BW,WAAWZ,MAAMe;AACjE;AAEJ;AAGF;IAEA,qBAAAC;QACOxB,KAAKC,OAIVD,KAAKC,IAAIwB,IAAI,KAAKC,OAAOC,KAAKC;kBACtB5B,KAAK6B,cAAcF,KAAKC;;AAElC;IAEA,aAAAE;QACE,OAAO9B,KAAKC;AACd;IAEA,WAAM8B,EAAMC,MAAEA,OAAO,KAAIC,MAAEA,OAAO,aAAc;QAC9C,OAAO,IAAIC,QAAQ,CAACC,SAASC;YAC3BpC,KAAKqC,SAASrC,KAAKC,IAAIqC,OAAON,MAAMC,MAAOM;gBACrCA,MACFH,OAAOG,QAGPvC,KAAKqC,OAAOG,GAAG,cAAeC;oBAC5BzC,MAAKL,YAAa+C,IAAID,OACtBA,KAAKD,GAAG,SAAS;wBACfxC,MAAKL,YAAagD,OAAOF;;oBAI7BzC,KAAKK,KAAKa,KAAK,sDAAsDe,QAAQD;gBAC7EG,QAAQnC,KAAKqC;;;AAIrB;IAEA,UAAMO;QACJ,IAAI5C,KAAKqC,QAAQQ,OAAO;YAEtB,KAAK,MAAMJ,QAAQzC,MAAKL,aACtB8C,KAAKK;YAIP,OAFA9C,MAAKL,YAAaoD,SAEX,IAAIb,QAASC;gBAClBnC,KAAKqC,OAAOQ,MAAM;oBAChB7C,KAAKK,KAAKa,KAAK,2BACfiB;;;AAGN;AACF;IAEA,mBAAMN,CAAcF,KAAKC;QACvB;YAEE,MAAMoB,MAAMhD,KAAKiD,cAActB,KAAKC,MAG9BsB,iBAAiBlD,KAAKG,OAAOgD,OAAOH;kBAGpChD,KAAKoD,aAAaxB,KAAKsB,UAAyB,WAAfvB,IAAI0B;AAE7C,UAAE,OAAO7C;kBACDR,KAAKsD,YAAY1B,KAAKpB;AAC9B;AACF;IAMA,cAAAG,CAAe4C;QACb,OAAOA,gBAAgBC,KAAKC;YAE1B,MAAMC,OAAOD,GAAGC,MAAMC,iBAAiBF,GAAGG,WAAWD;YACrD,OAAOD,KAAKG,SAAS,WAAWH,KAAKG,SAAS,iBAAiBH,KAAKG,SAAS;;AAEjF;;;"}
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
 
3
- var socket_io = require("socket.io"), createClovie = require("./createClovie-DdMTZ_dO.cjs");
3
+ var socket_io = require("socket.io"), createClovie = require("./createClovie-CJ1h3Pfo.cjs");
4
4
 
5
5
  require("stream"), require("module"), require("path"), require("crypto"), require("fs"),
6
6
  require("istextorbinary"), require("chokidar"), require("readline"), require("events"),
7
- require("node:process"), require("node:os"), require("node:tty");
7
+ require("node:process"), require("node:os"), require("node:tty"), require("fs/promises"),
8
+ require("child_process"), require("url");
8
9
 
9
10
  class LiveReload extends createClovie.ServiceProvider {
10
11
  static manifest={
@@ -72,4 +73,4 @@ class LiveReload extends createClovie.ServiceProvider {
72
73
  }
73
74
 
74
75
  exports.LiveReload = LiveReload;
75
- //# sourceMappingURL=LiveReload-TdgPIiCn.cjs.map
76
+ //# sourceMappingURL=LiveReload-BvFz-bG3.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"LiveReload-TdgPIiCn.cjs","sources":["../../lib/LiveReload.js"],"sourcesContent":["import { Server as SocketIOServer } from 'socket.io';\nimport { ServiceProvider } from '@brickworks/engine';\nimport { queueMacrotask } from './utils/tasks.js';\n\nexport class LiveReload extends ServiceProvider {\n static manifest = {\n name: 'Clovie LiveReload',\n namespace: 'liveReload',\n version: '1.0.0',\n };\n\n #io;\n #reloading = false;\n\n getter () {\n return {\n io: () => this.#io,\n }\n }\n\n actions() {\n return {\n initializeServer: async (server, opts) => {\n const log = this.useContext('log');\n if (opts.mode === 'development') {\n await this.#setupSocketIO(server, log);\n }\n },\n notifyReload: () => {\n if (this.#reloading) return;\n this.#reloading = true;\n \n queueMacrotask(() => {\n this.#reloading = false;\n if (this.#io) {\n this.#io.emit('reload')\n }\n });\n },\n injectLiveReloadScript: async (renderedContent, opts) => {\n const lastBodyIndex = renderedContent.lastIndexOf('</body>');\n if (lastBodyIndex !== -1) {\n const scriptConfig = { \n mode: opts.mode || 'development', \n port: opts.port || 3000 \n };\n \n try {\n // Import live reload script dynamically \n renderedContent = renderedContent.substring(0, lastBodyIndex) + \n this.#liveReloadScript(scriptConfig) + '\\n' + \n renderedContent.substring(lastBodyIndex);\n } catch (err) {\n console.warn('⚠️ Could not load live reload script:', err.message);\n }\n }\n return renderedContent;\n }\n }\n }\n\n async #setupSocketIO(server, log) {\n try {\n // Wait a bit for server to be fully initialized\n await new Promise(resolve => setTimeout(resolve, 100));\n\n log.debug('Setting up Socket.IO...');\n \n // Configure Socket.IO with proper CORS and options\n this.#io = new SocketIOServer(server, {\n cors: {\n origin: \"*\",\n methods: [\"GET\", \"POST\"]\n },\n transports: ['polling', 'websocket'],\n allowEIO3: true\n });\n\n this.#io.on('connection', (socket) => {\n log.debug(`Client connected: ${socket.id}`);\n \n socket.on('disconnect', (reason) => {\n log.debug(`Client disconnected: ${socket.id} - ${reason}`);\n });\n \n socket.on('error', (error) => {\n log.error('Socket error:', error);\n });\n });\n\n log.info('Socket.IO server ready');\n } catch (error) {\n log.error('Error setting up Socket.IO:', error);\n }\n }\n\n #liveReloadScript = (opts) => `<!-- Live Reload Script (Development Mode Only) -->\n<script src=\"https://cdn.socket.io/4.7.4/socket.io.min.js\"></script>\n<script>\n (function() {\n const opts = ${JSON.stringify(opts)};\n \n console.log('Initializing live reload with opts:', opts);\n \n // Configure Socket.IO client with proper options\n const socket = io({\n transports: ['polling', 'websocket'],\n timeout: 20000,\n forceNew: true\n });\n \n socket.on('reload', () => {\n console.log('Live reload triggered');\n window.location.reload();\n });\n \n socket.on('connect', () => {\n console.log('Connected to live reload server');\n });\n \n socket.on('disconnect', (reason) => {\n console.log('Disconnected from live reload server:', reason);\n });\n \n socket.on('connect_error', (error) => {\n console.error('Connection error:', error);\n console.log('Retrying connection in 3 seconds...');\n setTimeout(() => {\n socket.connect();\n }, 3000);\n });\n \n socket.on('error', (error) => {\n console.error('Socket error:', error);\n });\n \n // Add connection timeout\n setTimeout(() => {\n if (!socket.connected) {\n console.warn('Live reload connection timeout - server may not be running');\n }\n }, 5000);\n })();\n</script>`;\n}"],"names":["LiveReload","ServiceProvider","static","name","namespace","version","io","reloading","getter","this","actions","initializeServer","async","server","opts","log","useContext","mode","setupSocketIO","notifyReload","queueMacrotask","emit","injectLiveReloadScript","renderedContent","lastBodyIndex","lastIndexOf","scriptConfig","port","substring","liveReloadScript","err","console","warn","message","Promise","resolve","setTimeout","debug","SocketIOServer","cors","origin","methods","transports","allowEIO3","on","socket","id","reason","error","info","JSON","stringify"],"mappings":";;;;;;;;AAIO,MAAMA,mBAAmBC,aAAAA;IAC9BC,gBAAkB;QAChBC,MAAM;QACNC,WAAW;QACXC,SAAS;;IAGXC;IACAC,YAAa;IAEb,MAAAC;QACE,OAAO;YACLF,IAAI,MAAMG,MAAKH;;AAEnB;IAEA,OAAAI;QACE,OAAO;YACLC,kBAAkBC,OAAOC,QAAQC;gBAC/B,MAAMC,MAAMN,KAAKO,WAAW;gBACV,kBAAdF,KAAKG,cACDR,MAAKS,cAAeL,QAAQE;;YAGtCI,cAAc;gBACRV,MAAKF,cACTE,MAAKF,aAAa,GAElBa,aAAAA,eAAe;oBACbX,MAAKF,aAAa,GACdE,MAAKH,MACPG,MAAKH,GAAIe,KAAK;;;YAIpBC,wBAAwBV,OAAOW,iBAAiBT;gBAC9C,MAAMU,gBAAgBD,gBAAgBE,YAAY;gBAClD,KAAsB,MAAlBD,eAAsB;oBACxB,MAAME,eAAe;wBACnBT,MAAMH,KAAKG,QAAQ;wBACnBU,MAAMb,KAAKa,QAAQ;;oBAGrB;wBAEEJ,kBAAkBA,gBAAgBK,UAAU,GAAGJ,iBAC/Bf,MAAKoB,iBAAkBH,gBAAgB,OACvCH,gBAAgBK,UAAUJ;AAC5C,sBAAE,OAAOM;wBACPC,QAAQC,KAAK,0CAA0CF,IAAIG;AAC7D;AACF;gBACA,OAAOV;;;AAGb;IAEA,oBAAML,CAAeL,QAAQE;QAC3B;kBAEQ,IAAImB,QAAQC,WAAWC,WAAWD,SAAS,OAEjDpB,IAAIsB,MAAM;YAGV5B,MAAKH,KAAM,IAAIgC,UAAAA,OAAezB,QAAQ;gBACpC0B,MAAM;oBACJC,QAAQ;oBACRC,SAAS,EAAC,OAAO;;gBAEnBC,YAAY,EAAC,WAAW;gBACxBC,YAAW;gBAGblC,MAAKH,GAAIsC,GAAG,cAAeC;gBACzB9B,IAAIsB,MAAM,qBAAqBQ,OAAOC,OAEtCD,OAAOD,GAAG,cAAeG;oBACvBhC,IAAIsB,MAAM,wBAAwBQ,OAAOC,QAAQC;oBAGnDF,OAAOD,GAAG,SAAUI;oBAClBjC,IAAIiC,MAAM,iBAAiBA;;gBAI/BjC,IAAIkC,KAAK;AACX,UAAE,OAAOD;YACPjC,IAAIiC,MAAM,+BAA+BA;AAC3C;AACF;IAEAnB,kBAAqBf,QAAS,iLAIboC,KAAKC,UAAUrC;;;"}
1
+ {"version":3,"file":"LiveReload-BvFz-bG3.cjs","sources":["../../lib/LiveReload.js"],"sourcesContent":["import { Server as SocketIOServer } from 'socket.io';\nimport { ServiceProvider } from '@brickworks/engine';\nimport { queueMacrotask } from './utils/tasks.js';\n\nexport class LiveReload extends ServiceProvider {\n static manifest = {\n name: 'Clovie LiveReload',\n namespace: 'liveReload',\n version: '1.0.0',\n };\n\n #io;\n #reloading = false;\n\n getter () {\n return {\n io: () => this.#io,\n }\n }\n\n actions() {\n return {\n initializeServer: async (server, opts) => {\n const log = this.useContext('log');\n if (opts.mode === 'development') {\n await this.#setupSocketIO(server, log);\n }\n },\n notifyReload: () => {\n if (this.#reloading) return;\n this.#reloading = true;\n \n queueMacrotask(() => {\n this.#reloading = false;\n if (this.#io) {\n this.#io.emit('reload')\n }\n });\n },\n injectLiveReloadScript: async (renderedContent, opts) => {\n const lastBodyIndex = renderedContent.lastIndexOf('</body>');\n if (lastBodyIndex !== -1) {\n const scriptConfig = { \n mode: opts.mode || 'development', \n port: opts.port || 3000 \n };\n \n try {\n // Import live reload script dynamically \n renderedContent = renderedContent.substring(0, lastBodyIndex) + \n this.#liveReloadScript(scriptConfig) + '\\n' + \n renderedContent.substring(lastBodyIndex);\n } catch (err) {\n console.warn('⚠️ Could not load live reload script:', err.message);\n }\n }\n return renderedContent;\n }\n }\n }\n\n async #setupSocketIO(server, log) {\n try {\n // Wait a bit for server to be fully initialized\n await new Promise(resolve => setTimeout(resolve, 100));\n\n log.debug('Setting up Socket.IO...');\n \n // Configure Socket.IO with proper CORS and options\n this.#io = new SocketIOServer(server, {\n cors: {\n origin: \"*\",\n methods: [\"GET\", \"POST\"]\n },\n transports: ['polling', 'websocket'],\n allowEIO3: true\n });\n\n this.#io.on('connection', (socket) => {\n log.debug(`Client connected: ${socket.id}`);\n \n socket.on('disconnect', (reason) => {\n log.debug(`Client disconnected: ${socket.id} - ${reason}`);\n });\n \n socket.on('error', (error) => {\n log.error('Socket error:', error);\n });\n });\n\n log.info('Socket.IO server ready');\n } catch (error) {\n log.error('Error setting up Socket.IO:', error);\n }\n }\n\n #liveReloadScript = (opts) => `<!-- Live Reload Script (Development Mode Only) -->\n<script src=\"https://cdn.socket.io/4.7.4/socket.io.min.js\"></script>\n<script>\n (function() {\n const opts = ${JSON.stringify(opts)};\n \n console.log('Initializing live reload with opts:', opts);\n \n // Configure Socket.IO client with proper options\n const socket = io({\n transports: ['polling', 'websocket'],\n timeout: 20000,\n forceNew: true\n });\n \n socket.on('reload', () => {\n console.log('Live reload triggered');\n window.location.reload();\n });\n \n socket.on('connect', () => {\n console.log('Connected to live reload server');\n });\n \n socket.on('disconnect', (reason) => {\n console.log('Disconnected from live reload server:', reason);\n });\n \n socket.on('connect_error', (error) => {\n console.error('Connection error:', error);\n console.log('Retrying connection in 3 seconds...');\n setTimeout(() => {\n socket.connect();\n }, 3000);\n });\n \n socket.on('error', (error) => {\n console.error('Socket error:', error);\n });\n \n // Add connection timeout\n setTimeout(() => {\n if (!socket.connected) {\n console.warn('Live reload connection timeout - server may not be running');\n }\n }, 5000);\n })();\n</script>`;\n}"],"names":["LiveReload","ServiceProvider","static","name","namespace","version","io","reloading","getter","this","actions","initializeServer","async","server","opts","log","useContext","mode","setupSocketIO","notifyReload","queueMacrotask","emit","injectLiveReloadScript","renderedContent","lastBodyIndex","lastIndexOf","scriptConfig","port","substring","liveReloadScript","err","console","warn","message","Promise","resolve","setTimeout","debug","SocketIOServer","cors","origin","methods","transports","allowEIO3","on","socket","id","reason","error","info","JSON","stringify"],"mappings":";;;;;;;;;AAIO,MAAMA,mBAAmBC,aAAAA;IAC9BC,gBAAkB;QAChBC,MAAM;QACNC,WAAW;QACXC,SAAS;;IAGXC;IACAC,YAAa;IAEb,MAAAC;QACE,OAAO;YACLF,IAAI,MAAMG,MAAKH;;AAEnB;IAEA,OAAAI;QACE,OAAO;YACLC,kBAAkBC,OAAOC,QAAQC;gBAC/B,MAAMC,MAAMN,KAAKO,WAAW;gBACV,kBAAdF,KAAKG,cACDR,MAAKS,cAAeL,QAAQE;;YAGtCI,cAAc;gBACRV,MAAKF,cACTE,MAAKF,aAAa,GAElBa,aAAAA,eAAe;oBACbX,MAAKF,aAAa,GACdE,MAAKH,MACPG,MAAKH,GAAIe,KAAK;;;YAIpBC,wBAAwBV,OAAOW,iBAAiBT;gBAC9C,MAAMU,gBAAgBD,gBAAgBE,YAAY;gBAClD,KAAsB,MAAlBD,eAAsB;oBACxB,MAAME,eAAe;wBACnBT,MAAMH,KAAKG,QAAQ;wBACnBU,MAAMb,KAAKa,QAAQ;;oBAGrB;wBAEEJ,kBAAkBA,gBAAgBK,UAAU,GAAGJ,iBAC/Bf,MAAKoB,iBAAkBH,gBAAgB,OACvCH,gBAAgBK,UAAUJ;AAC5C,sBAAE,OAAOM;wBACPC,QAAQC,KAAK,0CAA0CF,IAAIG;AAC7D;AACF;gBACA,OAAOV;;;AAGb;IAEA,oBAAML,CAAeL,QAAQE;QAC3B;kBAEQ,IAAImB,QAAQC,WAAWC,WAAWD,SAAS,OAEjDpB,IAAIsB,MAAM;YAGV5B,MAAKH,KAAM,IAAIgC,UAAAA,OAAezB,QAAQ;gBACpC0B,MAAM;oBACJC,QAAQ;oBACRC,SAAS,EAAC,OAAO;;gBAEnBC,YAAY,EAAC,WAAW;gBACxBC,YAAW;gBAGblC,MAAKH,GAAIsC,GAAG,cAAeC;gBACzB9B,IAAIsB,MAAM,qBAAqBQ,OAAOC,OAEtCD,OAAOD,GAAG,cAAeG;oBACvBhC,IAAIsB,MAAM,wBAAwBQ,OAAOC,QAAQC;oBAGnDF,OAAOD,GAAG,SAAUI;oBAClBjC,IAAIiC,MAAM,iBAAiBA;;gBAI/BjC,IAAIkC,KAAK;AACX,UAAE,OAAOD;YACPjC,IAAIiC,MAAM,+BAA+BA;AAC3C;AACF;IAEAnB,kBAAqBf,QAAS,iLAIboC,KAAKC,UAAUrC;;;"}
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
 
3
- var createClovie = require("./createClovie-DdMTZ_dO.cjs"), http = require("node:http"), child_process = require("child_process"), util = require("util");
3
+ var createClovie = require("./createClovie-CJ1h3Pfo.cjs"), http = require("node:http"), child_process = require("child_process"), util = require("util");
4
4
 
5
5
  class Kernel {
6
6
  #routes=[];
7
+ #appHandlers=[];
7
8
  #hooks={
8
9
  onRequest: null,
9
10
  preHandler: null,
@@ -22,6 +23,13 @@ class Kernel {
22
23
  };
23
24
  this.#routes.push(compiledRoute);
24
25
  }
26
+ setAppHandlers(handlers = []) {
27
+ this.#appHandlers = Array.isArray(handlers) ? handlers : [];
28
+ }
29
+ async cleanupAppHandlers() {
30
+ for (const handler of this.#appHandlers) handler?.cleanup && await handler.cleanup();
31
+ this.#appHandlers = [];
32
+ }
25
33
  hooks(h) {
26
34
  Object.assign(this.#hooks, h);
27
35
  }
@@ -36,6 +44,16 @@ class Kernel {
36
44
  try {
37
45
  const onReqOut = await (this.#hooks.onRequest?.(ctx));
38
46
  if (onReqOut) return response = onReqOut, response;
47
+ for (const handler of this.#appHandlers) try {
48
+ if (!handler?.match || !handler?.handle) continue;
49
+ if (await handler.match(ctx.req)) {
50
+ const handlerResponse = await handler.handle(ctx);
51
+ if (handlerResponse) return response = handlerResponse, response;
52
+ }
53
+ } catch (handlerError) {
54
+ return response = await (this.#hooks.onError?.(ctx, handlerError)) ?? ctx.respond.text("Internal Server Error", 500),
55
+ response;
56
+ }
39
57
  let matchParams = null;
40
58
  const route = this.#routes.find(r => {
41
59
  if (r.method !== ctx.req.method) return !1;
@@ -165,7 +183,7 @@ class AdapterInterface {
165
183
  headers: {},
166
184
  body: ""
167
185
  };
168
- if (!res.headersSent) {
186
+ if ("handled" !== resp.type && !res.headersSent) {
169
187
  if (res.statusCode = resp.status ?? 200, resp.headers) for (const [k, v] of Object.entries(resp.headers)) res.setHeader(k.toLowerCase(), v);
170
188
  if (isHead) return res.end();
171
189
  switch (resp.type) {
@@ -242,6 +260,11 @@ class AdapterInterface {
242
260
  }
243
261
 
244
262
  class RespondHelpers {
263
+ handled() {
264
+ return {
265
+ type: "handled"
266
+ };
267
+ }
245
268
  json(data, status = 200, headers = {}) {
246
269
  return {
247
270
  type: "json",
@@ -433,11 +456,11 @@ class Server extends createClovie.ServiceProvider {
433
456
  } ]), opts.outputDir && kernel.setStaticFallback(opts.outputDir, (ctx, outputDir) => this.#serveStaticFile(ctx, outputDir)),
434
457
  !this.#adapter) if ("express" === opts.adapter || opts.middleware?.length > 0) {
435
458
  const {ExpressAdapter: ExpressAdapter} = await Promise.resolve().then(function() {
436
- return require("./ExpressAdapter-De7bTuZu.cjs");
459
+ return require("./ExpressAdapter--oUcRq-k.cjs");
437
460
  });
438
461
  this.#adapter = ExpressAdapter.create(), log.debug("Using Express adapter for middleware support");
439
462
  } else this.#adapter = HttpAdapter.create(), log.debug("Using HTTP adapter");
440
- await this.#adapter.initialize(kernel, opts, log);
463
+ await this.#adapter.initialize(kernel, opts, log), "function" == typeof this.#adapter.registerKernelHandler && this.#adapter.registerKernelHandler();
441
464
  const server = await this.#adapter.start({
442
465
  port: port,
443
466
  host: host
@@ -528,4 +551,4 @@ var Server$1 = Object.freeze({
528
551
  });
529
552
 
530
553
  exports.AdapterInterface = AdapterInterface, exports.Server = Server$1;
531
- //# sourceMappingURL=Server-BVelCzau.cjs.map
554
+ //# sourceMappingURL=Server-DkYaMJak.cjs.map