@gravito/core 1.6.1 → 2.0.1
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.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.d.ts +14 -910
- package/dist/engine/index.js +623 -622
- package/dist/engine/index.js.map +23 -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.d.ts +10 -0
- package/dist/ffi/index.js +131 -0
- package/dist/ffi/index.js.map +11 -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} +35 -16
- package/dist/index.browser.d.ts +34 -0
- package/dist/index.d.ts +60 -10981
- package/dist/index.js +10808 -11273
- package/dist/index.js.map +166 -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 +25 -56
- 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.cjs +0 -18
- package/dist/compat.d.cts +0 -1
- package/dist/engine/index.cjs +0 -1764
- package/dist/engine/index.d.cts +0 -922
- package/dist/index.cjs +0 -14906
- package/dist/index.d.cts +0 -11008
package/dist/engine/index.js
CHANGED
|
@@ -1,26 +1,78 @@
|
|
|
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
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __returnValue = (v) => v;
|
|
34
|
+
function __exportSetter(name, newValue) {
|
|
35
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
36
|
+
}
|
|
37
|
+
var __export = (target, all) => {
|
|
38
|
+
for (var name in all)
|
|
39
|
+
__defProp(target, name, {
|
|
40
|
+
get: all[name],
|
|
41
|
+
enumerable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
set: __exportSetter.bind(all, name)
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
var __require = import.meta.require;
|
|
47
|
+
|
|
48
|
+
// src/engine/index.ts
|
|
49
|
+
var exports_engine = {};
|
|
50
|
+
__export(exports_engine, {
|
|
51
|
+
extractPath: () => extractPath,
|
|
52
|
+
ObjectPool: () => ObjectPool,
|
|
53
|
+
MinimalContext: () => MinimalContext,
|
|
54
|
+
Gravito: () => Gravito,
|
|
55
|
+
FastContextImpl: () => FastContext,
|
|
56
|
+
AOTRouter: () => AOTRouter
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// src/adapters/bun/types.ts
|
|
60
|
+
var NodeType;
|
|
61
|
+
((NodeType2) => {
|
|
62
|
+
NodeType2[NodeType2["STATIC"] = 0] = "STATIC";
|
|
63
|
+
NodeType2[NodeType2["PARAM"] = 1] = "PARAM";
|
|
64
|
+
NodeType2[NodeType2["WILDCARD"] = 2] = "WILDCARD";
|
|
65
|
+
})(NodeType ||= {});
|
|
66
|
+
|
|
1
67
|
// src/adapters/bun/RadixNode.ts
|
|
2
|
-
|
|
3
|
-
// Path segment for this node (e.g., "users", ":id")
|
|
68
|
+
class RadixNode {
|
|
4
69
|
segment;
|
|
5
|
-
// Node type (Static, Param, Wildcard)
|
|
6
70
|
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)
|
|
71
|
+
children = new Map;
|
|
10
72
|
paramChild = null;
|
|
11
|
-
// Specialized child for wildcard node
|
|
12
73
|
wildcardChild = null;
|
|
13
|
-
|
|
14
|
-
handlers = /* @__PURE__ */ new Map();
|
|
15
|
-
// Parameter name if this is a PARAM node (e.g., "id" for ":id")
|
|
74
|
+
handlers = new Map;
|
|
16
75
|
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
76
|
regex = null;
|
|
25
77
|
constructor(segment = "", type = 0 /* STATIC */) {
|
|
26
78
|
this.segment = segment;
|
|
@@ -38,44 +90,61 @@ var RadixNode = class _RadixNode {
|
|
|
38
90
|
};
|
|
39
91
|
}
|
|
40
92
|
static fromJSON(json) {
|
|
41
|
-
const node = new
|
|
93
|
+
const node = new RadixNode(json.segment, json.type);
|
|
42
94
|
node.paramName = json.paramName;
|
|
43
95
|
if (json.regex) {
|
|
44
96
|
node.regex = new RegExp(json.regex);
|
|
45
97
|
}
|
|
46
98
|
if (json.children) {
|
|
47
99
|
for (const [key, childJson] of json.children) {
|
|
48
|
-
node.children.set(key,
|
|
100
|
+
node.children.set(key, RadixNode.fromJSON(childJson));
|
|
49
101
|
}
|
|
50
102
|
}
|
|
51
103
|
if (json.paramChild) {
|
|
52
|
-
node.paramChild =
|
|
104
|
+
node.paramChild = RadixNode.fromJSON(json.paramChild);
|
|
53
105
|
}
|
|
54
106
|
if (json.wildcardChild) {
|
|
55
|
-
node.wildcardChild =
|
|
107
|
+
node.wildcardChild = RadixNode.fromJSON(json.wildcardChild);
|
|
56
108
|
}
|
|
57
109
|
return node;
|
|
58
110
|
}
|
|
59
|
-
}
|
|
111
|
+
}
|
|
60
112
|
|
|
61
113
|
// src/adapters/bun/RadixRouter.ts
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
114
|
+
class RouteCache {
|
|
115
|
+
cache = new Map;
|
|
116
|
+
maxSize = 1e4;
|
|
117
|
+
get(key) {
|
|
118
|
+
return this.cache.get(key);
|
|
119
|
+
}
|
|
120
|
+
set(key, value) {
|
|
121
|
+
if (this.cache.size >= this.maxSize) {
|
|
122
|
+
const firstKey = this.cache.keys().next().value;
|
|
123
|
+
if (firstKey) {
|
|
124
|
+
this.cache.delete(firstKey);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
this.cache.set(key, value);
|
|
128
|
+
}
|
|
129
|
+
clear() {
|
|
130
|
+
this.cache.clear();
|
|
131
|
+
}
|
|
132
|
+
has(key) {
|
|
133
|
+
return this.cache.has(key);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
class RadixRouter {
|
|
138
|
+
root = new RadixNode;
|
|
139
|
+
globalConstraints = new Map;
|
|
140
|
+
routeCache = new RouteCache;
|
|
69
141
|
where(param, regex) {
|
|
70
142
|
this.globalConstraints.set(param, regex);
|
|
71
143
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Register a route
|
|
74
|
-
*/
|
|
75
144
|
add(method, path, handlers) {
|
|
76
145
|
let node = this.root;
|
|
77
146
|
const segments = this.splitPath(path);
|
|
78
|
-
for (let i = 0;
|
|
147
|
+
for (let i = 0;i < segments.length; i++) {
|
|
79
148
|
const segment = segments[i];
|
|
80
149
|
if (segment === "*") {
|
|
81
150
|
if (!node.wildcardChild) {
|
|
@@ -103,10 +172,8 @@ var RadixRouter = class _RadixRouter {
|
|
|
103
172
|
}
|
|
104
173
|
}
|
|
105
174
|
node.handlers.set(method.toLowerCase(), handlers);
|
|
175
|
+
this.routeCache.clear();
|
|
106
176
|
}
|
|
107
|
-
/**
|
|
108
|
-
* Match a request
|
|
109
|
-
*/
|
|
110
177
|
match(method, path) {
|
|
111
178
|
const normalizedMethod = method.toLowerCase();
|
|
112
179
|
if (path === "/" || path === "") {
|
|
@@ -116,9 +183,15 @@ var RadixRouter = class _RadixRouter {
|
|
|
116
183
|
}
|
|
117
184
|
return null;
|
|
118
185
|
}
|
|
186
|
+
const cacheKey = `${normalizedMethod}:${path}`;
|
|
187
|
+
if (this.routeCache.has(cacheKey)) {
|
|
188
|
+
return this.routeCache.get(cacheKey) ?? null;
|
|
189
|
+
}
|
|
119
190
|
const searchPath = path.startsWith("/") ? path.slice(1) : path;
|
|
120
191
|
const segments = searchPath.split("/");
|
|
121
|
-
|
|
192
|
+
const result = this.matchRecursive(this.root, segments, 0, {}, normalizedMethod);
|
|
193
|
+
this.routeCache.set(cacheKey, result);
|
|
194
|
+
return result;
|
|
122
195
|
}
|
|
123
196
|
matchRecursive(node, segments, depth, params, method) {
|
|
124
197
|
if (depth >= segments.length) {
|
|
@@ -141,8 +214,7 @@ var RadixRouter = class _RadixRouter {
|
|
|
141
214
|
}
|
|
142
215
|
const paramChild = node.paramChild;
|
|
143
216
|
if (paramChild) {
|
|
144
|
-
if (paramChild.regex && !paramChild.regex.test(segment)) {
|
|
145
|
-
} else {
|
|
217
|
+
if (paramChild.regex && !paramChild.regex.test(segment)) {} else {
|
|
146
218
|
if (paramChild.paramName) {
|
|
147
219
|
params[paramChild.paramName] = decodeURIComponent(segment);
|
|
148
220
|
const match = this.matchRecursive(paramChild, segments, depth + 1, params, method);
|
|
@@ -177,9 +249,6 @@ var RadixRouter = class _RadixRouter {
|
|
|
177
249
|
}
|
|
178
250
|
return p.split("/");
|
|
179
251
|
}
|
|
180
|
-
/**
|
|
181
|
-
* Serialize the router to a JSON string
|
|
182
|
-
*/
|
|
183
252
|
serialize() {
|
|
184
253
|
return JSON.stringify({
|
|
185
254
|
root: this.root.toJSON(),
|
|
@@ -189,12 +258,9 @@ var RadixRouter = class _RadixRouter {
|
|
|
189
258
|
])
|
|
190
259
|
});
|
|
191
260
|
}
|
|
192
|
-
/**
|
|
193
|
-
* Restore a router from a serialized JSON string
|
|
194
|
-
*/
|
|
195
261
|
static fromSerialized(json) {
|
|
196
262
|
const data = JSON.parse(json);
|
|
197
|
-
const router = new
|
|
263
|
+
const router = new RadixRouter;
|
|
198
264
|
router.root = RadixNode.fromJSON(data.root);
|
|
199
265
|
if (data.globalConstraints) {
|
|
200
266
|
for (const [key, source] of data.globalConstraints) {
|
|
@@ -203,49 +269,22 @@ var RadixRouter = class _RadixRouter {
|
|
|
203
269
|
}
|
|
204
270
|
return router;
|
|
205
271
|
}
|
|
206
|
-
}
|
|
272
|
+
}
|
|
207
273
|
|
|
208
274
|
// 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 */
|
|
275
|
+
class AOTRouter {
|
|
276
|
+
staticRoutes = new Map;
|
|
277
|
+
dynamicRouter = new RadixRouter;
|
|
217
278
|
routeDefinitions = [];
|
|
218
|
-
// Global middleware (applies to all routes)
|
|
219
|
-
/** @internal */
|
|
220
279
|
globalMiddleware = [];
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
// 用於追蹤動態路由的模式,防止高基數問題
|
|
226
|
-
dynamicRoutePatterns = /* @__PURE__ */ new Map();
|
|
227
|
-
middlewareCache = /* @__PURE__ */ new Map();
|
|
228
|
-
cacheMaxSize = 1e3;
|
|
280
|
+
pathMiddleware = new Map;
|
|
281
|
+
dynamicRoutePatterns = new Map;
|
|
282
|
+
middlewareCache = new Map;
|
|
283
|
+
cacheMaxSize = 1000;
|
|
229
284
|
_version = 0;
|
|
230
|
-
/**
|
|
231
|
-
* Get the current version for cache invalidation
|
|
232
|
-
* Incremented whenever middleware or routes are modified
|
|
233
|
-
*/
|
|
234
285
|
get version() {
|
|
235
286
|
return this._version;
|
|
236
287
|
}
|
|
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
288
|
add(method, path, handler, middleware = []) {
|
|
250
289
|
this.routeDefinitions.push({ method, path, handler, middleware });
|
|
251
290
|
const normalizedMethod = method.toLowerCase();
|
|
@@ -261,9 +300,6 @@ var AOTRouter = class {
|
|
|
261
300
|
}
|
|
262
301
|
}
|
|
263
302
|
}
|
|
264
|
-
/**
|
|
265
|
-
* Mount another router at a prefix
|
|
266
|
-
*/
|
|
267
303
|
mount(prefix, other) {
|
|
268
304
|
if (other.globalMiddleware.length > 0) {
|
|
269
305
|
this.usePattern(prefix, ...other.globalMiddleware);
|
|
@@ -271,7 +307,8 @@ var AOTRouter = class {
|
|
|
271
307
|
this.usePattern(wildcard, ...other.globalMiddleware);
|
|
272
308
|
}
|
|
273
309
|
for (const [pattern, mws] of other.pathMiddleware) {
|
|
274
|
-
|
|
310
|
+
const hasMethodPrefix = /^(get|post|put|delete|patch|options|head):/.test(pattern);
|
|
311
|
+
if (hasMethodPrefix) {
|
|
275
312
|
continue;
|
|
276
313
|
}
|
|
277
314
|
let newPattern;
|
|
@@ -296,25 +333,10 @@ var AOTRouter = class {
|
|
|
296
333
|
this.add(def.method, newPath, def.handler, def.middleware);
|
|
297
334
|
}
|
|
298
335
|
}
|
|
299
|
-
/**
|
|
300
|
-
* Add global middleware
|
|
301
|
-
*
|
|
302
|
-
* These run for every request, before route-specific middleware.
|
|
303
|
-
*
|
|
304
|
-
* @param middleware - Middleware functions
|
|
305
|
-
*/
|
|
306
336
|
use(...middleware) {
|
|
307
337
|
this.globalMiddleware.push(...middleware);
|
|
308
338
|
this._version++;
|
|
309
339
|
}
|
|
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
340
|
usePattern(pattern, ...middleware) {
|
|
319
341
|
if (pattern === "*") {
|
|
320
342
|
this.globalMiddleware.push(...middleware);
|
|
@@ -324,15 +346,6 @@ var AOTRouter = class {
|
|
|
324
346
|
}
|
|
325
347
|
this._version++;
|
|
326
348
|
}
|
|
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
349
|
match(method, path) {
|
|
337
350
|
const normalizedMethod = method.toLowerCase();
|
|
338
351
|
const staticKey = `${normalizedMethod}:${path}`;
|
|
@@ -365,28 +378,16 @@ var AOTRouter = class {
|
|
|
365
378
|
middleware: []
|
|
366
379
|
};
|
|
367
380
|
}
|
|
368
|
-
/**
|
|
369
|
-
* Public wrapper for collectMiddleware (used by Gravito for optimization)
|
|
370
|
-
*/
|
|
371
381
|
collectMiddlewarePublic(path, routeMiddleware) {
|
|
372
382
|
return this.collectMiddleware(path, routeMiddleware);
|
|
373
383
|
}
|
|
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
384
|
collectMiddleware(path, routeMiddleware) {
|
|
384
385
|
if (this.globalMiddleware.length === 0 && this.pathMiddleware.size === 0 && routeMiddleware.length === 0) {
|
|
385
386
|
return [];
|
|
386
387
|
}
|
|
387
388
|
const cacheKey = path;
|
|
388
389
|
const cached = this.middlewareCache.get(cacheKey);
|
|
389
|
-
if (cached !==
|
|
390
|
+
if (cached !== undefined && cached.version === this._version) {
|
|
390
391
|
return cached.data;
|
|
391
392
|
}
|
|
392
393
|
const middleware = [];
|
|
@@ -413,23 +414,21 @@ var AOTRouter = class {
|
|
|
413
414
|
}
|
|
414
415
|
return middleware;
|
|
415
416
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
417
|
+
getNativeRoutes(onMatch) {
|
|
418
|
+
const routes = {};
|
|
419
|
+
for (const [key, metadata] of this.staticRoutes) {
|
|
420
|
+
const [method, path] = key.split(":");
|
|
421
|
+
if (method !== "get") {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const allMiddleware = this.collectMiddleware(path, metadata.middleware);
|
|
425
|
+
routes[path] = onMatch(metadata.handler, allMiddleware, path);
|
|
426
|
+
}
|
|
427
|
+
return routes;
|
|
428
|
+
}
|
|
419
429
|
isStaticPath(path) {
|
|
420
430
|
return !path.includes(":") && !path.includes("*");
|
|
421
431
|
}
|
|
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
432
|
matchPattern(pattern, path) {
|
|
434
433
|
if (pattern === "*") {
|
|
435
434
|
return true;
|
|
@@ -443,9 +442,6 @@ var AOTRouter = class {
|
|
|
443
442
|
}
|
|
444
443
|
return false;
|
|
445
444
|
}
|
|
446
|
-
/**
|
|
447
|
-
* Get all registered routes (for debugging)
|
|
448
|
-
*/
|
|
449
445
|
getRoutes() {
|
|
450
446
|
const routes = [];
|
|
451
447
|
for (const key of this.staticRoutes.keys()) {
|
|
@@ -454,11 +450,107 @@ var AOTRouter = class {
|
|
|
454
450
|
}
|
|
455
451
|
return routes;
|
|
456
452
|
}
|
|
457
|
-
}
|
|
453
|
+
}
|
|
458
454
|
|
|
459
|
-
// src/
|
|
460
|
-
|
|
461
|
-
|
|
455
|
+
// src/transpiler-utils.ts
|
|
456
|
+
class TranspilerCache {
|
|
457
|
+
static instance = null;
|
|
458
|
+
transpiler;
|
|
459
|
+
cache;
|
|
460
|
+
maxSize;
|
|
461
|
+
ttlMs;
|
|
462
|
+
constructor(maxSize = 512, ttlMs = 5 * 60 * 1000) {
|
|
463
|
+
this.transpiler = new Bun.Transpiler({ loader: "ts" });
|
|
464
|
+
this.cache = new Map;
|
|
465
|
+
this.maxSize = maxSize;
|
|
466
|
+
this.ttlMs = ttlMs;
|
|
467
|
+
}
|
|
468
|
+
static getInstance() {
|
|
469
|
+
TranspilerCache.instance ??= new TranspilerCache;
|
|
470
|
+
return TranspilerCache.instance;
|
|
471
|
+
}
|
|
472
|
+
static resetInstance() {
|
|
473
|
+
TranspilerCache.instance = null;
|
|
474
|
+
}
|
|
475
|
+
transform(source) {
|
|
476
|
+
const cached = this.cache.get(source);
|
|
477
|
+
if (cached !== undefined) {
|
|
478
|
+
if (Date.now() - cached.createdAt < this.ttlMs) {
|
|
479
|
+
return cached.transformed;
|
|
480
|
+
}
|
|
481
|
+
this.cache.delete(source);
|
|
482
|
+
}
|
|
483
|
+
const transformed = this.doTransform(source);
|
|
484
|
+
if (transformed === null) {
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
if (this.cache.size >= this.maxSize) {
|
|
488
|
+
const firstKey = this.cache.keys().next().value;
|
|
489
|
+
if (firstKey !== undefined) {
|
|
490
|
+
this.cache.delete(firstKey);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
this.cache.set(source, { transformed, createdAt: Date.now() });
|
|
494
|
+
return transformed;
|
|
495
|
+
}
|
|
496
|
+
doTransform(source) {
|
|
497
|
+
try {
|
|
498
|
+
const out = this.transpiler.transformSync(source);
|
|
499
|
+
if (out.trim().length > 0) {
|
|
500
|
+
return out;
|
|
501
|
+
}
|
|
502
|
+
return this.transformWrapped(source);
|
|
503
|
+
} catch {
|
|
504
|
+
return this.transformWrapped(source);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
transformWrapped(source) {
|
|
508
|
+
try {
|
|
509
|
+
const wrapped = `const __fn = ${source}`;
|
|
510
|
+
const out = this.transpiler.transformSync(wrapped);
|
|
511
|
+
return out.trim().length > 0 ? out : null;
|
|
512
|
+
} catch {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
get size() {
|
|
517
|
+
return this.cache.size;
|
|
518
|
+
}
|
|
519
|
+
clear() {
|
|
520
|
+
this.cache.clear();
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
var PATTERNS = {
|
|
524
|
+
HEADERS_CALL: /\.req\.headers?\s*\(/,
|
|
525
|
+
HEADERS_DESTR: /\{[^}]*\bheaders?\b[^}]*\}\s*=\s*\w+\.req/,
|
|
526
|
+
QUERY_CALL: /\.req\.quer(?:y|ies)\s*\(/,
|
|
527
|
+
QUERY_DESTR: /\{[^}]*\bquer(?:y|ies)\b[^}]*\}\s*=\s*\w+\.req/,
|
|
528
|
+
BODY_CALL: /\.req\.(?:json|text|formData|blob|arrayBuffer)\s*\(/,
|
|
529
|
+
BODY_DESTR: /\{[^}]*\bbody\b[^}]*\}\s*=\s*\w+\.req/,
|
|
530
|
+
BODY_PROP: /\.req\.body\b/,
|
|
531
|
+
PARAMS_CALL: /\.req\.params?\s*\(/,
|
|
532
|
+
PARAMS_DESTR: /\{[^}]*\bparams?\b[^}]*\}\s*=\s*\w+\.req/,
|
|
533
|
+
IS_ASYNC: /\basync\b/
|
|
534
|
+
};
|
|
535
|
+
function analyzeHandlerWithTranspiler(source) {
|
|
536
|
+
const cache = TranspilerCache.getInstance();
|
|
537
|
+
const transformed = cache.transform(source);
|
|
538
|
+
const isAsync = PATTERNS.IS_ASYNC.test(source);
|
|
539
|
+
if (transformed !== null) {
|
|
540
|
+
return { ...analyzeTransformedCode(transformed), isAsync };
|
|
541
|
+
}
|
|
542
|
+
return { ...fallbackStringAnalysis(source), isAsync };
|
|
543
|
+
}
|
|
544
|
+
function analyzeTransformedCode(code) {
|
|
545
|
+
return {
|
|
546
|
+
usesHeaders: PATTERNS.HEADERS_CALL.test(code) || PATTERNS.HEADERS_DESTR.test(code),
|
|
547
|
+
usesQuery: PATTERNS.QUERY_CALL.test(code) || PATTERNS.QUERY_DESTR.test(code),
|
|
548
|
+
usesBody: PATTERNS.BODY_CALL.test(code) || PATTERNS.BODY_DESTR.test(code) || PATTERNS.BODY_PROP.test(code),
|
|
549
|
+
usesParams: PATTERNS.PARAMS_CALL.test(code) || PATTERNS.PARAMS_DESTR.test(code),
|
|
550
|
+
isAsync: PATTERNS.IS_ASYNC.test(code)
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
function fallbackStringAnalysis(source) {
|
|
462
554
|
return {
|
|
463
555
|
usesHeaders: source.includes(".header(") || source.includes(".header)") || source.includes(".headers(") || source.includes(".headers)"),
|
|
464
556
|
usesQuery: source.includes(".query(") || source.includes(".query)") || source.includes(".queries(") || source.includes(".queries)"),
|
|
@@ -467,6 +559,12 @@ function analyzeHandler(handler) {
|
|
|
467
559
|
isAsync: source.includes("async") || source.includes("await")
|
|
468
560
|
};
|
|
469
561
|
}
|
|
562
|
+
|
|
563
|
+
// src/engine/analyzer.ts
|
|
564
|
+
function analyzeHandler(handler) {
|
|
565
|
+
const source = handler.toString();
|
|
566
|
+
return analyzeHandlerWithTranspiler(source);
|
|
567
|
+
}
|
|
470
568
|
function getOptimalContextType(analysis) {
|
|
471
569
|
if (analysis.usesHeaders) {
|
|
472
570
|
return "fast";
|
|
@@ -484,7 +582,7 @@ function getOptimalContextType(analysis) {
|
|
|
484
582
|
}
|
|
485
583
|
|
|
486
584
|
// src/engine/constants.ts
|
|
487
|
-
var encoder = new TextEncoder
|
|
585
|
+
var encoder = new TextEncoder;
|
|
488
586
|
var CACHED_RESPONSES = {
|
|
489
587
|
NOT_FOUND: encoder.encode('{"error":"Not Found"}'),
|
|
490
588
|
INTERNAL_ERROR: encoder.encode('{"error":"Internal Server Error"}'),
|
|
@@ -498,25 +596,15 @@ var HEADERS = {
|
|
|
498
596
|
};
|
|
499
597
|
|
|
500
598
|
// src/Container/RequestScopeMetrics.ts
|
|
501
|
-
|
|
599
|
+
class RequestScopeMetrics {
|
|
502
600
|
cleanupStartTime = null;
|
|
503
601
|
cleanupDuration = null;
|
|
504
602
|
scopeSize = 0;
|
|
505
603
|
servicesCleaned = 0;
|
|
506
604
|
errorsOccurred = 0;
|
|
507
|
-
/**
|
|
508
|
-
* Record start of cleanup operation
|
|
509
|
-
*/
|
|
510
605
|
recordCleanupStart() {
|
|
511
606
|
this.cleanupStartTime = performance.now();
|
|
512
607
|
}
|
|
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
608
|
recordCleanupEnd(scopeSize, servicesCleaned, errorsOccurred = 0) {
|
|
521
609
|
if (this.cleanupStartTime !== null) {
|
|
522
610
|
this.cleanupDuration = performance.now() - this.cleanupStartTime;
|
|
@@ -526,28 +614,15 @@ var RequestScopeMetrics = class {
|
|
|
526
614
|
this.servicesCleaned = servicesCleaned;
|
|
527
615
|
this.errorsOccurred = errorsOccurred;
|
|
528
616
|
}
|
|
529
|
-
/**
|
|
530
|
-
* Get cleanup duration in milliseconds
|
|
531
|
-
*
|
|
532
|
-
* @returns Duration in ms, or null if cleanup not completed
|
|
533
|
-
*/
|
|
534
617
|
getCleanupDuration() {
|
|
535
618
|
return this.cleanupDuration;
|
|
536
619
|
}
|
|
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
620
|
isSlowCleanup(thresholdMs = 2) {
|
|
545
|
-
if (this.cleanupDuration === null)
|
|
621
|
+
if (this.cleanupDuration === null) {
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
546
624
|
return this.cleanupDuration > thresholdMs;
|
|
547
625
|
}
|
|
548
|
-
/**
|
|
549
|
-
* Export metrics as JSON for logging/monitoring
|
|
550
|
-
*/
|
|
551
626
|
toJSON() {
|
|
552
627
|
return {
|
|
553
628
|
cleanupDuration: this.cleanupDuration,
|
|
@@ -558,49 +633,66 @@ var RequestScopeMetrics = class {
|
|
|
558
633
|
isSlowCleanup: this.isSlowCleanup()
|
|
559
634
|
};
|
|
560
635
|
}
|
|
561
|
-
/**
|
|
562
|
-
* Export metrics as compact string for logging
|
|
563
|
-
*/
|
|
564
636
|
toString() {
|
|
565
637
|
const duration = this.cleanupDuration ?? "pending";
|
|
566
|
-
return `cleanup: ${duration}ms, scope: ${this.scopeSize}, cleaned: ${this.servicesCleaned}, errors: ${this.errorsOccurred}`;
|
|
638
|
+
return `cleanup: ${duration}ms, ` + `scope: ${this.scopeSize}, ` + `cleaned: ${this.servicesCleaned}, ` + `errors: ${this.errorsOccurred}`;
|
|
567
639
|
}
|
|
568
|
-
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
class RequestScopeMetricsCollector {
|
|
643
|
+
metrics = [];
|
|
644
|
+
record(metrics) {
|
|
645
|
+
this.metrics.push(metrics);
|
|
646
|
+
}
|
|
647
|
+
getStats() {
|
|
648
|
+
if (this.metrics.length === 0) {
|
|
649
|
+
return {
|
|
650
|
+
count: 0,
|
|
651
|
+
averageCleanupTime: null,
|
|
652
|
+
maxCleanupTime: null,
|
|
653
|
+
minCleanupTime: null,
|
|
654
|
+
totalErrorCount: 0,
|
|
655
|
+
errorRate: 0
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
const durations = this.metrics.map((m) => m.getCleanupDuration()).filter((d) => d !== null);
|
|
659
|
+
const errorCounts = this.metrics.map((m) => m.toJSON().errorsOccurred);
|
|
660
|
+
const totalErrors = errorCounts.reduce((a, b) => a + b, 0);
|
|
661
|
+
return {
|
|
662
|
+
count: this.metrics.length,
|
|
663
|
+
averageCleanupTime: durations.length > 0 ? durations.reduce((a, b) => a + b) / durations.length : null,
|
|
664
|
+
maxCleanupTime: durations.length > 0 ? Math.max(...durations) : null,
|
|
665
|
+
minCleanupTime: durations.length > 0 ? Math.min(...durations) : null,
|
|
666
|
+
totalErrorCount: totalErrors,
|
|
667
|
+
errorRate: totalErrors / this.metrics.length
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
clear() {
|
|
671
|
+
this.metrics = [];
|
|
672
|
+
}
|
|
673
|
+
size() {
|
|
674
|
+
return this.metrics.length;
|
|
675
|
+
}
|
|
676
|
+
toJSON() {
|
|
677
|
+
return this.metrics.map((m) => m.toJSON());
|
|
678
|
+
}
|
|
679
|
+
}
|
|
569
680
|
|
|
570
681
|
// src/Container/RequestScopeManager.ts
|
|
571
|
-
|
|
572
|
-
scoped =
|
|
573
|
-
metadata =
|
|
574
|
-
metrics = new RequestScopeMetrics
|
|
682
|
+
class RequestScopeManager {
|
|
683
|
+
scoped = new Map;
|
|
684
|
+
metadata = new Map;
|
|
685
|
+
metrics = new RequestScopeMetrics;
|
|
575
686
|
observer = null;
|
|
576
687
|
constructor(observer) {
|
|
577
688
|
this.observer = observer || null;
|
|
578
689
|
}
|
|
579
|
-
/**
|
|
580
|
-
* Set observer for monitoring scope lifecycle
|
|
581
|
-
*/
|
|
582
690
|
setObserver(observer) {
|
|
583
691
|
this.observer = observer;
|
|
584
692
|
}
|
|
585
|
-
/**
|
|
586
|
-
* Get metrics for this scope
|
|
587
|
-
*/
|
|
588
693
|
getMetrics() {
|
|
589
694
|
return this.metrics;
|
|
590
695
|
}
|
|
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
696
|
resolve(key, factory) {
|
|
605
697
|
const keyStr = String(key);
|
|
606
698
|
const isFromCache = this.scoped.has(keyStr);
|
|
@@ -614,15 +706,6 @@ var RequestScopeManager = class {
|
|
|
614
706
|
this.observer?.onServiceResolved?.(key, isFromCache);
|
|
615
707
|
return this.scoped.get(keyStr);
|
|
616
708
|
}
|
|
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
709
|
async cleanup() {
|
|
627
710
|
this.metrics.recordCleanupStart();
|
|
628
711
|
this.observer?.onCleanupStart?.();
|
|
@@ -637,9 +720,7 @@ var RequestScopeManager = class {
|
|
|
637
720
|
servicesCleaned++;
|
|
638
721
|
} catch (error) {
|
|
639
722
|
errors.push(error);
|
|
640
|
-
this.observer?.onCleanupError?.(
|
|
641
|
-
error instanceof Error ? error : new Error(String(error))
|
|
642
|
-
);
|
|
723
|
+
this.observer?.onCleanupError?.(error instanceof Error ? error : new Error(String(error)));
|
|
643
724
|
}
|
|
644
725
|
}
|
|
645
726
|
}
|
|
@@ -653,18 +734,20 @@ var RequestScopeManager = class {
|
|
|
653
734
|
console.error("RequestScope cleanup errors:", errors);
|
|
654
735
|
}
|
|
655
736
|
}
|
|
656
|
-
/**
|
|
657
|
-
* Get the number of services in this scope (for monitoring).
|
|
658
|
-
*
|
|
659
|
-
* @returns The count of cached services.
|
|
660
|
-
*/
|
|
661
737
|
size() {
|
|
662
738
|
return this.scoped.size;
|
|
663
739
|
}
|
|
664
|
-
}
|
|
740
|
+
}
|
|
665
741
|
|
|
666
742
|
// src/engine/FastContext.ts
|
|
667
|
-
var
|
|
743
|
+
var bunEscapeHTML = (html) => {
|
|
744
|
+
return html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
745
|
+
};
|
|
746
|
+
try {
|
|
747
|
+
bunEscapeHTML = globalThis.Bun.escapeHTML;
|
|
748
|
+
} catch {}
|
|
749
|
+
|
|
750
|
+
class FastRequestImpl {
|
|
668
751
|
_request;
|
|
669
752
|
_params;
|
|
670
753
|
_path;
|
|
@@ -672,21 +755,18 @@ var FastRequestImpl = class {
|
|
|
672
755
|
_url = null;
|
|
673
756
|
_query = null;
|
|
674
757
|
_headers = null;
|
|
675
|
-
_cachedJson =
|
|
758
|
+
_cachedJson = undefined;
|
|
676
759
|
_jsonParsed = false;
|
|
677
|
-
_cachedText =
|
|
760
|
+
_cachedText = undefined;
|
|
678
761
|
_textParsed = false;
|
|
679
|
-
_cachedFormData =
|
|
762
|
+
_cachedFormData = undefined;
|
|
680
763
|
_formDataParsed = false;
|
|
681
764
|
_cachedQueries = null;
|
|
682
|
-
|
|
765
|
+
_cachedCookies = null;
|
|
683
766
|
_ctx;
|
|
684
767
|
constructor(ctx) {
|
|
685
768
|
this._ctx = ctx;
|
|
686
769
|
}
|
|
687
|
-
/**
|
|
688
|
-
* Initialize for new request
|
|
689
|
-
*/
|
|
690
770
|
init(request, params = {}, path = "", routePattern) {
|
|
691
771
|
this._request = request;
|
|
692
772
|
this._params = params;
|
|
@@ -695,37 +775,34 @@ var FastRequestImpl = class {
|
|
|
695
775
|
this._url = null;
|
|
696
776
|
this._query = null;
|
|
697
777
|
this._headers = null;
|
|
698
|
-
this._cachedJson =
|
|
778
|
+
this._cachedJson = undefined;
|
|
699
779
|
this._jsonParsed = false;
|
|
700
|
-
this._cachedText =
|
|
780
|
+
this._cachedText = undefined;
|
|
701
781
|
this._textParsed = false;
|
|
702
|
-
this._cachedFormData =
|
|
782
|
+
this._cachedFormData = undefined;
|
|
703
783
|
this._formDataParsed = false;
|
|
704
784
|
this._cachedQueries = null;
|
|
785
|
+
this._cachedCookies = null;
|
|
705
786
|
return this;
|
|
706
787
|
}
|
|
707
|
-
/**
|
|
708
|
-
* Reset for pooling
|
|
709
|
-
*/
|
|
710
788
|
reset() {
|
|
711
|
-
this._request =
|
|
712
|
-
this._params =
|
|
789
|
+
this._request = undefined;
|
|
790
|
+
this._params = undefined;
|
|
713
791
|
this._url = null;
|
|
714
792
|
this._query = null;
|
|
715
793
|
this._headers = null;
|
|
716
|
-
this._cachedJson =
|
|
794
|
+
this._cachedJson = undefined;
|
|
717
795
|
this._jsonParsed = false;
|
|
718
|
-
this._cachedText =
|
|
796
|
+
this._cachedText = undefined;
|
|
719
797
|
this._textParsed = false;
|
|
720
|
-
this._cachedFormData =
|
|
798
|
+
this._cachedFormData = undefined;
|
|
721
799
|
this._formDataParsed = false;
|
|
722
800
|
this._cachedQueries = null;
|
|
801
|
+
this._cachedCookies = null;
|
|
723
802
|
}
|
|
724
803
|
checkReleased() {
|
|
725
804
|
if (this._ctx._isReleased) {
|
|
726
|
-
throw new Error(
|
|
727
|
-
"FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)"
|
|
728
|
-
);
|
|
805
|
+
throw new Error("FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)");
|
|
729
806
|
}
|
|
730
807
|
}
|
|
731
808
|
get url() {
|
|
@@ -763,7 +840,7 @@ var FastRequestImpl = class {
|
|
|
763
840
|
if (!this._query) {
|
|
764
841
|
this._query = this.getUrl().searchParams;
|
|
765
842
|
}
|
|
766
|
-
return this._query.get(name) ??
|
|
843
|
+
return this._query.get(name) ?? undefined;
|
|
767
844
|
}
|
|
768
845
|
queries() {
|
|
769
846
|
this.checkReleased();
|
|
@@ -776,7 +853,7 @@ var FastRequestImpl = class {
|
|
|
776
853
|
const result = {};
|
|
777
854
|
for (const [key, value] of this._query.entries()) {
|
|
778
855
|
const existing = result[key];
|
|
779
|
-
if (existing ===
|
|
856
|
+
if (existing === undefined) {
|
|
780
857
|
result[key] = value;
|
|
781
858
|
} else if (Array.isArray(existing)) {
|
|
782
859
|
existing.push(value);
|
|
@@ -789,7 +866,7 @@ var FastRequestImpl = class {
|
|
|
789
866
|
}
|
|
790
867
|
header(name) {
|
|
791
868
|
this.checkReleased();
|
|
792
|
-
return this._request.headers.get(name) ??
|
|
869
|
+
return this._request.headers.get(name) ?? undefined;
|
|
793
870
|
}
|
|
794
871
|
headers() {
|
|
795
872
|
this.checkReleased();
|
|
@@ -801,6 +878,39 @@ var FastRequestImpl = class {
|
|
|
801
878
|
}
|
|
802
879
|
return { ...this._headers };
|
|
803
880
|
}
|
|
881
|
+
get cookies() {
|
|
882
|
+
this.checkReleased();
|
|
883
|
+
if (this._cachedCookies !== null) {
|
|
884
|
+
return this._cachedCookies;
|
|
885
|
+
}
|
|
886
|
+
const nativeCookies = this._request.cookies;
|
|
887
|
+
if (nativeCookies) {
|
|
888
|
+
this._cachedCookies = nativeCookies;
|
|
889
|
+
return nativeCookies;
|
|
890
|
+
}
|
|
891
|
+
const cookieHeader = this._request.headers.get("cookie");
|
|
892
|
+
if (!cookieHeader) {
|
|
893
|
+
this._cachedCookies = {};
|
|
894
|
+
return {};
|
|
895
|
+
}
|
|
896
|
+
const cookies = {};
|
|
897
|
+
const pairs = cookieHeader.split(/;\s*/);
|
|
898
|
+
for (let i = 0;i < pairs.length; i++) {
|
|
899
|
+
const pair = pairs[i];
|
|
900
|
+
const idx = pair.indexOf("=");
|
|
901
|
+
if (idx > 0) {
|
|
902
|
+
const name = pair.substring(0, idx);
|
|
903
|
+
const value = pair.substring(idx + 1);
|
|
904
|
+
try {
|
|
905
|
+
cookies[name] = decodeURIComponent(value);
|
|
906
|
+
} catch {
|
|
907
|
+
cookies[name] = value;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
this._cachedCookies = cookies;
|
|
912
|
+
return cookies;
|
|
913
|
+
}
|
|
804
914
|
async json() {
|
|
805
915
|
this.checkReleased();
|
|
806
916
|
if (!this._jsonParsed) {
|
|
@@ -829,82 +939,57 @@ var FastRequestImpl = class {
|
|
|
829
939
|
this.checkReleased();
|
|
830
940
|
return this._request;
|
|
831
941
|
}
|
|
832
|
-
}
|
|
833
|
-
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
class FastContext {
|
|
834
945
|
req = new FastRequestImpl(this);
|
|
835
|
-
|
|
836
|
-
_headers = new Headers();
|
|
837
|
-
// Reuse this object
|
|
946
|
+
_headers = new Headers;
|
|
838
947
|
_isReleased = false;
|
|
839
|
-
// Made public for internal check access
|
|
840
948
|
_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
949
|
init(request, params = {}, path = "", routePattern) {
|
|
848
950
|
this._isReleased = false;
|
|
849
951
|
this.req.init(request, params, path, routePattern);
|
|
850
|
-
this._headers = new Headers
|
|
851
|
-
this._requestScope = new RequestScopeManager
|
|
952
|
+
this._headers = new Headers;
|
|
953
|
+
this._requestScope = new RequestScopeManager;
|
|
852
954
|
return this;
|
|
853
955
|
}
|
|
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
956
|
reset() {
|
|
861
957
|
this._isReleased = true;
|
|
862
958
|
this.req.reset();
|
|
863
959
|
this._store.clear();
|
|
960
|
+
this._requestScope = null;
|
|
864
961
|
}
|
|
865
|
-
/**
|
|
866
|
-
* Check if context is released
|
|
867
|
-
*/
|
|
868
962
|
checkReleased() {
|
|
869
963
|
if (this._isReleased) {
|
|
870
|
-
throw new Error(
|
|
871
|
-
"FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)"
|
|
872
|
-
);
|
|
964
|
+
throw new Error("FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)");
|
|
873
965
|
}
|
|
874
966
|
}
|
|
875
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
876
|
-
// Response Helpers
|
|
877
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
878
967
|
json(data, status = 200) {
|
|
879
968
|
this.checkReleased();
|
|
880
|
-
this._headers
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
headers: this._headers
|
|
884
|
-
});
|
|
969
|
+
const headers = new Headers(this._headers);
|
|
970
|
+
headers.set("Content-Type", "application/json; charset=utf-8");
|
|
971
|
+
return Response.json(data, { status, headers });
|
|
885
972
|
}
|
|
886
973
|
text(text, status = 200) {
|
|
887
974
|
this.checkReleased();
|
|
888
|
-
this._headers
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
headers: this._headers
|
|
892
|
-
});
|
|
975
|
+
const headers = new Headers(this._headers);
|
|
976
|
+
headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
977
|
+
return new Response(text, { status, headers });
|
|
893
978
|
}
|
|
894
979
|
html(html, status = 200) {
|
|
895
980
|
this.checkReleased();
|
|
896
|
-
this._headers
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
981
|
+
const headers = new Headers(this._headers);
|
|
982
|
+
headers.set("Content-Type", "text/html; charset=utf-8");
|
|
983
|
+
return new Response(html, { status, headers });
|
|
984
|
+
}
|
|
985
|
+
escape(html) {
|
|
986
|
+
return bunEscapeHTML(html);
|
|
901
987
|
}
|
|
902
988
|
redirect(url, status = 302) {
|
|
903
989
|
this.checkReleased();
|
|
904
|
-
this._headers.set("Location", url);
|
|
905
990
|
return new Response(null, {
|
|
906
991
|
status,
|
|
907
|
-
headers:
|
|
992
|
+
headers: { Location: url }
|
|
908
993
|
});
|
|
909
994
|
}
|
|
910
995
|
body(data, status = 200) {
|
|
@@ -914,12 +999,19 @@ var FastContext = class {
|
|
|
914
999
|
headers: this._headers
|
|
915
1000
|
});
|
|
916
1001
|
}
|
|
1002
|
+
binary(data, status = 200) {
|
|
1003
|
+
this.checkReleased();
|
|
1004
|
+
const body = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
|
|
1005
|
+
return new Response(body, {
|
|
1006
|
+
status,
|
|
1007
|
+
headers: { "Content-Type": "application/octet-stream" }
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
917
1010
|
stream(stream, status = 200) {
|
|
918
1011
|
this.checkReleased();
|
|
919
|
-
this._headers.set("Content-Type", "application/octet-stream");
|
|
920
1012
|
return new Response(stream, {
|
|
921
1013
|
status,
|
|
922
|
-
headers:
|
|
1014
|
+
headers: { "Content-Type": "application/octet-stream" }
|
|
923
1015
|
});
|
|
924
1016
|
}
|
|
925
1017
|
notFound(message = "Not Found") {
|
|
@@ -937,9 +1029,7 @@ var FastContext = class {
|
|
|
937
1029
|
async forward(target, _options = {}) {
|
|
938
1030
|
this.checkReleased();
|
|
939
1031
|
const url = new URL(this.req.url);
|
|
940
|
-
const targetUrl = new URL(
|
|
941
|
-
target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`
|
|
942
|
-
);
|
|
1032
|
+
const targetUrl = new URL(target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`);
|
|
943
1033
|
const searchParams = new URLSearchParams(url.search);
|
|
944
1034
|
searchParams.forEach((v, k) => {
|
|
945
1035
|
targetUrl.searchParams.set(k, v);
|
|
@@ -948,13 +1038,12 @@ var FastContext = class {
|
|
|
948
1038
|
method: this.req.method,
|
|
949
1039
|
headers: this.req.raw.headers,
|
|
950
1040
|
body: this.req.method !== "GET" && this.req.method !== "HEAD" ? this.req.raw.body : null,
|
|
951
|
-
// @ts-expect-error - Bun/Fetch specific
|
|
952
1041
|
duplex: "half"
|
|
953
1042
|
});
|
|
954
1043
|
}
|
|
955
1044
|
header(name, value) {
|
|
956
1045
|
this.checkReleased();
|
|
957
|
-
if (value !==
|
|
1046
|
+
if (value !== undefined) {
|
|
958
1047
|
this._headers.set(name, value);
|
|
959
1048
|
return;
|
|
960
1049
|
}
|
|
@@ -963,64 +1052,52 @@ var FastContext = class {
|
|
|
963
1052
|
status(_code) {
|
|
964
1053
|
this.checkReleased();
|
|
965
1054
|
}
|
|
966
|
-
|
|
967
|
-
// Context Variables
|
|
968
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
969
|
-
_store = /* @__PURE__ */ new Map();
|
|
1055
|
+
_store = new Map;
|
|
970
1056
|
get(key) {
|
|
971
1057
|
return this._store.get(key);
|
|
972
1058
|
}
|
|
973
1059
|
set(key, value) {
|
|
974
1060
|
this._store.set(key, value);
|
|
975
1061
|
}
|
|
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
1062
|
requestScope() {
|
|
986
1063
|
if (!this._requestScope) {
|
|
987
1064
|
throw new Error("RequestScope not initialized. Call init() first.");
|
|
988
1065
|
}
|
|
989
1066
|
return this._requestScope;
|
|
990
1067
|
}
|
|
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
1068
|
scoped(key, factory) {
|
|
1000
1069
|
return this.requestScope().resolve(key, factory);
|
|
1001
1070
|
}
|
|
1002
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1003
|
-
// Lifecycle helpers
|
|
1004
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1005
1071
|
route = () => "";
|
|
1006
1072
|
get native() {
|
|
1007
1073
|
return this;
|
|
1008
1074
|
}
|
|
1009
|
-
}
|
|
1075
|
+
}
|
|
1010
1076
|
|
|
1011
1077
|
// src/engine/MinimalContext.ts
|
|
1012
|
-
var
|
|
1078
|
+
var bunEscapeHTML2 = (html) => {
|
|
1079
|
+
return html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1080
|
+
};
|
|
1081
|
+
try {
|
|
1082
|
+
bunEscapeHTML2 = globalThis.Bun.escapeHTML;
|
|
1083
|
+
} catch {}
|
|
1084
|
+
|
|
1085
|
+
class MinimalRequest {
|
|
1086
|
+
_request;
|
|
1087
|
+
_params;
|
|
1088
|
+
_path;
|
|
1089
|
+
_routePattern;
|
|
1090
|
+
_searchParams = null;
|
|
1091
|
+
_cachedQueries = null;
|
|
1092
|
+
_cachedJsonPromise = null;
|
|
1093
|
+
_cachedTextPromise = null;
|
|
1094
|
+
_cachedFormDataPromise = null;
|
|
1013
1095
|
constructor(_request, _params, _path, _routePattern) {
|
|
1014
1096
|
this._request = _request;
|
|
1015
1097
|
this._params = _params;
|
|
1016
1098
|
this._path = _path;
|
|
1017
1099
|
this._routePattern = _routePattern;
|
|
1018
1100
|
}
|
|
1019
|
-
_searchParams = null;
|
|
1020
|
-
_cachedQueries = null;
|
|
1021
|
-
_cachedJsonPromise = null;
|
|
1022
|
-
_cachedTextPromise = null;
|
|
1023
|
-
_cachedFormDataPromise = null;
|
|
1024
1101
|
get url() {
|
|
1025
1102
|
return this._request.url;
|
|
1026
1103
|
}
|
|
@@ -1039,15 +1116,12 @@ var MinimalRequest = class {
|
|
|
1039
1116
|
params() {
|
|
1040
1117
|
return { ...this._params };
|
|
1041
1118
|
}
|
|
1042
|
-
/**
|
|
1043
|
-
* Lazy-initialize searchParams, only parse once
|
|
1044
|
-
*/
|
|
1045
1119
|
getSearchParams() {
|
|
1046
1120
|
if (this._searchParams === null) {
|
|
1047
1121
|
const url = this._request.url;
|
|
1048
1122
|
const queryStart = url.indexOf("?");
|
|
1049
1123
|
if (queryStart === -1) {
|
|
1050
|
-
this._searchParams = new URLSearchParams
|
|
1124
|
+
this._searchParams = new URLSearchParams;
|
|
1051
1125
|
} else {
|
|
1052
1126
|
const hashStart = url.indexOf("#", queryStart);
|
|
1053
1127
|
const queryString = hashStart === -1 ? url.slice(queryStart + 1) : url.slice(queryStart + 1, hashStart);
|
|
@@ -1057,7 +1131,7 @@ var MinimalRequest = class {
|
|
|
1057
1131
|
return this._searchParams;
|
|
1058
1132
|
}
|
|
1059
1133
|
query(name) {
|
|
1060
|
-
return this.getSearchParams().get(name) ??
|
|
1134
|
+
return this.getSearchParams().get(name) ?? undefined;
|
|
1061
1135
|
}
|
|
1062
1136
|
queries() {
|
|
1063
1137
|
if (this._cachedQueries !== null) {
|
|
@@ -1067,7 +1141,7 @@ var MinimalRequest = class {
|
|
|
1067
1141
|
const result = {};
|
|
1068
1142
|
for (const [key, value] of params.entries()) {
|
|
1069
1143
|
const existing = result[key];
|
|
1070
|
-
if (existing ===
|
|
1144
|
+
if (existing === undefined) {
|
|
1071
1145
|
result[key] = value;
|
|
1072
1146
|
} else if (Array.isArray(existing)) {
|
|
1073
1147
|
existing.push(value);
|
|
@@ -1079,7 +1153,7 @@ var MinimalRequest = class {
|
|
|
1079
1153
|
return result;
|
|
1080
1154
|
}
|
|
1081
1155
|
header(name) {
|
|
1082
|
-
return this._request.headers.get(name) ??
|
|
1156
|
+
return this._request.headers.get(name) ?? undefined;
|
|
1083
1157
|
}
|
|
1084
1158
|
headers() {
|
|
1085
1159
|
const result = {};
|
|
@@ -1106,23 +1180,45 @@ var MinimalRequest = class {
|
|
|
1106
1180
|
}
|
|
1107
1181
|
return this._cachedFormDataPromise;
|
|
1108
1182
|
}
|
|
1183
|
+
get cookies() {
|
|
1184
|
+
const nativeCookies = this._request.cookies;
|
|
1185
|
+
if (nativeCookies) {
|
|
1186
|
+
return nativeCookies;
|
|
1187
|
+
}
|
|
1188
|
+
const cookieHeader = this._request.headers.get("cookie");
|
|
1189
|
+
if (!cookieHeader) {
|
|
1190
|
+
return {};
|
|
1191
|
+
}
|
|
1192
|
+
const cookies = {};
|
|
1193
|
+
const pairs = cookieHeader.split(/;\s*/);
|
|
1194
|
+
for (let i = 0;i < pairs.length; i++) {
|
|
1195
|
+
const pair = pairs[i];
|
|
1196
|
+
const idx = pair.indexOf("=");
|
|
1197
|
+
if (idx > 0) {
|
|
1198
|
+
const name = pair.substring(0, idx);
|
|
1199
|
+
const value = pair.substring(idx + 1);
|
|
1200
|
+
try {
|
|
1201
|
+
cookies[name] = decodeURIComponent(value);
|
|
1202
|
+
} catch {
|
|
1203
|
+
cookies[name] = value;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
return cookies;
|
|
1208
|
+
}
|
|
1109
1209
|
get raw() {
|
|
1110
1210
|
return this._request;
|
|
1111
1211
|
}
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
class MinimalContext {
|
|
1114
1215
|
req;
|
|
1115
1216
|
_resHeaders = {};
|
|
1116
1217
|
_requestScope;
|
|
1117
1218
|
constructor(request, params, path, routePattern) {
|
|
1118
1219
|
this.req = new MinimalRequest(request, params, path, routePattern);
|
|
1119
|
-
this._requestScope = new RequestScopeManager
|
|
1220
|
+
this._requestScope = new RequestScopeManager;
|
|
1120
1221
|
}
|
|
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
1222
|
getHeaders(contentType) {
|
|
1127
1223
|
const headers = Object.assign({ "Content-Type": contentType }, this._resHeaders);
|
|
1128
1224
|
return headers;
|
|
@@ -1158,14 +1254,13 @@ var MinimalContext = class {
|
|
|
1158
1254
|
});
|
|
1159
1255
|
}
|
|
1160
1256
|
header(name, value) {
|
|
1161
|
-
if (value !==
|
|
1257
|
+
if (value !== undefined) {
|
|
1162
1258
|
this._resHeaders[name] = value;
|
|
1163
1259
|
return;
|
|
1164
1260
|
}
|
|
1165
1261
|
return this.req.header(name);
|
|
1166
1262
|
}
|
|
1167
|
-
status(_code) {
|
|
1168
|
-
}
|
|
1263
|
+
status(_code) {}
|
|
1169
1264
|
stream(stream, status = 200) {
|
|
1170
1265
|
return new Response(stream, {
|
|
1171
1266
|
status,
|
|
@@ -1186,35 +1281,22 @@ var MinimalContext = class {
|
|
|
1186
1281
|
}
|
|
1187
1282
|
async forward(target, _options = {}) {
|
|
1188
1283
|
const url = new URL(this.req.url);
|
|
1189
|
-
const targetUrl = new URL(
|
|
1190
|
-
target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`
|
|
1191
|
-
);
|
|
1284
|
+
const targetUrl = new URL(target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`);
|
|
1192
1285
|
return fetch(targetUrl.toString(), {
|
|
1193
1286
|
method: this.req.method,
|
|
1194
1287
|
headers: this.req.raw.headers
|
|
1195
1288
|
});
|
|
1196
1289
|
}
|
|
1197
|
-
|
|
1198
|
-
return
|
|
1290
|
+
escape(html) {
|
|
1291
|
+
return bunEscapeHTML2(html);
|
|
1199
1292
|
}
|
|
1200
|
-
|
|
1293
|
+
get(_key) {
|
|
1294
|
+
return;
|
|
1201
1295
|
}
|
|
1202
|
-
|
|
1203
|
-
* Get the request-scoped service manager for this request.
|
|
1204
|
-
*
|
|
1205
|
-
* @returns The RequestScopeManager for this request.
|
|
1206
|
-
*/
|
|
1296
|
+
set(_key, _value) {}
|
|
1207
1297
|
requestScope() {
|
|
1208
1298
|
return this._requestScope;
|
|
1209
1299
|
}
|
|
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
1300
|
scoped(key, factory) {
|
|
1219
1301
|
return this._requestScope.resolve(key, factory);
|
|
1220
1302
|
}
|
|
@@ -1222,14 +1304,11 @@ var MinimalContext = class {
|
|
|
1222
1304
|
get native() {
|
|
1223
1305
|
return this;
|
|
1224
1306
|
}
|
|
1225
|
-
// Required for interface compatibility
|
|
1226
1307
|
init(_request, _params, _path) {
|
|
1227
1308
|
throw new Error("MinimalContext does not support init. Create a new instance instead.");
|
|
1228
1309
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
}
|
|
1232
|
-
};
|
|
1310
|
+
reset() {}
|
|
1311
|
+
}
|
|
1233
1312
|
|
|
1234
1313
|
// src/engine/path.ts
|
|
1235
1314
|
function extractPath(url) {
|
|
@@ -1247,79 +1326,38 @@ function extractPath(url) {
|
|
|
1247
1326
|
}
|
|
1248
1327
|
|
|
1249
1328
|
// src/engine/pool.ts
|
|
1250
|
-
|
|
1329
|
+
class ObjectPool {
|
|
1251
1330
|
pool = [];
|
|
1252
1331
|
factory;
|
|
1253
1332
|
reset;
|
|
1254
1333
|
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
1334
|
constructor(factory, reset, maxSize = 256) {
|
|
1263
1335
|
this.factory = factory;
|
|
1264
1336
|
this.reset = reset;
|
|
1265
1337
|
this.maxSize = maxSize;
|
|
1266
1338
|
}
|
|
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
1339
|
acquire() {
|
|
1276
1340
|
const obj = this.pool.pop();
|
|
1277
|
-
if (obj !==
|
|
1341
|
+
if (obj !== undefined) {
|
|
1278
1342
|
return obj;
|
|
1279
1343
|
}
|
|
1280
1344
|
return this.factory();
|
|
1281
1345
|
}
|
|
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
1346
|
release(obj) {
|
|
1291
1347
|
if (this.pool.length < this.maxSize) {
|
|
1292
1348
|
this.reset(obj);
|
|
1293
1349
|
this.pool.push(obj);
|
|
1294
1350
|
}
|
|
1295
1351
|
}
|
|
1296
|
-
/**
|
|
1297
|
-
* Clear all objects from the pool
|
|
1298
|
-
*
|
|
1299
|
-
* Useful for testing or when you need to force a clean slate.
|
|
1300
|
-
*/
|
|
1301
1352
|
clear() {
|
|
1302
1353
|
this.pool = [];
|
|
1303
1354
|
}
|
|
1304
|
-
/**
|
|
1305
|
-
* Get current pool size
|
|
1306
|
-
*/
|
|
1307
1355
|
get size() {
|
|
1308
1356
|
return this.pool.length;
|
|
1309
1357
|
}
|
|
1310
|
-
/**
|
|
1311
|
-
* Get maximum pool size
|
|
1312
|
-
*/
|
|
1313
1358
|
get capacity() {
|
|
1314
1359
|
return this.maxSize;
|
|
1315
1360
|
}
|
|
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
1361
|
prewarm(count) {
|
|
1324
1362
|
const targetSize = Math.min(count, this.maxSize);
|
|
1325
1363
|
while (this.pool.length < targetSize) {
|
|
@@ -1328,9 +1366,16 @@ var ObjectPool = class {
|
|
|
1328
1366
|
this.pool.push(obj);
|
|
1329
1367
|
}
|
|
1330
1368
|
}
|
|
1331
|
-
}
|
|
1369
|
+
}
|
|
1332
1370
|
|
|
1333
1371
|
// src/engine/Gravito.ts
|
|
1372
|
+
var bunPeek = (promise) => promise;
|
|
1373
|
+
var bunFile = (path) => path;
|
|
1374
|
+
try {
|
|
1375
|
+
const bunModule = globalThis.Bun;
|
|
1376
|
+
bunPeek = bunModule.peek;
|
|
1377
|
+
bunFile = bunModule.file;
|
|
1378
|
+
} catch {}
|
|
1334
1379
|
function compileMiddlewareChain(middleware, handler) {
|
|
1335
1380
|
if (middleware.length === 0) {
|
|
1336
1381
|
return handler;
|
|
@@ -1339,64 +1384,76 @@ function compileMiddlewareChain(middleware, handler) {
|
|
|
1339
1384
|
const mw = middleware[0];
|
|
1340
1385
|
return async (ctx) => {
|
|
1341
1386
|
let nextCalled = false;
|
|
1342
|
-
const result =
|
|
1387
|
+
const result = mw(ctx, async () => {
|
|
1343
1388
|
nextCalled = true;
|
|
1344
|
-
return
|
|
1389
|
+
return;
|
|
1345
1390
|
});
|
|
1346
|
-
|
|
1347
|
-
|
|
1391
|
+
let finalResult;
|
|
1392
|
+
if (result instanceof Promise) {
|
|
1393
|
+
const peeked = bunPeek(result);
|
|
1394
|
+
finalResult = peeked === result ? await result : peeked;
|
|
1395
|
+
} else {
|
|
1396
|
+
finalResult = result;
|
|
1397
|
+
}
|
|
1398
|
+
if (finalResult instanceof Response) {
|
|
1399
|
+
return finalResult;
|
|
1348
1400
|
}
|
|
1349
1401
|
if (nextCalled) {
|
|
1350
|
-
|
|
1402
|
+
const hResult = handler(ctx);
|
|
1403
|
+
if (hResult instanceof Promise) {
|
|
1404
|
+
const p = bunPeek(hResult);
|
|
1405
|
+
return p === hResult ? await hResult : p;
|
|
1406
|
+
}
|
|
1407
|
+
return hResult;
|
|
1351
1408
|
}
|
|
1352
1409
|
return ctx.json({ error: "Middleware did not call next or return response" }, 500);
|
|
1353
1410
|
};
|
|
1354
1411
|
}
|
|
1355
1412
|
let compiled = handler;
|
|
1356
|
-
for (let i = middleware.length - 1;
|
|
1413
|
+
for (let i = middleware.length - 1;i >= 0; i--) {
|
|
1357
1414
|
const mw = middleware[i];
|
|
1358
1415
|
const nextHandler = compiled;
|
|
1359
1416
|
compiled = async (ctx) => {
|
|
1360
1417
|
let nextCalled = false;
|
|
1361
|
-
const result =
|
|
1418
|
+
const result = mw(ctx, async () => {
|
|
1362
1419
|
nextCalled = true;
|
|
1363
|
-
return
|
|
1420
|
+
return;
|
|
1364
1421
|
});
|
|
1365
|
-
|
|
1366
|
-
|
|
1422
|
+
let finalResult;
|
|
1423
|
+
if (result instanceof Promise) {
|
|
1424
|
+
const peeked = bunPeek(result);
|
|
1425
|
+
finalResult = peeked === result ? await result : peeked;
|
|
1426
|
+
} else {
|
|
1427
|
+
finalResult = result;
|
|
1428
|
+
}
|
|
1429
|
+
if (finalResult instanceof Response) {
|
|
1430
|
+
return finalResult;
|
|
1367
1431
|
}
|
|
1368
1432
|
if (nextCalled) {
|
|
1369
|
-
|
|
1433
|
+
const nextResult = nextHandler(ctx);
|
|
1434
|
+
if (nextResult instanceof Promise) {
|
|
1435
|
+
const p = bunPeek(nextResult);
|
|
1436
|
+
return p === nextResult ? await nextResult : p;
|
|
1437
|
+
}
|
|
1438
|
+
return nextResult;
|
|
1370
1439
|
}
|
|
1371
1440
|
return ctx.json({ error: "Middleware did not call next or return response" }, 500);
|
|
1372
1441
|
};
|
|
1373
1442
|
}
|
|
1374
1443
|
return compiled;
|
|
1375
1444
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1445
|
+
|
|
1446
|
+
class Gravito {
|
|
1447
|
+
router = new AOTRouter;
|
|
1378
1448
|
contextPool;
|
|
1379
1449
|
errorHandler;
|
|
1380
1450
|
notFoundHandler;
|
|
1381
|
-
// Direct reference to static routes Map (O(1) access)
|
|
1382
|
-
/** @internal */
|
|
1383
1451
|
staticRoutes;
|
|
1384
|
-
// Flag: pure static app (no middleware at all) allows ultra-fast path
|
|
1385
1452
|
isPureStaticApp = true;
|
|
1386
|
-
|
|
1387
|
-
compiledDynamicRoutes = /* @__PURE__ */ new Map();
|
|
1388
|
-
/**
|
|
1389
|
-
* Create a new Gravito instance
|
|
1390
|
-
*
|
|
1391
|
-
* @param options - Engine configuration options
|
|
1392
|
-
*/
|
|
1453
|
+
compiledDynamicRoutes = new Map;
|
|
1393
1454
|
constructor(options = {}) {
|
|
1394
1455
|
const poolSize = options.poolSize ?? 256;
|
|
1395
|
-
this.contextPool = new ObjectPool(
|
|
1396
|
-
() => new FastContext(),
|
|
1397
|
-
(ctx) => ctx.reset(),
|
|
1398
|
-
poolSize
|
|
1399
|
-
);
|
|
1456
|
+
this.contextPool = new ObjectPool(() => new FastContext, (ctx) => ctx.reset(), poolSize);
|
|
1400
1457
|
this.contextPool.prewarm(Math.min(32, poolSize));
|
|
1401
1458
|
if (options.onError) {
|
|
1402
1459
|
this.errorHandler = options.onError;
|
|
@@ -1406,58 +1463,27 @@ var Gravito = class {
|
|
|
1406
1463
|
}
|
|
1407
1464
|
this.compileRoutes();
|
|
1408
1465
|
}
|
|
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
1466
|
get(path, ...handlers) {
|
|
1420
1467
|
return this.addRoute("get", path, handlers);
|
|
1421
1468
|
}
|
|
1422
|
-
/**
|
|
1423
|
-
* Register a POST route
|
|
1424
|
-
*/
|
|
1425
1469
|
post(path, ...handlers) {
|
|
1426
1470
|
return this.addRoute("post", path, handlers);
|
|
1427
1471
|
}
|
|
1428
|
-
/**
|
|
1429
|
-
* Register a PUT route
|
|
1430
|
-
*/
|
|
1431
1472
|
put(path, ...handlers) {
|
|
1432
1473
|
return this.addRoute("put", path, handlers);
|
|
1433
1474
|
}
|
|
1434
|
-
/**
|
|
1435
|
-
* Register a DELETE route
|
|
1436
|
-
*/
|
|
1437
1475
|
delete(path, ...handlers) {
|
|
1438
1476
|
return this.addRoute("delete", path, handlers);
|
|
1439
1477
|
}
|
|
1440
|
-
/**
|
|
1441
|
-
* Register a PDF route
|
|
1442
|
-
*/
|
|
1443
1478
|
patch(path, ...handlers) {
|
|
1444
1479
|
return this.addRoute("patch", path, handlers);
|
|
1445
1480
|
}
|
|
1446
|
-
/**
|
|
1447
|
-
* Register an OPTIONS route
|
|
1448
|
-
*/
|
|
1449
1481
|
options(path, ...handlers) {
|
|
1450
1482
|
return this.addRoute("options", path, handlers);
|
|
1451
1483
|
}
|
|
1452
|
-
/**
|
|
1453
|
-
* Register a HEAD route
|
|
1454
|
-
*/
|
|
1455
1484
|
head(path, ...handlers) {
|
|
1456
1485
|
return this.addRoute("head", path, handlers);
|
|
1457
1486
|
}
|
|
1458
|
-
/**
|
|
1459
|
-
* Register a route for all HTTP methods
|
|
1460
|
-
*/
|
|
1461
1487
|
all(path, ...handlers) {
|
|
1462
1488
|
const methods = ["get", "post", "put", "delete", "patch", "options", "head"];
|
|
1463
1489
|
for (const method of methods) {
|
|
@@ -1475,45 +1501,19 @@ var Gravito = class {
|
|
|
1475
1501
|
this.compileRoutes();
|
|
1476
1502
|
return this;
|
|
1477
1503
|
}
|
|
1478
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1479
|
-
// Route Grouping
|
|
1480
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1481
|
-
/**
|
|
1482
|
-
* Mount a sub-application at a path prefix
|
|
1483
|
-
*/
|
|
1484
1504
|
route(path, app) {
|
|
1485
1505
|
this.router.mount(path, app.router);
|
|
1486
1506
|
this.compileRoutes();
|
|
1487
1507
|
return this;
|
|
1488
1508
|
}
|
|
1489
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1490
|
-
// Error Handling
|
|
1491
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1492
|
-
/**
|
|
1493
|
-
* Set custom error handler
|
|
1494
|
-
*/
|
|
1495
1509
|
onError(handler) {
|
|
1496
1510
|
this.errorHandler = handler;
|
|
1497
1511
|
return this;
|
|
1498
1512
|
}
|
|
1499
|
-
/**
|
|
1500
|
-
* Set custom 404 handler
|
|
1501
|
-
*/
|
|
1502
1513
|
notFound(handler) {
|
|
1503
1514
|
this.notFoundHandler = handler;
|
|
1504
1515
|
return this;
|
|
1505
1516
|
}
|
|
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
1517
|
async warmup(paths) {
|
|
1518
1518
|
const dummyReqOpts = { headers: { "User-Agent": "Gravito-Warmup/1.0" } };
|
|
1519
1519
|
for (const path of paths) {
|
|
@@ -1521,96 +1521,141 @@ var Gravito = class {
|
|
|
1521
1521
|
await this.fetch(req);
|
|
1522
1522
|
}
|
|
1523
1523
|
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1524
|
+
serveConfig(baseConfig = {}) {
|
|
1525
|
+
const nativeRoutes = this.router.getNativeRoutes((handler, middleware, path) => {
|
|
1526
|
+
const compiled = compileMiddlewareChain(middleware, handler);
|
|
1527
|
+
return async (req) => {
|
|
1528
|
+
const ctx = this.contextPool.acquire();
|
|
1529
|
+
ctx.init(req, {}, path, path);
|
|
1530
|
+
try {
|
|
1531
|
+
const result = compiled(ctx);
|
|
1532
|
+
let response;
|
|
1533
|
+
if (result instanceof Promise) {
|
|
1534
|
+
const peeked = bunPeek(result);
|
|
1535
|
+
response = peeked === result ? await result : peeked;
|
|
1536
|
+
} else {
|
|
1537
|
+
response = result;
|
|
1538
|
+
}
|
|
1539
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1540
|
+
if (cleanup instanceof Promise) {
|
|
1541
|
+
await cleanup;
|
|
1542
|
+
}
|
|
1543
|
+
this.contextPool.release(ctx);
|
|
1544
|
+
return response;
|
|
1545
|
+
} catch (error) {
|
|
1546
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1547
|
+
if (cleanup instanceof Promise) {
|
|
1548
|
+
await cleanup;
|
|
1549
|
+
}
|
|
1550
|
+
this.contextPool.release(ctx);
|
|
1551
|
+
return this.handleErrorSync(error, req, path);
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
});
|
|
1555
|
+
return {
|
|
1556
|
+
...baseConfig,
|
|
1557
|
+
routes: nativeRoutes,
|
|
1558
|
+
fetch: this.fetch,
|
|
1559
|
+
tls: baseConfig.tls ? this.optimizeTLS(baseConfig.tls) : undefined,
|
|
1560
|
+
error: (error) => {
|
|
1561
|
+
console.error("Native route error:", error);
|
|
1562
|
+
return new Response(CACHED_RESPONSES.INTERNAL_ERROR, {
|
|
1563
|
+
status: 500,
|
|
1564
|
+
headers: HEADERS.JSON
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
optimizeTLS(tls) {
|
|
1570
|
+
const isProd = false;
|
|
1571
|
+
const optimizeEntry = (entry) => {
|
|
1572
|
+
const optimized = { ...entry };
|
|
1573
|
+
if (typeof optimized.key === "string" && !optimized.key.startsWith("-----BEGIN")) {
|
|
1574
|
+
optimized.key = bunFile(optimized.key);
|
|
1575
|
+
}
|
|
1576
|
+
if (typeof optimized.cert === "string" && !optimized.cert.startsWith("-----BEGIN")) {
|
|
1577
|
+
optimized.cert = bunFile(optimized.cert);
|
|
1578
|
+
}
|
|
1579
|
+
if (isProd && optimized.lowMemoryMode === undefined) {
|
|
1580
|
+
optimized.lowMemoryMode = true;
|
|
1581
|
+
}
|
|
1582
|
+
return optimized;
|
|
1583
|
+
};
|
|
1584
|
+
return Array.isArray(tls) ? tls.map(optimizeEntry) : optimizeEntry(tls);
|
|
1585
|
+
}
|
|
1527
1586
|
fetch = async (request) => {
|
|
1528
1587
|
const path = extractPath(request.url);
|
|
1529
1588
|
const method = request.method.toLowerCase();
|
|
1530
1589
|
const staticKey = `${method}:${path}`;
|
|
1531
1590
|
const staticRoute = this.staticRoutes.get(staticKey);
|
|
1532
1591
|
if (staticRoute) {
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1592
|
+
const ctx = this.contextPool.acquire();
|
|
1593
|
+
ctx.init(request, {}, path, path);
|
|
1594
|
+
try {
|
|
1595
|
+
const compiled = staticRoute.compiled || compileMiddlewareChain(staticRoute.middleware, staticRoute.handler);
|
|
1596
|
+
const result = compiled(ctx);
|
|
1597
|
+
let response;
|
|
1598
|
+
if (result instanceof Promise) {
|
|
1599
|
+
const peeked = bunPeek(result);
|
|
1600
|
+
response = peeked === result ? await result : peeked;
|
|
1601
|
+
} else {
|
|
1602
|
+
response = result;
|
|
1603
|
+
}
|
|
1604
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1605
|
+
if (cleanup instanceof Promise) {
|
|
1606
|
+
await cleanup;
|
|
1607
|
+
}
|
|
1608
|
+
this.contextPool.release(ctx);
|
|
1609
|
+
return response;
|
|
1610
|
+
} catch (error) {
|
|
1611
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1612
|
+
if (cleanup instanceof Promise) {
|
|
1613
|
+
await cleanup;
|
|
1543
1614
|
}
|
|
1615
|
+
this.contextPool.release(ctx);
|
|
1616
|
+
return this.handleErrorSync(error, request, path);
|
|
1544
1617
|
}
|
|
1545
|
-
return await this.handleWithMiddleware(request, path, staticRoute);
|
|
1546
1618
|
}
|
|
1547
1619
|
return await this.handleDynamicRoute(request, method, path);
|
|
1548
1620
|
};
|
|
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 {
|
|
1621
|
+
async handleDynamicRoute(request, method, path) {
|
|
1622
|
+
const match = this.router.match(method, path);
|
|
1623
|
+
if (match.handler) {
|
|
1624
|
+
const ctx = this.contextPool.acquire();
|
|
1625
|
+
ctx.init(request, match.params, path, match.routePattern);
|
|
1567
1626
|
try {
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
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);
|
|
1627
|
+
const routeKey = `${method}:${match.routePattern}`;
|
|
1628
|
+
let compiledObj = this.compiledDynamicRoutes.get(routeKey);
|
|
1629
|
+
if (!compiledObj || compiledObj.version !== this.router.version) {
|
|
1630
|
+
const compiled = compileMiddlewareChain(match.middleware, match.handler);
|
|
1631
|
+
compiledObj = { compiled, version: this.router.version };
|
|
1632
|
+
this.compiledDynamicRoutes.set(routeKey, compiledObj);
|
|
1633
|
+
}
|
|
1634
|
+
const result = compiledObj.compiled(ctx);
|
|
1635
|
+
let response;
|
|
1636
|
+
if (result instanceof Promise) {
|
|
1637
|
+
const peeked = bunPeek(result);
|
|
1638
|
+
response = peeked === result ? await result : peeked;
|
|
1639
|
+
} else {
|
|
1640
|
+
response = result;
|
|
1641
|
+
}
|
|
1642
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1643
|
+
if (cleanup instanceof Promise) {
|
|
1644
|
+
await cleanup;
|
|
1645
|
+
}
|
|
1646
|
+
this.contextPool.release(ctx);
|
|
1647
|
+
return response;
|
|
1598
1648
|
} catch (error) {
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
await ctx.requestScope().cleanup();
|
|
1603
|
-
} catch (cleanupError) {
|
|
1604
|
-
console.error("RequestScope cleanup failed:", cleanupError);
|
|
1649
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1650
|
+
if (cleanup instanceof Promise) {
|
|
1651
|
+
await cleanup;
|
|
1605
1652
|
}
|
|
1606
1653
|
this.contextPool.release(ctx);
|
|
1654
|
+
return this.handleErrorSync(error, request, path);
|
|
1607
1655
|
}
|
|
1608
|
-
}
|
|
1609
|
-
return
|
|
1656
|
+
}
|
|
1657
|
+
return this.handleNotFoundSync(request, path);
|
|
1610
1658
|
}
|
|
1611
|
-
/**
|
|
1612
|
-
* Sync error handler (for ultra-fast path)
|
|
1613
|
-
*/
|
|
1614
1659
|
handleErrorSync(error, request, path) {
|
|
1615
1660
|
if (this.errorHandler) {
|
|
1616
1661
|
const ctx = new MinimalContext(request, {}, path);
|
|
@@ -1626,9 +1671,6 @@ var Gravito = class {
|
|
|
1626
1671
|
headers: HEADERS.JSON
|
|
1627
1672
|
});
|
|
1628
1673
|
}
|
|
1629
|
-
/**
|
|
1630
|
-
* Sync 404 handler (for ultra-fast path)
|
|
1631
|
-
*/
|
|
1632
1674
|
handleNotFoundSync(request, path) {
|
|
1633
1675
|
if (this.notFoundHandler) {
|
|
1634
1676
|
const ctx = new MinimalContext(request, {}, path);
|
|
@@ -1643,18 +1685,12 @@ var Gravito = class {
|
|
|
1643
1685
|
headers: HEADERS.JSON
|
|
1644
1686
|
});
|
|
1645
1687
|
}
|
|
1646
|
-
/**
|
|
1647
|
-
* Collect middleware for a specific path
|
|
1648
|
-
*/
|
|
1649
1688
|
collectMiddlewareForPath(path, routeMiddleware) {
|
|
1650
1689
|
if (this.router.globalMiddleware.length === 0 && this.router.pathMiddleware.size === 0) {
|
|
1651
1690
|
return routeMiddleware;
|
|
1652
1691
|
}
|
|
1653
1692
|
return this.router.collectMiddlewarePublic(path, routeMiddleware);
|
|
1654
1693
|
}
|
|
1655
|
-
/**
|
|
1656
|
-
* Compile routes for optimization
|
|
1657
|
-
*/
|
|
1658
1694
|
compileRoutes() {
|
|
1659
1695
|
this.staticRoutes = this.router.staticRoutes;
|
|
1660
1696
|
const hasGlobalMiddleware = this.router.globalMiddleware.length > 0;
|
|
@@ -1674,9 +1710,6 @@ var Gravito = class {
|
|
|
1674
1710
|
route.compiledVersion = this.router.version;
|
|
1675
1711
|
}
|
|
1676
1712
|
}
|
|
1677
|
-
/**
|
|
1678
|
-
* Add a route to the router
|
|
1679
|
-
*/
|
|
1680
1713
|
addRoute(method, path, handlers) {
|
|
1681
1714
|
if (handlers.length === 0) {
|
|
1682
1715
|
throw new Error(`No handler provided for ${method.toUpperCase()} ${path}`);
|
|
@@ -1687,46 +1720,14 @@ var Gravito = class {
|
|
|
1687
1720
|
this.compileRoutes();
|
|
1688
1721
|
return this;
|
|
1689
1722
|
}
|
|
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
|
-
};
|
|
1723
|
+
}
|
|
1725
1724
|
export {
|
|
1726
|
-
|
|
1727
|
-
FastContext as FastContextImpl,
|
|
1728
|
-
Gravito,
|
|
1729
|
-
MinimalContext,
|
|
1725
|
+
extractPath,
|
|
1730
1726
|
ObjectPool,
|
|
1731
|
-
|
|
1727
|
+
MinimalContext,
|
|
1728
|
+
Gravito,
|
|
1729
|
+
FastContext as FastContextImpl,
|
|
1730
|
+
AOTRouter
|
|
1732
1731
|
};
|
|
1732
|
+
|
|
1733
|
+
//# debugId=1A9EA2323316056B64756E2164756E21
|