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.
- package/README.md +70 -2
- package/dist/ExpressAdapter-DL0Y9BG2.js +100 -0
- package/dist/ExpressAdapter-DL0Y9BG2.js.map +1 -0
- package/dist/{LiveReload-OGN9VlKq.js → LiveReload-CXR08AFg.js} +2 -2
- package/dist/{LiveReload-OGN9VlKq.js.map → LiveReload-CXR08AFg.js.map} +1 -1
- package/dist/{Server-ChPACc2X.js → Server-BR7ZqQBN.js} +161 -159
- package/dist/Server-BR7ZqQBN.js.map +1 -0
- package/dist/cjs/ExpressAdapter-De7bTuZu.cjs +75 -0
- package/dist/cjs/ExpressAdapter-De7bTuZu.cjs.map +1 -0
- package/dist/cjs/{LiveReload-B9fx9nCV.cjs → LiveReload-TdgPIiCn.cjs} +2 -2
- package/dist/cjs/{LiveReload-B9fx9nCV.cjs.map → LiveReload-TdgPIiCn.cjs.map} +1 -1
- package/dist/cjs/{Server-BaCoAz1f.cjs → Server-BVelCzau.cjs} +39 -13
- package/dist/cjs/Server-BVelCzau.cjs.map +1 -0
- package/dist/cjs/{createClovie-CKxeV_aK.cjs → createClovie-DdMTZ_dO.cjs} +54 -23
- package/dist/cjs/createClovie-DdMTZ_dO.cjs.map +1 -0
- package/dist/cjs/{index-CXdRvvQ8.cjs → index-9MtAg70f.cjs} +3 -3
- package/dist/cjs/{index-CXdRvvQ8.cjs.map → index-9MtAg70f.cjs.map} +1 -1
- package/dist/cjs/index.cjs +1 -1
- package/dist/{createClovie-BxgkSnM8.js → createClovie-LSdi1lXJ.js} +53 -22
- package/dist/createClovie-LSdi1lXJ.js.map +1 -0
- package/dist/{index-DUZoyjx2.js → index-CpXKm4Ga.js} +2 -2
- package/dist/{index-DUZoyjx2.js.map → index-CpXKm4Ga.js.map} +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/templates/server/AI_DEVELOPMENT_GUIDE.md +71 -8
- package/dist/Server-ChPACc2X.js.map +0 -1
- package/dist/cjs/Server-BaCoAz1f.cjs.map +0 -1
- package/dist/cjs/createClovie-CKxeV_aK.cjs.map +0 -1
- 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
|
-
|
|
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-
|
|
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-
|
|
95
|
+
//# sourceMappingURL=LiveReload-CXR08AFg.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveReload-
|
|
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-
|
|
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,
|
|
332
|
-
await super.initialize(kernel,
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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
|
-
|
|
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
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
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
|
-
|
|
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-
|
|
530
|
+
export { AdapterInterface as A, Server$1 as S };
|
|
531
|
+
//# sourceMappingURL=Server-BR7ZqQBN.js.map
|