spooder 6.1.95 → 6.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.
Files changed (3) hide show
  1. package/bun.lock +1 -1
  2. package/package.json +1 -1
  3. package/src/api.ts +20 -4
package/bun.lock CHANGED
@@ -12,7 +12,7 @@
12
12
  "packages": {
13
13
  "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="],
14
14
 
15
- "@types/node": ["@types/node@25.0.6", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-NNu0sjyNxpoiW3YuVFfNz7mxSQ+S4X2G28uqg2s+CzoqoQjLPsWSbsFFyztIAqt2vb8kfEAsJNepMGPTxFDx3Q=="],
15
+ "@types/node": ["@types/node@25.0.7", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-C/er7DlIZgRJO7WtTdYovjIFzGsz0I95UlMyR9anTb4aCpBSRWe5Jc1/RvLKUfzmOxHPGjSE5+63HgLtndxU4w=="],
16
16
 
17
17
  "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
18
18
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "spooder",
3
3
  "author": "Kruithne <kruithne@gmail.com>",
4
4
  "type": "module",
5
- "version": "6.1.95",
5
+ "version": "6.2.1",
6
6
  "module": "./src/api.ts",
7
7
  "repository": {
8
8
  "url": "https://github.com/Kruithne/spooder"
package/src/api.ts CHANGED
@@ -1332,6 +1332,8 @@ export const HTTP_STATUS_CODE = {
1332
1332
  type HTTP_METHOD = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
1333
1333
  type HTTP_METHODS = HTTP_METHOD|HTTP_METHOD[];
1334
1334
 
1335
+ type BodylessMethod = 'GET' | 'HEAD';
1336
+
1335
1337
  export function http_apply_range(file: BunFile, request: Request): BunFile {
1336
1338
  const range_header = request.headers.get('range');
1337
1339
  if (range_header !== null) {
@@ -1384,7 +1386,7 @@ type ErrorHandler = (err: Error, req: Request, url: URL) => Resolvable<Response>
1384
1386
  type DefaultHandler = (req: Request, status_code: number) => HandlerReturnType;
1385
1387
  type StatusCodeHandler = (req: Request) => HandlerReturnType;
1386
1388
 
1387
- type JSONRequestHandler = (req: Request, url: URL, json: JsonObject) => HandlerReturnType;
1389
+ type JSONRequestHandler<T extends JsonObject | null = JsonObject> = (req: Request, url: URL, json: T) => HandlerReturnType;
1388
1390
 
1389
1391
  export type ServerSentEventClient = {
1390
1392
  message: (message: string) => void;
@@ -1823,10 +1825,20 @@ export function http_serve(port: number, hostname?: string) {
1823
1825
  log_spooder(`server started on port {${server.port}} (host: {${hostname ?? 'unspecified'}})`);
1824
1826
 
1825
1827
  type ThrottleHandler = {
1826
- (delta: number, handler: JSONRequestHandler): JSONRequestHandler;
1828
+ (delta: number, handler: JSONRequestHandler<JsonObject>): JSONRequestHandler<JsonObject>;
1829
+ (delta: number, handler: JSONRequestHandler<null>): JSONRequestHandler<null>;
1827
1830
  (delta: number, handler: RequestHandler): RequestHandler;
1828
1831
  };
1829
1832
 
1833
+ type JsonEndpointHandler = {
1834
+ <M extends BodylessMethod>(path: string, handler: JSONRequestHandler<null>, method: M): void;
1835
+ <M extends BodylessMethod[]>(path: string, handler: JSONRequestHandler<null>, method: M): void;
1836
+ <M extends Exclude<HTTP_METHOD, BodylessMethod>>(path: string, handler: JSONRequestHandler<JsonObject>, method: M): void;
1837
+ <M extends Exclude<HTTP_METHOD, BodylessMethod>[]>(path: string, handler: JSONRequestHandler<JsonObject>, method: M): void;
1838
+ <M extends HTTP_METHOD[]>(path: string, handler: JSONRequestHandler<JsonObject | null>, method: M): void;
1839
+ (path: string, handler: JSONRequestHandler<JsonObject>): void;
1840
+ };
1841
+
1830
1842
  return {
1831
1843
  /** Register a handler for a specific route. */
1832
1844
  route: (path: string, handler: RequestHandler, method: HTTP_METHODS = 'GET'): void => {
@@ -1853,7 +1865,7 @@ export function http_serve(port: number, hostname?: string) {
1853
1865
  }) as ThrottleHandler,
1854
1866
 
1855
1867
  /** Register a JSON endpoint with automatic content validation. */
1856
- json: (path: string, handler: JSONRequestHandler, method: HTTP_METHODS = 'POST'): void => {
1868
+ json: ((path: string, handler: JSONRequestHandler<JsonObject | null>, method: HTTP_METHODS = 'POST'): void => {
1857
1869
  const json_wrapper: RequestHandler = async (req: Request, url: URL) => {
1858
1870
  // handle CORS preflight
1859
1871
  if (req.method === 'OPTIONS') {
@@ -1868,6 +1880,10 @@ export function http_serve(port: number, hostname?: string) {
1868
1880
  }
1869
1881
 
1870
1882
  try {
1883
+ // GET/HEAD requests don't have bodies, skip validation
1884
+ if (req.method === 'GET' || req.method === 'HEAD')
1885
+ return handler(req, url, null);
1886
+
1871
1887
  if (req.headers.get('Content-Type') !== 'application/json')
1872
1888
  return 400; // Bad Request
1873
1889
 
@@ -1886,7 +1902,7 @@ export function http_serve(port: number, hostname?: string) {
1886
1902
 
1887
1903
  const methods: HTTP_METHODS = Array.isArray(method) ? [...method, 'OPTIONS'] : [method, 'OPTIONS'];
1888
1904
  routes.push([path.split('/'), json_wrapper, methods]);
1889
- },
1905
+ }) as JsonEndpointHandler,
1890
1906
 
1891
1907
  /** Unregister a specific route */
1892
1908
  unroute: (path: string): void => {