@gravito/core 1.0.0-beta.6 → 1.1.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-beta.6",
16
+ version: "1.1.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: [
@@ -107,7 +117,13 @@ var PhotonRequestWrapper = class {
107
117
  return this.photonCtx.req.header();
108
118
  }
109
119
  async json() {
110
- return this.photonCtx.req.json();
120
+ const ctx = this.photonCtx;
121
+ if (ctx._cachedJsonBody !== void 0) {
122
+ return ctx._cachedJsonBody;
123
+ }
124
+ const body = await this.photonCtx.req.json();
125
+ ctx._cachedJsonBody = body;
126
+ return body;
111
127
  }
112
128
  async text() {
113
129
  return this.photonCtx.req.text();
@@ -2982,12 +2998,6 @@ var Router = class {
2982
2998
  }
2983
2999
  handlers.push(resolvedHandler);
2984
3000
  if (options.domain) {
2985
- const _wrappedHandler = async (c) => {
2986
- if (c.req.header("host") !== options.domain) {
2987
- return new Response("Not Found", { status: 404 });
2988
- }
2989
- return void 0;
2990
- };
2991
3001
  const domainCheck = async (c, next) => {
2992
3002
  if (c.req.header("host") !== options.domain) {
2993
3003
  return c.text("Not Found", 404);
@@ -4603,6 +4613,944 @@ function createHttpTester(core) {
4603
4613
  return new HttpTester(core);
4604
4614
  }
4605
4615
 
4616
+ // src/engine/index.ts
4617
+ var engine_exports = {};
4618
+ __export(engine_exports, {
4619
+ AOTRouter: () => AOTRouter,
4620
+ FastContextImpl: () => FastContext,
4621
+ Gravito: () => Gravito,
4622
+ MinimalContext: () => MinimalContext,
4623
+ ObjectPool: () => ObjectPool,
4624
+ extractPath: () => extractPath
4625
+ });
4626
+
4627
+ // src/engine/AOTRouter.ts
4628
+ var AOTRouter = class {
4629
+ // Static route cache: "METHOD:PATH" -> RouteMetadata
4630
+ staticRoutes = /* @__PURE__ */ new Map();
4631
+ // Dynamic route handler (Radix Tree)
4632
+ dynamicRouter = new RadixRouter();
4633
+ // Global middleware (applies to all routes)
4634
+ globalMiddleware = [];
4635
+ // Path-based middleware: pattern -> middleware[]
4636
+ pathMiddleware = /* @__PURE__ */ new Map();
4637
+ /**
4638
+ * Register a route
4639
+ *
4640
+ * Automatically determines if route is static or dynamic.
4641
+ * Static routes are stored in a Map for O(1) lookup.
4642
+ * Dynamic routes use the Radix Tree.
4643
+ *
4644
+ * @param method - HTTP method
4645
+ * @param path - Route path
4646
+ * @param handler - Route handler
4647
+ * @param middleware - Route-specific middleware
4648
+ */
4649
+ add(method, path2, handler, middleware = []) {
4650
+ const normalizedMethod = method.toLowerCase();
4651
+ if (this.isStaticPath(path2)) {
4652
+ const key = `${normalizedMethod}:${path2}`;
4653
+ this.staticRoutes.set(key, { handler, middleware });
4654
+ } else {
4655
+ const wrappedHandler = handler;
4656
+ this.dynamicRouter.add(normalizedMethod, path2, [wrappedHandler]);
4657
+ if (middleware.length > 0) {
4658
+ this.pathMiddleware.set(`${normalizedMethod}:${path2}`, middleware);
4659
+ }
4660
+ }
4661
+ }
4662
+ /**
4663
+ * Add global middleware
4664
+ *
4665
+ * These run for every request, before route-specific middleware.
4666
+ *
4667
+ * @param middleware - Middleware functions
4668
+ */
4669
+ use(...middleware) {
4670
+ this.globalMiddleware.push(...middleware);
4671
+ }
4672
+ /**
4673
+ * Add path-based middleware
4674
+ *
4675
+ * Supports wildcard patterns like '/api/*'
4676
+ *
4677
+ * @param pattern - Path pattern
4678
+ * @param middleware - Middleware functions
4679
+ */
4680
+ usePattern(pattern, ...middleware) {
4681
+ const existing = this.pathMiddleware.get(pattern) ?? [];
4682
+ this.pathMiddleware.set(pattern, [...existing, ...middleware]);
4683
+ }
4684
+ /**
4685
+ * Match a request to a route
4686
+ *
4687
+ * Returns the handler, params, and all applicable middleware.
4688
+ *
4689
+ * @param method - HTTP method
4690
+ * @param path - Request path
4691
+ * @returns Route match or null if not found
4692
+ */
4693
+ match(method, path2) {
4694
+ const normalizedMethod = method.toLowerCase();
4695
+ const staticKey = `${normalizedMethod}:${path2}`;
4696
+ const staticRoute = this.staticRoutes.get(staticKey);
4697
+ if (staticRoute) {
4698
+ return {
4699
+ handler: staticRoute.handler,
4700
+ params: {},
4701
+ middleware: this.collectMiddleware(path2, staticRoute.middleware)
4702
+ };
4703
+ }
4704
+ const match = this.dynamicRouter.match(normalizedMethod, path2);
4705
+ if (match && match.handlers.length > 0) {
4706
+ const handler = match.handlers[0];
4707
+ const routeKey = this.findDynamicRouteKey(normalizedMethod, path2);
4708
+ const routeMiddleware = routeKey ? this.pathMiddleware.get(routeKey) ?? [] : [];
4709
+ return {
4710
+ handler,
4711
+ params: match.params,
4712
+ middleware: this.collectMiddleware(path2, routeMiddleware)
4713
+ };
4714
+ }
4715
+ return {
4716
+ handler: null,
4717
+ params: {},
4718
+ middleware: []
4719
+ };
4720
+ }
4721
+ /**
4722
+ * Public wrapper for collectMiddleware (used by Gravito for optimization)
4723
+ */
4724
+ collectMiddlewarePublic(path2, routeMiddleware) {
4725
+ return this.collectMiddleware(path2, routeMiddleware);
4726
+ }
4727
+ /**
4728
+ * Collect all applicable middleware for a path
4729
+ *
4730
+ * Order: global -> pattern-based -> route-specific
4731
+ *
4732
+ * @param path - Request path
4733
+ * @param routeMiddleware - Route-specific middleware
4734
+ * @returns Combined middleware array
4735
+ */
4736
+ collectMiddleware(path2, routeMiddleware) {
4737
+ if (this.globalMiddleware.length === 0 && this.pathMiddleware.size === 0 && routeMiddleware.length === 0) {
4738
+ return [];
4739
+ }
4740
+ const middleware = [];
4741
+ if (this.globalMiddleware.length > 0) {
4742
+ middleware.push(...this.globalMiddleware);
4743
+ }
4744
+ if (this.pathMiddleware.size > 0) {
4745
+ for (const [pattern, mw] of this.pathMiddleware) {
4746
+ if (pattern.includes(":")) continue;
4747
+ if (this.matchPattern(pattern, path2)) {
4748
+ middleware.push(...mw);
4749
+ }
4750
+ }
4751
+ }
4752
+ if (routeMiddleware.length > 0) {
4753
+ middleware.push(...routeMiddleware);
4754
+ }
4755
+ return middleware;
4756
+ }
4757
+ /**
4758
+ * Check if a path is static (no parameters or wildcards)
4759
+ */
4760
+ isStaticPath(path2) {
4761
+ return !path2.includes(":") && !path2.includes("*");
4762
+ }
4763
+ /**
4764
+ * Match a pattern against a path
4765
+ *
4766
+ * Supports:
4767
+ * - Exact match: '/api/users'
4768
+ * - Wildcard suffix: '/api/*'
4769
+ *
4770
+ * @param pattern - Pattern to match
4771
+ * @param path - Path to test
4772
+ * @returns True if pattern matches
4773
+ */
4774
+ matchPattern(pattern, path2) {
4775
+ if (pattern === "*") return true;
4776
+ if (pattern === path2) return true;
4777
+ if (pattern.endsWith("/*")) {
4778
+ const prefix = pattern.slice(0, -2);
4779
+ return path2.startsWith(prefix);
4780
+ }
4781
+ return false;
4782
+ }
4783
+ /**
4784
+ * Find the original route key for a matched dynamic route
4785
+ *
4786
+ * This is needed to look up route-specific middleware.
4787
+ * It's a bit of a hack, but avoids storing duplicate data.
4788
+ *
4789
+ * @param method - HTTP method
4790
+ * @param path - Matched path
4791
+ * @returns Route key or null
4792
+ */
4793
+ findDynamicRouteKey(method, path2) {
4794
+ for (const key of this.pathMiddleware.keys()) {
4795
+ if (key.startsWith(`${method}:`)) {
4796
+ return key;
4797
+ }
4798
+ }
4799
+ return null;
4800
+ }
4801
+ /**
4802
+ * Get all registered routes (for debugging)
4803
+ */
4804
+ getRoutes() {
4805
+ const routes = [];
4806
+ for (const key of this.staticRoutes.keys()) {
4807
+ const [method, path2] = key.split(":");
4808
+ routes.push({ method, path: path2, type: "static" });
4809
+ }
4810
+ return routes;
4811
+ }
4812
+ };
4813
+
4814
+ // src/engine/analyzer.ts
4815
+ function analyzeHandler(handler) {
4816
+ const source = handler.toString();
4817
+ return {
4818
+ usesHeaders: source.includes(".header(") || source.includes(".header)") || source.includes(".headers(") || source.includes(".headers)"),
4819
+ usesQuery: source.includes(".query(") || source.includes(".query)") || source.includes(".queries(") || source.includes(".queries)"),
4820
+ usesBody: source.includes(".json()") || source.includes(".text()") || source.includes(".formData()") || source.includes(".body"),
4821
+ usesParams: source.includes(".param(") || source.includes(".param)") || source.includes(".params(") || source.includes(".params)"),
4822
+ isAsync: source.includes("async") || source.includes("await")
4823
+ };
4824
+ }
4825
+ function getOptimalContextType(analysis) {
4826
+ if (analysis.usesHeaders) {
4827
+ return "fast";
4828
+ }
4829
+ if (!analysis.usesQuery && !analysis.usesBody && !analysis.usesParams) {
4830
+ return "minimal";
4831
+ }
4832
+ if (!analysis.usesQuery && !analysis.usesBody && analysis.usesParams) {
4833
+ return "minimal";
4834
+ }
4835
+ if (analysis.usesBody) {
4836
+ return "full";
4837
+ }
4838
+ return "fast";
4839
+ }
4840
+
4841
+ // src/engine/FastContext.ts
4842
+ var FastRequestImpl = class {
4843
+ _request;
4844
+ _params;
4845
+ _url = new URL("http://localhost");
4846
+ // Reuse this object
4847
+ _query = null;
4848
+ _headers = null;
4849
+ /**
4850
+ * Reset for pooling
4851
+ */
4852
+ reset(request, params = {}) {
4853
+ this._request = request;
4854
+ this._params = params;
4855
+ this._url.href = request.url;
4856
+ this._query = null;
4857
+ this._headers = null;
4858
+ }
4859
+ get url() {
4860
+ return this._request.url;
4861
+ }
4862
+ get method() {
4863
+ return this._request.method;
4864
+ }
4865
+ get path() {
4866
+ return this._url.pathname;
4867
+ }
4868
+ param(name) {
4869
+ return this._params[name];
4870
+ }
4871
+ params() {
4872
+ return { ...this._params };
4873
+ }
4874
+ query(name) {
4875
+ if (!this._query) {
4876
+ this._query = this._url.searchParams;
4877
+ }
4878
+ return this._query.get(name) ?? void 0;
4879
+ }
4880
+ queries() {
4881
+ if (!this._query) {
4882
+ this._query = this._url.searchParams;
4883
+ }
4884
+ const result = {};
4885
+ for (const [key, value2] of this._query.entries()) {
4886
+ const existing = result[key];
4887
+ if (existing === void 0) {
4888
+ result[key] = value2;
4889
+ } else if (Array.isArray(existing)) {
4890
+ existing.push(value2);
4891
+ } else {
4892
+ result[key] = [existing, value2];
4893
+ }
4894
+ }
4895
+ return result;
4896
+ }
4897
+ header(name) {
4898
+ return this._request.headers.get(name) ?? void 0;
4899
+ }
4900
+ headers() {
4901
+ if (!this._headers) {
4902
+ this._headers = {};
4903
+ for (const [key, value2] of this._request.headers.entries()) {
4904
+ this._headers[key] = value2;
4905
+ }
4906
+ }
4907
+ return { ...this._headers };
4908
+ }
4909
+ async json() {
4910
+ return this._request.json();
4911
+ }
4912
+ async text() {
4913
+ return this._request.text();
4914
+ }
4915
+ async formData() {
4916
+ return this._request.formData();
4917
+ }
4918
+ get raw() {
4919
+ return this._request;
4920
+ }
4921
+ };
4922
+ var FastContext = class {
4923
+ _req = new FastRequestImpl();
4924
+ _statusCode = 200;
4925
+ _headers = new Headers();
4926
+ // Reuse this object
4927
+ /**
4928
+ * Reset context for pooling
4929
+ *
4930
+ * This is called when acquiring from the pool.
4931
+ * Must clear all state from previous request.
4932
+ */
4933
+ reset(request, params = {}) {
4934
+ this._req.reset(request, params);
4935
+ this._statusCode = 200;
4936
+ this._headers = new Headers();
4937
+ return this;
4938
+ }
4939
+ get req() {
4940
+ return this._req;
4941
+ }
4942
+ // ─────────────────────────────────────────────────────────────────────────
4943
+ // Response Helpers
4944
+ // ─────────────────────────────────────────────────────────────────────────
4945
+ json(data, status = 200) {
4946
+ this._headers.set("Content-Type", "application/json; charset=utf-8");
4947
+ return new Response(JSON.stringify(data), {
4948
+ status,
4949
+ headers: this._headers
4950
+ });
4951
+ }
4952
+ text(text, status = 200) {
4953
+ this._headers.set("Content-Type", "text/plain; charset=utf-8");
4954
+ return new Response(text, {
4955
+ status,
4956
+ headers: this._headers
4957
+ });
4958
+ }
4959
+ html(html, status = 200) {
4960
+ this._headers.set("Content-Type", "text/html; charset=utf-8");
4961
+ return new Response(html, {
4962
+ status,
4963
+ headers: this._headers
4964
+ });
4965
+ }
4966
+ redirect(url, status = 302) {
4967
+ this._headers.set("Location", url);
4968
+ return new Response(null, {
4969
+ status,
4970
+ headers: this._headers
4971
+ });
4972
+ }
4973
+ body(data, status = 200) {
4974
+ return new Response(data, {
4975
+ status,
4976
+ headers: this._headers
4977
+ });
4978
+ }
4979
+ // ─────────────────────────────────────────────────────────────────────────
4980
+ // Header Management
4981
+ // ─────────────────────────────────────────────────────────────────────────
4982
+ header(name, value2) {
4983
+ this._headers.set(name, value2);
4984
+ }
4985
+ status(code) {
4986
+ this._statusCode = code;
4987
+ }
4988
+ };
4989
+
4990
+ // src/engine/MinimalContext.ts
4991
+ var MinimalRequest = class {
4992
+ constructor(_request, _params, _path) {
4993
+ this._request = _request;
4994
+ this._params = _params;
4995
+ this._path = _path;
4996
+ }
4997
+ get url() {
4998
+ return this._request.url;
4999
+ }
5000
+ get method() {
5001
+ return this._request.method;
5002
+ }
5003
+ get path() {
5004
+ return this._path;
5005
+ }
5006
+ param(name) {
5007
+ return this._params[name];
5008
+ }
5009
+ params() {
5010
+ return { ...this._params };
5011
+ }
5012
+ query(name) {
5013
+ const url = new URL(this._request.url);
5014
+ return url.searchParams.get(name) ?? void 0;
5015
+ }
5016
+ queries() {
5017
+ const url = new URL(this._request.url);
5018
+ const result = {};
5019
+ for (const [key, value2] of url.searchParams.entries()) {
5020
+ const existing = result[key];
5021
+ if (existing === void 0) {
5022
+ result[key] = value2;
5023
+ } else if (Array.isArray(existing)) {
5024
+ existing.push(value2);
5025
+ } else {
5026
+ result[key] = [existing, value2];
5027
+ }
5028
+ }
5029
+ return result;
5030
+ }
5031
+ header(name) {
5032
+ return this._request.headers.get(name) ?? void 0;
5033
+ }
5034
+ headers() {
5035
+ const result = {};
5036
+ for (const [key, value2] of this._request.headers.entries()) {
5037
+ result[key] = value2;
5038
+ }
5039
+ return result;
5040
+ }
5041
+ async json() {
5042
+ return this._request.json();
5043
+ }
5044
+ async text() {
5045
+ return this._request.text();
5046
+ }
5047
+ async formData() {
5048
+ return this._request.formData();
5049
+ }
5050
+ get raw() {
5051
+ return this._request;
5052
+ }
5053
+ };
5054
+ var MinimalContext = class {
5055
+ _req;
5056
+ constructor(request, params, path2) {
5057
+ this._req = new MinimalRequest(request, params, path2);
5058
+ }
5059
+ get req() {
5060
+ return this._req;
5061
+ }
5062
+ // Response helpers - create headers inline (no reuse overhead)
5063
+ json(data, status = 200) {
5064
+ return new Response(JSON.stringify(data), {
5065
+ status,
5066
+ headers: { "Content-Type": "application/json; charset=utf-8" }
5067
+ });
5068
+ }
5069
+ text(text, status = 200) {
5070
+ return new Response(text, {
5071
+ status,
5072
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
5073
+ });
5074
+ }
5075
+ html(html, status = 200) {
5076
+ return new Response(html, {
5077
+ status,
5078
+ headers: { "Content-Type": "text/html; charset=utf-8" }
5079
+ });
5080
+ }
5081
+ redirect(url, status = 302) {
5082
+ return new Response(null, {
5083
+ status,
5084
+ headers: { Location: url }
5085
+ });
5086
+ }
5087
+ body(data, status = 200) {
5088
+ return new Response(data, { status });
5089
+ }
5090
+ header(_name, _value) {
5091
+ console.warn("MinimalContext.header() is a no-op. Use FastContext for custom headers.");
5092
+ }
5093
+ status(_code) {
5094
+ }
5095
+ // Required for interface compatibility
5096
+ reset(_request, _params) {
5097
+ throw new Error("MinimalContext does not support reset. Create a new instance instead.");
5098
+ }
5099
+ };
5100
+
5101
+ // src/engine/path.ts
5102
+ function extractPath(url) {
5103
+ const protocolEnd = url.indexOf("://");
5104
+ const searchStart = protocolEnd === -1 ? 0 : protocolEnd + 3;
5105
+ const pathStart = url.indexOf("/", searchStart);
5106
+ if (pathStart === -1) {
5107
+ return "/";
5108
+ }
5109
+ const queryStart = url.indexOf("?", pathStart);
5110
+ if (queryStart === -1) {
5111
+ return url.slice(pathStart);
5112
+ }
5113
+ return url.slice(pathStart, queryStart);
5114
+ }
5115
+
5116
+ // src/engine/pool.ts
5117
+ var ObjectPool = class {
5118
+ pool = [];
5119
+ factory;
5120
+ reset;
5121
+ maxSize;
5122
+ /**
5123
+ * Create a new object pool
5124
+ *
5125
+ * @param factory - Function to create new objects
5126
+ * @param reset - Function to reset objects before reuse
5127
+ * @param maxSize - Maximum pool size (default: 256)
5128
+ */
5129
+ constructor(factory, reset, maxSize = 256) {
5130
+ this.factory = factory;
5131
+ this.reset = reset;
5132
+ this.maxSize = maxSize;
5133
+ }
5134
+ /**
5135
+ * Acquire an object from the pool
5136
+ *
5137
+ * If the pool is empty, creates a new object (overflow strategy).
5138
+ * This ensures the pool never blocks under high load.
5139
+ *
5140
+ * @returns Object from pool or newly created
5141
+ */
5142
+ acquire() {
5143
+ const obj = this.pool.pop();
5144
+ if (obj !== void 0) {
5145
+ return obj;
5146
+ }
5147
+ return this.factory();
5148
+ }
5149
+ /**
5150
+ * Release an object back to the pool
5151
+ *
5152
+ * If the pool is full, the object is discarded (will be GC'd).
5153
+ * This prevents unbounded memory growth.
5154
+ *
5155
+ * @param obj - Object to release
5156
+ */
5157
+ release(obj) {
5158
+ if (this.pool.length < this.maxSize) {
5159
+ this.reset(obj);
5160
+ this.pool.push(obj);
5161
+ }
5162
+ }
5163
+ /**
5164
+ * Clear all objects from the pool
5165
+ *
5166
+ * Useful for testing or when you need to force a clean slate.
5167
+ */
5168
+ clear() {
5169
+ this.pool = [];
5170
+ }
5171
+ /**
5172
+ * Get current pool size
5173
+ */
5174
+ get size() {
5175
+ return this.pool.length;
5176
+ }
5177
+ /**
5178
+ * Get maximum pool size
5179
+ */
5180
+ get capacity() {
5181
+ return this.maxSize;
5182
+ }
5183
+ /**
5184
+ * Pre-warm the pool by creating objects in advance
5185
+ *
5186
+ * This can reduce latency for the first N requests.
5187
+ *
5188
+ * @param count - Number of objects to pre-create
5189
+ */
5190
+ prewarm(count) {
5191
+ const targetSize = Math.min(count, this.maxSize);
5192
+ while (this.pool.length < targetSize) {
5193
+ const obj = this.factory();
5194
+ this.reset(obj);
5195
+ this.pool.push(obj);
5196
+ }
5197
+ }
5198
+ };
5199
+
5200
+ // src/engine/Gravito.ts
5201
+ var Gravito = class {
5202
+ router = new AOTRouter();
5203
+ contextPool;
5204
+ errorHandler;
5205
+ notFoundHandler;
5206
+ // Direct reference to static routes Map (O(1) access)
5207
+ // Optimization: Bypass getter/setter overhead
5208
+ staticRoutes;
5209
+ // Flag: pure static app (no middleware at all) allows ultra-fast path
5210
+ isPureStaticApp = true;
5211
+ /**
5212
+ * Create a new Gravito instance
5213
+ *
5214
+ * @param options - Engine configuration options
5215
+ */
5216
+ constructor(options = {}) {
5217
+ const poolSize = options.poolSize ?? 256;
5218
+ this.contextPool = new ObjectPool(
5219
+ () => new FastContext(),
5220
+ (ctx) => ctx.reset(new Request("http://localhost")),
5221
+ poolSize
5222
+ );
5223
+ this.contextPool.prewarm(Math.min(32, poolSize));
5224
+ if (options.onError) {
5225
+ this.errorHandler = options.onError;
5226
+ }
5227
+ if (options.onNotFound) {
5228
+ this.notFoundHandler = options.onNotFound;
5229
+ }
5230
+ this.compileRoutes();
5231
+ }
5232
+ // ─────────────────────────────────────────────────────────────────────────
5233
+ // HTTP Method Registration
5234
+ // ─────────────────────────────────────────────────────────────────────────
5235
+ /**
5236
+ * Register a GET route
5237
+ *
5238
+ * @param path - Route path (e.g., '/users/:id')
5239
+ * @param handlers - Handler and optional middleware
5240
+ * @returns This instance for chaining
5241
+ */
5242
+ get(path2, ...handlers) {
5243
+ return this.addRoute("get", path2, handlers);
5244
+ }
5245
+ /**
5246
+ * Register a POST route
5247
+ */
5248
+ post(path2, ...handlers) {
5249
+ return this.addRoute("post", path2, handlers);
5250
+ }
5251
+ /**
5252
+ * Register a PUT route
5253
+ */
5254
+ put(path2, ...handlers) {
5255
+ return this.addRoute("put", path2, handlers);
5256
+ }
5257
+ /**
5258
+ * Register a DELETE route
5259
+ */
5260
+ delete(path2, ...handlers) {
5261
+ return this.addRoute("delete", path2, handlers);
5262
+ }
5263
+ /**
5264
+ * Register a PATCH route
5265
+ */
5266
+ patch(path2, ...handlers) {
5267
+ return this.addRoute("patch", path2, handlers);
5268
+ }
5269
+ /**
5270
+ * Register an OPTIONS route
5271
+ */
5272
+ options(path2, ...handlers) {
5273
+ return this.addRoute("options", path2, handlers);
5274
+ }
5275
+ /**
5276
+ * Register a HEAD route
5277
+ */
5278
+ head(path2, ...handlers) {
5279
+ return this.addRoute("head", path2, handlers);
5280
+ }
5281
+ /**
5282
+ * Register a route for all HTTP methods
5283
+ */
5284
+ all(path2, ...handlers) {
5285
+ const methods = ["get", "post", "put", "delete", "patch", "options", "head"];
5286
+ for (const method of methods) {
5287
+ this.addRoute(method, path2, handlers);
5288
+ }
5289
+ return this;
5290
+ }
5291
+ use(pathOrMiddleware, ...middleware) {
5292
+ this.isPureStaticApp = false;
5293
+ if (typeof pathOrMiddleware === "string") {
5294
+ this.router.usePattern(pathOrMiddleware, ...middleware);
5295
+ } else {
5296
+ this.router.use(pathOrMiddleware, ...middleware);
5297
+ }
5298
+ return this;
5299
+ }
5300
+ // ─────────────────────────────────────────────────────────────────────────
5301
+ // Route Grouping
5302
+ // ─────────────────────────────────────────────────────────────────────────
5303
+ /**
5304
+ * Mount a sub-application at a path prefix
5305
+ *
5306
+ * @example
5307
+ * ```typescript
5308
+ * const api = new Gravito()
5309
+ * api.get('/users', (c) => c.json({ users: [] }))
5310
+ *
5311
+ * const app = new Gravito()
5312
+ * app.route('/api', api)
5313
+ * // Now accessible at /api/users
5314
+ * ```
5315
+ */
5316
+ route(path2, app2) {
5317
+ console.warn("route() method is not yet fully implemented");
5318
+ return this;
5319
+ }
5320
+ // ─────────────────────────────────────────────────────────────────────────
5321
+ // Error Handling
5322
+ // ─────────────────────────────────────────────────────────────────────────
5323
+ /**
5324
+ * Set custom error handler
5325
+ *
5326
+ * @example
5327
+ * ```typescript
5328
+ * app.onError((err, c) => {
5329
+ * console.error(err)
5330
+ * return c.json({ error: err.message }, 500)
5331
+ * })
5332
+ * ```
5333
+ */
5334
+ onError(handler) {
5335
+ this.errorHandler = handler;
5336
+ return this;
5337
+ }
5338
+ /**
5339
+ * Set custom 404 handler
5340
+ *
5341
+ * @example
5342
+ * ```typescript
5343
+ * app.notFound((c) => {
5344
+ * return c.json({ error: 'Not Found' }, 404)
5345
+ * })
5346
+ * ```
5347
+ */
5348
+ notFound(handler) {
5349
+ this.notFoundHandler = handler;
5350
+ return this;
5351
+ }
5352
+ // ─────────────────────────────────────────────────────────────────────────
5353
+ // Request Handling (Bun.serve integration)
5354
+ // ─────────────────────────────────────────────────────────────────────────
5355
+ /**
5356
+ * Handle an incoming request
5357
+ *
5358
+ * Optimized for minimal allocations and maximum throughput.
5359
+ * Uses sync/async dual-path strategy inspired by Hono.
5360
+ *
5361
+ * @param request - Incoming Request object
5362
+ * @returns Response object (sync or async)
5363
+ */
5364
+ fetch = (request) => {
5365
+ const path2 = extractPath(request.url);
5366
+ const method = request.method.toLowerCase();
5367
+ const staticKey = `${method}:${path2}`;
5368
+ const staticRoute = this.staticRoutes.get(staticKey);
5369
+ if (staticRoute) {
5370
+ if (staticRoute.useMinimal) {
5371
+ const ctx = new MinimalContext(request, {}, path2);
5372
+ try {
5373
+ const result = staticRoute.handler(ctx);
5374
+ if (result instanceof Response) {
5375
+ return result;
5376
+ }
5377
+ return result;
5378
+ } catch (error) {
5379
+ return this.handleErrorSync(error, request, path2);
5380
+ }
5381
+ }
5382
+ return this.handleWithMiddleware(request, path2, staticRoute);
5383
+ }
5384
+ return this.handleDynamicRoute(request, method, path2);
5385
+ };
5386
+ /**
5387
+ * Handle routes with middleware (async path)
5388
+ */
5389
+ async handleWithMiddleware(request, path2, route) {
5390
+ const ctx = this.contextPool.acquire();
5391
+ try {
5392
+ ctx.reset(request, {});
5393
+ const middleware = this.collectMiddlewareForPath(path2, route.middleware);
5394
+ if (middleware.length === 0) {
5395
+ return await route.handler(ctx);
5396
+ }
5397
+ return await this.executeMiddleware(ctx, middleware, route.handler);
5398
+ } catch (error) {
5399
+ return await this.handleError(error, ctx);
5400
+ } finally {
5401
+ this.contextPool.release(ctx);
5402
+ }
5403
+ }
5404
+ /**
5405
+ * Handle dynamic routes (Radix Tree lookup)
5406
+ */
5407
+ handleDynamicRoute(request, method, path2) {
5408
+ const match = this.router.match(method.toUpperCase(), path2);
5409
+ if (!match.handler) {
5410
+ return this.handleNotFoundSync(request, path2);
5411
+ }
5412
+ const ctx = this.contextPool.acquire();
5413
+ const execute = async () => {
5414
+ try {
5415
+ ctx.reset(request, match.params);
5416
+ if (match.middleware.length === 0) {
5417
+ return await match.handler(ctx);
5418
+ }
5419
+ return await this.executeMiddleware(ctx, match.middleware, match.handler);
5420
+ } catch (error) {
5421
+ return await this.handleError(error, ctx);
5422
+ } finally {
5423
+ this.contextPool.release(ctx);
5424
+ }
5425
+ };
5426
+ return execute();
5427
+ }
5428
+ /**
5429
+ * Sync error handler (for ultra-fast path)
5430
+ */
5431
+ handleErrorSync(error, request, path2) {
5432
+ if (this.errorHandler) {
5433
+ const ctx = new MinimalContext(request, {}, path2);
5434
+ const result = this.errorHandler(error, ctx);
5435
+ if (result instanceof Response) {
5436
+ return result;
5437
+ }
5438
+ return result;
5439
+ }
5440
+ console.error("Unhandled error:", error);
5441
+ return new Response(
5442
+ JSON.stringify({
5443
+ error: "Internal Server Error",
5444
+ message: error.message
5445
+ }),
5446
+ {
5447
+ status: 500,
5448
+ headers: { "Content-Type": "application/json" }
5449
+ }
5450
+ );
5451
+ }
5452
+ /**
5453
+ * Sync 404 handler (for ultra-fast path)
5454
+ */
5455
+ handleNotFoundSync(request, path2) {
5456
+ if (this.notFoundHandler) {
5457
+ const ctx = new MinimalContext(request, {}, path2);
5458
+ const result = this.notFoundHandler(ctx);
5459
+ if (result instanceof Response) {
5460
+ return result;
5461
+ }
5462
+ return result;
5463
+ }
5464
+ return new Response(JSON.stringify({ error: "Not Found" }), {
5465
+ status: 404,
5466
+ headers: { "Content-Type": "application/json" }
5467
+ });
5468
+ }
5469
+ /**
5470
+ * Collect middleware for a specific path
5471
+ * (Simplified version - assumes we've already checked for pure static)
5472
+ */
5473
+ collectMiddlewareForPath(path2, routeMiddleware) {
5474
+ if (this.router.globalMiddleware.length === 0 && this.router.pathMiddleware.size === 0) {
5475
+ return routeMiddleware;
5476
+ }
5477
+ return this.router.collectMiddlewarePublic(path2, routeMiddleware);
5478
+ }
5479
+ /**
5480
+ * Compile routes for optimization
5481
+ * Called once during initialization and when routes change
5482
+ */
5483
+ compileRoutes() {
5484
+ this.staticRoutes = this.router.staticRoutes;
5485
+ const hasGlobalMiddleware = this.router.globalMiddleware.length > 0;
5486
+ const hasPathMiddleware = this.router.pathMiddleware.size > 0;
5487
+ this.isPureStaticApp = !hasGlobalMiddleware && !hasPathMiddleware;
5488
+ for (const [_key, route] of this.staticRoutes) {
5489
+ const analysis = analyzeHandler(route.handler);
5490
+ const optimalType = getOptimalContextType(analysis);
5491
+ route.useMinimal = this.isPureStaticApp && route.middleware.length === 0 && optimalType === "minimal";
5492
+ }
5493
+ }
5494
+ // ─────────────────────────────────────────────────────────────────────────
5495
+ // Internal Methods
5496
+ // ─────────────────────────────────────────────────────────────────────────
5497
+ /**
5498
+ * Add a route to the router
5499
+ */
5500
+ addRoute(method, path2, handlers) {
5501
+ if (handlers.length === 0) {
5502
+ throw new Error(`No handler provided for ${method.toUpperCase()} ${path2}`);
5503
+ }
5504
+ const handler = handlers[handlers.length - 1];
5505
+ const middleware = handlers.slice(0, -1);
5506
+ this.router.add(method, path2, handler, middleware);
5507
+ this.compileRoutes();
5508
+ return this;
5509
+ }
5510
+ /**
5511
+ * Execute middleware chain followed by handler
5512
+ *
5513
+ * Implements the standard middleware pattern:
5514
+ * Each middleware can call next() to continue the chain.
5515
+ */
5516
+ async executeMiddleware(ctx, middleware, handler) {
5517
+ let index = 0;
5518
+ const next = async () => {
5519
+ if (index < middleware.length) {
5520
+ const mw = middleware[index++];
5521
+ await mw(ctx, next);
5522
+ }
5523
+ };
5524
+ await next();
5525
+ return await handler(ctx);
5526
+ }
5527
+ /**
5528
+ * Handle 404 Not Found (Async version for dynamic/middleware paths)
5529
+ */
5530
+ async handleNotFound(ctx) {
5531
+ if (this.notFoundHandler) {
5532
+ return await this.notFoundHandler(ctx);
5533
+ }
5534
+ return ctx.json({ error: "Not Found" }, 404);
5535
+ }
5536
+ /**
5537
+ * Handle errors (Async version for dynamic/middleware paths)
5538
+ */
5539
+ async handleError(error, ctx) {
5540
+ if (this.errorHandler) {
5541
+ return await this.errorHandler(error, ctx);
5542
+ }
5543
+ console.error("Unhandled error:", error);
5544
+ return ctx.json(
5545
+ {
5546
+ error: "Internal Server Error",
5547
+ message: error.message
5548
+ },
5549
+ 500
5550
+ );
5551
+ }
5552
+ };
5553
+
4606
5554
  // src/index.ts
4607
5555
  var VERSION = package_default.version;
4608
5556
  function defineConfig(config2) {
@@ -4661,6 +5609,7 @@ export {
4661
5609
  dd,
4662
5610
  defineConfig,
4663
5611
  dump,
5612
+ engine_exports as engine,
4664
5613
  env,
4665
5614
  errors,
4666
5615
  fail,