@xacos/server 1.0.0
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/LICENSE +21 -0
- package/README.md +17 -0
- package/dist/Logger.d.ts +11 -0
- package/dist/Logger.d.ts.map +1 -0
- package/dist/Logger.js +36 -0
- package/dist/Logger.js.map +1 -0
- package/dist/XServer.d.ts +20 -0
- package/dist/XServer.d.ts.map +1 -0
- package/dist/XServer.js +155 -0
- package/dist/XServer.js.map +1 -0
- package/dist/XServer.test.d.ts +2 -0
- package/dist/XServer.test.d.ts.map +1 -0
- package/dist/XServer.test.js +76 -0
- package/dist/XServer.test.js.map +1 -0
- package/dist/adapters/RequestAdapter.d.ts +6 -0
- package/dist/adapters/RequestAdapter.d.ts.map +1 -0
- package/dist/adapters/RequestAdapter.js +95 -0
- package/dist/adapters/RequestAdapter.js.map +1 -0
- package/dist/adapters/RequestAdapter.test.d.ts +2 -0
- package/dist/adapters/RequestAdapter.test.d.ts.map +1 -0
- package/dist/adapters/RequestAdapter.test.js +38 -0
- package/dist/adapters/RequestAdapter.test.js.map +1 -0
- package/dist/dev/mountVite.d.ts +3 -0
- package/dist/dev/mountVite.d.ts.map +1 -0
- package/dist/dev/mountVite.js +72 -0
- package/dist/dev/mountVite.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/routing/mountApiRoutes.d.ts +4 -0
- package/dist/routing/mountApiRoutes.d.ts.map +1 -0
- package/dist/routing/mountApiRoutes.js +136 -0
- package/dist/routing/mountApiRoutes.js.map +1 -0
- package/dist/static/serveStaticChunks.d.ts +7 -0
- package/dist/static/serveStaticChunks.d.ts.map +1 -0
- package/dist/static/serveStaticChunks.js +15 -0
- package/dist/static/serveStaticChunks.js.map +1 -0
- package/dist/views/buildHtmlShell.d.ts +14 -0
- package/dist/views/buildHtmlShell.d.ts.map +1 -0
- package/dist/views/buildHtmlShell.js +24 -0
- package/dist/views/buildHtmlShell.js.map +1 -0
- package/dist/views/buildHtmlShell.test.d.ts +2 -0
- package/dist/views/buildHtmlShell.test.d.ts.map +1 -0
- package/dist/views/buildHtmlShell.test.js +22 -0
- package/dist/views/buildHtmlShell.test.js.map +1 -0
- package/dist/views/mountViewRoutes.d.ts +7 -0
- package/dist/views/mountViewRoutes.d.ts.map +1 -0
- package/dist/views/mountViewRoutes.js +25 -0
- package/dist/views/mountViewRoutes.js.map +1 -0
- package/package.json +77 -0
- package/src/Logger.ts +45 -0
- package/src/XServer.test.ts +83 -0
- package/src/XServer.ts +172 -0
- package/src/adapters/RequestAdapter.test.ts +48 -0
- package/src/adapters/RequestAdapter.ts +108 -0
- package/src/dev/mountVite.ts +90 -0
- package/src/index.ts +9 -0
- package/src/routing/mountApiRoutes.ts +152 -0
- package/src/static/serveStaticChunks.ts +21 -0
- package/src/views/buildHtmlShell.test.ts +26 -0
- package/src/views/buildHtmlShell.ts +35 -0
- package/src/views/mountViewRoutes.ts +34 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 XAOCS Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @xacos/server
|
|
2
|
+
|
|
3
|
+
> Part of the [XAOCS Framework](https://xaocs.dev) — full-stack TypeScript, built for AI-native development.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @xacos/server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Docs
|
|
12
|
+
|
|
13
|
+
Full documentation at [xaocs.dev/docs/server](https://xaocs.dev/docs/server).
|
|
14
|
+
|
|
15
|
+
## License
|
|
16
|
+
|
|
17
|
+
MIT © XAOCS Team
|
package/dist/Logger.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class Logger {
|
|
2
|
+
private logger;
|
|
3
|
+
constructor();
|
|
4
|
+
info(message: string, ...args: any[]): void;
|
|
5
|
+
error(message: string, ...args: any[]): void;
|
|
6
|
+
warn(message: string, ...args: any[]): void;
|
|
7
|
+
debug(message: string, ...args: any[]): void;
|
|
8
|
+
fatal(message: string, ...args: any[]): void;
|
|
9
|
+
}
|
|
10
|
+
export declare const log: Logger;
|
|
11
|
+
//# sourceMappingURL=Logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Logger.d.ts","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"AAEA,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAc;;IAmB5B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;IAIpC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;IAIrC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;IAIpC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;IAIrC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;CAGtC;AAED,eAAO,MAAM,GAAG,QAAe,CAAC"}
|
package/dist/Logger.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import pino from 'pino';
|
|
2
|
+
export class Logger {
|
|
3
|
+
logger;
|
|
4
|
+
constructor() {
|
|
5
|
+
const isDev = process.env["APP_ENV"] !== "production";
|
|
6
|
+
this.logger = pino({
|
|
7
|
+
level: process.env["APP_DEBUG"] === "true" || isDev ? "debug" : "info",
|
|
8
|
+
transport: isDev
|
|
9
|
+
? {
|
|
10
|
+
target: "pino-pretty",
|
|
11
|
+
options: {
|
|
12
|
+
translateTime: "HH:MM:ss Z",
|
|
13
|
+
ignore: "pid,hostname",
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
: undefined,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
info(message, ...args) {
|
|
20
|
+
this.logger.info(message, ...args);
|
|
21
|
+
}
|
|
22
|
+
error(message, ...args) {
|
|
23
|
+
this.logger.error(message, ...args);
|
|
24
|
+
}
|
|
25
|
+
warn(message, ...args) {
|
|
26
|
+
this.logger.warn(message, ...args);
|
|
27
|
+
}
|
|
28
|
+
debug(message, ...args) {
|
|
29
|
+
this.logger.debug(message, ...args);
|
|
30
|
+
}
|
|
31
|
+
fatal(message, ...args) {
|
|
32
|
+
this.logger.fatal(message, ...args);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export const log = new Logger();
|
|
36
|
+
//# sourceMappingURL=Logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Logger.js","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,OAAO,MAAM;IACT,MAAM,CAAc;IAE5B;QACE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,YAAY,CAAC;QACtD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACjB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;YACtE,SAAS,EAAE,KAAK;gBACd,CAAC,CAAC;oBACE,MAAM,EAAE,aAAa;oBACrB,OAAO,EAAE;wBACP,aAAa,EAAE,YAAY;wBAC3B,MAAM,EAAE,cAAc;qBACvB;iBACF;gBACH,CAAC,CAAC,SAAS;SACP,CAAC,CAAC;IAEZ,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAG,IAAW;QAClC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAW;QACnC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAG,IAAW;QAClC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAW;QACnC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAW;QACnC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACtC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,GAAG,GAAG,IAAI,MAAM,EAAE,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FastifyInstance } from "fastify";
|
|
2
|
+
import type { XApp, XMiddleware } from "@xacos/shared";
|
|
3
|
+
import type { XacosConfig } from "@xacos/config";
|
|
4
|
+
export declare class XServer {
|
|
5
|
+
readonly fastify: FastifyInstance;
|
|
6
|
+
private readonly globalMiddlewares;
|
|
7
|
+
constructor();
|
|
8
|
+
private setupHealthCheck;
|
|
9
|
+
bootstrap(config: XacosConfig): Promise<void>;
|
|
10
|
+
private setupErrorHandler;
|
|
11
|
+
useGlobal(middleware: XMiddleware): void;
|
|
12
|
+
getGlobalMiddlewares(): readonly XMiddleware[];
|
|
13
|
+
/**
|
|
14
|
+
* Scan `apiDir` for `*.api.ts` modules and register routes on this Fastify instance.
|
|
15
|
+
*/
|
|
16
|
+
mountApi(apiDir: string, app: XApp): Promise<void>;
|
|
17
|
+
listen(port: number): Promise<void>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=XServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XServer.d.ts","sourceRoot":"","sources":["../src/XServer.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAQvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAOjD,qBAAa,OAAO;IAClB,SAAgB,OAAO,EAAE,eAAe,CAAC;IACzC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqB;;IA8CvD,OAAO,CAAC,gBAAgB;IAYlB,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAenD,OAAO,CAAC,iBAAiB;IAsCzB,SAAS,CAAC,UAAU,EAAE,WAAW,GAAG,IAAI;IAIxC,oBAAoB,IAAI,SAAS,WAAW,EAAE;IAI9C;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
package/dist/XServer.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import Fastify from "fastify";
|
|
2
|
+
import { mountApiRoutes } from "./routing/mountApiRoutes";
|
|
3
|
+
import cookie from "@fastify/cookie";
|
|
4
|
+
import multipart from "@fastify/multipart";
|
|
5
|
+
import formbody from "@fastify/formbody";
|
|
6
|
+
import cors from "@fastify/cors";
|
|
7
|
+
import rateLimit from "@fastify/rate-limit";
|
|
8
|
+
import { log } from "./Logger";
|
|
9
|
+
import { closeDb } from "@xacos/orm";
|
|
10
|
+
import pkg from "../package.json";
|
|
11
|
+
let shutdownRegistered = false;
|
|
12
|
+
export class XServer {
|
|
13
|
+
fastify;
|
|
14
|
+
globalMiddlewares = [];
|
|
15
|
+
constructor() {
|
|
16
|
+
const isDev = process.env["APP_ENV"] !== "production";
|
|
17
|
+
this.fastify = Fastify({
|
|
18
|
+
bodyLimit: 10 * 1024 * 1024, // 10MB max body size
|
|
19
|
+
logger: {
|
|
20
|
+
level: process.env["APP_DEBUG"] === "true" || isDev ? "debug" : "info",
|
|
21
|
+
transport: isDev
|
|
22
|
+
? {
|
|
23
|
+
target: "pino-pretty",
|
|
24
|
+
options: {
|
|
25
|
+
translateTime: "HH:MM:ss Z",
|
|
26
|
+
ignore: "pid,hostname",
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
: undefined,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
this.fastify.addHook('onSend', async (_request, reply) => {
|
|
33
|
+
reply.header('X-Content-Type-Options', 'nosniff');
|
|
34
|
+
reply.header('X-Frame-Options', 'SAMEORIGIN');
|
|
35
|
+
reply.header('X-XSS-Protection', '1; mode=block');
|
|
36
|
+
reply.header('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
37
|
+
if (process.env['APP_ENV'] === 'production') {
|
|
38
|
+
reply.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
void this.fastify.register(cookie);
|
|
42
|
+
void this.fastify.register(formbody);
|
|
43
|
+
const metaSym = Symbol.for('plugin-meta');
|
|
44
|
+
if (multipart[metaSym]) {
|
|
45
|
+
multipart[metaSym].fastify = '4.x';
|
|
46
|
+
}
|
|
47
|
+
void this.fastify.register(multipart, {
|
|
48
|
+
limits: {
|
|
49
|
+
fileSize: 10 * 1024 * 1024, // 10MB default
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
this.setupErrorHandler();
|
|
53
|
+
this.setupHealthCheck();
|
|
54
|
+
}
|
|
55
|
+
setupHealthCheck() {
|
|
56
|
+
this.fastify.get("/health", async (_req, reply) => {
|
|
57
|
+
return reply.send({
|
|
58
|
+
status: "ok",
|
|
59
|
+
uptime: process.uptime(),
|
|
60
|
+
memory: process.memoryUsage(),
|
|
61
|
+
version: pkg.version,
|
|
62
|
+
env: process.env["APP_ENV"] ?? "development",
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async bootstrap(config) {
|
|
67
|
+
// ── CORS ────────────────────────────────────────────────────────────────
|
|
68
|
+
await this.fastify.register(cors, {
|
|
69
|
+
origin: config.cors?.origin ?? true,
|
|
70
|
+
credentials: config.cors?.credentials ?? true,
|
|
71
|
+
methods: config.cors?.methods ?? ["GET", "POST", "PUT", "DELETE", "PATCH"],
|
|
72
|
+
});
|
|
73
|
+
// ── Rate Limit ──────────────────────────────────────────────────────────
|
|
74
|
+
await this.fastify.register(rateLimit, {
|
|
75
|
+
max: config.rateLimit?.max ?? 100,
|
|
76
|
+
timeWindow: config.rateLimit?.timeWindow ?? "1 minute",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
setupErrorHandler() {
|
|
80
|
+
this.fastify.setErrorHandler((error, request, reply) => {
|
|
81
|
+
const statusCode = error.statusCode ?? 500;
|
|
82
|
+
const isDev = process.env["APP_ENV"] !== "production";
|
|
83
|
+
let code = error.code;
|
|
84
|
+
if (!code) {
|
|
85
|
+
if (statusCode === 422 || error.name === "ValidationError")
|
|
86
|
+
code = "VALIDATION_FAILED";
|
|
87
|
+
else if (statusCode === 404)
|
|
88
|
+
code = "NOT_FOUND";
|
|
89
|
+
else if (statusCode === 401)
|
|
90
|
+
code = "UNAUTHORIZED";
|
|
91
|
+
else if (statusCode === 403)
|
|
92
|
+
code = "FORBIDDEN";
|
|
93
|
+
else if (statusCode === 400)
|
|
94
|
+
code = "BAD_REQUEST";
|
|
95
|
+
else
|
|
96
|
+
code = "INTERNAL_ERROR";
|
|
97
|
+
}
|
|
98
|
+
// Structured logging via Pino per error
|
|
99
|
+
log.error(`[XServer Error] ${error.message}`, {
|
|
100
|
+
url: request.url,
|
|
101
|
+
method: request.method,
|
|
102
|
+
statusCode,
|
|
103
|
+
code,
|
|
104
|
+
stack: isDev ? error.stack : undefined,
|
|
105
|
+
});
|
|
106
|
+
request.log.error(error);
|
|
107
|
+
void reply.status(statusCode).send({
|
|
108
|
+
success: false,
|
|
109
|
+
error: {
|
|
110
|
+
message: error.message || "Internal Server Error",
|
|
111
|
+
code,
|
|
112
|
+
details: error.details || error.errors,
|
|
113
|
+
stack: isDev ? error.stack : undefined,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
useGlobal(middleware) {
|
|
119
|
+
this.globalMiddlewares.push(middleware);
|
|
120
|
+
}
|
|
121
|
+
getGlobalMiddlewares() {
|
|
122
|
+
return [...this.globalMiddlewares];
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Scan `apiDir` for `*.api.ts` modules and register routes on this Fastify instance.
|
|
126
|
+
*/
|
|
127
|
+
async mountApi(apiDir, app) {
|
|
128
|
+
await mountApiRoutes(this.fastify, apiDir, app);
|
|
129
|
+
}
|
|
130
|
+
async listen(port) {
|
|
131
|
+
await this.fastify.listen({ port, host: "0.0.0.0" });
|
|
132
|
+
console.log(`\n XAOCS running at http://localhost:${port}\n`);
|
|
133
|
+
if (!shutdownRegistered && process.env["APP_ENV"] !== "test") {
|
|
134
|
+
shutdownRegistered = true;
|
|
135
|
+
const gracefulShutdown = async (signal) => {
|
|
136
|
+
log.info(`Received ${signal} — shutting down gracefully...`);
|
|
137
|
+
try {
|
|
138
|
+
await this.close();
|
|
139
|
+
await closeDb();
|
|
140
|
+
process.exit(0);
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
log.error(`Error during graceful shutdown: ${err.message}`);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
process.on("SIGTERM", () => void gracefulShutdown("SIGTERM"));
|
|
148
|
+
process.on("SIGINT", () => void gracefulShutdown("SIGINT"));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async close() {
|
|
152
|
+
await this.fastify.close();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=XServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XServer.js","sourceRoot":"","sources":["../src/XServer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XServer.test.d.ts","sourceRoot":"","sources":["../src/XServer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { XServer } from "./XServer";
|
|
5
|
+
describe("XServer", () => {
|
|
6
|
+
it("registers global middleware", () => {
|
|
7
|
+
const server = new XServer();
|
|
8
|
+
server.useGlobal(async (_req, _res, next) => {
|
|
9
|
+
await next();
|
|
10
|
+
});
|
|
11
|
+
expect(server.getGlobalMiddlewares().length).toBe(1);
|
|
12
|
+
});
|
|
13
|
+
it("mountApi registers scanned routes", async () => {
|
|
14
|
+
const apiDir = join(fileURLToPath(new URL(".", import.meta.url)), "..", "test-fixtures", "api");
|
|
15
|
+
const app = { make: (_key) => ({}) };
|
|
16
|
+
const server = new XServer();
|
|
17
|
+
const port = 18_900 + Math.floor(Math.random() * 500);
|
|
18
|
+
try {
|
|
19
|
+
await server.mountApi(apiDir, app);
|
|
20
|
+
await server.listen(port);
|
|
21
|
+
const res = await fetch(`http://127.0.0.1:${String(port)}/api/echo/`);
|
|
22
|
+
expect(res.ok).toBe(true);
|
|
23
|
+
const body = (await res.json());
|
|
24
|
+
expect(body.ok).toBe(true);
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
await server.close();
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
it("returns 422 JSON for route schema validation failures", async () => {
|
|
31
|
+
const apiDir = join(fileURLToPath(new URL(".", import.meta.url)), "..", "test-fixtures", "api");
|
|
32
|
+
const app = { make: (_key) => ({}) };
|
|
33
|
+
const server = new XServer();
|
|
34
|
+
const port = 19_050 + Math.floor(Math.random() * 500);
|
|
35
|
+
try {
|
|
36
|
+
await server.mountApi(apiDir, app);
|
|
37
|
+
await server.listen(port);
|
|
38
|
+
const bad = await fetch(`http://127.0.0.1:${String(port)}/api/validated/`, {
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: { "content-type": "application/json" },
|
|
41
|
+
body: JSON.stringify({ name: "x" }),
|
|
42
|
+
});
|
|
43
|
+
expect(bad.status).toBe(422);
|
|
44
|
+
const payload = (await bad.json());
|
|
45
|
+
expect(Array.isArray(payload.errors)).toBe(true);
|
|
46
|
+
const good = await fetch(`http://127.0.0.1:${String(port)}/api/validated/`, {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: { "content-type": "application/json" },
|
|
49
|
+
body: JSON.stringify({ name: "Ada" }),
|
|
50
|
+
});
|
|
51
|
+
expect(good.ok).toBe(true);
|
|
52
|
+
const okBody = (await good.json());
|
|
53
|
+
expect(okBody.name).toBe("Ada");
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
await server.close();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
it("provides a health check endpoint", async () => {
|
|
60
|
+
const server = new XServer();
|
|
61
|
+
const port = 19_200 + Math.floor(Math.random() * 500);
|
|
62
|
+
try {
|
|
63
|
+
await server.listen(port);
|
|
64
|
+
const res = await fetch(`http://127.0.0.1:${String(port)}/health`);
|
|
65
|
+
expect(res.ok).toBe(true);
|
|
66
|
+
const body = (await res.json());
|
|
67
|
+
expect(body.status).toBe("ok");
|
|
68
|
+
expect(body.version).toBeDefined();
|
|
69
|
+
expect(body.env).toBeDefined();
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
await server.close();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=XServer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XServer.test.js","sourceRoot":"","sources":["../src/XServer.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FastifyReply, FastifyRequest } from "fastify";
|
|
2
|
+
import type { XApp, XRequest, XResponse } from "@xacos/shared";
|
|
3
|
+
import "@fastify/multipart";
|
|
4
|
+
export declare function adaptRequest(req: FastifyRequest, app: XApp): XRequest;
|
|
5
|
+
export declare function adaptResponse(reply: FastifyReply): XResponse;
|
|
6
|
+
//# sourceMappingURL=RequestAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/RequestAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,KAAK,EAAiB,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,oBAAoB,CAAC;AAG5B,wBAAgB,YAAY,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,IAAI,GAAG,QAAQ,CA6CrE;AAuBD,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAiC5D"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import "@fastify/multipart";
|
|
2
|
+
export function adaptRequest(req, app) {
|
|
3
|
+
const xReq = {
|
|
4
|
+
ip: req.ip,
|
|
5
|
+
params: req.params ?? {},
|
|
6
|
+
query: req.query ?? {},
|
|
7
|
+
body: req.body,
|
|
8
|
+
headers: req.headers,
|
|
9
|
+
cookies: req.cookies ?? {},
|
|
10
|
+
app,
|
|
11
|
+
validate(schema) {
|
|
12
|
+
const result = schema.safeParse(this.body);
|
|
13
|
+
if (!result.success) {
|
|
14
|
+
const error = new Error('Validation failed');
|
|
15
|
+
error.statusCode = 422;
|
|
16
|
+
error.code = 'VALIDATION_ERROR';
|
|
17
|
+
error.details = result.error.flatten().fieldErrors;
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
return result.data;
|
|
21
|
+
},
|
|
22
|
+
async file(name) {
|
|
23
|
+
if (!req.isMultipart())
|
|
24
|
+
return undefined;
|
|
25
|
+
const fileIter = req.files();
|
|
26
|
+
for await (const part of fileIter) {
|
|
27
|
+
if (part.fieldname === name) {
|
|
28
|
+
return createXFile(part, app);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
},
|
|
33
|
+
async files(name) {
|
|
34
|
+
if (!req.isMultipart())
|
|
35
|
+
return [];
|
|
36
|
+
const results = [];
|
|
37
|
+
const fileIter = req.files();
|
|
38
|
+
for await (const part of fileIter) {
|
|
39
|
+
if (!name || part.fieldname === name) {
|
|
40
|
+
results.push(createXFile(part, app));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return results;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
return xReq;
|
|
47
|
+
}
|
|
48
|
+
function createXFile(part, app) {
|
|
49
|
+
return {
|
|
50
|
+
fieldname: part.fieldname,
|
|
51
|
+
filename: part.filename,
|
|
52
|
+
mimetype: part.mimetype,
|
|
53
|
+
encoding: part.encoding,
|
|
54
|
+
async getBuffer() {
|
|
55
|
+
return await part.toBuffer();
|
|
56
|
+
},
|
|
57
|
+
async store(path) {
|
|
58
|
+
const buf = await part.toBuffer();
|
|
59
|
+
const storage = app.make('storage');
|
|
60
|
+
await storage.put(path, buf);
|
|
61
|
+
return path;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function adaptResponse(reply) {
|
|
66
|
+
const res = {
|
|
67
|
+
_payload: undefined,
|
|
68
|
+
setHeader(name, value) {
|
|
69
|
+
reply.header(name, value);
|
|
70
|
+
return res;
|
|
71
|
+
},
|
|
72
|
+
json(data) {
|
|
73
|
+
this._payload = data;
|
|
74
|
+
return res;
|
|
75
|
+
},
|
|
76
|
+
status(code) {
|
|
77
|
+
reply.code(code);
|
|
78
|
+
return res;
|
|
79
|
+
},
|
|
80
|
+
send(data) {
|
|
81
|
+
this._payload = data ?? "";
|
|
82
|
+
return res;
|
|
83
|
+
},
|
|
84
|
+
redirect(url) {
|
|
85
|
+
reply.redirect(url);
|
|
86
|
+
},
|
|
87
|
+
setCookie(name, value, opts) {
|
|
88
|
+
const cookieReply = reply;
|
|
89
|
+
cookieReply.setCookie?.(name, value, opts);
|
|
90
|
+
return res;
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
return res;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=RequestAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestAdapter.js","sourceRoot":"","sources":["../../src/adapters/RequestAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestAdapter.test.d.ts","sourceRoot":"","sources":["../../src/adapters/RequestAdapter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { adaptRequest, adaptResponse } from "./RequestAdapter";
|
|
3
|
+
describe("RequestAdapter", () => {
|
|
4
|
+
it("adapts request shape", () => {
|
|
5
|
+
const req = {
|
|
6
|
+
params: { id: "42" },
|
|
7
|
+
query: { q: "ok" },
|
|
8
|
+
body: { x: 1 },
|
|
9
|
+
headers: { "x-test": "1" },
|
|
10
|
+
};
|
|
11
|
+
const app = {
|
|
12
|
+
make: (_key) => ({}),
|
|
13
|
+
};
|
|
14
|
+
const adapted = adaptRequest(req, app);
|
|
15
|
+
expect(adapted.params["id"]).toBe("42");
|
|
16
|
+
expect(adapted.query["q"]).toBe("ok");
|
|
17
|
+
});
|
|
18
|
+
it("adapts response shape", () => {
|
|
19
|
+
let statusCode = 200;
|
|
20
|
+
let payload;
|
|
21
|
+
const reply = {
|
|
22
|
+
code(code) {
|
|
23
|
+
statusCode = code;
|
|
24
|
+
return this;
|
|
25
|
+
},
|
|
26
|
+
send(data) {
|
|
27
|
+
payload = data;
|
|
28
|
+
return this;
|
|
29
|
+
},
|
|
30
|
+
redirect(_url) { },
|
|
31
|
+
};
|
|
32
|
+
const adapted = adaptResponse(reply);
|
|
33
|
+
adapted.status(201).json({ ok: true });
|
|
34
|
+
expect(statusCode).toBe(201);
|
|
35
|
+
expect(adapted._payload).toEqual({ ok: true });
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
//# sourceMappingURL=RequestAdapter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestAdapter.test.js","sourceRoot":"","sources":["../../src/adapters/RequestAdapter.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE/D,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,GAAG,GAAG;YACV,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;YACpB,KAAK,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE;YAClB,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE;YACd,OAAO,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE;SACE,CAAC;QAE/B,MAAM,GAAG,GAAG;YACV,IAAI,EAAE,CAAI,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAM;SACrC,CAAC;QAEF,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAEvC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,IAAI,UAAU,GAAG,GAAG,CAAC;QACrB,IAAI,OAAgB,CAAC;QAErB,MAAM,KAAK,GAAG;YACZ,IAAI,CAAC,IAAY;gBACf,UAAU,GAAG,IAAI,CAAC;gBAClB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,CAAC,IAAa;gBAChB,OAAO,GAAG,IAAI,CAAC;gBACf,OAAO,IAAI,CAAC;YACd,CAAC;YACD,QAAQ,CAAC,IAAY,IAAG,CAAC;SACC,CAAC;QAE7B,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QAErC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAE,OAAe,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mountVite.d.ts","sourceRoot":"","sources":["../../src/dev/mountVite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAK/C,wBAAsB,YAAY,CAChC,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CA+Ef"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createViteConfig, ViewScanner } from '@xacos/compiler';
|
|
2
|
+
import { createServer as createViteServer } from 'vite';
|
|
3
|
+
export async function mountViteDev(fastify, viewsDir, root) {
|
|
4
|
+
const scanner = new ViewScanner(viewsDir);
|
|
5
|
+
const routes = scanner.scan();
|
|
6
|
+
// Register routes in Fastify so it knows about them,
|
|
7
|
+
// even though Vite middleware will actually serve them.
|
|
8
|
+
for (const route of routes) {
|
|
9
|
+
fastify.get(route.path, async (request, reply) => {
|
|
10
|
+
const mod = await vite.ssrLoadModule(route.filePath);
|
|
11
|
+
if (mod['ssr'] === true) {
|
|
12
|
+
const Component = mod.default;
|
|
13
|
+
if (typeof Component !== 'function') {
|
|
14
|
+
throw new Error(`[XAOCS] Page at ${route.filePath} does not have a default export component`);
|
|
15
|
+
}
|
|
16
|
+
// In dev mode, we still let Vite handle the HTML shell usually,
|
|
17
|
+
// but for a true SSR opt-in, we might want to render the component here.
|
|
18
|
+
// For now, following the sprint goal: render to string if ssr=true.
|
|
19
|
+
const { renderSSR } = await import('@xacos/compiler');
|
|
20
|
+
const { html, meta } = await renderSSR(route.filePath, {
|
|
21
|
+
params: request.params,
|
|
22
|
+
query: request.query,
|
|
23
|
+
});
|
|
24
|
+
// We need a basic HTML shell. For now, we'll return the rendered HTML.
|
|
25
|
+
// Real implementation would inject into a template.
|
|
26
|
+
reply.type('text/html').send(`
|
|
27
|
+
<!DOCTYPE html>
|
|
28
|
+
<html>
|
|
29
|
+
<head>
|
|
30
|
+
<title>${meta.title ?? 'XAOCS App'}</title>
|
|
31
|
+
${meta.description ? `<meta name="description" content="${meta.description}">` : ''}
|
|
32
|
+
</head>
|
|
33
|
+
<body>
|
|
34
|
+
<div id="root">${html}</div>
|
|
35
|
+
</body>
|
|
36
|
+
</html>
|
|
37
|
+
`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// The onRequest hook handles the actual Vite forwarding for non-SSR.
|
|
41
|
+
return reply.callNotFound();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const config = createViteConfig(viewsDir, root);
|
|
45
|
+
const vite = await createViteServer(config);
|
|
46
|
+
// Forward all non-API requests to Vite in dev mode
|
|
47
|
+
fastify.addHook('onRequest', async (request, reply) => {
|
|
48
|
+
const url = request.raw.url ?? '/';
|
|
49
|
+
// Skip API routes — those are handled by XAOCS API router
|
|
50
|
+
if (url.startsWith('/api/'))
|
|
51
|
+
return;
|
|
52
|
+
// Use Vite's connect-style middleware
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
vite.middlewares(request.raw, reply.raw, (err) => {
|
|
55
|
+
if (err) {
|
|
56
|
+
reject(err);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// If Vite doesn't handle it, it will call next()
|
|
60
|
+
// We don't really have a 'next' in Fastify onRequest hook in this way,
|
|
61
|
+
// but resolve() lets Fastify continue.
|
|
62
|
+
resolve();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
// Handle graceful shutdown
|
|
68
|
+
fastify.addHook('onClose', async () => {
|
|
69
|
+
await vite.close();
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=mountVite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mountVite.js","sourceRoot":"","sources":["../../src/dev/mountVite.ts"],"names":[],"mappings":"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"}
|