@hyperspan/framework 1.0.29 → 2.0.0-alpha.2
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/package.json +22 -7
- package/src/actions.test.ts +1 -1
- package/src/actions.ts +3 -6
- package/src/client/_hs/hyperspan-actions.client.ts +37 -45
- package/src/client/js.test.ts +78 -4
- package/src/client/js.ts +172 -64
- package/src/client/manifest.ts +87 -0
- package/src/fetch-handler.test.ts +90 -0
- package/src/fetch-handler.ts +167 -0
- package/src/index.ts +14 -0
- package/src/island.test.ts +30 -0
- package/src/island.ts +37 -0
- package/src/layout.ts +34 -9
- package/src/server.test.ts +12 -1
- package/src/server.ts +6 -2
- package/src/ssr/mock-dom.test.ts +1 -1
- package/src/types.ts +12 -0
- package/src/utils.test.ts +72 -2
- package/src/utils.ts +66 -10
- package/tsconfig.json +4 -8
package/src/utils.ts
CHANGED
|
@@ -1,20 +1,36 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Hyperspan as HS } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FNV-1a 64-bit hash — portable sync hash for asset IDs (works on Node, Bun, Workers).
|
|
5
|
+
*/
|
|
6
|
+
function fnv1a64(input: string): string {
|
|
7
|
+
let hash = BigInt('0xcbf29ce484222325');
|
|
8
|
+
const prime = BigInt('0x100000001b3');
|
|
9
|
+
const mask = BigInt('0xffffffffffffffff');
|
|
10
|
+
for (let i = 0; i < input.length; i++) {
|
|
11
|
+
hash ^= BigInt(input.charCodeAt(i));
|
|
12
|
+
hash = (hash * prime) & mask;
|
|
13
|
+
}
|
|
14
|
+
return hash.toString(16).padStart(16, '0');
|
|
15
|
+
}
|
|
2
16
|
|
|
3
17
|
export function assetHash(content: string): string {
|
|
4
|
-
return
|
|
18
|
+
return fnv1a64(content);
|
|
5
19
|
}
|
|
6
20
|
|
|
7
21
|
export function randomHash(): string {
|
|
8
|
-
|
|
22
|
+
const bytes = new Uint8Array(16);
|
|
23
|
+
crypto.getRandomValues(bytes);
|
|
24
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
25
|
+
return fnv1a64(hex);
|
|
9
26
|
}
|
|
10
27
|
|
|
11
|
-
|
|
12
28
|
/**
|
|
13
29
|
* Normalize URL path
|
|
14
30
|
* Removes trailing slash and lowercases path
|
|
15
31
|
*/
|
|
16
32
|
const ROUTE_SEGMENT_REGEX = /(\[[a-zA-Z_\.]+\])/g;
|
|
17
|
-
export function parsePath(urlPath: string): { path: string
|
|
33
|
+
export function parsePath(urlPath: string): { path: string; params: string[] } {
|
|
18
34
|
const params: string[] = [];
|
|
19
35
|
urlPath = urlPath.replace('index', '').replace('.ts', '').replace('.js', '');
|
|
20
36
|
|
|
@@ -46,15 +62,14 @@ export function parsePath(urlPath: string): { path: string, params: string[] } {
|
|
|
46
62
|
|
|
47
63
|
// Only lowercase non-param segments (do not lowercase after ':')
|
|
48
64
|
return {
|
|
49
|
-
path:
|
|
65
|
+
path:
|
|
50
66
|
'/' +
|
|
51
67
|
urlPath
|
|
52
68
|
.split('/')
|
|
53
69
|
.map((segment) =>
|
|
54
70
|
segment.startsWith(':') || segment === '*' ? segment : segment.toLowerCase()
|
|
55
71
|
)
|
|
56
|
-
.join('/')
|
|
57
|
-
),
|
|
72
|
+
.join('/'),
|
|
58
73
|
params,
|
|
59
74
|
};
|
|
60
75
|
}
|
|
@@ -75,7 +90,9 @@ export function isValidRoutePath(path: string): boolean {
|
|
|
75
90
|
*
|
|
76
91
|
* @link https://stackoverflow.com/a/75406413
|
|
77
92
|
*/
|
|
78
|
-
export function formDataToJSON(
|
|
93
|
+
export function formDataToJSON(
|
|
94
|
+
formData: FormData | URLSearchParams
|
|
95
|
+
): Record<string, string | string[]> {
|
|
79
96
|
let object = {};
|
|
80
97
|
|
|
81
98
|
/**
|
|
@@ -170,4 +187,43 @@ export function formDataToJSON(formData: FormData | URLSearchParams): Record<str
|
|
|
170
187
|
*/
|
|
171
188
|
export function removeUndefined(obj: Record<string, any>): Record<string, any> {
|
|
172
189
|
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));
|
|
173
|
-
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Build a new URL from a base URL and an optional diff.
|
|
194
|
+
* Does not mutate the base URL.
|
|
195
|
+
*/
|
|
196
|
+
export function buildUrl(base: URL, diff?: HS.UrlDiff, options?: HS.UrlOptions): URL {
|
|
197
|
+
const next = new URL(base);
|
|
198
|
+
|
|
199
|
+
if (options?.clean) {
|
|
200
|
+
next.search = '';
|
|
201
|
+
next.hash = '';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (diff?.pathname === null) {
|
|
205
|
+
next.pathname = '/';
|
|
206
|
+
} else if (diff?.pathname !== undefined) {
|
|
207
|
+
next.pathname = diff.pathname;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (diff?.searchParams === null) {
|
|
211
|
+
next.search = '';
|
|
212
|
+
} else if (diff?.searchParams !== undefined) {
|
|
213
|
+
for (const [key, value] of Object.entries(diff.searchParams)) {
|
|
214
|
+
if (value === null || value === undefined) {
|
|
215
|
+
next.searchParams.delete(key);
|
|
216
|
+
} else {
|
|
217
|
+
next.searchParams.set(key, String(value));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (diff?.hash === null) {
|
|
223
|
+
next.hash = '';
|
|
224
|
+
} else if (diff?.hash !== undefined) {
|
|
225
|
+
next.hash = diff.hash;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return next;
|
|
229
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compileOnSave": true,
|
|
3
|
+
"include": ["src/**/*.ts"],
|
|
3
4
|
"compilerOptions": {
|
|
4
5
|
"outDir": "dist",
|
|
5
|
-
"target": "
|
|
6
|
+
"target": "ES2020",
|
|
6
7
|
"lib": ["ESNext", "dom", "dom.iterable"],
|
|
7
8
|
"module": "esnext",
|
|
8
9
|
"moduleResolution": "bundler",
|
|
@@ -10,7 +11,6 @@
|
|
|
10
11
|
"esModuleInterop": true,
|
|
11
12
|
"allowImportingTsExtensions": true,
|
|
12
13
|
"noEmit": true,
|
|
13
|
-
"composite": true,
|
|
14
14
|
"strict": true,
|
|
15
15
|
"declaration": true,
|
|
16
16
|
"sourceMap": true,
|
|
@@ -19,11 +19,7 @@
|
|
|
19
19
|
"allowSyntheticDefaultImports": true,
|
|
20
20
|
"forceConsistentCasingInFileNames": true,
|
|
21
21
|
"allowJs": true,
|
|
22
|
-
"baseUrl": "."
|
|
23
|
-
"paths": {
|
|
24
|
-
"@hyperspan/html": ["../html/src/html.ts"]
|
|
25
|
-
}
|
|
22
|
+
"baseUrl": "."
|
|
26
23
|
},
|
|
27
|
-
"
|
|
28
|
-
"exclude": ["node_modules", "__tests__", "*.test.ts"]
|
|
24
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
29
25
|
}
|