@async/framework 0.10.1 → 0.11.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/CHANGELOG.md +36 -0
- package/README.md +23 -7
- package/browser.d.ts +4 -7
- package/browser.js +143 -116
- package/browser.min.js +1 -1
- package/browser.ts +143 -116
- package/browser.umd.js +143 -116
- package/browser.umd.min.js +1 -1
- package/{server.d.ts → framework.d.ts} +4 -7
- package/framework.ts +5946 -0
- package/package.json +25 -17
- package/server.js +5945 -0
- package/examples/cache/index.html +0 -16
- package/examples/cache/main.js +0 -47
- package/examples/components/index.html +0 -11
- package/examples/components/main.js +0 -26
- package/examples/counter/index.html +0 -15
- package/examples/counter/main.js +0 -17
- package/examples/partials/index.html +0 -15
- package/examples/partials/main.js +0 -43
- package/examples/product/index.html +0 -32
- package/examples/product/main.js +0 -24
- package/examples/router/index.html +0 -18
- package/examples/router/main.js +0 -52
- package/examples/server-call/index.html +0 -21
- package/examples/server-call/main.js +0 -22
- package/examples/ssr/index.html +0 -12
- package/examples/ssr/main.js +0 -89
- package/examples/streaming/index.html +0 -16
- package/examples/streaming/main.js +0 -30
- package/src/app.js +0 -802
- package/src/async-signal.js +0 -277
- package/src/attributes.js +0 -52
- package/src/boundary-receiver.js +0 -302
- package/src/browser.js +0 -18
- package/src/cache.js +0 -189
- package/src/component.js +0 -373
- package/src/delay.js +0 -30
- package/src/elements.js +0 -63
- package/src/handlers.js +0 -219
- package/src/html.js +0 -158
- package/src/index.js +0 -20
- package/src/lazy-registry.js +0 -204
- package/src/loader.js +0 -765
- package/src/partials.js +0 -133
- package/src/registry-store.js +0 -267
- package/src/request-context.js +0 -40
- package/src/router.js +0 -571
- package/src/scheduler.js +0 -300
- package/src/server-entry.js +0 -20
- package/src/server-registry.js +0 -97
- package/src/server.js +0 -357
- package/src/signals.js +0 -592
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.0 - 2026-06-17
|
|
4
|
+
|
|
5
|
+
- Removed the networked `ssr-spa` router mode and route-fragment fetching so
|
|
6
|
+
`ssr` activates server-rendered HTML and snapshots without client route
|
|
7
|
+
fetches.
|
|
8
|
+
- Changed browser navigation to render registered SPA partials locally in
|
|
9
|
+
`spa` and `csr` modes while leaving same-origin document navigation alone in
|
|
10
|
+
`ssr` and `mpa` modes.
|
|
11
|
+
- Replaced the server proxy's implicit `globalThis.fetch` default with an
|
|
12
|
+
explicit `transport` callback supplied by application code.
|
|
13
|
+
- Published only generated runtime artifacts and declarations:
|
|
14
|
+
`browser.*`, `server.js`, `framework.ts`, and `framework.d.ts`; source,
|
|
15
|
+
tests, and examples are no longer included in the package tarball.
|
|
16
|
+
- Added regression coverage for SSR snapshot activation without fetch, SPA
|
|
17
|
+
stale/error navigation behavior, explicit server proxy transports, and static
|
|
18
|
+
generated-bundle scans for implicit global fetch access.
|
|
19
|
+
- Bundle size from bundled TypeScript source: `browser.ts` 171,908 B raw /
|
|
20
|
+
32,421 B gzip -> `browser.min.js` 72,827 B raw / 21,845 B gzip
|
|
21
|
+
(-99,081 B raw, -10,576 B gzip).
|
|
22
|
+
|
|
23
|
+
## 0.10.2 - 2026-06-17
|
|
24
|
+
|
|
25
|
+
- Fixed intercepted router link, form, and popstate navigation failures so they
|
|
26
|
+
update router error state and do not create unhandled promise rejections.
|
|
27
|
+
- Preserved native same-document hash link behavior and made malformed encoded
|
|
28
|
+
dynamic route params fall back to the raw segment instead of throwing.
|
|
29
|
+
- Hardened rootless detach cleanup, lazy descriptor import retry, server JSON
|
|
30
|
+
transport validation, and `cache.getOrSet(...)` handling for cached
|
|
31
|
+
`undefined` values.
|
|
32
|
+
- Expanded regression coverage for scheduler reentrancy, boundary receiver
|
|
33
|
+
patch shapes, root lifecycle, lazy descriptors, server transport edge cases,
|
|
34
|
+
component lifecycle ordering, and installed package export-map shape.
|
|
35
|
+
- Bundle size from bundled TypeScript source: `browser.ts` 173,774 B raw /
|
|
36
|
+
32,727 B gzip -> `browser.min.js` 73,680 B raw / 22,047 B gzip
|
|
37
|
+
(-100,094 B raw, -10,680 B gzip).
|
|
38
|
+
|
|
3
39
|
## 0.10.1 - 2026-06-17
|
|
4
40
|
|
|
5
41
|
- Added Terser-powered browser bundle minification and pointed legacy
|
package/README.md
CHANGED
|
@@ -102,8 +102,11 @@ production:
|
|
|
102
102
|
| `browser.min.js` | ESM | Compact browser module bundle |
|
|
103
103
|
| `browser.umd.js` | UMD | Readable script-tag/CommonJS-style bundle |
|
|
104
104
|
| `browser.umd.min.js` | UMD | Compact script-tag/CommonJS-style bundle and default CDN file |
|
|
105
|
-
| `browser.ts` | Bundled TypeScript source | TS-aware runtimes and higher-layer tooling |
|
|
105
|
+
| `browser.ts` | Bundled browser TypeScript source | TS-aware runtimes and higher-layer tooling |
|
|
106
106
|
| `browser.d.ts` | Type declarations | TypeScript declarations for the browser API |
|
|
107
|
+
| `server.js` | ESM | Server-capable Node.js bundle |
|
|
108
|
+
| `framework.ts` | Bundled server-capable TypeScript source | TS-aware runtimes and higher-layer tooling |
|
|
109
|
+
| `framework.d.ts` | Type declarations | TypeScript declarations for the server-capable API |
|
|
107
110
|
|
|
108
111
|
```html
|
|
109
112
|
<main async:container>
|
|
@@ -789,8 +792,9 @@ handlers.register("addToCart", async function () {
|
|
|
789
792
|
|
|
790
793
|
### Server Calls
|
|
791
794
|
|
|
792
|
-
Server registries run locally on the server
|
|
793
|
-
|
|
795
|
+
Server registries run locally on the server. Browser proxies use an explicit
|
|
796
|
+
transport supplied by the app, so network access is opt-in. Both expose the same
|
|
797
|
+
dotted call shape.
|
|
794
798
|
|
|
795
799
|
```js
|
|
796
800
|
import {
|
|
@@ -818,6 +822,7 @@ import {
|
|
|
818
822
|
|
|
819
823
|
const server = createServerProxy({
|
|
820
824
|
endpoint: "/__async/server",
|
|
825
|
+
transport: httpTransport,
|
|
821
826
|
signals,
|
|
822
827
|
loader,
|
|
823
828
|
router
|
|
@@ -884,8 +889,7 @@ Router modes:
|
|
|
884
889
|
| --- | --- |
|
|
885
890
|
| `csr` | Client renders local partial into boundary | Client renders local partial and swaps |
|
|
886
891
|
| `spa` | Existing HTML may already contain route | Client renders local partial and swaps |
|
|
887
|
-
| `ssr` | Server
|
|
888
|
-
| `ssr-spa` | Server rendered document/route boundary | Fetch route partial, apply effects, swap |
|
|
892
|
+
| `ssr` | Server-rendered document plus snapshot activation | Browser navigates normally |
|
|
889
893
|
| `mpa` | Any document source | Browser navigates normally |
|
|
890
894
|
|
|
891
895
|
CSR startup can use an empty route boundary:
|
|
@@ -1001,12 +1005,24 @@ The returned HTML includes a route boundary plus a JSON snapshot:
|
|
|
1001
1005
|
```
|
|
1002
1006
|
|
|
1003
1007
|
Browser activation scans the existing HTML and attaches events. It does not
|
|
1004
|
-
hydrate, diff, patch, or
|
|
1008
|
+
hydrate, diff, patch, rerender, or fetch route fragments:
|
|
1009
|
+
|
|
1010
|
+
```js
|
|
1011
|
+
createApp(browserApp, {
|
|
1012
|
+
root: document
|
|
1013
|
+
}).start();
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
If browser handlers or async signals need server commands, pass a server proxy
|
|
1017
|
+
with an explicit transport:
|
|
1005
1018
|
|
|
1006
1019
|
```js
|
|
1007
1020
|
createApp(browserApp, {
|
|
1008
1021
|
root: document,
|
|
1009
|
-
server: createServerProxy({
|
|
1022
|
+
server: createServerProxy({
|
|
1023
|
+
endpoint: "/__async/server",
|
|
1024
|
+
transport: httpTransport
|
|
1025
|
+
})
|
|
1010
1026
|
}).start();
|
|
1011
1027
|
```
|
|
1012
1028
|
|
package/browser.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Browser type declarations for @async/framework/browser.
|
|
3
3
|
|
|
4
4
|
export type RuntimeTarget = "browser" | "server";
|
|
5
|
-
export type RouterMode = "csr" | "spa" | "ssr" | "
|
|
5
|
+
export type RouterMode = "csr" | "spa" | "ssr" | "mpa";
|
|
6
6
|
export type AsyncSignalStatus = "idle" | "loading" | "ready" | "error";
|
|
7
7
|
export type MaybePromise<T> = T | Promise<T>;
|
|
8
8
|
export type Cleanup = () => void;
|
|
@@ -261,6 +261,7 @@ export interface ServerContext {
|
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
export type ServerFunction<T = unknown> = (this: ServerContext, ...args: unknown[]) => MaybePromise<ServerResult<T>>;
|
|
264
|
+
export type ServerProxyTransport = (url: string, init: RequestInit) => MaybePromise<Response>;
|
|
264
265
|
|
|
265
266
|
export interface ServerNamespace {
|
|
266
267
|
run<T = unknown>(id: string, args?: unknown[], context?: Partial<ServerContext>): Promise<T>;
|
|
@@ -275,7 +276,7 @@ export interface ServerNamespace {
|
|
|
275
276
|
|
|
276
277
|
export interface ServerProxyOptions {
|
|
277
278
|
endpoint?: string;
|
|
278
|
-
|
|
279
|
+
transport: ServerProxyTransport;
|
|
279
280
|
signals?: SignalRegistry;
|
|
280
281
|
loader?: LoaderInstance;
|
|
281
282
|
router?: Router;
|
|
@@ -378,8 +379,6 @@ export interface RouterOptions {
|
|
|
378
379
|
server?: ServerNamespace;
|
|
379
380
|
cache?: CacheRegistry;
|
|
380
381
|
partials?: PartialRegistry;
|
|
381
|
-
fetch?: typeof fetch;
|
|
382
|
-
routeEndpoint?: string;
|
|
383
382
|
attributes?: AttributeConfig;
|
|
384
383
|
scheduler?: Scheduler;
|
|
385
384
|
}
|
|
@@ -607,8 +606,6 @@ export interface CreateAppOptions extends LoaderOptions {
|
|
|
607
606
|
routes?: RouteRegistry;
|
|
608
607
|
partials?: PartialRegistry;
|
|
609
608
|
components?: ComponentRegistry;
|
|
610
|
-
fetch?: typeof fetch;
|
|
611
|
-
routeEndpoint?: string;
|
|
612
609
|
request?: Request;
|
|
613
610
|
locals?: unknown;
|
|
614
611
|
requestContext?: RequestContextStore;
|
|
@@ -722,7 +719,7 @@ export declare function createScheduler(options?: SchedulerOptions): Scheduler;
|
|
|
722
719
|
export declare function defineRoute(partial: string, options?: Omit<RouteDefinition, "partial">): RouteDefinition;
|
|
723
720
|
export declare const route: typeof defineRoute;
|
|
724
721
|
export declare function applyServerResult(result: unknown, context?: Record<string, unknown>): Promise<unknown>;
|
|
725
|
-
export declare function createServerProxy(options
|
|
722
|
+
export declare function createServerProxy(options: ServerProxyOptions): ServerNamespace;
|
|
726
723
|
export declare function resolveServerCommandArguments(args: Array<{ type: "local"; name: string } | { type: "signal"; path: string }>, context?: Record<string, unknown>): { args: unknown[]; signalValues: Record<string, unknown>; signalPaths: string[] };
|
|
727
724
|
export declare function unwrapServerResult<T = unknown>(result: ServerResult<T>): T | ServerResult<T>;
|
|
728
725
|
export declare function computed<T = unknown>(fn: (this: { signals: SignalRegistry; id: string; server?: ServerNamespace; router?: Router; loader?: LoaderInstance; cache?: CacheRegistry; scheduler?: Scheduler }) => T): ComputedSignal<T>;
|
package/browser.js
CHANGED
|
@@ -318,10 +318,20 @@ const __lazyRegistryModule = (() => {
|
|
|
318
318
|
const resolved = resolveDescriptorUrl(type, id, descriptor, registryAssets);
|
|
319
319
|
let modulePromise = moduleCache.get(resolved.moduleUrl);
|
|
320
320
|
if (!modulePromise) {
|
|
321
|
-
modulePromise = Promise.resolve(importModule(resolved.moduleUrl));
|
|
321
|
+
modulePromise = Promise.resolve().then(() => importModule(resolved.moduleUrl));
|
|
322
322
|
moduleCache.set(resolved.moduleUrl, modulePromise);
|
|
323
323
|
}
|
|
324
|
-
|
|
324
|
+
let module;
|
|
325
|
+
try {
|
|
326
|
+
module = await modulePromise;
|
|
327
|
+
} catch (cause) {
|
|
328
|
+
if (moduleCache.get(resolved.moduleUrl) === modulePromise) {
|
|
329
|
+
moduleCache.delete(resolved.moduleUrl);
|
|
330
|
+
}
|
|
331
|
+
throw new Error(`Lazy ${type} "${id}" failed to import ${resolved.moduleUrl}: ${errorMessage(cause)}`, {
|
|
332
|
+
cause
|
|
333
|
+
});
|
|
334
|
+
}
|
|
325
335
|
const value = resolveExport(module, resolved.exportNames, type, id);
|
|
326
336
|
exportCache.set(cacheKey, value);
|
|
327
337
|
return value;
|
|
@@ -481,6 +491,10 @@ const __lazyRegistryModule = (() => {
|
|
|
481
491
|
return /^[A-Za-z][A-Za-z\d+.-]*:/.test(value);
|
|
482
492
|
}
|
|
483
493
|
|
|
494
|
+
function errorMessage(error) {
|
|
495
|
+
return error instanceof Error ? error.message : String(error);
|
|
496
|
+
}
|
|
497
|
+
|
|
484
498
|
function stableStringify(value) {
|
|
485
499
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
486
500
|
return JSON.stringify(value);
|
|
@@ -809,15 +823,7 @@ const __cacheModule = (() => {
|
|
|
809
823
|
|
|
810
824
|
get(key) {
|
|
811
825
|
assertKey(key);
|
|
812
|
-
|
|
813
|
-
if (!entry) {
|
|
814
|
-
return undefined;
|
|
815
|
-
}
|
|
816
|
-
if (entry.expiresAt !== undefined && entry.expiresAt <= now()) {
|
|
817
|
-
entries.delete(key);
|
|
818
|
-
return undefined;
|
|
819
|
-
}
|
|
820
|
-
return entry.value;
|
|
826
|
+
return readEntry(key).value;
|
|
821
827
|
},
|
|
822
828
|
|
|
823
829
|
set(key, value, options = {}) {
|
|
@@ -835,9 +841,9 @@ const __cacheModule = (() => {
|
|
|
835
841
|
if (typeof fn !== "function") {
|
|
836
842
|
throw new TypeError("cache.getOrSet(key, fn) requires a function.");
|
|
837
843
|
}
|
|
838
|
-
const cached =
|
|
839
|
-
if (cached
|
|
840
|
-
return cached;
|
|
844
|
+
const cached = readEntry(key);
|
|
845
|
+
if (cached.found) {
|
|
846
|
+
return cached.value;
|
|
841
847
|
}
|
|
842
848
|
if (pending.has(key)) {
|
|
843
849
|
return pending.get(key);
|
|
@@ -888,8 +894,8 @@ const __cacheModule = (() => {
|
|
|
888
894
|
snapshot() {
|
|
889
895
|
const snapshot = {};
|
|
890
896
|
for (const [key] of entries) {
|
|
891
|
-
const value =
|
|
892
|
-
if (value !== undefined) {
|
|
897
|
+
const { found, value } = readEntry(key);
|
|
898
|
+
if (found && value !== undefined) {
|
|
893
899
|
snapshot[key] = value;
|
|
894
900
|
}
|
|
895
901
|
}
|
|
@@ -929,6 +935,18 @@ const __cacheModule = (() => {
|
|
|
929
935
|
const prefix = key.split(":")[0];
|
|
930
936
|
return definitions.get(prefix);
|
|
931
937
|
}
|
|
938
|
+
|
|
939
|
+
function readEntry(key) {
|
|
940
|
+
const entry = entries.get(key);
|
|
941
|
+
if (!entry) {
|
|
942
|
+
return { found: false, value: undefined };
|
|
943
|
+
}
|
|
944
|
+
if (entry.expiresAt !== undefined && entry.expiresAt <= now()) {
|
|
945
|
+
entries.delete(key);
|
|
946
|
+
return { found: false, value: undefined };
|
|
947
|
+
}
|
|
948
|
+
return { found: true, value: entry.value };
|
|
949
|
+
}
|
|
932
950
|
}
|
|
933
951
|
|
|
934
952
|
function normalizeDefinition(definition) {
|
|
@@ -2147,7 +2165,7 @@ const __serverModule = (() => {
|
|
|
2147
2165
|
|
|
2148
2166
|
function createServerProxy({
|
|
2149
2167
|
endpoint = "/__async/server",
|
|
2150
|
-
|
|
2168
|
+
transport,
|
|
2151
2169
|
signals,
|
|
2152
2170
|
loader,
|
|
2153
2171
|
router,
|
|
@@ -2155,8 +2173,8 @@ const __serverModule = (() => {
|
|
|
2155
2173
|
scheduler,
|
|
2156
2174
|
headers = {}
|
|
2157
2175
|
} = {}) {
|
|
2158
|
-
if (typeof
|
|
2159
|
-
throw new TypeError("createServerProxy(...) requires
|
|
2176
|
+
if (typeof transport !== "function") {
|
|
2177
|
+
throw new TypeError("createServerProxy(...) requires a transport function.");
|
|
2160
2178
|
}
|
|
2161
2179
|
|
|
2162
2180
|
const defaults = { signals, loader, router, cache, scheduler };
|
|
@@ -2171,7 +2189,7 @@ const __serverModule = (() => {
|
|
|
2171
2189
|
};
|
|
2172
2190
|
assertJsonTransportable(body);
|
|
2173
2191
|
|
|
2174
|
-
const response = await
|
|
2192
|
+
const response = await transport(joinEndpoint(endpoint, id), {
|
|
2175
2193
|
method: "POST",
|
|
2176
2194
|
headers: {
|
|
2177
2195
|
"content-type": "application/json",
|
|
@@ -2448,14 +2466,17 @@ const __serverModule = (() => {
|
|
|
2448
2466
|
return output;
|
|
2449
2467
|
}
|
|
2450
2468
|
|
|
2451
|
-
function assertJsonTransportable(value,
|
|
2469
|
+
function assertJsonTransportable(value, stack = new Set()) {
|
|
2470
|
+
if (typeof value === "bigint") {
|
|
2471
|
+
throw new Error("Server proxy JSON transport does not support BigInt values.");
|
|
2472
|
+
}
|
|
2452
2473
|
if (value == null || typeof value !== "object") {
|
|
2453
2474
|
return;
|
|
2454
2475
|
}
|
|
2455
|
-
if (
|
|
2456
|
-
|
|
2476
|
+
if (stack.has(value)) {
|
|
2477
|
+
throw new Error("Server proxy JSON transport does not support circular values.");
|
|
2457
2478
|
}
|
|
2458
|
-
|
|
2479
|
+
stack.add(value);
|
|
2459
2480
|
|
|
2460
2481
|
const tag = Object.prototype.toString.call(value);
|
|
2461
2482
|
if (tag === "[object File]" || tag === "[object Blob]" || tag === "[object FormData]") {
|
|
@@ -2463,13 +2484,15 @@ const __serverModule = (() => {
|
|
|
2463
2484
|
}
|
|
2464
2485
|
if (Array.isArray(value)) {
|
|
2465
2486
|
for (const item of value) {
|
|
2466
|
-
assertJsonTransportable(item,
|
|
2487
|
+
assertJsonTransportable(item, stack);
|
|
2467
2488
|
}
|
|
2489
|
+
stack.delete(value);
|
|
2468
2490
|
return;
|
|
2469
2491
|
}
|
|
2470
2492
|
for (const item of Object.values(value)) {
|
|
2471
|
-
assertJsonTransportable(item,
|
|
2493
|
+
assertJsonTransportable(item, stack);
|
|
2472
2494
|
}
|
|
2495
|
+
stack.delete(value);
|
|
2473
2496
|
}
|
|
2474
2497
|
|
|
2475
2498
|
function joinEndpoint(endpoint, id) {
|
|
@@ -3114,6 +3137,7 @@ const __loaderModule = (() => {
|
|
|
3114
3137
|
return;
|
|
3115
3138
|
}
|
|
3116
3139
|
destroyed = true;
|
|
3140
|
+
markDestroyedScopes(rootNode);
|
|
3117
3141
|
for (const cleanup of [...cleanups]) {
|
|
3118
3142
|
runCleanup(cleanup);
|
|
3119
3143
|
}
|
|
@@ -3564,6 +3588,12 @@ const __loaderModule = (() => {
|
|
|
3564
3588
|
});
|
|
3565
3589
|
}
|
|
3566
3590
|
|
|
3591
|
+
function markDestroyedScopes(scope) {
|
|
3592
|
+
for (const element of elementsIn(scope)) {
|
|
3593
|
+
schedulerInstance.markScopeDestroyed(element);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3567
3597
|
return api;
|
|
3568
3598
|
}
|
|
3569
3599
|
|
|
@@ -3943,6 +3973,8 @@ const __routerModule = (() => {
|
|
|
3943
3973
|
|
|
3944
3974
|
const route = defineRoute;
|
|
3945
3975
|
|
|
3976
|
+
const routerModes = new Set(["csr", "spa", "ssr", "mpa"]);
|
|
3977
|
+
|
|
3946
3978
|
function createRouteRegistry(initialMap = {}, options = {}) {
|
|
3947
3979
|
const registryStore = options.registry ?? createRegistryStore();
|
|
3948
3980
|
const type = options.type ?? "route";
|
|
@@ -3989,7 +4021,7 @@ const __routerModule = (() => {
|
|
|
3989
4021
|
}
|
|
3990
4022
|
const params = {};
|
|
3991
4023
|
candidate.keys.forEach((key, index) => {
|
|
3992
|
-
params[key] =
|
|
4024
|
+
params[key] = safeDecodeURIComponent(match[index + 1] ?? "");
|
|
3993
4025
|
});
|
|
3994
4026
|
return {
|
|
3995
4027
|
pattern: candidate.pattern,
|
|
@@ -4038,7 +4070,7 @@ const __routerModule = (() => {
|
|
|
4038
4070
|
}
|
|
4039
4071
|
|
|
4040
4072
|
function createRouter({
|
|
4041
|
-
mode = "ssr
|
|
4073
|
+
mode = "ssr",
|
|
4042
4074
|
root,
|
|
4043
4075
|
boundary = "route",
|
|
4044
4076
|
routes = createRouteRegistry(),
|
|
@@ -4048,11 +4080,10 @@ const __routerModule = (() => {
|
|
|
4048
4080
|
server,
|
|
4049
4081
|
cache,
|
|
4050
4082
|
partials,
|
|
4051
|
-
fetch: fetchImpl = globalThis.fetch?.bind(globalThis),
|
|
4052
|
-
routeEndpoint = "/__async/route",
|
|
4053
4083
|
attributes,
|
|
4054
4084
|
scheduler
|
|
4055
4085
|
} = {}) {
|
|
4086
|
+
assertRouterMode(mode);
|
|
4056
4087
|
const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
|
|
4057
4088
|
const rootNode = root ?? documentRef;
|
|
4058
4089
|
const signalRegistry = signals ?? loader?.signals ?? createSignalRegistry();
|
|
@@ -4104,11 +4135,11 @@ const __routerModule = (() => {
|
|
|
4104
4135
|
}
|
|
4105
4136
|
bindNavigation();
|
|
4106
4137
|
if (mode === "csr") {
|
|
4107
|
-
|
|
4138
|
+
handleNavigation(api.navigate(currentUrl(), {
|
|
4108
4139
|
replace: true,
|
|
4109
4140
|
initial: true,
|
|
4110
4141
|
source: "client"
|
|
4111
|
-
})
|
|
4142
|
+
}));
|
|
4112
4143
|
return api;
|
|
4113
4144
|
}
|
|
4114
4145
|
updateStateFromLocation();
|
|
@@ -4121,16 +4152,13 @@ const __routerModule = (() => {
|
|
|
4121
4152
|
|
|
4122
4153
|
prefetch(url) {
|
|
4123
4154
|
assertActive();
|
|
4124
|
-
if (mode === "
|
|
4125
|
-
return
|
|
4155
|
+
if (mode === "mpa" || mode === "ssr") {
|
|
4156
|
+
return Promise.resolve(null);
|
|
4126
4157
|
}
|
|
4127
4158
|
const matched = api.match(url);
|
|
4128
4159
|
if (matched?.route?.partial && partials?.resolve?.(matched.route.partial)) {
|
|
4129
4160
|
return partials.render(matched.route.partial, matched.params, contextFor(matched));
|
|
4130
4161
|
}
|
|
4131
|
-
if (typeof fetchImpl === "function") {
|
|
4132
|
-
return fetchRoute(url, { prefetch: true });
|
|
4133
|
-
}
|
|
4134
4162
|
return Promise.resolve(null);
|
|
4135
4163
|
},
|
|
4136
4164
|
|
|
@@ -4142,9 +4170,6 @@ const __routerModule = (() => {
|
|
|
4142
4170
|
}
|
|
4143
4171
|
|
|
4144
4172
|
const target = resolveUrl(url);
|
|
4145
|
-
if (mode === "ssr-spa") {
|
|
4146
|
-
return fetchRoutePartial(target, options);
|
|
4147
|
-
}
|
|
4148
4173
|
return renderLocalRoutePartial(target, options);
|
|
4149
4174
|
},
|
|
4150
4175
|
|
|
@@ -4173,7 +4198,7 @@ const __routerModule = (() => {
|
|
|
4173
4198
|
return;
|
|
4174
4199
|
}
|
|
4175
4200
|
event.preventDefault();
|
|
4176
|
-
api.navigate(anchor.href);
|
|
4201
|
+
handleNavigation(api.navigate(anchor.href));
|
|
4177
4202
|
};
|
|
4178
4203
|
const submit = (event) => {
|
|
4179
4204
|
const form = closest(event.target, "form");
|
|
@@ -4181,9 +4206,9 @@ const __routerModule = (() => {
|
|
|
4181
4206
|
return;
|
|
4182
4207
|
}
|
|
4183
4208
|
event.preventDefault();
|
|
4184
|
-
api.navigate(formActionUrl(form));
|
|
4209
|
+
handleNavigation(api.navigate(formActionUrl(form)));
|
|
4185
4210
|
};
|
|
4186
|
-
const popstate = () => api.navigate(currentUrl(), { history: false });
|
|
4211
|
+
const popstate = () => handleNavigation(api.navigate(currentUrl(), { history: false }));
|
|
4187
4212
|
|
|
4188
4213
|
rootNode.addEventListener?.("click", click);
|
|
4189
4214
|
rootNode.addEventListener?.("submit", submit);
|
|
@@ -4232,31 +4257,6 @@ const __routerModule = (() => {
|
|
|
4232
4257
|
}
|
|
4233
4258
|
}
|
|
4234
4259
|
|
|
4235
|
-
async function fetchRoutePartial(target, options = {}) {
|
|
4236
|
-
const matched = api.match(target);
|
|
4237
|
-
const navigation = beginNavigation(target, matched);
|
|
4238
|
-
setMatchedRouterState(target, matched, { pending: true, error: null });
|
|
4239
|
-
|
|
4240
|
-
try {
|
|
4241
|
-
const result = await fetchRoute(target.href, { signal: navigation.abort });
|
|
4242
|
-
if (!isActiveNavigation(navigation)) {
|
|
4243
|
-
return null;
|
|
4244
|
-
}
|
|
4245
|
-
await applyNavigationResult(result, target, options, navigation);
|
|
4246
|
-
if (!isActiveNavigation(navigation)) {
|
|
4247
|
-
return null;
|
|
4248
|
-
}
|
|
4249
|
-
setRouterState({ pending: false, error: null });
|
|
4250
|
-
return result;
|
|
4251
|
-
} catch (error) {
|
|
4252
|
-
if (!isActiveNavigation(navigation)) {
|
|
4253
|
-
return null;
|
|
4254
|
-
}
|
|
4255
|
-
setRouterState({ pending: false, error });
|
|
4256
|
-
throw error;
|
|
4257
|
-
}
|
|
4258
|
-
}
|
|
4259
|
-
|
|
4260
4260
|
async function applyNavigationResult(result, target, options, navigation) {
|
|
4261
4261
|
if (!isActiveNavigation(navigation)) {
|
|
4262
4262
|
return;
|
|
@@ -4287,29 +4287,6 @@ const __routerModule = (() => {
|
|
|
4287
4287
|
documentRef.defaultView?.history?.pushState?.({}, "", target.href);
|
|
4288
4288
|
}
|
|
4289
4289
|
|
|
4290
|
-
async function fetchRoute(url, { prefetch = false, signal } = {}) {
|
|
4291
|
-
if (typeof fetchImpl !== "function") {
|
|
4292
|
-
throw new Error("Router navigation requires a partial registry or fetch.");
|
|
4293
|
-
}
|
|
4294
|
-
const response = await fetchImpl(`${routeEndpoint}?to=${encodeURIComponent(String(url))}`, {
|
|
4295
|
-
headers: {
|
|
4296
|
-
accept: "application/json, text/html"
|
|
4297
|
-
},
|
|
4298
|
-
signal
|
|
4299
|
-
});
|
|
4300
|
-
if (!response.ok) {
|
|
4301
|
-
throw new Error(`Route "${url}" failed with ${response.status}.`);
|
|
4302
|
-
}
|
|
4303
|
-
if (prefetch) {
|
|
4304
|
-
return response;
|
|
4305
|
-
}
|
|
4306
|
-
const type = response.headers.get("content-type") ?? "";
|
|
4307
|
-
if (type.includes("application/json")) {
|
|
4308
|
-
return response.json();
|
|
4309
|
-
}
|
|
4310
|
-
return { boundary, html: await response.text() };
|
|
4311
|
-
}
|
|
4312
|
-
|
|
4313
4290
|
function contextFor(matched, navigation) {
|
|
4314
4291
|
return {
|
|
4315
4292
|
params: matched.params,
|
|
@@ -4376,6 +4353,19 @@ const __routerModule = (() => {
|
|
|
4376
4353
|
}
|
|
4377
4354
|
}
|
|
4378
4355
|
|
|
4356
|
+
function handleNavigation(promise) {
|
|
4357
|
+
void promise.catch((error) => {
|
|
4358
|
+
if (destroyed) {
|
|
4359
|
+
return;
|
|
4360
|
+
}
|
|
4361
|
+
setRouterState({
|
|
4362
|
+
pending: false,
|
|
4363
|
+
error
|
|
4364
|
+
});
|
|
4365
|
+
dispatchAsyncError(rootNode, error);
|
|
4366
|
+
});
|
|
4367
|
+
}
|
|
4368
|
+
|
|
4379
4369
|
function currentUrl() {
|
|
4380
4370
|
return resolveUrl(documentRef.defaultView?.location?.href ?? "http://localhost/");
|
|
4381
4371
|
}
|
|
@@ -4392,6 +4382,33 @@ const __routerModule = (() => {
|
|
|
4392
4382
|
throw new Error("Router has been destroyed.");
|
|
4393
4383
|
}
|
|
4394
4384
|
}
|
|
4385
|
+
|
|
4386
|
+
function shouldIgnoreLink(event, anchor) {
|
|
4387
|
+
if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
4388
|
+
return true;
|
|
4389
|
+
}
|
|
4390
|
+
if (anchor.target || anchor.hasAttribute("download")) {
|
|
4391
|
+
return true;
|
|
4392
|
+
}
|
|
4393
|
+
const target = resolveUrl(anchor.href);
|
|
4394
|
+
const current = currentUrl();
|
|
4395
|
+
if (target.origin !== current.origin) {
|
|
4396
|
+
return true;
|
|
4397
|
+
}
|
|
4398
|
+
return isHashOnlyNavigation(target, current, anchor);
|
|
4399
|
+
}
|
|
4400
|
+
|
|
4401
|
+
function shouldIgnoreForm(form) {
|
|
4402
|
+
const method = String(form.method || "get").toLowerCase();
|
|
4403
|
+
return method !== "get" || resolveUrl(form.action).origin !== currentUrl().origin;
|
|
4404
|
+
}
|
|
4405
|
+
|
|
4406
|
+
function formActionUrl(form) {
|
|
4407
|
+
const url = resolveUrl(form.action || form.ownerDocument.defaultView.location.href);
|
|
4408
|
+
const formData = new form.ownerDocument.defaultView.FormData(form);
|
|
4409
|
+
url.search = new URLSearchParams(formData).toString();
|
|
4410
|
+
return url.href;
|
|
4411
|
+
}
|
|
4395
4412
|
}
|
|
4396
4413
|
|
|
4397
4414
|
function normalizeRoute(pattern, definition) {
|
|
@@ -4429,28 +4446,6 @@ const __routerModule = (() => {
|
|
|
4429
4446
|
return { regex: new RegExp(`^${source}$`), keys };
|
|
4430
4447
|
}
|
|
4431
4448
|
|
|
4432
|
-
function shouldIgnoreLink(event, anchor) {
|
|
4433
|
-
if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
4434
|
-
return true;
|
|
4435
|
-
}
|
|
4436
|
-
if (anchor.target || anchor.hasAttribute("download")) {
|
|
4437
|
-
return true;
|
|
4438
|
-
}
|
|
4439
|
-
return toUrl(anchor.href).origin !== toUrl(anchor.ownerDocument.defaultView.location.href).origin;
|
|
4440
|
-
}
|
|
4441
|
-
|
|
4442
|
-
function shouldIgnoreForm(form) {
|
|
4443
|
-
const method = String(form.method || "get").toLowerCase();
|
|
4444
|
-
return method !== "get" || toUrl(form.action).origin !== toUrl(form.ownerDocument.defaultView.location.href).origin;
|
|
4445
|
-
}
|
|
4446
|
-
|
|
4447
|
-
function formActionUrl(form) {
|
|
4448
|
-
const url = toUrl(form.action || form.ownerDocument.defaultView.location.href);
|
|
4449
|
-
const formData = new form.ownerDocument.defaultView.FormData(form);
|
|
4450
|
-
url.search = new URLSearchParams(formData).toString();
|
|
4451
|
-
return url.href;
|
|
4452
|
-
}
|
|
4453
|
-
|
|
4454
4449
|
function closest(target, selector) {
|
|
4455
4450
|
return target?.closest?.(selector);
|
|
4456
4451
|
}
|
|
@@ -4466,6 +4461,40 @@ const __routerModule = (() => {
|
|
|
4466
4461
|
return Object.fromEntries(url.searchParams.entries());
|
|
4467
4462
|
}
|
|
4468
4463
|
|
|
4464
|
+
function safeDecodeURIComponent(value) {
|
|
4465
|
+
try {
|
|
4466
|
+
return decodeURIComponent(value);
|
|
4467
|
+
} catch {
|
|
4468
|
+
return value;
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
|
|
4472
|
+
function isHashOnlyNavigation(target, current, anchor) {
|
|
4473
|
+
if (target.origin !== current.origin || target.pathname !== current.pathname || target.search !== current.search) {
|
|
4474
|
+
return false;
|
|
4475
|
+
}
|
|
4476
|
+
return target.hash !== current.hash || anchor.getAttribute?.("href")?.startsWith("#") === true;
|
|
4477
|
+
}
|
|
4478
|
+
|
|
4479
|
+
function assertRouterMode(mode) {
|
|
4480
|
+
if (!routerModes.has(mode)) {
|
|
4481
|
+
throw new TypeError(`Unknown router mode "${mode}".`);
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4484
|
+
|
|
4485
|
+
function dispatchAsyncError(element, error) {
|
|
4486
|
+
const EventCtor = element.ownerDocument?.defaultView?.CustomEvent ?? globalThis.CustomEvent;
|
|
4487
|
+
if (typeof EventCtor !== "function") {
|
|
4488
|
+
return;
|
|
4489
|
+
}
|
|
4490
|
+
element.dispatchEvent?.(
|
|
4491
|
+
new EventCtor("async:error", {
|
|
4492
|
+
bubbles: true,
|
|
4493
|
+
detail: { error }
|
|
4494
|
+
})
|
|
4495
|
+
);
|
|
4496
|
+
}
|
|
4497
|
+
|
|
4469
4498
|
function escapeRegExp(value) {
|
|
4470
4499
|
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4471
4500
|
}
|
|
@@ -4851,7 +4880,7 @@ const __appModule = (() => {
|
|
|
4851
4880
|
return;
|
|
4852
4881
|
}
|
|
4853
4882
|
router = router ?? createRouter({
|
|
4854
|
-
mode: options.mode ?? "ssr
|
|
4883
|
+
mode: options.mode ?? "ssr",
|
|
4855
4884
|
root,
|
|
4856
4885
|
boundary: options.boundary ?? "route",
|
|
4857
4886
|
routes,
|
|
@@ -4862,8 +4891,6 @@ const __appModule = (() => {
|
|
|
4862
4891
|
cache: browserCache,
|
|
4863
4892
|
partials,
|
|
4864
4893
|
scheduler,
|
|
4865
|
-
fetch: options.fetch,
|
|
4866
|
-
routeEndpoint: options.routeEndpoint,
|
|
4867
4894
|
attributes
|
|
4868
4895
|
});
|
|
4869
4896
|
runtime.router = router;
|