@rapidrest/service-core 1.0.0-rc.21 → 1.0.0-rc.23
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/lib/ApiErrors.js +2 -0
- package/dist/lib/ApiErrors.js.map +1 -1
- package/dist/lib/OpenApiSpec.js.map +1 -1
- package/dist/lib/Server.js +25 -15
- package/dist/lib/Server.js.map +1 -1
- package/dist/lib/auth/AuthMiddleware.js +2 -1
- package/dist/lib/auth/AuthMiddleware.js.map +1 -1
- package/dist/lib/auth/JWTStrategy.js +21 -1
- package/dist/lib/auth/JWTStrategy.js.map +1 -1
- package/dist/lib/database/ConnectionManager.js +7 -1
- package/dist/lib/database/ConnectionManager.js.map +1 -1
- package/dist/lib/database/MongoSchemaSync.js.map +1 -1
- package/dist/lib/decorators/ModelDecorators.js +17 -0
- package/dist/lib/decorators/ModelDecorators.js.map +1 -1
- package/dist/lib/http/IWebSocketShim.js +2 -0
- package/dist/lib/http/IWebSocketShim.js.map +1 -0
- package/dist/lib/http/MiddlewareChain.js +129 -0
- package/dist/lib/http/MiddlewareChain.js.map +1 -0
- package/dist/lib/http/RuntimeDetect.js +8 -0
- package/dist/lib/http/RuntimeDetect.js.map +1 -0
- package/dist/lib/http/bun/BunAdapters.js +270 -0
- package/dist/lib/http/bun/BunAdapters.js.map +1 -0
- package/dist/lib/http/bun/BunRouter.js +377 -0
- package/dist/lib/http/bun/BunRouter.js.map +1 -0
- package/dist/lib/http/bun/BunWebSocket.js +48 -0
- package/dist/lib/http/bun/BunWebSocket.js.map +1 -0
- package/dist/lib/http/index.js +13 -3
- package/dist/lib/http/index.js.map +1 -1
- package/dist/lib/http/{Adapters.js → uWS/Adapters.js} +65 -27
- package/dist/lib/http/uWS/Adapters.js.map +1 -0
- package/dist/lib/http/{Router.js → uWS/Router.js} +24 -133
- package/dist/lib/http/uWS/Router.js.map +1 -0
- package/dist/lib/http/uWS/WebSocket.js.map +1 -0
- package/dist/lib/models/ModelUtils.js +104 -9
- package/dist/lib/models/ModelUtils.js.map +1 -1
- package/dist/lib/models/RepoUtils.js +89 -6
- package/dist/lib/models/RepoUtils.js.map +1 -1
- package/dist/lib/routes/BaseAdminRoute.js +7 -5
- package/dist/lib/routes/BaseAdminRoute.js.map +1 -1
- package/dist/lib/routes/BaseMetricsRoute.js.map +1 -1
- package/dist/lib/routes/BasePushRoute.js +32 -5
- package/dist/lib/routes/BasePushRoute.js.map +1 -1
- package/dist/lib/routes/CRUDRoute.js.map +1 -1
- package/dist/lib/routes/ModelRoute.js.map +1 -1
- package/dist/lib/routes/RouteUtils.js +6 -2
- package/dist/lib/routes/RouteUtils.js.map +1 -1
- package/dist/lib/security/ACLUtils.js +10 -5
- package/dist/lib/security/ACLUtils.js.map +1 -1
- package/dist/lib/security/AccessControlListMongo.js +29 -29
- package/dist/lib/security/AccessControlListSQL.js +29 -29
- package/dist/lib/security/BaseACLRoute.js.map +1 -1
- package/dist/lib/test/requestws.js +28 -3
- package/dist/lib/test/requestws.js.map +1 -1
- package/dist/types/ApiErrors.d.ts +2 -0
- package/dist/types/OpenApiSpec.d.ts +1 -1
- package/dist/types/Server.d.ts +4 -4
- package/dist/types/auth/AuthMiddleware.d.ts +2 -2
- package/dist/types/auth/AuthStrategy.d.ts +2 -2
- package/dist/types/auth/BasicStrategy.d.ts +3 -3
- package/dist/types/auth/JWTStrategy.d.ts +3 -2
- package/dist/types/decorators/ModelDecorators.d.ts +8 -1
- package/dist/types/decorators/RouteDecorators.d.ts +1 -1
- package/dist/types/http/IWebSocketShim.d.ts +11 -0
- package/dist/types/http/MiddlewareChain.d.ts +40 -0
- package/dist/types/http/RuntimeDetect.d.ts +2 -0
- package/dist/types/http/bun/BunAdapters.d.ts +128 -0
- package/dist/types/http/bun/BunRouter.d.ts +79 -0
- package/dist/types/http/bun/BunWebSocket.d.ts +35 -0
- package/dist/types/http/index.d.ts +12 -6
- package/dist/types/http/types.d.ts +21 -0
- package/dist/types/http/{Adapters.d.ts → uWS/Adapters.d.ts} +24 -2
- package/dist/types/http/{Router.d.ts → uWS/Router.d.ts} +8 -32
- package/dist/types/http/{WebSocket.d.ts → uWS/WebSocket.d.ts} +6 -5
- package/dist/types/models/ModelUtils.d.ts +30 -0
- package/dist/types/models/RepoUtils.d.ts +2 -2
- package/dist/types/routes/BaseAdminRoute.d.ts +3 -3
- package/dist/types/routes/BaseMetricsRoute.d.ts +1 -1
- package/dist/types/routes/BasePushRoute.d.ts +4 -0
- package/dist/types/routes/BaseStaticRoute.d.ts +1 -1
- package/dist/types/routes/CRUDRoute.d.ts +2 -2
- package/dist/types/routes/ModelRoute.d.ts +2 -2
- package/dist/types/security/ACLUtils.d.ts +6 -3
- package/dist/types/security/AccessControlListMongo.d.ts +1 -1
- package/dist/types/security/AccessControlListSQL.d.ts +1 -1
- package/dist/types/security/BaseACLRoute.d.ts +3 -3
- package/package.json +138 -132
- package/dist/lib/http/Adapters.js.map +0 -1
- package/dist/lib/http/Router.js.map +0 -1
- package/dist/lib/http/WebSocket.js.map +0 -1
- /package/dist/lib/http/{WebSocket.js → uWS/WebSocket.js} +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { HttpRequest, HttpResponse } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal structural slice of Bun's `Server` used by `BunRequest`. Deliberately NOT typed as
|
|
4
|
+
* `Bun.Server` — a public constructor parameter typed against the ambient `Bun` namespace would
|
|
5
|
+
* leak into this package's emitted `.d.ts`, breaking `tsc` for downstream consumers who don't have
|
|
6
|
+
* `@types/bun` installed even though they never touch the Bun adapter (confirmed: without this,
|
|
7
|
+
* the emitted declaration file contains a bare unresolvable `Bun.Server<any>` reference). Bun's
|
|
8
|
+
* real `Server` structurally satisfies this interface, so `bun/BunRouter.ts` (which has its own
|
|
9
|
+
* `/// <reference types="bun" />`) can pass a real instance unchanged.
|
|
10
|
+
*/
|
|
11
|
+
export interface RequestIPSource {
|
|
12
|
+
requestIP(req: Request): {
|
|
13
|
+
address?: string;
|
|
14
|
+
} | null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Adapts a Bun/Fetch API `Request` to the framework-agnostic `HttpRequest` interface.
|
|
18
|
+
*
|
|
19
|
+
* Unlike uWS's stack-allocated request, a Fetch API `Request` stays valid across `await`, so there
|
|
20
|
+
* is no urgency to capture everything synchronously in the constructor the way `UWSRequest` must —
|
|
21
|
+
* this is done here purely for parity/simplicity, not correctness.
|
|
22
|
+
*/
|
|
23
|
+
export declare class BunRequest implements HttpRequest {
|
|
24
|
+
readonly method: string;
|
|
25
|
+
readonly url: string;
|
|
26
|
+
readonly path: string;
|
|
27
|
+
readonly headers: Record<string, string>;
|
|
28
|
+
params: Record<string, string>;
|
|
29
|
+
readonly query: Record<string, string | string[]>;
|
|
30
|
+
body: any;
|
|
31
|
+
rawBody?: Buffer;
|
|
32
|
+
readonly cookies: Record<string, string>;
|
|
33
|
+
readonly signedCookies: Record<string, string>;
|
|
34
|
+
readonly socket: {
|
|
35
|
+
remoteAddress?: string;
|
|
36
|
+
};
|
|
37
|
+
user?: any;
|
|
38
|
+
authPayload?: any;
|
|
39
|
+
authToken?: string;
|
|
40
|
+
/** Allow arbitrary per-request properties (websocket, wsHandled, etc.) */
|
|
41
|
+
[key: string]: any;
|
|
42
|
+
constructor(req: Request, server: RequestIPSource);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Adapts a buffered/streamed set of response calls to the framework-agnostic `HttpResponse`
|
|
46
|
+
* interface, bridging them into a Fetch API `Response` that `Bun.serve()`'s `fetch()` handler
|
|
47
|
+
* resolves and returns.
|
|
48
|
+
*
|
|
49
|
+
* Two modes:
|
|
50
|
+
* - Buffered (default): `end()`/`json()`/`send()` finalize the body and resolve `responseReady`
|
|
51
|
+
* with a single complete `Response`.
|
|
52
|
+
* - Streaming: `flushHeaders()`/`write()` construct a `ReadableStream`-backed `Response` and
|
|
53
|
+
* resolve `responseReady` immediately (before the body is fully known) so the client starts
|
|
54
|
+
* receiving bytes right away; further `write()` calls enqueue chunks, `end()` closes the stream.
|
|
55
|
+
*/
|
|
56
|
+
export declare class BunResponse implements HttpResponse {
|
|
57
|
+
private _statusCode;
|
|
58
|
+
private _headers;
|
|
59
|
+
private _headersSent;
|
|
60
|
+
private _writableEnded;
|
|
61
|
+
private _streaming;
|
|
62
|
+
private _aborted;
|
|
63
|
+
private _abortHandlers;
|
|
64
|
+
/** Set to true for HEAD requests — body bytes must not be sent. */
|
|
65
|
+
isHead: boolean;
|
|
66
|
+
/** Intermediate result passed between middleware. */
|
|
67
|
+
result?: any;
|
|
68
|
+
/** Allow arbitrary per-response properties. */
|
|
69
|
+
[key: string]: any;
|
|
70
|
+
private _body;
|
|
71
|
+
private _controller;
|
|
72
|
+
private _stream;
|
|
73
|
+
private _resolveReady;
|
|
74
|
+
/** Resolves once headers are known — either the full buffered Response, or a streaming one. */
|
|
75
|
+
readonly responseReady: Promise<Response>;
|
|
76
|
+
constructor(rawRequest: Request);
|
|
77
|
+
get statusCode(): number;
|
|
78
|
+
get headersSent(): boolean;
|
|
79
|
+
get writableEnded(): boolean;
|
|
80
|
+
status(code: number): this;
|
|
81
|
+
setHeader(key: string, value: string | number): this;
|
|
82
|
+
getHeader(key: string): string | undefined;
|
|
83
|
+
json(data: any): void;
|
|
84
|
+
send(data?: any): void;
|
|
85
|
+
end(data?: any): void;
|
|
86
|
+
/**
|
|
87
|
+
* Flushes status and headers to the wire immediately without ending the response, via a
|
|
88
|
+
* `ReadableStream`, and resolves `responseReady` right away so the client starts receiving
|
|
89
|
+
* bytes before the rest of the middleware chain finishes. Required before streaming data
|
|
90
|
+
* (e.g. SSE). Safe to call multiple times — only acts on the first call.
|
|
91
|
+
*/
|
|
92
|
+
flushHeaders(): void;
|
|
93
|
+
/**
|
|
94
|
+
* Writes a chunk to the response without ending it (streaming / SSE).
|
|
95
|
+
* Flushes headers on the first call if they haven't been sent yet.
|
|
96
|
+
*/
|
|
97
|
+
write(data: string | Buffer): void;
|
|
98
|
+
/**
|
|
99
|
+
* Registers a callback to run when the client aborts the connection — either before any
|
|
100
|
+
* response was sent (via the request's `AbortSignal`) or mid-stream (via the `ReadableStream`'s
|
|
101
|
+
* `cancel` callback).
|
|
102
|
+
*/
|
|
103
|
+
onAbort(callback: () => void): void;
|
|
104
|
+
/**
|
|
105
|
+
* Errors the underlying stream if it's still open. Used when a middleware chain throws after
|
|
106
|
+
* streaming has already started and the `Response` has already been handed back to the client.
|
|
107
|
+
*/
|
|
108
|
+
abortStream(err: any): void;
|
|
109
|
+
private _enqueue;
|
|
110
|
+
private _buildHeaders;
|
|
111
|
+
private _buildResponse;
|
|
112
|
+
private _buildStreamingResponse;
|
|
113
|
+
}
|
|
114
|
+
export type ReadBunBodyResult = {
|
|
115
|
+
ok: true;
|
|
116
|
+
} | {
|
|
117
|
+
ok: false;
|
|
118
|
+
response: Response;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Reads the full request body from a Bun `Request` into a Buffer, mirroring `readBody()`'s uWS
|
|
122
|
+
* behavior exactly: body parsing (JSON / URL-encoded) is applied based on content-type and the
|
|
123
|
+
* result is cached on `req.body` / `req.rawBody`. Rejects with a 413 JSON `Response` if the body
|
|
124
|
+
* exceeds `maxBodySize` — checked both against the declared `Content-Length` up front (fast path)
|
|
125
|
+
* and against the actual accumulated byte count as chunks arrive, so chunked/unknown-length bodies
|
|
126
|
+
* that lie about or omit `Content-Length` are still caught, matching uWS's guarantee.
|
|
127
|
+
*/
|
|
128
|
+
export declare function readBunBody(req: BunRequest, rawRequest: Request, maxBodySize?: number): Promise<ReadBunBodyResult>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { IHttpRouter, RequestHandler } from "../types.js";
|
|
2
|
+
import { type WsUpgradeAuth } from "../MiddlewareChain.js";
|
|
3
|
+
/**
|
|
4
|
+
* Bun-native HTTP/WebSocket router built on `Bun.serve()`. Exposes the same public surface as the
|
|
5
|
+
* uWS-backed `HttpRouter` (`use/get/post/put/delete/patch/head/options/ws/listen/close/isListening/
|
|
6
|
+
* listenPort`) so `RouteUtils.registerRoute()` and `Server.ts` work against either implementation
|
|
7
|
+
* unchanged.
|
|
8
|
+
*
|
|
9
|
+
* Unlike uWS, `Bun.serve()` has no built-in dynamic router, so this class maintains its own route
|
|
10
|
+
* table matched inside a single `fetch()` callback, with static-segment > `:param` > `/*` wildcard
|
|
11
|
+
* precedence — replicating uWS's specificity-based matching.
|
|
12
|
+
*/
|
|
13
|
+
export declare class BunRouter implements IHttpRouter {
|
|
14
|
+
private readonly routesByMethod;
|
|
15
|
+
private readonly wsRoutes;
|
|
16
|
+
private readonly globalMiddleware;
|
|
17
|
+
private preRouteCount;
|
|
18
|
+
private readonly rootWildcardVerbs;
|
|
19
|
+
private readonly maxBodySize;
|
|
20
|
+
private readonly sslConfig;
|
|
21
|
+
private _bunServer;
|
|
22
|
+
/** The port the server is currently listening on (set after a successful `listen()` call). */
|
|
23
|
+
listenPort: number;
|
|
24
|
+
constructor(maxBodySize?: number, sslConfig?: any);
|
|
25
|
+
/** Returns `true` if the server is currently listening. */
|
|
26
|
+
get isListening(): boolean;
|
|
27
|
+
/** Register global middleware that runs before every route handler. */
|
|
28
|
+
use(...handlers: RequestHandler[]): this;
|
|
29
|
+
/**
|
|
30
|
+
* Freezes the pre-route middleware count the first time a route is registered.
|
|
31
|
+
* All global middleware registered BEFORE this call is "pre-route" middleware;
|
|
32
|
+
* everything added after is "post-route" (error handlers, metrics, etc.).
|
|
33
|
+
*/
|
|
34
|
+
private capturePreRouteCount;
|
|
35
|
+
private pushRoute;
|
|
36
|
+
private register;
|
|
37
|
+
get(routePath: string, ...handlers: RequestHandler[]): this;
|
|
38
|
+
post(routePath: string, ...handlers: RequestHandler[]): this;
|
|
39
|
+
put(routePath: string, ...handlers: RequestHandler[]): this;
|
|
40
|
+
delete(routePath: string, ...handlers: RequestHandler[]): this;
|
|
41
|
+
patch(routePath: string, ...handlers: RequestHandler[]): this;
|
|
42
|
+
head(routePath: string, ...handlers: RequestHandler[]): this;
|
|
43
|
+
options(routePath: string, ...handlers: RequestHandler[]): this;
|
|
44
|
+
/**
|
|
45
|
+
* Registers a WebSocket route. Handlers follow the same `(req, res, next)` pattern as HTTP
|
|
46
|
+
* routes; they receive `req.websocket` containing the Bun WebSocket shim.
|
|
47
|
+
*
|
|
48
|
+
* `upgradeAuth` is an optional pre-upgrade auth function, run synchronously inside `fetch()`
|
|
49
|
+
* before the handshake. If it returns `{ reject: true }`, an HTTP 401 is sent and the upgrade
|
|
50
|
+
* is aborted. If it returns `{ user, ... }`, those credentials are attached to the request so
|
|
51
|
+
* downstream middleware sees an authenticated user. If it returns `{}`, auth falls through to
|
|
52
|
+
* the post-upgrade message-based LOGIN flow.
|
|
53
|
+
*/
|
|
54
|
+
ws(routePath: string, handlers: RequestHandler[], _wsOptions?: any, upgradeAuth?: WsUpgradeAuth): this;
|
|
55
|
+
/**
|
|
56
|
+
* Matches a request against the route table. Exact-length static/param routes always outrank
|
|
57
|
+
* wildcard-suffixed routes, matching uWS's `best ?? wildcard` fallback behavior. Among wildcard
|
|
58
|
+
* candidates, the one with the longest (most specific) matching prefix wins — e.g. `/static/*`
|
|
59
|
+
* beats a bare `/*` for requests under `/static`.
|
|
60
|
+
*
|
|
61
|
+
* `reqHasTrailingSlash` is required to replicate uWS's own wildcard boundary rule, verified
|
|
62
|
+
* empirically against real uWS: a `<prefix>/*` route matches `<prefix>/` and anything nested
|
|
63
|
+
* under it, but NOT the bare `<prefix>` with no trailing slash — a distinction that's lost if
|
|
64
|
+
* matching were done on segment arrays alone, since `/static` and `/static/` produce the same
|
|
65
|
+
* segment list once empty segments are filtered out.
|
|
66
|
+
*/
|
|
67
|
+
private matchRoute;
|
|
68
|
+
private matchWsRoute;
|
|
69
|
+
private readonly fetchHandler;
|
|
70
|
+
private readonly websocketConfig;
|
|
71
|
+
/**
|
|
72
|
+
* Starts listening on the given host and port.
|
|
73
|
+
* Resolves when the server is ready; rejects if the port cannot be bound.
|
|
74
|
+
*/
|
|
75
|
+
listen(host: string, port: number): Promise<void>;
|
|
76
|
+
/** Stops the server, closing the listen socket. */
|
|
77
|
+
close(): void;
|
|
78
|
+
[key: string]: any;
|
|
79
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import type { IWebSocketShim } from "../IWebSocketShim.js";
|
|
3
|
+
/**
|
|
4
|
+
* Minimal structural slice of Bun's `ServerWebSocket` used by this shim. Deliberately NOT typed as
|
|
5
|
+
* `Bun.ServerWebSocket` — a public constructor parameter typed against the ambient `Bun` namespace
|
|
6
|
+
* would leak into this package's emitted `.d.ts`, breaking `tsc` for downstream consumers who don't
|
|
7
|
+
* have `@types/bun` installed even though they never touch the Bun adapter. Bun's real
|
|
8
|
+
* `ServerWebSocket` structurally satisfies this interface, so `bun/BunRouter.ts` (which has its own
|
|
9
|
+
* `/// <reference types="bun" />`) can pass a real instance unchanged.
|
|
10
|
+
*/
|
|
11
|
+
export interface WebSocketSendTarget {
|
|
12
|
+
send(data: string | Uint8Array): number;
|
|
13
|
+
close(code?: number, reason?: string): void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Wraps a Bun `ServerWebSocket` handle and exposes an EventEmitter-based API compatible with
|
|
17
|
+
* `IWebSocketShim`, mirroring `UWSWebSocketShim`'s behavior for the uWS-backed router so that
|
|
18
|
+
* route handlers (e.g. `BaseAdminRoute`) and `AuthMiddleware.authWebSocket()` work unchanged
|
|
19
|
+
* regardless of which runtime is active.
|
|
20
|
+
*/
|
|
21
|
+
export declare class BunWebSocketShim extends EventEmitter implements IWebSocketShim {
|
|
22
|
+
private readonly _ws;
|
|
23
|
+
readyState: number;
|
|
24
|
+
constructor(ws: WebSocketSendTarget);
|
|
25
|
+
/**
|
|
26
|
+
* Sends data over the WebSocket connection.
|
|
27
|
+
* @param data The data to send.
|
|
28
|
+
* @param cb Optional callback invoked on completion or error.
|
|
29
|
+
*/
|
|
30
|
+
send(data: any, cb?: (err?: Error) => void): void;
|
|
31
|
+
/**
|
|
32
|
+
* Closes the WebSocket connection with an optional code and reason.
|
|
33
|
+
*/
|
|
34
|
+
close(code?: number, reason?: string): void;
|
|
35
|
+
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
export type { HttpRequest, HttpResponse, NextFunction, RequestHandler
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export
|
|
5
|
-
export type {
|
|
6
|
-
export {
|
|
1
|
+
export type { ErrorHandler, HttpRequest, HttpResponse, IHttpRouter, NextFunction, RequestHandler } from "./types.js";
|
|
2
|
+
export { isBunRuntime } from "./RuntimeDetect.js";
|
|
3
|
+
export type { IWebSocketShim } from "./IWebSocketShim.js";
|
|
4
|
+
export { extractParamNames, makeWsStubResponse, runChain } from "./MiddlewareChain.js";
|
|
5
|
+
export type { WsUpgradeAuth, WsUpgradeAuthResult } from "./MiddlewareChain.js";
|
|
6
|
+
export { UWSRequest, UWSResponse, readBody } from "./uWS/Adapters.js";
|
|
7
|
+
export type { HttpRouter } from "./uWS/Router.js";
|
|
8
|
+
export type { RequestWS } from "./uWS/WebSocket.js";
|
|
9
|
+
export { UWSWebSocketShim, createWebSocketStream } from "./uWS/WebSocket.js";
|
|
10
|
+
export { BunRequest, BunResponse, readBunBody } from "./bun/BunAdapters.js";
|
|
11
|
+
export { BunRouter } from "./bun/BunRouter.js";
|
|
12
|
+
export { BunWebSocketShim } from "./bun/BunWebSocket.js";
|
|
@@ -52,3 +52,24 @@ export type NextFunction = (err?: any) => void;
|
|
|
52
52
|
export type RequestHandler = (req: HttpRequest, res: HttpResponse, next: NextFunction) => void | Promise<void>;
|
|
53
53
|
/** Standard 4-param error-handling middleware function. */
|
|
54
54
|
export type ErrorHandler = (err: any, req: HttpRequest, res: HttpResponse, next: NextFunction) => void | Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Public surface shared by every HTTP router implementation (uWS-backed, Bun-backed, ...).
|
|
57
|
+
* `Server.ts` depends only on this interface, never on a concrete router class, so the
|
|
58
|
+
* underlying HTTP server can be swapped per-runtime without touching route registration.
|
|
59
|
+
*/
|
|
60
|
+
export interface IHttpRouter {
|
|
61
|
+
use(...handlers: RequestHandler[]): this;
|
|
62
|
+
get(path: string, ...handlers: RequestHandler[]): this;
|
|
63
|
+
post(path: string, ...handlers: RequestHandler[]): this;
|
|
64
|
+
put(path: string, ...handlers: RequestHandler[]): this;
|
|
65
|
+
delete(path: string, ...handlers: RequestHandler[]): this;
|
|
66
|
+
patch(path: string, ...handlers: RequestHandler[]): this;
|
|
67
|
+
head(path: string, ...handlers: RequestHandler[]): this;
|
|
68
|
+
options(path: string, ...handlers: RequestHandler[]): this;
|
|
69
|
+
ws(path: string, handlers: RequestHandler[], wsOptions?: any, upgradeAuth?: any): this;
|
|
70
|
+
listen(host: string, port: number): Promise<void>;
|
|
71
|
+
close(): void;
|
|
72
|
+
readonly isListening: boolean;
|
|
73
|
+
listenPort: number;
|
|
74
|
+
[key: string]: any;
|
|
75
|
+
}
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import type { HttpRequest, HttpResponse } from "
|
|
1
|
+
import type { HttpRequest, HttpResponse } from "../types.js";
|
|
2
2
|
import type { HttpRequest as UWSHttpRequest, HttpResponse as UWSHttpResponse } from "uWebSockets.js";
|
|
3
|
+
/** Parses a `cookie` header string into a key/value map. */
|
|
4
|
+
export declare function parseCookies(cookieHeader: string): Record<string, string>;
|
|
5
|
+
/** Parses a URL query string (without leading `?`) into a key/value map. */
|
|
6
|
+
export declare function parseQueryString(qs: string): Record<string, string | string[]>;
|
|
7
|
+
/**
|
|
8
|
+
* Parses a raw request body Buffer according to its content-type header. Shared by both the
|
|
9
|
+
* uWS-backed and Bun-backed HTTP adapters so body-parsing rules stay in one place:
|
|
10
|
+
* `application/json` is parsed (falling back to the raw string on parse failure),
|
|
11
|
+
* `application/x-www-form-urlencoded` is parsed into a plain object, and anything else is
|
|
12
|
+
* returned as the raw Buffer. Returns `undefined` for an empty body.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseBodyByContentType(raw: Buffer, contentType: string): any;
|
|
3
15
|
/**
|
|
4
16
|
* Adapts a uWS `HttpRequest` to the framework-agnostic `HttpRequest` interface.
|
|
5
17
|
*
|
|
@@ -79,9 +91,19 @@ export declare class UWSResponse implements HttpResponse {
|
|
|
79
91
|
/** Converts a numeric status code to the "200 OK" string format uWS expects. */
|
|
80
92
|
private _statusToString;
|
|
81
93
|
}
|
|
94
|
+
/** Default maximum accepted request body size (10 MiB) when no explicit limit is configured. */
|
|
95
|
+
export declare const DEFAULT_MAX_BODY_SIZE: number;
|
|
82
96
|
/**
|
|
83
97
|
* Reads the full request body from a uWS response object as a Buffer.
|
|
84
98
|
* Body parsing (JSON / URL-encoded) is applied based on content-type and the result
|
|
85
99
|
* is cached on `req.body` / `req.rawBody`.
|
|
100
|
+
*
|
|
101
|
+
* If the accumulated body exceeds `maxBodySize`, a `413 Payload Too Large` response is written
|
|
102
|
+
* directly and the connection is ended — the caller must check the resolved value and skip running
|
|
103
|
+
* any further middleware/routing for this request when it's `false`, since a response has already
|
|
104
|
+
* been sent.
|
|
105
|
+
*
|
|
106
|
+
* @returns `true` if the body was read normally (or there was none to read), `false` if the request
|
|
107
|
+
* was rejected for exceeding `maxBodySize`.
|
|
86
108
|
*/
|
|
87
|
-
export declare function readBody(uwsRes: UWSHttpResponse, req: UWSRequest): Promise<
|
|
109
|
+
export declare function readBody(uwsRes: UWSHttpResponse, req: UWSRequest, maxBodySize?: number): Promise<boolean>;
|
|
@@ -1,34 +1,8 @@
|
|
|
1
1
|
import uWS from "uWebSockets.js";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* error handlers. On error, execution skips to the next error handler.
|
|
7
|
-
*
|
|
8
|
-
* Each handler is awaited via a Promise that resolves when next() is called, not when
|
|
9
|
-
* the handler's return value resolves. This correctly handles: (1) sync handlers that
|
|
10
|
-
* call next() synchronously, (2) async handlers that return next() or await before
|
|
11
|
-
* calling it, and (3) sync handlers that schedule next() via callbacks such as
|
|
12
|
-
* setTimeout or socket.once. The chain terminates early when a handler ends the
|
|
13
|
-
* response without calling next().
|
|
14
|
-
*/
|
|
15
|
-
export declare function runChain(handlers: RequestHandler[], req: HttpRequest, res: HttpResponse): Promise<void>;
|
|
16
|
-
/** Result returned by a pre-upgrade WebSocket auth function. */
|
|
17
|
-
export type WsUpgradeAuthResult = {
|
|
18
|
-
user?: any;
|
|
19
|
-
authPayload?: any;
|
|
20
|
-
authToken?: string;
|
|
21
|
-
/** Set to `true` to reject the connection with HTTP 401 before the WebSocket handshake. */
|
|
22
|
-
reject?: boolean;
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* Optional pre-upgrade auth function for WebSocket routes. Called synchronously inside the uWS
|
|
26
|
-
* `upgrade` callback — before the WebSocket handshake completes. Returning `{ reject: true }`
|
|
27
|
-
* sends an HTTP 401 and skips the upgrade entirely. Returning `{}` falls through to the
|
|
28
|
-
* post-upgrade message-based LOGIN flow. Returning `{ user, ... }` pre-authenticates the
|
|
29
|
-
* connection so clients that can send an Authorization header skip the LOGIN step.
|
|
30
|
-
*/
|
|
31
|
-
export type WsUpgradeAuth = (req: HttpRequest) => WsUpgradeAuthResult;
|
|
2
|
+
import type { IHttpRouter, RequestHandler } from "../types.js";
|
|
3
|
+
import { type WsUpgradeAuth } from "../MiddlewareChain.js";
|
|
4
|
+
export { runChain, extractParamNames } from "../MiddlewareChain.js";
|
|
5
|
+
export type { WsUpgradeAuth, WsUpgradeAuthResult } from "../MiddlewareChain.js";
|
|
32
6
|
/**
|
|
33
7
|
* Thin Express-compatible wrapper over `uWS.TemplatedApp`.
|
|
34
8
|
*
|
|
@@ -40,7 +14,7 @@ export type WsUpgradeAuth = (req: HttpRequest) => WsUpgradeAuthResult;
|
|
|
40
14
|
* - `ws(path, handlers)` — native uWS WebSocket routing
|
|
41
15
|
* - `listen(host, port)` / `close()` — server lifecycle
|
|
42
16
|
*/
|
|
43
|
-
export declare class HttpRouter {
|
|
17
|
+
export declare class HttpRouter implements IHttpRouter {
|
|
44
18
|
private readonly uwsApp;
|
|
45
19
|
private readonly globalMiddleware;
|
|
46
20
|
private listenSocket;
|
|
@@ -57,7 +31,9 @@ export declare class HttpRouter {
|
|
|
57
31
|
* avoid clobbering an app-defined root catch-all with the default JSON 404 fallback.
|
|
58
32
|
*/
|
|
59
33
|
private readonly rootWildcardVerbs;
|
|
60
|
-
|
|
34
|
+
/** Maximum accepted request body size, in bytes. */
|
|
35
|
+
private readonly maxBodySize;
|
|
36
|
+
constructor(uwsApp: uWS.TemplatedApp, maxBodySize?: number);
|
|
61
37
|
/** Returns `true` if the server is currently listening. */
|
|
62
38
|
get isListening(): boolean;
|
|
63
39
|
/** Register global middleware that runs before every route handler. */
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
2
|
import { Duplex } from "stream";
|
|
3
3
|
import type { WebSocket } from "uWebSockets.js";
|
|
4
|
-
import type { HttpRequest } from "
|
|
4
|
+
import type { HttpRequest } from "../types.js";
|
|
5
|
+
import type { IWebSocketShim } from "../IWebSocketShim.js";
|
|
5
6
|
/**
|
|
6
7
|
* HTTP request type for handling WebSocket upgrade requests.
|
|
7
8
|
* Extends `HttpRequest` with WebSocket-specific properties set by the router
|
|
8
|
-
* after the
|
|
9
|
+
* after the WebSocket connection is opened.
|
|
9
10
|
*/
|
|
10
11
|
export interface RequestWS extends HttpRequest {
|
|
11
12
|
/**
|
|
12
|
-
* The
|
|
13
|
+
* The WebSocket shim for this connection. Set on `open` after the upgrade.
|
|
13
14
|
*/
|
|
14
|
-
websocket:
|
|
15
|
+
websocket: IWebSocketShim | undefined;
|
|
15
16
|
/**
|
|
16
17
|
* Indicates that the WebSocket handler has processed this connection and the
|
|
17
18
|
* connection should remain open. Set to `true` by `wrapMiddleware` when a
|
|
@@ -31,7 +32,7 @@ export interface RequestWS extends HttpRequest {
|
|
|
31
32
|
* The Router's `ws()` implementation stores the shim in `ws.getUserData()` so that
|
|
32
33
|
* behavior callbacks can call `shim.emit(...)`.
|
|
33
34
|
*/
|
|
34
|
-
export declare class UWSWebSocketShim extends EventEmitter {
|
|
35
|
+
export declare class UWSWebSocketShim extends EventEmitter implements IWebSocketShim {
|
|
35
36
|
private readonly _ws;
|
|
36
37
|
readyState: number;
|
|
37
38
|
constructor(ws: WebSocket<any>);
|
|
@@ -10,6 +10,7 @@ export declare class ModelUtils {
|
|
|
10
10
|
/** The `typeorm` module containing the query operators used to build SQL queries. */
|
|
11
11
|
private static typeOrm;
|
|
12
12
|
private static idPropertyCache;
|
|
13
|
+
private static readOnlyPropertyCache;
|
|
13
14
|
/**
|
|
14
15
|
* Provides the `typeorm` module to use when building SQL queries. This is called automatically when a SQL
|
|
15
16
|
* datastore connection is established.
|
|
@@ -28,6 +29,13 @@ export declare class ModelUtils {
|
|
|
28
29
|
* @returns The list of all property names that have the @Identifier decorator applied.
|
|
29
30
|
*/
|
|
30
31
|
static getIdPropertyNames(modelClass: any): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Retrieves a list of all of the specified class's properties that have the @ReadOnly decorator applied.
|
|
34
|
+
*
|
|
35
|
+
* @param modelClass The class definition to search for read-only properties from.
|
|
36
|
+
* @returns The list of all property names that have the @ReadOnly decorator applied.
|
|
37
|
+
*/
|
|
38
|
+
static getReadOnlyPropertyNames(modelClass: any): string[];
|
|
31
39
|
/**
|
|
32
40
|
* Builds a query object for use with `find` functions of the given repository for retrieving objects matching the
|
|
33
41
|
* specified unique identifier.
|
|
@@ -68,6 +76,28 @@ export declare class ModelUtils {
|
|
|
68
76
|
* @param param
|
|
69
77
|
*/
|
|
70
78
|
private static getQueryParamValue;
|
|
79
|
+
/**
|
|
80
|
+
* Recursively verifies that no key in the given value (at any depth, including keys of objects nested inside
|
|
81
|
+
* arrays) is a MongoDB operator (starts with `$`) or uses dot-notation field addressing (contains `.`). Client
|
|
82
|
+
* input is only ever meant to supply plain field values/comparison operands — never raw Mongo query operators —
|
|
83
|
+
* so any such key indicates an attempt to inject arbitrary query behavior (e.g. `$where`, `$expr`, or reaching
|
|
84
|
+
* into a field the API doesn't expose via dot-notation).
|
|
85
|
+
*
|
|
86
|
+
* @param value The value to check, typically a parsed query parameter.
|
|
87
|
+
* @throws {ApiError} If an operator-like or dotted key is found anywhere in `value`.
|
|
88
|
+
*/
|
|
89
|
+
private static assertNoOperatorInjection;
|
|
90
|
+
/** Maximum accepted length of a client-supplied `like()` search pattern. */
|
|
91
|
+
private static readonly MAX_LIKE_PATTERN_LENGTH;
|
|
92
|
+
/**
|
|
93
|
+
* Best-effort check for regex patterns vulnerable to catastrophic backtracking (ReDoS). Rejects patterns that
|
|
94
|
+
* are unreasonably long, or that contain a quantified group whose contents are themselves quantified (e.g.
|
|
95
|
+
* `(a+)+`, `(a*)*`) — the classic shape that causes exponential backtracking in JS's regex engine. This is not
|
|
96
|
+
* an exhaustive defense; it catches the common cases a client would realistically send.
|
|
97
|
+
*
|
|
98
|
+
* @param pattern The user-supplied `like()` pattern.
|
|
99
|
+
*/
|
|
100
|
+
private static isUnsafeRegexPattern;
|
|
71
101
|
/**
|
|
72
102
|
* Given a string containing a parameter value and/or a comparison operation return a MongoDB compatible find value.
|
|
73
103
|
* e.g.
|
|
@@ -2,11 +2,11 @@ import type { Repository } from "typeorm";
|
|
|
2
2
|
import { MongoRepository } from "../database/MongoRepository.js";
|
|
3
3
|
import { BaseEntity } from "../models/BaseEntity.js";
|
|
4
4
|
import { SimpleEntity } from "../models/SimpleEntity.js";
|
|
5
|
-
import { JWTUser } from "@rapidrest/core";
|
|
5
|
+
import { type JWTUser } from "@rapidrest/core";
|
|
6
6
|
import { Redis } from "ioredis";
|
|
7
7
|
import { ObjectFactory } from "../ObjectFactory.js";
|
|
8
8
|
import { NotificationUtils } from "../NotificationUtils.js";
|
|
9
|
-
import { AccessControlList } from "../security/index.js";
|
|
9
|
+
import { type AccessControlList } from "../security/index.js";
|
|
10
10
|
import type { ACLUtils } from "../security/ACLUtils.js";
|
|
11
11
|
import { ConnectionManager } from "../database/index.js";
|
|
12
12
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { JWTUser } from "@rapidrest/core";
|
|
1
|
+
import { type JWTUser } from "@rapidrest/core";
|
|
2
2
|
import { Redis } from "ioredis";
|
|
3
3
|
import Transport from "winston-transport";
|
|
4
|
-
import { type
|
|
4
|
+
import { type IWebSocketShim } from "../http/IWebSocketShim.js";
|
|
5
5
|
/**
|
|
6
6
|
* Implements a Winston transport that pipes incoming log messages to a configured redis pubsub channel.
|
|
7
7
|
*/
|
|
@@ -53,7 +53,7 @@ export declare class BaseAdminRoute {
|
|
|
53
53
|
protected trustedRoles: string[];
|
|
54
54
|
protected init(): Promise<void>;
|
|
55
55
|
clearCache(user?: JWTUser): Promise<void>;
|
|
56
|
-
logs(socket:
|
|
56
|
+
logs(socket: IWebSocketShim, user: JWTUser): Promise<void>;
|
|
57
57
|
getReleaseNotes(user?: JWTUser): string | undefined;
|
|
58
58
|
restart(user?: JWTUser): void;
|
|
59
59
|
}
|
|
@@ -31,6 +31,10 @@ export declare class BasePushRoute {
|
|
|
31
31
|
private activeSubs;
|
|
32
32
|
private logger;
|
|
33
33
|
private redisConfig;
|
|
34
|
+
/** The maximum number of concurrent sockets a single user may hold open at once. */
|
|
35
|
+
private maxSocketsPerUser;
|
|
36
|
+
/** The maximum number of channels a single user may be subscribed to at once. */
|
|
37
|
+
private maxSubscriptionsPerUser;
|
|
34
38
|
/** A persistent redis client used to publish outgoing push messages. */
|
|
35
39
|
private redisPub?;
|
|
36
40
|
private init;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HttpRequest, HttpResponse } from "../http/types.js";
|
|
1
|
+
import type { HttpRequest, HttpResponse } from "../http/types.js";
|
|
2
2
|
/**
|
|
3
3
|
* The `BaseStaticRoute` class provides a single endpoint for serving static files from a designated path on the filesystem (e.g. `./public`).
|
|
4
4
|
* Uses the path specified in the project configuration key `static_files`. Default is `public`.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { BaseEntity } from "../models/BaseEntity.js";
|
|
2
2
|
import type { HttpRequest, HttpResponse } from "../http/index.js";
|
|
3
3
|
import { SimpleEntity } from "../models/SimpleEntity.js";
|
|
4
|
-
import { JWTUser } from "@rapidrest/core";
|
|
5
|
-
import { ModelRoute, UpdateObject } from "./ModelRoute.js";
|
|
4
|
+
import type { JWTUser } from "@rapidrest/core";
|
|
5
|
+
import { ModelRoute, type UpdateObject } from "./ModelRoute.js";
|
|
6
6
|
/**
|
|
7
7
|
* The `CRUDRoute` provides a base implementation of all CRUD endpoint behaviors that `ModelRoute` offers for a given
|
|
8
8
|
* data model class. This class provides the most common default settings for each route handler. If you desire additional
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RepoUtils, type RepoOperationOptions } from "../models/RepoUtils.js";
|
|
2
2
|
import { BaseEntity } from "../models/BaseEntity.js";
|
|
3
3
|
import { Redis } from "ioredis";
|
|
4
4
|
import type { HttpRequest, HttpResponse } from "../http/index.js";
|
|
5
5
|
import { SimpleEntity } from "../models/SimpleEntity.js";
|
|
6
|
-
import { AccessControlList } from "../security/AccessControlList.js";
|
|
6
|
+
import { type AccessControlList } from "../security/AccessControlList.js";
|
|
7
7
|
import { ACLUtils } from "../security/ACLUtils.js";
|
|
8
8
|
import { NotificationUtils } from "../NotificationUtils.js";
|
|
9
9
|
import { ObjectFactory } from "../ObjectFactory.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { JWTUser } from "@rapidrest/core";
|
|
1
|
+
import { type JWTUser } from "@rapidrest/core";
|
|
2
2
|
import type { HttpRequest as Request } from "../http/index.js";
|
|
3
|
-
import {
|
|
3
|
+
import { ACLAction, type AccessControlList, type ACLRecord } from "./AccessControlList.js";
|
|
4
4
|
/**
|
|
5
5
|
* Common utility functions for working with `AccessControlList` objects and validating user permissions.
|
|
6
6
|
*/
|
|
@@ -36,9 +36,12 @@ export declare class ACLUtils {
|
|
|
36
36
|
* @param user The user to validate permissions of.
|
|
37
37
|
* @param acl The ACL or uid of an ACL to validate permissions against.
|
|
38
38
|
* @param action The action that the user desires permission for.
|
|
39
|
+
* @param reqCache Optional request-scoped cache (see `findACL`) to avoid redundant Redis/DB round trips
|
|
40
|
+
* when the same ACL uid — typically a shared parent — is checked repeatedly within one request, such as
|
|
41
|
+
* once per record when filtering a page of search results.
|
|
39
42
|
* @returns `true` if the user has at least one of the permissions granted for the given entity, otherwise `false`.
|
|
40
43
|
*/
|
|
41
|
-
hasPermission(user: JWTUser | undefined, acl: AccessControlList | string, action: ACLAction): Promise<boolean>;
|
|
44
|
+
hasPermission(user: JWTUser | undefined, acl: AccessControlList | string, action: ACLAction, reqCache?: Map<string, AccessControlList | undefined>): Promise<boolean>;
|
|
42
45
|
/**
|
|
43
46
|
* Retrieves the access control list with the associated identifier and populates the parent(s).
|
|
44
47
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseMongoEntity } from "../models/index.js";
|
|
2
|
-
import { AccessControlList, ACLRecord } from "./AccessControlList.js";
|
|
2
|
+
import type { AccessControlList, ACLRecord } from "./AccessControlList.js";
|
|
3
3
|
/**
|
|
4
4
|
* Implementation of the `ACLRecord` interface for use with MongoDB databases.
|
|
5
5
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseEntity } from "../models/BaseEntity.js";
|
|
2
|
-
import { AccessControlList, ACLRecord } from "./AccessControlList.js";
|
|
2
|
+
import type { AccessControlList, ACLRecord } from "./AccessControlList.js";
|
|
3
3
|
/**
|
|
4
4
|
* Implementation of the `ACLRecord` interface for use with SQL databases.
|
|
5
5
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { HttpRequest, HttpResponse } from "../http/index.js";
|
|
2
|
-
import { AccessControlList } from "./AccessControlList.js";
|
|
3
|
-
import { JWTUser } from "@rapidrest/core";
|
|
4
|
-
import { UpdateObject } from "../routes/ModelRoute.js";
|
|
2
|
+
import { type AccessControlList } from "./AccessControlList.js";
|
|
3
|
+
import { type JWTUser } from "@rapidrest/core";
|
|
4
|
+
import type { UpdateObject } from "../routes/ModelRoute.js";
|
|
5
5
|
import { CRUDRoute } from "../routes/CRUDRoute.js";
|
|
6
6
|
/**
|
|
7
7
|
* The `BaseACLRoute` class provides a base set of endpoints for managing Access Control List records.
|