@gravito/core 1.0.0 → 1.2.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/dist/index.cjs CHANGED
@@ -54,6 +54,7 @@ __export(index_exports, {
54
54
  PhotonRequestWrapper: () => PhotonRequestWrapper,
55
55
  PlanetCore: () => PlanetCore,
56
56
  Route: () => Route,
57
+ RouteGroup: () => RouteGroup,
57
58
  Router: () => Router,
58
59
  ServiceProvider: () => ServiceProvider,
59
60
  Str: () => Str,
@@ -82,6 +83,7 @@ __export(index_exports, {
82
83
  dd: () => dd,
83
84
  defineConfig: () => defineConfig,
84
85
  dump: () => dump,
86
+ engine: () => engine_exports,
85
87
  env: () => env,
86
88
  errors: () => errors,
87
89
  fail: () => fail,
@@ -112,7 +114,7 @@ module.exports = __toCommonJS(index_exports);
112
114
  // package.json
113
115
  var package_default = {
114
116
  name: "@gravito/core",
115
- version: "1.0.0",
117
+ version: "1.2.0",
116
118
  description: "",
117
119
  module: "./dist/index.js",
118
120
  main: "./dist/index.cjs",
@@ -128,6 +130,11 @@ var package_default = {
128
130
  types: "./dist/compat.d.ts",
129
131
  import: "./dist/compat.js",
130
132
  require: "./dist/compat.cjs"
133
+ },
134
+ "./engine": {
135
+ types: "./dist/engine/index.d.ts",
136
+ import: "./dist/engine/index.js",
137
+ require: "./dist/engine/index.cjs"
131
138
  }
132
139
  },
133
140
  files: [
@@ -179,11 +186,45 @@ var package_default = {
179
186
  };
180
187
 
181
188
  // src/adapters/PhotonAdapter.ts
182
- var PhotonRequestWrapper = class {
189
+ var PhotonRequestWrapper = class _PhotonRequestWrapper {
183
190
  constructor(photonCtx) {
184
191
  this.photonCtx = photonCtx;
185
192
  }
186
- _cachedJson = null;
193
+ /**
194
+ * Create a proxied instance to delegate to Photon's request
195
+ */
196
+ static create(photonCtx) {
197
+ const instance = new _PhotonRequestWrapper(photonCtx);
198
+ return new Proxy(instance, {
199
+ get(target, prop, receiver) {
200
+ if (prop in target) {
201
+ const value2 = Reflect.get(target, prop, receiver);
202
+ if (typeof value2 === "function") {
203
+ return value2.bind(target);
204
+ }
205
+ return value2;
206
+ }
207
+ const nativeReq = target.photonCtx.req;
208
+ if (prop in nativeReq) {
209
+ const value2 = nativeReq[prop];
210
+ if (typeof value2 === "function") {
211
+ return value2.bind(nativeReq);
212
+ }
213
+ return value2;
214
+ }
215
+ return void 0;
216
+ },
217
+ // Allow setting properties (for addValidated etc.)
218
+ set(target, prop, value2) {
219
+ if (prop in target) {
220
+ return Reflect.set(target, prop, value2);
221
+ }
222
+ ;
223
+ target.photonCtx.req[prop] = value2;
224
+ return true;
225
+ }
226
+ });
227
+ }
187
228
  get url() {
188
229
  return this.photonCtx.req.url;
189
230
  }
@@ -242,7 +283,7 @@ var PhotonRequestWrapper = class {
242
283
  var PhotonContextWrapper = class _PhotonContextWrapper {
243
284
  constructor(photonCtx) {
244
285
  this.photonCtx = photonCtx;
245
- this._req = new PhotonRequestWrapper(photonCtx);
286
+ this._req = PhotonRequestWrapper.create(photonCtx);
246
287
  }
247
288
  _req;
248
289
  /**
@@ -354,7 +395,7 @@ function toPhotonMiddleware(middleware) {
354
395
  return async (c, next) => {
355
396
  const ctx = PhotonContextWrapper.create(c);
356
397
  const gravitoNext = async () => {
357
- await next();
398
+ return await next();
358
399
  };
359
400
  return middleware(ctx, gravitoNext);
360
401
  };
@@ -2818,20 +2859,20 @@ var RouteGroup = class _RouteGroup {
2818
2859
  group(callback) {
2819
2860
  callback(this);
2820
2861
  }
2821
- get(path2, requestOrHandler, handler) {
2822
- return this.router.req("get", path2, requestOrHandler, handler, this.options);
2862
+ get(path2, requestOrHandlerOrMiddleware, handler) {
2863
+ return this.router.req("get", path2, requestOrHandlerOrMiddleware, handler, this.options);
2823
2864
  }
2824
- post(path2, requestOrHandler, handler) {
2825
- return this.router.req("post", path2, requestOrHandler, handler, this.options);
2865
+ post(path2, requestOrHandlerOrMiddleware, handler) {
2866
+ return this.router.req("post", path2, requestOrHandlerOrMiddleware, handler, this.options);
2826
2867
  }
2827
- put(path2, requestOrHandler, handler) {
2828
- return this.router.req("put", path2, requestOrHandler, handler, this.options);
2868
+ put(path2, requestOrHandlerOrMiddleware, handler) {
2869
+ return this.router.req("put", path2, requestOrHandlerOrMiddleware, handler, this.options);
2829
2870
  }
2830
- delete(path2, requestOrHandler, handler) {
2831
- return this.router.req("delete", path2, requestOrHandler, handler, this.options);
2871
+ delete(path2, requestOrHandlerOrMiddleware, handler) {
2872
+ return this.router.req("delete", path2, requestOrHandlerOrMiddleware, handler, this.options);
2832
2873
  }
2833
- patch(path2, requestOrHandler, handler) {
2834
- return this.router.req("patch", path2, requestOrHandler, handler, this.options);
2874
+ patch(path2, requestOrHandlerOrMiddleware, handler) {
2875
+ return this.router.req("patch", path2, requestOrHandlerOrMiddleware, handler, this.options);
2835
2876
  }
2836
2877
  resource(name, controller, options = {}) {
2837
2878
  const actions = [
@@ -3007,20 +3048,20 @@ var Router = class {
3007
3048
  middleware(...handlers) {
3008
3049
  return new RouteGroup(this, { middleware: handlers.flat() });
3009
3050
  }
3010
- get(path2, requestOrHandler, handler) {
3011
- return this.req("get", path2, requestOrHandler, handler);
3051
+ get(path2, requestOrHandlerOrMiddleware, handler) {
3052
+ return this.req("get", path2, requestOrHandlerOrMiddleware, handler);
3012
3053
  }
3013
- post(path2, requestOrHandler, handler) {
3014
- return this.req("post", path2, requestOrHandler, handler);
3054
+ post(path2, requestOrHandlerOrMiddleware, handler) {
3055
+ return this.req("post", path2, requestOrHandlerOrMiddleware, handler);
3015
3056
  }
3016
- put(path2, requestOrHandler, handler) {
3017
- return this.req("put", path2, requestOrHandler, handler);
3057
+ put(path2, requestOrHandlerOrMiddleware, handler) {
3058
+ return this.req("put", path2, requestOrHandlerOrMiddleware, handler);
3018
3059
  }
3019
- delete(path2, requestOrHandler, handler) {
3020
- return this.req("delete", path2, requestOrHandler, handler);
3060
+ delete(path2, requestOrHandlerOrMiddleware, handler) {
3061
+ return this.req("delete", path2, requestOrHandlerOrMiddleware, handler);
3021
3062
  }
3022
- patch(path2, requestOrHandler, handler) {
3023
- return this.req("patch", path2, requestOrHandler, handler);
3063
+ patch(path2, requestOrHandlerOrMiddleware, handler) {
3064
+ return this.req("patch", path2, requestOrHandlerOrMiddleware, handler);
3024
3065
  }
3025
3066
  /**
3026
3067
  * Register a resource route (Laravel-style).
@@ -3066,17 +3107,25 @@ var Router = class {
3066
3107
  /**
3067
3108
  * Internal Request Registration
3068
3109
  */
3069
- req(method, path2, requestOrHandler, handler, options = {}) {
3110
+ req(method, path2, requestOrHandlerOrMiddleware, handler, options = {}) {
3070
3111
  const fullPath = (options.prefix || "") + path2;
3071
3112
  let formRequestMiddleware = null;
3113
+ let routeMiddleware = [];
3072
3114
  let finalRouteHandler;
3073
3115
  if (handler !== void 0) {
3074
- if (isFormRequestClass(requestOrHandler)) {
3075
- formRequestMiddleware = formRequestToMiddleware(requestOrHandler);
3116
+ if (isFormRequestClass(requestOrHandlerOrMiddleware)) {
3117
+ formRequestMiddleware = formRequestToMiddleware(requestOrHandlerOrMiddleware);
3118
+ } else {
3119
+ const middleware = requestOrHandlerOrMiddleware;
3120
+ if (Array.isArray(middleware)) {
3121
+ routeMiddleware = middleware;
3122
+ } else {
3123
+ routeMiddleware = [middleware];
3124
+ }
3076
3125
  }
3077
3126
  finalRouteHandler = handler;
3078
3127
  } else {
3079
- finalRouteHandler = requestOrHandler;
3128
+ finalRouteHandler = requestOrHandlerOrMiddleware;
3080
3129
  }
3081
3130
  let resolvedHandler;
3082
3131
  if (Array.isArray(finalRouteHandler)) {
@@ -3092,6 +3141,9 @@ var Router = class {
3092
3141
  if (formRequestMiddleware) {
3093
3142
  handlers.push(formRequestMiddleware);
3094
3143
  }
3144
+ if (routeMiddleware.length > 0) {
3145
+ handlers.push(...routeMiddleware);
3146
+ }
3095
3147
  handlers.push(resolvedHandler);
3096
3148
  if (options.domain) {
3097
3149
  const domainCheck = async (c, next) => {
@@ -3366,8 +3418,7 @@ var PlanetCore = class _PlanetCore {
3366
3418
  const cookieJar = new CookieJar(this.encrypter);
3367
3419
  c.set("cookieJar", cookieJar);
3368
3420
  c.route = (name, params, query) => this.router.url(name, params, query);
3369
- await next();
3370
- return void 0;
3421
+ return await next();
3371
3422
  });
3372
3423
  this.router = new Router(this);
3373
3424
  this.adapter.onError(async (err, c) => {
@@ -4709,6 +4760,947 @@ function createHttpTester(core) {
4709
4760
  return new HttpTester(core);
4710
4761
  }
4711
4762
 
4763
+ // src/engine/index.ts
4764
+ var engine_exports = {};
4765
+ __export(engine_exports, {
4766
+ AOTRouter: () => AOTRouter,
4767
+ FastContextImpl: () => FastContext,
4768
+ Gravito: () => Gravito,
4769
+ MinimalContext: () => MinimalContext,
4770
+ ObjectPool: () => ObjectPool,
4771
+ extractPath: () => extractPath
4772
+ });
4773
+
4774
+ // src/engine/AOTRouter.ts
4775
+ var AOTRouter = class {
4776
+ // Static route cache: "METHOD:PATH" -> RouteMetadata
4777
+ staticRoutes = /* @__PURE__ */ new Map();
4778
+ // Dynamic route handler (Radix Tree)
4779
+ dynamicRouter = new RadixRouter();
4780
+ // Global middleware (applies to all routes)
4781
+ globalMiddleware = [];
4782
+ // Path-based middleware: pattern -> middleware[]
4783
+ pathMiddleware = /* @__PURE__ */ new Map();
4784
+ /**
4785
+ * Register a route
4786
+ *
4787
+ * Automatically determines if route is static or dynamic.
4788
+ * Static routes are stored in a Map for O(1) lookup.
4789
+ * Dynamic routes use the Radix Tree.
4790
+ *
4791
+ * @param method - HTTP method
4792
+ * @param path - Route path
4793
+ * @param handler - Route handler
4794
+ * @param middleware - Route-specific middleware
4795
+ */
4796
+ add(method, path2, handler, middleware = []) {
4797
+ const normalizedMethod = method.toLowerCase();
4798
+ if (this.isStaticPath(path2)) {
4799
+ const key = `${normalizedMethod}:${path2}`;
4800
+ this.staticRoutes.set(key, { handler, middleware });
4801
+ } else {
4802
+ const wrappedHandler = handler;
4803
+ this.dynamicRouter.add(normalizedMethod, path2, [wrappedHandler]);
4804
+ if (middleware.length > 0) {
4805
+ this.pathMiddleware.set(`${normalizedMethod}:${path2}`, middleware);
4806
+ }
4807
+ }
4808
+ }
4809
+ /**
4810
+ * Add global middleware
4811
+ *
4812
+ * These run for every request, before route-specific middleware.
4813
+ *
4814
+ * @param middleware - Middleware functions
4815
+ */
4816
+ use(...middleware) {
4817
+ this.globalMiddleware.push(...middleware);
4818
+ }
4819
+ /**
4820
+ * Add path-based middleware
4821
+ *
4822
+ * Supports wildcard patterns like '/api/*'
4823
+ *
4824
+ * @param pattern - Path pattern
4825
+ * @param middleware - Middleware functions
4826
+ */
4827
+ usePattern(pattern, ...middleware) {
4828
+ const existing = this.pathMiddleware.get(pattern) ?? [];
4829
+ this.pathMiddleware.set(pattern, [...existing, ...middleware]);
4830
+ }
4831
+ /**
4832
+ * Match a request to a route
4833
+ *
4834
+ * Returns the handler, params, and all applicable middleware.
4835
+ *
4836
+ * @param method - HTTP method
4837
+ * @param path - Request path
4838
+ * @returns Route match or null if not found
4839
+ */
4840
+ match(method, path2) {
4841
+ const normalizedMethod = method.toLowerCase();
4842
+ const staticKey = `${normalizedMethod}:${path2}`;
4843
+ const staticRoute = this.staticRoutes.get(staticKey);
4844
+ if (staticRoute) {
4845
+ return {
4846
+ handler: staticRoute.handler,
4847
+ params: {},
4848
+ middleware: this.collectMiddleware(path2, staticRoute.middleware)
4849
+ };
4850
+ }
4851
+ const match = this.dynamicRouter.match(normalizedMethod, path2);
4852
+ if (match && match.handlers.length > 0) {
4853
+ const handler = match.handlers[0];
4854
+ const routeKey = this.findDynamicRouteKey(normalizedMethod, path2);
4855
+ const routeMiddleware = routeKey ? this.pathMiddleware.get(routeKey) ?? [] : [];
4856
+ return {
4857
+ handler,
4858
+ params: match.params,
4859
+ middleware: this.collectMiddleware(path2, routeMiddleware)
4860
+ };
4861
+ }
4862
+ return {
4863
+ handler: null,
4864
+ params: {},
4865
+ middleware: []
4866
+ };
4867
+ }
4868
+ /**
4869
+ * Public wrapper for collectMiddleware (used by Gravito for optimization)
4870
+ */
4871
+ collectMiddlewarePublic(path2, routeMiddleware) {
4872
+ return this.collectMiddleware(path2, routeMiddleware);
4873
+ }
4874
+ /**
4875
+ * Collect all applicable middleware for a path
4876
+ *
4877
+ * Order: global -> pattern-based -> route-specific
4878
+ *
4879
+ * @param path - Request path
4880
+ * @param routeMiddleware - Route-specific middleware
4881
+ * @returns Combined middleware array
4882
+ */
4883
+ collectMiddleware(path2, routeMiddleware) {
4884
+ if (this.globalMiddleware.length === 0 && this.pathMiddleware.size === 0 && routeMiddleware.length === 0) {
4885
+ return [];
4886
+ }
4887
+ const middleware = [];
4888
+ if (this.globalMiddleware.length > 0) {
4889
+ middleware.push(...this.globalMiddleware);
4890
+ }
4891
+ if (this.pathMiddleware.size > 0) {
4892
+ for (const [pattern, mw] of this.pathMiddleware) {
4893
+ if (pattern.includes(":")) continue;
4894
+ if (this.matchPattern(pattern, path2)) {
4895
+ middleware.push(...mw);
4896
+ }
4897
+ }
4898
+ }
4899
+ if (routeMiddleware.length > 0) {
4900
+ middleware.push(...routeMiddleware);
4901
+ }
4902
+ return middleware;
4903
+ }
4904
+ /**
4905
+ * Check if a path is static (no parameters or wildcards)
4906
+ */
4907
+ isStaticPath(path2) {
4908
+ return !path2.includes(":") && !path2.includes("*");
4909
+ }
4910
+ /**
4911
+ * Match a pattern against a path
4912
+ *
4913
+ * Supports:
4914
+ * - Exact match: '/api/users'
4915
+ * - Wildcard suffix: '/api/*'
4916
+ *
4917
+ * @param pattern - Pattern to match
4918
+ * @param path - Path to test
4919
+ * @returns True if pattern matches
4920
+ */
4921
+ matchPattern(pattern, path2) {
4922
+ if (pattern === "*") return true;
4923
+ if (pattern === path2) return true;
4924
+ if (pattern.endsWith("/*")) {
4925
+ const prefix = pattern.slice(0, -2);
4926
+ return path2.startsWith(prefix);
4927
+ }
4928
+ return false;
4929
+ }
4930
+ /**
4931
+ * Find the original route key for a matched dynamic route
4932
+ *
4933
+ * This is needed to look up route-specific middleware.
4934
+ * It's a bit of a hack, but avoids storing duplicate data.
4935
+ *
4936
+ * @param method - HTTP method
4937
+ * @param path - Matched path
4938
+ * @returns Route key or null
4939
+ */
4940
+ findDynamicRouteKey(method, _path) {
4941
+ for (const key of this.pathMiddleware.keys()) {
4942
+ if (key.startsWith(`${method}:`)) {
4943
+ return key;
4944
+ }
4945
+ }
4946
+ return null;
4947
+ }
4948
+ /**
4949
+ * Get all registered routes (for debugging)
4950
+ */
4951
+ getRoutes() {
4952
+ const routes = [];
4953
+ for (const key of this.staticRoutes.keys()) {
4954
+ const [method, path2] = key.split(":");
4955
+ routes.push({ method, path: path2, type: "static" });
4956
+ }
4957
+ return routes;
4958
+ }
4959
+ };
4960
+
4961
+ // src/engine/analyzer.ts
4962
+ function analyzeHandler(handler) {
4963
+ const source = handler.toString();
4964
+ return {
4965
+ usesHeaders: source.includes(".header(") || source.includes(".header)") || source.includes(".headers(") || source.includes(".headers)"),
4966
+ usesQuery: source.includes(".query(") || source.includes(".query)") || source.includes(".queries(") || source.includes(".queries)"),
4967
+ usesBody: source.includes(".json()") || source.includes(".text()") || source.includes(".formData()") || source.includes(".body"),
4968
+ usesParams: source.includes(".param(") || source.includes(".param)") || source.includes(".params(") || source.includes(".params)"),
4969
+ isAsync: source.includes("async") || source.includes("await")
4970
+ };
4971
+ }
4972
+ function getOptimalContextType(analysis) {
4973
+ if (analysis.usesHeaders) {
4974
+ return "fast";
4975
+ }
4976
+ if (!analysis.usesQuery && !analysis.usesBody && !analysis.usesParams) {
4977
+ return "minimal";
4978
+ }
4979
+ if (!analysis.usesQuery && !analysis.usesBody && analysis.usesParams) {
4980
+ return "minimal";
4981
+ }
4982
+ if (analysis.usesBody) {
4983
+ return "full";
4984
+ }
4985
+ return "fast";
4986
+ }
4987
+
4988
+ // src/engine/FastContext.ts
4989
+ var FastRequestImpl = class {
4990
+ _request;
4991
+ _params;
4992
+ _url = new URL("http://localhost");
4993
+ // Reuse this object
4994
+ _query = null;
4995
+ _headers = null;
4996
+ /**
4997
+ * Reset for pooling
4998
+ */
4999
+ reset(request, params = {}) {
5000
+ this._request = request;
5001
+ this._params = params;
5002
+ this._url.href = request.url;
5003
+ this._query = null;
5004
+ this._headers = null;
5005
+ }
5006
+ get url() {
5007
+ return this._request.url;
5008
+ }
5009
+ get method() {
5010
+ return this._request.method;
5011
+ }
5012
+ get path() {
5013
+ return this._url.pathname;
5014
+ }
5015
+ param(name) {
5016
+ return this._params[name];
5017
+ }
5018
+ params() {
5019
+ return { ...this._params };
5020
+ }
5021
+ query(name) {
5022
+ if (!this._query) {
5023
+ this._query = this._url.searchParams;
5024
+ }
5025
+ return this._query.get(name) ?? void 0;
5026
+ }
5027
+ queries() {
5028
+ if (!this._query) {
5029
+ this._query = this._url.searchParams;
5030
+ }
5031
+ const result = {};
5032
+ for (const [key, value2] of this._query.entries()) {
5033
+ const existing = result[key];
5034
+ if (existing === void 0) {
5035
+ result[key] = value2;
5036
+ } else if (Array.isArray(existing)) {
5037
+ existing.push(value2);
5038
+ } else {
5039
+ result[key] = [existing, value2];
5040
+ }
5041
+ }
5042
+ return result;
5043
+ }
5044
+ header(name) {
5045
+ return this._request.headers.get(name) ?? void 0;
5046
+ }
5047
+ headers() {
5048
+ if (!this._headers) {
5049
+ this._headers = {};
5050
+ for (const [key, value2] of this._request.headers.entries()) {
5051
+ this._headers[key] = value2;
5052
+ }
5053
+ }
5054
+ return { ...this._headers };
5055
+ }
5056
+ async json() {
5057
+ return this._request.json();
5058
+ }
5059
+ async text() {
5060
+ return this._request.text();
5061
+ }
5062
+ async formData() {
5063
+ return this._request.formData();
5064
+ }
5065
+ get raw() {
5066
+ return this._request;
5067
+ }
5068
+ };
5069
+ var FastContext = class {
5070
+ _req = new FastRequestImpl();
5071
+ // private _statusCode = 200
5072
+ _headers = new Headers();
5073
+ // Reuse this object
5074
+ /**
5075
+ * Reset context for pooling
5076
+ *
5077
+ * This is called when acquiring from the pool.
5078
+ * Must clear all state from previous request.
5079
+ */
5080
+ reset(request, params = {}) {
5081
+ this._req.reset(request, params);
5082
+ this._headers = new Headers();
5083
+ return this;
5084
+ }
5085
+ get req() {
5086
+ return this._req;
5087
+ }
5088
+ // ─────────────────────────────────────────────────────────────────────────
5089
+ // Response Helpers
5090
+ // ─────────────────────────────────────────────────────────────────────────
5091
+ json(data, status = 200) {
5092
+ this._headers.set("Content-Type", "application/json; charset=utf-8");
5093
+ return new Response(JSON.stringify(data), {
5094
+ status,
5095
+ headers: this._headers
5096
+ });
5097
+ }
5098
+ text(text, status = 200) {
5099
+ this._headers.set("Content-Type", "text/plain; charset=utf-8");
5100
+ return new Response(text, {
5101
+ status,
5102
+ headers: this._headers
5103
+ });
5104
+ }
5105
+ html(html, status = 200) {
5106
+ this._headers.set("Content-Type", "text/html; charset=utf-8");
5107
+ return new Response(html, {
5108
+ status,
5109
+ headers: this._headers
5110
+ });
5111
+ }
5112
+ redirect(url, status = 302) {
5113
+ this._headers.set("Location", url);
5114
+ return new Response(null, {
5115
+ status,
5116
+ headers: this._headers
5117
+ });
5118
+ }
5119
+ body(data, status = 200) {
5120
+ return new Response(data, {
5121
+ status,
5122
+ headers: this._headers
5123
+ });
5124
+ }
5125
+ // ─────────────────────────────────────────────────────────────────────────
5126
+ // Header Management
5127
+ // ─────────────────────────────────────────────────────────────────────────
5128
+ header(name, value2) {
5129
+ this._headers.set(name, value2);
5130
+ }
5131
+ status(_code) {
5132
+ }
5133
+ };
5134
+
5135
+ // src/engine/MinimalContext.ts
5136
+ var MinimalRequest = class {
5137
+ constructor(_request, _params, _path) {
5138
+ this._request = _request;
5139
+ this._params = _params;
5140
+ this._path = _path;
5141
+ }
5142
+ get url() {
5143
+ return this._request.url;
5144
+ }
5145
+ get method() {
5146
+ return this._request.method;
5147
+ }
5148
+ get path() {
5149
+ return this._path;
5150
+ }
5151
+ param(name) {
5152
+ return this._params[name];
5153
+ }
5154
+ params() {
5155
+ return { ...this._params };
5156
+ }
5157
+ query(name) {
5158
+ const url = new URL(this._request.url);
5159
+ return url.searchParams.get(name) ?? void 0;
5160
+ }
5161
+ queries() {
5162
+ const url = new URL(this._request.url);
5163
+ const result = {};
5164
+ for (const [key, value2] of url.searchParams.entries()) {
5165
+ const existing = result[key];
5166
+ if (existing === void 0) {
5167
+ result[key] = value2;
5168
+ } else if (Array.isArray(existing)) {
5169
+ existing.push(value2);
5170
+ } else {
5171
+ result[key] = [existing, value2];
5172
+ }
5173
+ }
5174
+ return result;
5175
+ }
5176
+ header(name) {
5177
+ return this._request.headers.get(name) ?? void 0;
5178
+ }
5179
+ headers() {
5180
+ const result = {};
5181
+ for (const [key, value2] of this._request.headers.entries()) {
5182
+ result[key] = value2;
5183
+ }
5184
+ return result;
5185
+ }
5186
+ async json() {
5187
+ return this._request.json();
5188
+ }
5189
+ async text() {
5190
+ return this._request.text();
5191
+ }
5192
+ async formData() {
5193
+ return this._request.formData();
5194
+ }
5195
+ get raw() {
5196
+ return this._request;
5197
+ }
5198
+ };
5199
+ var MinimalContext = class {
5200
+ _req;
5201
+ constructor(request, params, path2) {
5202
+ this._req = new MinimalRequest(request, params, path2);
5203
+ }
5204
+ get req() {
5205
+ return this._req;
5206
+ }
5207
+ // Response helpers - create headers inline (no reuse overhead)
5208
+ json(data, status = 200) {
5209
+ return new Response(JSON.stringify(data), {
5210
+ status,
5211
+ headers: { "Content-Type": "application/json; charset=utf-8" }
5212
+ });
5213
+ }
5214
+ text(text, status = 200) {
5215
+ return new Response(text, {
5216
+ status,
5217
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
5218
+ });
5219
+ }
5220
+ html(html, status = 200) {
5221
+ return new Response(html, {
5222
+ status,
5223
+ headers: { "Content-Type": "text/html; charset=utf-8" }
5224
+ });
5225
+ }
5226
+ redirect(url, status = 302) {
5227
+ return new Response(null, {
5228
+ status,
5229
+ headers: { Location: url }
5230
+ });
5231
+ }
5232
+ body(data, status = 200) {
5233
+ return new Response(data, { status });
5234
+ }
5235
+ header(_name, _value) {
5236
+ console.warn("MinimalContext.header() is a no-op. Use FastContext for custom headers.");
5237
+ }
5238
+ status(_code) {
5239
+ }
5240
+ // Required for interface compatibility
5241
+ reset(_request, _params) {
5242
+ throw new Error("MinimalContext does not support reset. Create a new instance instead.");
5243
+ }
5244
+ };
5245
+
5246
+ // src/engine/path.ts
5247
+ function extractPath(url) {
5248
+ const protocolEnd = url.indexOf("://");
5249
+ const searchStart = protocolEnd === -1 ? 0 : protocolEnd + 3;
5250
+ const pathStart = url.indexOf("/", searchStart);
5251
+ if (pathStart === -1) {
5252
+ return "/";
5253
+ }
5254
+ const queryStart = url.indexOf("?", pathStart);
5255
+ if (queryStart === -1) {
5256
+ return url.slice(pathStart);
5257
+ }
5258
+ return url.slice(pathStart, queryStart);
5259
+ }
5260
+
5261
+ // src/engine/pool.ts
5262
+ var ObjectPool = class {
5263
+ pool = [];
5264
+ factory;
5265
+ reset;
5266
+ maxSize;
5267
+ /**
5268
+ * Create a new object pool
5269
+ *
5270
+ * @param factory - Function to create new objects
5271
+ * @param reset - Function to reset objects before reuse
5272
+ * @param maxSize - Maximum pool size (default: 256)
5273
+ */
5274
+ constructor(factory, reset, maxSize = 256) {
5275
+ this.factory = factory;
5276
+ this.reset = reset;
5277
+ this.maxSize = maxSize;
5278
+ }
5279
+ /**
5280
+ * Acquire an object from the pool
5281
+ *
5282
+ * If the pool is empty, creates a new object (overflow strategy).
5283
+ * This ensures the pool never blocks under high load.
5284
+ *
5285
+ * @returns Object from pool or newly created
5286
+ */
5287
+ acquire() {
5288
+ const obj = this.pool.pop();
5289
+ if (obj !== void 0) {
5290
+ return obj;
5291
+ }
5292
+ return this.factory();
5293
+ }
5294
+ /**
5295
+ * Release an object back to the pool
5296
+ *
5297
+ * If the pool is full, the object is discarded (will be GC'd).
5298
+ * This prevents unbounded memory growth.
5299
+ *
5300
+ * @param obj - Object to release
5301
+ */
5302
+ release(obj) {
5303
+ if (this.pool.length < this.maxSize) {
5304
+ this.reset(obj);
5305
+ this.pool.push(obj);
5306
+ }
5307
+ }
5308
+ /**
5309
+ * Clear all objects from the pool
5310
+ *
5311
+ * Useful for testing or when you need to force a clean slate.
5312
+ */
5313
+ clear() {
5314
+ this.pool = [];
5315
+ }
5316
+ /**
5317
+ * Get current pool size
5318
+ */
5319
+ get size() {
5320
+ return this.pool.length;
5321
+ }
5322
+ /**
5323
+ * Get maximum pool size
5324
+ */
5325
+ get capacity() {
5326
+ return this.maxSize;
5327
+ }
5328
+ /**
5329
+ * Pre-warm the pool by creating objects in advance
5330
+ *
5331
+ * This can reduce latency for the first N requests.
5332
+ *
5333
+ * @param count - Number of objects to pre-create
5334
+ */
5335
+ prewarm(count) {
5336
+ const targetSize = Math.min(count, this.maxSize);
5337
+ while (this.pool.length < targetSize) {
5338
+ const obj = this.factory();
5339
+ this.reset(obj);
5340
+ this.pool.push(obj);
5341
+ }
5342
+ }
5343
+ };
5344
+
5345
+ // src/engine/Gravito.ts
5346
+ var Gravito = class {
5347
+ router = new AOTRouter();
5348
+ contextPool;
5349
+ errorHandler;
5350
+ notFoundHandler;
5351
+ // Direct reference to static routes Map (O(1) access)
5352
+ // Optimization: Bypass getter/setter overhead
5353
+ staticRoutes;
5354
+ // Flag: pure static app (no middleware at all) allows ultra-fast path
5355
+ isPureStaticApp = true;
5356
+ /**
5357
+ * Create a new Gravito instance
5358
+ *
5359
+ * @param options - Engine configuration options
5360
+ */
5361
+ constructor(options = {}) {
5362
+ const poolSize = options.poolSize ?? 256;
5363
+ this.contextPool = new ObjectPool(
5364
+ () => new FastContext(),
5365
+ (ctx) => ctx.reset(new Request("http://localhost")),
5366
+ poolSize
5367
+ );
5368
+ this.contextPool.prewarm(Math.min(32, poolSize));
5369
+ if (options.onError) {
5370
+ this.errorHandler = options.onError;
5371
+ }
5372
+ if (options.onNotFound) {
5373
+ this.notFoundHandler = options.onNotFound;
5374
+ }
5375
+ this.compileRoutes();
5376
+ }
5377
+ // ─────────────────────────────────────────────────────────────────────────
5378
+ // HTTP Method Registration
5379
+ // ─────────────────────────────────────────────────────────────────────────
5380
+ /**
5381
+ * Register a GET route
5382
+ *
5383
+ * @param path - Route path (e.g., '/users/:id')
5384
+ * @param handlers - Handler and optional middleware
5385
+ * @returns This instance for chaining
5386
+ */
5387
+ get(path2, ...handlers) {
5388
+ return this.addRoute("get", path2, handlers);
5389
+ }
5390
+ /**
5391
+ * Register a POST route
5392
+ */
5393
+ post(path2, ...handlers) {
5394
+ return this.addRoute("post", path2, handlers);
5395
+ }
5396
+ /**
5397
+ * Register a PUT route
5398
+ */
5399
+ put(path2, ...handlers) {
5400
+ return this.addRoute("put", path2, handlers);
5401
+ }
5402
+ /**
5403
+ * Register a DELETE route
5404
+ */
5405
+ delete(path2, ...handlers) {
5406
+ return this.addRoute("delete", path2, handlers);
5407
+ }
5408
+ /**
5409
+ * Register a PATCH route
5410
+ */
5411
+ patch(path2, ...handlers) {
5412
+ return this.addRoute("patch", path2, handlers);
5413
+ }
5414
+ /**
5415
+ * Register an OPTIONS route
5416
+ */
5417
+ options(path2, ...handlers) {
5418
+ return this.addRoute("options", path2, handlers);
5419
+ }
5420
+ /**
5421
+ * Register a HEAD route
5422
+ */
5423
+ head(path2, ...handlers) {
5424
+ return this.addRoute("head", path2, handlers);
5425
+ }
5426
+ /**
5427
+ * Register a route for all HTTP methods
5428
+ */
5429
+ all(path2, ...handlers) {
5430
+ const methods = ["get", "post", "put", "delete", "patch", "options", "head"];
5431
+ for (const method of methods) {
5432
+ this.addRoute(method, path2, handlers);
5433
+ }
5434
+ return this;
5435
+ }
5436
+ use(pathOrMiddleware, ...middleware) {
5437
+ this.isPureStaticApp = false;
5438
+ if (typeof pathOrMiddleware === "string") {
5439
+ this.router.usePattern(pathOrMiddleware, ...middleware);
5440
+ } else {
5441
+ this.router.use(pathOrMiddleware, ...middleware);
5442
+ }
5443
+ return this;
5444
+ }
5445
+ // ─────────────────────────────────────────────────────────────────────────
5446
+ // Route Grouping
5447
+ // ─────────────────────────────────────────────────────────────────────────
5448
+ /**
5449
+ * Mount a sub-application at a path prefix
5450
+ *
5451
+ * @example
5452
+ * ```typescript
5453
+ * const api = new Gravito()
5454
+ * api.get('/users', (c) => c.json({ users: [] }))
5455
+ *
5456
+ * const app = new Gravito()
5457
+ * app.route('/api', api)
5458
+ * // Now accessible at /api/users
5459
+ * ```
5460
+ */
5461
+ route(_path, _app) {
5462
+ console.warn("route() method is not yet fully implemented");
5463
+ return this;
5464
+ }
5465
+ // ─────────────────────────────────────────────────────────────────────────
5466
+ // Error Handling
5467
+ // ─────────────────────────────────────────────────────────────────────────
5468
+ /**
5469
+ * Set custom error handler
5470
+ *
5471
+ * @example
5472
+ * ```typescript
5473
+ * app.onError((err, c) => {
5474
+ * console.error(err)
5475
+ * return c.json({ error: err.message }, 500)
5476
+ * })
5477
+ * ```
5478
+ */
5479
+ onError(handler) {
5480
+ this.errorHandler = handler;
5481
+ return this;
5482
+ }
5483
+ /**
5484
+ * Set custom 404 handler
5485
+ *
5486
+ * @example
5487
+ * ```typescript
5488
+ * app.notFound((c) => {
5489
+ * return c.json({ error: 'Not Found' }, 404)
5490
+ * })
5491
+ * ```
5492
+ */
5493
+ notFound(handler) {
5494
+ this.notFoundHandler = handler;
5495
+ return this;
5496
+ }
5497
+ // ─────────────────────────────────────────────────────────────────────────
5498
+ // Request Handling (Bun.serve integration)
5499
+ // ─────────────────────────────────────────────────────────────────────────
5500
+ /**
5501
+ * Handle an incoming request
5502
+ *
5503
+ * Optimized for minimal allocations and maximum throughput.
5504
+ * Uses sync/async dual-path strategy inspired by Hono.
5505
+ *
5506
+ * @param request - Incoming Request object
5507
+ * @returns Response object (sync or async)
5508
+ */
5509
+ fetch = (request) => {
5510
+ const path2 = extractPath(request.url);
5511
+ const method = request.method.toLowerCase();
5512
+ const staticKey = `${method}:${path2}`;
5513
+ const staticRoute = this.staticRoutes.get(staticKey);
5514
+ if (staticRoute) {
5515
+ if (staticRoute.useMinimal) {
5516
+ const ctx = new MinimalContext(request, {}, path2);
5517
+ try {
5518
+ const result = staticRoute.handler(ctx);
5519
+ if (result instanceof Response) {
5520
+ return result;
5521
+ }
5522
+ return result;
5523
+ } catch (error) {
5524
+ return this.handleErrorSync(error, request, path2);
5525
+ }
5526
+ }
5527
+ return this.handleWithMiddleware(request, path2, staticRoute);
5528
+ }
5529
+ return this.handleDynamicRoute(request, method, path2);
5530
+ };
5531
+ /**
5532
+ * Handle routes with middleware (async path)
5533
+ */
5534
+ async handleWithMiddleware(request, path2, route) {
5535
+ const ctx = this.contextPool.acquire();
5536
+ try {
5537
+ ctx.reset(request, {});
5538
+ const middleware = this.collectMiddlewareForPath(path2, route.middleware);
5539
+ if (middleware.length === 0) {
5540
+ return await route.handler(ctx);
5541
+ }
5542
+ return await this.executeMiddleware(ctx, middleware, route.handler);
5543
+ } catch (error) {
5544
+ return await this.handleError(error, ctx);
5545
+ } finally {
5546
+ this.contextPool.release(ctx);
5547
+ }
5548
+ }
5549
+ /**
5550
+ * Handle dynamic routes (Radix Tree lookup)
5551
+ */
5552
+ handleDynamicRoute(request, method, path2) {
5553
+ const match = this.router.match(method.toUpperCase(), path2);
5554
+ if (!match.handler) {
5555
+ return this.handleNotFoundSync(request, path2);
5556
+ }
5557
+ const ctx = this.contextPool.acquire();
5558
+ const execute = async () => {
5559
+ try {
5560
+ ctx.reset(request, match.params);
5561
+ if (match.middleware.length === 0) {
5562
+ return await match.handler(ctx);
5563
+ }
5564
+ return await this.executeMiddleware(ctx, match.middleware, match.handler);
5565
+ } catch (error) {
5566
+ return await this.handleError(error, ctx);
5567
+ } finally {
5568
+ this.contextPool.release(ctx);
5569
+ }
5570
+ };
5571
+ return execute();
5572
+ }
5573
+ /**
5574
+ * Sync error handler (for ultra-fast path)
5575
+ */
5576
+ handleErrorSync(error, request, path2) {
5577
+ if (this.errorHandler) {
5578
+ const ctx = new MinimalContext(request, {}, path2);
5579
+ const result = this.errorHandler(error, ctx);
5580
+ if (result instanceof Response) {
5581
+ return result;
5582
+ }
5583
+ return result;
5584
+ }
5585
+ console.error("Unhandled error:", error);
5586
+ return new Response(
5587
+ JSON.stringify({
5588
+ error: "Internal Server Error",
5589
+ message: error.message
5590
+ }),
5591
+ {
5592
+ status: 500,
5593
+ headers: { "Content-Type": "application/json" }
5594
+ }
5595
+ );
5596
+ }
5597
+ /**
5598
+ * Sync 404 handler (for ultra-fast path)
5599
+ */
5600
+ handleNotFoundSync(request, path2) {
5601
+ if (this.notFoundHandler) {
5602
+ const ctx = new MinimalContext(request, {}, path2);
5603
+ const result = this.notFoundHandler(ctx);
5604
+ if (result instanceof Response) {
5605
+ return result;
5606
+ }
5607
+ return result;
5608
+ }
5609
+ return new Response(JSON.stringify({ error: "Not Found" }), {
5610
+ status: 404,
5611
+ headers: { "Content-Type": "application/json" }
5612
+ });
5613
+ }
5614
+ /**
5615
+ * Collect middleware for a specific path
5616
+ * (Simplified version - assumes we've already checked for pure static)
5617
+ */
5618
+ collectMiddlewareForPath(path2, routeMiddleware) {
5619
+ if (this.router.globalMiddleware.length === 0 && this.router.pathMiddleware.size === 0) {
5620
+ return routeMiddleware;
5621
+ }
5622
+ return this.router.collectMiddlewarePublic(path2, routeMiddleware);
5623
+ }
5624
+ /**
5625
+ * Compile routes for optimization
5626
+ * Called once during initialization and when routes change
5627
+ */
5628
+ compileRoutes() {
5629
+ this.staticRoutes = this.router.staticRoutes;
5630
+ const hasGlobalMiddleware = this.router.globalMiddleware.length > 0;
5631
+ const hasPathMiddleware = this.router.pathMiddleware.size > 0;
5632
+ this.isPureStaticApp = !hasGlobalMiddleware && !hasPathMiddleware;
5633
+ for (const [_key, route] of this.staticRoutes) {
5634
+ const analysis = analyzeHandler(route.handler);
5635
+ const optimalType = getOptimalContextType(analysis);
5636
+ route.useMinimal = this.isPureStaticApp && route.middleware.length === 0 && optimalType === "minimal";
5637
+ }
5638
+ }
5639
+ // ─────────────────────────────────────────────────────────────────────────
5640
+ // Internal Methods
5641
+ // ─────────────────────────────────────────────────────────────────────────
5642
+ /**
5643
+ * Add a route to the router
5644
+ */
5645
+ addRoute(method, path2, handlers) {
5646
+ if (handlers.length === 0) {
5647
+ throw new Error(`No handler provided for ${method.toUpperCase()} ${path2}`);
5648
+ }
5649
+ const handler = handlers[handlers.length - 1];
5650
+ const middleware = handlers.slice(0, -1);
5651
+ this.router.add(method, path2, handler, middleware);
5652
+ this.compileRoutes();
5653
+ return this;
5654
+ }
5655
+ /**
5656
+ * Execute middleware chain followed by handler
5657
+ *
5658
+ * Implements the standard middleware pattern:
5659
+ * Each middleware can call next() to continue the chain.
5660
+ */
5661
+ async executeMiddleware(ctx, middleware, handler) {
5662
+ let index = 0;
5663
+ const next = async () => {
5664
+ if (index < middleware.length) {
5665
+ const mw = middleware[index++];
5666
+ return await mw(ctx, next);
5667
+ }
5668
+ return void 0;
5669
+ };
5670
+ const result = await next();
5671
+ if (result instanceof Response) {
5672
+ return result;
5673
+ }
5674
+ return await handler(ctx);
5675
+ }
5676
+ /*
5677
+ * Handle 404 Not Found (Async version for dynamic/middleware paths)
5678
+ * Note: Currently unused as we handle 404s via handleNotFoundSync or inline
5679
+ */
5680
+ // private async handleNotFound(ctx: FastContext): Promise<Response> {
5681
+ // if (this.notFoundHandler) {
5682
+ // return await this.notFoundHandler(ctx)
5683
+ // }
5684
+ // return ctx.json({ error: 'Not Found' }, 404)
5685
+ // }
5686
+ /**
5687
+ * Handle errors (Async version for dynamic/middleware paths)
5688
+ */
5689
+ async handleError(error, ctx) {
5690
+ if (this.errorHandler) {
5691
+ return await this.errorHandler(error, ctx);
5692
+ }
5693
+ console.error("Unhandled error:", error);
5694
+ return ctx.json(
5695
+ {
5696
+ error: "Internal Server Error",
5697
+ message: error.message
5698
+ },
5699
+ 500
5700
+ );
5701
+ }
5702
+ };
5703
+
4712
5704
  // src/index.ts
4713
5705
  var VERSION = package_default.version;
4714
5706
  function defineConfig(config2) {
@@ -4740,6 +5732,7 @@ function defineConfig(config2) {
4740
5732
  PhotonRequestWrapper,
4741
5733
  PlanetCore,
4742
5734
  Route,
5735
+ RouteGroup,
4743
5736
  Router,
4744
5737
  ServiceProvider,
4745
5738
  Str,
@@ -4768,6 +5761,7 @@ function defineConfig(config2) {
4768
5761
  dd,
4769
5762
  defineConfig,
4770
5763
  dump,
5764
+ engine,
4771
5765
  env,
4772
5766
  errors,
4773
5767
  fail,