@gravito/core 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine/index.js +62 -14
- package/dist/engine/index.js.map +7 -6
- package/dist/ffi/index.js +22 -493
- package/dist/ffi/index.js.map +3 -6
- package/dist/http/types.d.ts +7 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +6225 -6554
- package/dist/index.js.map +15 -17
- package/dist/runtime.d.ts +1 -1
- package/package.json +10 -10
- package/dist/compat.cjs +0 -49
- package/dist/compat.cjs.map +0 -9
- package/dist/engine/index.cjs +0 -1693
- package/dist/engine/index.cjs.map +0 -22
- package/dist/ffi/index.cjs +0 -621
- package/dist/ffi/index.cjs.map +0 -14
- package/dist/index.cjs +0 -14260
- package/dist/index.cjs.map +0 -168
package/dist/engine/index.cjs
DELETED
|
@@ -1,1693 +0,0 @@
|
|
|
1
|
-
// @bun @bun-cjs
|
|
2
|
-
(function(exports, require, module, __filename, __dirname) {var __create = Object.create;
|
|
3
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
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);
|
|
34
|
-
var __export = (target, all) => {
|
|
35
|
-
for (var name in all)
|
|
36
|
-
__defProp(target, name, {
|
|
37
|
-
get: all[name],
|
|
38
|
-
enumerable: true,
|
|
39
|
-
configurable: true,
|
|
40
|
-
set: (newValue) => all[name] = () => newValue
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// src/engine/index.ts
|
|
45
|
-
var exports_engine = {};
|
|
46
|
-
__export(exports_engine, {
|
|
47
|
-
extractPath: () => extractPath,
|
|
48
|
-
ObjectPool: () => ObjectPool,
|
|
49
|
-
MinimalContext: () => MinimalContext,
|
|
50
|
-
Gravito: () => Gravito,
|
|
51
|
-
FastContextImpl: () => FastContext,
|
|
52
|
-
AOTRouter: () => AOTRouter
|
|
53
|
-
});
|
|
54
|
-
module.exports = __toCommonJS(exports_engine);
|
|
55
|
-
|
|
56
|
-
// src/adapters/bun/RadixNode.ts
|
|
57
|
-
class RadixNode {
|
|
58
|
-
segment;
|
|
59
|
-
type;
|
|
60
|
-
children = new Map;
|
|
61
|
-
paramChild = null;
|
|
62
|
-
wildcardChild = null;
|
|
63
|
-
handlers = new Map;
|
|
64
|
-
paramName = null;
|
|
65
|
-
regex = null;
|
|
66
|
-
constructor(segment = "", type = 0 /* STATIC */) {
|
|
67
|
-
this.segment = segment;
|
|
68
|
-
this.type = type;
|
|
69
|
-
}
|
|
70
|
-
toJSON() {
|
|
71
|
-
return {
|
|
72
|
-
segment: this.segment,
|
|
73
|
-
type: this.type,
|
|
74
|
-
children: Array.from(this.children.entries()).map(([k, v]) => [k, v.toJSON()]),
|
|
75
|
-
paramChild: this.paramChild?.toJSON() || null,
|
|
76
|
-
wildcardChild: this.wildcardChild?.toJSON() || null,
|
|
77
|
-
paramName: this.paramName,
|
|
78
|
-
regex: this.regex ? this.regex.source : null
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
static fromJSON(json) {
|
|
82
|
-
const node = new RadixNode(json.segment, json.type);
|
|
83
|
-
node.paramName = json.paramName;
|
|
84
|
-
if (json.regex) {
|
|
85
|
-
node.regex = new RegExp(json.regex);
|
|
86
|
-
}
|
|
87
|
-
if (json.children) {
|
|
88
|
-
for (const [key, childJson] of json.children) {
|
|
89
|
-
node.children.set(key, RadixNode.fromJSON(childJson));
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (json.paramChild) {
|
|
93
|
-
node.paramChild = RadixNode.fromJSON(json.paramChild);
|
|
94
|
-
}
|
|
95
|
-
if (json.wildcardChild) {
|
|
96
|
-
node.wildcardChild = RadixNode.fromJSON(json.wildcardChild);
|
|
97
|
-
}
|
|
98
|
-
return node;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// src/adapters/bun/RadixRouter.ts
|
|
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;
|
|
130
|
-
where(param, regex) {
|
|
131
|
-
this.globalConstraints.set(param, regex);
|
|
132
|
-
}
|
|
133
|
-
add(method, path, handlers) {
|
|
134
|
-
let node = this.root;
|
|
135
|
-
const segments = this.splitPath(path);
|
|
136
|
-
for (let i = 0;i < segments.length; i++) {
|
|
137
|
-
const segment = segments[i];
|
|
138
|
-
if (segment === "*") {
|
|
139
|
-
if (!node.wildcardChild) {
|
|
140
|
-
node.wildcardChild = new RadixNode("*", 2 /* WILDCARD */);
|
|
141
|
-
}
|
|
142
|
-
node = node.wildcardChild;
|
|
143
|
-
break;
|
|
144
|
-
} else if (segment.startsWith(":")) {
|
|
145
|
-
const paramName = segment.slice(1);
|
|
146
|
-
if (!node.paramChild) {
|
|
147
|
-
const child = new RadixNode(segment, 1 /* PARAM */);
|
|
148
|
-
child.paramName = paramName;
|
|
149
|
-
const constraint = this.globalConstraints.get(paramName);
|
|
150
|
-
if (constraint) {
|
|
151
|
-
child.regex = constraint;
|
|
152
|
-
}
|
|
153
|
-
node.paramChild = child;
|
|
154
|
-
}
|
|
155
|
-
node = node.paramChild;
|
|
156
|
-
} else {
|
|
157
|
-
if (!node.children.has(segment)) {
|
|
158
|
-
node.children.set(segment, new RadixNode(segment, 0 /* STATIC */));
|
|
159
|
-
}
|
|
160
|
-
node = node.children.get(segment);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
node.handlers.set(method.toLowerCase(), handlers);
|
|
164
|
-
this.routeCache.clear();
|
|
165
|
-
}
|
|
166
|
-
match(method, path) {
|
|
167
|
-
const normalizedMethod = method.toLowerCase();
|
|
168
|
-
if (path === "/" || path === "") {
|
|
169
|
-
const handlers = this.root.handlers.get(normalizedMethod);
|
|
170
|
-
if (handlers) {
|
|
171
|
-
return { handlers, params: {} };
|
|
172
|
-
}
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
const cacheKey = `${normalizedMethod}:${path}`;
|
|
176
|
-
if (this.routeCache.has(cacheKey)) {
|
|
177
|
-
return this.routeCache.get(cacheKey) ?? null;
|
|
178
|
-
}
|
|
179
|
-
const searchPath = path.startsWith("/") ? path.slice(1) : path;
|
|
180
|
-
const segments = searchPath.split("/");
|
|
181
|
-
const result = this.matchRecursive(this.root, segments, 0, {}, normalizedMethod);
|
|
182
|
-
this.routeCache.set(cacheKey, result);
|
|
183
|
-
return result;
|
|
184
|
-
}
|
|
185
|
-
matchRecursive(node, segments, depth, params, method) {
|
|
186
|
-
if (depth >= segments.length) {
|
|
187
|
-
let handlers = node.handlers.get(method);
|
|
188
|
-
if (!handlers) {
|
|
189
|
-
handlers = node.handlers.get("all");
|
|
190
|
-
}
|
|
191
|
-
if (handlers) {
|
|
192
|
-
return { handlers, params };
|
|
193
|
-
}
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
const segment = segments[depth];
|
|
197
|
-
const staticChild = node.children.get(segment);
|
|
198
|
-
if (staticChild) {
|
|
199
|
-
const match = this.matchRecursive(staticChild, segments, depth + 1, params, method);
|
|
200
|
-
if (match) {
|
|
201
|
-
return match;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
const paramChild = node.paramChild;
|
|
205
|
-
if (paramChild) {
|
|
206
|
-
if (paramChild.regex && !paramChild.regex.test(segment)) {} else {
|
|
207
|
-
if (paramChild.paramName) {
|
|
208
|
-
params[paramChild.paramName] = decodeURIComponent(segment);
|
|
209
|
-
const match = this.matchRecursive(paramChild, segments, depth + 1, params, method);
|
|
210
|
-
if (match) {
|
|
211
|
-
return match;
|
|
212
|
-
}
|
|
213
|
-
delete params[paramChild.paramName];
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (node.wildcardChild) {
|
|
218
|
-
let handlers = node.wildcardChild.handlers.get(method);
|
|
219
|
-
if (!handlers) {
|
|
220
|
-
handlers = node.wildcardChild.handlers.get("all");
|
|
221
|
-
}
|
|
222
|
-
if (handlers) {
|
|
223
|
-
return { handlers, params };
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
splitPath(path) {
|
|
229
|
-
if (path === "/" || path === "") {
|
|
230
|
-
return [];
|
|
231
|
-
}
|
|
232
|
-
let p = path;
|
|
233
|
-
if (p.startsWith("/")) {
|
|
234
|
-
p = p.slice(1);
|
|
235
|
-
}
|
|
236
|
-
if (p.endsWith("/")) {
|
|
237
|
-
p = p.slice(0, -1);
|
|
238
|
-
}
|
|
239
|
-
return p.split("/");
|
|
240
|
-
}
|
|
241
|
-
serialize() {
|
|
242
|
-
return JSON.stringify({
|
|
243
|
-
root: this.root.toJSON(),
|
|
244
|
-
globalConstraints: Array.from(this.globalConstraints.entries()).map(([k, v]) => [
|
|
245
|
-
k,
|
|
246
|
-
v.source
|
|
247
|
-
])
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
static fromSerialized(json) {
|
|
251
|
-
const data = JSON.parse(json);
|
|
252
|
-
const router = new RadixRouter;
|
|
253
|
-
router.root = RadixNode.fromJSON(data.root);
|
|
254
|
-
if (data.globalConstraints) {
|
|
255
|
-
for (const [key, source] of data.globalConstraints) {
|
|
256
|
-
router.globalConstraints.set(key, new RegExp(source));
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return router;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// src/engine/AOTRouter.ts
|
|
264
|
-
class AOTRouter {
|
|
265
|
-
staticRoutes = new Map;
|
|
266
|
-
dynamicRouter = new RadixRouter;
|
|
267
|
-
routeDefinitions = [];
|
|
268
|
-
globalMiddleware = [];
|
|
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
|
-
}
|
|
277
|
-
add(method, path, handler, middleware = []) {
|
|
278
|
-
this.routeDefinitions.push({ method, path, handler, middleware });
|
|
279
|
-
const normalizedMethod = method.toLowerCase();
|
|
280
|
-
if (this.isStaticPath(path)) {
|
|
281
|
-
const key = `${normalizedMethod}:${path}`;
|
|
282
|
-
this.staticRoutes.set(key, { handler, middleware });
|
|
283
|
-
} else {
|
|
284
|
-
const wrappedHandler = handler;
|
|
285
|
-
this.dynamicRouter.add(normalizedMethod, path, [wrappedHandler]);
|
|
286
|
-
this.dynamicRoutePatterns.set(wrappedHandler, path);
|
|
287
|
-
if (middleware.length > 0) {
|
|
288
|
-
this.pathMiddleware.set(`${normalizedMethod}:${path}`, middleware);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
mount(prefix, other) {
|
|
293
|
-
if (other.globalMiddleware.length > 0) {
|
|
294
|
-
this.usePattern(prefix, ...other.globalMiddleware);
|
|
295
|
-
const wildcard = prefix === "/" ? "/*" : `${prefix}/*`;
|
|
296
|
-
this.usePattern(wildcard, ...other.globalMiddleware);
|
|
297
|
-
}
|
|
298
|
-
for (const [pattern, mws] of other.pathMiddleware) {
|
|
299
|
-
const hasMethodPrefix = /^(get|post|put|delete|patch|options|head):/.test(pattern);
|
|
300
|
-
if (hasMethodPrefix) {
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
let newPattern;
|
|
304
|
-
if (pattern === "*") {
|
|
305
|
-
newPattern = prefix === "/" ? "/*" : `${prefix}/*`;
|
|
306
|
-
} else if (pattern.startsWith("/")) {
|
|
307
|
-
newPattern = prefix === "/" ? pattern : `${prefix}${pattern}`;
|
|
308
|
-
} else {
|
|
309
|
-
newPattern = prefix === "/" ? `/${pattern}` : `${prefix}/${pattern}`;
|
|
310
|
-
}
|
|
311
|
-
this.usePattern(newPattern, ...mws);
|
|
312
|
-
}
|
|
313
|
-
for (const def of other.routeDefinitions) {
|
|
314
|
-
let newPath;
|
|
315
|
-
if (prefix === "/") {
|
|
316
|
-
newPath = def.path;
|
|
317
|
-
} else if (def.path === "/") {
|
|
318
|
-
newPath = prefix;
|
|
319
|
-
} else {
|
|
320
|
-
newPath = `${prefix}${def.path}`;
|
|
321
|
-
}
|
|
322
|
-
this.add(def.method, newPath, def.handler, def.middleware);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
use(...middleware) {
|
|
326
|
-
this.globalMiddleware.push(...middleware);
|
|
327
|
-
this._version++;
|
|
328
|
-
}
|
|
329
|
-
usePattern(pattern, ...middleware) {
|
|
330
|
-
if (pattern === "*") {
|
|
331
|
-
this.globalMiddleware.push(...middleware);
|
|
332
|
-
} else {
|
|
333
|
-
const existing = this.pathMiddleware.get(pattern) ?? [];
|
|
334
|
-
this.pathMiddleware.set(pattern, [...existing, ...middleware]);
|
|
335
|
-
}
|
|
336
|
-
this._version++;
|
|
337
|
-
}
|
|
338
|
-
match(method, path) {
|
|
339
|
-
const normalizedMethod = method.toLowerCase();
|
|
340
|
-
const staticKey = `${normalizedMethod}:${path}`;
|
|
341
|
-
const staticRoute = this.staticRoutes.get(staticKey);
|
|
342
|
-
if (staticRoute) {
|
|
343
|
-
return {
|
|
344
|
-
handler: staticRoute.handler,
|
|
345
|
-
params: {},
|
|
346
|
-
middleware: this.collectMiddleware(path, staticRoute.middleware),
|
|
347
|
-
routePattern: path
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
const match = this.dynamicRouter.match(normalizedMethod, path);
|
|
351
|
-
if (match && match.handlers.length > 0) {
|
|
352
|
-
const handler = match.handlers[0];
|
|
353
|
-
const wrappedHandler = match.handlers[0];
|
|
354
|
-
const routePattern = this.dynamicRoutePatterns.get(wrappedHandler);
|
|
355
|
-
const routeKey = routePattern ? `${normalizedMethod}:${routePattern}` : null;
|
|
356
|
-
const routeMiddleware = routeKey ? this.pathMiddleware.get(routeKey) ?? [] : [];
|
|
357
|
-
return {
|
|
358
|
-
handler,
|
|
359
|
-
params: match.params,
|
|
360
|
-
middleware: this.collectMiddleware(path, routeMiddleware),
|
|
361
|
-
routePattern
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
return {
|
|
365
|
-
handler: null,
|
|
366
|
-
params: {},
|
|
367
|
-
middleware: []
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
collectMiddlewarePublic(path, routeMiddleware) {
|
|
371
|
-
return this.collectMiddleware(path, routeMiddleware);
|
|
372
|
-
}
|
|
373
|
-
collectMiddleware(path, routeMiddleware) {
|
|
374
|
-
if (this.globalMiddleware.length === 0 && this.pathMiddleware.size === 0 && routeMiddleware.length === 0) {
|
|
375
|
-
return [];
|
|
376
|
-
}
|
|
377
|
-
const cacheKey = path;
|
|
378
|
-
const cached = this.middlewareCache.get(cacheKey);
|
|
379
|
-
if (cached !== undefined && cached.version === this._version) {
|
|
380
|
-
return cached.data;
|
|
381
|
-
}
|
|
382
|
-
const middleware = [];
|
|
383
|
-
if (this.globalMiddleware.length > 0) {
|
|
384
|
-
middleware.push(...this.globalMiddleware);
|
|
385
|
-
}
|
|
386
|
-
if (this.pathMiddleware.size > 0) {
|
|
387
|
-
for (const [pattern, mw] of this.pathMiddleware) {
|
|
388
|
-
if (pattern.includes(":")) {
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
if (this.matchPattern(pattern, path)) {
|
|
392
|
-
middleware.push(...mw);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
if (routeMiddleware.length > 0) {
|
|
397
|
-
middleware.push(...routeMiddleware);
|
|
398
|
-
}
|
|
399
|
-
if (this.middlewareCache.size < this.cacheMaxSize) {
|
|
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 });
|
|
403
|
-
}
|
|
404
|
-
return middleware;
|
|
405
|
-
}
|
|
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
|
-
}
|
|
418
|
-
isStaticPath(path) {
|
|
419
|
-
return !path.includes(":") && !path.includes("*");
|
|
420
|
-
}
|
|
421
|
-
matchPattern(pattern, path) {
|
|
422
|
-
if (pattern === "*") {
|
|
423
|
-
return true;
|
|
424
|
-
}
|
|
425
|
-
if (pattern === path) {
|
|
426
|
-
return true;
|
|
427
|
-
}
|
|
428
|
-
if (pattern.endsWith("/*")) {
|
|
429
|
-
const prefix = pattern.slice(0, -2);
|
|
430
|
-
return path.startsWith(prefix);
|
|
431
|
-
}
|
|
432
|
-
return false;
|
|
433
|
-
}
|
|
434
|
-
getRoutes() {
|
|
435
|
-
const routes = [];
|
|
436
|
-
for (const key of this.staticRoutes.keys()) {
|
|
437
|
-
const [method, path] = key.split(":");
|
|
438
|
-
routes.push({ method, path, type: "static" });
|
|
439
|
-
}
|
|
440
|
-
return routes;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
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) {
|
|
543
|
-
return {
|
|
544
|
-
usesHeaders: source.includes(".header(") || source.includes(".header)") || source.includes(".headers(") || source.includes(".headers)"),
|
|
545
|
-
usesQuery: source.includes(".query(") || source.includes(".query)") || source.includes(".queries(") || source.includes(".queries)"),
|
|
546
|
-
usesBody: source.includes(".json()") || source.includes(".text()") || source.includes(".formData()") || source.includes(".body"),
|
|
547
|
-
usesParams: source.includes(".param(") || source.includes(".param)") || source.includes(".params(") || source.includes(".params)"),
|
|
548
|
-
isAsync: source.includes("async") || source.includes("await")
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// src/engine/analyzer.ts
|
|
553
|
-
function analyzeHandler(handler) {
|
|
554
|
-
const source = handler.toString();
|
|
555
|
-
return analyzeHandlerWithTranspiler(source);
|
|
556
|
-
}
|
|
557
|
-
function getOptimalContextType(analysis) {
|
|
558
|
-
if (analysis.usesHeaders) {
|
|
559
|
-
return "fast";
|
|
560
|
-
}
|
|
561
|
-
if (!analysis.usesQuery && !analysis.usesBody && !analysis.usesParams) {
|
|
562
|
-
return "minimal";
|
|
563
|
-
}
|
|
564
|
-
if (!analysis.usesQuery && !analysis.usesBody && analysis.usesParams) {
|
|
565
|
-
return "minimal";
|
|
566
|
-
}
|
|
567
|
-
if (analysis.usesBody) {
|
|
568
|
-
return "full";
|
|
569
|
-
}
|
|
570
|
-
return "fast";
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// src/engine/constants.ts
|
|
574
|
-
var encoder = new TextEncoder;
|
|
575
|
-
var CACHED_RESPONSES = {
|
|
576
|
-
NOT_FOUND: encoder.encode('{"error":"Not Found"}'),
|
|
577
|
-
INTERNAL_ERROR: encoder.encode('{"error":"Internal Server Error"}'),
|
|
578
|
-
OK: encoder.encode('{"ok":true}'),
|
|
579
|
-
EMPTY: new Uint8Array(0)
|
|
580
|
-
};
|
|
581
|
-
var HEADERS = {
|
|
582
|
-
JSON: { "Content-Type": "application/json; charset=utf-8" },
|
|
583
|
-
TEXT: { "Content-Type": "text/plain; charset=utf-8" },
|
|
584
|
-
HTML: { "Content-Type": "text/html; charset=utf-8" }
|
|
585
|
-
};
|
|
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
|
-
|
|
731
|
-
// src/engine/FastContext.ts
|
|
732
|
-
var bunEscapeHTML = globalThis.Bun.escapeHTML;
|
|
733
|
-
|
|
734
|
-
class FastRequestImpl {
|
|
735
|
-
_request;
|
|
736
|
-
_params;
|
|
737
|
-
_path;
|
|
738
|
-
_routePattern;
|
|
739
|
-
_url = null;
|
|
740
|
-
_query = null;
|
|
741
|
-
_headers = null;
|
|
742
|
-
_cachedJson = undefined;
|
|
743
|
-
_jsonParsed = false;
|
|
744
|
-
_cachedText = undefined;
|
|
745
|
-
_textParsed = false;
|
|
746
|
-
_cachedFormData = undefined;
|
|
747
|
-
_formDataParsed = false;
|
|
748
|
-
_cachedQueries = null;
|
|
749
|
-
_cachedCookies = null;
|
|
750
|
-
_ctx;
|
|
751
|
-
constructor(ctx) {
|
|
752
|
-
this._ctx = ctx;
|
|
753
|
-
}
|
|
754
|
-
init(request, params = {}, path = "", routePattern) {
|
|
755
|
-
this._request = request;
|
|
756
|
-
this._params = params;
|
|
757
|
-
this._path = path;
|
|
758
|
-
this._routePattern = routePattern;
|
|
759
|
-
this._url = null;
|
|
760
|
-
this._query = null;
|
|
761
|
-
this._headers = null;
|
|
762
|
-
this._cachedJson = undefined;
|
|
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;
|
|
770
|
-
return this;
|
|
771
|
-
}
|
|
772
|
-
reset() {
|
|
773
|
-
this._request = undefined;
|
|
774
|
-
this._params = undefined;
|
|
775
|
-
this._url = null;
|
|
776
|
-
this._query = null;
|
|
777
|
-
this._headers = null;
|
|
778
|
-
this._cachedJson = undefined;
|
|
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;
|
|
786
|
-
}
|
|
787
|
-
checkReleased() {
|
|
788
|
-
if (this._ctx._isReleased) {
|
|
789
|
-
throw new Error("FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)");
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
get url() {
|
|
793
|
-
this.checkReleased();
|
|
794
|
-
return this._request.url;
|
|
795
|
-
}
|
|
796
|
-
get method() {
|
|
797
|
-
this.checkReleased();
|
|
798
|
-
return this._request.method;
|
|
799
|
-
}
|
|
800
|
-
get path() {
|
|
801
|
-
this.checkReleased();
|
|
802
|
-
return this._path;
|
|
803
|
-
}
|
|
804
|
-
get routePattern() {
|
|
805
|
-
this.checkReleased();
|
|
806
|
-
return this._routePattern;
|
|
807
|
-
}
|
|
808
|
-
param(name) {
|
|
809
|
-
this.checkReleased();
|
|
810
|
-
return this._params[name];
|
|
811
|
-
}
|
|
812
|
-
params() {
|
|
813
|
-
this.checkReleased();
|
|
814
|
-
return { ...this._params };
|
|
815
|
-
}
|
|
816
|
-
getUrl() {
|
|
817
|
-
if (!this._url) {
|
|
818
|
-
this._url = new URL(this._request.url);
|
|
819
|
-
}
|
|
820
|
-
return this._url;
|
|
821
|
-
}
|
|
822
|
-
query(name) {
|
|
823
|
-
this.checkReleased();
|
|
824
|
-
if (!this._query) {
|
|
825
|
-
this._query = this.getUrl().searchParams;
|
|
826
|
-
}
|
|
827
|
-
return this._query.get(name) ?? undefined;
|
|
828
|
-
}
|
|
829
|
-
queries() {
|
|
830
|
-
this.checkReleased();
|
|
831
|
-
if (this._cachedQueries !== null) {
|
|
832
|
-
return this._cachedQueries;
|
|
833
|
-
}
|
|
834
|
-
if (!this._query) {
|
|
835
|
-
this._query = this.getUrl().searchParams;
|
|
836
|
-
}
|
|
837
|
-
const result = {};
|
|
838
|
-
for (const [key, value] of this._query.entries()) {
|
|
839
|
-
const existing = result[key];
|
|
840
|
-
if (existing === undefined) {
|
|
841
|
-
result[key] = value;
|
|
842
|
-
} else if (Array.isArray(existing)) {
|
|
843
|
-
existing.push(value);
|
|
844
|
-
} else {
|
|
845
|
-
result[key] = [existing, value];
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
this._cachedQueries = result;
|
|
849
|
-
return result;
|
|
850
|
-
}
|
|
851
|
-
header(name) {
|
|
852
|
-
this.checkReleased();
|
|
853
|
-
return this._request.headers.get(name) ?? undefined;
|
|
854
|
-
}
|
|
855
|
-
headers() {
|
|
856
|
-
this.checkReleased();
|
|
857
|
-
if (!this._headers) {
|
|
858
|
-
this._headers = {};
|
|
859
|
-
for (const [key, value] of this._request.headers.entries()) {
|
|
860
|
-
this._headers[key] = value;
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
return { ...this._headers };
|
|
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
|
-
}
|
|
898
|
-
async json() {
|
|
899
|
-
this.checkReleased();
|
|
900
|
-
if (!this._jsonParsed) {
|
|
901
|
-
this._cachedJson = await this._request.json();
|
|
902
|
-
this._jsonParsed = true;
|
|
903
|
-
}
|
|
904
|
-
return this._cachedJson;
|
|
905
|
-
}
|
|
906
|
-
async text() {
|
|
907
|
-
this.checkReleased();
|
|
908
|
-
if (!this._textParsed) {
|
|
909
|
-
this._cachedText = await this._request.text();
|
|
910
|
-
this._textParsed = true;
|
|
911
|
-
}
|
|
912
|
-
return this._cachedText;
|
|
913
|
-
}
|
|
914
|
-
async formData() {
|
|
915
|
-
this.checkReleased();
|
|
916
|
-
if (!this._formDataParsed) {
|
|
917
|
-
this._cachedFormData = await this._request.formData();
|
|
918
|
-
this._formDataParsed = true;
|
|
919
|
-
}
|
|
920
|
-
return this._cachedFormData;
|
|
921
|
-
}
|
|
922
|
-
get raw() {
|
|
923
|
-
this.checkReleased();
|
|
924
|
-
return this._request;
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
class FastContext {
|
|
929
|
-
req = new FastRequestImpl(this);
|
|
930
|
-
_headers = new Headers;
|
|
931
|
-
_isReleased = false;
|
|
932
|
-
_requestScope = null;
|
|
933
|
-
init(request, params = {}, path = "", routePattern) {
|
|
934
|
-
this._isReleased = false;
|
|
935
|
-
this.req.init(request, params, path, routePattern);
|
|
936
|
-
this._headers = new Headers;
|
|
937
|
-
this._requestScope = new RequestScopeManager;
|
|
938
|
-
return this;
|
|
939
|
-
}
|
|
940
|
-
reset() {
|
|
941
|
-
this._isReleased = true;
|
|
942
|
-
this.req.reset();
|
|
943
|
-
this._store.clear();
|
|
944
|
-
this._requestScope = null;
|
|
945
|
-
}
|
|
946
|
-
checkReleased() {
|
|
947
|
-
if (this._isReleased) {
|
|
948
|
-
throw new Error("FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)");
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
json(data, status = 200) {
|
|
952
|
-
this.checkReleased();
|
|
953
|
-
const headers = new Headers(this._headers);
|
|
954
|
-
headers.set("Content-Type", "application/json; charset=utf-8");
|
|
955
|
-
return Response.json(data, { status, headers });
|
|
956
|
-
}
|
|
957
|
-
text(text, status = 200) {
|
|
958
|
-
this.checkReleased();
|
|
959
|
-
const headers = new Headers(this._headers);
|
|
960
|
-
headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
961
|
-
return new Response(text, { status, headers });
|
|
962
|
-
}
|
|
963
|
-
html(html, status = 200) {
|
|
964
|
-
this.checkReleased();
|
|
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);
|
|
971
|
-
}
|
|
972
|
-
redirect(url, status = 302) {
|
|
973
|
-
this.checkReleased();
|
|
974
|
-
return new Response(null, {
|
|
975
|
-
status,
|
|
976
|
-
headers: { Location: url }
|
|
977
|
-
});
|
|
978
|
-
}
|
|
979
|
-
body(data, status = 200) {
|
|
980
|
-
this.checkReleased();
|
|
981
|
-
return new Response(data, {
|
|
982
|
-
status,
|
|
983
|
-
headers: this._headers
|
|
984
|
-
});
|
|
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
|
-
}
|
|
994
|
-
stream(stream, status = 200) {
|
|
995
|
-
this.checkReleased();
|
|
996
|
-
return new Response(stream, {
|
|
997
|
-
status,
|
|
998
|
-
headers: { "Content-Type": "application/octet-stream" }
|
|
999
|
-
});
|
|
1000
|
-
}
|
|
1001
|
-
notFound(message = "Not Found") {
|
|
1002
|
-
return this.text(message, 404);
|
|
1003
|
-
}
|
|
1004
|
-
forbidden(message = "Forbidden") {
|
|
1005
|
-
return this.text(message, 403);
|
|
1006
|
-
}
|
|
1007
|
-
unauthorized(message = "Unauthorized") {
|
|
1008
|
-
return this.text(message, 401);
|
|
1009
|
-
}
|
|
1010
|
-
badRequest(message = "Bad Request") {
|
|
1011
|
-
return this.text(message, 400);
|
|
1012
|
-
}
|
|
1013
|
-
async forward(target, _options = {}) {
|
|
1014
|
-
this.checkReleased();
|
|
1015
|
-
const url = new URL(this.req.url);
|
|
1016
|
-
const targetUrl = new URL(target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`);
|
|
1017
|
-
const searchParams = new URLSearchParams(url.search);
|
|
1018
|
-
searchParams.forEach((v, k) => {
|
|
1019
|
-
targetUrl.searchParams.set(k, v);
|
|
1020
|
-
});
|
|
1021
|
-
return fetch(targetUrl.toString(), {
|
|
1022
|
-
method: this.req.method,
|
|
1023
|
-
headers: this.req.raw.headers,
|
|
1024
|
-
body: this.req.method !== "GET" && this.req.method !== "HEAD" ? this.req.raw.body : null,
|
|
1025
|
-
duplex: "half"
|
|
1026
|
-
});
|
|
1027
|
-
}
|
|
1028
|
-
header(name, value) {
|
|
1029
|
-
this.checkReleased();
|
|
1030
|
-
if (value !== undefined) {
|
|
1031
|
-
this._headers.set(name, value);
|
|
1032
|
-
return;
|
|
1033
|
-
}
|
|
1034
|
-
return this.req.header(name);
|
|
1035
|
-
}
|
|
1036
|
-
status(_code) {
|
|
1037
|
-
this.checkReleased();
|
|
1038
|
-
}
|
|
1039
|
-
_store = new Map;
|
|
1040
|
-
get(key) {
|
|
1041
|
-
return this._store.get(key);
|
|
1042
|
-
}
|
|
1043
|
-
set(key, value) {
|
|
1044
|
-
this._store.set(key, value);
|
|
1045
|
-
}
|
|
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
|
-
}
|
|
1055
|
-
route = () => "";
|
|
1056
|
-
get native() {
|
|
1057
|
-
return this;
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
// src/engine/MinimalContext.ts
|
|
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;
|
|
1074
|
-
constructor(_request, _params, _path, _routePattern) {
|
|
1075
|
-
this._request = _request;
|
|
1076
|
-
this._params = _params;
|
|
1077
|
-
this._path = _path;
|
|
1078
|
-
this._routePattern = _routePattern;
|
|
1079
|
-
}
|
|
1080
|
-
get url() {
|
|
1081
|
-
return this._request.url;
|
|
1082
|
-
}
|
|
1083
|
-
get method() {
|
|
1084
|
-
return this._request.method;
|
|
1085
|
-
}
|
|
1086
|
-
get path() {
|
|
1087
|
-
return this._path;
|
|
1088
|
-
}
|
|
1089
|
-
get routePattern() {
|
|
1090
|
-
return this._routePattern;
|
|
1091
|
-
}
|
|
1092
|
-
param(name) {
|
|
1093
|
-
return this._params[name];
|
|
1094
|
-
}
|
|
1095
|
-
params() {
|
|
1096
|
-
return { ...this._params };
|
|
1097
|
-
}
|
|
1098
|
-
getSearchParams() {
|
|
1099
|
-
if (this._searchParams === null) {
|
|
1100
|
-
const url = this._request.url;
|
|
1101
|
-
const queryStart = url.indexOf("?");
|
|
1102
|
-
if (queryStart === -1) {
|
|
1103
|
-
this._searchParams = new URLSearchParams;
|
|
1104
|
-
} else {
|
|
1105
|
-
const hashStart = url.indexOf("#", queryStart);
|
|
1106
|
-
const queryString = hashStart === -1 ? url.slice(queryStart + 1) : url.slice(queryStart + 1, hashStart);
|
|
1107
|
-
this._searchParams = new URLSearchParams(queryString);
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
return this._searchParams;
|
|
1111
|
-
}
|
|
1112
|
-
query(name) {
|
|
1113
|
-
return this.getSearchParams().get(name) ?? undefined;
|
|
1114
|
-
}
|
|
1115
|
-
queries() {
|
|
1116
|
-
if (this._cachedQueries !== null) {
|
|
1117
|
-
return this._cachedQueries;
|
|
1118
|
-
}
|
|
1119
|
-
const params = this.getSearchParams();
|
|
1120
|
-
const result = {};
|
|
1121
|
-
for (const [key, value] of params.entries()) {
|
|
1122
|
-
const existing = result[key];
|
|
1123
|
-
if (existing === undefined) {
|
|
1124
|
-
result[key] = value;
|
|
1125
|
-
} else if (Array.isArray(existing)) {
|
|
1126
|
-
existing.push(value);
|
|
1127
|
-
} else {
|
|
1128
|
-
result[key] = [existing, value];
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
this._cachedQueries = result;
|
|
1132
|
-
return result;
|
|
1133
|
-
}
|
|
1134
|
-
header(name) {
|
|
1135
|
-
return this._request.headers.get(name) ?? undefined;
|
|
1136
|
-
}
|
|
1137
|
-
headers() {
|
|
1138
|
-
const result = {};
|
|
1139
|
-
for (const [key, value] of this._request.headers.entries()) {
|
|
1140
|
-
result[key] = value;
|
|
1141
|
-
}
|
|
1142
|
-
return result;
|
|
1143
|
-
}
|
|
1144
|
-
async json() {
|
|
1145
|
-
if (this._cachedJsonPromise === null) {
|
|
1146
|
-
this._cachedJsonPromise = this._request.json();
|
|
1147
|
-
}
|
|
1148
|
-
return this._cachedJsonPromise;
|
|
1149
|
-
}
|
|
1150
|
-
async text() {
|
|
1151
|
-
if (this._cachedTextPromise === null) {
|
|
1152
|
-
this._cachedTextPromise = this._request.text();
|
|
1153
|
-
}
|
|
1154
|
-
return this._cachedTextPromise;
|
|
1155
|
-
}
|
|
1156
|
-
async formData() {
|
|
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;
|
|
1187
|
-
}
|
|
1188
|
-
get raw() {
|
|
1189
|
-
return this._request;
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
class MinimalContext {
|
|
1194
|
-
req;
|
|
1195
|
-
_resHeaders = {};
|
|
1196
|
-
_requestScope;
|
|
1197
|
-
constructor(request, params, path, routePattern) {
|
|
1198
|
-
this.req = new MinimalRequest(request, params, path, routePattern);
|
|
1199
|
-
this._requestScope = new RequestScopeManager;
|
|
1200
|
-
}
|
|
1201
|
-
getHeaders(contentType) {
|
|
1202
|
-
const headers = Object.assign({ "Content-Type": contentType }, this._resHeaders);
|
|
1203
|
-
return headers;
|
|
1204
|
-
}
|
|
1205
|
-
json(data, status = 200) {
|
|
1206
|
-
return new Response(JSON.stringify(data), {
|
|
1207
|
-
status,
|
|
1208
|
-
headers: this.getHeaders("application/json; charset=utf-8")
|
|
1209
|
-
});
|
|
1210
|
-
}
|
|
1211
|
-
text(text, status = 200) {
|
|
1212
|
-
return new Response(text, {
|
|
1213
|
-
status,
|
|
1214
|
-
headers: this.getHeaders("text/plain; charset=utf-8")
|
|
1215
|
-
});
|
|
1216
|
-
}
|
|
1217
|
-
html(html, status = 200) {
|
|
1218
|
-
return new Response(html, {
|
|
1219
|
-
status,
|
|
1220
|
-
headers: this.getHeaders("text/html; charset=utf-8")
|
|
1221
|
-
});
|
|
1222
|
-
}
|
|
1223
|
-
redirect(url, status = 302) {
|
|
1224
|
-
return new Response(null, {
|
|
1225
|
-
status,
|
|
1226
|
-
headers: { ...this._resHeaders, Location: url }
|
|
1227
|
-
});
|
|
1228
|
-
}
|
|
1229
|
-
body(data, status = 200) {
|
|
1230
|
-
return new Response(data, {
|
|
1231
|
-
status,
|
|
1232
|
-
headers: this._resHeaders
|
|
1233
|
-
});
|
|
1234
|
-
}
|
|
1235
|
-
header(name, value) {
|
|
1236
|
-
if (value !== undefined) {
|
|
1237
|
-
this._resHeaders[name] = value;
|
|
1238
|
-
return;
|
|
1239
|
-
}
|
|
1240
|
-
return this.req.header(name);
|
|
1241
|
-
}
|
|
1242
|
-
status(_code) {}
|
|
1243
|
-
stream(stream, status = 200) {
|
|
1244
|
-
return new Response(stream, {
|
|
1245
|
-
status,
|
|
1246
|
-
headers: this.getHeaders("application/octet-stream")
|
|
1247
|
-
});
|
|
1248
|
-
}
|
|
1249
|
-
notFound(message = "Not Found") {
|
|
1250
|
-
return this.text(message, 404);
|
|
1251
|
-
}
|
|
1252
|
-
forbidden(message = "Forbidden") {
|
|
1253
|
-
return this.text(message, 403);
|
|
1254
|
-
}
|
|
1255
|
-
unauthorized(message = "Unauthorized") {
|
|
1256
|
-
return this.text(message, 401);
|
|
1257
|
-
}
|
|
1258
|
-
badRequest(message = "Bad Request") {
|
|
1259
|
-
return this.text(message, 400);
|
|
1260
|
-
}
|
|
1261
|
-
async forward(target, _options = {}) {
|
|
1262
|
-
const url = new URL(this.req.url);
|
|
1263
|
-
const targetUrl = new URL(target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`);
|
|
1264
|
-
return fetch(targetUrl.toString(), {
|
|
1265
|
-
method: this.req.method,
|
|
1266
|
-
headers: this.req.raw.headers
|
|
1267
|
-
});
|
|
1268
|
-
}
|
|
1269
|
-
escape(html) {
|
|
1270
|
-
return bunEscapeHTML2(html);
|
|
1271
|
-
}
|
|
1272
|
-
get(_key) {
|
|
1273
|
-
return;
|
|
1274
|
-
}
|
|
1275
|
-
set(_key, _value) {}
|
|
1276
|
-
requestScope() {
|
|
1277
|
-
return this._requestScope;
|
|
1278
|
-
}
|
|
1279
|
-
scoped(key, factory) {
|
|
1280
|
-
return this._requestScope.resolve(key, factory);
|
|
1281
|
-
}
|
|
1282
|
-
route = () => "";
|
|
1283
|
-
get native() {
|
|
1284
|
-
return this;
|
|
1285
|
-
}
|
|
1286
|
-
init(_request, _params, _path) {
|
|
1287
|
-
throw new Error("MinimalContext does not support init. Create a new instance instead.");
|
|
1288
|
-
}
|
|
1289
|
-
reset() {}
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
// src/engine/path.ts
|
|
1293
|
-
function extractPath(url) {
|
|
1294
|
-
const protocolEnd = url.indexOf("://");
|
|
1295
|
-
const searchStart = protocolEnd === -1 ? 0 : protocolEnd + 3;
|
|
1296
|
-
const pathStart = url.indexOf("/", searchStart);
|
|
1297
|
-
if (pathStart === -1) {
|
|
1298
|
-
return "/";
|
|
1299
|
-
}
|
|
1300
|
-
const queryStart = url.indexOf("?", pathStart);
|
|
1301
|
-
if (queryStart === -1) {
|
|
1302
|
-
return url.slice(pathStart);
|
|
1303
|
-
}
|
|
1304
|
-
return url.slice(pathStart, queryStart);
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
// src/engine/pool.ts
|
|
1308
|
-
class ObjectPool {
|
|
1309
|
-
pool = [];
|
|
1310
|
-
factory;
|
|
1311
|
-
reset;
|
|
1312
|
-
maxSize;
|
|
1313
|
-
constructor(factory, reset, maxSize = 256) {
|
|
1314
|
-
this.factory = factory;
|
|
1315
|
-
this.reset = reset;
|
|
1316
|
-
this.maxSize = maxSize;
|
|
1317
|
-
}
|
|
1318
|
-
acquire() {
|
|
1319
|
-
const obj = this.pool.pop();
|
|
1320
|
-
if (obj !== undefined) {
|
|
1321
|
-
return obj;
|
|
1322
|
-
}
|
|
1323
|
-
return this.factory();
|
|
1324
|
-
}
|
|
1325
|
-
release(obj) {
|
|
1326
|
-
if (this.pool.length < this.maxSize) {
|
|
1327
|
-
this.reset(obj);
|
|
1328
|
-
this.pool.push(obj);
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
clear() {
|
|
1332
|
-
this.pool = [];
|
|
1333
|
-
}
|
|
1334
|
-
get size() {
|
|
1335
|
-
return this.pool.length;
|
|
1336
|
-
}
|
|
1337
|
-
get capacity() {
|
|
1338
|
-
return this.maxSize;
|
|
1339
|
-
}
|
|
1340
|
-
prewarm(count) {
|
|
1341
|
-
const targetSize = Math.min(count, this.maxSize);
|
|
1342
|
-
while (this.pool.length < targetSize) {
|
|
1343
|
-
const obj = this.factory();
|
|
1344
|
-
this.reset(obj);
|
|
1345
|
-
this.pool.push(obj);
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
// src/engine/Gravito.ts
|
|
1351
|
-
var bunPeek = globalThis.Bun.peek;
|
|
1352
|
-
function compileMiddlewareChain(middleware, handler) {
|
|
1353
|
-
if (middleware.length === 0) {
|
|
1354
|
-
return handler;
|
|
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
|
-
}
|
|
1385
|
-
let compiled = handler;
|
|
1386
|
-
for (let i = middleware.length - 1;i >= 0; i--) {
|
|
1387
|
-
const mw = middleware[i];
|
|
1388
|
-
const nextHandler = compiled;
|
|
1389
|
-
compiled = async (ctx) => {
|
|
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
|
-
}
|
|
1411
|
-
return nextResult;
|
|
1412
|
-
}
|
|
1413
|
-
return ctx.json({ error: "Middleware did not call next or return response" }, 500);
|
|
1414
|
-
};
|
|
1415
|
-
}
|
|
1416
|
-
return compiled;
|
|
1417
|
-
}
|
|
1418
|
-
|
|
1419
|
-
class Gravito {
|
|
1420
|
-
router = new AOTRouter;
|
|
1421
|
-
contextPool;
|
|
1422
|
-
errorHandler;
|
|
1423
|
-
notFoundHandler;
|
|
1424
|
-
staticRoutes;
|
|
1425
|
-
isPureStaticApp = true;
|
|
1426
|
-
compiledDynamicRoutes = new Map;
|
|
1427
|
-
constructor(options = {}) {
|
|
1428
|
-
const poolSize = options.poolSize ?? 256;
|
|
1429
|
-
this.contextPool = new ObjectPool(() => new FastContext, (ctx) => ctx.reset(), poolSize);
|
|
1430
|
-
this.contextPool.prewarm(Math.min(32, poolSize));
|
|
1431
|
-
if (options.onError) {
|
|
1432
|
-
this.errorHandler = options.onError;
|
|
1433
|
-
}
|
|
1434
|
-
if (options.onNotFound) {
|
|
1435
|
-
this.notFoundHandler = options.onNotFound;
|
|
1436
|
-
}
|
|
1437
|
-
this.compileRoutes();
|
|
1438
|
-
}
|
|
1439
|
-
get(path, ...handlers) {
|
|
1440
|
-
return this.addRoute("get", path, handlers);
|
|
1441
|
-
}
|
|
1442
|
-
post(path, ...handlers) {
|
|
1443
|
-
return this.addRoute("post", path, handlers);
|
|
1444
|
-
}
|
|
1445
|
-
put(path, ...handlers) {
|
|
1446
|
-
return this.addRoute("put", path, handlers);
|
|
1447
|
-
}
|
|
1448
|
-
delete(path, ...handlers) {
|
|
1449
|
-
return this.addRoute("delete", path, handlers);
|
|
1450
|
-
}
|
|
1451
|
-
patch(path, ...handlers) {
|
|
1452
|
-
return this.addRoute("patch", path, handlers);
|
|
1453
|
-
}
|
|
1454
|
-
options(path, ...handlers) {
|
|
1455
|
-
return this.addRoute("options", path, handlers);
|
|
1456
|
-
}
|
|
1457
|
-
head(path, ...handlers) {
|
|
1458
|
-
return this.addRoute("head", path, handlers);
|
|
1459
|
-
}
|
|
1460
|
-
all(path, ...handlers) {
|
|
1461
|
-
const methods = ["get", "post", "put", "delete", "patch", "options", "head"];
|
|
1462
|
-
for (const method of methods) {
|
|
1463
|
-
this.addRoute(method, path, handlers);
|
|
1464
|
-
}
|
|
1465
|
-
return this;
|
|
1466
|
-
}
|
|
1467
|
-
use(pathOrMiddleware, ...middleware) {
|
|
1468
|
-
this.isPureStaticApp = false;
|
|
1469
|
-
if (typeof pathOrMiddleware === "string") {
|
|
1470
|
-
this.router.usePattern(pathOrMiddleware, ...middleware);
|
|
1471
|
-
} else {
|
|
1472
|
-
this.router.use(pathOrMiddleware, ...middleware);
|
|
1473
|
-
}
|
|
1474
|
-
this.compileRoutes();
|
|
1475
|
-
return this;
|
|
1476
|
-
}
|
|
1477
|
-
route(path, app) {
|
|
1478
|
-
this.router.mount(path, app.router);
|
|
1479
|
-
this.compileRoutes();
|
|
1480
|
-
return this;
|
|
1481
|
-
}
|
|
1482
|
-
onError(handler) {
|
|
1483
|
-
this.errorHandler = handler;
|
|
1484
|
-
return this;
|
|
1485
|
-
}
|
|
1486
|
-
notFound(handler) {
|
|
1487
|
-
this.notFoundHandler = handler;
|
|
1488
|
-
return this;
|
|
1489
|
-
}
|
|
1490
|
-
async warmup(paths) {
|
|
1491
|
-
const dummyReqOpts = { headers: { "User-Agent": "Gravito-Warmup/1.0" } };
|
|
1492
|
-
for (const path of paths) {
|
|
1493
|
-
const req = new Request(`http://localhost${path}`, dummyReqOpts);
|
|
1494
|
-
await this.fetch(req);
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
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
|
-
}
|
|
1557
|
-
fetch = async (request) => {
|
|
1558
|
-
const path = extractPath(request.url);
|
|
1559
|
-
const method = request.method.toLowerCase();
|
|
1560
|
-
const staticKey = `${method}:${path}`;
|
|
1561
|
-
const staticRoute = this.staticRoutes.get(staticKey);
|
|
1562
|
-
if (staticRoute) {
|
|
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
|
-
}
|
|
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);
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
1588
|
-
return await this.handleDynamicRoute(request, method, path);
|
|
1589
|
-
};
|
|
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);
|
|
1595
|
-
try {
|
|
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;
|
|
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);
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
return this.handleNotFoundSync(request, path);
|
|
1625
|
-
}
|
|
1626
|
-
handleErrorSync(error, request, path) {
|
|
1627
|
-
if (this.errorHandler) {
|
|
1628
|
-
const ctx = new MinimalContext(request, {}, path);
|
|
1629
|
-
const result = this.errorHandler(error, ctx);
|
|
1630
|
-
if (result instanceof Response) {
|
|
1631
|
-
return result;
|
|
1632
|
-
}
|
|
1633
|
-
return result;
|
|
1634
|
-
}
|
|
1635
|
-
console.error("Unhandled error:", error);
|
|
1636
|
-
return new Response(CACHED_RESPONSES.INTERNAL_ERROR, {
|
|
1637
|
-
status: 500,
|
|
1638
|
-
headers: HEADERS.JSON
|
|
1639
|
-
});
|
|
1640
|
-
}
|
|
1641
|
-
handleNotFoundSync(request, path) {
|
|
1642
|
-
if (this.notFoundHandler) {
|
|
1643
|
-
const ctx = new MinimalContext(request, {}, path);
|
|
1644
|
-
const result = this.notFoundHandler(ctx);
|
|
1645
|
-
if (result instanceof Response) {
|
|
1646
|
-
return result;
|
|
1647
|
-
}
|
|
1648
|
-
return result;
|
|
1649
|
-
}
|
|
1650
|
-
return new Response(CACHED_RESPONSES.NOT_FOUND, {
|
|
1651
|
-
status: 404,
|
|
1652
|
-
headers: HEADERS.JSON
|
|
1653
|
-
});
|
|
1654
|
-
}
|
|
1655
|
-
collectMiddlewareForPath(path, routeMiddleware) {
|
|
1656
|
-
if (this.router.globalMiddleware.length === 0 && this.router.pathMiddleware.size === 0) {
|
|
1657
|
-
return routeMiddleware;
|
|
1658
|
-
}
|
|
1659
|
-
return this.router.collectMiddlewarePublic(path, routeMiddleware);
|
|
1660
|
-
}
|
|
1661
|
-
compileRoutes() {
|
|
1662
|
-
this.staticRoutes = this.router.staticRoutes;
|
|
1663
|
-
const hasGlobalMiddleware = this.router.globalMiddleware.length > 0;
|
|
1664
|
-
const hasPathMiddleware = this.router.pathMiddleware.size > 0;
|
|
1665
|
-
this.isPureStaticApp = !hasGlobalMiddleware && !hasPathMiddleware;
|
|
1666
|
-
for (const [key, route] of this.staticRoutes) {
|
|
1667
|
-
if (route.compiledVersion === this.router.version) {
|
|
1668
|
-
continue;
|
|
1669
|
-
}
|
|
1670
|
-
const analysis = analyzeHandler(route.handler);
|
|
1671
|
-
const optimalType = getOptimalContextType(analysis);
|
|
1672
|
-
route.useMinimal = this.isPureStaticApp && route.middleware.length === 0 && optimalType === "minimal";
|
|
1673
|
-
if (!route.useMinimal) {
|
|
1674
|
-
const allMiddleware = this.collectMiddlewareForPath(key.split(":")[1], route.middleware);
|
|
1675
|
-
route.compiled = compileMiddlewareChain(allMiddleware, route.handler);
|
|
1676
|
-
}
|
|
1677
|
-
route.compiledVersion = this.router.version;
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
addRoute(method, path, handlers) {
|
|
1681
|
-
if (handlers.length === 0) {
|
|
1682
|
-
throw new Error(`No handler provided for ${method.toUpperCase()} ${path}`);
|
|
1683
|
-
}
|
|
1684
|
-
const handler = handlers[handlers.length - 1];
|
|
1685
|
-
const middleware = handlers.slice(0, -1);
|
|
1686
|
-
this.router.add(method, path, handler, middleware);
|
|
1687
|
-
this.compileRoutes();
|
|
1688
|
-
return this;
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
})
|
|
1692
|
-
|
|
1693
|
-
//# debugId=46FB5CAED852799864756E2164756E21
|