@stacksjs/bun-router 0.0.8 → 0.0.10

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.
@@ -39,7 +39,7 @@ export interface ResponseCookieOptions {
39
39
  sameSite?: 'strict' | 'lax' | 'none';
40
40
  }
41
41
  export interface ResponseFactory {
42
- json: <T>(data: T, options?: JsonResponseOptions) => Response;
42
+ json: <T>(data: T, options?: JsonResponseOptions | ResponseStatus) => Response;
43
43
  noContent: (headers?: Record<string, string>) => Response;
44
44
  download: (filePath: string, filename?: string, headers?: Record<string, string>) => Promise<Response>;
45
45
  file: (filePath: string, headers?: Record<string, string>) => Promise<Response>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/bun-router",
3
3
  "type": "module",
4
- "version": "0.0.8",
4
+ "version": "0.0.10",
5
5
  "description": "A fast, type-safe router for Bun.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",
@@ -423,6 +423,10 @@ export type MiddlewareWithConfig<T> = (config: T) => MiddlewareFunction
423
423
  const responseType = options.responseType || 'Response'
424
424
  const paramsType = options.paramsType || 'Record<string, string>'
425
425
 
426
+ // pickier mis-parses the embedded `req:` token in this template literal
427
+ // as a real unused function parameter — disable on the next line so a
428
+ // pre-existing rule false positive doesn't block CI.
429
+ // eslint-disable-next-line pickier/no-unused-vars
426
430
  return `(req: ${requestType} & { params: ${paramsType} }) => Promise<${responseType}>`
427
431
  }
428
432
 
@@ -53,7 +53,7 @@ export interface ResponseCookieOptions {
53
53
  // ============================================================================
54
54
 
55
55
  export interface ResponseFactory {
56
- json: <T>(data: T, options?: JsonResponseOptions) => Response
56
+ json: <T>(data: T, options?: JsonResponseOptions | ResponseStatus) => Response
57
57
  noContent: (headers?: Record<string, string>) => Response
58
58
  download: (filePath: string, filename?: string, headers?: Record<string, string>) => Promise<Response>
59
59
  file: (filePath: string, headers?: Record<string, string>) => Promise<Response>
@@ -89,10 +89,18 @@ export interface ResponseFactory {
89
89
  */
90
90
  export const response: ResponseFactory = {
91
91
  /**
92
- * Create a JSON response with proper typing
93
- */
94
- json: <T>(data: T, options: JsonResponseOptions = {}): Response => {
95
- const { status = 200, headers = {}, pretty = false } = options
92
+ * Create a JSON response with proper typing.
93
+ *
94
+ * The second argument may be either a status code (Hono/Express style) or
95
+ * a `JsonResponseOptions` bag. The number form exists because passing a
96
+ * status as the second arg is the most common pattern in other web
97
+ * frameworks, and the silent `response.json(data, 401)` → HTTP 200 trap
98
+ * (the number was being interpreted as `options` and discarded) caused
99
+ * real bugs.
100
+ */
101
+ json: <T>(data: T, options: JsonResponseOptions | ResponseStatus = {}): Response => {
102
+ const opts: JsonResponseOptions = typeof options === 'number' ? { status: options } : options
103
+ const { status = 200, headers = {}, pretty = false } = opts
96
104
  const body = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)
97
105
 
98
106
  return new Response(body, {
@@ -1,6 +1,7 @@
1
1
  import type { Server } from 'bun'
2
2
  import type { EnhancedRequest, HTTPMethod, ServerOptions } from '../types'
3
3
  import type { Router } from './router'
4
+ import { RequestWithMacros } from '../request/macros'
4
5
  import { runWithRequest, setCurrentRequest } from '../request/context'
5
6
 
6
7
  /**
@@ -333,7 +334,39 @@ export function registerServerHandling(RouterClass: typeof Router): void {
333
334
  _cookiesToDelete: [],
334
335
  }) as unknown as EnhancedRequest
335
336
 
336
- // Add cookie methods to the request
337
+ // Attach registered request macros (input, has, bearerToken, etc.) so
338
+ // they're available in middleware and handlers without manual setup.
339
+ // Applied *before* the cookie utility so the cookies macro doesn't
340
+ // overwrite the get/set/delete object form consumers depend on.
341
+ RequestWithMacros.applyMacros(enhancedReq)
342
+
343
+ // Attach the helpers consumers reach for that aren't shaped as
344
+ // generic input macros — `getParam`, the function-form `cookie`,
345
+ // the parsed-query `get` shorthand. These mirror the methods the
346
+ // bare `Router.enhanceRequest` (router.ts) attaches; without them
347
+ // here, requests that flow through the registered server.ts
348
+ // override are missing exactly the helpers that frameworks
349
+ // layered on bun-router (Stacks, etc.) treat as guaranteed.
350
+ ;(enhancedReq as any).getParam = <T = string>(name: string, defaultValue?: T): T | undefined => {
351
+ const value = params?.[name] as T | undefined
352
+ return value !== undefined ? value : defaultValue
353
+ }
354
+ ;(enhancedReq as any).cookie = (name: string, defaultValue?: string): string | null => {
355
+ const value = getCookies()[name]
356
+ return value !== undefined ? value : (defaultValue ?? null)
357
+ }
358
+ // `get(key)` — read query params (and only query params; the full
359
+ // input merge with body etc. is provided by the `input` macro).
360
+ // Skip if a macro already registered it under the same name.
361
+ if (typeof (enhancedReq as any).get !== 'function') {
362
+ ;(enhancedReq as any).get = <T = unknown>(key: string, defaultValue?: T): T | undefined => {
363
+ const url = new URL(req.url)
364
+ const value = url.searchParams.get(key) as T | null
365
+ return value !== null && value !== undefined ? value : defaultValue
366
+ }
367
+ }
368
+
369
+ // Add cookie methods to the request (overrides any macro named `cookies`).
337
370
  Object.assign(enhancedReq, { cookies: { ...getCookies(), ...cookies } })
338
371
 
339
372
  return enhancedReq