@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
package/dist/index.js
CHANGED
|
@@ -2763,6 +2763,27 @@ var RouterErrorFactory = {
|
|
|
2763
2763
|
handlerResolution: (ref, reason, context) => new HandlerResolutionException(ref, reason, context),
|
|
2764
2764
|
modelBinding: (model, param, value, reason, context) => new ModelBindingException(model, param, value, reason, context)
|
|
2765
2765
|
};
|
|
2766
|
+
// src/request/context.ts
|
|
2767
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
2768
|
+
var storage = new AsyncLocalStorage;
|
|
2769
|
+
function runWithRequest(initial, fn) {
|
|
2770
|
+
return storage.run({ current: initial }, fn);
|
|
2771
|
+
}
|
|
2772
|
+
function setCurrentRequest(req) {
|
|
2773
|
+
const store = storage.getStore();
|
|
2774
|
+
if (store)
|
|
2775
|
+
store.current = req;
|
|
2776
|
+
}
|
|
2777
|
+
function getCurrentRequest() {
|
|
2778
|
+
return storage.getStore()?.current ?? undefined;
|
|
2779
|
+
}
|
|
2780
|
+
function request() {
|
|
2781
|
+
const req = getCurrentRequest();
|
|
2782
|
+
if (!req) {
|
|
2783
|
+
throw new Error("request() called outside a request scope. It can only be invoked from within a route handler or middleware.");
|
|
2784
|
+
}
|
|
2785
|
+
return req;
|
|
2786
|
+
}
|
|
2766
2787
|
// src/response/response-factory.ts
|
|
2767
2788
|
var response = {
|
|
2768
2789
|
json: (data, options = {}) => {
|
|
@@ -2858,8 +2879,8 @@ var response = {
|
|
|
2858
2879
|
redirectTemporary: (url) => {
|
|
2859
2880
|
return response.redirect(url, 302);
|
|
2860
2881
|
},
|
|
2861
|
-
back: (
|
|
2862
|
-
const referer =
|
|
2882
|
+
back: (request2, fallback = "/") => {
|
|
2883
|
+
const referer = request2.headers.get("referer") || fallback;
|
|
2863
2884
|
return response.redirect(referer);
|
|
2864
2885
|
},
|
|
2865
2886
|
view: (html, status = 200, headers = {}) => {
|
|
@@ -3104,7 +3125,7 @@ class MiddlewarePipeline {
|
|
|
3104
3125
|
}
|
|
3105
3126
|
this.compiledPipelines.set(routeKey, compiled);
|
|
3106
3127
|
}
|
|
3107
|
-
async execute(routeKey,
|
|
3128
|
+
async execute(routeKey, request2, finalHandler) {
|
|
3108
3129
|
const startTime = performance.now();
|
|
3109
3130
|
this.stats.totalExecutions++;
|
|
3110
3131
|
const pipeline = this.compiledPipelines.get(routeKey);
|
|
@@ -3114,7 +3135,7 @@ class MiddlewarePipeline {
|
|
|
3114
3135
|
}
|
|
3115
3136
|
this.stats.cacheHits++;
|
|
3116
3137
|
const context = {
|
|
3117
|
-
request,
|
|
3138
|
+
request: request2,
|
|
3118
3139
|
dependencies: new Map,
|
|
3119
3140
|
metadata: {}
|
|
3120
3141
|
};
|
|
@@ -3129,7 +3150,7 @@ class MiddlewarePipeline {
|
|
|
3129
3150
|
this.stats.skippedMiddleware++;
|
|
3130
3151
|
return await next();
|
|
3131
3152
|
}
|
|
3132
|
-
const result2 = await middleware2.handler(
|
|
3153
|
+
const result2 = await middleware2.handler(request2, next);
|
|
3133
3154
|
if (result2 instanceof Response && middleware2.canShortCircuit) {
|
|
3134
3155
|
this.stats.shortCircuits++;
|
|
3135
3156
|
return result2;
|
|
@@ -4193,12 +4214,12 @@ class StreamHandler {
|
|
|
4193
4214
|
}
|
|
4194
4215
|
}
|
|
4195
4216
|
class FileStreamHandler {
|
|
4196
|
-
static async streamFile(filePath,
|
|
4217
|
+
static async streamFile(filePath, request2, options = {}) {
|
|
4197
4218
|
const { contentType, enableRanges = true, chunkSize: _chunkSize = 64 * 1024 } = options;
|
|
4198
4219
|
try {
|
|
4199
4220
|
const file = Bun.file(filePath);
|
|
4200
4221
|
const fileSize = file.size;
|
|
4201
|
-
const range = enableRanges ?
|
|
4222
|
+
const range = enableRanges ? request2.headers.get("range") : null;
|
|
4202
4223
|
if (range && enableRanges) {
|
|
4203
4224
|
return FileStreamHandler.handleRangeRequest(file, range, contentType);
|
|
4204
4225
|
}
|
|
@@ -4272,8 +4293,8 @@ class FileStreamHandler {
|
|
|
4272
4293
|
function registerFileStreaming(RouterClass) {
|
|
4273
4294
|
Object.defineProperties(RouterClass.prototype, {
|
|
4274
4295
|
streamFile: {
|
|
4275
|
-
value(filePath,
|
|
4276
|
-
return FileStreamHandler.streamFile(filePath,
|
|
4296
|
+
value(filePath, request2, options) {
|
|
4297
|
+
return FileStreamHandler.streamFile(filePath, request2, options);
|
|
4277
4298
|
},
|
|
4278
4299
|
writable: true,
|
|
4279
4300
|
configurable: true
|
|
@@ -5627,6 +5648,60 @@ var SubdomainPatterns = {
|
|
|
5627
5648
|
environment: (domain) => `{env}.${domain}`
|
|
5628
5649
|
};
|
|
5629
5650
|
|
|
5651
|
+
// src/url.ts
|
|
5652
|
+
import process3 from "process";
|
|
5653
|
+
var namedRouteRegistry = new Map;
|
|
5654
|
+
function registerNamedRoute(name, path) {
|
|
5655
|
+
namedRouteRegistry.set(name, path);
|
|
5656
|
+
}
|
|
5657
|
+
function getNamedRoutePath(name) {
|
|
5658
|
+
return namedRouteRegistry.get(name);
|
|
5659
|
+
}
|
|
5660
|
+
function getNamedRoutes() {
|
|
5661
|
+
return new Map(namedRouteRegistry);
|
|
5662
|
+
}
|
|
5663
|
+
function clearNamedRoutes() {
|
|
5664
|
+
namedRouteRegistry.clear();
|
|
5665
|
+
}
|
|
5666
|
+
function url(name, params = {}, options = {}) {
|
|
5667
|
+
const path = namedRouteRegistry.get(name);
|
|
5668
|
+
if (!path) {
|
|
5669
|
+
const known = [...namedRouteRegistry.keys()].sort().join(", ") || "(none registered)";
|
|
5670
|
+
throw new Error(`Named route '${name}' is not defined. Known routes: ${known}`);
|
|
5671
|
+
}
|
|
5672
|
+
const consumed = new Set;
|
|
5673
|
+
let resolved = path;
|
|
5674
|
+
for (const [key, value] of Object.entries(params)) {
|
|
5675
|
+
const required = `{${key}}`;
|
|
5676
|
+
const optional = `{${key}?}`;
|
|
5677
|
+
const encoded = encodeURIComponent(String(value));
|
|
5678
|
+
if (resolved.includes(required)) {
|
|
5679
|
+
resolved = resolved.replaceAll(required, encoded);
|
|
5680
|
+
consumed.add(key);
|
|
5681
|
+
} else if (resolved.includes(optional)) {
|
|
5682
|
+
resolved = resolved.replaceAll(optional, encoded);
|
|
5683
|
+
consumed.add(key);
|
|
5684
|
+
}
|
|
5685
|
+
}
|
|
5686
|
+
resolved = resolved.replace(/\/\{[^}]+\?\}/g, "");
|
|
5687
|
+
const missing = resolved.match(/\{([^}?]+)\}/g);
|
|
5688
|
+
if (missing && missing.length > 0)
|
|
5689
|
+
throw new Error(`Missing required params for route '${name}': ${missing.join(", ")}`);
|
|
5690
|
+
const leftover = Object.entries(params).filter(([k]) => !consumed.has(k));
|
|
5691
|
+
if (leftover.length > 0) {
|
|
5692
|
+
const qs = new URLSearchParams;
|
|
5693
|
+
for (const [k, v] of leftover)
|
|
5694
|
+
qs.set(k, String(v));
|
|
5695
|
+
resolved += `?${qs.toString()}`;
|
|
5696
|
+
}
|
|
5697
|
+
if (options.absolute) {
|
|
5698
|
+
const base = (options.base ?? process3.env.APP_URL ?? "http://localhost").replace(/\/$/, "");
|
|
5699
|
+
const prefixed = base.startsWith("http") ? base : `https://${base}`;
|
|
5700
|
+
return `${prefixed}${resolved.startsWith("/") ? resolved : `/${resolved}`}`;
|
|
5701
|
+
}
|
|
5702
|
+
return resolved;
|
|
5703
|
+
}
|
|
5704
|
+
|
|
5630
5705
|
// src/router/fluent-routing.ts
|
|
5631
5706
|
class FluentRouteBuilder {
|
|
5632
5707
|
method;
|
|
@@ -5880,6 +5955,8 @@ class FluentRouter {
|
|
|
5880
5955
|
register(builder) {
|
|
5881
5956
|
const route = builder.build();
|
|
5882
5957
|
this.routes.push(route);
|
|
5958
|
+
if (route.name)
|
|
5959
|
+
registerNamedRoute(route.name, route.path);
|
|
5883
5960
|
return this;
|
|
5884
5961
|
}
|
|
5885
5962
|
cached(tags = [], config2) {
|
|
@@ -5920,6 +5997,8 @@ class FluentRouter {
|
|
|
5920
5997
|
this.subdomainRouter.addDomainGroup(domainGroup);
|
|
5921
5998
|
} else {
|
|
5922
5999
|
this.routes.push(groupedRoute);
|
|
6000
|
+
if (groupedRoute.name)
|
|
6001
|
+
registerNamedRoute(groupedRoute.name, groupedRoute.path);
|
|
5923
6002
|
}
|
|
5924
6003
|
}
|
|
5925
6004
|
return this;
|
|
@@ -5965,45 +6044,45 @@ class FluentRouter {
|
|
|
5965
6044
|
getRoutes() {
|
|
5966
6045
|
return [...this.routes, ...this.subdomainRouter.getAllRoutes()];
|
|
5967
6046
|
}
|
|
5968
|
-
async handle(
|
|
5969
|
-
const _url = new URL(
|
|
5970
|
-
const
|
|
5971
|
-
const domain =
|
|
6047
|
+
async handle(request2) {
|
|
6048
|
+
const _url = new URL(request2.url);
|
|
6049
|
+
const url2 = new URL(request2.url);
|
|
6050
|
+
const domain = url2.hostname;
|
|
5972
6051
|
const match = this.subdomainRouter.findDomainGroup(domain);
|
|
5973
6052
|
if (match) {
|
|
5974
|
-
const enhancedReq =
|
|
6053
|
+
const enhancedReq = request2;
|
|
5975
6054
|
enhancedReq.domainParams = match.parameters;
|
|
5976
6055
|
for (const route of match.group.getRoutes()) {
|
|
5977
|
-
if (this.matchesRoute(route,
|
|
6056
|
+
if (this.matchesRoute(route, request2)) {
|
|
5978
6057
|
return await this.executeRoute(route, enhancedReq);
|
|
5979
6058
|
}
|
|
5980
6059
|
}
|
|
5981
6060
|
}
|
|
5982
6061
|
for (const route of this.routes) {
|
|
5983
|
-
if (this.matchesRoute(route,
|
|
5984
|
-
return await this.executeRoute(route,
|
|
6062
|
+
if (this.matchesRoute(route, request2)) {
|
|
6063
|
+
return await this.executeRoute(route, request2);
|
|
5985
6064
|
}
|
|
5986
6065
|
}
|
|
5987
6066
|
return null;
|
|
5988
6067
|
}
|
|
5989
|
-
matchesRoute(route,
|
|
5990
|
-
if (route.method !==
|
|
6068
|
+
matchesRoute(route, request2) {
|
|
6069
|
+
if (route.method !== request2.method) {
|
|
5991
6070
|
return false;
|
|
5992
6071
|
}
|
|
5993
|
-
const
|
|
6072
|
+
const url2 = new URL(request2.url);
|
|
5994
6073
|
const routePattern = route.path.replace(/\{([^:}]+):?([^}]*)\}/g, "([^/]+)");
|
|
5995
6074
|
const regex = new RegExp(`^${routePattern}$`);
|
|
5996
|
-
return regex.test(
|
|
6075
|
+
return regex.test(url2.pathname);
|
|
5997
6076
|
}
|
|
5998
|
-
async executeRoute(route,
|
|
6077
|
+
async executeRoute(route, request2) {
|
|
5999
6078
|
const allMiddleware = [...this.globalMiddleware, ...route.middleware];
|
|
6000
6079
|
let index = 0;
|
|
6001
6080
|
const next = async () => {
|
|
6002
6081
|
if (index < allMiddleware.length) {
|
|
6003
6082
|
const middleware2 = allMiddleware[index++];
|
|
6004
|
-
return await middleware2(
|
|
6083
|
+
return await middleware2(request2, next);
|
|
6005
6084
|
}
|
|
6006
|
-
return await route.handler(
|
|
6085
|
+
return await route.handler(request2);
|
|
6007
6086
|
};
|
|
6008
6087
|
return await next();
|
|
6009
6088
|
}
|
|
@@ -6041,20 +6120,20 @@ var RouteFactory = {
|
|
|
6041
6120
|
};
|
|
6042
6121
|
var RouterUtils = {
|
|
6043
6122
|
route: (name, params = {}, query = {}) => {
|
|
6044
|
-
let
|
|
6123
|
+
let url2 = `/${name}`;
|
|
6045
6124
|
for (const [key, value] of Object.entries(params)) {
|
|
6046
|
-
|
|
6125
|
+
url2 = url2.replace(`{${key}}`, value);
|
|
6047
6126
|
}
|
|
6048
6127
|
const queryString = new URLSearchParams(query).toString();
|
|
6049
6128
|
if (queryString) {
|
|
6050
|
-
|
|
6129
|
+
url2 += `?${queryString}`;
|
|
6051
6130
|
}
|
|
6052
|
-
return
|
|
6131
|
+
return url2;
|
|
6053
6132
|
},
|
|
6054
|
-
redirect: (
|
|
6133
|
+
redirect: (url2, status = 302) => {
|
|
6055
6134
|
return new Response(null, {
|
|
6056
6135
|
status,
|
|
6057
|
-
headers: { Location:
|
|
6136
|
+
headers: { Location: url2 }
|
|
6058
6137
|
});
|
|
6059
6138
|
},
|
|
6060
6139
|
json: (data, status = 200) => {
|
|
@@ -6408,10 +6487,10 @@ function registerHttpMethods(RouterClass) {
|
|
|
6408
6487
|
route.constraints = constraints;
|
|
6409
6488
|
}
|
|
6410
6489
|
route.pattern = {
|
|
6411
|
-
exec: (
|
|
6490
|
+
exec: (url2) => {
|
|
6412
6491
|
const params = {};
|
|
6413
6492
|
const constraintsRecord = route.constraints && !Array.isArray(route.constraints) ? route.constraints : undefined;
|
|
6414
|
-
const isMatch = matchPath(routePath,
|
|
6493
|
+
const isMatch = matchPath(routePath, url2.pathname, params, constraintsRecord);
|
|
6415
6494
|
if (!isMatch) {
|
|
6416
6495
|
return null;
|
|
6417
6496
|
}
|
|
@@ -6445,6 +6524,7 @@ function registerHttpMethods(RouterClass) {
|
|
|
6445
6524
|
if (name) {
|
|
6446
6525
|
route.name = name;
|
|
6447
6526
|
this.namedRoutes.set(name, route);
|
|
6527
|
+
registerNamedRoute(name, route.path);
|
|
6448
6528
|
}
|
|
6449
6529
|
if (this.addRouteToCompiler) {
|
|
6450
6530
|
this.addRouteToCompiler(route);
|
|
@@ -6529,12 +6609,12 @@ function registerHttpMethods(RouterClass) {
|
|
|
6529
6609
|
if (!route) {
|
|
6530
6610
|
throw new Error(`Route with name "${name}" not found`);
|
|
6531
6611
|
}
|
|
6532
|
-
let
|
|
6612
|
+
let url2 = route.path;
|
|
6533
6613
|
for (const [param, value] of Object.entries(params)) {
|
|
6534
|
-
|
|
6535
|
-
|
|
6614
|
+
url2 = url2.replace(`{${param}}`, encodeURIComponent(value));
|
|
6615
|
+
url2 = url2.replace(`{${param}?}`, encodeURIComponent(value));
|
|
6536
6616
|
}
|
|
6537
|
-
return
|
|
6617
|
+
return url2;
|
|
6538
6618
|
},
|
|
6539
6619
|
writable: true,
|
|
6540
6620
|
configurable: true
|
|
@@ -6552,9 +6632,9 @@ function registerHttpMethods(RouterClass) {
|
|
|
6552
6632
|
function registerRedirectMethods(RouterClass) {
|
|
6553
6633
|
Object.defineProperties(RouterClass.prototype, {
|
|
6554
6634
|
redirect: {
|
|
6555
|
-
value(
|
|
6635
|
+
value(url2, status = 302) {
|
|
6556
6636
|
const headers = new Headers;
|
|
6557
|
-
headers.set("Location",
|
|
6637
|
+
headers.set("Location", url2);
|
|
6558
6638
|
return new Response(null, {
|
|
6559
6639
|
status,
|
|
6560
6640
|
headers
|
|
@@ -6564,8 +6644,8 @@ function registerRedirectMethods(RouterClass) {
|
|
|
6564
6644
|
configurable: true
|
|
6565
6645
|
},
|
|
6566
6646
|
permanentRedirect: {
|
|
6567
|
-
value(
|
|
6568
|
-
return this.redirect(
|
|
6647
|
+
value(url2) {
|
|
6648
|
+
return this.redirect(url2, 301);
|
|
6569
6649
|
},
|
|
6570
6650
|
writable: true,
|
|
6571
6651
|
configurable: true
|
|
@@ -7560,8 +7640,8 @@ class RouteCompiler {
|
|
|
7560
7640
|
}
|
|
7561
7641
|
const pattern = this.convertToURLPattern(route.path, route.constraints && !Array.isArray(route.constraints) ? route.constraints : undefined);
|
|
7562
7642
|
const urlPatternAdapter = {
|
|
7563
|
-
exec: (
|
|
7564
|
-
const pathname =
|
|
7643
|
+
exec: (url2) => {
|
|
7644
|
+
const pathname = url2.pathname;
|
|
7565
7645
|
const result = pattern.exec(pathname);
|
|
7566
7646
|
if (!result)
|
|
7567
7647
|
return null;
|
|
@@ -7920,13 +8000,13 @@ function registerOptimizedRouteMatching(RouterClass) {
|
|
|
7920
8000
|
},
|
|
7921
8001
|
fallbackMatchRoute: {
|
|
7922
8002
|
value(path, method, domain) {
|
|
7923
|
-
const
|
|
7924
|
-
const cacheKey = `${domain || ""}:${method}:${
|
|
8003
|
+
const url2 = new URL(path, "http://localhost");
|
|
8004
|
+
const cacheKey = `${domain || ""}:${method}:${url2.pathname}`;
|
|
7925
8005
|
if (this.routeCache.has(cacheKey)) {
|
|
7926
8006
|
return this.routeCache.get(cacheKey);
|
|
7927
8007
|
}
|
|
7928
8008
|
if (this.staticRoutes.has(method)) {
|
|
7929
|
-
const staticRoute = this.staticRoutes.get(method).get(
|
|
8009
|
+
const staticRoute = this.staticRoutes.get(method).get(url2.pathname);
|
|
7930
8010
|
if (staticRoute && (!domain || !staticRoute.domain || staticRoute.domain === domain)) {
|
|
7931
8011
|
const result = {
|
|
7932
8012
|
route: staticRoute,
|
|
@@ -7939,7 +8019,7 @@ function registerOptimizedRouteMatching(RouterClass) {
|
|
|
7939
8019
|
const potentialRoutes = domain && this.domains[domain] ? this.domains[domain] : this.routes;
|
|
7940
8020
|
const methodRoutes = potentialRoutes.filter((route) => route.method === method);
|
|
7941
8021
|
for (const route of methodRoutes) {
|
|
7942
|
-
if (route.path ===
|
|
8022
|
+
if (route.path === url2.pathname) {
|
|
7943
8023
|
const result = {
|
|
7944
8024
|
route,
|
|
7945
8025
|
params: {}
|
|
@@ -7950,7 +8030,7 @@ function registerOptimizedRouteMatching(RouterClass) {
|
|
|
7950
8030
|
}
|
|
7951
8031
|
for (const route of methodRoutes) {
|
|
7952
8032
|
if (route.pattern) {
|
|
7953
|
-
const match = route.pattern.exec(
|
|
8033
|
+
const match = route.pattern.exec(url2);
|
|
7954
8034
|
if (match) {
|
|
7955
8035
|
const result = {
|
|
7956
8036
|
route,
|
|
@@ -8075,10 +8155,10 @@ function registerRouteBuilding(RouterClass) {
|
|
|
8075
8155
|
} catch {}
|
|
8076
8156
|
}
|
|
8077
8157
|
bunRoutes[path] = async (req) => {
|
|
8078
|
-
const
|
|
8158
|
+
const url2 = new URL(req.url);
|
|
8079
8159
|
const params = {};
|
|
8080
8160
|
const constraintsRecord = route.constraints && !Array.isArray(route.constraints) ? route.constraints : undefined;
|
|
8081
|
-
matchPath(path,
|
|
8161
|
+
matchPath(path, url2.pathname, params, constraintsRecord);
|
|
8082
8162
|
const enhancedReq = this.enhanceRequest(req, params);
|
|
8083
8163
|
const middlewareResult = await this.runMiddleware(enhancedReq, [...this.globalMiddleware, ...route.middleware]);
|
|
8084
8164
|
if (middlewareResult) {
|
|
@@ -8092,10 +8172,10 @@ function registerRouteBuilding(RouterClass) {
|
|
|
8092
8172
|
for (const route of routes) {
|
|
8093
8173
|
const method = route.method;
|
|
8094
8174
|
methodHandlers[method] = async (req) => {
|
|
8095
|
-
const
|
|
8175
|
+
const url2 = new URL(req.url);
|
|
8096
8176
|
const params = {};
|
|
8097
8177
|
const constraintsRecord = route.constraints && !Array.isArray(route.constraints) ? route.constraints : undefined;
|
|
8098
|
-
matchPath(path,
|
|
8178
|
+
matchPath(path, url2.pathname, params, constraintsRecord);
|
|
8099
8179
|
const enhancedReq = this.enhanceRequest(req, params);
|
|
8100
8180
|
const middlewareResult = await this.runMiddleware(enhancedReq, [...this.globalMiddleware, ...route.middleware]);
|
|
8101
8181
|
if (middlewareResult) {
|
|
@@ -8191,13 +8271,13 @@ function registerRouteMatching(RouterClass) {
|
|
|
8191
8271
|
Object.defineProperties(RouterClass.prototype, {
|
|
8192
8272
|
matchRoute: {
|
|
8193
8273
|
value(path, method, domain) {
|
|
8194
|
-
const
|
|
8195
|
-
const cacheKey = `${domain || ""}:${method}:${
|
|
8274
|
+
const url2 = new URL(path, "http://localhost");
|
|
8275
|
+
const cacheKey = `${domain || ""}:${method}:${url2.pathname}`;
|
|
8196
8276
|
if (this.routeCache.has(cacheKey)) {
|
|
8197
8277
|
return this.routeCache.get(cacheKey);
|
|
8198
8278
|
}
|
|
8199
8279
|
if (this.staticRoutes.has(method)) {
|
|
8200
|
-
const staticRoute = this.staticRoutes.get(method).get(
|
|
8280
|
+
const staticRoute = this.staticRoutes.get(method).get(url2.pathname);
|
|
8201
8281
|
if (staticRoute && (!domain || !staticRoute.domain || staticRoute.domain === domain)) {
|
|
8202
8282
|
const result = {
|
|
8203
8283
|
route: staticRoute,
|
|
@@ -8210,7 +8290,7 @@ function registerRouteMatching(RouterClass) {
|
|
|
8210
8290
|
const potentialRoutes = domain && this.domains[domain] ? this.domains[domain] : this.routes;
|
|
8211
8291
|
const methodRoutes = potentialRoutes.filter((route) => route.method === method);
|
|
8212
8292
|
for (const route of methodRoutes) {
|
|
8213
|
-
if (route.path ===
|
|
8293
|
+
if (route.path === url2.pathname) {
|
|
8214
8294
|
const result = {
|
|
8215
8295
|
route,
|
|
8216
8296
|
params: {}
|
|
@@ -8221,7 +8301,7 @@ function registerRouteMatching(RouterClass) {
|
|
|
8221
8301
|
}
|
|
8222
8302
|
for (const route of methodRoutes) {
|
|
8223
8303
|
if (route.pattern) {
|
|
8224
|
-
const match = route.pattern.exec(
|
|
8304
|
+
const match = route.pattern.exec(url2);
|
|
8225
8305
|
if (match) {
|
|
8226
8306
|
const result = {
|
|
8227
8307
|
route,
|
|
@@ -8236,11 +8316,11 @@ function registerRouteMatching(RouterClass) {
|
|
|
8236
8316
|
const wildcardRoutes = this.domains[domain].filter((route) => route.method === method && route.path.endsWith("*"));
|
|
8237
8317
|
for (const route of wildcardRoutes) {
|
|
8238
8318
|
const basePath = route.path.slice(0, -1);
|
|
8239
|
-
if (
|
|
8319
|
+
if (url2.pathname.startsWith(basePath)) {
|
|
8240
8320
|
const result = {
|
|
8241
8321
|
route,
|
|
8242
8322
|
params: {
|
|
8243
|
-
wildcard:
|
|
8323
|
+
wildcard: url2.pathname.slice(basePath.length)
|
|
8244
8324
|
}
|
|
8245
8325
|
};
|
|
8246
8326
|
this.routeCache.set(cacheKey, result);
|
|
@@ -8251,11 +8331,11 @@ function registerRouteMatching(RouterClass) {
|
|
|
8251
8331
|
const globalWildcardRoutes = this.routes.filter((route) => route.method === method && route.path.endsWith("*") && (!domain || !route.domain));
|
|
8252
8332
|
for (const route of globalWildcardRoutes) {
|
|
8253
8333
|
const basePath = route.path.slice(0, -1);
|
|
8254
|
-
if (
|
|
8334
|
+
if (url2.pathname.startsWith(basePath)) {
|
|
8255
8335
|
const result = {
|
|
8256
8336
|
route,
|
|
8257
8337
|
params: {
|
|
8258
|
-
wildcard:
|
|
8338
|
+
wildcard: url2.pathname.slice(basePath.length)
|
|
8259
8339
|
}
|
|
8260
8340
|
};
|
|
8261
8341
|
this.routeCache.set(cacheKey, result);
|
|
@@ -8356,9 +8436,9 @@ function registerRouteMatching(RouterClass) {
|
|
|
8356
8436
|
const routePath = lastRoute.path;
|
|
8357
8437
|
const constraintsRecord = lastRoute.constraints && !Array.isArray(lastRoute.constraints) ? lastRoute.constraints : undefined;
|
|
8358
8438
|
lastRoute.pattern = {
|
|
8359
|
-
exec: (
|
|
8439
|
+
exec: (url2) => {
|
|
8360
8440
|
const params2 = {};
|
|
8361
|
-
const isMatch = matchPath(routePath,
|
|
8441
|
+
const isMatch = matchPath(routePath, url2.pathname, params2, constraintsRecord);
|
|
8362
8442
|
if (!isMatch) {
|
|
8363
8443
|
return null;
|
|
8364
8444
|
}
|
|
@@ -8595,10 +8675,10 @@ class Router {
|
|
|
8595
8675
|
route.constraints = constraints;
|
|
8596
8676
|
}
|
|
8597
8677
|
route.pattern = {
|
|
8598
|
-
exec: (
|
|
8678
|
+
exec: (url2) => {
|
|
8599
8679
|
const params = {};
|
|
8600
8680
|
const constraintsRecord = route.constraints && !Array.isArray(route.constraints) ? route.constraints : undefined;
|
|
8601
|
-
const isMatch = matchPath(routePath,
|
|
8681
|
+
const isMatch = matchPath(routePath, url2.pathname, params, constraintsRecord);
|
|
8602
8682
|
if (!isMatch) {
|
|
8603
8683
|
return null;
|
|
8604
8684
|
}
|
|
@@ -8626,6 +8706,7 @@ class Router {
|
|
|
8626
8706
|
if (name) {
|
|
8627
8707
|
route.name = name;
|
|
8628
8708
|
this.namedRoutes.set(name, route);
|
|
8709
|
+
registerNamedRoute(name, route.path);
|
|
8629
8710
|
}
|
|
8630
8711
|
this.routeCache.clear();
|
|
8631
8712
|
return this;
|
|
@@ -8678,27 +8759,27 @@ class Router {
|
|
|
8678
8759
|
if (!route) {
|
|
8679
8760
|
throw new Error(`Route with name "${name}" not found`);
|
|
8680
8761
|
}
|
|
8681
|
-
let
|
|
8762
|
+
let url2 = route.path;
|
|
8682
8763
|
for (const [param, value] of Object.entries(params)) {
|
|
8683
|
-
|
|
8684
|
-
|
|
8764
|
+
url2 = url2.replace(`{${param}}`, encodeURIComponent(value));
|
|
8765
|
+
url2 = url2.replace(`{${param}?}`, encodeURIComponent(value));
|
|
8685
8766
|
}
|
|
8686
|
-
return
|
|
8767
|
+
return url2;
|
|
8687
8768
|
}
|
|
8688
8769
|
onError(handler) {
|
|
8689
8770
|
this.errorHandler = handler;
|
|
8690
8771
|
return this;
|
|
8691
8772
|
}
|
|
8692
|
-
redirect(
|
|
8773
|
+
redirect(url2, status = 302) {
|
|
8693
8774
|
const headers = new Headers;
|
|
8694
|
-
headers.set("Location",
|
|
8775
|
+
headers.set("Location", url2);
|
|
8695
8776
|
return new Response(null, {
|
|
8696
8777
|
status,
|
|
8697
8778
|
headers
|
|
8698
8779
|
});
|
|
8699
8780
|
}
|
|
8700
|
-
permanentRedirect(
|
|
8701
|
-
return this.redirect(
|
|
8781
|
+
permanentRedirect(url2) {
|
|
8782
|
+
return this.redirect(url2, 301);
|
|
8702
8783
|
}
|
|
8703
8784
|
redirectRoute(from, to, status = 302) {
|
|
8704
8785
|
this.get(from, (_req) => {
|
|
@@ -8725,13 +8806,13 @@ class Router {
|
|
|
8725
8806
|
}
|
|
8726
8807
|
async handleRequest(req) {
|
|
8727
8808
|
try {
|
|
8728
|
-
const
|
|
8809
|
+
const url2 = new URL(req.url);
|
|
8729
8810
|
if (this.config.verbose) {
|
|
8730
|
-
console.log(`${req.method} ${
|
|
8811
|
+
console.log(`${req.method} ${url2.pathname}`);
|
|
8731
8812
|
}
|
|
8732
8813
|
if (req.method === "OPTIONS") {
|
|
8733
|
-
const hostname2 =
|
|
8734
|
-
const optionsMatch = this.matchRoute(
|
|
8814
|
+
const hostname2 = url2.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
|
|
8815
|
+
const optionsMatch = this.matchRoute(url2.pathname, "OPTIONS", hostname2);
|
|
8735
8816
|
if (!optionsMatch) {
|
|
8736
8817
|
return new Response(null, {
|
|
8737
8818
|
status: 204,
|
|
@@ -8744,8 +8825,8 @@ class Router {
|
|
|
8744
8825
|
});
|
|
8745
8826
|
}
|
|
8746
8827
|
}
|
|
8747
|
-
const hostname =
|
|
8748
|
-
const match = this.matchRoute(
|
|
8828
|
+
const hostname = url2.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
|
|
8829
|
+
const match = this.matchRoute(url2.pathname, req.method, hostname);
|
|
8749
8830
|
const enhancedReq = this.enhanceRequest(req, match?.params || {});
|
|
8750
8831
|
if (match) {
|
|
8751
8832
|
enhancedReq.route = match.route;
|
|
@@ -8763,7 +8844,7 @@ class Router {
|
|
|
8763
8844
|
}
|
|
8764
8845
|
return new Response("No response from middleware chain", { status: 500 });
|
|
8765
8846
|
}
|
|
8766
|
-
const allowedMethods = this.getAllowedMethods(
|
|
8847
|
+
const allowedMethods = this.getAllowedMethods(url2.pathname, hostname);
|
|
8767
8848
|
if (allowedMethods.length > 0) {
|
|
8768
8849
|
const methodNotAllowedHandler = async (_req, _next) => {
|
|
8769
8850
|
return new Response(JSON.stringify({ error: "Method Not Allowed" }), {
|
|
@@ -8844,12 +8925,12 @@ class Router {
|
|
|
8844
8925
|
}
|
|
8845
8926
|
}
|
|
8846
8927
|
getAllowedMethods(path, domain) {
|
|
8847
|
-
const
|
|
8928
|
+
const url2 = new URL(path, "http://localhost");
|
|
8848
8929
|
const methods = new Set;
|
|
8849
8930
|
const allMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"];
|
|
8850
8931
|
for (const method of allMethods) {
|
|
8851
8932
|
if (this.staticRoutes.has(method)) {
|
|
8852
|
-
const staticRoute = this.staticRoutes.get(method).get(
|
|
8933
|
+
const staticRoute = this.staticRoutes.get(method).get(url2.pathname);
|
|
8853
8934
|
if (staticRoute && (!domain || !staticRoute.domain || staticRoute.domain === domain)) {
|
|
8854
8935
|
methods.add(method);
|
|
8855
8936
|
continue;
|
|
@@ -8858,7 +8939,7 @@ class Router {
|
|
|
8858
8939
|
const potentialRoutes = domain && this.domains[domain] ? this.domains[domain] : this.routes;
|
|
8859
8940
|
const methodRoutes = potentialRoutes.filter((route) => route.method === method);
|
|
8860
8941
|
for (const route of methodRoutes) {
|
|
8861
|
-
if (route.path ===
|
|
8942
|
+
if (route.path === url2.pathname) {
|
|
8862
8943
|
methods.add(method);
|
|
8863
8944
|
break;
|
|
8864
8945
|
}
|
|
@@ -8866,7 +8947,7 @@ class Router {
|
|
|
8866
8947
|
if (!methods.has(method)) {
|
|
8867
8948
|
for (const route of methodRoutes) {
|
|
8868
8949
|
if (route.pattern) {
|
|
8869
|
-
const match = route.pattern.exec(
|
|
8950
|
+
const match = route.pattern.exec(url2);
|
|
8870
8951
|
if (match) {
|
|
8871
8952
|
methods.add(method);
|
|
8872
8953
|
break;
|
|
@@ -8878,7 +8959,7 @@ class Router {
|
|
|
8878
8959
|
const wildcardRoutes = potentialRoutes.filter((route) => route.method === method && route.path.endsWith("*"));
|
|
8879
8960
|
for (const route of wildcardRoutes) {
|
|
8880
8961
|
const basePath = route.path.slice(0, -1);
|
|
8881
|
-
if (
|
|
8962
|
+
if (url2.pathname.startsWith(basePath)) {
|
|
8882
8963
|
methods.add(method);
|
|
8883
8964
|
break;
|
|
8884
8965
|
}
|
|
@@ -8891,13 +8972,13 @@ class Router {
|
|
|
8891
8972
|
return Array.from(methods);
|
|
8892
8973
|
}
|
|
8893
8974
|
matchRoute(path, method, domain) {
|
|
8894
|
-
const
|
|
8895
|
-
const cacheKey = `${domain || ""}:${method}:${
|
|
8975
|
+
const url2 = new URL(path, "http://localhost");
|
|
8976
|
+
const cacheKey = `${domain || ""}:${method}:${url2.pathname}`;
|
|
8896
8977
|
if (this.routeCache.has(cacheKey)) {
|
|
8897
8978
|
return this.routeCache.get(cacheKey);
|
|
8898
8979
|
}
|
|
8899
8980
|
if (this.staticRoutes.has(method)) {
|
|
8900
|
-
const staticRoute = this.staticRoutes.get(method).get(
|
|
8981
|
+
const staticRoute = this.staticRoutes.get(method).get(url2.pathname);
|
|
8901
8982
|
if (staticRoute && (!domain || !staticRoute.domain || staticRoute.domain === domain)) {
|
|
8902
8983
|
const result = {
|
|
8903
8984
|
route: staticRoute,
|
|
@@ -8910,7 +8991,7 @@ class Router {
|
|
|
8910
8991
|
const potentialRoutes = domain && this.domains[domain] ? this.domains[domain] : this.routes;
|
|
8911
8992
|
const methodRoutes = potentialRoutes.filter((route) => route.method === method);
|
|
8912
8993
|
for (const route of methodRoutes) {
|
|
8913
|
-
if (route.path ===
|
|
8994
|
+
if (route.path === url2.pathname) {
|
|
8914
8995
|
const result = {
|
|
8915
8996
|
route,
|
|
8916
8997
|
params: {}
|
|
@@ -8921,7 +9002,7 @@ class Router {
|
|
|
8921
9002
|
}
|
|
8922
9003
|
for (const route of methodRoutes) {
|
|
8923
9004
|
if (route.pattern) {
|
|
8924
|
-
const match = route.pattern.exec(
|
|
9005
|
+
const match = route.pattern.exec(url2);
|
|
8925
9006
|
if (match) {
|
|
8926
9007
|
const result = {
|
|
8927
9008
|
route,
|
|
@@ -8936,11 +9017,11 @@ class Router {
|
|
|
8936
9017
|
const wildcardRoutes = this.domains[domain].filter((route) => route.method === method && route.path.endsWith("*"));
|
|
8937
9018
|
for (const route of wildcardRoutes) {
|
|
8938
9019
|
const basePath = route.path.slice(0, -1);
|
|
8939
|
-
if (
|
|
9020
|
+
if (url2.pathname.startsWith(basePath)) {
|
|
8940
9021
|
const result = {
|
|
8941
9022
|
route,
|
|
8942
9023
|
params: {
|
|
8943
|
-
wildcard:
|
|
9024
|
+
wildcard: url2.pathname.slice(basePath.length)
|
|
8944
9025
|
}
|
|
8945
9026
|
};
|
|
8946
9027
|
this.routeCache.set(cacheKey, result);
|
|
@@ -8951,11 +9032,11 @@ class Router {
|
|
|
8951
9032
|
const globalWildcardRoutes = this.routes.filter((route) => route.method === method && route.path.endsWith("*") && (!domain || !route.domain));
|
|
8952
9033
|
for (const route of globalWildcardRoutes) {
|
|
8953
9034
|
const basePath = route.path.slice(0, -1);
|
|
8954
|
-
if (
|
|
9035
|
+
if (url2.pathname.startsWith(basePath)) {
|
|
8955
9036
|
const result = {
|
|
8956
9037
|
route,
|
|
8957
9038
|
params: {
|
|
8958
|
-
wildcard:
|
|
9039
|
+
wildcard: url2.pathname.slice(basePath.length)
|
|
8959
9040
|
}
|
|
8960
9041
|
};
|
|
8961
9042
|
this.routeCache.set(cacheKey, result);
|
|
@@ -8984,9 +9065,9 @@ class Router {
|
|
|
8984
9065
|
}
|
|
8985
9066
|
return parsedCookies;
|
|
8986
9067
|
};
|
|
8987
|
-
const
|
|
9068
|
+
const url2 = new URL(req.url);
|
|
8988
9069
|
const query = {};
|
|
8989
|
-
|
|
9070
|
+
url2.searchParams.forEach((value, key) => {
|
|
8990
9071
|
query[key] = value;
|
|
8991
9072
|
});
|
|
8992
9073
|
const enhancedReq = {
|
|
@@ -9173,6 +9254,20 @@ class Router {
|
|
|
9173
9254
|
return value;
|
|
9174
9255
|
return value !== undefined && value !== null ? [value] : [];
|
|
9175
9256
|
};
|
|
9257
|
+
enhancedReq.bearerToken = () => {
|
|
9258
|
+
const authHeader = req.headers.get("authorization") || req.headers.get("Authorization") || "";
|
|
9259
|
+
if (authHeader.startsWith("Bearer "))
|
|
9260
|
+
return authHeader.substring(7);
|
|
9261
|
+
return null;
|
|
9262
|
+
};
|
|
9263
|
+
enhancedReq.header = (name) => {
|
|
9264
|
+
return req.headers.get(name) || req.headers.get(name.toLowerCase()) || null;
|
|
9265
|
+
};
|
|
9266
|
+
enhancedReq.getParam = (name, defaultValue) => {
|
|
9267
|
+
const value = params?.[name];
|
|
9268
|
+
return value !== undefined ? value : defaultValue;
|
|
9269
|
+
};
|
|
9270
|
+
enhancedReq.params = params || {};
|
|
9176
9271
|
return enhancedReq;
|
|
9177
9272
|
}
|
|
9178
9273
|
applyModifiedCookies(response2, req) {
|
|
@@ -9310,6 +9405,7 @@ class Router {
|
|
|
9310
9405
|
this.routes.push(route);
|
|
9311
9406
|
if (route.name) {
|
|
9312
9407
|
this.namedRoutes.set(route.name, route);
|
|
9408
|
+
registerNamedRoute(route.name, route.path);
|
|
9313
9409
|
}
|
|
9314
9410
|
return this;
|
|
9315
9411
|
}
|
|
@@ -9341,7 +9437,7 @@ class Router {
|
|
|
9341
9437
|
}
|
|
9342
9438
|
return this;
|
|
9343
9439
|
}
|
|
9344
|
-
async executeMiddleware(middleware2,
|
|
9440
|
+
async executeMiddleware(middleware2, request2, handler) {
|
|
9345
9441
|
if (middleware2.length === 0) {
|
|
9346
9442
|
return handler();
|
|
9347
9443
|
}
|
|
@@ -9351,7 +9447,7 @@ class Router {
|
|
|
9351
9447
|
return handler();
|
|
9352
9448
|
}
|
|
9353
9449
|
const mw = middleware2[currentIndex++];
|
|
9354
|
-
const result = await mw(
|
|
9450
|
+
const result = await mw(request2, next);
|
|
9355
9451
|
return result || new Response("No response from middleware", { status: 500 });
|
|
9356
9452
|
};
|
|
9357
9453
|
return next();
|
|
@@ -9493,12 +9589,20 @@ function registerServerHandling(RouterClass) {
|
|
|
9493
9589
|
configurable: true
|
|
9494
9590
|
},
|
|
9495
9591
|
handleRequest: {
|
|
9592
|
+
async value(req) {
|
|
9593
|
+
const self = this;
|
|
9594
|
+
return runWithRequest(req, () => self.handleRequestImpl(req));
|
|
9595
|
+
},
|
|
9596
|
+
writable: true,
|
|
9597
|
+
configurable: true
|
|
9598
|
+
},
|
|
9599
|
+
handleRequestImpl: {
|
|
9496
9600
|
async value(req) {
|
|
9497
9601
|
try {
|
|
9498
|
-
const
|
|
9602
|
+
const url2 = new URL(req.url);
|
|
9499
9603
|
if (req.method === "OPTIONS") {
|
|
9500
|
-
const hostname2 =
|
|
9501
|
-
const optionsMatch = this.matchRoute(
|
|
9604
|
+
const hostname2 = url2.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
|
|
9605
|
+
const optionsMatch = this.matchRoute(url2.pathname, "OPTIONS", hostname2);
|
|
9502
9606
|
if (!optionsMatch) {
|
|
9503
9607
|
return new Response(null, {
|
|
9504
9608
|
status: 204,
|
|
@@ -9512,9 +9616,10 @@ function registerServerHandling(RouterClass) {
|
|
|
9512
9616
|
});
|
|
9513
9617
|
}
|
|
9514
9618
|
}
|
|
9515
|
-
const hostname =
|
|
9516
|
-
const match = this.matchRoute(
|
|
9619
|
+
const hostname = url2.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
|
|
9620
|
+
const match = this.matchRoute(url2.pathname, req.method, hostname);
|
|
9517
9621
|
const enhancedReq = this.enhanceRequest(req, match?.params || {});
|
|
9622
|
+
setCurrentRequest(enhancedReq);
|
|
9518
9623
|
if (match) {
|
|
9519
9624
|
enhancedReq.route = match.route;
|
|
9520
9625
|
const middlewareStack = [...this.globalMiddleware];
|
|
@@ -9531,7 +9636,7 @@ function registerServerHandling(RouterClass) {
|
|
|
9531
9636
|
}
|
|
9532
9637
|
return new Response("No response from middleware chain", { status: 500 });
|
|
9533
9638
|
}
|
|
9534
|
-
const allowedMethods = this.getAllowedMethods(
|
|
9639
|
+
const allowedMethods = this.getAllowedMethods(url2.pathname, hostname);
|
|
9535
9640
|
if (allowedMethods.length > 0) {
|
|
9536
9641
|
return new Response(JSON.stringify({ success: false, message: "Method Not Allowed" }), {
|
|
9537
9642
|
status: 405,
|
|
@@ -9865,34 +9970,34 @@ function registerWebSocketHandling(RouterClass) {
|
|
|
9865
9970
|
configurable: true
|
|
9866
9971
|
},
|
|
9867
9972
|
upgrade: {
|
|
9868
|
-
value(
|
|
9973
|
+
value(request2, options) {
|
|
9869
9974
|
if (!this.serverInstance) {
|
|
9870
9975
|
throw new Error("Server not started, cannot upgrade to WebSocket");
|
|
9871
9976
|
}
|
|
9872
9977
|
if (!this.wsConfig) {
|
|
9873
9978
|
throw new Error("WebSocket configuration not set, use router.websocket() to configure");
|
|
9874
9979
|
}
|
|
9875
|
-
return this.serverInstance.upgrade(
|
|
9980
|
+
return this.serverInstance.upgrade(request2, options);
|
|
9876
9981
|
},
|
|
9877
9982
|
writable: true,
|
|
9878
9983
|
configurable: true
|
|
9879
9984
|
},
|
|
9880
9985
|
timeout: {
|
|
9881
|
-
value(
|
|
9986
|
+
value(request2, seconds) {
|
|
9882
9987
|
if (!this.serverInstance) {
|
|
9883
9988
|
throw new Error("Server not started, cannot set timeout");
|
|
9884
9989
|
}
|
|
9885
|
-
this.serverInstance.timeout(
|
|
9990
|
+
this.serverInstance.timeout(request2, seconds);
|
|
9886
9991
|
},
|
|
9887
9992
|
writable: true,
|
|
9888
9993
|
configurable: true
|
|
9889
9994
|
},
|
|
9890
9995
|
requestIP: {
|
|
9891
|
-
value(
|
|
9996
|
+
value(request2) {
|
|
9892
9997
|
if (!this.serverInstance) {
|
|
9893
9998
|
throw new Error("Server not started, cannot get request IP");
|
|
9894
9999
|
}
|
|
9895
|
-
return this.serverInstance.requestIP(
|
|
10000
|
+
return this.serverInstance.requestIP(request2);
|
|
9896
10001
|
},
|
|
9897
10002
|
writable: true,
|
|
9898
10003
|
configurable: true
|
|
@@ -9927,12 +10032,12 @@ class RequestWithMacros {
|
|
|
9927
10032
|
static macro(name, handler) {
|
|
9928
10033
|
requestMacroRegistry.register(name, handler);
|
|
9929
10034
|
}
|
|
9930
|
-
static applyMacros(
|
|
10035
|
+
static applyMacros(request2) {
|
|
9931
10036
|
const macros = requestMacroRegistry.all();
|
|
9932
10037
|
macros.forEach((macro) => {
|
|
9933
|
-
|
|
10038
|
+
request2[macro.name] = macro.handler.bind(request2);
|
|
9934
10039
|
});
|
|
9935
|
-
return
|
|
10040
|
+
return request2;
|
|
9936
10041
|
}
|
|
9937
10042
|
static hasMacro(name) {
|
|
9938
10043
|
return requestMacroRegistry.has(name);
|
|
@@ -10138,8 +10243,8 @@ var BuiltInRequestMacros = {
|
|
|
10138
10243
|
return this.url;
|
|
10139
10244
|
},
|
|
10140
10245
|
root() {
|
|
10141
|
-
const
|
|
10142
|
-
return `${
|
|
10246
|
+
const url2 = new URL(this.url);
|
|
10247
|
+
return `${url2.protocol}//${url2.host}`;
|
|
10143
10248
|
},
|
|
10144
10249
|
is(pattern) {
|
|
10145
10250
|
const path = this.path();
|
|
@@ -10151,11 +10256,11 @@ var BuiltInRequestMacros = {
|
|
|
10151
10256
|
return this.routeName || null;
|
|
10152
10257
|
},
|
|
10153
10258
|
fingerprint() {
|
|
10154
|
-
const
|
|
10259
|
+
const url2 = new URL(this.url);
|
|
10155
10260
|
const data = {
|
|
10156
10261
|
method: this.method,
|
|
10157
|
-
path:
|
|
10158
|
-
query:
|
|
10262
|
+
path: url2.pathname,
|
|
10263
|
+
query: url2.search,
|
|
10159
10264
|
ip: BuiltInRequestMacros.ip.call(this)
|
|
10160
10265
|
};
|
|
10161
10266
|
return btoa(JSON.stringify(data));
|
|
@@ -10207,7 +10312,7 @@ function registerBuiltInRequestMacros() {
|
|
|
10207
10312
|
registerBuiltInRequestMacros();
|
|
10208
10313
|
|
|
10209
10314
|
// src/response/macros.ts
|
|
10210
|
-
import
|
|
10315
|
+
import process4 from "process";
|
|
10211
10316
|
|
|
10212
10317
|
class ResponseMacroRegistry {
|
|
10213
10318
|
macros = new Map;
|
|
@@ -10361,16 +10466,16 @@ var BuiltInResponseMacros = {
|
|
|
10361
10466
|
}
|
|
10362
10467
|
});
|
|
10363
10468
|
},
|
|
10364
|
-
redirect: (
|
|
10469
|
+
redirect: (url2, status = 302) => {
|
|
10365
10470
|
return new Response(null, {
|
|
10366
10471
|
status,
|
|
10367
10472
|
headers: {
|
|
10368
|
-
Location:
|
|
10473
|
+
Location: url2
|
|
10369
10474
|
}
|
|
10370
10475
|
});
|
|
10371
10476
|
},
|
|
10372
|
-
permanentRedirect: (
|
|
10373
|
-
return BuiltInResponseMacros.redirect(
|
|
10477
|
+
permanentRedirect: (url2) => {
|
|
10478
|
+
return BuiltInResponseMacros.redirect(url2, 301);
|
|
10374
10479
|
},
|
|
10375
10480
|
download: (data, filename, contentType = "application/octet-stream") => {
|
|
10376
10481
|
return new Response(data, {
|
|
@@ -10441,7 +10546,7 @@ var BuiltInResponseMacros = {
|
|
|
10441
10546
|
status,
|
|
10442
10547
|
timestamp: new Date().toISOString(),
|
|
10443
10548
|
checks,
|
|
10444
|
-
uptime:
|
|
10549
|
+
uptime: process4.uptime?.() || 0
|
|
10445
10550
|
};
|
|
10446
10551
|
return new Response(JSON.stringify(response2), {
|
|
10447
10552
|
status: statusCode,
|
|
@@ -10507,8 +10612,8 @@ var BuiltInRules = {
|
|
|
10507
10612
|
name: "url",
|
|
10508
10613
|
validate: (value) => {
|
|
10509
10614
|
try {
|
|
10510
|
-
const
|
|
10511
|
-
return Boolean(
|
|
10615
|
+
const url2 = new URL(value);
|
|
10616
|
+
return Boolean(url2);
|
|
10512
10617
|
} catch {
|
|
10513
10618
|
return false;
|
|
10514
10619
|
}
|
|
@@ -11332,8 +11437,8 @@ function createCookieAccessor2(cookies) {
|
|
|
11332
11437
|
|
|
11333
11438
|
class AuthTester {
|
|
11334
11439
|
request;
|
|
11335
|
-
constructor(
|
|
11336
|
-
this.request =
|
|
11440
|
+
constructor(request2) {
|
|
11441
|
+
this.request = request2 || createMockRequest();
|
|
11337
11442
|
}
|
|
11338
11443
|
as(user) {
|
|
11339
11444
|
this.request.user = user;
|
|
@@ -11561,16 +11666,16 @@ var authMocks = {
|
|
|
11561
11666
|
})
|
|
11562
11667
|
};
|
|
11563
11668
|
var authHelpers = {
|
|
11564
|
-
authenticatedRequest: (user,
|
|
11565
|
-
return new AuthTester(
|
|
11669
|
+
authenticatedRequest: (user, request2) => {
|
|
11670
|
+
return new AuthTester(request2).as(user).getRequest();
|
|
11566
11671
|
},
|
|
11567
|
-
jwtRequest: (payload = {},
|
|
11672
|
+
jwtRequest: (payload = {}, request2) => {
|
|
11568
11673
|
const jwt = new JWTTester;
|
|
11569
11674
|
const token = jwt.generateToken(payload);
|
|
11570
|
-
return new AuthTester(
|
|
11675
|
+
return new AuthTester(request2).withJWT(token).getRequest();
|
|
11571
11676
|
},
|
|
11572
|
-
sessionRequest: (sessionData = {},
|
|
11573
|
-
return new AuthTester(
|
|
11677
|
+
sessionRequest: (sessionData = {}, request2) => {
|
|
11678
|
+
return new AuthTester(request2).withSession(sessionData).getRequest();
|
|
11574
11679
|
},
|
|
11575
11680
|
verifyJWTStructure: (token) => {
|
|
11576
11681
|
const parts = token.split(".");
|
|
@@ -11585,8 +11690,8 @@ var authHelpers = {
|
|
|
11585
11690
|
}
|
|
11586
11691
|
}
|
|
11587
11692
|
};
|
|
11588
|
-
function createAuthTester(
|
|
11589
|
-
return new AuthTester(
|
|
11693
|
+
function createAuthTester(request2) {
|
|
11694
|
+
return new AuthTester(request2);
|
|
11590
11695
|
}
|
|
11591
11696
|
function createJWTTester(options) {
|
|
11592
11697
|
return new JWTTester(options);
|
|
@@ -11599,8 +11704,8 @@ import { mock as mock2 } from "bun:test";
|
|
|
11599
11704
|
class FileUploadTester {
|
|
11600
11705
|
request;
|
|
11601
11706
|
files;
|
|
11602
|
-
constructor(
|
|
11603
|
-
this.request =
|
|
11707
|
+
constructor(request2) {
|
|
11708
|
+
this.request = request2 || createMockRequest();
|
|
11604
11709
|
this.files = [];
|
|
11605
11710
|
}
|
|
11606
11711
|
addFile(testFile) {
|
|
@@ -11832,8 +11937,8 @@ var fileUploadScenarios = {
|
|
|
11832
11937
|
}
|
|
11833
11938
|
};
|
|
11834
11939
|
var fileUploadAssertions = {
|
|
11835
|
-
hasFiles: (
|
|
11836
|
-
const files =
|
|
11940
|
+
hasFiles: (request2, count) => {
|
|
11941
|
+
const files = request2.files || [];
|
|
11837
11942
|
if (count !== undefined && files.length !== count) {
|
|
11838
11943
|
throw new Error(`Expected ${count} files, but got ${files.length}`);
|
|
11839
11944
|
}
|
|
@@ -11841,8 +11946,8 @@ var fileUploadAssertions = {
|
|
|
11841
11946
|
throw new Error("Expected request to have files");
|
|
11842
11947
|
}
|
|
11843
11948
|
},
|
|
11844
|
-
hasFileWithField: (
|
|
11845
|
-
const files =
|
|
11949
|
+
hasFileWithField: (request2, fieldName) => {
|
|
11950
|
+
const files = request2.files || [];
|
|
11846
11951
|
const hasField = files.some((file) => file.fieldName === fieldName);
|
|
11847
11952
|
if (!hasField) {
|
|
11848
11953
|
throw new Error(`Expected file with field name '${fieldName}'`);
|
|
@@ -11866,8 +11971,8 @@ var fileUploadAssertions = {
|
|
|
11866
11971
|
}
|
|
11867
11972
|
}
|
|
11868
11973
|
};
|
|
11869
|
-
function createFileUploadTester(
|
|
11870
|
-
return new FileUploadTester(
|
|
11974
|
+
function createFileUploadTester(request2) {
|
|
11975
|
+
return new FileUploadTester(request2);
|
|
11871
11976
|
}
|
|
11872
11977
|
var testFiles = {
|
|
11873
11978
|
image: (fieldName = "image", filename = "test.jpg") => ({
|
|
@@ -11911,15 +12016,15 @@ class MiddlewareTester {
|
|
|
11911
12016
|
this.context = this.createContext();
|
|
11912
12017
|
}
|
|
11913
12018
|
createContext() {
|
|
11914
|
-
const
|
|
12019
|
+
const request2 = createMockRequest();
|
|
11915
12020
|
const next = mock3(() => Promise.resolve(new Response("Next called", { status: 200 })));
|
|
11916
12021
|
return {
|
|
11917
|
-
request,
|
|
12022
|
+
request: request2,
|
|
11918
12023
|
next
|
|
11919
12024
|
};
|
|
11920
12025
|
}
|
|
11921
|
-
withRequest(
|
|
11922
|
-
this.context.request =
|
|
12026
|
+
withRequest(request2) {
|
|
12027
|
+
this.context.request = request2;
|
|
11923
12028
|
return this;
|
|
11924
12029
|
}
|
|
11925
12030
|
withNext(nextFn) {
|
|
@@ -12017,8 +12122,8 @@ class MiddlewareChainTester {
|
|
|
12017
12122
|
this.request = createMockRequest();
|
|
12018
12123
|
this.finalHandler = mock3(async () => new Response("Final handler", { status: 200 }));
|
|
12019
12124
|
}
|
|
12020
|
-
withRequest(
|
|
12021
|
-
this.request =
|
|
12125
|
+
withRequest(request2) {
|
|
12126
|
+
this.request = request2;
|
|
12022
12127
|
return this;
|
|
12023
12128
|
}
|
|
12024
12129
|
withFinalHandler(handler) {
|
|
@@ -12058,11 +12163,11 @@ function createMiddlewareSpy() {
|
|
|
12058
12163
|
}
|
|
12059
12164
|
async function benchmarkMiddleware(middleware2, iterations = 1000) {
|
|
12060
12165
|
const times = [];
|
|
12061
|
-
const
|
|
12166
|
+
const request2 = createMockRequest();
|
|
12062
12167
|
const next = mock3(async () => new Response("OK"));
|
|
12063
12168
|
for (let i = 0;i < iterations; i++) {
|
|
12064
12169
|
const start = performance.now();
|
|
12065
|
-
await middleware2(
|
|
12170
|
+
await middleware2(request2, next);
|
|
12066
12171
|
const end = performance.now();
|
|
12067
12172
|
times.push(end - start);
|
|
12068
12173
|
}
|
|
@@ -12160,8 +12265,8 @@ class ModelBindingTester {
|
|
|
12160
12265
|
this.options = options;
|
|
12161
12266
|
this.request = createMockRequest();
|
|
12162
12267
|
}
|
|
12163
|
-
withRequest(
|
|
12164
|
-
this.request =
|
|
12268
|
+
withRequest(request2) {
|
|
12269
|
+
this.request = request2;
|
|
12165
12270
|
return this;
|
|
12166
12271
|
}
|
|
12167
12272
|
withParams(params) {
|
|
@@ -12299,18 +12404,18 @@ var modelMocks = {
|
|
|
12299
12404
|
};
|
|
12300
12405
|
var constraintHelpers = {
|
|
12301
12406
|
belongsTo: (userIdField = "userId") => {
|
|
12302
|
-
const fn = async (model,
|
|
12303
|
-
if (!
|
|
12407
|
+
const fn = async (model, request2) => {
|
|
12408
|
+
if (!request2.user)
|
|
12304
12409
|
return false;
|
|
12305
|
-
return model[userIdField] ===
|
|
12410
|
+
return model[userIdField] === request2.user.id;
|
|
12306
12411
|
};
|
|
12307
12412
|
return mock4(fn);
|
|
12308
12413
|
},
|
|
12309
12414
|
belongsToUser: () => {
|
|
12310
|
-
const fn = async (model,
|
|
12311
|
-
if (!
|
|
12415
|
+
const fn = async (model, request2) => {
|
|
12416
|
+
if (!request2.user)
|
|
12312
12417
|
return false;
|
|
12313
|
-
return model.userId ===
|
|
12418
|
+
return model.userId === request2.user.id;
|
|
12314
12419
|
};
|
|
12315
12420
|
return mock4(fn);
|
|
12316
12421
|
},
|
|
@@ -12330,9 +12435,9 @@ var constraintHelpers = {
|
|
|
12330
12435
|
return mock4(constraintFn);
|
|
12331
12436
|
},
|
|
12332
12437
|
combine: (...constraints) => {
|
|
12333
|
-
const fn = async (model,
|
|
12438
|
+
const fn = async (model, request2) => {
|
|
12334
12439
|
for (const constraint of constraints) {
|
|
12335
|
-
if (!await constraint(model,
|
|
12440
|
+
if (!await constraint(model, request2)) {
|
|
12336
12441
|
return false;
|
|
12337
12442
|
}
|
|
12338
12443
|
}
|
|
@@ -12389,24 +12494,24 @@ class ModelResolverTester {
|
|
|
12389
12494
|
}
|
|
12390
12495
|
var routeModelHelpers = {
|
|
12391
12496
|
testRoute: async (path, params, modelBindings) => {
|
|
12392
|
-
const
|
|
12393
|
-
|
|
12497
|
+
const request2 = createMockRequest("GET", path);
|
|
12498
|
+
request2.params = params;
|
|
12394
12499
|
for (const [key, model] of Object.entries(modelBindings)) {
|
|
12395
|
-
|
|
12396
|
-
|
|
12500
|
+
request2.context = request2.context || {};
|
|
12501
|
+
request2.context[key] = model;
|
|
12397
12502
|
}
|
|
12398
|
-
return
|
|
12503
|
+
return request2;
|
|
12399
12504
|
},
|
|
12400
12505
|
createHandler: (expectedModels) => {
|
|
12401
|
-
const fn = async (
|
|
12402
|
-
const context =
|
|
12506
|
+
const fn = async (request2) => {
|
|
12507
|
+
const context = request2.context || {};
|
|
12403
12508
|
for (const modelKey of expectedModels) {
|
|
12404
12509
|
if (!context[modelKey]) {
|
|
12405
12510
|
return new Response(`Model ${modelKey} not found`, { status: 404 });
|
|
12406
12511
|
}
|
|
12407
12512
|
}
|
|
12408
12513
|
return new Response(JSON.stringify({
|
|
12409
|
-
params:
|
|
12514
|
+
params: request2.params,
|
|
12410
12515
|
models: Object.fromEntries(expectedModels.map((key) => [key, context[key]]))
|
|
12411
12516
|
}), {
|
|
12412
12517
|
headers: { "Content-Type": "application/json" }
|
|
@@ -12415,16 +12520,16 @@ var routeModelHelpers = {
|
|
|
12415
12520
|
return mock4(fn);
|
|
12416
12521
|
},
|
|
12417
12522
|
testBindingMiddleware: (bindings) => {
|
|
12418
|
-
const fn = async (
|
|
12419
|
-
|
|
12523
|
+
const fn = async (request2, next) => {
|
|
12524
|
+
request2.context = request2.context || {};
|
|
12420
12525
|
for (const [key, resolver] of Object.entries(bindings)) {
|
|
12421
|
-
const paramValue =
|
|
12526
|
+
const paramValue = request2.params[key];
|
|
12422
12527
|
if (paramValue) {
|
|
12423
12528
|
try {
|
|
12424
12529
|
if (typeof resolver === "function") {
|
|
12425
|
-
|
|
12530
|
+
request2.context[key] = await resolver(paramValue);
|
|
12426
12531
|
} else {
|
|
12427
|
-
|
|
12532
|
+
request2.context[key] = resolver;
|
|
12428
12533
|
}
|
|
12429
12534
|
} catch (error) {
|
|
12430
12535
|
console.error(`Error resolving model ${key}:`, error);
|
|
@@ -12461,7 +12566,7 @@ var scopedBindingHelpers = {
|
|
|
12461
12566
|
};
|
|
12462
12567
|
// src/testing/performance-testing.ts
|
|
12463
12568
|
import { mock as mock5 } from "bun:test";
|
|
12464
|
-
import
|
|
12569
|
+
import process5 from "process";
|
|
12465
12570
|
class PerformanceTester {
|
|
12466
12571
|
metrics = [];
|
|
12467
12572
|
startTime = 0;
|
|
@@ -12580,10 +12685,10 @@ class LoadTester {
|
|
|
12580
12685
|
var benchmarkTests = {
|
|
12581
12686
|
routeHandler: async (handler, iterations = 1000) => {
|
|
12582
12687
|
const times = [];
|
|
12583
|
-
const
|
|
12688
|
+
const request2 = createMockRequest();
|
|
12584
12689
|
for (let i = 0;i < iterations; i++) {
|
|
12585
12690
|
const start = performance.now();
|
|
12586
|
-
await handler(
|
|
12691
|
+
await handler(request2);
|
|
12587
12692
|
const end = performance.now();
|
|
12588
12693
|
times.push(end - start);
|
|
12589
12694
|
}
|
|
@@ -12602,11 +12707,11 @@ var benchmarkTests = {
|
|
|
12602
12707
|
},
|
|
12603
12708
|
middleware: async (middleware2, iterations = 1000) => {
|
|
12604
12709
|
const times = [];
|
|
12605
|
-
const
|
|
12710
|
+
const request2 = createMockRequest();
|
|
12606
12711
|
const next = mock5(async () => new Response("OK"));
|
|
12607
12712
|
for (let i = 0;i < iterations; i++) {
|
|
12608
12713
|
const start = performance.now();
|
|
12609
|
-
await middleware2(
|
|
12714
|
+
await middleware2(request2, next);
|
|
12610
12715
|
const end = performance.now();
|
|
12611
12716
|
times.push(end - start);
|
|
12612
12717
|
}
|
|
@@ -12679,11 +12784,11 @@ var memoryTests = {
|
|
|
12679
12784
|
if (globalThis.gc) {
|
|
12680
12785
|
globalThis.gc();
|
|
12681
12786
|
}
|
|
12682
|
-
const initialHeap =
|
|
12787
|
+
const initialHeap = process5.memoryUsage().heapUsed;
|
|
12683
12788
|
for (let i = 0;i < iterations; i++) {
|
|
12684
12789
|
const result = await fn();
|
|
12685
12790
|
results.push(result);
|
|
12686
|
-
const memUsage =
|
|
12791
|
+
const memUsage = process5.memoryUsage();
|
|
12687
12792
|
heapUsages.push(memUsage.heapUsed);
|
|
12688
12793
|
if (i % 10 === 0 && globalThis.gc) {
|
|
12689
12794
|
globalThis.gc();
|
|
@@ -12692,7 +12797,7 @@ var memoryTests = {
|
|
|
12692
12797
|
if (globalThis.gc) {
|
|
12693
12798
|
globalThis.gc();
|
|
12694
12799
|
}
|
|
12695
|
-
const finalHeap =
|
|
12800
|
+
const finalHeap = process5.memoryUsage().heapUsed;
|
|
12696
12801
|
const averageHeapUsed = heapUsages.reduce((sum, usage) => sum + usage, 0) / heapUsages.length;
|
|
12697
12802
|
const maxHeapUsed = Math.max(...heapUsages);
|
|
12698
12803
|
const memoryLeakDetected = finalHeap - initialHeap > 1024 * 1024;
|
|
@@ -12706,17 +12811,17 @@ var memoryTests = {
|
|
|
12706
12811
|
testRouteHandlerMemoryLeaks: async (handler, iterations = 1000) => {
|
|
12707
12812
|
if (globalThis.gc)
|
|
12708
12813
|
globalThis.gc();
|
|
12709
|
-
const initialMemory =
|
|
12814
|
+
const initialMemory = process5.memoryUsage().heapUsed;
|
|
12710
12815
|
for (let i = 0;i < iterations; i++) {
|
|
12711
|
-
const
|
|
12712
|
-
await handler(
|
|
12816
|
+
const request2 = createMockRequest();
|
|
12817
|
+
await handler(request2);
|
|
12713
12818
|
if (i % 100 === 0 && globalThis.gc) {
|
|
12714
12819
|
globalThis.gc();
|
|
12715
12820
|
}
|
|
12716
12821
|
}
|
|
12717
12822
|
if (globalThis.gc)
|
|
12718
12823
|
globalThis.gc();
|
|
12719
|
-
const finalMemory =
|
|
12824
|
+
const finalMemory = process5.memoryUsage().heapUsed;
|
|
12720
12825
|
const memoryGrowth = finalMemory - initialMemory;
|
|
12721
12826
|
const memoryLeakDetected = memoryGrowth > 5 * 1024 * 1024;
|
|
12722
12827
|
return {
|
|
@@ -12808,7 +12913,7 @@ function createLoadTester(client, config2) {
|
|
|
12808
12913
|
}
|
|
12809
12914
|
var performanceUtils = {
|
|
12810
12915
|
getCurrentMemoryUsage: () => {
|
|
12811
|
-
const usage =
|
|
12916
|
+
const usage = process5.memoryUsage();
|
|
12812
12917
|
return {
|
|
12813
12918
|
heapUsed: usage.heapUsed,
|
|
12814
12919
|
heapTotal: usage.heapTotal,
|
|
@@ -12817,7 +12922,7 @@ var performanceUtils = {
|
|
|
12817
12922
|
};
|
|
12818
12923
|
},
|
|
12819
12924
|
getCPUUsage: () => {
|
|
12820
|
-
const usage =
|
|
12925
|
+
const usage = process5.cpuUsage();
|
|
12821
12926
|
return {
|
|
12822
12927
|
user: usage.user,
|
|
12823
12928
|
system: usage.system
|
|
@@ -12952,14 +13057,14 @@ class TestResponseWrapper {
|
|
|
12952
13057
|
}
|
|
12953
13058
|
return this;
|
|
12954
13059
|
}
|
|
12955
|
-
expectRedirect(
|
|
13060
|
+
expectRedirect(url2) {
|
|
12956
13061
|
if (this.status < 300 || this.status >= 400) {
|
|
12957
13062
|
throw new Error(`Expected redirect status (3xx), got ${this.status}`);
|
|
12958
13063
|
}
|
|
12959
|
-
if (
|
|
13064
|
+
if (url2) {
|
|
12960
13065
|
const location = this.headers.location;
|
|
12961
|
-
if (location !==
|
|
12962
|
-
throw new Error(`Expected redirect to '${
|
|
13066
|
+
if (location !== url2) {
|
|
13067
|
+
throw new Error(`Expected redirect to '${url2}', got '${location}'`);
|
|
12963
13068
|
}
|
|
12964
13069
|
}
|
|
12965
13070
|
return this;
|
|
@@ -13102,24 +13207,24 @@ class TestClient {
|
|
|
13102
13207
|
async request(method, path, options = {}) {
|
|
13103
13208
|
const headers = { ...this.defaultHeaders, ...options.headers };
|
|
13104
13209
|
const requestOptions = { ...options, headers };
|
|
13105
|
-
const
|
|
13210
|
+
const request2 = new TestRequestBuilder(method, path, requestOptions).build();
|
|
13106
13211
|
try {
|
|
13107
|
-
const response2 = await this.handleRequest(
|
|
13212
|
+
const response2 = await this.handleRequest(request2);
|
|
13108
13213
|
return await createTestResponse(response2);
|
|
13109
13214
|
} catch (error) {
|
|
13110
13215
|
throw new Error(`Test request failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
13111
13216
|
}
|
|
13112
13217
|
}
|
|
13113
|
-
async handleRequest(
|
|
13114
|
-
const
|
|
13115
|
-
const matchResult = this.findMatchingRoute(
|
|
13218
|
+
async handleRequest(request2) {
|
|
13219
|
+
const url2 = new URL(request2.url);
|
|
13220
|
+
const matchResult = this.findMatchingRoute(request2.method, url2.pathname);
|
|
13116
13221
|
if (!matchResult) {
|
|
13117
13222
|
return new Response("Not Found", { status: 404 });
|
|
13118
13223
|
}
|
|
13119
13224
|
const { route, params } = matchResult;
|
|
13120
|
-
|
|
13225
|
+
request2.params = { ...request2.params, ...params };
|
|
13121
13226
|
try {
|
|
13122
|
-
const response2 = await this.executeMiddlewareChain(
|
|
13227
|
+
const response2 = await this.executeMiddlewareChain(request2, route);
|
|
13123
13228
|
return response2 || new Response("No response", { status: 500 });
|
|
13124
13229
|
} catch (error) {
|
|
13125
13230
|
return new Response(JSON.stringify({ error: error instanceof Error ? error.message : "Internal Server Error" }), {
|
|
@@ -13159,28 +13264,28 @@ class TestClient {
|
|
|
13159
13264
|
}
|
|
13160
13265
|
return params;
|
|
13161
13266
|
}
|
|
13162
|
-
async executeMiddlewareChain(
|
|
13267
|
+
async executeMiddlewareChain(request2, route) {
|
|
13163
13268
|
const middlewares = [...this.router.globalMiddleware, ...route.middleware];
|
|
13164
13269
|
let index = 0;
|
|
13165
13270
|
const next = async () => {
|
|
13166
13271
|
if (index < middlewares.length) {
|
|
13167
13272
|
const middleware2 = middlewares[index++];
|
|
13168
|
-
return await middleware2(
|
|
13273
|
+
return await middleware2(request2, next);
|
|
13169
13274
|
} else {
|
|
13170
|
-
return await this.executeHandler(route.handler,
|
|
13275
|
+
return await this.executeHandler(route.handler, request2);
|
|
13171
13276
|
}
|
|
13172
13277
|
};
|
|
13173
13278
|
const result = await next();
|
|
13174
13279
|
return result || new Response("No response", { status: 500 });
|
|
13175
13280
|
}
|
|
13176
|
-
async executeHandler(handler,
|
|
13281
|
+
async executeHandler(handler, request2) {
|
|
13177
13282
|
if (typeof handler === "function") {
|
|
13178
|
-
return await handler(
|
|
13283
|
+
return await handler(request2);
|
|
13179
13284
|
} else if (typeof handler === "string") {
|
|
13180
13285
|
throw new TypeError(`String handlers not supported in test environment: ${handler}`);
|
|
13181
13286
|
} else if (typeof handler === "object" && handler.handle) {
|
|
13182
13287
|
const instance = new handler;
|
|
13183
|
-
return await instance.handle(
|
|
13288
|
+
return await instance.handle(request2);
|
|
13184
13289
|
}
|
|
13185
13290
|
throw new Error("Invalid handler type");
|
|
13186
13291
|
}
|
|
@@ -13564,6 +13669,7 @@ export {
|
|
|
13564
13669
|
verifySignature,
|
|
13565
13670
|
validatePath,
|
|
13566
13671
|
validate,
|
|
13672
|
+
url,
|
|
13567
13673
|
toActionPath,
|
|
13568
13674
|
testRequest,
|
|
13569
13675
|
testMiddlewareChain,
|
|
@@ -13574,10 +13680,12 @@ export {
|
|
|
13574
13680
|
spyOnMiddleware,
|
|
13575
13681
|
singleFileUpload,
|
|
13576
13682
|
signRequest,
|
|
13683
|
+
setCurrentRequest,
|
|
13577
13684
|
session,
|
|
13578
13685
|
securitySuite,
|
|
13579
13686
|
security,
|
|
13580
13687
|
scopedBindingHelpers,
|
|
13688
|
+
runWithRequest,
|
|
13581
13689
|
router,
|
|
13582
13690
|
routeModelHelpers,
|
|
13583
13691
|
routeCacheManager,
|
|
@@ -13588,6 +13696,8 @@ export {
|
|
|
13588
13696
|
requestTracer,
|
|
13589
13697
|
requestSigning,
|
|
13590
13698
|
requestId,
|
|
13699
|
+
request,
|
|
13700
|
+
registerNamedRoute,
|
|
13591
13701
|
rateLimitRegistry,
|
|
13592
13702
|
rateLimit,
|
|
13593
13703
|
processHtmlTemplate,
|
|
@@ -13615,6 +13725,9 @@ export {
|
|
|
13615
13725
|
injectQueryPreservationScript,
|
|
13616
13726
|
imageUpload,
|
|
13617
13727
|
globalMiddlewarePipeline,
|
|
13728
|
+
getNamedRoutes,
|
|
13729
|
+
getNamedRoutePath,
|
|
13730
|
+
getCurrentRequest,
|
|
13618
13731
|
getConfig,
|
|
13619
13732
|
generateSessionId,
|
|
13620
13733
|
generateQueryPreservationScript,
|
|
@@ -13659,6 +13772,7 @@ export {
|
|
|
13659
13772
|
contentSecurityPolicy,
|
|
13660
13773
|
constraintHelpers,
|
|
13661
13774
|
config,
|
|
13775
|
+
clearNamedRoutes,
|
|
13662
13776
|
buildUrlWithPreservedParams,
|
|
13663
13777
|
benchmarkTests,
|
|
13664
13778
|
benchmarkMiddleware,
|