Compare commits

...

10 Commits

Author SHA1 Message Date
MK
e0ba8ef39d 3.2.0
Some checks failed
Node.js CI / build (18.x) (push) Has been cancelled
Node.js CI / build (20.x) (push) Has been cancelled
Node.js CI / build (22.x) (push) Has been cancelled
2026-03-28 13:39:25 +08:00
killa
2503a1fb14 feat: defer AsyncLocalStorage creation for v8 startup snapshots (#1946)
## Summary

- Defer `AsyncLocalStorage` creation when
`v8.startupSnapshot.isBuildingSnapshot()` is true, making Koa compatible
with Node.js startup snapshots
- Register a `v8.startupSnapshot.addDeserializeCallback` to properly
initialize `ctxStorage` after snapshot restoration
- Extract `getAsyncLocalStorage()` helper to consolidate creation logic

## Test plan

- [x] All 429 existing tests pass with 0 failures
- [x] `currentContext` tests verify both `asyncLocalStorage: true` and
custom `AsyncLocalStorage` instance paths work correctly
- [x] Normal (non-snapshot) code path is unchanged —
`v8.startupSnapshot?.isBuildingSnapshot?.()` returns `undefined` in
regular execution

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 13:37:53 +08:00
Copilot
d3ea8bf964 chore: Add workflow_dispatch trigger to npm-publish workflow (#1930)
Enables manual triggering of the npm publish workflow with a specific
tag input, so tags created from non-default branches (e.g. `v2.x`) can
be published.

- Added `workflow_dispatch` trigger with a required `tag` string input
- Updated checkout step to use `inputs.tag || github.ref` — manual runs
check out the specified tag, tag-push runs retain existing behavior

Note: GitHub Actions `workflow_dispatch` does not support dynamic
dropdowns, so the tag is a free-text input field.

<!-- START COPILOT CODING AGENT TIPS -->
---

🔒 GitHub Advanced Security automatically protects Copilot coding agent
pull requests. You can protect all pull requests by enabling Advanced
Security for your repositories. [Learn more about Advanced
Security.](https://gh.io/cca-advanced-security)

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: fengmk2 <156269+fengmk2@users.noreply.github.com>
2026-02-25 21:53:24 +08:00
dependabot[bot]
3b0508e8d0 build(deps-dev): bump qs from 6.14.1 to 6.14.2 (#1927)
Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.14.2.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/ljharb/qs/blob/main/CHANGELOG.md">qs's
changelog</a>.</em></p>
<blockquote>
<h2><strong>6.14.2</strong></h2>
<ul>
<li>[Fix] <code>parse</code>: mark overflow objects for indexed notation
exceeding <code>arrayLimit</code> (<a
href="https://redirect.github.com/ljharb/qs/issues/546">#546</a>)</li>
<li>[Fix] <code>arrayLimit</code> means max count, not max index, in
<code>combine</code>/<code>merge</code>/<code>parseArrayValue</code></li>
<li>[Fix] <code>parse</code>: throw on <code>arrayLimit</code> exceeded
with indexed notation when <code>throwOnLimitExceeded</code> is true (<a
href="https://redirect.github.com/ljharb/qs/issues/529">#529</a>)</li>
<li>[Fix] <code>parse</code>: enforce <code>arrayLimit</code> on
<code>comma</code>-parsed values</li>
<li>[Fix] <code>parse</code>: fix error message to reflect arrayLimit as
max index; remove extraneous comments (<a
href="https://redirect.github.com/ljharb/qs/issues/545">#545</a>)</li>
<li>[Robustness] avoid <code>.push</code>, use <code>void</code></li>
<li>[readme] document that <code>addQueryPrefix</code> does not add
<code>?</code> to empty output (<a
href="https://redirect.github.com/ljharb/qs/issues/418">#418</a>)</li>
<li>[readme] clarify <code>parseArrays</code> and
<code>arrayLimit</code> documentation (<a
href="https://redirect.github.com/ljharb/qs/issues/543">#543</a>)</li>
<li>[readme] replace runkit CI badge with shields.io check-runs
badge</li>
<li>[meta] fix changelog typo (<code>arrayLength</code> →
<code>arrayLimit</code>)</li>
<li>[actions] fix rebase workflow permissions</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bdcf0c7f82"><code>bdcf0c7</code></a>
v6.14.2</li>
<li><a
href="294db90c81"><code>294db90</code></a>
[readme] document that <code>addQueryPrefix</code> does not add
<code>?</code> to empty output</li>
<li><a
href="5c308e5516"><code>5c308e5</code></a>
[readme] clarify <code>parseArrays</code> and <code>arrayLimit</code>
documentation</li>
<li><a
href="6addf8cf73"><code>6addf8c</code></a>
[Fix] <code>parse</code>: mark overflow objects for indexed notation
exceeding <code>arrayLimit</code></li>
<li><a
href="cfc108f662"><code>cfc108f</code></a>
[Fix] <code>arrayLimit</code> means max count, not max index, in
<code>combine</code>/<code>merge</code>/`pars...</li>
<li><a
href="febb64442a"><code>febb644</code></a>
[Fix] <code>parse</code>: throw on <code>arrayLimit</code> exceeded with
indexed notation when `thr...</li>
<li><a
href="f6a7abff1f"><code>f6a7abf</code></a>
[Fix] <code>parse</code>: enforce <code>arrayLimit</code> on
<code>comma</code>-parsed values</li>
<li><a
href="fbc5206c25"><code>fbc5206</code></a>
[Fix] <code>parse</code>: fix error message to reflect arrayLimit as max
index; remove e...</li>
<li><a
href="1b9a8b4e78"><code>1b9a8b4</code></a>
[actions] fix rebase workflow permissions</li>
<li><a
href="2a35775614"><code>2a35775</code></a>
[meta] fix changelog typo (<code>arrayLength</code> →
<code>arrayLimit</code>)</li>
<li>Additional commits viewable in <a
href="https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=qs&package-manager=npm_and_yarn&previous-version=6.14.1&new-version=6.14.2)](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)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

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 <dependency name> 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/koajs/koa/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-25 21:40:47 +08:00
LiShuai (阿木)
fd111407e9 docs: remove dead Job Board links (#1926)
## Checklist

- [x] I have ensured my pull request is not behind the main or master
branch of the original repository.
- [x] I have rebased all commits where necessary so that reviewing this
pull request can be done without having to merge it first.
- [x] I have written a commit message that passes commitlint linting.
- [x] I have ensured that my code changes pass linting tests.
- [x] I have ensured that my code changes pass unit tests.
- [x] I have described my pull request and the reasons for code changes
along with context if necessary.

## Summary
- Remove the "Job Board" section from README as all links are dead (404)

## Details
The `astro.netlify.com` service was added by TJ Holowaychuk in April
2017 as a
private job board sponsorship service for open source projects. This
service is
no longer operational - all URLs return 404 errors.

The affected links pointed to:
- Automattic
- Segment  
- Auth0

The Backers and Sponsors sections using OpenCollective remain functional
and are preserved.

## Test plan
- [x] Verified all `astro.netlify.com` URLs return 404
- [x] Confirmed OpenCollective links still work


Issues: [[docs] Job Board three link and image not show in readme
#1911](https://github.com/koajs/koa/issues/1911)
2026-02-25 21:40:27 +08:00
MK
c5a52e056d 3.1.2 2026-02-25 21:30:15 +08:00
killa
55ab9bab04 Merge commit from fork
When a malformed Host header containing @ symbol (e.g., "evil.com:fake@legitimate.com")
is received, use URL parser to correctly extract the actual host portion instead of
naively splitting by colon which would return attacker-controlled value.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 21:24:26 +08:00
dependabot[bot]
fecd464ae7 build(deps-dev): bump js-yaml from 4.1.0 to 4.1.1 (#1922)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md">js-yaml's
changelog</a>.</em></p>
<blockquote>
<h2>[4.1.1] - 2025-11-12</h2>
<h3>Security</h3>
<ul>
<li>Fix prototype pollution issue in yaml merge (&lt;&lt;)
operator.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="cc482e7759"><code>cc482e7</code></a>
4.1.1 released</li>
<li><a
href="50968b862e"><code>50968b8</code></a>
dist rebuild</li>
<li><a
href="d092d86603"><code>d092d86</code></a>
lint fix</li>
<li><a
href="383665ff42"><code>383665f</code></a>
fix prototype pollution in merge (&lt;&lt;)</li>
<li><a
href="0d3ca7a27b"><code>0d3ca7a</code></a>
README.md: HTTP =&gt; HTTPS (<a
href="https://redirect.github.com/nodeca/js-yaml/issues/678">#678</a>)</li>
<li><a
href="49baadd52a"><code>49baadd</code></a>
doc: 'empty' style option for !!null</li>
<li><a
href="ba3460eb9d"><code>ba3460e</code></a>
Fix demo link (<a
href="https://redirect.github.com/nodeca/js-yaml/issues/618">#618</a>)</li>
<li>See full diff in <a
href="https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=js-yaml&package-manager=npm_and_yarn&previous-version=4.1.0&new-version=4.1.1)](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)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

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 merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> 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/koajs/koa/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-04 00:14:56 +01:00
dependabot[bot]
d2066cf2a5 build(deps): bump content-disposition from 0.5.4 to 1.0.1 (#1917)
Bumps
[content-disposition](https://github.com/jshttp/content-disposition)
from 0.5.4 to 1.0.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/jshttp/content-disposition/releases">content-disposition's
releases</a>.</em></p>
<blockquote>
<h2>1.0.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Remove dependency <code>safe-buffer</code> by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/content-disposition/pull/53">jshttp/content-disposition#53</a></li>
<li>fix: update package.json engines field to reflect minimum supported
node version by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/content-disposition/pull/56">jshttp/content-disposition#56</a></li>
<li>tests: Spelling by <a
href="https://github.com/jsoref"><code>@​jsoref</code></a> in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/59">jshttp/content-disposition#59</a></li>
<li>chore: upgrade scorecard workflow pinned action versions by <a
href="https://github.com/carpasse"><code>@​carpasse</code></a> in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/65">jshttp/content-disposition#65</a></li>
<li>Fix badges by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/content-disposition/pull/55">jshttp/content-disposition#55</a></li>
<li>ci: updated github actions ci workflow by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/content-disposition/pull/69">jshttp/content-disposition#69</a></li>
<li>Replace var with const in example code by <a
href="https://github.com/Binilkks"><code>@​Binilkks</code></a> in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/67">jshttp/content-disposition#67</a></li>
<li>replace <code>mocha</code> and <code>nyc</code> with native node
test runner and <code>c8</code> by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/content-disposition/pull/54">jshttp/content-disposition#54</a></li>
<li>ci: add dependabot by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/content-disposition/pull/73">jshttp/content-disposition#73</a></li>
<li>ci: add CodeQl (SAST) by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/content-disposition/pull/71">jshttp/content-disposition#71</a></li>
<li>build(deps): bump ossf/scorecard-action from 2.4.0 to 2.4.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/75">jshttp/content-disposition#75</a></li>
<li>build(deps): bump github/codeql-action from 3.27.9 to 3.28.18 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/80">jshttp/content-disposition#80</a></li>
<li>build(deps): bump ossf/scorecard-action from 2.4.1 to 2.4.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/81">jshttp/content-disposition#81</a></li>
<li>chore: add funding to package.json by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/jshttp/content-disposition/pull/84">jshttp/content-disposition#84</a></li>
<li>build(deps): bump actions/upload-artifact from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/94">jshttp/content-disposition#94</a></li>
<li>build(deps): bump actions/download-artifact from 4 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/93">jshttp/content-disposition#93</a></li>
<li>build(deps): bump github/codeql-action from 3.28.18 to 4.31.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/92">jshttp/content-disposition#92</a></li>
<li>Release: 1.0.1 by <a
href="https://github.com/UlisesGascon"><code>@​UlisesGascon</code></a>
in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/58">jshttp/content-disposition#58</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a>
made their first contribution in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/53">jshttp/content-disposition#53</a></li>
<li><a href="https://github.com/jsoref"><code>@​jsoref</code></a> made
their first contribution in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/59">jshttp/content-disposition#59</a></li>
<li><a href="https://github.com/Binilkks"><code>@​Binilkks</code></a>
made their first contribution in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/67">jshttp/content-disposition#67</a></li>
<li><a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
made their first contribution in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/75">jshttp/content-disposition#75</a></li>
<li><a
href="https://github.com/UlisesGascon"><code>@​UlisesGascon</code></a>
made their first contribution in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/58">jshttp/content-disposition#58</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/jshttp/content-disposition/compare/v1.0.0...v1.0.1">https://github.com/jshttp/content-disposition/compare/v1.0.0...v1.0.1</a></p>
<h2>1.0.0</h2>
<h2>Breaking Changes</h2>
<ul>
<li>drop support to node &lt;18 versions <a
href="https://redirect.github.com/jshttp/content-disposition/pull/50">jshttp/content-disposition#50</a></li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>Fix CI GH action by <a
href="https://github.com/carpasse"><code>@​carpasse</code></a> in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/48">jshttp/content-disposition#48</a></li>
<li>Add OSSF scorecard pipeline by <a
href="https://github.com/carpasse"><code>@​carpasse</code></a> in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/49">jshttp/content-disposition#49</a></li>
<li>Replace deprecated String.prototype.substr() by <a
href="https://github.com/CommanderRoot"><code>@​CommanderRoot</code></a>
in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/42">jshttp/content-disposition#42</a></li>
<li>fix(ci)!:drop node &lt;18 and update ci by <a
href="https://github.com/wesleytodd"><code>@​wesleytodd</code></a> in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/50">jshttp/content-disposition#50</a></li>
<li>Support decode 'utf8' (no dash) by <a
href="https://github.com/alexstrat"><code>@​alexstrat</code></a> in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/13">jshttp/content-disposition#13</a></li>
<li>1.x Staging PR by <a
href="https://github.com/wesleytodd"><code>@​wesleytodd</code></a> in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/51">jshttp/content-disposition#51</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/CommanderRoot"><code>@​CommanderRoot</code></a>
made their first contribution in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/42">jshttp/content-disposition#42</a></li>
<li><a
href="https://github.com/wesleytodd"><code>@​wesleytodd</code></a> made
their first contribution in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/50">jshttp/content-disposition#50</a></li>
<li><a href="https://github.com/alexstrat"><code>@​alexstrat</code></a>
made their first contribution in <a
href="https://redirect.github.com/jshttp/content-disposition/pull/13">jshttp/content-disposition#13</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/jshttp/content-disposition/compare/v0.5.4...v1.0.0">https://github.com/jshttp/content-disposition/compare/v0.5.4...v1.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/jshttp/content-disposition/blob/master/HISTORY.md">content-disposition's
changelog</a>.</em></p>
<blockquote>
<h1>1.0.1 / 2025-11-18</h1>
<ul>
<li>Updated <code>engines</code> field to Node@18 or higher (fixed
reference, see 1.0.0)</li>
<li>Remove dependency <code>safe-buffer</code></li>
</ul>
<h1>1.0.0 / 2024-08-31</h1>
<ul>
<li>drop node &lt;18</li>
<li>allow utf8 as alias for utf-8</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b56faefa03"><code>b56faef</code></a>
1.0.1 (<a
href="https://redirect.github.com/jshttp/content-disposition/issues/58">#58</a>)</li>
<li><a
href="0839a62f09"><code>0839a62</code></a>
build(deps): bump github/codeql-action from 3.28.18 to 4.31.2 (<a
href="https://redirect.github.com/jshttp/content-disposition/issues/92">#92</a>)</li>
<li><a
href="5badd4b7da"><code>5badd4b</code></a>
build(deps): bump actions/download-artifact from 4 to 6 (<a
href="https://redirect.github.com/jshttp/content-disposition/issues/93">#93</a>)</li>
<li><a
href="4162dbd398"><code>4162dbd</code></a>
build(deps): bump actions/upload-artifact from 4 to 5 (<a
href="https://redirect.github.com/jshttp/content-disposition/issues/94">#94</a>)</li>
<li><a
href="b2ce0fbd8b"><code>b2ce0fb</code></a>
chore: add funding to package.json (<a
href="https://redirect.github.com/jshttp/content-disposition/issues/84">#84</a>)</li>
<li><a
href="f0c058a81d"><code>f0c058a</code></a>
build(deps): bump ossf/scorecard-action from 2.4.1 to 2.4.2 (<a
href="https://redirect.github.com/jshttp/content-disposition/issues/81">#81</a>)</li>
<li><a
href="1f4451c299"><code>1f4451c</code></a>
build(deps): bump github/codeql-action from 3.27.9 to 3.28.18 (<a
href="https://redirect.github.com/jshttp/content-disposition/issues/80">#80</a>)</li>
<li><a
href="765ef23860"><code>765ef23</code></a>
build(deps): bump ossf/scorecard-action from 2.4.0 to 2.4.1 (<a
href="https://redirect.github.com/jshttp/content-disposition/issues/75">#75</a>)</li>
<li><a
href="21c68cd454"><code>21c68cd</code></a>
ci: add CodeQl (SAST) (<a
href="https://redirect.github.com/jshttp/content-disposition/issues/71">#71</a>)</li>
<li><a
href="8fec68dca4"><code>8fec68d</code></a>
ci: add dependabot (<a
href="https://redirect.github.com/jshttp/content-disposition/issues/73">#73</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/jshttp/content-disposition/compare/v0.5.4...v1.0.1">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~ulisesgascon">ulisesgascon</a>, a new
releaser for content-disposition since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=content-disposition&package-manager=npm_and_yarn&previous-version=0.5.4&new-version=1.0.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

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 merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> 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)


</details>

> **Note**
> Automatic rebases have been disabled on this pull request as it has
been open for over 30 days.

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-04 00:10:11 +01:00
Josh Hsieh
8694a06eb6 docs: use correct term "Server-Sent Events" in guide (#1920)
This PR updates the terminology in `docs/guide.md` from **"Server-Side
Events"** to the correct term **"Server-Sent Events" (SSE)**.

Changes:

- Rename the section heading from `Server-Side Events` to `Server-Sent
Events`.
- Update the description text to use “server-sent events” consistently.

This aligns the docs with the standard naming used in the HTML
specification and MDN Web Docs, and should make it easier for readers to
search for related resources (e.g. “Server-Sent Events”, “SSE”,
`EventSource`).

## Checklist

- [x] I have ensured my pull request is not behind the main or master
branch of the original repository.
- [x] I have rebased all commits where necessary so that reviewing this
pull request can be done without having to merge it first.
- [x] I have written a commit message that passes commitlint linting.
- [x] I have ensured that my code changes pass linting tests.
- [x] I have ensured that my code changes pass unit tests.
- [x] I have described my pull request and the reasons for code changes
along with context if necessary.
2026-01-04 00:06:24 +01:00
10 changed files with 214 additions and 41 deletions

View File

@@ -1,6 +1,8 @@
name: NPM Publish
# Trigger only when tags matching semver format are pushed
# Trigger when tags matching semver format are pushed, or manually via workflow_dispatch.
# Manual triggers allow selecting a specific tag to publish (e.g. tags from the v2.x branch).
#
# Patterns match common semver formats:
# - v1.0.0 (standard)
# - v1.0.0-alpha (pre-release)
@@ -16,6 +18,12 @@ name: NPM Publish
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-[a-zA-Z0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-[a-zA-Z0-9]+.[0-9]+'
workflow_dispatch:
inputs:
tag:
description: 'Git tag to checkout and publish (e.g. v2.15.4)'
required: true
type: string
# Permissions for NPM trusted publishing with provenance
permissions:
@@ -27,6 +35,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
ref: ${{ inputs.tag || github.ref }}
- name: Setup Node.js
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 #v6

View File

@@ -189,14 +189,6 @@ See [AUTHORS](AUTHORS).
- [中文文档 v2.x](https://github.com/demopark/koa-docs-Zh-CN)
- __[#koajs]__ on freenode
## Job Board
Looking for a career upgrade?
<a href="https://astro.netlify.com/automattic"><img src="https://astro.netlify.com/static/automattic.png"></a>
<a href="https://astro.netlify.com/segment"><img src="https://astro.netlify.com/static/segment.png"></a>
<a href="https://astro.netlify.com/auth0"><img src="https://astro.netlify.com/static/auth0.png"/></a>
## Backers
Support us with a monthly donation and help us continue our activities.

View File

@@ -1,6 +1,7 @@
'use strict'
const { describe, it } = require('node:test')
const { describe, it, beforeEach, afterEach } = require('node:test')
const v8 = require('node:v8')
const request = require('supertest')
const assert = require('node:assert/strict')
const Koa = require('../..')
@@ -113,4 +114,85 @@ describe('app.currentContext', () => {
await request(app.callback()).get('/').expect('ok')
assert(app.currentContext === undefined)
})
describe('v8 startup snapshot', () => {
let originalStartupSnapshot
beforeEach(() => {
originalStartupSnapshot = v8.startupSnapshot
})
afterEach(() => {
v8.startupSnapshot = originalStartupSnapshot
})
it('should defer AsyncLocalStorage creation when building snapshot', () => {
let deserializeCallback
v8.startupSnapshot = {
isBuildingSnapshot: () => true,
addDeserializeCallback: (cb, data) => {
deserializeCallback = { cb, data }
}
}
const app = new Koa({ asyncLocalStorage: true })
assert.strictEqual(app.ctxStorage, null)
assert(deserializeCallback, 'deserialize callback should be registered')
// simulate snapshot deserialization
deserializeCallback.cb(deserializeCallback.data)
assert(app.ctxStorage instanceof AsyncLocalStorage)
})
it('should defer with custom AsyncLocalStorage when building snapshot', () => {
const customStorage = new AsyncLocalStorage()
let deserializeCallback
v8.startupSnapshot = {
isBuildingSnapshot: () => true,
addDeserializeCallback: (cb, data) => {
deserializeCallback = { cb, data }
}
}
const app = new Koa({ asyncLocalStorage: customStorage })
assert.strictEqual(app.ctxStorage, null)
// simulate snapshot deserialization
deserializeCallback.cb(deserializeCallback.data)
assert(app.ctxStorage instanceof AsyncLocalStorage)
assert.strictEqual(app.ctxStorage, customStorage)
})
it('should work normally after deserialization', async () => {
let deserializeCallback
v8.startupSnapshot = {
isBuildingSnapshot: () => true,
addDeserializeCallback: (cb, data) => {
deserializeCallback = { cb, data }
}
}
const app = new Koa({ asyncLocalStorage: true })
// simulate snapshot deserialization
deserializeCallback.cb(deserializeCallback.data)
app.use(async ctx => {
assert(ctx === app.currentContext)
ctx.body = 'ok'
})
await request(app.callback()).get('/').expect('ok')
assert(app.currentContext === undefined)
})
it('should not defer when not building snapshot', () => {
v8.startupSnapshot = {
isBuildingSnapshot: () => false
}
const app = new Koa({ asyncLocalStorage: true })
assert(app.ctxStorage instanceof AsyncLocalStorage)
})
})
})

View File

@@ -94,4 +94,48 @@ describe('req.host', () => {
})
})
})
describe('with Host header containing @', () => {
it('should correctly parse host from userinfo@host format', () => {
const req = request()
req.header.host = 'evil.com:fake@legitimate.com'
assert.strictEqual(req.host, 'legitimate.com')
})
it('should correctly parse host from user@host format', () => {
const req = request()
req.header.host = 'user@example.com'
assert.strictEqual(req.host, 'example.com')
})
it('should correctly parse host with port from userinfo@host:port format', () => {
const req = request()
req.header.host = 'user:pass@example.com:8080'
assert.strictEqual(req.host, 'example.com:8080')
})
it('should correctly parse @ in X-Forwarded-Host when proxy is trusted', () => {
const req = request()
req.app.proxy = true
req.header['x-forwarded-host'] = 'evil.com:fake@legitimate.com'
req.header.host = 'foo.com'
assert.strictEqual(req.host, 'legitimate.com')
})
it('should correctly parse @ in :authority on HTTP/2', () => {
const req = request({
httpVersionMajor: 2,
httpVersion: '2.0'
})
req.header[':authority'] = 'evil.com:fake@legitimate.com'
req.header.host = 'foo.com'
assert.strictEqual(req.host, 'legitimate.com')
})
it('should return empty string for invalid host with @', () => {
const req = request()
req.header.host = 'user@'
assert.strictEqual(req.host, '')
})
})
})

View File

@@ -70,4 +70,32 @@ describe('req.hostname', () => {
})
})
})
describe('with Host header containing @', () => {
it('should correctly parse hostname from userinfo@host format', () => {
const req = request()
req.header.host = 'evil.com:fake@legitimate.com'
assert.strictEqual(req.hostname, 'legitimate.com')
})
it('should correctly parse hostname from user@host format', () => {
const req = request()
req.header.host = 'user@example.com'
assert.strictEqual(req.hostname, 'example.com')
})
it('should correctly parse hostname with port from userinfo@host:port format', () => {
const req = request()
req.header.host = 'user:pass@example.com:8080'
assert.strictEqual(req.hostname, 'example.com')
})
it('should correctly parse @ in X-Forwarded-Host when proxy is trusted', () => {
const req = request()
req.app.proxy = true
req.header['x-forwarded-host'] = 'evil.com:fake@legitimate.com'
req.header.host = 'foo.com'
assert.strictEqual(req.hostname, 'legitimate.com')
})
})
})

View File

@@ -16,7 +16,7 @@
- [Async operations](#async-operations)
- [Debugging Koa](#debugging-koa)
- [HTTP2](#http2)
- [Server-Side Events](#server-side-events)
- [Server-Sent Events](#server-sent-events)
## Writing Middleware
@@ -272,9 +272,9 @@ const server = http2.createSecureServer(serverOptions, onRequestHandler);
server.listen(3000);
```
## Server-Side Events
## Server-Sent Events
An example of using server-side events with Koa:
An example of using server-sent events with Koa:
```js
import { PassThrough } from 'node:stream'

View File

@@ -4,6 +4,7 @@
* Module dependencies.
*/
const util = require('node:util')
const v8 = require('node:v8')
const debug = util.debuglog('koa:application')
const Emitter = require('node:events')
const Stream = require('node:stream')
@@ -40,6 +41,13 @@ const only = require('./only.js')
* Inherits from `Emitter.prototype`.
*/
function getAsyncLocalStorage (options) {
if (options.asyncLocalStorage instanceof AsyncLocalStorage) {
return options.asyncLocalStorage
}
return new AsyncLocalStorage()
}
module.exports = class Application extends Emitter {
/**
* Initialize a new `Application`.
@@ -81,10 +89,13 @@ module.exports = class Application extends Emitter {
this[util.inspect.custom] = this.inspect
}
if (options.asyncLocalStorage) {
if (options.asyncLocalStorage instanceof AsyncLocalStorage) {
this.ctxStorage = options.asyncLocalStorage
if (v8.startupSnapshot?.isBuildingSnapshot?.()) {
this.ctxStorage = null
v8.startupSnapshot.addDeserializeCallback(({ app, options }) => {
app.ctxStorage = getAsyncLocalStorage(options)
}, { app: this, options })
} else {
this.ctxStorage = new AsyncLocalStorage()
this.ctxStorage = getAsyncLocalStorage(options)
}
}
}

View File

@@ -256,7 +256,17 @@ module.exports = {
if (!host) host = this.get('Host')
}
if (!host) return ''
return splitCommaSeparatedValues(host, 1)[0]
host = splitCommaSeparatedValues(host, 1)[0]
// Host header may contain userinfo (e.g., "user@host") which is invalid per RFC 7230.
// Use URL parser to correctly extract the host portion.
if (host.includes('@')) {
try {
host = new URL(`http://${host}`).host
} catch (e) {
return ''
}
}
return host
},
/**

40
package-lock.json generated
View File

@@ -1,16 +1,16 @@
{
"name": "koa",
"version": "3.1.1",
"version": "3.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "koa",
"version": "3.1.1",
"version": "3.2.0",
"license": "MIT",
"dependencies": {
"accepts": "^1.3.8",
"content-disposition": "~0.5.4",
"content-disposition": "~1.0.1",
"content-type": "^1.0.5",
"cookies": "~0.9.1",
"delegates": "^1.0.0",
@@ -431,7 +431,6 @@
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -938,15 +937,16 @@
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
"integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/content-type": {
@@ -1447,7 +1447,6 @@
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -1655,7 +1654,6 @@
"integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.8",
@@ -1723,7 +1721,6 @@
"integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"builtins": "^5.0.1",
"eslint-plugin-es": "^4.1.0",
@@ -1750,7 +1747,6 @@
"integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==",
"dev": true,
"license": "ISC",
"peer": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@@ -1767,7 +1763,6 @@
"integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"array-includes": "^3.1.8",
"array.prototype.findlast": "^1.2.5",
@@ -3136,9 +3131,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3909,9 +3904,9 @@
}
},
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -4141,6 +4136,7 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",

View File

@@ -1,6 +1,6 @@
{
"name": "koa",
"version": "3.1.1",
"version": "3.2.0",
"description": "Koa web app framework",
"main": "lib/application.js",
"exports": {
@@ -37,7 +37,7 @@
"license": "MIT",
"dependencies": {
"accepts": "^1.3.8",
"content-disposition": "~0.5.4",
"content-disposition": "~1.0.1",
"content-type": "^1.0.5",
"cookies": "~0.9.1",
"delegates": "^1.0.0",