@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 +33 -67
- package/dist/server.js +168 -323
- package/package.json +9 -5
- package/src/actions.test.ts +95 -0
- package/src/actions.ts +189 -0
- package/src/clientjs/hyperspan-client.ts +65 -4
- package/src/server.ts +192 -220
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,
|
|
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
|
|
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
|
|
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
|
|
28
|
+
export declare function createRoute(handler?: THSRouteHandler): {
|
|
65
29
|
_kind: string;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
*
|
|
39
|
+
* Create new API Route
|
|
40
|
+
* API Route handlers should return a JSON object or a Response
|
|
72
41
|
*/
|
|
73
|
-
export
|
|
74
|
-
export declare class HSFormRoute {
|
|
42
|
+
export declare function createAPIRoute(handler?: THSRouteHandler): {
|
|
75
43
|
_kind: string;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
*
|
|
53
|
+
* Get a Hyperspan runnable route from a module import
|
|
54
|
+
* @throws Error if no runnable route found
|
|
94
55
|
*/
|
|
95
|
-
export declare function
|
|
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
|
|
1800
|
-
var
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
constructor() {
|
|
1804
|
-
super(
|
|
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
|
-
//
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
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
|
-
|
|
1873
|
+
return context.text(String(routeContent));
|
|
1830
1874
|
}
|
|
1831
1875
|
};
|
|
1876
|
+
return api;
|
|
1832
1877
|
}
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
}
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1947
|
-
|
|
1948
|
-
format(mapper) {
|
|
1949
|
-
return formatError(this, mapper);
|
|
1933
|
+
function getRunnableRoute(route) {
|
|
1934
|
+
if (isRouteRunnable(route)) {
|
|
1935
|
+
return route;
|
|
1950
1936
|
}
|
|
1951
|
-
|
|
1952
|
-
|
|
1937
|
+
const kind = typeof route;
|
|
1938
|
+
if (kind === "function") {
|
|
1939
|
+
return createRoute(route);
|
|
1953
1940
|
}
|
|
1954
|
-
|
|
1955
|
-
|
|
1941
|
+
if (kind === "object" && "default" in route) {
|
|
1942
|
+
return getRunnableRoute(route.default);
|
|
1956
1943
|
}
|
|
1957
|
-
|
|
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
|
|
1975
|
-
return
|
|
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(
|
|
2131
|
-
const routesDir = join(
|
|
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
|
-
|
|
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
|
-
|
|
2170
|
-
const fileRoutes = await buildRoutes(
|
|
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,
|
|
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
|
-
|
|
2039
|
+
config.afterRoutesAdded && config.afterRoutesAdded(app);
|
|
2196
2040
|
app.use("*", serveStatic2({
|
|
2197
|
-
root:
|
|
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
|
-
|
|
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
|
};
|