@hyperspan/framework 0.1.8 → 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 +39 -29
- 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 +5 -0
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|@[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/|^{{.*}}$|
|
|
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
|
}
|
|
@@ -841,7 +843,11 @@ var Hono = class {
|
|
|
841
843
|
optionHandler = options;
|
|
842
844
|
} else {
|
|
843
845
|
optionHandler = options.optionHandler;
|
|
844
|
-
|
|
846
|
+
if (options.replaceRequest === false) {
|
|
847
|
+
replaceRequest = (request) => request;
|
|
848
|
+
} else {
|
|
849
|
+
replaceRequest = options.replaceRequest;
|
|
850
|
+
}
|
|
845
851
|
}
|
|
846
852
|
}
|
|
847
853
|
const getOptions = optionHandler ? (c) => {
|
|
@@ -940,7 +946,7 @@ var Hono = class {
|
|
|
940
946
|
};
|
|
941
947
|
};
|
|
942
948
|
|
|
943
|
-
// node_modules/hono/dist/router/reg-exp-router/node.js
|
|
949
|
+
// ../../node_modules/hono/dist/router/reg-exp-router/node.js
|
|
944
950
|
var LABEL_REG_EXP_STR = "[^/]+";
|
|
945
951
|
var ONLY_WILDCARD_REG_EXP_STR = ".*";
|
|
946
952
|
var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
|
|
@@ -1041,7 +1047,7 @@ var Node = class {
|
|
|
1041
1047
|
}
|
|
1042
1048
|
};
|
|
1043
1049
|
|
|
1044
|
-
// node_modules/hono/dist/router/reg-exp-router/trie.js
|
|
1050
|
+
// ../../node_modules/hono/dist/router/reg-exp-router/trie.js
|
|
1045
1051
|
var Trie = class {
|
|
1046
1052
|
#context = { varIndex: 0 };
|
|
1047
1053
|
#root = new Node;
|
|
@@ -1097,7 +1103,7 @@ var Trie = class {
|
|
|
1097
1103
|
}
|
|
1098
1104
|
};
|
|
1099
1105
|
|
|
1100
|
-
// node_modules/hono/dist/router/reg-exp-router/router.js
|
|
1106
|
+
// ../../node_modules/hono/dist/router/reg-exp-router/router.js
|
|
1101
1107
|
var emptyParam = [];
|
|
1102
1108
|
var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
|
|
1103
1109
|
var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
|
|
@@ -1279,7 +1285,7 @@ var RegExpRouter = class {
|
|
|
1279
1285
|
}
|
|
1280
1286
|
};
|
|
1281
1287
|
|
|
1282
|
-
// node_modules/hono/dist/router/smart-router/router.js
|
|
1288
|
+
// ../../node_modules/hono/dist/router/smart-router/router.js
|
|
1283
1289
|
var SmartRouter = class {
|
|
1284
1290
|
name = "SmartRouter";
|
|
1285
1291
|
#routers = [];
|
|
@@ -1334,7 +1340,7 @@ var SmartRouter = class {
|
|
|
1334
1340
|
}
|
|
1335
1341
|
};
|
|
1336
1342
|
|
|
1337
|
-
// node_modules/hono/dist/router/trie-router/node.js
|
|
1343
|
+
// ../../node_modules/hono/dist/router/trie-router/node.js
|
|
1338
1344
|
var emptyParams = /* @__PURE__ */ Object.create(null);
|
|
1339
1345
|
var Node2 = class {
|
|
1340
1346
|
#methods;
|
|
@@ -1490,7 +1496,7 @@ var Node2 = class {
|
|
|
1490
1496
|
}
|
|
1491
1497
|
};
|
|
1492
1498
|
|
|
1493
|
-
// node_modules/hono/dist/router/trie-router/router.js
|
|
1499
|
+
// ../../node_modules/hono/dist/router/trie-router/router.js
|
|
1494
1500
|
var TrieRouter = class {
|
|
1495
1501
|
name = "TrieRouter";
|
|
1496
1502
|
#node;
|
|
@@ -1512,7 +1518,7 @@ var TrieRouter = class {
|
|
|
1512
1518
|
}
|
|
1513
1519
|
};
|
|
1514
1520
|
|
|
1515
|
-
// node_modules/hono/dist/hono.js
|
|
1521
|
+
// ../../node_modules/hono/dist/hono.js
|
|
1516
1522
|
var Hono2 = class extends Hono {
|
|
1517
1523
|
constructor(options = {}) {
|
|
1518
1524
|
super(options);
|
|
@@ -1522,13 +1528,13 @@ var Hono2 = class extends Hono {
|
|
|
1522
1528
|
}
|
|
1523
1529
|
};
|
|
1524
1530
|
|
|
1525
|
-
// node_modules/hono/dist/adapter/bun/serve-static.js
|
|
1531
|
+
// ../../node_modules/hono/dist/adapter/bun/serve-static.js
|
|
1526
1532
|
import { stat } from "node:fs/promises";
|
|
1527
1533
|
|
|
1528
|
-
// node_modules/hono/dist/utils/compress.js
|
|
1534
|
+
// ../../node_modules/hono/dist/utils/compress.js
|
|
1529
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;
|
|
1530
1536
|
|
|
1531
|
-
// node_modules/hono/dist/utils/filepath.js
|
|
1537
|
+
// ../../node_modules/hono/dist/utils/filepath.js
|
|
1532
1538
|
var getFilePath = (options) => {
|
|
1533
1539
|
let filename = options.filename;
|
|
1534
1540
|
const defaultDocument = options.defaultDocument || "index.html";
|
|
@@ -1560,7 +1566,7 @@ var getFilePathWithoutDefaultDocument = (options) => {
|
|
|
1560
1566
|
return path;
|
|
1561
1567
|
};
|
|
1562
1568
|
|
|
1563
|
-
// node_modules/hono/dist/utils/mime.js
|
|
1569
|
+
// ../../node_modules/hono/dist/utils/mime.js
|
|
1564
1570
|
var getMimeType = (filename, mimes = baseMimes) => {
|
|
1565
1571
|
const regexp = /\.([a-zA-Z0-9]+?)$/;
|
|
1566
1572
|
const match = filename.match(regexp);
|
|
@@ -1632,7 +1638,7 @@ var _baseMimes = {
|
|
|
1632
1638
|
};
|
|
1633
1639
|
var baseMimes = _baseMimes;
|
|
1634
1640
|
|
|
1635
|
-
// node_modules/hono/dist/middleware/serve-static/index.js
|
|
1641
|
+
// ../../node_modules/hono/dist/middleware/serve-static/index.js
|
|
1636
1642
|
var ENCODINGS = {
|
|
1637
1643
|
br: ".br",
|
|
1638
1644
|
zstd: ".zst",
|
|
@@ -1729,7 +1735,7 @@ var serveStatic = (options) => {
|
|
|
1729
1735
|
};
|
|
1730
1736
|
};
|
|
1731
1737
|
|
|
1732
|
-
// node_modules/hono/dist/adapter/bun/serve-static.js
|
|
1738
|
+
// ../../node_modules/hono/dist/adapter/bun/serve-static.js
|
|
1733
1739
|
var serveStatic2 = (options) => {
|
|
1734
1740
|
return async function serveStatic2(c, next) {
|
|
1735
1741
|
const getContent = async (path) => {
|
|
@@ -1757,7 +1763,7 @@ var serveStatic2 = (options) => {
|
|
|
1757
1763
|
};
|
|
1758
1764
|
};
|
|
1759
1765
|
|
|
1760
|
-
// node_modules/hono/dist/helper/ssg/middleware.js
|
|
1766
|
+
// ../../node_modules/hono/dist/helper/ssg/middleware.js
|
|
1761
1767
|
var X_HONO_DISABLE_SSG_HEADER_KEY = "x-hono-disable-ssg";
|
|
1762
1768
|
var SSG_DISABLED_RESPONSE = (() => {
|
|
1763
1769
|
try {
|
|
@@ -1769,10 +1775,10 @@ var SSG_DISABLED_RESPONSE = (() => {
|
|
|
1769
1775
|
return null;
|
|
1770
1776
|
}
|
|
1771
1777
|
})();
|
|
1772
|
-
// node_modules/hono/dist/adapter/bun/ssg.js
|
|
1778
|
+
// ../../node_modules/hono/dist/adapter/bun/ssg.js
|
|
1773
1779
|
var { write } = Bun;
|
|
1774
1780
|
|
|
1775
|
-
// node_modules/hono/dist/helper/websocket/index.js
|
|
1781
|
+
// ../../node_modules/hono/dist/helper/websocket/index.js
|
|
1776
1782
|
var WSContext = class {
|
|
1777
1783
|
#init;
|
|
1778
1784
|
constructor(init) {
|
|
@@ -1796,7 +1802,7 @@ var WSContext = class {
|
|
|
1796
1802
|
}
|
|
1797
1803
|
};
|
|
1798
1804
|
|
|
1799
|
-
// node_modules/hono/dist/http-exception.js
|
|
1805
|
+
// ../../node_modules/hono/dist/http-exception.js
|
|
1800
1806
|
var HTTPException = class extends Error {
|
|
1801
1807
|
res;
|
|
1802
1808
|
status;
|
|
@@ -1822,6 +1828,9 @@ var HTTPException = class extends Error {
|
|
|
1822
1828
|
// src/server.ts
|
|
1823
1829
|
var IS_PROD = false;
|
|
1824
1830
|
var CWD = process.cwd();
|
|
1831
|
+
function createConfig(config) {
|
|
1832
|
+
return config;
|
|
1833
|
+
}
|
|
1825
1834
|
function createRoute(handler) {
|
|
1826
1835
|
let _handlers = {};
|
|
1827
1836
|
if (handler) {
|
|
@@ -2093,6 +2102,7 @@ export {
|
|
|
2093
2102
|
createRoute,
|
|
2094
2103
|
createReadableStreamFromAsyncGenerator,
|
|
2095
2104
|
createLayout,
|
|
2105
|
+
createConfig,
|
|
2096
2106
|
createAPIRoute,
|
|
2097
2107
|
buildRoutes,
|
|
2098
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
|
-
await 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
|
@@ -28,6 +28,10 @@ export type THSRoute = {
|
|
|
28
28
|
run: (method: string, context: Context) => Promise<Response>;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
export function createConfig(config: THSServerConfig): THSServerConfig {
|
|
32
|
+
return config;
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
/**
|
|
32
36
|
* Define a route that can handle a direct HTTP request.
|
|
33
37
|
* Route handlers should return a HSHtml or Response object
|
|
@@ -234,6 +238,7 @@ export type THSServerConfig = {
|
|
|
234
238
|
appDir: string;
|
|
235
239
|
staticFileRoot: string;
|
|
236
240
|
rewrites?: Array<{ source: string; destination: string }>;
|
|
241
|
+
islandPlugins?: Array<any>; // Loaders for client islands
|
|
237
242
|
// For customizing the routes and adding your own...
|
|
238
243
|
beforeRoutesAdded?: (app: Hono) => void;
|
|
239
244
|
afterRoutesAdded?: (app: Hono) => void;
|