@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/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 as readdir2 } from "node:fs/promises";
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 = "&quot;";
28
- break;
29
- case 38:
30
- escape = "&amp;";
31
- break;
32
- case 39:
33
- escape = "&#39;";
34
- break;
35
- case 60:
36
- escape = "&lt;";
37
- break;
38
- case 62:
39
- escape = "&gt;";
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 = (html2, arg, headers) => {
699
+ html = (html, arg, headers) => {
924
700
  this.#preparedHeaders ??= {};
925
701
  this.#preparedHeaders["content-type"] = "text/html; charset=UTF-8";
926
- if (typeof html2 === "object") {
927
- return resolveCallback(html2, HtmlEscapedCallbackPhase.Stringify, false, {}).then((html22) => {
928
- return typeof arg === "number" ? this.#newResponse(html22, arg, headers) : this.#newResponse(html22, arg);
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(html2, arg, headers) : this.#newResponse(html2, arg);
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/@zod/core/dist/esm/core.js
2024
- var $brand = Symbol("zod_brand");
2025
-
2026
- class $ZodAsyncError extends Error {
2027
- constructor() {
2028
- 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
+ });
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
- // node_modules/@zod/core/dist/esm/util.js
2039
- function jsonStringifyReplacer(_, value) {
2040
- if (typeof value === "bigint")
2041
- return value.toString();
2042
- return value;
2043
- }
2044
- function cached(getter) {
2045
- const set = false;
2046
- return {
2047
- get value() {
2048
- if (!set) {
2049
- const value = getter();
2050
- Object.defineProperty(this, "value", { value });
2051
- 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
+ }
2052
1872
  }
2053
- throw new Error("cached value already set");
1873
+ return context.text(String(routeContent));
2054
1874
  }
2055
1875
  };
1876
+ return api;
2056
1877
  }
2057
- var allowsEval = cached(() => {
2058
- try {
2059
- new Function("");
2060
- return true;
2061
- } catch (_) {
2062
- return false;
2063
- }
2064
- });
2065
- var propertyKeyTypes = new Set(["string", "number", "symbol"]);
2066
- var primitiveTypes = new Set(["string", "number", "bigint", "boolean", "symbol", "undefined"]);
2067
- var NUMBER_FORMAT_RANGES = {
2068
- safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
2069
- int32: [-2147483648, 2147483647],
2070
- uint32: [0, 4294967295],
2071
- float32: [-340282346638528860000000000000000000000, 340282346638528860000000000000000000000],
2072
- float64: [-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000]
2073
- };
2074
- function unwrapMessage(message) {
2075
- return typeof message === "string" ? message : message?.message;
2076
- }
2077
- function finalizeIssue(iss, ctx, config2) {
2078
- const full = { ...iss, path: iss.path ?? [] };
2079
- if (!iss.message) {
2080
- const message = unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config2.customError?.(iss)) ?? unwrapMessage(config2.localeError?.(iss)) ?? "Invalid input";
2081
- full.message = message;
2082
- }
2083
- delete full.inst;
2084
- delete full.continue;
2085
- if (!ctx?.reportInput) {
2086
- delete full.input;
2087
- }
2088
- return full;
2089
- }
2090
-
2091
- // node_modules/@zod/core/dist/esm/errors.js
2092
- var ZOD_ERROR = Symbol.for("{{zod.error}}");
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
- processError(error);
2154
- return fieldErrors;
1931
+ return api;
2155
1932
  }
2156
-
2157
- // node_modules/@zod/core/dist/esm/parse.js
2158
- function _parse(schema, value, _ctx) {
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
- if (result.issues.length) {
2165
- throw new (this?.Error ?? $ZodError)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
1937
+ const kind = typeof route;
1938
+ if (kind === "function") {
1939
+ return createRoute(route);
2166
1940
  }
2167
- return result.value;
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
- // node_modules/zod/dist/esm/parse.js
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>${!IS_PROD2 && err.stack ? err.stack.split(`
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(config2) {
2355
- const routesDir = join(config2.appDir, "routes");
1964
+ async function buildRoutes(config) {
1965
+ const routesDir = join(config.appDir, "routes");
2356
1966
  console.log(routesDir);
2357
- const files = await readdir2(routesDir, { recursive: true });
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
- 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) {
2391
2017
  await Promise.all([buildClientJS(), buildClientCSS()]);
2392
2018
  const app = new Hono2;
2393
- config2.beforeRoutesAdded && config2.beforeRoutesAdded(app);
2394
- const fileRoutes = await buildRoutes(config2);
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, async (context) => {
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 (!IS_PROD2) {
2035
+ if (!IS_PROD) {
2416
2036
  console.log("[Hyperspan] File system routes (in app/routes):");
2417
2037
  console.table(routeMap);
2418
2038
  }
2419
- config2.afterRoutesAdded && config2.afterRoutesAdded(app);
2039
+ config.afterRoutesAdded && config.afterRoutesAdded(app);
2420
2040
  app.use("*", serveStatic2({
2421
- root: config2.staticFileRoot
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
- createForm,
2469
- createComponent,
2094
+ createAPIRoute,
2470
2095
  buildRoutes,
2471
2096
  StreamResponse,
2472
- IS_PROD2 as IS_PROD,
2473
- HS_DEFAULT_LOADING,
2474
- HSRoute,
2475
- HSFormRoute,
2476
- HSComponent
2097
+ IS_PROD
2477
2098
  };