From cb3060a30a10ea973e2dc5ba34751adffd7cf618 Mon Sep 17 00:00:00 2001 From: Nic Polumeyv <162764842+Nic-Polumeyv@users.noreply.github.com> Date: Fri, 24 Jul 2026 01:22:52 -0400 Subject: [PATCH] chore: deduplicate request hashing for serialized fetch responses --- .changeset/eighty-donuts-argue.md | 5 +++++ packages/kit/src/runtime/client/fetcher.js | 17 ++++----------- .../src/runtime/server/page/serialize_data.js | 15 ++----------- packages/kit/src/utils/hash.js | 21 +++++++++++++++++++ 4 files changed, 32 insertions(+), 26 deletions(-) create mode 100644 .changeset/eighty-donuts-argue.md diff --git a/.changeset/eighty-donuts-argue.md b/.changeset/eighty-donuts-argue.md new file mode 100644 index 000000000000..bd7ce1305678 --- /dev/null +++ b/.changeset/eighty-donuts-argue.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: deduplicate request hashing for serialized fetch responses diff --git a/packages/kit/src/runtime/client/fetcher.js b/packages/kit/src/runtime/client/fetcher.js index a601f7a293f8..c8de38bc1442 100644 --- a/packages/kit/src/runtime/client/fetcher.js +++ b/packages/kit/src/runtime/client/fetcher.js @@ -1,6 +1,6 @@ import { BROWSER, DEV } from 'esm-env'; import { noop } from '../../utils/functions.js'; -import { hash } from '../../utils/hash.js'; +import { hash_request } from '../../utils/hash.js'; import { base64_decode } from '../utils.js'; let loading = 0; @@ -162,18 +162,9 @@ function build_selector(resource, opts) { let selector = `script[data-sveltekit-fetched][data-url=${url}]`; if (opts?.headers || opts?.body) { - /** @type {import('types').StrictBody[]} */ - const values = []; - - if (opts.headers) { - values.push([...new Headers(opts.headers)].join(',')); - } - - if (opts.body && (typeof opts.body === 'string' || ArrayBuffer.isView(opts.body))) { - values.push(opts.body); - } - - selector += `[data-hash="${hash(...values)}"]`; + const body = + typeof opts.body === 'string' || ArrayBuffer.isView(opts.body) ? opts.body : undefined; + selector += `[data-hash="${hash_request(opts.headers, body)}"]`; } return selector; diff --git a/packages/kit/src/runtime/server/page/serialize_data.js b/packages/kit/src/runtime/server/page/serialize_data.js index 7e9a64963c3b..51ca9e65c68b 100644 --- a/packages/kit/src/runtime/server/page/serialize_data.js +++ b/packages/kit/src/runtime/server/page/serialize_data.js @@ -1,5 +1,5 @@ import { escape_html } from '../../../utils/escape.js'; -import { hash } from '../../../utils/hash.js'; +import { hash_request } from '../../../utils/hash.js'; /** * Inside a script element, only `>> 0).toString(36); } + +/** + * Hash of the headers and body a `fetch` was called with. The server-side serializer and the + * client-side cache lookup must produce identical values for cached responses to be found. + * @param {HeadersInit | undefined} headers + * @param {import('types').StrictBody | null | undefined} body + */ +export function hash_request(headers, body) { + /** @type {import('types').StrictBody[]} */ + const values = []; + + if (headers) { + values.push([...new Headers(headers)].join(',')); + } + + if (body) { + values.push(body); + } + + return hash(...values); +}