clovie 0.1.26 → 0.1.29

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 (29) hide show
  1. package/README.md +70 -2
  2. package/dist/ExpressAdapter-DL0Y9BG2.js +100 -0
  3. package/dist/ExpressAdapter-DL0Y9BG2.js.map +1 -0
  4. package/dist/{LiveReload-OGN9VlKq.js → LiveReload-CXR08AFg.js} +2 -2
  5. package/dist/{LiveReload-OGN9VlKq.js.map → LiveReload-CXR08AFg.js.map} +1 -1
  6. package/dist/{Server-ChPACc2X.js → Server-BR7ZqQBN.js} +161 -159
  7. package/dist/Server-BR7ZqQBN.js.map +1 -0
  8. package/dist/cjs/ExpressAdapter-De7bTuZu.cjs +75 -0
  9. package/dist/cjs/ExpressAdapter-De7bTuZu.cjs.map +1 -0
  10. package/dist/cjs/{LiveReload-B9fx9nCV.cjs → LiveReload-TdgPIiCn.cjs} +2 -2
  11. package/dist/cjs/{LiveReload-B9fx9nCV.cjs.map → LiveReload-TdgPIiCn.cjs.map} +1 -1
  12. package/dist/cjs/{Server-BaCoAz1f.cjs → Server-BVelCzau.cjs} +39 -13
  13. package/dist/cjs/Server-BVelCzau.cjs.map +1 -0
  14. package/dist/cjs/{createClovie-CKxeV_aK.cjs → createClovie-DdMTZ_dO.cjs} +54 -23
  15. package/dist/cjs/createClovie-DdMTZ_dO.cjs.map +1 -0
  16. package/dist/cjs/{index-CXdRvvQ8.cjs → index-9MtAg70f.cjs} +3 -3
  17. package/dist/cjs/{index-CXdRvvQ8.cjs.map → index-9MtAg70f.cjs.map} +1 -1
  18. package/dist/cjs/index.cjs +1 -1
  19. package/dist/{createClovie-BxgkSnM8.js → createClovie-LSdi1lXJ.js} +53 -22
  20. package/dist/createClovie-LSdi1lXJ.js.map +1 -0
  21. package/dist/{index-DUZoyjx2.js → index-CpXKm4Ga.js} +2 -2
  22. package/dist/{index-DUZoyjx2.js.map → index-CpXKm4Ga.js.map} +1 -1
  23. package/dist/index.js +1 -1
  24. package/package.json +2 -2
  25. package/templates/server/AI_DEVELOPMENT_GUIDE.md +71 -8
  26. package/dist/Server-ChPACc2X.js.map +0 -1
  27. package/dist/cjs/Server-BaCoAz1f.cjs.map +0 -1
  28. package/dist/cjs/createClovie-CKxeV_aK.cjs.map +0 -1
  29. package/dist/createClovie-BxgkSnM8.js.map +0 -1
package/README.md CHANGED
@@ -279,11 +279,23 @@ export default {
279
279
  // Database configuration (optional)
280
280
  dbPath: './data/app.db',
281
281
 
282
- // Express middleware
282
+ // Express middleware (auto-selects Express adapter)
283
283
  middleware: [
284
284
  express.json(),
285
285
  express.urlencoded({ extended: true }),
286
- // Add CORS, authentication, etc.
286
+
287
+ // Authentication middleware for protected routes
288
+ (req, res, next) => {
289
+ if (req.url.startsWith('/api/protected/')) {
290
+ const token = req.headers.authorization?.replace('Bearer ', '');
291
+ if (!token) {
292
+ return res.status(401).json({ error: 'Authentication required' });
293
+ }
294
+ // Verify token and attach user to request
295
+ req.user = verifyJWT(token);
296
+ }
297
+ next();
298
+ }
287
299
  ],
288
300
 
289
301
  // API endpoints
@@ -333,6 +345,62 @@ export default {
333
345
 
334
346
  ## 🚀 Advanced Features
335
347
 
348
+ ### 🔐 Middleware & Authentication
349
+
350
+ Clovie supports Express middleware for server applications. When you configure middleware, Clovie automatically uses the Express adapter for full compatibility.
351
+
352
+ **Common Authentication Pattern:**
353
+ ```javascript
354
+ export default {
355
+ type: 'server',
356
+
357
+ middleware: [
358
+ // Request logging
359
+ (req, res, next) => {
360
+ console.log(`${req.method} ${req.url}`);
361
+ next();
362
+ },
363
+
364
+ // Selective authentication
365
+ (req, res, next) => {
366
+ // Public routes
367
+ const publicPaths = ['/api/login', '/api/health'];
368
+ if (publicPaths.some(path => req.url.startsWith(path))) {
369
+ return next();
370
+ }
371
+
372
+ // Protect /api/protected/* routes
373
+ if (req.url.startsWith('/api/protected/')) {
374
+ const token = req.headers.authorization?.replace('Bearer ', '');
375
+ if (!token) {
376
+ return res.status(401).json({ error: 'Token required' });
377
+ }
378
+ try {
379
+ req.user = verifyJWT(token);
380
+ next();
381
+ } catch (error) {
382
+ res.status(401).json({ error: 'Invalid token' });
383
+ }
384
+ } else {
385
+ next();
386
+ }
387
+ }
388
+ ]
389
+ };
390
+ ```
391
+
392
+ **Test Authentication:**
393
+ ```bash
394
+ # Public endpoint
395
+ curl http://localhost:3000/api/health
396
+
397
+ # Protected endpoint (fails)
398
+ curl http://localhost:3000/api/protected/data
399
+
400
+ # Protected endpoint (works)
401
+ curl -H "Authorization: Bearer your-token" http://localhost:3000/api/protected/data
402
+ ```
403
+
336
404
  ### 📊 Database Integration (Server Mode)
337
405
 
338
406
  Clovie includes built-in SQLite database support for server applications:
@@ -0,0 +1,100 @@
1
+ import { A as AdapterInterface } from "./Server-BR7ZqQBN.js";
2
+
3
+ import "./createClovie-LSdi1lXJ.js";
4
+
5
+ import "stream";
6
+
7
+ import "module";
8
+
9
+ import "path";
10
+
11
+ import "crypto";
12
+
13
+ import "fs";
14
+
15
+ import "istextorbinary";
16
+
17
+ import "chokidar";
18
+
19
+ import "readline";
20
+
21
+ import "events";
22
+
23
+ import "node:process";
24
+
25
+ import "node:os";
26
+
27
+ import "node:tty";
28
+
29
+ import "node:http";
30
+
31
+ import "child_process";
32
+
33
+ import "util";
34
+
35
+ class ExpressAdapter extends AdapterInterface {
36
+ #connections=new Set;
37
+ constructor(express = null) {
38
+ super("express"), this.express = express, this.app = null;
39
+ }
40
+ async initialize(kernel, opts = {}, log = null) {
41
+ if (await super.initialize(kernel, opts, log), !this.express) try {
42
+ this.express = await import("express"), this.express = this.express.default;
43
+ } catch (error) {
44
+ throw new Error("Express not installed. Run: npm install express");
45
+ }
46
+ if (this.app = this.express(), opts.middleware && this.#hasBodyParser(opts.middleware) || (this.app.use(this.express.json()),
47
+ this.app.use(this.express.urlencoded({
48
+ extended: !0
49
+ }))), opts.middleware && Array.isArray(opts.middleware)) {
50
+ this.log?.info(`Applying ${opts.middleware.length} middleware functions`);
51
+ for (const [index, middleware] of opts.middleware.entries()) try {
52
+ this.app.use(middleware), this.log?.debug(`Applied middleware ${index + 1}/${opts.middleware.length}`);
53
+ } catch (error) {
54
+ throw new Error(`Failed to apply middleware[${index}]: ${error.message}`);
55
+ }
56
+ }
57
+ this.app.all("*", async (req, res) => {
58
+ await this.handleRequest(req, res);
59
+ });
60
+ }
61
+ async start({port: port = 3e3, host: host = "0.0.0.0"} = {}) {
62
+ return new Promise((resolve, reject) => {
63
+ this.server = this.app.listen(port, host, err => {
64
+ err ? reject(err) : (this.server.on("connection", conn => {
65
+ this.#connections.add(conn), conn.on("close", () => {
66
+ this.#connections.delete(conn);
67
+ });
68
+ }), this.log?.info(`Express Server with middleware listening on http://${host}:${port}`),
69
+ resolve(this.server));
70
+ });
71
+ });
72
+ }
73
+ async stop() {
74
+ if (this.server?.close) {
75
+ for (const conn of this.#connections) conn.destroy();
76
+ return this.#connections.clear(), new Promise(resolve => {
77
+ this.server.close(() => {
78
+ this.log?.info("Express Server stopped"), resolve();
79
+ });
80
+ });
81
+ }
82
+ }
83
+ async handleRequest(req, res) {
84
+ try {
85
+ const ctx = this.createContext(req, res), response = await this.kernel.handle(ctx);
86
+ await this.sendResponse(res, response, "HEAD" === req.method);
87
+ } catch (error) {
88
+ await this.handleError(res, error);
89
+ }
90
+ }
91
+ #hasBodyParser(middlewareArray) {
92
+ return middlewareArray.some(mw => {
93
+ const name = mw.name?.toLowerCase() || mw.toString().toLowerCase();
94
+ return name.includes("json") || name.includes("urlencoded") || name.includes("bodyparser");
95
+ });
96
+ }
97
+ }
98
+
99
+ export { ExpressAdapter };
100
+ //# sourceMappingURL=ExpressAdapter-DL0Y9BG2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpressAdapter-DL0Y9BG2.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 // Add catch-all route to delegate to kernel\n this.app.all('*', async (req, res) => {\n await this.handleRequest(req, res);\n });\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","all","async","req","res","handleRequest","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;QAGAvB,KAAKC,IAAIuB,IAAI,KAAKC,OAAOC,KAAKC;kBACtB3B,KAAK4B,cAAcF,KAAKC;;AAElC;IAEA,WAAME,EAAMC,MAAEA,OAAO,KAAIC,MAAEA,OAAO,aAAc;QAC9C,OAAO,IAAIC,QAAQ,CAACC,SAASC;YAC3BlC,KAAKmC,SAASnC,KAAKC,IAAImC,OAAON,MAAMC,MAAOM;gBACrCA,MACFH,OAAOG,QAGPrC,KAAKmC,OAAOG,GAAG,cAAeC;oBAC5BvC,MAAKL,YAAa6C,IAAID,OACtBA,KAAKD,GAAG,SAAS;wBACftC,MAAKL,YAAa8C,OAAOF;;oBAI7BvC,KAAKK,KAAKa,KAAK,sDAAsDa,QAAQD;gBAC7EG,QAAQjC,KAAKmC;;;AAIrB;IAEA,UAAMO;QACJ,IAAI1C,KAAKmC,QAAQQ,OAAO;YAEtB,KAAK,MAAMJ,QAAQvC,MAAKL,aACtB4C,KAAKK;YAIP,OAFA5C,MAAKL,YAAakD,SAEX,IAAIb,QAASC;gBAClBjC,KAAKmC,OAAOQ,MAAM;oBAChB3C,KAAKK,KAAKa,KAAK,2BACfe;;;AAGN;AACF;IAEA,mBAAML,CAAcF,KAAKC;QACvB;YAEE,MAAMmB,MAAM9C,KAAK+C,cAAcrB,KAAKC,MAG9BqB,iBAAiBhD,KAAKG,OAAO8C,OAAOH;kBAGpC9C,KAAKkD,aAAavB,KAAKqB,UAAyB,WAAftB,IAAIyB;AAE7C,UAAE,OAAO3C;kBACDR,KAAKoD,YAAYzB,KAAKnB;AAC9B;AACF;IAMA,cAAAG,CAAe0C;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-BxgkSnM8.js";
3
+ import { S as ServiceProvider, q as queueMacrotask } from "./createClovie-LSdi1lXJ.js";
4
4
 
5
5
  import "stream";
6
6
 
@@ -92,4 +92,4 @@ class LiveReload extends ServiceProvider {
92
92
  }
93
93
 
94
94
  export { LiveReload };
95
- //# sourceMappingURL=LiveReload-OGN9VlKq.js.map
95
+ //# sourceMappingURL=LiveReload-CXR08AFg.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LiveReload-OGN9VlKq.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-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,4 +1,4 @@
1
- import { c as compileRoute, a as chalk, S as ServiceProvider } from "./createClovie-BxgkSnM8.js";
1
+ import { c as compileRoute, a as chalk, S as ServiceProvider } from "./createClovie-LSdi1lXJ.js";
2
2
 
3
3
  import http from "node:http";
4
4
 
@@ -6,30 +6,6 @@ import { exec } from "child_process";
6
6
 
7
7
  import { promisify } from "util";
8
8
 
9
- import "stream";
10
-
11
- import "module";
12
-
13
- import "path";
14
-
15
- import "crypto";
16
-
17
- import "fs";
18
-
19
- import "istextorbinary";
20
-
21
- import "chokidar";
22
-
23
- import "readline";
24
-
25
- import "events";
26
-
27
- import "node:process";
28
-
29
- import "node:os";
30
-
31
- import "node:tty";
32
-
33
9
  class Kernel {
34
10
  #routes=[];
35
11
  #hooks={
@@ -148,8 +124,8 @@ class AdapterInterface {
148
124
  constructor(name) {
149
125
  this.name = name, this.server = null, this.kernel = null, this.log = null, this.hooks = null;
150
126
  }
151
- async initialize(kernel, log) {
152
- this.kernel = kernel, this.log = log;
127
+ async initialize(kernel, opts = {}, log = null) {
128
+ if (this.kernel = kernel, this.opts = opts, this.log = log, opts.middleware && !Array.isArray(opts.middleware)) throw new Error("opts.middleware must be an array");
153
129
  }
154
130
  async start(options) {
155
131
  throw new Error(`start() must be implemented by ${this.name} adapter`);
@@ -328,12 +304,16 @@ class HttpAdapter extends AdapterInterface {
328
304
  constructor() {
329
305
  super("http");
330
306
  }
331
- async initialize(kernel, services, log) {
332
- await super.initialize(kernel, services, log);
307
+ async initialize(kernel, opts = {}, log = null) {
308
+ await super.initialize(kernel, opts, log), opts.middleware?.length > 0 && (log?.warn('Middleware configured but HTTP adapter selected. Consider using adapter: "express" for full middleware support.'),
309
+ log?.info("HTTP adapter will simulate middleware using hooks for basic compatibility."));
333
310
  }
334
311
  async start({port: port = 3e3, host: host = "0.0.0.0"} = {}) {
335
312
  return this.server = http.createServer(async (req, res) => {
336
313
  try {
314
+ if (this.opts.middleware?.length > 0) {
315
+ if (!await this.#simulateMiddleware(req, res, this.opts.middleware)) return;
316
+ }
337
317
  req.body || (req.body = await parseBody(req));
338
318
  const ctx = this.createContext(req, res), out = await this.kernel.handle(ctx);
339
319
  await this.sendResponse(res, out, "HEAD" === req.method);
@@ -354,6 +334,21 @@ class HttpAdapter extends AdapterInterface {
354
334
  this.server = null;
355
335
  }
356
336
  }
337
+ async #simulateMiddleware(req, res, middlewareArray) {
338
+ for (const middleware of middlewareArray) try {
339
+ let nextCalled = !1;
340
+ const next = err => {
341
+ if (err) throw err;
342
+ nextCalled = !0;
343
+ };
344
+ if (res.json || (res.json = data => (res.setHeader("Content-Type", "application/json"),
345
+ res.end(JSON.stringify(data)), !1)), await middleware(req, res, next), !nextCalled && res.headersSent) return !1;
346
+ } catch (error) {
347
+ return this.log?.error(`Middleware simulation error: ${error.message}`), res.statusCode = 500,
348
+ res.end("Internal Server Error"), !1;
349
+ }
350
+ return !0;
351
+ }
357
352
  }
358
353
 
359
354
  const execAsync = promisify(exec);
@@ -388,142 +383,149 @@ async function displayServerReady({port: port, host: host, mode: mode}, logger)
388
383
  [ "", chalk.gray("💡 Quick Commands:"), chalk.gray(` • Press ${chalk.yellow("Ctrl+C")} to stop the server`), chalk.gray(` • Visit ${chalk.cyan(url)} to view your site`), chalk.gray(" • Check the console for live reload updates"), "" ].forEach(line => logger.info(line));
389
384
  }
390
385
 
391
- class Server extends ServiceProvider {
392
- static manifest={
393
- name: "Clovie Server",
394
- namespace: "server",
395
- version: "1.0.0"
396
- };
397
- #routes=[];
398
- #adapter=null;
399
- #hooks={
400
- onRequest: null,
401
- preHandler: null,
402
- onSend: null,
403
- onError: null
404
- };
405
- actions(useContext) {
406
- return {
407
- add: (method, path, handler, meta = {}) => {
408
- this.#routes.push({
409
- method: method.toUpperCase(),
410
- path: path,
411
- handler: handler,
412
- meta: meta
413
- });
414
- },
415
- useAdapter: adapter => (this.#adapter = adapter, this),
416
- hooks: hooks => (this.#hooks = {
417
- ...this.#hooks,
418
- ...hooks
419
- }, this),
420
- listen: async (opts = {}) => {
421
- const relay = useContext("relay"), port = opts.port || 3e3, host = opts.host || "0.0.0.0", log = useContext("log"), kernel = new Kernel({
422
- state: useContext("state"),
423
- stable: useContext("stable")
424
- });
425
- kernel.hooks(this.#hooks), kernel.registerRoutes(this.#routes), kernel.registerRoutes([ {
426
- method: "GET",
427
- path: "/health",
428
- handler: ctx => ctx.respond.json({
429
- status: "ok",
430
- timestamp: (new Date).toISOString(),
386
+ var Server$1 = Object.freeze({
387
+ __proto__: null,
388
+ Server: class extends ServiceProvider {
389
+ static manifest={
390
+ name: "Clovie Server",
391
+ namespace: "server",
392
+ version: "1.0.0"
393
+ };
394
+ #routes=[];
395
+ #adapter=null;
396
+ #hooks={
397
+ onRequest: null,
398
+ preHandler: null,
399
+ onSend: null,
400
+ onError: null
401
+ };
402
+ actions(useContext) {
403
+ return {
404
+ add: (method, path, handler, meta = {}) => {
405
+ this.#routes.push({
406
+ method: method.toUpperCase(),
407
+ path: path,
408
+ handler: handler,
409
+ meta: meta
410
+ });
411
+ },
412
+ useAdapter: adapter => (this.#adapter = adapter, this),
413
+ hooks: hooks => (this.#hooks = {
414
+ ...this.#hooks,
415
+ ...hooks
416
+ }, this),
417
+ listen: async (opts = {}) => {
418
+ const relay = useContext("relay"), port = opts.port || 3e3, host = opts.host || "0.0.0.0", log = useContext("log"), kernel = new Kernel({
419
+ state: useContext("state"),
420
+ stable: useContext("stable")
421
+ });
422
+ if (kernel.hooks(this.#hooks), kernel.registerRoutes(this.#routes), kernel.registerRoutes([ {
423
+ method: "GET",
424
+ path: "/health",
425
+ handler: ctx => ctx.respond.json({
426
+ status: "ok",
427
+ timestamp: (new Date).toISOString(),
428
+ mode: opts.mode || "production"
429
+ })
430
+ }, {
431
+ method: "GET",
432
+ path: "/api/info",
433
+ handler: ctx => ctx.respond.json({
434
+ name: "Clovie",
435
+ version: "1.0.0",
436
+ mode: opts.mode || "production",
437
+ routes: this.#routes.length
438
+ })
439
+ } ]), opts.outputDir && kernel.setStaticFallback(opts.outputDir, (ctx, outputDir) => this.#serveStaticFile(ctx, outputDir)),
440
+ !this.#adapter) if ("express" === opts.adapter || opts.middleware?.length > 0) {
441
+ const {ExpressAdapter: ExpressAdapter} = await import("./ExpressAdapter-DL0Y9BG2.js");
442
+ this.#adapter = ExpressAdapter.create(), log.debug("Using Express adapter for middleware support");
443
+ } else this.#adapter = HttpAdapter.create(), log.debug("Using HTTP adapter");
444
+ await this.#adapter.initialize(kernel, opts, log);
445
+ const server = await this.#adapter.start({
446
+ port: port,
447
+ host: host
448
+ });
449
+ try {
450
+ const liveReload = useContext("liveReload");
451
+ log.debug(`LiveReload service found: ${!!liveReload}, mode: ${opts.mode}`), liveReload && "development" === opts.mode && (log.info("Initializing LiveReload..."),
452
+ await liveReload.initializeServer(server, opts), log.info("LiveReload initialized successfully"));
453
+ } catch (error) {
454
+ log.debug("LiveReload not available:", error.message);
455
+ }
456
+ const actualPort = server.address()?.port || port;
457
+ return await displayServerReady({
458
+ port: actualPort,
459
+ host: host,
431
460
  mode: opts.mode || "production"
432
- })
433
- }, {
434
- method: "GET",
435
- path: "/api/info",
436
- handler: ctx => ctx.respond.json({
437
- name: "Clovie",
438
- version: "1.0.0",
439
- mode: opts.mode || "production",
440
- routes: this.#routes.length
441
- })
442
- } ]), opts.outputDir && kernel.setStaticFallback(opts.outputDir, (ctx, outputDir) => this.#serveStaticFile(ctx, outputDir)),
443
- this.#adapter = this.#adapter ?? HttpAdapter.create(), await this.#adapter.initialize(kernel, log);
444
- const server = await this.#adapter.start({
445
- port: port,
446
- host: host
447
- });
461
+ }, log), relay.broadcast("server:ready", server), server;
462
+ },
463
+ getRoutes: () => this.#routes,
464
+ clearRoutes: () => {
465
+ this.#routes = [];
466
+ },
467
+ stop: async () => {
468
+ this.#adapter && await this.#adapter.stop();
469
+ },
470
+ isRunning: () => this.#adapter && this.#adapter.isRunning(),
471
+ getHttpServer: () => this.#adapter ? this.#adapter.getHttpServer() : null
472
+ };
473
+ }
474
+ async #serveStaticFile(context, outputDir) {
475
+ const fs = await import("fs/promises"), path = await import("path");
476
+ try {
477
+ const requestPath = "/" === context.req.path ? "/index.html" : context.req.path;
478
+ let stats, filePath = path.join(outputDir, requestPath);
448
479
  try {
449
- const liveReload = useContext("liveReload");
450
- log.debug(`LiveReload service found: ${!!liveReload}, mode: ${opts.mode}`), liveReload && "development" === opts.mode && (log.info("Initializing LiveReload..."),
451
- await liveReload.initializeServer(server, opts), log.info("LiveReload initialized successfully"));
480
+ stats = await fs.stat(filePath);
452
481
  } catch (error) {
453
- log.debug("LiveReload not available:", error.message);
454
- }
455
- const actualPort = server.address()?.port || port;
456
- return await displayServerReady({
457
- port: actualPort,
458
- host: host,
459
- mode: opts.mode || "production"
460
- }, log), relay.broadcast("server:ready", server), server;
461
- },
462
- getRoutes: () => this.#routes,
463
- clearRoutes: () => {
464
- this.#routes = [];
465
- },
466
- stop: async () => {
467
- this.#adapter && await this.#adapter.stop();
468
- },
469
- isRunning: () => this.#adapter && this.#adapter.isRunning(),
470
- getHttpServer: () => this.#adapter ? this.#adapter.getHttpServer() : null
471
- };
472
- }
473
- async #serveStaticFile(context, outputDir) {
474
- const fs = await import("fs/promises"), path = await import("path");
475
- try {
476
- const requestPath = "/" === context.req.path ? "/index.html" : context.req.path;
477
- let stats, filePath = path.join(outputDir, requestPath);
478
- try {
479
- stats = await fs.stat(filePath);
480
- } catch (error) {
481
- if (path.extname(requestPath)) return context.respond.text("Not Found", 404);
482
- {
483
- const htmlPath = path.join(outputDir, `${requestPath}.html`);
484
- try {
485
- stats = await fs.stat(htmlPath), filePath = htmlPath;
486
- } catch (htmlError) {
487
- return context.respond.text("Not Found", 404);
482
+ if (path.extname(requestPath)) return context.respond.text("Not Found", 404);
483
+ {
484
+ const htmlPath = path.join(outputDir, `${requestPath}.html`);
485
+ try {
486
+ stats = await fs.stat(htmlPath), filePath = htmlPath;
487
+ } catch (htmlError) {
488
+ return context.respond.text("Not Found", 404);
489
+ }
488
490
  }
489
491
  }
492
+ if (stats.isFile()) {
493
+ const ext = path.extname(filePath), contentType = this.#getContentType(ext), content = await fs.readFile(filePath);
494
+ return "text/html" === contentType ? context.respond.html(content.toString(), 200) : "text/css" === contentType ? context.respond.text(content.toString(), 200, {
495
+ "Content-Type": "text/css"
496
+ }) : "application/javascript" === contentType ? context.respond.text(content.toString(), 200, {
497
+ "Content-Type": "application/javascript"
498
+ }) : contentType.startsWith("text/") ? context.respond.text(content.toString(), 200, {
499
+ "Content-Type": contentType
500
+ }) : context.respond.file(filePath, 200, {
501
+ "Content-Type": contentType
502
+ });
503
+ }
504
+ return context.respond.text("Not Found", 404);
505
+ } catch (error) {
506
+ return context.respond.text("Not Found", 404);
490
507
  }
491
- if (stats.isFile()) {
492
- const ext = path.extname(filePath), contentType = this.#getContentType(ext), content = await fs.readFile(filePath);
493
- return "text/html" === contentType ? context.respond.html(content.toString(), 200) : "text/css" === contentType ? context.respond.text(content.toString(), 200, {
494
- "Content-Type": "text/css"
495
- }) : "application/javascript" === contentType ? context.respond.text(content.toString(), 200, {
496
- "Content-Type": "application/javascript"
497
- }) : contentType.startsWith("text/") ? context.respond.text(content.toString(), 200, {
498
- "Content-Type": contentType
499
- }) : context.respond.file(filePath, 200, {
500
- "Content-Type": contentType
501
- });
502
- }
503
- return context.respond.text("Not Found", 404);
504
- } catch (error) {
505
- return context.respond.text("Not Found", 404);
508
+ }
509
+ #getContentType(ext) {
510
+ return {
511
+ ".html": "text/html",
512
+ ".css": "text/css",
513
+ ".js": "application/javascript",
514
+ ".json": "application/json",
515
+ ".png": "image/png",
516
+ ".jpg": "image/jpeg",
517
+ ".jpeg": "image/jpeg",
518
+ ".gif": "image/gif",
519
+ ".svg": "image/svg+xml",
520
+ ".ico": "image/x-icon",
521
+ ".woff": "font/woff",
522
+ ".woff2": "font/woff2",
523
+ ".ttf": "font/ttf",
524
+ ".eot": "application/vnd.ms-fontobject"
525
+ }[ext.toLowerCase()] || "application/octet-stream";
506
526
  }
507
527
  }
508
- #getContentType(ext) {
509
- return {
510
- ".html": "text/html",
511
- ".css": "text/css",
512
- ".js": "application/javascript",
513
- ".json": "application/json",
514
- ".png": "image/png",
515
- ".jpg": "image/jpeg",
516
- ".jpeg": "image/jpeg",
517
- ".gif": "image/gif",
518
- ".svg": "image/svg+xml",
519
- ".ico": "image/x-icon",
520
- ".woff": "font/woff",
521
- ".woff2": "font/woff2",
522
- ".ttf": "font/ttf",
523
- ".eot": "application/vnd.ms-fontobject"
524
- }[ext.toLowerCase()] || "application/octet-stream";
525
- }
526
- }
528
+ });
527
529
 
528
- export { Server };
529
- //# sourceMappingURL=Server-ChPACc2X.js.map
530
+ export { AdapterInterface as A, Server$1 as S };
531
+ //# sourceMappingURL=Server-BR7ZqQBN.js.map