@hyperspan/framework 0.0.3 → 0.1.1
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/README.md +3 -81
- package/build.ts +24 -26
- package/dist/assets.d.ts +34 -0
- package/dist/assets.js +394 -0
- package/dist/index.d.ts +101 -30
- package/dist/index.js +2421 -408
- package/dist/server.d.ts +85 -73
- package/dist/server.js +2142 -1603
- package/package.json +42 -29
- package/src/assets.ts +141 -0
- package/src/clientjs/hyperspan-client.ts +7 -175
- package/src/clientjs/idiomorph.esm.js +1278 -0
- package/src/clientjs/preact.ts +1 -0
- package/src/index.ts +1 -14
- package/src/server.ts +232 -151
- package/.prettierrc +0 -7
- package/bun.lockb +0 -0
- package/src/app.ts +0 -186
- package/src/clientjs/idomorph.esm.js +0 -854
- package/src/document.ts +0 -10
- package/src/forms.ts +0 -110
- package/src/html.test.ts +0 -69
- package/src/html.ts +0 -345
package/README.md
CHANGED
|
@@ -1,83 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @hyperspan/framework
|
|
2
2
|
|
|
3
|
-
> [!NOTE] > **Hyperspan is still in the
|
|
3
|
+
> [!NOTE] > **The Hyperspan framework package is still in the heavy development. APIs may change without notice.**
|
|
4
4
|
|
|
5
|
-
Hyperspan
|
|
6
|
-
|
|
7
|
-
No JSX. No Virtual DOM. No hydration time. No nonsense. Just blazing fast HTML strings with reactive templates.
|
|
8
|
-
|
|
9
|
-
## Who Is Hyperspan For?
|
|
10
|
-
|
|
11
|
-
Hyperspan is best for websites, web applications, and APIs that need fast streaming and dynamic server responses. It is
|
|
12
|
-
great for performance critical applications and delivers no client-side JavaScript by default. This helps to ensure your
|
|
13
|
-
website stays fast and lightweight for end users as it grows over time, since all client JavaScript is explicitly
|
|
14
|
-
opt-in.
|
|
15
|
-
|
|
16
|
-
## Why Bun?
|
|
17
|
-
|
|
18
|
-
[Bun](https://bun.sh) is a Node-compatible runtime that offers better performance and built-in TypeScript support so
|
|
19
|
-
transpiling is not required for writing type-safe server-side code. Bun also offers some other extras like built-in
|
|
20
|
-
ultra fast testing utilities so extra dependencies are not required to write high-quality code.
|
|
21
|
-
|
|
22
|
-
## React vs. Hyperspan
|
|
23
|
-
|
|
24
|
-
Hyperspan is not a React replacement. React is a client-side library to build interactive JavaScript based UIs.
|
|
25
|
-
Hyperspan is a full-stack server-side framework built to deliver high-performance streaming and dynamic responses, and
|
|
26
|
-
does not deliver any JavaScript to the client by default.
|
|
27
|
-
|
|
28
|
-
Hyperspan can be best thought of as an _alternative_ to React-based frameworks like Next.js. It has all the same types
|
|
29
|
-
of things, like file-based page routes, API routes, middleware, components, etc. but Hyperspan has a very different
|
|
30
|
-
runtime and execution model. React-based frameworks ship all React components and code to the client by default,
|
|
31
|
-
resulting in surprisingly large JS bundle sizes that grow more over time as your site does. Hyperspan takes the opposite
|
|
32
|
-
approach, and does not ship ANY components to the client by default. Client components are available, but are explicitly
|
|
33
|
-
opt-in and should generally be used sparingly.
|
|
34
|
-
|
|
35
|
-
React is all JavaScript, all the time, delivered to the client. Hyperspan is mostly static content and markup delivered
|
|
36
|
-
to the client, with JavaScript sprinkles where needed.
|
|
37
|
-
|
|
38
|
-
## Philosophy
|
|
39
|
-
|
|
40
|
-
### P1: Server-Side First.
|
|
41
|
-
|
|
42
|
-
Current JavaScript frameworks ship everything to the browser by default and use non-intuitive ways to opt-out of this.
|
|
43
|
-
The first clue that this approach was backwards was when JavaScript bundle sizes started being measured in hundreds of
|
|
44
|
-
kilobytes to megabytes.
|
|
45
|
-
|
|
46
|
-
Keeping bundle sizes down in large projects over time can be challenging. A full stack framework should render as much
|
|
47
|
-
as possible on the server by default for better performance. Sending JavaScript code to the client and increasing bundle
|
|
48
|
-
sizes for all your users should be explicit and opt-in.
|
|
49
|
-
|
|
50
|
-
Modern frameworks make it too easy to _accidentally_ ship code to the client that you don't want. They often mix client
|
|
51
|
-
and server concerns in a way that is hard to understand and keep separate. They return cryptic and confusing errors when
|
|
52
|
-
you make mistakes around client and server boundaries.
|
|
53
|
-
|
|
54
|
-
We've lost the forrest from the trees. The complixity has become too much, and users are paying the price.
|
|
55
|
-
|
|
56
|
-
This is the way back for a full-stack framework:
|
|
57
|
-
|
|
58
|
-
- Server fetching and rendering by default.
|
|
59
|
-
- Minimal client-side JavaScript by default. Code that ships to the client should be explicit and opt-in.
|
|
60
|
-
- Most of the work and state should be maintained on the server. The framework should help make this easy.
|
|
61
|
-
|
|
62
|
-
### P2: Performance Oriented.
|
|
63
|
-
|
|
64
|
-
Server-side code is fast because it sends HTML strings to the client and lets the browser do its job - the job that is
|
|
65
|
-
was built specifically to do. There is no sense in typing up the JavaScript thread for rendering markup that can just be
|
|
66
|
-
sent directly.
|
|
67
|
-
|
|
68
|
-
You don't need a Virtual DOM or expensive reconciliation diff calculations to make interactive web sites or
|
|
69
|
-
applications. You just need a more intelligent template that knows the pieces that update.
|
|
70
|
-
|
|
71
|
-
HTML strings are the fastest and easiest way to update the parts of the DOM that change. We have known this since the
|
|
72
|
-
Web 1.0 days of AJAX and template partials. Modern JavaScript built-ins like Tagged Template Literals are an obvious
|
|
73
|
-
choice to do do the same thing today, and are built into the language.
|
|
74
|
-
|
|
75
|
-
### P3: Works the Same In Every Context.
|
|
76
|
-
|
|
77
|
-
All templates and components should be rendered asynchronously to allow you to do any other kind of asynchronous work in
|
|
78
|
-
them that you need to, in any context that you do it in. It's just JavaScript. Not special framework-flavored
|
|
79
|
-
JavaScript.
|
|
80
|
-
|
|
81
|
-
There should be no difference in how a template or component is rendered in one context vs. another. It should not
|
|
82
|
-
matter if a component runs on the client or the server, or fetches data or streams in. The core conceptual semantics
|
|
83
|
-
should be the same everywhere.
|
|
5
|
+
Full Hyperspan framework docs coming soon...
|
package/build.ts
CHANGED
|
@@ -1,29 +1,27 @@
|
|
|
1
|
+
import {build} from 'bun';
|
|
1
2
|
import dts from 'bun-plugin-dts';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
entrypoints: ['./src/index.ts'],
|
|
7
|
-
outdir: './dist',
|
|
8
|
-
target: 'browser',
|
|
9
|
-
}),
|
|
10
|
-
Bun.build({
|
|
11
|
-
entrypoints: ['./src/server.ts'],
|
|
12
|
-
outdir: './dist',
|
|
13
|
-
target: 'node',
|
|
14
|
-
}),
|
|
4
|
+
const filesToBuild = ['./src/index.ts', './src/server.ts', './src/assets.ts'];
|
|
5
|
+
const outdir = './dist';
|
|
6
|
+
const target = 'node';
|
|
15
7
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
8
|
+
await Promise.all(
|
|
9
|
+
filesToBuild.map((file) =>
|
|
10
|
+
Promise.all([
|
|
11
|
+
// Build JS
|
|
12
|
+
build({
|
|
13
|
+
entrypoints: [file],
|
|
14
|
+
outdir,
|
|
15
|
+
target,
|
|
16
|
+
}),
|
|
17
|
+
|
|
18
|
+
// Build type files for TypeScript
|
|
19
|
+
build({
|
|
20
|
+
entrypoints: [file],
|
|
21
|
+
outdir,
|
|
22
|
+
target,
|
|
23
|
+
plugins: [dts()],
|
|
24
|
+
}),
|
|
25
|
+
])
|
|
26
|
+
)
|
|
27
|
+
);
|
package/dist/assets.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Generated by dts-bundle-generator v9.5.1
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build client JS for end users (minimal JS for Hyperspan to work)
|
|
5
|
+
*/
|
|
6
|
+
export declare const clientJSFiles: Map<string, {
|
|
7
|
+
src: string;
|
|
8
|
+
type?: string;
|
|
9
|
+
}>;
|
|
10
|
+
export declare function buildClientJS(): Promise<string>;
|
|
11
|
+
/**
|
|
12
|
+
* Find client CSS file built for end users
|
|
13
|
+
* @TODO: Build this in code here vs. relying on tailwindcss CLI tool from package scripts
|
|
14
|
+
*/
|
|
15
|
+
export declare const clientCSSFiles: Map<string, string>;
|
|
16
|
+
export declare function buildClientCSS(): Promise<string | undefined>;
|
|
17
|
+
/**
|
|
18
|
+
* Output HTML style tag for Hyperspan app
|
|
19
|
+
*/
|
|
20
|
+
export declare function hyperspanStyleTags(): import("@hyperspan/html").TmplHtml;
|
|
21
|
+
/**
|
|
22
|
+
* Output HTML script tag for Hyperspan app
|
|
23
|
+
* Required for functioning streaming so content can pop into place properly once ready
|
|
24
|
+
*/
|
|
25
|
+
export declare function hyperspanScriptTags(): import("@hyperspan/html").TmplHtml;
|
|
26
|
+
/**
|
|
27
|
+
* Return a Preact component, mounted as an island in a <script> tag so it can be embedded into the page response.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createPreactIsland(file: string): Promise<(props: any) => {
|
|
30
|
+
_kind: string;
|
|
31
|
+
content: string;
|
|
32
|
+
}>;
|
|
33
|
+
|
|
34
|
+
export {};
|
package/dist/assets.js
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
// node_modules/@hyperspan/html/dist/html.js
|
|
2
|
+
/*!
|
|
3
|
+
* escape-html
|
|
4
|
+
* Copyright(c) 2012-2013 TJ Holowaychuk
|
|
5
|
+
* Copyright(c) 2015 Andreas Lubbe
|
|
6
|
+
* Copyright(c) 2015 Tiancheng "Timothy" Gu
|
|
7
|
+
* MIT Licensed
|
|
8
|
+
*/
|
|
9
|
+
var matchHtmlRegExp = /["'&<>]/;
|
|
10
|
+
function escapeHtml(string) {
|
|
11
|
+
const str = "" + string;
|
|
12
|
+
const match = matchHtmlRegExp.exec(str);
|
|
13
|
+
if (!match) {
|
|
14
|
+
return str;
|
|
15
|
+
}
|
|
16
|
+
let escape;
|
|
17
|
+
let html = "";
|
|
18
|
+
let index = 0;
|
|
19
|
+
let lastIndex = 0;
|
|
20
|
+
for (index = match.index;index < str.length; index++) {
|
|
21
|
+
switch (str.charCodeAt(index)) {
|
|
22
|
+
case 34:
|
|
23
|
+
escape = """;
|
|
24
|
+
break;
|
|
25
|
+
case 38:
|
|
26
|
+
escape = "&";
|
|
27
|
+
break;
|
|
28
|
+
case 39:
|
|
29
|
+
escape = "'";
|
|
30
|
+
break;
|
|
31
|
+
case 60:
|
|
32
|
+
escape = "<";
|
|
33
|
+
break;
|
|
34
|
+
case 62:
|
|
35
|
+
escape = ">";
|
|
36
|
+
break;
|
|
37
|
+
default:
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (lastIndex !== index) {
|
|
41
|
+
html += str.substring(lastIndex, index);
|
|
42
|
+
}
|
|
43
|
+
lastIndex = index + 1;
|
|
44
|
+
html += escape;
|
|
45
|
+
}
|
|
46
|
+
return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class TmplHtml {
|
|
50
|
+
content = "";
|
|
51
|
+
asyncContent;
|
|
52
|
+
constructor(props) {
|
|
53
|
+
this.content = props.content;
|
|
54
|
+
this.asyncContent = props.asyncContent;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
var htmlId = 0;
|
|
58
|
+
function html(strings, ...values) {
|
|
59
|
+
const asyncContent = [];
|
|
60
|
+
let content = "";
|
|
61
|
+
for (let i = 0;i < strings.length; i++) {
|
|
62
|
+
let value = values[i];
|
|
63
|
+
let renderValue;
|
|
64
|
+
if (value !== null && value !== undefined) {
|
|
65
|
+
let id = `async_loading_${htmlId++}`;
|
|
66
|
+
let kind = _typeOf(value);
|
|
67
|
+
if (!renderValue) {
|
|
68
|
+
renderValue = _renderValue(value, { id, kind, asyncContent }) || "";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
content += strings[i] + (renderValue ? renderValue : "");
|
|
72
|
+
}
|
|
73
|
+
return new TmplHtml({ content, asyncContent });
|
|
74
|
+
}
|
|
75
|
+
html.raw = (content) => ({ _kind: "html_safe", content });
|
|
76
|
+
function _renderValue(value, opts = {
|
|
77
|
+
kind: undefined,
|
|
78
|
+
id: undefined,
|
|
79
|
+
asyncContent: []
|
|
80
|
+
}) {
|
|
81
|
+
if (value === null || value === undefined || Number.isNaN(value)) {
|
|
82
|
+
return "";
|
|
83
|
+
}
|
|
84
|
+
const kind = opts.kind || _typeOf(value);
|
|
85
|
+
const id = opts.id || "";
|
|
86
|
+
switch (kind) {
|
|
87
|
+
case "array":
|
|
88
|
+
return value.map((v) => _renderValue(v, { id, asyncContent: opts.asyncContent })).join("");
|
|
89
|
+
case "object":
|
|
90
|
+
if (value instanceof TmplHtml) {
|
|
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
|
+
opts.asyncContent.push({
|
|
113
|
+
id,
|
|
114
|
+
promise: value.then((result) => ({
|
|
115
|
+
id,
|
|
116
|
+
value: result,
|
|
117
|
+
asyncContent: opts.asyncContent
|
|
118
|
+
}))
|
|
119
|
+
});
|
|
120
|
+
return render(_htmlPlaceholder(id));
|
|
121
|
+
case "generator":
|
|
122
|
+
throw new Error("Generators are not supported as a template value at this time. Sorry :(");
|
|
123
|
+
}
|
|
124
|
+
return escapeHtml(String(value));
|
|
125
|
+
}
|
|
126
|
+
function _htmlPlaceholder(id, content = "Loading...") {
|
|
127
|
+
return html`<!--hs:loading:${id}--><slot id="${id}">${content}</slot><!--/hs:loading:${id}-->`;
|
|
128
|
+
}
|
|
129
|
+
function render(tmpl) {
|
|
130
|
+
return tmpl.content;
|
|
131
|
+
}
|
|
132
|
+
function _typeOf(obj) {
|
|
133
|
+
if (obj instanceof Promise)
|
|
134
|
+
return "promise";
|
|
135
|
+
if (obj instanceof Date)
|
|
136
|
+
return "date";
|
|
137
|
+
if (obj instanceof String)
|
|
138
|
+
return "string";
|
|
139
|
+
if (obj instanceof Number)
|
|
140
|
+
return "number";
|
|
141
|
+
if (obj instanceof Boolean)
|
|
142
|
+
return "boolean";
|
|
143
|
+
if (obj instanceof Function)
|
|
144
|
+
return "function";
|
|
145
|
+
if (Array.isArray(obj))
|
|
146
|
+
return "array";
|
|
147
|
+
if (Number.isNaN(obj))
|
|
148
|
+
return "NaN";
|
|
149
|
+
if (obj === undefined)
|
|
150
|
+
return "undefined";
|
|
151
|
+
if (obj === null)
|
|
152
|
+
return "null";
|
|
153
|
+
if (isGenerator(obj))
|
|
154
|
+
return "generator";
|
|
155
|
+
return typeof obj;
|
|
156
|
+
}
|
|
157
|
+
function isGenerator(obj) {
|
|
158
|
+
return obj && typeof obj.next == "function" && typeof obj.throw == "function";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/clientjs/md5.js
|
|
162
|
+
function md5cycle(x, k) {
|
|
163
|
+
var a = x[0], b = x[1], c = x[2], d = x[3];
|
|
164
|
+
a = ff(a, b, c, d, k[0], 7, -680876936);
|
|
165
|
+
d = ff(d, a, b, c, k[1], 12, -389564586);
|
|
166
|
+
c = ff(c, d, a, b, k[2], 17, 606105819);
|
|
167
|
+
b = ff(b, c, d, a, k[3], 22, -1044525330);
|
|
168
|
+
a = ff(a, b, c, d, k[4], 7, -176418897);
|
|
169
|
+
d = ff(d, a, b, c, k[5], 12, 1200080426);
|
|
170
|
+
c = ff(c, d, a, b, k[6], 17, -1473231341);
|
|
171
|
+
b = ff(b, c, d, a, k[7], 22, -45705983);
|
|
172
|
+
a = ff(a, b, c, d, k[8], 7, 1770035416);
|
|
173
|
+
d = ff(d, a, b, c, k[9], 12, -1958414417);
|
|
174
|
+
c = ff(c, d, a, b, k[10], 17, -42063);
|
|
175
|
+
b = ff(b, c, d, a, k[11], 22, -1990404162);
|
|
176
|
+
a = ff(a, b, c, d, k[12], 7, 1804603682);
|
|
177
|
+
d = ff(d, a, b, c, k[13], 12, -40341101);
|
|
178
|
+
c = ff(c, d, a, b, k[14], 17, -1502002290);
|
|
179
|
+
b = ff(b, c, d, a, k[15], 22, 1236535329);
|
|
180
|
+
a = gg(a, b, c, d, k[1], 5, -165796510);
|
|
181
|
+
d = gg(d, a, b, c, k[6], 9, -1069501632);
|
|
182
|
+
c = gg(c, d, a, b, k[11], 14, 643717713);
|
|
183
|
+
b = gg(b, c, d, a, k[0], 20, -373897302);
|
|
184
|
+
a = gg(a, b, c, d, k[5], 5, -701558691);
|
|
185
|
+
d = gg(d, a, b, c, k[10], 9, 38016083);
|
|
186
|
+
c = gg(c, d, a, b, k[15], 14, -660478335);
|
|
187
|
+
b = gg(b, c, d, a, k[4], 20, -405537848);
|
|
188
|
+
a = gg(a, b, c, d, k[9], 5, 568446438);
|
|
189
|
+
d = gg(d, a, b, c, k[14], 9, -1019803690);
|
|
190
|
+
c = gg(c, d, a, b, k[3], 14, -187363961);
|
|
191
|
+
b = gg(b, c, d, a, k[8], 20, 1163531501);
|
|
192
|
+
a = gg(a, b, c, d, k[13], 5, -1444681467);
|
|
193
|
+
d = gg(d, a, b, c, k[2], 9, -51403784);
|
|
194
|
+
c = gg(c, d, a, b, k[7], 14, 1735328473);
|
|
195
|
+
b = gg(b, c, d, a, k[12], 20, -1926607734);
|
|
196
|
+
a = hh(a, b, c, d, k[5], 4, -378558);
|
|
197
|
+
d = hh(d, a, b, c, k[8], 11, -2022574463);
|
|
198
|
+
c = hh(c, d, a, b, k[11], 16, 1839030562);
|
|
199
|
+
b = hh(b, c, d, a, k[14], 23, -35309556);
|
|
200
|
+
a = hh(a, b, c, d, k[1], 4, -1530992060);
|
|
201
|
+
d = hh(d, a, b, c, k[4], 11, 1272893353);
|
|
202
|
+
c = hh(c, d, a, b, k[7], 16, -155497632);
|
|
203
|
+
b = hh(b, c, d, a, k[10], 23, -1094730640);
|
|
204
|
+
a = hh(a, b, c, d, k[13], 4, 681279174);
|
|
205
|
+
d = hh(d, a, b, c, k[0], 11, -358537222);
|
|
206
|
+
c = hh(c, d, a, b, k[3], 16, -722521979);
|
|
207
|
+
b = hh(b, c, d, a, k[6], 23, 76029189);
|
|
208
|
+
a = hh(a, b, c, d, k[9], 4, -640364487);
|
|
209
|
+
d = hh(d, a, b, c, k[12], 11, -421815835);
|
|
210
|
+
c = hh(c, d, a, b, k[15], 16, 530742520);
|
|
211
|
+
b = hh(b, c, d, a, k[2], 23, -995338651);
|
|
212
|
+
a = ii(a, b, c, d, k[0], 6, -198630844);
|
|
213
|
+
d = ii(d, a, b, c, k[7], 10, 1126891415);
|
|
214
|
+
c = ii(c, d, a, b, k[14], 15, -1416354905);
|
|
215
|
+
b = ii(b, c, d, a, k[5], 21, -57434055);
|
|
216
|
+
a = ii(a, b, c, d, k[12], 6, 1700485571);
|
|
217
|
+
d = ii(d, a, b, c, k[3], 10, -1894986606);
|
|
218
|
+
c = ii(c, d, a, b, k[10], 15, -1051523);
|
|
219
|
+
b = ii(b, c, d, a, k[1], 21, -2054922799);
|
|
220
|
+
a = ii(a, b, c, d, k[8], 6, 1873313359);
|
|
221
|
+
d = ii(d, a, b, c, k[15], 10, -30611744);
|
|
222
|
+
c = ii(c, d, a, b, k[6], 15, -1560198380);
|
|
223
|
+
b = ii(b, c, d, a, k[13], 21, 1309151649);
|
|
224
|
+
a = ii(a, b, c, d, k[4], 6, -145523070);
|
|
225
|
+
d = ii(d, a, b, c, k[11], 10, -1120210379);
|
|
226
|
+
c = ii(c, d, a, b, k[2], 15, 718787259);
|
|
227
|
+
b = ii(b, c, d, a, k[9], 21, -343485551);
|
|
228
|
+
x[0] = add32(a, x[0]);
|
|
229
|
+
x[1] = add32(b, x[1]);
|
|
230
|
+
x[2] = add32(c, x[2]);
|
|
231
|
+
x[3] = add32(d, x[3]);
|
|
232
|
+
}
|
|
233
|
+
function cmn(q, a, b, x, s, t) {
|
|
234
|
+
a = add32(add32(a, q), add32(x, t));
|
|
235
|
+
return add32(a << s | a >>> 32 - s, b);
|
|
236
|
+
}
|
|
237
|
+
function ff(a, b, c, d, x, s, t) {
|
|
238
|
+
return cmn(b & c | ~b & d, a, b, x, s, t);
|
|
239
|
+
}
|
|
240
|
+
function gg(a, b, c, d, x, s, t) {
|
|
241
|
+
return cmn(b & d | c & ~d, a, b, x, s, t);
|
|
242
|
+
}
|
|
243
|
+
function hh(a, b, c, d, x, s, t) {
|
|
244
|
+
return cmn(b ^ c ^ d, a, b, x, s, t);
|
|
245
|
+
}
|
|
246
|
+
function ii(a, b, c, d, x, s, t) {
|
|
247
|
+
return cmn(c ^ (b | ~d), a, b, x, s, t);
|
|
248
|
+
}
|
|
249
|
+
function md51(s) {
|
|
250
|
+
var txt = "";
|
|
251
|
+
var n = s.length, state = [1732584193, -271733879, -1732584194, 271733878], i;
|
|
252
|
+
for (i = 64;i <= s.length; i += 64) {
|
|
253
|
+
md5cycle(state, md5blk(s.substring(i - 64, i)));
|
|
254
|
+
}
|
|
255
|
+
s = s.substring(i - 64);
|
|
256
|
+
var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
257
|
+
for (i = 0;i < s.length; i++)
|
|
258
|
+
tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3);
|
|
259
|
+
tail[i >> 2] |= 128 << (i % 4 << 3);
|
|
260
|
+
if (i > 55) {
|
|
261
|
+
md5cycle(state, tail);
|
|
262
|
+
for (i = 0;i < 16; i++)
|
|
263
|
+
tail[i] = 0;
|
|
264
|
+
}
|
|
265
|
+
tail[14] = n * 8;
|
|
266
|
+
md5cycle(state, tail);
|
|
267
|
+
return state;
|
|
268
|
+
}
|
|
269
|
+
function md5blk(s) {
|
|
270
|
+
var md5blks = [], i;
|
|
271
|
+
for (i = 0;i < 64; i += 4) {
|
|
272
|
+
md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
|
|
273
|
+
}
|
|
274
|
+
return md5blks;
|
|
275
|
+
}
|
|
276
|
+
var hex_chr = "0123456789abcdef".split("");
|
|
277
|
+
function rhex(n) {
|
|
278
|
+
var s = "", j = 0;
|
|
279
|
+
for (;j < 4; j++)
|
|
280
|
+
s += hex_chr[n >> j * 8 + 4 & 15] + hex_chr[n >> j * 8 & 15];
|
|
281
|
+
return s;
|
|
282
|
+
}
|
|
283
|
+
function hex(x) {
|
|
284
|
+
for (var i = 0;i < x.length; i++)
|
|
285
|
+
x[i] = rhex(x[i]);
|
|
286
|
+
return x.join("");
|
|
287
|
+
}
|
|
288
|
+
function add32(a, b) {
|
|
289
|
+
return a + b & 4294967295;
|
|
290
|
+
}
|
|
291
|
+
function md5(s) {
|
|
292
|
+
return hex(md51(s));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// src/assets.ts
|
|
296
|
+
import { readdir } from "node:fs/promises";
|
|
297
|
+
import { resolve } from "node:path";
|
|
298
|
+
var IS_PROD = false;
|
|
299
|
+
var PWD = import.meta.dir;
|
|
300
|
+
var clientJSFiles = new Map;
|
|
301
|
+
async function buildClientJS() {
|
|
302
|
+
const sourceFile = resolve(PWD, "../", "./hyperspan/clientjs/hyperspan-client.ts");
|
|
303
|
+
const output = await Bun.build({
|
|
304
|
+
entrypoints: [sourceFile],
|
|
305
|
+
outdir: `./public/_hs/js`,
|
|
306
|
+
naming: IS_PROD ? "[dir]/[name]-[hash].[ext]" : undefined,
|
|
307
|
+
minify: IS_PROD
|
|
308
|
+
});
|
|
309
|
+
const jsFile = output.outputs[0].path.split("/").reverse()[0];
|
|
310
|
+
clientJSFiles.set("_hs", { src: "/_hs/js/" + jsFile });
|
|
311
|
+
return jsFile;
|
|
312
|
+
}
|
|
313
|
+
var clientCSSFiles = new Map;
|
|
314
|
+
async function buildClientCSS() {
|
|
315
|
+
if (clientCSSFiles.has("_hs")) {
|
|
316
|
+
return clientCSSFiles.get("_hs");
|
|
317
|
+
}
|
|
318
|
+
const cssDir = "./public/_hs/css/";
|
|
319
|
+
const cssFiles = await readdir(cssDir);
|
|
320
|
+
let foundCSSFile = "";
|
|
321
|
+
for (const file of cssFiles) {
|
|
322
|
+
if (!file.endsWith(".css")) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
foundCSSFile = file.replace(cssDir, "");
|
|
326
|
+
clientCSSFiles.set("_hs", foundCSSFile);
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
if (!foundCSSFile) {
|
|
330
|
+
console.log(`Unable to build CSS files from ${cssDir}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function hyperspanStyleTags() {
|
|
334
|
+
const cssFiles = Array.from(clientCSSFiles.entries());
|
|
335
|
+
return html`${cssFiles.map(([key, file]) => html`<link rel="stylesheet" href="/_hs/css/${file}" />`)}`;
|
|
336
|
+
}
|
|
337
|
+
function hyperspanScriptTags() {
|
|
338
|
+
const jsFiles = Array.from(clientJSFiles.entries());
|
|
339
|
+
return html`
|
|
340
|
+
<script type="importmap">
|
|
341
|
+
{
|
|
342
|
+
"imports": {
|
|
343
|
+
"preact": "https://esm.sh/preact@10.26.4",
|
|
344
|
+
"preact/": "https://esm.sh/preact@10.26.4/",
|
|
345
|
+
"react": "https://esm.sh/preact@10.26.4/compat",
|
|
346
|
+
"react/": "https://esm.sh/preact@10.26.4/compat/",
|
|
347
|
+
"react-dom": "https://esm.sh/preact@10.26.4/compat"
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
</script>
|
|
351
|
+
${jsFiles.map(([key, file]) => html`<script
|
|
352
|
+
id="js-${key}"
|
|
353
|
+
type="${file.type || "text/javascript"}"
|
|
354
|
+
src="${file.src}"
|
|
355
|
+
></script>`)}
|
|
356
|
+
`;
|
|
357
|
+
}
|
|
358
|
+
async function createPreactIsland(file) {
|
|
359
|
+
let filePath = file.replace("file://", "");
|
|
360
|
+
let resultStr = 'import{h,render}from"preact";';
|
|
361
|
+
const build = await Bun.build({
|
|
362
|
+
entrypoints: [filePath],
|
|
363
|
+
minify: true,
|
|
364
|
+
external: ["react", "preact"],
|
|
365
|
+
env: "APP_PUBLIC_*"
|
|
366
|
+
});
|
|
367
|
+
for (const output of build.outputs) {
|
|
368
|
+
resultStr += await output.text();
|
|
369
|
+
}
|
|
370
|
+
const r = /export\{([a-zA-Z]+) as default\}/g;
|
|
371
|
+
const matchExport = r.exec(resultStr);
|
|
372
|
+
const jsId = md5(resultStr);
|
|
373
|
+
if (!matchExport) {
|
|
374
|
+
throw new Error("File does not have a default export! Ensure a function has export default to use this.");
|
|
375
|
+
}
|
|
376
|
+
const fn = matchExport[1];
|
|
377
|
+
let _mounted = false;
|
|
378
|
+
return (props) => {
|
|
379
|
+
if (!_mounted) {
|
|
380
|
+
_mounted = true;
|
|
381
|
+
resultStr += `render(h(${fn}, ${JSON.stringify(props)}), document.getElementById("${jsId}"));`;
|
|
382
|
+
}
|
|
383
|
+
return html.raw(`<div id="${jsId}"></div><script type="module" data-source-id="${jsId}">${resultStr}</script>`);
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
export {
|
|
387
|
+
hyperspanStyleTags,
|
|
388
|
+
hyperspanScriptTags,
|
|
389
|
+
createPreactIsland,
|
|
390
|
+
clientJSFiles,
|
|
391
|
+
clientCSSFiles,
|
|
392
|
+
buildClientJS,
|
|
393
|
+
buildClientCSS
|
|
394
|
+
};
|