shokupan 0.6.0 → 0.7.0

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 (61) hide show
  1. package/README.md +4 -2
  2. package/dist/{openapi-analyzer-Bei1sVWp.cjs → analyzer-Bei1sVWp.cjs} +1 -1
  3. package/dist/analyzer-Bei1sVWp.cjs.map +1 -0
  4. package/dist/{openapi-analyzer-Ce_7JxZh.js → analyzer-Ce_7JxZh.js} +1 -1
  5. package/dist/analyzer-Ce_7JxZh.js.map +1 -0
  6. package/dist/cli.cjs +2 -2
  7. package/dist/cli.cjs.map +1 -1
  8. package/dist/cli.js +1 -1
  9. package/dist/cli.js.map +1 -1
  10. package/dist/context.d.ts +72 -11
  11. package/dist/{server-adapter-0xH174zz.js → http-server-0xH174zz.js} +1 -1
  12. package/dist/http-server-0xH174zz.js.map +1 -0
  13. package/dist/{server-adapter-DFhwlK8e.cjs → http-server-DFhwlK8e.cjs} +1 -1
  14. package/dist/http-server-DFhwlK8e.cjs.map +1 -0
  15. package/dist/index.cjs +1022 -801
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.ts +17 -17
  18. package/dist/index.js +1022 -800
  19. package/dist/index.js.map +1 -1
  20. package/dist/middleware.d.ts +1 -1
  21. package/dist/plugins/{auth.d.ts → application/auth.d.ts} +72 -3
  22. package/dist/plugins/application/cluster.d.ts +33 -0
  23. package/dist/plugins/{failed-request-recorder.d.ts → application/dashboard/failed-request-recorder.d.ts} +1 -1
  24. package/dist/plugins/{debugview → application/dashboard}/plugin.d.ts +13 -6
  25. package/dist/plugins/{server-adapter.d.ts → application/http-server.d.ts} +1 -1
  26. package/dist/plugins/{idempotency → application/idempotency}/plugin.d.ts +7 -1
  27. package/dist/plugins/{openapi.d.ts → application/openapi/openapi.d.ts} +2 -2
  28. package/dist/plugins/application/scalar.d.ts +36 -0
  29. package/dist/plugins/middleware/compression.d.ts +17 -0
  30. package/dist/plugins/middleware/cors.d.ts +34 -0
  31. package/dist/plugins/{express.d.ts → middleware/express.d.ts} +1 -1
  32. package/dist/plugins/{openapi-validator.d.ts → middleware/openapi-validator.d.ts} +2 -2
  33. package/dist/plugins/middleware/proxy.d.ts +37 -0
  34. package/dist/plugins/middleware/rate-limit.d.ts +58 -0
  35. package/dist/plugins/{security-headers.d.ts → middleware/security-headers.d.ts} +51 -1
  36. package/dist/plugins/{serve-static.d.ts → middleware/serve-static.d.ts} +1 -1
  37. package/dist/plugins/{session.d.ts → middleware/session.d.ts} +89 -3
  38. package/dist/plugins/{validation.d.ts → middleware/validation.d.ts} +6 -1
  39. package/dist/router.d.ts +99 -40
  40. package/dist/shokupan.d.ts +74 -4
  41. package/dist/util/async-hooks.d.ts +8 -2
  42. package/dist/{decorators.d.ts → util/decorators.d.ts} +1 -1
  43. package/dist/util/http-status.d.ts +2 -0
  44. package/dist/util/instrumentation.d.ts +1 -1
  45. package/dist/{router → util}/trie.d.ts +1 -1
  46. package/dist/{types.d.ts → util/types.d.ts} +41 -2
  47. package/package.json +5 -5
  48. package/dist/openapi-analyzer-Bei1sVWp.cjs.map +0 -1
  49. package/dist/openapi-analyzer-Ce_7JxZh.js.map +0 -1
  50. package/dist/plugins/compression.d.ts +0 -5
  51. package/dist/plugins/cors.d.ts +0 -11
  52. package/dist/plugins/proxy.d.ts +0 -9
  53. package/dist/plugins/rate-limit.d.ts +0 -14
  54. package/dist/plugins/scalar.d.ts +0 -15
  55. package/dist/server-adapter-0xH174zz.js.map +0 -1
  56. package/dist/server-adapter-DFhwlK8e.cjs.map +0 -1
  57. /package/dist/{analysis/openapi-analyzer.d.ts → plugins/application/openapi/analyzer.d.ts} +0 -0
  58. /package/dist/{di.d.ts → util/di.d.ts} +0 -0
  59. /package/dist/{request.d.ts → util/request.d.ts} +0 -0
  60. /package/dist/{response.d.ts → util/response.d.ts} +0 -0
  61. /package/dist/{symbol.d.ts → util/symbol.d.ts} +0 -0
@@ -1,4 +1,4 @@
1
- import { Middleware } from '../types';
1
+ import { Middleware } from '../../util/types';
2
2
  export interface ValidationConfig {
3
3
  body?: any;
4
4
  query?: any;
@@ -15,4 +15,9 @@ export declare const valibot: (schema: any, parser: Function) => {
15
15
  schema: any;
16
16
  parser: Function;
17
17
  };
18
+ /**
19
+ * Validation middleware.
20
+ * @param config Validation configuration
21
+ * @returns Middleware function
22
+ */
18
23
  export declare function validate(config: ValidationConfig): Middleware;
package/dist/router.d.ts CHANGED
@@ -1,10 +1,68 @@
1
1
  import { ShokupanContext } from './context';
2
2
  import { Shokupan } from './shokupan';
3
- import { $appRoot, $childControllers, $childRouters, $isApplication, $isMounted, $isRouter, $mountPath, $parent, $routes } from './symbol';
4
- import { GuardAPISpec, JSXRenderer, Method, MethodAPISpec, Middleware, OpenAPIOptions, ProcessResult, RequestOptions, RouteMetadata, ShokupanController, ShokupanHandler, ShokupanHooks, ShokupanRoute, ShokupanRouteConfig, StaticServeOptions } from './types';
5
- type HeadersInit = Headers | Record<string, string> | [string, string][];
3
+ import { $appRoot, $childControllers, $childRouters, $isApplication, $isMounted, $isRouter, $mountPath, $parent, $routes } from './util/symbol';
4
+ import { GuardAPISpec, HeadersInit, JSXRenderer, Method, MethodAPISpec, Middleware, OpenAPIOptions, ProcessResult, RequestOptions, RouteMetadata, RouteParams, ShokupanController, ShokupanHandler, ShokupanHooks, ShokupanRoute, ShokupanRouteConfig, StaticServeOptions } from './util/types';
6
5
  export declare const RouterRegistry: Map<string, ShokupanRouter<any>>;
7
6
  export declare const ShokupanApplicationTree: {};
7
+ /**
8
+ * Shokupan Router
9
+ *
10
+ * A router for organizing and grouping routes with shared middleware and configuration.
11
+ *
12
+ * @template State - The shape of `ctx.state` for all routes in this router.
13
+ * Provides type safety for state management within the router's middleware and handlers.
14
+ *
15
+ * @example Basic Router
16
+ * ```typescript
17
+ * const router = new ShokupanRouter();
18
+ * router.get('/users', (ctx) => ctx.json({ users: [] }));
19
+ *
20
+ * app.mount('/api', router);
21
+ * // Routes: GET /api/users
22
+ * ```
23
+ *
24
+ * @example Typed State Router
25
+ * ```typescript
26
+ * interface AuthState {
27
+ * userId: string;
28
+ * isAuthenticated: boolean;
29
+ * }
30
+ *
31
+ * class AuthRouter extends ShokupanRouter<AuthState> {
32
+ * constructor() {
33
+ * super();
34
+ *
35
+ * // Router middleware has typed state
36
+ * this.use((ctx, next) => {
37
+ * ctx.state.userId = 'user-123';
38
+ * ctx.state.isAuthenticated = true;
39
+ * return next();
40
+ * });
41
+ *
42
+ * this.get('/me', (ctx) => {
43
+ * // State is fully typed
44
+ * return ctx.json({ userId: ctx.state.userId });
45
+ * });
46
+ * }
47
+ * }
48
+ *
49
+ * app.mount('/auth', new AuthRouter());
50
+ * ```
51
+ *
52
+ * @example Router with Middleware
53
+ * ```typescript
54
+ * const apiRouter = new ShokupanRouter();
55
+ *
56
+ * // Router-level middleware
57
+ * apiRouter.use(async (ctx, next) => {
58
+ * console.log(`API request: ${ctx.method} ${ctx.path}`);
59
+ * return next();
60
+ * });
61
+ *
62
+ * apiRouter.get('/status', (ctx) => ctx.json({ status: 'ok' }));
63
+ * app.mount('/api', apiRouter);
64
+ * ```
65
+ */
8
66
  export declare class ShokupanRouter<T extends Record<string, any> = Record<string, any>> {
9
67
  readonly config?: ShokupanRouteConfig;
10
68
  private [$isApplication];
@@ -34,7 +92,7 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
34
92
  enableMiddlewareTracking?: boolean;
35
93
  middlewareTrackingMaxCapacity?: number;
36
94
  middlewareTrackingTTL?: number;
37
- httpLogger?: (ctx: ShokupanContext<Record<string, any>>) => void;
95
+ httpLogger?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void;
38
96
  logger?: {
39
97
  verbose?: boolean;
40
98
  info?: (msg: string, props: Record<string, any>) => void;
@@ -47,29 +105,29 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
47
105
  requestTimeout?: number;
48
106
  writeTimeout?: number;
49
107
  renderer?: JSXRenderer;
50
- serverFactory?: import('./types').ServerFactory;
108
+ serverFactory?: import('.').ServerFactory;
51
109
  hooks?: {
52
- onError?: (ctx: ShokupanContext<Record<string, any>>, error: unknown) => void | Promise<void>;
53
- onRequestStart?: (ctx: ShokupanContext<Record<string, any>>) => void | Promise<void>;
54
- onRequestEnd?: (ctx: ShokupanContext<Record<string, any>>) => void | Promise<void>;
55
- onResponseStart?: (ctx: ShokupanContext<Record<string, any>>, response: Response) => void | Promise<void>;
56
- onResponseEnd?: (ctx: ShokupanContext<Record<string, any>>, response: Response) => void | Promise<void>;
57
- beforeValidate?: (ctx: ShokupanContext<Record<string, any>>, data: any) => void | Promise<void>;
58
- afterValidate?: (ctx: ShokupanContext<Record<string, any>>, data: any) => void | Promise<void>;
59
- onReadTimeout?: (ctx: ShokupanContext<Record<string, any>>) => void | Promise<void>;
60
- onWriteTimeout?: (ctx: ShokupanContext<Record<string, any>>) => void | Promise<void>;
61
- onRequestTimeout?: (ctx: ShokupanContext<Record<string, any>>) => void | Promise<void>;
110
+ onError?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, error: unknown) => void | Promise<void>;
111
+ onRequestStart?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
112
+ onRequestEnd?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
113
+ onResponseStart?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, response: Response) => void | Promise<void>;
114
+ onResponseEnd?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, response: Response) => void | Promise<void>;
115
+ beforeValidate?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, data: any) => void | Promise<void>;
116
+ afterValidate?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, data: any) => void | Promise<void>;
117
+ onReadTimeout?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
118
+ onWriteTimeout?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
119
+ onRequestTimeout?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
62
120
  } | {
63
- onError?: (ctx: ShokupanContext<Record<string, any>>, error: unknown) => void | Promise<void>;
64
- onRequestStart?: (ctx: ShokupanContext<Record<string, any>>) => void | Promise<void>;
65
- onRequestEnd?: (ctx: ShokupanContext<Record<string, any>>) => void | Promise<void>;
66
- onResponseStart?: (ctx: ShokupanContext<Record<string, any>>, response: Response) => void | Promise<void>;
67
- onResponseEnd?: (ctx: ShokupanContext<Record<string, any>>, response: Response) => void | Promise<void>;
68
- beforeValidate?: (ctx: ShokupanContext<Record<string, any>>, data: any) => void | Promise<void>;
69
- afterValidate?: (ctx: ShokupanContext<Record<string, any>>, data: any) => void | Promise<void>;
70
- onReadTimeout?: (ctx: ShokupanContext<Record<string, any>>) => void | Promise<void>;
71
- onWriteTimeout?: (ctx: ShokupanContext<Record<string, any>>) => void | Promise<void>;
72
- onRequestTimeout?: (ctx: ShokupanContext<Record<string, any>>) => void | Promise<void>;
121
+ onError?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, error: unknown) => void | Promise<void>;
122
+ onRequestStart?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
123
+ onRequestEnd?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
124
+ onResponseStart?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, response: Response) => void | Promise<void>;
125
+ onResponseEnd?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, response: Response) => void | Promise<void>;
126
+ beforeValidate?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, data: any) => void | Promise<void>;
127
+ afterValidate?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, data: any) => void | Promise<void>;
128
+ onReadTimeout?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
129
+ onWriteTimeout?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
130
+ onRequestTimeout?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
73
131
  }[];
74
132
  validateStatusCodes?: boolean;
75
133
  };
@@ -153,6 +211,8 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
153
211
  */
154
212
  testRequest(options: RequestOptions): Promise<ProcessResult>;
155
213
  private wrapWithHooks;
214
+ private mountRouter;
215
+ private scanControllerRoutes;
156
216
  /**
157
217
  * Find a route matching the given method and path.
158
218
  * @param method HTTP method
@@ -196,7 +256,7 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
196
256
  * @param path - URL path
197
257
  * @param handlers - Route handler functions
198
258
  */
199
- get(path: string, ...handlers: ShokupanHandler<T>[]): any;
259
+ get<Path extends string>(path: Path, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
200
260
  /**
201
261
  * Adds a GET route to the router.
202
262
  *
@@ -204,14 +264,14 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
204
264
  * @param spec - OpenAPI specification for the route
205
265
  * @param handlers - Route handler functions
206
266
  */
207
- get(path: string, spec: MethodAPISpec, ...handlers: ShokupanHandler<T>[]): any;
267
+ get<Path extends string>(path: Path, spec: MethodAPISpec, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
208
268
  /**
209
269
  * Adds a POST route to the router.
210
270
  *
211
271
  * @param path - URL path
212
272
  * @param handlers - Route handler functions
213
273
  */
214
- post(path: string, ...handlers: ShokupanHandler<T>[]): any;
274
+ post<Path extends string>(path: Path, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
215
275
  /**
216
276
  * Adds a POST route to the router.
217
277
  *
@@ -219,14 +279,14 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
219
279
  * @param spec - OpenAPI specification for the route
220
280
  * @param handlers - Route handler functions
221
281
  */
222
- post(path: string, spec: MethodAPISpec, ...handlers: ShokupanHandler<T>[]): any;
282
+ post<Path extends string>(path: Path, spec: MethodAPISpec, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
223
283
  /**
224
284
  * Adds a PUT route to the router.
225
285
  *
226
286
  * @param path - URL path
227
287
  * @param handlers - Route handler functions
228
288
  */
229
- put(path: string, ...handlers: ShokupanHandler<T>[]): any;
289
+ put<Path extends string>(path: Path, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
230
290
  /**
231
291
  * Adds a PUT route to the router.
232
292
  *
@@ -234,14 +294,14 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
234
294
  * @param spec - OpenAPI specification for the route
235
295
  * @param handlers - Route handler functions
236
296
  */
237
- put(path: string, spec: MethodAPISpec, ...handlers: ShokupanHandler<T>[]): any;
297
+ put<Path extends string>(path: Path, spec: MethodAPISpec, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
238
298
  /**
239
299
  * Adds a DELETE route to the router.
240
300
  *
241
301
  * @param path - URL path
242
302
  * @param handlers - Route handler functions
243
303
  */
244
- delete(path: string, ...handlers: ShokupanHandler<T>[]): any;
304
+ delete<Path extends string>(path: Path, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
245
305
  /**
246
306
  * Adds a DELETE route to the router.
247
307
  *
@@ -249,14 +309,14 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
249
309
  * @param spec - OpenAPI specification for the route
250
310
  * @param handlers - Route handler functions
251
311
  */
252
- delete(path: string, spec: MethodAPISpec, ...handlers: ShokupanHandler<T>[]): any;
312
+ delete<Path extends string>(path: Path, spec: MethodAPISpec, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
253
313
  /**
254
314
  * Adds a PATCH route to the router.
255
315
  *
256
316
  * @param path - URL path
257
317
  * @param handlers - Route handler functions
258
318
  */
259
- patch(path: string, ...handlers: ShokupanHandler<T>[]): any;
319
+ patch<Path extends string>(path: Path, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
260
320
  /**
261
321
  * Adds a PATCH route to the router.
262
322
  *
@@ -264,14 +324,14 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
264
324
  * @param spec - OpenAPI specification for the route
265
325
  * @param handlers - Route handler functions
266
326
  */
267
- patch(path: string, spec: MethodAPISpec, ...handlers: ShokupanHandler<T>[]): any;
327
+ patch<Path extends string>(path: Path, spec: MethodAPISpec, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
268
328
  /**
269
329
  * Adds a OPTIONS route to the router.
270
330
  *
271
331
  * @param path - URL path
272
332
  * @param handlers - Route handler functions
273
333
  */
274
- options(path: string, ...handlers: ShokupanHandler<T>[]): any;
334
+ options<Path extends string>(path: Path, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
275
335
  /**
276
336
  * Adds a OPTIONS route to the router.
277
337
  *
@@ -279,14 +339,14 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
279
339
  * @param spec - OpenAPI specification for the route
280
340
  * @param handlers - Route handler functions
281
341
  */
282
- options(path: string, spec: MethodAPISpec, ...handlers: ShokupanHandler<T>[]): any;
342
+ options<Path extends string>(path: Path, spec: MethodAPISpec, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
283
343
  /**
284
344
  * Adds a HEAD route to the router.
285
345
  *
286
346
  * @param path - URL path
287
347
  * @param handlers - Route handler functions
288
348
  */
289
- head(path: string, ...handlers: ShokupanHandler<T>[]): any;
349
+ head<Path extends string>(path: Path, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
290
350
  /**
291
351
  * Adds a HEAD route to the router.
292
352
  *
@@ -294,7 +354,7 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
294
354
  * @param spec - OpenAPI specification for the route
295
355
  * @param handlers - Route handler functions
296
356
  */
297
- head(path: string, spec: MethodAPISpec, ...handlers: ShokupanHandler<T>[]): any;
357
+ head<Path extends string>(path: Path, spec: MethodAPISpec, ...handlers: ShokupanHandler<T, RouteParams<Path>>[]): any;
298
358
  /**
299
359
  * Adds a guard to the router that applies to all routes added **after** this point.
300
360
  * Guards must return true or call `ctx.next()` to allow the request to continue.
@@ -329,4 +389,3 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
329
389
  private ensureHooksInitialized;
330
390
  runHooks(name: keyof ShokupanHooks, ...args: any[]): Promise<void>;
331
391
  }
332
- export {};
@@ -1,8 +1,72 @@
1
- import { ShokupanRequest } from './request';
2
- import { ShokupanRouter } from './router';
3
- import { $dispatch } from './symbol';
4
- import { Middleware, ProcessResult, RequestOptions, ShokupanConfig } from './types';
1
+ import { $dispatch } from './util/symbol';
2
+ import { Middleware, ProcessResult, RequestOptions, ShokupanConfig, ShokupanPlugin } from './util/types';
5
3
  import { Server } from 'bun';
4
+ import { ShokupanRouter } from './router';
5
+ import { ShokupanRequest } from './util/request';
6
+ /**
7
+ * Shokupan Application
8
+ *
9
+ * The main application class for creating a Shokupan web server.
10
+ *
11
+ * @template State - The shape of `ctx.state` for all routes in the application.
12
+ * Use this to provide type safety for state management across middleware and handlers.
13
+ *
14
+ * @example Basic Usage
15
+ * ```typescript
16
+ * const app = new Shokupan();
17
+ * app.get('/hello', (ctx) => ctx.json({ message: 'Hello' }));
18
+ * await app.listen(3000);
19
+ * ```
20
+ *
21
+ * @example Typed State
22
+ * ```typescript
23
+ * interface AppState {
24
+ * userId: string;
25
+ * tenant: string;
26
+ * requestId: string;
27
+ * }
28
+ *
29
+ * const app = new Shokupan<AppState>();
30
+ *
31
+ * // Middleware has typed state access
32
+ * app.use((ctx, next) => {
33
+ * ctx.state.userId = 'user-123'; // ✓ Type-safe
34
+ * ctx.state.requestId = crypto.randomUUID();
35
+ * return next();
36
+ * });
37
+ *
38
+ * // Handlers have typed state access
39
+ * app.get('/profile', (ctx) => {
40
+ * const { userId, tenant } = ctx.state; // ✓ TypeScript knows these exist
41
+ * return ctx.json({ userId, tenant });
42
+ * });
43
+ * ```
44
+ *
45
+ * @example Empty State (No State Management)
46
+ * ```typescript
47
+ * import { EmptyState } from 'shokupan';
48
+ *
49
+ * const app = new Shokupan<EmptyState>();
50
+ * // ctx.state will be an empty object with no properties
51
+ * ```
52
+ *
53
+ * @example Combining Path Params and State
54
+ * ```typescript
55
+ * interface RequestState {
56
+ * userId: string;
57
+ * permissions: string[];
58
+ * }
59
+ *
60
+ * const app = new Shokupan<RequestState>();
61
+ *
62
+ * app.get('/users/:userId/posts/:postId', (ctx) => {
63
+ * // Both params and state are fully typed!
64
+ * const { userId, postId } = ctx.params; // ✓ Path params typed
65
+ * const { permissions } = ctx.state; // ✓ State typed
66
+ * return ctx.json({ userId, postId, permissions });
67
+ * });
68
+ * ```
69
+ */
6
70
  export declare class Shokupan<T = any> extends ShokupanRouter<T> {
7
71
  readonly applicationConfig: ShokupanConfig;
8
72
  openApiSpec?: any;
@@ -21,6 +85,12 @@ export declare class Shokupan<T = any> extends ShokupanRouter<T> {
21
85
  * Adds middleware to the application.
22
86
  */
23
87
  use(middleware: Middleware): this;
88
+ /**
89
+ * Registers a plugin.
90
+ */
91
+ register(plugin: ShokupanPlugin, options?: {
92
+ path?: string;
93
+ }): this;
24
94
  private startupHooks;
25
95
  /**
26
96
  * Registers a callback to be executed before the server starts listening.
@@ -1,3 +1,9 @@
1
1
  import { AsyncLocalStorage } from 'node:async_hooks';
2
- export declare const asyncContext: AsyncLocalStorage<Map<string, any>>;
3
- export declare function runInContext<T>(callback: () => T, initialStore?: Map<string, any>): T;
2
+ import { Span } from '@opentelemetry/api';
3
+ export declare class RequestContextStore {
4
+ request?: Request;
5
+ span?: Span;
6
+ [key: string]: any;
7
+ }
8
+ export declare const asyncContext: AsyncLocalStorage<RequestContextStore>;
9
+ export declare function runInContext<T>(callback: () => T, initialStore?: RequestContextStore): T;
@@ -1,4 +1,4 @@
1
- import { RateLimitOptions } from './plugins/rate-limit';
1
+ import { RateLimitOptions } from '../plugins/middleware/rate-limit';
2
2
  import { GuardAPISpec, MethodAPISpec, Middleware } from './types';
3
3
  /**
4
4
  * Class Decorator: Defines the base path for a controller.
@@ -0,0 +1,2 @@
1
+ export declare const VALID_HTTP_STATUSES: Set<number>;
2
+ export declare const VALID_REDIRECT_STATUSES: Set<number>;
@@ -1,4 +1,4 @@
1
- import { Middleware, ShokupanHandler } from '../types';
1
+ import { Middleware, ShokupanHandler } from './types';
2
2
  /**
3
3
  * Wraps a middleware function with an OpenTelemetry span.
4
4
  */
@@ -1,4 +1,4 @@
1
- import { Method, ShokupanHandler } from '../types';
1
+ import { Method, ShokupanHandler } from './types';
2
2
  export interface RouteMatch<T = any> {
3
3
  handler: ShokupanHandler<T>;
4
4
  params: Record<string, string>;
@@ -1,11 +1,45 @@
1
1
  import { OpenAPI } from '@scalar/openapi-types';
2
2
  import { Server } from 'bun';
3
3
  import { Server as NodeServer } from 'node:http';
4
- import { ShokupanContext } from './context';
4
+ import { ShokupanContext } from '../context';
5
5
  import { $isRouter } from './symbol';
6
+ export type HeadersInit = Headers | Record<string, string> | [string, string][];
7
+ export interface ShokupanPluginOptions {
8
+ path?: string;
9
+ }
10
+ export interface ShokupanPlugin {
11
+ onInit: (app: any, options?: ShokupanPluginOptions) => void | Promise<void>;
12
+ }
6
13
  export type DeepPartial<T> = T extends Function ? T : T extends object ? {
7
14
  [P in keyof T]?: DeepPartial<T[P]>;
8
15
  } : T;
16
+ /**
17
+ * Helper type for applications that don't use ctx.state.
18
+ * Prevents accidental property access on state.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const app = new Shokupan<EmptyState>();
23
+ * ```
24
+ */
25
+ export type EmptyState = Record<string, never>;
26
+ /**
27
+ * Default state type that allows any properties.
28
+ * This is the default if no state type is specified.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const app = new Shokupan<DefaultState>();
33
+ * // Equivalent to: new Shokupan();
34
+ * ```
35
+ */
36
+ export type DefaultState = Record<string, any>;
37
+ type ParsePathParams<Path extends string> = Path extends `${infer _Start}:${infer Param}/${infer Rest}` ? {
38
+ [K in Param | keyof ParsePathParams<`/${Rest}`>]: string;
39
+ } : Path extends `${infer _Start}:${infer Param}` ? {
40
+ [K in Param]: string;
41
+ } : {};
42
+ export type RouteParams<Path extends string> = string extends Path ? Record<string, string> : ParsePathParams<Path> extends Record<string, never> ? Record<string, string> : ParsePathParams<Path>;
9
43
  export interface RouteMetadata {
10
44
  file: string;
11
45
  line: number;
@@ -49,7 +83,7 @@ export interface CookieOptions {
49
83
  sameSite?: boolean | 'lax' | 'strict' | 'none' | 'Lax' | 'Strict' | 'None';
50
84
  priority?: 'low' | 'medium' | 'high' | 'Low' | 'Medium' | 'High';
51
85
  }
52
- export type ShokupanHandler<T extends Record<string, any> = Record<string, any>> = (ctx: ShokupanContext<T>, next?: NextFn) => Promise<any> | any;
86
+ export type ShokupanHandler<State extends Record<string, any> = Record<string, any>, Params extends Record<string, string> = Record<string, string>> = (ctx: ShokupanContext<State, Params>, next?: NextFn) => Promise<any> | any;
53
87
  export declare const HTTPMethods: string[];
54
88
  export type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD" | "ALL";
55
89
  export declare enum RouteParamType {
@@ -350,6 +384,10 @@ export interface StaticServeOptions<T extends Record<string, any>> {
350
384
  root?: string;
351
385
  /**
352
386
  * Whether to list directory contents if no index file is found.
387
+ *
388
+ * Security Note: Directory listing is disabled by default to prevent information disclosure.
389
+ * Enable this only if you specifically need it and understand the security implications.
390
+ *
353
391
  * @default false
354
392
  */
355
393
  listDirectory?: boolean;
@@ -384,3 +422,4 @@ export interface StaticServeOptions<T extends Record<string, any>> {
384
422
  */
385
423
  openapi?: MethodAPISpec;
386
424
  }
425
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shokupan",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Shokupan is a low-lift modern web framework for Bun.",
5
5
  "author": "Andrew G. Knackstedt",
6
6
  "publishConfig": {
@@ -22,12 +22,12 @@
22
22
  ],
23
23
  "type": "module",
24
24
  "scripts": {
25
- "dev": "bun --watch --inspect src/example/main.ts",
25
+ "dev": "bun --watch --inspect examples/full/main.ts",
26
26
  "debug:otel": "sh scripts/debug-otel.sh",
27
27
  "docs": "cd docs && bun run dev",
28
28
  "build": "vite build",
29
- "bench": "cd src/benchmarking && bun runner.ts",
30
- "bench:advanced": "cd src/benchmarking && bun advanced-runner.ts",
29
+ "bench": "cd benchmarking && bun runner.ts",
30
+ "bench:advanced": "cd benchmarking && bun advanced-runner.ts",
31
31
  "retag": "git push origin :refs/tags/v$(node -p \"require('./package.json').version\") 2>/dev/null || true && git tag -d v$(node -p \"require('./package.json').version\") 2>/dev/null || true && git tag v$(node -p \"require('./package.json').version\") && git push origin v$(node -p \"require('./package.json').version\") --force"
32
32
  },
33
33
  "bin": {
@@ -152,4 +152,4 @@
152
152
  "vite-plugin-dts": "^4.5.4",
153
153
  "zod": "^4.2.1"
154
154
  }
155
- }
155
+ }