@gravito/core 1.6.0 → 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 +758 -532
- package/dist/engine/index.cjs.map +22 -0
- package/dist/engine/index.d.ts +14 -690
- package/dist/engine/index.js +758 -508
- 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-C4Src6NN.d.cts → http/types.d.ts} +48 -16
- package/dist/index.browser.d.ts +34 -0
- package/dist/index.cjs +12909 -9492
- package/dist/index.cjs.map +168 -0
- package/dist/index.d.ts +58 -8707
- package/dist/index.js +12906 -9381
- 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 +23 -53
- package/src/ffi/native/cbor.c +1148 -0
- package/dist/compat-C4Src6NN.d.ts +0 -357
- package/dist/compat.d.cts +0 -1
- package/dist/engine/index.d.cts +0 -702
- package/dist/index.d.cts +0 -8734
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,42 +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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
version = 0;
|
|
230
|
-
/**
|
|
231
|
-
* Register a route
|
|
232
|
-
*
|
|
233
|
-
* Automatically determines if route is static or dynamic.
|
|
234
|
-
* Static routes are stored in a Map for O(1) lookup.
|
|
235
|
-
* Dynamic routes use the Radix Tree.
|
|
236
|
-
*
|
|
237
|
-
* @param method - HTTP method
|
|
238
|
-
* @param path - Route path
|
|
239
|
-
* @param handler - Route handler
|
|
240
|
-
* @param middleware - Route-specific middleware
|
|
241
|
-
*/
|
|
254
|
+
pathMiddleware = new Map;
|
|
255
|
+
dynamicRoutePatterns = new Map;
|
|
256
|
+
middlewareCache = new Map;
|
|
257
|
+
cacheMaxSize = 1000;
|
|
258
|
+
_version = 0;
|
|
259
|
+
get version() {
|
|
260
|
+
return this._version;
|
|
261
|
+
}
|
|
242
262
|
add(method, path, handler, middleware = []) {
|
|
243
263
|
this.routeDefinitions.push({ method, path, handler, middleware });
|
|
244
264
|
const normalizedMethod = method.toLowerCase();
|
|
@@ -254,9 +274,6 @@ var AOTRouter = class {
|
|
|
254
274
|
}
|
|
255
275
|
}
|
|
256
276
|
}
|
|
257
|
-
/**
|
|
258
|
-
* Mount another router at a prefix
|
|
259
|
-
*/
|
|
260
277
|
mount(prefix, other) {
|
|
261
278
|
if (other.globalMiddleware.length > 0) {
|
|
262
279
|
this.usePattern(prefix, ...other.globalMiddleware);
|
|
@@ -264,7 +281,8 @@ var AOTRouter = class {
|
|
|
264
281
|
this.usePattern(wildcard, ...other.globalMiddleware);
|
|
265
282
|
}
|
|
266
283
|
for (const [pattern, mws] of other.pathMiddleware) {
|
|
267
|
-
|
|
284
|
+
const hasMethodPrefix = /^(get|post|put|delete|patch|options|head):/.test(pattern);
|
|
285
|
+
if (hasMethodPrefix) {
|
|
268
286
|
continue;
|
|
269
287
|
}
|
|
270
288
|
let newPattern;
|
|
@@ -289,25 +307,10 @@ var AOTRouter = class {
|
|
|
289
307
|
this.add(def.method, newPath, def.handler, def.middleware);
|
|
290
308
|
}
|
|
291
309
|
}
|
|
292
|
-
/**
|
|
293
|
-
* Add global middleware
|
|
294
|
-
*
|
|
295
|
-
* These run for every request, before route-specific middleware.
|
|
296
|
-
*
|
|
297
|
-
* @param middleware - Middleware functions
|
|
298
|
-
*/
|
|
299
310
|
use(...middleware) {
|
|
300
311
|
this.globalMiddleware.push(...middleware);
|
|
301
|
-
this.
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Add path-based middleware
|
|
305
|
-
*
|
|
306
|
-
* Supports wildcard patterns like '/api/*'
|
|
307
|
-
*
|
|
308
|
-
* @param pattern - Path pattern
|
|
309
|
-
* @param middleware - Middleware functions
|
|
310
|
-
*/
|
|
312
|
+
this._version++;
|
|
313
|
+
}
|
|
311
314
|
usePattern(pattern, ...middleware) {
|
|
312
315
|
if (pattern === "*") {
|
|
313
316
|
this.globalMiddleware.push(...middleware);
|
|
@@ -315,17 +318,8 @@ var AOTRouter = class {
|
|
|
315
318
|
const existing = this.pathMiddleware.get(pattern) ?? [];
|
|
316
319
|
this.pathMiddleware.set(pattern, [...existing, ...middleware]);
|
|
317
320
|
}
|
|
318
|
-
this.
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Match a request to a route
|
|
322
|
-
*
|
|
323
|
-
* Returns the handler, params, and all applicable middleware.
|
|
324
|
-
*
|
|
325
|
-
* @param method - HTTP method
|
|
326
|
-
* @param path - Request path
|
|
327
|
-
* @returns Route match or null if not found
|
|
328
|
-
*/
|
|
321
|
+
this._version++;
|
|
322
|
+
}
|
|
329
323
|
match(method, path) {
|
|
330
324
|
const normalizedMethod = method.toLowerCase();
|
|
331
325
|
const staticKey = `${normalizedMethod}:${path}`;
|
|
@@ -358,28 +352,16 @@ var AOTRouter = class {
|
|
|
358
352
|
middleware: []
|
|
359
353
|
};
|
|
360
354
|
}
|
|
361
|
-
/**
|
|
362
|
-
* Public wrapper for collectMiddleware (used by Gravito for optimization)
|
|
363
|
-
*/
|
|
364
355
|
collectMiddlewarePublic(path, routeMiddleware) {
|
|
365
356
|
return this.collectMiddleware(path, routeMiddleware);
|
|
366
357
|
}
|
|
367
|
-
/**
|
|
368
|
-
* Collect all applicable middleware for a path
|
|
369
|
-
*
|
|
370
|
-
* Order: global -> pattern-based -> route-specific
|
|
371
|
-
*
|
|
372
|
-
* @param path - Request path
|
|
373
|
-
* @param routeMiddleware - Route-specific middleware
|
|
374
|
-
* @returns Combined middleware array
|
|
375
|
-
*/
|
|
376
358
|
collectMiddleware(path, routeMiddleware) {
|
|
377
359
|
if (this.globalMiddleware.length === 0 && this.pathMiddleware.size === 0 && routeMiddleware.length === 0) {
|
|
378
360
|
return [];
|
|
379
361
|
}
|
|
380
|
-
const cacheKey =
|
|
362
|
+
const cacheKey = path;
|
|
381
363
|
const cached = this.middlewareCache.get(cacheKey);
|
|
382
|
-
if (cached !==
|
|
364
|
+
if (cached !== undefined && cached.version === this._version) {
|
|
383
365
|
return cached.data;
|
|
384
366
|
}
|
|
385
367
|
const middleware = [];
|
|
@@ -400,27 +382,27 @@ var AOTRouter = class {
|
|
|
400
382
|
middleware.push(...routeMiddleware);
|
|
401
383
|
}
|
|
402
384
|
if (this.middlewareCache.size < this.cacheMaxSize) {
|
|
403
|
-
this.middlewareCache.set(cacheKey, { data: middleware, version: this.
|
|
385
|
+
this.middlewareCache.set(cacheKey, { data: middleware, version: this._version });
|
|
386
|
+
} else if (this.middlewareCache.has(cacheKey)) {
|
|
387
|
+
this.middlewareCache.set(cacheKey, { data: middleware, version: this._version });
|
|
404
388
|
}
|
|
405
389
|
return middleware;
|
|
406
390
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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
|
+
}
|
|
410
403
|
isStaticPath(path) {
|
|
411
404
|
return !path.includes(":") && !path.includes("*");
|
|
412
405
|
}
|
|
413
|
-
/**
|
|
414
|
-
* Match a pattern against a path
|
|
415
|
-
*
|
|
416
|
-
* Supports:
|
|
417
|
-
* - Exact match: '/api/users'
|
|
418
|
-
* - Wildcard suffix: '/api/*'
|
|
419
|
-
*
|
|
420
|
-
* @param pattern - Pattern to match
|
|
421
|
-
* @param path - Path to test
|
|
422
|
-
* @returns True if pattern matches
|
|
423
|
-
*/
|
|
424
406
|
matchPattern(pattern, path) {
|
|
425
407
|
if (pattern === "*") {
|
|
426
408
|
return true;
|
|
@@ -434,9 +416,6 @@ var AOTRouter = class {
|
|
|
434
416
|
}
|
|
435
417
|
return false;
|
|
436
418
|
}
|
|
437
|
-
/**
|
|
438
|
-
* Get all registered routes (for debugging)
|
|
439
|
-
*/
|
|
440
419
|
getRoutes() {
|
|
441
420
|
const routes = [];
|
|
442
421
|
for (const key of this.staticRoutes.keys()) {
|
|
@@ -445,11 +424,107 @@ var AOTRouter = class {
|
|
|
445
424
|
}
|
|
446
425
|
return routes;
|
|
447
426
|
}
|
|
448
|
-
}
|
|
427
|
+
}
|
|
449
428
|
|
|
450
|
-
// src/
|
|
451
|
-
|
|
452
|
-
|
|
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) {
|
|
453
528
|
return {
|
|
454
529
|
usesHeaders: source.includes(".header(") || source.includes(".header)") || source.includes(".headers(") || source.includes(".headers)"),
|
|
455
530
|
usesQuery: source.includes(".query(") || source.includes(".query)") || source.includes(".queries(") || source.includes(".queries)"),
|
|
@@ -458,6 +533,12 @@ function analyzeHandler(handler) {
|
|
|
458
533
|
isAsync: source.includes("async") || source.includes("await")
|
|
459
534
|
};
|
|
460
535
|
}
|
|
536
|
+
|
|
537
|
+
// src/engine/analyzer.ts
|
|
538
|
+
function analyzeHandler(handler) {
|
|
539
|
+
const source = handler.toString();
|
|
540
|
+
return analyzeHandlerWithTranspiler(source);
|
|
541
|
+
}
|
|
461
542
|
function getOptimalContextType(analysis) {
|
|
462
543
|
if (analysis.usesHeaders) {
|
|
463
544
|
return "fast";
|
|
@@ -475,7 +556,7 @@ function getOptimalContextType(analysis) {
|
|
|
475
556
|
}
|
|
476
557
|
|
|
477
558
|
// src/engine/constants.ts
|
|
478
|
-
var encoder = new TextEncoder
|
|
559
|
+
var encoder = new TextEncoder;
|
|
479
560
|
var CACHED_RESPONSES = {
|
|
480
561
|
NOT_FOUND: encoder.encode('{"error":"Not Found"}'),
|
|
481
562
|
INTERNAL_ERROR: encoder.encode('{"error":"Internal Server Error"}'),
|
|
@@ -488,8 +569,154 @@ var HEADERS = {
|
|
|
488
569
|
HTML: { "Content-Type": "text/html; charset=utf-8" }
|
|
489
570
|
};
|
|
490
571
|
|
|
572
|
+
// src/Container/RequestScopeMetrics.ts
|
|
573
|
+
class RequestScopeMetrics {
|
|
574
|
+
cleanupStartTime = null;
|
|
575
|
+
cleanupDuration = null;
|
|
576
|
+
scopeSize = 0;
|
|
577
|
+
servicesCleaned = 0;
|
|
578
|
+
errorsOccurred = 0;
|
|
579
|
+
recordCleanupStart() {
|
|
580
|
+
this.cleanupStartTime = performance.now();
|
|
581
|
+
}
|
|
582
|
+
recordCleanupEnd(scopeSize, servicesCleaned, errorsOccurred = 0) {
|
|
583
|
+
if (this.cleanupStartTime !== null) {
|
|
584
|
+
this.cleanupDuration = performance.now() - this.cleanupStartTime;
|
|
585
|
+
this.cleanupStartTime = null;
|
|
586
|
+
}
|
|
587
|
+
this.scopeSize = scopeSize;
|
|
588
|
+
this.servicesCleaned = servicesCleaned;
|
|
589
|
+
this.errorsOccurred = errorsOccurred;
|
|
590
|
+
}
|
|
591
|
+
getCleanupDuration() {
|
|
592
|
+
return this.cleanupDuration;
|
|
593
|
+
}
|
|
594
|
+
isSlowCleanup(thresholdMs = 2) {
|
|
595
|
+
if (this.cleanupDuration === null) {
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
return this.cleanupDuration > thresholdMs;
|
|
599
|
+
}
|
|
600
|
+
toJSON() {
|
|
601
|
+
return {
|
|
602
|
+
cleanupDuration: this.cleanupDuration,
|
|
603
|
+
scopeSize: this.scopeSize,
|
|
604
|
+
servicesCleaned: this.servicesCleaned,
|
|
605
|
+
errorsOccurred: this.errorsOccurred,
|
|
606
|
+
hasErrors: this.errorsOccurred > 0,
|
|
607
|
+
isSlowCleanup: this.isSlowCleanup()
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
toString() {
|
|
611
|
+
const duration = this.cleanupDuration ?? "pending";
|
|
612
|
+
return `cleanup: ${duration}ms, ` + `scope: ${this.scopeSize}, ` + `cleaned: ${this.servicesCleaned}, ` + `errors: ${this.errorsOccurred}`;
|
|
613
|
+
}
|
|
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
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/Container/RequestScopeManager.ts
|
|
656
|
+
class RequestScopeManager {
|
|
657
|
+
scoped = new Map;
|
|
658
|
+
metadata = new Map;
|
|
659
|
+
metrics = new RequestScopeMetrics;
|
|
660
|
+
observer = null;
|
|
661
|
+
constructor(observer) {
|
|
662
|
+
this.observer = observer || null;
|
|
663
|
+
}
|
|
664
|
+
setObserver(observer) {
|
|
665
|
+
this.observer = observer;
|
|
666
|
+
}
|
|
667
|
+
getMetrics() {
|
|
668
|
+
return this.metrics;
|
|
669
|
+
}
|
|
670
|
+
resolve(key, factory) {
|
|
671
|
+
const keyStr = String(key);
|
|
672
|
+
const isFromCache = this.scoped.has(keyStr);
|
|
673
|
+
if (!isFromCache) {
|
|
674
|
+
const instance = factory();
|
|
675
|
+
this.scoped.set(keyStr, instance);
|
|
676
|
+
if (instance && typeof instance === "object" && "cleanup" in instance) {
|
|
677
|
+
this.metadata.set(keyStr, { hasCleanup: true });
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
this.observer?.onServiceResolved?.(key, isFromCache);
|
|
681
|
+
return this.scoped.get(keyStr);
|
|
682
|
+
}
|
|
683
|
+
async cleanup() {
|
|
684
|
+
this.metrics.recordCleanupStart();
|
|
685
|
+
this.observer?.onCleanupStart?.();
|
|
686
|
+
const errors = [];
|
|
687
|
+
let servicesCleaned = 0;
|
|
688
|
+
for (const [, instance] of this.scoped) {
|
|
689
|
+
if (instance && typeof instance === "object" && "cleanup" in instance) {
|
|
690
|
+
const fn = instance.cleanup;
|
|
691
|
+
if (typeof fn === "function") {
|
|
692
|
+
try {
|
|
693
|
+
await fn.call(instance);
|
|
694
|
+
servicesCleaned++;
|
|
695
|
+
} catch (error) {
|
|
696
|
+
errors.push(error);
|
|
697
|
+
this.observer?.onCleanupError?.(error instanceof Error ? error : new Error(String(error)));
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
const scopeSize = this.scoped.size;
|
|
703
|
+
this.scoped.clear();
|
|
704
|
+
this.metadata.clear();
|
|
705
|
+
this.metrics.recordCleanupEnd(scopeSize, servicesCleaned, errors.length);
|
|
706
|
+
this.observer?.onCleanupEnd?.(this.metrics);
|
|
707
|
+
if (errors.length > 0) {
|
|
708
|
+
console.error("RequestScope cleanup errors:", errors);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
size() {
|
|
712
|
+
return this.scoped.size;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
491
716
|
// src/engine/FastContext.ts
|
|
492
|
-
var
|
|
717
|
+
var bunEscapeHTML = globalThis.Bun.escapeHTML;
|
|
718
|
+
|
|
719
|
+
class FastRequestImpl {
|
|
493
720
|
_request;
|
|
494
721
|
_params;
|
|
495
722
|
_path;
|
|
@@ -497,16 +724,18 @@ var FastRequestImpl = class {
|
|
|
497
724
|
_url = null;
|
|
498
725
|
_query = null;
|
|
499
726
|
_headers = null;
|
|
500
|
-
_cachedJson =
|
|
727
|
+
_cachedJson = undefined;
|
|
501
728
|
_jsonParsed = false;
|
|
502
|
-
|
|
729
|
+
_cachedText = undefined;
|
|
730
|
+
_textParsed = false;
|
|
731
|
+
_cachedFormData = undefined;
|
|
732
|
+
_formDataParsed = false;
|
|
733
|
+
_cachedQueries = null;
|
|
734
|
+
_cachedCookies = null;
|
|
503
735
|
_ctx;
|
|
504
736
|
constructor(ctx) {
|
|
505
737
|
this._ctx = ctx;
|
|
506
738
|
}
|
|
507
|
-
/**
|
|
508
|
-
* Initialize for new request
|
|
509
|
-
*/
|
|
510
739
|
init(request, params = {}, path = "", routePattern) {
|
|
511
740
|
this._request = request;
|
|
512
741
|
this._params = params;
|
|
@@ -515,27 +744,34 @@ var FastRequestImpl = class {
|
|
|
515
744
|
this._url = null;
|
|
516
745
|
this._query = null;
|
|
517
746
|
this._headers = null;
|
|
518
|
-
this._cachedJson =
|
|
747
|
+
this._cachedJson = undefined;
|
|
519
748
|
this._jsonParsed = false;
|
|
749
|
+
this._cachedText = undefined;
|
|
750
|
+
this._textParsed = false;
|
|
751
|
+
this._cachedFormData = undefined;
|
|
752
|
+
this._formDataParsed = false;
|
|
753
|
+
this._cachedQueries = null;
|
|
754
|
+
this._cachedCookies = null;
|
|
520
755
|
return this;
|
|
521
756
|
}
|
|
522
|
-
/**
|
|
523
|
-
* Reset for pooling
|
|
524
|
-
*/
|
|
525
757
|
reset() {
|
|
526
|
-
this._request =
|
|
527
|
-
this._params =
|
|
758
|
+
this._request = undefined;
|
|
759
|
+
this._params = undefined;
|
|
528
760
|
this._url = null;
|
|
529
761
|
this._query = null;
|
|
530
762
|
this._headers = null;
|
|
531
|
-
this._cachedJson =
|
|
763
|
+
this._cachedJson = undefined;
|
|
532
764
|
this._jsonParsed = false;
|
|
765
|
+
this._cachedText = undefined;
|
|
766
|
+
this._textParsed = false;
|
|
767
|
+
this._cachedFormData = undefined;
|
|
768
|
+
this._formDataParsed = false;
|
|
769
|
+
this._cachedQueries = null;
|
|
770
|
+
this._cachedCookies = null;
|
|
533
771
|
}
|
|
534
772
|
checkReleased() {
|
|
535
773
|
if (this._ctx._isReleased) {
|
|
536
|
-
throw new Error(
|
|
537
|
-
"FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)"
|
|
538
|
-
);
|
|
774
|
+
throw new Error("FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)");
|
|
539
775
|
}
|
|
540
776
|
}
|
|
541
777
|
get url() {
|
|
@@ -573,17 +809,20 @@ var FastRequestImpl = class {
|
|
|
573
809
|
if (!this._query) {
|
|
574
810
|
this._query = this.getUrl().searchParams;
|
|
575
811
|
}
|
|
576
|
-
return this._query.get(name) ??
|
|
812
|
+
return this._query.get(name) ?? undefined;
|
|
577
813
|
}
|
|
578
814
|
queries() {
|
|
579
815
|
this.checkReleased();
|
|
816
|
+
if (this._cachedQueries !== null) {
|
|
817
|
+
return this._cachedQueries;
|
|
818
|
+
}
|
|
580
819
|
if (!this._query) {
|
|
581
820
|
this._query = this.getUrl().searchParams;
|
|
582
821
|
}
|
|
583
822
|
const result = {};
|
|
584
823
|
for (const [key, value] of this._query.entries()) {
|
|
585
824
|
const existing = result[key];
|
|
586
|
-
if (existing ===
|
|
825
|
+
if (existing === undefined) {
|
|
587
826
|
result[key] = value;
|
|
588
827
|
} else if (Array.isArray(existing)) {
|
|
589
828
|
existing.push(value);
|
|
@@ -591,11 +830,12 @@ var FastRequestImpl = class {
|
|
|
591
830
|
result[key] = [existing, value];
|
|
592
831
|
}
|
|
593
832
|
}
|
|
833
|
+
this._cachedQueries = result;
|
|
594
834
|
return result;
|
|
595
835
|
}
|
|
596
836
|
header(name) {
|
|
597
837
|
this.checkReleased();
|
|
598
|
-
return this._request.headers.get(name) ??
|
|
838
|
+
return this._request.headers.get(name) ?? undefined;
|
|
599
839
|
}
|
|
600
840
|
headers() {
|
|
601
841
|
this.checkReleased();
|
|
@@ -607,6 +847,39 @@ var FastRequestImpl = class {
|
|
|
607
847
|
}
|
|
608
848
|
return { ...this._headers };
|
|
609
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
|
+
}
|
|
610
883
|
async json() {
|
|
611
884
|
this.checkReleased();
|
|
612
885
|
if (!this._jsonParsed) {
|
|
@@ -617,89 +890,75 @@ var FastRequestImpl = class {
|
|
|
617
890
|
}
|
|
618
891
|
async text() {
|
|
619
892
|
this.checkReleased();
|
|
620
|
-
|
|
893
|
+
if (!this._textParsed) {
|
|
894
|
+
this._cachedText = await this._request.text();
|
|
895
|
+
this._textParsed = true;
|
|
896
|
+
}
|
|
897
|
+
return this._cachedText;
|
|
621
898
|
}
|
|
622
899
|
async formData() {
|
|
623
900
|
this.checkReleased();
|
|
624
|
-
|
|
901
|
+
if (!this._formDataParsed) {
|
|
902
|
+
this._cachedFormData = await this._request.formData();
|
|
903
|
+
this._formDataParsed = true;
|
|
904
|
+
}
|
|
905
|
+
return this._cachedFormData;
|
|
625
906
|
}
|
|
626
907
|
get raw() {
|
|
627
908
|
this.checkReleased();
|
|
628
909
|
return this._request;
|
|
629
910
|
}
|
|
630
|
-
}
|
|
631
|
-
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
class FastContext {
|
|
632
914
|
req = new FastRequestImpl(this);
|
|
633
|
-
|
|
634
|
-
_headers = new Headers();
|
|
635
|
-
// Reuse this object
|
|
915
|
+
_headers = new Headers;
|
|
636
916
|
_isReleased = false;
|
|
637
|
-
|
|
638
|
-
/**
|
|
639
|
-
* Initialize context for a new request
|
|
640
|
-
*
|
|
641
|
-
* This is called when acquiring from the pool.
|
|
642
|
-
*/
|
|
917
|
+
_requestScope = null;
|
|
643
918
|
init(request, params = {}, path = "", routePattern) {
|
|
644
919
|
this._isReleased = false;
|
|
645
920
|
this.req.init(request, params, path, routePattern);
|
|
646
|
-
this._headers = new Headers
|
|
921
|
+
this._headers = new Headers;
|
|
922
|
+
this._requestScope = new RequestScopeManager;
|
|
647
923
|
return this;
|
|
648
924
|
}
|
|
649
|
-
/**
|
|
650
|
-
* Reset context for pooling (Cleanup)
|
|
651
|
-
*
|
|
652
|
-
* This is called when releasing back to the pool.
|
|
653
|
-
* Implements "Deep-Reset Protocol" and "Release Guard".
|
|
654
|
-
*/
|
|
655
925
|
reset() {
|
|
656
926
|
this._isReleased = true;
|
|
657
927
|
this.req.reset();
|
|
658
928
|
this._store.clear();
|
|
929
|
+
this._requestScope = null;
|
|
659
930
|
}
|
|
660
|
-
/**
|
|
661
|
-
* Check if context is released
|
|
662
|
-
*/
|
|
663
931
|
checkReleased() {
|
|
664
932
|
if (this._isReleased) {
|
|
665
|
-
throw new Error(
|
|
666
|
-
"FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)"
|
|
667
|
-
);
|
|
933
|
+
throw new Error("FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)");
|
|
668
934
|
}
|
|
669
935
|
}
|
|
670
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
671
|
-
// Response Helpers
|
|
672
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
673
936
|
json(data, status = 200) {
|
|
674
937
|
this.checkReleased();
|
|
675
|
-
this._headers
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
headers: this._headers
|
|
679
|
-
});
|
|
938
|
+
const headers = new Headers(this._headers);
|
|
939
|
+
headers.set("Content-Type", "application/json; charset=utf-8");
|
|
940
|
+
return Response.json(data, { status, headers });
|
|
680
941
|
}
|
|
681
942
|
text(text, status = 200) {
|
|
682
943
|
this.checkReleased();
|
|
683
|
-
this._headers
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
headers: this._headers
|
|
687
|
-
});
|
|
944
|
+
const headers = new Headers(this._headers);
|
|
945
|
+
headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
946
|
+
return new Response(text, { status, headers });
|
|
688
947
|
}
|
|
689
948
|
html(html, status = 200) {
|
|
690
949
|
this.checkReleased();
|
|
691
|
-
this._headers
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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);
|
|
696
956
|
}
|
|
697
957
|
redirect(url, status = 302) {
|
|
698
958
|
this.checkReleased();
|
|
699
|
-
this._headers.set("Location", url);
|
|
700
959
|
return new Response(null, {
|
|
701
960
|
status,
|
|
702
|
-
headers:
|
|
961
|
+
headers: { Location: url }
|
|
703
962
|
});
|
|
704
963
|
}
|
|
705
964
|
body(data, status = 200) {
|
|
@@ -709,12 +968,19 @@ var FastContext = class {
|
|
|
709
968
|
headers: this._headers
|
|
710
969
|
});
|
|
711
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
|
+
}
|
|
712
979
|
stream(stream, status = 200) {
|
|
713
980
|
this.checkReleased();
|
|
714
|
-
this._headers.set("Content-Type", "application/octet-stream");
|
|
715
981
|
return new Response(stream, {
|
|
716
982
|
status,
|
|
717
|
-
headers:
|
|
983
|
+
headers: { "Content-Type": "application/octet-stream" }
|
|
718
984
|
});
|
|
719
985
|
}
|
|
720
986
|
notFound(message = "Not Found") {
|
|
@@ -732,9 +998,7 @@ var FastContext = class {
|
|
|
732
998
|
async forward(target, _options = {}) {
|
|
733
999
|
this.checkReleased();
|
|
734
1000
|
const url = new URL(this.req.url);
|
|
735
|
-
const targetUrl = new URL(
|
|
736
|
-
target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`
|
|
737
|
-
);
|
|
1001
|
+
const targetUrl = new URL(target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`);
|
|
738
1002
|
const searchParams = new URLSearchParams(url.search);
|
|
739
1003
|
searchParams.forEach((v, k) => {
|
|
740
1004
|
targetUrl.searchParams.set(k, v);
|
|
@@ -743,13 +1007,12 @@ var FastContext = class {
|
|
|
743
1007
|
method: this.req.method,
|
|
744
1008
|
headers: this.req.raw.headers,
|
|
745
1009
|
body: this.req.method !== "GET" && this.req.method !== "HEAD" ? this.req.raw.body : null,
|
|
746
|
-
// @ts-expect-error - Bun/Fetch specific
|
|
747
1010
|
duplex: "half"
|
|
748
1011
|
});
|
|
749
1012
|
}
|
|
750
1013
|
header(name, value) {
|
|
751
1014
|
this.checkReleased();
|
|
752
|
-
if (value !==
|
|
1015
|
+
if (value !== undefined) {
|
|
753
1016
|
this._headers.set(name, value);
|
|
754
1017
|
return;
|
|
755
1018
|
}
|
|
@@ -758,34 +1021,47 @@ var FastContext = class {
|
|
|
758
1021
|
status(_code) {
|
|
759
1022
|
this.checkReleased();
|
|
760
1023
|
}
|
|
761
|
-
|
|
762
|
-
// Context Variables
|
|
763
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
764
|
-
_store = /* @__PURE__ */ new Map();
|
|
1024
|
+
_store = new Map;
|
|
765
1025
|
get(key) {
|
|
766
1026
|
return this._store.get(key);
|
|
767
1027
|
}
|
|
768
1028
|
set(key, value) {
|
|
769
1029
|
this._store.set(key, value);
|
|
770
1030
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1031
|
+
requestScope() {
|
|
1032
|
+
if (!this._requestScope) {
|
|
1033
|
+
throw new Error("RequestScope not initialized. Call init() first.");
|
|
1034
|
+
}
|
|
1035
|
+
return this._requestScope;
|
|
1036
|
+
}
|
|
1037
|
+
scoped(key, factory) {
|
|
1038
|
+
return this.requestScope().resolve(key, factory);
|
|
1039
|
+
}
|
|
774
1040
|
route = () => "";
|
|
775
1041
|
get native() {
|
|
776
1042
|
return this;
|
|
777
1043
|
}
|
|
778
|
-
}
|
|
1044
|
+
}
|
|
779
1045
|
|
|
780
1046
|
// src/engine/MinimalContext.ts
|
|
781
|
-
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;
|
|
782
1059
|
constructor(_request, _params, _path, _routePattern) {
|
|
783
1060
|
this._request = _request;
|
|
784
1061
|
this._params = _params;
|
|
785
1062
|
this._path = _path;
|
|
786
1063
|
this._routePattern = _routePattern;
|
|
787
1064
|
}
|
|
788
|
-
_searchParams = null;
|
|
789
1065
|
get url() {
|
|
790
1066
|
return this._request.url;
|
|
791
1067
|
}
|
|
@@ -804,15 +1080,12 @@ var MinimalRequest = class {
|
|
|
804
1080
|
params() {
|
|
805
1081
|
return { ...this._params };
|
|
806
1082
|
}
|
|
807
|
-
/**
|
|
808
|
-
* Lazy-initialize searchParams, only parse once
|
|
809
|
-
*/
|
|
810
1083
|
getSearchParams() {
|
|
811
1084
|
if (this._searchParams === null) {
|
|
812
1085
|
const url = this._request.url;
|
|
813
1086
|
const queryStart = url.indexOf("?");
|
|
814
1087
|
if (queryStart === -1) {
|
|
815
|
-
this._searchParams = new URLSearchParams
|
|
1088
|
+
this._searchParams = new URLSearchParams;
|
|
816
1089
|
} else {
|
|
817
1090
|
const hashStart = url.indexOf("#", queryStart);
|
|
818
1091
|
const queryString = hashStart === -1 ? url.slice(queryStart + 1) : url.slice(queryStart + 1, hashStart);
|
|
@@ -822,14 +1095,17 @@ var MinimalRequest = class {
|
|
|
822
1095
|
return this._searchParams;
|
|
823
1096
|
}
|
|
824
1097
|
query(name) {
|
|
825
|
-
return this.getSearchParams().get(name) ??
|
|
1098
|
+
return this.getSearchParams().get(name) ?? undefined;
|
|
826
1099
|
}
|
|
827
1100
|
queries() {
|
|
1101
|
+
if (this._cachedQueries !== null) {
|
|
1102
|
+
return this._cachedQueries;
|
|
1103
|
+
}
|
|
828
1104
|
const params = this.getSearchParams();
|
|
829
1105
|
const result = {};
|
|
830
1106
|
for (const [key, value] of params.entries()) {
|
|
831
1107
|
const existing = result[key];
|
|
832
|
-
if (existing ===
|
|
1108
|
+
if (existing === undefined) {
|
|
833
1109
|
result[key] = value;
|
|
834
1110
|
} else if (Array.isArray(existing)) {
|
|
835
1111
|
existing.push(value);
|
|
@@ -837,10 +1113,11 @@ var MinimalRequest = class {
|
|
|
837
1113
|
result[key] = [existing, value];
|
|
838
1114
|
}
|
|
839
1115
|
}
|
|
1116
|
+
this._cachedQueries = result;
|
|
840
1117
|
return result;
|
|
841
1118
|
}
|
|
842
1119
|
header(name) {
|
|
843
|
-
return this._request.headers.get(name) ??
|
|
1120
|
+
return this._request.headers.get(name) ?? undefined;
|
|
844
1121
|
}
|
|
845
1122
|
headers() {
|
|
846
1123
|
const result = {};
|
|
@@ -850,33 +1127,65 @@ var MinimalRequest = class {
|
|
|
850
1127
|
return result;
|
|
851
1128
|
}
|
|
852
1129
|
async json() {
|
|
853
|
-
|
|
1130
|
+
if (this._cachedJsonPromise === null) {
|
|
1131
|
+
this._cachedJsonPromise = this._request.json();
|
|
1132
|
+
}
|
|
1133
|
+
return this._cachedJsonPromise;
|
|
854
1134
|
}
|
|
855
1135
|
async text() {
|
|
856
|
-
|
|
1136
|
+
if (this._cachedTextPromise === null) {
|
|
1137
|
+
this._cachedTextPromise = this._request.text();
|
|
1138
|
+
}
|
|
1139
|
+
return this._cachedTextPromise;
|
|
857
1140
|
}
|
|
858
1141
|
async formData() {
|
|
859
|
-
|
|
1142
|
+
if (this._cachedFormDataPromise === null) {
|
|
1143
|
+
this._cachedFormDataPromise = this._request.formData();
|
|
1144
|
+
}
|
|
1145
|
+
return this._cachedFormDataPromise;
|
|
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;
|
|
860
1172
|
}
|
|
861
1173
|
get raw() {
|
|
862
1174
|
return this._request;
|
|
863
1175
|
}
|
|
864
|
-
}
|
|
865
|
-
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
class MinimalContext {
|
|
866
1179
|
req;
|
|
867
1180
|
_resHeaders = {};
|
|
1181
|
+
_requestScope;
|
|
868
1182
|
constructor(request, params, path, routePattern) {
|
|
869
1183
|
this.req = new MinimalRequest(request, params, path, routePattern);
|
|
1184
|
+
this._requestScope = new RequestScopeManager;
|
|
870
1185
|
}
|
|
871
|
-
// get req(): FastRequest {
|
|
872
|
-
// return this._req
|
|
873
|
-
// }
|
|
874
|
-
// Response helpers - merge custom headers with defaults
|
|
875
1186
|
getHeaders(contentType) {
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
"Content-Type": contentType
|
|
879
|
-
};
|
|
1187
|
+
const headers = Object.assign({ "Content-Type": contentType }, this._resHeaders);
|
|
1188
|
+
return headers;
|
|
880
1189
|
}
|
|
881
1190
|
json(data, status = 200) {
|
|
882
1191
|
return new Response(JSON.stringify(data), {
|
|
@@ -909,14 +1218,13 @@ var MinimalContext = class {
|
|
|
909
1218
|
});
|
|
910
1219
|
}
|
|
911
1220
|
header(name, value) {
|
|
912
|
-
if (value !==
|
|
1221
|
+
if (value !== undefined) {
|
|
913
1222
|
this._resHeaders[name] = value;
|
|
914
1223
|
return;
|
|
915
1224
|
}
|
|
916
1225
|
return this.req.header(name);
|
|
917
1226
|
}
|
|
918
|
-
status(_code) {
|
|
919
|
-
}
|
|
1227
|
+
status(_code) {}
|
|
920
1228
|
stream(stream, status = 200) {
|
|
921
1229
|
return new Response(stream, {
|
|
922
1230
|
status,
|
|
@@ -937,31 +1245,34 @@ var MinimalContext = class {
|
|
|
937
1245
|
}
|
|
938
1246
|
async forward(target, _options = {}) {
|
|
939
1247
|
const url = new URL(this.req.url);
|
|
940
|
-
const targetUrl = new URL(
|
|
941
|
-
target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`
|
|
942
|
-
);
|
|
1248
|
+
const targetUrl = new URL(target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`);
|
|
943
1249
|
return fetch(targetUrl.toString(), {
|
|
944
1250
|
method: this.req.method,
|
|
945
1251
|
headers: this.req.raw.headers
|
|
946
1252
|
});
|
|
947
1253
|
}
|
|
1254
|
+
escape(html) {
|
|
1255
|
+
return bunEscapeHTML2(html);
|
|
1256
|
+
}
|
|
948
1257
|
get(_key) {
|
|
949
|
-
return
|
|
1258
|
+
return;
|
|
950
1259
|
}
|
|
951
|
-
set(_key, _value) {
|
|
1260
|
+
set(_key, _value) {}
|
|
1261
|
+
requestScope() {
|
|
1262
|
+
return this._requestScope;
|
|
1263
|
+
}
|
|
1264
|
+
scoped(key, factory) {
|
|
1265
|
+
return this._requestScope.resolve(key, factory);
|
|
952
1266
|
}
|
|
953
1267
|
route = () => "";
|
|
954
1268
|
get native() {
|
|
955
1269
|
return this;
|
|
956
1270
|
}
|
|
957
|
-
// Required for interface compatibility
|
|
958
1271
|
init(_request, _params, _path) {
|
|
959
1272
|
throw new Error("MinimalContext does not support init. Create a new instance instead.");
|
|
960
1273
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
}
|
|
964
|
-
};
|
|
1274
|
+
reset() {}
|
|
1275
|
+
}
|
|
965
1276
|
|
|
966
1277
|
// src/engine/path.ts
|
|
967
1278
|
function extractPath(url) {
|
|
@@ -979,79 +1290,38 @@ function extractPath(url) {
|
|
|
979
1290
|
}
|
|
980
1291
|
|
|
981
1292
|
// src/engine/pool.ts
|
|
982
|
-
|
|
1293
|
+
class ObjectPool {
|
|
983
1294
|
pool = [];
|
|
984
1295
|
factory;
|
|
985
1296
|
reset;
|
|
986
1297
|
maxSize;
|
|
987
|
-
/**
|
|
988
|
-
* Create a new object pool
|
|
989
|
-
*
|
|
990
|
-
* @param factory - Function to create new objects
|
|
991
|
-
* @param reset - Function to reset objects before reuse
|
|
992
|
-
* @param maxSize - Maximum pool size (default: 256)
|
|
993
|
-
*/
|
|
994
1298
|
constructor(factory, reset, maxSize = 256) {
|
|
995
1299
|
this.factory = factory;
|
|
996
1300
|
this.reset = reset;
|
|
997
1301
|
this.maxSize = maxSize;
|
|
998
1302
|
}
|
|
999
|
-
/**
|
|
1000
|
-
* Acquire an object from the pool
|
|
1001
|
-
*
|
|
1002
|
-
* If the pool is empty, creates a new object (overflow strategy).
|
|
1003
|
-
* This ensures the pool never blocks under high load.
|
|
1004
|
-
*
|
|
1005
|
-
* @returns Object from pool or newly created
|
|
1006
|
-
*/
|
|
1007
1303
|
acquire() {
|
|
1008
1304
|
const obj = this.pool.pop();
|
|
1009
|
-
if (obj !==
|
|
1305
|
+
if (obj !== undefined) {
|
|
1010
1306
|
return obj;
|
|
1011
1307
|
}
|
|
1012
1308
|
return this.factory();
|
|
1013
1309
|
}
|
|
1014
|
-
/**
|
|
1015
|
-
* Release an object back to the pool
|
|
1016
|
-
*
|
|
1017
|
-
* If the pool is full, the object is discarded (will be GC'd).
|
|
1018
|
-
* This prevents unbounded memory growth.
|
|
1019
|
-
*
|
|
1020
|
-
* @param obj - Object to release
|
|
1021
|
-
*/
|
|
1022
1310
|
release(obj) {
|
|
1023
1311
|
if (this.pool.length < this.maxSize) {
|
|
1024
1312
|
this.reset(obj);
|
|
1025
1313
|
this.pool.push(obj);
|
|
1026
1314
|
}
|
|
1027
1315
|
}
|
|
1028
|
-
/**
|
|
1029
|
-
* Clear all objects from the pool
|
|
1030
|
-
*
|
|
1031
|
-
* Useful for testing or when you need to force a clean slate.
|
|
1032
|
-
*/
|
|
1033
1316
|
clear() {
|
|
1034
1317
|
this.pool = [];
|
|
1035
1318
|
}
|
|
1036
|
-
/**
|
|
1037
|
-
* Get current pool size
|
|
1038
|
-
*/
|
|
1039
1319
|
get size() {
|
|
1040
1320
|
return this.pool.length;
|
|
1041
1321
|
}
|
|
1042
|
-
/**
|
|
1043
|
-
* Get maximum pool size
|
|
1044
|
-
*/
|
|
1045
1322
|
get capacity() {
|
|
1046
1323
|
return this.maxSize;
|
|
1047
1324
|
}
|
|
1048
|
-
/**
|
|
1049
|
-
* Pre-warm the pool by creating objects in advance
|
|
1050
|
-
*
|
|
1051
|
-
* This can reduce latency for the first N requests.
|
|
1052
|
-
*
|
|
1053
|
-
* @param count - Number of objects to pre-create
|
|
1054
|
-
*/
|
|
1055
1325
|
prewarm(count) {
|
|
1056
1326
|
const targetSize = Math.min(count, this.maxSize);
|
|
1057
1327
|
while (this.pool.length < targetSize) {
|
|
@@ -1060,55 +1330,88 @@ var ObjectPool = class {
|
|
|
1060
1330
|
this.pool.push(obj);
|
|
1061
1331
|
}
|
|
1062
1332
|
}
|
|
1063
|
-
}
|
|
1333
|
+
}
|
|
1064
1334
|
|
|
1065
1335
|
// src/engine/Gravito.ts
|
|
1336
|
+
var bunPeek = globalThis.Bun.peek;
|
|
1066
1337
|
function compileMiddlewareChain(middleware, handler) {
|
|
1067
1338
|
if (middleware.length === 0) {
|
|
1068
1339
|
return handler;
|
|
1069
1340
|
}
|
|
1341
|
+
if (middleware.length === 1) {
|
|
1342
|
+
const mw = middleware[0];
|
|
1343
|
+
return async (ctx) => {
|
|
1344
|
+
let nextCalled = false;
|
|
1345
|
+
const result = mw(ctx, async () => {
|
|
1346
|
+
nextCalled = true;
|
|
1347
|
+
return;
|
|
1348
|
+
});
|
|
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;
|
|
1358
|
+
}
|
|
1359
|
+
if (nextCalled) {
|
|
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;
|
|
1366
|
+
}
|
|
1367
|
+
return ctx.json({ error: "Middleware did not call next or return response" }, 500);
|
|
1368
|
+
};
|
|
1369
|
+
}
|
|
1070
1370
|
let compiled = handler;
|
|
1071
|
-
for (let i = middleware.length - 1;
|
|
1371
|
+
for (let i = middleware.length - 1;i >= 0; i--) {
|
|
1072
1372
|
const mw = middleware[i];
|
|
1073
1373
|
const nextHandler = compiled;
|
|
1074
1374
|
compiled = async (ctx) => {
|
|
1075
|
-
let
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1375
|
+
let nextCalled = false;
|
|
1376
|
+
const result = mw(ctx, async () => {
|
|
1377
|
+
nextCalled = true;
|
|
1378
|
+
return;
|
|
1379
|
+
});
|
|
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;
|
|
1389
|
+
}
|
|
1390
|
+
if (nextCalled) {
|
|
1391
|
+
const nextResult = nextHandler(ctx);
|
|
1392
|
+
if (nextResult instanceof Promise) {
|
|
1393
|
+
const p = bunPeek(nextResult);
|
|
1394
|
+
return p === nextResult ? await nextResult : p;
|
|
1395
|
+
}
|
|
1078
1396
|
return nextResult;
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
return result ?? nextResult;
|
|
1397
|
+
}
|
|
1398
|
+
return ctx.json({ error: "Middleware did not call next or return response" }, 500);
|
|
1082
1399
|
};
|
|
1083
1400
|
}
|
|
1084
1401
|
return compiled;
|
|
1085
1402
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1403
|
+
|
|
1404
|
+
class Gravito {
|
|
1405
|
+
router = new AOTRouter;
|
|
1088
1406
|
contextPool;
|
|
1089
1407
|
errorHandler;
|
|
1090
1408
|
notFoundHandler;
|
|
1091
|
-
// Direct reference to static routes Map (O(1) access)
|
|
1092
|
-
/** @internal */
|
|
1093
1409
|
staticRoutes;
|
|
1094
|
-
// Flag: pure static app (no middleware at all) allows ultra-fast path
|
|
1095
1410
|
isPureStaticApp = true;
|
|
1096
|
-
|
|
1097
|
-
compiledDynamicRoutes = /* @__PURE__ */ new Map();
|
|
1098
|
-
// Version tracking for cache invalidation
|
|
1099
|
-
middlewareVersion = 0;
|
|
1100
|
-
/**
|
|
1101
|
-
* Create a new Gravito instance
|
|
1102
|
-
*
|
|
1103
|
-
* @param options - Engine configuration options
|
|
1104
|
-
*/
|
|
1411
|
+
compiledDynamicRoutes = new Map;
|
|
1105
1412
|
constructor(options = {}) {
|
|
1106
1413
|
const poolSize = options.poolSize ?? 256;
|
|
1107
|
-
this.contextPool = new ObjectPool(
|
|
1108
|
-
() => new FastContext(),
|
|
1109
|
-
(ctx) => ctx.reset(),
|
|
1110
|
-
poolSize
|
|
1111
|
-
);
|
|
1414
|
+
this.contextPool = new ObjectPool(() => new FastContext, (ctx) => ctx.reset(), poolSize);
|
|
1112
1415
|
this.contextPool.prewarm(Math.min(32, poolSize));
|
|
1113
1416
|
if (options.onError) {
|
|
1114
1417
|
this.errorHandler = options.onError;
|
|
@@ -1118,58 +1421,27 @@ var Gravito = class {
|
|
|
1118
1421
|
}
|
|
1119
1422
|
this.compileRoutes();
|
|
1120
1423
|
}
|
|
1121
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1122
|
-
// HTTP Method Registration
|
|
1123
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1124
|
-
/**
|
|
1125
|
-
* Register a GET route
|
|
1126
|
-
*
|
|
1127
|
-
* @param path - Route path (e.g., '/users/:id')
|
|
1128
|
-
* @param handlers - Handler and optional middleware
|
|
1129
|
-
* @returns This instance for chaining
|
|
1130
|
-
*/
|
|
1131
1424
|
get(path, ...handlers) {
|
|
1132
1425
|
return this.addRoute("get", path, handlers);
|
|
1133
1426
|
}
|
|
1134
|
-
/**
|
|
1135
|
-
* Register a POST route
|
|
1136
|
-
*/
|
|
1137
1427
|
post(path, ...handlers) {
|
|
1138
1428
|
return this.addRoute("post", path, handlers);
|
|
1139
1429
|
}
|
|
1140
|
-
/**
|
|
1141
|
-
* Register a PUT route
|
|
1142
|
-
*/
|
|
1143
1430
|
put(path, ...handlers) {
|
|
1144
1431
|
return this.addRoute("put", path, handlers);
|
|
1145
1432
|
}
|
|
1146
|
-
/**
|
|
1147
|
-
* Register a DELETE route
|
|
1148
|
-
*/
|
|
1149
1433
|
delete(path, ...handlers) {
|
|
1150
1434
|
return this.addRoute("delete", path, handlers);
|
|
1151
1435
|
}
|
|
1152
|
-
/**
|
|
1153
|
-
* Register a PDF route
|
|
1154
|
-
*/
|
|
1155
1436
|
patch(path, ...handlers) {
|
|
1156
1437
|
return this.addRoute("patch", path, handlers);
|
|
1157
1438
|
}
|
|
1158
|
-
/**
|
|
1159
|
-
* Register an OPTIONS route
|
|
1160
|
-
*/
|
|
1161
1439
|
options(path, ...handlers) {
|
|
1162
1440
|
return this.addRoute("options", path, handlers);
|
|
1163
1441
|
}
|
|
1164
|
-
/**
|
|
1165
|
-
* Register a HEAD route
|
|
1166
|
-
*/
|
|
1167
1442
|
head(path, ...handlers) {
|
|
1168
1443
|
return this.addRoute("head", path, handlers);
|
|
1169
1444
|
}
|
|
1170
|
-
/**
|
|
1171
|
-
* Register a route for all HTTP methods
|
|
1172
|
-
*/
|
|
1173
1445
|
all(path, ...handlers) {
|
|
1174
1446
|
const methods = ["get", "post", "put", "delete", "patch", "options", "head"];
|
|
1175
1447
|
for (const method of methods) {
|
|
@@ -1184,49 +1456,22 @@ var Gravito = class {
|
|
|
1184
1456
|
} else {
|
|
1185
1457
|
this.router.use(pathOrMiddleware, ...middleware);
|
|
1186
1458
|
}
|
|
1187
|
-
this.middlewareVersion++;
|
|
1188
1459
|
this.compileRoutes();
|
|
1189
1460
|
return this;
|
|
1190
1461
|
}
|
|
1191
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1192
|
-
// Route Grouping
|
|
1193
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1194
|
-
/**
|
|
1195
|
-
* Mount a sub-application at a path prefix
|
|
1196
|
-
*/
|
|
1197
1462
|
route(path, app) {
|
|
1198
1463
|
this.router.mount(path, app.router);
|
|
1199
1464
|
this.compileRoutes();
|
|
1200
1465
|
return this;
|
|
1201
1466
|
}
|
|
1202
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1203
|
-
// Error Handling
|
|
1204
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1205
|
-
/**
|
|
1206
|
-
* Set custom error handler
|
|
1207
|
-
*/
|
|
1208
1467
|
onError(handler) {
|
|
1209
1468
|
this.errorHandler = handler;
|
|
1210
1469
|
return this;
|
|
1211
1470
|
}
|
|
1212
|
-
/**
|
|
1213
|
-
* Set custom 404 handler
|
|
1214
|
-
*/
|
|
1215
1471
|
notFound(handler) {
|
|
1216
1472
|
this.notFoundHandler = handler;
|
|
1217
1473
|
return this;
|
|
1218
1474
|
}
|
|
1219
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1220
|
-
// Request Handling (Bun.serve integration)
|
|
1221
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1222
|
-
/**
|
|
1223
|
-
* Predictive Route Warming (JIT Optimization)
|
|
1224
|
-
*
|
|
1225
|
-
* Simulates requests to specified routes to trigger JIT compilation (FTL)
|
|
1226
|
-
* before real traffic arrives.
|
|
1227
|
-
*
|
|
1228
|
-
* @param paths List of paths to warm up (e.g. ['/api/users', '/health'])
|
|
1229
|
-
*/
|
|
1230
1475
|
async warmup(paths) {
|
|
1231
1476
|
const dummyReqOpts = { headers: { "User-Agent": "Gravito-Warmup/1.0" } };
|
|
1232
1477
|
for (const path of paths) {
|
|
@@ -1234,86 +1479,135 @@ var Gravito = class {
|
|
|
1234
1479
|
await this.fetch(req);
|
|
1235
1480
|
}
|
|
1236
1481
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
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
|
+
}
|
|
1240
1542
|
fetch = async (request) => {
|
|
1241
1543
|
const path = extractPath(request.url);
|
|
1242
1544
|
const method = request.method.toLowerCase();
|
|
1243
1545
|
const staticKey = `${method}:${path}`;
|
|
1244
1546
|
const staticRoute = this.staticRoutes.get(staticKey);
|
|
1245
1547
|
if (staticRoute) {
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
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;
|
|
1256
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);
|
|
1257
1571
|
}
|
|
1258
|
-
return await this.handleWithMiddleware(request, path, staticRoute);
|
|
1259
1572
|
}
|
|
1260
1573
|
return await this.handleDynamicRoute(request, method, path);
|
|
1261
1574
|
};
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
try {
|
|
1268
|
-
ctx.init(request, {}, path, path);
|
|
1269
|
-
if (route.compiled) {
|
|
1270
|
-
return await route.compiled(ctx);
|
|
1271
|
-
}
|
|
1272
|
-
const middleware = this.collectMiddlewareForPath(path, route.middleware);
|
|
1273
|
-
if (middleware.length === 0) {
|
|
1274
|
-
return await route.handler(ctx);
|
|
1275
|
-
}
|
|
1276
|
-
return await this.executeMiddleware(ctx, middleware, route.handler);
|
|
1277
|
-
} catch (error) {
|
|
1278
|
-
return await this.handleError(error, ctx);
|
|
1279
|
-
} finally {
|
|
1280
|
-
this.contextPool.release(ctx);
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
/**
|
|
1284
|
-
* Handle dynamic routes (Radix Tree lookup)
|
|
1285
|
-
*/
|
|
1286
|
-
handleDynamicRoute(request, method, path) {
|
|
1287
|
-
const match = this.router.match(method.toUpperCase(), path);
|
|
1288
|
-
if (!match.handler) {
|
|
1289
|
-
return this.handleNotFoundSync(request, path);
|
|
1290
|
-
}
|
|
1291
|
-
const cacheKey = `${method}:${match.routePattern ?? path}`;
|
|
1292
|
-
let entry = this.compiledDynamicRoutes.get(cacheKey);
|
|
1293
|
-
if (!entry || entry.version !== this.middlewareVersion) {
|
|
1294
|
-
const compiled = compileMiddlewareChain(match.middleware, match.handler);
|
|
1295
|
-
if (this.compiledDynamicRoutes.size > 1e3) {
|
|
1296
|
-
this.compiledDynamicRoutes.clear();
|
|
1297
|
-
}
|
|
1298
|
-
entry = { compiled, version: this.middlewareVersion };
|
|
1299
|
-
this.compiledDynamicRoutes.set(cacheKey, entry);
|
|
1300
|
-
}
|
|
1301
|
-
const ctx = this.contextPool.acquire();
|
|
1302
|
-
const execute = async () => {
|
|
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);
|
|
1303
1580
|
try {
|
|
1304
|
-
|
|
1305
|
-
|
|
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);
|
|
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;
|
|
1306
1601
|
} catch (error) {
|
|
1307
|
-
|
|
1308
|
-
|
|
1602
|
+
const cleanup = ctx.requestScope().cleanup();
|
|
1603
|
+
if (cleanup instanceof Promise)
|
|
1604
|
+
await cleanup;
|
|
1309
1605
|
this.contextPool.release(ctx);
|
|
1606
|
+
return this.handleErrorSync(error, request, path);
|
|
1310
1607
|
}
|
|
1311
|
-
}
|
|
1312
|
-
return
|
|
1608
|
+
}
|
|
1609
|
+
return this.handleNotFoundSync(request, path);
|
|
1313
1610
|
}
|
|
1314
|
-
/**
|
|
1315
|
-
* Sync error handler (for ultra-fast path)
|
|
1316
|
-
*/
|
|
1317
1611
|
handleErrorSync(error, request, path) {
|
|
1318
1612
|
if (this.errorHandler) {
|
|
1319
1613
|
const ctx = new MinimalContext(request, {}, path);
|
|
@@ -1329,9 +1623,6 @@ var Gravito = class {
|
|
|
1329
1623
|
headers: HEADERS.JSON
|
|
1330
1624
|
});
|
|
1331
1625
|
}
|
|
1332
|
-
/**
|
|
1333
|
-
* Sync 404 handler (for ultra-fast path)
|
|
1334
|
-
*/
|
|
1335
1626
|
handleNotFoundSync(request, path) {
|
|
1336
1627
|
if (this.notFoundHandler) {
|
|
1337
1628
|
const ctx = new MinimalContext(request, {}, path);
|
|
@@ -1346,25 +1637,19 @@ var Gravito = class {
|
|
|
1346
1637
|
headers: HEADERS.JSON
|
|
1347
1638
|
});
|
|
1348
1639
|
}
|
|
1349
|
-
/**
|
|
1350
|
-
* Collect middleware for a specific path
|
|
1351
|
-
*/
|
|
1352
1640
|
collectMiddlewareForPath(path, routeMiddleware) {
|
|
1353
1641
|
if (this.router.globalMiddleware.length === 0 && this.router.pathMiddleware.size === 0) {
|
|
1354
1642
|
return routeMiddleware;
|
|
1355
1643
|
}
|
|
1356
1644
|
return this.router.collectMiddlewarePublic(path, routeMiddleware);
|
|
1357
1645
|
}
|
|
1358
|
-
/**
|
|
1359
|
-
* Compile routes for optimization
|
|
1360
|
-
*/
|
|
1361
1646
|
compileRoutes() {
|
|
1362
1647
|
this.staticRoutes = this.router.staticRoutes;
|
|
1363
1648
|
const hasGlobalMiddleware = this.router.globalMiddleware.length > 0;
|
|
1364
1649
|
const hasPathMiddleware = this.router.pathMiddleware.size > 0;
|
|
1365
1650
|
this.isPureStaticApp = !hasGlobalMiddleware && !hasPathMiddleware;
|
|
1366
1651
|
for (const [key, route] of this.staticRoutes) {
|
|
1367
|
-
if (route.compiledVersion === this.
|
|
1652
|
+
if (route.compiledVersion === this.router.version) {
|
|
1368
1653
|
continue;
|
|
1369
1654
|
}
|
|
1370
1655
|
const analysis = analyzeHandler(route.handler);
|
|
@@ -1374,12 +1659,9 @@ var Gravito = class {
|
|
|
1374
1659
|
const allMiddleware = this.collectMiddlewareForPath(key.split(":")[1], route.middleware);
|
|
1375
1660
|
route.compiled = compileMiddlewareChain(allMiddleware, route.handler);
|
|
1376
1661
|
}
|
|
1377
|
-
route.compiledVersion = this.
|
|
1662
|
+
route.compiledVersion = this.router.version;
|
|
1378
1663
|
}
|
|
1379
1664
|
}
|
|
1380
|
-
/**
|
|
1381
|
-
* Add a route to the router
|
|
1382
|
-
*/
|
|
1383
1665
|
addRoute(method, path, handlers) {
|
|
1384
1666
|
if (handlers.length === 0) {
|
|
1385
1667
|
throw new Error(`No handler provided for ${method.toUpperCase()} ${path}`);
|
|
@@ -1390,46 +1672,14 @@ var Gravito = class {
|
|
|
1390
1672
|
this.compileRoutes();
|
|
1391
1673
|
return this;
|
|
1392
1674
|
}
|
|
1393
|
-
|
|
1394
|
-
* Execute middleware chain followed by handler
|
|
1395
|
-
*/
|
|
1396
|
-
async executeMiddleware(ctx, middleware, handler) {
|
|
1397
|
-
let index = 0;
|
|
1398
|
-
const next = async () => {
|
|
1399
|
-
if (index < middleware.length) {
|
|
1400
|
-
const mw = middleware[index++];
|
|
1401
|
-
return await mw(ctx, next);
|
|
1402
|
-
}
|
|
1403
|
-
return void 0;
|
|
1404
|
-
};
|
|
1405
|
-
const result = await next();
|
|
1406
|
-
if (result instanceof Response) {
|
|
1407
|
-
return result;
|
|
1408
|
-
}
|
|
1409
|
-
return await handler(ctx);
|
|
1410
|
-
}
|
|
1411
|
-
/**
|
|
1412
|
-
* Handle errors (Async version for dynamic/middleware paths)
|
|
1413
|
-
*/
|
|
1414
|
-
async handleError(error, ctx) {
|
|
1415
|
-
if (this.errorHandler) {
|
|
1416
|
-
return await this.errorHandler(error, ctx);
|
|
1417
|
-
}
|
|
1418
|
-
console.error("Unhandled error:", error);
|
|
1419
|
-
return ctx.json(
|
|
1420
|
-
{
|
|
1421
|
-
error: "Internal Server Error",
|
|
1422
|
-
message: error.message
|
|
1423
|
-
},
|
|
1424
|
-
500
|
|
1425
|
-
);
|
|
1426
|
-
}
|
|
1427
|
-
};
|
|
1675
|
+
}
|
|
1428
1676
|
export {
|
|
1429
|
-
|
|
1430
|
-
FastContext as FastContextImpl,
|
|
1431
|
-
Gravito,
|
|
1432
|
-
MinimalContext,
|
|
1677
|
+
extractPath,
|
|
1433
1678
|
ObjectPool,
|
|
1434
|
-
|
|
1679
|
+
MinimalContext,
|
|
1680
|
+
Gravito,
|
|
1681
|
+
FastContext as FastContextImpl,
|
|
1682
|
+
AOTRouter
|
|
1435
1683
|
};
|
|
1684
|
+
|
|
1685
|
+
//# debugId=13A90DBD3E511D5864756E2164756E21
|