spooder 6.1.1 → 6.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/bun.lock +1 -1
- package/package.json +1 -1
- package/src/api.ts +22 -0
package/README.md
CHANGED
|
@@ -603,6 +603,7 @@ server.stop(immediate: boolean): Promise<void>;
|
|
|
603
603
|
// routing
|
|
604
604
|
server.route(path: string, handler: RequestHandler, method?: HTTP_METHODS);
|
|
605
605
|
server.json(path: string, handler: JSONRequestHandler, method?: HTTP_METHODS);
|
|
606
|
+
server.throttle(delta: number, handler: JSONRequestHandler|RequestHandler);
|
|
606
607
|
server.unroute(path: string);
|
|
607
608
|
|
|
608
609
|
// fallback handlers
|
|
@@ -892,6 +893,24 @@ server.route('/test/route', () => {});
|
|
|
892
893
|
server.unroute('/test/route');
|
|
893
894
|
```
|
|
894
895
|
|
|
896
|
+
### 🔧 `server.throttle(delta: number, handler: JSONRequestHandler|RequestHandler)`
|
|
897
|
+
|
|
898
|
+
Throttles requests going through the provided handler so that they take a **minimum** of `delta` milliseconds. Useful for preventing brute-force of sensitive endpoints.
|
|
899
|
+
|
|
900
|
+
> [!IMPORTANT]
|
|
901
|
+
> This is a rudimentary countermeasure for brute-force attacks, **not** a defence against timing-attacks. Always use constant-time/timing-safe comparison functions in sensitive endpoints.
|
|
902
|
+
|
|
903
|
+
```ts
|
|
904
|
+
server.json('/api/login', server.throttle(1000, (req, url, json) => {
|
|
905
|
+
// this endpoint will always take at least 1000ms to execute
|
|
906
|
+
}));
|
|
907
|
+
|
|
908
|
+
// works with regular routes
|
|
909
|
+
server.route('/reset-password', server.throttle(1000, (req, url) => {
|
|
910
|
+
// this route will also take at least 1000ms to execute
|
|
911
|
+
}));
|
|
912
|
+
```
|
|
913
|
+
|
|
895
914
|
### 🔧 `server.json(path: string, handler: JSONRequestHandler, method?: HTTP_METHODS)`
|
|
896
915
|
|
|
897
916
|
Register a JSON endpoint with automatic content validation. This method automatically validates that the request has the correct `Content-Type: application/json` header and that the request body contains a valid JSON object.
|
package/bun.lock
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"packages": {
|
|
12
12
|
"@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],
|
|
13
13
|
|
|
14
|
-
"@types/node": ["@types/node@24.9.
|
|
14
|
+
"@types/node": ["@types/node@24.9.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA=="],
|
|
15
15
|
|
|
16
16
|
"@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
|
|
17
17
|
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -1782,6 +1782,11 @@ export function http_serve(port: number, hostname?: string) {
|
|
|
1782
1782
|
|
|
1783
1783
|
log_spooder(`server started on port {${port}} (host: {${hostname ?? 'unspecified'}})`);
|
|
1784
1784
|
|
|
1785
|
+
type ThrottleHandler = {
|
|
1786
|
+
(delta: number, handler: JSONRequestHandler): JSONRequestHandler;
|
|
1787
|
+
(delta: number, handler: RequestHandler): RequestHandler;
|
|
1788
|
+
};
|
|
1789
|
+
|
|
1785
1790
|
return {
|
|
1786
1791
|
/** Register a handler for a specific route. */
|
|
1787
1792
|
route: (path: string, handler: RequestHandler, method: HTTP_METHODS = 'GET'): void => {
|
|
@@ -1789,6 +1794,23 @@ export function http_serve(port: number, hostname?: string) {
|
|
|
1789
1794
|
path = path.slice(0, -1);
|
|
1790
1795
|
routes.push([path.split('/'), handler, method]);
|
|
1791
1796
|
},
|
|
1797
|
+
|
|
1798
|
+
/** Throttles an endpoint to take at least the specified delta time (in ms) */
|
|
1799
|
+
throttle: ((delta: number, handler: JSONRequestHandler | RequestHandler): any => {
|
|
1800
|
+
return async (req: Request, ...args: any[]) => {
|
|
1801
|
+
const t_start = Date.now();
|
|
1802
|
+
const result = await (handler as any)(req, ...args);
|
|
1803
|
+
|
|
1804
|
+
const t_elapsed = Date.now() - t_start;
|
|
1805
|
+
const t_remaining = Math.max(0, delta - t_elapsed);
|
|
1806
|
+
|
|
1807
|
+
if (t_remaining > 0)
|
|
1808
|
+
await Bun.sleep(t_remaining);
|
|
1809
|
+
|
|
1810
|
+
slow_requests.add(req);
|
|
1811
|
+
return result;
|
|
1812
|
+
};
|
|
1813
|
+
}) as ThrottleHandler,
|
|
1792
1814
|
|
|
1793
1815
|
/** Register a JSON endpoint with automatic content validation. */
|
|
1794
1816
|
json: (path: string, handler: JSONRequestHandler, method: HTTP_METHODS = 'POST'): void => {
|