@sisu-ai/server 7.0.1 → 7.0.2
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/dist/index.d.ts +28 -23
- package/dist/index.js +75 -36
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import http, { type IncomingMessage, type ServerResponse } from
|
|
2
|
-
import https from
|
|
3
|
-
import type { Agent, Logger } from
|
|
4
|
-
import type { AddressInfo } from
|
|
5
|
-
export { matchRoute } from
|
|
1
|
+
import http, { type IncomingMessage, type ServerResponse } from "http";
|
|
2
|
+
import https from "https";
|
|
3
|
+
import type { Agent, Logger, Ctx } from "@sisu-ai/core";
|
|
4
|
+
import type { AddressInfo } from "net";
|
|
5
|
+
export { matchRoute } from "./router.js";
|
|
6
6
|
export interface ListenOptions<Ctx> {
|
|
7
7
|
tls?: https.ServerOptions;
|
|
8
8
|
port?: number;
|
|
@@ -15,10 +15,15 @@ export interface ListenOptions<Ctx> {
|
|
|
15
15
|
logBanner?: boolean;
|
|
16
16
|
bannerEndpoints?: string[];
|
|
17
17
|
logger?: Logger;
|
|
18
|
-
logLevel?:
|
|
18
|
+
logLevel?: "debug" | "info" | "warn" | "error";
|
|
19
19
|
redactLogKeys?: string[];
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
type HttpEnvelope<CtxT extends Ctx> = {
|
|
22
|
+
req?: IncomingMessage;
|
|
23
|
+
res?: ServerResponse;
|
|
24
|
+
agent?: Agent<CtxT>;
|
|
25
|
+
};
|
|
26
|
+
export declare class Server<CtxT extends Ctx & HttpEnvelope<CtxT>> {
|
|
22
27
|
private agent;
|
|
23
28
|
private opts;
|
|
24
29
|
private server?;
|
|
@@ -26,59 +31,59 @@ export declare class Server<Ctx = any> {
|
|
|
26
31
|
private healthPath;
|
|
27
32
|
private createCtx;
|
|
28
33
|
private emitter;
|
|
29
|
-
constructor(agent: Agent<
|
|
34
|
+
constructor(agent: Agent<CtxT>, opts?: ListenOptions<CtxT>);
|
|
30
35
|
private handle;
|
|
31
36
|
listener(): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
32
37
|
listen(cb?: () => void): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse> | https.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
33
38
|
attach(server: http.Server | https.Server): void;
|
|
34
39
|
close(cb?: (err?: Error) => void): void;
|
|
35
40
|
address(): string | AddressInfo | null | undefined;
|
|
36
|
-
on(event:
|
|
41
|
+
on(event: "listening", handler: (e: {
|
|
37
42
|
url: string;
|
|
38
43
|
address: string | AddressInfo | null | undefined;
|
|
39
44
|
}) => void): this;
|
|
40
|
-
on(event:
|
|
45
|
+
on(event: "request", handler: (e: {
|
|
41
46
|
method: string;
|
|
42
47
|
url: string;
|
|
43
48
|
}) => void): this;
|
|
44
|
-
on(event:
|
|
49
|
+
on(event: "response", handler: (e: {
|
|
45
50
|
method: string;
|
|
46
51
|
url: string;
|
|
47
52
|
status: number;
|
|
48
53
|
duration_ms: number;
|
|
49
54
|
}) => void): this;
|
|
50
|
-
on(event:
|
|
51
|
-
on(event:
|
|
52
|
-
once(event:
|
|
55
|
+
on(event: "error", handler: (err: Error) => void): this;
|
|
56
|
+
on(event: "close", handler: () => void): this;
|
|
57
|
+
once(event: "listening", handler: (e: {
|
|
53
58
|
url: string;
|
|
54
59
|
address: string | AddressInfo | null | undefined;
|
|
55
60
|
}) => void): this;
|
|
56
|
-
once(event:
|
|
61
|
+
once(event: "request", handler: (e: {
|
|
57
62
|
method: string;
|
|
58
63
|
url: string;
|
|
59
64
|
}) => void): this;
|
|
60
|
-
once(event:
|
|
65
|
+
once(event: "response", handler: (e: {
|
|
61
66
|
method: string;
|
|
62
67
|
url: string;
|
|
63
68
|
status: number;
|
|
64
69
|
duration_ms: number;
|
|
65
70
|
}) => void): this;
|
|
66
|
-
once(event:
|
|
67
|
-
once(event:
|
|
68
|
-
off(event:
|
|
71
|
+
once(event: "error", handler: (err: Error) => void): this;
|
|
72
|
+
once(event: "close", handler: () => void): this;
|
|
73
|
+
off(event: "listening", handler: (e: {
|
|
69
74
|
url: string;
|
|
70
75
|
address: string | AddressInfo | null | undefined;
|
|
71
76
|
}) => void): this;
|
|
72
|
-
off(event:
|
|
77
|
+
off(event: "request", handler: (e: {
|
|
73
78
|
method: string;
|
|
74
79
|
url: string;
|
|
75
80
|
}) => void): this;
|
|
76
|
-
off(event:
|
|
81
|
+
off(event: "response", handler: (e: {
|
|
77
82
|
method: string;
|
|
78
83
|
url: string;
|
|
79
84
|
status: number;
|
|
80
85
|
duration_ms: number;
|
|
81
86
|
}) => void): this;
|
|
82
|
-
off(event:
|
|
83
|
-
off(event:
|
|
87
|
+
off(event: "error", handler: (err: Error) => void): this;
|
|
88
|
+
off(event: "close", handler: () => void): this;
|
|
84
89
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,33 +1,50 @@
|
|
|
1
|
-
import http from
|
|
2
|
-
import https from
|
|
3
|
-
import { createConsoleLogger, createRedactingLogger } from
|
|
4
|
-
import { EventEmitter } from
|
|
5
|
-
export { matchRoute } from
|
|
1
|
+
import http from "http";
|
|
2
|
+
import https from "https";
|
|
3
|
+
import { createConsoleLogger, createRedactingLogger } from "@sisu-ai/core";
|
|
4
|
+
import { EventEmitter } from "events";
|
|
5
|
+
export { matchRoute } from "./router.js";
|
|
6
6
|
export class Server {
|
|
7
7
|
constructor(agent, opts = {}) {
|
|
8
8
|
this.agent = agent;
|
|
9
9
|
this.opts = opts;
|
|
10
10
|
this.emitter = new EventEmitter();
|
|
11
|
-
this.basePath = opts.basePath ??
|
|
12
|
-
this.healthPath = opts.healthPath ??
|
|
13
|
-
this.createCtx =
|
|
11
|
+
this.basePath = opts.basePath ?? "/api";
|
|
12
|
+
this.healthPath = opts.healthPath ?? "/health";
|
|
13
|
+
this.createCtx =
|
|
14
|
+
opts.createCtx ?? ((req, res) => ({ req, res }));
|
|
14
15
|
}
|
|
15
16
|
async handle(req, res) {
|
|
16
17
|
// Set up server-level request logging (independent of ctx)
|
|
17
|
-
const baseLogger = this.opts.logger ??
|
|
18
|
-
|
|
18
|
+
const baseLogger = this.opts.logger ??
|
|
19
|
+
createConsoleLogger({
|
|
20
|
+
level: this.opts.logLevel,
|
|
21
|
+
timestamps: true,
|
|
22
|
+
});
|
|
23
|
+
const srvLogger = createRedactingLogger(baseLogger, {
|
|
24
|
+
keys: this.opts.redactLogKeys,
|
|
25
|
+
});
|
|
19
26
|
const started = Date.now();
|
|
20
|
-
const { method =
|
|
21
|
-
srvLogger.info?.(
|
|
22
|
-
res.once?.(
|
|
27
|
+
const { method = "GET", url = "" } = req;
|
|
28
|
+
srvLogger.info?.("[server] request", { method, url });
|
|
29
|
+
res.once?.("finish", () => {
|
|
23
30
|
const ms = Date.now() - started;
|
|
24
|
-
srvLogger.info?.(
|
|
25
|
-
|
|
31
|
+
srvLogger.info?.("[server] response", {
|
|
32
|
+
method,
|
|
33
|
+
url,
|
|
34
|
+
status: res.statusCode,
|
|
35
|
+
duration_ms: ms,
|
|
36
|
+
});
|
|
37
|
+
this.emitter.emit("response", {
|
|
38
|
+
method,
|
|
39
|
+
url,
|
|
40
|
+
status: res.statusCode,
|
|
41
|
+
duration_ms: ms,
|
|
42
|
+
});
|
|
26
43
|
});
|
|
27
|
-
this.emitter.emit(
|
|
44
|
+
this.emitter.emit("request", { method, url });
|
|
28
45
|
if (this.healthPath && req.url === this.healthPath) {
|
|
29
46
|
res.statusCode = 200;
|
|
30
|
-
res.end(
|
|
47
|
+
res.end("ok");
|
|
31
48
|
return;
|
|
32
49
|
}
|
|
33
50
|
if (!req.url || !req.url.startsWith(this.basePath)) {
|
|
@@ -44,16 +61,24 @@ export class Server {
|
|
|
44
61
|
// Mark this context as an HTTP transport envelope and capture minimal request meta
|
|
45
62
|
const headers = req.headers || {};
|
|
46
63
|
const httpMeta = {
|
|
47
|
-
method: req.method ||
|
|
48
|
-
url: req.url ||
|
|
49
|
-
ip: (req.socket && (req.socket.remoteAddress ||
|
|
64
|
+
method: req.method || "GET",
|
|
65
|
+
url: req.url || "",
|
|
66
|
+
ip: (req.socket && (req.socket.remoteAddress || "")) || "",
|
|
50
67
|
headers: {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
"user-agent": typeof headers["user-agent"] === "string"
|
|
69
|
+
? headers["user-agent"]
|
|
70
|
+
: undefined,
|
|
71
|
+
accept: typeof headers["accept"] === "string" ? headers["accept"] : undefined,
|
|
72
|
+
"content-type": typeof headers["content-type"] === "string"
|
|
73
|
+
? headers["content-type"]
|
|
74
|
+
: undefined,
|
|
54
75
|
},
|
|
55
76
|
};
|
|
56
|
-
ctx.state = {
|
|
77
|
+
ctx.state = {
|
|
78
|
+
...(ctx.state ?? {}),
|
|
79
|
+
_transport: { type: "http" },
|
|
80
|
+
_http: httpMeta,
|
|
81
|
+
};
|
|
57
82
|
const handler = this.agent.handler();
|
|
58
83
|
await handler(ctx);
|
|
59
84
|
// Only synthesize a 404 when nothing has been written at all.
|
|
@@ -77,17 +102,19 @@ export class Server {
|
|
|
77
102
|
else {
|
|
78
103
|
this.server.listen(this.opts.port ?? 0, this.opts.host, this.opts.backlog, cb);
|
|
79
104
|
}
|
|
80
|
-
this.server.on(
|
|
81
|
-
this.server.on(
|
|
105
|
+
this.server.on("error", (err) => this.emitter.emit("error", err));
|
|
106
|
+
this.server.on("close", () => this.emitter.emit("close"));
|
|
82
107
|
const printBanner = this.opts.logBanner !== false;
|
|
83
108
|
if (printBanner) {
|
|
84
109
|
const addr = this.server.address();
|
|
85
|
-
let url =
|
|
86
|
-
if (typeof addr ===
|
|
87
|
-
const host = this.opts.host && this.opts.host !==
|
|
110
|
+
let url = "";
|
|
111
|
+
if (typeof addr === "object" && addr && "port" in addr) {
|
|
112
|
+
const host = this.opts.host && this.opts.host !== "0.0.0.0"
|
|
113
|
+
? this.opts.host
|
|
114
|
+
: "localhost";
|
|
88
115
|
url = `http://${host}:${addr.port}`;
|
|
89
116
|
}
|
|
90
|
-
else if (typeof addr ===
|
|
117
|
+
else if (typeof addr === "string") {
|
|
91
118
|
url = addr;
|
|
92
119
|
}
|
|
93
120
|
if (url)
|
|
@@ -97,16 +124,19 @@ export class Server {
|
|
|
97
124
|
if (this.basePath)
|
|
98
125
|
console.log(`[server] basePath: ${this.basePath}`);
|
|
99
126
|
if (this.opts.bannerEndpoints?.length) {
|
|
100
|
-
console.log(
|
|
127
|
+
console.log("[server] endpoints:");
|
|
101
128
|
for (const ep of this.opts.bannerEndpoints)
|
|
102
129
|
console.log(` ${ep}`);
|
|
103
130
|
}
|
|
104
|
-
this.emitter.emit(
|
|
131
|
+
this.emitter.emit("listening", {
|
|
132
|
+
url,
|
|
133
|
+
address: addr,
|
|
134
|
+
});
|
|
105
135
|
}
|
|
106
136
|
return this.server;
|
|
107
137
|
}
|
|
108
138
|
attach(server) {
|
|
109
|
-
server.on(
|
|
139
|
+
server.on("request", this.listener());
|
|
110
140
|
this.server = server;
|
|
111
141
|
}
|
|
112
142
|
close(cb) {
|
|
@@ -115,7 +145,16 @@ export class Server {
|
|
|
115
145
|
address() {
|
|
116
146
|
return this.server?.address();
|
|
117
147
|
}
|
|
118
|
-
on(event, handler) {
|
|
119
|
-
|
|
120
|
-
|
|
148
|
+
on(event, handler) {
|
|
149
|
+
this.emitter.on(event, handler);
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
once(event, handler) {
|
|
153
|
+
this.emitter.once(event, handler);
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
off(event, handler) {
|
|
157
|
+
this.emitter.off(event, handler);
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
121
160
|
}
|