From 8ea3ee8c62aed631d5c4bbc0870d491059ed7d03 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 15 Jun 2026 10:52:21 -0400
Subject: [PATCH 01/16] chore(deps-dev): bump esbuild from 0.25.11 to 0.28.1
(#18427)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.25.11 to
0.28.1.
Release notes
Sourced from esbuild's
releases .
v0.28.1
Disallow \ in local development server HTTP requests (GHSA-g7r4-m6w7-qqqr )
This release fixes a security issue where HTTP requests to esbuild's
local development server could traverse outside of the serve directory
on Windows using a \ backslash character. It happened due
to the use of Go's path.Clean() function, which only
handles Unix-style / characters. HTTP requests with paths
containing \ are no longer allowed.
Thanks to @dellalibera for
reporting this issue.
Add integrity checks to the Deno API (GHSA-gv7w-rqvm-qjhr )
The previous release of esbuild added integrity checks to esbuild's
npm install script. This release also adds integrity checks to esbuild's
Deno install script. Now esbuild's Deno API will also fail with an error
if the downloaded esbuild binary contains something other than the
expected content.
Note that esbuild's Deno API installs from
registry.npmjs.org by default, but allows the
NPM_CONFIG_REGISTRY environment variable to override this
with a custom package registry. This change means that the esbuild
executable served by NPM_CONFIG_REGISTRY must now match the
expected content.
Thanks to @sondt99 for
reporting this issue.
Avoid inlining using and await using
declarations (#4482 )
Previously esbuild's minifier sometimes incorrectly inlined
using and await using declarations into
subsequent uses of that declaration, which then fails to dispose of the
resource correctly. This bug happened because inlining was done for
let and const declarations by avoiding doing
it for var declarations, which no longer worked when more
declaration types were added. Here's an example:
// Original code
{
using x = new Resource()
x.activate()
}
// Old output (with --minify)
new Resource().activate();
// New output (with --minify)
{using e=new Resource;e.activate()}
Fix module evaluation when an error is thrown (#4461 ,
#4467 )
If an error is thrown during module evaluation, esbuild previously
didn't preserve the state of the module for subsequent module
references. This was observable if import() or
require() is used to import a module multiple times. The
thrown error is supposed to be thrown by every call to
import() or require(), not just the first.
With this release, esbuild will now throw the same error every time you
call import() or require() on a module that
throws during its evaluation.
Fix some edge cases around the new operator (#4477 )
Previously esbuild incorrectly printed certain edge cases involving
complex expressions inside the target of a new expression
(specifically an optional chain and/or a tagged template literal). The
generated code for the new target was not correctly wrapped
with parentheses, and either contained a syntax error or had different
semantics. These edge cases have been fixed so that they now correctly
wrap the new target in parentheses. Here is an example of
some affected code:
// Original code
new (foo()`bar`)()
new (foo()?.bar)()
// Old output
new foo()bar();
new (foo())?.bar();
... (truncated)
Changelog
Sourced from esbuild's
changelog .
Changelog: 2025
This changelog documents all esbuild versions published in the year
2025 (versions 0.25.0 through 0.27.2).
0.27.2
Allow import path specifiers starting with #/ (#4361 )
Previously the specification for package.json disallowed
import path specifiers starting with #/, but this
restriction has recently
been relaxed and support for it is being added across the JavaScript
ecosystem. One use case is using it for a wildcard pattern such as
mapping #/* to ./src/* (previously you had to
use another character such as #_* instead, which was more
confusing). There is some more context in nodejs/node#49182 .
This change was contributed by @hybrist .
Automatically add the -webkit-mask prefix (#4357 ,
#4358 )
This release automatically adds the -webkit- vendor
prefix for the mask
CSS shorthand property:
/* Original code */
main {
mask: url(x.png) center/5rem no-repeat
}
/* Old output (with --target=chrome110) */
main {
mask: url(x.png) center/5rem no-repeat;
}
/* New output (with --target=chrome110) */
main {
-webkit-mask: url(x.png) center/5rem no-repeat;
mask: url(x.png) center/5rem no-repeat;
}
This change was contributed by @BPJEnnova .
Additional minification of switch statements (#4176 ,
#4359 )
This release contains additional minification patterns for reducing
switch statements. Here is an example:
// Original code
switch (x) {
case 0:
foo()
break
case 1:
default:
bar()
}
... (truncated)
Commits
Maintainer changes
This version was pushed to npm by GitHub Actions , a new
releaser for esbuild since your current version.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/sveltejs/svelte/network/alerts).
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
packages/svelte/package.json | 2 +-
pnpm-lock.yaml | 342 ++++++++++++++++++-----------------
2 files changed, 178 insertions(+), 166 deletions(-)
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index 6bca1bd4a8..eeca8e032e 100644
--- a/packages/svelte/package.json
+++ b/packages/svelte/package.json
@@ -161,7 +161,7 @@
"@types/node": "^20.11.5",
"baseline-browser-mapping": "^2.10.32",
"dts-buddy": "^0.5.5",
- "esbuild": "^0.25.10",
+ "esbuild": "^0.28.1",
"rollup": "^4.59.0",
"source-map": "^0.7.4",
"tinyglobby": "^0.2.12",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a619c24ad6..e86c927efe 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -148,8 +148,8 @@ importers:
specifier: ^0.5.5
version: 0.5.5(typescript@5.5.4)
esbuild:
- specifier: ^0.25.10
- version: 0.25.11
+ specifier: ^0.28.1
+ version: 0.28.1
rollup:
specifier: ^4.59.0
version: 4.60.1
@@ -285,23 +285,17 @@ packages:
'@changesets/write@0.4.0':
resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==}
- '@esbuild/aix-ppc64@0.25.11':
- resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [aix]
-
'@esbuild/aix-ppc64@0.27.7':
resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
- '@esbuild/android-arm64@0.25.11':
- resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==}
+ '@esbuild/aix-ppc64@0.28.1':
+ resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
engines: {node: '>=18'}
- cpu: [arm64]
- os: [android]
+ cpu: [ppc64]
+ os: [aix]
'@esbuild/android-arm64@0.27.7':
resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
@@ -309,10 +303,10 @@ packages:
cpu: [arm64]
os: [android]
- '@esbuild/android-arm@0.25.11':
- resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==}
+ '@esbuild/android-arm64@0.28.1':
+ resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==}
engines: {node: '>=18'}
- cpu: [arm]
+ cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.27.7':
@@ -321,10 +315,10 @@ packages:
cpu: [arm]
os: [android]
- '@esbuild/android-x64@0.25.11':
- resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==}
+ '@esbuild/android-arm@0.28.1':
+ resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==}
engines: {node: '>=18'}
- cpu: [x64]
+ cpu: [arm]
os: [android]
'@esbuild/android-x64@0.27.7':
@@ -333,11 +327,11 @@ packages:
cpu: [x64]
os: [android]
- '@esbuild/darwin-arm64@0.25.11':
- resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==}
+ '@esbuild/android-x64@0.28.1':
+ resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==}
engines: {node: '>=18'}
- cpu: [arm64]
- os: [darwin]
+ cpu: [x64]
+ os: [android]
'@esbuild/darwin-arm64@0.27.7':
resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
@@ -345,10 +339,10 @@ packages:
cpu: [arm64]
os: [darwin]
- '@esbuild/darwin-x64@0.25.11':
- resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==}
+ '@esbuild/darwin-arm64@0.28.1':
+ resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==}
engines: {node: '>=18'}
- cpu: [x64]
+ cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.27.7':
@@ -357,11 +351,11 @@ packages:
cpu: [x64]
os: [darwin]
- '@esbuild/freebsd-arm64@0.25.11':
- resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==}
+ '@esbuild/darwin-x64@0.28.1':
+ resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==}
engines: {node: '>=18'}
- cpu: [arm64]
- os: [freebsd]
+ cpu: [x64]
+ os: [darwin]
'@esbuild/freebsd-arm64@0.27.7':
resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
@@ -369,10 +363,10 @@ packages:
cpu: [arm64]
os: [freebsd]
- '@esbuild/freebsd-x64@0.25.11':
- resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==}
+ '@esbuild/freebsd-arm64@0.28.1':
+ resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==}
engines: {node: '>=18'}
- cpu: [x64]
+ cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.27.7':
@@ -381,11 +375,11 @@ packages:
cpu: [x64]
os: [freebsd]
- '@esbuild/linux-arm64@0.25.11':
- resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==}
+ '@esbuild/freebsd-x64@0.28.1':
+ resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==}
engines: {node: '>=18'}
- cpu: [arm64]
- os: [linux]
+ cpu: [x64]
+ os: [freebsd]
'@esbuild/linux-arm64@0.27.7':
resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
@@ -393,10 +387,10 @@ packages:
cpu: [arm64]
os: [linux]
- '@esbuild/linux-arm@0.25.11':
- resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==}
+ '@esbuild/linux-arm64@0.28.1':
+ resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==}
engines: {node: '>=18'}
- cpu: [arm]
+ cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.27.7':
@@ -405,10 +399,10 @@ packages:
cpu: [arm]
os: [linux]
- '@esbuild/linux-ia32@0.25.11':
- resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==}
+ '@esbuild/linux-arm@0.28.1':
+ resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==}
engines: {node: '>=18'}
- cpu: [ia32]
+ cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.27.7':
@@ -417,10 +411,10 @@ packages:
cpu: [ia32]
os: [linux]
- '@esbuild/linux-loong64@0.25.11':
- resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==}
+ '@esbuild/linux-ia32@0.28.1':
+ resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==}
engines: {node: '>=18'}
- cpu: [loong64]
+ cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.27.7':
@@ -429,10 +423,10 @@ packages:
cpu: [loong64]
os: [linux]
- '@esbuild/linux-mips64el@0.25.11':
- resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==}
+ '@esbuild/linux-loong64@0.28.1':
+ resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==}
engines: {node: '>=18'}
- cpu: [mips64el]
+ cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.27.7':
@@ -441,10 +435,10 @@ packages:
cpu: [mips64el]
os: [linux]
- '@esbuild/linux-ppc64@0.25.11':
- resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==}
+ '@esbuild/linux-mips64el@0.28.1':
+ resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==}
engines: {node: '>=18'}
- cpu: [ppc64]
+ cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.27.7':
@@ -453,10 +447,10 @@ packages:
cpu: [ppc64]
os: [linux]
- '@esbuild/linux-riscv64@0.25.11':
- resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==}
+ '@esbuild/linux-ppc64@0.28.1':
+ resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==}
engines: {node: '>=18'}
- cpu: [riscv64]
+ cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.27.7':
@@ -465,10 +459,10 @@ packages:
cpu: [riscv64]
os: [linux]
- '@esbuild/linux-s390x@0.25.11':
- resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==}
+ '@esbuild/linux-riscv64@0.28.1':
+ resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==}
engines: {node: '>=18'}
- cpu: [s390x]
+ cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.27.7':
@@ -477,10 +471,10 @@ packages:
cpu: [s390x]
os: [linux]
- '@esbuild/linux-x64@0.25.11':
- resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==}
+ '@esbuild/linux-s390x@0.28.1':
+ resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==}
engines: {node: '>=18'}
- cpu: [x64]
+ cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.27.7':
@@ -489,11 +483,11 @@ packages:
cpu: [x64]
os: [linux]
- '@esbuild/netbsd-arm64@0.25.11':
- resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==}
+ '@esbuild/linux-x64@0.28.1':
+ resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==}
engines: {node: '>=18'}
- cpu: [arm64]
- os: [netbsd]
+ cpu: [x64]
+ os: [linux]
'@esbuild/netbsd-arm64@0.27.7':
resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
@@ -501,10 +495,10 @@ packages:
cpu: [arm64]
os: [netbsd]
- '@esbuild/netbsd-x64@0.25.11':
- resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==}
+ '@esbuild/netbsd-arm64@0.28.1':
+ resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==}
engines: {node: '>=18'}
- cpu: [x64]
+ cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.27.7':
@@ -513,11 +507,11 @@ packages:
cpu: [x64]
os: [netbsd]
- '@esbuild/openbsd-arm64@0.25.11':
- resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==}
+ '@esbuild/netbsd-x64@0.28.1':
+ resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==}
engines: {node: '>=18'}
- cpu: [arm64]
- os: [openbsd]
+ cpu: [x64]
+ os: [netbsd]
'@esbuild/openbsd-arm64@0.27.7':
resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
@@ -525,10 +519,10 @@ packages:
cpu: [arm64]
os: [openbsd]
- '@esbuild/openbsd-x64@0.25.11':
- resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==}
+ '@esbuild/openbsd-arm64@0.28.1':
+ resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==}
engines: {node: '>=18'}
- cpu: [x64]
+ cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.27.7':
@@ -537,11 +531,11 @@ packages:
cpu: [x64]
os: [openbsd]
- '@esbuild/openharmony-arm64@0.25.11':
- resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==}
+ '@esbuild/openbsd-x64@0.28.1':
+ resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==}
engines: {node: '>=18'}
- cpu: [arm64]
- os: [openharmony]
+ cpu: [x64]
+ os: [openbsd]
'@esbuild/openharmony-arm64@0.27.7':
resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
@@ -549,11 +543,11 @@ packages:
cpu: [arm64]
os: [openharmony]
- '@esbuild/sunos-x64@0.25.11':
- resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==}
+ '@esbuild/openharmony-arm64@0.28.1':
+ resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==}
engines: {node: '>=18'}
- cpu: [x64]
- os: [sunos]
+ cpu: [arm64]
+ os: [openharmony]
'@esbuild/sunos-x64@0.27.7':
resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
@@ -561,11 +555,11 @@ packages:
cpu: [x64]
os: [sunos]
- '@esbuild/win32-arm64@0.25.11':
- resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==}
+ '@esbuild/sunos-x64@0.28.1':
+ resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==}
engines: {node: '>=18'}
- cpu: [arm64]
- os: [win32]
+ cpu: [x64]
+ os: [sunos]
'@esbuild/win32-arm64@0.27.7':
resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
@@ -573,10 +567,10 @@ packages:
cpu: [arm64]
os: [win32]
- '@esbuild/win32-ia32@0.25.11':
- resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==}
+ '@esbuild/win32-arm64@0.28.1':
+ resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==}
engines: {node: '>=18'}
- cpu: [ia32]
+ cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.27.7':
@@ -585,10 +579,10 @@ packages:
cpu: [ia32]
os: [win32]
- '@esbuild/win32-x64@0.25.11':
- resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==}
+ '@esbuild/win32-ia32@0.28.1':
+ resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==}
engines: {node: '>=18'}
- cpu: [x64]
+ cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.27.7':
@@ -597,6 +591,12 @@ packages:
cpu: [x64]
os: [win32]
+ '@esbuild/win32-x64@0.28.1':
+ resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
'@eslint-community/eslint-utils@4.9.1':
resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -936,6 +936,7 @@ packages:
'@svitejs/changesets-changelog-github-compact@1.1.0':
resolution: {integrity: sha512-qhUGGDHcpbY2zpjW3SwqchuW8J/5EzlPFud7xNntHKA7f3a/mx5+g+ruJKFHSAiVZYo30PALt+AyhmPUNKH/Og==}
engines: {node: ^14.13.1 || ^16.0.0 || >=18}
+ deprecated: unmaintained
'@types/aria-query@5.0.4':
resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
@@ -1090,6 +1091,11 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
+ acorn@8.17.0:
+ resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
agent-base@7.1.1:
resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==}
engines: {node: '>= 14'}
@@ -1287,8 +1293,8 @@ packages:
peerDependencies:
typescript: '>=5.0.4 <5.8'
- enhanced-resolve@5.22.1:
- resolution: {integrity: sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==}
+ enhanced-resolve@5.24.0:
+ resolution: {integrity: sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==}
engines: {node: '>=10.13.0'}
enquirer@2.4.1:
@@ -1305,13 +1311,13 @@ packages:
es-module-lexer@2.1.0:
resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==}
- esbuild@0.25.11:
- resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==}
+ esbuild@0.27.7:
+ resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
engines: {node: '>=18'}
hasBin: true
- esbuild@0.27.7:
- resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
+ esbuild@0.28.1:
+ resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
engines: {node: '>=18'}
hasBin: true
@@ -2095,8 +2101,8 @@ packages:
engines: {node: '>=10'}
hasBin: true
- semver@7.8.1:
- resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==}
+ semver@7.8.4:
+ resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==}
engines: {node: '>=10'}
hasBin: true
@@ -2663,162 +2669,162 @@ snapshots:
human-id: 4.1.1
prettier: 2.8.8
- '@esbuild/aix-ppc64@0.25.11':
- optional: true
-
'@esbuild/aix-ppc64@0.27.7':
optional: true
- '@esbuild/android-arm64@0.25.11':
+ '@esbuild/aix-ppc64@0.28.1':
optional: true
'@esbuild/android-arm64@0.27.7':
optional: true
- '@esbuild/android-arm@0.25.11':
+ '@esbuild/android-arm64@0.28.1':
optional: true
'@esbuild/android-arm@0.27.7':
optional: true
- '@esbuild/android-x64@0.25.11':
+ '@esbuild/android-arm@0.28.1':
optional: true
'@esbuild/android-x64@0.27.7':
optional: true
- '@esbuild/darwin-arm64@0.25.11':
+ '@esbuild/android-x64@0.28.1':
optional: true
'@esbuild/darwin-arm64@0.27.7':
optional: true
- '@esbuild/darwin-x64@0.25.11':
+ '@esbuild/darwin-arm64@0.28.1':
optional: true
'@esbuild/darwin-x64@0.27.7':
optional: true
- '@esbuild/freebsd-arm64@0.25.11':
+ '@esbuild/darwin-x64@0.28.1':
optional: true
'@esbuild/freebsd-arm64@0.27.7':
optional: true
- '@esbuild/freebsd-x64@0.25.11':
+ '@esbuild/freebsd-arm64@0.28.1':
optional: true
'@esbuild/freebsd-x64@0.27.7':
optional: true
- '@esbuild/linux-arm64@0.25.11':
+ '@esbuild/freebsd-x64@0.28.1':
optional: true
'@esbuild/linux-arm64@0.27.7':
optional: true
- '@esbuild/linux-arm@0.25.11':
+ '@esbuild/linux-arm64@0.28.1':
optional: true
'@esbuild/linux-arm@0.27.7':
optional: true
- '@esbuild/linux-ia32@0.25.11':
+ '@esbuild/linux-arm@0.28.1':
optional: true
'@esbuild/linux-ia32@0.27.7':
optional: true
- '@esbuild/linux-loong64@0.25.11':
+ '@esbuild/linux-ia32@0.28.1':
optional: true
'@esbuild/linux-loong64@0.27.7':
optional: true
- '@esbuild/linux-mips64el@0.25.11':
+ '@esbuild/linux-loong64@0.28.1':
optional: true
'@esbuild/linux-mips64el@0.27.7':
optional: true
- '@esbuild/linux-ppc64@0.25.11':
+ '@esbuild/linux-mips64el@0.28.1':
optional: true
'@esbuild/linux-ppc64@0.27.7':
optional: true
- '@esbuild/linux-riscv64@0.25.11':
+ '@esbuild/linux-ppc64@0.28.1':
optional: true
'@esbuild/linux-riscv64@0.27.7':
optional: true
- '@esbuild/linux-s390x@0.25.11':
+ '@esbuild/linux-riscv64@0.28.1':
optional: true
'@esbuild/linux-s390x@0.27.7':
optional: true
- '@esbuild/linux-x64@0.25.11':
+ '@esbuild/linux-s390x@0.28.1':
optional: true
'@esbuild/linux-x64@0.27.7':
optional: true
- '@esbuild/netbsd-arm64@0.25.11':
+ '@esbuild/linux-x64@0.28.1':
optional: true
'@esbuild/netbsd-arm64@0.27.7':
optional: true
- '@esbuild/netbsd-x64@0.25.11':
+ '@esbuild/netbsd-arm64@0.28.1':
optional: true
'@esbuild/netbsd-x64@0.27.7':
optional: true
- '@esbuild/openbsd-arm64@0.25.11':
+ '@esbuild/netbsd-x64@0.28.1':
optional: true
'@esbuild/openbsd-arm64@0.27.7':
optional: true
- '@esbuild/openbsd-x64@0.25.11':
+ '@esbuild/openbsd-arm64@0.28.1':
optional: true
'@esbuild/openbsd-x64@0.27.7':
optional: true
- '@esbuild/openharmony-arm64@0.25.11':
+ '@esbuild/openbsd-x64@0.28.1':
optional: true
'@esbuild/openharmony-arm64@0.27.7':
optional: true
- '@esbuild/sunos-x64@0.25.11':
+ '@esbuild/openharmony-arm64@0.28.1':
optional: true
'@esbuild/sunos-x64@0.27.7':
optional: true
- '@esbuild/win32-arm64@0.25.11':
+ '@esbuild/sunos-x64@0.28.1':
optional: true
'@esbuild/win32-arm64@0.27.7':
optional: true
- '@esbuild/win32-ia32@0.25.11':
+ '@esbuild/win32-arm64@0.28.1':
optional: true
'@esbuild/win32-ia32@0.27.7':
optional: true
- '@esbuild/win32-x64@0.25.11':
+ '@esbuild/win32-ia32@0.28.1':
optional: true
'@esbuild/win32-x64@0.27.7':
optional: true
+ '@esbuild/win32-x64@0.28.1':
+ optional: true
+
'@eslint-community/eslint-utils@4.9.1(eslint@10.0.0)':
dependencies:
eslint: 10.0.0
@@ -3062,7 +3068,7 @@ snapshots:
'@stylistic/eslint-plugin-js@1.8.0(eslint@10.0.0)':
dependencies:
'@types/eslint': 8.56.12
- acorn: 8.16.0
+ acorn: 8.17.0
escape-string-regexp: 4.0.0
eslint: 10.0.0
eslint-visitor-keys: 3.4.3
@@ -3303,8 +3309,14 @@ snapshots:
dependencies:
acorn: 8.16.0
+ acorn-jsx@5.3.2(acorn@8.17.0):
+ dependencies:
+ acorn: 8.17.0
+
acorn@8.16.0: {}
+ acorn@8.17.0: {}
+
agent-base@7.1.1:
dependencies:
debug: 4.4.3
@@ -3475,7 +3487,7 @@ snapshots:
ts-api-utils: 1.3.0(typescript@5.5.4)
typescript: 5.5.4
- enhanced-resolve@5.22.1:
+ enhanced-resolve@5.24.0:
dependencies:
graceful-fs: 4.2.11
tapable: 2.3.3
@@ -3491,35 +3503,6 @@ snapshots:
es-module-lexer@2.1.0: {}
- esbuild@0.25.11:
- optionalDependencies:
- '@esbuild/aix-ppc64': 0.25.11
- '@esbuild/android-arm': 0.25.11
- '@esbuild/android-arm64': 0.25.11
- '@esbuild/android-x64': 0.25.11
- '@esbuild/darwin-arm64': 0.25.11
- '@esbuild/darwin-x64': 0.25.11
- '@esbuild/freebsd-arm64': 0.25.11
- '@esbuild/freebsd-x64': 0.25.11
- '@esbuild/linux-arm': 0.25.11
- '@esbuild/linux-arm64': 0.25.11
- '@esbuild/linux-ia32': 0.25.11
- '@esbuild/linux-loong64': 0.25.11
- '@esbuild/linux-mips64el': 0.25.11
- '@esbuild/linux-ppc64': 0.25.11
- '@esbuild/linux-riscv64': 0.25.11
- '@esbuild/linux-s390x': 0.25.11
- '@esbuild/linux-x64': 0.25.11
- '@esbuild/netbsd-arm64': 0.25.11
- '@esbuild/netbsd-x64': 0.25.11
- '@esbuild/openbsd-arm64': 0.25.11
- '@esbuild/openbsd-x64': 0.25.11
- '@esbuild/openharmony-arm64': 0.25.11
- '@esbuild/sunos-x64': 0.25.11
- '@esbuild/win32-arm64': 0.25.11
- '@esbuild/win32-ia32': 0.25.11
- '@esbuild/win32-x64': 0.25.11
-
esbuild@0.27.7:
optionalDependencies:
'@esbuild/aix-ppc64': 0.27.7
@@ -3549,12 +3532,41 @@ snapshots:
'@esbuild/win32-ia32': 0.27.7
'@esbuild/win32-x64': 0.27.7
+ esbuild@0.28.1:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.28.1
+ '@esbuild/android-arm': 0.28.1
+ '@esbuild/android-arm64': 0.28.1
+ '@esbuild/android-x64': 0.28.1
+ '@esbuild/darwin-arm64': 0.28.1
+ '@esbuild/darwin-x64': 0.28.1
+ '@esbuild/freebsd-arm64': 0.28.1
+ '@esbuild/freebsd-x64': 0.28.1
+ '@esbuild/linux-arm': 0.28.1
+ '@esbuild/linux-arm64': 0.28.1
+ '@esbuild/linux-ia32': 0.28.1
+ '@esbuild/linux-loong64': 0.28.1
+ '@esbuild/linux-mips64el': 0.28.1
+ '@esbuild/linux-ppc64': 0.28.1
+ '@esbuild/linux-riscv64': 0.28.1
+ '@esbuild/linux-s390x': 0.28.1
+ '@esbuild/linux-x64': 0.28.1
+ '@esbuild/netbsd-arm64': 0.28.1
+ '@esbuild/netbsd-x64': 0.28.1
+ '@esbuild/openbsd-arm64': 0.28.1
+ '@esbuild/openbsd-x64': 0.28.1
+ '@esbuild/openharmony-arm64': 0.28.1
+ '@esbuild/sunos-x64': 0.28.1
+ '@esbuild/win32-arm64': 0.28.1
+ '@esbuild/win32-ia32': 0.28.1
+ '@esbuild/win32-x64': 0.28.1
+
escape-string-regexp@4.0.0: {}
eslint-compat-utils@0.5.1(eslint@10.0.0):
dependencies:
eslint: 10.0.0
- semver: 7.8.1
+ semver: 7.8.4
eslint-config-prettier@9.1.0(eslint@10.0.0):
dependencies:
@@ -3574,14 +3586,14 @@ snapshots:
eslint-plugin-n@17.24.0(eslint@10.0.0)(typescript@5.5.4):
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.0.0)
- enhanced-resolve: 5.22.1
+ enhanced-resolve: 5.24.0
eslint: 10.0.0
eslint-plugin-es-x: 7.8.0(eslint@10.0.0)
get-tsconfig: 4.14.0
globals: 15.15.0
globrex: 0.1.2
ignore: 5.3.2
- semver: 7.8.1
+ semver: 7.8.4
ts-declaration-location: 1.0.7(typescript@5.5.4)
transitivePeerDependencies:
- typescript
@@ -3673,8 +3685,8 @@ snapshots:
espree@9.6.1:
dependencies:
- acorn: 8.16.0
- acorn-jsx: 5.3.2(acorn@8.16.0)
+ acorn: 8.17.0
+ acorn-jsx: 5.3.2(acorn@8.17.0)
eslint-visitor-keys: 3.4.3
esprima@4.0.1: {}
@@ -4312,7 +4324,7 @@ snapshots:
semver@7.7.4: {}
- semver@7.8.1: {}
+ semver@7.8.4: {}
serialize-javascript@6.0.2:
dependencies:
From 0510174bc0c908c9d4980274717191fdf46a38c6 Mon Sep 17 00:00:00 2001
From: Nic Polumeyv
Date: Mon, 15 Jun 2026 12:08:45 -0400
Subject: [PATCH 02/16] fix: make `SvelteURLSearchParams` notifications
accurate (#18425)
`SvelteURLSearchParams` has two notification bugs that surface through
`SvelteURL`.
The first is over-notification. Setting `url.href` always rebuilds the
params and bumps their version signal, even when the search string
didn't change at all. Effects that read `size` (or any params method)
re-run on every unrelated `href` write, which is #17218, where it caused
infinite effect loops in a router. The fix is a guard in the internal
replace path that bails out when the incoming params serialize
identically to the current ones.
The second is under-notification, and it's sneakier. Every read method
tracks the version signal, `get`, `getAll`, `has`, `keys`, `values`,
`entries`, `toString`, `size`, the iterator... except `forEach`, which
was never overridden. The inherited platform method reads the internal
list directly and subscribes to nothing, so a template that renders
params via `forEach` never updates:
```js
$effect(() => {
url.searchParams.forEach((value, key) => entries.push(`${key}=${value}`)); // never re-runs
});
```
We found this one in the wild while making `page.url` a `SvelteURL` in
sveltejs/kit#16031. Kit's own test suite renders query params with
`forEach`, and the coarse object-identity signal that previously masked
the gap went away, leaving stale UI after client-side navigations. The
fix is the same one-line tracking pattern every other read method
already uses.
Fixes #17218
---
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).
### Tests and linting
- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
---
.changeset/quick-meals-shout.md | 5 ++
.../src/reactivity/url-search-params.js | 15 ++++
.../src/reactivity/url-search-params.test.ts | 21 ++++++
packages/svelte/src/reactivity/url.test.ts | 74 +++++++++++++++++++
4 files changed, 115 insertions(+)
create mode 100644 .changeset/quick-meals-shout.md
diff --git a/.changeset/quick-meals-shout.md b/.changeset/quick-meals-shout.md
new file mode 100644
index 0000000000..21871edd67
--- /dev/null
+++ b/.changeset/quick-meals-shout.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: don't notify `searchParams` subscribers when the URL changes without affecting the search string
diff --git a/packages/svelte/src/reactivity/url-search-params.js b/packages/svelte/src/reactivity/url-search-params.js
index 38cf3ebe4f..d745bb6b08 100644
--- a/packages/svelte/src/reactivity/url-search-params.js
+++ b/packages/svelte/src/reactivity/url-search-params.js
@@ -54,6 +54,11 @@ export class SvelteURLSearchParams extends URLSearchParams {
*/
[REPLACE](params) {
if (this.#updating) return;
+
+ // the URL may have changed in a way that leaves the search string untouched —
+ // don't rebuild the params or notify readers if nothing changed
+ if (params.toString() === super.toString()) return;
+
this.#updating = true;
for (const key of [...super.keys()]) {
@@ -126,6 +131,16 @@ export class SvelteURLSearchParams extends URLSearchParams {
return super.keys();
}
+ /**
+ * @param {(value: string, key: string, parent: URLSearchParams) => void} callback
+ * @param {any} [this_arg]
+ * @returns {void}
+ */
+ forEach(callback, this_arg) {
+ get(this.#version);
+ super.forEach(callback, this_arg);
+ }
+
/**
* @param {string} name
* @param {string} value
diff --git a/packages/svelte/src/reactivity/url-search-params.test.ts b/packages/svelte/src/reactivity/url-search-params.test.ts
index b0c84872b0..765b4020f3 100644
--- a/packages/svelte/src/reactivity/url-search-params.test.ts
+++ b/packages/svelte/src/reactivity/url-search-params.test.ts
@@ -244,3 +244,24 @@ test('URLSearchParams.toString', () => {
test('SvelteURLSearchParams instanceof URLSearchParams', () => {
assert.ok(new SvelteURLSearchParams() instanceof URLSearchParams);
});
+
+test('params.forEach is reactive', () => {
+ const params = new SvelteURLSearchParams('foo=1');
+ const log: any = [];
+
+ const cleanup = effect_root(() => {
+ render_effect(() => {
+ const entries: string[] = [];
+ params.forEach((value, key) => entries.push(`${key}=${value}`));
+ log.push(entries.join('&'));
+ });
+ });
+
+ flushSync(() => {
+ params.set('foo', '2');
+ });
+
+ assert.deepEqual(log, ['foo=1', 'foo=2']);
+
+ cleanup();
+});
diff --git a/packages/svelte/src/reactivity/url.test.ts b/packages/svelte/src/reactivity/url.test.ts
index a79aa32315..d698116421 100644
--- a/packages/svelte/src/reactivity/url.test.ts
+++ b/packages/svelte/src/reactivity/url.test.ts
@@ -166,3 +166,77 @@ test('url.search normalizes value', () => {
test('SvelteURL instanceof URL', () => {
assert.ok(new SvelteURL('https://svelte.dev') instanceof URL);
});
+
+test('url.searchParams subscribers are not notified by changes that leave the search string untouched', () => {
+ const url = new SvelteURL('https://svelte.dev/a?foo=bar');
+ const log: any = [];
+
+ const cleanup = effect_root(() => {
+ render_effect(() => {
+ log.push(url.searchParams.toString());
+ });
+ });
+
+ flushSync(() => {
+ // does not affect the search string
+ url.pathname = '/b';
+ });
+
+ flushSync(() => {
+ // neither does this
+ url.href = 'https://svelte.dev/c?foo=bar#hash';
+ });
+
+ flushSync(() => {
+ // but this does
+ url.search = '?foo=baz';
+ });
+
+ assert.deepEqual(log, ['foo=bar', 'foo=baz']);
+
+ cleanup();
+});
+
+test('url.searchParams.size is not notified by unrelated href changes', () => {
+ const url = new SvelteURL('https://svelte.dev/?foo=bar');
+ const log: any = [];
+
+ const cleanup = effect_root(() => {
+ render_effect(() => {
+ log.push(url.searchParams.size);
+ });
+ });
+
+ flushSync(() => {
+ url.href = 'https://svelte.dev/other?foo=bar';
+ });
+
+ flushSync(() => {
+ url.searchParams.append('baz', 'qux');
+ });
+
+ assert.deepEqual(log, [1, 2]);
+
+ cleanup();
+});
+
+test('url.searchParams.forEach re-runs when the search string changes via the URL', () => {
+ const url = new SvelteURL('https://svelte.dev/?foo=1');
+ const log: any = [];
+
+ const cleanup = effect_root(() => {
+ render_effect(() => {
+ const entries: string[] = [];
+ url.searchParams.forEach((value, key) => entries.push(`${key}=${value}`));
+ log.push(entries.join('&'));
+ });
+ });
+
+ flushSync(() => {
+ url.href = 'https://svelte.dev/?bar=2';
+ });
+
+ assert.deepEqual(log, ['foo=1', 'bar=2']);
+
+ cleanup();
+});
From c4daa490bbbf483ebba117c1afb6f71b4b19e63c Mon Sep 17 00:00:00 2001
From: Yuichiro Yamashita
Date: Tue, 16 Jun 2026 01:18:02 +0900
Subject: [PATCH 03/16] fix: include wrapping parentheses in `{@const}`
declarator end (#18436)
`{@const x = (a)}` (and any `{@const}` whose initializer is wrapped in
parentheses, e.g. `(a = b)`, `({ ... })`) produced a
`VariableDeclarator` whose `end` fell before the closing `)`.
`read_expression` strips the wrapping parens from the returned node but
advances the parser past them, so `init.end` excludes the `)` while the
parser position does not. Use the parser position for the declarator
`end` instead. Fixes source slices / tooling that rely on the node range
(the range previously yielded invalid, unbalanced source).
### Before submitting the PR, please make sure you do the following
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).
### Tests and linting
- [ ] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
---
.changeset/clean-melons-attack.md | 5 +
.../src/compiler/phases/1-parse/state/tag.js | 6 +-
.../const-tag-parenthesized-init/input.svelte | 6 +
.../const-tag-parenthesized-init/output.json | 527 ++++++++++++++++++
4 files changed, 543 insertions(+), 1 deletion(-)
create mode 100644 .changeset/clean-melons-attack.md
create mode 100644 packages/svelte/tests/parser-modern/samples/const-tag-parenthesized-init/input.svelte
create mode 100644 packages/svelte/tests/parser-modern/samples/const-tag-parenthesized-init/output.json
diff --git a/.changeset/clean-melons-attack.md b/.changeset/clean-melons-attack.md
new file mode 100644
index 0000000000..8b5a9f2066
--- /dev/null
+++ b/.changeset/clean-melons-attack.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: include wrapping parentheses in `{@const}` declarator `end` position
diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js
index 15c79e0353..f5314d04ed 100644
--- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js
+++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js
@@ -783,6 +783,8 @@ function special(parser) {
const expression_start = parser.index;
const init = read_expression(parser);
+ // parser is past wrapping parens, but `init.end` is not — use the parser position
+ const declarator_end = parser.index;
if (
init.type === 'SequenceExpression' &&
!parser.template.substring(expression_start, init.start).includes('(')
@@ -801,7 +803,9 @@ function special(parser) {
declaration: {
type: 'VariableDeclaration',
kind: 'const',
- declarations: [{ type: 'VariableDeclarator', id, init, start: id.start, end: init.end }],
+ declarations: [
+ { type: 'VariableDeclarator', id, init, start: id.start, end: declarator_end }
+ ],
start: start + 2, // start at const, not at @const
end: parser.index - 1
},
diff --git a/packages/svelte/tests/parser-modern/samples/const-tag-parenthesized-init/input.svelte b/packages/svelte/tests/parser-modern/samples/const-tag-parenthesized-init/input.svelte
new file mode 100644
index 0000000000..39bf04093e
--- /dev/null
+++ b/packages/svelte/tests/parser-modern/samples/const-tag-parenthesized-init/input.svelte
@@ -0,0 +1,6 @@
+{#each items as item}
+ {@const x = (item.value)}
+ {@const y = (a = item.n)}
+ {@const z = ({ a: item.a })}
+ {x}{y}{z.a}
+{/each}
diff --git a/packages/svelte/tests/parser-modern/samples/const-tag-parenthesized-init/output.json b/packages/svelte/tests/parser-modern/samples/const-tag-parenthesized-init/output.json
new file mode 100644
index 0000000000..3e8d8b23d5
--- /dev/null
+++ b/packages/svelte/tests/parser-modern/samples/const-tag-parenthesized-init/output.json
@@ -0,0 +1,527 @@
+{
+ "css": null,
+ "js": [],
+ "start": 0,
+ "end": 126,
+ "type": "Root",
+ "fragment": {
+ "type": "Fragment",
+ "nodes": [
+ {
+ "type": "EachBlock",
+ "start": 0,
+ "end": 126,
+ "expression": {
+ "type": "Identifier",
+ "start": 7,
+ "end": 12,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 7
+ },
+ "end": {
+ "line": 1,
+ "column": 12
+ }
+ },
+ "name": "items"
+ },
+ "body": {
+ "type": "Fragment",
+ "nodes": [
+ {
+ "type": "Text",
+ "start": 21,
+ "end": 23,
+ "raw": "\n\t",
+ "data": "\n\t"
+ },
+ {
+ "type": "ConstTag",
+ "start": 23,
+ "end": 48,
+ "declaration": {
+ "type": "VariableDeclaration",
+ "kind": "const",
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "id": {
+ "type": "Identifier",
+ "name": "x",
+ "start": 31,
+ "end": 32,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 9,
+ "character": 31
+ },
+ "end": {
+ "line": 2,
+ "column": 10,
+ "character": 32
+ }
+ }
+ },
+ "init": {
+ "type": "MemberExpression",
+ "start": 36,
+ "end": 46,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 14
+ },
+ "end": {
+ "line": 2,
+ "column": 24
+ }
+ },
+ "object": {
+ "type": "Identifier",
+ "start": 36,
+ "end": 40,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 14
+ },
+ "end": {
+ "line": 2,
+ "column": 18
+ }
+ },
+ "name": "item"
+ },
+ "property": {
+ "type": "Identifier",
+ "start": 41,
+ "end": 46,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 19
+ },
+ "end": {
+ "line": 2,
+ "column": 24
+ }
+ },
+ "name": "value"
+ },
+ "computed": false,
+ "optional": false
+ },
+ "start": 31,
+ "end": 47
+ }
+ ],
+ "start": 25,
+ "end": 47
+ }
+ },
+ {
+ "type": "Text",
+ "start": 48,
+ "end": 50,
+ "raw": "\n\t",
+ "data": "\n\t"
+ },
+ {
+ "type": "ConstTag",
+ "start": 50,
+ "end": 75,
+ "declaration": {
+ "type": "VariableDeclaration",
+ "kind": "const",
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "id": {
+ "type": "Identifier",
+ "name": "y",
+ "start": 58,
+ "end": 59,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 9,
+ "character": 58
+ },
+ "end": {
+ "line": 3,
+ "column": 10,
+ "character": 59
+ }
+ }
+ },
+ "init": {
+ "type": "AssignmentExpression",
+ "start": 63,
+ "end": 73,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 14
+ },
+ "end": {
+ "line": 3,
+ "column": 24
+ }
+ },
+ "operator": "=",
+ "left": {
+ "type": "Identifier",
+ "start": 63,
+ "end": 64,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 14
+ },
+ "end": {
+ "line": 3,
+ "column": 15
+ }
+ },
+ "name": "a"
+ },
+ "right": {
+ "type": "MemberExpression",
+ "start": 67,
+ "end": 73,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 18
+ },
+ "end": {
+ "line": 3,
+ "column": 24
+ }
+ },
+ "object": {
+ "type": "Identifier",
+ "start": 67,
+ "end": 71,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 18
+ },
+ "end": {
+ "line": 3,
+ "column": 22
+ }
+ },
+ "name": "item"
+ },
+ "property": {
+ "type": "Identifier",
+ "start": 72,
+ "end": 73,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 23
+ },
+ "end": {
+ "line": 3,
+ "column": 24
+ }
+ },
+ "name": "n"
+ },
+ "computed": false,
+ "optional": false
+ }
+ },
+ "start": 58,
+ "end": 74
+ }
+ ],
+ "start": 52,
+ "end": 74
+ }
+ },
+ {
+ "type": "Text",
+ "start": 75,
+ "end": 77,
+ "raw": "\n\t",
+ "data": "\n\t"
+ },
+ {
+ "type": "ConstTag",
+ "start": 77,
+ "end": 105,
+ "declaration": {
+ "type": "VariableDeclaration",
+ "kind": "const",
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "id": {
+ "type": "Identifier",
+ "name": "z",
+ "start": 85,
+ "end": 86,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 9,
+ "character": 85
+ },
+ "end": {
+ "line": 4,
+ "column": 10,
+ "character": 86
+ }
+ }
+ },
+ "init": {
+ "type": "ObjectExpression",
+ "start": 90,
+ "end": 103,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 14
+ },
+ "end": {
+ "line": 4,
+ "column": 27
+ }
+ },
+ "properties": [
+ {
+ "type": "Property",
+ "start": 92,
+ "end": 101,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 16
+ },
+ "end": {
+ "line": 4,
+ "column": 25
+ }
+ },
+ "method": false,
+ "shorthand": false,
+ "computed": false,
+ "key": {
+ "type": "Identifier",
+ "start": 92,
+ "end": 93,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 16
+ },
+ "end": {
+ "line": 4,
+ "column": 17
+ }
+ },
+ "name": "a"
+ },
+ "value": {
+ "type": "MemberExpression",
+ "start": 95,
+ "end": 101,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 19
+ },
+ "end": {
+ "line": 4,
+ "column": 25
+ }
+ },
+ "object": {
+ "type": "Identifier",
+ "start": 95,
+ "end": 99,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 19
+ },
+ "end": {
+ "line": 4,
+ "column": 23
+ }
+ },
+ "name": "item"
+ },
+ "property": {
+ "type": "Identifier",
+ "start": 100,
+ "end": 101,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 24
+ },
+ "end": {
+ "line": 4,
+ "column": 25
+ }
+ },
+ "name": "a"
+ },
+ "computed": false,
+ "optional": false
+ },
+ "kind": "init"
+ }
+ ]
+ },
+ "start": 85,
+ "end": 104
+ }
+ ],
+ "start": 79,
+ "end": 104
+ }
+ },
+ {
+ "type": "Text",
+ "start": 105,
+ "end": 107,
+ "raw": "\n\t",
+ "data": "\n\t"
+ },
+ {
+ "type": "ExpressionTag",
+ "start": 107,
+ "end": 110,
+ "expression": {
+ "type": "Identifier",
+ "start": 108,
+ "end": 109,
+ "loc": {
+ "start": {
+ "line": 5,
+ "column": 2
+ },
+ "end": {
+ "line": 5,
+ "column": 3
+ }
+ },
+ "name": "x"
+ }
+ },
+ {
+ "type": "ExpressionTag",
+ "start": 110,
+ "end": 113,
+ "expression": {
+ "type": "Identifier",
+ "start": 111,
+ "end": 112,
+ "loc": {
+ "start": {
+ "line": 5,
+ "column": 5
+ },
+ "end": {
+ "line": 5,
+ "column": 6
+ }
+ },
+ "name": "y"
+ }
+ },
+ {
+ "type": "ExpressionTag",
+ "start": 113,
+ "end": 118,
+ "expression": {
+ "type": "MemberExpression",
+ "start": 114,
+ "end": 117,
+ "loc": {
+ "start": {
+ "line": 5,
+ "column": 8
+ },
+ "end": {
+ "line": 5,
+ "column": 11
+ }
+ },
+ "object": {
+ "type": "Identifier",
+ "start": 114,
+ "end": 115,
+ "loc": {
+ "start": {
+ "line": 5,
+ "column": 8
+ },
+ "end": {
+ "line": 5,
+ "column": 9
+ }
+ },
+ "name": "z"
+ },
+ "property": {
+ "type": "Identifier",
+ "start": 116,
+ "end": 117,
+ "loc": {
+ "start": {
+ "line": 5,
+ "column": 10
+ },
+ "end": {
+ "line": 5,
+ "column": 11
+ }
+ },
+ "name": "a"
+ },
+ "computed": false,
+ "optional": false
+ }
+ },
+ {
+ "type": "Text",
+ "start": 118,
+ "end": 119,
+ "raw": "\n",
+ "data": "\n"
+ }
+ ]
+ },
+ "context": {
+ "type": "Identifier",
+ "name": "item",
+ "start": 16,
+ "end": 20,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 16,
+ "character": 16
+ },
+ "end": {
+ "line": 1,
+ "column": 20,
+ "character": 20
+ }
+ }
+ }
+ }
+ ]
+ },
+ "options": null
+}
From a6985bcd24faa8ec4a8de671b2013d98b0bc629f Mon Sep 17 00:00:00 2001
From: "jyc.dev"
Date: Mon, 22 Jun 2026 13:03:18 +0200
Subject: [PATCH 04/16] fix: strip `?` from optional parameters in svelte lang
ts (#18448)
In `esrap@2.2.12` we fixed the missing `?` in ts files
https://github.com/sveltejs/esrap/pull/139
It's good for `ts`, but bad for svelte compiler that was not removing it
as it was a esrap issue not printing it :/
This issue came: https://github.com/sveltejs/esrap/issues/141
This PR is fixing the issue in the compiler stripping the `?`
---
.changeset/strip-optional-param-marker.md | 5 +++++
packages/svelte/package.json | 2 +-
.../phases/1-parse/remove_typescript_nodes.js | 6 +++++
.../_expected/client/index.svelte.js | 22 +++++++++++++++++++
.../_expected/server/index.svelte.js | 15 +++++++++++++
.../index.svelte | 14 ++++++++++++
pnpm-lock.yaml | 12 +++++-----
7 files changed, 69 insertions(+), 7 deletions(-)
create mode 100644 .changeset/strip-optional-param-marker.md
create mode 100644 packages/svelte/tests/snapshot/samples/typescript-optional-parameter/_expected/client/index.svelte.js
create mode 100644 packages/svelte/tests/snapshot/samples/typescript-optional-parameter/_expected/server/index.svelte.js
create mode 100644 packages/svelte/tests/snapshot/samples/typescript-optional-parameter/index.svelte
diff --git a/.changeset/strip-optional-param-marker.md b/.changeset/strip-optional-param-marker.md
new file mode 100644
index 0000000000..a6b759747c
--- /dev/null
+++ b/.changeset/strip-optional-param-marker.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: strip `?` from optional parameters in `
+
+{v}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e86c927efe..6331fd0789 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -102,8 +102,8 @@ importers:
specifier: ^1.2.1
version: 1.2.1
esrap:
- specifier: ^2.2.11
- version: 2.2.11(@typescript-eslint/types@8.59.4)
+ specifier: ^2.2.12
+ version: 2.2.12(@typescript-eslint/types@8.59.4)
is-reference:
specifier: ^3.0.3
version: 3.0.3
@@ -1418,8 +1418,8 @@ packages:
resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
engines: {node: '>=0.10'}
- esrap@2.2.11:
- resolution: {integrity: sha512-gPdx+I+BjYEinNMQaBXFjbaJVyoPMU4ZODg5mE+M4DqVG9VusAVHHjcBX+zqyITlI0DIARwDMMzZwAWj36dRoQ==}
+ esrap@2.2.12:
+ resolution: {integrity: sha512-On0QbLyaiAkVC4eXtgnXK9Kh2opit+3rcUSOc45DqJ2s/X2eXAHsGOKRSJ6IDagQEW5vPyivANfXUiqgXC67Rw==}
peerDependencies:
'@typescript-eslint/types': ^8.2.0
peerDependenciesMeta:
@@ -2886,7 +2886,7 @@ snapshots:
'@jridgewell/gen-mapping@0.3.5':
dependencies:
'@jridgewell/set-array': 1.2.1
- '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/sourcemap-codec': 1.5.5
'@jridgewell/trace-mapping': 0.3.31
'@jridgewell/remapping@2.3.4':
@@ -3695,7 +3695,7 @@ snapshots:
dependencies:
estraverse: 5.3.0
- esrap@2.2.11(@typescript-eslint/types@8.59.4):
+ esrap@2.2.12(@typescript-eslint/types@8.59.4):
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
optionalDependencies:
From 36ae0622a8da4c6e60d32cde199af3d7c3121cd3 Mon Sep 17 00:00:00 2001
From: Simon H <5968653+dummdidumm@users.noreply.github.com>
Date: Mon, 22 Jun 2026 20:09:04 +0200
Subject: [PATCH 05/16] fix: always unset reactivity context after restoring it
(#18453)
When calling `save`, we restore the context after the promise resolves.
But we do not unset it after the subsequent synchronous execution. That
means that until `unset_context` runs in `async_derived` we will not
have the correct (nulled) context. That causes problems if the `save`
isn't the last promise contributing to the `async_derived`, because it
means the context is not properly unset until the promise _after_ the
`save` within the `async_derived` settles. This can cause all sorts of
mixups, including a wrong mutation error.
fixes #18441
---
.changeset/late-geese-fix.md | 5 +++
.../src/internal/client/reactivity/async.js | 2 +
.../_config.js | 44 +++++++++++++++++++
.../main.svelte | 28 ++++++++++++
4 files changed, 79 insertions(+)
create mode 100644 .changeset/late-geese-fix.md
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-parallel-derived-template-mutation/_config.js
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-parallel-derived-template-mutation/main.svelte
diff --git a/.changeset/late-geese-fix.md b/.changeset/late-geese-fix.md
new file mode 100644
index 0000000000..b8e83fc8cd
--- /dev/null
+++ b/.changeset/late-geese-fix.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: always unset reactivity context after restoring it
diff --git a/packages/svelte/src/internal/client/reactivity/async.js b/packages/svelte/src/internal/client/reactivity/async.js
index 13b6e42b3e..2e7df92dc0 100644
--- a/packages/svelte/src/internal/client/reactivity/async.js
+++ b/packages/svelte/src/internal/client/reactivity/async.js
@@ -25,6 +25,7 @@ import {
set_reactivity_loss_tracker
} from './deriveds.js';
import { aborted } from './effects.js';
+import { queue_micro_task } from '../dom/task.js';
/**
* @param {Blocker[]} blockers
@@ -169,6 +170,7 @@ export async function save(promise) {
return () => {
restore();
+ queue_micro_task(unset_context);
return value;
};
}
diff --git a/packages/svelte/tests/runtime-runes/samples/async-parallel-derived-template-mutation/_config.js b/packages/svelte/tests/runtime-runes/samples/async-parallel-derived-template-mutation/_config.js
new file mode 100644
index 0000000000..9650e88993
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-parallel-derived-template-mutation/_config.js
@@ -0,0 +1,44 @@
+import { tick } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ mode: ['client'],
+
+ compileOptions: {
+ dev: true
+ },
+
+ async test({ assert, target, errors }) {
+ await tick();
+ const [increment, update, resolve] = target.querySelectorAll('button');
+
+ increment.click();
+ await tick();
+
+ resolve.click();
+ await tick();
+
+ update.click();
+ await tick();
+
+ resolve.click();
+ await tick();
+
+ assert.deepEqual(
+ errors.filter((error) => error.includes('state_unsafe_mutation')),
+ []
+ );
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ increment
+ update
+ resolve
+ count: 1
+ submits: 1
+ pending: 0
+ 2
+ `
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/async-parallel-derived-template-mutation/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-parallel-derived-template-mutation/main.svelte
new file mode 100644
index 0000000000..d9c804b314
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-parallel-derived-template-mutation/main.svelte
@@ -0,0 +1,28 @@
+
+
+ (count += 1)}>increment
+update
+ queued.shift()?.()}>resolve
+
+count: {count}
+submits: {submits}
+pending: {$effect.pending()}
+{await a + await b}
From eae50dfd1c2269e37258ef5c09527003bcf61573 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 23 Jun 2026 14:44:52 -0400
Subject: [PATCH 06/16] Version Packages (#18435)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.56.4
### Patch Changes
- fix: include wrapping parentheses in `{@const}` declarator `end`
position ([#18436](https://github.com/sveltejs/svelte/pull/18436))
- fix: always unset reactivity context after restoring it
([#18453](https://github.com/sveltejs/svelte/pull/18453))
- fix: don't notify `searchParams` subscribers when the URL changes
without affecting the search string
([#18425](https://github.com/sveltejs/svelte/pull/18425))
- fix: strip `?` from optional parameters in `
+
+clicks: {count}
From c2b7642263ec1f9d89f5d027d68f272742c449a7 Mon Sep 17 00:00:00 2001
From: Simon H <5968653+dummdidumm@users.noreply.github.com>
Date: Fri, 10 Jul 2026 15:24:35 +0200
Subject: [PATCH 12/16] chore: add benchmarks (#18523)
Add two kairo variants to better measure of `#traverse` / block effects
perf
---
.../tests/kairo_broad_block.bench.js | 47 ++++++++++++++++++
.../tests/kairo_deep_block.bench.js | 48 +++++++++++++++++++
2 files changed, 95 insertions(+)
create mode 100644 benchmarking/benchmarks/reactivity/tests/kairo_broad_block.bench.js
create mode 100644 benchmarking/benchmarks/reactivity/tests/kairo_deep_block.bench.js
diff --git a/benchmarking/benchmarks/reactivity/tests/kairo_broad_block.bench.js b/benchmarking/benchmarks/reactivity/tests/kairo_broad_block.bench.js
new file mode 100644
index 0000000000..e8451ec3fa
--- /dev/null
+++ b/benchmarking/benchmarks/reactivity/tests/kairo_broad_block.bench.js
@@ -0,0 +1,47 @@
+import assert from 'node:assert';
+import * as $ from 'svelte/internal/client';
+import { block } from '../../../../packages/svelte/src/internal/client/reactivity/effects.js';
+
+// Like `kairo_broad`, but each derived is also read by a block effect, as
+// happens with e.g. `{#if derived}` in a component. Measures our #traverse perf better.
+export default () => {
+ let head = $.state(0);
+ let last = head;
+ let counter = 0;
+
+ const destroy = $.effect_root(() => {
+ for (let i = 0; i < 50; i++) {
+ let current = $.derived(() => {
+ return $.get(head) + i;
+ });
+ let current2 = $.derived(() => {
+ return $.get(current) + 1;
+ });
+ block(() => {
+ $.get(current2);
+ });
+ $.render_effect(() => {
+ $.get(current2);
+ counter++;
+ });
+ last = current2;
+ }
+ });
+
+ return {
+ destroy,
+ run() {
+ $.flush(() => {
+ $.set(head, 1);
+ });
+ counter = 0;
+ for (let i = 0; i < 50; i++) {
+ $.flush(() => {
+ $.set(head, i);
+ });
+ assert.equal($.get(last), i + 50);
+ }
+ assert.equal(counter, 50 * 50);
+ }
+ };
+};
diff --git a/benchmarking/benchmarks/reactivity/tests/kairo_deep_block.bench.js b/benchmarking/benchmarks/reactivity/tests/kairo_deep_block.bench.js
new file mode 100644
index 0000000000..a8d9fa8c35
--- /dev/null
+++ b/benchmarking/benchmarks/reactivity/tests/kairo_deep_block.bench.js
@@ -0,0 +1,48 @@
+import assert from 'node:assert';
+import * as $ from 'svelte/internal/client';
+import { block } from '../../../../packages/svelte/src/internal/client/reactivity/effects.js';
+
+let len = 50;
+const iter = 50;
+
+// Like `kairo_deep`, but the derived chain is also read by a block effect, as
+// happens with e.g. `{#if derived}` in a component. Measures our #traverse perf better.
+export default () => {
+ let head = $.state(0);
+ let current = head;
+ for (let i = 0; i < len; i++) {
+ let c = current;
+ current = $.derived(() => {
+ return $.get(c) + 1;
+ });
+ }
+ let counter = 0;
+
+ const destroy = $.effect_root(() => {
+ block(() => {
+ $.get(current);
+ });
+
+ $.render_effect(() => {
+ $.get(current);
+ counter++;
+ });
+ });
+
+ return {
+ destroy,
+ run() {
+ $.flush(() => {
+ $.set(head, 1);
+ });
+ counter = 0;
+ for (let i = 0; i < iter; i++) {
+ $.flush(() => {
+ $.set(head, i);
+ });
+ assert.equal($.get(current), len + i);
+ }
+ assert.equal(counter, iter);
+ }
+ };
+};
From bfbb026f2f7db6ced0d86ba0feb40587c0e8f598 Mon Sep 17 00:00:00 2001
From: JY Wey <34165386+JaiWey@users.noreply.github.com>
Date: Sat, 11 Jul 2026 01:42:01 +1200
Subject: [PATCH 13/16] fix: skip unnecessary derived effect in earlier batch
(#18525)
fixes #18438
Currently the `mark` method inside `#merge` for `earlier_batch` will
schedule effect for undirty derived. Add the condition for derived to
prevent unnecessary effect be scheduled.
```
if ((flags & DERIVED) !== 0) {
mark(/** @type {Derived} */ (reaction));
}
```
---
.changeset/curly-wasps-hide.md | 5 ++++
.../src/internal/client/reactivity/batch.js | 6 ++++
.../samples/async-batch-derived/_config.js | 28 ++++++++++++++++++
.../samples/async-batch-derived/main.svelte | 29 +++++++++++++++++++
4 files changed, 68 insertions(+)
create mode 100644 .changeset/curly-wasps-hide.md
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-batch-derived/_config.js
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-batch-derived/main.svelte
diff --git a/.changeset/curly-wasps-hide.md b/.changeset/curly-wasps-hide.md
new file mode 100644
index 0000000000..7e55d77ba0
--- /dev/null
+++ b/.changeset/curly-wasps-hide.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: skip unnecessary derived effect in earlier batch
diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js
index 7d14b80519..5becae2dc9 100644
--- a/packages/svelte/src/internal/client/reactivity/batch.js
+++ b/packages/svelte/src/internal/client/reactivity/batch.js
@@ -530,6 +530,12 @@ export class Batch {
const mark = (value) => {
var reactions = value.reactions;
if (reactions === null) return;
+ // skip if value is derived and is neither dirty nor maybe dirty. transitive
+ // deriveds (a derived depending on another derived) are only MAYBE_DIRTY, so
+ // we must continue traversing them to reach the effects that depend on them
+ if ((value.f & DERIVED) !== 0 && (value.f & (DIRTY | MAYBE_DIRTY)) === 0) {
+ return;
+ }
for (const reaction of reactions) {
var flags = reaction.f;
diff --git a/packages/svelte/tests/runtime-runes/samples/async-batch-derived/_config.js b/packages/svelte/tests/runtime-runes/samples/async-batch-derived/_config.js
new file mode 100644
index 0000000000..d5286d9d95
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-batch-derived/_config.js
@@ -0,0 +1,28 @@
+import { tick } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target }) {
+ const [increment, pop] = target.querySelectorAll('button');
+
+ increment.click();
+ await tick();
+ assert.htmlEqual(
+ target.innerHTML,
+ `increment pop Loading...
`
+ );
+ increment.click();
+ await tick();
+ assert.htmlEqual(
+ target.innerHTML,
+ `increment pop Loading...
`
+ );
+ pop.click();
+ await tick();
+ assert.htmlEqual(target.innerHTML, `increment pop 2 2 1`);
+
+ pop.click();
+ await tick();
+ assert.htmlEqual(target.innerHTML, `increment pop 2 2 1`);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/async-batch-derived/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-batch-derived/main.svelte
new file mode 100644
index 0000000000..af7f0f468c
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-batch-derived/main.svelte
@@ -0,0 +1,29 @@
+
+
+ {
+ if (count === 0) {
+ other++;
+ count++;
+ } else {
+ count++
+ }
+}}>increment
+ queue.pop()?.()}>pop
+{#snippet defaultPending()}
+ Loading...
+{/snippet}
+
+{#if count > 0}
+
+ {await push(count)} {count} {other}
+
+{/if}
From 08a9e9e7e43453e2146357dd72d44cca1e41e29e Mon Sep 17 00:00:00 2001
From: Rabindra Kumar Meher
Date: Fri, 10 Jul 2026 19:29:34 +0530
Subject: [PATCH 14/16] fix: prevent derived connection leak in untracked
contexts (#18517)
Fixes #18501.
Problem is that is_updating_effect was also set to true for branch/root effects which are not reactive
---------
Co-authored-by: Simon Holthausen
---
packages/svelte/src/internal/client/runtime.js | 5 ++++-
packages/svelte/tests/signals/test.ts | 18 ++++++++++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js
index 227203523e..188d16a820 100644
--- a/packages/svelte/src/internal/client/runtime.js
+++ b/packages/svelte/src/internal/client/runtime.js
@@ -61,6 +61,9 @@ import { without_reactive_context } from './dom/elements/bindings/shared.js';
import { set_signal_status, update_derived_status } from './reactivity/status.js';
import * as w from './warnings.js';
+/**
+ * True if updating in an effect context that is reactive (i.e. not branch/root effects)
+ */
let is_updating_effect = false;
export let is_destroying_effect = false;
@@ -444,7 +447,7 @@ export function update_effect(effect) {
var was_updating_effect = is_updating_effect;
active_effect = effect;
- is_updating_effect = true;
+ is_updating_effect = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0; // Branch/root effects are not reactive contexts
if (DEV) {
var previous_component_fn = dev_current_component_function;
diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts
index 927ce2e665..07f79bd395 100644
--- a/packages/svelte/tests/signals/test.ts
+++ b/packages/svelte/tests/signals/test.ts
@@ -1503,4 +1503,22 @@ describe('signals', () => {
assert.deepEqual(log, ['inner destroyed', 'inner destroyed']);
};
});
+
+ test('derived read in an untracked context should not leak in deps reactions', () => {
+ return () => {
+ let s = state('hello');
+ let a = derived(() => $.get(s));
+ let b = derived(() => $.get(a));
+
+ let destroy = effect_root(() => {
+ $.get(b);
+ });
+
+ destroy();
+
+ // a was spuriously added to s.reactions via is_updating_effect
+ // even though the entire derived chain was read in an untracked context
+ assert.equal(s.reactions, null);
+ };
+ });
});
From 8e4f26552c7dd40cc056a28357f6171542d2549d Mon Sep 17 00:00:00 2001
From: Simon H <5968653+dummdidumm@users.noreply.github.com>
Date: Fri, 10 Jul 2026 16:01:20 +0200
Subject: [PATCH 15/16] chore: changeset (#18527)
for #18517
---
.changeset/common-ways-deny.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 .changeset/common-ways-deny.md
diff --git a/.changeset/common-ways-deny.md b/.changeset/common-ways-deny.md
new file mode 100644
index 0000000000..75e9375965
--- /dev/null
+++ b/.changeset/common-ways-deny.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: don't (re)connect deriveds when read inside branch/root effects
From b4d1583ae20f3869a88a731d9a265c546c099f66 Mon Sep 17 00:00:00 2001
From: Thribhuvan
Date: Fri, 10 Jul 2026 19:31:48 +0530
Subject: [PATCH 16/16] fix: transform computed keys in keyed each block
destructuring patterns (#18521)
fixes #18519
---
.changeset/shiny-keys-dance.md | 5 ++++
.../3-transform/client/visitors/EachBlock.js | 5 +++-
.../Child.svelte | 7 +++++
.../_config.js | 29 +++++++++++++++++++
.../main.svelte | 12 ++++++++
5 files changed, 57 insertions(+), 1 deletion(-)
create mode 100644 .changeset/shiny-keys-dance.md
create mode 100644 packages/svelte/tests/runtime-runes/samples/each-keyed-computed-destructuring-key/Child.svelte
create mode 100644 packages/svelte/tests/runtime-runes/samples/each-keyed-computed-destructuring-key/_config.js
create mode 100644 packages/svelte/tests/runtime-runes/samples/each-keyed-computed-destructuring-key/main.svelte
diff --git a/.changeset/shiny-keys-dance.md b/.changeset/shiny-keys-dance.md
new file mode 100644
index 0000000000..20cf754212
--- /dev/null
+++ b/.changeset/shiny-keys-dance.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: transform computed keys in keyed `{#each}` destructuring patterns
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js
index a1371b516a..b33eddd461 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js
@@ -294,7 +294,10 @@ export function EachBlock(node, context) {
let key_function = b.id('$.index');
if (node.metadata.keyed) {
- const pattern = /** @type {Pattern} */ (node.context); // can only be keyed when a context is provided
+ // can only be keyed when a context is provided
+ const pattern = /** @type {Pattern} */ (
+ context.visit(/** @type {Pattern} */ (node.context), key_state)
+ );
const expression = /** @type {Expression} */ (
context.visit(/** @type {Expression} */ (node.key), key_state)
);
diff --git a/packages/svelte/tests/runtime-runes/samples/each-keyed-computed-destructuring-key/Child.svelte b/packages/svelte/tests/runtime-runes/samples/each-keyed-computed-destructuring-key/Child.svelte
new file mode 100644
index 0000000000..35544d0a55
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/each-keyed-computed-destructuring-key/Child.svelte
@@ -0,0 +1,7 @@
+
+
+{#each options as { [labelKey]: label, [valueKey]: value } (value)}
+ {label}: {value}
+{/each}
diff --git a/packages/svelte/tests/runtime-runes/samples/each-keyed-computed-destructuring-key/_config.js b/packages/svelte/tests/runtime-runes/samples/each-keyed-computed-destructuring-key/_config.js
new file mode 100644
index 0000000000..8a8789d712
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/each-keyed-computed-destructuring-key/_config.js
@@ -0,0 +1,29 @@
+import { flushSync } from 'svelte';
+import { ok, test } from '../../test';
+
+// https://github.com/sveltejs/svelte/issues/18519
+export default test({
+ html: `
+ reverse
+ 1: a1
+ 2: a2
+ 3: a3
+ `,
+
+ test({ assert, target }) {
+ const btn = target.querySelector('button');
+ ok(btn);
+
+ flushSync(() => btn.click());
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ reverse
+ 3: a3
+ 2: a2
+ 1: a1
+ `
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/each-keyed-computed-destructuring-key/main.svelte b/packages/svelte/tests/runtime-runes/samples/each-keyed-computed-destructuring-key/main.svelte
new file mode 100644
index 0000000000..6885a6918e
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/each-keyed-computed-destructuring-key/main.svelte
@@ -0,0 +1,12 @@
+
+
+ options.reverse()}>reverse
+