@hyperspan/framework 0.1.3 → 0.1.4

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/server.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
- import { Context, Handler, Hono } from 'hono';
3
+ import { Context, Hono } from 'hono';
4
4
 
5
5
  declare class TmplHtml {
6
6
  _kind: string;
@@ -15,84 +15,46 @@ declare class TmplHtml {
15
15
  constructor(props: Pick<TmplHtml, "content" | "asyncContent">);
16
16
  }
17
17
  export declare const IS_PROD: boolean;
18
- /**
19
- * Route
20
- * Define a route that can handle a direct HTTP request
21
- * Route handlers should return a Response or TmplHtml object
22
- */
23
- export declare function createRoute(handler: Handler): HSRoute;
24
- /**
25
- * Component
26
- * Define a component or partial with an optional loading placeholder
27
- * These can be rendered anywhere inside other templates - even if async.
28
- */
29
- export declare function createComponent(render: () => THSComponentReturn | Promise<THSComponentReturn>): HSComponent;
30
- /**
31
- * Form + route handler
32
- * Automatically handles and parses form data
33
- *
34
- * INITIAL IDEA OF HOW THIS WILL WORK:
35
- * ---
36
- * 1. Renders component as initial form markup for GET request
37
- * 2. Bind form onSubmit function to custom client JS handling
38
- * 3. Submits form with JavaScript fetch()
39
- * 4. Replaces form content with content from server
40
- * 5. All validation and save logic is on the server
41
- * 6. Handles any Exception thrown on server as error displayed in client
42
- */
43
- export declare function createForm(renderForm: (data?: any) => THSResponseTypes, schema?: z.ZodSchema | null): HSFormRoute;
44
18
  /**
45
19
  * Types
46
20
  */
47
- export type THSComponentReturn = TmplHtml | string | number | null;
48
21
  export type THSResponseTypes = TmplHtml | Response | string | null;
49
- export declare const HS_DEFAULT_LOADING: () => TmplHtml;
50
- /**
51
- * Route handler helper
52
- */
53
- export declare class HSComponent {
54
- _kind: string;
55
- _handlers: Record<string, Handler>;
56
- _loading?: () => TmplHtml;
57
- render: () => THSComponentReturn | Promise<THSComponentReturn>;
58
- constructor(render: () => THSComponentReturn | Promise<THSComponentReturn>);
59
- loading(fn: () => TmplHtml): this;
60
- }
22
+ export type THSRouteHandler = (context: Context) => THSResponseTypes | Promise<THSResponseTypes>;
61
23
  /**
62
- * Route handler helper
24
+ * Route
25
+ * Define a route that can handle a direct HTTP request
26
+ * Route handlers should return a Response or TmplHtml object
63
27
  */
64
- export declare class HSRoute {
28
+ export declare function createRoute(handler?: THSRouteHandler): {
65
29
  _kind: string;
66
- _handlers: Record<string, Handler>;
67
- _methods: null | string[];
68
- constructor(handler: Handler);
69
- }
30
+ get(handler: THSRouteHandler): any;
31
+ post(handler: THSRouteHandler): any;
32
+ put(handler: THSRouteHandler): any;
33
+ delete(handler: THSRouteHandler): any;
34
+ patch(handler: THSRouteHandler): any;
35
+ run(method: string, context: Context): Promise<Response>;
36
+ };
37
+ export type THSRoute = ReturnType<typeof createRoute>;
70
38
  /**
71
- * Form route handler helper
39
+ * Create new API Route
40
+ * API Route handlers should return a JSON object or a Response
72
41
  */
73
- export type THSFormRenderer = (data?: any) => THSResponseTypes;
74
- export declare class HSFormRoute {
42
+ export declare function createAPIRoute(handler?: THSRouteHandler): {
75
43
  _kind: string;
76
- _handlers: Record<string, Handler>;
77
- _form: THSFormRenderer;
78
- _methods: null | string[];
79
- _schema: null | z.ZodSchema;
80
- constructor(renderForm: THSFormRenderer, schema?: z.ZodSchema | null);
81
- getDefaultData(): unknown;
82
- /**
83
- * Get form renderer method
84
- */
85
- renderForm(data?: any): THSResponseTypes;
86
- get(handler: Handler): this;
87
- patch(handler: Handler): this;
88
- post(handler: Handler): this;
89
- put(handler: Handler): this;
90
- delete(handler: Handler): this;
91
- }
44
+ get(handler: THSRouteHandler): any;
45
+ post(handler: THSRouteHandler): any;
46
+ put(handler: THSRouteHandler): any;
47
+ delete(handler: THSRouteHandler): any;
48
+ patch(handler: THSRouteHandler): any;
49
+ run(method: string, context: Context): Promise<Response>;
50
+ };
51
+ export type THSAPIRoute = ReturnType<typeof createAPIRoute>;
92
52
  /**
93
- * Run route from file
53
+ * Get a Hyperspan runnable route from a module import
54
+ * @throws Error if no runnable route found
94
55
  */
95
- export declare function runFileRoute(RouteModule: any, context: Context): Promise<Response | false>;
56
+ export declare function getRunnableRoute(route: unknown): THSRoute;
57
+ export declare function isRouteRunnable(route: unknown): boolean;
96
58
  export type THSServerConfig = {
97
59
  appDir: string;
98
60
  staticFileRoot: string;
@@ -109,6 +71,10 @@ export type THSRouteMap = {
109
71
  params: string[];
110
72
  };
111
73
  export declare function buildRoutes(config: THSServerConfig): Promise<THSRouteMap[]>;
74
+ /**
75
+ * Run route from file
76
+ */
77
+ export declare function createRouteFromModule(RouteModule: any): (context: Context) => Promise<Response>;
112
78
  /**
113
79
  * Create and start Bun HTTP server
114
80
  */
package/dist/server.js CHANGED
@@ -1796,321 +1796,155 @@ var WSContext = class {
1796
1796
  }
1797
1797
  };
1798
1798
 
1799
- // node_modules/@zod/core/dist/esm/core.js
1800
- var $brand = Symbol("zod_brand");
1801
-
1802
- class $ZodAsyncError extends Error {
1803
- constructor() {
1804
- super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`);
1799
+ // node_modules/hono/dist/http-exception.js
1800
+ var HTTPException = class extends Error {
1801
+ res;
1802
+ status;
1803
+ constructor(status = 500, options) {
1804
+ super(options?.message, { cause: options?.cause });
1805
+ this.res = options?.res;
1806
+ this.status = status;
1807
+ }
1808
+ getResponse() {
1809
+ if (this.res) {
1810
+ const newResponse = new Response(this.res.body, {
1811
+ status: this.status,
1812
+ headers: this.res.headers
1813
+ });
1814
+ return newResponse;
1815
+ }
1816
+ return new Response(this.message, {
1817
+ status: this.status
1818
+ });
1805
1819
  }
1806
- }
1807
- var globalConfig = {};
1808
- function config(config2) {
1809
- if (config2)
1810
- Object.assign(globalConfig, config2);
1811
- return globalConfig;
1812
- }
1820
+ };
1813
1821
 
1814
- // node_modules/@zod/core/dist/esm/util.js
1815
- function jsonStringifyReplacer(_, value) {
1816
- if (typeof value === "bigint")
1817
- return value.toString();
1818
- return value;
1819
- }
1820
- function cached(getter) {
1821
- const set = false;
1822
- return {
1823
- get value() {
1824
- if (!set) {
1825
- const value = getter();
1826
- Object.defineProperty(this, "value", { value });
1827
- return value;
1822
+ // src/server.ts
1823
+ var IS_PROD = false;
1824
+ var CWD = process.cwd();
1825
+ function createRoute(handler) {
1826
+ let _handlers = {};
1827
+ if (handler) {
1828
+ _handlers["GET"] = handler;
1829
+ }
1830
+ const api = {
1831
+ _kind: "hsRoute",
1832
+ get(handler2) {
1833
+ _handlers["GET"] = handler2;
1834
+ return api;
1835
+ },
1836
+ post(handler2) {
1837
+ _handlers["POST"] = handler2;
1838
+ return api;
1839
+ },
1840
+ put(handler2) {
1841
+ _handlers["PUT"] = handler2;
1842
+ return api;
1843
+ },
1844
+ delete(handler2) {
1845
+ _handlers["DELETE"] = handler2;
1846
+ return api;
1847
+ },
1848
+ patch(handler2) {
1849
+ _handlers["PATCH"] = handler2;
1850
+ return api;
1851
+ },
1852
+ async run(method, context) {
1853
+ const handler2 = _handlers[method];
1854
+ if (!handler2) {
1855
+ throw new HTTPException(405, { message: "Method not allowed" });
1856
+ }
1857
+ const routeContent = await handler2(context);
1858
+ if (routeContent instanceof Response) {
1859
+ return routeContent;
1860
+ }
1861
+ const userIsBot = isbot(context.req.header("User-Agent"));
1862
+ const streamOpt = context.req.query("__nostream");
1863
+ const streamingEnabled = !userIsBot && (streamOpt !== undefined ? streamOpt : true);
1864
+ const routeKind = typeof routeContent;
1865
+ if (routeContent && routeKind === "object" && (routeContent instanceof TmplHtml || routeContent.constructor.name === "TmplHtml" || routeContent?._kind === "TmplHtml")) {
1866
+ if (streamingEnabled) {
1867
+ return new StreamResponse(renderStream(routeContent));
1868
+ } else {
1869
+ const output = await renderAsync(routeContent);
1870
+ return context.html(output);
1871
+ }
1828
1872
  }
1829
- throw new Error("cached value already set");
1873
+ return context.text(String(routeContent));
1830
1874
  }
1831
1875
  };
1876
+ return api;
1832
1877
  }
1833
- var allowsEval = cached(() => {
1834
- try {
1835
- new Function("");
1836
- return true;
1837
- } catch (_) {
1838
- return false;
1839
- }
1840
- });
1841
- var propertyKeyTypes = new Set(["string", "number", "symbol"]);
1842
- var primitiveTypes = new Set(["string", "number", "bigint", "boolean", "symbol", "undefined"]);
1843
- var NUMBER_FORMAT_RANGES = {
1844
- safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
1845
- int32: [-2147483648, 2147483647],
1846
- uint32: [0, 4294967295],
1847
- float32: [-340282346638528860000000000000000000000, 340282346638528860000000000000000000000],
1848
- float64: [-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000]
1849
- };
1850
- function unwrapMessage(message) {
1851
- return typeof message === "string" ? message : message?.message;
1852
- }
1853
- function finalizeIssue(iss, ctx, config2) {
1854
- const full = { ...iss, path: iss.path ?? [] };
1855
- if (!iss.message) {
1856
- const message = unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config2.customError?.(iss)) ?? unwrapMessage(config2.localeError?.(iss)) ?? "Invalid input";
1857
- full.message = message;
1858
- }
1859
- delete full.inst;
1860
- delete full.continue;
1861
- if (!ctx?.reportInput) {
1862
- delete full.input;
1863
- }
1864
- return full;
1865
- }
1866
-
1867
- // node_modules/@zod/core/dist/esm/errors.js
1868
- var ZOD_ERROR = Symbol.for("{{zod.error}}");
1869
-
1870
- class $ZodError {
1871
- get message() {
1872
- return JSON.stringify(this.issues, jsonStringifyReplacer, 2);
1873
- }
1874
- constructor(issues) {
1875
- Object.defineProperty(this, "_tag", { value: ZOD_ERROR, enumerable: false });
1876
- Object.defineProperty(this, "name", { value: "$ZodError", enumerable: false });
1877
- this.issues = issues;
1878
- }
1879
- static [Symbol.hasInstance](inst) {
1880
- return inst?._tag === ZOD_ERROR;
1881
- }
1882
- }
1883
- function flattenError(error, mapper = (issue) => issue.message) {
1884
- const fieldErrors = {};
1885
- const formErrors = [];
1886
- for (const sub of error.issues) {
1887
- if (sub.path.length > 0) {
1888
- fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || [];
1889
- fieldErrors[sub.path[0]].push(mapper(sub));
1890
- } else {
1891
- formErrors.push(mapper(sub));
1892
- }
1893
- }
1894
- return { formErrors, fieldErrors };
1895
- }
1896
- function formatError(error, _mapper) {
1897
- const mapper = _mapper || function(issue) {
1898
- return issue.message;
1899
- };
1900
- const fieldErrors = { _errors: [] };
1901
- const processError = (error2) => {
1902
- for (const issue of error2.issues) {
1903
- if (issue.code === "invalid_union") {
1904
- issue.errors.map((issues) => processError({ issues }));
1905
- } else if (issue.code === "invalid_key") {
1906
- processError({ issues: issue.issues });
1907
- } else if (issue.code === "invalid_element") {
1908
- processError({ issues: issue.issues });
1909
- } else if (issue.path.length === 0) {
1910
- fieldErrors._errors.push(mapper(issue));
1911
- } else {
1912
- let curr = fieldErrors;
1913
- let i = 0;
1914
- while (i < issue.path.length) {
1915
- const el = issue.path[i];
1916
- const terminal = i === issue.path.length - 1;
1917
- if (!terminal) {
1918
- curr[el] = curr[el] || { _errors: [] };
1919
- } else {
1920
- curr[el] = curr[el] || { _errors: [] };
1921
- curr[el]._errors.push(mapper(issue));
1922
- }
1923
- curr = curr[el];
1924
- i++;
1878
+ function createAPIRoute(handler) {
1879
+ let _handlers = {};
1880
+ if (handler) {
1881
+ _handlers["GET"] = handler;
1882
+ }
1883
+ const api = {
1884
+ _kind: "hsRoute",
1885
+ get(handler2) {
1886
+ _handlers["GET"] = handler2;
1887
+ return api;
1888
+ },
1889
+ post(handler2) {
1890
+ _handlers["POST"] = handler2;
1891
+ return api;
1892
+ },
1893
+ put(handler2) {
1894
+ _handlers["PUT"] = handler2;
1895
+ return api;
1896
+ },
1897
+ delete(handler2) {
1898
+ _handlers["DELETE"] = handler2;
1899
+ return api;
1900
+ },
1901
+ patch(handler2) {
1902
+ _handlers["PATCH"] = handler2;
1903
+ return api;
1904
+ },
1905
+ async run(method, context) {
1906
+ const handler2 = _handlers[method];
1907
+ if (!handler2) {
1908
+ throw new Error("Method not allowed");
1909
+ }
1910
+ try {
1911
+ const response = await handler2(context);
1912
+ if (response instanceof Response) {
1913
+ return response;
1925
1914
  }
1915
+ return context.json({ meta: { success: true, dtResponse: new Date }, data: response }, { status: 200 });
1916
+ } catch (err) {
1917
+ const e = err;
1918
+ console.error(e);
1919
+ return context.json({
1920
+ meta: { success: false, dtResponse: new Date },
1921
+ data: {},
1922
+ error: {
1923
+ message: e.message,
1924
+ stack: IS_PROD ? undefined : e.stack?.split(`
1925
+ `)
1926
+ }
1927
+ }, { status: 500 });
1926
1928
  }
1927
1929
  }
1928
1930
  };
1929
- processError(error);
1930
- return fieldErrors;
1931
- }
1932
-
1933
- // node_modules/@zod/core/dist/esm/parse.js
1934
- function _parse(schema, value, _ctx) {
1935
- const ctx = _ctx ? { ..._ctx, async: false } : { async: false };
1936
- const result = schema._zod.run({ value, issues: [] }, ctx);
1937
- if (result instanceof Promise) {
1938
- throw new $ZodAsyncError;
1939
- }
1940
- if (result.issues.length) {
1941
- throw new (this?.Error ?? $ZodError)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
1942
- }
1943
- return result.value;
1931
+ return api;
1944
1932
  }
1945
-
1946
- // node_modules/zod/dist/esm/errors.js
1947
- class ZodError extends $ZodError {
1948
- format(mapper) {
1949
- return formatError(this, mapper);
1933
+ function getRunnableRoute(route) {
1934
+ if (isRouteRunnable(route)) {
1935
+ return route;
1950
1936
  }
1951
- flatten(mapper) {
1952
- return flattenError(this, mapper);
1937
+ const kind = typeof route;
1938
+ if (kind === "function") {
1939
+ return createRoute(route);
1953
1940
  }
1954
- addIssue(issue) {
1955
- this.issues.push(issue);
1941
+ if (kind === "object" && "default" in route) {
1942
+ return getRunnableRoute(route.default);
1956
1943
  }
1957
- addIssues(issues) {
1958
- this.issues.push(...issues);
1959
- }
1960
- get isEmpty() {
1961
- return this.issues.length === 0;
1962
- }
1963
- }
1964
-
1965
- // node_modules/zod/dist/esm/parse.js
1966
- var parse = /* @__PURE__ */ _parse.bind({ Error: ZodError });
1967
-
1968
- // src/server.ts
1969
- var IS_PROD = false;
1970
- var CWD = process.cwd();
1971
- function createRoute(handler) {
1972
- return new HSRoute(handler);
1944
+ throw new Error('Route not runnable. Use "export default createRoute()" to create a Hyperspan route.');
1973
1945
  }
1974
- function createComponent(render2) {
1975
- return new HSComponent(render2);
1976
- }
1977
- function createForm(renderForm, schema) {
1978
- return new HSFormRoute(renderForm, schema);
1979
- }
1980
- var HS_DEFAULT_LOADING = () => html`<div>Loading...</div>`;
1981
-
1982
- class HSComponent {
1983
- _kind = "hsComponent";
1984
- _handlers = {};
1985
- _loading;
1986
- render;
1987
- constructor(render2) {
1988
- this.render = render2;
1989
- }
1990
- loading(fn) {
1991
- this._loading = fn;
1992
- return this;
1993
- }
1994
- }
1995
-
1996
- class HSRoute {
1997
- _kind = "hsRoute";
1998
- _handlers = {};
1999
- _methods = null;
2000
- constructor(handler) {
2001
- this._handlers.GET = handler;
2002
- }
2003
- }
2004
-
2005
- class HSFormRoute {
2006
- _kind = "hsFormRoute";
2007
- _handlers = {};
2008
- _form;
2009
- _methods = null;
2010
- _schema = null;
2011
- constructor(renderForm, schema = null) {
2012
- if (schema) {
2013
- this._form = renderForm;
2014
- this._schema = schema;
2015
- } else {
2016
- this._form = renderForm;
2017
- }
2018
- this._handlers.GET = () => renderForm(this.getDefaultData());
2019
- }
2020
- getDefaultData() {
2021
- if (!this._schema) {
2022
- return {};
2023
- }
2024
- const data = parse(this._schema, {});
2025
- return data;
2026
- }
2027
- renderForm(data) {
2028
- return this._form(data || this.getDefaultData());
2029
- }
2030
- get(handler) {
2031
- this._handlers.GET = handler;
2032
- return this;
2033
- }
2034
- patch(handler) {
2035
- this._handlers.PATCH = handler;
2036
- return this;
2037
- }
2038
- post(handler) {
2039
- this._handlers.POST = handler;
2040
- return this;
2041
- }
2042
- put(handler) {
2043
- this._handlers.PUT = handler;
2044
- return this;
2045
- }
2046
- delete(handler) {
2047
- this._handlers.DELETE = handler;
2048
- return this;
2049
- }
2050
- }
2051
- async function runFileRoute(RouteModule, context) {
2052
- const req = context.req;
2053
- const url = new URL(req.url);
2054
- const qs = url.searchParams;
2055
- const userIsBot = isbot(context.req.header("User-Agent"));
2056
- const streamOpt = qs.get("__nostream") ? !Boolean(qs.get("__nostream")) : undefined;
2057
- const streamingEnabled = !userIsBot && (streamOpt !== undefined ? streamOpt : true);
2058
- const RouteComponent = RouteModule.default;
2059
- const reqMethod = req.method.toUpperCase();
2060
- try {
2061
- if (RouteModule[reqMethod] !== undefined) {
2062
- return await runAPIRoute(RouteModule[reqMethod], context);
2063
- }
2064
- let routeContent;
2065
- if (!RouteComponent) {
2066
- throw new Error("No route was exported by default in matched route file.");
2067
- }
2068
- if (typeof RouteComponent._handlers !== "undefined") {
2069
- const routeMethodHandler = RouteComponent._handlers[reqMethod];
2070
- if (!routeMethodHandler) {
2071
- return new Response("Method Not Allowed", {
2072
- status: 405,
2073
- headers: { "content-type": "text/plain" }
2074
- });
2075
- }
2076
- routeContent = await routeMethodHandler(context);
2077
- } else {
2078
- routeContent = await RouteComponent(context);
2079
- }
2080
- if (routeContent instanceof Response) {
2081
- return routeContent;
2082
- }
2083
- let routeKind = typeof routeContent;
2084
- if (routeKind === "object" && (routeContent instanceof TmplHtml || routeContent.constructor.name === "TmplHtml" || routeContent?._kind === "TmplHtml")) {
2085
- if (streamingEnabled) {
2086
- return new StreamResponse(renderStream(routeContent));
2087
- } else {
2088
- const output = await renderAsync(routeContent);
2089
- return context.html(output);
2090
- }
2091
- }
2092
- console.log("Returning unknown type... ", routeContent);
2093
- return routeContent;
2094
- } catch (e) {
2095
- console.error(e);
2096
- return await showErrorReponse(context, e);
2097
- }
2098
- }
2099
- async function runAPIRoute(routeFn, context, middlewareResult) {
2100
- try {
2101
- return await routeFn(context, middlewareResult);
2102
- } catch (err) {
2103
- const e = err;
2104
- console.error(e);
2105
- return context.json({
2106
- meta: { success: false },
2107
- data: {
2108
- message: e.message,
2109
- stack: IS_PROD ? undefined : e.stack?.split(`
2110
- `)
2111
- }
2112
- }, { status: 500 });
2113
- }
1946
+ function isRouteRunnable(route) {
1947
+ return typeof route === "object" && "run" in route;
2114
1948
  }
2115
1949
  async function showErrorReponse(context, err) {
2116
1950
  const output = render(html`
@@ -2127,8 +1961,8 @@ async function showErrorReponse(context, err) {
2127
1961
  });
2128
1962
  }
2129
1963
  var ROUTE_SEGMENT = /(\[[a-zA-Z_\.]+\])/g;
2130
- async function buildRoutes(config2) {
2131
- const routesDir = join(config2.appDir, "routes");
1964
+ async function buildRoutes(config) {
1965
+ const routesDir = join(config.appDir, "routes");
2132
1966
  console.log(routesDir);
2133
1967
  const files = await readdir(routesDir, { recursive: true });
2134
1968
  const routes = [];
@@ -2163,11 +1997,27 @@ async function buildRoutes(config2) {
2163
1997
  }
2164
1998
  return routes;
2165
1999
  }
2166
- async function createServer(config2) {
2000
+ function createRouteFromModule(RouteModule) {
2001
+ return async (context) => {
2002
+ const reqMethod = context.req.method.toUpperCase();
2003
+ try {
2004
+ const runnableRoute = getRunnableRoute(RouteModule);
2005
+ const content = await runnableRoute.run(reqMethod, context);
2006
+ if (content instanceof Response) {
2007
+ return content;
2008
+ }
2009
+ return context.text(String(content));
2010
+ } catch (e) {
2011
+ console.error(e);
2012
+ return await showErrorReponse(context, e);
2013
+ }
2014
+ };
2015
+ }
2016
+ async function createServer(config) {
2167
2017
  await Promise.all([buildClientJS(), buildClientCSS()]);
2168
2018
  const app = new Hono2;
2169
- config2.beforeRoutesAdded && config2.beforeRoutesAdded(app);
2170
- const fileRoutes = await buildRoutes(config2);
2019
+ config.beforeRoutesAdded && config.beforeRoutesAdded(app);
2020
+ const fileRoutes = await buildRoutes(config);
2171
2021
  const routeMap = [];
2172
2022
  for (let i = 0;i < fileRoutes.length; i++) {
2173
2023
  let route = fileRoutes[i];
@@ -2175,13 +2025,7 @@ async function createServer(config2) {
2175
2025
  const routePattern = normalizePath(route.route);
2176
2026
  routeMap.push({ route: routePattern, file: route.file });
2177
2027
  const routeModule = await import(fullRouteFile);
2178
- app.all(routePattern, async (context) => {
2179
- const matchedRoute = await runFileRoute(routeModule, context);
2180
- if (matchedRoute) {
2181
- return matchedRoute;
2182
- }
2183
- return context.notFound();
2184
- });
2028
+ app.all(routePattern, createRouteFromModule(routeModule));
2185
2029
  }
2186
2030
  if (routeMap.length === 0) {
2187
2031
  app.get("/", (context) => {
@@ -2192,9 +2036,12 @@ async function createServer(config2) {
2192
2036
  console.log("[Hyperspan] File system routes (in app/routes):");
2193
2037
  console.table(routeMap);
2194
2038
  }
2195
- config2.afterRoutesAdded && config2.afterRoutesAdded(app);
2039
+ config.afterRoutesAdded && config.afterRoutesAdded(app);
2196
2040
  app.use("*", serveStatic2({
2197
- root: config2.staticFileRoot
2041
+ root: config.staticFileRoot,
2042
+ onFound: IS_PROD ? (_, c) => {
2043
+ c.header("Cache-Control", "public, max-age=2592000");
2044
+ } : undefined
2198
2045
  }));
2199
2046
  app.notFound((context) => {
2200
2047
  return context.text("Not... found?", { status: 404 });
@@ -2209,8 +2056,9 @@ class StreamResponse extends Response {
2209
2056
  return new Response(stream, {
2210
2057
  status: 200,
2211
2058
  headers: {
2212
- "Content-Type": "text/html",
2213
2059
  "Transfer-Encoding": "chunked",
2060
+ "Content-Type": "text/html; charset=UTF-8",
2061
+ "Content-Encoding": "Identity",
2214
2062
  ...options?.headers ?? {}
2215
2063
  },
2216
2064
  ...options
@@ -2236,18 +2084,15 @@ function normalizePath(urlPath) {
2236
2084
  return (urlPath.endsWith("/") ? urlPath.substring(0, urlPath.length - 1) : urlPath).toLowerCase() || "/";
2237
2085
  }
2238
2086
  export {
2239
- runFileRoute,
2240
2087
  normalizePath,
2088
+ isRouteRunnable,
2089
+ getRunnableRoute,
2241
2090
  createServer,
2091
+ createRouteFromModule,
2242
2092
  createRoute,
2243
2093
  createReadableStreamFromAsyncGenerator,
2244
- createForm,
2245
- createComponent,
2094
+ createAPIRoute,
2246
2095
  buildRoutes,
2247
2096
  StreamResponse,
2248
- IS_PROD,
2249
- HS_DEFAULT_LOADING,
2250
- HSRoute,
2251
- HSFormRoute,
2252
- HSComponent
2097
+ IS_PROD
2253
2098
  };