yinzerflow 0.2.10 → 0.3.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.
package/index.d.ts CHANGED
@@ -7,11 +7,158 @@ export type DeepPartial<T> = {
7
7
  [P in keyof T]?: T[P] extends object ? T[P] extends Array<infer U> ? Array<U> // Keep arrays as-is, don't make array items partial
8
8
  : DeepPartial<T[P]> : T[P];
9
9
  };
10
+ /**
11
+ * Generic types for handler callbacks that define the structure of request data
12
+ *
13
+ * This interface allows you to customize the types of body, response, query,
14
+ * params, and state data that your handlers can work with. Extend this interface
15
+ * to create type-safe contexts for your specific use cases.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // Basic usage with default types
20
+ * const handler: HandlerCallback = async (ctx) => {
21
+ * // ctx.request.body is unknown
22
+ * // ctx.state is Record<string, unknown>
23
+ * return { message: 'Hello' };
24
+ * };
25
+ *
26
+ * // Custom types for specific endpoints
27
+ * interface UserCreateContext extends InternalHandlerCallbackGenerics {
28
+ * body: { name: string; email: string; age: number };
29
+ * response: { id: string; name: string; email: string };
30
+ * state: { requestId: string; userAgent: string };
31
+ * }
32
+ *
33
+ * const createUser: HandlerCallback<UserCreateContext> = async (ctx) => {
34
+ * // Fully typed!
35
+ * const { name, email, age } = ctx.request.body; // Type: { name: string; email: string; age: number }
36
+ * const { requestId, userAgent } = ctx.state; // Type: { requestId: string; userAgent: string }
37
+ *
38
+ * const user = await createUserInDatabase({ name, email, age });
39
+ *
40
+ * return { id: user.id, name: user.name, email: user.email }; // Must match response type
41
+ * };
42
+ * ```
43
+ */
10
44
  export interface InternalHandlerCallbackGenerics {
45
+ /**
46
+ * The expected type of the request body
47
+ *
48
+ * Defaults to `unknown` for safety. Override with your specific body schema
49
+ * to get full type safety and IntelliSense.
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * interface LoginContext extends InternalHandlerCallbackGenerics {
54
+ * body: { username: string; password: string };
55
+ * }
56
+ *
57
+ * const login: HandlerCallback<LoginContext> = async (ctx) => {
58
+ * const { username, password } = ctx.request.body; // Fully typed!
59
+ * // ... authentication logic
60
+ * };
61
+ * ```
62
+ */
11
63
  body?: unknown;
64
+ /**
65
+ * The expected type of the response data
66
+ *
67
+ * Defaults to `unknown`. Override to ensure your handler returns the correct
68
+ * data structure and get compile-time validation.
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * interface UserListContext extends InternalHandlerCallbackGenerics {
73
+ * response: { users: Array<{ id: string; name: string }>; total: number };
74
+ * }
75
+ *
76
+ * const listUsers: HandlerCallback<UserListContext> = async (ctx) => {
77
+ * const users = await getUsersFromDatabase();
78
+ *
79
+ * // TypeScript will ensure this matches the response type
80
+ * return {
81
+ * users: users.map(u => ({ id: u.id, name: u.name })),
82
+ * total: users.length
83
+ * };
84
+ * };
85
+ * ```
86
+ */
12
87
  response?: unknown;
88
+ /**
89
+ * The expected type of query parameters
90
+ *
91
+ * Defaults to `Record<string, string>`. Override for more specific query
92
+ * parameter validation and typing.
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * interface SearchContext extends InternalHandlerCallbackGenerics {
97
+ * query: { q: string; limit?: string; page?: string };
98
+ * }
99
+ *
100
+ * const search: HandlerCallback<SearchContext> = async (ctx) => {
101
+ * const { q, limit = '10', page = '1' } = ctx.request.query;
102
+ * const limitNum = parseInt(limit);
103
+ * const pageNum = parseInt(page);
104
+ *
105
+ * // ... search logic
106
+ * };
107
+ * ```
108
+ */
13
109
  query?: Record<string, string>;
110
+ /**
111
+ * The expected type of route parameters
112
+ *
113
+ * Defaults to `Record<string, string>`. Override for more specific route
114
+ * parameter validation and typing.
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * interface UserDetailContext extends InternalHandlerCallbackGenerics {
119
+ * params: { id: string; tab?: string };
120
+ * }
121
+ *
122
+ * const getUser: HandlerCallback<UserDetailContext> = async (ctx) => {
123
+ * const { id, tab = 'profile' } = ctx.request.params;
124
+ *
125
+ * // ... user retrieval logic
126
+ * };
127
+ * ```
128
+ */
14
129
  params?: Record<string, string>;
130
+ /**
131
+ * User-defined state data that persists throughout the request lifecycle
132
+ *
133
+ * This allows you to store custom data like:
134
+ * - Authenticated user information
135
+ * - Request-scoped variables
136
+ * - Middleware data
137
+ * - Custom context information
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * interface AuthContext extends InternalHandlerCallbackGenerics {
142
+ * state: {
143
+ * user: User;
144
+ * permissions: string[];
145
+ * requestId: string;
146
+ * };
147
+ * }
148
+ *
149
+ * const handler: HandlerCallback<AuthContext> = async (ctx) => {
150
+ * // Fully typed state access
151
+ * const { user, permissions, requestId } = ctx.state;
152
+ *
153
+ * if (permissions.includes('admin')) {
154
+ * return { message: 'Admin access granted', user };
155
+ * }
156
+ *
157
+ * return { error: 'Insufficient permissions' };
158
+ * };
159
+ * ```
160
+ */
161
+ state?: Record<string, unknown>;
15
162
  }
16
163
  declare const httpStatusCode: {
17
164
  readonly ok: 200;
@@ -126,38 +273,861 @@ declare const httpHeaders: {
126
273
  readonly clearSiteData: "Clear-Site-Data";
127
274
  readonly noVarySearch: "No-Vary-Search";
128
275
  };
276
+ /**
277
+ * HTTP status code constants for response status.
278
+ *
279
+ * Standard HTTP status codes used in responses to indicate
280
+ * the result of processing a request.
281
+ *
282
+ * ## Status Code Categories
283
+ *
284
+ * - **2xx Success**: Request was successful
285
+ * - **3xx Redirection**: Further action needed
286
+ * - **4xx Client Error**: Request was invalid
287
+ * - **5xx Server Error**: Server failed to process request
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * // Setting response status codes
292
+ * ctx.response.setStatusCode(InternalHttpStatusCode.ok); // 200
293
+ * ctx.response.setStatusCode(InternalHttpStatusCode.created); // 201
294
+ * ctx.response.setStatusCode(InternalHttpStatusCode.noContent); // 204
295
+ *
296
+ * // Error handling
297
+ * try {
298
+ * const result = await processRequest(ctx.request);
299
+ * ctx.response.setStatusCode(InternalHttpStatusCode.ok);
300
+ * return result;
301
+ * } catch (error) {
302
+ * if (error.name === 'ValidationError') {
303
+ * ctx.response.setStatusCode(InternalHttpStatusCode.badRequest); // 400
304
+ * return { error: 'Validation failed' };
305
+ * }
306
+ *
307
+ * if (error.name === 'UnauthorizedError') {
308
+ * ctx.response.setStatusCode(InternalHttpStatusCode.unauthorized); // 401
309
+ * return { error: 'Unauthorized' };
310
+ * }
311
+ *
312
+ * ctx.response.setStatusCode(InternalHttpStatusCode.internalServerError); // 500
313
+ * return { error: 'Internal server error' };
314
+ * }
315
+ *
316
+ * // Status code validation
317
+ * const isValidStatusCode = (code: number): boolean => {
318
+ * return Object.values(InternalHttpStatusCode).includes(code as any);
319
+ * };
320
+ * ```
321
+ *
322
+ * @see {@link InternalHttpStatus} for corresponding status text
323
+ * @see {@link httpStatusCode} for the underlying constant values
324
+ */
129
325
  export type InternalHttpStatusCode = CreateEnum<typeof httpStatusCode>;
326
+ /**
327
+ * HTTP method constants for request methods.
328
+ *
329
+ * Standard HTTP methods used in requests to specify the desired
330
+ * action to be performed on the identified resource.
331
+ *
332
+ * ## HTTP Methods
333
+ *
334
+ * - **GET**: Retrieve a resource
335
+ * - **POST**: Create a new resource
336
+ * - **PUT**: Replace an entire resource
337
+ * - **PATCH**: Partially modify a resource
338
+ * - **DELETE**: Remove a resource
339
+ * - **HEAD**: Get resource metadata only
340
+ * - **OPTIONS**: Get available methods
341
+ *
342
+ * @example
343
+ * ```typescript
344
+ * // Method-based routing
345
+ * const handleRequest = async (ctx: Context) => {
346
+ * switch (ctx.request.method) {
347
+ * case InternalHttpMethod.get:
348
+ * return await getResource(ctx.request.params.id);
349
+ *
350
+ * case InternalHttpMethod.post:
351
+ * return await createResource(ctx.request.body);
352
+ *
353
+ * case InternalHttpMethod.put:
354
+ * return await updateResource(ctx.request.params.id, ctx.request.body);
355
+ *
356
+ * case InternalHttpMethod.patch:
357
+ * return await patchResource(ctx.request.params.id, ctx.request.body);
358
+ *
359
+ * case InternalHttpMethod.delete:
360
+ * return await deleteResource(ctx.request.params.id);
361
+ *
362
+ * case InternalHttpMethod.options:
363
+ * return { methods: ['GET', 'POST', 'PUT', 'DELETE'] };
364
+ *
365
+ * default:
366
+ * throw new Error(`Method ${ctx.request.method} not supported`);
367
+ * }
368
+ * };
369
+ *
370
+ * // Method validation
371
+ * const isReadMethod = (method: string): boolean => {
372
+ * return method === InternalHttpMethod.get || method === InternalHttpMethod.head;
373
+ * };
374
+ *
375
+ * const isWriteMethod = (method: string): boolean => {
376
+ * return [InternalHttpMethod.post, InternalHttpMethod.put,
377
+ * InternalHttpMethod.patch, InternalHttpMethod.delete].includes(method as any);
378
+ * };
379
+ * ```
380
+ *
381
+ * @see {@link httpMethod} for the underlying constant values
382
+ */
130
383
  export type InternalHttpMethod = CreateEnum<typeof httpMethod>;
384
+ /**
385
+ * HTTP header constants for request and response headers.
386
+ *
387
+ * Comprehensive collection of HTTP header names organized by category,
388
+ * including standard headers, security headers, and custom headers.
389
+ *
390
+ * ## Header Categories
391
+ *
392
+ * - **Authentication**: Authorization, WWW-Authenticate
393
+ * - **Caching**: Cache-Control, ETag, Expires
394
+ * - **Content**: Content-Type, Content-Length, Content-Encoding
395
+ * - **CORS**: Access-Control-Allow-*, Access-Control-Request-*
396
+ * - **Security**: Content-Security-Policy, X-Frame-Options
397
+ * - **Custom**: X-Powered-By, X-Request-ID
398
+ *
399
+ * @example
400
+ * ```typescript
401
+ * // Setting security headers
402
+ * ctx.response.addHeaders({
403
+ * [InternalHttpHeaders.contentSecurityPolicy]: "default-src 'self'",
404
+ * [InternalHttpHeaders.xFrameOptions]: 'DENY',
405
+ * [InternalHttpHeaders.xContentTypeOptions]: 'nosniff',
406
+ * [InternalHttpHeaders.strictTransportSecurity]: 'max-age=31536000'
407
+ * });
408
+ *
409
+ * // CORS headers
410
+ * ctx.response.addHeaders({
411
+ * [InternalHttpHeaders.accessControlAllowOrigin]: '*',
412
+ * [InternalHttpHeaders.accessControlAllowMethods]: 'GET, POST, PUT, DELETE',
413
+ * [InternalHttpHeaders.accessControlAllowHeaders]: 'Content-Type, Authorization'
414
+ * });
415
+ *
416
+ * // Custom headers
417
+ * ctx.response.addHeaders({
418
+ * [InternalHttpHeaders.xRequestId]: generateRequestId(),
419
+ * [InternalHttpHeaders.xProcessingTime]: `${processingTime}ms`
420
+ * });
421
+ *
422
+ * // Header validation
423
+ * const isSecurityHeader = (headerName: string): boolean => {
424
+ * const securityHeaders = [
425
+ * InternalHttpHeaders.contentSecurityPolicy,
426
+ * InternalHttpHeaders.xFrameOptions,
427
+ * InternalHttpHeaders.xContentTypeOptions,
428
+ * InternalHttpHeaders.strictTransportSecurity
429
+ * ];
430
+ * return securityHeaders.includes(headerName as any);
431
+ * };
432
+ *
433
+ * // Request header processing
434
+ * const processRequestHeaders = (headers: Record<string, string>) => {
435
+ * const authToken = headers[InternalHttpHeaders.authorization];
436
+ * const contentType = headers[InternalHttpHeaders.contentType];
437
+ * const userAgent = headers[InternalHttpHeaders.userAgent];
438
+ *
439
+ * // Process headers...
440
+ * return { authToken, contentType, userAgent };
441
+ * };
442
+ * ```
443
+ *
444
+ * @see {@link httpHeaders} for the underlying constant values
445
+ * @see {@link Request} for accessing headers in requests
446
+ * @see {@link Response} for setting headers in responses
447
+ */
131
448
  export type InternalHttpHeaders = Lowercase<CreateEnum<typeof httpHeaders>> | string;
132
449
  interface Request$1<T extends InternalHandlerCallbackGenerics = InternalHandlerCallbackGenerics> {
450
+ /**
451
+ * The HTTP protocol version (e.g., "HTTP/1.1").
452
+ *
453
+ * @example
454
+ * ```typescript
455
+ * const handler: HandlerCallback = async (ctx) => {
456
+ * if (ctx.request.protocol === 'HTTP/2.0') {
457
+ * // Use HTTP/2 specific features
458
+ * ctx.response.addHeaders({ 'X-HTTP-Version': '2.0' });
459
+ * }
460
+ *
461
+ * return { protocol: ctx.request.protocol };
462
+ * };
463
+ * ```
464
+ */
133
465
  protocol: string;
466
+ /**
467
+ * The HTTP method of the request (GET, POST, PUT, DELETE, etc.).
468
+ *
469
+ * @example
470
+ * ```typescript
471
+ * const handler: HandlerCallback = async (ctx) => {
472
+ * const { method } = ctx.request;
473
+ *
474
+ * switch (method) {
475
+ * case 'GET':
476
+ * return { action: 'retrieve', data: await getData() };
477
+ * case 'POST':
478
+ * return { action: 'create', data: await createData(ctx.request.body) };
479
+ * case 'PUT':
480
+ * return { action: 'update', data: await updateData(ctx.request.body) };
481
+ * case 'DELETE':
482
+ * return { action: 'delete', data: await deleteData(ctx.request.params.id) };
483
+ * default:
484
+ * throw new Error(`Unsupported method: ${method}`);
485
+ * }
486
+ * };
487
+ * ```
488
+ *
489
+ * @see {@link InternalHttpMethod} for all available HTTP methods
490
+ */
134
491
  method: InternalHttpMethod;
492
+ /**
493
+ * The request path/URL without query parameters.
494
+ *
495
+ * @example
496
+ * ```typescript
497
+ * const handler: HandlerCallback = async (ctx) => {
498
+ * const { path } = ctx.request;
499
+ *
500
+ * // Log the requested path
501
+ * console.log(`Request to: ${path}`);
502
+ *
503
+ * // Route-specific logic based on path
504
+ * if (path.startsWith('/api/v1/')) {
505
+ * ctx.state.apiVersion = 'v1';
506
+ * } else if (path.startsWith('/api/v2/')) {
507
+ * ctx.state.apiVersion = 'v2';
508
+ * }
509
+ *
510
+ * return { requestedPath: path, apiVersion: ctx.state.apiVersion };
511
+ * };
512
+ * ```
513
+ */
135
514
  path: string;
515
+ /**
516
+ * HTTP headers sent with the request.
517
+ *
518
+ * Headers are case-insensitive and commonly include authorization,
519
+ * content-type, user-agent, and custom headers.
520
+ *
521
+ * @example
522
+ * ```typescript
523
+ * const handler: HandlerCallback = async (ctx) => {
524
+ * const { headers } = ctx.request;
525
+ *
526
+ * // Authentication
527
+ * const authToken = headers.authorization;
528
+ * if (!authToken) {
529
+ * throw new Error('Authorization header required');
530
+ * }
531
+ *
532
+ * // Content type validation
533
+ * const contentType = headers['content-type'];
534
+ * if (contentType !== 'application/json') {
535
+ * throw new Error('Content-Type must be application/json');
536
+ * }
537
+ *
538
+ * // Custom headers
539
+ * const requestId = headers['x-request-id'];
540
+ * const userId = headers['x-user-id'];
541
+ *
542
+ * return {
543
+ * hasAuth: !!authToken,
544
+ * contentType,
545
+ * requestId,
546
+ * userId
547
+ * };
548
+ * };
549
+ * ```
550
+ *
551
+ * @see {@link InternalHttpHeaders} for all available header names
552
+ */
136
553
  headers: Partial<Record<InternalHttpHeaders, string>>;
554
+ /**
555
+ * The parsed request body, typed according to the generic parameter.
556
+ *
557
+ * The body is automatically parsed based on Content-Type header.
558
+ * For JSON requests, this will be the parsed JavaScript object.
559
+ *
560
+ * @example
561
+ * ```typescript
562
+ * // Basic body access
563
+ * const handler: HandlerCallback = async (ctx) => {
564
+ * const { body } = ctx.request;
565
+ *
566
+ * // body is typed as 'unknown' by default
567
+ * if (typeof body === 'object' && body !== null) {
568
+ * const userData = body as { name: string; email: string };
569
+ * return { received: userData };
570
+ * }
571
+ *
572
+ * return { error: 'Invalid body format' };
573
+ * };
574
+ *
575
+ * // Typed body with generics
576
+ * interface CreateUserRequest extends InternalHandlerCallbackGenerics {
577
+ * body: { name: string; email: string; age: number };
578
+ * }
579
+ *
580
+ * const createUser: HandlerCallback<CreateUserRequest> = async (ctx) => {
581
+ * const { name, email, age } = ctx.request.body; // Fully typed!
582
+ *
583
+ * // Validate age
584
+ * if (age < 18) {
585
+ * throw new Error('User must be 18 or older');
586
+ * }
587
+ *
588
+ * const user = await createUserInDatabase({ name, email, age });
589
+ * return { success: true, user };
590
+ * };
591
+ * ```
592
+ *
593
+ * @see {@link InternalHandlerCallbackGenerics} for custom body typing
594
+ */
137
595
  body: T["body"];
596
+ /**
597
+ * Query parameters from the URL, typed according to the generic parameter.
598
+ *
599
+ * Query parameters are the key-value pairs after the ? in the URL.
600
+ * They're automatically parsed and made available here.
601
+ *
602
+ * @example
603
+ * ```typescript
604
+ * // Basic query parameter access
605
+ * const handler: HandlerCallback = async (ctx) => {
606
+ * const { query } = ctx.request;
607
+ *
608
+ * // query is typed as 'unknown' by default
609
+ * if (typeof query === 'object' && query !== null) {
610
+ * const { page, limit, search } = query as { page?: string; limit?: string; search?: string };
611
+ *
612
+ * const pageNum = parseInt(page || '1');
613
+ * const limitNum = parseInt(limit || '10');
614
+ *
615
+ * return { pagination: { page: pageNum, limit: limitNum }, search };
616
+ * }
617
+ *
618
+ * return { pagination: { page: 1, limit: 10 } };
619
+ * };
620
+ *
621
+ * // Typed query parameters with generics
622
+ * interface UserListRequest extends InternalHandlerCallbackGenerics {
623
+ * query: { page: string; limit: string; search?: string; sort?: string };
624
+ * }
625
+ *
626
+ * const listUsers: HandlerCallback<UserListRequest> = async (ctx) => {
627
+ * const { page, limit, search, sort } = ctx.request.query; // Fully typed!
628
+ *
629
+ * const pageNum = parseInt(page);
630
+ * const limitNum = parseInt(limit);
631
+ *
632
+ * const users = await getUsersFromDatabase({
633
+ * page: pageNum,
634
+ * limit: limitNum,
635
+ * search: search || '',
636
+ * sort: sort || 'name'
637
+ * });
638
+ *
639
+ * return { users, pagination: { page: pageNum, limit: limitNum } };
640
+ * };
641
+ * ```
642
+ *
643
+ * @see {@link InternalHandlerCallbackGenerics} for custom query typing
644
+ */
138
645
  query: T["query"];
646
+ /**
647
+ * Route parameters extracted from the URL path, typed according to the generic parameter.
648
+ *
649
+ * Route parameters are the dynamic parts of the URL path (e.g., /users/:id).
650
+ * They're automatically extracted and made available here.
651
+ *
652
+ * @example
653
+ * ```typescript
654
+ * // Basic route parameter access
655
+ * const handler: HandlerCallback = async (ctx) => {
656
+ * const { params } = ctx.request;
657
+ *
658
+ * // params is typed as 'unknown' by default
659
+ * if (typeof params === 'object' && params !== null) {
660
+ * const { id, category } = params as { id?: string; category?: string };
661
+ *
662
+ * if (!id) {
663
+ * throw new Error('User ID is required');
664
+ * }
665
+ *
666
+ * return { userId: id, category: category || 'default' };
667
+ * }
668
+ *
669
+ * return { error: 'No parameters found' };
670
+ * };
671
+ *
672
+ * // Typed route parameters with generics
673
+ * interface UserDetailRequest extends InternalHandlerCallbackGenerics {
674
+ * params: { id: string; tab?: string };
675
+ * }
676
+ *
677
+ * const getUser: HandlerCallback<UserDetailRequest> = async (ctx) => {
678
+ * const { id, tab } = ctx.request.params; // Fully typed!
679
+ *
680
+ * const user = await getUserById(id);
681
+ * if (!user) {
682
+ * throw new Error('User not found');
683
+ * }
684
+ *
685
+ * // Return different data based on tab parameter
686
+ * switch (tab) {
687
+ * case 'profile':
688
+ * return { user: { id: user.id, name: user.name, email: user.email } };
689
+ * case 'settings':
690
+ * return { user: { id: user.id, preferences: user.preferences } };
691
+ * default:
692
+ * return { user };
693
+ * }
694
+ * };
695
+ * ```
696
+ *
697
+ * @see {@link InternalHandlerCallbackGenerics} for custom params typing
698
+ */
139
699
  params: T["params"];
140
- ipAddress: string; // The ip address of the client (Configure proxy hops if behind a proxy, load balancer, etc.)
141
- rawBody: Buffer | string; // The raw body of the request. Useful for parsing the body manually.
700
+ /**
701
+ * The IP address of the client making the request.
702
+ *
703
+ * **Important**: If you're behind a proxy, load balancer, or reverse proxy,
704
+ * you may need to configure it to forward the real client IP address.
705
+ *
706
+ * @example
707
+ * ```typescript
708
+ * const handler: HandlerCallback = async (ctx) => {
709
+ * const { ipAddress } = ctx.request;
710
+ *
711
+ * // Log client IP for security and analytics
712
+ * console.log(`Request from IP: ${ipAddress}`);
713
+ *
714
+ * // Rate limiting by IP
715
+ * const requestCount = await getRequestCount(ipAddress);
716
+ * if (requestCount > 100) {
717
+ * throw new Error('Rate limit exceeded');
718
+ * }
719
+ *
720
+ * // Geolocation (if needed)
721
+ * const country = await getCountryFromIP(ipAddress);
722
+ * ctx.state.clientCountry = country;
723
+ *
724
+ * return {
725
+ * message: 'Request processed',
726
+ * clientIP: ipAddress,
727
+ * country
728
+ * };
729
+ * };
730
+ * ```
731
+ */
732
+ ipAddress: string;
733
+ /**
734
+ * The raw, unparsed request body as a Buffer or string.
735
+ *
736
+ * This is useful when you need to parse the body manually or when
737
+ * the automatic parsing doesn't meet your needs.
738
+ *
739
+ * @example
740
+ * ```typescript
741
+ * const handler: HandlerCallback = async (ctx) => {
742
+ * const { rawBody, headers } = ctx.request;
743
+ *
744
+ * // Manual parsing for specific content types
745
+ * const contentType = headers['content-type'];
746
+ *
747
+ * if (contentType === 'application/xml') {
748
+ * // Parse XML manually
749
+ * const xmlString = rawBody.toString('utf8');
750
+ * const xmlDoc = await parseXML(xmlString);
751
+ * return { parsed: xmlDoc };
752
+ * }
753
+ *
754
+ * if (contentType === 'multipart/form-data') {
755
+ * // Handle file uploads manually
756
+ * const formData = await parseMultipartFormData(rawBody);
757
+ * return { files: formData.files, fields: formData.fields };
758
+ * }
759
+ *
760
+ * // For other types, use the parsed body
761
+ * return { body: ctx.request.body, rawBodyLength: rawBody.length };
762
+ * };
763
+ * ```
764
+ */
765
+ rawBody: Buffer | string;
142
766
  }
143
767
  interface Response$1 {
768
+ /**
769
+ * Sets the HTTP status code for the response.
770
+ *
771
+ * Common status codes:
772
+ * - **2xx Success**: 200 (OK), 201 (Created), 204 (No Content)
773
+ * - **3xx Redirection**: 301 (Moved), 302 (Found), 304 (Not Modified)
774
+ * - **4xx Client Error**: 400 (Bad Request), 401 (Unauthorized), 404 (Not Found)
775
+ * - **5xx Server Error**: 500 (Internal Server Error), 502 (Bad Gateway)
776
+ *
777
+ * @param statusCode - The HTTP status code to set
778
+ *
779
+ * @example
780
+ * ```typescript
781
+ * const handler: HandlerCallback = async (ctx) => {
782
+ * const { response, request } = ctx;
783
+ *
784
+ * try {
785
+ * if (request.method === 'POST') {
786
+ * // Resource created successfully
787
+ * response.setStatusCode(201);
788
+ * return { message: 'Resource created' };
789
+ * }
790
+ *
791
+ * if (request.method === 'DELETE') {
792
+ * // Resource deleted successfully
793
+ * response.setStatusCode(204);
794
+ * return; // No content for DELETE
795
+ * }
796
+ *
797
+ * // Default success response
798
+ * response.setStatusCode(200);
799
+ * return { message: 'Operation successful' };
800
+ *
801
+ * } catch (error) {
802
+ * if (error.name === 'ValidationError') {
803
+ * response.setStatusCode(400);
804
+ * return { error: 'Validation failed', details: error.message };
805
+ * }
806
+ *
807
+ * if (error.name === 'UnauthorizedError') {
808
+ * response.setStatusCode(401);
809
+ * return { error: 'Unauthorized' };
810
+ * }
811
+ *
812
+ * // Default error response
813
+ * response.setStatusCode(500);
814
+ * return { error: 'Internal server error' };
815
+ * }
816
+ * };
817
+ * ```
818
+ *
819
+ * @see {@link InternalHttpStatusCode} for all available status codes
820
+ */
144
821
  setStatusCode: (statusCode: InternalHttpStatusCode) => void;
822
+ /**
823
+ * Adds or updates HTTP response headers.
824
+ *
825
+ * Headers are key-value pairs that provide metadata about the response.
826
+ * Common headers include Content-Type, Cache-Control, and custom headers.
827
+ *
828
+ * @param headers - Object containing header names and values
829
+ *
830
+ * @example
831
+ * ```typescript
832
+ * const handler: HandlerCallback = async (ctx) => {
833
+ * const { response, request } = ctx;
834
+ *
835
+ * // Set basic response headers
836
+ * response.addHeaders({
837
+ * 'Content-Type': 'application/json',
838
+ * 'Cache-Control': 'max-age=3600, public'
839
+ * });
840
+ *
841
+ * // Add security headers
842
+ * response.addHeaders({
843
+ * 'X-Content-Type-Options': 'nosniff',
844
+ * 'X-Frame-Options': 'DENY',
845
+ * 'X-XSS-Protection': '1; mode=block',
846
+ * 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains'
847
+ * });
848
+ *
849
+ * // Add custom headers
850
+ * response.addHeaders({
851
+ * 'X-API-Version': 'v1.0.0',
852
+ * 'X-Request-ID': generateRequestId(),
853
+ * 'X-Processing-Time': `${Date.now() - ctx.state.startTime}ms`
854
+ * });
855
+ *
856
+ * // CORS headers for cross-origin requests
857
+ * response.addHeaders({
858
+ * 'Access-Control-Allow-Origin': '*',
859
+ * 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
860
+ * 'Access-Control-Allow-Headers': 'Content-Type, Authorization'
861
+ * });
862
+ *
863
+ * return { message: 'Headers set successfully' };
864
+ * };
865
+ * ```
866
+ *
867
+ * @see {@link InternalHttpHeaders} for all available header names
868
+ */
145
869
  addHeaders: (headers: Partial<Record<InternalHttpHeaders, string>>) => void;
870
+ /**
871
+ * Removes specific HTTP response headers by name.
872
+ *
873
+ * This is useful when you want to remove headers that might have been
874
+ * set by default or by previous middleware.
875
+ *
876
+ * @param headerNames - Array of header names to remove
877
+ *
878
+ * @example
879
+ * ```typescript
880
+ * const handler: HandlerCallback = async (ctx) => {
881
+ * const { response } = ctx;
882
+ *
883
+ * // Remove default headers that might interfere
884
+ * response.removeHeaders([
885
+ * 'X-Powered-By',
886
+ * 'Server',
887
+ * 'Date'
888
+ * ]);
889
+ *
890
+ * // Add custom headers
891
+ * response.addHeaders({
892
+ * 'X-Custom-Header': 'Custom Value',
893
+ * 'Cache-Control': 'no-cache, no-store, must-revalidate'
894
+ * });
895
+ *
896
+ * return { message: 'Custom response configured' };
897
+ * };
898
+ *
899
+ * // Conditional header removal
900
+ * const conditionalHandler: HandlerCallback = async (ctx) => {
901
+ * const { response, request } = ctx;
902
+ *
903
+ * // Remove cache headers for sensitive operations
904
+ * if (request.method === 'POST' || request.method === 'PUT') {
905
+ * response.removeHeaders(['Cache-Control', 'ETag', 'Last-Modified']);
906
+ *
907
+ * // Add no-cache headers
908
+ * response.addHeaders({
909
+ * 'Cache-Control': 'no-cache, no-store, must-revalidate',
910
+ * 'Pragma': 'no-cache',
911
+ * 'Expires': '0'
912
+ * });
913
+ * }
914
+ *
915
+ * return { message: 'Response configured based on method' };
916
+ * };
917
+ * ```
918
+ *
919
+ * @see {@link InternalHttpHeaders} for all available header names
920
+ */
146
921
  removeHeaders: (headerNames: Array<InternalHttpHeaders>) => void;
147
922
  }
923
+ /**
924
+ * Request context that provides access to request, response, and user-defined state
925
+ *
926
+ * The context is the central object passed to all route handlers and middleware.
927
+ * It contains the request and response objects, plus any custom state data
928
+ * defined by the user through generics.
929
+ *
930
+ * @template T - Extends InternalHandlerCallbackGenerics to provide custom typing
931
+ *
932
+ * @example
933
+ * ```typescript
934
+ * // Basic usage with default context
935
+ * const handler: HandlerCallback = async (ctx) => {
936
+ * // Access request data
937
+ * const userId = ctx.request.params.id;
938
+ * const userData = ctx.request.body;
939
+ *
940
+ * // Store custom data in state
941
+ * ctx.state.user = { id: userId, name: 'John' };
942
+ * ctx.state.requestId = generateRequestId();
943
+ *
944
+ * // Control response
945
+ * ctx.response.setStatusCode(200);
946
+ * ctx.response.addHeaders({ 'X-User-ID': userId });
947
+ *
948
+ * // Return response body
949
+ * return { success: true, user: ctx.state.user };
950
+ * };
951
+ *
952
+ * // Advanced usage with custom state typing
953
+ * interface AuthContext extends InternalHandlerCallbackGenerics {
954
+ * state: {
955
+ * user: User;
956
+ * permissions: string[];
957
+ * session: Session;
958
+ * };
959
+ * }
960
+ *
961
+ * const authHandler: HandlerCallback<AuthContext> = async (ctx) => {
962
+ * // Fully type-safe access to state
963
+ * const { user, permissions, session } = ctx.state;
964
+ *
965
+ * // No type assertions needed!
966
+ * if (permissions.includes('admin')) {
967
+ * ctx.response.setStatusCode(200);
968
+ * return { message: 'Admin access granted', user };
969
+ * }
970
+ *
971
+ * ctx.response.setStatusCode(403);
972
+ * return { error: 'Insufficient permissions' };
973
+ * };
974
+ * ```
975
+ */
148
976
  export interface Context<T extends InternalHandlerCallbackGenerics = InternalHandlerCallbackGenerics> {
977
+ /**
978
+ * The incoming request object containing all request data and metadata
979
+ *
980
+ * Provides access to headers, body, query parameters, route parameters, and metadata.
981
+ *
982
+ * @example
983
+ * ```typescript
984
+ * const handler: HandlerCallback = async (ctx) => {
985
+ * const userId = ctx.request.params.id;
986
+ * const userData = ctx.request.body;
987
+ * const authToken = ctx.request.headers.authorization;
988
+ * const clientIp = ctx.request.ipAddress;
989
+ *
990
+ * return { userId, userData, hasAuth: !!authToken };
991
+ * };
992
+ * ```
993
+ *
994
+ * @see {@link Request} for complete request interface documentation
995
+ */
149
996
  request: Request$1<T>;
997
+ /**
998
+ * The outgoing response object for controlling HTTP response behavior
999
+ *
1000
+ * Provides methods to set status codes, add headers, and control response formatting.
1001
+ *
1002
+ * @example
1003
+ * ```typescript
1004
+ * const handler: HandlerCallback = async (ctx) => {
1005
+ * const { request, response } = ctx;
1006
+ *
1007
+ * response.setStatusCode(201);
1008
+ * response.addHeaders({
1009
+ * 'Location': `/api/users/${request.params.id}`,
1010
+ * 'X-User-ID': request.params.id
1011
+ * });
1012
+ *
1013
+ * return { message: 'User created successfully' };
1014
+ * };
1015
+ * ```
1016
+ *
1017
+ * @see {@link Response} for complete response interface documentation
1018
+ */
150
1019
  response: Response$1;
1020
+ /**
1021
+ * User-defined state data that persists throughout the request lifecycle
1022
+ *
1023
+ * State is request-scoped data that can be accessed by route handlers and middleware.
1024
+ * Each request gets its own isolated state object that's automatically cleaned up.
1025
+ *
1026
+ * ## State Lifecycle
1027
+ *
1028
+ * 1. **Request Start**: State object is created as empty object
1029
+ * 2. **Global Hooks**: `beforeAll` hooks can populate state
1030
+ * 3. **Route Hooks**: `beforeHooks` can access and modify state
1031
+ * 4. **Route Handler**: Your handler can access and modify state
1032
+ * 5. **Route Hooks**: `afterHooks` can access state and modify response
1033
+ * 6. **Global Hooks**: `afterAll` hooks can access state and modify response
1034
+ * 7. **Request End**: State is automatically garbage collected
1035
+ *
1036
+ * @example
1037
+ * ```typescript
1038
+ * // Store data in state
1039
+ * ctx.state.user = { id: 1, name: 'John' };
1040
+ * ctx.state.requestId = generateRequestId();
1041
+ *
1042
+ * // Access the data
1043
+ * const user = ctx.state.user;
1044
+ * const requestId = ctx.state.requestId;
1045
+ * ```
1046
+ */
1047
+ state: T["state"] extends Record<string, unknown> ? T["state"] : Record<string, unknown>;
151
1048
  }
152
1049
  /**
153
- * Represents a route handler function that returns a response body
1050
+ * Represents a route handler function that processes requests and returns responses.
1051
+ *
1052
+ * This type defines the signature for all route handlers, middleware, and hooks
1053
+ * in YinzerFlow. The function receives a context object and can optionally
1054
+ * receive an error parameter for error handlers.
1055
+ *
1056
+ * ## Handler Types
1057
+ *
1058
+ * - **Route Handlers**: Process requests and return response data
1059
+ * - **Middleware**: Modify context or perform side effects
1060
+ * - **Hooks**: beforeHooks, afterHooks, beforeAll, afterAll
1061
+ * - **Error Handlers**: Handle errors with error parameter
1062
+ *
1063
+ * ## Return Values
1064
+ *
1065
+ * Handlers can return:
1066
+ * - **Response Data**: Objects, strings, numbers, etc. (automatically JSON serialized)
1067
+ * - **Promise**: Async operations that resolve to response data
1068
+ * - **Void**: No response body (useful for middleware)
1069
+ * - **Error**: Thrown errors are caught by error handlers
1070
+ *
1071
+ * @template T - Extends InternalHandlerCallbackGenerics for custom typing
1072
+ * @param ctx - The request context containing request, response, and state objects
1073
+ * @param error - Optional error object (only provided to error handlers)
1074
+ * @returns Response data, promise, or void
1075
+ *
1076
+ * @example
1077
+ * ```typescript
1078
+ * // Basic route handler
1079
+ * const userHandler: HandlerCallback = async (ctx) => {
1080
+ * const userId = ctx.request.params.id;
1081
+ * const user = await getUserById(userId);
1082
+ *
1083
+ * return { user, timestamp: new Date().toISOString() };
1084
+ * };
1085
+ *
1086
+ * // Typed route handler with custom state
1087
+ * interface UserContext extends InternalHandlerCallbackGenerics {
1088
+ * body: { name: string; email: string };
1089
+ * response: { id: string; name: string; email: string };
1090
+ * state: { user: User; permissions: string[] };
1091
+ * }
154
1092
  *
155
- * This type defines the signature for route handlers that process requests
156
- * and return a response. The function can return either a promise that resolves
157
- * to a response body or a response body directly.
1093
+ * const createUser: HandlerCallback<UserContext> = async (ctx) => {
1094
+ * const { name, email } = ctx.request.body; // Fully typed!
1095
+ * const { user, permissions } = ctx.state; // Fully typed!
158
1096
  *
159
- * @param ctx - The request context containing request and response objects
160
- * @returns A response body or a promise that resolves to a response body
1097
+ * if (!permissions.includes('create')) {
1098
+ * throw new Error('Insufficient permissions');
1099
+ * }
1100
+ *
1101
+ * const newUser = await createUserInDatabase({ name, email });
1102
+ *
1103
+ * return { id: newUser.id, name: newUser.name, email: newUser.email };
1104
+ * };
1105
+ *
1106
+ * // Middleware that doesn't return data
1107
+ * const authMiddleware: HandlerCallback = async (ctx) => {
1108
+ * const token = ctx.request.headers.authorization;
1109
+ * const user = await validateToken(token);
1110
+ *
1111
+ * ctx.state.user = user;
1112
+ * ctx.state.isAuthenticated = true;
1113
+ *
1114
+ * // No return value needed for middleware
1115
+ * };
1116
+ *
1117
+ * // Error handler
1118
+ * const errorHandler: HandlerCallback = async (ctx, error) => {
1119
+ * console.error('Error occurred:', error);
1120
+ *
1121
+ * ctx.response.setStatusCode(500);
1122
+ * return {
1123
+ * error: 'Internal server error',
1124
+ * message: process.env.NODE_ENV === 'production' ? 'Something went wrong' : error.message
1125
+ * };
1126
+ * };
1127
+ * ```
1128
+ *
1129
+ * @see {@link Context} for context interface details
1130
+ * @see {@link InternalHandlerCallbackGenerics} for custom typing options
161
1131
  */
162
1132
  export type HandlerCallback<T extends InternalHandlerCallbackGenerics = InternalHandlerCallbackGenerics> = (ctx: Context<T>, error?: unknown) => Promise<T["response"] | void> | T["response"] | void;
163
1133
  export type InternalGlobalHookOptions = {
@@ -181,39 +1151,406 @@ export interface InternalHookRegistryImpl {
181
1151
  _addOnError: (handler: HandlerCallback) => void;
182
1152
  _addOnNotFound: (handler: HandlerCallback) => void;
183
1153
  }
1154
+ /**
1155
+ * Internal route registry implementation for managing route storage and lookup.
1156
+ *
1157
+ * This interface provides the core functionality for registering routes and
1158
+ * finding them efficiently at runtime. It maintains separate collections for
1159
+ * exact routes and parameterized routes for optimal performance.
1160
+ *
1161
+ * ## Route Types
1162
+ *
1163
+ * - **Exact Routes**: Direct string matches (e.g., "/api/users")
1164
+ * - **Parameterized Routes**: Dynamic routes with parameters (e.g., "/users/:id")
1165
+ *
1166
+ * ## Performance Characteristics
1167
+ *
1168
+ * - **Exact Routes**: O(1) lookup time using Map
1169
+ * - **Parameterized Routes**: O(n) lookup time with pre-compiled regex patterns
1170
+ *
1171
+ * @example
1172
+ * ```typescript
1173
+ * // This interface is used internally by YinzerFlow
1174
+ * // Users typically don't interact with it directly
1175
+ *
1176
+ * // However, if you're extending the framework:
1177
+ * class CustomRouteRegistry implements InternalRouteRegistryImpl {
1178
+ * readonly _exactRoutes = new Map();
1179
+ * readonly _parameterizedRoutes = new Map();
1180
+ *
1181
+ * _register(route: InternalRouteRegistry) {
1182
+ * // Custom registration logic
1183
+ * }
1184
+ *
1185
+ * _findRoute(method: InternalHttpMethod, path: string) {
1186
+ * // Custom route finding logic
1187
+ * return undefined;
1188
+ * }
1189
+ * }
1190
+ * ```
1191
+ *
1192
+ * @see {@link InternalRouteRegistry} for individual route structure
1193
+ * @see {@link InternalPreCompiledRoute} for parameterized route details
1194
+ * @see {@link InternalHttpMethod} for available HTTP methods
1195
+ */
184
1196
  export interface InternalRouteRegistryImpl {
1197
+ /**
1198
+ * Map of exact route matches organized by HTTP method and path.
1199
+ *
1200
+ * Provides O(1) lookup for routes that have no dynamic parameters.
1201
+ *
1202
+ * @example
1203
+ * ```typescript
1204
+ * // Structure: Map<Method, Map<Path, Route>>
1205
+ * _exactRoutes = new Map([
1206
+ * ['GET', new Map([
1207
+ * ['/api/users', userListRoute],
1208
+ * ['/api/posts', postListRoute]
1209
+ * ])],
1210
+ * ['POST', new Map([
1211
+ * ['/api/users', createUserRoute]
1212
+ * ])]
1213
+ * ]);
1214
+ * ```
1215
+ */
185
1216
  readonly _exactRoutes: Map<InternalHttpMethod, Map<string, InternalRouteRegistry>>;
1217
+ /**
1218
+ * Array of parameterized routes with pre-compiled regex patterns.
1219
+ *
1220
+ * These routes contain dynamic parameters and require regex matching
1221
+ * at runtime. They're pre-compiled for performance.
1222
+ *
1223
+ * @example
1224
+ * ```typescript
1225
+ * // Structure: Array of pre-compiled routes
1226
+ * _parameterizedRoutes = [
1227
+ * {
1228
+ * pattern: /^\/users\/([^\/]+)$/,
1229
+ * paramNames: ['id'],
1230
+ * path: '/users/:id',
1231
+ * method: 'GET'
1232
+ * }
1233
+ * ];
1234
+ * ```
1235
+ */
186
1236
  readonly _parameterizedRoutes: Map<InternalHttpMethod, Array<InternalPreCompiledRoute>>;
1237
+ /**
1238
+ * Registers a new route in the appropriate collection.
1239
+ *
1240
+ * Routes are automatically categorized as exact or parameterized
1241
+ * based on whether they contain dynamic parameters.
1242
+ *
1243
+ * @param route - The route to register
1244
+ *
1245
+ * @example
1246
+ * ```typescript
1247
+ * _register({
1248
+ * path: '/api/users/:id',
1249
+ * method: 'GET',
1250
+ * handler: getUserHandler,
1251
+ * options: { beforeHooks: [authMiddleware] },
1252
+ * params: {}
1253
+ * });
1254
+ * ```
1255
+ */
187
1256
  _register: (route: InternalRouteRegistry) => void;
1257
+ /**
1258
+ * Finds a matching route for the given HTTP method and path.
1259
+ *
1260
+ * Searches exact routes first (O(1)), then parameterized routes (O(n))
1261
+ * until a match is found.
1262
+ *
1263
+ * @param method - The HTTP method to search for
1264
+ * @param path - The request path to match
1265
+ * @returns The matching route or undefined if no match found
1266
+ *
1267
+ * @example
1268
+ * ```typescript
1269
+ * const route = _findRoute('GET', '/api/users/123');
1270
+ * if (route) {
1271
+ * // Extract parameters from path
1272
+ * const params = extractParams(route, '/api/users/123');
1273
+ * // Execute route handler
1274
+ * await route.handler(context);
1275
+ * }
1276
+ * ```
1277
+ */
188
1278
  _findRoute: (method: InternalHttpMethod, path: string) => InternalRouteRegistry | undefined;
189
1279
  }
1280
+ /**
1281
+ * Configuration options for route registration.
1282
+ *
1283
+ * Defines hooks that will be executed before and after the route handler.
1284
+ * Both hooks are optional and can be used independently.
1285
+ *
1286
+ * @example
1287
+ * ```typescript
1288
+ * // Route with authentication middleware
1289
+ * const authRoute: InternalRouteRegistryOptions = {
1290
+ * beforeHooks: [
1291
+ * async (ctx) => {
1292
+ * const token = ctx.request.headers.authorization;
1293
+ * if (!token) throw new Error('Unauthorized');
1294
+ * ctx.state.user = await validateToken(token);
1295
+ * }
1296
+ * ]
1297
+ * };
1298
+ *
1299
+ * // Route with response logging
1300
+ * const loggingRoute: InternalRouteRegistryOptions = {
1301
+ * afterHooks: [
1302
+ * async (ctx, result) => {
1303
+ * console.log(`Response: ${JSON.stringify(result)}`);
1304
+ * }
1305
+ * ]
1306
+ * };
1307
+ *
1308
+ * // Route with both hooks
1309
+ * const fullRoute: InternalRouteRegistryOptions = {
1310
+ * beforeHooks: [authMiddleware],
1311
+ * afterHooks: [loggingMiddleware, metricsMiddleware]
1312
+ * };
1313
+ * ```
1314
+ *
1315
+ * @see {@link HandlerCallback} for hook function signature
1316
+ */
190
1317
  export interface InternalRouteRegistryOptions {
191
- beforeHooks: Array<HandlerCallback>;
192
- afterHooks: Array<HandlerCallback>;
1318
+ /**
1319
+ * Hooks to execute before the route handler.
1320
+ *
1321
+ * These hooks run in order and can modify the context or throw errors.
1322
+ * If any hook throws an error, the route handler is not executed.
1323
+ *
1324
+ * @example
1325
+ * ```typescript
1326
+ * beforeHooks: [
1327
+ * async (ctx) => {
1328
+ * // Authentication
1329
+ * ctx.state.user = await authenticate(ctx.request.headers.authorization);
1330
+ * },
1331
+ * async (ctx) => {
1332
+ * // Rate limiting
1333
+ * await checkRateLimit(ctx.request.ipAddress);
1334
+ * },
1335
+ * async (ctx) => {
1336
+ * // Request validation
1337
+ * validateRequest(ctx.request.body);
1338
+ * }
1339
+ * ]
1340
+ * ```
1341
+ */
1342
+ beforeHooks?: Array<HandlerCallback>;
1343
+ /**
1344
+ * Hooks to execute after the route handler.
1345
+ *
1346
+ * These hooks run in reverse order and can modify the response or context.
1347
+ * They always execute, even if the route handler throws an error.
1348
+ *
1349
+ * @example
1350
+ * ```typescript
1351
+ * afterHooks: [
1352
+ * async (ctx, result) => {
1353
+ * // Response logging
1354
+ * console.log(`Response: ${JSON.stringify(result)}`);
1355
+ * },
1356
+ * async (ctx) => {
1357
+ * // Add response headers
1358
+ * ctx.response.addHeaders({
1359
+ * 'X-Processing-Time': `${Date.now() - ctx.state.startTime}ms`
1360
+ * });
1361
+ * }
1362
+ * ]
1363
+ * ```
1364
+ */
1365
+ afterHooks?: Array<HandlerCallback>;
193
1366
  }
1367
+ /**
1368
+ * Individual route registration with all necessary metadata.
1369
+ *
1370
+ * Each route contains the path, method, handler, and configuration options.
1371
+ * Routes can be either exact matches or parameterized with dynamic segments.
1372
+ *
1373
+ * @example
1374
+ * ```typescript
1375
+ * const userRoute: InternalRouteRegistry = {
1376
+ * path: '/api/users/:id',
1377
+ * method: 'GET',
1378
+ * handler: async (ctx) => {
1379
+ * const { id } = ctx.request.params;
1380
+ * const user = await getUserById(id);
1381
+ * return user;
1382
+ * },
1383
+ * options: {
1384
+ * beforeHooks: [authMiddleware],
1385
+ * afterHooks: [loggingMiddleware]
1386
+ * },
1387
+ * params: { id: ':id' }
1388
+ * };
1389
+ * ```
1390
+ *
1391
+ * @see {@link InternalRouteRegistryOptions} for hook configuration
1392
+ * @see {@link HandlerCallback} for handler function signature
1393
+ * @see {@link InternalHttpMethod} for available HTTP methods
1394
+ */
194
1395
  export interface InternalRouteRegistry {
1396
+ /**
1397
+ * Optional path prefix for route groups.
1398
+ *
1399
+ * When routes are registered in groups, this contains the group prefix.
1400
+ * The full route path is constructed by combining prefix + path.
1401
+ *
1402
+ * @example
1403
+ * ```typescript
1404
+ * // For a route in group '/api/v1'
1405
+ * prefix: '/api/v1'
1406
+ * path: '/users'
1407
+ * // Full route: /api/v1/users
1408
+ * ```
1409
+ */
195
1410
  prefix?: string;
1411
+ /**
1412
+ * The route path pattern.
1413
+ *
1414
+ * Can contain static segments and dynamic parameters (e.g., ':id').
1415
+ *
1416
+ * @example
1417
+ * ```typescript
1418
+ * path: '/users/:id/posts/:postId'
1419
+ * // Matches: /users/123/posts/456
1420
+ * // Extracts: { id: '123', postId: '456' }
1421
+ * ```
1422
+ */
196
1423
  path: string;
1424
+ /**
1425
+ * HTTP method this route responds to.
1426
+ *
1427
+ * @example
1428
+ * ```typescript
1429
+ * method: 'POST' // Only responds to POST requests
1430
+ * ```
1431
+ */
197
1432
  method: InternalHttpMethod;
1433
+ /**
1434
+ * The function that handles requests to this route.
1435
+ *
1436
+ * Receives the request context and returns the response data.
1437
+ *
1438
+ * @example
1439
+ * ```typescript
1440
+ * handler: async (ctx) => {
1441
+ * const { id } = ctx.request.params;
1442
+ * const user = await getUserById(id);
1443
+ * return { user, timestamp: new Date() };
1444
+ * }
1445
+ * ```
1446
+ */
198
1447
  handler: HandlerCallback;
1448
+ /**
1449
+ * Configuration options including hooks and middleware.
1450
+ *
1451
+ * @example
1452
+ * ```typescript
1453
+ * options: {
1454
+ * beforeHooks: [authMiddleware, validationMiddleware],
1455
+ * afterHooks: [loggingMiddleware]
1456
+ * }
1457
+ * ```
1458
+ */
199
1459
  options: InternalRouteRegistryOptions;
1460
+ /**
1461
+ * Parameter placeholders extracted from the path.
1462
+ *
1463
+ * Maps parameter names to their placeholder values (e.g., ':id').
1464
+ *
1465
+ * @example
1466
+ * ```typescript
1467
+ * // For path '/users/:id/posts/:postId'
1468
+ * params: {
1469
+ * id: ':id',
1470
+ * postId: ':postId'
1471
+ * }
1472
+ * ```
1473
+ */
200
1474
  params: Record<string, string>;
201
1475
  }
202
1476
  /**
203
- * Pre-compiled route with regex pattern for efficient runtime matching
1477
+ * Pre-compiled route with regex pattern for efficient runtime matching.
204
1478
  *
205
1479
  * We compile route patterns into regexes at registration time (server startup)
206
1480
  * rather than at request time for performance reasons:
207
1481
  * - Registration: O(1) one-time cost per route
208
1482
  * - Runtime: O(1) for exact routes, O(n) for parameterized routes with pre-compiled regex
209
1483
  *
210
- * Example: "/users/:id/posts/:postId" becomes:
211
- * - pattern: /^\/users\/([^\/]+)\/posts\/([^\/]+)$/
212
- * - paramNames: ["id", "postId"]
1484
+ * @example
1485
+ * ```typescript
1486
+ * // Route: "/users/:id/posts/:postId"
1487
+ * const compiledRoute: InternalPreCompiledRoute = {
1488
+ * pattern: /^\/users\/([^\/]+)\/posts\/([^\/]+)$/,
1489
+ * paramNames: ["id", "postId"],
1490
+ * isParameterized: true,
1491
+ * path: "/users/:id/posts/:postId",
1492
+ * method: "GET",
1493
+ * handler: getUserPostsHandler,
1494
+ * options: { beforeHooks: [authMiddleware] },
1495
+ * params: { id: ":id", postId: ":postId" }
1496
+ * };
1497
+ *
1498
+ * // Runtime matching
1499
+ * const match = "/users/123/posts/456".match(compiledRoute.pattern);
1500
+ * if (match) {
1501
+ * const params = {
1502
+ * [compiledRoute.paramNames[0]]: match[1], // id: "123"
1503
+ * [compiledRoute.paramNames[1]]: match[2] // postId: "456"
1504
+ * };
1505
+ * }
1506
+ * ```
1507
+ *
1508
+ * @see {@link InternalRouteRegistry} for base route structure
213
1509
  */
214
1510
  export interface InternalPreCompiledRoute extends InternalRouteRegistry {
1511
+ /**
1512
+ * Pre-compiled regex pattern for matching this route.
1513
+ *
1514
+ * The regex is created once at registration time and reused
1515
+ * for all subsequent requests to this route.
1516
+ *
1517
+ * @example
1518
+ * ```typescript
1519
+ * // Path: "/users/:id"
1520
+ * pattern: /^\/users\/([^\/]+)$/
1521
+ *
1522
+ * // Path: "/posts/:category/:id"
1523
+ * pattern: /^\/posts\/([^\/]+)\/([^\/]+)$/
1524
+ * ```
1525
+ */
215
1526
  pattern: RegExp;
1527
+ /**
1528
+ * Names of parameters in the order they appear in the path.
1529
+ *
1530
+ * Used to map regex capture groups to parameter names.
1531
+ *
1532
+ * @example
1533
+ * ```typescript
1534
+ * // Path: "/users/:id/posts/:postId"
1535
+ * paramNames: ["id", "postId"]
1536
+ *
1537
+ * // Regex: /^\/users\/([^\/]+)\/posts\/([^\/]+)$/
1538
+ * // match[1] = "123" -> params.id = "123"
1539
+ * // match[2] = "456" -> params.postId = "456"
1540
+ * ```
1541
+ */
216
1542
  paramNames: Array<string>;
1543
+ /**
1544
+ * Flag indicating this route has dynamic parameters.
1545
+ *
1546
+ * Always true for InternalPreCompiledRoute since these are
1547
+ * specifically for parameterized routes.
1548
+ *
1549
+ * @example
1550
+ * ```typescript
1551
+ * isParameterized: true // Always true for this interface
1552
+ * ```
1553
+ */
217
1554
  isParameterized: boolean;
218
1555
  }
219
1556
  declare const logLevels: {
@@ -585,18 +1922,279 @@ export interface InternalServerConfiguration {
585
1922
  */
586
1923
  autoGracefulShutdown: boolean;
587
1924
  }
588
- export type RouteGroup = Record<Lowercase<InternalHttpMethod>, InternalSetupMethod>;
589
- export interface Setup {
590
- get: InternalSetupMethod;
591
- post: InternalSetupMethod;
592
- put: InternalSetupMethod;
593
- patch: InternalSetupMethod;
594
- delete: InternalSetupMethod;
595
- options: InternalSetupMethod;
596
- group: (prefix: string, callback: (group: RouteGroup) => void, options?: InternalRouteRegistryOptions) => void;
1925
+ export type HttpMethodHandlers = Record<Lowercase<keyof typeof httpMethod>, InternalSetupMethod>;
1926
+ export type RouteGroupMethod = (prefix: string, callback: (group: InternalGroupApp) => void, options?: InternalRouteRegistryOptions) => InternalGroupApp;
1927
+ export interface InternalGroupApp extends HttpMethodHandlers {
1928
+ readonly group: RouteGroupMethod;
1929
+ }
1930
+ /**
1931
+ * Main setup interface for configuring YinzerFlow routes, hooks, and middleware.
1932
+ *
1933
+ * The Setup interface provides methods for:
1934
+ * - **Route Registration**: HTTP method handlers (GET, POST, PUT, etc.)
1935
+ * - **Route Grouping**: Organized route prefixes with shared hooks
1936
+ * - **Global Hooks**: beforeAll/afterAll hooks that run for all routes
1937
+ * - **Error Handling**: Custom error and not-found handlers
1938
+ *
1939
+ * @example
1940
+ * ```typescript
1941
+ * import { YinzerFlow } from 'yinzerflow';
1942
+ *
1943
+ * const app = new YinzerFlow({ port: 3000 });
1944
+ *
1945
+ * // Register routes
1946
+ * app.get('/api/users', async (ctx) => {
1947
+ * return { users: ['John', 'Jane'] };
1948
+ * });
1949
+ *
1950
+ * app.post('/api/users', async (ctx) => {
1951
+ * const userData = ctx.request.body;
1952
+ * return { message: 'User created', data: userData };
1953
+ * });
1954
+ *
1955
+ * // Set up global hooks
1956
+ * app.beforeAll([
1957
+ * async (ctx) => {
1958
+ * ctx.state.requestId = generateRequestId();
1959
+ * ctx.state.timestamp = Date.now();
1960
+ * }
1961
+ * ]);
1962
+ *
1963
+ * app.afterAll([
1964
+ * async (ctx, result) => {
1965
+ * ctx.response.addHeaders({
1966
+ * 'X-Request-ID': ctx.state.requestId,
1967
+ * 'X-Processing-Time': `${Date.now() - ctx.state.timestamp}ms`
1968
+ * });
1969
+ * }
1970
+ * ]);
1971
+ *
1972
+ * // Create route groups
1973
+ * app.group('/api/v1', (api) => {
1974
+ * api.group('/admin', (admin) => {
1975
+ * admin.get('/users', async (ctx) => {
1976
+ * return { adminUsers: ['Admin1', 'Admin2'] };
1977
+ * });
1978
+ * });
1979
+ * });
1980
+ *
1981
+ * // Custom error handlers
1982
+ * app.onError(async (ctx, error) => {
1983
+ * ctx.response.setStatusCode(500);
1984
+ * return { error: 'Internal server error', message: error.message };
1985
+ * });
1986
+ *
1987
+ * app.onNotFound(async (ctx) => {
1988
+ * ctx.response.setStatusCode(404);
1989
+ * return { error: 'Not found', path: ctx.request.url };
1990
+ * });
1991
+ * ```
1992
+ *
1993
+ * @see {@link HttpMethodHandlers} for HTTP method registration methods
1994
+ * @see {@link RouteGroupMethod} for route group creation method
1995
+ * @see {@link InternalGlobalHookOptions} for global hook configuration options
1996
+ * @see {@link HandlerCallback} for route handler function signature
1997
+ */
1998
+ export interface Setup extends HttpMethodHandlers {
1999
+ /**
2000
+ * Creates a route group with a shared prefix and optional shared hooks.
2001
+ *
2002
+ * Route groups allow you to organize related routes under a common path
2003
+ * prefix and apply shared hooks to all routes within the group.
2004
+ *
2005
+ * @param prefix - The URL prefix for all routes in this group (e.g., '/api/v1')
2006
+ * @param callback - Function that receives the group instance for registering routes
2007
+ * @param options - Optional shared hooks and configuration for the group
2008
+ *
2009
+ * @example
2010
+ * ```typescript
2011
+ * // Basic route group
2012
+ * app.group('/api', (api) => {
2013
+ * api.get('/users', () => ({ users: [] }));
2014
+ * api.post('/users', () => ({ created: true }));
2015
+ * });
2016
+ *
2017
+ * // Route group with shared hooks
2018
+ * app.group('/api/v1', (api) => {
2019
+ * api.get('/users', () => ({ users: [] }));
2020
+ * api.post('/users', () => ({ created: true }));
2021
+ * }, {
2022
+ * beforeHooks: [
2023
+ * async (ctx) => {
2024
+ * ctx.state.apiVersion = 'v1';
2025
+ * ctx.state.requiresAuth = true;
2026
+ * }
2027
+ * ]
2028
+ * });
2029
+ *
2030
+ * // Nested route groups
2031
+ * app.group('/api/v1', (api) => {
2032
+ * api.group('/admin', (admin) => {
2033
+ * admin.get('/users', () => ({ adminUsers: [] }));
2034
+ * admin.get('/stats', () => ({ stats: {} }));
2035
+ * });
2036
+ * });
2037
+ * ```
2038
+ *
2039
+ * @see {@link RouteGroupMethod} for method signature details
2040
+ * @see {@link InternalGroupApp} for group instance interface
2041
+ */
2042
+ group: RouteGroupMethod;
2043
+ /**
2044
+ * Registers global hooks that run before all route handlers.
2045
+ *
2046
+ * These hooks execute for every request before any route-specific hooks
2047
+ * or the route handler itself. They're perfect for setting up global
2048
+ * state, authentication, logging, or request preprocessing.
2049
+ *
2050
+ * @param handlers - Array of hook functions to execute
2051
+ * @param options - Optional configuration for hook execution scope
2052
+ *
2053
+ * @example
2054
+ * ```typescript
2055
+ * // Global authentication hook
2056
+ * app.beforeAll([
2057
+ * async (ctx) => {
2058
+ * const token = ctx.request.headers.authorization;
2059
+ * if (token) {
2060
+ * const user = await validateToken(token);
2061
+ * ctx.state.user = user;
2062
+ * ctx.state.isAuthenticated = true;
2063
+ * }
2064
+ * }
2065
+ * ]);
2066
+ *
2067
+ * // Global logging hook
2068
+ * app.beforeAll([
2069
+ * async (ctx) => {
2070
+ * ctx.state.requestId = generateRequestId();
2071
+ * ctx.state.startTime = Date.now();
2072
+ *
2073
+ * console.log(`Request ${ctx.state.requestId} to ${ctx.request.url}`);
2074
+ * }
2075
+ * ], {
2076
+ * routesToExclude: ['/health', '/metrics'] // Skip logging for health checks
2077
+ * });
2078
+ * ```
2079
+ *
2080
+ * @see {@link InternalGlobalHookOptions} for hook configuration options
2081
+ * @see {@link HandlerCallback} for hook function signature
2082
+ */
597
2083
  beforeAll: (handlers: Array<HandlerCallback>, options?: InternalGlobalHookOptions) => void;
2084
+ /**
2085
+ * Registers global hooks that run after all route handlers.
2086
+ *
2087
+ * These hooks execute for every request after the route handler completes
2088
+ * and after any route-specific after hooks. They're perfect for response
2089
+ * modification, logging, cleanup, or adding response headers.
2090
+ *
2091
+ * @param handlers - Array of hook functions to execute
2092
+ * @param options - Optional configuration for hook execution scope
2093
+ *
2094
+ * @example
2095
+ * ```typescript
2096
+ * // Global response modification hook
2097
+ * app.afterAll([
2098
+ * async (ctx, result) => {
2099
+ * // Add response headers based on state
2100
+ * if (ctx.state.requestId) {
2101
+ * ctx.response.addHeaders({
2102
+ * 'X-Request-ID': ctx.state.requestId,
2103
+ * 'X-Processing-Time': `${Date.now() - ctx.state.startTime}ms`
2104
+ * });
2105
+ * }
2106
+ *
2107
+ * // Log response
2108
+ * console.log(`Request ${ctx.state.requestId} completed with status ${ctx.response.statusCode}`);
2109
+ * }
2110
+ * ]);
2111
+ *
2112
+ * // Global CORS hook
2113
+ * app.afterAll([
2114
+ * async (ctx) => {
2115
+ * ctx.response.addHeaders({
2116
+ * 'Access-Control-Allow-Origin': '*',
2117
+ * 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
2118
+ * 'Access-Control-Allow-Headers': 'Content-Type, Authorization'
2119
+ * });
2120
+ * }
2121
+ * ]);
2122
+ * ```
2123
+ *
2124
+ * @see {@link InternalGlobalHookOptions} for hook configuration options
2125
+ * @see {@link HandlerCallback} for hook function signature
2126
+ */
598
2127
  afterAll: (handlers: Array<HandlerCallback>, options?: InternalGlobalHookOptions) => void;
2128
+ /**
2129
+ * Registers a custom error handler for all routes.
2130
+ *
2131
+ * This handler is called when any route throws an error or returns
2132
+ * a rejected promise. It receives the context and the error object,
2133
+ * allowing you to customize error responses and logging.
2134
+ *
2135
+ * @param handler - Function to handle errors
2136
+ *
2137
+ * @example
2138
+ * ```typescript
2139
+ * // Custom error handler
2140
+ * app.onError(async (ctx, error) => {
2141
+ * // Log the error
2142
+ * console.error(`Error in ${ctx.request.method} ${ctx.request.url}:`, error);
2143
+ *
2144
+ * // Set appropriate status code
2145
+ * if (error.name === 'ValidationError') {
2146
+ * ctx.response.setStatusCode(400);
2147
+ * return { error: 'Validation failed', details: error.message };
2148
+ * }
2149
+ *
2150
+ * if (error.name === 'UnauthorizedError') {
2151
+ * ctx.response.setStatusCode(401);
2152
+ * return { error: 'Unauthorized', message: error.message };
2153
+ * }
2154
+ *
2155
+ * // Default error response
2156
+ * ctx.response.setStatusCode(500);
2157
+ * return {
2158
+ * error: 'Internal server error',
2159
+ * message: process.env.NODE_ENV === 'production' ? 'Something went wrong' : error.message
2160
+ * };
2161
+ * });
2162
+ * ```
2163
+ *
2164
+ * @see {@link HandlerCallback} for error handler function signature
2165
+ */
599
2166
  onError: (handler: HandlerCallback) => void;
2167
+ /**
2168
+ * Registers a custom not-found handler for unmatched routes.
2169
+ *
2170
+ * This handler is called when a request doesn't match any registered
2171
+ * route. It receives the context and allows you to customize the
2172
+ * 404 response.
2173
+ *
2174
+ * @param handler - Function to handle not-found requests
2175
+ *
2176
+ * @example
2177
+ * ```typescript
2178
+ * // Custom not-found handler
2179
+ * app.onNotFound(async (ctx) => {
2180
+ * ctx.response.setStatusCode(404);
2181
+ *
2182
+ * // Return helpful error message
2183
+ * return {
2184
+ * error: 'Not found',
2185
+ * message: `The requested resource '${ctx.request.url}' was not found`,
2186
+ * availableEndpoints: [
2187
+ * '/api/users',
2188
+ * '/api/posts',
2189
+ * '/health'
2190
+ * ],
2191
+ * documentation: 'https://api.example.com/docs'
2192
+ * };
2193
+ * });
2194
+ * ```
2195
+ *
2196
+ * @see {@link HandlerCallback} for not-found handler function signature
2197
+ */
600
2198
  onNotFound: (handler: HandlerCallback) => void;
601
2199
  }
602
2200
  export type InternalSetupMethod = (path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions) => void;
@@ -623,10 +2221,60 @@ declare class HookRegistryImpl implements InternalHookRegistryImpl {
623
2221
  _addOnNotFound(handler: HandlerCallback): void;
624
2222
  }
625
2223
  /**
626
- * User-facing configuration interface where all properties are optional
627
- * Users only need to specify what they want to override from defaults
2224
+ * User-facing configuration interface where all properties are optional.
2225
+ *
2226
+ * Users only need to specify what they want to override from defaults.
2227
+ * This is created by making the complete internal ServerConfigurationShape
2228
+ * partially optional using DeepPartial.
2229
+ *
2230
+ * ## Configuration Options
2231
+ *
2232
+ * - **Server Settings**: Port, host, and network configuration
2233
+ * - **Logging**: Log levels and custom logger configuration
2234
+ * - **CORS**: Cross-origin resource sharing settings
2235
+ * - **Performance**: Request handling and timeout settings
2236
+ * - **Security**: Headers and security-related options
2237
+ *
2238
+ * @example
2239
+ * ```typescript
2240
+ * import { YinzerFlow } from 'yinzerflow';
2241
+ *
2242
+ * // Minimal configuration - only specify what you need
2243
+ * const app = new YinzerFlow({ port: 3000 });
2244
+ *
2245
+ * // Basic configuration with logging
2246
+ * const app = new YinzerFlow({
2247
+ * port: 8080,
2248
+ * logLevel: 'info',
2249
+ * networkLogs: true
2250
+ * });
2251
+ *
2252
+ * // Full configuration example
2253
+ * const app = new YinzerFlow({
2254
+ * port: 9000,
2255
+ * host: '0.0.0.0',
2256
+ * logLevel: 'debug',
2257
+ * networkLogs: true,
2258
+ * autoGracefulShutdown: true,
2259
+ * cors: {
2260
+ * enabled: true,
2261
+ * origin: ['https://example.com', 'https://app.example.com'],
2262
+ * methods: ['GET', 'POST', 'PUT', 'DELETE'],
2263
+ * headers: ['Content-Type', 'Authorization'],
2264
+ * credentials: true
2265
+ * },
2266
+ * logger: {
2267
+ * info: (message, ...args) => console.log(`[APP] ${message}`, ...args),
2268
+ * warn: (message, ...args) => console.warn(`[APP] ${message}`, ...args),
2269
+ * error: (message, ...args) => console.error(`[APP] ${message}`, ...args),
2270
+ * debug: (message, ...args) => console.debug(`[APP] ${message}`, ...args)
2271
+ * }
2272
+ * });
2273
+ * ```
628
2274
  *
629
- * This is created by making the complete internal ServerConfigurationShape partially optional
2275
+ * @see {@link InternalServerConfiguration} for complete internal configuration
2276
+ * @see {@link DeepPartial} for how optional properties are created
2277
+ * @see {@link CorsConfiguration} for CORS configuration options
630
2278
  */
631
2279
  export type ServerConfiguration = DeepPartial<InternalServerConfiguration>;
632
2280
  declare class RouteRegistryImpl implements InternalRouteRegistryImpl {
@@ -651,7 +2299,7 @@ declare class SetupImpl implements InternalSetupImpl {
651
2299
  patch(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void;
652
2300
  delete(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void;
653
2301
  options(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void;
654
- group(prefix: string, callback: (group: Record<Lowercase<InternalHttpMethod>, InternalSetupMethod>) => void, options?: InternalRouteRegistryOptions): void;
2302
+ group(prefix: string, callback: (group: InternalGroupApp) => void, options?: InternalRouteRegistryOptions): InternalGroupApp;
655
2303
  beforeAll(handlers: Array<HandlerCallback<any>>, options?: InternalGlobalHookOptions): void;
656
2304
  afterAll(handlers: Array<HandlerCallback<any>>, options?: InternalGlobalHookOptions): void;
657
2305
  onError(handler: HandlerCallback<any>): void;