diff --git a/src/core/cache.js b/src/core/cache.js index 580c6ab1f..0750a938c 100644 --- a/src/core/cache.js +++ b/src/core/cache.js @@ -1,9 +1,8 @@ import { setMetaContent } from "../util" -import { SnapshotCache } from "./drive/snapshot_cache" export class Cache { clear() { - this.store.clear() + this.session.clearCache() } resetCacheControl() { @@ -18,18 +17,6 @@ export class Cache { this.#setCacheControl("no-preview") } - set store(store) { - if (typeof store === "string") { - SnapshotCache.setStore(store) - } else { - SnapshotCache.currentStore = store - } - } - - get store() { - return SnapshotCache.currentStore - } - #setCacheControl(value) { setMetaContent("turbo-cache-control", value) } diff --git a/src/core/drive/cache_stores/disk_store.js b/src/core/drive/cache_stores/disk_store.js deleted file mode 100644 index 285caa5d9..000000000 --- a/src/core/drive/cache_stores/disk_store.js +++ /dev/null @@ -1,64 +0,0 @@ -import { PageSnapshot } from "../page_snapshot" - -export class DiskStore { - _version = "v1" - - constructor() { - if (typeof caches === "undefined") { - throw new Error("windows.caches is undefined. CacheStore requires a secure context.") - } - - this.storage = this.openStorage() - } - - async has(location) { - const storage = await this.openStorage() - return (await storage.match(location)) !== undefined - } - - async get(location) { - const storage = await this.openStorage() - const response = await storage.match(location) - - if (response && response.ok) { - const html = await response.text() - return PageSnapshot.fromHTMLString(html) - } - } - - async put(location, snapshot) { - const storage = await this.openStorage() - - const response = new Response(snapshot.html, { - status: 200, - statusText: "OK", - headers: { - "Content-Type": "text/html" - } - }) - await storage.put(location, response) - return snapshot - } - - async clear() { - const storage = await this.openStorage() - const keys = await storage.keys() - await Promise.all(keys.map((key) => storage.delete(key))) - } - - openStorage() { - this.storage ||= caches.open(`turbo-${this.version}`) - return this.storage - } - - set version(value) { - if (value !== this._version) { - this._version = value - this.storage ||= caches.open(`turbo-${this.version}`) - } - } - - get version() { - return this._version - } -} diff --git a/src/core/drive/cache_stores/memory_store.js b/src/core/drive/cache_stores/memory_store.js deleted file mode 100644 index 3ec8ae0b1..000000000 --- a/src/core/drive/cache_stores/memory_store.js +++ /dev/null @@ -1,56 +0,0 @@ -import { toCacheKey } from "../../url" - -export class MemoryStore { - keys = [] - snapshots = {} - - constructor(size) { - this.size = size - } - - async has(location) { - return toCacheKey(location) in this.snapshots - } - - async get(location) { - if (await this.has(location)) { - const snapshot = this.read(location) - this.touch(location) - return snapshot - } - } - - async put(location, snapshot) { - this.write(location, snapshot) - this.touch(location) - return snapshot - } - - async clear() { - this.snapshots = {} - } - - // Private - - read(location) { - return this.snapshots[toCacheKey(location)] - } - - write(location, snapshot) { - this.snapshots[toCacheKey(location)] = snapshot - } - - touch(location) { - const key = toCacheKey(location) - const index = this.keys.indexOf(key) - if (index > -1) this.keys.splice(index, 1) - this.keys.unshift(key) - this.trim() - } - - trim() { - for (const key of this.keys.splice(this.size)) { - delete this.snapshots[key] - } - } -} diff --git a/src/core/drive/page_snapshot.js b/src/core/drive/page_snapshot.js index 599749d98..8b5a6e9d1 100644 --- a/src/core/drive/page_snapshot.js +++ b/src/core/drive/page_snapshot.js @@ -45,10 +45,6 @@ export class PageSnapshot extends Snapshot { return this.documentElement.getAttribute("lang") } - get html() { - return `${this.headElement.outerHTML}\n\n${this.element.outerHTML}` - } - get headElement() { return this.headSnapshot.element } diff --git a/src/core/drive/page_view.js b/src/core/drive/page_view.js index 09a7299c6..1b95bb1d1 100644 --- a/src/core/drive/page_view.js +++ b/src/core/drive/page_view.js @@ -6,7 +6,7 @@ import { PageSnapshot } from "./page_snapshot" import { SnapshotCache } from "./snapshot_cache" export class PageView extends View { - snapshotCache = new SnapshotCache() + snapshotCache = new SnapshotCache(10) lastRenderedLocation = new URL(location.href) forceReloaded = false @@ -32,10 +32,6 @@ export class PageView extends View { return this.render(renderer) } - setCacheStore(cacheName) { - SnapshotCache.setStore(cacheName) - } - clearSnapshotCache() { this.snapshotCache.clear() } diff --git a/src/core/drive/preloader.js b/src/core/drive/preloader.js index b971cea19..23871a530 100644 --- a/src/core/drive/preloader.js +++ b/src/core/drive/preloader.js @@ -30,7 +30,9 @@ export class Preloader { async preloadURL(link) { const location = new URL(link.href) - if (await this.snapshotCache.has(location)) return + if (this.snapshotCache.has(location)) { + return + } try { const response = await fetch(location.toString(), { headers: { "Sec-Purpose": "prefetch", Accept: "text/html" } }) diff --git a/src/core/drive/snapshot_cache.js b/src/core/drive/snapshot_cache.js index 8e8c53c02..6ed37e8fd 100644 --- a/src/core/drive/snapshot_cache.js +++ b/src/core/drive/snapshot_cache.js @@ -1,35 +1,56 @@ -import { DiskStore } from "./cache_stores/disk_store" -import { MemoryStore } from "./cache_stores/memory_store" +import { toCacheKey } from "../url" export class SnapshotCache { - static currentStore = new MemoryStore(10) - - static setStore(storeName) { - switch (storeName) { - case "memory": - SnapshotCache.currentStore = new MemoryStore(10) - break - case "disk": - SnapshotCache.currentStore = new DiskStore() - break - default: - throw new Error(`Invalid store name: ${storeName}`) - } + keys = [] + snapshots = {} + + constructor(size) { + this.size = size } has(location) { - return SnapshotCache.currentStore.has(location) + return toCacheKey(location) in this.snapshots } get(location) { - return SnapshotCache.currentStore.get(location) + if (this.has(location)) { + const snapshot = this.read(location) + this.touch(location) + return snapshot + } } put(location, snapshot) { - return SnapshotCache.currentStore.put(location, snapshot) + this.write(location, snapshot) + this.touch(location) + return snapshot } clear() { - return SnapshotCache.currentStore.clear() + this.snapshots = {} + } + + // Private + + read(location) { + return this.snapshots[toCacheKey(location)] + } + + write(location, snapshot) { + this.snapshots[toCacheKey(location)] = snapshot + } + + touch(location) { + const key = toCacheKey(location) + const index = this.keys.indexOf(key) + if (index > -1) this.keys.splice(index, 1) + this.keys.unshift(key) + this.trim() + } + + trim() { + for (const key of this.keys.splice(this.size)) { + delete this.snapshots[key] + } } } diff --git a/src/core/drive/visit.js b/src/core/drive/visit.js index d1929b03e..7fc494c19 100644 --- a/src/core/drive/visit.js +++ b/src/core/drive/visit.js @@ -215,8 +215,8 @@ export class Visit { } } - async getCachedSnapshot() { - const snapshot = (await this.view.getCachedSnapshotForLocation(this.location)) || this.getPreloadedSnapshot() + getCachedSnapshot() { + const snapshot = this.view.getCachedSnapshotForLocation(this.location) || this.getPreloadedSnapshot() if (snapshot && (!getAnchor(this.location) || snapshot.hasAnchor(getAnchor(this.location)))) { if (this.action == "restore" || snapshot.isPreviewable) { @@ -235,8 +235,8 @@ export class Visit { return this.getCachedSnapshot() != null } - async loadCachedSnapshot() { - const snapshot = await this.getCachedSnapshot() + loadCachedSnapshot() { + const snapshot = this.getCachedSnapshot() if (snapshot) { const isPreview = this.shouldIssueRequest() this.render(async () => { diff --git a/src/tests/fixtures/disk_cache.html b/src/tests/fixtures/disk_cache.html deleted file mode 100644 index f8ba78a64..000000000 --- a/src/tests/fixtures/disk_cache.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - Turbo - - - - - -

Cached pages:

- - -

Links:

- - - - - diff --git a/src/tests/functional/disk_cache_tests.js b/src/tests/functional/disk_cache_tests.js deleted file mode 100644 index ca9bd6ec4..000000000 --- a/src/tests/functional/disk_cache_tests.js +++ /dev/null @@ -1,58 +0,0 @@ -import { test, expect } from "@playwright/test" -import { nextBody } from "../helpers/page" - -const path = "/src/tests/fixtures/disk_cache.html" - -test.beforeEach(async ({ page }) => { - await page.goto(path) -}) - -test("stores pages in the disk cache", async ({ page }) => { - await assertCachedURLs(page, []) - - page.click("#second-link") - await nextBody(page) - - await assertCachedURLs(page, ["http://localhost:9000/src/tests/fixtures/disk_cache.html"]) - - page.click("#third-link") - await nextBody(page) - - await assertCachedURLs(page, [ - "http://localhost:9000/src/tests/fixtures/disk_cache.html", - "http://localhost:9000/src/tests/fixtures/disk_cache.html?page=2" - ]) - - // Cache persists across reloads - await page.reload() - - await assertCachedURLs(page, [ - "http://localhost:9000/src/tests/fixtures/disk_cache.html", - "http://localhost:9000/src/tests/fixtures/disk_cache.html?page=2" - ]) -}) - -test("can clear the disk cache", async ({ page }) => { - page.click("#second-link") - await nextBody(page) - - await assertCachedURLs(page, ["http://localhost:9000/src/tests/fixtures/disk_cache.html"]) - - page.click("#clear-cache") - await assertCachedURLs(page, []) - - await page.reload() - await assertCachedURLs(page, []) -}) - -const assertCachedURLs = async (page, urls) => { - if (urls.length == 0) { - await expect(page.locator("#caches")).toBeEmpty() - } else { - await Promise.all( - urls.map((url) => { - return expect(page.locator("#caches")).toContainText(url) - }) - ) - } -} diff --git a/src/tests/functional/preloader_tests.js b/src/tests/functional/preloader_tests.js index 1337fa5fd..83293342a 100644 --- a/src/tests/functional/preloader_tests.js +++ b/src/tests/functional/preloader_tests.js @@ -8,11 +8,11 @@ test("preloads snapshot on initial load", async ({ page }) => { await nextBeat() assert.ok( - await page.evaluate(async () => { - const preloadedUrl = new URL("http://localhost:9000/src/tests/fixtures/preloaded.html") - const cache = window.Turbo.session.preloader.snapshotCache + await page.evaluate(() => { + const preloadedUrl = "http://localhost:9000/src/tests/fixtures/preloaded.html" + const cache = window.Turbo.session.preloader.snapshotCache.snapshots - return await cache.has(preloadedUrl) + return preloadedUrl in cache }) ) }) @@ -27,11 +27,11 @@ test("preloads snapshot on page visit", async ({ page }) => { await nextBeat() assert.ok( - await page.evaluate(async () => { - const preloadedUrl = new URL("http://localhost:9000/src/tests/fixtures/preloaded.html") - const cache = window.Turbo.session.preloader.snapshotCache + await page.evaluate(() => { + const preloadedUrl = "http://localhost:9000/src/tests/fixtures/preloaded.html" + const cache = window.Turbo.session.preloader.snapshotCache.snapshots - return await cache.has(preloadedUrl) + return preloadedUrl in cache }) ) }) @@ -43,11 +43,11 @@ test("navigates to preloaded snapshot from frame", async ({ page }) => { await nextBeat() assert.ok( - await page.evaluate(async () => { - const preloadedUrl = new URL("http://localhost:9000/src/tests/fixtures/preloaded.html") - const cache = window.Turbo.session.preloader.snapshotCache + await page.evaluate(() => { + const preloadedUrl = "http://localhost:9000/src/tests/fixtures/preloaded.html" + const cache = window.Turbo.session.preloader.snapshotCache.snapshots - return await cache.has(preloadedUrl) + return preloadedUrl in cache }) ) })