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