@gravito/core 1.6.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -6
- package/README.zh-TW.md +101 -6
- package/dist/Application.d.ts +256 -0
- package/dist/CommandKernel.d.ts +33 -0
- package/dist/ConfigManager.d.ts +65 -0
- package/dist/Container/RequestScopeManager.d.ts +62 -0
- package/dist/Container/RequestScopeMetrics.d.ts +144 -0
- package/dist/Container.d.ts +153 -0
- package/dist/ErrorHandler.d.ts +66 -0
- package/dist/Event.d.ts +5 -0
- package/dist/EventManager.d.ts +123 -0
- package/dist/GlobalErrorHandlers.d.ts +47 -0
- package/dist/GravitoServer.d.ts +28 -0
- package/dist/HookManager.d.ts +435 -0
- package/dist/Listener.d.ts +4 -0
- package/dist/Logger.d.ts +20 -0
- package/dist/PlanetCore.d.ts +402 -0
- package/dist/RequestContext.d.ts +97 -0
- package/dist/Route.d.ts +36 -0
- package/dist/Router.d.ts +270 -0
- package/dist/ServiceProvider.d.ts +178 -0
- package/dist/adapters/GravitoEngineAdapter.d.ts +27 -0
- package/dist/adapters/bun/AdaptiveAdapter.d.ts +99 -0
- package/dist/adapters/bun/BunContext.d.ts +54 -0
- package/dist/adapters/bun/BunNativeAdapter.d.ts +66 -0
- package/dist/adapters/bun/BunRequest.d.ts +31 -0
- package/dist/adapters/bun/BunWebSocketHandler.d.ts +48 -0
- package/dist/adapters/bun/RadixNode.d.ts +19 -0
- package/dist/adapters/bun/RadixRouter.d.ts +32 -0
- package/dist/adapters/bun/index.d.ts +7 -0
- package/dist/adapters/bun/types.d.ts +20 -0
- package/dist/adapters/index.d.ts +12 -0
- package/dist/adapters/types.d.ts +235 -0
- package/dist/binary/BinaryUtils.d.ts +105 -0
- package/dist/binary/index.d.ts +5 -0
- package/dist/cli/queue-commands.d.ts +6 -0
- package/dist/compat/async-local-storage.browser.d.ts +9 -0
- package/dist/compat/async-local-storage.d.ts +7 -0
- package/dist/compat/crypto.browser.d.ts +5 -0
- package/dist/compat/crypto.d.ts +6 -0
- package/dist/compat.cjs +42 -11
- package/dist/compat.cjs.map +9 -0
- package/dist/compat.d.ts +23 -1
- package/dist/compat.js +3 -0
- package/dist/compat.js.map +9 -0
- package/dist/engine/AOTRouter.d.ts +139 -0
- package/dist/engine/FastContext.d.ts +141 -0
- package/dist/engine/Gravito.d.ts +131 -0
- package/dist/engine/MinimalContext.d.ts +102 -0
- package/dist/engine/analyzer.d.ts +113 -0
- package/dist/engine/constants.d.ts +23 -0
- package/dist/engine/index.cjs +576 -647
- package/dist/engine/index.cjs.map +22 -0
- package/dist/engine/index.d.ts +14 -910
- package/dist/engine/index.js +576 -623
- package/dist/engine/index.js.map +22 -0
- package/dist/engine/path.d.ts +26 -0
- package/dist/engine/pool.d.ts +83 -0
- package/dist/engine/types.d.ts +149 -0
- package/dist/error-handling/RequestScopeErrorContext.d.ts +126 -0
- package/dist/events/BackpressureManager.d.ts +215 -0
- package/dist/events/CircuitBreaker.d.ts +229 -0
- package/dist/events/DeadLetterQueue.d.ts +219 -0
- package/dist/events/EventBackend.d.ts +12 -0
- package/dist/events/EventOptions.d.ts +204 -0
- package/dist/events/EventPriorityQueue.d.ts +63 -0
- package/dist/events/FlowControlStrategy.d.ts +109 -0
- package/dist/events/IdempotencyCache.d.ts +60 -0
- package/dist/events/MessageQueueBridge.d.ts +184 -0
- package/dist/events/PriorityEscalationManager.d.ts +82 -0
- package/dist/events/RetryScheduler.d.ts +104 -0
- package/dist/events/WorkerPool.d.ts +98 -0
- package/dist/events/WorkerPoolConfig.d.ts +153 -0
- package/dist/events/WorkerPoolMetrics.d.ts +65 -0
- package/dist/events/aggregation/AggregationWindow.d.ts +77 -0
- package/dist/events/aggregation/DeduplicationManager.d.ts +135 -0
- package/dist/events/aggregation/EventAggregationManager.d.ts +108 -0
- package/dist/events/aggregation/EventBatcher.d.ts +99 -0
- package/dist/events/aggregation/index.d.ts +10 -0
- package/dist/events/aggregation/types.d.ts +117 -0
- package/dist/events/index.d.ts +26 -0
- package/dist/events/observability/EventMetrics.d.ts +132 -0
- package/dist/events/observability/EventTracer.d.ts +68 -0
- package/dist/events/observability/EventTracing.d.ts +161 -0
- package/dist/events/observability/OTelEventMetrics.d.ts +332 -0
- package/dist/events/observability/ObservableHookManager.d.ts +108 -0
- package/dist/events/observability/StreamWorkerMetrics.d.ts +76 -0
- package/dist/events/observability/index.d.ts +24 -0
- package/dist/events/observability/metrics-types.d.ts +16 -0
- package/dist/events/queue-core.d.ts +77 -0
- package/dist/events/task-executor.d.ts +51 -0
- package/dist/events/types.d.ts +134 -0
- package/dist/exceptions/AuthenticationException.d.ts +8 -0
- package/dist/exceptions/AuthorizationException.d.ts +8 -0
- package/dist/exceptions/CircularDependencyException.d.ts +9 -0
- package/dist/exceptions/GravitoException.d.ts +23 -0
- package/dist/exceptions/HttpException.d.ts +9 -0
- package/dist/exceptions/ModelNotFoundException.d.ts +10 -0
- package/dist/exceptions/ValidationException.d.ts +22 -0
- package/dist/exceptions/index.d.ts +7 -0
- package/dist/ffi/NativeAccelerator.d.ts +69 -0
- package/dist/ffi/NativeHasher.d.ts +139 -0
- package/dist/ffi/cbor-fallback.d.ts +96 -0
- package/dist/ffi/hash-fallback.d.ts +33 -0
- package/dist/ffi/index.cjs +621 -0
- package/dist/ffi/index.cjs.map +14 -0
- package/dist/ffi/index.d.ts +10 -0
- package/dist/ffi/index.js +602 -0
- package/dist/ffi/index.js.map +14 -0
- package/dist/ffi/types.d.ts +135 -0
- package/dist/health/HealthProvider.d.ts +67 -0
- package/dist/helpers/Arr.d.ts +19 -0
- package/dist/helpers/Str.d.ts +38 -0
- package/dist/helpers/data.d.ts +25 -0
- package/dist/helpers/errors.d.ts +34 -0
- package/dist/helpers/response.d.ts +41 -0
- package/dist/helpers.d.ts +338 -0
- package/dist/hooks/ActionManager.d.ts +132 -0
- package/dist/hooks/AsyncDetector.d.ts +84 -0
- package/dist/hooks/FilterManager.d.ts +71 -0
- package/dist/hooks/MigrationWarner.d.ts +24 -0
- package/dist/hooks/dlq-operations.d.ts +60 -0
- package/dist/hooks/index.d.ts +11 -0
- package/dist/hooks/types.d.ts +107 -0
- package/dist/http/CookieJar.d.ts +51 -0
- package/dist/http/cookie.d.ts +29 -0
- package/dist/http/index.d.ts +12 -0
- package/dist/{compat-CI8hiulX.d.cts → http/types.d.ts} +29 -16
- package/dist/index.browser.d.ts +34 -0
- package/dist/index.cjs +10525 -11171
- package/dist/index.cjs.map +168 -0
- package/dist/index.d.ts +58 -10981
- package/dist/index.js +10861 -10997
- package/dist/index.js.map +168 -0
- package/dist/observability/QueueDashboard.d.ts +136 -0
- package/dist/observability/contracts.d.ts +137 -0
- package/dist/observability/index.d.ts +13 -0
- package/dist/reliability/DeadLetterQueueManager.d.ts +349 -0
- package/dist/reliability/RetryPolicy.d.ts +217 -0
- package/dist/reliability/index.d.ts +6 -0
- package/dist/router/ControllerDispatcher.d.ts +12 -0
- package/dist/router/RequestValidator.d.ts +20 -0
- package/dist/runtime/adapter-bun.d.ts +12 -0
- package/dist/runtime/adapter-deno.d.ts +12 -0
- package/dist/runtime/adapter-node.d.ts +12 -0
- package/dist/runtime/adapter-unknown.d.ts +13 -0
- package/dist/runtime/archive.d.ts +17 -0
- package/dist/runtime/compression.d.ts +21 -0
- package/dist/runtime/deep-equals.d.ts +56 -0
- package/dist/runtime/detection.d.ts +22 -0
- package/dist/runtime/escape.d.ts +34 -0
- package/dist/runtime/index.browser.d.ts +20 -0
- package/dist/runtime/index.d.ts +44 -0
- package/dist/runtime/markdown.d.ts +44 -0
- package/dist/runtime/types.d.ts +436 -0
- package/dist/runtime-helpers.d.ts +67 -0
- package/dist/runtime.d.ts +11 -0
- package/dist/security/Encrypter.d.ts +33 -0
- package/dist/security/Hasher.d.ts +29 -0
- package/dist/testing/HttpTester.d.ts +39 -0
- package/dist/testing/TestResponse.d.ts +78 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/transpiler-utils.d.ts +170 -0
- package/dist/types/events.d.ts +94 -0
- package/dist/types.d.ts +13 -0
- package/package.json +21 -52
- package/src/ffi/native/cbor.c +1148 -0
- package/dist/Metrics-VOWWRNNR.js +0 -219
- package/dist/chunk-R5U7XKVJ.js +0 -16
- package/dist/compat-CI8hiulX.d.ts +0 -376
- package/dist/compat.d.cts +0 -1
- package/dist/engine/index.d.cts +0 -922
- package/dist/index.d.cts +0 -11008
package/dist/engine/index.js
CHANGED
|
@@ -1,26 +1,52 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
19
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, {
|
|
22
|
+
get: all[name],
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
set: (newValue) => all[name] = () => newValue
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
var __require = import.meta.require;
|
|
29
|
+
|
|
30
|
+
// src/engine/index.ts
|
|
31
|
+
var exports_engine = {};
|
|
32
|
+
__export(exports_engine, {
|
|
33
|
+
extractPath: () => extractPath,
|
|
34
|
+
ObjectPool: () => ObjectPool,
|
|
35
|
+
MinimalContext: () => MinimalContext,
|
|
36
|
+
Gravito: () => Gravito,
|
|
37
|
+
FastContextImpl: () => FastContext,
|
|
38
|
+
AOTRouter: () => AOTRouter
|
|
39
|
+
});
|
|
40
|
+
|
|
1
41
|
// src/adapters/bun/RadixNode.ts
|
|
2
|
-
|
|
3
|
-
// Path segment for this node (e.g., "users", ":id")
|
|
42
|
+
class RadixNode {
|
|
4
43
|
segment;
|
|
5
|
-
// Node type (Static, Param, Wildcard)
|
|
6
44
|
type;
|
|
7
|
-
|
|
8
|
-
children = /* @__PURE__ */ new Map();
|
|
9
|
-
// Specialized child for parameter node (only one per level allowed usually to avoid ambiguity, though some routers support multiple)
|
|
45
|
+
children = new Map;
|
|
10
46
|
paramChild = null;
|
|
11
|
-
// Specialized child for wildcard node
|
|
12
47
|
wildcardChild = null;
|
|
13
|
-
|
|
14
|
-
handlers = /* @__PURE__ */ new Map();
|
|
15
|
-
// Parameter name if this is a PARAM node (e.g., "id" for ":id")
|
|
48
|
+
handlers = new Map;
|
|
16
49
|
paramName = null;
|
|
17
|
-
// Parameter constraints (regex) - only applicable if this is a PARAM node
|
|
18
|
-
// If we support per-route constraints, they might need to be stored differently,
|
|
19
|
-
// but for now assume constraints are defined at node level (uncommon) or checked at match time.
|
|
20
|
-
// Laravel allows global pattern constraints or per-route.
|
|
21
|
-
// Ideally, constraints should be stored with the handler or part of matching logic.
|
|
22
|
-
// For a Radix tree, if we have constraints, we might need to backtrack if constraint fails?
|
|
23
|
-
// Or simply store constraint with the param node.
|
|
24
50
|
regex = null;
|
|
25
51
|
constructor(segment = "", type = 0 /* STATIC */) {
|
|
26
52
|
this.segment = segment;
|
|
@@ -38,44 +64,61 @@ var RadixNode = class _RadixNode {
|
|
|
38
64
|
};
|
|
39
65
|
}
|
|
40
66
|
static fromJSON(json) {
|
|
41
|
-
const node = new
|
|
67
|
+
const node = new RadixNode(json.segment, json.type);
|
|
42
68
|
node.paramName = json.paramName;
|
|
43
69
|
if (json.regex) {
|
|
44
70
|
node.regex = new RegExp(json.regex);
|
|
45
71
|
}
|
|
46
72
|
if (json.children) {
|
|
47
73
|
for (const [key, childJson] of json.children) {
|
|
48
|
-
node.children.set(key,
|
|
74
|
+
node.children.set(key, RadixNode.fromJSON(childJson));
|
|
49
75
|
}
|
|
50
76
|
}
|
|
51
77
|
if (json.paramChild) {
|
|
52
|
-
node.paramChild =
|
|
78
|
+
node.paramChild = RadixNode.fromJSON(json.paramChild);
|
|
53
79
|
}
|
|
54
80
|
if (json.wildcardChild) {
|
|
55
|
-
node.wildcardChild =
|
|
81
|
+
node.wildcardChild = RadixNode.fromJSON(json.wildcardChild);
|
|
56
82
|
}
|
|
57
83
|
return node;
|
|
58
84
|
}
|
|
59
|
-
}
|
|
85
|
+
}
|
|
60
86
|
|
|
61
87
|
// src/adapters/bun/RadixRouter.ts
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
88
|
+
class RouteCache {
|
|
89
|
+
cache = new Map;
|
|
90
|
+
maxSize = 1e4;
|
|
91
|
+
get(key) {
|
|
92
|
+
return this.cache.get(key);
|
|
93
|
+
}
|
|
94
|
+
set(key, value) {
|
|
95
|
+
if (this.cache.size >= this.maxSize) {
|
|
96
|
+
const firstKey = this.cache.keys().next().value;
|
|
97
|
+
if (firstKey) {
|
|
98
|
+
this.cache.delete(firstKey);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
this.cache.set(key, value);
|
|
102
|
+
}
|
|
103
|
+
clear() {
|
|
104
|
+
this.cache.clear();
|
|
105
|
+
}
|
|
106
|
+
has(key) {
|
|
107
|
+
return this.cache.has(key);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
class RadixRouter {
|
|
112
|
+
root = new RadixNode;
|
|
113
|
+
globalConstraints = new Map;
|
|
114
|
+
routeCache = new RouteCache;
|
|
69
115
|
where(param, regex) {
|
|
70
116
|
this.globalConstraints.set(param, regex);
|
|
71
117
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Register a route
|
|
74
|
-
*/
|
|
75
118
|
add(method, path, handlers) {
|
|
76
119
|
let node = this.root;
|
|
77
120
|
const segments = this.splitPath(path);
|
|
78
|
-
for (let i = 0;
|
|
121
|
+
for (let i = 0;i < segments.length; i++) {
|
|
79
122
|
const segment = segments[i];
|
|
80
123
|
if (segment === "*") {
|
|
81
124
|
if (!node.wildcardChild) {
|
|
@@ -103,10 +146,8 @@ var RadixRouter = class _RadixRouter {
|
|
|
103
146
|
}
|
|
104
147
|
}
|
|
105
148
|
node.handlers.set(method.toLowerCase(), handlers);
|
|
149
|
+
this.routeCache.clear();
|
|
106
150
|
}
|
|
107
|
-
/**
|
|
108
|
-
* Match a request
|
|
109
|
-
*/
|
|
110
151
|
match(method, path) {
|
|
111
152
|
const normalizedMethod = method.toLowerCase();
|
|
112
153
|
if (path === "/" || path === "") {
|
|
@@ -116,9 +157,15 @@ var RadixRouter = class _RadixRouter {
|
|
|
116
157
|
}
|
|
117
158
|
return null;
|
|
118
159
|
}
|
|
160
|
+
const cacheKey = `${normalizedMethod}:${path}`;
|
|
161
|
+
if (this.routeCache.has(cacheKey)) {
|
|
162
|
+
return this.routeCache.get(cacheKey) ?? null;
|
|
163
|
+
}
|
|
119
164
|
const searchPath = path.startsWith("/") ? path.slice(1) : path;
|
|
120
165
|
const segments = searchPath.split("/");
|
|
121
|
-
|
|
166
|
+
const result = this.matchRecursive(this.root, segments, 0, {}, normalizedMethod);
|
|
167
|
+
this.routeCache.set(cacheKey, result);
|
|
168
|
+
return result;
|
|
122
169
|
}
|
|
123
170
|
matchRecursive(node, segments, depth, params, method) {
|
|
124
171
|
if (depth >= segments.length) {
|
|
@@ -141,8 +188,7 @@ var RadixRouter = class _RadixRouter {
|
|
|
141
188
|
}
|
|
142
189
|
const paramChild = node.paramChild;
|
|
143
190
|
if (paramChild) {
|
|
144
|
-
if (paramChild.regex && !paramChild.regex.test(segment)) {
|
|
145
|
-
} else {
|
|
191
|
+
if (paramChild.regex && !paramChild.regex.test(segment)) {} else {
|
|
146
192
|
if (paramChild.paramName) {
|
|
147
193
|
params[paramChild.paramName] = decodeURIComponent(segment);
|
|
148
194
|
const match = this.matchRecursive(paramChild, segments, depth + 1, params, method);
|
|
@@ -177,9 +223,6 @@ var RadixRouter = class _RadixRouter {
|
|
|
177
223
|
}
|
|
178
224
|
return p.split("/");
|
|
179
225
|
}
|
|
180
|
-
/**
|
|
181
|
-
* Serialize the router to a JSON string
|
|
182
|
-
*/
|
|
183
226
|
serialize() {
|
|
184
227
|
return JSON.stringify({
|
|
185
228
|
root: this.root.toJSON(),
|
|
@@ -189,12 +232,9 @@ var RadixRouter = class _RadixRouter {
|
|
|
189
232
|
])
|
|
190
233
|
});
|
|
191
234
|
}
|
|
192
|
-
/**
|
|
193
|
-
* Restore a router from a serialized JSON string
|
|
194
|
-
*/
|
|
195
235
|
static fromSerialized(json) {
|
|
196
236
|
const data = JSON.parse(json);
|
|
197
|
-
const router = new
|
|
237
|
+
const router = new RadixRouter;
|
|
198
238
|
router.root = RadixNode.fromJSON(data.root);
|
|
199
239
|
if (data.globalConstraints) {
|
|
200
240
|
for (const [key, source] of data.globalConstraints) {
|
|
@@ -203,49 +243,22 @@ var RadixRouter = class _RadixRouter {
|
|
|
203
243
|
}
|
|
204
244
|
return router;
|
|
205
245
|
}
|
|
206
|
-
}
|
|
246
|
+
}
|
|
207
247
|
|
|
208
248
|
// src/engine/AOTRouter.ts
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
staticRoutes = /* @__PURE__ */ new Map();
|
|
213
|
-
// Dynamic route handler (Radix Tree)
|
|
214
|
-
dynamicRouter = new RadixRouter();
|
|
215
|
-
// Store all route definitions to support mounting/merging
|
|
216
|
-
/** @internal */
|
|
249
|
+
class AOTRouter {
|
|
250
|
+
staticRoutes = new Map;
|
|
251
|
+
dynamicRouter = new RadixRouter;
|
|
217
252
|
routeDefinitions = [];
|
|
218
|
-
// Global middleware (applies to all routes)
|
|
219
|
-
/** @internal */
|
|
220
253
|
globalMiddleware = [];
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
// 用於追蹤動態路由的模式,防止高基數問題
|
|
226
|
-
dynamicRoutePatterns = /* @__PURE__ */ new Map();
|
|
227
|
-
middlewareCache = /* @__PURE__ */ new Map();
|
|
228
|
-
cacheMaxSize = 1e3;
|
|
254
|
+
pathMiddleware = new Map;
|
|
255
|
+
dynamicRoutePatterns = new Map;
|
|
256
|
+
middlewareCache = new Map;
|
|
257
|
+
cacheMaxSize = 1000;
|
|
229
258
|
_version = 0;
|
|
230
|
-
/**
|
|
231
|
-
* Get the current version for cache invalidation
|
|
232
|
-
* Incremented whenever middleware or routes are modified
|
|
233
|
-
*/
|
|
234
259
|
get version() {
|
|
235
260
|
return this._version;
|
|
236
261
|
}
|
|
237
|
-
/**
|
|
238
|
-
* Register a route
|
|
239
|
-
*
|
|
240
|
-
* Automatically determines if route is static or dynamic.
|
|
241
|
-
* Static routes are stored in a Map for O(1) lookup.
|
|
242
|
-
* Dynamic routes use the Radix Tree.
|
|
243
|
-
*
|
|
244
|
-
* @param method - HTTP method
|
|
245
|
-
* @param path - Route path
|
|
246
|
-
* @param handler - Route handler
|
|
247
|
-
* @param middleware - Route-specific middleware
|
|
248
|
-
*/
|
|
249
262
|
add(method, path, handler, middleware = []) {
|
|
250
263
|
this.routeDefinitions.push({ method, path, handler, middleware });
|
|
251
264
|
const normalizedMethod = method.toLowerCase();
|
|
@@ -261,9 +274,6 @@ var AOTRouter = class {
|
|
|
261
274
|
}
|
|
262
275
|
}
|
|
263
276
|
}
|
|
264
|
-
/**
|
|
265
|
-
* Mount another router at a prefix
|
|
266
|
-
*/
|
|
267
277
|
mount(prefix, other) {
|
|
268
278
|
if (other.globalMiddleware.length > 0) {
|
|
269
279
|
this.usePattern(prefix, ...other.globalMiddleware);
|
|
@@ -271,7 +281,8 @@ var AOTRouter = class {
|
|
|
271
281
|
this.usePattern(wildcard, ...other.globalMiddleware);
|
|
272
282
|
}
|
|
273
283
|
for (const [pattern, mws] of other.pathMiddleware) {
|
|
274
|
-
|
|
284
|
+
const hasMethodPrefix = /^(get|post|put|delete|patch|options|head):/.test(pattern);
|
|
285
|
+
if (hasMethodPrefix) {
|
|
275
286
|
continue;
|
|
276
287
|
}
|
|
277
288
|
let newPattern;
|
|
@@ -296,25 +307,10 @@ var AOTRouter = class {
|
|
|
296
307
|
this.add(def.method, newPath, def.handler, def.middleware);
|
|
297
308
|
}
|
|
298
309
|
}
|
|
299
|
-
/**
|
|
300
|
-
* Add global middleware
|
|
301
|
-
*
|
|
302
|
-
* These run for every request, before route-specific middleware.
|
|
303
|
-
*
|
|
304
|
-
* @param middleware - Middleware functions
|
|
305
|
-
*/
|
|
306
310
|
use(...middleware) {
|
|
307
311
|
this.globalMiddleware.push(...middleware);
|
|
308
312
|
this._version++;
|
|
309
313
|
}
|
|
310
|
-
/**
|
|
311
|
-
* Add path-based middleware
|
|
312
|
-
*
|
|
313
|
-
* Supports wildcard patterns like '/api/*'
|
|
314
|
-
*
|
|
315
|
-
* @param pattern - Path pattern
|
|
316
|
-
* @param middleware - Middleware functions
|
|
317
|
-
*/
|
|
318
314
|
usePattern(pattern, ...middleware) {
|
|
319
315
|
if (pattern === "*") {
|
|
320
316
|
this.globalMiddleware.push(...middleware);
|
|
@@ -324,15 +320,6 @@ var AOTRouter = class {
|
|
|
324
320
|
}
|
|
325
321
|
this._version++;
|
|
326
322
|
}
|
|
327
|
-
/**
|
|
328
|
-
* Match a request to a route
|
|
329
|
-
*
|
|
330
|
-
* Returns the handler, params, and all applicable middleware.
|
|
331
|
-
*
|
|
332
|
-
* @param method - HTTP method
|
|
333
|
-
* @param path - Request path
|
|
334
|
-
* @returns Route match or null if not found
|
|
335
|
-
*/
|
|
336
323
|
match(method, path) {
|
|
337
324
|
const normalizedMethod = method.toLowerCase();
|
|
338
325
|
const staticKey = `${normalizedMethod}:${path}`;
|
|
@@ -365,28 +352,16 @@ var AOTRouter = class {
|
|
|
365
352
|
middleware: []
|
|
366
353
|
};
|
|
367
354
|
}
|
|
368
|
-
/**
|
|
369
|
-
* Public wrapper for collectMiddleware (used by Gravito for optimization)
|
|
370
|
-
*/
|
|
371
355
|
collectMiddlewarePublic(path, routeMiddleware) {
|
|
372
356
|
return this.collectMiddleware(path, routeMiddleware);
|
|
373
357
|
}
|
|
374
|
-
/**
|
|
375
|
-
* Collect all applicable middleware for a path
|
|
376
|
-
*
|
|
377
|
-
* Order: global -> pattern-based -> route-specific
|
|
378
|
-
*
|
|
379
|
-
* @param path - Request path
|
|
380
|
-
* @param routeMiddleware - Route-specific middleware
|
|
381
|
-
* @returns Combined middleware array
|
|
382
|
-
*/
|
|
383
358
|
collectMiddleware(path, routeMiddleware) {
|
|
384
359
|
if (this.globalMiddleware.length === 0 && this.pathMiddleware.size === 0 && routeMiddleware.length === 0) {
|
|
385
360
|
return [];
|
|
386
361
|
}
|
|
387
362
|
const cacheKey = path;
|
|
388
363
|
const cached = this.middlewareCache.get(cacheKey);
|
|
389
|
-
if (cached !==
|
|
364
|
+
if (cached !== undefined && cached.version === this._version) {
|
|
390
365
|
return cached.data;
|
|
391
366
|
}
|
|
392
367
|
const middleware = [];
|
|
@@ -413,23 +388,21 @@ var AOTRouter = class {
|
|
|
413
388
|
}
|
|
414
389
|
return middleware;
|
|
415
390
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
391
|
+
getNativeRoutes(onMatch) {
|
|
392
|
+
const routes = {};
|
|
393
|
+
for (const [key, metadata] of this.staticRoutes) {
|
|
394
|
+
const [method, path] = key.split(":");
|
|
395
|
+
if (method !== "get") {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
const allMiddleware = this.collectMiddleware(path, metadata.middleware);
|
|
399
|
+
routes[path] = onMatch(metadata.handler, allMiddleware, path);
|
|
400
|
+
}
|
|
401
|
+
return routes;
|
|
402
|
+
}
|
|
419
403
|
isStaticPath(path) {
|
|
420
404
|
return !path.includes(":") && !path.includes("*");
|
|
421
405
|
}
|
|
422
|
-
/**
|
|
423
|
-
* Match a pattern against a path
|
|
424
|
-
*
|
|
425
|
-
* Supports:
|
|
426
|
-
* - Exact match: '/api/users'
|
|
427
|
-
* - Wildcard suffix: '/api/*'
|
|
428
|
-
*
|
|
429
|
-
* @param pattern - Pattern to match
|
|
430
|
-
* @param path - Path to test
|
|
431
|
-
* @returns True if pattern matches
|
|
432
|
-
*/
|
|
433
406
|
matchPattern(pattern, path) {
|
|
434
407
|
if (pattern === "*") {
|
|
435
408
|
return true;
|
|
@@ -443,9 +416,6 @@ var AOTRouter = class {
|
|
|
443
416
|
}
|
|
444
417
|
return false;
|
|
445
418
|
}
|
|
446
|
-
/**
|
|
447
|
-
* Get all registered routes (for debugging)
|
|
448
|
-
*/
|
|
449
419
|
getRoutes() {
|
|
450
420
|
const routes = [];
|
|
451
421
|
for (const key of this.staticRoutes.keys()) {
|
|
@@ -454,11 +424,107 @@ var AOTRouter = class {
|
|
|
454
424
|
}
|
|
455
425
|
return routes;
|
|
456
426
|
}
|
|
457
|
-
}
|
|
427
|
+
}
|
|
458
428
|
|
|
459
|
-
// src/
|
|
460
|
-
|
|
461
|
-
|
|
429
|
+
// src/transpiler-utils.ts
|
|
430
|
+
class TranspilerCache {
|
|
431
|
+
static instance = null;
|
|
432
|
+
transpiler;
|
|
433
|
+
cache;
|
|
434
|
+
maxSize;
|
|
435
|
+
ttlMs;
|
|
436
|
+
constructor(maxSize = 512, ttlMs = 5 * 60 * 1000) {
|
|
437
|
+
this.transpiler = new Bun.Transpiler({ loader: "ts" });
|
|
438
|
+
this.cache = new Map;
|
|
439
|
+
this.maxSize = maxSize;
|
|
440
|
+
this.ttlMs = ttlMs;
|
|
441
|
+
}
|
|
442
|
+
static getInstance() {
|
|
443
|
+
TranspilerCache.instance ??= new TranspilerCache;
|
|
444
|
+
return TranspilerCache.instance;
|
|
445
|
+
}
|
|
446
|
+
static resetInstance() {
|
|
447
|
+
TranspilerCache.instance = null;
|
|
448
|
+
}
|
|
449
|
+
transform(source) {
|
|
450
|
+
const cached = this.cache.get(source);
|
|
451
|
+
if (cached !== undefined) {
|
|
452
|
+
if (Date.now() - cached.createdAt < this.ttlMs) {
|
|
453
|
+
return cached.transformed;
|
|
454
|
+
}
|
|
455
|
+
this.cache.delete(source);
|
|
456
|
+
}
|
|
457
|
+
const transformed = this.doTransform(source);
|
|
458
|
+
if (transformed === null) {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
if (this.cache.size >= this.maxSize) {
|
|
462
|
+
const firstKey = this.cache.keys().next().value;
|
|
463
|
+
if (firstKey !== undefined) {
|
|
464
|
+
this.cache.delete(firstKey);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
this.cache.set(source, { transformed, createdAt: Date.now() });
|
|
468
|
+
return transformed;
|
|
469
|
+
}
|
|
470
|
+
doTransform(source) {
|
|
471
|
+
try {
|
|
472
|
+
const out = this.transpiler.transformSync(source);
|
|
473
|
+
if (out.trim().length > 0) {
|
|
474
|
+
return out;
|
|
475
|
+
}
|
|
476
|
+
return this.transformWrapped(source);
|
|
477
|
+
} catch {
|
|
478
|
+
return this.transformWrapped(source);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
transformWrapped(source) {
|
|
482
|
+
try {
|
|
483
|
+
const wrapped = `const __fn = ${source}`;
|
|
484
|
+
const out = this.transpiler.transformSync(wrapped);
|
|
485
|
+
return out.trim().length > 0 ? out : null;
|
|
486
|
+
} catch {
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
get size() {
|
|
491
|
+
return this.cache.size;
|
|
492
|
+
}
|
|
493
|
+
clear() {
|
|
494
|
+
this.cache.clear();
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
var PATTERNS = {
|
|
498
|
+
HEADERS_CALL: /\.req\.headers?\s*\(/,
|
|
499
|
+
HEADERS_DESTR: /\{[^}]*\bheaders?\b[^}]*\}\s*=\s*\w+\.req/,
|
|
500
|
+
QUERY_CALL: /\.req\.quer(?:y|ies)\s*\(/,
|
|
501
|
+
QUERY_DESTR: /\{[^}]*\bquer(?:y|ies)\b[^}]*\}\s*=\s*\w+\.req/,
|
|
502
|
+
BODY_CALL: /\.req\.(?:json|text|formData|blob|arrayBuffer)\s*\(/,
|
|
503
|
+
BODY_DESTR: /\{[^}]*\bbody\b[^}]*\}\s*=\s*\w+\.req/,
|
|
504
|
+
BODY_PROP: /\.req\.body\b/,
|
|
505
|
+
PARAMS_CALL: /\.req\.params?\s*\(/,
|
|
506
|
+
PARAMS_DESTR: /\{[^}]*\bparams?\b[^}]*\}\s*=\s*\w+\.req/,
|
|
507
|
+
IS_ASYNC: /\basync\b/
|
|
508
|
+
};
|
|
509
|
+
function analyzeHandlerWithTranspiler(source) {
|
|
510
|
+
const cache = TranspilerCache.getInstance();
|
|
511
|
+
const transformed = cache.transform(source);
|
|
512
|
+
const isAsync = PATTERNS.IS_ASYNC.test(source);
|
|
513
|
+
if (transformed !== null) {
|
|
514
|
+
return { ...analyzeTransformedCode(transformed), isAsync };
|
|
515
|
+
}
|
|
516
|
+
return { ...fallbackStringAnalysis(source), isAsync };
|
|
517
|
+
}
|
|
518
|
+
function analyzeTransformedCode(code) {
|
|
519
|
+
return {
|
|
520
|
+
usesHeaders: PATTERNS.HEADERS_CALL.test(code) || PATTERNS.HEADERS_DESTR.test(code),
|
|
521
|
+
usesQuery: PATTERNS.QUERY_CALL.test(code) || PATTERNS.QUERY_DESTR.test(code),
|
|
522
|
+
usesBody: PATTERNS.BODY_CALL.test(code) || PATTERNS.BODY_DESTR.test(code) || PATTERNS.BODY_PROP.test(code),
|
|
523
|
+
usesParams: PATTERNS.PARAMS_CALL.test(code) || PATTERNS.PARAMS_DESTR.test(code),
|
|
524
|
+
isAsync: PATTERNS.IS_ASYNC.test(code)
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
function fallbackStringAnalysis(source) {
|
|
462
528
|
return {
|
|
463
529
|
usesHeaders: source.includes(".header(") || source.includes(".header)") || source.includes(".headers(") || source.includes(".headers)"),
|
|
464
530
|
usesQuery: source.includes(".query(") || source.includes(".query)") || source.includes(".queries(") || source.includes(".queries)"),
|
|
@@ -467,6 +533,12 @@ function analyzeHandler(handler) {
|
|
|
467
533
|
isAsync: source.includes("async") || source.includes("await")
|
|
468
534
|
};
|
|
469
535
|
}
|
|
536
|
+
|
|
537
|
+
// src/engine/analyzer.ts
|
|
538
|
+
function analyzeHandler(handler) {
|
|
539
|
+
const source = handler.toString();
|
|
540
|
+
return analyzeHandlerWithTranspiler(source);
|
|
541
|
+
}
|
|
470
542
|
function getOptimalContextType(analysis) {
|
|
471
543
|
if (analysis.usesHeaders) {
|
|
472
544
|
return "fast";
|
|
@@ -484,7 +556,7 @@ function getOptimalContextType(analysis) {
|
|
|
484
556
|
}
|
|
485
557
|
|
|
486
558
|
// src/engine/constants.ts
|
|
487
|
-
var encoder = new TextEncoder
|
|
559
|
+
var encoder = new TextEncoder;
|
|
488
560
|
var CACHED_RESPONSES = {
|
|
489
561
|
NOT_FOUND: encoder.encode('{"error":"Not Found"}'),
|
|
490
562
|
INTERNAL_ERROR: encoder.encode('{"error":"Internal Server Error"}'),
|
|
@@ -498,25 +570,15 @@ var HEADERS = {
|
|
|
498
570
|
};
|
|
499
571
|
|
|
500
572
|
// src/Container/RequestScopeMetrics.ts
|
|
501
|
-
|
|
573
|
+
class RequestScopeMetrics {
|
|
502
574
|
cleanupStartTime = null;
|
|
503
575
|
cleanupDuration = null;
|
|
504
576
|
scopeSize = 0;
|
|
505
577
|
servicesCleaned = 0;
|
|
506
578
|
errorsOccurred = 0;
|
|
507
|
-
/**
|
|
508
|
-
* Record start of cleanup operation
|
|
509
|
-
*/
|
|
510
579
|
recordCleanupStart() {
|
|
511
580
|
this.cleanupStartTime = performance.now();
|
|
512
581
|
}
|
|
513
|
-
/**
|
|
514
|
-
* Record end of cleanup operation
|
|
515
|
-
*
|
|
516
|
-
* @param scopeSize - Number of services in the scope
|
|
517
|
-
* @param servicesCleaned - Number of services that had cleanup called
|
|
518
|
-
* @param errorsOccurred - Number of cleanup errors
|
|
519
|
-
*/
|
|
520
582
|
recordCleanupEnd(scopeSize, servicesCleaned, errorsOccurred = 0) {
|
|
521
583
|
if (this.cleanupStartTime !== null) {
|
|
522
584
|
this.cleanupDuration = performance.now() - this.cleanupStartTime;
|
|
@@ -526,28 +588,15 @@ var RequestScopeMetrics = class {
|
|
|
526
588
|
this.servicesCleaned = servicesCleaned;
|
|
527
589
|
this.errorsOccurred = errorsOccurred;
|
|
528
590
|
}
|
|
529
|
-
/**
|
|
530
|
-
* Get cleanup duration in milliseconds
|
|
531
|
-
*
|
|
532
|
-
* @returns Duration in ms, or null if cleanup not completed
|
|
533
|
-
*/
|
|
534
591
|
getCleanupDuration() {
|
|
535
592
|
return this.cleanupDuration;
|
|
536
593
|
}
|
|
537
|
-
/**
|
|
538
|
-
* Check if cleanup took longer than threshold (default 2ms)
|
|
539
|
-
* Useful for detecting slow cleanups
|
|
540
|
-
*
|
|
541
|
-
* @param thresholdMs - Threshold in milliseconds
|
|
542
|
-
* @returns True if cleanup exceeded threshold
|
|
543
|
-
*/
|
|
544
594
|
isSlowCleanup(thresholdMs = 2) {
|
|
545
|
-
if (this.cleanupDuration === null)
|
|
595
|
+
if (this.cleanupDuration === null) {
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
546
598
|
return this.cleanupDuration > thresholdMs;
|
|
547
599
|
}
|
|
548
|
-
/**
|
|
549
|
-
* Export metrics as JSON for logging/monitoring
|
|
550
|
-
*/
|
|
551
600
|
toJSON() {
|
|
552
601
|
return {
|
|
553
602
|
cleanupDuration: this.cleanupDuration,
|
|
@@ -558,49 +607,66 @@ var RequestScopeMetrics = class {
|
|
|
558
607
|
isSlowCleanup: this.isSlowCleanup()
|
|
559
608
|
};
|
|
560
609
|
}
|
|
561
|
-
/**
|
|
562
|
-
* Export metrics as compact string for logging
|
|
563
|
-
*/
|
|
564
610
|
toString() {
|
|
565
611
|
const duration = this.cleanupDuration ?? "pending";
|
|
566
|
-
return `cleanup: ${duration}ms, scope: ${this.scopeSize}, cleaned: ${this.servicesCleaned}, errors: ${this.errorsOccurred}`;
|
|
612
|
+
return `cleanup: ${duration}ms, ` + `scope: ${this.scopeSize}, ` + `cleaned: ${this.servicesCleaned}, ` + `errors: ${this.errorsOccurred}`;
|
|
567
613
|
}
|
|
568
|
-
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
class RequestScopeMetricsCollector {
|
|
617
|
+
metrics = [];
|
|
618
|
+
record(metrics) {
|
|
619
|
+
this.metrics.push(metrics);
|
|
620
|
+
}
|
|
621
|
+
getStats() {
|
|
622
|
+
if (this.metrics.length === 0) {
|
|
623
|
+
return {
|
|
624
|
+
count: 0,
|
|
625
|
+
averageCleanupTime: null,
|
|
626
|
+
maxCleanupTime: null,
|
|
627
|
+
minCleanupTime: null,
|
|
628
|
+
totalErrorCount: 0,
|
|
629
|
+
errorRate: 0
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
const durations = this.metrics.map((m) => m.getCleanupDuration()).filter((d) => d !== null);
|
|
633
|
+
const errorCounts = this.metrics.map((m) => m.toJSON().errorsOccurred);
|
|
634
|
+
const totalErrors = errorCounts.reduce((a, b) => a + b, 0);
|
|
635
|
+
return {
|
|
636
|
+
count: this.metrics.length,
|
|
637
|
+
averageCleanupTime: durations.length > 0 ? durations.reduce((a, b) => a + b) / durations.length : null,
|
|
638
|
+
maxCleanupTime: durations.length > 0 ? Math.max(...durations) : null,
|
|
639
|
+
minCleanupTime: durations.length > 0 ? Math.min(...durations) : null,
|
|
640
|
+
totalErrorCount: totalErrors,
|
|
641
|
+
errorRate: totalErrors / this.metrics.length
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
clear() {
|
|
645
|
+
this.metrics = [];
|
|
646
|
+
}
|
|
647
|
+
size() {
|
|
648
|
+
return this.metrics.length;
|
|
649
|
+
}
|
|
650
|
+
toJSON() {
|
|
651
|
+
return this.metrics.map((m) => m.toJSON());
|
|
652
|
+
}
|
|
653
|
+
}
|
|
569
654
|
|
|
570
655
|
// src/Container/RequestScopeManager.ts
|
|
571
|
-
|
|
572
|
-
scoped =
|
|
573
|
-
metadata =
|
|
574
|
-
metrics = new RequestScopeMetrics
|
|
656
|
+
class RequestScopeManager {
|
|
657
|
+
scoped = new Map;
|
|
658
|
+
metadata = new Map;
|
|
659
|
+
metrics = new RequestScopeMetrics;
|
|
575
660
|
observer = null;
|
|
576
661
|
constructor(observer) {
|
|
577
662
|
this.observer = observer || null;
|
|
578
663
|
}
|
|
579
|
-
/**
|
|
580
|
-
* Set observer for monitoring scope lifecycle
|
|
581
|
-
*/
|
|
582
664
|
setObserver(observer) {
|
|
583
665
|
this.observer = observer;
|
|
584
666
|
}
|
|
585
|
-
/**
|
|
586
|
-
* Get metrics for this scope
|
|
587
|
-
*/
|
|
588
667
|
getMetrics() {
|
|
589
668
|
return this.metrics;
|
|
590
669
|
}
|
|
591
|
-
/**
|
|
592
|
-
* Resolve or retrieve a request-scoped service instance.
|
|
593
|
-
*
|
|
594
|
-
* If the service already exists in this scope, returns the cached instance.
|
|
595
|
-
* Otherwise, calls the factory function to create a new instance and caches it.
|
|
596
|
-
*
|
|
597
|
-
* Automatically detects and records services with cleanup methods.
|
|
598
|
-
*
|
|
599
|
-
* @template T - The type of the service.
|
|
600
|
-
* @param key - The service key (for caching).
|
|
601
|
-
* @param factory - Factory function to create the instance if not cached.
|
|
602
|
-
* @returns The cached or newly created instance.
|
|
603
|
-
*/
|
|
604
670
|
resolve(key, factory) {
|
|
605
671
|
const keyStr = String(key);
|
|
606
672
|
const isFromCache = this.scoped.has(keyStr);
|
|
@@ -614,15 +680,6 @@ var RequestScopeManager = class {
|
|
|
614
680
|
this.observer?.onServiceResolved?.(key, isFromCache);
|
|
615
681
|
return this.scoped.get(keyStr);
|
|
616
682
|
}
|
|
617
|
-
/**
|
|
618
|
-
* Clean up all request-scoped instances.
|
|
619
|
-
*
|
|
620
|
-
* Calls the cleanup() method on each service that has one.
|
|
621
|
-
* Silently ignores cleanup errors to prevent cascading failures.
|
|
622
|
-
* Called automatically by the Gravito engine in the request finally block.
|
|
623
|
-
*
|
|
624
|
-
* @returns Promise that resolves when all cleanup is complete.
|
|
625
|
-
*/
|
|
626
683
|
async cleanup() {
|
|
627
684
|
this.metrics.recordCleanupStart();
|
|
628
685
|
this.observer?.onCleanupStart?.();
|
|
@@ -637,9 +694,7 @@ var RequestScopeManager = class {
|
|
|
637
694
|
servicesCleaned++;
|
|
638
695
|
} catch (error) {
|
|
639
696
|
errors.push(error);
|
|
640
|
-
this.observer?.onCleanupError?.(
|
|
641
|
-
error instanceof Error ? error : new Error(String(error))
|
|
642
|
-
);
|
|
697
|
+
this.observer?.onCleanupError?.(error instanceof Error ? error : new Error(String(error)));
|
|
643
698
|
}
|
|
644
699
|
}
|
|
645
700
|
}
|
|
@@ -653,18 +708,15 @@ var RequestScopeManager = class {
|
|
|
653
708
|
console.error("RequestScope cleanup errors:", errors);
|
|
654
709
|
}
|
|
655
710
|
}
|
|
656
|
-
/**
|
|
657
|
-
* Get the number of services in this scope (for monitoring).
|
|
658
|
-
*
|
|
659
|
-
* @returns The count of cached services.
|
|
660
|
-
*/
|
|
661
711
|
size() {
|
|
662
712
|
return this.scoped.size;
|
|
663
713
|
}
|
|
664
|
-
}
|
|
714
|
+
}
|
|
665
715
|
|
|
666
716
|
// src/engine/FastContext.ts
|
|
667
|
-
var
|
|
717
|
+
var bunEscapeHTML = globalThis.Bun.escapeHTML;
|
|
718
|
+
|
|
719
|
+
class FastRequestImpl {
|
|
668
720
|
_request;
|
|
669
721
|
_params;
|
|
670
722
|
_path;
|
|
@@ -672,21 +724,18 @@ var FastRequestImpl = class {
|
|
|
672
724
|
_url = null;
|
|
673
725
|
_query = null;
|
|
674
726
|
_headers = null;
|
|
675
|
-
_cachedJson =
|
|
727
|
+
_cachedJson = undefined;
|
|
676
728
|
_jsonParsed = false;
|
|
677
|
-
_cachedText =
|
|
729
|
+
_cachedText = undefined;
|
|
678
730
|
_textParsed = false;
|
|
679
|
-
_cachedFormData =
|
|
731
|
+
_cachedFormData = undefined;
|
|
680
732
|
_formDataParsed = false;
|
|
681
733
|
_cachedQueries = null;
|
|
682
|
-
|
|
734
|
+
_cachedCookies = null;
|
|
683
735
|
_ctx;
|
|
684
736
|
constructor(ctx) {
|
|
685
737
|
this._ctx = ctx;
|
|
686
738
|
}
|
|
687
|
-
/**
|
|
688
|
-
* Initialize for new request
|
|
689
|
-
*/
|
|
690
739
|
init(request, params = {}, path = "", routePattern) {
|
|
691
740
|
this._request = request;
|
|
692
741
|
this._params = params;
|
|
@@ -695,37 +744,34 @@ var FastRequestImpl = class {
|
|
|
695
744
|
this._url = null;
|
|
696
745
|
this._query = null;
|
|
697
746
|
this._headers = null;
|
|
698
|
-
this._cachedJson =
|
|
747
|
+
this._cachedJson = undefined;
|
|
699
748
|
this._jsonParsed = false;
|
|
700
|
-
this._cachedText =
|
|
749
|
+
this._cachedText = undefined;
|
|
701
750
|
this._textParsed = false;
|
|
702
|
-
this._cachedFormData =
|
|
751
|
+
this._cachedFormData = undefined;
|
|
703
752
|
this._formDataParsed = false;
|
|
704
753
|
this._cachedQueries = null;
|
|
754
|
+
this._cachedCookies = null;
|
|
705
755
|
return this;
|
|
706
756
|
}
|
|
707
|
-
/**
|
|
708
|
-
* Reset for pooling
|
|
709
|
-
*/
|
|
710
757
|
reset() {
|
|
711
|
-
this._request =
|
|
712
|
-
this._params =
|
|
758
|
+
this._request = undefined;
|
|
759
|
+
this._params = undefined;
|
|
713
760
|
this._url = null;
|
|
714
761
|
this._query = null;
|
|
715
762
|
this._headers = null;
|
|
716
|
-
this._cachedJson =
|
|
763
|
+
this._cachedJson = undefined;
|
|
717
764
|
this._jsonParsed = false;
|
|
718
|
-
this._cachedText =
|
|
765
|
+
this._cachedText = undefined;
|
|
719
766
|
this._textParsed = false;
|
|
720
|
-
this._cachedFormData =
|
|
767
|
+
this._cachedFormData = undefined;
|
|
721
768
|
this._formDataParsed = false;
|
|
722
769
|
this._cachedQueries = null;
|
|
770
|
+
this._cachedCookies = null;
|
|
723
771
|
}
|
|
724
772
|
checkReleased() {
|
|
725
773
|
if (this._ctx._isReleased) {
|
|
726
|
-
throw new Error(
|
|
727
|
-
"FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)"
|
|
728
|
-
);
|
|
774
|
+
throw new Error("FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)");
|
|
729
775
|
}
|
|
730
776
|
}
|
|
731
777
|
get url() {
|
|
@@ -763,7 +809,7 @@ var FastRequestImpl = class {
|
|
|
763
809
|
if (!this._query) {
|
|
764
810
|
this._query = this.getUrl().searchParams;
|
|
765
811
|
}
|
|
766
|
-
return this._query.get(name) ??
|
|
812
|
+
return this._query.get(name) ?? undefined;
|
|
767
813
|
}
|
|
768
814
|
queries() {
|
|
769
815
|
this.checkReleased();
|
|
@@ -776,7 +822,7 @@ var FastRequestImpl = class {
|
|
|
776
822
|
const result = {};
|
|
777
823
|
for (const [key, value] of this._query.entries()) {
|
|
778
824
|
const existing = result[key];
|
|
779
|
-
if (existing ===
|
|
825
|
+
if (existing === undefined) {
|
|
780
826
|
result[key] = value;
|
|
781
827
|
} else if (Array.isArray(existing)) {
|
|
782
828
|
existing.push(value);
|
|
@@ -789,7 +835,7 @@ var FastRequestImpl = class {
|
|
|
789
835
|
}
|
|
790
836
|
header(name) {
|
|
791
837
|
this.checkReleased();
|
|
792
|
-
return this._request.headers.get(name) ??
|
|
838
|
+
return this._request.headers.get(name) ?? undefined;
|
|
793
839
|
}
|
|
794
840
|
headers() {
|
|
795
841
|
this.checkReleased();
|
|
@@ -801,6 +847,39 @@ var FastRequestImpl = class {
|
|
|
801
847
|
}
|
|
802
848
|
return { ...this._headers };
|
|
803
849
|
}
|
|
850
|
+
get cookies() {
|
|
851
|
+
this.checkReleased();
|
|
852
|
+
if (this._cachedCookies !== null) {
|
|
853
|
+
return this._cachedCookies;
|
|
854
|
+
}
|
|
855
|
+
const nativeCookies = this._request.cookies;
|
|
856
|
+
if (nativeCookies) {
|
|
857
|
+
this._cachedCookies = nativeCookies;
|
|
858
|
+
return nativeCookies;
|
|
859
|
+
}
|
|
860
|
+
const cookieHeader = this._request.headers.get("cookie");
|
|
861
|
+
if (!cookieHeader) {
|
|
862
|
+
this._cachedCookies = {};
|
|
863
|
+
return {};
|
|
864
|
+
}
|
|
865
|
+
const cookies = {};
|
|
866
|
+
const pairs = cookieHeader.split(/;\s*/);
|
|
867
|
+
for (let i = 0;i < pairs.length; i++) {
|
|
868
|
+
const pair = pairs[i];
|
|
869
|
+
const idx = pair.indexOf("=");
|
|
870
|
+
if (idx > 0) {
|
|
871
|
+
const name = pair.substring(0, idx);
|
|
872
|
+
const value = pair.substring(idx + 1);
|
|
873
|
+
try {
|
|
874
|
+
cookies[name] = decodeURIComponent(value);
|
|
875
|
+
} catch {
|
|
876
|
+
cookies[name] = value;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
this._cachedCookies = cookies;
|
|
881
|
+
return cookies;
|
|
882
|
+
}
|
|
804
883
|
async json() {
|
|
805
884
|
this.checkReleased();
|
|
806
885
|
if (!this._jsonParsed) {
|
|
@@ -829,82 +908,57 @@ var FastRequestImpl = class {
|
|
|
829
908
|
this.checkReleased();
|
|
830
909
|
return this._request;
|
|
831
910
|
}
|
|
832
|
-
}
|
|
833
|
-
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
class FastContext {
|
|
834
914
|
req = new FastRequestImpl(this);
|
|
835
|
-
|
|
836
|
-
_headers = new Headers();
|
|
837
|
-
// Reuse this object
|
|
915
|
+
_headers = new Headers;
|
|
838
916
|
_isReleased = false;
|
|
839
|
-
// Made public for internal check access
|
|
840
917
|
_requestScope = null;
|
|
841
|
-
// Request-scoped services
|
|
842
|
-
/**
|
|
843
|
-
* Initialize context for a new request
|
|
844
|
-
*
|
|
845
|
-
* This is called when acquiring from the pool.
|
|
846
|
-
*/
|
|
847
918
|
init(request, params = {}, path = "", routePattern) {
|
|
848
919
|
this._isReleased = false;
|
|
849
920
|
this.req.init(request, params, path, routePattern);
|
|
850
|
-
this._headers = new Headers
|
|
851
|
-
this._requestScope = new RequestScopeManager
|
|
921
|
+
this._headers = new Headers;
|
|
922
|
+
this._requestScope = new RequestScopeManager;
|
|
852
923
|
return this;
|
|
853
924
|
}
|
|
854
|
-
/**
|
|
855
|
-
* Reset context for pooling (Cleanup)
|
|
856
|
-
*
|
|
857
|
-
* This is called when releasing back to the pool.
|
|
858
|
-
* Implements "Deep-Reset Protocol" and "Release Guard".
|
|
859
|
-
*/
|
|
860
925
|
reset() {
|
|
861
926
|
this._isReleased = true;
|
|
862
927
|
this.req.reset();
|
|
863
928
|
this._store.clear();
|
|
929
|
+
this._requestScope = null;
|
|
864
930
|
}
|
|
865
|
-
/**
|
|
866
|
-
* Check if context is released
|
|
867
|
-
*/
|
|
868
931
|
checkReleased() {
|
|
869
932
|
if (this._isReleased) {
|
|
870
|
-
throw new Error(
|
|
871
|
-
"FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)"
|
|
872
|
-
);
|
|
933
|
+
throw new Error("FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)");
|
|
873
934
|
}
|
|
874
935
|
}
|
|
875
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
876
|
-
// Response Helpers
|
|
877
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
878
936
|
json(data, status = 200) {
|
|
879
937
|
this.checkReleased();
|
|
880
|
-
this._headers
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
headers: this._headers
|
|
884
|
-
});
|
|
938
|
+
const headers = new Headers(this._headers);
|
|
939
|
+
headers.set("Content-Type", "application/json; charset=utf-8");
|
|
940
|
+
return Response.json(data, { status, headers });
|
|
885
941
|
}
|
|
886
942
|
text(text, status = 200) {
|
|
887
943
|
this.checkReleased();
|
|
888
|
-
this._headers
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
headers: this._headers
|
|
892
|
-
});
|
|
944
|
+
const headers = new Headers(this._headers);
|
|
945
|
+
headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
946
|
+
return new Response(text, { status, headers });
|
|
893
947
|
}
|
|
894
948
|
html(html, status = 200) {
|
|
895
949
|
this.checkReleased();
|
|
896
|
-
this._headers
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
950
|
+
const headers = new Headers(this._headers);
|
|
951
|
+
headers.set("Content-Type", "text/html; charset=utf-8");
|
|
952
|
+
return new Response(html, { status, headers });
|
|
953
|
+
}
|
|
954
|
+
escape(html) {
|
|
955
|
+
return bunEscapeHTML(html);
|
|
901
956
|
}
|
|
902
957
|
redirect(url, status = 302) {
|
|
903
958
|
this.checkReleased();
|
|
904
|
-
this._headers.set("Location", url);
|
|
905
959
|
return new Response(null, {
|
|
906
960
|
status,
|
|
907
|
-
headers:
|
|
961
|
+
headers: { Location: url }
|
|
908
962
|
});
|
|
909
963
|
}
|
|
910
964
|
body(data, status = 200) {
|
|
@@ -914,12 +968,19 @@ var FastContext = class {
|
|
|
914
968
|
headers: this._headers
|
|
915
969
|
});
|
|
916
970
|
}
|
|
971
|
+
binary(data, status = 200) {
|
|
972
|
+
this.checkReleased();
|
|
973
|
+
const body = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
|
|
974
|
+
return new Response(body, {
|
|
975
|
+
status,
|
|
976
|
+
headers: { "Content-Type": "application/octet-stream" }
|
|
977
|
+
});
|
|
978
|
+
}
|
|
917
979
|
stream(stream, status = 200) {
|
|
918
980
|
this.checkReleased();
|
|
919
|
-
this._headers.set("Content-Type", "application/octet-stream");
|
|
920
981
|
return new Response(stream, {
|
|
921
982
|
status,
|
|
922
|
-
headers:
|
|
983
|
+
headers: { "Content-Type": "application/octet-stream" }
|
|
923
984
|
});
|
|
924
985
|
}
|
|
925
986
|
notFound(message = "Not Found") {
|
|
@@ -937,9 +998,7 @@ var FastContext = class {
|
|
|
937
998
|
async forward(target, _options = {}) {
|
|
938
999
|
this.checkReleased();
|
|
939
1000
|
const url = new URL(this.req.url);
|
|
940
|
-
const targetUrl = new URL(
|
|
941
|
-
target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`
|
|
942
|
-
);
|
|
1001
|
+
const targetUrl = new URL(target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`);
|
|
943
1002
|
const searchParams = new URLSearchParams(url.search);
|
|
944
1003
|
searchParams.forEach((v, k) => {
|
|
945
1004
|
targetUrl.searchParams.set(k, v);
|
|
@@ -948,13 +1007,12 @@ var FastContext = class {
|
|
|
948
1007
|
method: this.req.method,
|
|
949
1008
|
headers: this.req.raw.headers,
|
|
950
1009
|
body: this.req.method !== "GET" && this.req.method !== "HEAD" ? this.req.raw.body : null,
|
|
951
|
-
// @ts-expect-error - Bun/Fetch specific
|
|
952
1010
|
duplex: "half"
|
|
953
1011
|
});
|
|
954
1012
|
}
|
|
955
1013
|
header(name, value) {
|
|
956
1014
|
this.checkReleased();
|
|
957
|
-
if (value !==
|
|
1015
|
+
if (value !== undefined) {
|
|
958
1016
|
this._headers.set(name, value);
|
|
959
1017
|
return;
|
|
960
1018
|
}
|
|
@@ -963,64 +1021,47 @@ var FastContext = class {
|
|
|
963
1021
|
status(_code) {
|
|
964
1022
|
this.checkReleased();
|
|
965
1023
|
}
|
|
966
|
-
|
|
967
|
-
// Context Variables
|
|
968
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
969
|
-
_store = /* @__PURE__ */ new Map();
|
|
1024
|
+
_store = new Map;
|
|
970
1025
|
get(key) {
|
|
971
1026
|
return this._store.get(key);
|
|
972
1027
|
}
|
|
973
1028
|
set(key, value) {
|
|
974
1029
|
this._store.set(key, value);
|
|
975
1030
|
}
|
|
976
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
977
|
-
// Request Scope Management
|
|
978
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
979
|
-
/**
|
|
980
|
-
* Get the request-scoped service manager for this request.
|
|
981
|
-
*
|
|
982
|
-
* @returns The RequestScopeManager for this request.
|
|
983
|
-
* @throws Error if called before init() or after reset().
|
|
984
|
-
*/
|
|
985
1031
|
requestScope() {
|
|
986
1032
|
if (!this._requestScope) {
|
|
987
1033
|
throw new Error("RequestScope not initialized. Call init() first.");
|
|
988
1034
|
}
|
|
989
1035
|
return this._requestScope;
|
|
990
1036
|
}
|
|
991
|
-
/**
|
|
992
|
-
* Resolve a request-scoped service (convenience method).
|
|
993
|
-
*
|
|
994
|
-
* @template T - The service type.
|
|
995
|
-
* @param key - The service key for caching.
|
|
996
|
-
* @param factory - Factory function to create the service.
|
|
997
|
-
* @returns The cached or newly created service instance.
|
|
998
|
-
*/
|
|
999
1037
|
scoped(key, factory) {
|
|
1000
1038
|
return this.requestScope().resolve(key, factory);
|
|
1001
1039
|
}
|
|
1002
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1003
|
-
// Lifecycle helpers
|
|
1004
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1005
1040
|
route = () => "";
|
|
1006
1041
|
get native() {
|
|
1007
1042
|
return this;
|
|
1008
1043
|
}
|
|
1009
|
-
}
|
|
1044
|
+
}
|
|
1010
1045
|
|
|
1011
1046
|
// src/engine/MinimalContext.ts
|
|
1012
|
-
var
|
|
1047
|
+
var bunEscapeHTML2 = globalThis.Bun.escapeHTML;
|
|
1048
|
+
|
|
1049
|
+
class MinimalRequest {
|
|
1050
|
+
_request;
|
|
1051
|
+
_params;
|
|
1052
|
+
_path;
|
|
1053
|
+
_routePattern;
|
|
1054
|
+
_searchParams = null;
|
|
1055
|
+
_cachedQueries = null;
|
|
1056
|
+
_cachedJsonPromise = null;
|
|
1057
|
+
_cachedTextPromise = null;
|
|
1058
|
+
_cachedFormDataPromise = null;
|
|
1013
1059
|
constructor(_request, _params, _path, _routePattern) {
|
|
1014
1060
|
this._request = _request;
|
|
1015
1061
|
this._params = _params;
|
|
1016
1062
|
this._path = _path;
|
|
1017
1063
|
this._routePattern = _routePattern;
|
|
1018
1064
|
}
|
|
1019
|
-
_searchParams = null;
|
|
1020
|
-
_cachedQueries = null;
|
|
1021
|
-
_cachedJsonPromise = null;
|
|
1022
|
-
_cachedTextPromise = null;
|
|
1023
|
-
_cachedFormDataPromise = null;
|
|
1024
1065
|
get url() {
|
|
1025
1066
|
return this._request.url;
|
|
1026
1067
|
}
|
|
@@ -1039,15 +1080,12 @@ var MinimalRequest = class {
|
|
|
1039
1080
|
params() {
|
|
1040
1081
|
return { ...this._params };
|
|
1041
1082
|
}
|
|
1042
|
-
/**
|
|
1043
|
-
* Lazy-initialize searchParams, only parse once
|
|
1044
|
-
*/
|
|
1045
1083
|
getSearchParams() {
|
|
1046
1084
|
if (this._searchParams === null) {
|
|
1047
1085
|
const url = this._request.url;
|
|
1048
1086
|
const queryStart = url.indexOf("?");
|
|
1049
1087
|
if (queryStart === -1) {
|
|
1050
|
-
this._searchParams = new URLSearchParams
|
|
1088
|
+
this._searchParams = new URLSearchParams;
|
|
1051
1089
|
} else {
|
|
1052
1090
|
const hashStart = url.indexOf("#", queryStart);
|
|
1053
1091
|
const queryString = hashStart === -1 ? url.slice(queryStart + 1) : url.slice(queryStart + 1, hashStart);
|
|
@@ -1057,7 +1095,7 @@ var MinimalRequest = class {
|
|
|
1057
1095
|
return this._searchParams;
|
|
1058
1096
|
}
|
|
1059
1097
|
query(name) {
|
|
1060
|
-
return this.getSearchParams().get(name) ??
|
|
1098
|
+
return this.getSearchParams().get(name) ?? undefined;
|
|
1061
1099
|
}
|
|
1062
1100
|
queries() {
|
|
1063
1101
|
if (this._cachedQueries !== null) {
|
|
@@ -1067,7 +1105,7 @@ var MinimalRequest = class {
|
|
|
1067
1105
|
const result = {};
|
|
1068
1106
|
for (const [key, value] of params.entries()) {
|
|
1069
1107
|
const existing = result[key];
|
|
1070
|
-
if (existing ===
|
|
1108
|
+
if (existing === undefined) {
|
|
1071
1109
|
result[key] = value;
|
|
1072
1110
|
} else if (Array.isArray(existing)) {
|
|
1073
1111
|
existing.push(value);
|
|
@@ -1079,7 +1117,7 @@ var MinimalRequest = class {
|
|
|
1079
1117
|
return result;
|
|
1080
1118
|
}
|
|
1081
1119
|
header(name) {
|
|
1082
|
-
return this._request.headers.get(name) ??
|
|
1120
|
+
return this._request.headers.get(name) ?? undefined;
|
|
1083
1121
|
}
|
|
1084
1122
|
headers() {
|
|
1085
1123
|
const result = {};
|
|
@@ -1106,23 +1144,45 @@ var MinimalRequest = class {
|
|
|
1106
1144
|
}
|
|
1107
1145
|
return this._cachedFormDataPromise;
|
|
1108
1146
|
}
|
|
1147
|
+
get cookies() {
|
|
1148
|
+
const nativeCookies = this._request.cookies;
|
|
1149
|
+
if (nativeCookies) {
|
|
1150
|
+
return nativeCookies;
|
|
1151
|
+
}
|
|
1152
|
+
const cookieHeader = this._request.headers.get("cookie");
|
|
1153
|
+
if (!cookieHeader) {
|
|
1154
|
+
return {};
|
|
1155
|
+
}
|
|
1156
|
+
const cookies = {};
|
|
1157
|
+
const pairs = cookieHeader.split(/;\s*/);
|
|
1158
|
+
for (let i = 0;i < pairs.length; i++) {
|
|
1159
|
+
const pair = pairs[i];
|
|
1160
|
+
const idx = pair.indexOf("=");
|
|
1161
|
+
if (idx > 0) {
|
|
1162
|
+
const name = pair.substring(0, idx);
|
|
1163
|
+
const value = pair.substring(idx + 1);
|
|
1164
|
+
try {
|
|
1165
|
+
cookies[name] = decodeURIComponent(value);
|
|
1166
|
+
} catch {
|
|
1167
|
+
cookies[name] = value;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
return cookies;
|
|
1172
|
+
}
|
|
1109
1173
|
get raw() {
|
|
1110
1174
|
return this._request;
|
|
1111
1175
|
}
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
class MinimalContext {
|
|
1114
1179
|
req;
|
|
1115
1180
|
_resHeaders = {};
|
|
1116
1181
|
_requestScope;
|
|
1117
1182
|
constructor(request, params, path, routePattern) {
|
|
1118
1183
|
this.req = new MinimalRequest(request, params, path, routePattern);
|
|
1119
|
-
this._requestScope = new RequestScopeManager
|
|
1184
|
+
this._requestScope = new RequestScopeManager;
|
|
1120
1185
|
}
|
|
1121
|
-
// get req(): FastRequest {
|
|
1122
|
-
// return this._req
|
|
1123
|
-
// }
|
|
1124
|
-
// Response helpers - merge custom headers with defaults
|
|
1125
|
-
// Optimized: use Object.assign instead of spread to avoid shallow copy overhead
|
|
1126
1186
|
getHeaders(contentType) {
|
|
1127
1187
|
const headers = Object.assign({ "Content-Type": contentType }, this._resHeaders);
|
|
1128
1188
|
return headers;
|
|
@@ -1158,14 +1218,13 @@ var MinimalContext = class {
|
|
|
1158
1218
|
});
|
|
1159
1219
|
}
|
|
1160
1220
|
header(name, value) {
|
|
1161
|
-
if (value !==
|
|
1221
|
+
if (value !== undefined) {
|
|
1162
1222
|
this._resHeaders[name] = value;
|
|
1163
1223
|
return;
|
|
1164
1224
|
}
|
|
1165
1225
|
return this.req.header(name);
|
|
1166
1226
|
}
|
|
1167
|
-
status(_code) {
|
|
1168
|
-
}
|
|
1227
|
+
status(_code) {}
|
|
1169
1228
|
stream(stream, status = 200) {
|
|
1170
1229
|
return new Response(stream, {
|
|
1171
1230
|
status,
|
|
@@ -1186,35 +1245,22 @@ var MinimalContext = class {
|
|
|
1186
1245
|
}
|
|
1187
1246
|
async forward(target, _options = {}) {
|
|
1188
1247
|
const url = new URL(this.req.url);
|
|
1189
|
-
const targetUrl = new URL(
|
|
1190
|
-
target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`
|
|
1191
|
-
);
|
|
1248
|
+
const targetUrl = new URL(target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`);
|
|
1192
1249
|
return fetch(targetUrl.toString(), {
|
|
1193
1250
|
method: this.req.method,
|
|
1194
1251
|
headers: this.req.raw.headers
|
|
1195
1252
|
});
|
|
1196
1253
|
}
|
|
1197
|
-
|
|
1198
|
-
return
|
|
1254
|
+
escape(html) {
|
|
1255
|
+
return bunEscapeHTML2(html);
|
|
1199
1256
|
}
|
|
1200
|
-
|
|
1257
|
+
get(_key) {
|
|
1258
|
+
return;
|
|
1201
1259
|
}
|
|
1202
|
-
|
|
1203
|
-
* Get the request-scoped service manager for this request.
|
|
1204
|
-
*
|
|
1205
|
-
* @returns The RequestScopeManager for this request.
|
|
1206
|
-
*/
|
|
1260
|
+
set(_key, _value) {}
|
|
1207
1261
|
requestScope() {
|
|
1208
1262
|
return this._requestScope;
|
|
1209
1263
|
}
|
|
1210
|
-
/**
|
|
1211
|
-
* Resolve a request-scoped service (convenience method).
|
|
1212
|
-
*
|
|
1213
|
-
* @template T - The service type.
|
|
1214
|
-
* @param key - The service key for caching.
|
|
1215
|
-
* @param factory - Factory function to create the service.
|
|
1216
|
-
* @returns The cached or newly created service instance.
|
|
1217
|
-
*/
|
|
1218
1264
|
scoped(key, factory) {
|
|
1219
1265
|
return this._requestScope.resolve(key, factory);
|
|
1220
1266
|
}
|
|
@@ -1222,14 +1268,11 @@ var MinimalContext = class {
|
|
|
1222
1268
|
get native() {
|
|
1223
1269
|
return this;
|
|
1224
1270
|
}
|
|
1225
|
-
// Required for interface compatibility
|
|
1226
1271
|
init(_request, _params, _path) {
|
|
1227
1272
|
throw new Error("MinimalContext does not support init. Create a new instance instead.");
|
|
1228
1273
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
}
|
|
1232
|
-
};
|
|
1274
|
+
reset() {}
|
|
1275
|
+
}
|
|
1233
1276
|
|
|
1234
1277
|
// src/engine/path.ts
|
|
1235
1278
|
function extractPath(url) {
|
|
@@ -1247,79 +1290,38 @@ function extractPath(url) {
|
|
|
1247
1290
|
}
|
|
1248
1291
|
|
|
1249
1292
|
// src/engine/pool.ts
|
|
1250
|
-
|
|
1293
|
+
class ObjectPool {
|
|
1251
1294
|
pool = [];
|
|
1252
1295
|
factory;
|
|
1253
1296
|
reset;
|
|
1254
1297
|
maxSize;
|
|
1255
|
-
/**
|
|
1256
|
-
* Create a new object pool
|
|
1257
|
-
*
|
|
1258
|
-
* @param factory - Function to create new objects
|
|
1259
|
-
* @param reset - Function to reset objects before reuse
|
|
1260
|
-
* @param maxSize - Maximum pool size (default: 256)
|
|
1261
|
-
*/
|
|
1262
1298
|
constructor(factory, reset, maxSize = 256) {
|
|
1263
1299
|
this.factory = factory;
|
|
1264
1300
|
this.reset = reset;
|
|
1265
1301
|
this.maxSize = maxSize;
|
|
1266
1302
|
}
|
|
1267
|
-
/**
|
|
1268
|
-
* Acquire an object from the pool
|
|
1269
|
-
*
|
|
1270
|
-
* If the pool is empty, creates a new object (overflow strategy).
|
|
1271
|
-
* This ensures the pool never blocks under high load.
|
|
1272
|
-
*
|
|
1273
|
-
* @returns Object from pool or newly created
|
|
1274
|
-
*/
|
|
1275
1303
|
acquire() {
|
|
1276
1304
|
const obj = this.pool.pop();
|
|
1277
|
-
if (obj !==
|
|
1305
|
+
if (obj !== undefined) {
|
|
1278
1306
|
return obj;
|
|
1279
1307
|
}
|
|
1280
1308
|
return this.factory();
|
|
1281
1309
|
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Release an object back to the pool
|
|
1284
|
-
*
|
|
1285
|
-
* If the pool is full, the object is discarded (will be GC'd).
|
|
1286
|
-
* This prevents unbounded memory growth.
|
|
1287
|
-
*
|
|
1288
|
-
* @param obj - Object to release
|
|
1289
|
-
*/
|
|
1290
1310
|
release(obj) {
|
|
1291
1311
|
if (this.pool.length < this.maxSize) {
|
|
1292
1312
|
this.reset(obj);
|
|
1293
1313
|
this.pool.push(obj);
|
|
1294
1314
|
}
|
|
1295
1315
|
}
|
|
1296
|
-
/**
|
|
1297
|
-
* Clear all objects from the pool
|
|
1298
|
-
*
|
|
1299
|
-
* Useful for testing or when you need to force a clean slate.
|
|
1300
|
-
*/
|
|
1301
1316
|
clear() {
|
|
1302
1317
|
this.pool = [];
|
|
1303
1318
|
}
|
|
1304
|
-
/**
|
|
1305
|
-
* Get current pool size
|
|
1306
|
-
*/
|
|
1307
1319
|
get size() {
|
|
1308
1320
|
return this.pool.length;
|
|
1309
1321
|
}
|
|
1310
|
-
/**
|
|
1311
|
-
* Get maximum pool size
|
|
1312
|
-
*/
|
|
1313
1322
|
get capacity() {
|
|
1314
1323
|
return this.maxSize;
|
|
1315
1324
|
}
|
|
1316
|
-
/**
|
|
1317
|
-
* Pre-warm the pool by creating objects in advance
|
|
1318
|
-
*
|
|
1319
|
-
* This can reduce latency for the first N requests.
|
|
1320
|
-
*
|
|
1321
|
-
* @param count - Number of objects to pre-create
|
|
1322
|
-
*/
|
|
1323
1325
|
prewarm(count) {
|
|
1324
1326
|
const targetSize = Math.min(count, this.maxSize);
|
|
1325
1327
|
while (this.pool.length < targetSize) {
|
|
@@ -1328,9 +1330,10 @@ var ObjectPool = class {
|
|
|
1328
1330
|
this.pool.push(obj);
|
|
1329
1331
|
}
|
|
1330
1332
|
}
|
|
1331
|
-
}
|
|
1333
|
+
}
|
|
1332
1334
|
|
|
1333
1335
|
// src/engine/Gravito.ts
|
|
1336
|
+
var bunPeek = globalThis.Bun.peek;
|
|
1334
1337
|
function compileMiddlewareChain(middleware, handler) {
|
|
1335
1338
|
if (middleware.length === 0) {
|
|
1336
1339
|
return handler;
|
|
@@ -1339,64 +1342,76 @@ function compileMiddlewareChain(middleware, handler) {
|
|
|
1339
1342
|
const mw = middleware[0];
|
|
1340
1343
|
return async (ctx) => {
|
|
1341
1344
|
let nextCalled = false;
|
|
1342
|
-
const result =
|
|
1345
|
+
const result = mw(ctx, async () => {
|
|
1343
1346
|
nextCalled = true;
|
|
1344
|
-
return
|
|
1347
|
+
return;
|
|
1345
1348
|
});
|
|
1346
|
-
|
|
1347
|
-
|
|
1349
|
+
let finalResult;
|
|
1350
|
+
if (result instanceof Promise) {
|
|
1351
|
+
const peeked = bunPeek(result);
|
|
1352
|
+
finalResult = peeked === result ? await result : peeked;
|
|
1353
|
+
} else {
|
|
1354
|
+
finalResult = result;
|
|
1355
|
+
}
|
|
1356
|
+
if (finalResult instanceof Response) {
|
|
1357
|
+
return finalResult;
|
|
1348
1358
|
}
|
|
1349
1359
|
if (nextCalled) {
|
|
1350
|
-
|
|
1360
|
+
const hResult = handler(ctx);
|
|
1361
|
+
if (hResult instanceof Promise) {
|
|
1362
|
+
const p = bunPeek(hResult);
|
|
1363
|
+
return p === hResult ? await hResult : p;
|
|
1364
|
+
}
|
|
1365
|
+
return hResult;
|
|
1351
1366
|
}
|
|
1352
1367
|
return ctx.json({ error: "Middleware did not call next or return response" }, 500);
|
|
1353
1368
|
};
|
|
1354
1369
|
}
|
|
1355
1370
|
let compiled = handler;
|
|
1356
|
-
for (let i = middleware.length - 1;
|
|
1371
|
+
for (let i = middleware.length - 1;i >= 0; i--) {
|
|
1357
1372
|
const mw = middleware[i];
|
|
1358
1373
|
const nextHandler = compiled;
|
|
1359
1374
|
compiled = async (ctx) => {
|
|
1360
1375
|
let nextCalled = false;
|
|
1361
|
-
const result =
|
|
1376
|
+
const result = mw(ctx, async () => {
|
|
1362
1377
|
nextCalled = true;
|
|
1363
|
-
return
|
|
1378
|
+
return;
|
|
1364
1379
|
});
|
|
1365
|
-
|
|
1366
|
-
|
|
1380
|
+
let finalResult;
|
|
1381
|
+
if (result instanceof Promise) {
|
|
1382
|
+
const peeked = bunPeek(result);
|
|
1383
|
+
finalResult = peeked === result ? await result : peeked;
|
|
1384
|
+
} else {
|
|
1385
|
+
finalResult = result;
|
|
1386
|
+
}
|
|
1387
|
+
if (finalResult instanceof Response) {
|
|
1388
|
+
return finalResult;
|
|
1367
1389
|
}
|
|
1368
1390
|
if (nextCalled) {
|
|
1369
|
-
|
|
1391
|
+
const nextResult = nextHandler(ctx);
|
|
1392
|
+
if (nextResult instanceof Promise) {
|
|
1393
|
+
const p = bunPeek(nextResult);
|
|
1394
|
+
return p === nextResult ? await nextResult : p;
|
|
1395
|
+
}
|
|
1396
|
+
return nextResult;
|
|
1370
1397
|
}
|
|
1371
1398
|
return ctx.json({ error: "Middleware did not call next or return response" }, 500);
|
|
1372
1399
|
};
|
|
1373
1400
|
}
|
|
1374
1401
|
return compiled;
|
|
1375
1402
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1403
|
+
|
|
1404
|
+
class Gravito {
|
|
1405
|
+
router = new AOTRouter;
|
|
1378
1406
|
contextPool;
|
|
1379
1407
|
errorHandler;
|
|
1380
1408
|
notFoundHandler;
|
|
1381
|
-
// Direct reference to static routes Map (O(1) access)
|
|
1382
|
-
/** @internal */
|
|
1383
1409
|
staticRoutes;
|
|
1384
|
-
// Flag: pure static app (no middleware at all) allows ultra-fast path
|
|
1385
1410
|
isPureStaticApp = true;
|
|
1386
|
-
|
|
1387
|
-
compiledDynamicRoutes = /* @__PURE__ */ new Map();
|
|
1388
|
-
/**
|
|
1389
|
-
* Create a new Gravito instance
|
|
1390
|
-
*
|
|
1391
|
-
* @param options - Engine configuration options
|
|
1392
|
-
*/
|
|
1411
|
+
compiledDynamicRoutes = new Map;
|
|
1393
1412
|
constructor(options = {}) {
|
|
1394
1413
|
const poolSize = options.poolSize ?? 256;
|
|
1395
|
-
this.contextPool = new ObjectPool(
|
|
1396
|
-
() => new FastContext(),
|
|
1397
|
-
(ctx) => ctx.reset(),
|
|
1398
|
-
poolSize
|
|
1399
|
-
);
|
|
1414
|
+
this.contextPool = new ObjectPool(() => new FastContext, (ctx) => ctx.reset(), poolSize);
|
|
1400
1415
|
this.contextPool.prewarm(Math.min(32, poolSize));
|
|
1401
1416
|
if (options.onError) {
|
|
1402
1417
|
this.errorHandler = options.onError;
|
|
@@ -1406,58 +1421,27 @@ var Gravito = class {
|
|
|
1406
1421
|
}
|
|
1407
1422
|
this.compileRoutes();
|
|
1408
1423
|
}
|
|
1409
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1410
|
-
// HTTP Method Registration
|
|
1411
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1412
|
-
/**
|
|
1413
|
-
* Register a GET route
|
|
1414
|
-
*
|
|
1415
|
-
* @param path - Route path (e.g., '/users/:id')
|
|
1416
|
-
* @param handlers - Handler and optional middleware
|
|
1417
|
-
* @returns This instance for chaining
|
|
1418
|
-
*/
|
|
1419
1424
|
get(path, ...handlers) {
|
|
1420
1425
|
return this.addRoute("get", path, handlers);
|
|
1421
1426
|
}
|
|
1422
|
-
/**
|
|
1423
|
-
* Register a POST route
|
|
1424
|
-
*/
|
|
1425
1427
|
post(path, ...handlers) {
|
|
1426
1428
|
return this.addRoute("post", path, handlers);
|
|
1427
1429
|
}
|
|
1428
|
-
/**
|
|
1429
|
-
* Register a PUT route
|
|
1430
|
-
*/
|
|
1431
1430
|
put(path, ...handlers) {
|
|
1432
1431
|
return this.addRoute("put", path, handlers);
|
|
1433
1432
|
}
|
|
1434
|
-
/**
|
|
1435
|
-
* Register a DELETE route
|
|
1436
|
-
*/
|
|
1437
1433
|
delete(path, ...handlers) {
|
|
1438
1434
|
return this.addRoute("delete", path, handlers);
|
|
1439
1435
|
}
|
|
1440
|
-
/**
|
|
1441
|
-
* Register a PDF route
|
|
1442
|
-
*/
|
|
1443
1436
|
patch(path, ...handlers) {
|
|
1444
1437
|
return this.addRoute("patch", path, handlers);
|
|
1445
1438
|
}
|
|
1446
|
-
/**
|
|
1447
|
-
* Register an OPTIONS route
|
|
1448
|
-
*/
|
|
1449
1439
|
options(path, ...handlers) {
|
|
1450
1440
|
return this.addRoute("options", path, handlers);
|
|
1451
1441
|
}
|
|
1452
|
-
/**
|
|
1453
|
-
* Register a HEAD route
|
|
1454
|
-
*/
|
|
1455
1442
|
head(path, ...handlers) {
|
|
1456
1443
|
return this.addRoute("head", path, handlers);
|
|
1457
1444
|
}
|
|
1458
|
-
/**
|
|
1459
|
-
* Register a route for all HTTP methods
|
|
1460
|
-
*/
|
|
1461
1445
|
all(path, ...handlers) {
|
|
1462
1446
|
const methods = ["get", "post", "put", "delete", "patch", "options", "head"];
|
|
1463
1447
|
for (const method of methods) {
|
|
@@ -1475,45 +1459,19 @@ var Gravito = class {
|
|
|
1475
1459
|
this.compileRoutes();
|
|
1476
1460
|
return this;
|
|
1477
1461
|
}
|
|
1478
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1479
|
-
// Route Grouping
|
|
1480
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1481
|
-
/**
|
|
1482
|
-
* Mount a sub-application at a path prefix
|
|
1483
|
-
*/
|
|
1484
1462
|
route(path, app) {
|
|
1485
1463
|
this.router.mount(path, app.router);
|
|
1486
1464
|
this.compileRoutes();
|
|
1487
1465
|
return this;
|
|
1488
1466
|
}
|
|
1489
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1490
|
-
// Error Handling
|
|
1491
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1492
|
-
/**
|
|
1493
|
-
* Set custom error handler
|
|
1494
|
-
*/
|
|
1495
1467
|
onError(handler) {
|
|
1496
1468
|
this.errorHandler = handler;
|
|
1497
1469
|
return this;
|
|
1498
1470
|
}
|
|
1499
|
-
/**
|
|
1500
|
-
* Set custom 404 handler
|
|
1501
|
-
*/
|
|
1502
1471
|
notFound(handler) {
|
|
1503
1472
|
this.notFoundHandler = handler;
|
|
1504
1473
|
return this;
|
|
1505
1474
|
}
|
|
1506
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1507
|
-
// Request Handling (Bun.serve integration)
|
|
1508
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1509
|
-
/**
|
|
1510
|
-
* Predictive Route Warming (JIT Optimization)
|
|
1511
|
-
*
|
|
1512
|
-
* Simulates requests to specified routes to trigger JIT compilation (FTL)
|
|
1513
|
-
* before real traffic arrives.
|
|
1514
|
-
*
|
|
1515
|
-
* @param paths List of paths to warm up (e.g. ['/api/users', '/health'])
|
|
1516
|
-
*/
|
|
1517
1475
|
async warmup(paths) {
|
|
1518
1476
|
const dummyReqOpts = { headers: { "User-Agent": "Gravito-Warmup/1.0" } };
|
|
1519
1477
|
for (const path of paths) {
|
|
@@ -1521,96 +1479,135 @@ var Gravito = class {
|
|
|
1521
1479
|
await this.fetch(req);
|
|
1522
1480
|
}
|
|
1523
1481
|
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1482
|
+
serveConfig(baseConfig = {}) {
|
|
1483
|
+
const nativeRoutes = this.router.getNativeRoutes((handler, middleware, path) => {
|
|
1484
|
+
const compiled = compileMiddlewareChain(middleware, handler);
|
|
1485
|
+
return async (req) => {
|
|
1486
|
+
const ctx = this.contextPool.acquire();
|
|
1487
|
+
ctx.init(req, {}, path, path);
|
|
1488
|
+
try {
|
|
1489
|
+
const result = compiled(ctx);
|
|
1490
|
+
let response;
|
|
1491
|
+
if (result instanceof Promise) {
|
|
1492
|
+
const peeked = bunPeek(result);
|
|
1493
|
+
response = peeked === result ? await result : peeked;
|
|
1494
|
+
} else {
|
|
1495
|
+
response = result;
|
|
1496
|
+
}
|
|
1497
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1498
|
+
if (cleanup instanceof Promise)
|
|
1499
|
+
await cleanup;
|
|
1500
|
+
this.contextPool.release(ctx);
|
|
1501
|
+
return response;
|
|
1502
|
+
} catch (error) {
|
|
1503
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1504
|
+
if (cleanup instanceof Promise)
|
|
1505
|
+
await cleanup;
|
|
1506
|
+
this.contextPool.release(ctx);
|
|
1507
|
+
return this.handleErrorSync(error, req, path);
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
});
|
|
1511
|
+
return {
|
|
1512
|
+
...baseConfig,
|
|
1513
|
+
routes: nativeRoutes,
|
|
1514
|
+
fetch: this.fetch,
|
|
1515
|
+
tls: baseConfig.tls ? this.optimizeTLS(baseConfig.tls) : undefined,
|
|
1516
|
+
error: (error) => {
|
|
1517
|
+
console.error("Native route error:", error);
|
|
1518
|
+
return new Response(CACHED_RESPONSES.INTERNAL_ERROR, {
|
|
1519
|
+
status: 500,
|
|
1520
|
+
headers: HEADERS.JSON
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
optimizeTLS(tls) {
|
|
1526
|
+
const isProd = false;
|
|
1527
|
+
const optimizeEntry = (entry) => {
|
|
1528
|
+
const optimized = { ...entry };
|
|
1529
|
+
if (typeof optimized.key === "string" && !optimized.key.startsWith("-----BEGIN")) {
|
|
1530
|
+
optimized.key = globalThis.Bun.file(optimized.key);
|
|
1531
|
+
}
|
|
1532
|
+
if (typeof optimized.cert === "string" && !optimized.cert.startsWith("-----BEGIN")) {
|
|
1533
|
+
optimized.cert = globalThis.Bun.file(optimized.cert);
|
|
1534
|
+
}
|
|
1535
|
+
if (isProd && optimized.lowMemoryMode === undefined) {
|
|
1536
|
+
optimized.lowMemoryMode = true;
|
|
1537
|
+
}
|
|
1538
|
+
return optimized;
|
|
1539
|
+
};
|
|
1540
|
+
return Array.isArray(tls) ? tls.map(optimizeEntry) : optimizeEntry(tls);
|
|
1541
|
+
}
|
|
1527
1542
|
fetch = async (request) => {
|
|
1528
1543
|
const path = extractPath(request.url);
|
|
1529
1544
|
const method = request.method.toLowerCase();
|
|
1530
1545
|
const staticKey = `${method}:${path}`;
|
|
1531
1546
|
const staticRoute = this.staticRoutes.get(staticKey);
|
|
1532
1547
|
if (staticRoute) {
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1548
|
+
const ctx = this.contextPool.acquire();
|
|
1549
|
+
ctx.init(request, {}, path, path);
|
|
1550
|
+
try {
|
|
1551
|
+
const compiled = staticRoute.compiled || compileMiddlewareChain(staticRoute.middleware, staticRoute.handler);
|
|
1552
|
+
const result = compiled(ctx);
|
|
1553
|
+
let response;
|
|
1554
|
+
if (result instanceof Promise) {
|
|
1555
|
+
const peeked = bunPeek(result);
|
|
1556
|
+
response = peeked === result ? await result : peeked;
|
|
1557
|
+
} else {
|
|
1558
|
+
response = result;
|
|
1543
1559
|
}
|
|
1560
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1561
|
+
if (cleanup instanceof Promise)
|
|
1562
|
+
await cleanup;
|
|
1563
|
+
this.contextPool.release(ctx);
|
|
1564
|
+
return response;
|
|
1565
|
+
} catch (error) {
|
|
1566
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1567
|
+
if (cleanup instanceof Promise)
|
|
1568
|
+
await cleanup;
|
|
1569
|
+
this.contextPool.release(ctx);
|
|
1570
|
+
return this.handleErrorSync(error, request, path);
|
|
1544
1571
|
}
|
|
1545
|
-
return await this.handleWithMiddleware(request, path, staticRoute);
|
|
1546
1572
|
}
|
|
1547
1573
|
return await this.handleDynamicRoute(request, method, path);
|
|
1548
1574
|
};
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
try {
|
|
1555
|
-
ctx.init(request, {}, path, path);
|
|
1556
|
-
if (route.compiled) {
|
|
1557
|
-
return await route.compiled(ctx);
|
|
1558
|
-
}
|
|
1559
|
-
const middleware = this.collectMiddlewareForPath(path, route.middleware);
|
|
1560
|
-
if (middleware.length === 0) {
|
|
1561
|
-
return await route.handler(ctx);
|
|
1562
|
-
}
|
|
1563
|
-
return await this.executeMiddleware(ctx, middleware, route.handler);
|
|
1564
|
-
} catch (error) {
|
|
1565
|
-
return await this.handleError(error, ctx);
|
|
1566
|
-
} finally {
|
|
1575
|
+
async handleDynamicRoute(request, method, path) {
|
|
1576
|
+
const match = this.router.match(method, path);
|
|
1577
|
+
if (match.handler) {
|
|
1578
|
+
const ctx = this.contextPool.acquire();
|
|
1579
|
+
ctx.init(request, match.params, path, match.routePattern);
|
|
1567
1580
|
try {
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
}
|
|
1575
|
-
/**
|
|
1576
|
-
* Handle dynamic routes (Radix Tree lookup)
|
|
1577
|
-
*/
|
|
1578
|
-
handleDynamicRoute(request, method, path) {
|
|
1579
|
-
const match = this.router.match(method.toUpperCase(), path);
|
|
1580
|
-
if (!match.handler) {
|
|
1581
|
-
return this.handleNotFoundSync(request, path);
|
|
1582
|
-
}
|
|
1583
|
-
const cacheKey = `${method}:${match.routePattern ?? path}`;
|
|
1584
|
-
let entry = this.compiledDynamicRoutes.get(cacheKey);
|
|
1585
|
-
if (!entry || entry.version !== this.router.version) {
|
|
1586
|
-
const compiled = compileMiddlewareChain(match.middleware, match.handler);
|
|
1587
|
-
if (this.compiledDynamicRoutes.size > 1e3) {
|
|
1588
|
-
this.compiledDynamicRoutes.clear();
|
|
1589
|
-
}
|
|
1590
|
-
entry = { compiled, version: this.router.version };
|
|
1591
|
-
this.compiledDynamicRoutes.set(cacheKey, entry);
|
|
1592
|
-
}
|
|
1593
|
-
const ctx = this.contextPool.acquire();
|
|
1594
|
-
const execute = async () => {
|
|
1595
|
-
try {
|
|
1596
|
-
ctx.init(request, match.params, path, match.routePattern);
|
|
1597
|
-
return await entry?.compiled(ctx);
|
|
1598
|
-
} catch (error) {
|
|
1599
|
-
return await this.handleError(error, ctx);
|
|
1600
|
-
} finally {
|
|
1601
|
-
try {
|
|
1602
|
-
await ctx.requestScope().cleanup();
|
|
1603
|
-
} catch (cleanupError) {
|
|
1604
|
-
console.error("RequestScope cleanup failed:", cleanupError);
|
|
1581
|
+
const routeKey = `${method}:${match.routePattern}`;
|
|
1582
|
+
let compiledObj = this.compiledDynamicRoutes.get(routeKey);
|
|
1583
|
+
if (!compiledObj || compiledObj.version !== this.router.version) {
|
|
1584
|
+
const compiled = compileMiddlewareChain(match.middleware, match.handler);
|
|
1585
|
+
compiledObj = { compiled, version: this.router.version };
|
|
1586
|
+
this.compiledDynamicRoutes.set(routeKey, compiledObj);
|
|
1605
1587
|
}
|
|
1588
|
+
const result = compiledObj.compiled(ctx);
|
|
1589
|
+
let response;
|
|
1590
|
+
if (result instanceof Promise) {
|
|
1591
|
+
const peeked = bunPeek(result);
|
|
1592
|
+
response = peeked === result ? await result : peeked;
|
|
1593
|
+
} else {
|
|
1594
|
+
response = result;
|
|
1595
|
+
}
|
|
1596
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1597
|
+
if (cleanup instanceof Promise)
|
|
1598
|
+
await cleanup;
|
|
1599
|
+
this.contextPool.release(ctx);
|
|
1600
|
+
return response;
|
|
1601
|
+
} catch (error) {
|
|
1602
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1603
|
+
if (cleanup instanceof Promise)
|
|
1604
|
+
await cleanup;
|
|
1606
1605
|
this.contextPool.release(ctx);
|
|
1606
|
+
return this.handleErrorSync(error, request, path);
|
|
1607
1607
|
}
|
|
1608
|
-
}
|
|
1609
|
-
return
|
|
1608
|
+
}
|
|
1609
|
+
return this.handleNotFoundSync(request, path);
|
|
1610
1610
|
}
|
|
1611
|
-
/**
|
|
1612
|
-
* Sync error handler (for ultra-fast path)
|
|
1613
|
-
*/
|
|
1614
1611
|
handleErrorSync(error, request, path) {
|
|
1615
1612
|
if (this.errorHandler) {
|
|
1616
1613
|
const ctx = new MinimalContext(request, {}, path);
|
|
@@ -1626,9 +1623,6 @@ var Gravito = class {
|
|
|
1626
1623
|
headers: HEADERS.JSON
|
|
1627
1624
|
});
|
|
1628
1625
|
}
|
|
1629
|
-
/**
|
|
1630
|
-
* Sync 404 handler (for ultra-fast path)
|
|
1631
|
-
*/
|
|
1632
1626
|
handleNotFoundSync(request, path) {
|
|
1633
1627
|
if (this.notFoundHandler) {
|
|
1634
1628
|
const ctx = new MinimalContext(request, {}, path);
|
|
@@ -1643,18 +1637,12 @@ var Gravito = class {
|
|
|
1643
1637
|
headers: HEADERS.JSON
|
|
1644
1638
|
});
|
|
1645
1639
|
}
|
|
1646
|
-
/**
|
|
1647
|
-
* Collect middleware for a specific path
|
|
1648
|
-
*/
|
|
1649
1640
|
collectMiddlewareForPath(path, routeMiddleware) {
|
|
1650
1641
|
if (this.router.globalMiddleware.length === 0 && this.router.pathMiddleware.size === 0) {
|
|
1651
1642
|
return routeMiddleware;
|
|
1652
1643
|
}
|
|
1653
1644
|
return this.router.collectMiddlewarePublic(path, routeMiddleware);
|
|
1654
1645
|
}
|
|
1655
|
-
/**
|
|
1656
|
-
* Compile routes for optimization
|
|
1657
|
-
*/
|
|
1658
1646
|
compileRoutes() {
|
|
1659
1647
|
this.staticRoutes = this.router.staticRoutes;
|
|
1660
1648
|
const hasGlobalMiddleware = this.router.globalMiddleware.length > 0;
|
|
@@ -1674,9 +1662,6 @@ var Gravito = class {
|
|
|
1674
1662
|
route.compiledVersion = this.router.version;
|
|
1675
1663
|
}
|
|
1676
1664
|
}
|
|
1677
|
-
/**
|
|
1678
|
-
* Add a route to the router
|
|
1679
|
-
*/
|
|
1680
1665
|
addRoute(method, path, handlers) {
|
|
1681
1666
|
if (handlers.length === 0) {
|
|
1682
1667
|
throw new Error(`No handler provided for ${method.toUpperCase()} ${path}`);
|
|
@@ -1687,46 +1672,14 @@ var Gravito = class {
|
|
|
1687
1672
|
this.compileRoutes();
|
|
1688
1673
|
return this;
|
|
1689
1674
|
}
|
|
1690
|
-
|
|
1691
|
-
* Execute middleware chain followed by handler
|
|
1692
|
-
*/
|
|
1693
|
-
async executeMiddleware(ctx, middleware, handler) {
|
|
1694
|
-
let index = 0;
|
|
1695
|
-
const next = async () => {
|
|
1696
|
-
if (index < middleware.length) {
|
|
1697
|
-
const mw = middleware[index++];
|
|
1698
|
-
return await mw(ctx, next);
|
|
1699
|
-
}
|
|
1700
|
-
return void 0;
|
|
1701
|
-
};
|
|
1702
|
-
const result = await next();
|
|
1703
|
-
if (result instanceof Response) {
|
|
1704
|
-
return result;
|
|
1705
|
-
}
|
|
1706
|
-
return await handler(ctx);
|
|
1707
|
-
}
|
|
1708
|
-
/**
|
|
1709
|
-
* Handle errors (Async version for dynamic/middleware paths)
|
|
1710
|
-
*/
|
|
1711
|
-
async handleError(error, ctx) {
|
|
1712
|
-
if (this.errorHandler) {
|
|
1713
|
-
return await this.errorHandler(error, ctx);
|
|
1714
|
-
}
|
|
1715
|
-
console.error("Unhandled error:", error);
|
|
1716
|
-
return ctx.json(
|
|
1717
|
-
{
|
|
1718
|
-
error: "Internal Server Error",
|
|
1719
|
-
message: error.message
|
|
1720
|
-
},
|
|
1721
|
-
500
|
|
1722
|
-
);
|
|
1723
|
-
}
|
|
1724
|
-
};
|
|
1675
|
+
}
|
|
1725
1676
|
export {
|
|
1726
|
-
|
|
1727
|
-
FastContext as FastContextImpl,
|
|
1728
|
-
Gravito,
|
|
1729
|
-
MinimalContext,
|
|
1677
|
+
extractPath,
|
|
1730
1678
|
ObjectPool,
|
|
1731
|
-
|
|
1679
|
+
MinimalContext,
|
|
1680
|
+
Gravito,
|
|
1681
|
+
FastContext as FastContextImpl,
|
|
1682
|
+
AOTRouter
|
|
1732
1683
|
};
|
|
1684
|
+
|
|
1685
|
+
//# debugId=13A90DBD3E511D5864756E2164756E21
|