stitchkit 0.2.0 → 0.3.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/dist/browser/http.d.ts +5 -0
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/{index-a35v22fh.js → index-0t460v7k.js} +12 -4
- package/dist/{index-n7bmdwmz.js → index-4b1j82gp.js} +8 -4
- package/dist/{index-5sxnvwb1.js → index-4kz3aqpx.js} +2 -2
- package/dist/{index-v2z2v3mq.js → index-dpj1jcys.js} +150 -60
- package/dist/index-kzfs85xp.js +9 -0
- package/dist/index-mwmpw6j1.js +60 -0
- package/dist/index-q6ja2qwq.js +7 -0
- package/dist/index.js +4 -3
- package/dist/internal/errors.d.ts +13 -1
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/safe-json.d.ts +19 -0
- package/dist/internal/safe-json.d.ts.map +1 -0
- package/dist/internal/within-dir.d.ts +7 -0
- package/dist/internal/within-dir.d.ts.map +1 -0
- package/dist/node.js +6 -3
- package/dist/observability/context.d.ts +9 -1
- package/dist/observability/context.d.ts.map +1 -1
- package/dist/observability/index.js +35 -15
- package/dist/observability/sanitize.d.ts.map +1 -1
- package/dist/server/context.d.ts +3 -2
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/event-bus.d.ts +10 -1
- package/dist/server/event-bus.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +86 -32
- package/dist/server/logger.d.ts +5 -4
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware/auth.d.ts +24 -1
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/middleware/cookies.d.ts.map +1 -1
- package/dist/server/middleware/cors.d.ts +7 -0
- package/dist/server/middleware/cors.d.ts.map +1 -1
- package/dist/server/multipart.d.ts +2 -1
- package/dist/server/multipart.d.ts.map +1 -1
- package/dist/server/rate-limit.d.ts +7 -2
- package/dist/server/rate-limit.d.ts.map +1 -1
- package/dist/server/request.d.ts +27 -4
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/router.d.ts +4 -2
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/stream.d.ts +3 -1
- package/dist/server/stream.d.ts.map +1 -1
- package/dist/server/types.d.ts +12 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/tools/coerce.d.ts +8 -4
- package/dist/tools/coerce.d.ts.map +1 -1
- package/dist/tools/execute.d.ts +1 -1
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/manifest.d.ts +4 -0
- package/dist/tools/manifest.d.ts.map +1 -1
- package/dist/tools/mcp-handler.d.ts.map +1 -1
- package/dist/tools/mount.d.ts +2 -2
- package/dist/tools/mount.d.ts.map +1 -1
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +140 -93
- package/package.json +1 -1
- package/dist/index-ke4mx4ea.js +0 -38
package/dist/node.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createHandler
|
|
3
|
-
} from "./index-
|
|
4
|
-
import"./index-
|
|
3
|
+
} from "./index-dpj1jcys.js";
|
|
4
|
+
import"./index-q6ja2qwq.js";
|
|
5
|
+
import"./index-0t460v7k.js";
|
|
5
6
|
import"./index-kckky6zw.js";
|
|
6
|
-
import"./index-
|
|
7
|
+
import"./index-mwmpw6j1.js";
|
|
8
|
+
import"./index-kzfs85xp.js";
|
|
9
|
+
import"./index-809wc1tt.js";
|
|
7
10
|
// src/server/node.ts
|
|
8
11
|
import { serve } from "srvx";
|
|
9
12
|
async function serveNode(config) {
|
|
@@ -46,6 +46,14 @@ export declare function setRequestError(error: {
|
|
|
46
46
|
code?: string;
|
|
47
47
|
message?: string;
|
|
48
48
|
}): void;
|
|
49
|
+
/** Options for `wrapInRequestContext`. */
|
|
50
|
+
export interface WrapRequestContextOptions {
|
|
51
|
+
/**
|
|
52
|
+
* Trust `x-forwarded-for` for the client IP — enable only behind a proxy
|
|
53
|
+
* that rewrites it. Default `false`: the real socket peer IP is used.
|
|
54
|
+
*/
|
|
55
|
+
trustProxy?: boolean;
|
|
56
|
+
}
|
|
49
57
|
/**
|
|
50
58
|
* Wrap a fetch handler so it runs inside a fresh `RequestContext` — trace ids
|
|
51
59
|
* (the inbound `traceparent` continued, or freshly minted), timing and client
|
|
@@ -56,5 +64,5 @@ export declare function setRequestError(error: {
|
|
|
56
64
|
* logs share one id), pass `traceId: getTraceId` to `createServer` /
|
|
57
65
|
* `createHandler`.
|
|
58
66
|
*/
|
|
59
|
-
export declare function wrapInRequestContext<S>(handler: (req: Request, server: S) => Promise<Response
|
|
67
|
+
export declare function wrapInRequestContext<S>(handler: (req: Request, server: S) => Promise<Response>, options?: WrapRequestContextOptions): (req: Request, server: S) => Promise<Response>;
|
|
60
68
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/observability/context.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAEjE,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,KAAK,EAAE,YAAY,CAAC;IACpB,4CAA4C;IAC5C,MAAM,EAAE,eAAe,CAAC;IACxB,iBAAiB;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAID,yDAAyD;AACzD,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE5E;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED,mEAAmE;AACnE,wBAAgB,UAAU,IAAI,MAAM,GAAG,SAAS,CAE/C;AAED,qDAAqD;AACrD,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGnD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGhF;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/observability/context.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAEjE,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,KAAK,EAAE,YAAY,CAAC;IACpB,4CAA4C;IAC5C,MAAM,EAAE,eAAe,CAAC;IACxB,iBAAiB;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAID,yDAAyD;AACzD,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE5E;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED,mEAAmE;AACnE,wBAAgB,UAAU,IAAI,MAAM,GAAG,SAAS,CAE/C;AAED,qDAAqD;AACrD,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGnD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGhF;AAED,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,EACvD,OAAO,GAAE,yBAA8B,GACtC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAiBhD"}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getClientInfo,
|
|
3
|
+
resolveSocketIp
|
|
4
|
+
} from "../index-mwmpw6j1.js";
|
|
5
|
+
import {
|
|
6
|
+
isUnsafeKey
|
|
7
|
+
} from "../index-kzfs85xp.js";
|
|
1
8
|
import {
|
|
2
9
|
isRecord
|
|
3
10
|
} from "../index-809wc1tt.js";
|
|
4
|
-
import {
|
|
5
|
-
getClientInfo
|
|
6
|
-
} from "../index-ke4mx4ea.js";
|
|
7
11
|
|
|
8
12
|
// src/observability/context.ts
|
|
9
13
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -27,11 +31,11 @@ function parseTraceparent(header) {
|
|
|
27
31
|
const match = TRACEPARENT_RE.exec(header.trim());
|
|
28
32
|
if (!match?.[1] || !match[2])
|
|
29
33
|
return null;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
};
|
|
34
|
+
const traceId = match[1].toLowerCase();
|
|
35
|
+
const parentSpanId = match[2].toLowerCase();
|
|
36
|
+
if (/^0+$/.test(traceId) || /^0+$/.test(parentSpanId))
|
|
37
|
+
return null;
|
|
38
|
+
return { traceId, spanId: randomHex(8), parentSpanId };
|
|
35
39
|
}
|
|
36
40
|
function formatTraceparent(ctx) {
|
|
37
41
|
return `00-${ctx.traceId}-${ctx.spanId}-01`;
|
|
@@ -71,15 +75,18 @@ function setRequestError(error) {
|
|
|
71
75
|
if (ctx)
|
|
72
76
|
ctx.error = error;
|
|
73
77
|
}
|
|
74
|
-
function wrapInRequestContext(handler) {
|
|
78
|
+
function wrapInRequestContext(handler, options = {}) {
|
|
75
79
|
return (req, server) => {
|
|
76
80
|
const ctx = {
|
|
77
81
|
trace: resolveTraceContext(req),
|
|
78
82
|
source: "http",
|
|
79
83
|
method: req.method,
|
|
80
|
-
path: new URL(req.url).pathname,
|
|
84
|
+
path: new URL(req.url, "http://localhost").pathname,
|
|
81
85
|
startedAt: process.hrtime.bigint(),
|
|
82
|
-
...getClientInfo(req
|
|
86
|
+
...getClientInfo(req, {
|
|
87
|
+
trustProxy: options.trustProxy,
|
|
88
|
+
socketIp: resolveSocketIp(req, server)
|
|
89
|
+
})
|
|
83
90
|
};
|
|
84
91
|
return runWithRequestContext(ctx, () => handler(req, server));
|
|
85
92
|
};
|
|
@@ -93,6 +100,7 @@ var MASK = "[redacted]";
|
|
|
93
100
|
function redact(value, options = {}) {
|
|
94
101
|
const sensitive = options.sensitiveKeys ?? DEFAULT_SENSITIVE_KEYS;
|
|
95
102
|
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
103
|
+
const ancestors = new Set;
|
|
96
104
|
const walk = (input, depth) => {
|
|
97
105
|
if (depth > maxDepth)
|
|
98
106
|
return "[max depth]";
|
|
@@ -111,13 +119,24 @@ function redact(value, options = {}) {
|
|
|
111
119
|
return { _type: "formdata", keys: [...input.keys()] };
|
|
112
120
|
}
|
|
113
121
|
if (Array.isArray(input)) {
|
|
114
|
-
|
|
122
|
+
if (ancestors.has(input))
|
|
123
|
+
return "[circular]";
|
|
124
|
+
ancestors.add(input);
|
|
125
|
+
const result = input.map((item) => walk(item, depth + 1));
|
|
126
|
+
ancestors.delete(input);
|
|
127
|
+
return result;
|
|
115
128
|
}
|
|
116
129
|
if (isRecord(input)) {
|
|
130
|
+
if (ancestors.has(input))
|
|
131
|
+
return "[circular]";
|
|
132
|
+
ancestors.add(input);
|
|
117
133
|
const out = {};
|
|
118
134
|
for (const [key, val] of Object.entries(input)) {
|
|
135
|
+
if (isUnsafeKey(key))
|
|
136
|
+
continue;
|
|
119
137
|
out[key] = sensitive.test(key) ? MASK : walk(val, depth + 1);
|
|
120
138
|
}
|
|
139
|
+
ancestors.delete(input);
|
|
121
140
|
return out;
|
|
122
141
|
}
|
|
123
142
|
return String(input);
|
|
@@ -126,11 +145,12 @@ function redact(value, options = {}) {
|
|
|
126
145
|
}
|
|
127
146
|
function truncatePreview(value, maxBytes = DEFAULT_MAX_BYTES) {
|
|
128
147
|
const json = JSON.stringify(value);
|
|
129
|
-
|
|
148
|
+
const bytes = Buffer.byteLength(json, "utf8");
|
|
149
|
+
if (bytes <= maxBytes)
|
|
130
150
|
return value;
|
|
131
151
|
return {
|
|
132
152
|
_truncated: true,
|
|
133
|
-
_originalBytes:
|
|
153
|
+
_originalBytes: bytes,
|
|
134
154
|
preview: `${json.slice(0, maxBytes)}…`
|
|
135
155
|
};
|
|
136
156
|
}
|
|
@@ -147,7 +167,7 @@ function measureSize(value) {
|
|
|
147
167
|
if (Array.isArray(value)) {
|
|
148
168
|
return { resultSize: value.length, responseBytes };
|
|
149
169
|
}
|
|
150
|
-
if (
|
|
170
|
+
if (isRecord(value) && Array.isArray(value.items) && "nextCursor" in value) {
|
|
151
171
|
return { resultSize: value.items.length, responseBytes };
|
|
152
172
|
}
|
|
153
173
|
return { resultSize: null, responseBytes };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/observability/sanitize.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/observability/sanitize.ts"],"names":[],"mappings":"AAOA,8EAA8E;AAC9E,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjC,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,mCAAmC;AACnC,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,8BAA8B;IAC9B,aAAa,EAAE,MAAM,CAAC;CACvB;AASD;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,eAAoB,GAAG,SAAS,CAoD/E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,SAAoB,GAAG,SAAS,CASzF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,eAAoB,GAC5B,SAAS,GAAG,IAAI,CAGlB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAcvD"}
|
package/dist/server/context.d.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* endpoint schemas and gathers request metadata (trace id, client info).
|
|
4
4
|
*/
|
|
5
5
|
import { type RuntimeContext } from '../contract';
|
|
6
|
+
import { type ClientIpOptions } from './request';
|
|
6
7
|
import type { MethodDef } from './types';
|
|
7
|
-
export declare function buildContext(req: Request, url: URL, method: MethodDef, pathParams: Record<string, string>, traceId: string): Promise<RuntimeContext>;
|
|
8
|
-
export declare function buildErrorContext(req: Request, url: URL, traceId: string): RuntimeContext;
|
|
8
|
+
export declare function buildContext(req: Request, url: URL, method: MethodDef, pathParams: Record<string, string>, traceId: string, clientIp: ClientIpOptions): Promise<RuntimeContext>;
|
|
9
|
+
export declare function buildErrorContext(req: Request, url: URL, traceId: string, clientIp: ClientIpOptions): RuntimeContext;
|
|
9
10
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,EAAE,KAAK,eAAe,EAAmC,MAAM,WAAW,CAAC;AAClF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAmCzC,wBAAsB,YAAY,CAChC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,cAAc,CAAC,CA2CzB;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,eAAe,GACxB,cAAc,CAWhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/server/create.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/server/create.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAEV,eAAe,EACf,aAAa,EAGd,MAAM,SAAS,CAAC;AAEjB,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAwMxF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,uBAsBnD"}
|
|
@@ -19,6 +19,15 @@ export interface EventBus<M extends Record<string, unknown> = DefaultEventMap> {
|
|
|
19
19
|
off<K extends keyof M & string>(event: K, handler: EventHandler<M[K]>): void;
|
|
20
20
|
clear(): void;
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
/** Options for `createEventBus`. */
|
|
23
|
+
export interface EventBusOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Called when a listener throws or rejects. A bus listener's failure must
|
|
26
|
+
* never break the bus, so it is caught — but without this hook it is also
|
|
27
|
+
* invisible. Wire it to a logger to surface listener bugs.
|
|
28
|
+
*/
|
|
29
|
+
onListenerError?: (error: unknown, event: string) => void;
|
|
30
|
+
}
|
|
31
|
+
export declare function createEventBus<M extends Record<string, unknown> = DefaultEventMap>(options?: EventBusOptions): EventBus<M>;
|
|
23
32
|
export {};
|
|
24
33
|
//# sourceMappingURL=event-bus.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../../src/server/event-bus.ts"],"names":[],"mappings":"AAAA,KAAK,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnE,sEAAsE;AACtE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe;IAC3E,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7D,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;IAClF,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;IACpF,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7E,KAAK,IAAI,IAAI,CAAC;CACf;AAYD,wBAAgB,cAAc,
|
|
1
|
+
{"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../../src/server/event-bus.ts"],"names":[],"mappings":"AAAA,KAAK,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnE,sEAAsE;AACtE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe;IAC3E,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7D,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;IAClF,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;IACpF,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7E,KAAK,IAAI,IAAI,CAAC;CACf;AAYD,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,EAChF,OAAO,GAAE,eAAoB,GAC5B,QAAQ,CAAC,CAAC,CAAC,CA0Db"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export { type CookieDef, type CookieOptions, defineCookie, parseCookies, seriali
|
|
|
8
8
|
export { type CorsConfig, corsHeaders, corsPreflightResponse, } from './middleware/cors';
|
|
9
9
|
export { parseMultipart } from './multipart';
|
|
10
10
|
export { createRateLimiter, type RateLimitConfig } from './rate-limit';
|
|
11
|
-
export { extractIp, generateTraceId, getClientInfo, resolveTraceId, } from './request';
|
|
11
|
+
export { type ClientIpOptions, extractIp, generateTraceId, getClientInfo, resolveSocketIp, resolveTraceId, } from './request';
|
|
12
12
|
export { staticRoute } from './router';
|
|
13
13
|
export type { SocketIOServerConfig, SocketIOServerHandle } from './socket-io';
|
|
14
14
|
export { createSocketIOServer } from './socket-io';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,KAAK,UAAU,EACf,SAAS,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,UAAU,EACf,WAAW,EACX,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EACL,SAAS,EACT,eAAe,EACf,aAAa,EACb,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrE,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,QAAQ,EACR,cAAc,EACd,SAAS,EACT,QAAQ,EACR,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,KAAK,UAAU,EACf,SAAS,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,UAAU,EACf,WAAW,EACX,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EACL,KAAK,eAAe,EACpB,SAAS,EACT,eAAe,EACf,aAAa,EACb,eAAe,EACf,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrE,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,QAAQ,EACR,cAAc,EACd,SAAS,EACT,QAAQ,EACR,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAC"}
|
package/dist/server/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseSSE,
|
|
3
3
|
streamSSE
|
|
4
|
-
} from "../index-
|
|
4
|
+
} from "../index-4b1j82gp.js";
|
|
5
5
|
import {
|
|
6
6
|
corsHeaders,
|
|
7
7
|
corsPreflightResponse,
|
|
@@ -9,8 +9,9 @@ import {
|
|
|
9
9
|
createServer,
|
|
10
10
|
parseMultipart,
|
|
11
11
|
staticRoute
|
|
12
|
-
} from "../index-
|
|
13
|
-
import"../index-
|
|
12
|
+
} from "../index-dpj1jcys.js";
|
|
13
|
+
import"../index-q6ja2qwq.js";
|
|
14
|
+
import"../index-0t460v7k.js";
|
|
14
15
|
import {
|
|
15
16
|
AppError,
|
|
16
17
|
appError,
|
|
@@ -25,8 +26,16 @@ import {
|
|
|
25
26
|
extractIp,
|
|
26
27
|
generateTraceId,
|
|
27
28
|
getClientInfo,
|
|
29
|
+
resolveSocketIp,
|
|
28
30
|
resolveTraceId
|
|
29
|
-
} from "../index-
|
|
31
|
+
} from "../index-mwmpw6j1.js";
|
|
32
|
+
import {
|
|
33
|
+
isUnsafeKey,
|
|
34
|
+
safeJsonParse
|
|
35
|
+
} from "../index-kzfs85xp.js";
|
|
36
|
+
import {
|
|
37
|
+
isRecord
|
|
38
|
+
} from "../index-809wc1tt.js";
|
|
30
39
|
// src/server/swept-map.ts
|
|
31
40
|
function createSweptMap(options) {
|
|
32
41
|
const store = new Map;
|
|
@@ -85,7 +94,7 @@ function cacheHeaders(maxAge, scope = "public") {
|
|
|
85
94
|
};
|
|
86
95
|
}
|
|
87
96
|
// src/server/event-bus.ts
|
|
88
|
-
function createEventBus() {
|
|
97
|
+
function createEventBus(options = {}) {
|
|
89
98
|
const subscriptions = new Map;
|
|
90
99
|
function add(event, fn, once) {
|
|
91
100
|
let set = subscriptions.get(event);
|
|
@@ -109,11 +118,12 @@ function createEventBus() {
|
|
|
109
118
|
set.delete(sub);
|
|
110
119
|
try {
|
|
111
120
|
const result = sub.fn(data);
|
|
112
|
-
if (result instanceof Promise)
|
|
113
|
-
result.catch(() =>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
if (result instanceof Promise) {
|
|
122
|
+
result.catch((err) => options.onListenerError?.(err, event));
|
|
123
|
+
}
|
|
124
|
+
} catch (err) {
|
|
125
|
+
options.onListenerError?.(err, event);
|
|
126
|
+
}
|
|
117
127
|
}
|
|
118
128
|
},
|
|
119
129
|
on(event, handler) {
|
|
@@ -172,14 +182,17 @@ function parseCookies(header) {
|
|
|
172
182
|
return {};
|
|
173
183
|
const cookies = {};
|
|
174
184
|
for (const pair of header.split(";")) {
|
|
175
|
-
const
|
|
176
|
-
if (
|
|
185
|
+
const eq = pair.indexOf("=");
|
|
186
|
+
if (eq === -1)
|
|
187
|
+
continue;
|
|
188
|
+
const key = pair.slice(0, eq).trim();
|
|
189
|
+
if (!key || isUnsafeKey(key))
|
|
177
190
|
continue;
|
|
178
|
-
const raw =
|
|
191
|
+
const raw = pair.slice(eq + 1).trim();
|
|
179
192
|
try {
|
|
180
|
-
cookies[key
|
|
193
|
+
cookies[key] = decodeURIComponent(raw);
|
|
181
194
|
} catch {
|
|
182
|
-
cookies[key
|
|
195
|
+
cookies[key] = raw;
|
|
183
196
|
}
|
|
184
197
|
}
|
|
185
198
|
return cookies;
|
|
@@ -216,26 +229,37 @@ function defineCookie(config) {
|
|
|
216
229
|
}
|
|
217
230
|
|
|
218
231
|
// src/server/middleware/auth.ts
|
|
232
|
+
var MAX_TOKEN_BYTES = 8192;
|
|
219
233
|
function decodeBase64Url(segment) {
|
|
234
|
+
if (!/^[A-Za-z0-9_-]*$/.test(segment) || segment.length % 4 === 1) {
|
|
235
|
+
throw new Error("invalid base64url segment");
|
|
236
|
+
}
|
|
220
237
|
const b64 = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
221
238
|
const padded = b64.padEnd(Math.ceil(b64.length / 4) * 4, "=");
|
|
222
239
|
return Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
223
240
|
}
|
|
224
|
-
async function verifyJwt(token, secret) {
|
|
241
|
+
async function verifyJwt(token, secret, options = {}) {
|
|
242
|
+
if (!secret)
|
|
243
|
+
throw new Error("verifyJwt: a non-empty secret is required");
|
|
244
|
+
if (token.length > MAX_TOKEN_BYTES)
|
|
245
|
+
throw unauthorized("Token too large");
|
|
225
246
|
const [headerB64, payloadB64, signatureB64] = token.split(".");
|
|
226
247
|
if (!headerB64 || !payloadB64 || !signatureB64)
|
|
227
248
|
throw unauthorized("Invalid token format");
|
|
228
249
|
let header;
|
|
229
|
-
let
|
|
250
|
+
let payloadRaw;
|
|
230
251
|
let signature;
|
|
231
252
|
try {
|
|
232
253
|
const decoder = new TextDecoder;
|
|
233
254
|
header = JSON.parse(decoder.decode(decodeBase64Url(headerB64)));
|
|
234
|
-
|
|
255
|
+
payloadRaw = safeJsonParse(decoder.decode(decodeBase64Url(payloadB64)));
|
|
235
256
|
signature = decodeBase64Url(signatureB64);
|
|
236
257
|
} catch {
|
|
237
258
|
throw unauthorized("Malformed token");
|
|
238
259
|
}
|
|
260
|
+
if (!isRecord(payloadRaw))
|
|
261
|
+
throw unauthorized("Malformed token");
|
|
262
|
+
const payload = payloadRaw;
|
|
239
263
|
if (header.alg !== "HS256")
|
|
240
264
|
throw unauthorized("Unsupported token algorithm");
|
|
241
265
|
const encoder = new TextEncoder;
|
|
@@ -244,12 +268,28 @@ async function verifyJwt(token, secret) {
|
|
|
244
268
|
const valid = await crypto.subtle.verify("HMAC", key, signature, data);
|
|
245
269
|
if (!valid)
|
|
246
270
|
throw unauthorized("Invalid token signature");
|
|
271
|
+
const leeway = options.leewaySeconds ?? 60;
|
|
247
272
|
const now = Date.now() / 1000;
|
|
248
|
-
if (
|
|
249
|
-
|
|
273
|
+
if ("exp" in payload) {
|
|
274
|
+
if (typeof payload.exp !== "number")
|
|
275
|
+
throw unauthorized("Malformed token");
|
|
276
|
+
if (payload.exp < now - leeway)
|
|
277
|
+
throw unauthorized("Token expired");
|
|
278
|
+
}
|
|
279
|
+
if ("nbf" in payload) {
|
|
280
|
+
if (typeof payload.nbf !== "number")
|
|
281
|
+
throw unauthorized("Malformed token");
|
|
282
|
+
if (payload.nbf > now + leeway)
|
|
283
|
+
throw unauthorized("Token not yet valid");
|
|
250
284
|
}
|
|
251
|
-
if (
|
|
252
|
-
throw unauthorized("Token
|
|
285
|
+
if (options.issuer !== undefined && payload.iss !== options.issuer) {
|
|
286
|
+
throw unauthorized("Token issuer mismatch");
|
|
287
|
+
}
|
|
288
|
+
if (options.audience !== undefined) {
|
|
289
|
+
const aud = payload.aud;
|
|
290
|
+
const ok = Array.isArray(aud) ? aud.includes(options.audience) : aud === options.audience;
|
|
291
|
+
if (!ok)
|
|
292
|
+
throw unauthorized("Token audience mismatch");
|
|
253
293
|
}
|
|
254
294
|
return payload;
|
|
255
295
|
}
|
|
@@ -267,15 +307,16 @@ function createAuthHook(config) {
|
|
|
267
307
|
const onAnonymous = config.onAnonymous ?? (() => unauthorized());
|
|
268
308
|
const onForbidden = config.onForbidden ?? (() => forbidden());
|
|
269
309
|
return async (ctx, endpoint) => {
|
|
270
|
-
|
|
271
|
-
return;
|
|
272
|
-
const identity = await config.resolve(ctx);
|
|
310
|
+
const identity = ctx.source === "http" ? await config.resolve(ctx) : await config.resolveFromContext?.(ctx) ?? null;
|
|
273
311
|
config.inject?.(ctx, identity);
|
|
274
312
|
const scope = endpoint.scope ?? config.defaultScope;
|
|
275
313
|
if (!scope)
|
|
276
314
|
return;
|
|
277
315
|
const rule = config.rules[scope];
|
|
278
|
-
if (!rule
|
|
316
|
+
if (!rule) {
|
|
317
|
+
throw new Error(`[stitchkit] auth: no rule for scope "${scope}"`);
|
|
318
|
+
}
|
|
319
|
+
if (rule === "public")
|
|
279
320
|
return;
|
|
280
321
|
if (!identity) {
|
|
281
322
|
onAnonymous();
|
|
@@ -301,6 +342,7 @@ function refillBucket(entry, config, now) {
|
|
|
301
342
|
entry.tokens = Math.min(config.max, entry.tokens + elapsed / config.window * config.max);
|
|
302
343
|
entry.lastRefill = now;
|
|
303
344
|
}
|
|
345
|
+
var MAX_BUCKETS = 1e5;
|
|
304
346
|
function createRateLimiter() {
|
|
305
347
|
const { store: buckets, destroy } = createSweptMap({
|
|
306
348
|
intervalMs: 60000,
|
|
@@ -310,15 +352,26 @@ function createRateLimiter() {
|
|
|
310
352
|
destroy,
|
|
311
353
|
check(key, config) {
|
|
312
354
|
const now = Date.now();
|
|
313
|
-
|
|
314
|
-
if (
|
|
315
|
-
|
|
355
|
+
const entry = buckets.get(key);
|
|
356
|
+
if (entry) {
|
|
357
|
+
buckets.delete(key);
|
|
316
358
|
buckets.set(key, entry);
|
|
359
|
+
refillBucket(entry, config, now);
|
|
360
|
+
if (entry.tokens < 1)
|
|
361
|
+
return false;
|
|
362
|
+
entry.tokens -= 1;
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
if (buckets.size >= MAX_BUCKETS) {
|
|
366
|
+
const lru = buckets.keys().next().value;
|
|
367
|
+
if (lru !== undefined)
|
|
368
|
+
buckets.delete(lru);
|
|
317
369
|
}
|
|
318
|
-
|
|
319
|
-
|
|
370
|
+
const fresh = { tokens: config.max, lastRefill: now };
|
|
371
|
+
buckets.set(key, fresh);
|
|
372
|
+
if (fresh.tokens < 1)
|
|
320
373
|
return false;
|
|
321
|
-
|
|
374
|
+
fresh.tokens -= 1;
|
|
322
375
|
return true;
|
|
323
376
|
},
|
|
324
377
|
remaining(key, config) {
|
|
@@ -368,6 +421,7 @@ export {
|
|
|
368
421
|
staticRoute,
|
|
369
422
|
serializeCookie,
|
|
370
423
|
resolveTraceId,
|
|
424
|
+
resolveSocketIp,
|
|
371
425
|
rateLimited,
|
|
372
426
|
parseSSE,
|
|
373
427
|
parseMultipart,
|
package/dist/server/logger.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/** Elapsed milliseconds since a `performance.now()` mark. */
|
|
2
|
+
export declare function elapsedMs(startTime: number): number;
|
|
2
3
|
/** Map an HTTP status to a log level. */
|
|
3
4
|
export declare function levelForStatus(status: number): 'error' | 'warn' | 'info';
|
|
4
5
|
/** The structured fields shared by every completed-request log line. */
|
|
@@ -11,11 +12,11 @@ export declare function buildLogFields(method: string, path: string, status: num
|
|
|
11
12
|
};
|
|
12
13
|
export interface RequestLog {
|
|
13
14
|
traceId: string;
|
|
14
|
-
startTime:
|
|
15
|
+
startTime: number;
|
|
15
16
|
}
|
|
16
17
|
export declare function shouldLog(pathname: string, method: string): boolean;
|
|
17
18
|
/** Open the timing window for a request. Development prints a `→` line. */
|
|
18
|
-
export declare function logIncoming(req: Request, pathname: string, traceId: string): RequestLog;
|
|
19
|
+
export declare function logIncoming(req: Request, pathname: string, traceId: string, ipAddress?: string): RequestLog;
|
|
19
20
|
/** Close a request. Development: `←` line. Production: one structured JSON line. */
|
|
20
|
-
export declare function logOutgoing(req: Request, pathname: string, status: number, log: RequestLog): void;
|
|
21
|
+
export declare function logOutgoing(req: Request, pathname: string, status: number, log: RequestLog, ipAddress?: string): void;
|
|
21
22
|
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"AAwCA,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnD;AAQD,yCAAyC;AACzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAIxE;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAEvF;AA0BD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGnE;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,UAAU,CASZ;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,UAAU,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,IAAI,CAoBN"}
|
|
@@ -3,7 +3,16 @@ import type { MethodDef } from '../types';
|
|
|
3
3
|
export interface JwtPayload {
|
|
4
4
|
[key: string]: unknown;
|
|
5
5
|
}
|
|
6
|
-
|
|
6
|
+
/** Tuning for `verifyJwt`. */
|
|
7
|
+
export interface VerifyJwtOptions {
|
|
8
|
+
/** Clock-skew tolerance for `exp` / `nbf`, in seconds. Default `60`. */
|
|
9
|
+
leewaySeconds?: number;
|
|
10
|
+
/** Required `iss` claim — the token is rejected on mismatch. */
|
|
11
|
+
issuer?: string;
|
|
12
|
+
/** Required `aud` claim — the token is rejected if it does not carry it. */
|
|
13
|
+
audience?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function verifyJwt(token: string, secret: string, options?: VerifyJwtOptions): Promise<JwtPayload>;
|
|
7
16
|
export declare function extractToken(req: Request, cookieName?: string): string | null;
|
|
8
17
|
/**
|
|
9
18
|
* Per-scope access predicate. `endpoint.scope` selects which rule applies:
|
|
@@ -17,6 +26,14 @@ export type AuthRule<TIdentity> = 'public' | 'authenticated' | ((identity: Await
|
|
|
17
26
|
export interface AuthHookConfig<TIdentity> {
|
|
18
27
|
/** Resolve the request identity — cookie / bearer + DB lookup. */
|
|
19
28
|
resolve: (ctx: RuntimeContext) => Promise<TIdentity | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve identity on a non-HTTP context (a tool call) where there is no
|
|
31
|
+
* `req`. The transport (MCP / agent) has already authenticated the caller;
|
|
32
|
+
* this locates the identity it injected into `ctx` (via `buildMcpServer`'s
|
|
33
|
+
* `context`). Without it, a scoped tool call with no `req` **fails closed**
|
|
34
|
+
* — the scope rule sees no identity and `onAnonymous` rejects the call.
|
|
35
|
+
*/
|
|
36
|
+
resolveFromContext?: (ctx: RuntimeContext) => TIdentity | null | Promise<TIdentity | null>;
|
|
20
37
|
/** Access rule per scope; `endpoint.scope` is the key. */
|
|
21
38
|
rules: Record<string, AuthRule<TIdentity>>;
|
|
22
39
|
/** Scope applied when an endpoint declares none. */
|
|
@@ -35,6 +52,12 @@ export type AuthHook = (ctx: RuntimeContext, endpoint: MethodDef) => Promise<voi
|
|
|
35
52
|
* Identity resolution and the scope vocabulary stay in the project; the
|
|
36
53
|
* project annotates its `rules` object with `satisfies Record<MyScope, …>` to
|
|
37
54
|
* keep scope coverage exhaustive.
|
|
55
|
+
*
|
|
56
|
+
* The hook runs on both surfaces: as `createServer`'s `beforeHandle` (HTTP) and
|
|
57
|
+
* as a tool mount's `lifecycle.beforeHandle` (MCP / agent). On HTTP it resolves
|
|
58
|
+
* identity from `ctx.req` via `resolve`; on a tool call — where there is no
|
|
59
|
+
* `req` — it uses `resolveFromContext`. If `resolveFromContext` is omitted, a
|
|
60
|
+
* scoped tool call has no identity and **fails closed** (never silently passes).
|
|
38
61
|
*/
|
|
39
62
|
export declare function createAuthHook<TIdentity>(config: AuthHookConfig<TIdentity>): AuthHook;
|
|
40
63
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAIrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAG1C,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,8BAA8B;AAC9B,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAiBD,wBAAsB,SAAS,CAC7B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,UAAU,CAAC,CAgErB;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU7E;AAUD;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,CAAC,SAAS,IAC1B,QAAQ,GACR,eAAe,GACf,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;AAExF,MAAM,WAAW,cAAc,CAAC,SAAS;IACvC,kEAAkE;IAClE,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAC5D;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAC3F,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IAC3C,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5E,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,KAAK,CAAC;IAC1B,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,KAAK,CAAC;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnF;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC,GAAG,QAAQ,CAgCrF;AAID;;;;GAIG;AACH,MAAM,WAAW,oBAAoB,CAAC,SAAS;IAC7C,4EAA4E;IAC5E,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;CACpE;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAC5C,MAAM,EAAE,oBAAoB,CAAC,SAAS,CAAC,GACtC,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAM7C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/cookies.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/cookies.ts"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkB1E;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;CACtC;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,aAAkB,GACvB,MAAM,CASR;AAED,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACxB,uEAAuE;IACvE,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACtC,+EAA+E;IAC/E,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,gEAAgE;IAChE,KAAK,IAAI,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAahF"}
|
|
@@ -4,6 +4,13 @@ export interface CorsConfig {
|
|
|
4
4
|
methods?: string;
|
|
5
5
|
headers?: string;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Reject an unsafe CORS config at construction. `credentials: true` with a
|
|
9
|
+
* wildcard origin would reflect *any* caller's `Origin` with
|
|
10
|
+
* `Allow-Credentials: true` — every site could make authenticated cross-origin
|
|
11
|
+
* requests. Credentials require an explicit origin allow-list.
|
|
12
|
+
*/
|
|
13
|
+
export declare function assertCorsConfig(config: CorsConfig): void;
|
|
7
14
|
export declare function corsHeaders(config: CorsConfig, requestOrigin?: string | null): Record<string, string>;
|
|
8
15
|
export declare function corsPreflightResponse(config: CorsConfig, req: Request): Response;
|
|
9
16
|
//# sourceMappingURL=cors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/cors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/cors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAOzD;AAoBD,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAsBxB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,QAAQ,CAKhF"}
|
|
@@ -2,7 +2,8 @@ import type { ZodType } from 'zod';
|
|
|
2
2
|
/** A parsed multipart request — the uploaded `file` and the validated `fields`. */
|
|
3
3
|
export interface MultipartResult {
|
|
4
4
|
file: File;
|
|
5
|
-
|
|
5
|
+
/** Validated when a `fieldsSchema` was given, else the raw decoded fields. */
|
|
6
|
+
fields: unknown;
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
9
|
* Parse a `multipart/form-data` request — extract the file at `fileField` and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multipart.d.ts","sourceRoot":"","sources":["../../src/server/multipart.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"multipart.d.ts","sourceRoot":"","sources":["../../src/server/multipart.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAInC,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,IAAI,CAAC;IACX,8EAA8E;IAC9E,MAAM,EAAE,OAAO,CAAC;CACjB;AA0CD;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,EAC/B,QAAQ,SAA2B,GAClC,OAAO,CAAC,eAAe,CAAC,CA6B1B"}
|
|
@@ -6,8 +6,13 @@ export interface RateLimitConfig {
|
|
|
6
6
|
/**
|
|
7
7
|
* In-memory token-bucket rate limiter. `check(key, config)` consumes a token
|
|
8
8
|
* and returns whether the request is allowed; `remaining(key, config)` reports
|
|
9
|
-
* the count without consuming. Idle keys are swept on a 60-second timer
|
|
10
|
-
* `destroy()`
|
|
9
|
+
* the count without consuming. Idle keys are swept on a 60-second timer and the
|
|
10
|
+
* map is capped at `MAX_BUCKETS` (least-recently-used eviction); `destroy()`
|
|
11
|
+
* stops the timer.
|
|
12
|
+
*
|
|
13
|
+
* `config` is per-call — reusing one `key` with two different configs lets
|
|
14
|
+
* whichever call created the bucket fix its `max` / `window`. Key a bucket to
|
|
15
|
+
* one config (one limiter per limit) if that matters.
|
|
11
16
|
*/
|
|
12
17
|
export declare function createRateLimiter(): {
|
|
13
18
|
destroy: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/server/rate-limit.ts"],"names":[],"mappings":"AAOA,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/server/rate-limit.ts"],"names":[],"mappings":"AAOA,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAeD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB;;eAQlB,MAAM,UAAU,eAAe,GAAG,OAAO;mBA6BrC,MAAM,UAAU,eAAe,GAAG,MAAM;EAO1D"}
|