@hyperspan/framework 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +30 -0
- package/README.md +8 -2
- package/build.ts +1 -1
- package/dist/assets.js +18 -49
- package/dist/server.js +40 -33
- package/package.json +10 -15
- package/src/actions.test.ts +1 -1
- package/src/actions.ts +1 -1
- package/src/assets.ts +20 -67
- package/src/clientjs/preact.ts +2 -1
- package/src/server.ts +22 -8
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Hyperspan copyright (c) 2025, Vance Lucas.
|
|
4
|
+
LINK: https://www.hyperspan.dev
|
|
5
|
+
All rights reserved.
|
|
6
|
+
|
|
7
|
+
Redistribution and use in source and binary forms, with or without
|
|
8
|
+
modification, are permitted provided that the following conditions are met:
|
|
9
|
+
|
|
10
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
11
|
+
list of conditions and the following disclaimer.
|
|
12
|
+
|
|
13
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
14
|
+
this list of conditions and the following disclaimer in the documentation
|
|
15
|
+
and/or other materials provided with the distribution.
|
|
16
|
+
|
|
17
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
18
|
+
contributors may be used to endorse or promote products derived from
|
|
19
|
+
this software without specific prior written permission.
|
|
20
|
+
|
|
21
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
22
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
23
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
24
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
25
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
26
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
27
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
28
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
29
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
30
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @hyperspan/framework
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
Hyperspan web framework!
|
|
10
|
+
|
|
11
|
+
Information and docs are at [Hyperspan.dev](https://www.hyperspan.dev)
|
package/build.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { build } from 'bun';
|
|
2
2
|
|
|
3
3
|
const entrypoints = ['./src/server.ts', './src/assets.ts'];
|
|
4
|
-
const external = ['@hyperspan/html'];
|
|
4
|
+
const external = ['@hyperspan/html', 'preact', 'preact-render-to-string'];
|
|
5
5
|
const outdir = './dist';
|
|
6
6
|
const target = 'node';
|
|
7
7
|
const splitting = true;
|
package/dist/assets.js
CHANGED
|
@@ -55,67 +55,36 @@ function hyperspanScriptTags() {
|
|
|
55
55
|
></script>`)}
|
|
56
56
|
`;
|
|
57
57
|
}
|
|
58
|
-
|
|
59
|
-
function md5(content) {
|
|
58
|
+
function assetHash(content) {
|
|
60
59
|
return createHash("md5").update(content).digest("hex");
|
|
61
60
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
target: "browser"
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
async function createPreactIsland(file) {
|
|
73
|
-
let filePath = file.replace("file://", "");
|
|
74
|
-
const jsId = md5(filePath);
|
|
75
|
-
if (!clientImportMap.has("preact")) {
|
|
76
|
-
await copyPreactToPublicFolder();
|
|
77
|
-
clientImportMap.set("preact", "" + PREACT_PUBLIC_FILE_PATH);
|
|
78
|
-
clientImportMap.set("preact/compat", "" + PREACT_PUBLIC_FILE_PATH);
|
|
79
|
-
clientImportMap.set("preact/hooks", "" + PREACT_PUBLIC_FILE_PATH);
|
|
80
|
-
clientImportMap.set("preact/jsx-runtime", "" + PREACT_PUBLIC_FILE_PATH);
|
|
61
|
+
var ISLAND_PUBLIC_PATH = "/_hs/js/islands";
|
|
62
|
+
var ISLAND_DEFAULTS = () => ({
|
|
63
|
+
ssr: true
|
|
64
|
+
});
|
|
65
|
+
function renderIsland(Component, props, options = ISLAND_DEFAULTS()) {
|
|
66
|
+
if (Component.__HS_ISLAND?.render) {
|
|
67
|
+
return Component.__HS_ISLAND.render(props, options);
|
|
81
68
|
}
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
clientImportMap.set("react-dom", "." + PREACT_PUBLIC_FILE_PATH);
|
|
69
|
+
if (Component.__HS_ISLAND?.ssr && options.ssr) {
|
|
70
|
+
return Component.__HS_ISLAND.ssr(props);
|
|
85
71
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
entrypoints: [filePath],
|
|
89
|
-
minify: true,
|
|
90
|
-
external: ["react", "preact"],
|
|
91
|
-
env: "APP_PUBLIC_*"
|
|
92
|
-
});
|
|
93
|
-
for (const output of buildResult.outputs) {
|
|
94
|
-
resultStr += await output.text();
|
|
95
|
-
}
|
|
96
|
-
const r = /export\{([a-zA-Z]+) as default\}/g;
|
|
97
|
-
const matchExport = r.exec(resultStr);
|
|
98
|
-
if (!matchExport) {
|
|
99
|
-
throw new Error("File does not have a default export! Ensure a function has export default to use this.");
|
|
72
|
+
if (Component.__HS_ISLAND?.clientOnly) {
|
|
73
|
+
return Component.__HS_ISLAND.clientOnly(props);
|
|
100
74
|
}
|
|
101
|
-
|
|
102
|
-
let _mounted = false;
|
|
103
|
-
return (props) => {
|
|
104
|
-
if (!_mounted) {
|
|
105
|
-
_mounted = true;
|
|
106
|
-
resultStr += `render(h(${fn}, ${JSON.stringify(props)}), document.getElementById("${jsId}"));`;
|
|
107
|
-
}
|
|
108
|
-
return html.raw(`<div id="${jsId}"></div><script type="module" data-source-id="${jsId}">${resultStr}</script>`);
|
|
109
|
-
};
|
|
75
|
+
throw new Error(`Module ${Component.name} was not loaded with an island plugin! Did you forget to install an island plugin and add it to the createServer() 'islandPlugins' config?`);
|
|
110
76
|
}
|
|
111
77
|
export {
|
|
78
|
+
renderIsland,
|
|
112
79
|
hyperspanStyleTags,
|
|
113
80
|
hyperspanScriptTags,
|
|
114
|
-
createPreactIsland,
|
|
115
81
|
clientJSFiles,
|
|
116
82
|
clientImportMap,
|
|
117
83
|
clientCSSFiles,
|
|
118
84
|
buildClientJS,
|
|
119
|
-
buildClientCSS
|
|
85
|
+
buildClientCSS,
|
|
86
|
+
assetHash,
|
|
87
|
+
ISLAND_PUBLIC_PATH,
|
|
88
|
+
ISLAND_DEFAULTS
|
|
120
89
|
};
|
|
121
90
|
|
package/dist/server.js
CHANGED
|
@@ -8,8 +8,8 @@ import { readdir } from "node:fs/promises";
|
|
|
8
8
|
import { basename, extname, join } from "node:path";
|
|
9
9
|
import { html, isHSHtml, renderStream, renderAsync, render } from "@hyperspan/html";
|
|
10
10
|
|
|
11
|
-
// node_modules/isbot/index.mjs
|
|
12
|
-
var fullPattern = " daum[ /]| deusu/| yadirectfetcher|(?:^|[^g])news(?!sapphire)|(?<! (?:channel/|google/))google(?!(app|/google| pixel))|(?<! cu)bots?(?:\\b|_)|(?<!(?:lib))http|(?<![hg]m)score|(?<!cam)scan|@[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/|^{{.*}}$|analyzer|archive|ask jeeves/teoma|audit|bit\\.ly/|bluecoat drtr|browsex|burpcollaborator|capture|catch|check\\b|checker|chrome-lighthouse|chromeframe|classifier|cloudflare|convertify|crawl|cypress/|dareboost|datanyze|dejaclick|detect|dmbrowser|download|evc-batch/|exaleadcloudview|feed|firephp|functionize|gomezagent|grab|headless|httrack|hubspot marketing grader|hydra|ibisbrowser|infrawatch|insight|inspect|iplabel|ips-agent|java(?!;)|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|scrape|server|sogou|sparkler/|speedcurve|spider|splash|statuscake|supercleaner|synapse|synthetic|tools|torrent|transcoder|url|validator|virtuoso|wappalyzer|webglance|webkit2png|whatcms/";
|
|
11
|
+
// ../../node_modules/isbot/index.mjs
|
|
12
|
+
var fullPattern = " daum[ /]| deusu/| yadirectfetcher|(?:^|[^g])news(?!sapphire)|(?<! (?:channel/|google/))google(?!(app|/google| pixel))|(?<! cu)bots?(?:\\b|_)|(?<!(?:lib))http|(?<![hg]m)score|(?<!cam)scan|@[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/|^{{.*}}$|analyzer|archive|ask jeeves/teoma|audit|bit\\.ly/|bluecoat drtr|browsex|burpcollaborator|capture|catch|check\\b|checker|chrome-lighthouse|chromeframe|classifier|cloudflare|convertify|crawl|cypress/|dareboost|datanyze|dejaclick|detect|dmbrowser|download|evc-batch/|exaleadcloudview|feed|firephp|functionize|gomezagent|grab|headless|httrack|hubspot marketing grader|hydra|ibisbrowser|infrawatch|insight|inspect|iplabel|ips-agent|java(?!;)|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|scrape|server|sogou|sparkler/|speedcurve|spider|splash|statuscake|supercleaner|synapse|synthetic|tools|torrent|transcoder|url|validator|virtuoso|wappalyzer|webglance|webkit2png|whatcms/|xtate/";
|
|
13
13
|
var naivePattern = /bot|crawl|http|lighthouse|scan|search|spider/i;
|
|
14
14
|
var pattern;
|
|
15
15
|
function getPattern() {
|
|
@@ -27,7 +27,7 @@ function isbot(userAgent) {
|
|
|
27
27
|
return Boolean(userAgent) && getPattern().test(userAgent);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
// node_modules/hono/dist/compose.js
|
|
30
|
+
// ../../node_modules/hono/dist/compose.js
|
|
31
31
|
var compose = (middleware, onError, onNotFound) => {
|
|
32
32
|
return (context, next) => {
|
|
33
33
|
let index = -1;
|
|
@@ -71,7 +71,7 @@ var compose = (middleware, onError, onNotFound) => {
|
|
|
71
71
|
};
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
// node_modules/hono/dist/utils/body.js
|
|
74
|
+
// ../../node_modules/hono/dist/utils/body.js
|
|
75
75
|
var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
|
|
76
76
|
const { all = false, dot = false } = options;
|
|
77
77
|
const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;
|
|
@@ -135,7 +135,7 @@ var handleParsingNestedValues = (form, key, value) => {
|
|
|
135
135
|
});
|
|
136
136
|
};
|
|
137
137
|
|
|
138
|
-
// node_modules/hono/dist/utils/url.js
|
|
138
|
+
// ../../node_modules/hono/dist/utils/url.js
|
|
139
139
|
var splitPath = (path) => {
|
|
140
140
|
const paths = path.split("/");
|
|
141
141
|
if (paths[0] === "") {
|
|
@@ -330,7 +330,7 @@ var getQueryParams = (url, key) => {
|
|
|
330
330
|
};
|
|
331
331
|
var decodeURIComponent_ = decodeURIComponent;
|
|
332
332
|
|
|
333
|
-
// node_modules/hono/dist/request.js
|
|
333
|
+
// ../../node_modules/hono/dist/request.js
|
|
334
334
|
var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
|
|
335
335
|
var HonoRequest = class {
|
|
336
336
|
raw;
|
|
@@ -438,7 +438,7 @@ var HonoRequest = class {
|
|
|
438
438
|
}
|
|
439
439
|
};
|
|
440
440
|
|
|
441
|
-
// node_modules/hono/dist/utils/html.js
|
|
441
|
+
// ../../node_modules/hono/dist/utils/html.js
|
|
442
442
|
var HtmlEscapedCallbackPhase = {
|
|
443
443
|
Stringify: 1,
|
|
444
444
|
BeforeStream: 2,
|
|
@@ -476,7 +476,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) =>
|
|
|
476
476
|
}
|
|
477
477
|
};
|
|
478
478
|
|
|
479
|
-
// node_modules/hono/dist/context.js
|
|
479
|
+
// ../../node_modules/hono/dist/context.js
|
|
480
480
|
var TEXT_PLAIN = "text/plain; charset=UTF-8";
|
|
481
481
|
var setHeaders = (headers, map = {}) => {
|
|
482
482
|
for (const key of Object.keys(map)) {
|
|
@@ -717,7 +717,7 @@ var Context = class {
|
|
|
717
717
|
};
|
|
718
718
|
};
|
|
719
719
|
|
|
720
|
-
// node_modules/hono/dist/router.js
|
|
720
|
+
// ../../node_modules/hono/dist/router.js
|
|
721
721
|
var METHOD_NAME_ALL = "ALL";
|
|
722
722
|
var METHOD_NAME_ALL_LOWERCASE = "all";
|
|
723
723
|
var METHODS = ["get", "post", "put", "delete", "options", "patch"];
|
|
@@ -725,10 +725,10 @@ var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is
|
|
|
725
725
|
var UnsupportedPathError = class extends Error {
|
|
726
726
|
};
|
|
727
727
|
|
|
728
|
-
// node_modules/hono/dist/utils/constants.js
|
|
728
|
+
// ../../node_modules/hono/dist/utils/constants.js
|
|
729
729
|
var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
|
|
730
730
|
|
|
731
|
-
// node_modules/hono/dist/hono-base.js
|
|
731
|
+
// ../../node_modules/hono/dist/hono-base.js
|
|
732
732
|
var notFoundHandler = (c) => {
|
|
733
733
|
return c.text("404 Not Found", 404);
|
|
734
734
|
};
|
|
@@ -801,6 +801,8 @@ var Hono = class {
|
|
|
801
801
|
router: this.router,
|
|
802
802
|
getPath: this.getPath
|
|
803
803
|
});
|
|
804
|
+
clone.errorHandler = this.errorHandler;
|
|
805
|
+
clone.#notFoundHandler = this.#notFoundHandler;
|
|
804
806
|
clone.routes = this.routes;
|
|
805
807
|
return clone;
|
|
806
808
|
}
|
|
@@ -855,8 +857,7 @@ var Hono = class {
|
|
|
855
857
|
let executionContext = undefined;
|
|
856
858
|
try {
|
|
857
859
|
executionContext = c.executionCtx;
|
|
858
|
-
} catch {
|
|
859
|
-
}
|
|
860
|
+
} catch {}
|
|
860
861
|
return [c.env, executionContext];
|
|
861
862
|
};
|
|
862
863
|
replaceRequest ||= (() => {
|
|
@@ -945,7 +946,7 @@ var Hono = class {
|
|
|
945
946
|
};
|
|
946
947
|
};
|
|
947
948
|
|
|
948
|
-
// node_modules/hono/dist/router/reg-exp-router/node.js
|
|
949
|
+
// ../../node_modules/hono/dist/router/reg-exp-router/node.js
|
|
949
950
|
var LABEL_REG_EXP_STR = "[^/]+";
|
|
950
951
|
var ONLY_WILDCARD_REG_EXP_STR = ".*";
|
|
951
952
|
var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
|
|
@@ -1046,7 +1047,7 @@ var Node = class {
|
|
|
1046
1047
|
}
|
|
1047
1048
|
};
|
|
1048
1049
|
|
|
1049
|
-
// node_modules/hono/dist/router/reg-exp-router/trie.js
|
|
1050
|
+
// ../../node_modules/hono/dist/router/reg-exp-router/trie.js
|
|
1050
1051
|
var Trie = class {
|
|
1051
1052
|
#context = { varIndex: 0 };
|
|
1052
1053
|
#root = new Node;
|
|
@@ -1102,7 +1103,7 @@ var Trie = class {
|
|
|
1102
1103
|
}
|
|
1103
1104
|
};
|
|
1104
1105
|
|
|
1105
|
-
// node_modules/hono/dist/router/reg-exp-router/router.js
|
|
1106
|
+
// ../../node_modules/hono/dist/router/reg-exp-router/router.js
|
|
1106
1107
|
var emptyParam = [];
|
|
1107
1108
|
var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
|
|
1108
1109
|
var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
|
|
@@ -1284,7 +1285,7 @@ var RegExpRouter = class {
|
|
|
1284
1285
|
}
|
|
1285
1286
|
};
|
|
1286
1287
|
|
|
1287
|
-
// node_modules/hono/dist/router/smart-router/router.js
|
|
1288
|
+
// ../../node_modules/hono/dist/router/smart-router/router.js
|
|
1288
1289
|
var SmartRouter = class {
|
|
1289
1290
|
name = "SmartRouter";
|
|
1290
1291
|
#routers = [];
|
|
@@ -1339,7 +1340,7 @@ var SmartRouter = class {
|
|
|
1339
1340
|
}
|
|
1340
1341
|
};
|
|
1341
1342
|
|
|
1342
|
-
// node_modules/hono/dist/router/trie-router/node.js
|
|
1343
|
+
// ../../node_modules/hono/dist/router/trie-router/node.js
|
|
1343
1344
|
var emptyParams = /* @__PURE__ */ Object.create(null);
|
|
1344
1345
|
var Node2 = class {
|
|
1345
1346
|
#methods;
|
|
@@ -1495,7 +1496,7 @@ var Node2 = class {
|
|
|
1495
1496
|
}
|
|
1496
1497
|
};
|
|
1497
1498
|
|
|
1498
|
-
// node_modules/hono/dist/router/trie-router/router.js
|
|
1499
|
+
// ../../node_modules/hono/dist/router/trie-router/router.js
|
|
1499
1500
|
var TrieRouter = class {
|
|
1500
1501
|
name = "TrieRouter";
|
|
1501
1502
|
#node;
|
|
@@ -1517,7 +1518,7 @@ var TrieRouter = class {
|
|
|
1517
1518
|
}
|
|
1518
1519
|
};
|
|
1519
1520
|
|
|
1520
|
-
// node_modules/hono/dist/hono.js
|
|
1521
|
+
// ../../node_modules/hono/dist/hono.js
|
|
1521
1522
|
var Hono2 = class extends Hono {
|
|
1522
1523
|
constructor(options = {}) {
|
|
1523
1524
|
super(options);
|
|
@@ -1527,13 +1528,13 @@ var Hono2 = class extends Hono {
|
|
|
1527
1528
|
}
|
|
1528
1529
|
};
|
|
1529
1530
|
|
|
1530
|
-
// node_modules/hono/dist/adapter/bun/serve-static.js
|
|
1531
|
+
// ../../node_modules/hono/dist/adapter/bun/serve-static.js
|
|
1531
1532
|
import { stat } from "node:fs/promises";
|
|
1532
1533
|
|
|
1533
|
-
// node_modules/hono/dist/utils/compress.js
|
|
1534
|
+
// ../../node_modules/hono/dist/utils/compress.js
|
|
1534
1535
|
var COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/(?!event-stream(?:[;\s]|$))[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
|
|
1535
1536
|
|
|
1536
|
-
// node_modules/hono/dist/utils/filepath.js
|
|
1537
|
+
// ../../node_modules/hono/dist/utils/filepath.js
|
|
1537
1538
|
var getFilePath = (options) => {
|
|
1538
1539
|
let filename = options.filename;
|
|
1539
1540
|
const defaultDocument = options.defaultDocument || "index.html";
|
|
@@ -1565,7 +1566,7 @@ var getFilePathWithoutDefaultDocument = (options) => {
|
|
|
1565
1566
|
return path;
|
|
1566
1567
|
};
|
|
1567
1568
|
|
|
1568
|
-
// node_modules/hono/dist/utils/mime.js
|
|
1569
|
+
// ../../node_modules/hono/dist/utils/mime.js
|
|
1569
1570
|
var getMimeType = (filename, mimes = baseMimes) => {
|
|
1570
1571
|
const regexp = /\.([a-zA-Z0-9]+?)$/;
|
|
1571
1572
|
const match = filename.match(regexp);
|
|
@@ -1637,7 +1638,7 @@ var _baseMimes = {
|
|
|
1637
1638
|
};
|
|
1638
1639
|
var baseMimes = _baseMimes;
|
|
1639
1640
|
|
|
1640
|
-
// node_modules/hono/dist/middleware/serve-static/index.js
|
|
1641
|
+
// ../../node_modules/hono/dist/middleware/serve-static/index.js
|
|
1641
1642
|
var ENCODINGS = {
|
|
1642
1643
|
br: ".br",
|
|
1643
1644
|
zstd: ".zst",
|
|
@@ -1734,7 +1735,7 @@ var serveStatic = (options) => {
|
|
|
1734
1735
|
};
|
|
1735
1736
|
};
|
|
1736
1737
|
|
|
1737
|
-
// node_modules/hono/dist/adapter/bun/serve-static.js
|
|
1738
|
+
// ../../node_modules/hono/dist/adapter/bun/serve-static.js
|
|
1738
1739
|
var serveStatic2 = (options) => {
|
|
1739
1740
|
return async function serveStatic2(c, next) {
|
|
1740
1741
|
const getContent = async (path) => {
|
|
@@ -1750,8 +1751,7 @@ var serveStatic2 = (options) => {
|
|
|
1750
1751
|
try {
|
|
1751
1752
|
const stats = await stat(path);
|
|
1752
1753
|
isDir2 = stats.isDirectory();
|
|
1753
|
-
} catch {
|
|
1754
|
-
}
|
|
1754
|
+
} catch {}
|
|
1755
1755
|
return isDir2;
|
|
1756
1756
|
};
|
|
1757
1757
|
return serveStatic({
|
|
@@ -1763,7 +1763,7 @@ var serveStatic2 = (options) => {
|
|
|
1763
1763
|
};
|
|
1764
1764
|
};
|
|
1765
1765
|
|
|
1766
|
-
// node_modules/hono/dist/helper/ssg/middleware.js
|
|
1766
|
+
// ../../node_modules/hono/dist/helper/ssg/middleware.js
|
|
1767
1767
|
var X_HONO_DISABLE_SSG_HEADER_KEY = "x-hono-disable-ssg";
|
|
1768
1768
|
var SSG_DISABLED_RESPONSE = (() => {
|
|
1769
1769
|
try {
|
|
@@ -1775,10 +1775,10 @@ var SSG_DISABLED_RESPONSE = (() => {
|
|
|
1775
1775
|
return null;
|
|
1776
1776
|
}
|
|
1777
1777
|
})();
|
|
1778
|
-
// node_modules/hono/dist/adapter/bun/ssg.js
|
|
1778
|
+
// ../../node_modules/hono/dist/adapter/bun/ssg.js
|
|
1779
1779
|
var { write } = Bun;
|
|
1780
1780
|
|
|
1781
|
-
// node_modules/hono/dist/helper/websocket/index.js
|
|
1781
|
+
// ../../node_modules/hono/dist/helper/websocket/index.js
|
|
1782
1782
|
var WSContext = class {
|
|
1783
1783
|
#init;
|
|
1784
1784
|
constructor(init) {
|
|
@@ -1802,7 +1802,7 @@ var WSContext = class {
|
|
|
1802
1802
|
}
|
|
1803
1803
|
};
|
|
1804
1804
|
|
|
1805
|
-
// node_modules/hono/dist/http-exception.js
|
|
1805
|
+
// ../../node_modules/hono/dist/http-exception.js
|
|
1806
1806
|
var HTTPException = class extends Error {
|
|
1807
1807
|
res;
|
|
1808
1808
|
status;
|
|
@@ -1828,6 +1828,9 @@ var HTTPException = class extends Error {
|
|
|
1828
1828
|
// src/server.ts
|
|
1829
1829
|
var IS_PROD = false;
|
|
1830
1830
|
var CWD = process.cwd();
|
|
1831
|
+
function createConfig(config) {
|
|
1832
|
+
return config;
|
|
1833
|
+
}
|
|
1831
1834
|
function createRoute(handler) {
|
|
1832
1835
|
let _handlers = {};
|
|
1833
1836
|
if (handler) {
|
|
@@ -1952,6 +1955,9 @@ function getRunnableRoute(route) {
|
|
|
1952
1955
|
function isRunnableRoute(route) {
|
|
1953
1956
|
return typeof route === "object" && "run" in route;
|
|
1954
1957
|
}
|
|
1958
|
+
function createLayout(layout) {
|
|
1959
|
+
return layout;
|
|
1960
|
+
}
|
|
1955
1961
|
async function showErrorReponse(context, err) {
|
|
1956
1962
|
const output = render(html`
|
|
1957
1963
|
<main>
|
|
@@ -1969,7 +1975,6 @@ async function showErrorReponse(context, err) {
|
|
|
1969
1975
|
var ROUTE_SEGMENT = /(\[[a-zA-Z_\.]+\])/g;
|
|
1970
1976
|
async function buildRoutes(config) {
|
|
1971
1977
|
const routesDir = join(config.appDir, "routes");
|
|
1972
|
-
console.log(routesDir);
|
|
1973
1978
|
const files = await readdir(routesDir, { recursive: true });
|
|
1974
1979
|
const routes = [];
|
|
1975
1980
|
for (const file of files) {
|
|
@@ -2096,6 +2101,8 @@ export {
|
|
|
2096
2101
|
createRouteFromModule,
|
|
2097
2102
|
createRoute,
|
|
2098
2103
|
createReadableStreamFromAsyncGenerator,
|
|
2104
|
+
createLayout,
|
|
2105
|
+
createConfig,
|
|
2099
2106
|
createAPIRoute,
|
|
2100
2107
|
buildRoutes,
|
|
2101
2108
|
StreamResponse,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperspan/framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Hyperspan Web Framework",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"types": "src/server.ts",
|
|
@@ -53,21 +53,16 @@
|
|
|
53
53
|
"prepack": "bun run clean && bun run build"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@types/bun": "^1.
|
|
57
|
-
"@types/node": "^22.
|
|
58
|
-
"@types/react": "^19.1.
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"prettier": "^3.2.5"
|
|
62
|
-
},
|
|
63
|
-
"peerDependencies": {
|
|
64
|
-
"typescript": "^5.0.0"
|
|
56
|
+
"@types/bun": "^1.2.14",
|
|
57
|
+
"@types/node": "^22.15.20",
|
|
58
|
+
"@types/react": "^19.1.5",
|
|
59
|
+
"prettier": "^3.5.3",
|
|
60
|
+
"typescript": "^5.8.3"
|
|
65
61
|
},
|
|
66
62
|
"dependencies": {
|
|
67
|
-
"@hyperspan/html": "^0.1.
|
|
68
|
-
"hono": "^4.7.
|
|
69
|
-
"isbot": "^5.1.
|
|
70
|
-
"
|
|
71
|
-
"zod": "^4.0.0-beta.20250415T232143"
|
|
63
|
+
"@hyperspan/html": "^0.1.7",
|
|
64
|
+
"hono": "^4.7.10",
|
|
65
|
+
"isbot": "^5.1.28",
|
|
66
|
+
"zod": "^3.25.28"
|
|
72
67
|
}
|
|
73
68
|
}
|
package/src/actions.test.ts
CHANGED
package/src/actions.ts
CHANGED
package/src/assets.ts
CHANGED
|
@@ -89,82 +89,35 @@ export function hyperspanScriptTags() {
|
|
|
89
89
|
`;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
const PREACT_PUBLIC_FILE_PATH = '/_hs/js/preact.js';
|
|
94
|
-
|
|
95
|
-
function md5(content: string): string {
|
|
92
|
+
export function assetHash(content: string): string {
|
|
96
93
|
return createHash('md5').update(content).digest('hex');
|
|
97
94
|
}
|
|
98
95
|
|
|
99
96
|
/**
|
|
100
|
-
*
|
|
101
|
-
*/
|
|
102
|
-
async function copyPreactToPublicFolder() {
|
|
103
|
-
const sourceFile = resolve(PWD, '../', './src/clientjs/preact.ts');
|
|
104
|
-
const preactClient = Bun.build({
|
|
105
|
-
entrypoints: [sourceFile],
|
|
106
|
-
outdir: './public/_hs/js',
|
|
107
|
-
minify: true,
|
|
108
|
-
format: 'esm',
|
|
109
|
-
target: 'browser',
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Return a Preact component, mounted as an island in a <script> tag so it can be embedded into the page response.
|
|
97
|
+
* Island defaults
|
|
115
98
|
*/
|
|
116
|
-
export
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
clientImportMap.set('preact/hooks', '' + PREACT_PUBLIC_FILE_PATH);
|
|
126
|
-
clientImportMap.set('preact/jsx-runtime', '' + PREACT_PUBLIC_FILE_PATH);
|
|
99
|
+
export const ISLAND_PUBLIC_PATH = '/_hs/js/islands';
|
|
100
|
+
export const ISLAND_DEFAULTS = () => ({
|
|
101
|
+
ssr: true,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
export function renderIsland(Component: any, props: any, options = ISLAND_DEFAULTS()) {
|
|
105
|
+
// Render is an OPTIONAL override that allows you to render the island with your own logic
|
|
106
|
+
if (Component.__HS_ISLAND?.render) {
|
|
107
|
+
return Component.__HS_ISLAND.render(props, options);
|
|
127
108
|
}
|
|
128
|
-
if (!clientImportMap.has('react')) {
|
|
129
|
-
clientImportMap.set('react', '.' + PREACT_PUBLIC_FILE_PATH);
|
|
130
|
-
clientImportMap.set('react-dom', '.' + PREACT_PUBLIC_FILE_PATH);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
let resultStr = 'import{h,render}from"preact";';
|
|
134
|
-
const buildResult = await Bun.build({
|
|
135
|
-
entrypoints: [filePath],
|
|
136
|
-
minify: true,
|
|
137
|
-
external: ['react', 'preact'],
|
|
138
|
-
// @ts-ignore
|
|
139
|
-
env: 'APP_PUBLIC_*', // Inlines any ENV that starts with 'APP_PUBLIC_'
|
|
140
|
-
});
|
|
141
109
|
|
|
142
|
-
|
|
143
|
-
|
|
110
|
+
// If ssr is true, render the island with the ssr function
|
|
111
|
+
if (Component.__HS_ISLAND?.ssr && options.ssr) {
|
|
112
|
+
return Component.__HS_ISLAND.ssr(props);
|
|
144
113
|
}
|
|
145
114
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (!matchExport) {
|
|
151
|
-
throw new Error(
|
|
152
|
-
'File does not have a default export! Ensure a function has export default to use this.'
|
|
153
|
-
);
|
|
115
|
+
// If ssr is false, render the island with the clientOnly function
|
|
116
|
+
if (Component.__HS_ISLAND?.clientOnly) {
|
|
117
|
+
return Component.__HS_ISLAND.clientOnly(props);
|
|
154
118
|
}
|
|
155
119
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// Return HTML that will embed this component
|
|
161
|
-
return (props: any) => {
|
|
162
|
-
if (!_mounted) {
|
|
163
|
-
_mounted = true;
|
|
164
|
-
resultStr += `render(h(${fn}, ${JSON.stringify(props)}), document.getElementById("${jsId}"));`;
|
|
165
|
-
}
|
|
166
|
-
return html.raw(
|
|
167
|
-
`<div id="${jsId}"></div><script type="module" data-source-id="${jsId}">${resultStr}</script>`
|
|
168
|
-
);
|
|
169
|
-
};
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Module ${Component.name} was not loaded with an island plugin! Did you forget to install an island plugin and add it to the createServer() 'islandPlugins' config?`
|
|
122
|
+
);
|
|
170
123
|
}
|
package/src/clientjs/preact.ts
CHANGED
package/src/server.ts
CHANGED
|
@@ -15,6 +15,8 @@ const CWD = process.cwd();
|
|
|
15
15
|
*/
|
|
16
16
|
export type THSResponseTypes = HSHtml | Response | string | null;
|
|
17
17
|
export type THSRouteHandler = (context: Context) => THSResponseTypes | Promise<THSResponseTypes>;
|
|
18
|
+
export type THSAPIResponseTypes = Response | Record<any, any> | void;
|
|
19
|
+
export type THSAPIRouteHandler = (context: Context) => THSResponseTypes | Promise<THSResponseTypes>;
|
|
18
20
|
|
|
19
21
|
export type THSRoute = {
|
|
20
22
|
_kind: 'hsRoute';
|
|
@@ -26,6 +28,10 @@ export type THSRoute = {
|
|
|
26
28
|
run: (method: string, context: Context) => Promise<Response>;
|
|
27
29
|
};
|
|
28
30
|
|
|
31
|
+
export function createConfig(config: THSServerConfig): THSServerConfig {
|
|
32
|
+
return config;
|
|
33
|
+
}
|
|
34
|
+
|
|
29
35
|
/**
|
|
30
36
|
* Define a route that can handle a direct HTTP request.
|
|
31
37
|
* Route handlers should return a HSHtml or Response object
|
|
@@ -100,8 +106,8 @@ export function createRoute(handler?: THSRouteHandler): THSRoute {
|
|
|
100
106
|
* Create new API Route
|
|
101
107
|
* API Route handlers should return a JSON object or a Response
|
|
102
108
|
*/
|
|
103
|
-
export function createAPIRoute(handler?:
|
|
104
|
-
let _handlers: Record<string,
|
|
109
|
+
export function createAPIRoute(handler?: THSAPIRouteHandler): THSRoute {
|
|
110
|
+
let _handlers: Record<string, THSAPIRouteHandler> = {};
|
|
105
111
|
|
|
106
112
|
if (handler) {
|
|
107
113
|
_handlers['GET'] = handler;
|
|
@@ -109,23 +115,23 @@ export function createAPIRoute(handler?: THSRouteHandler): THSRoute {
|
|
|
109
115
|
|
|
110
116
|
const api: THSRoute = {
|
|
111
117
|
_kind: 'hsRoute',
|
|
112
|
-
get(handler:
|
|
118
|
+
get(handler: THSAPIRouteHandler) {
|
|
113
119
|
_handlers['GET'] = handler;
|
|
114
120
|
return api;
|
|
115
121
|
},
|
|
116
|
-
post(handler:
|
|
122
|
+
post(handler: THSAPIRouteHandler) {
|
|
117
123
|
_handlers['POST'] = handler;
|
|
118
124
|
return api;
|
|
119
125
|
},
|
|
120
|
-
put(handler:
|
|
126
|
+
put(handler: THSAPIRouteHandler) {
|
|
121
127
|
_handlers['PUT'] = handler;
|
|
122
128
|
return api;
|
|
123
129
|
},
|
|
124
|
-
delete(handler:
|
|
130
|
+
delete(handler: THSAPIRouteHandler) {
|
|
125
131
|
_handlers['DELETE'] = handler;
|
|
126
132
|
return api;
|
|
127
133
|
},
|
|
128
|
-
patch(handler:
|
|
134
|
+
patch(handler: THSAPIRouteHandler) {
|
|
129
135
|
_handlers['PATCH'] = handler;
|
|
130
136
|
return api;
|
|
131
137
|
},
|
|
@@ -202,6 +208,14 @@ export function isRunnableRoute(route: unknown): boolean {
|
|
|
202
208
|
return typeof route === 'object' && 'run' in route;
|
|
203
209
|
}
|
|
204
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Create a layout for a Hyperspan app. Passthrough for now.
|
|
213
|
+
* Future intent is to be able to conditionally render a layout for full page content vs. partial content.
|
|
214
|
+
*/
|
|
215
|
+
export function createLayout<T>(layout: (props: T) => HSHtml | Promise<HSHtml>) {
|
|
216
|
+
return layout;
|
|
217
|
+
}
|
|
218
|
+
|
|
205
219
|
/**
|
|
206
220
|
* Basic error handling
|
|
207
221
|
* @TODO: Should check for and load user-customizeable template with special name (app/__error.ts ?)
|
|
@@ -224,6 +238,7 @@ export type THSServerConfig = {
|
|
|
224
238
|
appDir: string;
|
|
225
239
|
staticFileRoot: string;
|
|
226
240
|
rewrites?: Array<{ source: string; destination: string }>;
|
|
241
|
+
islandPlugins?: Array<any>; // Loaders for client islands
|
|
227
242
|
// For customizing the routes and adding your own...
|
|
228
243
|
beforeRoutesAdded?: (app: Hono) => void;
|
|
229
244
|
afterRoutesAdded?: (app: Hono) => void;
|
|
@@ -242,7 +257,6 @@ const ROUTE_SEGMENT = /(\[[a-zA-Z_\.]+\])/g;
|
|
|
242
257
|
export async function buildRoutes(config: THSServerConfig): Promise<THSRouteMap[]> {
|
|
243
258
|
// Walk all pages and add them as routes
|
|
244
259
|
const routesDir = join(config.appDir, 'routes');
|
|
245
|
-
console.log(routesDir);
|
|
246
260
|
const files = await readdir(routesDir, { recursive: true });
|
|
247
261
|
const routes: THSRouteMap[] = [];
|
|
248
262
|
|