clovie 0.1.28 → 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 (31) hide show
  1. package/README.md +102 -2
  2. package/dist/ExpressAdapter-BsYIpF7A.js +109 -0
  3. package/dist/ExpressAdapter-BsYIpF7A.js.map +1 -0
  4. package/dist/{LiveReload-PORXKqL8.js → LiveReload-ClrG9hAe.js} +8 -2
  5. package/dist/{LiveReload-PORXKqL8.js.map → LiveReload-ClrG9hAe.js.map} +1 -1
  6. package/dist/{Server-BkZZGWiq.js → Server-GcEIC8Hj.js} +185 -160
  7. package/dist/Server-GcEIC8Hj.js.map +1 -0
  8. package/dist/cjs/ExpressAdapter--oUcRq-k.cjs +81 -0
  9. package/dist/cjs/ExpressAdapter--oUcRq-k.cjs.map +1 -0
  10. package/dist/cjs/{LiveReload-YaB3G8c8.cjs → LiveReload-BvFz-bG3.cjs} +4 -3
  11. package/dist/cjs/{LiveReload-YaB3G8c8.cjs.map → LiveReload-BvFz-bG3.cjs.map} +1 -1
  12. package/dist/cjs/{Server-BqbjrDXw.cjs → Server-DkYaMJak.cjs} +63 -14
  13. package/dist/cjs/Server-DkYaMJak.cjs.map +1 -0
  14. package/dist/cjs/{createClovie-bhagLY-k.cjs → createClovie-CJ1h3Pfo.cjs} +74 -12
  15. package/dist/cjs/createClovie-CJ1h3Pfo.cjs.map +1 -0
  16. package/dist/cjs/{index-D5kY4esA.cjs → index-DU3_mYKB.cjs} +4 -4
  17. package/dist/cjs/{index-D5kY4esA.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-ChsQoilz.js → createClovie-Dh7RbB5L.js} +74 -10
  21. package/dist/createClovie-Dh7RbB5L.js.map +1 -0
  22. package/dist/{index-CIb3KPoa.js → index-D8ThZ_1b.js} +6 -2
  23. package/dist/{index-CIb3KPoa.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/templates/server/AI_DEVELOPMENT_GUIDE.md +71 -8
  28. package/dist/Server-BkZZGWiq.js.map +0 -1
  29. package/dist/cjs/Server-BqbjrDXw.cjs.map +0 -1
  30. package/dist/cjs/createClovie-bhagLY-k.cjs.map +0 -1
  31. package/dist/createClovie-ChsQoilz.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
@@ -279,11 +280,23 @@ export default {
279
280
  // Database configuration (optional)
280
281
  dbPath: './data/app.db',
281
282
 
282
- // Express middleware
283
+ // Express middleware (auto-selects Express adapter)
283
284
  middleware: [
284
285
  express.json(),
285
286
  express.urlencoded({ extended: true }),
286
- // Add CORS, authentication, etc.
287
+
288
+ // Authentication middleware for protected routes
289
+ (req, res, next) => {
290
+ if (req.url.startsWith('/api/protected/')) {
291
+ const token = req.headers.authorization?.replace('Bearer ', '');
292
+ if (!token) {
293
+ return res.status(401).json({ error: 'Authentication required' });
294
+ }
295
+ // Verify token and attach user to request
296
+ req.user = verifyJWT(token);
297
+ }
298
+ next();
299
+ }
287
300
  ],
288
301
 
289
302
  // API endpoints
@@ -333,6 +346,93 @@ export default {
333
346
 
334
347
  ## 🚀 Advanced Features
335
348
 
349
+ ### 🔐 Middleware & Authentication
350
+
351
+ Clovie supports Express middleware for server applications. When you configure middleware, Clovie automatically uses the Express adapter for full compatibility.
352
+
353
+ **Common Authentication Pattern:**
354
+ ```javascript
355
+ export default {
356
+ type: 'server',
357
+
358
+ middleware: [
359
+ // Request logging
360
+ (req, res, next) => {
361
+ console.log(`${req.method} ${req.url}`);
362
+ next();
363
+ },
364
+
365
+ // Selective authentication
366
+ (req, res, next) => {
367
+ // Public routes
368
+ const publicPaths = ['/api/login', '/api/health'];
369
+ if (publicPaths.some(path => req.url.startsWith(path))) {
370
+ return next();
371
+ }
372
+
373
+ // Protect /api/protected/* routes
374
+ if (req.url.startsWith('/api/protected/')) {
375
+ const token = req.headers.authorization?.replace('Bearer ', '');
376
+ if (!token) {
377
+ return res.status(401).json({ error: 'Token required' });
378
+ }
379
+ try {
380
+ req.user = verifyJWT(token);
381
+ next();
382
+ } catch (error) {
383
+ res.status(401).json({ error: 'Invalid token' });
384
+ }
385
+ } else {
386
+ next();
387
+ }
388
+ }
389
+ ]
390
+ };
391
+ ```
392
+
393
+ **Test Authentication:**
394
+ ```bash
395
+ # Public endpoint
396
+ curl http://localhost:3000/api/health
397
+
398
+ # Protected endpoint (fails)
399
+ curl http://localhost:3000/api/protected/data
400
+
401
+ # Protected endpoint (works)
402
+ curl -H "Authorization: Bearer your-token" http://localhost:3000/api/protected/data
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
+
336
436
  ### 📊 Database Integration (Server Mode)
337
437
 
338
438
  Clovie includes built-in SQLite database support for server applications:
@@ -0,0 +1,109 @@
1
+ import { A as AdapterInterface } from "./Server-GcEIC8Hj.js";
2
+
3
+ import "./createClovie-Dh7RbB5L.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 "fs/promises";
30
+
31
+ import "child_process";
32
+
33
+ import "url";
34
+
35
+ import "node:http";
36
+
37
+ import "util";
38
+
39
+ class ExpressAdapter extends AdapterInterface {
40
+ #connections=new Set;
41
+ constructor(express = null) {
42
+ super("express"), this.express = express, this.app = null;
43
+ }
44
+ async initialize(kernel, opts = {}, log = null) {
45
+ if (await super.initialize(kernel, opts, log), !this.express) try {
46
+ this.express = await import("express"), this.express = this.express.default;
47
+ } catch (error) {
48
+ throw new Error("Express not installed. Run: npm install express");
49
+ }
50
+ if (this.app = this.express(), opts.middleware && this.#hasBodyParser(opts.middleware) || (this.app.use(this.express.json()),
51
+ this.app.use(this.express.urlencoded({
52
+ extended: !0
53
+ }))), opts.middleware && Array.isArray(opts.middleware)) {
54
+ this.log?.info(`Applying ${opts.middleware.length} middleware functions`);
55
+ for (const [index, middleware] of opts.middleware.entries()) try {
56
+ this.app.use(middleware), this.log?.debug(`Applied middleware ${index + 1}/${opts.middleware.length}`);
57
+ } catch (error) {
58
+ throw new Error(`Failed to apply middleware[${index}]: ${error.message}`);
59
+ }
60
+ }
61
+ }
62
+ registerKernelHandler() {
63
+ this.app && this.app.all("*", async (req, res) => {
64
+ await this.handleRequest(req, res);
65
+ });
66
+ }
67
+ getExpressApp() {
68
+ return this.app;
69
+ }
70
+ async start({port: port = 3e3, host: host = "0.0.0.0"} = {}) {
71
+ return new Promise((resolve, reject) => {
72
+ this.server = this.app.listen(port, host, err => {
73
+ err ? reject(err) : (this.server.on("connection", conn => {
74
+ this.#connections.add(conn), conn.on("close", () => {
75
+ this.#connections.delete(conn);
76
+ });
77
+ }), this.log?.info(`Express Server with middleware listening on http://${host}:${port}`),
78
+ resolve(this.server));
79
+ });
80
+ });
81
+ }
82
+ async stop() {
83
+ if (this.server?.close) {
84
+ for (const conn of this.#connections) conn.destroy();
85
+ return this.#connections.clear(), new Promise(resolve => {
86
+ this.server.close(() => {
87
+ this.log?.info("Express Server stopped"), resolve();
88
+ });
89
+ });
90
+ }
91
+ }
92
+ async handleRequest(req, res) {
93
+ try {
94
+ const ctx = this.createContext(req, res), response = await this.kernel.handle(ctx);
95
+ await this.sendResponse(res, response, "HEAD" === req.method);
96
+ } catch (error) {
97
+ await this.handleError(res, error);
98
+ }
99
+ }
100
+ #hasBodyParser(middlewareArray) {
101
+ return middlewareArray.some(mw => {
102
+ const name = mw.name?.toLowerCase() || mw.toString().toLowerCase();
103
+ return name.includes("json") || name.includes("urlencoded") || name.includes("bodyparser");
104
+ });
105
+ }
106
+ }
107
+
108
+ export { ExpressAdapter };
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-ChsQoilz.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-PORXKqL8.js.map
101
+ //# sourceMappingURL=LiveReload-ClrG9hAe.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LiveReload-PORXKqL8.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;;;"}