@absolutejs/absolute 0.20.0-beta.169 → 0.20.0-beta.170
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 +80 -0
- package/README.md +232 -0
- package/changelog.json +228 -0
- package/dist/angular/browser.js +7 -8
- package/dist/angular/browser.js.map +6 -5
- package/dist/angular/components/core/streamingSlotRegistrar.js +6 -2
- package/dist/angular/components/core/streamingSlotRegistry.js +7 -3
- package/dist/angular/index.js +555 -156
- package/dist/angular/index.js.map +29 -23
- package/dist/angular/server.js +555 -156
- package/dist/angular/server.js.map +29 -23
- package/dist/build/buildWorker.js +14 -8
- package/dist/build.js +4949 -1941
- package/dist/build.js.map +72 -42
- package/dist/cli/{add-7azr0c83.js → add-6a7pjkn9.js} +11 -7
- package/dist/cli/{analyze-3cyek6v3.js → analyze-606fshwa.js} +1 -1
- package/dist/cli/{build-25rz8v25.js → build-g81xqbb9.js} +1 -1
- package/dist/cli/{compile-4jnfxts9.js → compile-9y1w9zpj.js} +3 -3
- package/dist/cli/config/server.js +10699 -1538
- package/dist/cli/{dev-9tk5hn2e.js → dev-hyeb89hp.js} +5 -1
- package/dist/cli/{doctor-4nv2c2j3.js → doctor-h55y9hj1.js} +1 -1
- package/dist/cli/{generate-v75p2fct.js → generate-20x1pkf7.js} +4 -4
- package/dist/cli/{htmx-427swtdz.js → htmx-6mz2h7k1.js} +2 -2
- package/dist/cli/{index-bhbs6d5z.js → index-32ge2w1f.js} +12 -4
- package/dist/cli/{index-a4ws2jwd.js → index-3wz3mktm.js} +1 -1
- package/dist/cli/{index-b7hjgknf.js → index-9sksd8c8.js} +13 -4
- package/dist/cli/{index-eyzwc7gz.js → index-qbrzvmh9.js} +4 -2
- package/dist/cli/index-qw98g1p6.js +269 -0
- package/dist/cli/{index-434b7j4a.js → index-vtp20gwf.js} +69 -1
- package/dist/cli/{index-h7qhz02e.js → index-wk0pyxrg.js} +3 -0
- package/dist/cli/{index-k3y8cb6c.js → index-z6f43b20.js} +1 -1
- package/dist/cli/index.js +16 -16
- package/dist/cli/{islands-8sgky9gh.js → islands-dc54e5y1.js} +1 -1
- package/dist/cli/{ls-4m4yqwww.js → ls-b6zz24nt.js} +1 -1
- package/dist/cli/{mobile-hcxjr2gf.js → mobile-ajnnvqtv.js} +3 -3
- package/dist/cli/prerender-fqpp0m1j.js +10 -0
- package/dist/cli/{remove-1ycjx85k.js → remove-t8x1mrxg.js} +4 -4
- package/dist/cli/{start-mbaejxhm.js → start-d6p831ny.js} +3 -3
- package/dist/cli/{typecheck-dr068pmh.js → typecheck-stbt0qt5.js} +6 -1
- package/dist/cli/{workspace-1sycp9g1.js → workspace-61hh6jq1.js} +1 -1
- package/dist/client/chunks/index-6b66nzyf.js +381 -0
- package/dist/client/chunks/index-6b66nzyf.js.map +11 -0
- package/dist/client/chunks/{islands-rc28k5sx.js → islands-s2ta5ajb.js} +5 -2
- package/dist/client/chunks/{islands-rc28k5sx.js.map → islands-s2ta5ajb.js.map} +7 -6
- package/dist/client/index.js +160 -3
- package/dist/client/index.js.map +6 -4
- package/dist/client/navigation.js +764 -0
- package/dist/client/navigation.js.map +10 -0
- package/dist/client/prefetch.js +33 -375
- package/dist/client/prefetch.js.map +3 -4
- package/dist/core/streamingSlotRegistrar.js +5 -2
- package/dist/core/streamingSlotRegistrar.js.map +5 -4
- package/dist/core/streamingSlotRegistry.js +5 -2
- package/dist/core/streamingSlotRegistry.js.map +5 -4
- package/dist/dev/client/cspNonce.ts +28 -0
- package/dist/dev/client/handlers/html.ts +4 -3
- package/dist/dev/client/handlers/htmx.ts +3 -2
- package/dist/dev/client/handlers/svelte.ts +15 -90
- package/dist/dev/client/syncDevtools.ts +2 -1
- package/dist/dev/serverBootstrap.js +11 -4
- package/dist/i18n/index.js +543 -0
- package/dist/i18n/index.js.map +15 -0
- package/dist/index.js +6339 -2233
- package/dist/index.js.map +89 -54
- package/dist/islands/index.js +282 -40
- package/dist/islands/index.js.map +14 -10
- package/dist/mobile/browser.js +7 -2
- package/dist/mobile/browser.js.map +5 -4
- package/dist/mobile/index.js +7 -2
- package/dist/mobile/index.js.map +5 -4
- package/dist/mobile/remoteMacAgentEntry.js +1 -1
- package/dist/react/components/browser/index.js +94 -4
- package/dist/react/components/index.js +150 -10
- package/dist/react/components/index.js.map +15 -10
- package/dist/react/hooks/index.js +479 -13
- package/dist/react/hooks/index.js.map +12 -4
- package/dist/react/index.js +769 -166
- package/dist/react/index.js.map +32 -21
- package/dist/react/router/browser.js +21 -4
- package/dist/react/router/browser.js.map +5 -4
- package/dist/react/router/index.js +21 -4
- package/dist/react/router/index.js.map +5 -4
- package/dist/react/server.js +597 -203
- package/dist/react/server.js.map +25 -15
- package/dist/src/angular/zoneRuntime.d.ts +5 -0
- package/dist/src/build/buildReactServerComponents.d.ts +22 -0
- package/dist/src/build/downlevelAsyncForZone.d.ts +1 -0
- package/dist/src/build/fileRoutes.d.ts +65 -0
- package/dist/src/build/fonts/buildFonts.d.ts +25 -0
- package/dist/src/build/fonts/fallbackMetrics.d.ts +13 -0
- package/dist/src/build/fonts/googleFonts.d.ts +18 -0
- package/dist/src/build/fonts/injectFontTags.d.ts +7 -0
- package/dist/src/build/fonts/parseFontMetrics.d.ts +4 -0
- package/dist/src/build/generateReactIndexes.d.ts +7 -1
- package/dist/src/build/htmlIncludes.d.ts +16 -0
- package/dist/src/build/inlineSourceMapComment.d.ts +1 -0
- package/dist/src/build/scanConventions.d.ts +3 -0
- package/dist/src/build/vitals.d.ts +21 -0
- package/dist/src/client/formActions.d.ts +29 -0
- package/dist/src/client/index.d.ts +2 -0
- package/dist/src/client/navigation.d.ts +16 -0
- package/dist/src/client/prefetch.d.ts +2 -30
- package/dist/src/core/build.d.ts +2 -2
- package/dist/src/core/cspNonce.d.ts +12 -0
- package/dist/src/core/fileRouteAdapter.d.ts +15 -0
- package/dist/src/core/fileRoutes.d.ts +5 -0
- package/dist/src/core/formActionFlash.d.ts +25 -0
- package/dist/src/core/formActions.d.ts +249 -0
- package/dist/src/core/index.d.ts +5 -0
- package/dist/src/core/partialPrerender.d.ts +28 -0
- package/dist/src/core/partialPrerenderServe.d.ts +24 -0
- package/dist/src/core/precompressed.d.ts +11 -1
- package/dist/src/core/prerenderNonce.d.ts +9 -0
- package/dist/src/core/productionRuntime.d.ts +4 -2
- package/dist/src/core/serverOnlyStaticFiles.d.ts +8 -0
- package/dist/src/core/staticHtmlNonce.d.ts +2 -0
- package/dist/src/core/vitals.d.ts +108 -0
- package/dist/src/dev/angular/fastHmrCompiler.d.ts +1 -1
- package/dist/src/dev/client/cspNonce.d.ts +2 -0
- package/dist/src/dev/fileRouteRefresher.d.ts +5 -0
- package/dist/src/dev/importCost/instrument.d.ts +1 -1
- package/dist/src/dev/reactServerComponentsRebuild.d.ts +3 -0
- package/dist/src/i18n/defineMessages.d.ts +5 -0
- package/dist/src/i18n/formatMessage.d.ts +26 -0
- package/dist/src/i18n/index.d.ts +6 -0
- package/dist/src/i18n/intlCache.d.ts +5 -0
- package/dist/src/i18n/routing.d.ts +28 -0
- package/dist/src/i18n/translator.d.ts +11 -0
- package/dist/src/mobile/androidRelease.d.ts +2 -2
- package/dist/src/mobile/iosRelease.d.ts +4 -4
- package/dist/src/mobile/materializedBundle.d.ts +1 -1
- package/dist/src/mobile/mobileInspect.d.ts +1 -1
- package/dist/src/mobile/releaseArtifact.d.ts +2 -2
- package/dist/src/mobile/remoteMacProtocol.d.ts +2 -2
- package/dist/src/plugins/i18n.d.ts +30 -0
- package/dist/src/plugins/index.d.ts +2 -1
- package/dist/src/plugins/secureHeaders.d.ts +48 -0
- package/dist/src/react/components/Head.d.ts +1 -1
- package/dist/src/react/components/StreamSlot.browser.d.ts +1 -0
- package/dist/src/react/components/StreamSlot.d.ts +5 -1
- package/dist/src/react/components/SuspenseSlot.browser.d.ts +2 -0
- package/dist/src/react/components/SuspenseSlot.d.ts +12 -0
- package/dist/src/react/fileRoute.d.ts +18 -0
- package/dist/src/react/hooks/index.d.ts +4 -0
- package/dist/src/react/hooks/useFormAction.d.ts +42 -0
- package/dist/src/react/hooks/useI18n.d.ts +15 -0
- package/dist/src/react/index.d.ts +2 -0
- package/dist/src/react/server.d.ts +2 -0
- package/dist/src/react/serverComponents/constants.d.ts +11 -0
- package/dist/src/react/serverComponents/flightPayload.d.ts +2 -0
- package/dist/src/react/serverComponents/pageHandler.d.ts +14 -0
- package/dist/src/svelte/browser.d.ts +1 -0
- package/dist/src/svelte/fileRoute.d.ts +15 -0
- package/dist/src/svelte/i18n.d.ts +8 -0
- package/dist/src/svelte/index.d.ts +2 -0
- package/dist/src/svelte/lowerAwaitSlotSyntax.d.ts +5 -1
- package/dist/src/svelte/renderToReadableStream.d.ts +10 -2
- package/dist/src/svelte/router/browser.d.ts +3 -1
- package/dist/src/svelte/router/goto.d.ts +13 -10
- package/dist/src/svelte/router/guards.d.ts +43 -0
- package/dist/src/svelte/router/index.d.ts +3 -1
- package/dist/src/svelte/router/lazy.d.ts +17 -0
- package/dist/src/svelte/router/lazyModule.d.ts +29 -0
- package/dist/src/svelte/router/navigation.d.ts +55 -0
- package/dist/src/svelte/router/pushState.d.ts +4 -2
- package/dist/src/svelte/router/scroll.d.ts +34 -0
- package/dist/src/svelte/routerSsr.d.ts +9 -0
- package/dist/src/svelte/server.d.ts +1 -0
- package/dist/src/utils/cspNonceRef.d.ts +8 -0
- package/dist/src/utils/fontAssets.d.ts +17 -0
- package/dist/src/utils/formActionProtocol.d.ts +10 -0
- package/dist/src/utils/generateHeadElement.d.ts +1 -1
- package/dist/src/utils/index.d.ts +1 -0
- package/dist/src/utils/inlinePageCss.d.ts +18 -0
- package/dist/src/utils/loadConfig.d.ts +5 -0
- package/dist/src/utils/localeRef.d.ts +10 -0
- package/dist/src/utils/resolveConvention.d.ts +6 -1
- package/dist/src/utils/streamingSlots.d.ts +5 -0
- package/dist/src/vue/browser.d.ts +2 -0
- package/dist/src/vue/components/Image.d.ts +2 -2
- package/dist/src/vue/index.d.ts +2 -0
- package/dist/src/vue/serverDataPayload.d.ts +32 -0
- package/dist/src/vue/serverDataState.d.ts +9 -0
- package/dist/src/vue/useServerData.browser.d.ts +6 -0
- package/dist/src/vue/useServerData.d.ts +24 -0
- package/dist/svelte/browser.js +330 -3
- package/dist/svelte/browser.js.map +8 -4
- package/dist/svelte/components/AwaitSlot.svelte +5 -0
- package/dist/svelte/components/Head.svelte +50 -0
- package/dist/svelte/components/JsonLd.svelte +9 -1
- package/dist/svelte/components/StreamSlot.svelte +5 -0
- package/dist/svelte/index.js +1017 -117
- package/dist/svelte/index.js.map +38 -24
- package/dist/svelte/router/Link.svelte +40 -8
- package/dist/svelte/router/Route.svelte +157 -12
- package/dist/svelte/router/Route.svelte.d.ts +39 -5
- package/dist/svelte/router/Router.svelte +140 -25
- package/dist/svelte/router/Router.svelte.d.ts +5 -1
- package/dist/svelte/router/browser.ts +10 -0
- package/dist/svelte/router/goto.ts +15 -86
- package/dist/svelte/router/guards.ts +112 -0
- package/dist/svelte/router/index.ts +10 -0
- package/dist/svelte/router/lazy.ts +82 -0
- package/dist/svelte/router/lazyModule.ts +74 -0
- package/dist/svelte/router/navigation.ts +412 -0
- package/dist/svelte/router/pushState.ts +7 -4
- package/dist/svelte/router/scroll.ts +286 -0
- package/dist/svelte/server.js +747 -252
- package/dist/svelte/server.js.map +30 -20
- package/dist/types/angular.d.ts +2 -0
- package/dist/types/build.d.ts +37 -0
- package/dist/types/conventions.d.ts +3 -0
- package/dist/types/fileRoutes.d.ts +72 -0
- package/dist/types/fonts.d.ts +86 -0
- package/dist/types/formAction.d.ts +54 -0
- package/dist/types/globals.d.ts +10 -0
- package/dist/types/i18n.d.ts +185 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/metadata.d.ts +11 -0
- package/dist/types/navigation.d.ts +37 -0
- package/dist/types/prefetch.d.ts +10 -0
- package/dist/types/sitemap.d.ts +4 -0
- package/dist/types/svelteRouter.d.ts +89 -2
- package/dist/types/svelteRouter.ts +112 -2
- package/dist/types/vue.d.ts +46 -1
- package/dist/utils/iconVersion.ts +34 -0
- package/dist/vue/browser.js +107 -11
- package/dist/vue/browser.js.map +7 -4
- package/dist/vue/components/index.js +5 -2
- package/dist/vue/components/index.js.map +5 -4
- package/dist/vue/index.js +712 -158
- package/dist/vue/index.js.map +32 -23
- package/dist/vue/server.js +310 -79
- package/dist/vue/server.js.map +20 -15
- package/package.json +9 -1
- package/dist/cli/prerender-mfnwsvyt.js +0 -7
- package/dist/src/plugins/pageRouter.d.ts +0 -1
- /package/dist/cli/{index-j7m8pq48.js → index-rervww4p.js} +0 -0
|
@@ -1,93 +1,22 @@
|
|
|
1
|
-
import type { GotoOptions
|
|
2
|
-
import {
|
|
3
|
-
import { setPage } from './page.js';
|
|
4
|
-
import { withViewTransition } from './viewTransitions';
|
|
1
|
+
import type { GotoOptions } from '../../types/svelteRouter';
|
|
2
|
+
import { navigate } from './navigation';
|
|
5
3
|
|
|
6
|
-
|
|
4
|
+
export { setRouterMode } from './navigation';
|
|
7
5
|
|
|
8
6
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// Programmatic goto on the server is rare but we tolerate it as a
|
|
20
|
-
// way for tests to drive the router without a real DOM.
|
|
21
|
-
return new URL(target, 'http://localhost/');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return new URL(target, window.location.href);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const isExternal = (target: URL) => {
|
|
28
|
-
if (typeof window === 'undefined') return false;
|
|
29
|
-
|
|
30
|
-
return target.origin !== window.location.origin;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const writeHistory = (target: URL, options: GotoOptions) => {
|
|
34
|
-
if (typeof window === 'undefined') return;
|
|
35
|
-
|
|
36
|
-
const href =
|
|
37
|
-
activeMode === 'hash'
|
|
38
|
-
? `${window.location.pathname}${window.location.search}${buildHashHref(target.pathname + target.search)}`
|
|
39
|
-
: `${target.pathname}${target.search}${target.hash}`;
|
|
40
|
-
|
|
41
|
-
const method = options.replaceState
|
|
42
|
-
? window.history.replaceState
|
|
43
|
-
: window.history.pushState;
|
|
44
|
-
method.call(window.history, options.state ?? null, '', href);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const applyScrollAndFocus = (options: GotoOptions) => {
|
|
48
|
-
if (typeof window === 'undefined') return;
|
|
49
|
-
|
|
50
|
-
if (!options.noScroll) window.scrollTo({ left: 0, top: 0 });
|
|
51
|
-
if (!options.keepFocus && document.activeElement instanceof HTMLElement) {
|
|
52
|
-
document.activeElement.blur();
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Programmatically navigate to a URL. Updates `page.url`, writes history,
|
|
58
|
-
* and (when supported) wraps the swap in `document.startViewTransition`.
|
|
7
|
+
* Programmatically navigate to a URL. Runs the navigation guards
|
|
8
|
+
* (`beforeNavigate`, `<Router guard>`, `<Route guard>`), then updates
|
|
9
|
+
* `page.url`, writes history, swaps the route (inside
|
|
10
|
+
* `document.startViewTransition` when supported) and scrolls: to the
|
|
11
|
+
* `#hash` target when the URL has one, otherwise to the top — unless
|
|
12
|
+
* `noScroll` is set.
|
|
13
|
+
*
|
|
14
|
+
* Resolves to what happened: `'committed'`, `'cancelled'` (a guard said
|
|
15
|
+
* no), `'redirected'`, `'superseded'` (a newer navigation started while a
|
|
16
|
+
* guard was pending) or `'external'` (another origin; the browser loads it).
|
|
59
17
|
*
|
|
60
18
|
* Mirrors SvelteKit's `goto` from `$app/navigation` — same name, same
|
|
61
19
|
* options shape, so a SvelteKit user finds the primitive familiar.
|
|
62
20
|
*/
|
|
63
|
-
export const goto =
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (isExternal(url)) {
|
|
67
|
-
// External URLs go through the browser — we don't try to SPA them.
|
|
68
|
-
if (typeof window !== 'undefined') {
|
|
69
|
-
window.location.href = url.href;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// `goto` performs no network fetch of its own: the swap is a client-side
|
|
76
|
-
// route match against the registered <Route>s. What `<Link prefetch>`
|
|
77
|
-
// buys the click is therefore already in place by now — the target's
|
|
78
|
-
// HTML sits in the HTTP cache (content-hash ETag → 304 on a later real
|
|
79
|
-
// navigation), its client modules are in the modulepreload map and, when
|
|
80
|
-
// `prerender` was set, a speculation rule has the page rendered in a
|
|
81
|
-
// hidden tab. Nothing to consume here.
|
|
82
|
-
const mutate = () => {
|
|
83
|
-
writeHistory(url, options);
|
|
84
|
-
setPage({
|
|
85
|
-
params: {},
|
|
86
|
-
state: options.state ?? null,
|
|
87
|
-
url
|
|
88
|
-
});
|
|
89
|
-
applyScrollAndFocus(options);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
await withViewTransition(mutate);
|
|
93
|
-
};
|
|
21
|
+
export const goto = (target: string, options: GotoOptions = {}) =>
|
|
22
|
+
navigate(target, options, 'goto');
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
NavigationContext,
|
|
3
|
+
NavigationRedirectTarget,
|
|
4
|
+
RouteGuard
|
|
5
|
+
} from '../../types/svelteRouter';
|
|
6
|
+
|
|
7
|
+
/* Guard evaluation shared by the browser navigation path and the server
|
|
8
|
+
* render: run a chain of guards in order and reduce their answers to one
|
|
9
|
+
* decision. Pure apart from awaiting the guards themselves. */
|
|
10
|
+
|
|
11
|
+
export type GuardEntry = {
|
|
12
|
+
guard: RouteGuard;
|
|
13
|
+
params: Record<string, string | undefined>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type GuardDecision =
|
|
17
|
+
| { kind: 'allow' }
|
|
18
|
+
| { kind: 'cancel' }
|
|
19
|
+
| { kind: 'redirect'; location: string; replace: boolean }
|
|
20
|
+
| { kind: 'stale' };
|
|
21
|
+
|
|
22
|
+
export type GuardRunContext = Omit<NavigationContext, 'params'>;
|
|
23
|
+
|
|
24
|
+
const ALLOW: GuardDecision = { kind: 'allow' };
|
|
25
|
+
const CANCEL: GuardDecision = { kind: 'cancel' };
|
|
26
|
+
const STALE: GuardDecision = { kind: 'stale' };
|
|
27
|
+
|
|
28
|
+
/** Thrown (or returned) by `redirect()`. An `Error` so a throw from deep
|
|
29
|
+
* inside a helper keeps a useful stack; recognised structurally, so a
|
|
30
|
+
* copy of the router bundled twice still understands it. */
|
|
31
|
+
export class NavigationRedirect
|
|
32
|
+
extends Error
|
|
33
|
+
implements NavigationRedirectTarget
|
|
34
|
+
{
|
|
35
|
+
redirect: string;
|
|
36
|
+
replace: boolean;
|
|
37
|
+
|
|
38
|
+
constructor(location: string, replace: boolean) {
|
|
39
|
+
super(`Redirect to ${location}`);
|
|
40
|
+
this.name = 'NavigationRedirect';
|
|
41
|
+
this.redirect = location;
|
|
42
|
+
this.replace = replace;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const isRedirectTarget = (value: unknown): value is NavigationRedirectTarget =>
|
|
47
|
+
typeof value === 'object' &&
|
|
48
|
+
value !== null &&
|
|
49
|
+
'redirect' in value &&
|
|
50
|
+
typeof value.redirect === 'string';
|
|
51
|
+
|
|
52
|
+
const toDecision = (result: unknown): GuardDecision => {
|
|
53
|
+
if (result === false) return CANCEL;
|
|
54
|
+
if (typeof result === 'string') {
|
|
55
|
+
return { kind: 'redirect', location: result, replace: false };
|
|
56
|
+
}
|
|
57
|
+
if (!isRedirectTarget(result)) return ALLOW;
|
|
58
|
+
const replace = 'replace' in result && result.replace === true;
|
|
59
|
+
|
|
60
|
+
return { kind: 'redirect', location: result.redirect, replace };
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const evaluateGuard = async (entry: GuardEntry, context: GuardRunContext) => {
|
|
64
|
+
try {
|
|
65
|
+
return toDecision(
|
|
66
|
+
await entry.guard({ ...context, params: entry.params })
|
|
67
|
+
);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (isRedirectTarget(error)) return toDecision(error);
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const runFrom = async (
|
|
75
|
+
entries: GuardEntry[],
|
|
76
|
+
index: number,
|
|
77
|
+
context: GuardRunContext,
|
|
78
|
+
isStale: () => boolean
|
|
79
|
+
): Promise<GuardDecision> => {
|
|
80
|
+
const entry = entries[index];
|
|
81
|
+
if (!entry) return ALLOW;
|
|
82
|
+
const decision = await evaluateGuard(entry, context);
|
|
83
|
+
if (isStale()) return STALE;
|
|
84
|
+
if (decision.kind !== 'allow') return decision;
|
|
85
|
+
|
|
86
|
+
return runFrom(entries, index + 1, context, isStale);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Redirect from inside a guard. Return it, or throw it from a helper the
|
|
91
|
+
* guard calls — both short-circuit the guard chain the same way:
|
|
92
|
+
*
|
|
93
|
+
* guard={({ request }) => {
|
|
94
|
+
* if (!isSignedIn(request)) throw redirect('/login');
|
|
95
|
+
* }}
|
|
96
|
+
*
|
|
97
|
+
* In the browser the redirect adds a history entry unless
|
|
98
|
+
* `{ replace: true }`; on the server it is a 302.
|
|
99
|
+
*/
|
|
100
|
+
export const redirect = (
|
|
101
|
+
location: string,
|
|
102
|
+
options: { replace?: boolean } = {}
|
|
103
|
+
) => new NavigationRedirect(location, options.replace === true);
|
|
104
|
+
|
|
105
|
+
/** Run `entries` one after another; the first guard that cancels or
|
|
106
|
+
* redirects decides. `isStale` is checked after every guard so a newer
|
|
107
|
+
* navigation can abandon this one mid-chain. */
|
|
108
|
+
export const runGuardChain = (
|
|
109
|
+
entries: GuardEntry[],
|
|
110
|
+
context: GuardRunContext,
|
|
111
|
+
isStale: () => boolean = () => false
|
|
112
|
+
) => runFrom(entries, 0, context, isStale);
|
|
@@ -11,13 +11,23 @@
|
|
|
11
11
|
// AbsoluteJS's Svelte compile pipeline.
|
|
12
12
|
|
|
13
13
|
export { goto } from './goto';
|
|
14
|
+
export { NavigationRedirect, redirect } from './guards';
|
|
15
|
+
export { beforeNavigate, preloadRoute } from './navigation';
|
|
14
16
|
export { page } from './page.js';
|
|
15
17
|
export { pushState, replaceState } from './pushState';
|
|
16
18
|
|
|
17
19
|
export type {
|
|
18
20
|
ExtractRouteParams,
|
|
19
21
|
GotoOptions,
|
|
22
|
+
LazyRouteLoader,
|
|
23
|
+
LazyRouteModule,
|
|
20
24
|
LinkPrefetchMode,
|
|
25
|
+
NavigationContext,
|
|
26
|
+
NavigationOutcome,
|
|
27
|
+
NavigationRedirectTarget,
|
|
28
|
+
NavigationType,
|
|
21
29
|
PageState,
|
|
30
|
+
RouteGuard,
|
|
31
|
+
RouteGuardResult,
|
|
22
32
|
RouterMode
|
|
23
33
|
} from '../../types/svelteRouter';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { LazyRouteLoader } from '../../types/svelteRouter';
|
|
2
|
+
import {
|
|
3
|
+
type LazyComponent,
|
|
4
|
+
readSeededLazy,
|
|
5
|
+
toLazyComponent
|
|
6
|
+
} from './lazyModule';
|
|
7
|
+
|
|
8
|
+
/* Browser-side cache of lazy route components. A `<Route loader>` usually
|
|
9
|
+
* passes an inline arrow, so a fresh function arrives with every Route
|
|
10
|
+
* instance; the cache is keyed by the route pattern plus the loader's
|
|
11
|
+
* source text instead, which is stable for the life of the bundle. A
|
|
12
|
+
* route visited (or prefetched) once therefore renders synchronously the
|
|
13
|
+
* next time it becomes active — no pending flash on revisits. */
|
|
14
|
+
|
|
15
|
+
const resolvedComponents = new Map<string, LazyComponent>();
|
|
16
|
+
const inflightLoads = new Map<string, Promise<LazyComponent>>();
|
|
17
|
+
const LAZY_SETTLE_TIMEOUT_MS = 3_000;
|
|
18
|
+
|
|
19
|
+
/** Forget a failed (or stale) load so the next attempt calls the loader
|
|
20
|
+
* again. */
|
|
21
|
+
export const forgetLazy = (key: string) => {
|
|
22
|
+
resolvedComponents.delete(key);
|
|
23
|
+
inflightLoads.delete(key);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const hasPendingLazyLoads = () => inflightLoads.size > 0;
|
|
27
|
+
|
|
28
|
+
export const lazyCacheKey = (pattern: string, loader: LazyRouteLoader) =>
|
|
29
|
+
`${pattern}\u0000${String(loader)}`;
|
|
30
|
+
|
|
31
|
+
/** Load (once) and cache the component behind `loader`. */
|
|
32
|
+
export const loadLazy = (key: string, loader: LazyRouteLoader) => {
|
|
33
|
+
const cached = resolvedComponents.get(key);
|
|
34
|
+
if (cached) return Promise.resolve(cached);
|
|
35
|
+
const inflight = inflightLoads.get(key);
|
|
36
|
+
if (inflight) return inflight;
|
|
37
|
+
|
|
38
|
+
const load = loader()
|
|
39
|
+
.then((value) => {
|
|
40
|
+
const component = toLazyComponent(value);
|
|
41
|
+
if (!component) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
'A lazy <Route> loader must resolve to a Svelte component or to a module whose default export is one, e.g. `() => import("./Page.svelte")`.'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
resolvedComponents.set(key, component);
|
|
47
|
+
|
|
48
|
+
return component;
|
|
49
|
+
})
|
|
50
|
+
.finally(() => {
|
|
51
|
+
inflightLoads.delete(key);
|
|
52
|
+
});
|
|
53
|
+
inflightLoads.set(key, load);
|
|
54
|
+
|
|
55
|
+
return load;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** The component for `key` if it has already loaded — or, during the
|
|
59
|
+
* first hydration, the module the hydration entry loaded ahead of time
|
|
60
|
+
* for this route pattern. */
|
|
61
|
+
export const peekLazy = (key: string, pattern: string) => {
|
|
62
|
+
const cached = resolvedComponents.get(key);
|
|
63
|
+
if (cached) return cached;
|
|
64
|
+
const seeded = readSeededLazy(pattern);
|
|
65
|
+
if (seeded) resolvedComponents.set(key, seeded);
|
|
66
|
+
|
|
67
|
+
return seeded;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** Resolves once every lazy route load in flight has settled, or after a
|
|
71
|
+
* few seconds — scroll restoration waits on this so it doesn't restore a
|
|
72
|
+
* position into content that hasn't rendered yet. */
|
|
73
|
+
export const whenLazySettled = async () => {
|
|
74
|
+
if (inflightLoads.size === 0) return;
|
|
75
|
+
const settled = Promise.allSettled(Array.from(inflightLoads.values()));
|
|
76
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
77
|
+
const timeout = new Promise<void>((resolve) => {
|
|
78
|
+
timer = setTimeout(resolve, LAZY_SETTLE_TIMEOUT_MS);
|
|
79
|
+
});
|
|
80
|
+
await Promise.race([settled, timeout]);
|
|
81
|
+
clearTimeout(timer);
|
|
82
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
|
|
3
|
+
/* Pure helpers shared by the router (browser + server render) and the
|
|
4
|
+
* Svelte page handler: turn whatever a lazy route's loader resolved to
|
|
5
|
+
* into a component, and read the source id the AbsoluteJS Svelte compiler
|
|
6
|
+
* stamps on every compiled component module. No module state lives here,
|
|
7
|
+
* so it is safe to bundle more than once. */
|
|
8
|
+
|
|
9
|
+
export type LazyComponent = Component<Record<string, unknown>>;
|
|
10
|
+
|
|
11
|
+
/** `globalThis` record of `route pattern → module` that the hydration
|
|
12
|
+
* entry fills before it hydrates a page whose server render included lazy
|
|
13
|
+
* routes. Compiled client modules fill a sibling registry,
|
|
14
|
+
* `Symbol.for('absolutejs.svelte.lazy-modules')`, of `source id →
|
|
15
|
+
* () => import(chunk)` for every `.svelte` file they import dynamically
|
|
16
|
+
* (see {@link lazyModuleRegistration}). */
|
|
17
|
+
export const LAZY_RESOLVED_KEY = Symbol.for('absolutejs.svelte.lazy-resolved');
|
|
18
|
+
|
|
19
|
+
/** Export (and component property) the Svelte compiler adds to every
|
|
20
|
+
* compiled component: the component's source path, relative to the
|
|
21
|
+
* project root. The server records it for each lazy route it rendered so
|
|
22
|
+
* the browser can load the same module before hydrating. */
|
|
23
|
+
export const LAZY_SOURCE_EXPORT = '__ABSOLUTE_SVELTE_SOURCE__';
|
|
24
|
+
|
|
25
|
+
const isRecord = (value: unknown): value is Record<string | symbol, unknown> =>
|
|
26
|
+
typeof value === 'object' && value !== null;
|
|
27
|
+
|
|
28
|
+
const isComponent = (value: unknown): value is LazyComponent =>
|
|
29
|
+
typeof value === 'function';
|
|
30
|
+
|
|
31
|
+
/** The component a loader resolved to: a module namespace's `default`, or
|
|
32
|
+
* the value itself when the loader already unwrapped it. */
|
|
33
|
+
export const toLazyComponent = (value: unknown) => {
|
|
34
|
+
if (isComponent(value)) return value;
|
|
35
|
+
if (!isRecord(value)) return undefined;
|
|
36
|
+
const fallback = value.default;
|
|
37
|
+
|
|
38
|
+
return isComponent(fallback) ? fallback : undefined;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const readSourceFrom = (value: unknown) => {
|
|
42
|
+
if (!isRecord(value) && !isComponent(value)) return undefined;
|
|
43
|
+
const source: unknown = Reflect.get(value, LAZY_SOURCE_EXPORT);
|
|
44
|
+
|
|
45
|
+
return typeof source === 'string' ? source : undefined;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** The statement a compiled client module appends for each `.svelte` file
|
|
49
|
+
* it imports dynamically, registering `source id → () => import(chunk)`,
|
|
50
|
+
* so the hydration entry can load the module a lazy route rendered on the
|
|
51
|
+
* server before it hydrates. Used by the build (compileSvelte) and the dev
|
|
52
|
+
* module server. */
|
|
53
|
+
export const lazyModuleRegistration = (sourceId: string, specifier: string) =>
|
|
54
|
+
`\n;(globalThis[Symbol.for("absolutejs.svelte.lazy-modules")] ??= new Map()).set(${JSON.stringify(sourceId)}, () => import(${JSON.stringify(specifier)}));`;
|
|
55
|
+
|
|
56
|
+
/** The compiler-stamped source id of a loader result, from the module
|
|
57
|
+
* namespace or from the component itself. */
|
|
58
|
+
export const readLazySource = (value: unknown) => {
|
|
59
|
+
const fromValue = readSourceFrom(value);
|
|
60
|
+
if (fromValue !== undefined) return fromValue;
|
|
61
|
+
if (!isRecord(value)) return undefined;
|
|
62
|
+
|
|
63
|
+
return readSourceFrom(value.default);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** The module the hydration entry loaded for `pattern` before hydrating,
|
|
67
|
+
* if any. */
|
|
68
|
+
export const readSeededLazy = (pattern: string) => {
|
|
69
|
+
if (typeof window === 'undefined') return undefined;
|
|
70
|
+
const seeded: unknown = Reflect.get(globalThis, LAZY_RESOLVED_KEY);
|
|
71
|
+
if (!isRecord(seeded)) return undefined;
|
|
72
|
+
|
|
73
|
+
return toLazyComponent(seeded[pattern]);
|
|
74
|
+
};
|