@hyperspan/framework 0.1.2 → 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/build.ts +22 -20
- package/dist/assets.d.ts +1 -1
- package/dist/assets.js +3 -159
- package/dist/server.d.ts +33 -67
- package/dist/server.js +183 -562
- package/package.json +16 -8
- package/src/actions.test.ts +95 -0
- package/src/actions.ts +189 -0
- package/src/assets.ts +11 -10
- package/src/clientjs/hyperspan-client.ts +65 -4
- package/src/server.ts +192 -220
- package/dist/index.d.ts +0 -132
- package/dist/index.js +0 -2477
- package/src/index.ts +0 -1
package/dist/server.js
CHANGED
|
@@ -1,194 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildClientCSS,
|
|
3
|
+
buildClientJS
|
|
4
|
+
} from "./assets.js";
|
|
5
|
+
|
|
1
6
|
// src/server.ts
|
|
2
|
-
import { readdir
|
|
7
|
+
import { readdir } from "node:fs/promises";
|
|
3
8
|
import { basename, extname, join } from "node:path";
|
|
4
|
-
|
|
5
|
-
// ../html/dist/html.js
|
|
6
|
-
/*!
|
|
7
|
-
* escape-html
|
|
8
|
-
* Copyright(c) 2012-2013 TJ Holowaychuk
|
|
9
|
-
* Copyright(c) 2015 Andreas Lubbe
|
|
10
|
-
* Copyright(c) 2015 Tiancheng "Timothy" Gu
|
|
11
|
-
* MIT Licensed
|
|
12
|
-
*/
|
|
13
|
-
var matchHtmlRegExp = /["'&<>]/;
|
|
14
|
-
function escapeHtml(string) {
|
|
15
|
-
const str = "" + string;
|
|
16
|
-
const match = matchHtmlRegExp.exec(str);
|
|
17
|
-
if (!match) {
|
|
18
|
-
return str;
|
|
19
|
-
}
|
|
20
|
-
let escape;
|
|
21
|
-
let html = "";
|
|
22
|
-
let index = 0;
|
|
23
|
-
let lastIndex = 0;
|
|
24
|
-
for (index = match.index;index < str.length; index++) {
|
|
25
|
-
switch (str.charCodeAt(index)) {
|
|
26
|
-
case 34:
|
|
27
|
-
escape = """;
|
|
28
|
-
break;
|
|
29
|
-
case 38:
|
|
30
|
-
escape = "&";
|
|
31
|
-
break;
|
|
32
|
-
case 39:
|
|
33
|
-
escape = "'";
|
|
34
|
-
break;
|
|
35
|
-
case 60:
|
|
36
|
-
escape = "<";
|
|
37
|
-
break;
|
|
38
|
-
case 62:
|
|
39
|
-
escape = ">";
|
|
40
|
-
break;
|
|
41
|
-
default:
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
if (lastIndex !== index) {
|
|
45
|
-
html += str.substring(lastIndex, index);
|
|
46
|
-
}
|
|
47
|
-
lastIndex = index + 1;
|
|
48
|
-
html += escape;
|
|
49
|
-
}
|
|
50
|
-
return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
class TmplHtml {
|
|
54
|
-
_kind = "hstmpl";
|
|
55
|
-
content = "";
|
|
56
|
-
asyncContent;
|
|
57
|
-
constructor(props) {
|
|
58
|
-
this.content = props.content;
|
|
59
|
-
this.asyncContent = props.asyncContent;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
var htmlId = 0;
|
|
63
|
-
function html(strings, ...values) {
|
|
64
|
-
const asyncContent = [];
|
|
65
|
-
let content = "";
|
|
66
|
-
for (let i = 0;i < strings.length; i++) {
|
|
67
|
-
const value = values[i];
|
|
68
|
-
const kind = _typeOf(value);
|
|
69
|
-
const renderValue = _renderValue(value, { kind, asyncContent }) || "";
|
|
70
|
-
content += strings[i] + (renderValue ? renderValue : "");
|
|
71
|
-
}
|
|
72
|
-
return new TmplHtml({ content, asyncContent });
|
|
73
|
-
}
|
|
74
|
-
html.raw = (content) => ({ _kind: "html_safe", content });
|
|
75
|
-
function _renderValue(value, opts = {
|
|
76
|
-
kind: undefined,
|
|
77
|
-
id: undefined,
|
|
78
|
-
asyncContent: []
|
|
79
|
-
}) {
|
|
80
|
-
if (value === null || value === undefined || Number.isNaN(value)) {
|
|
81
|
-
return "";
|
|
82
|
-
}
|
|
83
|
-
const kind = opts.kind || _typeOf(value);
|
|
84
|
-
let id = opts.id;
|
|
85
|
-
switch (kind) {
|
|
86
|
-
case "array":
|
|
87
|
-
return value.map((v) => _renderValue(v, { id, asyncContent: opts.asyncContent })).join("");
|
|
88
|
-
case "object":
|
|
89
|
-
id = `async_loading_${htmlId++}`;
|
|
90
|
-
if (value instanceof TmplHtml || value.constructor.name === "TmplHtml" || value?._kind === "hstmpl") {
|
|
91
|
-
opts.asyncContent.push(...value.asyncContent);
|
|
92
|
-
return value.content;
|
|
93
|
-
}
|
|
94
|
-
if (value?._kind === "html_safe") {
|
|
95
|
-
return value?.content || "";
|
|
96
|
-
}
|
|
97
|
-
if (typeof value.renderAsync === "function") {
|
|
98
|
-
opts.asyncContent.push({
|
|
99
|
-
id,
|
|
100
|
-
promise: value.renderAsync().then((result) => ({
|
|
101
|
-
id,
|
|
102
|
-
value: result,
|
|
103
|
-
asyncContent: opts.asyncContent
|
|
104
|
-
}))
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
if (typeof value.render === "function") {
|
|
108
|
-
return render(_htmlPlaceholder(id, value.render()));
|
|
109
|
-
}
|
|
110
|
-
return JSON.stringify(value);
|
|
111
|
-
case "promise":
|
|
112
|
-
id = `async_loading_${htmlId++}`;
|
|
113
|
-
opts.asyncContent.push({
|
|
114
|
-
id,
|
|
115
|
-
promise: value.then((result) => ({
|
|
116
|
-
id,
|
|
117
|
-
value: result,
|
|
118
|
-
asyncContent: opts.asyncContent
|
|
119
|
-
}))
|
|
120
|
-
});
|
|
121
|
-
return render(_htmlPlaceholder(id));
|
|
122
|
-
case "generator":
|
|
123
|
-
throw new Error("Generators are not supported as a template value at this time. Sorry :(");
|
|
124
|
-
default:
|
|
125
|
-
console.log("_renderValue kind =", kind, value);
|
|
126
|
-
}
|
|
127
|
-
return escapeHtml(String(value));
|
|
128
|
-
}
|
|
129
|
-
function _htmlPlaceholder(id, content = "Loading...") {
|
|
130
|
-
return html`<!--hs:loading:${id}--><slot id="${id}">${content}</slot><!--/hs:loading:${id}-->`;
|
|
131
|
-
}
|
|
132
|
-
function render(tmpl) {
|
|
133
|
-
return tmpl.content;
|
|
134
|
-
}
|
|
135
|
-
async function renderAsync(tmpl) {
|
|
136
|
-
let { content, asyncContent } = tmpl;
|
|
137
|
-
while (asyncContent.length !== 0) {
|
|
138
|
-
const resolvedHtml = await Promise.all(asyncContent.map((p) => p.promise));
|
|
139
|
-
asyncContent = [];
|
|
140
|
-
resolvedHtml.map((obj) => {
|
|
141
|
-
const r = new RegExp(`<!--hs:loading:${obj.id}-->(.*?)<!--/hs:loading:${obj.id}-->`);
|
|
142
|
-
const found = content.match(r);
|
|
143
|
-
if (found) {
|
|
144
|
-
content = content.replace(found[0], _renderValue(obj.value, { asyncContent }));
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
return content;
|
|
149
|
-
}
|
|
150
|
-
async function* renderStream(tmpl) {
|
|
151
|
-
yield render(tmpl);
|
|
152
|
-
let asyncContent = tmpl.asyncContent;
|
|
153
|
-
while (asyncContent.length > 0) {
|
|
154
|
-
const nextContent = await Promise.race(asyncContent.map((p) => p.promise));
|
|
155
|
-
asyncContent = asyncContent.filter((p) => p.id !== nextContent.id);
|
|
156
|
-
const id = nextContent.id;
|
|
157
|
-
const content = _renderValue(nextContent.value, {
|
|
158
|
-
asyncContent
|
|
159
|
-
});
|
|
160
|
-
const script = html`<template id="${id}_content">${html.raw(content)}<!--end--></template>`;
|
|
161
|
-
yield render(script);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
function _typeOf(obj) {
|
|
165
|
-
if (obj instanceof Promise)
|
|
166
|
-
return "promise";
|
|
167
|
-
if (obj instanceof Date)
|
|
168
|
-
return "date";
|
|
169
|
-
if (obj instanceof String)
|
|
170
|
-
return "string";
|
|
171
|
-
if (obj instanceof Number)
|
|
172
|
-
return "number";
|
|
173
|
-
if (obj instanceof Boolean)
|
|
174
|
-
return "boolean";
|
|
175
|
-
if (obj instanceof Function)
|
|
176
|
-
return "function";
|
|
177
|
-
if (Array.isArray(obj))
|
|
178
|
-
return "array";
|
|
179
|
-
if (Number.isNaN(obj))
|
|
180
|
-
return "NaN";
|
|
181
|
-
if (obj === undefined)
|
|
182
|
-
return "undefined";
|
|
183
|
-
if (obj === null)
|
|
184
|
-
return "null";
|
|
185
|
-
if (isGenerator(obj))
|
|
186
|
-
return "generator";
|
|
187
|
-
return typeof obj;
|
|
188
|
-
}
|
|
189
|
-
function isGenerator(obj) {
|
|
190
|
-
return obj && typeof obj.next == "function" && typeof obj.throw == "function";
|
|
191
|
-
}
|
|
9
|
+
import { TmplHtml, html, renderStream, renderAsync, render } from "@hyperspan/html";
|
|
192
10
|
|
|
193
11
|
// node_modules/isbot/index.mjs
|
|
194
12
|
var fullPattern = " daum[ /]| deusu/| yadirectfetcher|(?:^|[^g])news(?!sapphire)|(?<! (?:channel/|google/))google(?!(app|/google| pixel))|(?<! cu)bots?(?:\\b|_)|(?<!(?:lib))http|(?<![hg]m)score|@[a-z][\\w-]+\\.|\\(\\)|\\.com\\b|\\btime/|\\||^<|^[\\w \\.\\-\\(?:\\):%]+(?:/v?\\d+(?:\\.\\d+)?(?:\\.\\d{1,10})*?)?(?:,|$)|^[^ ]{50,}$|^\\d+\\b|^\\w*search\\b|^\\w+/[\\w\\(\\)]*$|^active|^ad muncher|^amaya|^avsdevicesdk/|^biglotron|^bot|^bw/|^clamav[ /]|^client/|^cobweb/|^custom|^ddg[_-]android|^discourse|^dispatch/\\d|^downcast/|^duckduckgo|^email|^facebook|^getright/|^gozilla/|^hobbit|^hotzonu|^hwcdn/|^igetter/|^jeode/|^jetty/|^jigsaw|^microsoft bits|^movabletype|^mozilla/\\d\\.\\d\\s[\\w\\.-]+$|^mozilla/\\d\\.\\d\\s\\(compatible;?(?:\\s\\w+\\/\\d+\\.\\d+)?\\)$|^navermailapp|^netsurf|^offline|^openai/|^owler|^php|^postman|^python|^rank|^read|^reed|^rest|^rss|^snapchat|^space bison|^svn|^swcd |^taringa|^thumbor/|^track|^w3c|^webbandit/|^webcopier|^wget|^whatsapp|^wordpress|^xenu link sleuth|^yahoo|^yandex|^zdm/\\d|^zoom marketplace/|^{{.*}}$|adscanner/|analyzer|archive|ask jeeves/teoma|audit|bit\\.ly/|bluecoat drtr|browsex|burpcollaborator|capture|catch|check\\b|checker|chrome-lighthouse|chromeframe|classifier|cloudflare|convertify|cookiehubscan|crawl|cypress/|dareboost|datanyze|dejaclick|detect|dmbrowser|download|evc-batch/|exaleadcloudview|feed|firephp|functionize|gomezagent|headless|httrack|hubspot marketing grader|hydra|ibisbrowser|infrawatch|insight|inspect|iplabel|ips-agent|java(?!;)|jsjcw_scanner|library|linkcheck|mail\\.ru/|manager|measure|neustar wpm|node|nutch|offbyone|onetrust|optimize|pageburst|pagespeed|parser|perl|phantomjs|pingdom|powermarks|preview|proxy|ptst[ /]\\d|retriever|rexx;|rigor|rss\\b|scanner\\.|scrape|server|sogou|sparkler/|speedcurve|spider|splash|statuscake|supercleaner|synapse|synthetic|tools|torrent|transcoder|url|validator|virtuoso|wappalyzer|webglance|webkit2png|whatcms/|zgrab";
|
|
@@ -209,48 +27,6 @@ function isbot(userAgent) {
|
|
|
209
27
|
return Boolean(userAgent) && getPattern().test(userAgent);
|
|
210
28
|
}
|
|
211
29
|
|
|
212
|
-
// src/clientjs/md5.js
|
|
213
|
-
var hex_chr = "0123456789abcdef".split("");
|
|
214
|
-
|
|
215
|
-
// src/assets.ts
|
|
216
|
-
import { readdir } from "node:fs/promises";
|
|
217
|
-
import { resolve } from "node:path";
|
|
218
|
-
var IS_PROD = false;
|
|
219
|
-
var PWD = import.meta.dir;
|
|
220
|
-
var clientJSFiles = new Map;
|
|
221
|
-
async function buildClientJS() {
|
|
222
|
-
const sourceFile = resolve(PWD, "../", "./src/clientjs/hyperspan-client.ts");
|
|
223
|
-
const output = await Bun.build({
|
|
224
|
-
entrypoints: [sourceFile],
|
|
225
|
-
outdir: `./public/_hs/js`,
|
|
226
|
-
naming: IS_PROD ? "[dir]/[name]-[hash].[ext]" : undefined,
|
|
227
|
-
minify: IS_PROD
|
|
228
|
-
});
|
|
229
|
-
const jsFile = output.outputs[0].path.split("/").reverse()[0];
|
|
230
|
-
clientJSFiles.set("_hs", { src: "/_hs/js/" + jsFile });
|
|
231
|
-
return jsFile;
|
|
232
|
-
}
|
|
233
|
-
var clientCSSFiles = new Map;
|
|
234
|
-
async function buildClientCSS() {
|
|
235
|
-
if (clientCSSFiles.has("_hs")) {
|
|
236
|
-
return clientCSSFiles.get("_hs");
|
|
237
|
-
}
|
|
238
|
-
const cssDir = "./public/_hs/css/";
|
|
239
|
-
const cssFiles = await readdir(cssDir);
|
|
240
|
-
let foundCSSFile = "";
|
|
241
|
-
for (const file of cssFiles) {
|
|
242
|
-
if (!file.endsWith(".css")) {
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
foundCSSFile = file.replace(cssDir, "");
|
|
246
|
-
clientCSSFiles.set("_hs", foundCSSFile);
|
|
247
|
-
break;
|
|
248
|
-
}
|
|
249
|
-
if (!foundCSSFile) {
|
|
250
|
-
console.log(`Unable to build CSS files from ${cssDir}`);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
30
|
// node_modules/hono/dist/compose.js
|
|
255
31
|
var compose = (middleware, onError, onNotFound) => {
|
|
256
32
|
return (context, next) => {
|
|
@@ -920,15 +696,15 @@ var Context = class {
|
|
|
920
696
|
this.#preparedHeaders["content-type"] = "application/json";
|
|
921
697
|
return typeof arg === "number" ? this.#newResponse(body, arg, headers) : this.#newResponse(body, arg);
|
|
922
698
|
};
|
|
923
|
-
html = (
|
|
699
|
+
html = (html, arg, headers) => {
|
|
924
700
|
this.#preparedHeaders ??= {};
|
|
925
701
|
this.#preparedHeaders["content-type"] = "text/html; charset=UTF-8";
|
|
926
|
-
if (typeof
|
|
927
|
-
return resolveCallback(
|
|
928
|
-
return typeof arg === "number" ? this.#newResponse(
|
|
702
|
+
if (typeof html === "object") {
|
|
703
|
+
return resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then((html2) => {
|
|
704
|
+
return typeof arg === "number" ? this.#newResponse(html2, arg, headers) : this.#newResponse(html2, arg);
|
|
929
705
|
});
|
|
930
706
|
}
|
|
931
|
-
return typeof arg === "number" ? this.#newResponse(
|
|
707
|
+
return typeof arg === "number" ? this.#newResponse(html, arg, headers) : this.#newResponse(html, arg);
|
|
932
708
|
};
|
|
933
709
|
redirect = (location, status) => {
|
|
934
710
|
this.#headers ??= new Headers;
|
|
@@ -2020,328 +1796,162 @@ var WSContext = class {
|
|
|
2020
1796
|
}
|
|
2021
1797
|
};
|
|
2022
1798
|
|
|
2023
|
-
// node_modules
|
|
2024
|
-
var
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
constructor() {
|
|
2028
|
-
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
|
+
});
|
|
2029
1819
|
}
|
|
2030
|
-
}
|
|
2031
|
-
var globalConfig = {};
|
|
2032
|
-
function config(config2) {
|
|
2033
|
-
if (config2)
|
|
2034
|
-
Object.assign(globalConfig, config2);
|
|
2035
|
-
return globalConfig;
|
|
2036
|
-
}
|
|
1820
|
+
};
|
|
2037
1821
|
|
|
2038
|
-
//
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
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
|
+
}
|
|
2052
1872
|
}
|
|
2053
|
-
|
|
1873
|
+
return context.text(String(routeContent));
|
|
2054
1874
|
}
|
|
2055
1875
|
};
|
|
1876
|
+
return api;
|
|
2056
1877
|
}
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
}
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
class $ZodError {
|
|
2095
|
-
get message() {
|
|
2096
|
-
return JSON.stringify(this.issues, jsonStringifyReplacer, 2);
|
|
2097
|
-
}
|
|
2098
|
-
constructor(issues) {
|
|
2099
|
-
Object.defineProperty(this, "_tag", { value: ZOD_ERROR, enumerable: false });
|
|
2100
|
-
Object.defineProperty(this, "name", { value: "$ZodError", enumerable: false });
|
|
2101
|
-
this.issues = issues;
|
|
2102
|
-
}
|
|
2103
|
-
static [Symbol.hasInstance](inst) {
|
|
2104
|
-
return inst?._tag === ZOD_ERROR;
|
|
2105
|
-
}
|
|
2106
|
-
}
|
|
2107
|
-
function flattenError(error, mapper = (issue) => issue.message) {
|
|
2108
|
-
const fieldErrors = {};
|
|
2109
|
-
const formErrors = [];
|
|
2110
|
-
for (const sub of error.issues) {
|
|
2111
|
-
if (sub.path.length > 0) {
|
|
2112
|
-
fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || [];
|
|
2113
|
-
fieldErrors[sub.path[0]].push(mapper(sub));
|
|
2114
|
-
} else {
|
|
2115
|
-
formErrors.push(mapper(sub));
|
|
2116
|
-
}
|
|
2117
|
-
}
|
|
2118
|
-
return { formErrors, fieldErrors };
|
|
2119
|
-
}
|
|
2120
|
-
function formatError(error, _mapper) {
|
|
2121
|
-
const mapper = _mapper || function(issue) {
|
|
2122
|
-
return issue.message;
|
|
2123
|
-
};
|
|
2124
|
-
const fieldErrors = { _errors: [] };
|
|
2125
|
-
const processError = (error2) => {
|
|
2126
|
-
for (const issue of error2.issues) {
|
|
2127
|
-
if (issue.code === "invalid_union") {
|
|
2128
|
-
issue.errors.map((issues) => processError({ issues }));
|
|
2129
|
-
} else if (issue.code === "invalid_key") {
|
|
2130
|
-
processError({ issues: issue.issues });
|
|
2131
|
-
} else if (issue.code === "invalid_element") {
|
|
2132
|
-
processError({ issues: issue.issues });
|
|
2133
|
-
} else if (issue.path.length === 0) {
|
|
2134
|
-
fieldErrors._errors.push(mapper(issue));
|
|
2135
|
-
} else {
|
|
2136
|
-
let curr = fieldErrors;
|
|
2137
|
-
let i = 0;
|
|
2138
|
-
while (i < issue.path.length) {
|
|
2139
|
-
const el = issue.path[i];
|
|
2140
|
-
const terminal = i === issue.path.length - 1;
|
|
2141
|
-
if (!terminal) {
|
|
2142
|
-
curr[el] = curr[el] || { _errors: [] };
|
|
2143
|
-
} else {
|
|
2144
|
-
curr[el] = curr[el] || { _errors: [] };
|
|
2145
|
-
curr[el]._errors.push(mapper(issue));
|
|
2146
|
-
}
|
|
2147
|
-
curr = curr[el];
|
|
2148
|
-
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;
|
|
2149
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 });
|
|
2150
1928
|
}
|
|
2151
1929
|
}
|
|
2152
1930
|
};
|
|
2153
|
-
|
|
2154
|
-
return fieldErrors;
|
|
1931
|
+
return api;
|
|
2155
1932
|
}
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
const ctx = _ctx ? { ..._ctx, async: false } : { async: false };
|
|
2160
|
-
const result = schema._zod.run({ value, issues: [] }, ctx);
|
|
2161
|
-
if (result instanceof Promise) {
|
|
2162
|
-
throw new $ZodAsyncError;
|
|
1933
|
+
function getRunnableRoute(route) {
|
|
1934
|
+
if (isRouteRunnable(route)) {
|
|
1935
|
+
return route;
|
|
2163
1936
|
}
|
|
2164
|
-
|
|
2165
|
-
|
|
1937
|
+
const kind = typeof route;
|
|
1938
|
+
if (kind === "function") {
|
|
1939
|
+
return createRoute(route);
|
|
2166
1940
|
}
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
// node_modules/zod/dist/esm/errors.js
|
|
2171
|
-
class ZodError extends $ZodError {
|
|
2172
|
-
format(mapper) {
|
|
2173
|
-
return formatError(this, mapper);
|
|
2174
|
-
}
|
|
2175
|
-
flatten(mapper) {
|
|
2176
|
-
return flattenError(this, mapper);
|
|
2177
|
-
}
|
|
2178
|
-
addIssue(issue) {
|
|
2179
|
-
this.issues.push(issue);
|
|
2180
|
-
}
|
|
2181
|
-
addIssues(issues) {
|
|
2182
|
-
this.issues.push(...issues);
|
|
2183
|
-
}
|
|
2184
|
-
get isEmpty() {
|
|
2185
|
-
return this.issues.length === 0;
|
|
1941
|
+
if (kind === "object" && "default" in route) {
|
|
1942
|
+
return getRunnableRoute(route.default);
|
|
2186
1943
|
}
|
|
1944
|
+
throw new Error('Route not runnable. Use "export default createRoute()" to create a Hyperspan route.');
|
|
2187
1945
|
}
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
var parse = /* @__PURE__ */ _parse.bind({ Error: ZodError });
|
|
2191
|
-
|
|
2192
|
-
// src/server.ts
|
|
2193
|
-
var IS_PROD2 = false;
|
|
2194
|
-
var CWD = process.cwd();
|
|
2195
|
-
function createRoute(handler) {
|
|
2196
|
-
return new HSRoute(handler);
|
|
2197
|
-
}
|
|
2198
|
-
function createComponent(render2) {
|
|
2199
|
-
return new HSComponent(render2);
|
|
2200
|
-
}
|
|
2201
|
-
function createForm(renderForm, schema) {
|
|
2202
|
-
return new HSFormRoute(renderForm, schema);
|
|
2203
|
-
}
|
|
2204
|
-
var HS_DEFAULT_LOADING = () => html`<div>Loading...</div>`;
|
|
2205
|
-
|
|
2206
|
-
class HSComponent {
|
|
2207
|
-
_kind = "hsComponent";
|
|
2208
|
-
_handlers = {};
|
|
2209
|
-
_loading;
|
|
2210
|
-
render;
|
|
2211
|
-
constructor(render2) {
|
|
2212
|
-
this.render = render2;
|
|
2213
|
-
}
|
|
2214
|
-
loading(fn) {
|
|
2215
|
-
this._loading = fn;
|
|
2216
|
-
return this;
|
|
2217
|
-
}
|
|
2218
|
-
}
|
|
2219
|
-
|
|
2220
|
-
class HSRoute {
|
|
2221
|
-
_kind = "hsRoute";
|
|
2222
|
-
_handlers = {};
|
|
2223
|
-
_methods = null;
|
|
2224
|
-
constructor(handler) {
|
|
2225
|
-
this._handlers.GET = handler;
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
|
|
2229
|
-
class HSFormRoute {
|
|
2230
|
-
_kind = "hsFormRoute";
|
|
2231
|
-
_handlers = {};
|
|
2232
|
-
_form;
|
|
2233
|
-
_methods = null;
|
|
2234
|
-
_schema = null;
|
|
2235
|
-
constructor(renderForm, schema = null) {
|
|
2236
|
-
if (schema) {
|
|
2237
|
-
this._form = renderForm;
|
|
2238
|
-
this._schema = schema;
|
|
2239
|
-
} else {
|
|
2240
|
-
this._form = renderForm;
|
|
2241
|
-
}
|
|
2242
|
-
this._handlers.GET = () => renderForm(this.getDefaultData());
|
|
2243
|
-
}
|
|
2244
|
-
getDefaultData() {
|
|
2245
|
-
if (!this._schema) {
|
|
2246
|
-
return {};
|
|
2247
|
-
}
|
|
2248
|
-
const data = parse(this._schema, {});
|
|
2249
|
-
return data;
|
|
2250
|
-
}
|
|
2251
|
-
renderForm(data) {
|
|
2252
|
-
return this._form(data || this.getDefaultData());
|
|
2253
|
-
}
|
|
2254
|
-
get(handler) {
|
|
2255
|
-
this._handlers.GET = handler;
|
|
2256
|
-
return this;
|
|
2257
|
-
}
|
|
2258
|
-
patch(handler) {
|
|
2259
|
-
this._handlers.PATCH = handler;
|
|
2260
|
-
return this;
|
|
2261
|
-
}
|
|
2262
|
-
post(handler) {
|
|
2263
|
-
this._handlers.POST = handler;
|
|
2264
|
-
return this;
|
|
2265
|
-
}
|
|
2266
|
-
put(handler) {
|
|
2267
|
-
this._handlers.PUT = handler;
|
|
2268
|
-
return this;
|
|
2269
|
-
}
|
|
2270
|
-
delete(handler) {
|
|
2271
|
-
this._handlers.DELETE = handler;
|
|
2272
|
-
return this;
|
|
2273
|
-
}
|
|
2274
|
-
}
|
|
2275
|
-
async function runFileRoute(RouteModule, context) {
|
|
2276
|
-
const req = context.req;
|
|
2277
|
-
const url = new URL(req.url);
|
|
2278
|
-
const qs = url.searchParams;
|
|
2279
|
-
const userIsBot = isbot(context.req.header("User-Agent"));
|
|
2280
|
-
const streamOpt = qs.get("__nostream") ? !Boolean(qs.get("__nostream")) : undefined;
|
|
2281
|
-
const streamingEnabled = !userIsBot && (streamOpt !== undefined ? streamOpt : true);
|
|
2282
|
-
const RouteComponent = RouteModule.default;
|
|
2283
|
-
const reqMethod = req.method.toUpperCase();
|
|
2284
|
-
try {
|
|
2285
|
-
if (RouteModule[reqMethod] !== undefined) {
|
|
2286
|
-
return await runAPIRoute(RouteModule[reqMethod], context);
|
|
2287
|
-
}
|
|
2288
|
-
let routeContent;
|
|
2289
|
-
if (!RouteComponent) {
|
|
2290
|
-
throw new Error("No route was exported by default in matched route file.");
|
|
2291
|
-
}
|
|
2292
|
-
if (typeof RouteComponent._handlers !== "undefined") {
|
|
2293
|
-
const routeMethodHandler = RouteComponent._handlers[reqMethod];
|
|
2294
|
-
if (!routeMethodHandler) {
|
|
2295
|
-
return new Response("Method Not Allowed", {
|
|
2296
|
-
status: 405,
|
|
2297
|
-
headers: { "content-type": "text/plain" }
|
|
2298
|
-
});
|
|
2299
|
-
}
|
|
2300
|
-
routeContent = await routeMethodHandler(context);
|
|
2301
|
-
} else {
|
|
2302
|
-
routeContent = await RouteComponent(context);
|
|
2303
|
-
}
|
|
2304
|
-
if (routeContent instanceof Response) {
|
|
2305
|
-
return routeContent;
|
|
2306
|
-
}
|
|
2307
|
-
let routeKind = typeof routeContent;
|
|
2308
|
-
if (routeKind === "object" && (routeContent instanceof TmplHtml || routeContent.constructor.name === "TmplHtml" || routeContent?._kind === "TmplHtml")) {
|
|
2309
|
-
if (streamingEnabled) {
|
|
2310
|
-
return new StreamResponse(renderStream(routeContent));
|
|
2311
|
-
} else {
|
|
2312
|
-
const output = await renderAsync(routeContent);
|
|
2313
|
-
return context.html(output);
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
console.log("Returning unknown type... ", routeContent);
|
|
2317
|
-
return routeContent;
|
|
2318
|
-
} catch (e) {
|
|
2319
|
-
console.error(e);
|
|
2320
|
-
return await showErrorReponse(context, e);
|
|
2321
|
-
}
|
|
2322
|
-
}
|
|
2323
|
-
async function runAPIRoute(routeFn, context, middlewareResult) {
|
|
2324
|
-
try {
|
|
2325
|
-
return await routeFn(context, middlewareResult);
|
|
2326
|
-
} catch (err) {
|
|
2327
|
-
const e = err;
|
|
2328
|
-
console.error(e);
|
|
2329
|
-
return context.json({
|
|
2330
|
-
meta: { success: false },
|
|
2331
|
-
data: {
|
|
2332
|
-
message: e.message,
|
|
2333
|
-
stack: IS_PROD2 ? undefined : e.stack?.split(`
|
|
2334
|
-
`)
|
|
2335
|
-
}
|
|
2336
|
-
}, { status: 500 });
|
|
2337
|
-
}
|
|
1946
|
+
function isRouteRunnable(route) {
|
|
1947
|
+
return typeof route === "object" && "run" in route;
|
|
2338
1948
|
}
|
|
2339
1949
|
async function showErrorReponse(context, err) {
|
|
2340
1950
|
const output = render(html`
|
|
2341
1951
|
<main>
|
|
2342
1952
|
<h1>Error</h1>
|
|
2343
1953
|
<pre>${err.message}</pre>
|
|
2344
|
-
<pre>${!
|
|
1954
|
+
<pre>${!IS_PROD && err.stack ? err.stack.split(`
|
|
2345
1955
|
`).slice(1).join(`
|
|
2346
1956
|
`) : ""}</pre>
|
|
2347
1957
|
</main>
|
|
@@ -2351,10 +1961,10 @@ async function showErrorReponse(context, err) {
|
|
|
2351
1961
|
});
|
|
2352
1962
|
}
|
|
2353
1963
|
var ROUTE_SEGMENT = /(\[[a-zA-Z_\.]+\])/g;
|
|
2354
|
-
async function buildRoutes(
|
|
2355
|
-
const routesDir = join(
|
|
1964
|
+
async function buildRoutes(config) {
|
|
1965
|
+
const routesDir = join(config.appDir, "routes");
|
|
2356
1966
|
console.log(routesDir);
|
|
2357
|
-
const files = await
|
|
1967
|
+
const files = await readdir(routesDir, { recursive: true });
|
|
2358
1968
|
const routes = [];
|
|
2359
1969
|
for (const file of files) {
|
|
2360
1970
|
if (!file.includes(".") || basename(file).startsWith(".")) {
|
|
@@ -2387,11 +1997,27 @@ async function buildRoutes(config2) {
|
|
|
2387
1997
|
}
|
|
2388
1998
|
return routes;
|
|
2389
1999
|
}
|
|
2390
|
-
|
|
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) {
|
|
2391
2017
|
await Promise.all([buildClientJS(), buildClientCSS()]);
|
|
2392
2018
|
const app = new Hono2;
|
|
2393
|
-
|
|
2394
|
-
const fileRoutes = await buildRoutes(
|
|
2019
|
+
config.beforeRoutesAdded && config.beforeRoutesAdded(app);
|
|
2020
|
+
const fileRoutes = await buildRoutes(config);
|
|
2395
2021
|
const routeMap = [];
|
|
2396
2022
|
for (let i = 0;i < fileRoutes.length; i++) {
|
|
2397
2023
|
let route = fileRoutes[i];
|
|
@@ -2399,26 +2025,23 @@ async function createServer(config2) {
|
|
|
2399
2025
|
const routePattern = normalizePath(route.route);
|
|
2400
2026
|
routeMap.push({ route: routePattern, file: route.file });
|
|
2401
2027
|
const routeModule = await import(fullRouteFile);
|
|
2402
|
-
app.all(routePattern,
|
|
2403
|
-
const matchedRoute = await runFileRoute(routeModule, context);
|
|
2404
|
-
if (matchedRoute) {
|
|
2405
|
-
return matchedRoute;
|
|
2406
|
-
}
|
|
2407
|
-
return context.notFound();
|
|
2408
|
-
});
|
|
2028
|
+
app.all(routePattern, createRouteFromModule(routeModule));
|
|
2409
2029
|
}
|
|
2410
2030
|
if (routeMap.length === 0) {
|
|
2411
2031
|
app.get("/", (context) => {
|
|
2412
2032
|
return context.text("No routes found. Add routes to app/routes. Example: `app/routes/index.ts`", { status: 404 });
|
|
2413
2033
|
});
|
|
2414
2034
|
}
|
|
2415
|
-
if (!
|
|
2035
|
+
if (!IS_PROD) {
|
|
2416
2036
|
console.log("[Hyperspan] File system routes (in app/routes):");
|
|
2417
2037
|
console.table(routeMap);
|
|
2418
2038
|
}
|
|
2419
|
-
|
|
2039
|
+
config.afterRoutesAdded && config.afterRoutesAdded(app);
|
|
2420
2040
|
app.use("*", serveStatic2({
|
|
2421
|
-
root:
|
|
2041
|
+
root: config.staticFileRoot,
|
|
2042
|
+
onFound: IS_PROD ? (_, c) => {
|
|
2043
|
+
c.header("Cache-Control", "public, max-age=2592000");
|
|
2044
|
+
} : undefined
|
|
2422
2045
|
}));
|
|
2423
2046
|
app.notFound((context) => {
|
|
2424
2047
|
return context.text("Not... found?", { status: 404 });
|
|
@@ -2433,8 +2056,9 @@ class StreamResponse extends Response {
|
|
|
2433
2056
|
return new Response(stream, {
|
|
2434
2057
|
status: 200,
|
|
2435
2058
|
headers: {
|
|
2436
|
-
"Content-Type": "text/html",
|
|
2437
2059
|
"Transfer-Encoding": "chunked",
|
|
2060
|
+
"Content-Type": "text/html; charset=UTF-8",
|
|
2061
|
+
"Content-Encoding": "Identity",
|
|
2438
2062
|
...options?.headers ?? {}
|
|
2439
2063
|
},
|
|
2440
2064
|
...options
|
|
@@ -2460,18 +2084,15 @@ function normalizePath(urlPath) {
|
|
|
2460
2084
|
return (urlPath.endsWith("/") ? urlPath.substring(0, urlPath.length - 1) : urlPath).toLowerCase() || "/";
|
|
2461
2085
|
}
|
|
2462
2086
|
export {
|
|
2463
|
-
runFileRoute,
|
|
2464
2087
|
normalizePath,
|
|
2088
|
+
isRouteRunnable,
|
|
2089
|
+
getRunnableRoute,
|
|
2465
2090
|
createServer,
|
|
2091
|
+
createRouteFromModule,
|
|
2466
2092
|
createRoute,
|
|
2467
2093
|
createReadableStreamFromAsyncGenerator,
|
|
2468
|
-
|
|
2469
|
-
createComponent,
|
|
2094
|
+
createAPIRoute,
|
|
2470
2095
|
buildRoutes,
|
|
2471
2096
|
StreamResponse,
|
|
2472
|
-
|
|
2473
|
-
HS_DEFAULT_LOADING,
|
|
2474
|
-
HSRoute,
|
|
2475
|
-
HSFormRoute,
|
|
2476
|
-
HSComponent
|
|
2097
|
+
IS_PROD
|
|
2477
2098
|
};
|