@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.js CHANGED
@@ -1,14 +1,19 @@
1
+ var __defProp = Object.defineProperty;
1
2
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
3
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
4
  }) : x)(function(x) {
4
5
  if (typeof require !== "undefined") return require.apply(this, arguments);
5
6
  throw Error('Dynamic require of "' + x + '" is not supported');
6
7
  });
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
7
12
 
8
13
  // package.json
9
14
  var package_default = {
10
15
  name: "@gravito/core",
11
- version: "1.0.0",
16
+ version: "1.2.0",
12
17
  description: "",
13
18
  module: "./dist/index.js",
14
19
  main: "./dist/index.cjs",
@@ -24,6 +29,11 @@ var package_default = {
24
29
  types: "./dist/compat.d.ts",
25
30
  import: "./dist/compat.js",
26
31
  require: "./dist/compat.cjs"
32
+ },
33
+ "./engine": {
34
+ types: "./dist/engine/index.d.ts",
35
+ import: "./dist/engine/index.js",
36
+ require: "./dist/engine/index.cjs"
27
37
  }
28
38
  },
29
39
  files: [
@@ -75,11 +85,45 @@ var package_default = {
75
85
  };
76
86
 
77
87
  // src/adapters/PhotonAdapter.ts
78
- var PhotonRequestWrapper = class {
88
+ var PhotonRequestWrapper = class _PhotonRequestWrapper {
79
89
  constructor(photonCtx) {
80
90
  this.photonCtx = photonCtx;
81
91
  }
82
- _cachedJson = null;
92
+ /**
93
+ * Create a proxied instance to delegate to Photon's request
94
+ */
95
+ static create(photonCtx) {
96
+ const instance = new _PhotonRequestWrapper(photonCtx);
97
+ return new Proxy(instance, {
98
+ get(target, prop, receiver) {
99
+ if (prop in target) {
100
+ const value2 = Reflect.get(target, prop, receiver);
101
+ if (typeof value2 === "function") {
102
+ return value2.bind(target);
103
+ }
104
+ return value2;
105
+ }
106
+ const nativeReq = target.photonCtx.req;
107
+ if (prop in nativeReq) {
108
+ const value2 = nativeReq[prop];
109
+ if (typeof value2 === "function") {
110
+ return value2.bind(nativeReq);
111
+ }
112
+ return value2;
113
+ }
114
+ return void 0;
115
+ },
116
+ // Allow setting properties (for addValidated etc.)
117
+ set(target, prop, value2) {
118
+ if (prop in target) {
119
+ return Reflect.set(target, prop, value2);
120
+ }
121
+ ;
122
+ target.photonCtx.req[prop] = value2;
123
+ return true;
124
+ }
125
+ });
126
+ }
83
127
  get url() {
84
128
  return this.photonCtx.req.url;
85
129
  }
@@ -138,7 +182,7 @@ var PhotonRequestWrapper = class {
138
182
  var PhotonContextWrapper = class _PhotonContextWrapper {
139
183
  constructor(photonCtx) {
140
184
  this.photonCtx = photonCtx;
141
- this._req = new PhotonRequestWrapper(photonCtx);
185
+ this._req = PhotonRequestWrapper.create(photonCtx);
142
186
  }
143
187
  _req;
144
188
  /**
@@ -250,7 +294,7 @@ function toPhotonMiddleware(middleware) {
250
294
  return async (c, next) => {
251
295
  const ctx = PhotonContextWrapper.create(c);
252
296
  const gravitoNext = async () => {
253
- await next();
297
+ return await next();
254
298
  };
255
299
  return middleware(ctx, gravitoNext);
256
300
  };
@@ -2713,20 +2757,20 @@ var RouteGroup = class _RouteGroup {
2713
2757
  group(callback) {
2714
2758
  callback(this);
2715
2759
  }
2716
- get(path2, requestOrHandler, handler) {
2717
- return this.router.req("get", path2, requestOrHandler, handler, this.options);
2760
+ get(path2, requestOrHandlerOrMiddleware, handler) {
2761
+ return this.router.req("get", path2, requestOrHandlerOrMiddleware, handler, this.options);
2718
2762
  }
2719
- post(path2, requestOrHandler, handler) {
2720
- return this.router.req("post", path2, requestOrHandler, handler, this.options);
2763
+ post(path2, requestOrHandlerOrMiddleware, handler) {
2764
+ return this.router.req("post", path2, requestOrHandlerOrMiddleware, handler, this.options);
2721
2765
  }
2722
- put(path2, requestOrHandler, handler) {
2723
- return this.router.req("put", path2, requestOrHandler, handler, this.options);
2766
+ put(path2, requestOrHandlerOrMiddleware, handler) {
2767
+ return this.router.req("put", path2, requestOrHandlerOrMiddleware, handler, this.options);
2724
2768
  }
2725
- delete(path2, requestOrHandler, handler) {
2726
- return this.router.req("delete", path2, requestOrHandler, handler, this.options);
2769
+ delete(path2, requestOrHandlerOrMiddleware, handler) {
2770
+ return this.router.req("delete", path2, requestOrHandlerOrMiddleware, handler, this.options);
2727
2771
  }
2728
- patch(path2, requestOrHandler, handler) {
2729
- return this.router.req("patch", path2, requestOrHandler, handler, this.options);
2772
+ patch(path2, requestOrHandlerOrMiddleware, handler) {
2773
+ return this.router.req("patch", path2, requestOrHandlerOrMiddleware, handler, this.options);
2730
2774
  }
2731
2775
  resource(name, controller, options = {}) {
2732
2776
  const actions = [
@@ -2902,20 +2946,20 @@ var Router = class {
2902
2946
  middleware(...handlers) {
2903
2947
  return new RouteGroup(this, { middleware: handlers.flat() });
2904
2948
  }
2905
- get(path2, requestOrHandler, handler) {
2906
- return this.req("get", path2, requestOrHandler, handler);
2949
+ get(path2, requestOrHandlerOrMiddleware, handler) {
2950
+ return this.req("get", path2, requestOrHandlerOrMiddleware, handler);
2907
2951
  }
2908
- post(path2, requestOrHandler, handler) {
2909
- return this.req("post", path2, requestOrHandler, handler);
2952
+ post(path2, requestOrHandlerOrMiddleware, handler) {
2953
+ return this.req("post", path2, requestOrHandlerOrMiddleware, handler);
2910
2954
  }
2911
- put(path2, requestOrHandler, handler) {
2912
- return this.req("put", path2, requestOrHandler, handler);
2955
+ put(path2, requestOrHandlerOrMiddleware, handler) {
2956
+ return this.req("put", path2, requestOrHandlerOrMiddleware, handler);
2913
2957
  }
2914
- delete(path2, requestOrHandler, handler) {
2915
- return this.req("delete", path2, requestOrHandler, handler);
2958
+ delete(path2, requestOrHandlerOrMiddleware, handler) {
2959
+ return this.req("delete", path2, requestOrHandlerOrMiddleware, handler);
2916
2960
  }
2917
- patch(path2, requestOrHandler, handler) {
2918
- return this.req("patch", path2, requestOrHandler, handler);
2961
+ patch(path2, requestOrHandlerOrMiddleware, handler) {
2962
+ return this.req("patch", path2, requestOrHandlerOrMiddleware, handler);
2919
2963
  }
2920
2964
  /**
2921
2965
  * Register a resource route (Laravel-style).
@@ -2961,17 +3005,25 @@ var Router = class {
2961
3005
  /**
2962
3006
  * Internal Request Registration
2963
3007
  */
2964
- req(method, path2, requestOrHandler, handler, options = {}) {
3008
+ req(method, path2, requestOrHandlerOrMiddleware, handler, options = {}) {
2965
3009
  const fullPath = (options.prefix || "") + path2;
2966
3010
  let formRequestMiddleware = null;
3011
+ let routeMiddleware = [];
2967
3012
  let finalRouteHandler;
2968
3013
  if (handler !== void 0) {
2969
- if (isFormRequestClass(requestOrHandler)) {
2970
- formRequestMiddleware = formRequestToMiddleware(requestOrHandler);
3014
+ if (isFormRequestClass(requestOrHandlerOrMiddleware)) {
3015
+ formRequestMiddleware = formRequestToMiddleware(requestOrHandlerOrMiddleware);
3016
+ } else {
3017
+ const middleware = requestOrHandlerOrMiddleware;
3018
+ if (Array.isArray(middleware)) {
3019
+ routeMiddleware = middleware;
3020
+ } else {
3021
+ routeMiddleware = [middleware];
3022
+ }
2971
3023
  }
2972
3024
  finalRouteHandler = handler;
2973
3025
  } else {
2974
- finalRouteHandler = requestOrHandler;
3026
+ finalRouteHandler = requestOrHandlerOrMiddleware;
2975
3027
  }
2976
3028
  let resolvedHandler;
2977
3029
  if (Array.isArray(finalRouteHandler)) {
@@ -2987,6 +3039,9 @@ var Router = class {
2987
3039
  if (formRequestMiddleware) {
2988
3040
  handlers.push(formRequestMiddleware);
2989
3041
  }
3042
+ if (routeMiddleware.length > 0) {
3043
+ handlers.push(...routeMiddleware);
3044
+ }
2990
3045
  handlers.push(resolvedHandler);
2991
3046
  if (options.domain) {
2992
3047
  const domainCheck = async (c, next) => {
@@ -3261,8 +3316,7 @@ var PlanetCore = class _PlanetCore {
3261
3316
  const cookieJar = new CookieJar(this.encrypter);
3262
3317
  c.set("cookieJar", cookieJar);
3263
3318
  c.route = (name, params, query) => this.router.url(name, params, query);
3264
- await next();
3265
- return void 0;
3319
+ return await next();
3266
3320
  });
3267
3321
  this.router = new Router(this);
3268
3322
  this.adapter.onError(async (err, c) => {
@@ -4604,6 +4658,947 @@ function createHttpTester(core) {
4604
4658
  return new HttpTester(core);
4605
4659
  }
4606
4660
 
4661
+ // src/engine/index.ts
4662
+ var engine_exports = {};
4663
+ __export(engine_exports, {
4664
+ AOTRouter: () => AOTRouter,
4665
+ FastContextImpl: () => FastContext,
4666
+ Gravito: () => Gravito,
4667
+ MinimalContext: () => MinimalContext,
4668
+ ObjectPool: () => ObjectPool,
4669
+ extractPath: () => extractPath
4670
+ });
4671
+
4672
+ // src/engine/AOTRouter.ts
4673
+ var AOTRouter = class {
4674
+ // Static route cache: "METHOD:PATH" -> RouteMetadata
4675
+ staticRoutes = /* @__PURE__ */ new Map();
4676
+ // Dynamic route handler (Radix Tree)
4677
+ dynamicRouter = new RadixRouter();
4678
+ // Global middleware (applies to all routes)
4679
+ globalMiddleware = [];
4680
+ // Path-based middleware: pattern -> middleware[]
4681
+ pathMiddleware = /* @__PURE__ */ new Map();
4682
+ /**
4683
+ * Register a route
4684
+ *
4685
+ * Automatically determines if route is static or dynamic.
4686
+ * Static routes are stored in a Map for O(1) lookup.
4687
+ * Dynamic routes use the Radix Tree.
4688
+ *
4689
+ * @param method - HTTP method
4690
+ * @param path - Route path
4691
+ * @param handler - Route handler
4692
+ * @param middleware - Route-specific middleware
4693
+ */
4694
+ add(method, path2, handler, middleware = []) {
4695
+ const normalizedMethod = method.toLowerCase();
4696
+ if (this.isStaticPath(path2)) {
4697
+ const key = `${normalizedMethod}:${path2}`;
4698
+ this.staticRoutes.set(key, { handler, middleware });
4699
+ } else {
4700
+ const wrappedHandler = handler;
4701
+ this.dynamicRouter.add(normalizedMethod, path2, [wrappedHandler]);
4702
+ if (middleware.length > 0) {
4703
+ this.pathMiddleware.set(`${normalizedMethod}:${path2}`, middleware);
4704
+ }
4705
+ }
4706
+ }
4707
+ /**
4708
+ * Add global middleware
4709
+ *
4710
+ * These run for every request, before route-specific middleware.
4711
+ *
4712
+ * @param middleware - Middleware functions
4713
+ */
4714
+ use(...middleware) {
4715
+ this.globalMiddleware.push(...middleware);
4716
+ }
4717
+ /**
4718
+ * Add path-based middleware
4719
+ *
4720
+ * Supports wildcard patterns like '/api/*'
4721
+ *
4722
+ * @param pattern - Path pattern
4723
+ * @param middleware - Middleware functions
4724
+ */
4725
+ usePattern(pattern, ...middleware) {
4726
+ const existing = this.pathMiddleware.get(pattern) ?? [];
4727
+ this.pathMiddleware.set(pattern, [...existing, ...middleware]);
4728
+ }
4729
+ /**
4730
+ * Match a request to a route
4731
+ *
4732
+ * Returns the handler, params, and all applicable middleware.
4733
+ *
4734
+ * @param method - HTTP method
4735
+ * @param path - Request path
4736
+ * @returns Route match or null if not found
4737
+ */
4738
+ match(method, path2) {
4739
+ const normalizedMethod = method.toLowerCase();
4740
+ const staticKey = `${normalizedMethod}:${path2}`;
4741
+ const staticRoute = this.staticRoutes.get(staticKey);
4742
+ if (staticRoute) {
4743
+ return {
4744
+ handler: staticRoute.handler,
4745
+ params: {},
4746
+ middleware: this.collectMiddleware(path2, staticRoute.middleware)
4747
+ };
4748
+ }
4749
+ const match = this.dynamicRouter.match(normalizedMethod, path2);
4750
+ if (match && match.handlers.length > 0) {
4751
+ const handler = match.handlers[0];
4752
+ const routeKey = this.findDynamicRouteKey(normalizedMethod, path2);
4753
+ const routeMiddleware = routeKey ? this.pathMiddleware.get(routeKey) ?? [] : [];
4754
+ return {
4755
+ handler,
4756
+ params: match.params,
4757
+ middleware: this.collectMiddleware(path2, routeMiddleware)
4758
+ };
4759
+ }
4760
+ return {
4761
+ handler: null,
4762
+ params: {},
4763
+ middleware: []
4764
+ };
4765
+ }
4766
+ /**
4767
+ * Public wrapper for collectMiddleware (used by Gravito for optimization)
4768
+ */
4769
+ collectMiddlewarePublic(path2, routeMiddleware) {
4770
+ return this.collectMiddleware(path2, routeMiddleware);
4771
+ }
4772
+ /**
4773
+ * Collect all applicable middleware for a path
4774
+ *
4775
+ * Order: global -> pattern-based -> route-specific
4776
+ *
4777
+ * @param path - Request path
4778
+ * @param routeMiddleware - Route-specific middleware
4779
+ * @returns Combined middleware array
4780
+ */
4781
+ collectMiddleware(path2, routeMiddleware) {
4782
+ if (this.globalMiddleware.length === 0 && this.pathMiddleware.size === 0 && routeMiddleware.length === 0) {
4783
+ return [];
4784
+ }
4785
+ const middleware = [];
4786
+ if (this.globalMiddleware.length > 0) {
4787
+ middleware.push(...this.globalMiddleware);
4788
+ }
4789
+ if (this.pathMiddleware.size > 0) {
4790
+ for (const [pattern, mw] of this.pathMiddleware) {
4791
+ if (pattern.includes(":")) continue;
4792
+ if (this.matchPattern(pattern, path2)) {
4793
+ middleware.push(...mw);
4794
+ }
4795
+ }
4796
+ }
4797
+ if (routeMiddleware.length > 0) {
4798
+ middleware.push(...routeMiddleware);
4799
+ }
4800
+ return middleware;
4801
+ }
4802
+ /**
4803
+ * Check if a path is static (no parameters or wildcards)
4804
+ */
4805
+ isStaticPath(path2) {
4806
+ return !path2.includes(":") && !path2.includes("*");
4807
+ }
4808
+ /**
4809
+ * Match a pattern against a path
4810
+ *
4811
+ * Supports:
4812
+ * - Exact match: '/api/users'
4813
+ * - Wildcard suffix: '/api/*'
4814
+ *
4815
+ * @param pattern - Pattern to match
4816
+ * @param path - Path to test
4817
+ * @returns True if pattern matches
4818
+ */
4819
+ matchPattern(pattern, path2) {
4820
+ if (pattern === "*") return true;
4821
+ if (pattern === path2) return true;
4822
+ if (pattern.endsWith("/*")) {
4823
+ const prefix = pattern.slice(0, -2);
4824
+ return path2.startsWith(prefix);
4825
+ }
4826
+ return false;
4827
+ }
4828
+ /**
4829
+ * Find the original route key for a matched dynamic route
4830
+ *
4831
+ * This is needed to look up route-specific middleware.
4832
+ * It's a bit of a hack, but avoids storing duplicate data.
4833
+ *
4834
+ * @param method - HTTP method
4835
+ * @param path - Matched path
4836
+ * @returns Route key or null
4837
+ */
4838
+ findDynamicRouteKey(method, _path) {
4839
+ for (const key of this.pathMiddleware.keys()) {
4840
+ if (key.startsWith(`${method}:`)) {
4841
+ return key;
4842
+ }
4843
+ }
4844
+ return null;
4845
+ }
4846
+ /**
4847
+ * Get all registered routes (for debugging)
4848
+ */
4849
+ getRoutes() {
4850
+ const routes = [];
4851
+ for (const key of this.staticRoutes.keys()) {
4852
+ const [method, path2] = key.split(":");
4853
+ routes.push({ method, path: path2, type: "static" });
4854
+ }
4855
+ return routes;
4856
+ }
4857
+ };
4858
+
4859
+ // src/engine/analyzer.ts
4860
+ function analyzeHandler(handler) {
4861
+ const source = handler.toString();
4862
+ return {
4863
+ usesHeaders: source.includes(".header(") || source.includes(".header)") || source.includes(".headers(") || source.includes(".headers)"),
4864
+ usesQuery: source.includes(".query(") || source.includes(".query)") || source.includes(".queries(") || source.includes(".queries)"),
4865
+ usesBody: source.includes(".json()") || source.includes(".text()") || source.includes(".formData()") || source.includes(".body"),
4866
+ usesParams: source.includes(".param(") || source.includes(".param)") || source.includes(".params(") || source.includes(".params)"),
4867
+ isAsync: source.includes("async") || source.includes("await")
4868
+ };
4869
+ }
4870
+ function getOptimalContextType(analysis) {
4871
+ if (analysis.usesHeaders) {
4872
+ return "fast";
4873
+ }
4874
+ if (!analysis.usesQuery && !analysis.usesBody && !analysis.usesParams) {
4875
+ return "minimal";
4876
+ }
4877
+ if (!analysis.usesQuery && !analysis.usesBody && analysis.usesParams) {
4878
+ return "minimal";
4879
+ }
4880
+ if (analysis.usesBody) {
4881
+ return "full";
4882
+ }
4883
+ return "fast";
4884
+ }
4885
+
4886
+ // src/engine/FastContext.ts
4887
+ var FastRequestImpl = class {
4888
+ _request;
4889
+ _params;
4890
+ _url = new URL("http://localhost");
4891
+ // Reuse this object
4892
+ _query = null;
4893
+ _headers = null;
4894
+ /**
4895
+ * Reset for pooling
4896
+ */
4897
+ reset(request, params = {}) {
4898
+ this._request = request;
4899
+ this._params = params;
4900
+ this._url.href = request.url;
4901
+ this._query = null;
4902
+ this._headers = null;
4903
+ }
4904
+ get url() {
4905
+ return this._request.url;
4906
+ }
4907
+ get method() {
4908
+ return this._request.method;
4909
+ }
4910
+ get path() {
4911
+ return this._url.pathname;
4912
+ }
4913
+ param(name) {
4914
+ return this._params[name];
4915
+ }
4916
+ params() {
4917
+ return { ...this._params };
4918
+ }
4919
+ query(name) {
4920
+ if (!this._query) {
4921
+ this._query = this._url.searchParams;
4922
+ }
4923
+ return this._query.get(name) ?? void 0;
4924
+ }
4925
+ queries() {
4926
+ if (!this._query) {
4927
+ this._query = this._url.searchParams;
4928
+ }
4929
+ const result = {};
4930
+ for (const [key, value2] of this._query.entries()) {
4931
+ const existing = result[key];
4932
+ if (existing === void 0) {
4933
+ result[key] = value2;
4934
+ } else if (Array.isArray(existing)) {
4935
+ existing.push(value2);
4936
+ } else {
4937
+ result[key] = [existing, value2];
4938
+ }
4939
+ }
4940
+ return result;
4941
+ }
4942
+ header(name) {
4943
+ return this._request.headers.get(name) ?? void 0;
4944
+ }
4945
+ headers() {
4946
+ if (!this._headers) {
4947
+ this._headers = {};
4948
+ for (const [key, value2] of this._request.headers.entries()) {
4949
+ this._headers[key] = value2;
4950
+ }
4951
+ }
4952
+ return { ...this._headers };
4953
+ }
4954
+ async json() {
4955
+ return this._request.json();
4956
+ }
4957
+ async text() {
4958
+ return this._request.text();
4959
+ }
4960
+ async formData() {
4961
+ return this._request.formData();
4962
+ }
4963
+ get raw() {
4964
+ return this._request;
4965
+ }
4966
+ };
4967
+ var FastContext = class {
4968
+ _req = new FastRequestImpl();
4969
+ // private _statusCode = 200
4970
+ _headers = new Headers();
4971
+ // Reuse this object
4972
+ /**
4973
+ * Reset context for pooling
4974
+ *
4975
+ * This is called when acquiring from the pool.
4976
+ * Must clear all state from previous request.
4977
+ */
4978
+ reset(request, params = {}) {
4979
+ this._req.reset(request, params);
4980
+ this._headers = new Headers();
4981
+ return this;
4982
+ }
4983
+ get req() {
4984
+ return this._req;
4985
+ }
4986
+ // ─────────────────────────────────────────────────────────────────────────
4987
+ // Response Helpers
4988
+ // ─────────────────────────────────────────────────────────────────────────
4989
+ json(data, status = 200) {
4990
+ this._headers.set("Content-Type", "application/json; charset=utf-8");
4991
+ return new Response(JSON.stringify(data), {
4992
+ status,
4993
+ headers: this._headers
4994
+ });
4995
+ }
4996
+ text(text, status = 200) {
4997
+ this._headers.set("Content-Type", "text/plain; charset=utf-8");
4998
+ return new Response(text, {
4999
+ status,
5000
+ headers: this._headers
5001
+ });
5002
+ }
5003
+ html(html, status = 200) {
5004
+ this._headers.set("Content-Type", "text/html; charset=utf-8");
5005
+ return new Response(html, {
5006
+ status,
5007
+ headers: this._headers
5008
+ });
5009
+ }
5010
+ redirect(url, status = 302) {
5011
+ this._headers.set("Location", url);
5012
+ return new Response(null, {
5013
+ status,
5014
+ headers: this._headers
5015
+ });
5016
+ }
5017
+ body(data, status = 200) {
5018
+ return new Response(data, {
5019
+ status,
5020
+ headers: this._headers
5021
+ });
5022
+ }
5023
+ // ─────────────────────────────────────────────────────────────────────────
5024
+ // Header Management
5025
+ // ─────────────────────────────────────────────────────────────────────────
5026
+ header(name, value2) {
5027
+ this._headers.set(name, value2);
5028
+ }
5029
+ status(_code) {
5030
+ }
5031
+ };
5032
+
5033
+ // src/engine/MinimalContext.ts
5034
+ var MinimalRequest = class {
5035
+ constructor(_request, _params, _path) {
5036
+ this._request = _request;
5037
+ this._params = _params;
5038
+ this._path = _path;
5039
+ }
5040
+ get url() {
5041
+ return this._request.url;
5042
+ }
5043
+ get method() {
5044
+ return this._request.method;
5045
+ }
5046
+ get path() {
5047
+ return this._path;
5048
+ }
5049
+ param(name) {
5050
+ return this._params[name];
5051
+ }
5052
+ params() {
5053
+ return { ...this._params };
5054
+ }
5055
+ query(name) {
5056
+ const url = new URL(this._request.url);
5057
+ return url.searchParams.get(name) ?? void 0;
5058
+ }
5059
+ queries() {
5060
+ const url = new URL(this._request.url);
5061
+ const result = {};
5062
+ for (const [key, value2] of url.searchParams.entries()) {
5063
+ const existing = result[key];
5064
+ if (existing === void 0) {
5065
+ result[key] = value2;
5066
+ } else if (Array.isArray(existing)) {
5067
+ existing.push(value2);
5068
+ } else {
5069
+ result[key] = [existing, value2];
5070
+ }
5071
+ }
5072
+ return result;
5073
+ }
5074
+ header(name) {
5075
+ return this._request.headers.get(name) ?? void 0;
5076
+ }
5077
+ headers() {
5078
+ const result = {};
5079
+ for (const [key, value2] of this._request.headers.entries()) {
5080
+ result[key] = value2;
5081
+ }
5082
+ return result;
5083
+ }
5084
+ async json() {
5085
+ return this._request.json();
5086
+ }
5087
+ async text() {
5088
+ return this._request.text();
5089
+ }
5090
+ async formData() {
5091
+ return this._request.formData();
5092
+ }
5093
+ get raw() {
5094
+ return this._request;
5095
+ }
5096
+ };
5097
+ var MinimalContext = class {
5098
+ _req;
5099
+ constructor(request, params, path2) {
5100
+ this._req = new MinimalRequest(request, params, path2);
5101
+ }
5102
+ get req() {
5103
+ return this._req;
5104
+ }
5105
+ // Response helpers - create headers inline (no reuse overhead)
5106
+ json(data, status = 200) {
5107
+ return new Response(JSON.stringify(data), {
5108
+ status,
5109
+ headers: { "Content-Type": "application/json; charset=utf-8" }
5110
+ });
5111
+ }
5112
+ text(text, status = 200) {
5113
+ return new Response(text, {
5114
+ status,
5115
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
5116
+ });
5117
+ }
5118
+ html(html, status = 200) {
5119
+ return new Response(html, {
5120
+ status,
5121
+ headers: { "Content-Type": "text/html; charset=utf-8" }
5122
+ });
5123
+ }
5124
+ redirect(url, status = 302) {
5125
+ return new Response(null, {
5126
+ status,
5127
+ headers: { Location: url }
5128
+ });
5129
+ }
5130
+ body(data, status = 200) {
5131
+ return new Response(data, { status });
5132
+ }
5133
+ header(_name, _value) {
5134
+ console.warn("MinimalContext.header() is a no-op. Use FastContext for custom headers.");
5135
+ }
5136
+ status(_code) {
5137
+ }
5138
+ // Required for interface compatibility
5139
+ reset(_request, _params) {
5140
+ throw new Error("MinimalContext does not support reset. Create a new instance instead.");
5141
+ }
5142
+ };
5143
+
5144
+ // src/engine/path.ts
5145
+ function extractPath(url) {
5146
+ const protocolEnd = url.indexOf("://");
5147
+ const searchStart = protocolEnd === -1 ? 0 : protocolEnd + 3;
5148
+ const pathStart = url.indexOf("/", searchStart);
5149
+ if (pathStart === -1) {
5150
+ return "/";
5151
+ }
5152
+ const queryStart = url.indexOf("?", pathStart);
5153
+ if (queryStart === -1) {
5154
+ return url.slice(pathStart);
5155
+ }
5156
+ return url.slice(pathStart, queryStart);
5157
+ }
5158
+
5159
+ // src/engine/pool.ts
5160
+ var ObjectPool = class {
5161
+ pool = [];
5162
+ factory;
5163
+ reset;
5164
+ maxSize;
5165
+ /**
5166
+ * Create a new object pool
5167
+ *
5168
+ * @param factory - Function to create new objects
5169
+ * @param reset - Function to reset objects before reuse
5170
+ * @param maxSize - Maximum pool size (default: 256)
5171
+ */
5172
+ constructor(factory, reset, maxSize = 256) {
5173
+ this.factory = factory;
5174
+ this.reset = reset;
5175
+ this.maxSize = maxSize;
5176
+ }
5177
+ /**
5178
+ * Acquire an object from the pool
5179
+ *
5180
+ * If the pool is empty, creates a new object (overflow strategy).
5181
+ * This ensures the pool never blocks under high load.
5182
+ *
5183
+ * @returns Object from pool or newly created
5184
+ */
5185
+ acquire() {
5186
+ const obj = this.pool.pop();
5187
+ if (obj !== void 0) {
5188
+ return obj;
5189
+ }
5190
+ return this.factory();
5191
+ }
5192
+ /**
5193
+ * Release an object back to the pool
5194
+ *
5195
+ * If the pool is full, the object is discarded (will be GC'd).
5196
+ * This prevents unbounded memory growth.
5197
+ *
5198
+ * @param obj - Object to release
5199
+ */
5200
+ release(obj) {
5201
+ if (this.pool.length < this.maxSize) {
5202
+ this.reset(obj);
5203
+ this.pool.push(obj);
5204
+ }
5205
+ }
5206
+ /**
5207
+ * Clear all objects from the pool
5208
+ *
5209
+ * Useful for testing or when you need to force a clean slate.
5210
+ */
5211
+ clear() {
5212
+ this.pool = [];
5213
+ }
5214
+ /**
5215
+ * Get current pool size
5216
+ */
5217
+ get size() {
5218
+ return this.pool.length;
5219
+ }
5220
+ /**
5221
+ * Get maximum pool size
5222
+ */
5223
+ get capacity() {
5224
+ return this.maxSize;
5225
+ }
5226
+ /**
5227
+ * Pre-warm the pool by creating objects in advance
5228
+ *
5229
+ * This can reduce latency for the first N requests.
5230
+ *
5231
+ * @param count - Number of objects to pre-create
5232
+ */
5233
+ prewarm(count) {
5234
+ const targetSize = Math.min(count, this.maxSize);
5235
+ while (this.pool.length < targetSize) {
5236
+ const obj = this.factory();
5237
+ this.reset(obj);
5238
+ this.pool.push(obj);
5239
+ }
5240
+ }
5241
+ };
5242
+
5243
+ // src/engine/Gravito.ts
5244
+ var Gravito = class {
5245
+ router = new AOTRouter();
5246
+ contextPool;
5247
+ errorHandler;
5248
+ notFoundHandler;
5249
+ // Direct reference to static routes Map (O(1) access)
5250
+ // Optimization: Bypass getter/setter overhead
5251
+ staticRoutes;
5252
+ // Flag: pure static app (no middleware at all) allows ultra-fast path
5253
+ isPureStaticApp = true;
5254
+ /**
5255
+ * Create a new Gravito instance
5256
+ *
5257
+ * @param options - Engine configuration options
5258
+ */
5259
+ constructor(options = {}) {
5260
+ const poolSize = options.poolSize ?? 256;
5261
+ this.contextPool = new ObjectPool(
5262
+ () => new FastContext(),
5263
+ (ctx) => ctx.reset(new Request("http://localhost")),
5264
+ poolSize
5265
+ );
5266
+ this.contextPool.prewarm(Math.min(32, poolSize));
5267
+ if (options.onError) {
5268
+ this.errorHandler = options.onError;
5269
+ }
5270
+ if (options.onNotFound) {
5271
+ this.notFoundHandler = options.onNotFound;
5272
+ }
5273
+ this.compileRoutes();
5274
+ }
5275
+ // ─────────────────────────────────────────────────────────────────────────
5276
+ // HTTP Method Registration
5277
+ // ─────────────────────────────────────────────────────────────────────────
5278
+ /**
5279
+ * Register a GET route
5280
+ *
5281
+ * @param path - Route path (e.g., '/users/:id')
5282
+ * @param handlers - Handler and optional middleware
5283
+ * @returns This instance for chaining
5284
+ */
5285
+ get(path2, ...handlers) {
5286
+ return this.addRoute("get", path2, handlers);
5287
+ }
5288
+ /**
5289
+ * Register a POST route
5290
+ */
5291
+ post(path2, ...handlers) {
5292
+ return this.addRoute("post", path2, handlers);
5293
+ }
5294
+ /**
5295
+ * Register a PUT route
5296
+ */
5297
+ put(path2, ...handlers) {
5298
+ return this.addRoute("put", path2, handlers);
5299
+ }
5300
+ /**
5301
+ * Register a DELETE route
5302
+ */
5303
+ delete(path2, ...handlers) {
5304
+ return this.addRoute("delete", path2, handlers);
5305
+ }
5306
+ /**
5307
+ * Register a PATCH route
5308
+ */
5309
+ patch(path2, ...handlers) {
5310
+ return this.addRoute("patch", path2, handlers);
5311
+ }
5312
+ /**
5313
+ * Register an OPTIONS route
5314
+ */
5315
+ options(path2, ...handlers) {
5316
+ return this.addRoute("options", path2, handlers);
5317
+ }
5318
+ /**
5319
+ * Register a HEAD route
5320
+ */
5321
+ head(path2, ...handlers) {
5322
+ return this.addRoute("head", path2, handlers);
5323
+ }
5324
+ /**
5325
+ * Register a route for all HTTP methods
5326
+ */
5327
+ all(path2, ...handlers) {
5328
+ const methods = ["get", "post", "put", "delete", "patch", "options", "head"];
5329
+ for (const method of methods) {
5330
+ this.addRoute(method, path2, handlers);
5331
+ }
5332
+ return this;
5333
+ }
5334
+ use(pathOrMiddleware, ...middleware) {
5335
+ this.isPureStaticApp = false;
5336
+ if (typeof pathOrMiddleware === "string") {
5337
+ this.router.usePattern(pathOrMiddleware, ...middleware);
5338
+ } else {
5339
+ this.router.use(pathOrMiddleware, ...middleware);
5340
+ }
5341
+ return this;
5342
+ }
5343
+ // ─────────────────────────────────────────────────────────────────────────
5344
+ // Route Grouping
5345
+ // ─────────────────────────────────────────────────────────────────────────
5346
+ /**
5347
+ * Mount a sub-application at a path prefix
5348
+ *
5349
+ * @example
5350
+ * ```typescript
5351
+ * const api = new Gravito()
5352
+ * api.get('/users', (c) => c.json({ users: [] }))
5353
+ *
5354
+ * const app = new Gravito()
5355
+ * app.route('/api', api)
5356
+ * // Now accessible at /api/users
5357
+ * ```
5358
+ */
5359
+ route(_path, _app) {
5360
+ console.warn("route() method is not yet fully implemented");
5361
+ return this;
5362
+ }
5363
+ // ─────────────────────────────────────────────────────────────────────────
5364
+ // Error Handling
5365
+ // ─────────────────────────────────────────────────────────────────────────
5366
+ /**
5367
+ * Set custom error handler
5368
+ *
5369
+ * @example
5370
+ * ```typescript
5371
+ * app.onError((err, c) => {
5372
+ * console.error(err)
5373
+ * return c.json({ error: err.message }, 500)
5374
+ * })
5375
+ * ```
5376
+ */
5377
+ onError(handler) {
5378
+ this.errorHandler = handler;
5379
+ return this;
5380
+ }
5381
+ /**
5382
+ * Set custom 404 handler
5383
+ *
5384
+ * @example
5385
+ * ```typescript
5386
+ * app.notFound((c) => {
5387
+ * return c.json({ error: 'Not Found' }, 404)
5388
+ * })
5389
+ * ```
5390
+ */
5391
+ notFound(handler) {
5392
+ this.notFoundHandler = handler;
5393
+ return this;
5394
+ }
5395
+ // ─────────────────────────────────────────────────────────────────────────
5396
+ // Request Handling (Bun.serve integration)
5397
+ // ─────────────────────────────────────────────────────────────────────────
5398
+ /**
5399
+ * Handle an incoming request
5400
+ *
5401
+ * Optimized for minimal allocations and maximum throughput.
5402
+ * Uses sync/async dual-path strategy inspired by Hono.
5403
+ *
5404
+ * @param request - Incoming Request object
5405
+ * @returns Response object (sync or async)
5406
+ */
5407
+ fetch = (request) => {
5408
+ const path2 = extractPath(request.url);
5409
+ const method = request.method.toLowerCase();
5410
+ const staticKey = `${method}:${path2}`;
5411
+ const staticRoute = this.staticRoutes.get(staticKey);
5412
+ if (staticRoute) {
5413
+ if (staticRoute.useMinimal) {
5414
+ const ctx = new MinimalContext(request, {}, path2);
5415
+ try {
5416
+ const result = staticRoute.handler(ctx);
5417
+ if (result instanceof Response) {
5418
+ return result;
5419
+ }
5420
+ return result;
5421
+ } catch (error) {
5422
+ return this.handleErrorSync(error, request, path2);
5423
+ }
5424
+ }
5425
+ return this.handleWithMiddleware(request, path2, staticRoute);
5426
+ }
5427
+ return this.handleDynamicRoute(request, method, path2);
5428
+ };
5429
+ /**
5430
+ * Handle routes with middleware (async path)
5431
+ */
5432
+ async handleWithMiddleware(request, path2, route) {
5433
+ const ctx = this.contextPool.acquire();
5434
+ try {
5435
+ ctx.reset(request, {});
5436
+ const middleware = this.collectMiddlewareForPath(path2, route.middleware);
5437
+ if (middleware.length === 0) {
5438
+ return await route.handler(ctx);
5439
+ }
5440
+ return await this.executeMiddleware(ctx, middleware, route.handler);
5441
+ } catch (error) {
5442
+ return await this.handleError(error, ctx);
5443
+ } finally {
5444
+ this.contextPool.release(ctx);
5445
+ }
5446
+ }
5447
+ /**
5448
+ * Handle dynamic routes (Radix Tree lookup)
5449
+ */
5450
+ handleDynamicRoute(request, method, path2) {
5451
+ const match = this.router.match(method.toUpperCase(), path2);
5452
+ if (!match.handler) {
5453
+ return this.handleNotFoundSync(request, path2);
5454
+ }
5455
+ const ctx = this.contextPool.acquire();
5456
+ const execute = async () => {
5457
+ try {
5458
+ ctx.reset(request, match.params);
5459
+ if (match.middleware.length === 0) {
5460
+ return await match.handler(ctx);
5461
+ }
5462
+ return await this.executeMiddleware(ctx, match.middleware, match.handler);
5463
+ } catch (error) {
5464
+ return await this.handleError(error, ctx);
5465
+ } finally {
5466
+ this.contextPool.release(ctx);
5467
+ }
5468
+ };
5469
+ return execute();
5470
+ }
5471
+ /**
5472
+ * Sync error handler (for ultra-fast path)
5473
+ */
5474
+ handleErrorSync(error, request, path2) {
5475
+ if (this.errorHandler) {
5476
+ const ctx = new MinimalContext(request, {}, path2);
5477
+ const result = this.errorHandler(error, ctx);
5478
+ if (result instanceof Response) {
5479
+ return result;
5480
+ }
5481
+ return result;
5482
+ }
5483
+ console.error("Unhandled error:", error);
5484
+ return new Response(
5485
+ JSON.stringify({
5486
+ error: "Internal Server Error",
5487
+ message: error.message
5488
+ }),
5489
+ {
5490
+ status: 500,
5491
+ headers: { "Content-Type": "application/json" }
5492
+ }
5493
+ );
5494
+ }
5495
+ /**
5496
+ * Sync 404 handler (for ultra-fast path)
5497
+ */
5498
+ handleNotFoundSync(request, path2) {
5499
+ if (this.notFoundHandler) {
5500
+ const ctx = new MinimalContext(request, {}, path2);
5501
+ const result = this.notFoundHandler(ctx);
5502
+ if (result instanceof Response) {
5503
+ return result;
5504
+ }
5505
+ return result;
5506
+ }
5507
+ return new Response(JSON.stringify({ error: "Not Found" }), {
5508
+ status: 404,
5509
+ headers: { "Content-Type": "application/json" }
5510
+ });
5511
+ }
5512
+ /**
5513
+ * Collect middleware for a specific path
5514
+ * (Simplified version - assumes we've already checked for pure static)
5515
+ */
5516
+ collectMiddlewareForPath(path2, routeMiddleware) {
5517
+ if (this.router.globalMiddleware.length === 0 && this.router.pathMiddleware.size === 0) {
5518
+ return routeMiddleware;
5519
+ }
5520
+ return this.router.collectMiddlewarePublic(path2, routeMiddleware);
5521
+ }
5522
+ /**
5523
+ * Compile routes for optimization
5524
+ * Called once during initialization and when routes change
5525
+ */
5526
+ compileRoutes() {
5527
+ this.staticRoutes = this.router.staticRoutes;
5528
+ const hasGlobalMiddleware = this.router.globalMiddleware.length > 0;
5529
+ const hasPathMiddleware = this.router.pathMiddleware.size > 0;
5530
+ this.isPureStaticApp = !hasGlobalMiddleware && !hasPathMiddleware;
5531
+ for (const [_key, route] of this.staticRoutes) {
5532
+ const analysis = analyzeHandler(route.handler);
5533
+ const optimalType = getOptimalContextType(analysis);
5534
+ route.useMinimal = this.isPureStaticApp && route.middleware.length === 0 && optimalType === "minimal";
5535
+ }
5536
+ }
5537
+ // ─────────────────────────────────────────────────────────────────────────
5538
+ // Internal Methods
5539
+ // ─────────────────────────────────────────────────────────────────────────
5540
+ /**
5541
+ * Add a route to the router
5542
+ */
5543
+ addRoute(method, path2, handlers) {
5544
+ if (handlers.length === 0) {
5545
+ throw new Error(`No handler provided for ${method.toUpperCase()} ${path2}`);
5546
+ }
5547
+ const handler = handlers[handlers.length - 1];
5548
+ const middleware = handlers.slice(0, -1);
5549
+ this.router.add(method, path2, handler, middleware);
5550
+ this.compileRoutes();
5551
+ return this;
5552
+ }
5553
+ /**
5554
+ * Execute middleware chain followed by handler
5555
+ *
5556
+ * Implements the standard middleware pattern:
5557
+ * Each middleware can call next() to continue the chain.
5558
+ */
5559
+ async executeMiddleware(ctx, middleware, handler) {
5560
+ let index = 0;
5561
+ const next = async () => {
5562
+ if (index < middleware.length) {
5563
+ const mw = middleware[index++];
5564
+ return await mw(ctx, next);
5565
+ }
5566
+ return void 0;
5567
+ };
5568
+ const result = await next();
5569
+ if (result instanceof Response) {
5570
+ return result;
5571
+ }
5572
+ return await handler(ctx);
5573
+ }
5574
+ /*
5575
+ * Handle 404 Not Found (Async version for dynamic/middleware paths)
5576
+ * Note: Currently unused as we handle 404s via handleNotFoundSync or inline
5577
+ */
5578
+ // private async handleNotFound(ctx: FastContext): Promise<Response> {
5579
+ // if (this.notFoundHandler) {
5580
+ // return await this.notFoundHandler(ctx)
5581
+ // }
5582
+ // return ctx.json({ error: 'Not Found' }, 404)
5583
+ // }
5584
+ /**
5585
+ * Handle errors (Async version for dynamic/middleware paths)
5586
+ */
5587
+ async handleError(error, ctx) {
5588
+ if (this.errorHandler) {
5589
+ return await this.errorHandler(error, ctx);
5590
+ }
5591
+ console.error("Unhandled error:", error);
5592
+ return ctx.json(
5593
+ {
5594
+ error: "Internal Server Error",
5595
+ message: error.message
5596
+ },
5597
+ 500
5598
+ );
5599
+ }
5600
+ };
5601
+
4607
5602
  // src/index.ts
4608
5603
  var VERSION = package_default.version;
4609
5604
  function defineConfig(config2) {
@@ -4634,6 +5629,7 @@ export {
4634
5629
  PhotonRequestWrapper,
4635
5630
  PlanetCore,
4636
5631
  Route,
5632
+ RouteGroup,
4637
5633
  Router,
4638
5634
  ServiceProvider,
4639
5635
  Str,
@@ -4662,6 +5658,7 @@ export {
4662
5658
  dd,
4663
5659
  defineConfig,
4664
5660
  dump,
5661
+ engine_exports as engine,
4665
5662
  env,
4666
5663
  errors,
4667
5664
  fail,