Skip to content

[fix] tighten up navigation and invalidation logic #6924

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/silent-wasps-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

[fix] tighten up navigation and invalidation logic
46 changes: 23 additions & 23 deletions packages/kit/src/runtime/client/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,13 @@ export function create_client({ target, base, trailing_slash }) {
let current = {
branch: [],
error: null,
session_id: 0,
// @ts-ignore - we need the initial value to be null
url: null
};

let started = false;
let autoscroll = true;
let updating = false;
let session_id = 1;

/** @type {Promise<void> | null} */
let invalidating = null;
let force_invalidation = false;

/** @type {import('svelte').SvelteComponent} */
Expand Down Expand Up @@ -140,20 +135,20 @@ export function create_client({ target, base, trailing_slash }) {
/** @type {{}} */
let token;

function invalidate() {
if (!invalidating) {
const url = new URL(location.href);

invalidating = Promise.resolve().then(async () => {
const intent = get_navigation_intent(url, true);
await update(intent, url, []);

invalidating = null;
force_invalidation = false;
});
}
async function invalidate() {
// Accept all invalidations as they come, don't swallow any while another invalidation
// is running because subsequent invalidations may make earlier ones outdated.
const url = new URL(location.href);

return invalidating;
await Promise.resolve();
// Clear prefetch, it might be affected by the invalidation.
// Also solves an edge case where a prefetch is triggered, the navigation for it
// was then triggered and is still running while the invalidation kicks in,
// at which point the invalidation should take over and "win".
load_cache.promise = null;
load_cache.id = null;
const intent = get_navigation_intent(url, true);
await update(intent, url, []);
}

/**
Expand Down Expand Up @@ -208,7 +203,10 @@ export function create_client({ target, base, trailing_slash }) {
* @param {() => void} [callback]
*/
async function update(intent, url, redirect_chain, opts, callback) {
const current_token = (token = {});
// Do not update the token during a redirect, it belongs to a ongoing navigation; if a new one
// is started, it will should take precedence. If we don't do this, a race condition could
// make the old navigation the one that wins due to redirects.
const current_token = (token = redirect_chain.length ? token : {});
let navigation_result = intent && (await load_route(intent));

if (
Expand Down Expand Up @@ -241,8 +239,6 @@ export function create_client({ target, base, trailing_slash }) {
// abort if user navigated during update
if (token !== current_token) return false;

invalidated.length = 0;

if (navigation_result.type === 'redirect') {
if (redirect_chain.length > 10 || redirect_chain.includes(url.pathname)) {
navigation_result = await load_root_error_page({
Expand All @@ -262,6 +258,11 @@ export function create_client({ target, base, trailing_slash }) {
}
}

// reset invalidation only after a finished navigation. If there are redirects or
// additional invalidations, they should get the same invalidation treatment
invalidated.length = 0;
force_invalidation = false;

updating = true;

if (opts && opts.details) {
Expand Down Expand Up @@ -410,8 +411,7 @@ export function create_client({ target, base, trailing_slash }) {
params,
branch,
error,
route,
session_id
route
},
props: {
components: filtered.map((branch_node) => branch_node.node.component)
Expand Down
1 change: 0 additions & 1 deletion packages/kit/src/runtime/client/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,5 @@ export interface NavigationState {
error: App.PageError | null;
params: Record<string, string>;
route: CSRRoute | null;
session_id: number;
url: URL;
}