@stacksjs/bun-router 0.0.5 → 0.0.6
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/dist/auth.d.ts +134 -0
- package/dist/cache/lru-cache.d.ts +139 -0
- package/dist/cache/middleware-memoization.d.ts +151 -0
- package/dist/cache/route-cache-warmer.d.ts +161 -0
- package/dist/cache/sqlite-cache.d.ts +209 -0
- package/dist/cache/streaming-cache.d.ts +132 -0
- package/dist/cli/colors.d.ts +15 -0
- package/dist/cli/index.d.ts +10 -0
- package/dist/cli/middleware.d.ts +31 -0
- package/dist/cli/openapi.d.ts +17 -0
- package/dist/cli/router.d.ts +15 -0
- package/dist/cli/routes.d.ts +29 -0
- package/dist/cli/utils.d.ts +74 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/container/container.d.ts +273 -0
- package/dist/container/contextual-binding.d.ts +240 -0
- package/dist/container/decorators.d.ts +141 -0
- package/dist/container/service-provider.d.ts +285 -0
- package/dist/development/hot-reload.d.ts +187 -0
- package/dist/development/index.d.ts +167 -0
- package/dist/development/performance-profiler.d.ts +217 -0
- package/dist/development/route-debugger.d.ts +154 -0
- package/dist/development/route-inspector.d.ts +211 -0
- package/dist/development/typescript-utilities.d.ts +209 -0
- package/dist/docs.d.ts +11 -0
- package/dist/errors/circuit-breaker.d.ts +195 -0
- package/dist/errors/error-handler.d.ts +88 -0
- package/dist/errors/error-reporting.d.ts +144 -0
- package/dist/errors/exceptions.d.ts +184 -0
- package/dist/errors/graceful-degradation.d.ts +154 -0
- package/dist/errors/index.d.ts +9 -0
- package/dist/errors/router-errors.d.ts +110 -0
- package/dist/file-serving/static-files.d.ts +143 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +319 -205
- package/dist/middleware/auth.d.ts +63 -0
- package/dist/middleware/content_security_policy.d.ts +53 -0
- package/dist/middleware/cors.d.ts +5 -0
- package/dist/middleware/csrf.d.ts +7 -0
- package/dist/middleware/ddos_protection.d.ts +39 -0
- package/dist/middleware/file_security.d.ts +26 -0
- package/dist/middleware/file_upload.d.ts +38 -0
- package/dist/middleware/helmet.d.ts +54 -0
- package/dist/middleware/index.d.ts +44 -0
- package/dist/middleware/input_validation.d.ts +45 -0
- package/dist/middleware/json_body.d.ts +4 -0
- package/dist/middleware/performance_alerting.d.ts +87 -0
- package/dist/middleware/performance_dashboard.d.ts +87 -0
- package/dist/middleware/performance_monitor.d.ts +209 -0
- package/dist/middleware/pipeline.d.ts +131 -0
- package/dist/middleware/rate_limit.d.ts +36 -0
- package/dist/middleware/request_id.d.ts +4 -0
- package/dist/middleware/request_signing.d.ts +153 -0
- package/dist/middleware/request_tracer.d.ts +72 -0
- package/dist/middleware/response_cache.d.ts +97 -0
- package/dist/middleware/security.d.ts +69 -0
- package/dist/middleware/security_suite.d.ts +46 -0
- package/dist/middleware/session.d.ts +23 -0
- package/dist/model-binding/index.d.ts +2 -0
- package/dist/model-binding/model-middleware.d.ts +118 -0
- package/dist/model-binding/model-registry.d.ts +164 -0
- package/dist/model-binding.d.ts +185 -0
- package/dist/model-resolver-factory.d.ts +31 -0
- package/dist/observability/correlation.d.ts +187 -0
- package/dist/observability/health-checks.d.ts +186 -0
- package/dist/observability/index.d.ts +60 -0
- package/dist/observability/integration.d.ts +147 -0
- package/dist/observability/metrics.d.ts +184 -0
- package/dist/observability/tracing.d.ts +187 -0
- package/dist/optimization/bun-utilities.d.ts +224 -0
- package/dist/query-builder-integration.d.ts +36 -0
- package/dist/request/context.d.ts +34 -0
- package/dist/request/enhanced-request.d.ts +213 -0
- package/dist/request/macros.d.ts +300 -0
- package/dist/response/macros.d.ts +245 -0
- package/dist/response/response-factory.d.ts +133 -0
- package/dist/router/api-routes.d.ts +5 -0
- package/dist/router/file-based-routing.d.ts +118 -0
- package/dist/router/file-streaming.d.ts +5 -0
- package/dist/router/fluent-router.d.ts +315 -0
- package/dist/router/fluent-routing.d.ts +271 -0
- package/dist/router/group-organization.d.ts +5 -0
- package/dist/router/handler-resolver.d.ts +24 -0
- package/dist/router/http-methods.d.ts +10 -0
- package/dist/router/index.d.ts +113 -0
- package/dist/router/middleware-groups.d.ts +94 -0
- package/dist/router/middleware-integration.d.ts +112 -0
- package/dist/router/middleware.d.ts +5 -0
- package/dist/router/model-binding.d.ts +5 -0
- package/dist/router/optimized-route-matching.d.ts +6 -0
- package/dist/router/route-building.d.ts +5 -0
- package/dist/router/route-compiler.d.ts +153 -0
- package/dist/router/route-matching.d.ts +5 -0
- package/dist/router/route-trie.d.ts +114 -0
- package/dist/router/router.d.ts +276 -0
- package/dist/router/server.d.ts +5 -0
- package/dist/router/validation-integration.d.ts +170 -0
- package/dist/router/view-rendering.d.ts +5 -0
- package/dist/router/websocket.d.ts +5 -0
- package/dist/routing/route-caching.d.ts +129 -0
- package/dist/routing/route-throttling.d.ts +150 -0
- package/dist/routing/subdomain-routing.d.ts +206 -0
- package/dist/session/database-store.d.ts +51 -0
- package/dist/session/file-store.d.ts +22 -0
- package/dist/session/index.d.ts +106 -0
- package/dist/session/memory-store.d.ts +21 -0
- package/dist/session/redis-store.d.ts +31 -0
- package/dist/streaming/index.d.ts +3 -0
- package/dist/streaming/sse-handler.d.ts +138 -0
- package/dist/streaming/stream-handler.d.ts +114 -0
- package/dist/testing/auth-testing.d.ts +156 -0
- package/dist/testing/file-upload-testing.d.ts +190 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/middleware-testing.d.ts +138 -0
- package/dist/testing/model-binding-testing.d.ts +186 -0
- package/dist/testing/performance-testing.d.ts +235 -0
- package/dist/testing/test-client.d.ts +117 -0
- package/dist/testing/test-request.d.ts +85 -0
- package/dist/testing/test-response.d.ts +90 -0
- package/dist/testing/types.d.ts +207 -0
- package/dist/testing/websocket-testing.d.ts +227 -0
- package/dist/types/controller-types.d.ts +208 -0
- package/dist/types/core.d.ts +543 -0
- package/dist/types/middleware-types.d.ts +227 -0
- package/dist/types/request-response-augmentation.d.ts +261 -0
- package/dist/types/route-inference.d.ts +168 -0
- package/dist/types.d.ts +1776 -0
- package/dist/url.d.ts +56 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/query-preservation.d.ts +48 -0
- package/dist/utils.d.ts +69 -0
- package/dist/validation/validator.d.ts +140 -0
- package/dist/websocket/clustering.d.ts +185 -0
- package/package.json +3 -2
- package/src/auth.ts +469 -0
- package/src/cache/lru-cache.ts +457 -0
- package/src/cache/middleware-memoization.ts +531 -0
- package/src/cache/route-cache-warmer.ts +486 -0
- package/src/cache/sqlite-cache.ts +783 -0
- package/src/cache/streaming-cache.ts +572 -0
- package/src/cli/colors.ts +29 -0
- package/src/cli/index.ts +287 -0
- package/src/cli/middleware.ts +291 -0
- package/src/cli/openapi.ts +407 -0
- package/src/cli/router.ts +188 -0
- package/src/cli/routes.ts +265 -0
- package/src/cli/utils.ts +531 -0
- package/src/cli.ts +5 -0
- package/src/config.ts +322 -0
- package/src/container/container.ts +740 -0
- package/src/container/contextual-binding.ts +603 -0
- package/src/container/decorators.ts +359 -0
- package/src/container/service-provider.ts +596 -0
- package/src/development/hot-reload.ts +673 -0
- package/src/development/index.ts +499 -0
- package/src/development/performance-profiler.ts +717 -0
- package/src/development/route-debugger.ts +527 -0
- package/src/development/route-inspector.ts +749 -0
- package/src/development/typescript-utilities.ts +678 -0
- package/src/docs.ts +397 -0
- package/src/errors/circuit-breaker.ts +732 -0
- package/src/errors/error-handler.ts +569 -0
- package/src/errors/error-reporting.ts +672 -0
- package/src/errors/exceptions.ts +536 -0
- package/src/errors/graceful-degradation.ts +621 -0
- package/src/errors/index.ts +21 -0
- package/src/errors/router-errors.ts +632 -0
- package/src/file-serving/static-files.ts +581 -0
- package/src/index.ts +14 -0
- package/src/middleware/auth.ts +219 -0
- package/src/middleware/content_security_policy.ts +215 -0
- package/src/middleware/cors.ts +74 -0
- package/src/middleware/csrf.ts +108 -0
- package/src/middleware/ddos_protection.ts +255 -0
- package/src/middleware/file_security.ts +191 -0
- package/src/middleware/file_upload.ts +264 -0
- package/src/middleware/helmet.ts +268 -0
- package/src/middleware/index.ts +117 -0
- package/src/middleware/input_validation.ts +449 -0
- package/src/middleware/json_body.ts +37 -0
- package/src/middleware/performance_alerting.ts +538 -0
- package/src/middleware/performance_dashboard.ts +661 -0
- package/src/middleware/performance_monitor.ts +943 -0
- package/src/middleware/pipeline.ts +489 -0
- package/src/middleware/rate_limit.ts +245 -0
- package/src/middleware/request_id.ts +36 -0
- package/src/middleware/request_signing.ts +636 -0
- package/src/middleware/request_tracer.ts +636 -0
- package/src/middleware/response_cache.ts +743 -0
- package/src/middleware/security.ts +482 -0
- package/src/middleware/security_suite.ts +257 -0
- package/src/middleware/session.ts +91 -0
- package/src/model-binding/index.ts +18 -0
- package/src/model-binding/model-middleware.ts +425 -0
- package/src/model-binding/model-registry.ts +550 -0
- package/src/model-binding.ts +370 -0
- package/src/model-resolver-factory.ts +106 -0
- package/src/observability/correlation.ts +691 -0
- package/src/observability/health-checks.ts +729 -0
- package/src/observability/index.ts +184 -0
- package/src/observability/integration.ts +548 -0
- package/src/observability/metrics.ts +753 -0
- package/src/observability/tracing.ts +638 -0
- package/src/optimization/bun-utilities.ts +778 -0
- package/src/query-builder-integration.ts +137 -0
- package/src/request/context.ts +62 -0
- package/src/request/enhanced-request.ts +857 -0
- package/src/request/macros.ts +688 -0
- package/src/response/macros.ts +612 -0
- package/src/response/response-factory.ts +588 -0
- package/src/router/api-routes.ts +243 -0
- package/src/router/file-based-routing.ts +670 -0
- package/src/router/file-streaming.ts +372 -0
- package/src/router/fluent-router.ts +927 -0
- package/src/router/fluent-routing.ts +797 -0
- package/src/router/group-organization.ts +213 -0
- package/src/router/handler-resolver.ts +291 -0
- package/src/router/http-methods.ts +377 -0
- package/src/router/index.ts +185 -0
- package/src/router/middleware-groups.ts +222 -0
- package/src/router/middleware-integration.ts +399 -0
- package/src/router/middleware.ts +231 -0
- package/src/router/model-binding.ts +215 -0
- package/src/router/optimized-route-matching.ts +253 -0
- package/src/router/route-building.ts +212 -0
- package/src/router/route-compiler.ts +702 -0
- package/src/router/route-matching.ts +349 -0
- package/src/router/route-trie.ts +464 -0
- package/src/router/router.ts +1508 -0
- package/src/router/server.ts +406 -0
- package/src/router/validation-integration.ts +443 -0
- package/src/router/view-rendering.ts +233 -0
- package/src/router/websocket.ts +100 -0
- package/src/routing/route-caching.ts +402 -0
- package/src/routing/route-throttling.ts +469 -0
- package/src/routing/subdomain-routing.ts +492 -0
- package/src/session/database-store.ts +109 -0
- package/src/session/file-store.ts +148 -0
- package/src/session/index.ts +244 -0
- package/src/session/memory-store.ts +88 -0
- package/src/session/redis-store.ts +93 -0
- package/src/streaming/index.ts +17 -0
- package/src/streaming/sse-handler.ts +482 -0
- package/src/streaming/stream-handler.ts +552 -0
- package/src/testing/auth-testing.ts +446 -0
- package/src/testing/file-upload-testing.ts +543 -0
- package/src/testing/index.ts +10 -0
- package/src/testing/middleware-testing.ts +322 -0
- package/src/testing/model-binding-testing.ts +645 -0
- package/src/testing/performance-testing.ts +738 -0
- package/src/testing/test-client.ts +310 -0
- package/src/testing/test-request.ts +315 -0
- package/src/testing/test-response.ts +332 -0
- package/src/testing/types.ts +224 -0
- package/src/testing/websocket-testing.ts +590 -0
- package/src/types/controller-types.ts +385 -0
- package/src/types/core.ts +683 -0
- package/src/types/middleware-types.ts +419 -0
- package/src/types/request-response-augmentation.ts +489 -0
- package/src/types/route-inference.ts +357 -0
- package/src/types.ts +2049 -0
- package/src/url.ts +126 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/query-preservation.ts +201 -0
- package/src/utils.ts +327 -0
- package/src/validation/validator.ts +685 -0
- package/src/websocket/clustering.ts +762 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router Validation Integration
|
|
3
|
+
*
|
|
4
|
+
* Integration layer for validation and macros with the router.
|
|
5
|
+
* Provides RouteBuilder and FluentRouter for building routes with validation support.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { EnhancedRequest, MiddlewareHandler, NextFunction, RouteHandler } from '../types'
|
|
9
|
+
import type { ValidationRules, ValidatorConfig } from '../validation/validator'
|
|
10
|
+
import { RequestWithMacros } from '../request/macros'
|
|
11
|
+
import { ResponseWithMacros } from '../response/macros'
|
|
12
|
+
import { createValidationMiddleware } from '../validation/validator'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Route builder with validation support
|
|
16
|
+
*/
|
|
17
|
+
export class RouteBuilder {
|
|
18
|
+
private validationRules?: ValidationRules
|
|
19
|
+
private validatorConfig?: ValidatorConfig
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
private method: string,
|
|
23
|
+
private path: string,
|
|
24
|
+
private handler: RouteHandler,
|
|
25
|
+
) {}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Add validation to the route
|
|
29
|
+
*/
|
|
30
|
+
validate(rules: ValidationRules, config?: ValidatorConfig): this {
|
|
31
|
+
this.validationRules = rules
|
|
32
|
+
this.validatorConfig = config
|
|
33
|
+
return this
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Build the route with middleware
|
|
38
|
+
*/
|
|
39
|
+
build(): { method: string, path: string, handler: RouteHandler, middleware?: MiddlewareHandler[] } {
|
|
40
|
+
const middleware: MiddlewareHandler[] = []
|
|
41
|
+
|
|
42
|
+
// Add validation middleware if rules are provided
|
|
43
|
+
if (this.validationRules) {
|
|
44
|
+
middleware.push(createValidationMiddleware(this.validationRules, this.validatorConfig))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Add request macro middleware
|
|
48
|
+
middleware.push(async (req: EnhancedRequest, next: NextFunction) => {
|
|
49
|
+
// Apply request macros
|
|
50
|
+
RequestWithMacros.applyMacros(req)
|
|
51
|
+
const result = await next()
|
|
52
|
+
return result || new Response('Not Found', { status: 404 })
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
method: this.method,
|
|
57
|
+
path: this.path,
|
|
58
|
+
handler: this.handler,
|
|
59
|
+
middleware: middleware.length > 0 ? middleware : undefined,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Fluent router for enhanced routing functionality
|
|
66
|
+
*/
|
|
67
|
+
export class FluentRouter {
|
|
68
|
+
private routes: Array<{
|
|
69
|
+
method: string
|
|
70
|
+
path: string
|
|
71
|
+
handler: RouteHandler
|
|
72
|
+
middleware?: MiddlewareHandler[]
|
|
73
|
+
}> = []
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Create GET route
|
|
77
|
+
*/
|
|
78
|
+
get(path: string, handler: RouteHandler): RouteBuilder {
|
|
79
|
+
return new RouteBuilder('GET', path, handler)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Create POST route
|
|
84
|
+
*/
|
|
85
|
+
post(path: string, handler: RouteHandler): RouteBuilder {
|
|
86
|
+
return new RouteBuilder('POST', path, handler)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Create PUT route
|
|
91
|
+
*/
|
|
92
|
+
put(path: string, handler: RouteHandler): RouteBuilder {
|
|
93
|
+
return new RouteBuilder('PUT', path, handler)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Create PATCH route
|
|
98
|
+
*/
|
|
99
|
+
patch(path: string, handler: RouteHandler): RouteBuilder {
|
|
100
|
+
return new RouteBuilder('PATCH', path, handler)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create DELETE route
|
|
105
|
+
*/
|
|
106
|
+
delete(path: string, handler: RouteHandler): RouteBuilder {
|
|
107
|
+
return new RouteBuilder('DELETE', path, handler)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Register a built route
|
|
112
|
+
*/
|
|
113
|
+
register(routeBuilder: RouteBuilder): void {
|
|
114
|
+
const route = routeBuilder.build()
|
|
115
|
+
this.routes.push(route)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get all registered routes
|
|
120
|
+
*/
|
|
121
|
+
getRoutes(): Array<{
|
|
122
|
+
method: string
|
|
123
|
+
path: string
|
|
124
|
+
handler: RouteHandler
|
|
125
|
+
middleware?: MiddlewareHandler[]
|
|
126
|
+
}> {
|
|
127
|
+
return this.routes
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Router middleware factory for request/response enhancements
|
|
133
|
+
*/
|
|
134
|
+
export function createEnhancementMiddleware() {
|
|
135
|
+
return async (req: EnhancedRequest, next: () => Promise<Response>): Promise<Response> => {
|
|
136
|
+
// Apply request macros
|
|
137
|
+
RequestWithMacros.applyMacros(req)
|
|
138
|
+
|
|
139
|
+
// Add start time for age calculation
|
|
140
|
+
;(req as any).startTime = Date.now()
|
|
141
|
+
|
|
142
|
+
return await next()
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Validation middleware factory with fluent API
|
|
148
|
+
*/
|
|
149
|
+
export class ValidationMiddlewareBuilder {
|
|
150
|
+
private rules: ValidationRules = {}
|
|
151
|
+
private config: ValidatorConfig = {}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Add validation rule for field
|
|
155
|
+
*/
|
|
156
|
+
field(name: string, rules: string): this {
|
|
157
|
+
this.rules[name] = rules
|
|
158
|
+
return this
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Add multiple validation rules
|
|
163
|
+
*/
|
|
164
|
+
fields(rules: ValidationRules): this {
|
|
165
|
+
Object.assign(this.rules, rules)
|
|
166
|
+
return this
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Set validator configuration
|
|
171
|
+
*/
|
|
172
|
+
configure(config: ValidatorConfig): this {
|
|
173
|
+
Object.assign(this.config, config)
|
|
174
|
+
return this
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Stop on first validation failure
|
|
179
|
+
*/
|
|
180
|
+
stopOnFirstFailure(): this {
|
|
181
|
+
this.config.stopOnFirstFailure = true
|
|
182
|
+
return this
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Set custom error messages
|
|
187
|
+
*/
|
|
188
|
+
messages(messages: Record<string, string>): this {
|
|
189
|
+
this.config.customMessages = messages
|
|
190
|
+
return this
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Set custom field attributes
|
|
195
|
+
*/
|
|
196
|
+
attributes(attributes: Record<string, string>): this {
|
|
197
|
+
this.config.customAttributes = attributes
|
|
198
|
+
return this
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Build the validation middleware
|
|
203
|
+
*/
|
|
204
|
+
build(): MiddlewareHandler {
|
|
205
|
+
return createValidationMiddleware(this.rules, this.config)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Factory function to create validation middleware builder
|
|
211
|
+
*/
|
|
212
|
+
export function validate(): ValidationMiddlewareBuilder {
|
|
213
|
+
return new ValidationMiddlewareBuilder()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Resource handlers configuration */
|
|
217
|
+
export interface ResourceHandlers {
|
|
218
|
+
index?: RouteHandler
|
|
219
|
+
show?: RouteHandler
|
|
220
|
+
store?: RouteHandler
|
|
221
|
+
update?: RouteHandler
|
|
222
|
+
destroy?: RouteHandler
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Resource validation configuration */
|
|
226
|
+
export interface ResourceValidation {
|
|
227
|
+
store?: ValidationRules
|
|
228
|
+
update?: ValidationRules
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Route helper functions
|
|
233
|
+
*/
|
|
234
|
+
export const RouteHelpers: {
|
|
235
|
+
validated: (method: string, path: string, handler: RouteHandler, rules: ValidationRules, config?: ValidatorConfig) => RouteBuilder
|
|
236
|
+
apiRoute: (method: string, path: string, handler: RouteHandler, rules?: ValidationRules) => RouteBuilder
|
|
237
|
+
resourceRoute: (basePath: string, handlers: ResourceHandlers, validation?: ResourceValidation) => RouteBuilder[]
|
|
238
|
+
} = {
|
|
239
|
+
/**
|
|
240
|
+
* Create validated route
|
|
241
|
+
*/
|
|
242
|
+
validated: (
|
|
243
|
+
method: string,
|
|
244
|
+
path: string,
|
|
245
|
+
handler: RouteHandler,
|
|
246
|
+
rules: ValidationRules,
|
|
247
|
+
config?: ValidatorConfig,
|
|
248
|
+
): RouteBuilder => {
|
|
249
|
+
const builder = new RouteBuilder(method, path, handler)
|
|
250
|
+
return builder.validate(rules, config)
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Create API route with JSON validation
|
|
255
|
+
*/
|
|
256
|
+
apiRoute: (
|
|
257
|
+
method: string,
|
|
258
|
+
path: string,
|
|
259
|
+
handler: RouteHandler,
|
|
260
|
+
rules?: ValidationRules,
|
|
261
|
+
): RouteBuilder => {
|
|
262
|
+
const builder = new RouteBuilder(method, path, async (req): Promise<Response> => {
|
|
263
|
+
// Ensure request expects JSON
|
|
264
|
+
if (!req.expectsJson()) {
|
|
265
|
+
return ResponseWithMacros.callMacro('error', 'API endpoint requires JSON Accept header', undefined, 406)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return await handler(req)
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
if (rules) {
|
|
272
|
+
builder.validate(rules)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return builder
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Create resource route with validation
|
|
280
|
+
*/
|
|
281
|
+
resourceRoute: (
|
|
282
|
+
basePath: string,
|
|
283
|
+
handlers: ResourceHandlers,
|
|
284
|
+
validation?: ResourceValidation,
|
|
285
|
+
): RouteBuilder[] => {
|
|
286
|
+
const routes: RouteBuilder[] = []
|
|
287
|
+
|
|
288
|
+
if (handlers.index) {
|
|
289
|
+
routes.push(new RouteBuilder('GET', basePath, handlers.index))
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (handlers.show) {
|
|
293
|
+
routes.push(new RouteBuilder('GET', `${basePath}/{id}`, handlers.show))
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (handlers.store) {
|
|
297
|
+
const builder = new RouteBuilder('POST', basePath, handlers.store)
|
|
298
|
+
if (validation?.store) {
|
|
299
|
+
builder.validate(validation.store)
|
|
300
|
+
}
|
|
301
|
+
routes.push(builder)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (handlers.update) {
|
|
305
|
+
const builder = new RouteBuilder('PUT', `${basePath}/{id}`, handlers.update)
|
|
306
|
+
if (validation?.update) {
|
|
307
|
+
builder.validate(validation.update)
|
|
308
|
+
}
|
|
309
|
+
routes.push(builder)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (handlers.destroy) {
|
|
313
|
+
routes.push(new RouteBuilder('DELETE', `${basePath}/{id}`, handlers.destroy))
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return routes
|
|
317
|
+
},
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Fluent router factory
|
|
322
|
+
*/
|
|
323
|
+
export function createFluentRouter(): FluentRouter {
|
|
324
|
+
return new FluentRouter()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Middleware composition helpers
|
|
329
|
+
*/
|
|
330
|
+
export const MiddlewareHelpers = {
|
|
331
|
+
/**
|
|
332
|
+
* Compose multiple middleware into one
|
|
333
|
+
*/
|
|
334
|
+
compose: (...middleware: MiddlewareHandler[]): MiddlewareHandler => {
|
|
335
|
+
return async (req: EnhancedRequest, next: NextFunction): Promise<Response | null> => {
|
|
336
|
+
let index = 0
|
|
337
|
+
|
|
338
|
+
const dispatch = async (): Promise<Response | null> => {
|
|
339
|
+
if (index >= middleware.length) {
|
|
340
|
+
return await next()
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const currentMiddleware = middleware[index++]
|
|
344
|
+
return await currentMiddleware(req, dispatch)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return await dispatch()
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Create conditional middleware
|
|
353
|
+
*/
|
|
354
|
+
when: (
|
|
355
|
+
condition: (req: EnhancedRequest) => boolean,
|
|
356
|
+
middleware: MiddlewareHandler,
|
|
357
|
+
): MiddlewareHandler => {
|
|
358
|
+
return async (req: EnhancedRequest, next: NextFunction): Promise<Response | null> => {
|
|
359
|
+
if (condition(req)) {
|
|
360
|
+
return await middleware(req, next)
|
|
361
|
+
}
|
|
362
|
+
return await next()
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Create middleware that runs unless condition is met
|
|
368
|
+
*/
|
|
369
|
+
unless: (
|
|
370
|
+
condition: (req: EnhancedRequest) => boolean,
|
|
371
|
+
middleware: MiddlewareHandler,
|
|
372
|
+
): MiddlewareHandler => {
|
|
373
|
+
return MiddlewareHelpers.when(req => !condition(req), middleware)
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Create middleware group
|
|
378
|
+
*/
|
|
379
|
+
group: (middleware: MiddlewareHandler[]): MiddlewareHandler => {
|
|
380
|
+
return MiddlewareHelpers.compose(...middleware)
|
|
381
|
+
},
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/** Enhancement middleware type */
|
|
385
|
+
export type EnhancementMiddleware = (req: EnhancedRequest, next: () => Promise<Response>) => Promise<Response>
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Request/Response enhancement presets
|
|
389
|
+
*/
|
|
390
|
+
export const EnhancementPresets: {
|
|
391
|
+
api: () => EnhancementMiddleware[]
|
|
392
|
+
web: () => EnhancementMiddleware[]
|
|
393
|
+
validation: (rules: ValidationRules, config?: ValidatorConfig) => EnhancementMiddleware[]
|
|
394
|
+
} = {
|
|
395
|
+
/**
|
|
396
|
+
* API preset with JSON validation and response macros
|
|
397
|
+
*/
|
|
398
|
+
api: (): EnhancementMiddleware[] => [
|
|
399
|
+
createEnhancementMiddleware(),
|
|
400
|
+
async (req: EnhancedRequest, next: () => Promise<Response>): Promise<Response> => {
|
|
401
|
+
try {
|
|
402
|
+
const response = await next()
|
|
403
|
+
|
|
404
|
+
// Add API headers
|
|
405
|
+
const headers = new Headers(response.headers)
|
|
406
|
+
headers.set('X-API-Version', '1.0')
|
|
407
|
+
headers.set('X-Response-Time', new Date().toISOString())
|
|
408
|
+
|
|
409
|
+
return new Response(response.body, {
|
|
410
|
+
status: response.status,
|
|
411
|
+
statusText: response.statusText,
|
|
412
|
+
headers,
|
|
413
|
+
})
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
console.error(error)
|
|
417
|
+
// Return JSON error response
|
|
418
|
+
return ResponseWithMacros.callMacro('error', 'Internal server error', undefined, 500)
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
],
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Web preset with request enhancements and CSRF protection
|
|
425
|
+
*/
|
|
426
|
+
web: (): EnhancementMiddleware[] => [
|
|
427
|
+
createEnhancementMiddleware(),
|
|
428
|
+
async (req: EnhancedRequest, next: () => Promise<Response>): Promise<Response> => {
|
|
429
|
+
// Add web-specific enhancements
|
|
430
|
+
;(req as any).session = {} // Placeholder for session
|
|
431
|
+
|
|
432
|
+
return await next()
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Validation preset with comprehensive validation
|
|
438
|
+
*/
|
|
439
|
+
validation: (rules: ValidationRules, config?: ValidatorConfig): EnhancementMiddleware[] => [
|
|
440
|
+
createEnhancementMiddleware(),
|
|
441
|
+
createValidationMiddleware(rules, config) as EnhancementMiddleware,
|
|
442
|
+
],
|
|
443
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import type { ActionHandler, EnhancedRequest, RouterConfig } from '../types'
|
|
2
|
+
import type { Router } from './router'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import { processHtmlTemplate, resolveViewPath, injectQueryPreservationScript } from '../utils'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* View rendering extension for Router class
|
|
8
|
+
*/
|
|
9
|
+
export function registerViewRendering(RouterClass: typeof Router): void {
|
|
10
|
+
Object.defineProperties(RouterClass.prototype, {
|
|
11
|
+
/**
|
|
12
|
+
* Render a view with data
|
|
13
|
+
*/
|
|
14
|
+
renderView: {
|
|
15
|
+
async value(
|
|
16
|
+
view: string,
|
|
17
|
+
data: Record<string, any> = {},
|
|
18
|
+
options: { layout?: string } = {},
|
|
19
|
+
): Promise<string> {
|
|
20
|
+
const viewsConfig = this.config.views || {
|
|
21
|
+
viewsPath: 'resources/views',
|
|
22
|
+
extensions: ['.html', '.stx'],
|
|
23
|
+
cache: Bun.env.NODE_ENV === 'production',
|
|
24
|
+
engine: 'auto',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Check cache first if enabled
|
|
28
|
+
const cacheKey = `${view}:${options.layout || ''}`
|
|
29
|
+
if (viewsConfig.cache && this.templateCache.has(cacheKey)) {
|
|
30
|
+
return this.processTemplate(this.templateCache.get(cacheKey)!, data, viewsConfig)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Resolve the view path
|
|
34
|
+
const viewPath = await resolveViewPath(view, viewsConfig.viewsPath, viewsConfig.extensions)
|
|
35
|
+
if (!viewPath) {
|
|
36
|
+
throw new Error(`View '${view}' not found`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Read the view file
|
|
40
|
+
let viewContent = await Bun.file(viewPath).text()
|
|
41
|
+
|
|
42
|
+
// Apply layout if specified or if a default layout is configured
|
|
43
|
+
const layoutName = options.layout || viewsConfig.defaultLayout
|
|
44
|
+
if (layoutName) {
|
|
45
|
+
const layoutPath = await resolveViewPath(
|
|
46
|
+
join('layouts', layoutName),
|
|
47
|
+
viewsConfig.viewsPath,
|
|
48
|
+
viewsConfig.extensions,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if (!layoutPath) {
|
|
52
|
+
throw new Error(`Layout '${layoutName}' not found`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const layoutContent = await Bun.file(layoutPath).text()
|
|
56
|
+
// Replace {{content}} placeholder with the view content
|
|
57
|
+
viewContent = layoutContent.replace(/\{\{\s*content\s*\}\}/g, viewContent)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Cache the template if caching is enabled
|
|
61
|
+
if (viewsConfig.cache) {
|
|
62
|
+
this.templateCache.set(cacheKey, viewContent)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Process the template with data
|
|
66
|
+
return this.processTemplate(viewContent, data, viewsConfig)
|
|
67
|
+
},
|
|
68
|
+
writable: true,
|
|
69
|
+
configurable: true,
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Process a template with data
|
|
74
|
+
*/
|
|
75
|
+
processTemplate: {
|
|
76
|
+
async value(
|
|
77
|
+
template: string,
|
|
78
|
+
data: Record<string, any>,
|
|
79
|
+
viewConfig: RouterConfig['views'],
|
|
80
|
+
): Promise<string> {
|
|
81
|
+
// Choose template engine based on configuration
|
|
82
|
+
const engine = viewConfig?.engine || 'auto'
|
|
83
|
+
|
|
84
|
+
if (engine === 'custom' && viewConfig?.customRenderer) {
|
|
85
|
+
return viewConfig.customRenderer(template, data, {})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Default to simple template processing
|
|
89
|
+
let processedTemplate = processHtmlTemplate(template, data)
|
|
90
|
+
|
|
91
|
+
// Apply minification if configured
|
|
92
|
+
if (viewConfig?.minify?.enabled) {
|
|
93
|
+
try {
|
|
94
|
+
const _minifyOptions = viewConfig.minify.options || {
|
|
95
|
+
removeComments: true,
|
|
96
|
+
collapseWhitespace: true,
|
|
97
|
+
minifyJS: true,
|
|
98
|
+
minifyCSS: true,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// In a real implementation, you would use a library like html-minifier
|
|
102
|
+
// This is just a placeholder
|
|
103
|
+
processedTemplate = processedTemplate
|
|
104
|
+
.replace(/\s+/g, ' ')
|
|
105
|
+
.replace(/<!--[\s\S]*?-->/g, '')
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
console.error('Error minifying HTML:', err)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return processedTemplate
|
|
113
|
+
},
|
|
114
|
+
writable: true,
|
|
115
|
+
configurable: true,
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Register a view route
|
|
120
|
+
*/
|
|
121
|
+
view: {
|
|
122
|
+
value(
|
|
123
|
+
path: string,
|
|
124
|
+
viewOrData: string | Record<string, any>,
|
|
125
|
+
dataOrOptions: Record<string, any> | { layout?: string, status?: number, headers?: Record<string, string> } = {},
|
|
126
|
+
optionsOrType: { layout?: string, status?: number, headers?: Record<string, string> } | 'web' | 'api' = {},
|
|
127
|
+
typeOrName: 'web' | 'api' | string = 'web',
|
|
128
|
+
name?: string,
|
|
129
|
+
): Router {
|
|
130
|
+
let view: string
|
|
131
|
+
let data: Record<string, any> = {}
|
|
132
|
+
let options: { layout?: string, status?: number, headers?: Record<string, string> } = {}
|
|
133
|
+
let type: 'web' | 'api' = 'web'
|
|
134
|
+
let routeName: string | undefined
|
|
135
|
+
|
|
136
|
+
// Parse the flexible parameters
|
|
137
|
+
if (typeof viewOrData === 'string') {
|
|
138
|
+
view = viewOrData
|
|
139
|
+
|
|
140
|
+
if (typeof dataOrOptions === 'object' && !Array.isArray(dataOrOptions)) {
|
|
141
|
+
// If dataOrOptions has layout, status, or headers, it's options
|
|
142
|
+
if ('layout' in dataOrOptions || 'status' in dataOrOptions || 'headers' in dataOrOptions) {
|
|
143
|
+
options = dataOrOptions as { layout?: string, status?: number, headers?: Record<string, string> }
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// Otherwise it's data
|
|
147
|
+
data = dataOrOptions
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (typeof optionsOrType === 'object') {
|
|
152
|
+
options = { ...options, ...optionsOrType }
|
|
153
|
+
}
|
|
154
|
+
else if (typeof optionsOrType === 'string') {
|
|
155
|
+
type = optionsOrType as 'web' | 'api'
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (typeof typeOrName === 'string') {
|
|
159
|
+
if (typeOrName === 'web' || typeOrName === 'api') {
|
|
160
|
+
type = typeOrName
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
routeName = typeOrName
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (name) {
|
|
168
|
+
routeName = name
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// viewOrData is actually data
|
|
173
|
+
data = viewOrData
|
|
174
|
+
|
|
175
|
+
if (typeof dataOrOptions === 'string') {
|
|
176
|
+
view = dataOrOptions
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
throw new TypeError('View name must be a string')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (typeof optionsOrType === 'object') {
|
|
183
|
+
options = optionsOrType
|
|
184
|
+
}
|
|
185
|
+
else if (typeof optionsOrType === 'string') {
|
|
186
|
+
if (optionsOrType === 'web' || optionsOrType === 'api') {
|
|
187
|
+
type = optionsOrType
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
view = optionsOrType
|
|
191
|
+
type = 'web'
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Create the route handler
|
|
197
|
+
const handler: ActionHandler = async (_req: EnhancedRequest) => {
|
|
198
|
+
let renderedView = await this.renderView(view, data, {
|
|
199
|
+
layout: options.layout,
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
// Inject query preservation script if configured
|
|
203
|
+
const queryPreservationConfig = this.config?.queryPreservation
|
|
204
|
+
if (queryPreservationConfig?.enabled !== false && queryPreservationConfig?.preserve?.length) {
|
|
205
|
+
renderedView = injectQueryPreservationScript(renderedView, queryPreservationConfig)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const headers = new Headers()
|
|
209
|
+
headers.set('Content-Type', 'text/html; charset=utf-8')
|
|
210
|
+
|
|
211
|
+
// Add custom headers if provided
|
|
212
|
+
if (options.headers) {
|
|
213
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
214
|
+
headers.set(key, value)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return new Response(renderedView, {
|
|
219
|
+
status: options.status || 200,
|
|
220
|
+
headers,
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Register the route
|
|
225
|
+
this.get(path, handler, type, routeName)
|
|
226
|
+
|
|
227
|
+
return this
|
|
228
|
+
},
|
|
229
|
+
writable: true,
|
|
230
|
+
configurable: true,
|
|
231
|
+
},
|
|
232
|
+
})
|
|
233
|
+
}
|