@relkit/runtime-hono 0.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 +5 -0
- package/dist/api-docs.d.ts +21 -0
- package/dist/api-docs.d.ts.map +1 -0
- package/dist/api-docs.js +91 -0
- package/dist/api-docs.js.map +1 -0
- package/dist/auth.d.ts +18 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +52 -0
- package/dist/auth.js.map +1 -0
- package/dist/client-contract.d.ts +8 -0
- package/dist/client-contract.d.ts.map +1 -0
- package/dist/client-contract.js +10 -0
- package/dist/client-contract.js.map +1 -0
- package/dist/create-app.d.ts +22 -0
- package/dist/create-app.d.ts.map +1 -0
- package/dist/create-app.js +76 -0
- package/dist/create-app.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/internal-endpoints-utils.d.ts +16 -0
- package/dist/internal-endpoints-utils.d.ts.map +1 -0
- package/dist/internal-endpoints-utils.js +154 -0
- package/dist/internal-endpoints-utils.js.map +1 -0
- package/dist/internal-endpoints.d.ts +58 -0
- package/dist/internal-endpoints.d.ts.map +1 -0
- package/dist/internal-endpoints.js +106 -0
- package/dist/internal-endpoints.js.map +1 -0
- package/dist/materialize-routes-utils.d.ts +8 -0
- package/dist/materialize-routes-utils.d.ts.map +1 -0
- package/dist/materialize-routes-utils.js +82 -0
- package/dist/materialize-routes-utils.js.map +1 -0
- package/dist/materialize-routes.d.ts +90 -0
- package/dist/materialize-routes.d.ts.map +1 -0
- package/dist/materialize-routes.js +86 -0
- package/dist/materialize-routes.js.map +1 -0
- package/dist/mcp.d.ts +7 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +81 -0
- package/dist/mcp.js.map +1 -0
- package/dist/middleware-limits.d.ts +4 -0
- package/dist/middleware-limits.d.ts.map +1 -0
- package/dist/middleware-limits.js +53 -0
- package/dist/middleware-limits.js.map +1 -0
- package/dist/middleware-utils.d.ts +57 -0
- package/dist/middleware-utils.d.ts.map +1 -0
- package/dist/middleware-utils.js +90 -0
- package/dist/middleware-utils.js.map +1 -0
- package/dist/middleware.d.ts +17 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +106 -0
- package/dist/middleware.js.map +1 -0
- package/dist/rate-limit-store.d.ts +17 -0
- package/dist/rate-limit-store.d.ts.map +1 -0
- package/dist/rate-limit-store.js +77 -0
- package/dist/rate-limit-store.js.map +1 -0
- package/dist/rate-limit-telemetry.d.ts +8 -0
- package/dist/rate-limit-telemetry.d.ts.map +1 -0
- package/dist/rate-limit-telemetry.js +57 -0
- package/dist/rate-limit-telemetry.js.map +1 -0
- package/dist/rate-limit.d.ts +12 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +126 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/request-context.d.ts +4 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +65 -0
- package/dist/request-context.js.map +1 -0
- package/dist/request-mapping-body.d.ts +27 -0
- package/dist/request-mapping-body.d.ts.map +1 -0
- package/dist/request-mapping-body.js +101 -0
- package/dist/request-mapping-body.js.map +1 -0
- package/dist/request-mapping-fields.d.ts +16 -0
- package/dist/request-mapping-fields.d.ts.map +1 -0
- package/dist/request-mapping-fields.js +63 -0
- package/dist/request-mapping-fields.js.map +1 -0
- package/dist/request-mapping-object.d.ts +14 -0
- package/dist/request-mapping-object.d.ts.map +1 -0
- package/dist/request-mapping-object.js +26 -0
- package/dist/request-mapping-object.js.map +1 -0
- package/dist/request-mapping-sources.d.ts +7 -0
- package/dist/request-mapping-sources.d.ts.map +1 -0
- package/dist/request-mapping-sources.js +60 -0
- package/dist/request-mapping-sources.js.map +1 -0
- package/dist/request-mapping-transform.d.ts +3 -0
- package/dist/request-mapping-transform.d.ts.map +1 -0
- package/dist/request-mapping-transform.js +55 -0
- package/dist/request-mapping-transform.js.map +1 -0
- package/dist/request-mapping.d.ts +33 -0
- package/dist/request-mapping.d.ts.map +1 -0
- package/dist/request-mapping.js +111 -0
- package/dist/request-mapping.js.map +1 -0
- package/dist/request-record-middleware.d.ts +6 -0
- package/dist/request-record-middleware.d.ts.map +1 -0
- package/dist/request-record-middleware.js +58 -0
- package/dist/request-record-middleware.js.map +1 -0
- package/dist/request-record-utils.d.ts +11 -0
- package/dist/request-record-utils.d.ts.map +1 -0
- package/dist/request-record-utils.js +71 -0
- package/dist/request-record-utils.js.map +1 -0
- package/dist/response-mapping-utils.d.ts +19 -0
- package/dist/response-mapping-utils.d.ts.map +1 -0
- package/dist/response-mapping-utils.js +72 -0
- package/dist/response-mapping-utils.js.map +1 -0
- package/dist/response-mapping.d.ts +18 -0
- package/dist/response-mapping.d.ts.map +1 -0
- package/dist/response-mapping.js +100 -0
- package/dist/response-mapping.js.map +1 -0
- package/dist/route-middleware.d.ts +4 -0
- package/dist/route-middleware.d.ts.map +1 -0
- package/dist/route-middleware.js +62 -0
- package/dist/route-middleware.js.map +1 -0
- package/dist/rpc.d.ts +4 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +163 -0
- package/dist/rpc.js.map +1 -0
- package/dist/static-files.d.ts +6 -0
- package/dist/static-files.d.ts.map +1 -0
- package/dist/static-files.js +100 -0
- package/dist/static-files.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createFallbackState, emitLifecycle, getRequestState, lifecycleEvent, setRequestState, validateLimit, } from "./middleware-utils.js";
|
|
2
|
+
import { ensureRequestRecord, finishRequestRecord } from "./request-record-middleware.js";
|
|
3
|
+
export function limitsMiddleware(options = {}) {
|
|
4
|
+
validateLimit(options.maxBodyBytes, "maxBodyBytes");
|
|
5
|
+
validateLimit(options.timeoutMs, "timeoutMs");
|
|
6
|
+
return async (context, next) => {
|
|
7
|
+
const current = ensureRequestRecord(context, getRequestState(context) ?? createFallbackState(context, options), options);
|
|
8
|
+
const length = Number(context.req.header("content-length"));
|
|
9
|
+
if (options.maxBodyBytes !== undefined &&
|
|
10
|
+
Number.isSafeInteger(length) &&
|
|
11
|
+
length > options.maxBodyBytes) {
|
|
12
|
+
const response = new Response(JSON.stringify({ error: "payload-too-large" }), {
|
|
13
|
+
status: 413,
|
|
14
|
+
headers: { "content-type": "application/json" },
|
|
15
|
+
});
|
|
16
|
+
current.requestRecord?.add({
|
|
17
|
+
kind: "mapping",
|
|
18
|
+
status: response.status,
|
|
19
|
+
outcome: "validation-error",
|
|
20
|
+
});
|
|
21
|
+
current.requestRecord?.setOutcome("validation-error");
|
|
22
|
+
setRequestState(context, Object.freeze({ ...current, signal: context.req.raw.signal }));
|
|
23
|
+
context.res = response;
|
|
24
|
+
finishRequestRecord(context, current, options, "validation-error");
|
|
25
|
+
await emitLifecycle(options, lifecycleEvent(context, current, "request.started"), "onStart");
|
|
26
|
+
await emitLifecycle(options, lifecycleEvent(context, current, "request.completed", undefined, response.status), "onComplete");
|
|
27
|
+
return response;
|
|
28
|
+
}
|
|
29
|
+
const controller = new AbortController();
|
|
30
|
+
const source = context.req.raw.signal;
|
|
31
|
+
const abort = () => controller.abort(source.reason);
|
|
32
|
+
if (source.aborted)
|
|
33
|
+
abort();
|
|
34
|
+
else
|
|
35
|
+
source.addEventListener("abort", abort, { once: true });
|
|
36
|
+
const deadlineMs = options.timeoutMs === undefined ? undefined : current.startedAt + options.timeoutMs;
|
|
37
|
+
setRequestState(context, Object.freeze(deadlineMs === undefined
|
|
38
|
+
? { ...current, signal: controller.signal }
|
|
39
|
+
: { ...current, signal: controller.signal, deadlineMs }));
|
|
40
|
+
const timer = deadlineMs === undefined
|
|
41
|
+
? undefined
|
|
42
|
+
: setTimeout(() => controller.abort(new DOMException("HTTP request timeout", "TimeoutError")), options.timeoutMs);
|
|
43
|
+
try {
|
|
44
|
+
await next();
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
if (timer !== undefined)
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
source.removeEventListener("abort", abort);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=middleware-limits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware-limits.js","sourceRoot":"","sources":["../src/middleware-limits.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { MaybePromise, RequestId, TraceId } from "@relkit/contracts";
|
|
2
|
+
import type { RequestRecordBuilder, RequestRecordSink } from "@relkit/observability";
|
|
3
|
+
import type { Context } from "hono";
|
|
4
|
+
export declare const REQUEST_ID_HEADER: "x-request-id";
|
|
5
|
+
export declare const TRACE_ID_HEADER: "x-trace-id";
|
|
6
|
+
export declare const REQUEST_CONTEXT_KEY: "relkit.request";
|
|
7
|
+
export interface HttpRequestState {
|
|
8
|
+
readonly requestId: RequestId;
|
|
9
|
+
readonly traceId: TraceId;
|
|
10
|
+
readonly signal: AbortSignal;
|
|
11
|
+
readonly startedAt: number;
|
|
12
|
+
readonly deadlineMs?: number;
|
|
13
|
+
readonly requestRecord?: RequestRecordBuilder;
|
|
14
|
+
}
|
|
15
|
+
export type RequestLifecycleType = "request.started" | "request.completed" | "request.failed" | "request.cancelled";
|
|
16
|
+
export interface RequestLifecycleEvent {
|
|
17
|
+
readonly type: RequestLifecycleType;
|
|
18
|
+
readonly requestId: RequestId;
|
|
19
|
+
readonly traceId: TraceId;
|
|
20
|
+
readonly method: string;
|
|
21
|
+
readonly path: string;
|
|
22
|
+
readonly startedAt: string;
|
|
23
|
+
readonly completedAt?: string;
|
|
24
|
+
readonly durationMs?: number;
|
|
25
|
+
readonly status?: number;
|
|
26
|
+
readonly errorName?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface RequestLifecycleHooks {
|
|
29
|
+
readonly emit?: (event: RequestLifecycleEvent) => MaybePromise<void>;
|
|
30
|
+
readonly onStart?: (event: RequestLifecycleEvent) => MaybePromise<void>;
|
|
31
|
+
readonly onComplete?: (event: RequestLifecycleEvent) => MaybePromise<void>;
|
|
32
|
+
readonly onError?: (event: RequestLifecycleEvent) => MaybePromise<void>;
|
|
33
|
+
readonly onCancel?: (event: RequestLifecycleEvent) => MaybePromise<void>;
|
|
34
|
+
}
|
|
35
|
+
export interface HttpMiddlewareOptions {
|
|
36
|
+
readonly requestIdHeader?: string;
|
|
37
|
+
readonly traceIdHeader?: string;
|
|
38
|
+
readonly requestId?: () => string;
|
|
39
|
+
readonly traceId?: () => string;
|
|
40
|
+
readonly maxBodyBytes?: number;
|
|
41
|
+
readonly timeoutMs?: number;
|
|
42
|
+
readonly generationId?: string;
|
|
43
|
+
readonly graphHash?: string;
|
|
44
|
+
readonly now?: () => number;
|
|
45
|
+
readonly observability?: RequestRecordSink;
|
|
46
|
+
readonly lifecycle?: RequestLifecycleHooks;
|
|
47
|
+
readonly onLifecycleEvent?: (event: RequestLifecycleEvent) => MaybePromise<void>;
|
|
48
|
+
}
|
|
49
|
+
export declare function getRequestState(context: Context): HttpRequestState | undefined;
|
|
50
|
+
export declare function setRequestState(context: Context, state: HttpRequestState): void;
|
|
51
|
+
export declare function setResponseHeader(context: Context, name: string, value: string): void;
|
|
52
|
+
export declare function createFallbackState(context: Context, options: HttpMiddlewareOptions): HttpRequestState;
|
|
53
|
+
export declare function readId<T extends RequestId | TraceId>(incoming: string | undefined, generate: (() => string) | undefined, prefix: string, normalize: (value: unknown) => T): T;
|
|
54
|
+
export declare function lifecycleEvent(context: Context, state: HttpRequestState, type: RequestLifecycleType, error?: unknown, status?: number): RequestLifecycleEvent;
|
|
55
|
+
export declare function emitLifecycle(options: HttpMiddlewareOptions, event: RequestLifecycleEvent, hook: keyof RequestLifecycleHooks): Promise<void>;
|
|
56
|
+
export declare function validateLimit(value: number | undefined, name: string): void;
|
|
57
|
+
//# sourceMappingURL=middleware-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware-utils.d.ts","sourceRoot":"","sources":["../src/middleware-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { toRequestId, toTraceId } from "@relkit/contracts";
|
|
2
|
+
export const REQUEST_ID_HEADER = "x-request-id";
|
|
3
|
+
export const TRACE_ID_HEADER = "x-trace-id";
|
|
4
|
+
export const REQUEST_CONTEXT_KEY = "relkit.request";
|
|
5
|
+
export function getRequestState(context) {
|
|
6
|
+
const value = context.get(REQUEST_CONTEXT_KEY);
|
|
7
|
+
return isRequestState(value) ? value : undefined;
|
|
8
|
+
}
|
|
9
|
+
export function setRequestState(context, state) {
|
|
10
|
+
const target = context;
|
|
11
|
+
target.set(REQUEST_CONTEXT_KEY, state);
|
|
12
|
+
target.set("requestId", state.requestId);
|
|
13
|
+
target.set("traceId", state.traceId);
|
|
14
|
+
target.set("signal", state.signal);
|
|
15
|
+
}
|
|
16
|
+
export function setResponseHeader(context, name, value) {
|
|
17
|
+
const response = context.res;
|
|
18
|
+
const headers = new Headers(response.headers);
|
|
19
|
+
headers.set(name, value);
|
|
20
|
+
context.res = new Response(response.body, {
|
|
21
|
+
status: response.status,
|
|
22
|
+
statusText: response.statusText,
|
|
23
|
+
headers,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export function createFallbackState(context, options) {
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
requestId: readId(context.req.header(options.requestIdHeader ?? REQUEST_ID_HEADER), options.requestId, "request", toRequestId),
|
|
29
|
+
traceId: readId(context.req.header(options.traceIdHeader ?? TRACE_ID_HEADER), options.traceId, "trace", toTraceId),
|
|
30
|
+
signal: context.req.raw.signal,
|
|
31
|
+
startedAt: Date.now(),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function readId(incoming, generate, prefix, normalize) {
|
|
35
|
+
try {
|
|
36
|
+
return normalize(incoming ?? generate?.() ?? `${prefix}-${crypto.randomUUID()}`);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return normalize(`${prefix}-${crypto.randomUUID()}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function lifecycleEvent(context, state, type, error, status) {
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
return Object.freeze({
|
|
45
|
+
type,
|
|
46
|
+
requestId: state.requestId,
|
|
47
|
+
traceId: state.traceId,
|
|
48
|
+
method: context.req.method,
|
|
49
|
+
path: new URL(context.req.url).pathname,
|
|
50
|
+
startedAt: new Date(state.startedAt).toISOString(),
|
|
51
|
+
...(type === "request.started"
|
|
52
|
+
? {}
|
|
53
|
+
: {
|
|
54
|
+
completedAt: new Date(now).toISOString(),
|
|
55
|
+
durationMs: Math.max(0, now - state.startedAt),
|
|
56
|
+
status: status ?? context.res.status,
|
|
57
|
+
}),
|
|
58
|
+
...(error === undefined ? {} : { errorName: errorName(error) }),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export async function emitLifecycle(options, event, hook) {
|
|
62
|
+
await call(options.onLifecycleEvent, event);
|
|
63
|
+
await call(options.lifecycle?.emit, event);
|
|
64
|
+
await call(options.lifecycle?.[hook], event);
|
|
65
|
+
}
|
|
66
|
+
export function validateLimit(value, name) {
|
|
67
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < 1))
|
|
68
|
+
throw new TypeError(`${name} must be a positive integer`);
|
|
69
|
+
}
|
|
70
|
+
async function call(hook, value) {
|
|
71
|
+
try {
|
|
72
|
+
await hook?.(value);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Lifecycle observers are advisory and cannot alter the response.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function errorName(value) {
|
|
79
|
+
return value instanceof Error && value.name.length > 0 ? value.name : "Error";
|
|
80
|
+
}
|
|
81
|
+
function isRequestState(value) {
|
|
82
|
+
if (value === null || typeof value !== "object")
|
|
83
|
+
return false;
|
|
84
|
+
const candidate = value;
|
|
85
|
+
return (typeof candidate.requestId === "string" &&
|
|
86
|
+
typeof candidate.traceId === "string" &&
|
|
87
|
+
candidate.signal instanceof AbortSignal &&
|
|
88
|
+
typeof candidate.startedAt === "number");
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=middleware-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware-utils.js","sourceRoot":"","sources":["../src/middleware-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { MiddlewareHandler } from "hono";
|
|
2
|
+
import { type HttpMiddlewareOptions } from "./middleware-utils.js";
|
|
3
|
+
export { REQUEST_CONTEXT_KEY, REQUEST_ID_HEADER, TRACE_ID_HEADER, getRequestState, type HttpMiddlewareOptions, type HttpRequestState, type RequestLifecycleEvent, type RequestLifecycleHooks, type RequestLifecycleType, } from "./middleware-utils.js";
|
|
4
|
+
export declare const FRAMEWORK_MIDDLEWARE_ORDER: readonly ["request-id", "trace", "limits", "request-record"];
|
|
5
|
+
export type FrameworkMiddlewareName = (typeof FRAMEWORK_MIDDLEWARE_ORDER)[number];
|
|
6
|
+
export interface FrameworkMiddleware {
|
|
7
|
+
readonly name: FrameworkMiddlewareName;
|
|
8
|
+
readonly handler: MiddlewareHandler;
|
|
9
|
+
}
|
|
10
|
+
/** Installs the four framework-owned HTTP middleware layers in v3 order. */
|
|
11
|
+
export declare function createFrameworkMiddleware(options?: HttpMiddlewareOptions): readonly FrameworkMiddleware[];
|
|
12
|
+
export declare const createHttpMiddleware: typeof createFrameworkMiddleware;
|
|
13
|
+
export { limitsMiddleware } from "./middleware-limits.js";
|
|
14
|
+
export declare function requestIdMiddleware(options?: HttpMiddlewareOptions): MiddlewareHandler;
|
|
15
|
+
export declare function traceMiddleware(options?: HttpMiddlewareOptions): MiddlewareHandler;
|
|
16
|
+
export declare function requestLifecycleMiddleware(options?: HttpMiddlewareOptions): MiddlewareHandler;
|
|
17
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,EAUL,KAAK,qBAAqB,EAK3B,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,uBAAuB,CAAC;AAE/B,eAAO,MAAM,0BAA0B,8DAK5B,CAAC;AACZ,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAC;AAElF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;CACrC;AAED,4EAA4E;AAC5E,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,qBAA0B,GAClC,SAAS,mBAAmB,EAAE,CAOhC;AAED,eAAO,MAAM,oBAAoB,kCAA4B,CAAC;AAE9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,qBAA0B,GAAG,iBAAiB,CA+B1F;AAED,wBAAgB,eAAe,CAAC,OAAO,GAAE,qBAA0B,GAAG,iBAAiB,CAsBtF;AAED,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,qBAA0B,GAAG,iBAAiB,CAgDjG"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { toRequestId, toTraceId } from "@relkit/contracts";
|
|
2
|
+
import { createFallbackState, emitLifecycle, getRequestState, lifecycleEvent, readId, REQUEST_ID_HEADER, setResponseHeader, setRequestState, TRACE_ID_HEADER, } from "./middleware-utils.js";
|
|
3
|
+
import { ensureRequestRecord, finishRequestRecord } from "./request-record-middleware.js";
|
|
4
|
+
import { limitsMiddleware } from "./middleware-limits.js";
|
|
5
|
+
import { failureOutcome } from "./request-record-utils.js";
|
|
6
|
+
export { REQUEST_CONTEXT_KEY, REQUEST_ID_HEADER, TRACE_ID_HEADER, getRequestState, } from "./middleware-utils.js";
|
|
7
|
+
export const FRAMEWORK_MIDDLEWARE_ORDER = Object.freeze([
|
|
8
|
+
"request-id",
|
|
9
|
+
"trace",
|
|
10
|
+
"limits",
|
|
11
|
+
"request-record",
|
|
12
|
+
]);
|
|
13
|
+
/** Installs the four framework-owned HTTP middleware layers in v3 order. */
|
|
14
|
+
export function createFrameworkMiddleware(options = {}) {
|
|
15
|
+
return Object.freeze([
|
|
16
|
+
{ name: "request-id", handler: requestIdMiddleware(options) },
|
|
17
|
+
{ name: "trace", handler: traceMiddleware(options) },
|
|
18
|
+
{ name: "limits", handler: limitsMiddleware(options) },
|
|
19
|
+
{ name: "request-record", handler: requestLifecycleMiddleware(options) },
|
|
20
|
+
]);
|
|
21
|
+
}
|
|
22
|
+
export const createHttpMiddleware = createFrameworkMiddleware;
|
|
23
|
+
export { limitsMiddleware } from "./middleware-limits.js";
|
|
24
|
+
export function requestIdMiddleware(options = {}) {
|
|
25
|
+
return async (context, next) => {
|
|
26
|
+
const requestId = readId(context.req.header(options.requestIdHeader ?? REQUEST_ID_HEADER), options.requestId, "request", toRequestId);
|
|
27
|
+
const traceId = readId(context.req.header(options.traceIdHeader ?? TRACE_ID_HEADER), options.traceId, "trace", toTraceId);
|
|
28
|
+
const startedAt = options.now?.() ?? Date.now();
|
|
29
|
+
const state = Object.freeze({
|
|
30
|
+
requestId,
|
|
31
|
+
traceId,
|
|
32
|
+
signal: context.req.raw.signal,
|
|
33
|
+
startedAt,
|
|
34
|
+
});
|
|
35
|
+
setRequestState(context, state);
|
|
36
|
+
ensureRequestRecord(context, state, options);
|
|
37
|
+
context.header(options.requestIdHeader ?? REQUEST_ID_HEADER, requestId);
|
|
38
|
+
context.header(options.traceIdHeader ?? TRACE_ID_HEADER, traceId);
|
|
39
|
+
try {
|
|
40
|
+
await next();
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
setResponseHeader(context, options.requestIdHeader ?? REQUEST_ID_HEADER, requestId);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function traceMiddleware(options = {}) {
|
|
48
|
+
return async (context, next) => {
|
|
49
|
+
const current = ensureRequestRecord(context, getRequestState(context) ?? createFallbackState(context, options), options);
|
|
50
|
+
const traceId = readId(context.req.header(options.traceIdHeader ?? TRACE_ID_HEADER), options.traceId, "trace", toTraceId);
|
|
51
|
+
current.requestRecord?.setTraceId(traceId);
|
|
52
|
+
setRequestState(context, Object.freeze({ ...current, traceId }));
|
|
53
|
+
context.header(options.traceIdHeader ?? TRACE_ID_HEADER, traceId);
|
|
54
|
+
try {
|
|
55
|
+
await next();
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
setResponseHeader(context, options.traceIdHeader ?? TRACE_ID_HEADER, traceId);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function requestLifecycleMiddleware(options = {}) {
|
|
63
|
+
return async (context, next) => {
|
|
64
|
+
const state = ensureRequestRecord(context, getRequestState(context) ?? createFallbackState(context, options), options);
|
|
65
|
+
const started = lifecycleEvent(context, state, "request.started");
|
|
66
|
+
await emitLifecycle(options, started, "onStart");
|
|
67
|
+
let failure;
|
|
68
|
+
let cancelled = state.signal.aborted;
|
|
69
|
+
let cancellation;
|
|
70
|
+
const onAbort = () => {
|
|
71
|
+
cancelled = true;
|
|
72
|
+
cancellation = emitLifecycle(options, lifecycleEvent(context, state, "request.cancelled"), "onCancel");
|
|
73
|
+
};
|
|
74
|
+
if (state.signal.aborted)
|
|
75
|
+
onAbort();
|
|
76
|
+
else
|
|
77
|
+
state.signal.addEventListener("abort", onAbort, { once: true });
|
|
78
|
+
try {
|
|
79
|
+
await next();
|
|
80
|
+
}
|
|
81
|
+
catch (cause) {
|
|
82
|
+
failure = cause;
|
|
83
|
+
throw cause;
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
state.signal.removeEventListener("abort", onAbort);
|
|
87
|
+
if (cancellation !== undefined)
|
|
88
|
+
await cancellation;
|
|
89
|
+
if (cancelled) {
|
|
90
|
+
state.requestRecord?.setOutcome("cancelled");
|
|
91
|
+
finishRequestRecord(context, state, options, "cancelled");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const outcome = failure === undefined ? undefined : failureOutcome(failure, state.signal);
|
|
95
|
+
if (outcome !== undefined)
|
|
96
|
+
state.requestRecord?.setOutcome(outcome.outcome, outcome.errorId);
|
|
97
|
+
if (failure !== undefined)
|
|
98
|
+
finishRequestRecord(context, state, options, outcome.outcome);
|
|
99
|
+
else
|
|
100
|
+
finishRequestRecord(context, state, options, "success");
|
|
101
|
+
const type = failure === undefined ? "request.completed" : "request.failed";
|
|
102
|
+
await emitLifecycle(options, lifecycleEvent(context, state, type, failure), failure === undefined ? "onComplete" : "onError");
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { MaybePromise } from "@relkit/contracts";
|
|
2
|
+
import type { Store } from "hono-rate-limiter";
|
|
3
|
+
export interface RateLimitCounter {
|
|
4
|
+
readonly get: (key: string) => MaybePromise<unknown | undefined>;
|
|
5
|
+
readonly increment: (key: string, delta: number, options?: {
|
|
6
|
+
readonly ttlMs?: number;
|
|
7
|
+
}) => MaybePromise<unknown>;
|
|
8
|
+
readonly delete: (key: string) => MaybePromise<void>;
|
|
9
|
+
}
|
|
10
|
+
export type RateLimitStoreResolver = (storeId: string) => MaybePromise<RateLimitCounter>;
|
|
11
|
+
export declare class RateLimitStoreError extends Error {
|
|
12
|
+
readonly code: "RELKIT_RATE_LIMIT_STORE_UNAVAILABLE";
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
/** Adapts a numeric RELKIT cache provider to hono-rate-limiter's fixed-window store contract. */
|
|
16
|
+
export declare function createRateLimitStore(routeId: string, storeId: string, windowMs: number, resolve: RateLimitStoreResolver, now?: () => number): Store;
|
|
17
|
+
//# sourceMappingURL=rate-limit-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-store.d.ts","sourceRoot":"","sources":["../src/rate-limit-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAuB,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,YAAY,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACjE,QAAQ,CAAC,SAAS,EAAE,CAClB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAClC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;CACtD;AAED,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,YAAY,CAAC,gBAAgB,CAAC,CAAC;AAEzF,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAG,qCAAqC,CAAU;gBAEnD,OAAO,EAAE,MAAM;CAI5B;AAED,iGAAiG;AACjG,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,sBAAsB,EAC/B,GAAG,GAAE,MAAM,MAAiB,GAC3B,KAAK,CAgDP"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export class RateLimitStoreError extends Error {
|
|
2
|
+
code = "RELKIT_RATE_LIMIT_STORE_UNAVAILABLE";
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "RateLimitStoreError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/** Adapts a numeric RELKIT cache provider to hono-rate-limiter's fixed-window store contract. */
|
|
9
|
+
export function createRateLimitStore(routeId, storeId, windowMs, resolve, now = Date.now) {
|
|
10
|
+
let pending;
|
|
11
|
+
const counter = async () => {
|
|
12
|
+
pending ??= Promise.resolve(resolve(storeId)).then(assertCounter);
|
|
13
|
+
return pending;
|
|
14
|
+
};
|
|
15
|
+
const location = async (key) => {
|
|
16
|
+
const current = now();
|
|
17
|
+
const resetAt = (Math.floor(current / windowMs) + 1) * windowMs;
|
|
18
|
+
const digest = await sha256(key);
|
|
19
|
+
return {
|
|
20
|
+
key: `relkit:rate-limit:${routeId}:${Math.floor(current / windowMs)}:${digest}`,
|
|
21
|
+
resetAt,
|
|
22
|
+
ttlMs: Math.max(1, resetAt - current),
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
const read = async (key) => {
|
|
26
|
+
const target = await location(key);
|
|
27
|
+
const value = await (await counter()).get(target.key);
|
|
28
|
+
if (value === undefined)
|
|
29
|
+
return undefined;
|
|
30
|
+
return { totalHits: numeric(value), resetTime: new Date(target.resetAt) };
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
localKeys: false,
|
|
34
|
+
prefix: `relkit:rate-limit:${routeId}:`,
|
|
35
|
+
get: read,
|
|
36
|
+
increment: async (key) => {
|
|
37
|
+
const target = await location(key);
|
|
38
|
+
const value = await (await counter()).increment(target.key, 1, {
|
|
39
|
+
ttlMs: target.ttlMs,
|
|
40
|
+
});
|
|
41
|
+
return { totalHits: numeric(value), resetTime: new Date(target.resetAt) };
|
|
42
|
+
},
|
|
43
|
+
decrement: async (key) => {
|
|
44
|
+
// ponytail: skip modes are disabled; add provider-side conditional decrement if enabled later.
|
|
45
|
+
const current = await read(key);
|
|
46
|
+
if (current === undefined || current.totalHits <= 0)
|
|
47
|
+
return;
|
|
48
|
+
const target = await location(key);
|
|
49
|
+
await (await counter()).increment(target.key, -1, { ttlMs: target.ttlMs });
|
|
50
|
+
},
|
|
51
|
+
resetKey: async (key) => {
|
|
52
|
+
const target = await location(key);
|
|
53
|
+
await (await counter()).delete(target.key);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function assertCounter(value) {
|
|
58
|
+
if (value === null ||
|
|
59
|
+
typeof value !== "object" ||
|
|
60
|
+
typeof value.get !== "function" ||
|
|
61
|
+
typeof value.increment !== "function" ||
|
|
62
|
+
typeof value.delete !== "function") {
|
|
63
|
+
throw new RateLimitStoreError("Rate-limit cache provider must support get, increment, and delete.");
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
function numeric(value) {
|
|
68
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
69
|
+
throw new RateLimitStoreError("Rate-limit cache provider returned an invalid counter.");
|
|
70
|
+
}
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
async function sha256(value) {
|
|
74
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
|
|
75
|
+
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=rate-limit-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-store.js","sourceRoot":"","sources":["../src/rate-limit-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HttpTriggerRegistration } from "@relkit/graph";
|
|
2
|
+
import type { RequestOutcome, SpanRecord } from "@relkit/observability";
|
|
3
|
+
import type { RouteMaterializationOptions } from "./materialize-routes.js";
|
|
4
|
+
import type { HttpRequestState } from "./middleware.js";
|
|
5
|
+
import type { RateLimitInfo } from "hono-rate-limiter";
|
|
6
|
+
export declare function recordRateLimitResult(trigger: HttpTriggerRegistration, state: HttpRequestState | undefined, startedAt: number, status: number, blocked: boolean, continued: boolean): void;
|
|
7
|
+
export declare function emitRateLimitSpan(options: RouteMaterializationOptions, trigger: HttpTriggerRegistration, state: HttpRequestState | undefined, spanId: string, startedAt: number, status: SpanRecord["status"], blocked: boolean, info?: RateLimitInfo, outcome?: RequestOutcome): void;
|
|
8
|
+
//# sourceMappingURL=rate-limit-telemetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-telemetry.d.ts","sourceRoot":"","sources":["../src/rate-limit-telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,gBAAgB,GAAG,SAAS,EACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,OAAO,GACjB,IAAI,CAcN;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,2BAA2B,EACpC,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,gBAAgB,GAAG,SAAS,EACnC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,EAC5B,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,aAAa,EACpB,OAAO,CAAC,EAAE,cAAc,GACvB,IAAI,CAoCN"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export function recordRateLimitResult(trigger, state, startedAt, status, blocked, continued) {
|
|
2
|
+
const builder = state?.requestRecord;
|
|
3
|
+
builder?.setRoute(trigger.id, trigger.targetFunctionId);
|
|
4
|
+
builder?.setServiceId(trigger.serviceId);
|
|
5
|
+
if (blocked && !continued)
|
|
6
|
+
builder?.add({ kind: "match", at: startedAt, targetId: trigger.id });
|
|
7
|
+
builder?.add({
|
|
8
|
+
kind: "middleware",
|
|
9
|
+
at: startedAt,
|
|
10
|
+
durationMs: Math.max(0, Date.now() - startedAt),
|
|
11
|
+
targetId: "relkit.rate-limit",
|
|
12
|
+
status,
|
|
13
|
+
outcome: blocked ? "declared-error" : "success",
|
|
14
|
+
});
|
|
15
|
+
if (blocked)
|
|
16
|
+
builder?.setOutcome("declared-error", "rate-limit");
|
|
17
|
+
}
|
|
18
|
+
export function emitRateLimitSpan(options, trigger, state, spanId, startedAt, status, blocked, info, outcome) {
|
|
19
|
+
if (state === undefined || options.observability === undefined)
|
|
20
|
+
return;
|
|
21
|
+
const policy = trigger.config.rateLimit;
|
|
22
|
+
const record = {
|
|
23
|
+
version: 1,
|
|
24
|
+
signal: "span",
|
|
25
|
+
requestId: state.requestId,
|
|
26
|
+
traceId: state.traceId,
|
|
27
|
+
generationId: options.generationId ?? "generation.runtime",
|
|
28
|
+
graphHash: options.plan.graphHash,
|
|
29
|
+
invocationId: `rate-limit:${state.requestId}`,
|
|
30
|
+
spanId,
|
|
31
|
+
name: "relkit.http.rate_limit",
|
|
32
|
+
source: "http",
|
|
33
|
+
status,
|
|
34
|
+
startedAt: new Date(startedAt).toISOString(),
|
|
35
|
+
...(status === "completed"
|
|
36
|
+
? {
|
|
37
|
+
completedAt: new Date().toISOString(),
|
|
38
|
+
durationMs: Math.max(0, Date.now() - startedAt),
|
|
39
|
+
outcome: outcome ?? (blocked ? "declared-error" : "success"),
|
|
40
|
+
}
|
|
41
|
+
: {}),
|
|
42
|
+
attributes: {
|
|
43
|
+
"relkit.route.id": trigger.id,
|
|
44
|
+
"relkit.rate_limit.limit": policy.limit,
|
|
45
|
+
"relkit.rate_limit.remaining": info?.remaining ?? policy.limit,
|
|
46
|
+
"relkit.rate_limit.blocked": blocked,
|
|
47
|
+
"relkit.rate_limit.store": policy.storeId === undefined ? "memory" : "shared",
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
options.observability.collect(record);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Telemetry is advisory and cannot change request behavior.
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=rate-limit-telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-telemetry.js","sourceRoot":"","sources":["../src/rate-limit-telemetry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HttpTriggerRegistration } from "@relkit/graph";
|
|
2
|
+
import type { Context } from "hono";
|
|
3
|
+
import type { RouteMaterializationOptions } from "./materialize-routes.js";
|
|
4
|
+
import { type RateLimitStoreResolver } from "./rate-limit-store.js";
|
|
5
|
+
export declare const ROUTE_MIDDLEWARE_ORDER: readonly ["rate-limit", "declared-middleware", "request-mapping", "target"];
|
|
6
|
+
export interface RateLimitRuntimeOptions {
|
|
7
|
+
readonly resolveStore?: RateLimitStoreResolver;
|
|
8
|
+
}
|
|
9
|
+
type RouteHandler = (context: Context) => Promise<Response>;
|
|
10
|
+
export declare function withRateLimit(trigger: HttpTriggerRegistration, options: RouteMaterializationOptions, handler: RouteHandler): RouteHandler;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAE3E,OAAO,EAAwB,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAG1F,eAAO,MAAM,sBAAsB,6EAKxB,CAAC;AAEZ,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,sBAAsB,CAAC;CAChD;AAED,KAAK,YAAY,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAG5D,wBAAgB,aAAa,CAC3B,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,2BAA2B,EACpC,OAAO,EAAE,YAAY,GACpB,YAAY,CAuEd"}
|