clovie 0.1.29 → 0.1.33
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 +26 -25
- package/bin/cli.js +0 -10
- package/dist/{ExpressAdapter-DL0Y9BG2.js → ExpressAdapter-D6owl_CX.js} +16 -11
- package/dist/ExpressAdapter-D6owl_CX.js.map +1 -0
- package/dist/cjs/{ExpressAdapter-De7bTuZu.cjs → ExpressAdapter-Z_FtN3dq.cjs} +13 -8
- package/dist/cjs/ExpressAdapter-Z_FtN3dq.cjs.map +1 -0
- package/dist/cjs/createClovie-BziskasS.cjs +4260 -0
- package/dist/cjs/createClovie-BziskasS.cjs.map +1 -0
- package/dist/cjs/index.cjs +5 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/createClovie-x0YNcttr.js +4282 -0
- package/dist/createClovie-x0YNcttr.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +13 -5
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
- package/scripts/build-types.js +1 -1
- package/templates/server/AI_DEVELOPMENT_GUIDE.md +59 -439
- package/templates/server/README.md +21 -116
- package/templates/server/clovie.config.js +7 -98
- package/templates/server/views/about.html +2 -12
- package/templates/server/views/index.html +2 -2
- package/templates/static/AI_DEVELOPMENT_GUIDE.md +20 -77
- package/dist/ExpressAdapter-DL0Y9BG2.js.map +0 -1
- package/dist/LiveReload-CXR08AFg.js +0 -95
- package/dist/LiveReload-CXR08AFg.js.map +0 -1
- package/dist/Server-BR7ZqQBN.js +0 -531
- package/dist/Server-BR7ZqQBN.js.map +0 -1
- package/dist/cjs/ExpressAdapter-De7bTuZu.cjs.map +0 -1
- package/dist/cjs/LiveReload-TdgPIiCn.cjs +0 -75
- package/dist/cjs/LiveReload-TdgPIiCn.cjs.map +0 -1
- package/dist/cjs/Server-BVelCzau.cjs +0 -531
- package/dist/cjs/Server-BVelCzau.cjs.map +0 -1
- package/dist/cjs/createClovie-DdMTZ_dO.cjs +0 -6380
- package/dist/cjs/createClovie-DdMTZ_dO.cjs.map +0 -1
- package/dist/cjs/index-9MtAg70f.cjs +0 -461
- package/dist/cjs/index-9MtAg70f.cjs.map +0 -1
- package/dist/createClovie-LSdi1lXJ.js +0 -6396
- package/dist/createClovie-LSdi1lXJ.js.map +0 -1
- package/dist/index-CpXKm4Ga.js +0 -480
- package/dist/index-CpXKm4Ga.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> *"The Hollow Knight of Web Dev"* - Simple but deep, easy to start but room to grow.
|
|
4
4
|
|
|
5
|
-
A powerful Node.js-based **static site generator** and **full-stack web framework** that bridges the gap between simple static sites and complex web applications. Built on the **@
|
|
5
|
+
A powerful Node.js-based **static site generator** and **full-stack web framework** that bridges the gap between simple static sites and complex web applications. Built on the **@jucie.io/engine** service architecture for maximum flexibility and maintainability.
|
|
6
6
|
|
|
7
7
|
## 🚀 Quick Start
|
|
8
8
|
|
|
@@ -24,13 +24,13 @@ npm run dev
|
|
|
24
24
|
- **🎨 Template Agnostic**: Handlebars, Nunjucks, Pug, Mustache, or custom engines
|
|
25
25
|
- **📦 Asset Pipeline**: SCSS compilation, JavaScript bundling with esbuild
|
|
26
26
|
- **🔄 Live Reload**: WebSocket-based live reload during development
|
|
27
|
-
- **🗄️ Database Ready**: SQLite integration for server mode applications
|
|
28
27
|
- **🛣️ Dynamic Routing**: Data-driven page generation and API endpoints
|
|
28
|
+
- **🧩 App Orchestration**: Build and serve external Vite/Webpack/Rollup/esbuild apps via kernel handlers
|
|
29
29
|
- **🔧 Service Architecture**: Modular, extensible service-oriented design
|
|
30
30
|
|
|
31
31
|
## 🏗️ Architecture Overview
|
|
32
32
|
|
|
33
|
-
Clovie uses a **service-oriented architecture** built on `@
|
|
33
|
+
Clovie uses a **service-oriented architecture** built on `@jucie.io/engine`. All functionality is provided by services that extend `ServiceProvider`, orchestrated through dependency injection with reactive state management.
|
|
34
34
|
|
|
35
35
|
### Core Services
|
|
36
36
|
|
|
@@ -276,9 +276,6 @@ export default {
|
|
|
276
276
|
}
|
|
277
277
|
},
|
|
278
278
|
|
|
279
|
-
// Database configuration (optional)
|
|
280
|
-
dbPath: './data/app.db',
|
|
281
|
-
|
|
282
279
|
// Express middleware (auto-selects Express adapter)
|
|
283
280
|
middleware: [
|
|
284
281
|
express.json(),
|
|
@@ -401,28 +398,37 @@ curl http://localhost:3000/api/protected/data
|
|
|
401
398
|
curl -H "Authorization: Bearer your-token" http://localhost:3000/api/protected/data
|
|
402
399
|
```
|
|
403
400
|
|
|
404
|
-
###
|
|
401
|
+
### 🧩 App Orchestration
|
|
402
|
+
|
|
403
|
+
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:
|
|
405
404
|
|
|
406
|
-
|
|
405
|
+
- Detect the build tool (Vite, Webpack, Rollup, esbuild) or use the specified `buildTool`
|
|
406
|
+
- Run builds during `clovie build` and `clovie serve`
|
|
407
|
+
- Launch tool-specific dev middleware during `clovie dev`
|
|
408
|
+
- Mount bundles or dev servers at configurable mount paths (e.g., `/admin`, `/studio`)
|
|
407
409
|
|
|
408
410
|
```javascript
|
|
409
411
|
export default {
|
|
410
412
|
type: 'server',
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
413
|
+
apps: [
|
|
414
|
+
{
|
|
415
|
+
name: 'studio',
|
|
416
|
+
source: './apps/studio',
|
|
417
|
+
buildTool: 'vite',
|
|
418
|
+
buildOptions: {
|
|
419
|
+
watch: true,
|
|
420
|
+
build: { outDir: './apps/studio/dist' }
|
|
421
|
+
},
|
|
422
|
+
dev: {
|
|
423
|
+
mountPath: '/studio'
|
|
424
|
+
}
|
|
421
425
|
}
|
|
422
|
-
|
|
426
|
+
]
|
|
423
427
|
};
|
|
424
428
|
```
|
|
425
429
|
|
|
430
|
+
See [Apps Integration](docs/CONFIGURATION.md#apps-integration) for full examples covering Webpack, Rollup, and esbuild setups.
|
|
431
|
+
|
|
426
432
|
### 🔄 Async Data Loading
|
|
427
433
|
|
|
428
434
|
Load data dynamically at build time or runtime:
|
|
@@ -596,7 +602,7 @@ my-site/
|
|
|
596
602
|
- Validate input data in API actions
|
|
597
603
|
- Use middleware for authentication and request parsing
|
|
598
604
|
- Handle errors gracefully in API endpoints
|
|
599
|
-
- Implement proper
|
|
605
|
+
- Implement proper data migrations and seeding
|
|
600
606
|
|
|
601
607
|
### 🔧 Configuration Tips
|
|
602
608
|
|
|
@@ -683,11 +689,6 @@ clovie kill --check # Check port status
|
|
|
683
689
|
- Check that template files have correct extensions
|
|
684
690
|
- Ensure partial files are in the `partials` directory
|
|
685
691
|
|
|
686
|
-
**Database Connection Issues (Server Mode)**
|
|
687
|
-
- Check that `dbPath` in config points to a writable directory
|
|
688
|
-
- Ensure SQLite is properly installed
|
|
689
|
-
- Verify database initialization in your API actions
|
|
690
|
-
|
|
691
692
|
**Live Reload Not Working**
|
|
692
693
|
- Check that you're in development mode (`npm run dev`)
|
|
693
694
|
- Verify WebSocket connection in browser dev tools
|
package/bin/cli.js
CHANGED
|
@@ -361,16 +361,6 @@ function setupGracefulShutdown(clovie) {
|
|
|
361
361
|
}, 5000); // 5 second timeout
|
|
362
362
|
|
|
363
363
|
try {
|
|
364
|
-
// Close database if it exists and is initialized
|
|
365
|
-
const database = clovie.database;
|
|
366
|
-
if (database && database.isInitialized && database.isInitialized()) {
|
|
367
|
-
console.log('💾 Closing database...');
|
|
368
|
-
if (typeof database.checkpoint === 'function') {
|
|
369
|
-
await database.checkpoint();
|
|
370
|
-
console.log('✅ Database written successfully');
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
364
|
// Stop server if it's running
|
|
375
365
|
const server = clovie.server;
|
|
376
366
|
if (server && server.isRunning && server.isRunning()) {
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import { A as AdapterInterface } from "./
|
|
2
|
-
|
|
3
|
-
import "./createClovie-LSdi1lXJ.js";
|
|
4
|
-
|
|
5
|
-
import "stream";
|
|
6
|
-
|
|
7
|
-
import "module";
|
|
1
|
+
import { A as AdapterInterface } from "./createClovie-x0YNcttr.js";
|
|
8
2
|
|
|
9
3
|
import "path";
|
|
10
4
|
|
|
@@ -26,12 +20,18 @@ import "node:os";
|
|
|
26
20
|
|
|
27
21
|
import "node:tty";
|
|
28
22
|
|
|
29
|
-
import "
|
|
23
|
+
import "fs/promises";
|
|
30
24
|
|
|
31
25
|
import "child_process";
|
|
32
26
|
|
|
27
|
+
import "url";
|
|
28
|
+
|
|
29
|
+
import "node:http";
|
|
30
|
+
|
|
33
31
|
import "util";
|
|
34
32
|
|
|
33
|
+
import "socket.io";
|
|
34
|
+
|
|
35
35
|
class ExpressAdapter extends AdapterInterface {
|
|
36
36
|
#connections=new Set;
|
|
37
37
|
constructor(express = null) {
|
|
@@ -54,10 +54,15 @@ class ExpressAdapter extends AdapterInterface {
|
|
|
54
54
|
throw new Error(`Failed to apply middleware[${index}]: ${error.message}`);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
|
|
57
|
+
}
|
|
58
|
+
registerKernelHandler() {
|
|
59
|
+
this.app && this.app.all("*", async (req, res) => {
|
|
58
60
|
await this.handleRequest(req, res);
|
|
59
61
|
});
|
|
60
62
|
}
|
|
63
|
+
getExpressApp() {
|
|
64
|
+
return this.app;
|
|
65
|
+
}
|
|
61
66
|
async start({port: port = 3e3, host: host = "0.0.0.0"} = {}) {
|
|
62
67
|
return new Promise((resolve, reject) => {
|
|
63
68
|
this.server = this.app.listen(port, host, err => {
|
|
@@ -81,7 +86,7 @@ class ExpressAdapter extends AdapterInterface {
|
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
88
|
async handleRequest(req, res) {
|
|
84
|
-
try {
|
|
89
|
+
if (!res.headersSent) try {
|
|
85
90
|
const ctx = this.createContext(req, res), response = await this.kernel.handle(ctx);
|
|
86
91
|
await this.sendResponse(res, response, "HEAD" === req.method);
|
|
87
92
|
} catch (error) {
|
|
@@ -97,4 +102,4 @@ class ExpressAdapter extends AdapterInterface {
|
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
export { ExpressAdapter };
|
|
100
|
-
//# sourceMappingURL=ExpressAdapter-
|
|
105
|
+
//# sourceMappingURL=ExpressAdapter-D6owl_CX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpressAdapter-D6owl_CX.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 if (res.headersSent) return;\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","headersSent","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,KAAIA,IAAIoB,aACR;YAEE,MAAMC,MAAMjD,KAAKkD,cAAcvB,KAAKC,MAG9BuB,iBAAiBnD,KAAKG,OAAOiD,OAAOH;kBAGpCjD,KAAKqD,aAAazB,KAAKuB,UAAyB,WAAfxB,IAAI2B;AAE7C,UAAE,OAAO9C;kBACDR,KAAKuD,YAAY3B,KAAKpB;AAC9B;AACF;IAMA,cAAAG,CAAe6C;QACb,OAAOA,gBAAgBC,KAAKC;YAE1B,MAAMC,OAAOD,GAAGC,MAAMC,iBAAiBF,GAAGG,WAAWD;YACrD,OAAOD,KAAKG,SAAS,WAAWH,KAAKG,SAAS,iBAAiBH,KAAKG,SAAS;;AAEjF;;;"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var createClovie = require("./createClovie-BziskasS.cjs");
|
|
4
4
|
|
|
5
|
-
require("
|
|
6
|
-
require("crypto"), require("fs"), require("istextorbinary"), require("chokidar"),
|
|
5
|
+
require("path"), require("crypto"), require("fs"), require("istextorbinary"), require("chokidar"),
|
|
7
6
|
require("readline"), require("events"), require("node:process"), require("node:os"),
|
|
8
|
-
require("node:tty"), require("
|
|
7
|
+
require("node:tty"), require("fs/promises"), require("child_process"), require("url"),
|
|
8
|
+
require("node:http"), require("util"), require("socket.io");
|
|
9
9
|
|
|
10
|
-
class ExpressAdapter extends
|
|
10
|
+
class ExpressAdapter extends createClovie.AdapterInterface {
|
|
11
11
|
#connections=new Set;
|
|
12
12
|
constructor(express = null) {
|
|
13
13
|
super("express"), this.express = express, this.app = null;
|
|
@@ -29,10 +29,15 @@ class ExpressAdapter extends Server.AdapterInterface {
|
|
|
29
29
|
throw new Error(`Failed to apply middleware[${index}]: ${error.message}`);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
}
|
|
33
|
+
registerKernelHandler() {
|
|
34
|
+
this.app && this.app.all("*", async (req, res) => {
|
|
33
35
|
await this.handleRequest(req, res);
|
|
34
36
|
});
|
|
35
37
|
}
|
|
38
|
+
getExpressApp() {
|
|
39
|
+
return this.app;
|
|
40
|
+
}
|
|
36
41
|
async start({port: port = 3e3, host: host = "0.0.0.0"} = {}) {
|
|
37
42
|
return new Promise((resolve, reject) => {
|
|
38
43
|
this.server = this.app.listen(port, host, err => {
|
|
@@ -56,7 +61,7 @@ class ExpressAdapter extends Server.AdapterInterface {
|
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
async handleRequest(req, res) {
|
|
59
|
-
try {
|
|
64
|
+
if (!res.headersSent) try {
|
|
60
65
|
const ctx = this.createContext(req, res), response = await this.kernel.handle(ctx);
|
|
61
66
|
await this.sendResponse(res, response, "HEAD" === req.method);
|
|
62
67
|
} catch (error) {
|
|
@@ -72,4 +77,4 @@ class ExpressAdapter extends Server.AdapterInterface {
|
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
exports.ExpressAdapter = ExpressAdapter;
|
|
75
|
-
//# sourceMappingURL=ExpressAdapter-
|
|
80
|
+
//# sourceMappingURL=ExpressAdapter-Z_FtN3dq.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpressAdapter-Z_FtN3dq.cjs","sources":["../../lib/Server/adapters/ExpressAdapter.js"],"sourcesContent":["/**\n * Express adapter for Clovie with full middleware support\n */\nimport { AdapterInterface } from './AdapterInterface.js';\n\nexport class ExpressAdapter extends AdapterInterface {\n #connections = new Set();\n\n constructor(express = null) {\n super('express');\n this.express = express;\n this.app = null;\n }\n\n async initialize(kernel, opts = {}, log = null) {\n await super.initialize(kernel, opts, log);\n \n // Import Express\n if (!this.express) {\n try {\n this.express = await import('express');\n this.express = this.express.default;\n } catch (error) {\n throw new Error('Express not installed. Run: npm install express');\n }\n }\n\n // Create Express app\n this.app = this.express();\n\n // Apply default middleware (can be overridden by user middleware)\n if (!opts.middleware || !this.#hasBodyParser(opts.middleware)) {\n this.app.use(this.express.json());\n this.app.use(this.express.urlencoded({ extended: true }));\n }\n\n // Apply user-defined middleware\n if (opts.middleware && Array.isArray(opts.middleware)) {\n this.log?.info(`Applying ${opts.middleware.length} middleware functions`);\n \n for (const [index, middleware] of opts.middleware.entries()) {\n try {\n this.app.use(middleware);\n this.log?.debug(`Applied middleware ${index + 1}/${opts.middleware.length}`);\n } catch (error) {\n throw new Error(`Failed to apply middleware[${index}]: ${error.message}`);\n }\n }\n }\n\n // Additional middleware will be mounted by the server before kernel handler registration\n }\n\n registerKernelHandler() {\n if (!this.app) {\n return;\n }\n\n this.app.all('*', async (req, res) => {\n await this.handleRequest(req, res);\n });\n }\n\n getExpressApp() {\n return this.app;\n }\n\n async start({ port = 3000, host = '0.0.0.0' } = {}) {\n return new Promise((resolve, reject) => {\n this.server = this.app.listen(port, host, (err) => {\n if (err) {\n reject(err);\n } else {\n // Track connections for clean shutdown\n this.server.on('connection', (conn) => {\n this.#connections.add(conn);\n conn.on('close', () => {\n this.#connections.delete(conn);\n });\n });\n\n this.log?.info(`Express Server with middleware listening on http://${host}:${port}`);\n resolve(this.server);\n }\n });\n });\n }\n\n async stop() {\n if (this.server?.close) {\n // Destroy all active connections\n for (const conn of this.#connections) {\n conn.destroy();\n }\n this.#connections.clear();\n\n return new Promise((resolve) => {\n this.server.close(() => {\n this.log?.info('Express Server stopped');\n resolve();\n });\n });\n }\n }\n\n async handleRequest(req, res) {\n if (res.headersSent) return;\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","headersSent","ctx","createContext","response","handle","sendResponse","method","handleError","middlewareArray","some","mw","name","toLowerCase","toString","includes"],"mappings":";;;;;;;;;AAKO,MAAMA,uBAAuBC,aAAAA;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,KAAIA,IAAIoB,aACR;YAEE,MAAMC,MAAMjD,KAAKkD,cAAcvB,KAAKC,MAG9BuB,iBAAiBnD,KAAKG,OAAOiD,OAAOH;kBAGpCjD,KAAKqD,aAAazB,KAAKuB,UAAyB,WAAfxB,IAAI2B;AAE7C,UAAE,OAAO9C;kBACDR,KAAKuD,YAAY3B,KAAKpB;AAC9B;AACF;IAMA,cAAAG,CAAe6C;QACb,OAAOA,gBAAgBC,KAAKC;YAE1B,MAAMC,OAAOD,GAAGC,MAAMC,iBAAiBF,GAAGG,WAAWD;YACrD,OAAOD,KAAKG,SAAS,WAAWH,KAAKG,SAAS,iBAAiBH,KAAKG,SAAS;;AAEjF;;;"}
|