@@ -98,9 +98,37 @@ export const startRouter = ({ unmount, islands }) => {
9898 . map ( ( { href } ) => href )
9999 ) ;
100100
101- /** @type {Map<string, { page: Promise<import('./page.mjs').Page | null>, expires: number }> } */
101+ /**
102+ * @typedef {object } Entry A fetched page, kept for reuse.
103+ * @property {Promise<import('./page.mjs').Page | null> } page
104+ * @property {ReturnType<typeof setTimeout> } [expiry] - Drops the page once
105+ * it is too old to reuse
106+ */
107+
108+ /**
109+ * The pages fetched lately, by URL, oldest first. A page the host redirected
110+ * is kept under the URL it was fetched from as well.
111+ *
112+ * @type {Map<string, Entry> }
113+ */
102114 const pages = new Map ( ) ;
103115
116+ /**
117+ * Drops a page from memory, under every URL it is kept under.
118+ *
119+ * @param {Entry } entry
120+ */
121+ const forget = entry => {
122+ // A pending expiry would otherwise hold on to the page until it runs
123+ clearTimeout ( entry . expiry ) ;
124+
125+ for ( const [ href , kept ] of pages ) {
126+ if ( kept === entry ) {
127+ pages . delete ( href ) ;
128+ }
129+ }
130+ } ;
131+
104132 /**
105133 * Fetches a page, or reuses the copy fetched moments ago.
106134 *
@@ -111,34 +139,35 @@ export const startRouter = ({ unmount, islands }) => {
111139 const loadPage = href => {
112140 const cached = pages . get ( href ) ;
113141
114- if ( cached && cached . expires > Date . now ( ) ) {
142+ if ( cached ) {
115143 return cached . page ;
116144 }
117145
118- const page = fetchPage ( href ) ;
119- const entry = { page, expires : Date . now ( ) + ROUTER_PAGE_LIFETIME } ;
146+ /** @type { Entry } */
147+ const entry = { page : fetchPage ( href ) } ;
120148
121- pages . delete ( href ) ;
149+ entry . expiry = setTimeout ( forget , ROUTER_PAGE_LIFETIME , entry ) ;
122150 pages . set ( href , entry ) ;
123151
124- // Redirected pages take two entries (below)
125- while ( pages . size > ROUTER_MAX_PAGES ) {
126- pages . delete ( pages . keys ( ) . next ( ) . value ) ;
127- }
152+ // The limit counts pages, not the URLs they are kept under
153+ const kept = new Set ( pages . values ( ) ) ;
128154
129- page . then ( result => {
130- // A failure is not kept, so the next attempt fetches again
131- if ( ! result && pages . get ( href ) ?. page === page ) {
132- pages . delete ( href ) ;
133- }
155+ if ( kept . size > ROUTER_MAX_PAGES ) {
156+ forget ( kept . values ( ) . next ( ) . value ) ;
157+ }
134158
135- // Following the redirect (see the handler) then needs no second fetch
136- if ( result && result . url !== href ) {
159+ entry . page . then ( result => {
160+ if ( ! result ) {
161+ // A failure is not kept, so the next attempt fetches again
162+ forget ( entry ) ;
163+ } else if ( result . url !== href && pages . get ( href ) === entry ) {
164+ // Following the redirect (see the handler) then needs no second fetch
165+ pages . delete ( result . url ) ;
137166 pages . set ( result . url , entry ) ;
138167 }
139168 } ) ;
140169
141- return page ;
170+ return entry . page ;
142171 } ;
143172
144173 /**
0 commit comments