@rapidrest/service-core 2.1.1 → 2.2.1
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/decorators/RouteDecorators.js +44 -0
- package/dist/lib/decorators/RouteDecorators.js.map +1 -1
- package/dist/lib/http/MiddlewareChain.js +15 -0
- package/dist/lib/http/MiddlewareChain.js.map +1 -1
- package/dist/lib/http/bun/BunAdapters.js +35 -0
- package/dist/lib/http/bun/BunAdapters.js.map +1 -1
- package/dist/lib/http/bun/BunRouter.js +44 -27
- package/dist/lib/http/bun/BunRouter.js.map +1 -1
- package/dist/lib/http/index.js +3 -3
- package/dist/lib/http/index.js.map +1 -1
- package/dist/lib/http/types.js.map +1 -1
- package/dist/lib/http/uWS/Adapters.js +172 -0
- package/dist/lib/http/uWS/Adapters.js.map +1 -1
- package/dist/lib/http/uWS/Router.js +55 -29
- package/dist/lib/http/uWS/Router.js.map +1 -1
- package/dist/lib/models/ModelUtils.js +91 -1
- package/dist/lib/models/ModelUtils.js.map +1 -1
- package/dist/lib/routes/RouteUtils.js +52 -4
- package/dist/lib/routes/RouteUtils.js.map +1 -1
- package/dist/types/ApiErrors.d.ts +2 -0
- package/dist/types/decorators/RouteDecorators.d.ts +29 -0
- package/dist/types/http/MiddlewareChain.d.ts +14 -1
- package/dist/types/http/bun/BunAdapters.d.ts +24 -0
- package/dist/types/http/bun/BunRouter.d.ts +8 -8
- package/dist/types/http/index.d.ts +4 -4
- package/dist/types/http/types.d.ts +46 -7
- package/dist/types/http/uWS/Adapters.d.ts +88 -0
- package/dist/types/http/uWS/Router.d.ts +8 -8
- package/dist/types/models/ModelUtils.d.ts +39 -0
- package/dist/types/routes/RouteUtils.d.ts +20 -0
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { JWTUser } from "@rapidrest/core";
|
|
2
|
+
import type { Readable } from "stream";
|
|
2
3
|
/**
|
|
3
4
|
* Framework-agnostic HTTP request interface. Mirrors the Express `Request` surface used throughout
|
|
4
5
|
* this codebase so that route handlers, middleware, and utilities require no changes when the
|
|
@@ -13,6 +14,25 @@ export interface HttpRequest {
|
|
|
13
14
|
query: Record<string, string | string[]>;
|
|
14
15
|
body: any;
|
|
15
16
|
rawBody?: Buffer;
|
|
17
|
+
/**
|
|
18
|
+
* The raw request body as a Node `Readable` stream, populated instead of `body`/`rawBody` for a
|
|
19
|
+
* route registered with `{ streamingBody: true }` (see `HttpRouteOptions`) — e.g. via the
|
|
20
|
+
* `@StreamingBody()` decorator. `undefined` for every ordinary route, where `body`/`rawBody` are
|
|
21
|
+
* populated as before.
|
|
22
|
+
*
|
|
23
|
+
* The stream yields `Buffer` chunks and supports `for await (const chunk of req.bodyStream)` as
|
|
24
|
+
* well as `.pipe()`. Backpressure is enforced end-to-end by both adapters — a slow consumer
|
|
25
|
+
* throttles how fast bytes are read off the underlying connection rather than buffering
|
|
26
|
+
* unbounded data in memory — which is the entire point of opting into streaming (e.g. a
|
|
27
|
+
* multi-GB file upload). The stream is destroyed (with an error) if the client disconnects
|
|
28
|
+
* mid-upload; a handler consuming it via `for await` sees that as a thrown error and should
|
|
29
|
+
* clean up (e.g. delete a partially-written temp file) in a `catch`/`finally`.
|
|
30
|
+
*
|
|
31
|
+
* A streaming route does NOT get the framework's default `maxBodySize` enforcement — that check
|
|
32
|
+
* only runs as part of the ordinary buffering path. The handler is responsible for enforcing
|
|
33
|
+
* whatever size limit makes sense for the route itself.
|
|
34
|
+
*/
|
|
35
|
+
bodyStream?: Readable;
|
|
16
36
|
cookies: Record<string, string>;
|
|
17
37
|
signedCookies: Record<string, string>;
|
|
18
38
|
/**
|
|
@@ -83,6 +103,25 @@ export type NextFunction = (err?: any) => void;
|
|
|
83
103
|
export type RequestHandler = (req: HttpRequest, res: HttpResponse, next: NextFunction) => void | Promise<void>;
|
|
84
104
|
/** Standard 4-param error-handling middleware function. */
|
|
85
105
|
export type ErrorHandler = (err: any, req: HttpRequest, res: HttpResponse, next: NextFunction) => void | Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Per-route registration options, optionally passed as the first element of a route's handler list
|
|
108
|
+
* (e.g. `app.post(path, { streamingBody: true }, ...handlers)`). Detected at runtime by both router
|
|
109
|
+
* implementations: a non-function first argument is treated as `HttpRouteOptions` rather than a
|
|
110
|
+
* `RequestHandler`, so omitting it entirely (the overwhelmingly common case) is unaffected — every
|
|
111
|
+
* existing call site that only ever passes handler functions keeps its exact current behavior.
|
|
112
|
+
*/
|
|
113
|
+
export interface HttpRouteOptions {
|
|
114
|
+
/**
|
|
115
|
+
* When `true`, the router does not buffer this route's request body into `req.body`/`req.rawBody`
|
|
116
|
+
* before running its middleware/handler chain. Instead the raw body is exposed as a stream on
|
|
117
|
+
* `req.bodyStream` (see its doc comment on `HttpRequest`), and the route's own handler is
|
|
118
|
+
* responsible for consuming it and enforcing any size limit it needs — the framework's
|
|
119
|
+
* `maxBodySize` 413 rejection does not apply to a streaming route. Set by the `@StreamingBody()`
|
|
120
|
+
* route decorator; see `RouteUtils.registerRoute()`.
|
|
121
|
+
*/
|
|
122
|
+
streamingBody?: boolean;
|
|
123
|
+
[key: string]: any;
|
|
124
|
+
}
|
|
86
125
|
/**
|
|
87
126
|
* Public surface shared by every HTTP router implementation (uWS-backed, Bun-backed, ...).
|
|
88
127
|
* `Server.ts` depends only on this interface, never on a concrete router class, so the
|
|
@@ -90,13 +129,13 @@ export type ErrorHandler = (err: any, req: HttpRequest, res: HttpResponse, next:
|
|
|
90
129
|
*/
|
|
91
130
|
export interface IHttpRouter {
|
|
92
131
|
use(...handlers: RequestHandler[]): this;
|
|
93
|
-
get(path: string, ...handlers: RequestHandler
|
|
94
|
-
post(path: string, ...handlers: RequestHandler
|
|
95
|
-
put(path: string, ...handlers: RequestHandler
|
|
96
|
-
delete(path: string, ...handlers: RequestHandler
|
|
97
|
-
patch(path: string, ...handlers: RequestHandler
|
|
98
|
-
head(path: string, ...handlers: RequestHandler
|
|
99
|
-
options(path: string, ...handlers: RequestHandler
|
|
132
|
+
get(path: string, ...handlers: Array<RequestHandler | HttpRouteOptions>): this;
|
|
133
|
+
post(path: string, ...handlers: Array<RequestHandler | HttpRouteOptions>): this;
|
|
134
|
+
put(path: string, ...handlers: Array<RequestHandler | HttpRouteOptions>): this;
|
|
135
|
+
delete(path: string, ...handlers: Array<RequestHandler | HttpRouteOptions>): this;
|
|
136
|
+
patch(path: string, ...handlers: Array<RequestHandler | HttpRouteOptions>): this;
|
|
137
|
+
head(path: string, ...handlers: Array<RequestHandler | HttpRouteOptions>): this;
|
|
138
|
+
options(path: string, ...handlers: Array<RequestHandler | HttpRouteOptions>): this;
|
|
100
139
|
/** `true` if the application has registered its own literal `OPTIONS` route at `path` (as
|
|
101
140
|
* opposed to the framework's own internal `/*` CORS-preflight fallback registered in `listen()`).
|
|
102
141
|
* Consulted by `Server.ts`'s global CORS middleware so a real app-defined `OPTIONS` handler gets a
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { HttpRequest, HttpResponse } from "../types.js";
|
|
2
2
|
import type { HttpRequest as UWSHttpRequest, HttpResponse as UWSHttpResponse } from "uWebSockets.js";
|
|
3
|
+
import { Readable } from "stream";
|
|
3
4
|
/** Parses a `cookie` header string into a key/value map. */
|
|
4
5
|
export declare function parseCookies(cookieHeader: string): Record<string, string>;
|
|
5
6
|
/**
|
|
@@ -70,9 +71,23 @@ export declare class UWSResponse implements HttpResponse {
|
|
|
70
71
|
isHead: boolean;
|
|
71
72
|
/** Intermediate result passed between middleware. */
|
|
72
73
|
result?: any;
|
|
74
|
+
/**
|
|
75
|
+
* Set by the router via `attachBodyStream()` for a streaming-body route (see
|
|
76
|
+
* `HttpRouteOptions.streamingBody`). `end()` checks `isBodyStreamFullyReceived()` against it —
|
|
77
|
+
* see that function's doc comment and `end()` for the full rationale.
|
|
78
|
+
*/
|
|
79
|
+
private _bodyStream?;
|
|
73
80
|
/** Allow arbitrary per-response properties. */
|
|
74
81
|
[key: string]: any;
|
|
75
82
|
constructor(uwsRes: UWSHttpResponse);
|
|
83
|
+
/**
|
|
84
|
+
* Associates this response with a streaming-body route's `req.bodyStream`, so `end()` can detect
|
|
85
|
+
* a response finalizing before uWS has received the whole declared request body and force the
|
|
86
|
+
* connection closed instead of hanging it — see `isBodyStreamFullyReceived()`'s doc comment for
|
|
87
|
+
* the full rationale. Called once, by the router, right after `makeBodyStream()` creates the
|
|
88
|
+
* stream (`HttpRouteOptions.streamingBody` routes only — never called otherwise).
|
|
89
|
+
*/
|
|
90
|
+
attachBodyStream(stream: Readable): void;
|
|
76
91
|
get statusCode(): number;
|
|
77
92
|
get headersSent(): boolean;
|
|
78
93
|
get writableEnded(): boolean;
|
|
@@ -136,3 +151,76 @@ export declare const DEFAULT_MAX_BODY_SIZE: number;
|
|
|
136
151
|
export declare function readBody(uwsRes: UWSHttpResponse, req: UWSRequest, maxBodySize?: number, res?: {
|
|
137
152
|
onAbort: (callback: () => void) => void;
|
|
138
153
|
}): Promise<boolean>;
|
|
154
|
+
/**
|
|
155
|
+
* Internal buffer size (in bytes) at which `makeBodyStream()`'s stream applies backpressure — i.e.
|
|
156
|
+
* pauses pulling more bytes off the uWS connection until its consumer catches up. Chosen much larger
|
|
157
|
+
* than a Node `Readable`'s 16 KiB default: a streaming route exists specifically to move large
|
|
158
|
+
* payloads (e.g. a multi-GB mailbox import) efficiently, and throttling every 16 KiB would make
|
|
159
|
+
* pause()/resume() churn dominate. 1 MiB bounds worst-case extra memory per in-flight streaming
|
|
160
|
+
* request to a small, predictable amount while still giving a slow consumer (e.g. writing to disk)
|
|
161
|
+
* plenty of headroom before the network is throttled.
|
|
162
|
+
*/
|
|
163
|
+
export declare const STREAMING_BODY_HIGH_WATER_MARK: number;
|
|
164
|
+
/**
|
|
165
|
+
* Returns `true` once uWS has delivered the final chunk of `stream`'s request body — i.e. it is safe
|
|
166
|
+
* to let the underlying connection return to uWS's keep-alive pool — regardless of whether anything
|
|
167
|
+
* has actually consumed the stream. Returns `true` for `undefined` (nothing to wait for) so callers
|
|
168
|
+
* can pass `req.bodyStream` directly without an existence check first.
|
|
169
|
+
*
|
|
170
|
+
* See `UWSResponse.end()`: a streaming route's handler can legitimately respond (an auth failure, a
|
|
171
|
+
* validation error, anything) without ever reading `req.bodyStream`. If the client declared a large
|
|
172
|
+
* `Content-Length` and hasn't actually sent it all yet — or, in the malicious case, never intends to
|
|
173
|
+
* — a plain `uwsRes.end()` at that point leaves the keep-alive connection open indefinitely: uWS
|
|
174
|
+
* won't consider it clean for reuse until it has received every byte of the declared body, and nothing
|
|
175
|
+
* in that scenario ever makes that happen. `end()` uses this check to force the connection closed
|
|
176
|
+
* instead whenever it would otherwise leave that promise unfulfilled.
|
|
177
|
+
*/
|
|
178
|
+
export declare function isBodyStreamFullyReceived(stream: Readable | undefined): boolean;
|
|
179
|
+
/**
|
|
180
|
+
* Exposes a uWS request body as a Node `Readable` stream instead of buffering it into memory, for a
|
|
181
|
+
* route registered with `{ streamingBody: true }` (see `HttpRouteOptions`). Does NOT populate
|
|
182
|
+
* `req.body`/`req.rawBody` and does NOT enforce any `maxBodySize` limit — this is the entire point:
|
|
183
|
+
* unlike `readBody()`, the caller has opted out of buffering specifically so an arbitrarily large
|
|
184
|
+
* body (e.g. a 20 GB PST/mbox import) never needs to fit in memory at once. Enforcing a size limit,
|
|
185
|
+
* if wanted, is the route handler's own responsibility while consuming the stream.
|
|
186
|
+
*
|
|
187
|
+
* Backpressure is real, not simulated: a chunk that overflows the stream's internal buffer
|
|
188
|
+
* (`push()` returning `false`) calls `uwsRes.pause()` (uWS suspends delivering more `onData`
|
|
189
|
+
* chunks), and the returned `Readable`'s `_read()` calls `uwsRes.resume()` — but ONLY when this
|
|
190
|
+
* function itself previously paused it. uWS's `pause()`/`resume()` are not simple idempotent
|
|
191
|
+
* throttle toggles: empirically, calling `resume()` when the connection was never paused (e.g. from
|
|
192
|
+
* `_read()`'s very first call, before any backpressure has ever been applied) stops any further
|
|
193
|
+
* `onData` delivery for the rest of the request, hanging it forever. A local `paused` flag makes
|
|
194
|
+
* `resume()` a no-op unless a matching `pause()` was actually issued, matching the exact
|
|
195
|
+
* pause/resume pairing uWS expects. A slow consumer (a route piping to a slow disk, or one simply
|
|
196
|
+
* not reading yet) still throttles how fast bytes are pulled off the client connection rather than
|
|
197
|
+
* piling up unbounded data in process memory — the failure mode this whole feature exists to avoid.
|
|
198
|
+
* The same pairing discipline also applies to the final chunk: when a chunk that overflows the
|
|
199
|
+
* buffer also happens to be the last one (`isLast`), `pause()` is skipped entirely — the stream is
|
|
200
|
+
* about to signal EOF via `push(null)` regardless, so Node's `Readable` never calls `_read()` again
|
|
201
|
+
* to issue the matching `resume()`, which would otherwise leave uWS's connection paused forever with
|
|
202
|
+
* nothing left in this function to ever un-pause it.
|
|
203
|
+
*
|
|
204
|
+
* The stream is destroyed with an error if the client disconnects mid-upload (wired through the
|
|
205
|
+
* existing single `onAborted` fan-out via `res.onAbort()`, matching `readBody()`'s own abort
|
|
206
|
+
* handling) so a handler awaiting `for await (const chunk of req.bodyStream)` sees a thrown error
|
|
207
|
+
* instead of hanging forever, and can clean up (e.g. delete a partial temp file) in its own
|
|
208
|
+
* `catch`/`finally`. A route that never reads `req.bodyStream` at all can still hit this path — e.g.
|
|
209
|
+
* `UWSResponse.end()` itself calls `uwsRes.close()` (which fires `onAborted`) when finalizing a
|
|
210
|
+
* response before the body was fully received — so a permanent no-op `error` listener is attached
|
|
211
|
+
* below as well: a `Readable` with no active consumer still emits `error` on `destroy(err)`, and
|
|
212
|
+
* Node treats an `error` event with zero listeners as fatal (crashes the process). This listener
|
|
213
|
+
* doesn't swallow anything from a real consumer — `for await`/`.pipe()`/an explicit `.on("error")`
|
|
214
|
+
* all still see the same event; EventEmitter calls every registered listener, not just the first.
|
|
215
|
+
*
|
|
216
|
+
* Must be called synchronously, before any `await`, in the same tick as request handling begins —
|
|
217
|
+
* uWS requires `onData`/`onAborted` to be registered before any asynchronous operation, matching the
|
|
218
|
+
* exact constraint `readBody()` above is already subject to.
|
|
219
|
+
*
|
|
220
|
+
* @param uwsRes The uWS HttpResponse to read the body from.
|
|
221
|
+
* @param res Used only to register the abort callback through the framework's existing single
|
|
222
|
+
* `onAborted` slot (see `UWSResponse.onAbort()`) — never written to otherwise.
|
|
223
|
+
*/
|
|
224
|
+
export declare function makeBodyStream(uwsRes: UWSHttpResponse, res: {
|
|
225
|
+
onAbort: (callback: () => void) => void;
|
|
226
|
+
}): Readable;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import uWS from "uWebSockets.js";
|
|
2
|
-
import { type IHttpRouter, type RequestHandler, type WebSocketOptions } from "../types.js";
|
|
2
|
+
import { type HttpRouteOptions, type IHttpRouter, type RequestHandler, type WebSocketOptions } from "../types.js";
|
|
3
3
|
import { type WsUpgradeAuth } from "../MiddlewareChain.js";
|
|
4
4
|
export { runChain, extractParamNames } from "../MiddlewareChain.js";
|
|
5
5
|
export type { WsUpgradeAuth, WsUpgradeAuthResult } from "../MiddlewareChain.js";
|
|
@@ -55,13 +55,13 @@ export declare class HttpRouter implements IHttpRouter {
|
|
|
55
55
|
private capturePreRouteCount;
|
|
56
56
|
/** Builds the uWS handler for a route registered at `routePattern` (`undefined` for the router's own fallbacks). */
|
|
57
57
|
private makeHandler;
|
|
58
|
-
get(routePath: string, ...
|
|
59
|
-
post(routePath: string, ...
|
|
60
|
-
put(routePath: string, ...
|
|
61
|
-
delete(routePath: string, ...
|
|
62
|
-
patch(routePath: string, ...
|
|
63
|
-
head(routePath: string, ...
|
|
64
|
-
options(routePath: string, ...
|
|
58
|
+
get(routePath: string, ...handlersOrOptions: Array<RequestHandler | HttpRouteOptions>): this;
|
|
59
|
+
post(routePath: string, ...handlersOrOptions: Array<RequestHandler | HttpRouteOptions>): this;
|
|
60
|
+
put(routePath: string, ...handlersOrOptions: Array<RequestHandler | HttpRouteOptions>): this;
|
|
61
|
+
delete(routePath: string, ...handlersOrOptions: Array<RequestHandler | HttpRouteOptions>): this;
|
|
62
|
+
patch(routePath: string, ...handlersOrOptions: Array<RequestHandler | HttpRouteOptions>): this;
|
|
63
|
+
head(routePath: string, ...handlersOrOptions: Array<RequestHandler | HttpRouteOptions>): this;
|
|
64
|
+
options(routePath: string, ...handlersOrOptions: Array<RequestHandler | HttpRouteOptions>): this;
|
|
65
65
|
/** Returns `true` if the application has registered its own literal `OPTIONS` route at `path`
|
|
66
66
|
* (not the framework's own `/*` CORS-preflight fallback). See `explicitOptionsPaths`'s own doc
|
|
67
67
|
* comment. `path` is normalized the same way registered routes are, so a trailing-slash mismatch
|
|
@@ -67,6 +67,8 @@ export declare class ModelUtils {
|
|
|
67
67
|
private static idPropertyCache;
|
|
68
68
|
private static readOnlyPropertyCache;
|
|
69
69
|
private static columnTypeCache;
|
|
70
|
+
/** Caches each class's `@RequiresScope`-decorated property names to their required scopes (see `assertFieldScope()`). */
|
|
71
|
+
private static scopedPropertyCache;
|
|
70
72
|
/** Sequence used to give every `Raw()` SQL expression's named parameter a unique name within one query. */
|
|
71
73
|
private static rawParamSeq;
|
|
72
74
|
/**
|
|
@@ -120,6 +122,43 @@ export declare class ModelUtils {
|
|
|
120
122
|
* @returns The list of all property names that have the @ReadOnly decorator applied.
|
|
121
123
|
*/
|
|
122
124
|
static getReadOnlyPropertyNames(modelClass: any): string[];
|
|
125
|
+
/**
|
|
126
|
+
* Returns a map of every `@RequiresScope`-decorated property on `modelClass` to the scope(s) it requires (the
|
|
127
|
+
* exact same `rrst:scopes` metadata `@rapidrest/core`'s `ObjectUtils.deleteScopedProps()` reads to redact a
|
|
128
|
+
* property from an already-fetched result). `undefined` when `modelClass` isn't provided.
|
|
129
|
+
*
|
|
130
|
+
* Deliberately does NOT use `getReadOnlyPropertyNames()`'s prototype-walking-only pattern: a plain class field
|
|
131
|
+
* initializer (`public salary: number = 0`) compiles to an own-INSTANCE assignment, so it never appears on the
|
|
132
|
+
* prototype at all unless its OWN decorator specifically forces a placeholder there (this framework's
|
|
133
|
+
* `@ReadOnly`/`@Identifier` do; `@rapidrest/core`'s `RequiresScope` has no reason to and doesn't). Confirmed
|
|
134
|
+
* empirically: `Object.getOwnPropertyNames(SomeClass.prototype)` for a bare `@RequiresScope("x") salary = 0`
|
|
135
|
+
* field returns only `["constructor"]`. Instead this walks the property names of an actual constructed
|
|
136
|
+
* instance (mirroring `deleteScopedProps()`'s own `Object.getOwnPropertyNames(obj)` over a data object) union
|
|
137
|
+
* the prototype chain (still needed to also catch a property that IS forced onto the prototype, e.g. one
|
|
138
|
+
* additionally decorated with `@ReadOnly`) — metadata itself is always read off `modelClass.prototype`
|
|
139
|
+
* (`Reflect.getMetadata` walks the prototype chain on its own), regardless of which own-properties surfaced
|
|
140
|
+
* the candidate name.
|
|
141
|
+
*/
|
|
142
|
+
private static getScopedPropertyNames;
|
|
143
|
+
/**
|
|
144
|
+
* Rejects (400) a search filter, sort or count referencing a `@RequiresScope`-protected property when the
|
|
145
|
+
* requesting user doesn't hold at least one of its required scopes.
|
|
146
|
+
*
|
|
147
|
+
* Without this, `@RequiresScope` was only ever a RESPONSE-time redaction (`ObjectUtils.deleteScopedProps()`,
|
|
148
|
+
* applied after a query already ran) — a scope-restricted field could still drive `WHERE`/`ORDER BY`/count
|
|
149
|
+
* itself, letting a caller who could never READ the field's value still binary-search it out via repeated
|
|
150
|
+
* `gt()`/`lt()`/`range()` filters, turn a `HEAD` request's `Content-Length` into an existence/equality oracle
|
|
151
|
+
* via `count()`, or read its relative ordering via `sort`. Checked at query-BUILD time instead, so a
|
|
152
|
+
* disallowed field never reaches `WHERE`/`ORDER BY`/count in the first place — matches `hasScopes()`'s exact
|
|
153
|
+
* semantics (same function `deleteScopedProps()` uses), so query-time rejection and response-time redaction
|
|
154
|
+
* never disagree about who has access to a given field.
|
|
155
|
+
*
|
|
156
|
+
* Applied uniformly to every field referenced by a filter (regardless of operator — `eq`/`gt`/`like`/`regex`/
|
|
157
|
+
* `exists`/... all pass through this same per-key check) and to every `sort` key, on both SQL and Mongo.
|
|
158
|
+
*
|
|
159
|
+
* @throws {ApiError} 400 `SEARCH_SCOPED_FIELD` if `property` requires a scope `user` doesn't hold.
|
|
160
|
+
*/
|
|
161
|
+
private static assertFieldScope;
|
|
123
162
|
/**
|
|
124
163
|
* Resolves the declared type of a model property, from an explicit `type` override on `@Column` or (falling
|
|
125
164
|
* back) the TypeScript design-time type reflected at decoration time. Returns `undefined` when `modelClass`
|
|
@@ -71,6 +71,26 @@ export declare class RouteUtils {
|
|
|
71
71
|
* @param requiredScopes The list of scopes of which the authenticated user's token must carry at least one.
|
|
72
72
|
*/
|
|
73
73
|
checkRequiredScopes(requiredScopes: string[]): RequestHandler;
|
|
74
|
+
/**
|
|
75
|
+
* Creates a WebSocket-specific error-sanitizing middleware, appended as the last item in every
|
|
76
|
+
* `@WebSocket()` route's handler chain (see `registerRoute()`'s WS branch below). Mirrors what
|
|
77
|
+
* `Server.ts`'s `handleError()`/`serializeError()` already do for ordinary HTTP routes: an
|
|
78
|
+
* `ApiError`'s own status/code/message pass through unchanged (it's meant to reach the client),
|
|
79
|
+
* but anything else — a raw DB driver error, an unexpected throw from `RateLimiter`,
|
|
80
|
+
* `ACLUtils.findACL()`, or any other non-`ApiError` thrown by a middleware in the chain — is
|
|
81
|
+
* replaced with a generic internal error before `runChain()`'s own end-of-chain fallback can turn
|
|
82
|
+
* it into a response.
|
|
83
|
+
*
|
|
84
|
+
* This matters specifically for WebSocket routes because `runChain()`'s fallback response, for a
|
|
85
|
+
* WS route, ends up as the literal WebSocket close reason (`ws.close(1002, message)` — see
|
|
86
|
+
* `Router.ts`/`BunRouter.ts`'s `ws()` `open` handlers), visible to any client via the browser
|
|
87
|
+
* close event's `event.reason`. An ordinary HTTP route gets the same sanitization for free
|
|
88
|
+
* because `Server.ts` registers `handleError` as global middleware, which `registerRoute()`
|
|
89
|
+
* concatenates onto every HTTP route's own handler chain — but `app.ws()` is never given
|
|
90
|
+
* `globalMiddleware` (only the WS-specific chain built here), so WS routes need their own copy of
|
|
91
|
+
* the same safety net.
|
|
92
|
+
*/
|
|
93
|
+
sanitizeWsError(): RequestHandler;
|
|
74
94
|
/**
|
|
75
95
|
* Converts the given array of string or Function objects to functions bound to the given route object.
|
|
76
96
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rapidrest/service-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "Provides all core functionality for RapidREST based backend services.",
|
|
5
5
|
"repository": "https://github.com/rapidrest/service-core.git",
|
|
6
6
|
"author": "RapidREST <rapidrests@gmail.com>",
|