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