diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000000000..f53ec0ae2900b --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,11 @@ +# Changesets + +Changesets in this directory describe user-facing changes to published packages. Add one by running: + +```shell +pnpm changeset +``` + +Commit the generated Markdown file with the package change. The publishing workflow consumes these files to update package versions and changelogs before publishing to npm. + +See the [package publishing guide](../docs/package-publishing.md) for the complete release process. diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000000000..19d462977d6da --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json", + "changelog": [ + "@changesets/changelog-github", + { "repo": "nodejs/nodejs.org" } + ], + "commit": false, + "fixed": [], + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.changeset/import-aliases-no-preconditions.md b/.changeset/import-aliases-no-preconditions.md new file mode 100644 index 0000000000000..252399d58c0f9 --- /dev/null +++ b/.changeset/import-aliases-no-preconditions.md @@ -0,0 +1,8 @@ +--- +'@node-core/ui-components': patch +--- + +Resolve the `#ui/*` import alias to the compiled output by default, so consumers +of the published package no longer need to opt into a bundler-specific +`rolldown` resolution condition. The uncompiled sources stay reachable through +the new `source` condition, which this repository's own tooling opts into. diff --git a/.changeset/shiki-more-more.md b/.changeset/shiki-more-more.md new file mode 100644 index 0000000000000..9f206d3fe804a --- /dev/null +++ b/.changeset/shiki-more-more.md @@ -0,0 +1,5 @@ +--- +'@node-core/rehype-shiki': minor +--- + +Add support for all Shiki languages when the `minimal` preset is not selected. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8e5899448d137..158da6920f6c7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -17,7 +17,7 @@ apps/site/next.config.mjs @nodejs/web-infra apps/site/next.dynamic.mjs @nodejs/web-infra apps/site/middleware.ts @nodejs/web-infra apps/site/navigation.mjs @nodejs/web-infra -apps/site/playwright.config.ts @nodejs/web-infra +apps/site/playwright.config.mjs @nodejs/web-infra # Package Ecosystem package.json @nodejs/nodejs-website @@ -27,19 +27,19 @@ turbo.json @nodejs/nodejs-website @nodejs/web-infra crowdin.yml @nodejs/web-infra apps/site/redirects.json @nodejs/web-infra apps/site/site.json @nodejs/web-infra -apps/site/wrangler.jsonc @nodejs/web-infra -apps/site/open-next.config.ts @nodejs/web-infra -apps/site/redirects.json @nodejs/web-infra +platforms @nodejs/web-infra # Critical Documents LICENSE @nodejs/tsc CONTRIBUTING.md @nodejs/nodejs-website @nodejs/web-infra docs @nodejs/nodejs-website @nodejs/web-infra SECURITY.md @nodejs/security-wg +apps/site/public/.well-known/security.txt @nodejs/security-wg # Node.js Release Blog Posts apps/site/pages/en/blog/release @nodejs/releasers apps/site/pages/en/blog/announcements @nodejs/releasers +apps/site/pages/en/blog/vulnerability @nodejs/releasers # Marketing apps/site/pages/en/about/partners.mdx @nodejs/marketing diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d9c83dd2bac88..bb9dc3da2e04c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,7 +10,7 @@ updates: - 'github_actions:pull-request' - 'auto-merge' commit-message: - prefix: meta + prefix: chore(deps) cooldown: default-days: 7 open-pull-requests-limit: 10 @@ -26,7 +26,7 @@ updates: - 'github_actions:pull-request' - 'auto-merge' commit-message: - prefix: meta + prefix: chore(deps) cooldown: default-days: 7 groups: diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index cb81df456bc1f..2fa6e29c31ab4 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d16b8ab584dc..d822f64cf115a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,8 +44,6 @@ jobs: with: pnpm: true use-version-file: true - # We only want to install required production packages - install-flags: --prod - name: Use GNU tar instead BSD tar # This ensures that we use GNU `tar` which is more efficient for extracting caches's @@ -53,7 +51,7 @@ jobs: shell: cmd run: echo C:\Program Files\Git\usr\bin>>"%GITHUB_PATH%" - - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ${{ github.workspace }}/apps/site/.next/cache key: ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }} @@ -61,24 +59,24 @@ jobs: - name: Build Next.js # We want a ISR build on CI to ensure that regular Next.js builds work as expected. - run: node_modules/.bin/turbo build --cache-dir=.turbo/cache + run: node_modules/.bin/turbo build --filter=@node-core/website --cache-dir=.turbo/cache env: # We want to ensure we have enough RAM allocated to the Node.js process # this should be a last resort in case by any chances the build memory gets too high # but in general this should never happen NODE_OPTIONS: '--max_old_space_size=4096' - # See https://github.com/vercel/next.js/pull/81318 + # See https://github.com/vercel/next.js/pull/90949 TURBOPACK_STATS: ${{ matrix.os == 'ubuntu-latest' }} - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: matrix.os == 'ubuntu-latest' with: name: webpack-stats - path: apps/site/.next/server/webpack-stats.json + path: apps/site/.next/diagnostics/route-bundle-stats.json - name: Build Next.js (Static Export) # We want to generate a static build, as it is a requirement of our website. - run: node_modules/.bin/turbo deploy --cache-dir=.turbo/cache + run: node_modules/.bin/turbo deploy --filter=@node-core/website --cache-dir=.turbo/cache env: # We want to ensure we have enough RAM allocated to the Node.js process # this should be a last resort in case by any chances the build memory gets too high @@ -87,3 +85,73 @@ jobs: # We want to ensure that static exports for all locales do not occur on `pull_request` events # TODO: The output of this is too large, and it crashes the GitHub Runner NEXT_PUBLIC_STATIC_EXPORT_LOCALE: false # ${{ github.event_name == 'push' }} + + compare-bundle-size: + name: Compare Bundle Size + runs-on: ubuntu-latest + needs: build + if: github.event_name == 'pull_request' + + steps: + - name: Harden Runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Git Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Download Stats (HEAD) + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: webpack-stats + path: head-stats + + - name: Get Run ID from BASE + id: base-run + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + GH_TOKEN: ${{ github.token }} + run: | + ID=$(gh run list -c "$BASE_SHA" -w build.yml -s success -L 1 --json databaseId --jq ".[].databaseId") + echo "run_id=$ID" >> "$GITHUB_OUTPUT" + + - name: Download Stats (BASE) + id: base-stats + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: webpack-stats + path: base-stats + run-id: ${{ steps.base-run.outputs.run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Compare Bundle Size + id: compare-bundle-size + if: steps.base-stats.outcome == 'success' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + HEAD_STATS_PATH: ./head-stats/route-bundle-stats.json + BASE_STATS_PATH: ./base-stats/route-bundle-stats.json + with: + script: | + const { compare } = await import('${{github.workspace}}/apps/site/scripts/compare-size/index.mjs') + await compare({core}) + + - name: Prepare Comment + if: steps.base-stats.outcome == 'success' + env: + COMMENT: ${{ steps.compare-bundle-size.outputs.comment }} + run: | + mkdir -p pr-comment + printf '%s' "$COMMENT" > pr-comment/comment.md + printf '%s' 'compare_bundle_size' > pr-comment/tag.txt + + - name: Upload Comment + if: steps.base-stats.outcome == 'success' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: pr-comment + path: pr-comment/ diff --git a/.github/workflows/bump-security-txt-expiry.yml b/.github/workflows/bump-security-txt-expiry.yml new file mode 100644 index 0000000000000..97cb139347bf1 --- /dev/null +++ b/.github/workflows/bump-security-txt-expiry.yml @@ -0,0 +1,65 @@ +# Security Notes +# Only selected Actions are allowed within this repository. Please refer to (https://github.com/nodejs/nodejs.org/settings/actions) +# for the full list of available actions. If you want to add a new one, please reach out a maintainer with Admin permissions. +# REVIEWERS, please always double-check security practices before merging a PR that contains Workflow changes!! +# AUTHORS, please only use actions with explicit SHA references, and avoid using `@master` or `@main` references or `@version` tags. + +name: Bump security.txt Expiry + +on: + workflow_dispatch: + schedule: + - cron: '0 8 1 1 *' # once a year, Jan 1 at 08:00 UTC + +permissions: {} + +jobs: + bump-expiry: + runs-on: ubuntu-latest + if: github.repository_owner == 'nodejs' + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Bump the Expires year + id: update-expiry + env: + SECURITY_TXT: apps/site/public/.well-known/security.txt + run: | + set -euo pipefail + old=$(sed -n 's/^Expires: *//p' "$SECURITY_TXT") + year=${old%%-*} + new="$((year + 1))${old#"$year"}" + sed -i "s|^Expires: .*|Expires: ${new}|" "$SECURITY_TXT" + { + echo "old=${old}" + echo "new=${new}" + } >> "$GITHUB_OUTPUT" + + - name: Open pull request + uses: gr2m/create-or-update-pull-request-action@b65137ca591da0b9f43bad7b24df13050ea45d1b # v1.10.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + update-pull-request-title-and-body: true + branch: chore/update-security-txt + body: | + Updates the `Expiry` on our [`security.txt`](https://nodejs.org/.well-known/security.txt) from ${{ steps.update-expiry.outputs.old }} to ${{ steps.update-expiry.outputs.new }} + + Before merging, please explicitly confirm that: + - The security contact listed is still correct and actively monitored + - The linked security policy is still accurate + - The project's preferred vulnerability disclosure path has not changed + + cc @nodejs/security-wg + + Check this workflow's logs at ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}. + commit-message: 'meta: update security `Expiry` from ${{ steps.update-expiry.outputs.old }} to ${{ steps.update-expiry.outputs.new }}' + title: 'meta: update security `Expiry` from ${{ steps.update-expiry.outputs.old }} to ${{ steps.update-expiry.outputs.new }}' + draft: true + labels: security diff --git a/.github/workflows/bundle-compare.yml b/.github/workflows/bundle-compare.yml deleted file mode 100644 index 9666ba631cbc3..0000000000000 --- a/.github/workflows/bundle-compare.yml +++ /dev/null @@ -1,77 +0,0 @@ -name: Compare Bundle Size - -on: - workflow_run: - workflows: ['Build'] - types: [completed] - -permissions: - contents: read - actions: read - -concurrency: - group: ${{ github.workflow }}-${{ github.event.workflow_run.id }} - cancel-in-progress: true - -jobs: - compare: - name: Compare Bundle Stats - runs-on: ubuntu-latest - permissions: - # Required to comment on pull requests - pull-requests: write - if: github.event.workflow_run.event == 'pull_request' - - steps: - - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 - with: - egress-policy: audit - - - name: Git Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Download Stats (HEAD) - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: webpack-stats - path: head-stats - run-id: ${{ github.event.workflow_run.id }} - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Get Run ID from BASE - id: base-run - env: - WORKFLOW_ID: ${{ github.event.workflow_run.workflow_id }} - GH_TOKEN: ${{ github.token }} - run: | - ID=$(gh run list -c $GITHUB_SHA -w $WORKFLOW_ID -L 1 --json databaseId --jq ".[].databaseId") - echo "run_id=$ID" >> $GITHUB_OUTPUT - - - name: Download Stats (BASE) - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: webpack-stats - path: base-stats - run-id: ${{ steps.base-run.outputs.run_id }} - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Compare Bundle Size - id: compare-bundle-size - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - HEAD_STATS_PATH: ./head-stats/webpack-stats.json - BASE_STATS_PATH: ./base-stats/webpack-stats.json - with: - script: | - const { compare } = await import('${{github.workspace}}/apps/site/scripts/compare-size/index.mjs') - await compare({core}) - - - name: Add Comment to PR - uses: thollander/actions-comment-pull-request@e2c37e53a7d2227b61585343765f73a9ca57eda9 # v3.0.0 - with: - comment-tag: 'compare_bundle_size' - message: ${{ steps.compare-bundle-size.outputs.comment }} - pr-number: ${{ github.event.workflow_run.pull_requests[0].number }} diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index 6eb0f6432d4bd..48af4d5ab7836 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -14,14 +14,12 @@ on: paths: - packages/ui-components/** - .github/workflows/chromatic.yml - pull_request_target: + pull_request: branches: - main paths: - packages/ui-components/** - .github/workflows/chromatic.yml - types: - - labeled workflow_dispatch: defaults: @@ -42,9 +40,8 @@ jobs: # We only need to run Storybook Builds and Storybook Visual Regression Tests within Pull Requests that actually # introduce changes to the Storybook. Hence, we skip running these on Crowdin PRs and Dependabot PRs if: | - github.event_name != 'pull_request_target' || + github.event_name != 'pull_request' || ( - github.event.label.name == 'github_actions:pull-request' && github.actor != 'dependabot[bot]' && github.event.pull_request.head.ref != 'chore/crowdin' ) @@ -66,10 +63,10 @@ jobs: - name: Start Visual Regression Tests (Chromatic) # This assigns the Environment Deployment for Storybook id: chromatic-deploy - uses: chromaui/action@f191a0224b10e1a38b2091cefb7b7a2337009116 # v16.0.0 + uses: chromaui/action@14cfaef73576e69f95f47f60058063f46ca38719 # v18.1.0 with: workingDir: packages/ui-components buildScriptName: storybook:build - projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + projectToken: ${{ vars.CHROMATIC_PROJECT_TOKEN }} exitOnceUploaded: true onlyChanged: true diff --git a/.github/workflows/leave-comment.yml b/.github/workflows/leave-comment.yml new file mode 100644 index 0000000000000..d79ec042481d5 --- /dev/null +++ b/.github/workflows/leave-comment.yml @@ -0,0 +1,97 @@ +name: Leave Comment + +on: + workflow_run: # zizmor: ignore[dangerous-triggers] + # While it's true (as reported by Zizmor) that many `workflow_run` + # workflows are unsafe, we are using it purely to paste a code comment + # on a pull request. If an attacker were to attempt to modify the _only_ + # file of data we collect from their unsafe code, the worst they could do + # is leave a comment on their own PR, which has no harmful impact + # whatsover. + # + # Any Workflow that uploads a `pr-comment` artifact should be listed here + workflows: ['Build', 'Lighthouse'] + types: [completed] + +permissions: + contents: read + actions: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.workflow_run.id }} + cancel-in-progress: true + +jobs: + leave-comment: + name: Leave Comment + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Download Comment Artifact + # The Workflow may not have produced a comment (e.g. the comparison was skipped), so this is + # allowed to fail and every subsequent step is gated on it having succeeded. + id: download + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: pr-comment + path: pr-comment + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Resolve Pull Request Number + id: pr + if: steps.download.outcome == 'success' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const run = context.payload.workflow_run; + + // 1. For same-repo Pull Requests the run is already linked to its PR(s). + if (run.pull_requests && run.pull_requests.length) { + core.setOutput('number', run.pull_requests[0].number); + return; + } + + // 2. For forks that list is empty, so find the open Pull Request who has the + // correct branch information + const match = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + head: `${context.payload.workflow_run.head_repository.owner.login}:${context.payload.workflow_run.head_branch}`, + sort: 'updated', + direction: 'desc', + per_page: 1, + }).then(r => r.data[0]); + + if (!match) { + core.info(`No open pull request found for HEAD ${run.head_sha}`); + return; + } + + core.setOutput('number', match.number); + + - name: Read Comment Tag + id: meta + if: steps.download.outcome == 'success' + run: | + tag="$(tr -cd 'A-Za-z0-9_-' < pr-comment/tag.txt)" + echo "tag=$tag" >> "$GITHUB_OUTPUT" + + - name: Add Comment to PR + # The comment body is untrusted markdown, so it is passed as a file (data) rather than + # interpolated into an expression or shell command. + if: steps.download.outcome == 'success' && steps.pr.outputs.number != '' + uses: thollander/actions-comment-pull-request@e2c37e53a7d2227b61585343765f73a9ca57eda9 # v3.0.0 + with: + file-path: pr-comment/comment.md + comment-tag: ${{ steps.meta.outputs.tag }} + pr-number: ${{ steps.pr.outputs.number }} diff --git a/.github/workflows/lighthouse.yml b/.github/workflows/lighthouse.yml index 9e531d44e11ba..b49f72e712bd9 100644 --- a/.github/workflows/lighthouse.yml +++ b/.github/workflows/lighthouse.yml @@ -1,5 +1,7 @@ # Security Notes -# This workflow uses `pull_request_target`, so will run against all PRs automatically (without approval), be careful with allowing any user-provided code to be run here +# This Workflow runs in the untrusted `pull_request` context and therefore must not rely on any +# repository secrets. It does not comment on the Pull Request itself; instead it uploads a +# `pr-comment` artifact which the trusted `Leave Comment` Workflow posts once this Workflow completes. # Only selected Actions are allowed within this repository. Please refer to (https://github.com/nodejs/nodejs.org/settings/actions) # for the full list of available actions. If you want to add a new one, please reach out a maintainer with Admin permissions. # REVIEWERS, please always double-check security practices before merging a PR that contains Workflow changes!! @@ -9,7 +11,7 @@ name: Lighthouse on: - pull_request_target: + pull_request: branches: - main types: @@ -36,13 +38,10 @@ jobs: github.event.label.name == 'github_actions:pull-request' name: Lighthouse Report runs-on: ubuntu-latest - permissions: - # Required by `thollander/actions-comment-pull-request` - pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -55,21 +54,11 @@ jobs: check_interval: 10 # check every 10 seconds - name: Git Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + # Only needed for the Lighthouse formatting script; no credentials are persisted. + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - # Provides the Pull Request commit SHA or the GitHub merge group ref - ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.ref }} persist-credentials: false - - name: Add Comment to PR - # Signal that a lighthouse run is about to start - uses: thollander/actions-comment-pull-request@e2c37e53a7d2227b61585343765f73a9ca57eda9 # v3.0.0 - with: - message: | - Running Lighthouse audit... - # Used later to edit the existing comment - comment-tag: 'lighthouse_audit' - - name: Audit Preview URL with Lighthouse # Conduct the lighthouse audit id: lighthouse_audit @@ -91,7 +80,7 @@ jobs: - name: Format Lighthouse Score # Transform the audit results into a single, friendlier output id: format_lighthouse_score - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: # using env as input to our script # see https://github.com/actions/github-script#use-env-as-input @@ -105,11 +94,16 @@ jobs: const { formatLighthouseResults } = await import('${{github.workspace}}/apps/site/scripts/lighthouse/index.mjs') await formatLighthouseResults({core}) - - name: Add Comment to PR - # Replace the previous message with our formatted lighthouse results - uses: thollander/actions-comment-pull-request@e2c37e53a7d2227b61585343765f73a9ca57eda9 # v3.0.0 + - name: Prepare Comment + env: + COMMENT: ${{ steps.format_lighthouse_score.outputs.comment }} + run: | + mkdir -p pr-comment + printf '%s' "$COMMENT" > pr-comment/comment.md + printf '%s' 'lighthouse_audit' > pr-comment/tag.txt + + - name: Upload Comment + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - # Reference the previously created comment - comment-tag: 'lighthouse_audit' - message: | - ${{ steps.format_lighthouse_score.outputs.comment }} + name: pr-comment + path: pr-comment/ diff --git a/.github/workflows/lint-and-tests.yml b/.github/workflows/lint-and-tests.yml index 54e1d388971fa..c61cfafcdb3fb 100644 --- a/.github/workflows/lint-and-tests.yml +++ b/.github/workflows/lint-and-tests.yml @@ -49,7 +49,7 @@ jobs: use-version-file: true - name: Restore Lint Cache - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | .turbo/cache @@ -86,7 +86,7 @@ jobs: (github.event_name == 'pull_request' && startsWith(github.event.pull_request.head.ref, 'dependabot/') == false && github.event.pull_request.head.ref != 'chore/crowdin') - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | .turbo/cache @@ -112,13 +112,13 @@ jobs: - name: Upload test coverage to Codecov if: ${{ !cancelled() && github.event_name != 'merge_group' }} - uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: files: ./apps/site/lcov.info,./packages/*/lcov.info - name: Upload test results to Codecov if: ${{ !cancelled() && github.event_name != 'merge_group' }} - uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: report_type: test_results files: ./apps/site/junit.xml,./packages/*/junit.xml diff --git a/.github/workflows/playwright-cloudflare-open-next.yml b/.github/workflows/playwright-cloudflare-open-next.yml index 8a42b4675fcd8..b049a3d08ddde 100644 --- a/.github/workflows/playwright-cloudflare-open-next.yml +++ b/.github/workflows/playwright-cloudflare-open-next.yml @@ -41,7 +41,7 @@ jobs: - name: Cache Playwright browsers id: playwright-cache - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.cache/ms-playwright key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} @@ -50,16 +50,15 @@ jobs: working-directory: apps/site run: node_modules/.bin/playwright install --with-deps + - name: Build open-next site + run: node_modules/.bin/turbo build --filter=@node-core/platform-cloudflare --cache-dir=.turbo/cache + - name: Run Playwright tests - working-directory: apps/site - run: node --run playwright - env: - PLAYWRIGHT_RUN_CLOUDFLARE_PREVIEW: true - PLAYWRIGHT_BASE_URL: http://127.0.0.1:8787 + run: pnpm --filter=@node-core/platform-cloudflare test:e2e - name: Upload Playwright test results if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: playwright-report path: apps/site/playwright-report/ diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 549809eb16c8c..f09764aafd418 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -49,7 +49,7 @@ jobs: - name: Cache Playwright browsers id: playwright-cache - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.cache/ms-playwright key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} @@ -66,7 +66,7 @@ jobs: - name: Upload Playwright test results if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: playwright-report path: apps/site/playwright-report/ diff --git a/.github/workflows/pnpm-updater.yml b/.github/workflows/pnpm-updater.yml index 1a09198e39f3b..6419dcd780c71 100644 --- a/.github/workflows/pnpm-updater.yml +++ b/.github/workflows/pnpm-updater.yml @@ -29,17 +29,17 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - name: Git Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Update pnpm and package.json id: update-pnpm diff --git a/.github/workflows/publish-packages.yml b/.github/workflows/publish-packages.yml index a1333508003be..c12447643fd12 100644 --- a/.github/workflows/publish-packages.yml +++ b/.github/workflows/publish-packages.yml @@ -1,19 +1,12 @@ name: Publish Packages -# This workflow publishes packages to npm when changes are merged to main branch or when manually triggered. +# Changesets opens or updates a version PR when release notes are present. Once that PR is merged, +# this workflow publishes the versioned packages to npm. on: push: - paths: - - 'packages/**' # For security reasons, this should never be set to anything but `main` branches: [main] - workflow_dispatch: - inputs: - package: - description: 'Specific package to publish (leave empty for all packages)' - required: false - type: string permissions: contents: read @@ -26,15 +19,18 @@ env: COMMIT_SHA: ${{ github.sha }} jobs: - prepare-packages: - name: Prepare Packages + release: + name: Create Release PR or Publish runs-on: ubuntu-latest - outputs: - # Output the matrix of packages to publish for use in the publish job - matrix: ${{ steps.generate-matrix.outputs.matrix }} + # Never attempt to publish from forks (no trusted-publisher match / secrets). + if: github.repository == 'nodejs/nodejs.org' + permissions: + contents: write + id-token: write + pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -62,83 +58,47 @@ jobs: echo "✅ Commit is verified and trusted." - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 2 # Need at least 2 commits to detect changes between commits - persist-credentials: false - - - name: Generate package matrix - id: generate-matrix - env: - PACKAGE: ${{ github.event.inputs.package }} - EVENT_NAME: ${{ github.event_name }} - run: | - if [ -n "$PACKAGE" ]; then - # If a specific package is requested via workflow_dispatch, just publish that one - echo "matrix={\"package\":[\"$PACKAGE\"]}" >> $GITHUB_OUTPUT - else - CHANGED_PACKAGES=() - for pkg in $(ls -d packages/*); do - PKG_NAME=$(basename "$pkg") - PKG_JSON="$pkg/package.json" - - # Determine if the package has changed (or include all on manual trigger) - if [ "$EVENT_NAME" == "workflow_dispatch" ] || ! git diff --quiet $COMMIT_SHA~1 $COMMIT_SHA -- "$pkg/"; then - OLD_VERSION=$(git show $COMMIT_SHA~1:$PKG_JSON | jq -r '.version') - NEW_VERSION=$(jq -r '.version' "$PKG_JSON") - if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then - CHANGED_PACKAGES+=("$PKG_NAME") - fi - fi - done - - # Format the output for GitHub Actions matrix using jq - PACKAGES_JSON=$(jq -n '$ARGS.positional' --args "${CHANGED_PACKAGES[@]}" -c) - echo "matrix={\"package\":$PACKAGES_JSON}" >> $GITHUB_OUTPUT - fi - - publish: - name: Publish - needs: prepare-packages - runs-on: ubuntu-latest - permissions: - # Required for npm OIDC publishing (https://docs.npmjs.com/trusted-publishers) - id-token: write - # Skip if no packages need to be published - if: fromJson(needs.prepare-packages.outputs.matrix).package[0] != null - # Use the dynamic matrix from prepare-packages job to create parallel jobs for each package - strategy: - matrix: ${{ fromJson(needs.prepare-packages.outputs.matrix) }} - fail-fast: false # Continue publishing other packages even if one fails - steps: - uses: nodejs/web-team/actions/setup-environment@9f3c83af227d721768d9dbb63009a47ed4f4282f with: pnpm: true use-version-file: true registry-url: 'https://registry.npmjs.org' + fetch-depth: 0 - - name: Publish - working-directory: packages/${{ matrix.package }} - run: | - # Check if a custom publish script exists in package.json - if jq -e '.scripts.release' package.json > /dev/null; then - pnpm run release - fi - - # Then publish the package to npm - pnpm publish --access public --no-git-checks + - name: Create release pull request or publish packages + id: changesets + uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0 + with: + commit: 'chore: version packages' + title: 'chore: version packages' + version: node --run changeset:version + publish: node --run release + commitMode: github-api + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Notify on Manual Release - if: ${{ github.event_name == 'workflow_dispatch' }} - uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # 2.3.3 + - name: Format published packages + if: steps.changesets.outputs.published == 'true' + id: notification + env: + PUBLISHED_PACKAGES: ${{ steps.changesets.outputs.publishedPackages }} + run: | + { + echo 'packages<)"' <<< "$PUBLISHED_PACKAGES" + echo 'EOF' + } >> "$GITHUB_OUTPUT" + + - name: Notify + if: steps.changesets.outputs.published == 'true' + uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0 env: SLACK_COLOR: '#43853D' SLACK_ICON: https://github.com/nodejs.png?size=48 - SLACK_TITLE: ':rocket: Package Published: ${{ matrix.package }}' + SLACK_TITLE: ':rocket: Packages Published' SLACK_MESSAGE: | - :package: *Package*: `${{ matrix.package }}` () + ${{ steps.notification.outputs.packages }} :bust_in_silhouette: *Published by*: ${{ github.triggering_actor }} - :octocat: *Commit*: + :octocat: *Commit*: SLACK_USERNAME: nodejs-bot SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} diff --git a/.github/workflows/pull-request-label.yml b/.github/workflows/pull-request-label.yml index 95a072e87f3dd..4642bc449cc78 100644 --- a/.github/workflows/pull-request-label.yml +++ b/.github/workflows/pull-request-label.yml @@ -36,7 +36,7 @@ jobs: pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/pull-request-policy.yml b/.github/workflows/pull-request-policy.yml new file mode 100644 index 0000000000000..2277df7ecdb70 --- /dev/null +++ b/.github/workflows/pull-request-policy.yml @@ -0,0 +1,54 @@ +# Security Notes +# Only selected Actions are allowed within this repository. Please refer to (https://github.com/nodejs/nodejs.org/settings/actions) +# for the full list of available actions. If you want to add a new one, please reach out a maintainer with Admin permissions. +# REVIEWERS, please always double-check security practices before merging a PR that contains workflow changes!! +# AUTHORS, please only use actions with explicit SHA references, and avoid using `@master` or `@main` references or `@version` tags. + +name: Pull Request Policy + +on: + pull_request: + branches: + - main + paths: + - packages/** + types: [opened, edited, synchronize, reopened, ready_for_review] + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + changesets: + name: Changesets + runs-on: ubuntu-latest + if: | + github.event_name == 'pull_request' && + !(github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.head.ref == 'changeset-release/main') + steps: + - uses: nodejs/web-team/actions/setup-environment@9f3c83af227d721768d9dbb63009a47ed4f4282f + with: + pnpm: true + use-version-file: true + fetch-depth: 0 + + - run: pnpm changeset status --since origin/main + + conventional-commits: + name: Conventional commits + runs-on: ubuntu-latest + steps: + - name: Check PR Title Format + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + REGEX="^[a-z][a-z0-9-]*(\([a-z0-9_.\/-]+\))?!?: .+" + + if [[ ! "$PR_TITLE" =~ $REGEX || "$PR_TITLE" == *. ]]; then + echo 'PR titles must use [optional scope][!]: and must not end with a period.' + exit 1 + fi diff --git a/.github/workflows/tmp-cloudflare-open-next-deploy.yml b/.github/workflows/tmp-cloudflare-open-next-deploy.yml index e03a0af7fb209..da8cba163dda9 100644 --- a/.github/workflows/tmp-cloudflare-open-next-deploy.yml +++ b/.github/workflows/tmp-cloudflare-open-next-deploy.yml @@ -30,20 +30,20 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - name: Git Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up pnpm - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Set up Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: # We want to ensure that the Node.js version running here respects our supported versions node-version-file: '.nvmrc' @@ -52,17 +52,11 @@ jobs: - name: Install packages run: pnpm install --frozen-lockfile - - name: Build blog data - working-directory: apps/site - run: node --run build:blog-data - - name: Build open-next site - working-directory: apps/site - run: node --run cloudflare:build:worker + run: node_modules/.bin/turbo build --filter=@node-core/platform-cloudflare --cache-dir=.turbo/cache - name: Deploy open-next site - working-directory: apps/site - run: node --run cloudflare:deploy + run: pnpm --filter=@node-core/platform-cloudflare run deploy env: CF_WORKERS_SCRIPTS_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/.github/workflows/translations-pr-lint.yml b/.github/workflows/translations-pr-lint.yml index 3e494fc5158cd..d6529c6f06e19 100644 --- a/.github/workflows/translations-pr-lint.yml +++ b/.github/workflows/translations-pr-lint.yml @@ -41,7 +41,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/translations-sync.yml b/.github/workflows/translations-sync.yml index f634457f03fa7..241455aafe1e6 100644 --- a/.github/workflows/translations-sync.yml +++ b/.github/workflows/translations-sync.yml @@ -27,19 +27,19 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: token: ${{ secrets.CROWDIN_GITHUB_BOT_TOKEN }} persist-credentials: false # see all the options at https://github.com/crowdin/github-action - name: Crowdin PR - uses: crowdin/github-action@ce33ce793a5cbc401d9cd748716e03fc90c001f1 # v2.15.2 + uses: crowdin/github-action@c7af9bc98b01694653031fef2a0dc6c7888ce9bc # v2.17.0 with: # do not upload anything - this is a one-way operation download upload_sources: false @@ -48,9 +48,9 @@ jobs: download_translations: true localization_branch_name: ${{ env.BRANCH_NAME }} create_pull_request: true - pull_request_title: '[automated]: crowdin sync' + pull_request_title: 'chore(i18n): sync translations from crowdin' pull_request_body: 'New Crowdin translations from the [Node.js Crowdin project](https://crowdin.com/project/nodejs-web)' - commit_message: 'chore: synced translations from crowdin' + commit_message: 'chore(i18n): sync translations from crowdin' env: GITHUB_TOKEN: ${{ secrets.CROWDIN_GITHUB_BOT_TOKEN }} # A numeric ID, found at https://crowdin.com/project/nodejs-web/tools/api @@ -77,7 +77,7 @@ jobs: fetch-depth: 2 - name: Restore Lint Cache - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | apps/site/.eslintmdcache @@ -92,12 +92,17 @@ jobs: cache-lint-${{ hashFiles('pnpm-lock.yaml') }}- cache-lint- - - name: Patch version if the files changed - working-directory: packages/i18n + - name: Add changeset if the files changed run: | CHANGED_FILES=$(git diff --name-only HEAD^1 HEAD) if [ -n "$CHANGED_FILES" ]; then - pnpm version patch --no-git-tag-version + cat > .changeset/crowdin-translations.md <<'EOF' + --- + '@node-core/website-i18n': patch + --- + + Update translations from Crowdin. + EOF fi - name: Run ESLint @@ -108,14 +113,14 @@ jobs: run: node --run prettier:fix - name: Push Changes back to Pull Request - uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0 + uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7.2.0 with: commit_options: '--no-verify --signoff' - commit_message: 'chore: automated format of translated files' + commit_message: 'chore(i18n): format translated files' branch: ${{ env.BRANCH_NAME }} - name: Save Lint Cache - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | apps/site/.eslintmdcache diff --git a/.github/workflows/translations-upload.yml b/.github/workflows/translations-upload.yml index 2ff72b5062e46..c561c7df98750 100644 --- a/.github/workflows/translations-upload.yml +++ b/.github/workflows/translations-upload.yml @@ -22,18 +22,18 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false # see all the options at https://github.com/crowdin/github-action - name: crowdin action - uses: crowdin/github-action@ce33ce793a5cbc401d9cd748716e03fc90c001f1 # v2.15.2 + uses: crowdin/github-action@c7af9bc98b01694653031fef2a0dc6c7888ce9bc # v2.17.0 with: # only upload sources, ensuring this is a one-way operation upload_sources: true diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 97a13621d022c..f2e1f2f2c7fb8 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -24,9 +24,9 @@ jobs: security-events: write steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Run zizmor 🌈 - uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 + uses: zizmorcore/zizmor-action@6fc4b006235f201fdab3722e17240ab420d580e5 # v0.6.1 diff --git a/.gitignore b/.gitignore index f461599c85893..a756afeb5c8ef 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ apps/site/build apps/site/public/blog-data.json apps/site/next-env.d.ts +# Generated Build Artifacts +platforms/cloudflare/generated + # Test Runner junit.xml lcov.info diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 8fff30e0a6cca..0000000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,4 +0,0 @@ -# Code of Conduct - -- [Node.js Code of Conduct](https://github.com/nodejs/admin/blob/HEAD/CODE_OF_CONDUCT.md) -- [Node.js Moderation Policy](https://github.com/nodejs/admin/blob/HEAD/Moderation-Policy.md) diff --git a/apps/site/.remarkrc.json b/apps/site/.remarkrc.json index 71f00f776d25e..721eda175ea09 100644 --- a/apps/site/.remarkrc.json +++ b/apps/site/.remarkrc.json @@ -1,3 +1,13 @@ { - "plugins": ["remark-frontmatter", "@node-core/remark-lint"] + "plugins": [ + "remark-frontmatter", + "@node-core/remark-lint", + [ + "@node-core/remark-lint/rules/validate-links", + { + "ignoreFiles": ["pages/!(en)/**/*.{md,mdx}"], + "ignoreLinks": ["/learn", "/learn/**/*", "/static/**/*", "/feed/*"] + } + ] + ] } diff --git a/apps/site/.stylelintrc.mjs b/apps/site/.stylelintrc.mjs index c618c7b3dca9e..cbb76b9e67ca4 100644 --- a/apps/site/.stylelintrc.mjs +++ b/apps/site/.stylelintrc.mjs @@ -26,7 +26,7 @@ export default { plugins: [ 'stylelint-order', 'stylelint-selector-bem-pattern', - '@node-core/ui-components/stylelint/one-utility-class-per-line.mjs', + '@node-core/ui-components/src/stylelint/one-utility-class-per-line.mjs', ], rules: { // Enforces Element Class Names to be camelCase diff --git a/apps/site/app/[locale]/layout.tsx b/apps/site/app/[locale]/layout.tsx index 5e1e440c5b974..7b905a0fd03a3 100644 --- a/apps/site/app/[locale]/layout.tsx +++ b/apps/site/app/[locale]/layout.tsx @@ -1,11 +1,9 @@ +import PlatformAnalytics from '#platform/analytics'; import { availableLocales, defaultLocale } from '@node-core/website-i18n'; -import { Analytics } from '@vercel/analytics/react'; -import { SpeedInsights } from '@vercel/speed-insights/next'; import classNames from 'classnames'; import { NextIntlClientProvider } from 'next-intl'; import BaseLayout from '#site/layouts/Base'; -import { VERCEL_ENV } from '#site/next.constants.mjs'; import { IBM_PLEX_MONO, OPEN_SANS } from '#site/next.fonts'; import { ThemeProvider } from '#site/providers/themeProvider'; @@ -46,12 +44,7 @@ const RootLayout: FC = async ({ children, params }) => { href="https://social.lfx.dev/@nodejs" /> - {VERCEL_ENV && ( - <> - - - - )} + ); diff --git a/apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx b/apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx index edd24873a9e89..f99b8d756b819 100644 --- a/apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx +++ b/apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx @@ -35,7 +35,14 @@ export const GET = async (_: Request, props: StaticParams) => { return new ImageResponse(
- +
diff --git a/apps/site/authors.json b/apps/site/authors.json index 3afd0cd6f7631..20217976eec33 100644 --- a/apps/site/authors.json +++ b/apps/site/authors.json @@ -1,317 +1,319 @@ { "Anna Henningsen": { - "id": "addaleax", + "id": 899444, "name": "Anna Henningsen", "website": "https://github.com/addaleax" }, "Antoine du Hamel": { - "id": "aduh95", + "id": 14309773, "name": "Antoine du Hamel", "website": "https://github.com/aduh95" }, "AugustinMauroy": { - "id": "AugustinMauroy", + "id": 97875033, "name": "Augustin Mauroy", "website": "https://github.com/AugustinMauroy" }, "Aviv Keller": { - "id": "avivkeller", + "id": 38299977, "name": "Aviv Keller", "website": "https://github.com/avivkeller" }, "Ben Noordhuis": { - "id": "bnoordhuis", + "id": 275871, "name": "Ben Noordhuis", "website": "https://github.com/bnoordhuis" }, "Bethany Nicolle Griggs": { - "id": "BethGriggs", + "id": 8297234, "name": "Bethany Nicolle Griggs", "website": "https://github.com/BethGriggs" }, "Brian Muenzenmeyer": { - "id": "bmuenzenmeyer", + "id": 298435, "name": "Brian Muenzenmeyer", "website": "https://github.com/bmuenzenmeyer" }, "Bryan English": { - "id": "bengl", + "id": 110455, "name": "Bryan English", "website": "https://github.com/bengl" }, "Carl Vitullo": { - "id": "vcarl", + "id": 1551487, "name": "Carl Vitullo", "website": "https://github.com/vcarl" }, "Charlie Robbins": { - "id": "indexzero", + "id": 4624, "name": "Charlie Robbins", "website": "https://github.com/indexzero" }, "Chengzhong Wu": { - "id": "legendecas", + "id": 8500303, "name": "Chengzhong Wu", "website": "https://github.com/legendecas" }, "Claudio Wunder": { - "id": "ovflowd", + "id": 12037269, "name": "Claudio Wunder", "website": "https://github.com/ovflowd" }, "Colin Ihrig": { - "id": "cjihrig", + "id": 2512748, "name": "Colin Ihrig", "website": "https://github.com/cjihrig" }, "Danielle Adams": { - "id": "danielleadams", + "id": 6271256, "name": "Danielle Adams", "website": "https://github.com/danielleadams" }, "Daniel Bevenius": { - "id": "danbev", + "id": 432351, "name": "Daniel Bevenius", - "webiste": "https://github.com/danbev" + "website": "https://github.com/danbev" }, "Dave Pacheco": { - "id": "dave-pacheco", + "id": 70340013, "name": "Dave Pacheco" }, "Domenic Denicola": { - "id": "domenic", + "id": 617481, "name": "Domenic Denicola", "website": "https://github.com/domenic" }, "Emelia Smith": { - "id": "thisismissem", + "id": 30827, "name": "Emelia Smith", "website": "https://github.com/thisismissem" }, "Emily Rose": { - "id": "emilyrose", + "id": 32688683, "name": "Emily Rose", "website": "https://github.com/emilyrose" }, "Evan Lucas": { - "id": "evanlucas", + "id": 677994, "name": "Evan Lucas", "website": "https://github.com/evanlucas" }, "flakey5": { - "id": "flakey5", + "id": 73616808, "name": "flakey5", "website": "https://github.com/flakey5" }, "Gibson Fahnestock": { - "id": "gibfahn", + "id": 15943089, "name": "Gibson Fahnestock", "website": "https://github.com/gibfahn" }, "Giovanny Gongora": { - "id": "Gioyik", + "id": 1834718, "name": "Giovanny Gongora", "website": "https://github.com/Gioyik" }, + "Guilherme Araújo": { + "id": 68445491, + "name": "Guilherme Araújo", + "website": "https://github.com/araujogui" + }, "Isaac Schlueter": { - "id": "isaacs", + "id": 9287, "name": "Isaac Schlueter", "website": "https://github.com/isaacs" }, "Italo Casas": { - "id": "italoacasas", + "id": 6354455, "name": "Italo Casas", "website": "https://github.com/italoacasas" }, "James M Snell": { - "id": "jasnell", + "id": 439929, "name": "James M Snell", "website": "https://github.com/jasnell" }, "Jeremiah Senkpiel": { - "id": "Fishrock123", + "id": 1093990, "name": "Jeremiah Senkpiel", "website": "https://github.com/Fishrock123" }, "Joe Sepi": { - "id": "joesepi", + "id": 143668, "name": "Joe Sepi", "website": "https://github.com/joesepi" }, "Joyee Cheung": { - "id": "joyeecheung", + "id": 4299420, "name": "Joyee Cheung", "website": "https://github.com/joyeecheung" }, "Juan José": { - "id": "juanarbol", + "id": 17013303, "name": "Juan José Arboleda", "website": "https://github.com/juanarbol" }, "Juan José Arboleda": { - "id": "juanarbol", + "id": 17013303, "name": "Juan José Arboleda", "website": "https://github.com/juanarbol" }, "Julián Duque": { - "id": "julianduque", + "id": 733877, "name": "Julián Duque", "website": "https://github.com/julianduque" }, "Node.js Releasers": { - "id": "nodejs", + "id": 9950313, "name": "Node.js Release Working Group", "website": "https://github.com/nodejs/release" }, "Node.js Technical Steering Committee": { - "id": "nodejs", + "id": 9950313, "name": "Node.js Technical Steering Committee", "website": "https://github.com/nodejs/tsc" }, "Matteo Collina": { - "id": "mcollina", + "id": 52195, "name": "Matteo Collina", - "webiste": "https://github.com/mcollina" + "website": "https://github.com/mcollina" }, "Marco Ippolito": { - "id": "marco-ippolito", + "id": 36735501, "name": "Marco Ippolito", "website": "https://github.com/marco-ippolito" }, "Matt Cowley": { - "id": "MattIPv4", + "id": 12371363, "name": "Matt Cowley", "website": "https://github.com/MattIPv4" }, "Michael Dawson": { - "id": "mhdawson", + "id": 9373002, "name": "Michael Dawson", "website": "https://github.com/mhdawson" }, "Michaël Zasso": { - "id": "targos", + "id": 2352663, "name": "Michaël Zasso", "website": "https://github.com/targos" }, "Mike Dolan": { - "id": "mike-dolan", - "name": "Mike Dolan" + "id": 7398917, + "name": "Mike Dolan", + "website": "https://github.com/mkdolan" }, "Mikeal Rogers": { - "id": "mikeal", + "id": 579, "name": "Mikeal Rogers", "website": "https://github.com/mikeal" }, "Minwoo Jung": { - "id": "minwoo-jung", + "id": 4557258, "name": "Minwoo Jung", "website": "https://github.com/minwoo-jung" }, "Myles Borins": { - "id": "mylesborins", + "id": 498775, "name": "Myles Borins", "website": "https://github.com/mylesborins" }, "piscisaureus": { - "id": "piscisaureus", + "id": 218257, "name": "Bert Belder", "website": "https://github.com/piscisaureus" }, "Rafael Gonzaga": { - "id": "rafaelgss", + "id": 26234614, "name": "Rafael Gonzaga", "website": "https://github.com/RafaelGSS" }, "Richard Lau": { - "id": "richardlau", + "id": 5445507, "name": "Richard Lau", "website": "https://github.com/richardlau" }, - "Richie McColl": { - "id": "richiemccoll", - "name": "Richie McColl", - "website": "https://github.com/richiemccoll" - }, "Robin Bender Ginn": { - "id": "rginn", + "id": 4296937, "name": "Robin Bender Ginn", "website": "https://github.com/rginn" }, "Rod Vagg": { - "id": "rvagg", + "id": 495647, "name": "Rod Vagg", "website": "https://github.com/rvagg" }, "Ruben Bridgewater": { - "id": "BridgeAR", + "id": 8822573, "name": "Ruben Bridgewater", "website": "https://github.com/BridgeAR" }, "Ruy Adorno": { - "id": "ruyadorno", + "id": 220900, "name": "Ruy Adorno", "website": "https://github.com/ruyadorno" }, "Ryan Dahl": { - "id": "ry", + "id": 80, "name": "Ryan Dahl", "website": "https://github.com/ry" }, "Sam Roberts": { - "id": "sam-github", + "id": 17607, "name": "Sam Roberts", - "webiste": "https://github.com/sam-github" + "website": "https://github.com/sam-github" }, "Scott Hammond": { - "id": "scott-hammond", + "id": 95775645, "name": "Scott Hammond" }, "Shelley Vohr": { - "id": "codebytere", + "id": 2036040, "name": "Shelley Vohr", "website": "https://github.com/codebytere" }, "Stewart X Addison": { - "id": "sxa", + "id": 6487691, "name": "Stewart X Addison", "website": "https://github.com/sxa" }, "Steven Sinatra": { - "id": "diagramatics", + "id": 5153378, "name": "Steven Sinatra", "website": "https://github.com/diagramatics" }, "The Node.js Project": { - "id": "nodejs", + "id": 9950313, "name": "The Node.js Project", "website": "https://github.com/nodejs" }, "Tierney Cyren": { - "id": "bnb", + "id": 502396, "name": "Tierney Cyren", "website": "https://github.com/bnb" }, "Timothy J Fontaine": { - "id": "tjfontaine", + "id": 146447, "name": "Timothy J Fontaine", "website": "https://github.com/tjfontaine" }, "Tracy Hinds": { - "id": "tracy-hinds", - "name": "Tracy Hinds" + "id": 1981088, + "name": "Tracy Hinds", + "website": "https://github.com/hackygolucky" }, "Ulises Gascón": { - "id": "UlisesGascon", + "id": 5110813, "name": "Ulises Gascón", "website": "https://github.com/UlisesGascon" }, "Vladimir de Turckheim": { - "id": "vdeturckheim", + "id": 7135896, "name": "Vladimir de Turckheim", - "webiste": "https://github.com/vdeturckheim" + "website": "https://github.com/vdeturckheim" }, "Yosuke Furukawa": { - "id": "yosuke-furukawa", + "id": 555645, "name": "Yosuke Furukawa", "website": "https://github.com/yosuke-furukawa" } diff --git a/apps/site/components/Blog/BlogPostCard/index.module.css b/apps/site/components/Blog/BlogPostCard/index.module.css index 53ec2686369f0..e9e8f6a26c377 100644 --- a/apps/site/components/Blog/BlogPostCard/index.module.css +++ b/apps/site/components/Blog/BlogPostCard/index.module.css @@ -6,13 +6,13 @@ } .subtitle { - @apply mt-6 + @apply text-brand-600 + dark:text-brand-400 + mt-6 mb-2 inline-block text-xs - font-semibold - text-green-600 - dark:text-green-400; + font-semibold; } .title { diff --git a/apps/site/components/Common/FormattedTime.tsx b/apps/site/components/Common/FormattedTime.tsx index 3b8aea40770d0..775fed96e4824 100644 --- a/apps/site/components/Common/FormattedTime.tsx +++ b/apps/site/components/Common/FormattedTime.tsx @@ -14,7 +14,7 @@ const FormattedTime: FC = ({ date, format }) => { const formatter = useFormatter(); // "date" is deterministic - // eslint-disable-next-line @eslint-react/purity + const dateObject = new Date(date); return ( diff --git a/apps/site/components/Common/LinkWithArrow/index.module.css b/apps/site/components/Common/LinkWithArrow/index.module.css index 8f1566281e65a..b523bdb4b0e6b 100644 --- a/apps/site/components/Common/LinkWithArrow/index.module.css +++ b/apps/site/components/Common/LinkWithArrow/index.module.css @@ -9,8 +9,8 @@ } .button { - @apply text-green-600 - hover:text-green-900 - dark:text-green-400 - dark:hover:text-green-200; + @apply text-brand-600 + hover:text-brand-900 + dark:text-brand-400 + dark:hover:text-brand-200; } diff --git a/apps/site/components/Common/LinkWithArrow/index.tsx b/apps/site/components/Common/LinkWithArrow/index.tsx index d15b59df4c491..66d6e8c39613a 100644 --- a/apps/site/components/Common/LinkWithArrow/index.tsx +++ b/apps/site/components/Common/LinkWithArrow/index.tsx @@ -13,8 +13,7 @@ import type { import styles from './index.module.css'; type LinkWithArrowProps = - | ComponentProps - | ButtonHTMLAttributes; + ComponentProps | ButtonHTMLAttributes; const LinkWithArrow: FC> = ({ children, diff --git a/apps/site/components/Common/Partners/PartnerButton/index.module.css b/apps/site/components/Common/Partners/PartnerButton/index.module.css index 12c4633d4994b..9477a4286b4b4 100644 --- a/apps/site/components/Common/Partners/PartnerButton/index.module.css +++ b/apps/site/components/Common/Partners/PartnerButton/index.module.css @@ -25,6 +25,7 @@ svg { @apply h-4 - w-auto; + w-auto + max-w-4; } } diff --git a/apps/site/components/Common/Partners/index.module.css b/apps/site/components/Common/Partners/index.module.css index 32c7743e32ba0..a293da7dd8feb 100644 --- a/apps/site/components/Common/Partners/index.module.css +++ b/apps/site/components/Common/Partners/index.module.css @@ -5,6 +5,7 @@ flex-row flex-wrap items-center + justify-center gap-2; } diff --git a/apps/site/components/Common/Partners/index.tsx b/apps/site/components/Common/Partners/index.tsx index 430ca83b6abb7..949c47d8ec81a 100644 --- a/apps/site/components/Common/Partners/index.tsx +++ b/apps/site/components/Common/Partners/index.tsx @@ -70,7 +70,7 @@ const PartnersList: FC = async ({ }) => { const isSmall = size === 'small'; - const SMALL_PARTNER_LIMIT = 6; + const SMALL_PARTNER_LIMIT = 12; const partners = await getPartners( length ?? (isSmall ? SMALL_PARTNER_LIMIT : undefined), diff --git a/apps/site/components/Common/Supporters/index.tsx b/apps/site/components/Common/Supporters/index.tsx index e9cbfa387ed99..2797e94241ed9 100644 --- a/apps/site/components/Common/Supporters/index.tsx +++ b/apps/site/components/Common/Supporters/index.tsx @@ -6,18 +6,18 @@ import type { Supporter } from '#site/types'; import type { FC } from 'react'; type SupportersListProps = { - supporters: Array>; + supporters: Array>; }; const SupportersList: FC = ({ supporters }) => (
- {supporters.map(({ name, image, profile }) => ( + {supporters.map(({ name, image, source, url }) => ( ))}
diff --git a/apps/site/components/Downloads/Release/ReleaseCodeBox.tsx b/apps/site/components/Downloads/Release/ReleaseCodeBox.tsx index 11694415fd116..829131d7ceed5 100644 --- a/apps/site/components/Downloads/Release/ReleaseCodeBox.tsx +++ b/apps/site/components/Downloads/Release/ReleaseCodeBox.tsx @@ -164,7 +164,7 @@ const ReleaseCodeBox: FC = () => { {/* Code display with skeleton loading */} - {/* eslint-disable-next-line @eslint-react/dom/no-dangerously-set-innerhtml */} + {/* eslint-disable-next-line @eslint-react/dom-no-dangerously-set-innerhtml */} diff --git a/apps/site/components/withBanner.tsx b/apps/site/components/withBanner.tsx index 1023051e1d0d5..b8a186d01b23a 100644 --- a/apps/site/components/withBanner.tsx +++ b/apps/site/components/withBanner.tsx @@ -1,6 +1,9 @@ +'use client'; + import { ArrowUpRightIcon } from '@heroicons/react/24/outline'; import Banner from '@node-core/ui-components/Common/Banner'; import { useTranslations } from 'next-intl'; +import { useEffect, useState } from 'react'; import Link from '#site/components/Link'; import { siteConfig } from '#site/next.json.mjs'; @@ -8,17 +11,35 @@ import { dateIsBetween } from '#site/util/date'; import type { FC } from 'react'; +const STORAGE_KEY = 'banner-dismissal'; + const WithBanner: FC<{ section: string }> = ({ section }) => { const banner = siteConfig.websiteBanners[section]; const t = useTranslations(); - if (banner && dateIsBetween(banner.startDate, banner.endDate)) { + const [open, setOpen] = useState(false); + + useEffect(() => { + if (banner) { + // eslint-disable-next-line @eslint-react/set-state-in-effect + setOpen(localStorage.getItem(STORAGE_KEY) !== banner.text); + } + }, [banner]); + + if (banner && open && dateIsBetween(banner.startDate, banner.endDate)) { const bannerType = banner.type || 'default'; + const onClose = () => { + localStorage.setItem(STORAGE_KEY, banner.text); + setOpen(false); + }; + return ( {banner.link ? ( {banner.text} diff --git a/apps/site/components/withMetaBar.tsx b/apps/site/components/withMetaBar.tsx index 4c1eb322cdb97..64b796734972a 100644 --- a/apps/site/components/withMetaBar.tsx +++ b/apps/site/components/withMetaBar.tsx @@ -20,7 +20,7 @@ const WithMetaBar: FC = () => { const formatter = useFormatter(); const lastUpdated = frontmatter.date ? // "frontmatter.date" is deterministic - // eslint-disable-next-line @eslint-react/purity + formatter.dateTime(new Date(frontmatter.date), DEFAULT_DATE_FORMAT) : undefined; const readingTimeText = formatter.number(readingTime.minutes, { diff --git a/apps/site/components/withNavBar.tsx b/apps/site/components/withNavBar.tsx index 275419bbf18a8..722fb3ede57f0 100644 --- a/apps/site/components/withNavBar.tsx +++ b/apps/site/components/withNavBar.tsx @@ -54,11 +54,14 @@ const WithNavBar: FC = () => { ({ - link, - text: label, - target, - }))} + navItems={navigationItems.map( + ([, { label, link, target, accent }]) => ({ + link, + text: label, + target, + accent, + }) + )} pathname={pathname} as={Link} Logo={WithNodejsLogo} diff --git a/apps/site/components/withSidebarCrossLinks.tsx b/apps/site/components/withSidebarCrossLinks.tsx deleted file mode 100644 index 565b459524859..0000000000000 --- a/apps/site/components/withSidebarCrossLinks.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { getClientContext } from '#site/client-context'; -import CrossLink from '#site/components/Common/CrossLink'; -import useSiteNavigation from '#site/hooks/useSiteNavigation'; - -import type { NavigationKeys } from '#site/types'; -import type { FC } from 'react'; - -type WithCrossLinksProps = { navKey: NavigationKeys }; - -const WithSidebarCrossLinks: FC = ({ navKey }) => { - const { getSideNavigation } = useSiteNavigation(); - const { pathname } = getClientContext(); - - const [[, sidebarNavigation]] = getSideNavigation([navKey]); - - const crossLinkItems = sidebarNavigation.items - .map(([, { items }]) => items.map(([, item]) => item)) - .flat(); - - const currentItem = crossLinkItems.findIndex(({ link }) => link === pathname); - - const [previousCrossLink, nextCrossLink] = [ - crossLinkItems[currentItem - 1], - crossLinkItems[currentItem + 1], - ]; - - return ( -
- {(previousCrossLink && ( - - )) ||
} - - {nextCrossLink && ( - - )} -
- ); -}; - -export default WithSidebarCrossLinks; diff --git a/apps/site/eslint.config.js b/apps/site/eslint.config.js index 67130ffb3ae42..d55ea84d42f55 100644 --- a/apps/site/eslint.config.js +++ b/apps/site/eslint.config.js @@ -6,15 +6,7 @@ import baseConfig from '../../eslint.config.js'; export default baseConfig.concat([ { - ignores: [ - 'pages/en/blog/**/*.{md,mdx}/**', - 'public', - 'next-env.d.ts', - // The worker entrypoint is bundled by wrangler, not tsc. Its imports - // trigger a tsc crash (see tsconfig.json), so it is excluded from both - // type checking and ESLint's type-aware linting. - 'cloudflare/worker-entrypoint.ts', - ], + ignores: ['pages/en/blog/**/*.{md,mdx}/**', 'public', 'next-env.d.ts'], }, eslintReact.configs['recommended-typescript'], @@ -43,10 +35,15 @@ export default baseConfig.concat([ }, { + // TODO: Enable validate-links for translations once their routes and + // anchors are kept in sync with the English content. ...mdx.flat, processor: mdx.createRemarkProcessor({ lintCodeBlocks: true }), rules: { ...mdx.flat.rules, + // Components used in MDX are never imported; they are injected by our + // compiler from `mdx/components.mjs`, so they are never in lexical scope. + 'no-undef': 'off', 'no-irregular-whitespace': 'off', '@next/next/no-img-element': 'off', '@next/next/no-html-link-for-pages': ['error', 'apps/site/pages/'], diff --git a/apps/site/hooks/useSiteNavigation.ts b/apps/site/hooks/useSiteNavigation.ts index aa7a2be8efb78..d0c8236010b45 100644 --- a/apps/site/hooks/useSiteNavigation.ts +++ b/apps/site/hooks/useSiteNavigation.ts @@ -19,6 +19,7 @@ type MappedNavigationEntry = { label: FormattedMessage; link: string; target?: HTMLAttributeAnchorTarget | undefined; + accent?: boolean; }; // Provides Context replacement for variables within the Link. This is also something that is not going @@ -44,13 +45,14 @@ const useSiteNavigation = () => { t.rich(label, context[key] || {}) as FormattedMessage; return Object.entries(entries).map( - ([key, { label, link, items, target }]): [ + ([key, { label, link, items, target, accent }]): [ string, MappedNavigationEntry, ] => [ key, { target, + accent, label: label ? getFormattedMessage(label, key) : '', link: link ? replaceLinkWithContext(link, context[key]) : '', items: items ? mapNavigationEntries(items, context) : [], diff --git a/apps/site/instrumentation.ts b/apps/site/instrumentation.ts index 86097a48800b1..cb1ffbbeb0be6 100644 --- a/apps/site/instrumentation.ts +++ b/apps/site/instrumentation.ts @@ -1,7 +1 @@ -export async function register() { - if (!('Cloudflare' in globalThis)) { - // Note: we don't need to set up the Vercel OTEL if the application is running on Cloudflare - const { registerOTel } = await import('@vercel/otel'); - registerOTel({ serviceName: 'nodejs-org' }); - } -} +export { register } from '#platform/instrumentation'; diff --git a/apps/site/mdx/plugins.mjs b/apps/site/mdx/plugins.mjs index 02166643aaf26..a220d13ec19ce 100644 --- a/apps/site/mdx/plugins.mjs +++ b/apps/site/mdx/plugins.mjs @@ -1,5 +1,6 @@ 'use strict'; +import { shikiOptions } from '#platform/shiki.mjs'; import rehypeShikiji from '@node-core/rehype-shiki/plugin'; import remarkHeadings from '@vcarl/remark-headings'; import rehypeAutolinkHeadings from 'rehype-autolink-headings'; @@ -9,25 +10,8 @@ import readingTime from 'remark-reading-time'; import remarkTableTitles from '../util/table'; -// TODO(@avivkeller): When available, use `OPEN_NEXT_CLOUDFLARE` environment -// variable for detection instead of current method, which will enable better -// tree-shaking. -// Reference: https://github.com/nodejs/nodejs.org/pull/7896#issuecomment-3009480615 -const OPEN_NEXT_CLOUDFLARE = 'Cloudflare' in global; - -// Shiki is created out here to avoid an async rehype plugin -const singletonShiki = await rehypeShikiji({ - // We use the faster WASM engine on the server instead of the web-optimized version. - // - // Currently we fall back to the JavaScript RegEx engine - // on Cloudflare workers because `shiki/wasm` requires loading via - // `WebAssembly.instantiate` with custom imports, which Cloudflare doesn't support - // for security reasons. - wasm: !OPEN_NEXT_CLOUDFLARE, - - // TODO(@avivkeller): Find a way to enable Twoslash w/ a VFS on Cloudflare - twoslash: !OPEN_NEXT_CLOUDFLARE, -}); +// Shiki is created out here to avoid an async rehype plugin. +const singletonShiki = await rehypeShikiji(shikiOptions); /** * Provides all our Rehype Plugins that are used within MDX diff --git a/apps/site/navigation.json b/apps/site/navigation.json index 65d0e9f69b295..c90136d5edbd8 100644 --- a/apps/site/navigation.json +++ b/apps/site/navigation.json @@ -20,6 +20,12 @@ "link": "https://nodejs.org/docs/latest/api/", "label": "components.containers.navBar.links.docs" }, + "betaDocs": { + "link": "https://beta.docs.nodejs.org/", + "label": "components.containers.navBar.links.betaDocs", + "target": "_blank", + "accent": true + }, "contribute": { "link": "https://github.com/nodejs/node/blob/main/CONTRIBUTING.md", "label": "components.containers.navBar.links.contribute", diff --git a/apps/site/next-data/generators/supportersData.mjs b/apps/site/next-data/generators/supportersData.mjs index 0a3423918ea1a..0aa6f6b5eb318 100644 --- a/apps/site/next-data/generators/supportersData.mjs +++ b/apps/site/next-data/generators/supportersData.mjs @@ -1,5 +1,85 @@ -import { OPENCOLLECTIVE_MEMBERS_URL } from '#site/next.constants.mjs'; +import { + OPENCOLLECTIVE_MEMBERS_URL, + GITHUB_GRAPHQL_URL, + GITHUB_READ_API_KEY, +} from '#site/next.constants.mjs'; import { fetchWithRetry } from '#site/next.fetch.mjs'; +import { shuffle } from '#site/util/array'; + +const SPONSORSHIPS_QUERY = ` + query ($cursor: String) { + organization(login: "nodejs") { + sponsorshipsAsMaintainer( + first: 100 + includePrivate: false + after: $cursor + activeOnly: false + ) { + nodes { + sponsor: sponsorEntity { + ...on User { + id: databaseId + name + login + avatarUrl + url + websiteUrl + } + ...on Organization { + id: databaseId + name + login + avatarUrl + url + websiteUrl + } + } + } + pageInfo { + endCursor + startCursor + hasNextPage + hasPreviousPage + } + } + } + } +`; + +const DONATIONS_QUERY = ` + query { + organization(login: "nodejs") { + sponsorsActivities(first: 100, includePrivate: false) { + nodes { + id + sponsor { + ...on User { + id: databaseId + name + login + avatarUrl + url + websiteUrl + } + ...on Organization { + id: databaseId + name + login + avatarUrl + url + websiteUrl + } + } + timestamp + tier: sponsorsTier { + monthlyPriceInDollars + isOneTime + } + } + } + } + } +`; /** * Fetches supporters data from Open Collective API, filters active backers, @@ -14,13 +94,13 @@ async function fetchOpenCollectiveData() { const members = payload .filter(({ role, isActive }) => role === 'BACKER' && isActive) + .filter(({ name, image }) => name || image) // Ensure we have a name or image for the supporter .sort((a, b) => b.totalAmountDonated - a.totalAmountDonated) - .map(({ name, website, image, profile }) => ({ + .map(({ name, image, profile }) => ({ name, image, - url: website, // If profile starts with the guest- prefix, it's a non-existing account - profile: profile.startsWith('https://opencollective.com/guest-') + url: profile.startsWith('https://opencollective.com/guest-') ? undefined : profile, source: 'opencollective', @@ -29,4 +109,136 @@ async function fetchOpenCollectiveData() { return members; } -export default fetchOpenCollectiveData; +/** + * Fetches supporters data from Github API, filters active backers, + * and maps it to the Supporters type. + * + * @returns {Promise>} Array of supporters + */ +async function fetchGithubSponsorsData() { + if (!GITHUB_READ_API_KEY) { + return []; + } + + const [sponsorships, donations] = await Promise.all([ + fetchSponsorshipsQuery(), + fetchDonationsQuery(), + ]); + + return [...sponsorships, ...donations]; +} + +async function fetchSponsorshipsQuery() { + const sponsors = []; + let cursor = null; + + while (true) { + const data = await graphql( + SPONSORSHIPS_QUERY, + cursor ? { cursor } : undefined + ); + + if (data.errors) { + throw new Error(JSON.stringify(data.errors)); + } + + const nodeRes = data.data.organization?.sponsorshipsAsMaintainer; + if (!nodeRes) { + break; + } + + const { nodes, pageInfo } = nodeRes; + const mapped = nodes.map(n => { + const s = n.sponsor || n.sponsorEntity; // support different field names + return { + name: s?.name || s?.login || null, + image: s?.avatarUrl || null, + url: s?.url || null, + source: 'github', + }; + }); + + sponsors.push(...mapped); + + if (!pageInfo.hasNextPage) { + break; + } + + cursor = pageInfo.endCursor; + } + + return sponsors; +} + +async function fetchDonationsQuery() { + const data = await graphql(DONATIONS_QUERY); + + if (data.errors) { + throw new Error(JSON.stringify(data.errors)); + } + + const nodeRes = data.data.organization?.sponsorsActivities; + if (!nodeRes) { + return []; + } + + const { nodes } = nodeRes; + return nodes.map(n => { + const s = n.sponsor || n.sponsorEntity; // support different field names + return { + name: s?.name || s?.login || null, + image: s?.avatarUrl || null, + url: s?.url || null, + source: 'github', + }; + }); +} + +const graphql = async (query, variables = {}) => { + const res = await fetchWithRetry(GITHUB_GRAPHQL_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${GITHUB_READ_API_KEY}`, + }, + body: JSON.stringify({ query, variables }), + }); + + if (!res.ok) { + const text = await res.text(); + throw new Error(`GitHub API error: ${res.status} ${text}`); + } + + return res.json(); +}; + +/** + * Fetches supporters data from Open Collective API and GitHub Sponsors, filters active backers, + * and maps it to the Supporters type. + * + * @returns {Promise>} Array of supporters + */ +async function sponsorsData() { + const seconds = 300; // Change every 5 minutes + const seed = Math.floor(Date.now() / (seconds * 1000)); + + const sponsorsResults = await Promise.allSettled([ + fetchGithubSponsorsData(), + fetchOpenCollectiveData(), + ]); + + const sponsors = sponsorsResults.flatMap(result => { + if (result.status === 'fulfilled') { + return result.value; + } + + console.error('Supporters data source failed:', result.reason); + return []; + }); + + const shuffled = await shuffle(sponsors, seed); + + return shuffled; +} + +export default sponsorsData; diff --git a/apps/site/next.config.mjs b/apps/site/next.config.mjs index 40d1f89e87cd3..6f392d51b492d 100644 --- a/apps/site/next.config.mjs +++ b/apps/site/next.config.mjs @@ -1,23 +1,10 @@ 'use strict'; +import platformConfig from '#platform/next.config.mjs'; import createNextIntlPlugin from 'next-intl/plugin'; -import { OPEN_NEXT_CLOUDFLARE } from './next.constants.cloudflare.mjs'; import { BASE_PATH, ENABLE_STATIC_EXPORT } from './next.constants.mjs'; -import { getImagesConfig } from './next.image.config.mjs'; import { redirects, rewrites } from './next.rewrites.mjs'; -const getDeploymentId = async () => { - if (OPEN_NEXT_CLOUDFLARE) { - // If we're building for the Cloudflare deployment we want to set - // an appropriate deploymentId (needed for skew protection) - const openNextAdapter = await import('@opennextjs/cloudflare'); - - return openNextAdapter.getDeploymentId(); - } - - return undefined; -}; - /** @type {import('next').NextConfig} */ const nextConfig = { // Full Support of React 18 SSR and Streaming @@ -27,8 +14,19 @@ const nextConfig = { // We allow the BASE_PATH to be overridden in case that the Website // is being built on a subdirectory (e.g. /nodejs-website) basePath: BASE_PATH, - // Vercel/Next.js Image Optimization Settings - images: getImagesConfig(), + images: { + // We disable image optimisation during static export builds + unoptimized: ENABLE_STATIC_EXPORT, + // We add it to the remote pattern for the static images we use from multiple sources + // to be marked as safe sources (these come from Markdown files) + remotePatterns: [ + 'https://avatars.githubusercontent.com/**', + 'https://bestpractices.coreinfrastructure.org/**', + 'https://raw.githubusercontent.com/nodejs/**', + 'https://user-images.githubusercontent.com/**', + 'https://website-assets.oramasearch.com/**', + ].map(url => new URL(url)), + }, serverExternalPackages: ['twoslash'], outputFileTracingIncludes: { // Twoslash needs TypeScript declarations to function, and, by default, Next.js @@ -81,8 +79,8 @@ const nextConfig = { // Faster Development Servers with Turbopack turbopackFileSystemCacheForDev: true, }, - deploymentId: await getDeploymentId(), }; const withNextIntl = createNextIntlPlugin('./i18n.tsx'); -export default withNextIntl(nextConfig); + +export default withNextIntl({ ...nextConfig, ...platformConfig }); diff --git a/apps/site/next.constants.cloudflare.mjs b/apps/site/next.constants.cloudflare.mjs deleted file mode 100644 index a87b3ed2a5214..0000000000000 --- a/apps/site/next.constants.cloudflare.mjs +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -/** - * Whether the build process is targeting the Cloudflare open-next build or not. - * - * TODO: The `OPEN_NEXT_CLOUDFLARE` environment variable is being - * defined in the worker building script, ideally the open-next - * adapter should set it itself when it invokes the Next.js build - * process, once it does that remove the manual `OPEN_NEXT_CLOUDFLARE` - * definition in the package.json script. - */ -export const OPEN_NEXT_CLOUDFLARE = process.env.OPEN_NEXT_CLOUDFLARE; diff --git a/apps/site/next.constants.mjs b/apps/site/next.constants.mjs index 00cc9d12c9c94..abc3e4da2a87d 100644 --- a/apps/site/next.constants.mjs +++ b/apps/site/next.constants.mjs @@ -5,15 +5,6 @@ */ export const IS_DEV_ENV = process.env.NODE_ENV === 'development'; -/** - * This is used for telling Next.js if the Website is deployed on Vercel - * - * Can be used for conditionally enabling features that we know are Vercel only - * - * @see https://vercel.com/docs/projects/environment-variables/system-environment-variables#VERCEL_ENV - */ -export const VERCEL_ENV = process.env.VERCEL_ENV || undefined; - /** * This is used for telling Next.js to do a Static Export Build of the Website * @@ -39,19 +30,9 @@ export const ENABLE_STATIC_EXPORT_LOCALE = /** * This is used for any place that requires the full canonical URL path for the Node.js Website (and its deployment), such as for example, the Node.js RSS Feed. - * - * This variable can either come from the Vercel Deployment as `NEXT_PUBLIC_VERCEL_URL` or from the `NEXT_PUBLIC_BASE_URL` Environment Variable that is manually defined - * by us if necessary. Otherwise it will fallback to the default Node.js Website URL. - * - * @TODO: We should get rid of needing to rely on `VERCEL_URL` for deployment URL. - * - * @see https://vercel.com/docs/concepts/projects/environment-variables/system-environment-variables#framework-environment-variables */ -export const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL - ? process.env.NEXT_PUBLIC_BASE_URL - : process.env.VERCEL_URL - ? `https://${process.env.VERCEL_URL}` - : 'https://nodejs.org'; +export const BASE_URL = + process.env.NEXT_PUBLIC_BASE_URL || 'https://nodejs.org'; /** * This is used for any place that requires the Node.js distribution URL (which by default is nodejs.org/dist) @@ -131,7 +112,7 @@ export const EXTERNAL_LINKS_SITEMAP = [ * * Note: This has no NEXT_PUBLIC prefix as it should not be exposed to the Browser. */ -export const GITHUB_API_KEY = process.env.NEXT_GITHUB_API_KEY || ''; +export const GITHUB_READ_API_KEY = process.env.NEXT_GITHUB_READ_API_KEY || ''; /** * The resource we point people to when discussing internationalization efforts. @@ -178,6 +159,11 @@ export const VULNERABILITIES_URL = export const OPENCOLLECTIVE_MEMBERS_URL = 'https://opencollective.com/nodejs/members/all.json'; +/** + * The location of the GitHub GraphQL API + */ +export const GITHUB_GRAPHQL_URL = 'https://api.github.com/graphql'; + /** * Orama DB URLs for the Learn and API sections of the website */ diff --git a/apps/site/next.image.config.mjs b/apps/site/next.image.config.mjs deleted file mode 100644 index 519523ca2aafe..0000000000000 --- a/apps/site/next.image.config.mjs +++ /dev/null @@ -1,33 +0,0 @@ -import { OPEN_NEXT_CLOUDFLARE } from './next.constants.cloudflare.mjs'; -import { ENABLE_STATIC_EXPORT } from './next.constants.mjs'; - -const remotePatterns = [ - 'https://avatars.githubusercontent.com/**', - 'https://bestpractices.coreinfrastructure.org/**', - 'https://raw.githubusercontent.com/nodejs/**', - 'https://user-images.githubusercontent.com/**', - 'https://website-assets.oramasearch.com/**', -]; - -export const getImagesConfig = () => { - if (OPEN_NEXT_CLOUDFLARE) { - // If we're building for the Cloudflare deployment we want to use the custom cloudflare image loader - // - // Important: The custom loader ignores `remotePatterns` as those are configured as allowed source origins - // (https://developers.cloudflare.com/images/transform-images/sources/) - // in the Cloudflare dashboard itself instead (to the exact same values present in `remotePatterns` above). - // - return { - loader: 'custom', - loaderFile: './cloudflare/image-loader.ts', - }; - } - - return { - // We disable image optimisation during static export builds - unoptimized: ENABLE_STATIC_EXPORT, - // We add it to the remote pattern for the static images we use from multiple sources - // to be marked as safe sources (these come from Markdown files) - remotePatterns: remotePatterns.map(url => new URL(url)), - }; -}; diff --git a/apps/site/package.json b/apps/site/package.json index e6edbbbe2e3f4..366f6f9eff9a3 100644 --- a/apps/site/package.json +++ b/apps/site/package.json @@ -2,13 +2,9 @@ "name": "@node-core/website", "type": "module", "scripts": { - "prebuild": "node --run build:blog-data", "build": "cross-env NODE_NO_WARNINGS=1 next build", "build:blog-data": "cross-env NODE_NO_WARNINGS=1 node ./scripts/blog-data/index.mjs", "build:blog-data:watch": "node --watch --watch-path=pages/en/blog ./scripts/blog-data/index.mjs", - "cloudflare:build:worker": "OPEN_NEXT_CLOUDFLARE=true opennextjs-cloudflare build", - "cloudflare:deploy": "opennextjs-cloudflare deploy", - "cloudflare:preview": "wrangler dev", "predeploy": "node --run build:blog-data", "deploy": "cross-env NEXT_PUBLIC_STATIC_EXPORT=true node --run build", "predev": "node --run build:blog-data", @@ -37,80 +33,98 @@ "@node-core/ui-components": "workspace:*", "@node-core/website-i18n": "workspace:*", "@nodevu/core": "0.3.0", - "@opentelemetry/api-logs": "~0.215.0", - "@opentelemetry/instrumentation": "~0.215.0", - "@opentelemetry/resources": "~1.30.1", - "@opentelemetry/sdk-logs": "~0.215.0", "@orama/orama": "^3.1.18", - "@radix-ui/react-tabs": "^1.1.13", - "@radix-ui/react-tooltip": "^1.2.8", - "@tailwindcss/postcss": "~4.2.2", + "@radix-ui/react-tabs": "^1.1.18", + "@radix-ui/react-tooltip": "^1.2.13", + "@tailwindcss/postcss": "~4.3.3", "@types/node": "catalog:", "@types/react": "catalog:", "@vcarl/remark-headings": "~0.1.0", - "@vercel/analytics": "~2.0.1", - "@vercel/otel": "~2.1.2", - "@vercel/speed-insights": "~2.0.0", "classnames": "catalog:", "cross-env": "catalog:", "feed": "~5.2.0", "github-slugger": "~2.0.0", "gray-matter": "~4.0.3", "mdast-util-to-string": "^4.0.0", - "next": "16.2.4", - "next-intl": "~4.9.1", + "next": "catalog:", + "next-intl": "~4.13.4", "next-themes": "~0.4.6", "postcss-calc": "~10.1.1", "react": "catalog:", - "react-dom": "^19.2.4", + "react-dom": "^19.2.8", "reading-time": "~1.5.0", "rehype-autolink-headings": "~7.1.0", "rehype-slug": "~6.0.0", "remark-gfm": "~4.0.1", "remark-reading-time": "~2.1.0", - "semver": "~7.7.4", - "sval": "^0.6.9", + "semver": "~7.8.1", + "sval": "^0.6.12", "tailwindcss": "catalog:", - "twoslash": "^0.3.6", "typescript": "catalog:", "unist-util-visit": "^5.1.0", "vfile": "~6.0.3", "vfile-matter": "~5.0.1" }, "devDependencies": { - "@cloudflare/workers-types": "^4.20260418.1", - "@eslint-react/eslint-plugin": "~3.0.0", - "@flarelabs-net/wrangler-build-time-fs-assets-polyfilling": "^0.0.1", - "@next/eslint-plugin-next": "16.2.4", + "@eslint-react/eslint-plugin": "~5.8.6", + "@next/eslint-plugin-next": "16.2.11", "@node-core/remark-lint": "workspace:*", - "@opennextjs/cloudflare": "^1.19.3", "@orama/core": "^1.2.19", - "@playwright/test": "^1.58.2", - "@sentry/cloudflare": "^10.49.0", + "@playwright/test": "^1.61.1", "@testing-library/user-event": "~14.6.1", "@types/mdast": "^4.0.4", - "@types/mdx": "^2.0.13", + "@types/mdx": "^2.0.14", "@types/semver": "~7.7.1", "babel-plugin-react-compiler": "^1.0.0", "dedent": "^1.7.2", - "eslint-plugin-mdx": "~3.7.0", + "eslint-plugin-mdx": "~3.8.1", "global-jsdom": "^29.0.0", "handlebars": "4.7.9", - "jsdom": "^29.0.1", + "jsdom": "^29.1.1", "mdast-util-from-markdown": "^2.0.3", - "nock": "^14.0.11", + "nock": "^14.0.15", "remark-frontmatter": "^5.0.0", - "stylelint": "17.6.0", + "stylelint": "17.14.1", "stylelint-config-standard": "40.0.0", "stylelint-order": "8.1.1", - "stylelint-selector-bem-pattern": "4.0.1", - "tsx": "^4.21.0", + "stylelint-selector-bem-pattern": "5.0.0", + "tsx": "^4.23.1", "typescript": "catalog:", - "typescript-eslint": "~8.57.2", - "user-agent-data-types": "0.4.2", - "wrangler": "^4.77.0" + "typescript-eslint": "~8.65.0", + "user-agent-data-types": "0.4.3" + }, + "peerDependencies": { + "@node-core/platform-cloudflare": "workspace:*", + "@node-core/platform-default": "workspace:*", + "@node-core/platform-vercel": "workspace:*" + }, + "peerDependenciesMeta": { + "@node-core/platform-cloudflare": { + "optional": true + }, + "@node-core/platform-default": { + "optional": true + }, + "@node-core/platform-vercel": { + "optional": true + } }, "imports": { + "#platform/analytics": { + "cloudflare": "@node-core/platform-cloudflare/analytics.mjs", + "vercel": "@node-core/platform-vercel/analytics.tsx", + "default": "@node-core/platform-default/analytics.mjs" + }, + "#platform/instrumentation": { + "cloudflare": "@node-core/platform-cloudflare/instrumentation.mjs", + "vercel": "@node-core/platform-vercel/instrumentation.ts", + "default": "@node-core/platform-default/instrumentation.mjs" + }, + "#platform/*": { + "cloudflare": "@node-core/platform-cloudflare/*", + "vercel": "@node-core/platform-vercel/*", + "default": "@node-core/platform-default/*" + }, "#site/*": [ "./*", "./*.tsx", diff --git a/apps/site/pages/ar/about/branding.mdx b/apps/site/pages/ar/about/branding.mdx new file mode 100644 index 0000000000000..6a74361d0e74f --- /dev/null +++ b/apps/site/pages/ar/about/branding.mdx @@ -0,0 +1,128 @@ +--- +title: العلامة التجارية لـ Node.js +layout: about +--- + +# العلامة التجارية لـ Node.js + +يرجى الاطلاع على [سياسة العلامات التجارية](https://trademark-policy.openjsf.org/) لمعرفة تفاصيل الاستخدام المسموح به لشعارات وعلامات Node.js®. + +## تميمة Node.js® + +يعود الفضل لـ [Angela Angelini](https://www.linkedin.com/in/angeliningl/) في تصميم والمساهمة بشخصية Rocket Turtle. + +تميمة Node.js + +## شعار Node.js® + +### شعار Node.js® السداسي + +شعار Node.js السداسي + +### شعار Node.js® الأفقي + + + + + + + + +
+ شعار Node.js الأفقي الداكن + + شعار Node.js الأفقي الفاتح +
+ +### شعار Node.js® العمودي + + + + + + + + + + + + +
+ شعار Node.js العمودي الداكن + + شعار Node.js العمودي الفاتح +
+ شعار Node.js العمودي الأسود + + شعار Node.js العمودي الأبيض +
+ +### JS Icons + + + + + + + + +
+ JS Icons Green + + White JS Icons +
diff --git a/apps/site/pages/ar/about/eol.mdx b/apps/site/pages/ar/about/eol.mdx new file mode 100644 index 0000000000000..329a51fcb4ff6 --- /dev/null +++ b/apps/site/pages/ar/about/eol.mdx @@ -0,0 +1,48 @@ +--- +title: نهاية الدعم +layout: about +description: تعرّف على معنى نهاية دعم إصدارات Node.js، وتأثيرها على الأمان والأدوات والامتثال، بالإضافة إلى تفاصيل إصدار EOL وخيارات الدعم التجاري. +--- + +# نهاية الدعم (EOL) + +## لماذا وكيف تصل إصدارات Node.js إلى نهاية الدعم + +تُطرح الإصدارات الرئيسية من Node.js وتُحدّث ثم تصل إلى نهاية الدعم وفق جدول معروف. وبما أنه لا يمكن الاستمرار في صيانة كل مسارات الإصدارات إلى الأبد، يتوقف المشروع عن صيانة أي إصدار رئيسي من Node.js بعد انتهاء فترة الصيانة المحددة له. + +
+ + أو + +
+ +[اطّلع على جدول إصدارات Node.js](/about/releases/). + +## ماذا يحدث عند وصول إصدار إلى نهاية الدعم + +عندما يصل إصدار إلى نهاية الدعم، فهذا يعني أنه لن يتلقى أي تحديثات جديدة، بما في ذلك التحديثات الأمنية. وقد يجعل ذلك التطبيقات التي تعمل على هذه الإصدارات عرضة لثغرات ومشكلات لن يتم إصلاحها. + +- **توقف إصلاح الثغرات**: عند صدور تحديثات أمنية تكشف عن مشكلات وإصلاحات في الإصدارات الرئيسية الأحدث، فلن تصدر تحديثات للإصدارات المنتهية حتى لو كانت الثغرة نفسها تؤثر عليها. لذلك تصبح التطبيقات التي ما زالت تستخدم هذه الإصدارات ومسارات الكود المتأثرة معرضة مباشرة للهجمات التي تستغل الثغرات المعلنة. +- **تعطّل سلسلة الأدوات**: قد لا تتمكن الإصدارات المنتهية من الارتباط ديناميكيًا بالإصدارات الأحدث من المكتبات المشتركة التي تعتمد عليها، مما قد يمنع تحديثات النظام أو يسبب تعطلها. +- **الابتعاد عن منظومة الأدوات**: مع الوقت، تتوقف كثير من الحزم الشائعة عن دعم إصدارات Node.js المنتهية. وعندما يبقى التطبيق معتمدًا على حزم قديمة، فقد يتعرض لمزيد من الثغرات والمشكلات غير المعالجة ويبتعد أكثر عن المعايير الشائعة في المنظومة. +- **مشكلات في الامتثال**: كثير من عمليات التدقيق في القطاعات المختلفة لا تسمح باستخدام بيئات تشغيل غير مدعومة. + +## الإصدارات المنتهية + + + +## الدعم التجاري + +رغم السلبيات الواضحة لاستخدام إصدارات EOL منتهية الدعم، تواجه المؤسسات عملياً قيوداً تمنعها من الترقية الفورية، مثل الأكواد القديمة Legacy، أو متطلبات الامتثال، أو سلاسل الاعتمادية المعقدة. ومن خلال [برنامج استدامة منظومة OpenJS Foundation](https://openjsf.org/blog/ecosystem-sustainability-program)، يحصل Node.js على دعم من HeroDevs و NodeSource و TuxCare لتقديم خدمات تجارية للإصلاحات الأمنية. + +رغم سلبيات استخدام الإصدارات المنتهية، تواجه المؤسسات أحيانًا قيودًا تمنعها من التحديث فورًا، مثل قواعد الكود القديمة، أو متطلبات الامتثال، أو سلاسل الاعتماد المعقدة. ومن خلال [برنامج استدامة منظومة OpenJS Foundation](https://openjsf.org/blog/ecosystem-sustainability-program)، يحصل Node.js على دعم من HeroDevs وNodeSource لتقديم خدمات تجارية للإصلاحات الأمنية. + +تقدم NodeSource تقييمات ترقية بمستوى تجاري ودعماً من الخبراء لتحديث تطبيقات Node.js التي لا تزال تعمل على إصدارات EOL منتهية الدعم بأمان. يوفر [برنامج الترقية](https://nodesource.com/products/nodejs-upgrade) الموجه الخاص بهم تحليلاً عميقاً للمخاطر، ويحدد عوائق النقل، ويقدم خارطة طريق مخصصة لتسهيل رحلة الترقية الخاصة بك. + +يوفر برنامج [الدعم اللانهائي لدورة الحياة (ELS)](https://tuxcare.com/endless-lifecycle-support/) من TuxCare ترقيعات أمنية مستمرة لإصدارات Node.js منتهية الدعم. يتم نقل الإصلاحات الأمنية من النسخ الأحدث واستيرادها خلفياً، واختبار سلامتها وعدم تسببها في تراجع الأداء، وتوقيعها، ودعمها باتفاقية مستوى الخدمة SLA، وتسليمها عبر مديري الحزم الحاليين لديك. + +تقدّم HeroDevs خدمة [Never-Ending Support (NES)](https://nodejs.org/esp/herodevs) لإصدارات Node.js التي تجاوزت مرحلة الصيانة الرسمية. يشمل ذلك تحديثات أمنية، ومساعدة في الامتثال، ودعمًا فنيًا يساعدك على تجاوز الفترة الانتقالية أثناء التخطيط للتحديث. diff --git a/apps/site/pages/ar/about/get-involved/collab-summit.md b/apps/site/pages/ar/about/get-involved/collab-summit.md new file mode 100644 index 0000000000000..aa9b0fc2ad663 --- /dev/null +++ b/apps/site/pages/ar/about/get-involved/collab-summit.md @@ -0,0 +1,16 @@ +--- +title: القمة التعاونية +layout: about +--- + +# القمة التعاونية + +القمة التعاونية هي مؤتمر يهدف إلى جمع المساهمين الحاليين والراغبين في المساهمة لمناقشة Node.js بالتعاون المباشر و التعلم وتبادل المعرفة. تجتمع الفرق و مجموعات العمل ومساهمو المجتمع مرتين سنويًا لإجراء نقاشات مهمة، والعمل حضوريًا على بعض المبادرات التي يريدون دفعها إلى الأمام. + +## من سيحضر؟ + +يحضر القمة التعاونية في المقام الأول المساهمون الحاليون وأعضاء المجتمع، لكنها ترحب أيضًا بمن لم يساهموا بعد ويرغبون في الانضمام. إذا كنت جديدًا على المساهمة في Node.js، فقد تكون القمة التعاونية فرصة مناسبة لمعرفة ما يحدث داخل المجتمع، والمساهمة بالمهارات التي تملكها وترغب في تطويرها. + +قبل انعقاد القمة، يرسل المساهمون وأعضاء المجتمع مقترحات للجلسات من أجل إعداد الجدول. يمكن للحاضرين الاطلاع على الجلسات مسبقًا قبل الوصول إلى الموقع، ثم المشاركة في النقاشات العامة بين المتعاونين، وبعدها الدخول في الجلسات. وستكون هناك أيضًا فرص كثيرة للنقاشات الجانبية وتبادل الأفكار. + +لمعرفة المزيد عن القمم التعاونية القادمة والسابقة، راجع [مستودع القمة](https://github.com/openjs-foundation/summit). ويمكنك أيضًا الاطلاع على [المسائل المسجلة](https://github.com/nodejs/summit/issues) لمعرفة المواضيع التي يقترح المساهمون وأعضاء المجتمع مناقشتها حضوريًا. diff --git a/apps/site/pages/ar/about/get-involved/events.mdx b/apps/site/pages/ar/about/get-involved/events.mdx new file mode 100644 index 0000000000000..d0c48041b167c --- /dev/null +++ b/apps/site/pages/ar/about/get-involved/events.mdx @@ -0,0 +1,16 @@ +--- +title: الأحداث القادمة +layout: about +--- + +## الأحداث القادمة + +فعاليات Node.js مفتوحة ومتاحة للجميع. نرحب بانضمام ومشاركة أي شخص. + +### اجتماعات Node.js® القادمة + +يعقد مشروع Node.js العديد من الاجتماعات على مدار العام لمناقشة وتخطيط جوانب المشروع. + +الاجتماعات التالية ستُعقد خلال 7 الأيام القادمة. + + diff --git a/apps/site/pages/ar/about/get-involved/index.md b/apps/site/pages/ar/about/get-involved/index.md new file mode 100644 index 0000000000000..f473c400b2c47 --- /dev/null +++ b/apps/site/pages/ar/about/get-involved/index.md @@ -0,0 +1,35 @@ +--- +title: شارك معنا +layout: about +--- + +# شارك معنا + +إذا كنت مهتمًا بالمشاركة في مجتمع Node.js، فهناك طرق كثيرة لذلك Node.js مجتمع كبير ومتنوع، ويمكنك المساهمة فيه بطرق عدّة تتجاوز كتابة الكود فقط. + +## نقاشات المجتمع + +- مستودع [`nodejs/node` على GitHub](https://github.com/nodejs/node/issues) هو المكان المخصص لمناقشة ميزات نواة Node.js والإبلاغ عن المشكلات. +- مستودع [`nodejs/help` على GitHub](https://github.com/nodejs/help/issues) هو المكان الرسمي لطرح الأسئلة حول Node.js. +- [خادم Discord الرسمي لـ Node.js](https://discord.gg/nodejs) هو مكان للتواصل مع مطوري Node.js الآخرين والحصول على الأخبار الرسمية من مشروع Node.js. +- [تقويم مشروع Node.js](https://nodejs.org/calendar) يتضمن جميع اجتماعات فرق Node.js العامة. + +## مواد التعلّم + +إذا كنت ترغب في تعلم المزيد عن Node.js، فهناك العديد من الموارد المتاحة لك. + +- [مواد التعلم الرسمية لـ Node.js](https://nodejs.org/learn/). +- [توثيق مرجع API الرسمي لـ Node.js](https://nodejs.org/api/). +- يعلّم [NodeSchool.io](https://nodeschool.io/) مفاهيم Node.js من خلال تمارين تفاعلية عبر سطر الأوامر. +- يحتوي [وسم Node.js في StackOverflow](https://stackoverflow.com/questions/tagged/node.js) على عدد كبير من النقاشات والموارد المفيدة. +- يحتوي [وسم Node.js في DEV Community](https://dev.to/t/node) على مقالات ومحتوى متعلق بـ Node.js. + +## مجتمعات النقاش غير الرسمية + +إذا كنت تفضل النقاشات المجتمعية غير الرسمية حول Node.js، فهناك العديد من المنصات والمجتمعات التي يمكنك الانضمام إليها. +تجدر الإشارة إلى أن هذه المجتمعات مستقلة عن مشروع Node.js ولا تمثل قنواته الرسمية. يُرجى الالتزام بقواعد السلوك والأنظمة المعمول بها في كل مجتمع. + +- [Node Slackers](https://www.nodeslackers.com/) هو مجتمع Slack يركّز على Node.js. +- [OpenJSF Slack](https://slack-invite.openjsf.org/) هو مساحة Slack خاصة بـ OpenJS Foundation. توجد عدة قنوات مرتبطة بـ Node.js. _(القنوات التي تبدأ بـ `#nodejs-` مرتبطة بالمشروع)_ +- [r/node](https://www.reddit.com/r/node/) هو مجتمع subreddit يركّز على Node.js. +- يمكنك الانضمام إلى قناة `#node.js` على `irc.libera.chat` باستخدام [عميل IRC](https://en.wikipedia.org/wiki/Comparison_of_Internet_Relay_Chat_clients)، أو الاتصال بها من المتصفح عبر [عميل ويب](https://kiwiirc.com/nextclient/). diff --git a/apps/site/pages/ar/about/governance.md b/apps/site/pages/ar/about/governance.md new file mode 100644 index 0000000000000..b651b766fc875 --- /dev/null +++ b/apps/site/pages/ar/about/governance.md @@ -0,0 +1,24 @@ +--- +title: حوكمة المشروع +layout: about +--- + +# حوكمة المشروع + +## التوافق في السعي + +يتبع مشروع Node.js نموذجًا لاتخاذ القرارات يعتمد على [Consensus Seeking](https://en.wikipedia.org/wiki/Consensus-seeking_decision-making). + +## المساهمون + +تتم صيانة GitHub repository الأساسي [nodejs/node](https://github.com/nodejs/node) بواسطة Collaborators، ويتم ترشيحهم بشكل مستمر من Collaborators الحاليين. + +الأشخاص الذين يقدمون مساهمات مهمة وذات قيمة يصبحون Collaborators، ويحصلون على commit-access في المشروع. يتم تحديد هؤلاء الأشخاص من قبل Collaborators آخرين، ثم تتم مناقشة ترشيحهم مع Collaborators الحاليين. + +للاطلاع على القائمة الحالية من المساهمون، راجع [README.md](https://github.com/nodejs/node/blob/main/README.md#current-project-team-members) الخاص بالمشروع. + +يتوفر guide خاص بـ Collaborators في [collaborator-guide.md](https://github.com/nodejs/node/blob/main/doc/contributing/collaborator-guide.md). + +## اللجنة التوجيهية الفنية + +تتم حوكمة المشروع بواسطة [Technical Steering Committee (TSC)](https://github.com/nodejs/TSC/blob/main/TSC-Charter.md)، وهي الجهة المسؤولة عن التوجيه العام للمشروع. تتكوّن TSC من مجموعة فرعية من المساهمون النشطين، ويتم ترشيحهم من أعضاء TSC الحاليين. diff --git a/apps/site/pages/ar/about/index.mdx b/apps/site/pages/ar/about/index.mdx new file mode 100644 index 0000000000000..042214337044c --- /dev/null +++ b/apps/site/pages/ar/about/index.mdx @@ -0,0 +1,120 @@ +--- +title: حول Node.js® +layout: about +--- + +# حول Node.js® + +Node.js هي بيئة تشغيل JavaScript غير متزامنة ومعتمدة على الأحداث، ومصممة لبناء +تطبيقات شبكية قابلة للتوسع. في مثال "hello world" التالي، يمكن التعامل مع +عدد كبير من الاتصالات في الوقت نفسه. عند كل اتصال، يتم تشغيل الـ callback، +لكن إذا لم يكن هناك عمل يجب تنفيذه، فستدخل Node.js في وضع الانتظار. + +```cjs +const { createServer } = require('node:http'); + +const hostname = '127.0.0.1'; +const port = 3000; + +const server = createServer((req, res) => { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end('Hello World'); +}); + +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +}); +``` + +```mjs +import { createServer } from 'node:http'; + +const hostname = '127.0.0.1'; +const port = 3000; + +const server = createServer((req, res) => { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end('Hello World'); +}); + +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +}); +``` + +يختلف هذا عن نموذج concurrency الأكثر شيوعًا اليوم، حيث يتم الاعتماد على OS threads. +تعد الشبكات المبنية على threads أقل كفاءة نسبيًا، كما أنها صعبة الاستخدام. إضافة إلى ذلك، +لا يحتاج مستخدمو Node.js إلى القلق من dead-locking للـ process، لأن Node.js لا تستخدم locks. +تقريبًا لا توجد أي function في Node.js تنفذ I/O مباشرة، لذلك لا يتوقف الـ process إلا عند تنفيذ I/O +باستخدام synchronous methods من مكتبة Node.js القياسية. وبما أن لا شيء يوقف التنفيذ، +فإن بناء أنظمة قابلة للتوسع في Node.js يعد أمرًا منطقيًا وسهلًا نسبيًا. + +إذا كانت بعض هذه المصطلحات غير مألوفة لك، فهناك مقال كامل عن +[Blocking vs. Non-Blocking](/learn/asynchronous-work/overview-of-blocking-vs-non-blocking). + +--- + +تشبه Node.js في تصميمها أنظمة مثل +[Event Machine](https://github.com/eventmachine/eventmachine) في Ruby و[Twisted](https://twisted.org/) في Python، وقد تأثرت بها. لكن Node.js تأخذ نموذج الأحداث إلى مستوى أبعد. +فهي تقدم event loop كجزء من runtime بدلًا من تقديمها كمكتبة. في الأنظمة الأخرى، +يوجد دائمًا blocking call لبدء event loop. +عادةً يتم تعريف السلوك عبر callbacks في بداية السكربت، وفي النهاية يتم تشغيل server +من خلال blocking call مثل `EventMachine::run()`. +في Node.js، لا يوجد استدعاء خاص من نوع start-the-event-loop. تدخل Node.js ببساطة إلى event loop بعد تنفيذ السكربت المدخل. +وتخرج Node.js من event loop عندما لا تبقى callbacks أخرى يجب تنفيذها. يشبه هذا السلوك +JavaScript في المتصفح، حيث تكون event loop مخفية عن المستخدم. + +يعد HTTP جزءًا أساسيًا في Node.js، وقد صُمم مع مراعاة streaming وlow latency. +وهذا يجعل Node.js مناسبة جدًا لتكون أساسًا لمكتبة أو framework للويب. + +كون Node.js مصممة بدون threads لا يعني أنك لا تستطيع الاستفادة من عدة cores في بيئتك. +يمكن إنشاء child processes باستخدام API الخاصة بنا +[`child_process.fork()`](https://nodejs.org/api/child_process.html)، وهي مصممة لتكون سهلة التواصل معها. +وعلى الواجهة نفسها بُنيت وحدة [`cluster`](https://nodejs.org/api/cluster.html)، +التي تسمح لك بمشاركة sockets بين processes لتفعيل load balancing +على مستوى cores لديك. + +## موارد Node.js الرسمية + +لضمان الأصالة والأمان عند العمل مع Node.js، استخدم دائمًا المصادر الرسمية. تجنب الوثوق برسائل البريد، +أو binaries، أو التنزيلات القادمة من مصادر غير رسمية. + +### نطاقات Node.js الرسمية + +لتنزيل Node.js binaries والوصول إلى التوثيق الرسمي، استخدم هذه النطاقات فقط: + +- [nodejs.org](https://nodejs.org) +- [nodejs.dev](https://nodejs.dev) _(يعيد التوجيه إلى https://nodejs.org)_ +- [iojs.org](https://iojs.org) _(يعيد التوجيه إلى https://nodejs.org)_ + +### npm packages الرسمية + +يحافظ فريق Node.js على npm package scopes الرسمية التالية: + +- [`@node-core`](https://npmjs.com/~node-core) +- [`@pkgjs`](https://npmjs.com/~pkgjs) + +إضافة إلى ذلك، يحافظ فريق Node.js على packages منشورة عبر حساب npm +[`nodejs-foundation`](https://npmjs.com/~nodejs-foundation)، +مع أن packages أخرى مرتبطة بـ Node.js مثل [`undici`](https://www.npmjs.com/package/undici) قد تتم صيانتها أيضًا بواسطة مساهمين مرتبطين بالمشروع بشكل وثيق. + +استخدام packages من فريق Node.js يضمن أنك تعمل مع مكونات Node.js مدعومة رسميًا. + +### GitHub Organizations الرسمية + +تتم صيانة Node.js والمشاريع المرتبطة بها ضمن GitHub organizations الرسمية التالية: + +- [nodejs](https://github.com/nodejs) +- [pkgjs](https://github.com/pkgjs) + +### قنوات التواصل الرسمية + +تتواصل Node.js وOpenJS Foundation عبر عدة قنوات رسمية وقنوات مدعومة من المجتمع. يمكنك العثور على التفاصيل حول +كيفية المشاركة في صفحة [شارك معنا](https://nodejs.org/en/about/get-involved). + +### الإبلاغ عن مشاكل الموقع والتوقفات + +إذا واجهت مشاكل في موقع Node.js، فأبلغ عنها في [مستودع موقع Node.js](https://github.com/nodejs/nodejs.org/issues). +وللحصول على تحديثات فورية حول الانقطاعات، زر [صفحة حالة Node.js](https://status.nodejs.org). diff --git a/apps/site/pages/ar/about/partners.mdx b/apps/site/pages/ar/about/partners.mdx new file mode 100644 index 0000000000000..ed673eb6349d6 --- /dev/null +++ b/apps/site/pages/ar/about/partners.mdx @@ -0,0 +1,51 @@ +--- +title: الشركاء والداعمون +layout: about +--- + +# الشركاء والداعمون + +يزدهر مجتمع Node.js بفضل شبكة نشطة من الشركاء الذين يساهمون +بطرق متنوعة. ومن خلال هذه الشراكات، نحافظ على بنية تحتية قوية، +ونعزز الأمان، وندعم الإصدارات الجديدة، وننمي منظومتنا بشكل مستدام. + +نقدّر دعم شركائنا الذين يؤدون دورًا مهمًا في نجاح Node.js +واستمرار تطويره. + +## البنية التحتية + +يقدّم شركاء البنية التحتية دعمًا مهمًا لمشروع Node.js، +من خلال توفير العتاد والأجهزة اللازمة لعمليات التكامل المستمر والاختبار، +وبدون هذا الدعم لا يمكننا اختبار الإصدارات الجديدة من Node.js وإطلاقها. + + + +## الداعمون + +الداعمون هم أفراد ومؤسسات يقدّمون دعمًا ماليًا لمشروع Node.js +عبر [OpenCollective](https://opencollective.com/nodejs) و[GitHub Sponsors](https://github.com/sponsors/nodejs). + + + +## برنامج استدامة المنظومة (ESP) + +هل تستخدم إصدارًا من Node.js وصل إلى نهاية الدعم (EOL)؟ برنامج استدامة منظومة OpenJS (ESP) +يساعد المؤسسات على صيانة تطبيقات Node.js التي تعمل على إصدارات منتهية الدعم. +يوفر البرنامج الوصول إلى تحديثات أمنية، ومساعدة في الامتثال، ودعمًا فنيًا +يساعد على تجاوز الفترة الانتقالية أثناء التخطيط لاستراتيجية التحديث. لمزيد من المعلومات حول +الإصدارات المنتهية، يرجى زيارة [إصدارات Node.js المنتهية الدعم](/about/eol) + +> ينبغي النظر إلى استخدام الإصدارات المنتهية عبر الدعم التجاري كحل مؤقت، فالهدف دائمًا +> هو التحديث إلى إصدارات ما زالت مدعومة رسميًا. + + + +## كن شريكًا + +كن شريكًا في مشروع Node.js وساعدنا على مواصلة تطوير هذا المشروع وصيانته. +دعمك ضروري لضمان بقاء Node.js منصة موثوقة وآمنة للمطورين والمؤسسات حول العالم. +إذا كنت مهتمًا بأن تصبح شريكًا، فيرجى التواصل معنا عبر OpenJS Foundation. + +
+ +
diff --git a/apps/site/pages/ar/about/previous-releases.mdx b/apps/site/pages/ar/about/previous-releases.mdx new file mode 100644 index 0000000000000..0e665d45de99b --- /dev/null +++ b/apps/site/pages/ar/about/previous-releases.mdx @@ -0,0 +1,52 @@ +--- +title: إصدارات Node.js +layout: about +--- + +# إصدارات Node.js + + + +تدخل الإصدارات الرئيسية من Node.js في حالة _Current_ لمدة ستة أشهر، مما يمنح مطوّري المكتبات وقتًا لإضافة دعمها. +تاريخيًا، وحتى Node.js 26، تصبح الإصدارات ذات الأرقام الفردية مثل 9 و11 غير مدعومة بعد ستة أشهر، بينما تنتقل الإصدارات ذات الأرقام الزوجية مثل 10 و12 إلى حالة _Active LTS_ وتصبح مناسبة للاستخدام العام. +ابتداءً من Node.js 27، ستصبح دورة الإصدارات سنوية، وسينتقل كل إصدار رئيسي إلى حالة _LTS_ بعد مرحلة _Current_ التي تستمر ستة أشهر، إضافة إلى ستة أشهر أخرى في مرحلة _Alpha_. +تعني حالة _LTS_ "الدعم طويل الأمد"، وهي تضمن عادةً إصلاح الأخطاء الحرجة لمدة إجمالية قدرها 30 شهرًا. +ينبغي لتطبيقات الإنتاج استخدام إصدارات _Active LTS_ أو _Maintenance LTS_ فقط. + +## جدول الإصدارات + +![الإصدارات](https://raw.githubusercontent.com/nodejs/Release/main/schedule.svg?sanitize=true) + +تتوفر التفاصيل الكاملة حول جدول إصدارات Node.js على [GitHub](https://github.com/nodejs/release#release-schedule). + +## هل تبحث عن أحدث إصدار ضمن فرع معين؟ + + + +## طرق التثبيت الرسمية والمجتمعية + +يوفر موقع Node.js عدة طرق تثبيت غير تفاعلية، بما في ذلك واجهات سطر الأوامر (CLIs)، ومديري حزم أنظمة التشغيل (OS) مثل `brew`، ومديري إصدارات Node.js مثل `nvm`. + +ولتسليط الضوء على مساهمات المجتمع ودعمها، قدّم مشروع Node.js صفحة تنزيلات محدّثة تصنّف طرق التثبيت إلى “رسمية” أو “مجتمعية”. يمنح هذا المستخدمين مرونة وخيارات أكثر. ولتوضيح الفرق، حددنا معايير لكل فئة. + +### طرق التثبيت الرسمية + +يجب أن تستوفي طرق التثبيت المصنّفة على أنها “رسمية” المتطلبات التالية: + +| المتطلبات (طرق التثبيت الرسمية) | +| :------------------------------------------------------------------------------------------------------------ | +| يجب أن تكون إصدارات Node.js الجديدة متاحة في نفس وقت الإصدار الرسمي. | +| يجب أن تكون لدى مشرفي المشروع علاقة وثيقة بمشروع Node.js، بما في ذلك قنوات تواصل مباشرة. | +| يجب أن تقوم طريقة التثبيت بتنزيل الملفات الثنائية الرسمية التي يوفّرها مشروع Node.js. | +| يجب ألا تبني طريقة التثبيت من المصدر عند توفر ملفات ثنائية جاهزة، كما يجب ألا تعدّل الملفات الثنائية الرسمية. | + +### طرق التثبيت المجتمعية + +يجب أن تلتزم طرق التثبيت المجتمعية المدرجة في صفحة التنزيل الذاتية الموجودة على /download بحد أدنى من المعايير: + +- **دعم الإصدارات:** يجب أن تدعم جميع إصدارات Node.js المدعومة حاليًا وغير المنتهية (EOL). +- **التوافق مع أنظمة التشغيل:** يجب أن تعمل على نظام تشغيل (OS) واحد على الأقل من الأنظمة المدعومة رسميًا. +- **دعم واسع لأنظمة التشغيل:** يجب ألا تقتصر على مجموعة محددة من توزيعات أو إصدارات أنظمة التشغيل. + - على سبيل المثال، إذا كانت طريقة التثبيت تدّعي التوافق مع “Windows”، فيجب أن تعمل على “Windows 10” و“Windows 11” وجميع إصداراتهما، بما في ذلك إصدارات الخوادم. + - وبالمثل، إذا كانت طريقة التثبيت تدّعي التوافق مع “Linux”، فيجب أن تكون قابلة للتثبيت على جميع توزيعات Linux الرئيسية، وليس على مجموعة محددة فقط. كما يجب ألا تعتمد على مديري حزم خاصين بتوزيعات معينة مثل `apt` أو `dnf`. +- **مجانية ومفتوحة المصدر:** يجب أن تكون مجانية الاستخدام ومفتوحة المصدر، وألا تُباع كمنتج تجاري أو تُقدّم كخدمة مدفوعة. diff --git a/apps/site/pages/ar/about/security-reporting.mdx b/apps/site/pages/ar/about/security-reporting.mdx new file mode 100644 index 0000000000000..914a59341c4c3 --- /dev/null +++ b/apps/site/pages/ar/about/security-reporting.mdx @@ -0,0 +1,82 @@ +--- +title: الإبلاغ الأمني +layout: about +--- + +# الإبلاغ الأمني + +لمزيد من التفاصيل حول سياسات الأمان، راجع [هذه الصفحة](https://github.com/nodejs/node/security/policy). + +## الإبلاغ عن ثغرة أمنية في Node.js + +أبلغ عن الثغرات الأمنية في Node.js عبر [HackerOne](https://hackerone.com/nodejs). + +> **ملاحظة:** إرسال بلاغ عبر HackerOne يتطلب حدًا أدنى من +> [Signal](https://docs.hackerone.com/en/articles/8369891-signal-impact) score قدره **1.0**. +> إذا كانت درجة Signal لديك أقل من هذا الحد، فتواصل بدلًا من ذلك مباشرة مع مشرفي إصدارات الأمان في Node.js +> عبر [OpenJS Foundation Slack](https://slack-invite.openjsf.org/). + +عادةً يتم تأكيد استلام البلاغ خلال 5 أيام، وستتلقى ردًا أكثر تفصيلًا خلال 10 أيام +يوضح الخطوات التالية في التعامل مع ما أرسلته. قد تمتد هذه المدة عندما يكون متطوعو فرز البلاغات (triage) +في إجازة، خاصة في نهاية العام. + +بعد الرد الأولي على البلاغ، سيحرص فريق الأمان على إبقائك على اطلاع بالتقدم نحو الإصلاح +والإعلان الكامل، وقد يطلب منك معلومات إضافية أو توضيحات حول المشكلة التي أبلغت عنها. + +### برنامَج مكافآت الثغرات الأمنية في Node.js + +يشارك مشروع Node.js في برنامج مكافآت الثغرات (bug bounty program) رسمي مخصص للباحثين الأمنيين والإفصاح العام المسؤول. +تتم إدارة البرنامج عبر منصة HackerOne. راجع [https://hackerone.com/nodejs](https://hackerone.com/nodejs) +لمزيد من التفاصيل. + +## الإبلاغ عن bug في module من جهة خارجية + +يجب الإبلاغ عن الثغرات الأمنية في third party modules إلى المسؤولين عن صيانتها (maintainers). + +## سياسة الإفصاح + +هذه هي سياسة الإفصاح الأمني الخاصة بـ Node.js: + +- يتم استلام البلاغ الأمني وتعيين مسؤول أساسي للتعامل معه. ينسق هذا الشخص عملية الإصلاح والإصدار. + يتم التحقق من المشكلة على جميع إصدارات Node.js المدعومة. بعد تأكيدها، يتم تحديد قائمة بكل الإصدارات المتأثرة. + تتم مراجعة الكود للبحث عن أي مشاكل مشابهة محتملة. ثم تُجهز الإصلاحات لكل الإصدارات المدعومة. + لا تُرسل هذه الإصلاحات إلى المستودع العام (public repository) مباشرة، بل تبقى محليًا إلى حين الإعلان. + +- يتم اختيار تاريخ حظر نشر (embargo) مقترح لهذه الثغرة، ويتم طلب CVE + (Common Vulnerabilities and Exposures (CVE®)) لها. + +- في تاريخ حظر النشر (embargo)، تُرسل نسخة من الإعلان إلى القائمة البريدية الأمنية (security mailing list) الخاصة بـ Node.js. + تُدفع التغييرات إلى المستودع العام (public repository)، ويتم نشر builds جديدة على nodejs.org. + خلال 6 ساعات من إشعار القائمة البريدية (mailing list)، تُنشر نسخة من التنبيه الأمني (advisory) على مدونة Node.js. + +- عادةً يتم تحديد تاريخ حظر النشر (embargo) بعد 72 ساعة من وقت إصدار CVE. لكن قد يختلف ذلك حسب خطورة bug + أو صعوبة تطبيق الإصلاح. + +- قد تستغرق هذه العملية بعض الوقت، خاصة عندما نحتاج إلى التنسيق مع المسؤولين عن صيانة مشاريع أخرى (maintainers). + سنحاول التعامل مع bug بأسرع ما يمكن، لكن يجب علينا اتباع عملية الإصدار (release process) الموضحة أعلاه + لضمان التعامل مع الإفصاح بطريقة متسقة. + +## تلقي تحديثات الأمان + +سيتم توزيع إشعارات الأمان عبر الطرق التالية: + +- [Google Group](https://groups.google.com/group/nodejs-sec) +- [مدونة Node.js](/blog) + +## التعليقات على هذه السياسة + +إذا كانت لديك اقتراحات لتحسين هذه العملية، فيرجى زيارة مستودع +[nodejs/security-wg](https://github.com/nodejs/security-wg). + +## أفضل ممارسات OpenSSF + + + + شارة OpenSSF + + +تعد شارة [Best Practices](https://github.com/coreinfrastructure/best-practices-badge) من Open Source Security Foundation (OpenSSF) +طريقة تتيح لمشاريع Free/Libre and Open Source Software (FLOSS) إظهار أنها تتبع أفضل الممارسات. +يمكن للمشاريع أن تمنح نفسها هذا التصديق طوعًا من خلال توضيح كيفية اتباعها لكل ممارسة. +ويستطيع مستخدمو الشارة تقييم مشاريع FLOSS التي تتبع أفضل الممارسات بسرعة، مما يزيد احتمال إنتاجها +لبرمجيات آمنة وذات جودة أعلى. diff --git a/apps/site/pages/ar/download/archive/index.mdx b/apps/site/pages/ar/download/archive/index.mdx new file mode 100644 index 0000000000000..2f62863035e04 --- /dev/null +++ b/apps/site/pages/ar/download/archive/index.mdx @@ -0,0 +1,59 @@ +--- +title: تحميل ®Node.js +layout: download-archive +--- + + + {({ binaries, installers, version, release, sources }) => ( + <> +

أرشيف تحميلات Node.js®

+ +

+ Node.js Logo + {version} + {release.codename && ` (${release.codename})`} +

+ + + + + +
    +
  • + اقرأ سجل التغييرات (changelog) أو المقالة الخاصة بهذا الإصدار. +
  • +
  • + تعرّف أكثر على إصدارات Node.js، بما في ذلك جدول الإصدارات وحالة دعم LTS. +
  • + +
  • + ملفات SHASUMS الموقعة للتحقق من ملفات الإصدار. كيفية التحقق من ملفات SHASUMS الموقعة. +
  • + +
  • + تحميل ملف الكود المصدري لـ Node.js {version} كمستند tarball موقّع. +
  • + +
+ +

إصدارات أخرى

+ + +

تحميل Binary

+ + +

حزم التثبيت (Installers)

+ + +

الإصدارات الفرعية (Minor versions)

+ + + +)} + +
diff --git a/apps/site/pages/ar/download/current.mdx b/apps/site/pages/ar/download/current.mdx new file mode 100644 index 0000000000000..dda4126ef2989 --- /dev/null +++ b/apps/site/pages/ar/download/current.mdx @@ -0,0 +1,49 @@ +--- +layout: download +title: تحميل ®Node.js +--- + +
+ +احصل على Node.js® لنظام باستخدام عبر + + + + + +أو احصل على نسخة ®Node.js جاهزة لنظام تعمل بمعمارية . + + + + + +
+ +
+ +اقرأ سجل التغييرات أو منشور المدونة لهذا الإصدار. + +تعرّف أكثر على [إصدارات Node.js](/about/previous-releases)، بما في ذلك جدول المواعيد وحالة LTS. + +تعلم كيف تتحقق من قيم SHASUMS الموقعة. + +تبحث عن الكود المصدري لـ Node.js؟ حمّل ملف الكود المصدري لـ Node.js المضغوط والموقّع. + +اطلع على نسخنا nightly الجاهزة أو +كل الإصدارات السابقة +أو النسخ غير الرسمية للمنصات الأخرى. + +
+ +--- + +
+

بفخر، مدعومون من قِبل الشركاء أدناه:

+ + + نحن قادرون على توفير تحميلات Node.js وصيانة بنيتنا التحتية بكل فخر بفضل دعم هؤلاء الشركاء، وغيرهم الكثير. + +
+ +
+
diff --git a/apps/site/pages/ar/download/index.mdx b/apps/site/pages/ar/download/index.mdx new file mode 100644 index 0000000000000..3d41ec480014a --- /dev/null +++ b/apps/site/pages/ar/download/index.mdx @@ -0,0 +1,49 @@ +--- +layout: download +title: تحميل ®Node.js +--- + +
+ +احصل على Node.js® لنظام باستخدام عبر + + + + + +أو احصل على نسخة ®Node.js جاهزة لنظام تعمل بمعمارية . + + + + + +
+ +
+ +اقرأ سجل التغييرات أو منشور المدونة لهذا الإصدار. + +تعرّف أكثر على [إصدارات Node.js](/about/previous-releases)، بما في ذلك جدول المواعيد وحالة LTS. + +تعلم كيف تتحقق من قيم SHASUMS الموقعة. + +تبحث عن الكود المصدري لـ Node.js؟ حمّل ملف الكود المصدري لـ Node.js المضغوط والموقّع. + +اطلع على نسخنا nightly الجاهزة أو +كل الإصدارات السابقة +أو النسخ غير الرسمية للمنصات الأخرى. + +
+ +--- + +
+

بفخر، مدعومون من قِبل الشركاء أدناه:

+ + + نحن قادرون على توفير تحميلات Node.js وصيانة بنيتنا التحتية بكل فخر بفضل دعم هؤلاء الشركاء، وغيرهم الكثير. + +
+ +
+
diff --git a/apps/site/pages/ar/index.mdx b/apps/site/pages/ar/index.mdx new file mode 100644 index 0000000000000..b2167a51be057 --- /dev/null +++ b/apps/site/pages/ar/index.mdx @@ -0,0 +1,127 @@ +--- +title: شغّل JavaScript في كل مكان +layout: home +--- + +
+ + +
+

شغّل JavaScript في كل مكان

+ + Node.js® هي بيئة تشغيل JavaScript مجانية، ومفتوحة المصدر، ومتعددة المنصات، تتيح للمطورين إنشاء الخوادم، وتطبيقات الويب، وأدوات الـ command line، والـ scripts. + +
+ +
+ +
+ + + + + + +
+ +
+ بفخر، Node.js مدعومة من قِبل الشركاء أعلاه وغيرهم الكثير. +
+ +
+
+ +
+
+ ```js displayName="Create an HTTP Server" + // server.mjs + import { createServer } from 'node:http'; + + const server = createServer((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('Hello World!\n'); + }); + + // starts a simple http server locally on port 3000 + server.listen(3000, '127.0.0.1', () => { + console.log('Listening on 127.0.0.1:3000'); + }); + + // run with `node server.mjs` + ``` + + ```js displayName="Write Tests" + // tests.mjs + import assert from 'node:assert'; + import test from 'node:test'; + + test('that 1 is equal 1', () => { + assert.strictEqual(1, 1); + }); + + test('that throws as 1 is not equal 2', () => { + // throws an exception because 1 != 2 + assert.strictEqual(1, 2); + }); + + // run with `node tests.mjs` + ``` + + ```js displayName="Read and Hash a File" + // crypto.mjs + import { createHash } from 'node:crypto'; + import { readFile } from 'node:fs/promises'; + + const hasher = createHash('sha1'); + + hasher.setEncoding('hex'); + // ensure you have a `package.json` file for this test! + hasher.write(await readFile('package.json')); + hasher.end(); + + const fileHash = hasher.read(); + + // run with `node crypto.mjs` + ``` + + ```js displayName="Streams Pipeline" + // streams.mjs + import { createReadStream, createWriteStream } from 'node:fs'; + import { pipeline } from 'node:stream/promises'; + import { createGzip } from 'node:zlib'; + + // ensure you have a `package.json` file for this test! + await pipeline( + createReadStream('package.json'), + createGzip(), + createWriteStream('package.json.gz') + ); + + + // run with `node streams.mjs` + ``` + + ```js displayName="Work with Threads" + // threads.mjs + import { Worker, isMainThread, + workerData, parentPort } from 'node:worker_threads'; + + if (isMainThread) { + const data = 'some data'; + const worker = new Worker(import.meta.filename, { workerData: data }); + worker.on('message', msg => console.log('Reply from Thread:', msg)); + } else { + const source = workerData; + parentPort.postMessage(btoa(source.toUpperCase())); + } + + // run with `node threads.mjs` + ``` + +
+ تعرّف أكثر على ما يمكن لـ Node.js تقديمه عبر [المواد التعليمية](/learn) الخاصة بنا. +
diff --git a/apps/site/pages/en/about/eol.mdx b/apps/site/pages/en/about/eol.mdx index 7860f6ecd360c..7acf2d60646e8 100644 --- a/apps/site/pages/en/about/eol.mdx +++ b/apps/site/pages/en/about/eol.mdx @@ -20,7 +20,7 @@ Major versions of Node.js are released, patched, and designated End-Of-Life on a
-[View the Node.js release schedule](/about/releases/). +[View the Node.js release schedule](/about/previous-releases/). ## What Happens When a Release Line Reaches EOL @@ -37,8 +37,12 @@ When a version reaches End-Of-Life, it means that it will no longer receive upda ## Commercial Support -Despite the obvious downsides of using EOL releases, in practice, organizations face constraints that prevent immediate upgrades, such as legacy codebases, compliance requirements, or complex dependency chains. Through the [OpenJS Foundation Ecosystem Sustainability Program](https://openjsf.org/blog/ecosystem-sustainability-program), Node.js is supported by HeroDevs and NodeSource to provide commercial services for security fixes. +Despite the obvious downsides of using EOL releases, in practice, organizations face constraints that prevent immediate upgrades, such as legacy codebases, compliance requirements, or complex dependency chains. Through the [OpenJS Foundation Ecosystem Sustainability Program](https://openjsf.org/blog/ecosystem-sustainability-program), Node.js is supported by HeroDevs, NodeSource, and TuxCare to provide commercial services for security fixes. HeroDevs provides [Never-Ending Support (NES)](https://nodejs.org/esp/herodevs) for Node.js versions past their official maintenance phase. This includes security patches, compliance assistance, and technical support to help bridge the gap while you plan your upgrade strategy. +NodeSource offers commercial-grade upgrade assessments and expert support to safely modernize Node.js applications still running on EOL versions. Their guided [Upgrade Program](https://nodesource.com/products/nodejs-upgrade) delivers in-depth risk analysis, identifies migration blockers, and provides a custom roadmap to streamline your upgrade journey. + +TuxCare’s [Endless Lifecycle Support (ELS)](https://tuxcare.com/endless-lifecycle-support/) gives you continued security patching for end-of-life Node.js versions. Security fixes are backported from upstream, tested for integrity and regression, signed, SLA-backed, and delivered through your existing package managers. + Using EOL releases through commercial support should be viewed as a temporary solution—the goal should always be to upgrade to actively supported versions. diff --git a/apps/site/pages/en/about/partners.mdx b/apps/site/pages/en/about/partners.mdx index 7fd69d685fdfc..5498ebbdab72b 100644 --- a/apps/site/pages/en/about/partners.mdx +++ b/apps/site/pages/en/about/partners.mdx @@ -22,8 +22,8 @@ without we can't test and release new versions of Node.js. ## Supporters -Supporters are individuals and organizations that provide financial support through -[OpenCollective](https://opencollective.com/nodejs) of the Node.js project. +Supporters are individuals and organizations who financially support the Node.js project +through [OpenCollective](https://opencollective.com/nodejs) and [GitHub Sponsors](https://github.com/sponsors/nodejs). diff --git a/apps/site/pages/en/about/previous-releases.mdx b/apps/site/pages/en/about/previous-releases.mdx index 01aa12d5e4a59..83988d91ac758 100644 --- a/apps/site/pages/en/about/previous-releases.mdx +++ b/apps/site/pages/en/about/previous-releases.mdx @@ -8,7 +8,8 @@ layout: about Major Node.js versions enter _Current_ release status for six months, which gives library authors time to add support for them. -After six months, odd-numbered releases (9, 11, etc.) become unsupported, and even-numbered releases (10, 12, etc.) move to _Active LTS_ status and are ready for general use. +Historically (up to Node.js 26), odd-numbered releases (9, 11, etc.) become unsupported after six months, and even-numbered releases (10, 12, etc.) move to _Active LTS_ status and are ready for general use. +Starting with Node.js 27, the release cycle will be annual and every major version will move to _LTS_ status after its six-month _Current_ phase (and six additional months of _Alpha_ phase). _LTS_ release status is "long-term support", which typically guarantees that critical bugs will be fixed for a total of 30 months. Production applications should only use _Active LTS_ or _Maintenance LTS_ releases. diff --git a/apps/site/pages/en/blog/announcements/new-api-docs-beta.md b/apps/site/pages/en/blog/announcements/new-api-docs-beta.md new file mode 100644 index 0000000000000..bf8f6fc4fc3d6 --- /dev/null +++ b/apps/site/pages/en/blog/announcements/new-api-docs-beta.md @@ -0,0 +1,37 @@ +--- +date: '2026-07-24T19:00:00.000Z' +category: announcements +title: Check out the New Node.js API Documentation Preview +layout: blog-post +author: Guilherme Araújo +--- + +We've been rebuilding how Node.js generates and presents its API documentation, and a preview is now live at [beta.docs.nodejs.org](https://beta.docs.nodejs.org/). Before it becomes the default, we want as many people as possible to use it for real work and tell us what you think. + +## What's different for readers + +The content itself hasn't changed: every page is generated from the same Markdown files in the [nodejs/node](https://github.com/nodejs/node) repository that power the current docs. The features you rely on today made the trip too: light and dark themes, ESM/CJS switching and copy buttons on snippets, stability badges, version history, and the Markdown version of every page. + +The redesign goes deeper than visuals, though: navigation, layout, and readability were all reworked so that finding and understanding an API takes less effort. On top of that, the beta adds things the current docs have never had: + +- **Search**: For the first time, the API docs have built-in search. A search box on every page, with a keyboard shortcut, lets you jump anywhere in the API without a detour through a search engine. +- **One design across the project**: The API docs now share the design system of the nodejs.org website, with a persistent sidebar listing every module, an always-visible per-page table of contents, and a layout that works on small screens. +- **llms.txt**: We've already shipped [llms.txt](https://nodejs.org/llms.txt), giving AI tools a clean, structured entry point to the API reference. + +And that's not the whole list: the beta also ships features like reading times and announcement bars. + +We also care about the needs of every user, and we pay attention to the ones that are easy to overlook. Every page stays usable even with JavaScript disabled and offline. + +## Under-the-hood + +The redesigned documentation is built with [doc-kit](https://github.com/nodejs/doc-kit), a standalone tool developed by the Node.js Project to replace the previous legacy documentation generator. + +For more information on doc-kit and its capabilities for other projects, please refer to [its repository](https://github.com/nodejs/doc-kit). This will also be the place where bug reports, feature requests, and other tasks should be tracked. + +## We need your feedback + +While we are proud to state that the redesigned documentation is entering its final stage of development, feedback is still a crucial part of this migration, and we'd love to hear your thoughts on this process. Please open an issue on the [nodejs/doc-kit](https://github.com/nodejs/doc-kit/issues) repository with anything you find, big or small. + +## Acknowledgments + +None of this would exist without the [Node.js Web Team](https://github.com/nodejs/web-team/blob/main/MEMBERS.md), whose members designed, built, and reviewed everything described in this post. Thanks as well to the Node.js community and the collaborators who have already helped by reviewing, testing, and making suggestions along the way. Thank you all for making this possible. diff --git a/apps/site/pages/en/blog/community/building-nodejs-together.md b/apps/site/pages/en/blog/community/building-nodejs-together.md index 71e2dc06fd694..5459a0310856b 100644 --- a/apps/site/pages/en/blog/community/building-nodejs-together.md +++ b/apps/site/pages/en/blog/community/building-nodejs-together.md @@ -26,17 +26,17 @@ living breathing website whose content is created by our end users and team. The website should be the canonical location for documentation on how to use Node.js, how Node.js works, and how to find out what's going on in the Node community. We have seeded the initial documentation with [how to -contribute](/about/get-involved/contribute/), [who the core team -is](/about/organization/#index_md_technical_steering_committee), +contribute](https://github.com/nodejs/node/blob/main/CONTRIBUTING.md), [who the core team +is](https://github.com/nodejs/node), and some basic documentation of the [project -itself](/about/organization). From there we're looking to +itself](https://github.com/nodejs/TSC). From there we're looking to enable the community to come in and build out the rest of the framework for documentation. One of the key changes here is that we're extending the tools that generate API documentation to work for the website in general. That means the website is now written in markdown. Contributions work with the same -[pull-request](/about/get-involved/contribute/#code-contributions) +[pull-request](https://github.com/nodejs/node/blob/main/CONTRIBUTING.md#pull-requests) way as contributions to Node itself. The intent here is to be able to quickly generate new documentation and improve it with feedback from the community. @@ -56,8 +56,7 @@ Road](http://blog.nodejs.org/2014/06/11/notes-from-the-road/) events there are often questions about what does and doesn't go into core. How the team identifies what those features are and when you decide to integrate them. I've spent a lot of time talking about that but I've also -[added](/about/organization) it to the new documentation on -the site. +added it to the new documentation on the site. It's pretty straight forward, but in short if Node.js needs an interface to provide an abstraction, or if everyone in the community is using the same @@ -144,7 +143,7 @@ responsible for the entirety of that subsystem, but they guide its progress by communicating with end users, reviewing bugs and pull requests, and identifying test cases and consumers of new features. People come and go from the core team, and recently we've added [some -documentation](/about/organization) that describes how you +documentation](https://github.com/nodejs/TSC) that describes how you find your way onto that team. It's based largely around our contribution process. It's not about who you work for, or about who you know, it's about your ability to provide technical improvement to the project itself. diff --git a/apps/site/pages/en/blog/events/collab-summit-2026-london.md b/apps/site/pages/en/blog/events/collab-summit-2026-london.md new file mode 100644 index 0000000000000..216081a12b0bf --- /dev/null +++ b/apps/site/pages/en/blog/events/collab-summit-2026-london.md @@ -0,0 +1,109 @@ +--- +date: '2026-04-24T00:00:00.000Z' +category: events +title: 'Trip report: Node.js collaboration summit (2026 London)' +layout: blog-post +author: Chengzhong Wu, Joyee Cheung +--- + +This April, the first [Node.js Collaboration Summit](https://nodejs.org/en/about/get-involved/collab-summit) of 2026 was hosted by [Bloomberg][] in London. In this edition, we welcomed over 40 in-person participants, and around a dozen more joined remotely. + +The recording of the summit is available in [this playlist](https://www.youtube.com/playlist?list=PLfMzBWSH11xZhA93H_9ulECtLVWtSm6zy). Here is a recap of what happened at the summit. + +### Next 10 + +In this session, [Jacob Smith][] reviewed results from the Collaborator Health survey and compared health numbers between 2025 and 2026, as well as some key highlights from the 2025 user survey (including new areas, which have generally different usage); he and [Marco Ippolito][] led an on-site review of the questions that would be asked in the [Next-10 survey for 2026](https://github.com/nodejs/next-10/issues/369), including suggestions to existing questions, questions and options to add or remove, and recent AI discussions. + +### New Release Schedule + +Rafael Gonzaga walked the audience through the new release schedule that will go into effect starting with Node.js v27. TL;DR, starting with Node.js v27, the Node.js version numbers align with the calendar year of their initial Current release! + +This is a reflection of the current Node.js volunteer-based maintenance and an effort to keep the Node.js project sustainable in the long run. When it comes to security vulnerabilities, managing security releases across four or five active release lines has become difficult to sustain in the current Node.js voluntary work model. By reducing the number of concurrent release lines, the project can focus on better supporting the releases people actually use. + +Read more about the new release schedule in [this blog post](https://nodejs.org/en/blog/announcements/evolving-the-nodejs-release-schedule). + +### New Streams API + +[James Snell][] presented a new, more unified Streams API for the Web and Node.js. Historically, Node.js has relied on the highly optimized `node:stream`. In recent years, Node.js has also implemented WHATWG Web Streams to ensure cross-platform compatibility with browsers, Deno, Bun, and edge environments (like Cloudflare Workers). However, managing two different stream ecosystems has created friction for developers. + +The Web Streams API has significant promise and allocation overhead. It was designed primarily for browsers, and when applied to high-throughput backend environments, the architectural cost becomes obvious. + +The proposed New Streams API takes advantage of modern JavaScript Async Iteration, treats streams natively as async iterables, leaning heavily into `async/await` and `for await...of` loops, which is how modern developers naturally write code. Handling a filled data buffer has historically been a confusing black box. The new API also proposes forcing developers to explicitly choose a backpressure strategy when a buffer is full. + +The new [`stream/iter`](https://nodejs.org/docs/latest-v25.x/api/stream_iter.html) API has landed in Node.js core and shipped in v25.9.0 as an experimental feature. At the summit, we collected feedback on the design, compatibility with existing stream APIs, performance implications, additional use cases, whether the new proposal can help address some common issues in the existing APIs, and discussed whether the new Streams API can be implemented on the Web. + +### Node.js Collaboratorship + +[Jacob Smith][] led the session on exploring ways to lower the barrier to entry for developers who want to contribute to the Node.js core, ensuring the project remains accessible to new talent. + +Triaging issues and reviewing pull requests takes a massive amount of maintainer bandwidth. At the session, we discussed ideas to enforce code ownership to encourage collaborators to take over more review work. Since in the Node.js voluntary work model, this can come with difficulties, we also brainstormed ideas about decoupling collaboratorship from the commit bits and extending reviewership to working group/team members, in order to encourage non-collaborators to review and build trust. We'll continue the discussions on GitHub. + +### OpenTelemetry + +[Chengzhong Wu][] presented the CNCF OpenTelemetry project. +The session was prompted by a pull request from [Bryan English][] to add Node.js built-in OpenTelemetry support. OpenTelemetry is one of the most active projects in the Cloud Native Computing Foundation, right after Kubernetes. OpenTelemetry has emerged as the de-facto standard in the world of observability with three pillars: traces, metrics, and logs. The project defines a vendor-neutral API for instrumentation and an SDK with a data processing pipeline. Apart from the OpenTelemetry API and SDK, there are also efforts to define Semantic Conventions (SemConv) and the native data format OpenTelemetry Protocol (OTLP) for sending telemetry data to backends. OpenTelemetry is an open governance project and consists of contributors from both vendors and users. + +### Observability Infrastructure + +[Stephen Belanger][] shared thoughts and work on improving Node.js observability infrastructure. The presentation covered `using` syntax support for `AsyncLocalStorage` and a new `diagnostic_channel` API `BoundedChannel`. Furthermore, Stephen presented ideas about new Node.js built-in modules for metrics and traces respectively, and the potential to add built-in support for the OpenTelemetry OTLP data protocol, improving OpenTelemetry serialization performance. + +### Node.js use of AI contributions + +A few weeks before the summit, a large pull request implementing VFS in Node.js with the assistance of an AI coding agent led to debates over the use of AI in Node.js core. In this session, [Jacob Smith][] walked the audience through the background of this controversy, the legal opinion from the OpenJS foundation, and the current status of the use of AI in other OSS projects. + +We then started listing pros & cons, concerns and thoughts about the use of AI in Node.js core using a retrospective board. There were a lot of diverse opinions about this topic. At the session, we discussed the challenges from reviewer bandwidth exacerbated by the use of AI, the ethical concerns about AI, whether a ban is feasible or desirable, whether a disclosure on the use of AI may help or draw further legal and ethical concerns, how AI may help maintenance and lower barrier of entry, and ideas on reducing the amount of noise caused by the use of AI. + +We also brainstormed how to adapt open-source governance for the modern era. Strategies discussed include enforcing a process that requires lightweight design documents, RFCs, explicit maintainer buy-in before a large PR, and pledge from the human contributor submitting the code who should take full responsibility for its quality, security, and integration. We'll continue the discussions and iteration of the policy documents on GitHub. + +### Userland migrations + +[Jacob Smith][] and [Bruno Rodrigues][] presented increased usage of userland migrations. Migrations for Node.js 22.x to 24.x deprecations are almost complete. For Node.js 25.9.0, a codemod was published along side the deprecation introduced. + +### Stabilization of module customization hooks and vm.Modules + +[Joyee Cheung][] facilitated a session to discuss the [stabilization of module loader hooks and the vm Module API](https://github.com/openjs-foundation/summit/issues/482). + +As `module.register()` is set to be [doc-deprecated for 25 and below](https://github.com/nodejs/node/pull/62395) and [runtime-deprecated for 26 and above](https://github.com/nodejs/node/pull/62401) due to [maintenance issues](https://nodejs.org/docs/latest-v25.x/api/module.html#caveats-of-asynchronous-customization-hooks), we are looking into helping the ecosystem migrate to the `module.registerHooks()` API. Ideas include providing a [userland ponyfill](https://github.com/joyeecheung/module-register-ponyfill) that re-implements most of the `module.register()` functionality using `module.registerHooks()`, and having some [userland-migration](https://github.com/nodejs/userland-migrations) automation tools to help facilitate migration. + +Joyee also presented [a new design for the vm Module APIs](https://github.com/nodejs/node/issues/62720) that have been experimental for 9 years in order to address known issues and finally bring it to stabilization. We collected feedback on how to integrate it with WebAssembly modules to ensure it's future-proof with ongoing ESM integration proposals in standardization bodies. We'll continue iterating on the design in the GitHub issue. + +### Libuv v2 + +[Santiago Gimeno][] shared that after more than a decade on version 1, there is a renewed push to launch libuv [v2](https://github.com/libuv/libuv/issues/4622), which introduces necessary breaking changes to clean up the codebase, remove legacy APIs, and improve cross-platform consistency—capabilities already being leveraged by [Julia](https://julialang.org/). + +As migrating to libuv v2 can break the ABI, we discussed ideas on how to mitigate it, for example by leveraging [Node-API](https://nodejs.org/api/n-api.html), and the nuances in this approach e.g. [`napi_get_uv_event_loop`](https://nodejs.org/api/n-api.html#napi-get-uv-event-loop) can still be impacted by libuv ABI changes, though its use is limited and its ABI stability is warned in the documentation. We also discussed getting help to maintain v1 with security patches for a limited timespan, how to bring back `io_uring`, and which Node.js can start to ship libuv v2 (a very tentative timeline could be in 27). + +### Node.js Virtual File System + +[Matteo Collina][] presented the proposal for a Node.js built-in Virtual File System. By taking concepts previously explored in userland libraries (like `@platformatic/vfs`) and standardizing them into a core `node:vfs` module, Node.js can intercept standard filesystem calls and route them through a virtualized, memory-based layer. Developers can define specific data sources in memory (providers) and "mount" them so the runtime treats them exactly like local directories. The proposal also provides the ability to layer virtual filesystems on top of one another, or place a virtual layer directly over the physical disk to safely mock or override files. + +In this session, Matteo walked the audience through the motivation of this feature: making file system virtualization easier for single-executable applications and testing, and replaces existing brittle practices that achieve this through user-land monkey-patching. Following a brief overview of the implementation architecture and the design choices, we collected more feedback on the PR, including stack trace visibility, observability, security, worker thread propagation, the ability to toggle it in specific paths, and support in native code. We'll continue iterating on this PR in GitHub. + +### Node.js Security - State of the Ecosystem & What's Next + +[Rafael Gonzaga][] shared that the security team has recently advanced the ecosystem through a refined threat model, improved permission models, and enhanced release automation, and the new VEX (Vulnerability Exploitability eXchange) files that the security team is working on to reduce false positives for security scanners. + +These efforts are currently being overshadowed by a massive influx of AI-generated vulnerability reports. This industry-wide surge, driven largely by users seeking CVE attribution and financial bounties, has severely strained maintainer capacity with high-noise, duplicative submissions that often lack reproduction steps or misclassify standard bugs as severe security threats. Despite attempted mitigations like pausing bug bounties, raising HackerOne signal requirements, and clarifying guidelines, the overwhelming volume has significantly driven up resolution times. To combat this bottleneck, the team is exploring strategies such as securing early access for proactive testing, attempting to alter reporting agent behaviors, and adopting a public security flow to bypass embargoes and speed up CI testing. + +In the session we revisited a thought-provoking proposal of making the security triage and bugfix process completely public to prevent a false sense of security in the age of AI, and to get rid of the CI embargo that has made testing inefficient. We discussed a few middle ground solutions, including keeping extending the visibility to more members in the organization, handling vulnerabilities of different severity with different visibility settings, and creating better documentations to drive AI agents away from generating false positive reports. + +## Thanks + +Thank you to all the attendees! Special appreciation goes to [Bloomberg][] for hosting the summit and creating a welcoming space for the Node.js community. + +A big thanks as well to Thomas Chetwin ([@tchetwin](https://bsky.app/profile/tchetwin.bsky.social)), [Chengzhong Wu][], [Matteo Collina][], [Joyee Cheung][], and the OpenJS Foundation for organizing and making this event possible. + +The Node.js Collaboration Summit recordings are now available at [YouTube](https://www.youtube.com/playlist?list=PLfMzBWSH11xZhA93H_9ulECtLVWtSm6zy). + +[Bloomberg]: https://www.bloomberg.com/company/values/tech-at-bloomberg/ +[Bruno Rodrigues]: https://github.com/brunocroh +[Bryan English]: https://github.com/bengl +[Chengzhong Wu]: https://github.com/legendecas +[Jacob Smith]: https://github.com/JakobJingleheimer +[James Snell]: https://github.com/jasnell +[Joyee Cheung]: https://github.com/joyeecheung +[Marco Ippolito]: https://github.com/marco-ippolito +[Matteo Collina]: https://github.com/mcollina +[Rafael Gonzaga]: https://github.com/RafaelGSS +[Santiago Gimeno]: https://github.com/santigimeno +[Stephen Belanger]: https://github.com/Qard diff --git a/apps/site/pages/en/blog/events/nodejs-interactive-2026.md b/apps/site/pages/en/blog/events/nodejs-interactive-2026.md new file mode 100644 index 0000000000000..135e61f080444 --- /dev/null +++ b/apps/site/pages/en/blog/events/nodejs-interactive-2026.md @@ -0,0 +1,127 @@ +--- +date: '2026-08-14T00:00:00Z' +category: events +title: 'Node.js Interactive 2026: A Recap' +layout: blog-post +author: Aviv Keller +--- + +# Node.js Interactive 2026: A Recap + +More than a decade after [the first Node.js Interactive was announced](https://nodejs.org/en/blog/announcements/interactive-2015), the conference returned on August 12 and 13, 2026, as a dedicated experience inside [RenderATL](https://www.renderatl.com/node) and alongside [Atlanta Tech Week](https://atl.tech/). + +Across the [two-day speaker program](https://openjsf.org/blog/node-interactive-speakers-2026), attendees learned that the future of Node.js is more than its extensive APIs. It also depends on the people who maintain critical infrastructure, the standards that keep runtimes interoperable, the platforms that make safe development easier, and the documentation and tests that turn new ideas into dependable software. + +Here are the themes that stood out and the takeaways we think every Node.js-er should know. + +## Open source infrastructure still runs on people + +[Robin Bender Ginn](https://github.com/rginn), Executive Director of the [OpenJS Foundation](https://openjsf.org/), opened the conference with her talk, **"Node.js Runs AI. Who Runs Node.js?"**: + +The JavaScript ecosystem powers an enormous share of the modern internet, including much of the infrastructure behind the current AI wave. But projects such as [Node.js](https://nodejs.org/), [Express](https://expressjs.com/), [Electron](https://www.electronjs.org/), and [webpack](https://webpack.js.org/) do not maintain themselves. They depend on people, often surprisingly small teams, doing the ongoing work of reviewing changes, responding to security reports, cutting releases, improving documentation, and keeping communities healthy. + +Robin described those maintainers as part of the "missing middle": essential to millions of developers and businesses, but too easily treated as an invisible resource. Her session focused on what sustainable maintainer support looks like and what developers and companies can do to strengthen the projects they rely on. + +Open source also depends on people agreeing about how independently developed systems should work together. In **"Code, Consensus, and Community: How the JS Ecosystem Continues To Move Forward Together,"** [Joe Sepi](https://github.com/joesepi), Workers Engineering Manager at [Cloudflare](https://www.cloudflare.com/), walked through the "alphabet soup" of JavaScript governance, including the [W3C](https://www.w3.org/), [Ecma International](https://ecma-international.org/), and the OpenJS Foundation. + +For instance, the [Web-interoperable Runtimes Community Group](https://wintercg.org/), or WinterCG, began as a grassroots effort to align APIs across Node.js, [Deno](https://deno.land), [Cloudflare Workers](https://www.cloudflare.com/products/workers/), and other server runtimes. Today, it has moved into a formal standardization track as [Ecma TC55](https://ecma-international.org/technical-committees/tc55/), or WinterTC. Its initial goal is to define a verifiable minimum common API for server-side JavaScript environments, letting runtimes become more interoperable without requiring them to become identical. + +## The supply chain is an identity problem + +In **"AI Slop and the Vulnerability Treadmill,"** [Kate Holterhoff](https://redmonk.com/kholterhoff/) traced our past year, in which package security repeatedly became equal to account security. + +In September 2025, the self-replicating [Shai-Hulud attack](https://github.blog/security/supply-chain-security/our-plan-for-a-more-secure-npm-supply-chain/) spread through compromised npm maintainer accounts. GitHub ultimately removed more than 500 compromised packages from the registry. A second wave in November [backdoored at least 796 packages](https://securitylabs.datadoghq.com/articles/shai-hulud-2.0-npm-worm/), representing more than 20 million weekly downloads, and exfiltrated stolen credentials through public GitHub repositories. + +Then, in March 2026, an attacker compromised an Axios maintainer account and [published malicious releases of a package with more than 100 million weekly downloads](https://securitylabs.datadoghq.com/articles/axios-npm-supply-chain-compromise/). The legitimate release had used OIDC-based trusted publishing through GitHub Actions. The attacker bypassed that path and published directly from the compromised account. + +While the incidents differed in execution, they generally followed a similar chain of attack: compromise a human identity, then use its legitimate publishing rights to move downstream. That is why defenses such as [npm trusted publishing](https://docs.npmjs.com/trusted-publishers), short-lived credentials, phishing-resistant authentication, protected recovery paths, dependency cooldowns, and tightly scoped automation all matter now more than ever. + +Additionally, AI now sits on both sides of that struggle. It makes convincing social engineering and high-volume, low-quality vulnerability reports cheaper to produce. In January, the curl project [ended its monetary bug bounty](https://daniel.haxx.se/blog/2026/01/26/the-end-of-the-curl-bug-bounty/) after its rate of confirmed reports fell below five percent. The project [later returned to HackerOne as an intake platform](https://daniel.haxx.se/blog/2026/02/25/curl-security-moves-again/), but without monetary rewards. + +The same broad class of technology can help defenders. Security researchers at AISLE used an AI-driven system to [identify all 12 vulnerabilities addressed in OpenSSL's January 2026 security release](https://aisle.com/newsroom/press-releases/aisle-finds-12-vulnerabilities-in-openssl). Human researchers validated the findings, worked through responsible disclosure, and collaborated with OpenSSL on remediation. [Some of the underlying bugs had remained in the codebase for more than 25 years](https://aisle.com/blog/aisle-discovers-20-openssl-zero-days-in-6-months). + +## Reproducibility needs more than version numbers + +Supply-chain reliability is also shaped by how package managers interpret metadata. In **"Beyond SemVer,"** [Darcy Clarke](https://x.com/darcy), founder and CEO of [vlt](https://vlt.sh/) discussed the future of versioning packages and the interpretation of package metadata. + +[Semantic Versioning 2.0.0](https://semver.org/) defines the structure and precedence of versions. It does not, however, define the complete range language people use in `package.json` files. Carets, tildes, and many advanced range expressions are conventions implemented by tools such as [`node-semver`](https://www.npmjs.com/package/semver). + +Darcy focused on another frequently overlooked part of the specification: build metadata. SemVer intentionally ignores build metadata when calculating version precedence. Rather than changing that rule, his talk explored using build metadata as a backwards-compatible extension point for richer package information. He proposed a brand new variant of the SemVer specification, open-source and available at [semver.xyz](https://semver.xyz). + +## More code needs stronger platforms + +While AI coding tools can increase how quickly a team produces code, they do not change what makes that code safe to ship. + +In **"Platform Thinking for the AI Era,"** [Bekah Suttner Cheek](https://bekah.io/), Staff Software Engineer at [Fastly](https://www.fastly.com/), argued that familiar platform fundamentals become more important as development speeds up: fast CI, meaningful tests, safe rollbacks, healthy codebase patterns, and clear paths from a local change to production. + +Despite the name, platform thinking is not exclusive to people with "platform" in their title. Everyone who designs a workflow, API, test suite, or deployment process can make the safe choice easier. Good guardrails prevent common mistakes, surface failures quickly, and make recovery routine instead of heroic. They also treat a near miss as useful evidence. Waiting for the same weakness to become a full incident is an expensive way to learn. + +This is ultimately a trust problem. Users and internal teams should not need to remember hidden rules or jump through unnecessary hoops to do the right thing. The platform should guide them there by default. + +## Node.js is becoming more capable out of the box + +Not long ago, starting a Node.js project often meant installing a collection of packages before writing application code. Environment variables, tests, HTTP clients, file watching, and TypeScript support all commonly began in userland. + +In **"The New Node.js: Built-in Batteries and the Road Ahead,"** [Matteo Collina](https://nodeland.dev/), Co-Founder and CTO of [Platformatic](https://platformatic.dev/), showed how much of that foundation now ships with the runtime. Modern Node.js includes built-in TypeScript type stripping, [`node:test`](https://nodejs.org/api/test.html) and [`node:assert`](https://nodejs.org/api/assert.html), native `.env` loading, [`fetch`](https://nodejs.org/api/globals.html#fetch) powered by [Undici](https://undici.nodejs.org/), watch mode, [`node:sqlite`](https://nodejs.org/api/sqlite.html), and the stable [Permission Model](https://nodejs.org/api/permissions.html) (among a great many other things). + +However, despite all this addition, adoption remains the harder part. Teams still run unsupported versions because upgrades require time, testing, and coordination. That's why, starting with the 27.x line, [Node.js will move from two major releases per year to one](https://nodejs.org/en/blog/announcements/evolving-the-nodejs-release-schedule). An Alpha phase begins in October 2026, Node.js 27.0.0 becomes Current in April 2027, and it enters LTS in October 2027. Version numbers will align with the calendar year of their initial Current release, and every release will eventually become LTS. This change reduces the number of concurrent release lines maintainers must support while preserving a predictable testing and migration window for users. + +## QUIC and HTTP/3 are taking shape in Node.js + +[James Snell](https://www.jasnell.me/), System Engineer at Cloudflare, has led much of Node.js's work on HTTP/2, QUIC, and HTTP/3. In **"QUIC in Node.js,"** he reviewed an implementation that has gone through several major iterations since the effort began in 2018. + +QUIC and HTTP/3 are related, but they are not interchangeable names. [QUIC](https://www.rfc-editor.org/rfc/rfc9000) is a secure, multiplexed transport protocol built on UDP. [HTTP/3](https://www.rfc-editor.org/rfc/rfc9114) maps HTTP semantics onto QUIC. + +Node.js's [`node:quic`](https://nodejs.org/api/quic.html) implementation is still in active development. Using it requires a Node.js binary built with experimental QUIC support and then starting that binary with [`--experimental-quic`](https://nodejs.org/api/cli.html#--experimental-quic). The runtime flag cannot add QUIC support to a binary that was compiled without it. + +James also discussed the work needed to move the implementation forward, including tests, documentation, and the use of AI to help make progress on a change set whose scale had previously stalled development. Completing the work would allow more of Node.js's networking stack, including `fetch`, to benefit from HTTP/3 over time. + +The session also previewed a proposed unified server API spanning HTTP/1.1, HTTP/2, and HTTP/3, with the intent to bring the design to WinterTC for broader discussion, something Node.js & James are both very excited about. + +## Documentation and testing are infrastructure + +New runtime features only matter when developers can understand and trust them. That makes documentation and testing infrastructure, not supporting material. + +In **"Replacing What Works: doc-kit and the Next 10 Years of Node.js Documentation,"** [Brian Muenzenmeyer](https://github.com/bmuenzenmeyer) and [Claudio Wunder](https://github.com/ovflowd) told the story of replacing a documentation pipeline that dates back to Node.js v0.6. + +The new [`doc-kit`](https://github.com/nodejs/doc-kit) CLI parses, lints, and transforms Markdown into an annotated representation that can produce redesigned web pages, legacy HTML, man pages, JSON schemas, search indexes, and `llms.txt` files. Keeping those outputs aligned is one of the major advantages of moving them onto a shared pipeline. + +The migration also brings a redesigned reader experience shaped by research from the Node.js website and infrastructure teams. You can [preview the new API documentation](https://beta.docs.nodejs.org/) today and read more about [the design and the tooling behind it](https://nodejs.org/en/blog/announcements/new-api-docs-beta). The beta remains usable without JavaScript and offline, while adding faster navigation, improved search, and clearer information architecture. + +Testing the systems described by that documentation can be just as difficult. Local-only failures, operating-system differences, port conflicts, and CI constraints all make integration tests harder to run reliably and in parallel. + +In **"Performant, Parallelizable, Framework Agnostic Node.js Integration Testing,"** [Ethan Arrowood](https://github.com/Ethan-Arrowood), Head of Open Source Engineering at [Harper](https://harper.fast/), presented [`@harperfast/integration-testing`](https://www.npmjs.com/package/@harperfast/integration-testing). The framework runs real processes, allocates ports dynamically, supports parallel execution, and integrates with both the Node.js test runner and external runners like Playwright. + +Integration tests, like the ones Ethan showed us, verify that the pieces of an application work together as a system. Good test tooling makes that level of confidence repeatable across local machines, operating systems, and CI rather than reserving it for the one environment where everything happens to line up. + +## AI may change the workflow, but not the responsibility + +Several sessions approached AI from different layers and views of the software stack, but ultimately, all asserted that developers still own the systems they ship. + +In **"Node.js After the AI Shift: Building Tools Developers Can Trust,"** [Andrea Griffiths](https://github.com/AndreaGriffiths11), Senior Developer Advocate at [GitHub](https://github.com/), broke AI-powered Node.js features into practical components: inputs, context, tool calls, guardrails, evaluation, and developer experience. + +[Aileen Villanueva Lecuona](https://aileenvl.com/), a Senior Software Engineer and Google Developer Expert, made those boundaries more concrete in **"Guiding AI Agents with MCP, Skills and Spec-Driven Development for Reliable Node.js Backends."** Her workflow combined the [Model Context Protocol](https://modelcontextprotocol.io/), specialized skills, and specifications that act as the source of truth. + +In **"Imagineering Future Interfaces,"** [Charlie Gerard](https://charliegerard.dev/), Senior Research Engineer at [CrowdStrike](https://www.crowdstrike.com/), shifted the focus from control to creativity. AI is often good at reproducing patterns that already exist. Humans remain responsible for imagining what has not been built yet. Her examples showed how agents can help product developers to prototype new interfaces based on previous human interaction in order to improve user experience. + +Finally, in **"Client Performance in the Age of AI,"** [Jenna Zeigen](https://jenna.is/), Senior Staff Engineer at [Notion](https://www.notion.so/), argued that generating more code does not guarantee generating better patterns. In fact, it creates more surface area for regressions and more opportunities for users to feel the accumulated cost. + +However, there is a solution to this: _specific_ metrics, flamegraphs, and representative benchmarks. For example, [Interaction to Next Paint](https://web.dev/articles/inp) measures whether an interface responds promptly when someone clicks, taps, or types. Another metric, Time to First Token measures how long an AI-backed experience takes to begin responding. No single metric tells the entire story, which is why choosing the right benchmarks is just as important as having one. + +## Code & Learn + +Finally, Node.js Interactive also ended the conference with a Code & Learn session in partnership with [Harper](https://harper.fast/). Attendees at every experience level sat down with core maintainers and worked through a curated set of ready-to-go contributions to Node.js core. + +The session began with the ethos of open source, the project's contribution guidelines, responsible use of AI-assisted development, and practical advice for finding an issue to work on. From there, participants moved into reproducing problems, changing code, adding tests, receiving review, and submitting patches. Plus, everyone who submitted a contribution also walked away with some cool new Node.js swag. + +The event may be over, but the [Node.js contribution guide](https://nodejs.org/en/contribute) is always open, and we'd love to have you contribute to the future of open source software. + +## Thank you, Atlanta + +Thank you to everyone who spoke, attended, volunteered, mentored, and sponsored, and to [RenderATL](https://www.renderatl.com/node), [Atlanta Tech Week](https://atl.tech/), and [Harper](https://harper.fast/) for helping make the week larger than any single conference or session. + +To keep the conversation going, join the community in the [OpenJS Slack](https://slack-invite.openjsf.org/), follow the [OpenJS community calendar](https://calendar.openjsf.org/), or make your first contribution to [Node.js](https://nodejs.org/en/contribute). + +See you at the next one! + +\- [Aviv Keller](https://aviv.sh), Node.js Core Collaborator diff --git a/apps/site/pages/en/blog/migrations/chalk-to-styletext.mdx b/apps/site/pages/en/blog/migrations/chalk-to-styletext.mdx deleted file mode 100644 index a2c4a011576d3..0000000000000 --- a/apps/site/pages/en/blog/migrations/chalk-to-styletext.mdx +++ /dev/null @@ -1,83 +0,0 @@ ---- -date: '2026-01-23T00:00:00.000Z' -category: migrations -title: Chalk to Node.js util styleText -layout: blog-post -author: richiemccoll ---- - -# Migrate from Chalk to Node.js util styleText - -## `chalk-to-util-styletext` - -This codemod aims to help you reduce external dependencies by transforming chalk method calls to use the native Node.js styling functionality. It will also handle automatic removal of the [`chalk`](https://github.com/chalk/chalk) package from the package.json. - -### Compatible Features: - -- Basic colors (red, green, blue, yellow, etc.) -- Bright colors (redBright, greenBright, etc.) -- Background colors (bgRed, bgGreen, etc.) -- Text modifiers (bold, dim, italic, underline, strikethrough, etc.) -- Style chaining via array syntax -- Environment variable support (NO_COLOR, NODE_DISABLE_COLORS, FORCE_COLOR) - -### Incompatible Features: - -- Custom RGB colors (chalk.rgb(), chalk.hex()) -- 256-color palette (chalk.ansi256()) -- Template literal syntax (chalk...``) -- Advanced modifiers with limited terminal support (overline, blink, etc.) - -### Prerequisites: - -#### Node.js Version Requirements - -- Node.js v20.12.0 or later (for util.styleText) -- `util.styleText` became stable in Node.js v22.13.0 (and v23.5.0) - -> If your package currently supports Node.js versions earlier than v20.12.0, you cannot migrate to util.styleText without dropping support for those versions. -> This requires bumping the major version of your package AND updating the engines field in your package.json to require Node.js >= v20.12.0. - -### Usage: - -The source code for this codemod can be found in the [chalk-to-util-styletext directory](https://github.com/nodejs/userland-migrations/tree/main/recipes/chalk-to-util-styletext). - -You can find this codemod in the [Codemod Registry](https://app.codemod.com/registry/@nodejs/chalk-to-util-styletext). - -```bash -npx codemod @nodejs/chalk-to-util-styletext -``` - -### Example: - -```js displayName="Before" -import chalk from 'chalk'; - -console.log(chalk.red('Error message')); - -console.log(chalk.red.bold('Important error')); - -const red = chalk.red; -console.log(red('Error')); - -const boldBlue = chalk.blue.bold; -console.log(boldBlue('Info')); -``` - -```js displayName="After" -import { styleText } from 'node:util'; - -console.log(styleText('red', 'Error message')); - -console.log(styleText(['red', 'bold'], 'Important error')); - -const red = text => styleText('red', text); -console.log(red('Error')); - -const boldBlue = text => styleText(['blue', 'bold'], text); -console.log(boldBlue('Info')); -``` - -## Recognition - -We would like to thank the maintainers of [`chalk`](https://github.com/chalk/chalk) for their support of the package over time and for its contributions to the ecosystem. diff --git a/apps/site/pages/en/blog/migrations/v12-to-v14.mdx b/apps/site/pages/en/blog/migrations/v12-to-v14.mdx index 58d1753f524d8..16bf51667c01b 100644 --- a/apps/site/pages/en/blog/migrations/v12-to-v14.mdx +++ b/apps/site/pages/en/blog/migrations/v12-to-v14.mdx @@ -30,23 +30,20 @@ The source code for this codemod can be found in the [util-print-to-console-log You can find this codemod in the [Codemod Registry](https://app.codemod.com/registry/@nodejs/util-print-to-console-log). ```bash -npx codemod run @nodejs/create-require-from-path +npx codemod run @nodejs/util-print-to-console-log ``` ### Example: -```js displayName="Before" -const util = require('node:util'); - -util.print('Hello world'); -util.puts('Hello world'); -util.debug('Hello world'); -util.error('Hello world'); -``` - -```js displayName="After" -console.log('Hello world'); -console.log('Hello world'); -console.error('Hello world'); -console.error('Hello world'); +```diff +- const util = require("node:util"); + +- util.print("Hello world"); ++ console.log("Hello world"); +- util.puts("Hello world"); ++ console.log("Hello world"); +- util.debug("Hello world"); ++ console.error("Hello world"); +- util.error("Hello world"); ++ console.error("Hello world"); ``` diff --git a/apps/site/pages/en/blog/migrations/v14-to-v16.mdx b/apps/site/pages/en/blog/migrations/v14-to-v16.mdx index d3dae0e804cc7..a59e413a94cbc 100644 --- a/apps/site/pages/en/blog/migrations/v14-to-v16.mdx +++ b/apps/site/pages/en/blog/migrations/v14-to-v16.mdx @@ -30,18 +30,15 @@ npx codemod run @nodejs/create-require-from-path ### Example: -```js displayName="Before" -import { createRequireFromPath } from 'node:module'; +```diff +- const { createRequireFromPath } = require('node:module'); ++ const { createRequire } = require('node:module'); -const requireFromPath = createRequireFromPath('/path/to/module'); -const myModule = requireFromPath('./myModule.cjs'); -``` - -```js displayName="After" -import { createRequire } from 'node:module'; +- const requireFromPath = createRequireFromPath('/path/to/module'); ++ const require = createRequire('/path/to/module'); -const require = createRequire('/path/to/module'); -const myModule = require('./myModule.cjs'); +- const myModule = requireFromPath('./myModule.cjs'); ++ const myModule = require('./myModule.cjs'); ``` ## `process-main-module` @@ -60,20 +57,13 @@ npx codemod run @nodejs/process-main-module ### Example: -```js displayName="Before" -if (process.mainModule === 'mod.js') { - // cli thing -} else { - // module thing -} -``` - -```js displayName="After" -if (require.main === 'mod.js') { - // cli thing -} else { - // module thing -} +```diff +- if (process.mainModule === "mod.js") { ++ if (require.main === "mod.js") { + // cli thing + } else { + // module thing + } ``` ## `rmdir` @@ -92,30 +82,21 @@ npx codemod run @nodejs/rmdir ### Example: -```js displayName="Before" -const fs = require('node:fs'); - -// Using fs.rmdir with the recursive option -fs.rmdir(path, { recursive: true }, callback); - -// Using fs.rmdirSync with the recursive option -fs.rmdirSync(path, { recursive: true }); - -// Using fs.promises.rmdir with the recursive option -fs.promises.rmdir(path, { recursive: true }); -``` - -```js displayName="After" -const fs = require('node:fs'); - -// Using fs.rm with recursive and force options -fs.rm(path, { recursive: true, force: true }, callback); - -// Using fs.rmSync with recursive and force options -fs.rmSync(path, { recursive: true, force: true }); - -// Using fs.promises.rm with recursive and force options -fs.promises.rm(path, { recursive: true, force: true }); +```diff + // Using fs.rmdir with the recursive option +- fs.rmdir(path, { recursive: true }, callback); ++ // Using fs.rm with recursive and force options ++ fs.rm(path, { recursive: true, force: true }, callback); + + // Using fs.rmdirSync with the recursive option +- fs.rmdirSync(path, { recursive: true }); ++ // Using fs.rmSync with recursive and force options ++ fs.rmSync(path, { recursive: true, force: true }); + + // Using fs.promises.rmdir with the recursive option +- fs.promises.rmdir(path, { recursive: true }); ++ // Using fs.promises.rm with recursive and force options ++ fs.promises.rm(path, { recursive: true, force: true }); ``` ## `tmpDir-to-tmpdir` @@ -134,14 +115,9 @@ npx codemod run @nodejs/tmpDir-to-tmpdir ### Example: -```js displayName="Before" -import { tmpDir } from 'node:os'; - -const foo = tmpDir(); -``` - -```js displayName="After" -import { tmpdir } from 'node:os'; - -const foo = tmpdir(); +```diff +- import { tmpDir } from 'node:os'; ++ import { tmpdir } from 'node:os'; +- const foo = tmpDir() ++ const foo = tmpdir() ``` diff --git a/apps/site/pages/en/blog/migrations/v16-to-v18.mdx b/apps/site/pages/en/blog/migrations/v16-to-v18.mdx new file mode 100644 index 0000000000000..b469c14b80ee2 --- /dev/null +++ b/apps/site/pages/en/blog/migrations/v16-to-v18.mdx @@ -0,0 +1,51 @@ +--- +date: '2025-10-28T00:02:00.000Z' +category: migrations +title: Node.js v16 to v18 +layout: blog-post +author: AugustinMauroy +--- + +# Node.js v16 to v18 + + + This article covers a part of the migration from Node.js v16 to v18. The + userland migrations team is working on more codemods to help you with the + migration. + + +This page provides a list of codemods to help you migrate your code from Node.js v16 to v18. + +## `err-invalid-callback` + +In Node.js v18, the [DEP0159](https://nodejs.org/api/deprecations.html#DEP0159) deprecation was introduced, which deprecated the `ERR_INVALID_CALLBACK` error code in favor of `ERR_INVALID_ARG_TYPE`. This codemod will help you replace any references to the old error code with the new one. + +The source code for this codemod can be found in the [err-invalid-callback directory](https://github.com/nodejs/userland-migrations/tree/main/recipes/err-invalid-callback). + +You can find this codemod in the [Codemod Registry](https://app.codemod.com/registry/@nodejs/err-invalid-callback). + +```bash +npx codemod run @nodejs/err-invalid-callback +``` + +### Example: + +```diff + try { + fs.readFile("file.txt", "invalid-callback"); + } catch (err) { +- if (err.code === "ERR_INVALID_CALLBACK") { ++ if (err.code === "ERR_INVALID_ARG_TYPE") { + console.error("Invalid callback provided"); + } + } +``` + +Also handles deduplication when both codes were already checked: + +```diff + const isCallbackError = +- err.code === "ERR_INVALID_CALLBACK" || +- err.code === "ERR_INVALID_ARG_TYPE"; ++ err.code === "ERR_INVALID_ARG_TYPE"; +``` diff --git a/apps/site/pages/en/blog/migrations/v20-to-v22.mdx b/apps/site/pages/en/blog/migrations/v20-to-v22.mdx index b4bfbe8071ffa..3703da6e63481 100644 --- a/apps/site/pages/en/blog/migrations/v20-to-v22.mdx +++ b/apps/site/pages/en/blog/migrations/v20-to-v22.mdx @@ -1,5 +1,5 @@ --- -date: '2025-10-28T00:02:00.000Z' +date: '2025-10-28T00:03:00.000Z' category: migrations title: Node.js v20 to v22 layout: blog-post @@ -34,10 +34,49 @@ npx codemod run @nodejs/import-assertions-to-attributes ### Example: -```js displayName="Before" -import jsonData from './data.json' assert { type: 'json' }; +```diff +- import data from './data.json' assert { type: 'json' }; ++ import data from './data.json' with { type: 'json' }; ``` -```js displayName="After" -import jsonData from './data.json' with { type: 'json' }; +## `crypto-createcipheriv-migration` + +Migrates deprecated `crypto.createCipher()` / `crypto.createDecipher()` usage to the supported `crypto.createCipheriv()` / `crypto.createDecipheriv()` APIs with explicit key derivation and IV handling. + +Node.js removed `crypto.createCipher()` and `crypto.createDecipher()` in v22.0.0 ([DEP0106](https://nodejs.org/api/deprecations.html#DEP0106)). The legacy helpers derived keys with MD5 and no salt, and silently reused static IVs. This codemod replaces those calls with the modern, explicit APIs and scaffolds secure key derivation and IV management. + +The source code for this codemod can be found in the [crypto-createcipheriv-migration directory](https://github.com/nodejs/userland-migrations/tree/main/recipes/crypto-createcipheriv-migration). + +You can find this codemod in the [Codemod Registry](https://app.codemod.com/registry/@nodejs/crypto-createcipheriv-migration). + +```bash +npx codemod run @nodejs/crypto-createcipheriv-migration ``` + +### What it does + +- Replaces invocations of `createCipher()` / `createDecipher()` with `createCipheriv()` / `createDecipheriv()`. +- Inserts scaffolding that derives keys with `crypto.scryptSync()` and generates random salts and IVs. +- Reminds developers to persist salt + IV for decryption and to adjust key/IV lengths per algorithm. +- Updates destructured imports to include the new helpers (`createCipheriv`, `createDecipheriv`, `randomBytes`, `scryptSync`). + +### Example + +```diff +- const cipher = crypto.createCipher(algorithm, password); ++ const cipher = (() => { ++ const __dep0106Salt = crypto.randomBytes(16); ++ const __dep0106Key = crypto.scryptSync(password, __dep0106Salt, 32); ++ const __dep0106Iv = crypto.randomBytes(16); ++ // DEP0106: Persist __dep0106Salt and __dep0106Iv alongside the ciphertext so it can be decrypted later. ++ return crypto.createCipheriv(algorithm, __dep0106Key, __dep0106Iv); ++ })(); +``` + +### Important notes + +- The codemod cannot guarantee algorithm-specific key/IV sizes. Review the generated `scryptSync` length and IV length defaults and adjust as needed. +- Decryption snippets include placeholders (`Buffer.alloc(16)`) that must be replaced with the salt and IV stored during encryption. +- If your project already wraps key derivation logic, you may prefer to adapt the generated scaffolding to call existing helpers. +- The generated code is not backward-compatible and will be unable to decrypt data that was encrypted using `createCipher()`. +- The use of scrypt is one possible choice for deriving a key from a password, but you may wish to use Argon2id or PBKDF2 instead. diff --git a/apps/site/pages/en/blog/migrations/v22-to-v24.mdx b/apps/site/pages/en/blog/migrations/v22-to-v24.mdx index f6655aeadbdbd..8a0ebf78b1749 100644 --- a/apps/site/pages/en/blog/migrations/v22-to-v24.mdx +++ b/apps/site/pages/en/blog/migrations/v22-to-v24.mdx @@ -1,5 +1,5 @@ --- -date: '2025-10-28T00:03:00.000Z' +date: '2025-10-28T00:04:00.000Z' category: migrations title: Node.js v22 to v24 layout: blog-post @@ -16,7 +16,7 @@ author: AugustinMauroy, Richard Lau Missing codemods will be added over time. If you have specific needs. - Read this issue to learn where we are in the process. And how to manually migrate your code. + Read this issue to learn where we are in the process and how to manually migrate your code. @@ -83,38 +83,29 @@ npx codemod run @nodejs/crypto-rsa-pss-update #### Example -```js displayName="Before" -const crypto = require('node:crypto'); +```diff +const crypto = require("node:crypto"); crypto.generateKeyPair( - 'rsa-pss', + "rsa-pss", { modulusLength: 2048, - hash: 'sha256', - mgf1Hash: 'sha1', +- hash: "sha256", +- mgf1Hash: "sha1", ++ hashAlgorithm: "sha256", ++ mgf1HashAlgorithm: "sha1", saltLength: 32, }, (err, publicKey, privateKey) => { // callback - } -); -``` - -```js displayName="After" -const crypto = require('node:crypto'); - -crypto.generateKeyPair( - 'rsa-pss', - { - modulusLength: 2048, - hashAlgorithm: 'sha256', - mgf1HashAlgorithm: 'sha1', - saltLength: 32, }, - (err, publicKey, privateKey) => { - // callback - } ); + +crypto.generateKeyPairSync("rsa-pss", { + modulusLength: 2048, +- hash: "sha256", ++ hashAlgorithm: "sha256", +}); ``` ### `dirent-path-to-parent-path` @@ -123,6 +114,8 @@ This codemod transforms the usage of `dirent.path` to use `dirent.parentPath`. See [DEP0178](https://nodejs.org/api/deprecations.html#DEP0178). +The source code for this codemod can be found in the [dirent-path-to-parent-path directory](https://github.com/nodejs/userland-migrations/tree/main/recipes/dirent-path-to-parent-path). + You can find this codemod in the [Codemod Registry](https://app.codemod.com/registry/@nodejs/dirent-path-to-parent-path). ```bash @@ -131,40 +124,26 @@ npx codemod run @nodejs/dirent-path-to-parent-path #### Examples -##### readdir - -```js displayName="Before" -const { readdir } = require('node:fs/promises'); -const entries = await readdir('/some/path', { withFileTypes: true }); -for (const dirent of entries) { - console.log(dirent.path); -} -``` +### readdir -```js displayName="After" -const { readdir } = require('node:fs/promises'); -const entries = await readdir('/some/path', { withFileTypes: true }); -for (const dirent of entries) { - console.log(dirent.parentPath); -} +```diff + const { readdir } = require('node:fs/promises'); + const entries = await readdir('/some/path', { withFileTypes: true }); + for (const dirent of entries) { +- console.log(dirent.path); ++ console.log(dirent.parentPath); + } ``` -##### opendir +### opendir -```js displayName="Before" -import { opendir } from 'node:fs/promises'; -const dir = await opendir('./'); -for await (const dirent of dir) { - console.log(`Found ${dirent.name} in ${dirent.path}`); -} -``` - -```js displayName="After" -import { opendir } from 'node:fs/promises'; -const dir = await opendir('./'); -for await (const dirent of dir) { - console.log(`Found ${dirent.name} in ${dirent.parentPath}`); -} +```diff + import { opendir } from 'node:fs/promises'; + const dir = await opendir('./'); + for await (const dirent of dir) { +- console.log(`Found ${dirent.name} in ${dirent.path}`); ++ console.log(`Found ${dirent.name} in ${dirent.parentPath}`); + } ``` ### `fs-access-mode-constants` @@ -183,21 +162,16 @@ npx codemod run @nodejs/fs-access-mode-constants #### Example -```js displayName="Before" -const fs = require('node:fs'); +```diff + const fs = require('node:fs'); -fs.access('/path/to/file', fs.F_OK, callback); -fs.access('/path/to/file', fs.R_OK | fs.W_OK, callback); +- fs.access('/path/to/file', fs.F_OK, callback); ++ fs.access('/path/to/file', fs.constants.F_OK, callback); +- fs.access('/path/to/file', fs.R_OK | fs.W_OK, callback); ++ fs.access('/path/to/file', fs.constants.R_OK | fs.constants.W_OK, callback); ``` -```js displayName="After" -const fs = require('node:fs'); - -fs.access('/path/to/file', fs.constants.F_OK, callback); -fs.access('/path/to/file', fs.constants.R_OK | fs.constants.W_OK, callback); -``` - -### `fs-truncate-to-ftruncate` +### `fs-truncate-fd-deprecation` The [`fs.truncate`](https://nodejs.org/api/fs.html#fs_fs_truncate_path_len_callback) function was deprecated ([DEP0081](https://nodejs.org/api/deprecations.html#DEP0081)) when used with a file descriptor. Use [`fs.ftruncate`](https://nodejs.org/api/fs.html#fs_fs_ftruncate_fd_len_callback) instead. @@ -211,35 +185,67 @@ npx codemod run @nodejs/fs-truncate-fd-deprecation #### Example -```js displayName="Before" -const { truncate, open, close } = require('node:fs'); +```diff +- const { truncate, open, close } = require('node:fs'); ++ const { ftruncate, open, close } = require('node:fs'); -open('file.txt', 'w', (err, fd) => { - if (err) throw err; - truncate(fd, 10, err => { + open('file.txt', 'w', (err, fd) => { if (err) throw err; - close(fd, () => {}); +- truncate(fd, 10, (err) => { ++ ftruncate(fd, 10, (err) => { + if (err) throw err; + close(fd, () => {}); + }); }); -}); ``` -```js displayName="After" -const { ftruncate, open, close } = require('node:fs'); +### `http2-priority-signaling` -open('file.txt', 'w', (err, fd) => { - if (err) throw err; - ftruncate(fd, 10, err => { - if (err) throw err; - close(fd, () => {}); - }); +This recipe removes HTTP/2 priority-related options and methods since priority signaling has been deprecated. + +See [DEP0194](https://nodejs.org/api/deprecations.html#DEP0194). + +The source code for this codemod can be found in the [http2-priority-signaling directory](https://github.com/nodejs/userland-migrations/tree/main/recipes/http2-priority-signaling). + +You can find this codemod in the [Codemod Registry](https://app.codemod.com/registry/@nodejs/http2-priority-signaling). + +```bash +npx codemod run @nodejs/http2-priority-signaling +``` + +## What this codemod does + +- Removes the `priority` property from `http2.connect()` call options +- Removes the `priority` property from `session.request()` call options +- Removes entire `stream.priority()` method call statements +- Removes the `priority` property from `client.settings()` call options +- Handles both CommonJS (`require()`) and ESM (`import`) imports + +## Examples + +```diff +import http2 from "node:http2"; + +const session = http2.connect("https://example.com", { +- priority: { weight: 16, parent: 0, exclusive: false } +}); + +const stream = session.request({ + ":path": "/api/data", +- priority: { weight: 32 } }); +- stream.priority({ exclusive: true, parent: 0, weight: 128 }); + +const client = http2.connect("https://example.com"); +-client.settings({ enablePush: false, priority: true }); ++client.settings({ enablePush: false }); ``` ### `process-assert-to-node-assert` -This recipe transforms the usage of `process.assert` to use `node:assert` module. +In Node.js v23 the [DEP0100](https://nodejs.org/api/deprecations.html#DEP0100) deprecation reached End-of-Life, and `process.assert` was removed in favor of using the `assert` module directly. This codemod will help you replace any references to `process.assert` with the appropriate import of the `assert` module and usage of `assert()`. -See [DEP0100](https://nodejs.org/api/deprecations.html#DEP0100). +The source code for this codemod can be found in the [process-assert-to-node-assert directory](https://github.com/nodejs/userland-migrations/tree/main/recipes/process-assert-to-node-assert). You can find this codemod in the [Codemod Registry](https://app.codemod.com/registry/@nodejs/process-assert-to-node-assert). @@ -249,15 +255,41 @@ npx codemod run @nodejs/process-assert-to-node-assert #### Example -```js displayName="Before" -process.assert(condition, 'Assertion failed'); +```diff ++ import assert from "node:assert"; +- process.assert(condition, "Assertion failed"); ++ assert(condition, "Assertion failed"); ``` -```js displayName="After" -import assert from 'node:assert'; -assert(condition, 'Assertion failed'); +#### Additional Notes + +This codemod uses the [`fs` capability](https://docs.codemod.com/jssg/security) to read the `package.json` file and determine if the project is using ES modules or CommonJS. Based on this information, it adds the appropriate import statement for the `assert` module. + +### `tls-create-secure-pair-to-tls-socket` + +This recipe transforms the usage from the deprecated `createSecurePair()` to `TLSSocket()`. + +See [DEP0064](https://nodejs.org/api/deprecations.html#dep0064-tlscreatesecurepair). + +The source code for this codemod can be found in the [tls-create-secure-pair-to-tls-socket directory](https://github.com/nodejs/userland-migrations/tree/main/recipes/tls-create-secure-pair-to-tls-socket). + +You can find this codemod in the [Codemod Registry](https://app.codemod.com/registry/@nodejs/tls-create-secure-pair-to-tls-socket). + +```bash +npx codemod run @nodejs/tls-create-secure-pair-to-tls-socket ``` -#### Additional Notes +## Examples + +```diff +-const { createSecurePair } = require('node:tls'); ++const { TLSSocket } = require('node:tls'); -This codemod use [`fs` capability](https://docs.codemod.com/jssg/security) to read the `package.json` file and determine if the project is using ES modules or CommonJS. Based on this information, it adds the appropriate import statement for the `assert` module. +-const pair = createSecurePair(credentials, true, true, false); ++const socket = new TLSSocket(underlyingSocket, { ++ secureContext: credentials, ++ isServer: true, ++ requestCert: true, ++ rejectUnauthorized: false ++}); +``` diff --git a/apps/site/pages/en/blog/npm/npm-1-0-link.md b/apps/site/pages/en/blog/npm/npm-1-0-link.md index bbcf993656c1d..35c90f35add77 100644 --- a/apps/site/pages/en/blog/npm/npm-1-0-link.md +++ b/apps/site/pages/en/blog/npm/npm-1-0-link.md @@ -42,6 +42,7 @@ _Also_, there’s a third use case that I didn’t really appreciate until I sta 3. Globally install something, and use it in development in a bunch of projects, and then update them all at once so that they all use the latest version. + Really, the second case above is a special-case of this third case. diff --git a/apps/site/pages/en/blog/release/v22.22.3.md b/apps/site/pages/en/blog/release/v22.22.3.md new file mode 100644 index 0000000000000..f1e84e5eb6fde --- /dev/null +++ b/apps/site/pages/en/blog/release/v22.22.3.md @@ -0,0 +1,180 @@ +--- +date: '2026-05-13T18:45:03.090Z' +category: release +title: Node.js 22.22.3 (LTS) +layout: blog-post +author: Marco Ippolito +--- + +## 2026-05-13, Version 22.22.3 'Jod' (LTS), @marco-ippolito + +### Commits + +- \[[`4f780905c5`](https://github.com/nodejs/node/commit/4f780905c5)] - **crypto**: fix potential null pointer dereference when BIO_meth_new() fails (Nora Dossche) [#61788](https://github.com/nodejs/node/pull/61788) +- \[[`4a09efb947`](https://github.com/nodejs/node/commit/4a09efb947)] - **crypto**: update root certificates to NSS 3.121 (Node.js GitHub Bot) [#62485](https://github.com/nodejs/node/pull/62485) +- \[[`e4c0d99839`](https://github.com/nodejs/node/commit/e4c0d99839)] - **deps**: update timezone to 2026a (Node.js GitHub Bot) [#62164](https://github.com/nodejs/node/pull/62164) +- \[[`0226c8dd7a`](https://github.com/nodejs/node/commit/0226c8dd7a)] - **deps**: update simdjson to 4.5.0 (Node.js GitHub Bot) [#62382](https://github.com/nodejs/node/pull/62382) +- \[[`e742ab748c`](https://github.com/nodejs/node/commit/e742ab748c)] - **deps**: update sqlite to 3.51.3 (Node.js GitHub Bot) [#62256](https://github.com/nodejs/node/pull/62256) +- \[[`73cac0571a`](https://github.com/nodejs/node/commit/73cac0571a)] - **deps**: update amaro to 1.1.8 (Node.js GitHub Bot) [#62151](https://github.com/nodejs/node/pull/62151) +- \[[`ae5c162b93`](https://github.com/nodejs/node/commit/ae5c162b93)] - **deps**: update amaro to 1.1.7 (Node.js GitHub Bot) [#61730](https://github.com/nodejs/node/pull/61730) +- \[[`b819cb9977`](https://github.com/nodejs/node/commit/b819cb9977)] - **deps**: update amaro to 1.1.6 (Node.js GitHub Bot) [#61603](https://github.com/nodejs/node/pull/61603) +- \[[`bbcce09dc7`](https://github.com/nodejs/node/commit/bbcce09dc7)] - **deps**: update sqlite to 3.52.0 (Node.js GitHub Bot) [#62150](https://github.com/nodejs/node/pull/62150) +- \[[`22ff2d81ce`](https://github.com/nodejs/node/commit/22ff2d81ce)] - **deps**: update simdjson to 4.3.1 (Node.js GitHub Bot) [#61930](https://github.com/nodejs/node/pull/61930) +- \[[`f49b51d75c`](https://github.com/nodejs/node/commit/f49b51d75c)] - **deps**: update acorn-walk to 8.3.5 (Node.js GitHub Bot) [#61928](https://github.com/nodejs/node/pull/61928) +- \[[`1a5cec0d49`](https://github.com/nodejs/node/commit/1a5cec0d49)] - **deps**: update acorn to 8.16.0 (Node.js GitHub Bot) [#61925](https://github.com/nodejs/node/pull/61925) +- \[[`d339497688`](https://github.com/nodejs/node/commit/d339497688)] - **deps**: update nbytes to 0.1.3 (Node.js GitHub Bot) [#61879](https://github.com/nodejs/node/pull/61879) +- \[[`3ff8ffd459`](https://github.com/nodejs/node/commit/3ff8ffd459)] - **deps**: remove stale OpenSSL arch configs (René) [#61834](https://github.com/nodejs/node/pull/61834) +- \[[`b8ddbc1e9a`](https://github.com/nodejs/node/commit/b8ddbc1e9a)] - **deps**: update llhttp to 9.3.1 (Node.js GitHub Bot) [#61827](https://github.com/nodejs/node/pull/61827) +- \[[`ffda97afd4`](https://github.com/nodejs/node/commit/ffda97afd4)] - **deps**: update googletest to 2461743991f9aa53e9a3625eafcbacd81a3c74cd (Node.js GitHub Bot) [#62484](https://github.com/nodejs/node/pull/62484) +- \[[`79aa32cf4f`](https://github.com/nodejs/node/commit/79aa32cf4f)] - **deps**: update googletest to 73a63ea05dc8ca29ec1d2c1d66481dd0de1950f1 (Node.js GitHub Bot) [#61927](https://github.com/nodejs/node/pull/61927) +- \[[`b6957e13b6`](https://github.com/nodejs/node/commit/b6957e13b6)] - **deps**: update archs files for openssl-3.5.6 (Node.js GitHub Bot) [#62629](https://github.com/nodejs/node/pull/62629) +- \[[`3a27669063`](https://github.com/nodejs/node/commit/3a27669063)] - **deps**: upgrade openssl sources to openssl-3.5.6 (Node.js GitHub Bot) [#62629](https://github.com/nodejs/node/pull/62629) +- \[[`d568a1bb53`](https://github.com/nodejs/node/commit/d568a1bb53)] - **deps**: upgrade npm to 10.9.8 (npm team) [#62463](https://github.com/nodejs/node/pull/62463) +- \[[`ec11f3c1d5`](https://github.com/nodejs/node/commit/ec11f3c1d5)] - **deps**: V8: backport 85b390089e51 (Thibaud Michaud) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`08609712ed`](https://github.com/nodejs/node/commit/08609712ed)] - **deps**: V8: backport 1b27e4674f11 (Thibaud Michaud) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`dcc60d5ab2`](https://github.com/nodejs/node/commit/dcc60d5ab2)] - **deps**: V8: backport 9997fc013952 (Thibaud Michaud) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`1d1f4451fb`](https://github.com/nodejs/node/commit/1d1f4451fb)] - **deps**: V8: cherry-pick b96e40d5ac85 (Clemens Backes) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`2268567237`](https://github.com/nodejs/node/commit/2268567237)] - **deps**: V8: cherry-pick 7cb6188cf913 (Thibaud Michaud) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`92804cdbea`](https://github.com/nodejs/node/commit/92804cdbea)] - **deps**: V8: cherry-pick e7ccf0af1bdd (Thibaud Michaud) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`eae2c27a40`](https://github.com/nodejs/node/commit/eae2c27a40)] - **deps**: V8: cherry-pick 8e214ec3ec8c (Thibaud Michaud) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`a1799a49bb`](https://github.com/nodejs/node/commit/a1799a49bb)] - **deps**: V8: backport 63b8849d73ae (Thibaud Michaud) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`a2df2d8731`](https://github.com/nodejs/node/commit/a2df2d8731)] - **deps**: V8: backport 323942700cfe (Thibaud Michaud) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`e3d65c7dca`](https://github.com/nodejs/node/commit/e3d65c7dca)] - **deps**: V8: backport 89dc6eab605c (Thibaud Michaud) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`5e7db133de`](https://github.com/nodejs/node/commit/5e7db133de)] - **deps**: V8: backport 910cb91733dc (Jakob Kummerow) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`d0c24a28af`](https://github.com/nodejs/node/commit/d0c24a28af)] - **deps**: V8: cherry-pick b8f91e510e0f (Thibaud Michaud) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`d358687824`](https://github.com/nodejs/node/commit/d358687824)] - **deps**: V8: cherry-pick cf03d55db2a0 (Thibaud Michaud) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`67c8b2c349`](https://github.com/nodejs/node/commit/67c8b2c349)] - **deps**: V8: cherry-pick 692f3d526a38 (Sébastien Doeraene) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`71e5a59ffd`](https://github.com/nodejs/node/commit/71e5a59ffd)] - **deps**: V8: cherry-pick c734674e03f9 (Manos Koukoutos) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`f0dbe81c7b`](https://github.com/nodejs/node/commit/f0dbe81c7b)] - **deps**: V8: cherry-pick b2f3aea23a01 (Thibaud Michaud) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`d333f480c3`](https://github.com/nodejs/node/commit/d333f480c3)] - **deps**: V8: cherry-pick 5f1342c20b59 (Matthias Liedtke) [#62783](https://github.com/nodejs/node/pull/62783) +- \[[`db722725bb`](https://github.com/nodejs/node/commit/db722725bb)] - **deps**: use npm undici\@six tag in `update-undici.sh` (Matteo Collina) [#63012](https://github.com/nodejs/node/pull/63012) +- \[[`9b57979d9c`](https://github.com/nodejs/node/commit/9b57979d9c)] - **doc**: add Rafael to last security release steward (Rafael Gonzaga) [#62423](https://github.com/nodejs/node/pull/62423) +- \[[`d8075585bf`](https://github.com/nodejs/node/commit/d8075585bf)] - **doc**: add path to vulnerabilities.json mention (Rafael Gonzaga) [#62355](https://github.com/nodejs/node/pull/62355) +- \[[`6ec9a70204`](https://github.com/nodejs/node/commit/6ec9a70204)] - **doc**: clarify fs.ReadStream and fs.WriteStream are not constructable (Kit Dallege) [#62208](https://github.com/nodejs/node/pull/62208) +- \[[`1fc86fcb6e`](https://github.com/nodejs/node/commit/1fc86fcb6e)] - **doc**: add note (and caveat) for `mock.module` about customization hooks (Jacob Smith) [#62075](https://github.com/nodejs/node/pull/62075) +- \[[`491be80bd9`](https://github.com/nodejs/node/commit/491be80bd9)] - **doc**: add efekrskl as triager (Efe) [#61876](https://github.com/nodejs/node/pull/61876) +- \[[`18558293a3`](https://github.com/nodejs/node/commit/18558293a3)] - **doc**: fix module.stripTypeScriptTypes indentation (René) [#61992](https://github.com/nodejs/node/pull/61992) +- \[[`8e20976522`](https://github.com/nodejs/node/commit/8e20976522)] - **doc**: explicitly mention Slack handle (Rafael Gonzaga) [#61986](https://github.com/nodejs/node/pull/61986) +- \[[`70b8e6b4fb`](https://github.com/nodejs/node/commit/70b8e6b4fb)] - **doc**: rename invalid `function` parameter (René) [#61942](https://github.com/nodejs/node/pull/61942) +- \[[`4045c76f6c`](https://github.com/nodejs/node/commit/4045c76f6c)] - **doc**: clarify status of feature request issues (Antoine du Hamel) [#61505](https://github.com/nodejs/node/pull/61505) +- \[[`c54652f2aa`](https://github.com/nodejs/node/commit/c54652f2aa)] - **doc**: remove incorrect mention of `module` in `typescript.md` (Rob Palmer) [#61839](https://github.com/nodejs/node/pull/61839) +- \[[`9fad6cedf5`](https://github.com/nodejs/node/commit/9fad6cedf5)] - **doc**: clarify async caveats for `events.once()` (René) [#61572](https://github.com/nodejs/node/pull/61572) +- \[[`2f1e5733fe`](https://github.com/nodejs/node/commit/2f1e5733fe)] - **doc**: update Juan's security steward info (Juan José) [#61754](https://github.com/nodejs/node/pull/61754) +- \[[`a64bdb5068`](https://github.com/nodejs/node/commit/a64bdb5068)] - **doc**: fix overstated Date header requirement in response.sendDate (Kit Dallege) [#62206](https://github.com/nodejs/node/pull/62206) +- \[[`02797de923`](https://github.com/nodejs/node/commit/02797de923)] - **doc**: fix small environment_variables typo (chris) [#62279](https://github.com/nodejs/node/pull/62279) +- \[[`f22ebdc809`](https://github.com/nodejs/node/commit/f22ebdc809)] - **doc**: fix small logic error in DETECT_MODULE_SYNTAX (René) [#62025](https://github.com/nodejs/node/pull/62025) +- \[[`9f4508062a`](https://github.com/nodejs/node/commit/9f4508062a)] - **doc**: fix methods being documented as properties in `process.md` (Antoine du Hamel) [#61765](https://github.com/nodejs/node/pull/61765) +- \[[`3ea39ff135`](https://github.com/nodejs/node/commit/3ea39ff135)] - **doc**: fix dropdown menu being obscured at <600px due to stacking context (Jeff) [#61735](https://github.com/nodejs/node/pull/61735) +- \[[`c22445079b`](https://github.com/nodejs/node/commit/c22445079b)] - **doc**: fix spacing in process message event (Aviv Keller) [#61756](https://github.com/nodejs/node/pull/61756) +- \[[`32831b5223`](https://github.com/nodejs/node/commit/32831b5223)] - **doc**: fix broken links of net.md (YuSheng Chen) [#61673](https://github.com/nodejs/node/pull/61673) +- \[[`005508d509`](https://github.com/nodejs/node/commit/005508d509)] - **doc**: remove obsolete Boxstarter automated install (Mike McCready) [#61785](https://github.com/nodejs/node/pull/61785) +- \[[`45b3506eb8`](https://github.com/nodejs/node/commit/45b3506eb8)] - **doc,src,test**: fix dead inspector help URL (semimikoh) [#62745](https://github.com/nodejs/node/pull/62745) +- \[[`37c2fd6f7d`](https://github.com/nodejs/node/commit/37c2fd6f7d)] - **esm**: fix path normalization in `finalizeResolution` (Antoine du Hamel) [#62080](https://github.com/nodejs/node/pull/62080) +- \[[`1769d74613`](https://github.com/nodejs/node/commit/1769d74613)] - **esm**: populate separate cache for require(esm) in imported CJS (Joyee Cheung) [#59679](https://github.com/nodejs/node/pull/59679) +- \[[`ee02966ffc`](https://github.com/nodejs/node/commit/ee02966ffc)] - **http**: fix keep-alive socket reuse race in requestOnFinish (Martin Slota) [#61710](https://github.com/nodejs/node/pull/61710) +- \[[`2fdb5ce6cc`](https://github.com/nodejs/node/commit/2fdb5ce6cc)] - **http2**: fix FileHandle leak in respondWithFile (sangwook) [#61707](https://github.com/nodejs/node/pull/61707) +- \[[`aa2c1eca04`](https://github.com/nodejs/node/commit/aa2c1eca04)] - **lib**: fix source map url parse in dynamic imports (Chengzhong Wu) [#61990](https://github.com/nodejs/node/pull/61990) +- \[[`785b00cbeb`](https://github.com/nodejs/node/commit/785b00cbeb)] - **meta**: pass release version to release worker (flakey5) [#62777](https://github.com/nodejs/node/pull/62777) +- \[[`447fb9a0b5`](https://github.com/nodejs/node/commit/447fb9a0b5)] - **meta**: persist sccache daemon until end of build workflows (René) [#61639](https://github.com/nodejs/node/pull/61639) +- \[[`5065a0acb3`](https://github.com/nodejs/node/commit/5065a0acb3)] - **module**: do not invoke resolve hooks twice for imported cjs (Joyee Cheung) [#61529](https://github.com/nodejs/node/pull/61529) +- \[[`9a2e21305d`](https://github.com/nodejs/node/commit/9a2e21305d)] - **module**: do not wrap module.\_load when tracing is not enabled (Joyee Cheung) [#61479](https://github.com/nodejs/node/pull/61479) +- \[[`b9240bc063`](https://github.com/nodejs/node/commit/b9240bc063)] - **module**: fix sync resolve hooks for require with node: prefixes (Joyee Cheung) [#61088](https://github.com/nodejs/node/pull/61088) +- \[[`2e91b28aaf`](https://github.com/nodejs/node/commit/2e91b28aaf)] - **module**: handle null source from async loader hooks in sync hooks (Joyee Cheung) [#59929](https://github.com/nodejs/node/pull/59929) +- \[[`39147c154e`](https://github.com/nodejs/node/commit/39147c154e)] - **module**: use sync cjs when importing cts (Marco Ippolito) [#60072](https://github.com/nodejs/node/pull/60072) +- \[[`12a2462b2c`](https://github.com/nodejs/node/commit/12a2462b2c)] - **module**: only put directly require-d ESM into require.cache (Joyee Cheung) [#59874](https://github.com/nodejs/node/pull/59874) +- \[[`cf39566277`](https://github.com/nodejs/node/commit/cf39566277)] - **src**: fix flags argument offset in JSUdpWrap (Weixie Cui) [#61948](https://github.com/nodejs/node/pull/61948) +- \[[`578a9a9230`](https://github.com/nodejs/node/commit/578a9a9230)] - **src**: clamp WriteUtf8 capacity to INT_MAX in EncodeInto (semimikoh) [#62621](https://github.com/nodejs/node/pull/62621) +- \[[`57c3035fec`](https://github.com/nodejs/node/commit/57c3035fec)] - **stream**: fix decoded fromList chunk boundary check (Thomas Watson) [#61884](https://github.com/nodejs/node/pull/61884) +- \[[`57fb008bb8`](https://github.com/nodejs/node/commit/57fb008bb8)] - **test**: update tls junk data error expectations (Filip Skokan) [#62629](https://github.com/nodejs/node/pull/62629) +- \[[`363f9a9d18`](https://github.com/nodejs/node/commit/363f9a9d18)] - **test**: skip `test-url` on `--shared-ada` builds (Antoine du Hamel) [#62019](https://github.com/nodejs/node/pull/62019) +- \[[`daaead342b`](https://github.com/nodejs/node/commit/daaead342b)] - **test**: simplify encodeInto large buffer regression test (semimikoh) [#62621](https://github.com/nodejs/node/pull/62621) +- \[[`ecfa766b41`](https://github.com/nodejs/node/commit/ecfa766b41)] - **tools**: fix auto-start-ci (Antoine du Hamel) [#61900](https://github.com/nodejs/node/pull/61900) +- \[[`17c0a610af`](https://github.com/nodejs/node/commit/17c0a610af)] - **tools**: fix parsing of commit trailers in `lint-release-proposal` GHA (Antoine du Hamel) [#62077](https://github.com/nodejs/node/pull/62077) +- \[[`89ad7dc63b`](https://github.com/nodejs/node/commit/89ad7dc63b)] - **tools**: enforce removal of `lts-watch-*` labels on release proposals (Antoine du Hamel) [#61672](https://github.com/nodejs/node/pull/61672) +- \[[`5f9bb8ef0c`](https://github.com/nodejs/node/commit/5f9bb8ef0c)] - **tools**: revert tools GHA workflow to ubuntu-latest (Richard Lau) [#62024](https://github.com/nodejs/node/pull/62024) +- \[[`977ef80ac1`](https://github.com/nodejs/node/commit/977ef80ac1)] - **url**: process crash via malformed UNC hostname in pathToFileURL() (Nicola Del Gobbo) [#62574](https://github.com/nodejs/node/pull/62574) +- \[[`ad8f518a81`](https://github.com/nodejs/node/commit/ad8f518a81)] - **zlib**: fix use-after-free when reset() is called during write (Matteo Collina) [#62325](https://github.com/nodejs/node/pull/62325) + +Windows 32-bit Installer: https://nodejs.org/dist/v22.22.3/node-v22.22.3-x86.msi \ +Windows 64-bit Installer: https://nodejs.org/dist/v22.22.3/node-v22.22.3-x64.msi \ +Windows ARM 64-bit Installer: https://nodejs.org/dist/v22.22.3/node-v22.22.3-arm64.msi \ +Windows 32-bit Binary: https://nodejs.org/dist/v22.22.3/win-x86/node.exe \ +Windows 64-bit Binary: https://nodejs.org/dist/v22.22.3/win-x64/node.exe \ +Windows ARM 64-bit Binary: https://nodejs.org/dist/v22.22.3/win-arm64/node.exe \ +macOS 64-bit Installer: https://nodejs.org/dist/v22.22.3/node-v22.22.3.pkg \ +macOS Apple Silicon 64-bit Binary: https://nodejs.org/dist/v22.22.3/node-v22.22.3-darwin-arm64.tar.gz \ +macOS Intel 64-bit Binary: https://nodejs.org/dist/v22.22.3/node-v22.22.3-darwin-x64.tar.gz \ +Linux 64-bit Binary: https://nodejs.org/dist/v22.22.3/node-v22.22.3-linux-x64.tar.xz \ +Linux PPC LE 64-bit Binary: https://nodejs.org/dist/v22.22.3/node-v22.22.3-linux-ppc64le.tar.xz \ +Linux s390x 64-bit Binary: https://nodejs.org/dist/v22.22.3/node-v22.22.3-linux-s390x.tar.xz \ +AIX 64-bit Binary: https://nodejs.org/dist/v22.22.3/node-v22.22.3-aix-ppc64.tar.gz \ +ARMv7 32-bit Binary: https://nodejs.org/dist/v22.22.3/node-v22.22.3-linux-armv7l.tar.xz \ +ARMv8 64-bit Binary: https://nodejs.org/dist/v22.22.3/node-v22.22.3-linux-arm64.tar.xz \ +Source Code: https://nodejs.org/dist/v22.22.3/node-v22.22.3.tar.gz \ +Other release files: https://nodejs.org/dist/v22.22.3/ \ +Documentation: https://nodejs.org/docs/v22.22.3/api/ + +### SHASUMS + +``` +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +40026acf78a808e43d222a0b0e0e711b95ff27934696b7925b53298bc7c8c60a node-v22.22.3-aix-ppc64.tar.gz +fcc834b0954d47fe1a8f4fcbb9157037b7fc6fe8071c5dab2690f0d6bbf39fdf node-v22.22.3-arm64.msi +0da7ff74ef8611328c8212f17943368713a2ad953fb7d89a8c8a0eae87c23207 node-v22.22.3-darwin-arm64.tar.gz +753c1629e168cc788ccc46ab61e0b35549fce08c07f82fcd3bb0d41f7fb01e7b node-v22.22.3-darwin-arm64.tar.xz +45830ba752fa0d892c6dcd640946669801293cac820a33591ded40ac075198ec node-v22.22.3-darwin-x64.tar.gz +939beff36e3adf3f93c5a9078d559e53245b488d5d47c5faf9fa0f1d21ede54d node-v22.22.3-darwin-x64.tar.xz +723b896d68a288e9877ea929538494afe8b5808f6fedc3358d2ad77307d1b393 node-v22.22.3-headers.tar.gz +59606231c025ace448404fcd66cf842ada73dd334319e5cae028d5d4ed293430 node-v22.22.3-headers.tar.xz +cc8bc82b2dd0b595c3b95a4c3c9c8c350907cff011afbdee3d1379e812e1e3e3 node-v22.22.3-linux-arm64.tar.gz +1c4a9933a5e45bc88f54f70b5f91232c127ec49f1a5989d23fb85824c7adf9b7 node-v22.22.3-linux-arm64.tar.xz +18fbe1bfdd42045af15f02256ae3e59490d1b9ff93dfc27a223acc2c92740250 node-v22.22.3-linux-armv7l.tar.gz +556e3a339c960d1e3cef3d7244ffaf88e1fa0df757f6fd545c59587603fdd8f1 node-v22.22.3-linux-armv7l.tar.xz +a90f523cf164e1c761bf4c59b5c6aa0d9d552892701ab69499e683ee1fc06bbc node-v22.22.3-linux-ppc64le.tar.gz +edb5478071bd1375e80195ca52f72823998bb5141b1a09e68bc54b3e2eb67754 node-v22.22.3-linux-ppc64le.tar.xz +4b84e43c29d90e4f901815c399106a49af4ee730d12933af82c82d4dc450308c node-v22.22.3-linux-s390x.tar.gz +ce398c057830d57a24c458177279a17bc51742d5c22dd4cbe97b10dbd43f2617 node-v22.22.3-linux-s390x.tar.xz +c7a10d6816da8eaaa7534dd73c71c6e2b2c391dbbf845e364902d156615dd1b8 node-v22.22.3-linux-x64.tar.gz +2e5d13569282d016861fae7c8f935e741693c269101a5bebcf761a5376d1f99f node-v22.22.3-linux-x64.tar.xz +17b494e467956af08d23c092ba10534c2991306360a97d37eac0a66ce4e7981c node-v22.22.3.pkg +3c354fe130e6a8b71701784f48f010ce9a0af40d9f20292c7a8fb8efed1e694c node-v22.22.3.tar.gz +f3e6a578db1ab335a4a72785c1e87ad18a2cf6d2fc25747a1d741fb34af0bd0f node-v22.22.3.tar.xz +09fac9d9531fa3030e3c782b3588165cb9f70559bd576f91ea3472774efb9bbf node-v22.22.3-win-arm64.7z +00be129a09e8872cd52d3bb8bba12412c5733d2224123a482a2dca4a6fbf2586 node-v22.22.3-win-arm64.zip +42d1ecf662fe82005bbcef990959f07d8dee56e3f6c5e5ddf811ef772073daec node-v22.22.3-win-x64.7z +6c8d54f635feff4df76c2ca80f45332eb2ff57d25226edce36592e51a177ee33 node-v22.22.3-win-x64.zip +1b9c5d28dbe236f158ce4be3a3963ba32b5b4b5fe328d4145dc77991dd08ac60 node-v22.22.3-win-x86.7z +7ba4260f69e16ba9626d077cb1124f3fb01f60421af2a6c7396aeb4e2d0d8dae node-v22.22.3-win-x86.zip +f453126a7d40a3e0b5ce3e53cba86a441eaf76b3345563c2b707955c4f1818e1 node-v22.22.3-x64.msi +1917d1dda99daf6127fe89a131206e8fcc31aa7b2f2a6a25249c86a7c5348283 node-v22.22.3-x86.msi +65044d409333b941086486545992141d1145198d7f0e0fc0c3bf62080fd8ee51 win-arm64/node.exe +9b75bbc3be72c84f1d41cd6abb6e5ecc333836015e40a6267ce755554874a13a win-arm64/node.lib +c08c179a184f6606d1fdf2b92b0f90c2c20ff14a8465bef2e25664676f813f8f win-arm64/node_pdb.7z +0f926fc7865cf90053055c3fa87753586447ff167bb7e7d0e9c521e578932d97 win-arm64/node_pdb.zip +780f44f2c53c108bae261ada21a525b4bfe733c020ac85e41bfe94479090ac9b win-x64/node.exe +0d8d8bcc11daea60f5dd4da414e72ccb785718345ec8fbec52cfc7d1a2326293 win-x64/node.lib +77fac850aa97333e8d779830762a03731926eaea8315a18fc1dc8719a8fd0790 win-x64/node_pdb.7z +e6489fe61083682b10319c8e01ecbdd5e3c6ad7b3f05767513a7b37334d6e787 win-x64/node_pdb.zip +2bdf1db06048fa8040dff1518d7693da4cb3d178a236467971d5b77d58e76c26 win-x86/node.exe +a07e94777fb491c1a59103b6987417df35a1dd0a9682220bba43d3c602b8b414 win-x86/node.lib +e2f25e2c0b76c3dc788cd4048edd6d19821c387a8fe8f5c067964ad68702ddda win-x86/node_pdb.7z +cf8218a77548abb14749f584a12aab6aa9c4badec5f1a69691e659fc186e1a6f win-x86/node_pdb.zip +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCAAdFiEEzGj1oxBv9EgyLkjtJ/XjjVsKIV8FAmoExfsACgkQJ/XjjVsK +IV+ppw//V46pliQ8laVN7kpc/jtRu+wWvIi06BZrwmARegnyLOZXLRJP+wV4ItkW +gMH+YbpmVAAFyBKZgaLdDjeOE83R/60JQHZ22Smd3P2FIaW8LHlOQ7JM11Nhbyx8 +vfWnYzykCITonrTCQt/SEX0dS3L8B3HfAXX83QZoMERJ9oY3SlzZlwJvjTrmShla +wryLgWt0oSTdXx6jBOS5ye8HwjwUq+zhK8C0MbRkGohF6tSXeUwBMMHxOS2dNCaO +yy98ljjd1HXvNRPJz28Em5LZYuL01pn8KzC3goOWcC8sVioN7+fcPdWv3d7unXlw +p8IyjGTUV+zQU5x+4yiQ6GK9HDVeqOA/PdDdLb6bszVHpMXC883SabqHtKodcorG +E9eMjxfN/IwjMaX3A7md48W84IsUFlmutAqsD5Rann6hBYxWrUg2RrULOP21sU3T +V1D6T12ICKh9zeiaSpxCgzZRvMZZAjnX2l/NZ7YtOWRbl/Cgli0forZGQWIO8ZsK +xiuthb4HypMd0pMOtvFmh4V5XNWgHnBEWZVSYKmTTDyWjf6nSR1pPkGS0bzbqMbB +qOlOCT0Rxe83J9evRcpM4V/SpHC6C0tPNTgbihwwtGbcz3JpG+gbac7FR2ZPPJIB +UKkhZg7qpV/IiuHgl4wPcVP+O1peeFg6KipLbKseO/3fsOyyu8U= +=CCHK +-----END PGP SIGNATURE----- +``` diff --git a/apps/site/pages/en/blog/release/v22.23.0.md b/apps/site/pages/en/blog/release/v22.23.0.md new file mode 100644 index 0000000000000..bc0634740f63f --- /dev/null +++ b/apps/site/pages/en/blog/release/v22.23.0.md @@ -0,0 +1,131 @@ +--- +date: '2026-06-18T04:38:19.322Z' +category: release +title: Node.js 22.23.0 (LTS) +layout: blog-post +author: Antoine du Hamel +--- + +## 2026-06-18, Version 22.23.0 'Jod' (LTS), @aduh95 + +This is a security release. + +### Notable Changes + +- (CVE-2026-48618) tls: normalize hostname for server identity checks (Matteo Collina) – High +- (CVE-2026-48933) crypto: guard WebCrypto cipher output length (Filip Skokan) – High +- (CVE-2026-48937) deps: fix integration issues with the latest nghttp2 – Medium +- (CVE-2026-48930) dns,net: reject hostnames with embedded NUL bytes (Matteo Collina) – Medium +- (CVE-2026-48619) http2: cap originSet size to prevent unbounded memory growth (Matteo Collina) – Medium +- (CVE-2026-48615) lib,test: redact proxy credentials in tunnel errors (Matteo Collina) – Medium +- (CVE-2026-48934) tls: bind reusable sessions to authenticated host (Matteo Collina) – Medium +- (CVE-2026-48928) tls: fix case-sensitive SNI context matching (Matteo Collina) – Medium +- (CVE-2026-48617) permission: handle process.chdir on writereport (RafaelGSS) – Low +- (CVE-2026-48931) http: fix response queue poisoning in http.Agent (Matteo Collina) – Low +- (CVE-2026-48935) permission: disable FileHandle utimes with permission model (RafaelGSS) – Low + +### Commits + +- \[[`38b4c5ed51`](https://github.com/nodejs/node/commit/38b4c5ed51)] - **(CVE-2026-48933)** **crypto**: guard WebCrypto cipher output length (Filip Skokan) [nodejs-private/node-private#878](https://github.com/nodejs-private/node-private/pull/878) +- \[[`ad8a10c1bb`](https://github.com/nodejs/node/commit/ad8a10c1bb)] - **deps**: update llhttp to 9.4.2 (Antoine du Hamel) [nodejs-private/node-private#890](https://github.com/nodejs-private/node-private/pull/890) +- \[[`ca825a87cc`](https://github.com/nodejs/node/commit/ca825a87cc)] - **deps**: update undici to 6.27.0 (aduh95) [#63711](https://github.com/nodejs/node/pull/63711) +- \[[`a1a5bb9683`](https://github.com/nodejs/node/commit/a1a5bb9683)] - **(CVE-2026-48937)** **deps**: fix integration issues with the latest nghttp2 (Tim Perry) [#62891](https://github.com/nodejs/node/pull/62891) +- \[[`0f48583512`](https://github.com/nodejs/node/commit/0f48583512)] - **(SEMVER-MAJOR)** **deps**: update nghttp2 to 1.69.0 (Node.js GitHub Bot) [#62891](https://github.com/nodejs/node/pull/62891) +- \[[`38c869fc05`](https://github.com/nodejs/node/commit/38c869fc05)] - **deps**: update nghttp2 to 1.68.0 (nodejs-github-bot) [#61136](https://github.com/nodejs/node/pull/61136) +- \[[`290667c84f`](https://github.com/nodejs/node/commit/290667c84f)] - **deps**: update nghttp2 to 1.67.1 (nodejs-github-bot) [#59790](https://github.com/nodejs/node/pull/59790) +- \[[`c9f3da76aa`](https://github.com/nodejs/node/commit/c9f3da76aa)] - **deps**: update nghttp2 to 1.66.0 (Node.js GitHub Bot) [#58786](https://github.com/nodejs/node/pull/58786) +- \[[`60890be563`](https://github.com/nodejs/node/commit/60890be563)] - **deps**: update nghttp2 to 1.65.0 (Node.js GitHub Bot) [#57269](https://github.com/nodejs/node/pull/57269) +- \[[`5024c7d5d8`](https://github.com/nodejs/node/commit/5024c7d5d8)] - **deps**: update archs files for openssl-3.5.7 (Node.js GitHub Bot) [#63820](https://github.com/nodejs/node/pull/63820) +- \[[`7f4eb5af2e`](https://github.com/nodejs/node/commit/7f4eb5af2e)] - **deps**: upgrade openssl sources to openssl-3.5.7 (Node.js GitHub Bot) [#63820](https://github.com/nodejs/node/pull/63820) +- \[[`ebb4ec78a8`](https://github.com/nodejs/node/commit/ebb4ec78a8)] - **deps**: fix aix implicit declaration in OpenSSL (Abdirahim Musse) [#62656](https://github.com/nodejs/node/pull/62656) +- \[[`5763d40826`](https://github.com/nodejs/node/commit/5763d40826)] - **deps**: update llhttp to 9.4.1 (Node.js GitHub Bot) [#63045](https://github.com/nodejs/node/pull/63045) +- \[[`c551a51d0c`](https://github.com/nodejs/node/commit/c551a51d0c)] - **(CVE-2026-48930)** **dns,net**: reject hostnames with embedded NUL bytes (Matteo Collina) [nodejs-private/node-private#868](https://github.com/nodejs-private/node-private/pull/868) +- \[[`0a22d40180`](https://github.com/nodejs/node/commit/0a22d40180)] - **(CVE-2026-48931)** **http**: fix response queue poisoning in http.Agent (Matteo Collina) [nodejs-private/node-private#846](https://github.com/nodejs-private/node-private/pull/846) +- \[[`c79968e108`](https://github.com/nodejs/node/commit/c79968e108)] - **(CVE-2026-48619)** **http2**: cap originSet size to prevent unbounded memory growth (Matteo Collina) [nodejs-private/node-private#855](https://github.com/nodejs-private/node-private/pull/855) +- \[[`0c37bff2ff`](https://github.com/nodejs/node/commit/0c37bff2ff)] - **http2**: fix DEP0194 message (KaKa) [#58669](https://github.com/nodejs/node/pull/58669) +- \[[`ea5dc6b529`](https://github.com/nodejs/node/commit/ea5dc6b529)] - **(SEMVER-MAJOR)** **http2**: remove support for priority signaling (Matteo Collina) [#58293](https://github.com/nodejs/node/pull/58293) +- \[[`9b6af26132`](https://github.com/nodejs/node/commit/9b6af26132)] - **(CVE-2026-48615)** **lib,test**: redact proxy credentials in tunnel errors (Matteo Collina) [nodejs-private/node-private#867](https://github.com/nodejs-private/node-private/pull/867) +- \[[`28dcd38864`](https://github.com/nodejs/node/commit/28dcd38864)] - **(CVE-2026-48935)** **permission**: disable FileHandle utimes with permission model (RafaelGSS) [nodejs-private/node-private#873](https://github.com/nodejs-private/node-private/pull/873) +- \[[`2f62693801`](https://github.com/nodejs/node/commit/2f62693801)] - **(CVE-2026-48617)** **permission**: handle process.chdir on writereport (RafaelGSS) [nodejs-private/node-private#870](https://github.com/nodejs-private/node-private/pull/870) +- \[[`1662a3ea09`](https://github.com/nodejs/node/commit/1662a3ea09)] - **test**: add session reuse host verification regressions (Matteo Collina) [nodejs-private/node-private#854](https://github.com/nodejs-private/node-private/pull/854) +- \[[`718d5d0e2c`](https://github.com/nodejs/node/commit/718d5d0e2c)] - **test**: skip `test-fs-utimes-y2K38` on armv7 (Richard Lau) [#63836](https://github.com/nodejs/node/pull/63836) +- \[[`041185b61f`](https://github.com/nodejs/node/commit/041185b61f)] - **test**: skip test-cluster-dgram-reuse on AIX 7.3 (Stewart X Addison) [#62238](https://github.com/nodejs/node/pull/62238) +- \[[`fd890ba01d`](https://github.com/nodejs/node/commit/fd890ba01d)] - **(CVE-2026-48934)** **tls**: bind reusable sessions to authenticated host (Matteo Collina) [nodejs-private/node-private#854](https://github.com/nodejs-private/node-private/pull/854) +- \[[`39d1d09684`](https://github.com/nodejs/node/commit/39d1d09684)] - **(CVE-2026-48928)** **tls**: fix case-sensitive SNI context matching (Matteo Collina) [nodejs-private/node-private#857](https://github.com/nodejs-private/node-private/pull/857) +- \[[`2197a47144`](https://github.com/nodejs/node/commit/2197a47144)] - **(CVE-2026-48618)** **tls**: normalize hostname for server identity checks (Matteo Collina) [nodejs-private/node-private#869](https://github.com/nodejs-private/node-private/pull/869) + +Windows 32-bit Installer: https://nodejs.org/dist/v22.23.0/node-v22.23.0-x86.msi \ +Windows 64-bit Installer: https://nodejs.org/dist/v22.23.0/node-v22.23.0-x64.msi \ +Windows ARM 64-bit Installer: https://nodejs.org/dist/v22.23.0/node-v22.23.0-arm64.msi \ +Windows 32-bit Binary: https://nodejs.org/dist/v22.23.0/win-x86/node.exe \ +Windows 64-bit Binary: https://nodejs.org/dist/v22.23.0/win-x64/node.exe \ +Windows ARM 64-bit Binary: https://nodejs.org/dist/v22.23.0/win-arm64/node.exe \ +macOS 64-bit Installer: https://nodejs.org/dist/v22.23.0/node-v22.23.0.pkg \ +macOS Apple Silicon 64-bit Binary: https://nodejs.org/dist/v22.23.0/node-v22.23.0-darwin-arm64.tar.gz \ +macOS Intel 64-bit Binary: https://nodejs.org/dist/v22.23.0/node-v22.23.0-darwin-x64.tar.gz \ +Linux 64-bit Binary: https://nodejs.org/dist/v22.23.0/node-v22.23.0-linux-x64.tar.xz \ +Linux PPC LE 64-bit Binary: https://nodejs.org/dist/v22.23.0/node-v22.23.0-linux-ppc64le.tar.xz \ +Linux s390x 64-bit Binary: https://nodejs.org/dist/v22.23.0/node-v22.23.0-linux-s390x.tar.xz \ +AIX 64-bit Binary: https://nodejs.org/dist/v22.23.0/node-v22.23.0-aix-ppc64.tar.gz \ +ARMv7 32-bit Binary: https://nodejs.org/dist/v22.23.0/node-v22.23.0-linux-armv7l.tar.xz \ +ARMv8 64-bit Binary: https://nodejs.org/dist/v22.23.0/node-v22.23.0-linux-arm64.tar.xz \ +Source Code: https://nodejs.org/dist/v22.23.0/node-v22.23.0.tar.gz \ +Other release files: https://nodejs.org/dist/v22.23.0/ \ +Documentation: https://nodejs.org/docs/v22.23.0/api/ + +### SHASUMS + +``` +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +5c595ef33e1b1dcd48128690ab599cf246b51abed470928706edaf56f1d04248 node-v22.23.0-aix-ppc64.tar.gz +d963392c08a9d0f7f1458410a43c8c0e7dd78712f01b5ffa561f00a3044a47ff node-v22.23.0-arm64.msi +e0f383a215dd3093de6d2c74f87056dc2306a2e09ad494cbffdba28f89046f56 node-v22.23.0-darwin-arm64.tar.gz +69a741a8938e3efbd7098ca1681a179f771bc9bbf733a799e9ed81949a602f73 node-v22.23.0-darwin-arm64.tar.xz +dc2ccab261fd70c347e4cc52085d8d226f471ccba1fc2a7252283949b31ca9f9 node-v22.23.0-darwin-x64.tar.gz +c339a9bc031a98bac0dba90f57370ac5ae68e4549045c8f51e740f375a0b8799 node-v22.23.0-darwin-x64.tar.xz +ac07673009b92d70f3d842df28204e92785234bae6b4d5785f63fbca8408fc69 node-v22.23.0-headers.tar.gz +b0a0a71751aa17ac9190a47c34d1439a2118493dcc323c5eae27fb691b8446d9 node-v22.23.0-headers.tar.xz +0c96aa074abd109e0b5da8d10202a9bbcea9bcf9ddb587b20944f71b8f21f8c8 node-v22.23.0-linux-arm64.tar.gz +4018815ac1bed4f18208901bbde524fee881253b591ee7bc952660e69bd057af node-v22.23.0-linux-arm64.tar.xz +2a6ac877b2381711c55178052a6f481171e0964c2f8d33d0cfe36d8e35f1c2c0 node-v22.23.0-linux-armv7l.tar.gz +6f983135f5bbf1bf017686c93bbabcb616473578f75ef43c773c1556b404a50a node-v22.23.0-linux-armv7l.tar.xz +740fa84ba1d3e7f5d3155f9f7b71e2189d2fbba119df0c0c59533dc7e415efe1 node-v22.23.0-linux-ppc64le.tar.gz +864760dde36a03bf0da8f74b511c41a31adae4f50284a20066518775269539aa node-v22.23.0-linux-ppc64le.tar.xz +274c8946cd95bd3b7b586c1bfb35de315836f9e8863e29dd264decc73e652b8a node-v22.23.0-linux-s390x.tar.gz +8c5ba195dff6c11a292ffbe199931c7b52d3f233d25fa908718b99d0e0f9d09d node-v22.23.0-linux-s390x.tar.xz +535eeb608ca1e0b71d49a0e36991d449d5f935fbb04eca61677519b010cd673a node-v22.23.0-linux-x64.tar.gz +14d7de44f235534799f8b171a4050d9a6a4bc99c87e053a25d3d54afa580aa20 node-v22.23.0-linux-x64.tar.xz +e42354513cbee40eba62db30cd8132bbc2a1896c4f6f85483bd67e318c115cc0 node-v22.23.0-win-arm64.7z +8d540a7a1eeb3ff6681f516c47d786964b874acdaa4fd83338d6898bbb4f68a4 node-v22.23.0-win-arm64.zip +b9114632eff5fd061da5ec72656b13e50ca9736af5551f0737f10b26cba2a12b node-v22.23.0-win-x64.7z +425a5bd68cc95e8eb16bcccd0a75081b48983fc6a26f67126bd4d6c7198231e8 node-v22.23.0-win-x64.zip +21ef41b8a7e9904643f813db751d75599ef0c5774845b534ad70c46aa8d0c14c node-v22.23.0-win-x86.7z +28948dbed0828d20cf64aca0a451fa38967214ccb87d02e7048db6398545b0c0 node-v22.23.0-win-x86.zip +aea5493d05c20996a817c45312d9bc8c6b062bfb6737afc2bab542c42fbb8835 node-v22.23.0-x64.msi +87154158fa4ad843e0e03a08c2f7d17b414bbd4a74b1986d7b011403edffd511 node-v22.23.0-x86.msi +62e5eb26a68aadf2458e3a3eaf84fae9ecc4870aeaabcefc636b69997552842e node-v22.23.0.pkg +61fd42cd1c3ff04a849f5ad5d08c58b111831944b5b94bc90fc623eab41418a2 node-v22.23.0.tar.gz +3acfae100c7b855a4c76520ee0f95cadcace3f4254f16b7d4887f178fc95d4a0 node-v22.23.0.tar.xz +abe829ba4e4fbabdcf3117ac013ecbb61469c2ccebebb87f84b41c7cdd8dfaf9 win-arm64/node.exe +e56e62f4a7ae6643a40db01f09072e8a93ccbe73be8abff927b793344691d6b7 win-arm64/node.lib +35a4655f6475f6387e13938439f925cb8ab33cf74721f88b1d77f5c855285c95 win-arm64/node_pdb.7z +d81c12c2a42291a4f8317313f8e8d19ba9cd9a9fd722d0aba3e13e64d1fcdedc win-arm64/node_pdb.zip +17347995af08dadcc73a1a154f0942559fbc3f37b9ba57d4576b4d2bcb2834a2 win-x64/node.exe +ac15f1e9d7c8279353723a77f6319967f1a41c06026521094a8234c2e6fbe052 win-x64/node.lib +901e9eef6f41c2b2a4a17670edc7854f7434bbb3120815bb8c146e244c2527e4 win-x64/node_pdb.7z +8b4d97c4454f37dc2a4dbe5821f428fc45ef066fb076ce6f395cc45c76aab692 win-x64/node_pdb.zip +8b4369dd36679fcaf7146d6627d1ca1c1ea04bf933f91f9c61630d158df82bc0 win-x86/node.exe +66949ba371a159f5e3626f6ce960f85ab6bcdd237eafcdfbc11d1377a2767652 win-x86/node.lib +a52afc61556fee95d22f0a401a761714d585f5596b7cf92d175a3f238b08bdba win-x86/node_pdb.7z +8df46485e8d327e66eea65538845bdf31350ed76d921c6512ec6fa044a814a7e win-x86/node_pdb.zip + +-----BEGIN PGP SIGNATURE----- + +iHUEARYIAB0WIQRb6KP2yKXAHRBsCtggsaOQsWjTVgUCajN0uQAKCRAgsaOQsWjT +VrCOAQCGN/ZUCGhmojChsyAmHwY6dkE0C6v+pkT+R2Z0gi6M4AD9HGnS7wyeo2qY ++2RqcJ1iGGuYD7/DPIY1emFpLja9hws= +=nIm4 +-----END PGP SIGNATURE----- +``` diff --git a/apps/site/pages/en/blog/release/v22.23.1.md b/apps/site/pages/en/blog/release/v22.23.1.md new file mode 100644 index 0000000000000..e336ee7360402 --- /dev/null +++ b/apps/site/pages/en/blog/release/v22.23.1.md @@ -0,0 +1,99 @@ +--- +date: '2026-06-23T16:45:21.150Z' +category: release +title: Node.js 22.23.1 (LTS) +layout: blog-post +author: Rafael Gonzaga +--- + +## 2026-06-23, Version 22.23.1 'Jod' (LTS), @RafaelGSS + +This release includes a fix for an unexpected behavior introduced +by the recent security release (22.23.0). + +### Commits + +- \[[`41d2ee13be`](https://github.com/nodejs/node/commit/41d2ee13be)] - **build**: switch coverage-windows to `windows-2022` (Richard Lau) [#63940](https://github.com/nodejs/node/pull/63940) +- \[[`eaa292549e`](https://github.com/nodejs/node/commit/eaa292549e)] - **http**: avoid stream listeners on idle agent sockets (Matteo Collina) [#64004](https://github.com/nodejs/node/pull/64004) + +Windows 32-bit Installer: https://nodejs.org/dist/v22.23.1/node-v22.23.1-x86.msi \ +Windows 64-bit Installer: https://nodejs.org/dist/v22.23.1/node-v22.23.1-x64.msi \ +Windows ARM 64-bit Installer: https://nodejs.org/dist/v22.23.1/node-v22.23.1-arm64.msi \ +Windows 32-bit Binary: https://nodejs.org/dist/v22.23.1/win-x86/node.exe \ +Windows 64-bit Binary: https://nodejs.org/dist/v22.23.1/win-x64/node.exe \ +Windows ARM 64-bit Binary: https://nodejs.org/dist/v22.23.1/win-arm64/node.exe \ +macOS 64-bit Installer: https://nodejs.org/dist/v22.23.1/node-v22.23.1.pkg \ +macOS Apple Silicon 64-bit Binary: https://nodejs.org/dist/v22.23.1/node-v22.23.1-darwin-arm64.tar.gz \ +macOS Intel 64-bit Binary: https://nodejs.org/dist/v22.23.1/node-v22.23.1-darwin-x64.tar.gz \ +Linux 64-bit Binary: https://nodejs.org/dist/v22.23.1/node-v22.23.1-linux-x64.tar.xz \ +Linux PPC LE 64-bit Binary: https://nodejs.org/dist/v22.23.1/node-v22.23.1-linux-ppc64le.tar.xz \ +Linux s390x 64-bit Binary: https://nodejs.org/dist/v22.23.1/node-v22.23.1-linux-s390x.tar.xz \ +AIX 64-bit Binary: https://nodejs.org/dist/v22.23.1/node-v22.23.1-aix-ppc64.tar.gz \ +ARMv7 32-bit Binary: https://nodejs.org/dist/v22.23.1/node-v22.23.1-linux-armv7l.tar.xz \ +ARMv8 64-bit Binary: https://nodejs.org/dist/v22.23.1/node-v22.23.1-linux-arm64.tar.xz \ +Source Code: https://nodejs.org/dist/v22.23.1/node-v22.23.1.tar.gz \ +Other release files: https://nodejs.org/dist/v22.23.1/ \ +Documentation: https://nodejs.org/docs/v22.23.1/api/ + +### SHASUMS + +``` +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +afab071f771073235dc1191e2ed872505d7a08d23cdff5103c678f9ec2ba44f9 node-v22.23.1-aix-ppc64.tar.gz +c32a95033580ebd54d1e10ade9bab80945df1309e53fdb1f424e657ce7baf8ef node-v22.23.1-arm64.msi +ef28d8fab2c0e4314522d4bb1b7173270aa3937e93b92cb7de79c112ac1fa953 node-v22.23.1-darwin-arm64.tar.gz +fb526811860f81dcac7dd8b2b55eca4accfc5d61c3b7c2508f2639faee8a738d node-v22.23.1-darwin-arm64.tar.xz +b8da981b8a0b1241b70249204916da76c63573ddf5814dbd2d1e41069105cb81 node-v22.23.1-darwin-x64.tar.gz +efeec6641a2f15f5396d27cd0b32f5062d6689d1e9e5d89607d0b29bda890233 node-v22.23.1-darwin-x64.tar.xz +caf38d839815df92000b41b756056f575140f38f1d8c0cbef97448c33eb7d7b2 node-v22.23.1-headers.tar.gz +b4d28a1c11bf6f643597e09101980904390df9480d835e1f07cc0a20d41768dc node-v22.23.1-headers.tar.xz +543fa39e57d4c07855939459a323f4deb9a79dd1bb45e6e99458b0f2de10db8d node-v22.23.1-linux-arm64.tar.gz +0294e8b915ab75f92c7513d2fcb830ae06e10684e6c603e99a87dbf8835389c1 node-v22.23.1-linux-arm64.tar.xz +03c56ac0bd3ef3cce967c2f7b2f7ac2259a4ae7ceeaa661291aadf65729a8b53 node-v22.23.1-linux-armv7l.tar.gz +c24dcdd3df032a34d1c858ddac9e514d3cac6fa655cc92cd4afe4e589c866a10 node-v22.23.1-linux-armv7l.tar.xz +75ba0ad4949cc90fe0cf5f1e0c28bb7e5d17c1edaecd35dbf01f5830f9b3b552 node-v22.23.1-linux-ppc64le.tar.gz +a645bcb7bbc498d09ba9249b5c6414aa6d0a461d8854afc61e32096646ba5e66 node-v22.23.1-linux-ppc64le.tar.xz +94c7c55553ad5520172f5bfbd143389eebd1e6a200a5e2d8d309e9ed73f370e5 node-v22.23.1-linux-s390x.tar.gz +f16b5636ba925b462e627c5e0ef47de3255bf4a7b38d56db58e2cf165df19c3c node-v22.23.1-linux-s390x.tar.xz +7a8cb04b4a1df4eaf432125324b81b29a088e73570a23259a8de1c65d07fc129 node-v22.23.1-linux-x64.tar.gz +9749e988f437343b7fa832c69ded82a312e41a03116d766797ac14f6f9eee578 node-v22.23.1-linux-x64.tar.xz +6fa537484f545991961061112624ab9c40b2d5b78e7848c096adde40e0a248c4 node-v22.23.1-win-arm64.7z +b470fdfe3502c05151656e06d495e3f47544f2ee8b1d9c8705090f2dd5996bd0 node-v22.23.1-win-arm64.zip +036de547d4bf8b916f25a41064baed7cd61f12ce7d8cfe283d12f91688b51cb9 node-v22.23.1-win-x64.7z +7df0bc9375723f4a86b3aa1b7cc73342423d9677a8df4538aca31a049e309c29 node-v22.23.1-win-x64.zip +6b757e4bc3d293fe926a1be2727ab02d7411b95ece6c94a60d1d5de2279e846f node-v22.23.1-win-x86.7z +e298b368aad86c571447a3650db3ce19063373ffd39d6d73d014a5d9ad31dc62 node-v22.23.1-win-x86.zip +4e41d4fea6661eb330fa88b1cdce2ba5e6b07d93f689c9d549cb9dd09cb9b2b0 node-v22.23.1-x64.msi +de75961b3162771f8f071608226fabc5029174c279a49c2b88621a8091acffa3 node-v22.23.1-x86.msi +e22de03c865a06ac8f2714eab5110dbe71c70d635042c3e305e62dbba5792bd5 node-v22.23.1.pkg +f40ff2e6b99196271caffae2b240e9148d66675994afd00e522dad7ecce6f27b node-v22.23.1.tar.gz +b27385d6845089bdb91285d94b06c2a5cf1c37f8173a3c4e10824cc1ffadeaba node-v22.23.1.tar.xz +f55db97c9924b0b37b05e8cf1be4e04c72aec01dc1c22420b5c31ab9cd118b89 win-arm64/node.exe +e56e62f4a7ae6643a40db01f09072e8a93ccbe73be8abff927b793344691d6b7 win-arm64/node.lib +9890235b52f0503986ee1690bf33217edbe888c1cb90f77e54169a0077161435 win-arm64/node_pdb.7z +7a4e1f4cc45ed60a304c42d91bce5073743a13b62e5db1fb58370b73caf7f101 win-arm64/node_pdb.zip +f8d162c0641dcee512132f3bcf8a68169c7ecb852efd8e1a46c9fec5a0f469ed win-x64/node.exe +ac15f1e9d7c8279353723a77f6319967f1a41c06026521094a8234c2e6fbe052 win-x64/node.lib +99ce2684f147d28d89b07bc4ed6d0c9112f3ed60ad8253267b0838cce3c4322e win-x64/node_pdb.7z +69e9012ec5474edb2d8643b944d067b4d2749d61c2e207beff95bc8edaa05d7d win-x64/node_pdb.zip +b1c3e891f327f59594345068505b052da6c93dbb19e5e5af631202eeb0c4015b win-x86/node.exe +66949ba371a159f5e3626f6ce960f85ab6bcdd237eafcdfbc11d1377a2767652 win-x86/node.lib +174398659805e6e5e42855498386e1c2777b00351cb253b6b2938447ffcca299 win-x86/node_pdb.7z +cbdc77eda7ac39ada59ca3505c50a43165904b23118fde06f79ab572f02a658e win-x86/node_pdb.zip +-----BEGIN PGP SIGNATURE----- + +iQGzBAEBCAAdFiEEiQwI24V5Fi/uDfnbi+q0389VXvQFAmo6tz0ACgkQi+q0389V +XvTeOQv+McTx/EZcpWUk6baUUIC40CdbSAT3rckk7ydSzQ6ZNwES6RhMYSSvYDXi +mUgcWQPP5Mk2Al+ua07V6Eidezov7CydoFQxS08xqy/ln1a6LsrRjCoUs6j36G9n +KdQ++FlXmFXmNPRqbb+fZ/Pozm4pfKzcZTC/mZ4cr1WWBdlZe2ae0dKFb3hyR7wg +574Gzqj2iispclqtTJ3cbcKiCWBdov9MnpaCyJzKq1WY5SSqKzgGvlvVpSlNgPTv +jUWfDwUHGOh5+Osk+h/lkwagJE24II/AKnnTKYNq/QwMi9c0cF3ogw76b5GY5MYU +TjlTEt2eQuw6pdnI/fdpoeDBnYC/v73vSoI6SsrJv6Rv2XOny1GxkA8XZSCHKa6c +hHcHd0KC7zCmMxxBzTz79swM4td0zG+Wmst2Su0M/G5IKoO5YHkat0IkTCWYS7Dx +Qxbz36CZjGGt812ZaAS2MlljoRSg+l6o5OIa5YvPxUiyEmbR9xb+AlVJDGUpnI+V +NuJUUHVD +=Ss2w +-----END PGP SIGNATURE----- +``` diff --git a/apps/site/pages/en/blog/release/v22.23.2.md b/apps/site/pages/en/blog/release/v22.23.2.md new file mode 100644 index 0000000000000..4a6dfaf7f5140 --- /dev/null +++ b/apps/site/pages/en/blog/release/v22.23.2.md @@ -0,0 +1,126 @@ +--- +date: '2026-07-29T14:01:35.511Z' +category: release +title: Node.js 22.23.2 (LTS) +layout: blog-post +author: Marco Ippolito +--- + +## 2026-07-29, Version 22.23.2 'Jod' (LTS), @marco-ippolito + +This is a security release. + +### Notable Changes + +- (CVE-2026-56846) http2: retain header memory in session accounting (Matteo Collina) – High +- (CVE-2026-56848) http2: defer rst stream while in scope (Matteo Collina) – High +- (CVE-2026-58043) permission: avoid granting radix split nodes (RafaelGSS) – High +- (CVE-2026-56850) https: distinguish PFX object-array agent keys (RafaelGSS) – Medium +- (CVE-2026-58040) https: bind identity checks to session reuse (Matteo Collina) – Medium +- (CVE-2026-58042) dns: handle large resolveAny address replies (RafaelGSS) – Medium +- (CVE-2026-58045) zlib: throw on out-of-bounds write buffers (RafaelGSS) – Medium +- (CVE-2026-56847) permission: enforce fs write permission for trace events (RafaelGSS) – Low +- (CVE-2026-58039) permission: check final report output path (RafaelGSS) – Low +- (CVE-2026-58044) http: reject requests exceeding max header count (Matteo Collina) – Low +- deps: update llhttp to 9.4.3 (Paolo Insogna) +- deps: update undici to 6.28.0 (Node.js GitHub Bot) + +### Commits + +- \[[`4b12ac38a1`](https://github.com/nodejs/node/commit/4b12ac38a1)] - **deps**: update llhttp to 9.4.3 (Paolo Insogna) [nodejs-private/node-private#935](https://github.com/nodejs-private/node-private/pull/935) +- \[[`3fd0aa51d0`](https://github.com/nodejs/node/commit/3fd0aa51d0)] - **deps**: update undici to 6.28.0 (Node.js GitHub Bot) [#64714](https://github.com/nodejs/node/pull/64714) +- \[[`22efc051a3`](https://github.com/nodejs/node/commit/22efc051a3)] - **(CVE-2026-58042)** **dns**: handle large resolveAny address replies (RafaelGSS) [nodejs-private/node-private#929](https://github.com/nodejs-private/node-private/pull/929) +- \[[`c8525ac3a6`](https://github.com/nodejs/node/commit/c8525ac3a6)] - **(CVE-2026-58044)** **http**: reject requests exceeding max header count (Matteo Collina) [nodejs-private/node-private#932](https://github.com/nodejs-private/node-private/pull/932) +- \[[`daa6d25e3d`](https://github.com/nodejs/node/commit/daa6d25e3d)] - **(CVE-2026-56848)** **http2**: defer rst stream while in scope (Matteo Collina) [nodejs-private/node-private#921](https://github.com/nodejs-private/node-private/pull/921) +- \[[`f14d78b9e0`](https://github.com/nodejs/node/commit/f14d78b9e0)] - **(CVE-2026-56846)** **http2**: retain header memory in session accounting (Matteo Collina) [#63752](https://github.com/nodejs/node/pull/63752) +- \[[`51123159fe`](https://github.com/nodejs/node/commit/51123159fe)] - **(CVE-2026-58040)** **https**: bind identity checks to session reuse (Matteo Collina) [nodejs-private/node-private#934](https://github.com/nodejs-private/node-private/pull/934) +- \[[`acaf4266b2`](https://github.com/nodejs/node/commit/acaf4266b2)] - **(CVE-2026-56850)** **https**: distinguish PFX object-array agent keys (RafaelGSS) [nodejs-private/node-private#930](https://github.com/nodejs-private/node-private/pull/930) +- \[[`440329f624`](https://github.com/nodejs/node/commit/440329f624)] - **(CVE-2026-58043)** **permission**: avoid granting radix split nodes (RafaelGSS) [nodejs-private/node-private#911](https://github.com/nodejs-private/node-private/pull/911) +- \[[`ed18b9cc07`](https://github.com/nodejs/node/commit/ed18b9cc07)] - **(CVE-2026-58039)** **permission**: check final report output path (RafaelGSS) [nodejs-private/node-private#926](https://github.com/nodejs-private/node-private/pull/926) +- \[[`0566c3cccd`](https://github.com/nodejs/node/commit/0566c3cccd)] - **(CVE-2026-56847)** **permission**: enforce fs write permission for trace events (RafaelGSS) [nodejs-private/node-private#927](https://github.com/nodejs-private/node-private/pull/927) +- \[[`0d072480c3`](https://github.com/nodejs/node/commit/0d072480c3)] - **(CVE-2026-58045)** **zlib**: throw on out-of-bounds write buffers (RafaelGSS) [nodejs-private/node-private#931](https://github.com/nodejs-private/node-private/pull/931) + +Windows 32-bit Installer: https://nodejs.org/dist/v22.23.2/node-v22.23.2-x86.msi \ +Windows 64-bit Installer: https://nodejs.org/dist/v22.23.2/node-v22.23.2-x64.msi \ +Windows ARM 64-bit Installer: https://nodejs.org/dist/v22.23.2/node-v22.23.2-arm64.msi \ +Windows 32-bit Binary: https://nodejs.org/dist/v22.23.2/win-x86/node.exe \ +Windows 64-bit Binary: https://nodejs.org/dist/v22.23.2/win-x64/node.exe \ +Windows ARM 64-bit Binary: https://nodejs.org/dist/v22.23.2/win-arm64/node.exe \ +macOS 64-bit Installer: https://nodejs.org/dist/v22.23.2/node-v22.23.2.pkg \ +macOS Apple Silicon 64-bit Binary: https://nodejs.org/dist/v22.23.2/node-v22.23.2-darwin-arm64.tar.gz \ +macOS Intel 64-bit Binary: https://nodejs.org/dist/v22.23.2/node-v22.23.2-darwin-x64.tar.gz \ +Linux 64-bit Binary: https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-x64.tar.xz \ +Linux PPC LE 64-bit Binary: https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-ppc64le.tar.xz \ +Linux s390x 64-bit Binary: https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-s390x.tar.xz \ +AIX 64-bit Binary: https://nodejs.org/dist/v22.23.2/node-v22.23.2-aix-ppc64.tar.gz \ +ARMv7 32-bit Binary: https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-armv7l.tar.xz \ +ARMv8 64-bit Binary: https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-arm64.tar.xz \ +Source Code: https://nodejs.org/dist/v22.23.2/node-v22.23.2.tar.gz \ +Other release files: https://nodejs.org/dist/v22.23.2/ \ +Documentation: https://nodejs.org/docs/v22.23.2/api/ + +### SHASUMS + +``` +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +77c63032fd4ab8e98e0ae7db9d79e396659e04af95a5edf8e1ca869e426c04a5 node-v22.23.2-aix-ppc64.tar.gz +963c0f779d8f893f17f6b2a0060932c366ffa59f7c79527748a9c558c152dd38 node-v22.23.2-arm64.msi +61130f394c1630d211dd50aecc4353d379480f36d3ac913cd85dbba1aed585c6 node-v22.23.2-darwin-arm64.tar.gz +5eff7a9011895aae3f29d06f167b84a62b028a591370c7cafb59103559fd26e1 node-v22.23.2-darwin-arm64.tar.xz +58e99022c2ff89395576cc7fd4d98cea24bb68081475d5f88b801ee8729fb026 node-v22.23.2-darwin-x64.tar.gz +96dff79f4e19a78715da559ec7cac2028f4985a175ea0c3454625a269c21deb7 node-v22.23.2-darwin-x64.tar.xz +daaf13ec5d45a38bbcfcff06d0723f72a6813b89ab27bb5af2ac8e1f6259dde0 node-v22.23.2-headers.tar.gz +b141dcc466e2bc15bbc57602898915cf7b73bff974e72a357b1e2e3a4cfbd2e5 node-v22.23.2-headers.tar.xz +013b59cfd2819703a6f4a14ab891fc46fc2a4e3f5bcd92de3fb4929b43e35b30 node-v22.23.2-linux-arm64.tar.gz +fff4078c5def658577f92c88db7db3bc0072924bfb93fe52c1e744a54e94abb8 node-v22.23.2-linux-arm64.tar.xz +2a2f59eb8fd9dec27b3bee17c729131d1fd3e6d9943d479f1156ce38af8cd599 node-v22.23.2-linux-armv7l.tar.gz +ef8f26a3de19acd8c23548e6c3cfc2052610b0e67abb5fd64dbd92c8b1c1245b node-v22.23.2-linux-armv7l.tar.xz +65c8aa9e647196f30d3564bb6b201306b1b520f72f0dd3278da326014a018379 node-v22.23.2-linux-ppc64le.tar.gz +e2ba10110ad34096c051fe47134fcd12175ecc48e7e879d7d4c78b70cf66442c node-v22.23.2-linux-ppc64le.tar.xz +986b8441b1d1a01c70ed710bab201741fd0d196d302a7e4338fefd5e1c10e61e node-v22.23.2-linux-s390x.tar.gz +50aa0935c7caee2f95434f84c2c19f16e4f223257eadb341a3a2d5aaa545bbe6 node-v22.23.2-linux-s390x.tar.xz +b294a556e639d64338823920e5866c21c02741742d2e1529ee1a225c1ec9252a node-v22.23.2-linux-x64.tar.gz +d60acfe00a2932254bb0ad20e01b0d74397a0875595de719654b214f4b03f307 node-v22.23.2-linux-x64.tar.xz +0422d63395d28cad31d92a202b8a5ff76d3bf09f234786926622bde7fe2d7a89 node-v22.23.2.pkg +cdaed46fcd8923a55974b7bbc7caaadeaaf7aed60cc137e59837b72f575ef641 node-v22.23.2.tar.gz +bbe768df8d5815d7fa76124052985332452e0a4742d39f32027550d1aab8f6fb node-v22.23.2.tar.xz +f8bdc72090f0e13223c6b974cb0a2919be9650f27f45223df4bb78231df3fb7c node-v22.23.2-win-arm64.7z +fec025a6da31757e3b6af84c5a1628e9d38442ca99a2161091d78f2fcfa35ef3 node-v22.23.2-win-arm64.zip +4f72483c39634934c77b1ece25911d84d2a929d6171751ea701d13f348368838 node-v22.23.2-win-x64.7z +1177b4137ba5adaa56354ae40f1080c7450e8ae09cecb47da459d1c52ac99f97 node-v22.23.2-win-x64.zip +211b8539e37b4f0a31d11016bdb882f036b1f3bcd2da879f365e0d712d7973a1 node-v22.23.2-win-x86.7z +725c9e2bdd1c2016b41c995a81f4fa36ce4e2ee565b7455d8f889182727df647 node-v22.23.2-win-x86.zip +ce9572ae220c345fbae2340bbb4d084e8ca5e0fe093ee7067d43094ae23be989 node-v22.23.2-x64.msi +bacc6689c6b6953e120fedaf0bba9f16349bb2755f6ee9fecf33317daab9d029 node-v22.23.2-x86.msi +97cce5301a815d2dce07ac5bfd1e6039eae88185ec1d10ae4f8cb712f1732878 win-arm64/node.exe +e56e62f4a7ae6643a40db01f09072e8a93ccbe73be8abff927b793344691d6b7 win-arm64/node.lib +42263b4322f459ad5c665a1569e77a53125853f43069ded98432bbdb29378fab win-arm64/node_pdb.7z +9b1d825eebb86b58829d72ce3b0c28a84d80bf7e2204c835e1ec99a8729559df win-arm64/node_pdb.zip +0d0f5e39f9f3d9587bc19f73eab3c2c9c4903fd02d6dbf9c853dd81b3d95fad4 win-x64/node.exe +ac15f1e9d7c8279353723a77f6319967f1a41c06026521094a8234c2e6fbe052 win-x64/node.lib +bf6be5c7dae68227e6fd6a105aa5b2eb8b3511a6f1ab4488f27ce36b85d66f30 win-x64/node_pdb.7z +f5836a84ab8aa5ecf5e5e415cf8fa92c2b8fc11c3128e9225877c2628dd00318 win-x64/node_pdb.zip +ae3f4245f4b87a79b1a9f66fbede12f526adc3dea9c4bdb50d66ed8c88eb395a win-x86/node.exe +66949ba371a159f5e3626f6ce960f85ab6bcdd237eafcdfbc11d1377a2767652 win-x86/node.lib +754c6fa0c493d19baa93cccf25668637b48b46edb810f604034c87cf7a1c3ed7 win-x86/node_pdb.7z +65d6b0a8590eb1401bb6756d63b0ab12db3c31bc0ce71a892e3079c8fdeca305 win-x86/node_pdb.zip +-----BEGIN PGP SIGNATURE----- + +iQJPBAEBCAA5FiEEzGj1oxBv9EgyLkjtJ/XjjVsKIV8FAmpqBnEbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJECf1441bCiFfLkYQAI5Lu6hHMJGYjWieVVc3 +izv4gfMxrwr4doLo4DTVywRE7ZdKd8ylgmWQ4lfHK4XAmRDnZfm5dMtK6xDRkqBi +AVAKIvBznIOvNMoCQvj8dKUqLTOpmTBS0chARWwq6WqQSUnTAfvejDHD7QEo/jYm +04mfdOFupcU/RnnBTlNLNMgye1F8owj2MjccvjRkA/ML7LSY7esYgLPfeulVY+k8 +VcJ1xcaIzr/tgbURyIxzfkmoY+urebvPAAsVGZoDBjMelUmX5xs1PXO+jy4uNMg4 +JT/qI3Oadn4OYkhNczWtaB4j5HhFxtkY8CFek8k1c+1XpWw1KvfbHMhd/O81Tq4W +hHFIeKyEYKG2DApAYDcfouPprknnC8gAp7xnMZvTHsdqWCxbgCXw24Der3t74KpS +FyIB1DhpL6x9cQ7gSWPo4SlThalUBJhqUDZIlnarafPzs2mywP4FeDlSwv/f44u0 +xpeP9BBjB3PoHGQ+MMDqpTNEBGZoo8pf3fmPwCv3W41LOiHy6Tce0NgLImCJXpaj +AxO0LL+BFEDr4qasfOMQkU+r2uSlPLdeSx7MLVziy4yLUF5WDKc5fdEJlxyhMA+5 +8AQY65xR+Jfrl9s3M7I2PYtN7+yzwvJrU+AhCDujSoxzWFII8aITKyrsrgJ+4Z4B +dTZJ6z2spVlbPqiFdhAGjbk8 +=TLBg +-----END PGP SIGNATURE----- +``` diff --git a/apps/site/pages/en/blog/release/v24.16.0.md b/apps/site/pages/en/blog/release/v24.16.0.md new file mode 100644 index 0000000000000..c346adc49af2f --- /dev/null +++ b/apps/site/pages/en/blog/release/v24.16.0.md @@ -0,0 +1,319 @@ +--- +date: '2026-05-21T13:25:43.186Z' +category: release +title: Node.js 24.16.0 (LTS) +layout: blog-post +author: Antoine du Hamel +--- + +## 2026-05-21, Version 24.16.0 'Krypton' (LTS), @aduh95 + +### Notable Changes + +- \[[`b267f6bca3`](https://github.com/nodejs/node/commit/b267f6bca3)] - **(SEMVER-MINOR)** **crypto**: implement `randomUUIDv7()` (nabeel378) [#62553](https://github.com/nodejs/node/pull/62553) +- \[[`ec2451b9cd`](https://github.com/nodejs/node/commit/ec2451b9cd)] - **(SEMVER-MINOR)** **debugger**: add edit-free runtime expression probes to `node inspect` (Joyee Cheung) [#62713](https://github.com/nodejs/node/pull/62713) +- \[[`9705f628d9`](https://github.com/nodejs/node/commit/9705f628d9)] - **(SEMVER-MINOR)** **fs**: add signal option to `fs.stat()` (Mert Can Altin) [#57775](https://github.com/nodejs/node/pull/57775) +- \[[`40ccfdecf9`](https://github.com/nodejs/node/commit/40ccfdecf9)] - **(SEMVER-MINOR)** **fs**: expose `frsize` field in `statfs` (Jinho Jang) [#62277](https://github.com/nodejs/node/pull/62277) +- \[[`d7188af5c9`](https://github.com/nodejs/node/commit/d7188af5c9)] - **(SEMVER-MINOR)** **http**: harden `ClientRequest` options merge (Matteo Collina) [#63082](https://github.com/nodejs/node/pull/63082) +- \[[`aa1d8a9afc`](https://github.com/nodejs/node/commit/aa1d8a9afc)] - **(SEMVER-MINOR)** **http**: add `req.signal` to `IncomingMessage` (Akshat) [#62541](https://github.com/nodejs/node/pull/62541) +- \[[`6f37f7e240`](https://github.com/nodejs/node/commit/6f37f7e240)] - **(SEMVER-MINOR)** **stream**: propagate destruction in `duplexPair` (Ahmed Elhor) [#61098](https://github.com/nodejs/node/pull/61098) +- \[[`d14029be7f`](https://github.com/nodejs/node/commit/d14029be7f)] - **(SEMVER-MINOR)** **test_runner**: support test order randomization (Pietro Marchini) [#61747](https://github.com/nodejs/node/pull/61747) +- \[[`d142c584cd`](https://github.com/nodejs/node/commit/d142c584cd)] - **(SEMVER-MINOR)** **test_runner**: align mock timeout api (sangwook) [#62820](https://github.com/nodejs/node/pull/62820) +- \[[`01a9552585`](https://github.com/nodejs/node/commit/01a9552585)] - **(SEMVER-MINOR)** **test_runner**: add mock-timers support for `AbortSignal.timeout` (DeveloperViraj) [#60751](https://github.com/nodejs/node/pull/60751) +- \[[`00705a459a`](https://github.com/nodejs/node/commit/00705a459a)] - **(SEMVER-MINOR)** **util**: colorize text with hex colors (Guilherme Araújo) [#61556](https://github.com/nodejs/node/pull/61556) + +### Commits + +- \[[`dd72df060d`](https://github.com/nodejs/node/commit/dd72df060d)] - **assert,util**: fix stale nested cycle memo entries (Ruben Bridgewater) [#62509](https://github.com/nodejs/node/pull/62509) +- \[[`add94f4bc3`](https://github.com/nodejs/node/commit/add94f4bc3)] - **build**: track PDL files as inputs in inspector GN build (Robo) [#62888](https://github.com/nodejs/node/pull/62888) +- \[[`1b1eb9e334`](https://github.com/nodejs/node/commit/1b1eb9e334)] - **build**: remove redundant -fuse-linker-plugin from GCC LTO flags (Daniel Lando) [#62667](https://github.com/nodejs/node/pull/62667) +- \[[`8752b604ec`](https://github.com/nodejs/node/commit/8752b604ec)] - **crypto**: deduplicate and canonicalize CryptoKey usages (Filip Skokan) [#62902](https://github.com/nodejs/node/pull/62902) +- \[[`341947e7fd`](https://github.com/nodejs/node/commit/341947e7fd)] - **crypto**: reject unintended raw key format string input (Filip Skokan) [#62974](https://github.com/nodejs/node/pull/62974) +- \[[`28a78747fc`](https://github.com/nodejs/node/commit/28a78747fc)] - **crypto**: remove Argon2 KDF derivation from its job setup (Filip Skokan) [#62863](https://github.com/nodejs/node/pull/62863) +- \[[`16e8c2b54d`](https://github.com/nodejs/node/commit/16e8c2b54d)] - **crypto**: fix unsigned conversion of 4-byte RSA publicExponent (DeepView Autofix) [#62839](https://github.com/nodejs/node/pull/62839) +- \[[`eeae754a87`](https://github.com/nodejs/node/commit/eeae754a87)] - **crypto**: reject inherited key type names (Jonathan Lopes) [#62875](https://github.com/nodejs/node/pull/62875) +- \[[`9dd5540325`](https://github.com/nodejs/node/commit/9dd5540325)] - **crypto**: add memory tracking for secureContext openssl objects (Mert Can Altin) [#59051](https://github.com/nodejs/node/pull/59051) +- \[[`b267f6bca3`](https://github.com/nodejs/node/commit/b267f6bca3)] - **(SEMVER-MINOR)** **crypto**: implement randomUUIDv7() (nabeel378) [#62553](https://github.com/nodejs/node/pull/62553) +- \[[`7597d204c1`](https://github.com/nodejs/node/commit/7597d204c1)] - **crypto**: add support for Ed25519 context parameter (Filip Skokan) [#62474](https://github.com/nodejs/node/pull/62474) +- \[[`4bf85845da`](https://github.com/nodejs/node/commit/4bf85845da)] - **debugger**: move ProbeInspectorSession and helpers to separate files (Joyee Cheung) [#63013](https://github.com/nodejs/node/pull/63013) +- \[[`ec2451b9cd`](https://github.com/nodejs/node/commit/ec2451b9cd)] - **(SEMVER-MINOR)** **debugger**: add edit-free runtime expression probes to `node inspect` (Joyee Cheung) [#62713](https://github.com/nodejs/node/pull/62713) +- \[[`83e98f77b7`](https://github.com/nodejs/node/commit/83e98f77b7)] - **deps**: update corepack to 0.35.0 (Node.js GitHub Bot) [#63375](https://github.com/nodejs/node/pull/63375) +- \[[`ec8c6b939a`](https://github.com/nodejs/node/commit/ec8c6b939a)] - **deps**: V8: cherry-pick 657d8de27427 (Guy Bedford) [#62784](https://github.com/nodejs/node/pull/62784) +- \[[`722c0c3274`](https://github.com/nodejs/node/commit/722c0c3274)] - **deps**: update nghttp3 to 1.14.0 (Node.js GitHub Bot) [#61187](https://github.com/nodejs/node/pull/61187) +- \[[`5304db93d3`](https://github.com/nodejs/node/commit/5304db93d3)] - **deps**: update nghttp3 to 1.13.1 (Node.js GitHub Bot) [#60046](https://github.com/nodejs/node/pull/60046) +- \[[`e073b3811d`](https://github.com/nodejs/node/commit/e073b3811d)] - **deps**: update nghttp3 to 1.11.0 (James M Snell) [#59249](https://github.com/nodejs/node/pull/59249) +- \[[`1d00313fb2`](https://github.com/nodejs/node/commit/1d00313fb2)] - **deps**: update ngtcp2 to 1.14.0 (James M Snell) [#59249](https://github.com/nodejs/node/pull/59249) +- \[[`8b3a4fc18f`](https://github.com/nodejs/node/commit/8b3a4fc18f)] - **deps**: update amaro to 1.1.9 (Node.js GitHub Bot) [#63090](https://github.com/nodejs/node/pull/63090) +- \[[`62fe0cfcd1`](https://github.com/nodejs/node/commit/62fe0cfcd1)] - **deps**: update llhttp to 9.4.1 (Node.js GitHub Bot) [#63045](https://github.com/nodejs/node/pull/63045) +- \[[`137e09c8e9`](https://github.com/nodejs/node/commit/137e09c8e9)] - **deps**: update corepack to 0.34.7 (Node.js GitHub Bot) [#62810](https://github.com/nodejs/node/pull/62810) +- \[[`14a4cb8fbc`](https://github.com/nodejs/node/commit/14a4cb8fbc)] - **deps**: update timezone to 2026b (Node.js GitHub Bot) [#62962](https://github.com/nodejs/node/pull/62962) +- \[[`3e1036583a`](https://github.com/nodejs/node/commit/3e1036583a)] - **deps**: upgrade npm to 11.13.0 (npm team) [#62898](https://github.com/nodejs/node/pull/62898) +- \[[`01dfe5961c`](https://github.com/nodejs/node/commit/01dfe5961c)] - **deps**: cherry-pick libuv/libuv\@439a54b (skooch) [#62881](https://github.com/nodejs/node/pull/62881) +- \[[`6cd368b10c`](https://github.com/nodejs/node/commit/6cd368b10c)] - **deps**: update sqlite to 3.53.0 (Node.js GitHub Bot) [#62699](https://github.com/nodejs/node/pull/62699) +- \[[`f218a4f553`](https://github.com/nodejs/node/commit/f218a4f553)] - **deps**: update nbytes to 0.1.4 (Node.js GitHub Bot) [#62698](https://github.com/nodejs/node/pull/62698) +- \[[`b47688524a`](https://github.com/nodejs/node/commit/b47688524a)] - **deps**: update archs files for openssl-3.5.6 (Node.js GitHub Bot) [#62629](https://github.com/nodejs/node/pull/62629) +- \[[`d202e2d343`](https://github.com/nodejs/node/commit/d202e2d343)] - **deps**: upgrade openssl sources to openssl-3.5.6 (Node.js GitHub Bot) [#62629](https://github.com/nodejs/node/pull/62629) +- \[[`2faba66341`](https://github.com/nodejs/node/commit/2faba66341)] - **deps**: update minimatch to 10.2.5 (Node.js GitHub Bot) [#62594](https://github.com/nodejs/node/pull/62594) +- \[[`fa46c90c5d`](https://github.com/nodejs/node/commit/fa46c90c5d)] - **deps**: update googletest to d72f9c8aea6817cdf1ca0ac10887f328de7f3da2 (Node.js GitHub Bot) [#62593](https://github.com/nodejs/node/pull/62593) +- \[[`099ded5713`](https://github.com/nodejs/node/commit/099ded5713)] - **deps**: update simdjson to 4.6.1 (Node.js GitHub Bot) [#62592](https://github.com/nodejs/node/pull/62592) +- \[[`7ce95afe96`](https://github.com/nodejs/node/commit/7ce95afe96)] - **deps**: libuv: cherry-pick aabb7651de (Santiago Gimeno) [#62561](https://github.com/nodejs/node/pull/62561) +- \[[`57ef845623`](https://github.com/nodejs/node/commit/57ef845623)] - **deps**: update icu to 78.3 (Node.js GitHub Bot) [#62324](https://github.com/nodejs/node/pull/62324) +- \[[`493ac40e12`](https://github.com/nodejs/node/commit/493ac40e12)] - **deps**: update libuv to 1.52.1 (Node.js GitHub Bot) [#61829](https://github.com/nodejs/node/pull/61829) +- \[[`b39508b368`](https://github.com/nodejs/node/commit/b39508b368)] - **deps**: update undici to 7.25.0 (Node.js GitHub Bot) [#63011](https://github.com/nodejs/node/pull/63011) +- \[[`cb67a925e9`](https://github.com/nodejs/node/commit/cb67a925e9)] - **deps**: use npm undici\@seven tag in `update-undici.sh` (Matteo Collina) [#62739](https://github.com/nodejs/node/pull/62739) +- \[[`aa1e0bc28b`](https://github.com/nodejs/node/commit/aa1e0bc28b)] - **doc**: fix typos and inconsistencies in crypto.md and webcrypto.md (Filip Skokan) [#62828](https://github.com/nodejs/node/pull/62828) +- \[[`f2a1735ed9`](https://github.com/nodejs/node/commit/f2a1735ed9)] - **doc**: fix duplicate word "to to" in util.styleText (Daijiro Wachi) [#62917](https://github.com/nodejs/node/pull/62917) +- \[[`b6378e215c`](https://github.com/nodejs/node/commit/b6378e215c)] - **doc**: fix node-config-schema (Сковорода Никита Андреевич) [#61596](https://github.com/nodejs/node/pull/61596) +- \[[`233894a9ce`](https://github.com/nodejs/node/commit/233894a9ce)] - **doc**: fix the TypeScript Execute (tsx) project link (David Thornton) [#63093](https://github.com/nodejs/node/pull/63093) +- \[[`5d97919f8f`](https://github.com/nodejs/node/commit/5d97919f8f)] - **doc**: correct diagnostics_channel built-in channel names (Bryan English) [#62995](https://github.com/nodejs/node/pull/62995) +- \[[`2a9ccc927e`](https://github.com/nodejs/node/commit/2a9ccc927e)] - **doc**: use mjs/cjs blocks for callbackify null reason example (Daijiro Wachi) [#62884](https://github.com/nodejs/node/pull/62884) +- \[[`ef413b5358`](https://github.com/nodejs/node/commit/ef413b5358)] - **doc**: fix typo in test.md (Rich Trott) [#62960](https://github.com/nodejs/node/pull/62960) +- \[[`76f21c5070`](https://github.com/nodejs/node/commit/76f21c5070)] - **doc**: correct typo in PR contribution instructions (Mike McCready) [#62738](https://github.com/nodejs/node/pull/62738) +- \[[`ca02af1f7d`](https://github.com/nodejs/node/commit/ca02af1f7d)] - **doc**: fix duplicate word "of of" in postMessageToThread (Daijiro Wachi) [#62917](https://github.com/nodejs/node/pull/62917) +- \[[`46c99ed526`](https://github.com/nodejs/node/commit/46c99ed526)] - **doc**: fix duplicate word "for for" in compile cache (Daijiro Wachi) [#62917](https://github.com/nodejs/node/pull/62917) +- \[[`1a60851734`](https://github.com/nodejs/node/commit/1a60851734)] - **doc**: fix typo in dns.lookup options description (Daijiro Wachi) [#62882](https://github.com/nodejs/node/pull/62882) +- \[[`169b5ea2ed`](https://github.com/nodejs/node/commit/169b5ea2ed)] - **doc**: fix Argon2 parameter bounds (Tobias Nießen) [#62868](https://github.com/nodejs/node/pull/62868) +- \[[`9a3a190f4e`](https://github.com/nodejs/node/commit/9a3a190f4e)] - **doc**: clarify diffieHellman.generateKeys recomputes same key (Kit Dallege) [#62205](https://github.com/nodejs/node/pull/62205) +- \[[`0fba9e87d6`](https://github.com/nodejs/node/commit/0fba9e87d6)] - **doc**: remove Ayase-252 and meixg from triagger team (Antoine du Hamel) [#62841](https://github.com/nodejs/node/pull/62841) +- \[[`9c700f3446`](https://github.com/nodejs/node/commit/9c700f3446)] - **doc**: clarify dns.lookup() callback signature when all is true (eungi) [#62800](https://github.com/nodejs/node/pull/62800) +- \[[`6b7280bc17`](https://github.com/nodejs/node/commit/6b7280bc17)] - **doc**: add experimental modules lifetime policy (Paolo Insogna) [#62753](https://github.com/nodejs/node/pull/62753) +- \[[`ce47ea31c9`](https://github.com/nodejs/node/commit/ce47ea31c9)] - **doc**: clarify process.\_debugProcess() in Permission Model (Fahad Khan) [#62537](https://github.com/nodejs/node/pull/62537) +- \[[`ba01633757`](https://github.com/nodejs/node/commit/ba01633757)] - **doc**: fix typo in devcontainer guide (Rohan Santhosh Kumar) [#62687](https://github.com/nodejs/node/pull/62687) +- \[[`70b4d5839b`](https://github.com/nodejs/node/commit/70b4d5839b)] - **doc**: clarify Backport-PR-URL metadata added automatically (Mike McCready) [#62668](https://github.com/nodejs/node/pull/62668) +- \[[`8126d1c3eb`](https://github.com/nodejs/node/commit/8126d1c3eb)] - **doc**: update WPT test runner README.md (Filip Skokan) [#62680](https://github.com/nodejs/node/pull/62680) +- \[[`978afea4b5`](https://github.com/nodejs/node/commit/978afea4b5)] - **doc**: fix spelling in release announcement guidance (Rohan Santhosh Kumar) [#62663](https://github.com/nodejs/node/pull/62663) +- \[[`1684ab8ff8`](https://github.com/nodejs/node/commit/1684ab8ff8)] - **doc**: note non-monotonic clock in crypto.randomUUIDv7 (nabeel378) [#62600](https://github.com/nodejs/node/pull/62600) +- \[[`86d4f07930`](https://github.com/nodejs/node/commit/86d4f07930)] - **doc**: update bug bounty program (Rafael Gonzaga) [#62590](https://github.com/nodejs/node/pull/62590) +- \[[`736ed8a08f`](https://github.com/nodejs/node/commit/736ed8a08f)] - **doc**: document TransformStream transformer.cancel option (Tom Pereira) [#62566](https://github.com/nodejs/node/pull/62566) +- \[[`938af9be01`](https://github.com/nodejs/node/commit/938af9be01)] - **doc**: mention test runner retry attemp is zero based (Moshe Atlow) [#62504](https://github.com/nodejs/node/pull/62504) +- \[[`94433e450f`](https://github.com/nodejs/node/commit/94433e450f)] - **doc,src,test**: fix dead inspector help URL (semimikoh) [#62745](https://github.com/nodejs/node/pull/62745) +- \[[`ddf1f01659`](https://github.com/nodejs/node/commit/ddf1f01659)] - **esm**: add `ERR_REQUIRE_ESM_RACE_CONDITION` (Antoine du Hamel) [#62462](https://github.com/nodejs/node/pull/62462) +- \[[`4a506acd16`](https://github.com/nodejs/node/commit/4a506acd16)] - **fs**: add followSymlinks option to glob (Matteo Collina) [#62695](https://github.com/nodejs/node/pull/62695) +- \[[`f4ea495f9b`](https://github.com/nodejs/node/commit/f4ea495f9b)] - **fs**: restore fs patchability in ESM loader (Joyee Cheung) [#62835](https://github.com/nodejs/node/pull/62835) +- \[[`63c111cd60`](https://github.com/nodejs/node/commit/63c111cd60)] - **fs**: validate position argument before length === 0 early return (Edy Silva) [#62674](https://github.com/nodejs/node/pull/62674) +- \[[`9705f628d9`](https://github.com/nodejs/node/commit/9705f628d9)] - **(SEMVER-MINOR)** **fs**: add signal option to fs.stat() (Mert Can Altin) [#57775](https://github.com/nodejs/node/pull/57775) +- \[[`40ccfdecf9`](https://github.com/nodejs/node/commit/40ccfdecf9)] - **(SEMVER-MINOR)** **fs**: expose frsize field in statfs (Jinho Jang) [#62277](https://github.com/nodejs/node/pull/62277) +- \[[`717476a24e`](https://github.com/nodejs/node/commit/717476a24e)] - **http**: emit 'drain' on OutgoingMessage only after buffers drain (Robert Nagy) [#62936](https://github.com/nodejs/node/pull/62936) +- \[[`d7188af5c9`](https://github.com/nodejs/node/commit/d7188af5c9)] - **(SEMVER-MINOR)** **http**: harden ClientRequest options merge (Matteo Collina) [#63082](https://github.com/nodejs/node/pull/63082) +- \[[`64f15c274a`](https://github.com/nodejs/node/commit/64f15c274a)] - **http**: fix leaked error listener on sync HTTP req create + destroy (Tim Perry) [#62872](https://github.com/nodejs/node/pull/62872) +- \[[`5c4798d799`](https://github.com/nodejs/node/commit/5c4798d799)] - **http**: fix no_proxy leading-dot suffix matching (Daijiro Wachi) [#62333](https://github.com/nodejs/node/pull/62333) +- \[[`9f3bc70ae5`](https://github.com/nodejs/node/commit/9f3bc70ae5)] - **http**: cleanup pipeline queue (Robert Nagy) [#62534](https://github.com/nodejs/node/pull/62534) +- \[[`aa1d8a9afc`](https://github.com/nodejs/node/commit/aa1d8a9afc)] - **(SEMVER-MINOR)** **http**: add req.signal to IncomingMessage (Akshat) [#62541](https://github.com/nodejs/node/pull/62541) +- \[[`900dc758ff`](https://github.com/nodejs/node/commit/900dc758ff)] - **http2**: expose writable stream state on compat response (T) [#63003](https://github.com/nodejs/node/pull/63003) +- \[[`b3bfe35912`](https://github.com/nodejs/node/commit/b3bfe35912)] - **inspector**: coerce key and value to string in webstorage events (Ali Hassan) [#62616](https://github.com/nodejs/node/pull/62616) +- \[[`3dc3fb6ad8`](https://github.com/nodejs/node/commit/3dc3fb6ad8)] - **inspector**: return errors when CDP protocol event emission fails (Ryuhei Shima) [#62162](https://github.com/nodejs/node/pull/62162) +- \[[`4f3f21bd7c`](https://github.com/nodejs/node/commit/4f3f21bd7c)] - **inspector**: auto collect webstorage data (Ryuhei Shima) [#62145](https://github.com/nodejs/node/pull/62145) +- \[[`36cc04189d`](https://github.com/nodejs/node/commit/36cc04189d)] - **inspector**: initial support storage inspection (Ryuhei Shima) [#61139](https://github.com/nodejs/node/pull/61139) +- \[[`1718bc3b9b`](https://github.com/nodejs/node/commit/1718bc3b9b)] - **inspector**: fix absolute URLs in network http (bugyaluwang) [#62955](https://github.com/nodejs/node/pull/62955) +- \[[`97e32c7a74`](https://github.com/nodejs/node/commit/97e32c7a74)] - **lib**: avoid quadratic shift() in startup snapshot callback (Daijiro Wachi) [#62914](https://github.com/nodejs/node/pull/62914) +- \[[`25d2e999de`](https://github.com/nodejs/node/commit/25d2e999de)] - **lib**: harden kKeyOps lookup with null prototype (Filip Skokan) [#62877](https://github.com/nodejs/node/pull/62877) +- \[[`37d3913c8f`](https://github.com/nodejs/node/commit/37d3913c8f)] - **lib**: short-circuit WebIDL BufferSource SAB check (Filip Skokan) [#62833](https://github.com/nodejs/node/pull/62833) +- \[[`430c69d25f`](https://github.com/nodejs/node/commit/430c69d25f)] - **lib**: use js-only implementation of `isDataView()` (René) [#62780](https://github.com/nodejs/node/pull/62780) +- \[[`3ba0add6a0`](https://github.com/nodejs/node/commit/3ba0add6a0)] - **lib**: fix lint in internal/webstreams/util.js (Filip Skokan) [#62806](https://github.com/nodejs/node/pull/62806) +- \[[`9b95c41398`](https://github.com/nodejs/node/commit/9b95c41398)] - **lib**: fix sequence argument handling in Blob constructor (Ms2ger) [#62179](https://github.com/nodejs/node/pull/62179) +- \[[`314dacdbee`](https://github.com/nodejs/node/commit/314dacdbee)] - **lib**: improve Web Cryptography key validation ordering (Filip Skokan) [#62749](https://github.com/nodejs/node/pull/62749) +- \[[`3d18162430`](https://github.com/nodejs/node/commit/3d18162430)] - **lib**: reject SharedArrayBuffer in web APIs per spec (Ali Hassan) [#62632](https://github.com/nodejs/node/pull/62632) +- \[[`ada3ce879d`](https://github.com/nodejs/node/commit/ada3ce879d)] - **lib**: defer AbortSignal.any() following (sangwook) [#62367](https://github.com/nodejs/node/pull/62367) +- \[[`b2981ec7eb`](https://github.com/nodejs/node/commit/b2981ec7eb)] - **meta**: bump actions/download-artifact from 8.0.0 to 8.0.1 (dependabot\[bot]) [#62549](https://github.com/nodejs/node/pull/62549) +- \[[`7cd20667b5`](https://github.com/nodejs/node/commit/7cd20667b5)] - **meta**: bump github/codeql-action from 4.35.1 to 4.35.3 (dependabot\[bot]) [#63074](https://github.com/nodejs/node/pull/63074) +- \[[`91a07cfe9f`](https://github.com/nodejs/node/commit/91a07cfe9f)] - **meta**: bump Mozilla-Actions/sccache-action from 0.0.9 to 0.0.10 (dependabot\[bot]) [#63073](https://github.com/nodejs/node/pull/63073) +- \[[`09e17fe47c`](https://github.com/nodejs/node/commit/09e17fe47c)] - **meta**: add automation policy (Chengzhong Wu) [#62871](https://github.com/nodejs/node/pull/62871) +- \[[`59e7fb7986`](https://github.com/nodejs/node/commit/59e7fb7986)] - **meta**: move VoltrexKeyva to emeritus (Matteo Collina) [#62895](https://github.com/nodejs/node/pull/62895) +- \[[`1e2915cfa6`](https://github.com/nodejs/node/commit/1e2915cfa6)] - **meta**: bump peter-evans/create-pull-request from 8.1.0 to 8.1.1 (dependabot\[bot]) [#62845](https://github.com/nodejs/node/pull/62845) +- \[[`0253c6e2be`](https://github.com/nodejs/node/commit/0253c6e2be)] - **meta**: bump step-security/harden-runner from 2.16.1 to 2.19.0 (dependabot\[bot]) [#62844](https://github.com/nodejs/node/pull/62844) +- \[[`f503675b86`](https://github.com/nodejs/node/commit/f503675b86)] - **meta**: bump actions/setup-node from 6.3.0 to 6.4.0 (dependabot\[bot]) [#62842](https://github.com/nodejs/node/pull/62842) +- \[[`5e14e4d26e`](https://github.com/nodejs/node/commit/5e14e4d26e)] - **meta**: broaden stale bot (Aviv Keller) [#62658](https://github.com/nodejs/node/pull/62658) +- \[[`795db76f87`](https://github.com/nodejs/node/commit/795db76f87)] - **meta**: pass release version to release worker (flakey5) [#62777](https://github.com/nodejs/node/pull/62777) +- \[[`ef384fe39f`](https://github.com/nodejs/node/commit/ef384fe39f)] - **meta**: add QUIC to CODEOWNERS (Tim Perry) [#62652](https://github.com/nodejs/node/pull/62652) +- \[[`67e0ac568d`](https://github.com/nodejs/node/commit/67e0ac568d)] - **meta**: move Michael to emeritus (Michael Dawson) [#62536](https://github.com/nodejs/node/pull/62536) +- \[[`5dad616393`](https://github.com/nodejs/node/commit/5dad616393)] - **meta**: populate apt list for slim runner in update-openssl workflow (René) [#62628](https://github.com/nodejs/node/pull/62628) +- \[[`a869d25d8a`](https://github.com/nodejs/node/commit/a869d25d8a)] - **meta**: bump step-security/harden-runner from 2.15.0 to 2.16.1 (dependabot\[bot]) [#62550](https://github.com/nodejs/node/pull/62550) +- \[[`769efc0403`](https://github.com/nodejs/node/commit/769efc0403)] - **meta**: bump actions/setup-node from 6.2.0 to 6.3.0 (dependabot\[bot]) [#62548](https://github.com/nodejs/node/pull/62548) +- \[[`73fcc2b055`](https://github.com/nodejs/node/commit/73fcc2b055)] - **meta**: bump github/codeql-action from 4.32.4 to 4.35.1 (dependabot\[bot]) [#62547](https://github.com/nodejs/node/pull/62547) +- \[[`6c001246fe`](https://github.com/nodejs/node/commit/6c001246fe)] - **meta**: bump codecov/codecov-action from 5.5.2 to 6.0.0 (dependabot\[bot]) [#62545](https://github.com/nodejs/node/pull/62545) +- \[[`5ee40d6a03`](https://github.com/nodejs/node/commit/5ee40d6a03)] - **meta**: bump actions/cache from 5.0.3 to 5.0.4 (dependabot\[bot]) [#62543](https://github.com/nodejs/node/pull/62543) +- \[[`ca16ad8a05`](https://github.com/nodejs/node/commit/ca16ad8a05)] - **meta**: require DCO signoff in commit message guidelines (James M Snell) [#62510](https://github.com/nodejs/node/pull/62510) +- \[[`db9497fc41`](https://github.com/nodejs/node/commit/db9497fc41)] - **meta**: expand memory leak DoS criteria to all DoS (Joyee Cheung) [#62505](https://github.com/nodejs/node/pull/62505) +- \[[`13b7d08b8d`](https://github.com/nodejs/node/commit/13b7d08b8d)] - **module**: remove duplicated checks from `_resolveFilename` (Antoine du Hamel) [#62729](https://github.com/nodejs/node/pull/62729) +- \[[`6b53efb53a`](https://github.com/nodejs/node/commit/6b53efb53a)] - **module,win**: fix long subpath import (Stefan Stojanovic) [#62101](https://github.com/nodejs/node/pull/62101) +- \[[`841dfbf6fc`](https://github.com/nodejs/node/commit/841dfbf6fc)] - **node-api**: update libuv ABI stability note (Chengzhong Wu) [#62789](https://github.com/nodejs/node/pull/62789) +- \[[`01090f2aa1`](https://github.com/nodejs/node/commit/01090f2aa1)] - **node-api**: add napi_create_external_sharedarraybuffer (Ben Noordhuis) [#62623](https://github.com/nodejs/node/pull/62623) +- \[[`87443b4355`](https://github.com/nodejs/node/commit/87443b4355)] - **node-api**: execute tsfn finalizer after queue drains when aborted (Kevin Eady) [#61956](https://github.com/nodejs/node/pull/61956) +- \[[`e95570c054`](https://github.com/nodejs/node/commit/e95570c054)] - **process**: handle rejections only when needed (Gürgün Dayıoğlu) [#62919](https://github.com/nodejs/node/pull/62919) +- \[[`37d49f3219`](https://github.com/nodejs/node/commit/37d49f3219)] - **process**: optimize asyncHandledRejections by using FixedQueue (Gürgün Dayıoğlu) [#60854](https://github.com/nodejs/node/pull/60854) +- \[[`f697c55e38`](https://github.com/nodejs/node/commit/f697c55e38)] - **quic**: add QuicEndpoint.listening & QuicStream.destroy() and tests (Tim Perry) [#62648](https://github.com/nodejs/node/pull/62648) +- \[[`c128942b69`](https://github.com/nodejs/node/commit/c128942b69)] - **quic**: fixup token verification to handle zero expiration (James M Snell) [#62620](https://github.com/nodejs/node/pull/62620) +- \[[`abb881ec92`](https://github.com/nodejs/node/commit/abb881ec92)] - **quic**: support multiple ALPN negotiation (James M Snell) [#62620](https://github.com/nodejs/node/pull/62620) +- \[[`476926c2ad`](https://github.com/nodejs/node/commit/476926c2ad)] - **quic**: apply multiple TLS context improvements and SNI support (James M Snell) [#62620](https://github.com/nodejs/node/pull/62620) +- \[[`76d9c24b95`](https://github.com/nodejs/node/commit/76d9c24b95)] - **quic**: implement rapidhash for hashing improvements (James M Snell) [#62620](https://github.com/nodejs/node/pull/62620) +- \[[`08726cd43d`](https://github.com/nodejs/node/commit/08726cd43d)] - **quic**: move quic behind compile time flag (Matteo Collina) [#61444](https://github.com/nodejs/node/pull/61444) +- \[[`ea4f19aaa7`](https://github.com/nodejs/node/commit/ea4f19aaa7)] - **quic**: use arena allocation for packets (James M Snell) [#62589](https://github.com/nodejs/node/pull/62589) +- \[[`21e9239e2a`](https://github.com/nodejs/node/commit/21e9239e2a)] - **quic**: fixup linting/formatting issues (James M Snell) [#62387](https://github.com/nodejs/node/pull/62387) +- \[[`edeed4303b`](https://github.com/nodejs/node/commit/edeed4303b)] - **quic**: update http3 impl details (James M Snell) [#62387](https://github.com/nodejs/node/pull/62387) +- \[[`7f3a85e6aa`](https://github.com/nodejs/node/commit/7f3a85e6aa)] - **quic**: fix a handful of bugs and missing functionality (James M Snell) [#62387](https://github.com/nodejs/node/pull/62387) +- \[[`45c1ebddf8`](https://github.com/nodejs/node/commit/45c1ebddf8)] - **quic**: copy options.certs buffer instead of detaching (Chengzhong Wu) [#61403](https://github.com/nodejs/node/pull/61403) +- \[[`a31a8ee680`](https://github.com/nodejs/node/commit/a31a8ee680)] - **quic**: reduce boilerplate and other minor cleanups (James M Snell) [#59342](https://github.com/nodejs/node/pull/59342) +- \[[`3be70ff43a`](https://github.com/nodejs/node/commit/3be70ff43a)] - **quic**: multiple fixups and updates (James M Snell) [#59342](https://github.com/nodejs/node/pull/59342) +- \[[`b91a93444c`](https://github.com/nodejs/node/commit/b91a93444c)] - **quic**: update more of the quic to the new compile guard (James M Snell) [#59342](https://github.com/nodejs/node/pull/59342) +- \[[`ca0080c164`](https://github.com/nodejs/node/commit/ca0080c164)] - **quic**: few additional small comment edits in cid.h (James M Snell) [#59342](https://github.com/nodejs/node/pull/59342) +- \[[`6553202d83`](https://github.com/nodejs/node/commit/6553202d83)] - **quic**: fixup NO_ERROR macro conflict on windows (James M Snell) [#59381](https://github.com/nodejs/node/pull/59381) +- \[[`6df1508ac2`](https://github.com/nodejs/node/commit/6df1508ac2)] - **quic**: fixup windows coverage compile error (James M Snell) [#59381](https://github.com/nodejs/node/pull/59381) +- \[[`b2b0bf8b04`](https://github.com/nodejs/node/commit/b2b0bf8b04)] - **quic**: update the guard to check openssl version (James M Snell) [#59249](https://github.com/nodejs/node/pull/59249) +- \[[`5556b154bd`](https://github.com/nodejs/node/commit/5556b154bd)] - **quic**: start re-enabling quic with openssl 3.5 (James M Snell) [#59249](https://github.com/nodejs/node/pull/59249) +- \[[`2ca42c8263`](https://github.com/nodejs/node/commit/2ca42c8263)] - **repl**: keep reference count for `process.on('newListener')` (Anna Henningsen) [#61895](https://github.com/nodejs/node/pull/61895) +- \[[`2f37f9177f`](https://github.com/nodejs/node/commit/2f37f9177f)] - **sqlite**: use OneByte for ASCII text and internalize col names (Ali Hassan) [#61954](https://github.com/nodejs/node/pull/61954) +- \[[`3c96ae1b2f`](https://github.com/nodejs/node/commit/3c96ae1b2f)] - **sqlite**: add serialize() and deserialize() (Ali Hassan) [#62579](https://github.com/nodejs/node/pull/62579) +- \[[`be4d2f3a4c`](https://github.com/nodejs/node/commit/be4d2f3a4c)] - **sqlite**: enable Percentile extension (Jurj Andrei George) [#61295](https://github.com/nodejs/node/pull/61295) +- \[[`dafed453b2`](https://github.com/nodejs/node/commit/dafed453b2)] - **src**: clean up experimental flag variables (Antoine du Hamel) [#62759](https://github.com/nodejs/node/pull/62759) +- \[[`dca1e6aeea`](https://github.com/nodejs/node/commit/dca1e6aeea)] - **src**: expose help texts into node-config-schema.json (Pietro Marchini) [#58680](https://github.com/nodejs/node/pull/58680) +- \[[`28c4f44eb1`](https://github.com/nodejs/node/commit/28c4f44eb1)] - **src**: add permission support to config file (Marco Ippolito) [#60746](https://github.com/nodejs/node/pull/60746) +- \[[`f49175b220`](https://github.com/nodejs/node/commit/f49175b220)] - **src**: fix small compile warning in quic/streams.cc (James M Snell) [#60118](https://github.com/nodejs/node/pull/60118) +- \[[`c9d4a446d8`](https://github.com/nodejs/node/commit/c9d4a446d8)] - **src**: cleanup quic TransportParams class (James M Snell) [#59884](https://github.com/nodejs/node/pull/59884) +- \[[`99bb02fd9e`](https://github.com/nodejs/node/commit/99bb02fd9e)] - **src**: swap dotenv and config file parsing order (Marco Ippolito) [#63035](https://github.com/nodejs/node/pull/63035) +- \[[`ecb4d49b7b`](https://github.com/nodejs/node/commit/ecb4d49b7b)] - **src**: add missing \ for abort() declaration (Charles Kerr) [#63001](https://github.com/nodejs/node/pull/63001) +- \[[`b6219b6362`](https://github.com/nodejs/node/commit/b6219b6362)] - **src**: fix crash in GetErrorSource() for invalid using syntax (semimikoh) [#62770](https://github.com/nodejs/node/pull/62770) +- \[[`b5ca5ad4c5`](https://github.com/nodejs/node/commit/b5ca5ad4c5)] - **src**: simplify `TCPWrap::Connect` signature (Anna Henningsen) [#62929](https://github.com/nodejs/node/pull/62929) +- \[[`ef7ffce7cf`](https://github.com/nodejs/node/commit/ef7ffce7cf)] - **src**: use DCHECK in AsyncWrap::MakeCallback instead emiting a warning (Gerhard Stöbich) [#62795](https://github.com/nodejs/node/pull/62795) +- \[[`cd9890a5ab`](https://github.com/nodejs/node/commit/cd9890a5ab)] - **src**: fix MaybeStackBuffer char_traits deprecation warning (om-ghante) [#62507](https://github.com/nodejs/node/pull/62507) +- \[[`c70ff44aee`](https://github.com/nodejs/node/commit/c70ff44aee)] - **src**: use context-free V8 message column getters (René) [#62778](https://github.com/nodejs/node/pull/62778) +- \[[`06c405f1d7`](https://github.com/nodejs/node/commit/06c405f1d7)] - **src**: coerce `spawnSync` args to string once (Antoine du Hamel) [#62633](https://github.com/nodejs/node/pull/62633) +- \[[`6151999ad6`](https://github.com/nodejs/node/commit/6151999ad6)] - **src**: use stack allocation for small string encoding (Ali Hassan) [#62431](https://github.com/nodejs/node/pull/62431) +- \[[`a71a4ac7a3`](https://github.com/nodejs/node/commit/a71a4ac7a3)] - **src**: add contextify interceptor debug logs (Chengzhong Wu) [#62460](https://github.com/nodejs/node/pull/62460) +- \[[`ad9a2909c2`](https://github.com/nodejs/node/commit/ad9a2909c2)] - **src**: workaround AIX libc++ std::filesystem bug (Richard Lau) [#62788](https://github.com/nodejs/node/pull/62788) +- \[[`7792f1ae47`](https://github.com/nodejs/node/commit/7792f1ae47)] - **stream**: copyedit `webstreams/adapter.js` (Antoine du Hamel) [#63034](https://github.com/nodejs/node/pull/63034) +- \[[`1397d8ce5c`](https://github.com/nodejs/node/commit/1397d8ce5c)] - **stream**: remove duplicated utility (Antoine du Hamel) [#63031](https://github.com/nodejs/node/pull/63031) +- \[[`ff86b1d64f`](https://github.com/nodejs/node/commit/ff86b1d64f)] - **stream**: simplify `setPromiseHandled` utility (Antoine du Hamel) [#63032](https://github.com/nodejs/node/pull/63032) +- \[[`24a078149a`](https://github.com/nodejs/node/commit/24a078149a)] - **stream**: validate ReadableStream.from iterator objects (Daeyeon Jeong) [#62911](https://github.com/nodejs/node/pull/62911) +- \[[`cfb1fa9680`](https://github.com/nodejs/node/commit/cfb1fa9680)] - **stream**: reject duplicate nested transferables (Daeyeon Jeong) [#62831](https://github.com/nodejs/node/pull/62831) +- \[[`d0c913758a`](https://github.com/nodejs/node/commit/d0c913758a)] - **stream**: ensuring cross-destruction in \_duplexify to prevent leaks (Daijiro Wachi) [#62824](https://github.com/nodejs/node/pull/62824) +- \[[`978f5c15d7`](https://github.com/nodejs/node/commit/978f5c15d7)] - **stream**: simplify `readableStreamFromIterable` (Antoine du Hamel) [#62651](https://github.com/nodejs/node/pull/62651) +- \[[`3527646ba5`](https://github.com/nodejs/node/commit/3527646ba5)] - **stream**: fix nested compose error propagation (Matteo Collina) [#62556](https://github.com/nodejs/node/pull/62556) +- \[[`dfb9edef4f`](https://github.com/nodejs/node/commit/dfb9edef4f)] - **stream**: allow shared array buffer sources in writable webstream adapter (René) [#62163](https://github.com/nodejs/node/pull/62163) +- \[[`f00cdab627`](https://github.com/nodejs/node/commit/f00cdab627)] - **stream**: simplify `createPromiseCallback` (Antoine du Hamel) [#62650](https://github.com/nodejs/node/pull/62650) +- \[[`3ed783535f`](https://github.com/nodejs/node/commit/3ed783535f)] - **stream**: fix writev unhandled rejection in fromWeb (sangwook) [#62297](https://github.com/nodejs/node/pull/62297) +- \[[`29b196694c`](https://github.com/nodejs/node/commit/29b196694c)] - **stream**: noop pause/resume on destroyed streams (Robert Nagy) [#62557](https://github.com/nodejs/node/pull/62557) +- \[[`d73dbb9fc8`](https://github.com/nodejs/node/commit/d73dbb9fc8)] - **stream**: refactor duplexify to be less suceptible to prototype pollution (Antoine du Hamel) [#62559](https://github.com/nodejs/node/pull/62559) +- \[[`6f37f7e240`](https://github.com/nodejs/node/commit/6f37f7e240)] - **(SEMVER-MINOR)** **stream**: propagate destruction in duplexPair (Ahmed Elhor) [#61098](https://github.com/nodejs/node/pull/61098) +- \[[`b8816580e9`](https://github.com/nodejs/node/commit/b8816580e9)] - **test**: generate `localstorage.db` in a temp dir (Chengzhong Wu) [#62660](https://github.com/nodejs/node/pull/62660) +- \[[`31a863fd29`](https://github.com/nodejs/node/commit/31a863fd29)] - **test**: update WPT for url to 258f285de0 (Node.js GitHub Bot) [#63087](https://github.com/nodejs/node/pull/63087) +- \[[`d0d19bd8e3`](https://github.com/nodejs/node/commit/d0d19bd8e3)] - **test**: update WPT for streams to f8f26a372f (Node.js GitHub Bot) [#62864](https://github.com/nodejs/node/pull/62864) +- \[[`f50ac5bc78`](https://github.com/nodejs/node/commit/f50ac5bc78)] - **test**: improve config-file permission test coverage (Rafael Gonzaga) [#60929](https://github.com/nodejs/node/pull/60929) +- \[[`a0f90000f4`](https://github.com/nodejs/node/commit/a0f90000f4)] - **test**: export isRiscv64 from common module (Jamie Magee) [#62609](https://github.com/nodejs/node/pull/62609) +- \[[`da4dd8646f`](https://github.com/nodejs/node/commit/da4dd8646f)] - **test**: normalize known inspector crash as completion (Joyee Cheung) [#62851](https://github.com/nodejs/node/pull/62851) +- \[[`b7fdd94a4c`](https://github.com/nodejs/node/commit/b7fdd94a4c)] - **test**: account for RFC 7919 FFDHE negotiation in OpenSSL 4.0 (Filip Skokan) [#62805](https://github.com/nodejs/node/pull/62805) +- \[[`375a993aaf`](https://github.com/nodejs/node/commit/375a993aaf)] - **test**: skip tls-deprecated secp256k1 on OpenSSL 4.0 (Filip Skokan) [#62805](https://github.com/nodejs/node/pull/62805) +- \[[`698d8287d1`](https://github.com/nodejs/node/commit/698d8287d1)] - **test**: use an always invalid cipher and cover OpenSSL 4.0 behaviours (Filip Skokan) [#62805](https://github.com/nodejs/node/pull/62805) +- \[[`036bc6f300`](https://github.com/nodejs/node/commit/036bc6f300)] - **test**: use valid DER OCSP responses (Filip Skokan) [#62805](https://github.com/nodejs/node/pull/62805) +- \[[`3aa9938da8`](https://github.com/nodejs/node/commit/3aa9938da8)] - **test**: skip test-tls-error-stack when engines are unsupported (Filip Skokan) [#62805](https://github.com/nodejs/node/pull/62805) +- \[[`947f1ae246`](https://github.com/nodejs/node/commit/947f1ae246)] - **test**: accept renamed OpenSSL 4.0 error code and reason (Filip Skokan) [#62805](https://github.com/nodejs/node/pull/62805) +- \[[`afdd355622`](https://github.com/nodejs/node/commit/afdd355622)] - **test**: update test/addons/openssl-binding for OpenSSL 4.0 (Filip Skokan) [#62805](https://github.com/nodejs/node/pull/62805) +- \[[`8637524a99`](https://github.com/nodejs/node/commit/8637524a99)] - **test**: mark test-snapshot-reproducible flaky (Filip Skokan) [#62808](https://github.com/nodejs/node/pull/62808) +- \[[`c22d34134b`](https://github.com/nodejs/node/commit/c22d34134b)] - **test**: check contextify contextual store behavior in strict mode (René) [#62571](https://github.com/nodejs/node/pull/62571) +- \[[`0b4e0d3c94`](https://github.com/nodejs/node/commit/0b4e0d3c94)] - **test**: update tls junk data error expectations (Filip Skokan) [#62629](https://github.com/nodejs/node/pull/62629) +- \[[`85d83c2cdb`](https://github.com/nodejs/node/commit/85d83c2cdb)] - **test**: ensure WPT report is in out/wpt (Filip Skokan) [#62637](https://github.com/nodejs/node/pull/62637) +- \[[`9e21711c60`](https://github.com/nodejs/node/commit/9e21711c60)] - **test**: improve WPT runner summary (Filip Skokan) [#62636](https://github.com/nodejs/node/pull/62636) +- \[[`e04e2c9ac1`](https://github.com/nodejs/node/commit/e04e2c9ac1)] - **test**: skip url WPT subtests instead of modifying test script (Filip Skokan) [#62635](https://github.com/nodejs/node/pull/62635) +- \[[`7b1211f88c`](https://github.com/nodejs/node/commit/7b1211f88c)] - **test**: capture negative utimes mtime at call time (Yuya Inoue) [#62490](https://github.com/nodejs/node/pull/62490) +- \[[`f1a6e9fcc7`](https://github.com/nodejs/node/commit/f1a6e9fcc7)] - **test**: allow skipping individual WPT subtests (Filip Skokan) [#62517](https://github.com/nodejs/node/pull/62517) +- \[[`23f927542e`](https://github.com/nodejs/node/commit/23f927542e)] - **test**: use on-disk fixture for test-npm-install (Joyee Cheung) [#62584](https://github.com/nodejs/node/pull/62584) +- \[[`4739c45879`](https://github.com/nodejs/node/commit/4739c45879)] - **test**: update WPT for url to 7a3645b79a (Node.js GitHub Bot) [#62591](https://github.com/nodejs/node/pull/62591) +- \[[`f68189b839`](https://github.com/nodejs/node/commit/f68189b839)] - **test_runner**: add `testId` to test events (Moshe Atlow) [#62772](https://github.com/nodejs/node/pull/62772) +- \[[`5c2770446e`](https://github.com/nodejs/node/commit/5c2770446e)] - **test_runner**: publish to TracingChannel for OTel instrumentation (Moshe Atlow) [#62502](https://github.com/nodejs/node/pull/62502) +- \[[`d14029be7f`](https://github.com/nodejs/node/commit/d14029be7f)] - **(SEMVER-MINOR)** **test_runner**: support test order randomization (Pietro Marchini) [#61747](https://github.com/nodejs/node/pull/61747) +- \[[`3f74a58979`](https://github.com/nodejs/node/commit/3f74a58979)] - **test_runner**: update node-config-schema (Pietro Marchini) [#58680](https://github.com/nodejs/node/pull/58680) +- \[[`60c83f6199`](https://github.com/nodejs/node/commit/60c83f6199)] - **test_runner**: fix failing suite hooks when marked with `todo` (Moshe Atlow) [#63097](https://github.com/nodejs/node/pull/63097) +- \[[`d142c584cd`](https://github.com/nodejs/node/commit/d142c584cd)] - **(SEMVER-MINOR)** **test_runner**: align mock timeout api (sangwook) [#62820](https://github.com/nodejs/node/pull/62820) +- \[[`3e72065ed6`](https://github.com/nodejs/node/commit/3e72065ed6)] - **test_runner**: fix suite rerun edge case (Moshe Atlow) [#62860](https://github.com/nodejs/node/pull/62860) +- \[[`01a9552585`](https://github.com/nodejs/node/commit/01a9552585)] - **(SEMVER-MINOR)** **test_runner**: add mock-timers support for AbortSignal.timeout (DeveloperViraj) [#60751](https://github.com/nodejs/node/pull/60751) +- \[[`dd43efffa6`](https://github.com/nodejs/node/commit/dd43efffa6)] - **test_runner**: add passed, attempt, and diagnostic to SuiteContext (Moshe Atlow) [#62504](https://github.com/nodejs/node/pull/62504) +- \[[`a12dc445cc`](https://github.com/nodejs/node/commit/a12dc445cc)] - **tools**: add a check for clean git tree after tests (Antoine du Hamel) [#62661](https://github.com/nodejs/node/pull/62661) +- \[[`5b49178375`](https://github.com/nodejs/node/commit/5b49178375)] - **tools**: use LTS Node.js in notify-on-push workflow (Nenad Spasenic) [#63084](https://github.com/nodejs/node/pull/63084) +- \[[`5a93bde5bb`](https://github.com/nodejs/node/commit/5a93bde5bb)] - **tools**: update gr2m/create-or-update-pull-request-action to v1.10.1 (Mike McCready) [#63065](https://github.com/nodejs/node/pull/63065) +- \[[`b133019d19`](https://github.com/nodejs/node/commit/b133019d19)] - **tools**: simplify `update-undici.sh` (Antoine du Hamel) [#63044](https://github.com/nodejs/node/pull/63044) +- \[[`04d3538074`](https://github.com/nodejs/node/commit/04d3538074)] - **tools**: do not run `test-linux` on unrelated tools changes (Antoine du Hamel) [#63037](https://github.com/nodejs/node/pull/63037) +- \[[`4d396ac4a5`](https://github.com/nodejs/node/commit/4d396ac4a5)] - **tools**: bump the eslint group in /tools/eslint with 4 updates (dependabot\[bot]) [#62848](https://github.com/nodejs/node/pull/62848) +- \[[`9354bf40e7`](https://github.com/nodejs/node/commit/9354bf40e7)] - **tools**: update gyp-next to 0.22.1 (Node.js GitHub Bot) [#62961](https://github.com/nodejs/node/pull/62961) +- \[[`c23db1ca85`](https://github.com/nodejs/node/commit/c23db1ca85)] - **tools**: fix commit linter for semver-major release proposals (Antoine du Hamel) [#62993](https://github.com/nodejs/node/pull/62993) +- \[[`6e097ee3f1`](https://github.com/nodejs/node/commit/6e097ee3f1)] - **tools**: consolidate and simplify .editorconfig deps section (Daijiro Wachi) [#62887](https://github.com/nodejs/node/pull/62887) +- \[[`a47ea6d6ea`](https://github.com/nodejs/node/commit/a47ea6d6ea)] - **tools**: set bot as author of tools-deps-update PRs (Antoine du Hamel) [#62856](https://github.com/nodejs/node/pull/62856) +- \[[`00e86f0471`](https://github.com/nodejs/node/commit/00e86f0471)] - **tools**: bump brace-expansion from 5.0.4 to 5.0.5 in /tools/eslint (dependabot\[bot]) [#62458](https://github.com/nodejs/node/pull/62458) +- \[[`cd7e262e75`](https://github.com/nodejs/node/commit/cd7e262e75)] - **tools**: bump brace-expansion in /tools/clang-format (dependabot\[bot]) [#62467](https://github.com/nodejs/node/pull/62467) +- \[[`bfc1319bc8`](https://github.com/nodejs/node/commit/bfc1319bc8)] - **tools**: exclude @node-core/doc-kit from dependabot cooldown (Levi Zim) [#62775](https://github.com/nodejs/node/pull/62775) +- \[[`a932fbd10b`](https://github.com/nodejs/node/commit/a932fbd10b)] - **tools**: re-enable undici WPTs in daily wpt.fyi job (Filip Skokan) [#62677](https://github.com/nodejs/node/pull/62677) +- \[[`f7bd9e3055`](https://github.com/nodejs/node/commit/f7bd9e3055)] - **tools**: update gyp-next to 0.22.0 (Node.js GitHub Bot) [#62697](https://github.com/nodejs/node/pull/62697) +- \[[`c400d46d87`](https://github.com/nodejs/node/commit/c400d46d87)] - **tools**: improve backport review script (Antoine du Hamel) [#62573](https://github.com/nodejs/node/pull/62573) +- \[[`be23b75814`](https://github.com/nodejs/node/commit/be23b75814)] - **tools**: improve output for unexpected passes in WTP tests (Antoine du Hamel) [#62587](https://github.com/nodejs/node/pull/62587) +- \[[`609c013ece`](https://github.com/nodejs/node/commit/609c013ece)] - **tools**: revert OpenSSL update workflow to ubuntu-latest (Richard Lau) [#62627](https://github.com/nodejs/node/pull/62627) +- \[[`81bac1ebfd`](https://github.com/nodejs/node/commit/81bac1ebfd)] - **tools**: bump the eslint group in /tools/eslint with 2 updates (dependabot\[bot]) [#62552](https://github.com/nodejs/node/pull/62552) +- \[[`1fee26522d`](https://github.com/nodejs/node/commit/1fee26522d)] - **tools**: allow triagers to queue a PR for CI until it's reviewed (Antoine du Hamel) [#62524](https://github.com/nodejs/node/pull/62524) +- \[[`332088f929`](https://github.com/nodejs/node/commit/332088f929)] - **tools**: do not run `commit-lint` on release proposals (Antoine du Hamel) [#62523](https://github.com/nodejs/node/pull/62523) +- \[[`9a25fc8a4d`](https://github.com/nodejs/node/commit/9a25fc8a4d)] - **url**: process crash via malformed UNC hostname in pathToFileURL() (Nicola Del Gobbo) [#62574](https://github.com/nodejs/node/pull/62574) +- \[[`7bd08ff60a`](https://github.com/nodejs/node/commit/7bd08ff60a)] - **url**: optimize URLSearchParams set/delete duplicate handling (Gürgün Dayıoğlu) [#62266](https://github.com/nodejs/node/pull/62266) +- \[[`2d636388fa`](https://github.com/nodejs/node/commit/2d636388fa)] - **url**: align default argument handling for URLPattern with webidl (Filip Skokan) [#62719](https://github.com/nodejs/node/pull/62719) +- \[[`00705a459a`](https://github.com/nodejs/node/commit/00705a459a)] - **(SEMVER-MINOR)** **util**: colorize text with hex colors (Guilherme Araújo) [#61556](https://github.com/nodejs/node/pull/61556) +- \[[`0e2adb3e45`](https://github.com/nodejs/node/commit/0e2adb3e45)] - **watch**: track worker entry files in watch mode (SudhansuBandha) [#62368](https://github.com/nodejs/node/pull/62368) +- \[[`c58fe38211`](https://github.com/nodejs/node/commit/c58fe38211)] - **watch**: fix --env-file-if-exists crashing on linux if the file is missing (Efe) [#61870](https://github.com/nodejs/node/pull/61870) + +Windows 64-bit Installer: https://nodejs.org/dist/v24.16.0/node-v24.16.0-x64.msi \ +Windows ARM 64-bit Installer: https://nodejs.org/dist/v24.16.0/node-v24.16.0-arm64.msi \ +Windows 64-bit Binary: https://nodejs.org/dist/v24.16.0/win-x64/node.exe \ +Windows ARM 64-bit Binary: https://nodejs.org/dist/v24.16.0/win-arm64/node.exe \ +macOS 64-bit Installer: https://nodejs.org/dist/v24.16.0/node-v24.16.0.pkg \ +macOS Apple Silicon 64-bit Binary: https://nodejs.org/dist/v24.16.0/node-v24.16.0-darwin-arm64.tar.gz \ +macOS Intel 64-bit Binary: https://nodejs.org/dist/v24.16.0/node-v24.16.0-darwin-x64.tar.gz \ +Linux 64-bit Binary: https://nodejs.org/dist/v24.16.0/node-v24.16.0-linux-x64.tar.xz \ +Linux PPC LE 64-bit Binary: https://nodejs.org/dist/v24.16.0/node-v24.16.0-linux-ppc64le.tar.xz \ +Linux s390x 64-bit Binary: https://nodejs.org/dist/v24.16.0/node-v24.16.0-linux-s390x.tar.xz \ +AIX 64-bit Binary: https://nodejs.org/dist/v24.16.0/node-v24.16.0-aix-ppc64.tar.gz \ +ARMv8 64-bit Binary: https://nodejs.org/dist/v24.16.0/node-v24.16.0-linux-arm64.tar.xz \ +Source Code: https://nodejs.org/dist/v24.16.0/node-v24.16.0.tar.gz \ +Other release files: https://nodejs.org/dist/v24.16.0/ \ +Documentation: https://nodejs.org/docs/v24.16.0/api/ + +### SHASUMS + +``` +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +30dfd8e44322c2712813f25e163c253cad53be43e098907b8b5348bf174a4968 node-v24.16.0-aix-ppc64.tar.gz +beac2056574ebc523d5feaad7cdc434cb1d752eba076db7ebb4b62bc13ec70b9 node-v24.16.0-arm64.msi +39189dab4eeb15706c424af0ac08a3044c9e48f7db12a7d77f6b7aafc7dd5df6 node-v24.16.0-darwin-arm64.tar.gz +e28ad5531b2aafe0ea555a51b2412c42fdc0f91a6a53fbd03ac93e3847e91389 node-v24.16.0-darwin-arm64.tar.xz +298b4c7b3cb80765c8703e42b90324a4ece3b6634947b89e769c3c980ab55185 node-v24.16.0-darwin-x64.tar.gz +6b144acbcfdbca75a1366100ff96e6bf6a4fe666b88a4bda7bfbd0299c82cca2 node-v24.16.0-darwin-x64.tar.xz +ee3466c7ed5101cdc82978a5164148204bfffae0ddacda24ef6d9805bdf4fc07 node-v24.16.0-headers.tar.gz +ea9d6006feabea5f73022968ee943d55474dd1589fe3f1118f1ed5492d688a45 node-v24.16.0-headers.tar.xz +589f5b6dd4fcfee4dfda73013903c966abaa8abd93dbc9d436544e472b4f0e74 node-v24.16.0-linux-arm64.tar.gz +524659219d6a207a7400f2bde15d19ba060ffbe0d32a8643319ad67e3bb64c78 node-v24.16.0-linux-arm64.tar.xz +252b5820534dc0304a28541c9a44437cfa7202e7f20225d28d493932c58e97aa node-v24.16.0-linux-ppc64le.tar.gz +d3d9f688f22c0293d3f72d6d0c541a41c90c3c6d4f3dd6d0acff3a90656d2c51 node-v24.16.0-linux-ppc64le.tar.xz +567af0975b3405516b9b1ddc64429a23ec8c5a2fa6cf01391261a4cc774e3edd node-v24.16.0-linux-s390x.tar.gz +b03477fddc8775ba73d74a3c5fce8582eb183741f6f55b7a992ccd252cffad91 node-v24.16.0-linux-s390x.tar.xz +2faf6a387e9b62b888e21c54f01249fb27537ffecf1842f29f4c919d0a59a0ff node-v24.16.0-linux-x64.tar.gz +d804845d34eddc21dc1092b519d643ef40b1f58ec5dec5c22b1f4bd8fabde6c9 node-v24.16.0-linux-x64.tar.xz +65843aafbab48999c9d5f072746836965340c9ef2fbf17a377d3f919dcb0cb7a node-v24.16.0.pkg +f511d32e3876cb54fa6ddccaa8dd46649ae6ebe9e499c57531c5ca56e7ad4548 node-v24.16.0.tar.gz +2ff84a6de70b6165290111b0fc656ded1ad207a799816fe720cc7c31232df30f node-v24.16.0.tar.xz +e4357cd1ef3b6c67fb99547c4b736aa6732e2b4abd38ece252e119332fb49621 node-v24.16.0-win-arm64.7z +14834611d4c6b3c06054e7007732b90474c16e0b32f395e05b55a571ef71c6d2 node-v24.16.0-win-arm64.zip +9f0ad977a75a1ca1a2ebe1294caf64e6c6b4de89d3b6dff218455de3fa0a3211 node-v24.16.0-win-x64.7z +edaca9bd58ec8e92037dac4e877d52f6b8f430b81c18b57e264b4e2fb111cd56 node-v24.16.0-win-x64.zip +43749d78a28ff11a36cb279407bc13e79bcfb8670e7926e469018d31c2ec6453 node-v24.16.0-x64.msi +6694c2553f41d6b7cd6e396536e9ad5a9f4e1b898d59e4e0ec5de127096eb6db win-arm64/node.exe +d32c3ff35f34b9593e5fcddc23ca779f4b40abfb9aa5a031d620f1ecb44ca935 win-arm64/node.lib +445c1f8fd832815ec168581153c4d6b9b631eefdab37774eb3856064b0a231de win-arm64/node_pdb.7z +798a58c15ba26972cbfcbe3b89ec5069e76652c1da9d6eb92cc6c4baef7f14a6 win-arm64/node_pdb.zip +b3094d0b49f9ad602262a9921551737bb97637c05dd357a06ae98188d7290aa3 win-x64/node.exe +4ab42af597bc4f0957e9e2dcd5db18bdf223406a0c8e0b6be0f28e57977b808b win-x64/node.lib +1f130dae5d59ea47c556b23460b891264d363c2f1a206ababd4e73ca4d2faaeb win-x64/node_pdb.7z +21dad3725d11505288cb93c0cffdc7a7f28ab09aee9e7981e03967ea50cbfd7d win-x64/node_pdb.zip + +-----BEGIN PGP SIGNATURE----- + +iHUEARYIAB0WIQRb6KP2yKXAHRBsCtggsaOQsWjTVgUCag8GZwAKCRAgsaOQsWjT +VtNGAP9bmtHKXwhC4/zRS9dH1rg29N4rkewTC/JYLU6kVnwRMQD9FrvhnV0OiQzN +KAz6viuqtPt/jun0JHEw1AcKCmIVOQY= +=oONv +-----END PGP SIGNATURE----- +``` diff --git a/apps/site/pages/en/blog/release/v24.17.0.md b/apps/site/pages/en/blog/release/v24.17.0.md new file mode 100644 index 0000000000000..96eedd08f6ebf --- /dev/null +++ b/apps/site/pages/en/blog/release/v24.17.0.md @@ -0,0 +1,110 @@ +--- +date: '2026-06-18T04:38:38.484Z' +category: release +title: Node.js 24.17.0 (LTS) +layout: blog-post +author: Antoine du Hamel +--- + +## 2026-06-18, Version 24.17.0 'Krypton' (LTS), @aduh95 + +This is a security release. + +### Notable Changes + +- (CVE-2026-48618) tls: normalize hostname for server identity checks (Matteo Collina) – High +- (CVE-2026-48933) crypto: guard WebCrypto cipher output length (Filip Skokan) – High +- (CVE-2026-48615) lib,test: redact proxy credentials in tunnel errors (Matteo Collina) – Medium +- (CVE-2026-48619) http2: cap originSet size to prevent unbounded memory growth (Matteo Collina) – Medium +- (CVE-2026-48928) tls: fix case-sensitive SNI context matching (Matteo Collina) – Medium +- (CVE-2026-48930) dns,net: reject hostnames with embedded NUL bytes (Matteo Collina) – Medium +- (CVE-2026-48934) tls: bind reusable sessions to authenticated host (Matteo Collina) – Medium +- (CVE-2026-48937) deps: fix integration issues with the latest nghttp2 – Medium +- (CVE-2026-48617) permission: handle process.chdir on writereport (RafaelGSS) – Low +- (CVE-2026-48931) http: fix response queue poisoning in http.Agent (Matteo Collina) – Low +- (CVE-2026-48935) permission: disable FileHandle utimes with permission model (RafaelGSS) – Low + +### Commits + +- \[[`9e4dfc7bba`](https://github.com/nodejs/node/commit/9e4dfc7bba)] - **(CVE-2026-48933)** **crypto**: guard WebCrypto cipher output length (Filip Skokan) [nodejs-private/node-private#878](https://github.com/nodejs-private/node-private/pull/878) +- \[[`cb2aed980c`](https://github.com/nodejs/node/commit/cb2aed980c)] - **deps**: update llhttp to 9.4.2 (Antoine du Hamel) [nodejs-private/node-private#890](https://github.com/nodejs-private/node-private/pull/890) +- \[[`a8a0d12875`](https://github.com/nodejs/node/commit/a8a0d12875)] - **(CVE-2026-48937)** **deps**: fix integration issues with the latest nghttp2 (Tim Perry) [#62891](https://github.com/nodejs/node/pull/62891) +- \[[`66e6203c1c`](https://github.com/nodejs/node/commit/66e6203c1c)] - **(SEMVER-MAJOR)** **deps**: update nghttp2 to 1.69.0 (Node.js GitHub Bot) [#62891](https://github.com/nodejs/node/pull/62891) +- \[[`dd627ced27`](https://github.com/nodejs/node/commit/dd627ced27)] - **deps**: update archs files for openssl-3.5.7 (Node.js GitHub Bot) [#63820](https://github.com/nodejs/node/pull/63820) +- \[[`684bae568f`](https://github.com/nodejs/node/commit/684bae568f)] - **deps**: upgrade openssl sources to openssl-3.5.7 (Node.js GitHub Bot) [#63820](https://github.com/nodejs/node/pull/63820) +- \[[`3a631e7f83`](https://github.com/nodejs/node/commit/3a631e7f83)] - **deps**: fix aix implicit declaration in OpenSSL (Abdirahim Musse) [#62656](https://github.com/nodejs/node/pull/62656) +- \[[`cf44df3996`](https://github.com/nodejs/node/commit/cf44df3996)] - **deps**: update undici to 7.28.0 (Node.js GitHub Bot) [#63703](https://github.com/nodejs/node/pull/63703) +- \[[`138c70294b`](https://github.com/nodejs/node/commit/138c70294b)] - **(CVE-2026-48930)** **dns,net**: reject hostnames with embedded NUL bytes (Matteo Collina) [nodejs-private/node-private#868](https://github.com/nodejs-private/node-private/pull/868) +- \[[`be7e719c3f`](https://github.com/nodejs/node/commit/be7e719c3f)] - **(CVE-2026-48931)** **http**: fix response queue poisoning in http.Agent (Matteo Collina) [nodejs-private/node-private#846](https://github.com/nodejs-private/node-private/pull/846) +- \[[`cc7c11b4d1`](https://github.com/nodejs/node/commit/cc7c11b4d1)] - **(CVE-2026-48619)** **http2**: cap originSet size to prevent unbounded memory growth (Matteo Collina) [nodejs-private/node-private#855](https://github.com/nodejs-private/node-private/pull/855) +- \[[`9224427b92`](https://github.com/nodejs/node/commit/9224427b92)] - **(CVE-2026-48615)** **lib,test**: redact proxy credentials in tunnel errors (Matteo Collina) [nodejs-private/node-private#867](https://github.com/nodejs-private/node-private/pull/867) +- \[[`cf85d54839`](https://github.com/nodejs/node/commit/cf85d54839)] - **(CVE-2026-48935)** **permission**: disable FileHandle utimes with permission model (RafaelGSS) [nodejs-private/node-private#873](https://github.com/nodejs-private/node-private/pull/873) +- \[[`a1bbc24f96`](https://github.com/nodejs/node/commit/a1bbc24f96)] - **(CVE-2026-48617)** **permission**: handle process.chdir on writereport (RafaelGSS) [nodejs-private/node-private#870](https://github.com/nodejs-private/node-private/pull/870) +- \[[`e3723ff2d6`](https://github.com/nodejs/node/commit/e3723ff2d6)] - **test**: add session reuse host verification regressions (Matteo Collina) [nodejs-private/node-private#854](https://github.com/nodejs-private/node-private/pull/854) +- \[[`a77af4867b`](https://github.com/nodejs/node/commit/a77af4867b)] - **(CVE-2026-48934)** **tls**: bind reusable sessions to authenticated host (Matteo Collina) [nodejs-private/node-private#854](https://github.com/nodejs-private/node-private/pull/854) +- \[[`31beb4f707`](https://github.com/nodejs/node/commit/31beb4f707)] - **(CVE-2026-48928)** **tls**: fix case-sensitive SNI context matching (Matteo Collina) [nodejs-private/node-private#857](https://github.com/nodejs-private/node-private/pull/857) +- \[[`8e75c73f91`](https://github.com/nodejs/node/commit/8e75c73f91)] - **(CVE-2026-48618)** **tls**: normalize hostname for server identity checks (Matteo Collina) [nodejs-private/node-private#869](https://github.com/nodejs-private/node-private/pull/869) + +Windows 64-bit Installer: https://nodejs.org/dist/v24.17.0/node-v24.17.0-x64.msi \ +Windows ARM 64-bit Installer: https://nodejs.org/dist/v24.17.0/node-v24.17.0-arm64.msi \ +Windows 64-bit Binary: https://nodejs.org/dist/v24.17.0/win-x64/node.exe \ +Windows ARM 64-bit Binary: https://nodejs.org/dist/v24.17.0/win-arm64/node.exe \ +macOS 64-bit Installer: https://nodejs.org/dist/v24.17.0/node-v24.17.0.pkg \ +macOS Apple Silicon 64-bit Binary: https://nodejs.org/dist/v24.17.0/node-v24.17.0-darwin-arm64.tar.gz \ +macOS Intel 64-bit Binary: https://nodejs.org/dist/v24.17.0/node-v24.17.0-darwin-x64.tar.gz \ +Linux 64-bit Binary: https://nodejs.org/dist/v24.17.0/node-v24.17.0-linux-x64.tar.xz \ +Linux PPC LE 64-bit Binary: https://nodejs.org/dist/v24.17.0/node-v24.17.0-linux-ppc64le.tar.xz \ +Linux s390x 64-bit Binary: https://nodejs.org/dist/v24.17.0/node-v24.17.0-linux-s390x.tar.xz \ +AIX 64-bit Binary: https://nodejs.org/dist/v24.17.0/node-v24.17.0-aix-ppc64.tar.gz \ +ARMv8 64-bit Binary: https://nodejs.org/dist/v24.17.0/node-v24.17.0-linux-arm64.tar.xz \ +Source Code: https://nodejs.org/dist/v24.17.0/node-v24.17.0.tar.gz \ +Other release files: https://nodejs.org/dist/v24.17.0/ \ +Documentation: https://nodejs.org/docs/v24.17.0/api/ + +### SHASUMS + +``` +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +31e9fc249c74a3a6dfeca4758229f003620459b42c3749f7c423f2999d09a727 node-v24.17.0-aix-ppc64.tar.gz +adee7826d2840efd66cec5e79c9b4e151f4815ac0e24f92cf334bc28d7f1f83c node-v24.17.0-arm64.msi +4fc3266a3702eebc39cc37661cf4eeceeade307e242ab64e4d7ce7949197e11f node-v24.17.0-darwin-arm64.tar.gz +cf7e9152d7bd86c140f6eccf3577abfbaf8960be1ca49d9d900e8484984dcb9a node-v24.17.0-darwin-arm64.tar.xz +80da552fe037290cb130e9dea590f5eeeb7aa450636f0c89ab41415511c1ec27 node-v24.17.0-darwin-x64.tar.gz +fe50e386f6a5e0b29ce44b989e543da9fb9a80aed0b91a2f0cb19c55106921fc node-v24.17.0-darwin-x64.tar.xz +ac60c4ba92204658efaac112efea5d3597348b011be679af0eec324d8c08915e node-v24.17.0-headers.tar.gz +aab64d32cd1690e4027326e746877bdac62f0a8458215241638477cbfe0a4192 node-v24.17.0-headers.tar.xz +faa0d59ba7fe7045c950ed09b190578fb8eee73e4358686d38fcc99ca58c1480 node-v24.17.0-linux-arm64.tar.gz +67324b9e515e7d13da72571a5dd522bb23145a820f7dde15497897e466759ab3 node-v24.17.0-linux-arm64.tar.xz +804ed4a1a0ef28d592408b84ac2a85e858ab9124dad933e12b4323609411b809 node-v24.17.0-linux-ppc64le.tar.gz +7657dfb803132a05cfc83353f43f603cba790e1d2366caeb36083aa8f351124b node-v24.17.0-linux-ppc64le.tar.xz +a659e9c26fcd648f3359dbfd292f078434168040f2fb1acf3c9c1bcd3fc37b2b node-v24.17.0-linux-s390x.tar.gz +a8e6f79fac2e17e5a9a9d479bad3b6f19921049bbb5888fa238347427502f23c node-v24.17.0-linux-s390x.tar.xz +e0472427aa791ad80bdc426ff7cc73cdd28ed0f616d1ff9689a23a7f47f1265f node-v24.17.0-linux-x64.tar.gz +ab343a1b747c7cbf3630dfd7dbf818c5423fab2eb4f5ad1afc896f6bd121a917 node-v24.17.0-linux-x64.tar.xz +0ef6a68334882bb74f5afd2d370cf2cdabc3ef823c8fdd649d9d779478c09607 node-v24.17.0-win-arm64.7z +4957712f67fce55779cc794d9b4df9e0e802a18c841ad5a4e42f17be490e634d node-v24.17.0-win-arm64.zip +91382ab13fea6cfdd475fc0f5b74727c979f609a94905ae338f8b9f1cce32457 node-v24.17.0-win-x64.7z +f2aa33b35b75aca5f3f7b85675a6f6423201053e9381911e64961f3bda2528ab node-v24.17.0-win-x64.zip +ae5d9e9f6c85b8d35717f499ba907259ec80672c289858bae19074355906a240 node-v24.17.0-x64.msi +6d795ec7986972ac377bcf017eb2a4f970962f36e1584bfebe79326a194f365f node-v24.17.0.pkg +66a10e05fa7875ff1d7d669de405ea6ce8725f2352bd07550f520dea2f880825 node-v24.17.0.tar.gz +a7ab562ed2369a29c68b72fa00e3103bcdfe37063dff799c6acc8e404e275fcd node-v24.17.0.tar.xz +44999f9ec6486d01202d8961f343eac8c9f2847b234a8637c3fd0f1e2bb3288a win-arm64/node.exe +d32c3ff35f34b9593e5fcddc23ca779f4b40abfb9aa5a031d620f1ecb44ca935 win-arm64/node.lib +60c69df69e22db238ab670efb7ad57ab6da92adcea33c6eea152daf3c2182ad6 win-arm64/node_pdb.7z +93262aebc5c28f3f2218cad37a7635b9fb95ea89cb068c124b7eac9446682029 win-arm64/node_pdb.zip +c6335d08331c23d68b9f2b18adb102002d76ef150b47248e954c507e0d033664 win-x64/node.exe +4ab42af597bc4f0957e9e2dcd5db18bdf223406a0c8e0b6be0f28e57977b808b win-x64/node.lib +0e2a2937823b7fbca4d4ed344ed13d6c8a519d06460f77966fce59e1d146826b win-x64/node_pdb.7z +bd231782ef5e062395d6d2a259c3aaee994db694df9bdcd4beeb9d6bac9e69ec win-x64/node_pdb.zip + +-----BEGIN PGP SIGNATURE----- + +iHUEARYIAB0WIQRb6KP2yKXAHRBsCtggsaOQsWjTVgUCajN1TwAKCRAgsaOQsWjT +VsDcAQClE4tL8dBeOyi941MK78i7o4iOFfoYYdyIayxQww7nTgD/Zmdx1h1gjwo/ +J5fumRrmsNRDj6JYjUfEzKSSJjFX7Q8= +=nnSg +-----END PGP SIGNATURE----- +``` diff --git a/apps/site/pages/en/blog/release/v24.18.0.md b/apps/site/pages/en/blog/release/v24.18.0.md new file mode 100644 index 0000000000000..6475eb5fb2ea0 --- /dev/null +++ b/apps/site/pages/en/blog/release/v24.18.0.md @@ -0,0 +1,245 @@ +--- +date: '2026-06-23T23:12:41.150Z' +category: release +title: Node.js 24.18.0 (LTS) +layout: blog-post +author: Richard Lau +--- + +## 2026-06-23, Version 24.18.0 'Krypton' (LTS), @richardlau prepared by @sxa + +### Notable Changes + +- \[[`e07e7a31e1`](https://github.com/nodejs/node/commit/e07e7a31e1)] - **crypto**: update root certificates to NSS 3.123.1 (Node.js GitHub Bot) [#63527](https://github.com/nodejs/node/pull/63527) +- \[[`44c8ebcbd6`](https://github.com/nodejs/node/commit/44c8ebcbd6)] - **http**: avoid stream listeners on idle agent sockets (Matteo Collina) [#64004](https://github.com/nodejs/node/pull/64004) +- \[[`d3ef4122ee`](https://github.com/nodejs/node/commit/d3ef4122ee)] - **(SEMVER-MINOR)** **buffer**: increase Buffer.poolSize default to 64 KiB (Matteo Collina) [#63597](https://github.com/nodejs/node/pull/63597) +- \[[`bb2857b85a`](https://github.com/nodejs/node/commit/bb2857b85a)] - **(SEMVER-MINOR)** **crypto**: align key argument names in docs and error messages (Filip Skokan) [#62527](https://github.com/nodejs/node/pull/62527) +- \[[`b9d5e87880`](https://github.com/nodejs/node/commit/b9d5e87880)] - **(SEMVER-MINOR)** **crypto**: accept key data in crypto.diffieHellman() and cleanup DH jobs (Filip Skokan) [#62527](https://github.com/nodejs/node/pull/62527) +- \[[`ccd756d61e`](https://github.com/nodejs/node/commit/ccd756d61e)] - **(SEMVER-MINOR)** **crypto**: add TurboSHAKE and KangarooTwelve Web Cryptography algorithms (Filip Skokan) [#62183](https://github.com/nodejs/node/pull/62183) +- \[[`4c9251fc09`](https://github.com/nodejs/node/commit/4c9251fc09)] - **(SEMVER-MINOR)** **http**: add writeInformation to send arbitrary 1xx status codes (Tim Perry) [#63155](https://github.com/nodejs/node/pull/63155) +- \[[`8c989ec4a3`](https://github.com/nodejs/node/commit/8c989ec4a3)] - **(SEMVER-MINOR)** **inspector**: expose precise coverage start to JS runtime (sangwook) [#63079](https://github.com/nodejs/node/pull/63079) +- \[[`3f54c8ba32`](https://github.com/nodejs/node/commit/3f54c8ba32)] - _**Revert**_ "**stream**: noop pause/resume on destroyed streams" (Stewart X Addison) [#63834](https://github.com/nodejs/node/pull/63834) + +### Commits + +- \[[`d3ef4122ee`](https://github.com/nodejs/node/commit/d3ef4122ee)] - **(SEMVER-MINOR)** **buffer**: increase Buffer.poolSize default to 64 KiB (Matteo Collina) [#63597](https://github.com/nodejs/node/pull/63597) +- \[[`9ff36e40f0`](https://github.com/nodejs/node/commit/9ff36e40f0)] - **build**: add --enable-all-experimentals build flag (Paolo Insogna) [#62755](https://github.com/nodejs/node/pull/62755) +- \[[`7c22ee23aa`](https://github.com/nodejs/node/commit/7c22ee23aa)] - **build**: def `NODE_USE_NODE_CODE_CACHE` only used in node_mksnapshot (Chengzhong Wu) [#63588](https://github.com/nodejs/node/pull/63588) +- \[[`2551abdb4a`](https://github.com/nodejs/node/commit/2551abdb4a)] - **build,win**: enable x64 PGO (Stefan Stojanovic) [#62761](https://github.com/nodejs/node/pull/62761) +- \[[`e8a55ce9b1`](https://github.com/nodejs/node/commit/e8a55ce9b1)] - **crypto**: strengthen argument CHECKs in TurboSHAKE (Tobias Nießen) [#62763](https://github.com/nodejs/node/pull/62763) +- \[[`ae61cd68f3`](https://github.com/nodejs/node/commit/ae61cd68f3)] - **crypto**: harden WebCrypto against prototype pollution (Filip Skokan) [#63363](https://github.com/nodejs/node/pull/63363) +- \[[`3d05a1d396`](https://github.com/nodejs/node/commit/3d05a1d396)] - **crypto**: pass CryptoKey handles to KDF jobs (Filip Skokan) [#63363](https://github.com/nodejs/node/pull/63363) +- \[[`f9d10a3f6b`](https://github.com/nodejs/node/commit/f9d10a3f6b)] - **crypto**: remove async from WebCrypto methods (Filip Skokan) [#63363](https://github.com/nodejs/node/pull/63363) +- \[[`e431d93e9e`](https://github.com/nodejs/node/commit/e431d93e9e)] - **crypto**: add WebCrypto CryptoJob mode (Filip Skokan) [#63363](https://github.com/nodejs/node/pull/63363) +- \[[`56e2505e48`](https://github.com/nodejs/node/commit/56e2505e48)] - **crypto**: wire ML-DSA and ML-KEM for use when using BoringSSL (Filip Skokan) [#63255](https://github.com/nodejs/node/pull/63255) +- \[[`3bac77f2a8`](https://github.com/nodejs/node/commit/3bac77f2a8)] - **crypto**: wire ChaCha20-Poly1305 in Web Cryptography when using BoringSSL (Filip Skokan) [#63255](https://github.com/nodejs/node/pull/63255) +- \[[`1bff901b09`](https://github.com/nodejs/node/commit/1bff901b09)] - **crypto**: wire AES-KW in Web Cryptography when using BoringSSL (Filip Skokan) [#63255](https://github.com/nodejs/node/pull/63255) +- \[[`4433fca3df`](https://github.com/nodejs/node/commit/4433fca3df)] - **crypto**: harden CryptoKey algorithm slots (Filip Skokan) [#63111](https://github.com/nodejs/node/pull/63111) +- \[[`b5cf01217a`](https://github.com/nodejs/node/commit/b5cf01217a)] - **crypto**: harden KeyObject internal slots (Filip Skokan) [#63111](https://github.com/nodejs/node/pull/63111) +- \[[`ce84aef37d`](https://github.com/nodejs/node/commit/ce84aef37d)] - **crypto**: add guards and adjust tests for BoringSSL (Filip Skokan) [#62883](https://github.com/nodejs/node/pull/62883) +- \[[`26781689b0`](https://github.com/nodejs/node/commit/26781689b0)] - **crypto**: reject duplicate ML-KEM JWK key_ops (Filip Skokan) [#62905](https://github.com/nodejs/node/pull/62905) +- \[[`aeea8f4970`](https://github.com/nodejs/node/commit/aeea8f4970)] - **crypto**: add JWK support for ML-KEM and SLH-DSA key types (Filip Skokan) [#62706](https://github.com/nodejs/node/pull/62706) +- \[[`407cf91656`](https://github.com/nodejs/node/commit/407cf91656)] - **crypto**: guard against size_t overflow on experimental 32-bit arch (Filip Skokan) [#62626](https://github.com/nodejs/node/pull/62626) +- \[[`bb2857b85a`](https://github.com/nodejs/node/commit/bb2857b85a)] - **(SEMVER-MINOR)** **crypto**: align key argument names in docs and error messages (Filip Skokan) [#62527](https://github.com/nodejs/node/pull/62527) +- \[[`b9d5e87880`](https://github.com/nodejs/node/commit/b9d5e87880)] - **(SEMVER-MINOR)** **crypto**: accept key data in crypto.diffieHellman() and cleanup DH jobs (Filip Skokan) [#62527](https://github.com/nodejs/node/pull/62527) +- \[[`b46d52b283`](https://github.com/nodejs/node/commit/b46d52b283)] - **crypto**: unify asymmetric key import through KeyObjectHandle::Init (Filip Skokan) [#62499](https://github.com/nodejs/node/pull/62499) +- \[[`ccd756d61e`](https://github.com/nodejs/node/commit/ccd756d61e)] - **(SEMVER-MINOR)** **crypto**: add TurboSHAKE and KangarooTwelve Web Cryptography algorithms (Filip Skokan) [#62183](https://github.com/nodejs/node/pull/62183) +- \[[`e07e7a31e1`](https://github.com/nodejs/node/commit/e07e7a31e1)] - **crypto**: update root certificates to NSS 3.123.1 (Node.js GitHub Bot) [#63527](https://github.com/nodejs/node/pull/63527) +- \[[`61826df455`](https://github.com/nodejs/node/commit/61826df455)] - **crypto**: coerce -0 keylen to +0 in pbkdf2 and scrypt (Jordan Harband) [#63531](https://github.com/nodejs/node/pull/63531) +- \[[`16d2fd3c07`](https://github.com/nodejs/node/commit/16d2fd3c07)] - **crypto**: align verifyOneShot accepted types (Anshika Jain) [#63280](https://github.com/nodejs/node/pull/63280) +- \[[`3b8330deda`](https://github.com/nodejs/node/commit/3b8330deda)] - **crypto**: improve system certificate enumeration logic on macOS (Robo) [#62576](https://github.com/nodejs/node/pull/62576) +- \[[`141de35399`](https://github.com/nodejs/node/commit/141de35399)] - **debugger**: add --help to `node inspect` and improve docs (Joyee Cheung) [#63201](https://github.com/nodejs/node/pull/63201) +- \[[`b76bfcd4fa`](https://github.com/nodejs/node/commit/b76bfcd4fa)] - **deps**: upgrade npm to 11.16.0 (npm team) [#63602](https://github.com/nodejs/node/pull/63602) +- \[[`4ec142314c`](https://github.com/nodejs/node/commit/4ec142314c)] - **deps**: SQLite: cherry-pick b869ed6b067d623cb1383549f2a18aa35508385d (Junsu Han) [#63525](https://github.com/nodejs/node/pull/63525) +- \[[`19e8ce1c36`](https://github.com/nodejs/node/commit/19e8ce1c36)] - **deps**: upgrade npm to 11.15.0 (npm team) [#63463](https://github.com/nodejs/node/pull/63463) +- \[[`8a264260e2`](https://github.com/nodejs/node/commit/8a264260e2)] - **deps**: update sqlite to 3.53.1 (Node.js GitHub Bot) [#63217](https://github.com/nodejs/node/pull/63217) +- \[[`50c8ff3f94`](https://github.com/nodejs/node/commit/50c8ff3f94)] - **deps**: update simdjson to 4.6.4 (Node.js GitHub Bot) [#62811](https://github.com/nodejs/node/pull/62811) +- \[[`6e56f01c4b`](https://github.com/nodejs/node/commit/6e56f01c4b)] - **deps**: V8: cherry-pick 435a2cdf664c (Matthias Liedtke) [#63136](https://github.com/nodejs/node/pull/63136) +- \[[`3ba813b242`](https://github.com/nodejs/node/commit/3ba813b242)] - **deps**: cherry-pick libuv/libuv\@a43e543 (Ali Hassan) [#63222](https://github.com/nodejs/node/pull/63222) +- \[[`2390e3a5ac`](https://github.com/nodejs/node/commit/2390e3a5ac)] - **doc**: remove duplicated sentences in large-pull-requests.md (Joyee Cheung) [#63650](https://github.com/nodejs/node/pull/63650) +- \[[`52a1c18374`](https://github.com/nodejs/node/commit/52a1c18374)] - **doc**: update `git node land` instructions for security releases (Antoine du Hamel) [#63586](https://github.com/nodejs/node/pull/63586) +- \[[`3e6b4da037`](https://github.com/nodejs/node/commit/3e6b4da037)] - **doc**: drop --experimental from --permission (Rafael Gonzaga) [#63583](https://github.com/nodejs/node/pull/63583) +- \[[`84d05163b9`](https://github.com/nodejs/node/commit/84d05163b9)] - **doc**: explicitly ask for reproducible in JS (Rafael Gonzaga) [#63479](https://github.com/nodejs/node/pull/63479) +- \[[`7da2a4450e`](https://github.com/nodejs/node/commit/7da2a4450e)] - **doc**: fix URL postMessage example in worker_threads (Kit Dallege) [#62203](https://github.com/nodejs/node/pull/62203) +- \[[`3d79bd8b29`](https://github.com/nodejs/node/commit/3d79bd8b29)] - **doc**: clarify `filter` option of `sqlite.database.applyChangeset` (Antoine du Hamel) [#63515](https://github.com/nodejs/node/pull/63515) +- \[[`4f4174aace`](https://github.com/nodejs/node/commit/4f4174aace)] - **doc**: fix double spaces in ERR_TLS_INVALID_PROTOCOL_METHOD (Daijiro Wachi) [#63511](https://github.com/nodejs/node/pull/63511) +- \[[`388323ca4b`](https://github.com/nodejs/node/commit/388323ca4b)] - **doc**: fix double space in modules.md (Daijiro Wachi) [#63512](https://github.com/nodejs/node/pull/63512) +- \[[`5258ccc058`](https://github.com/nodejs/node/commit/5258ccc058)] - **doc**: fix "options" to "option" in tls.createServer (Daijiro Wachi) [#63453](https://github.com/nodejs/node/pull/63453) +- \[[`43e83e6507`](https://github.com/nodejs/node/commit/43e83e6507)] - **doc**: fix typo in deprecations (Daijiro Wachi) [#63434](https://github.com/nodejs/node/pull/63434) +- \[[`f05a61d54c`](https://github.com/nodejs/node/commit/f05a61d54c)] - **doc**: remove unsupported template type from v8.md (René) [#63410](https://github.com/nodejs/node/pull/63410) +- \[[`c39d5fc820`](https://github.com/nodejs/node/commit/c39d5fc820)] - **doc**: fix article usage before vowel-sound acronyms (joao-oliveira-softtor) [#62696](https://github.com/nodejs/node/pull/62696) +- \[[`398261f911`](https://github.com/nodejs/node/commit/398261f911)] - **doc**: remove the bi-monthly contributor spotlight section (Claudio Wunder) [#62734](https://github.com/nodejs/node/pull/62734) +- \[[`fd9e14c405`](https://github.com/nodejs/node/commit/fd9e14c405)] - **doc**: update http2's `push` and `trailers` events with `rawHeaders` param (YuSheng Chen) [#63259](https://github.com/nodejs/node/pull/63259) +- \[[`b943ce6933`](https://github.com/nodejs/node/commit/b943ce6933)] - **doc**: remove inactive members from Triagers list (Antoine du Hamel) [#63329](https://github.com/nodejs/node/pull/63329) +- \[[`4b9cdfc022`](https://github.com/nodejs/node/commit/4b9cdfc022)] - **doc**: reference correct function in Module docs (Robin Malfait) [#63247](https://github.com/nodejs/node/pull/63247) +- \[[`bed84b6df2`](https://github.com/nodejs/node/commit/bed84b6df2)] - **doc**: replace Visual Studio 2022 Evergreen version reference with 17.14 (Mike McCready) [#63211](https://github.com/nodejs/node/pull/63211) +- \[[`32ea70569b`](https://github.com/nodejs/node/commit/32ea70569b)] - **doc**: recommend explicitly Tier 1 or 2 for production applications (Mike McCready) [#63187](https://github.com/nodejs/node/pull/63187) +- \[[`4627bcfd82`](https://github.com/nodejs/node/commit/4627bcfd82)] - **doc**: run license-builder (github-actions\[bot]) [#63232](https://github.com/nodejs/node/pull/63232) +- \[[`28eba71845`](https://github.com/nodejs/node/commit/28eba71845)] - **doc**: add large pull requests contributing guide (Matteo Collina) [#62829](https://github.com/nodejs/node/pull/62829) +- \[[`2648efd438`](https://github.com/nodejs/node/commit/2648efd438)] - **doc**: remove unnecessary ` B[Pull Request] - B --> C[CI Tests Pass] - C --> D[Merge to Main] - D --> E[Merge Queue] - E --> F[Auto Publish Trigger] - F --> G[Package Analysis] - G --> H[Version Bump] - H --> I[npm publish] + A[Package change and changeset] --> B[Pull request] + B --> C[Merge queue] + C --> D[Merge to main] + D --> E[Create or update Version Packages PR] + E --> F[Review and merge version PR] + F --> G[Build release artifacts] + G --> H[Publish to npm] ``` diff --git a/docs/partners.md b/docs/partners.md index 53effb4e624a3..9c06a62f55ee2 100644 --- a/docs/partners.md +++ b/docs/partners.md @@ -49,9 +49,6 @@ The same randomization algorithm used on the homepage is applied here as well. ## Supporters -> [!NOTE] -> Donations made through GitHub Sponsors are not displayed yet; they will be added soon. - Supporters are individuals and organizations that provide financial support to the Node.js project through OpenCollective and GitHub Sponsors. They are displayed on the Partner page, but not on the homepage or Downloads section. ## Partner Randomization Algorithm diff --git a/docs/technologies.md b/docs/technologies.md index e54940426bc72..c2070160578f2 100644 --- a/docs/technologies.md +++ b/docs/technologies.md @@ -293,14 +293,14 @@ Benefits: #### Multiple Build Targets -- **`pnpm build`**: Production build for Vercel +- **`pnpm build`**: Production build for the default platform - **`pnpm deploy`**: Export build for legacy servers - **`pnpm dev`**: Development server #### Vercel Integration - Automatic deployments for branches (ignoring automated branches) -- Custom install + ignore scripts ([see `vercel.json`](../apps/site/vercel.json)) +- Custom install + ignore scripts ([see `vercel.json`](../platforms/vercel/vercel.json)) - Build-time dependencies must be in `dependencies`, not `devDependencies` - Sponsorship maintained by OpenJS Foundation diff --git a/package.json b/package.json index c28dac27c87ba..23033abda224e 100644 --- a/package.json +++ b/package.json @@ -13,11 +13,10 @@ "name": "Node.js Website Team and Contributors" }, "scripts": { - "compile": "turbo compile", - "build": "turbo build", - "cloudflare:deploy": "turbo cloudflare:deploy", - "cloudflare:preview": "turbo cloudflare:preview", - "deploy": "turbo deploy", + "build": "turbo build --filter=@node-core/website", + "changeset": "changeset", + "changeset:version": "changeset version", + "deploy": "turbo deploy --filter=@node-core/website", "dev": "turbo dev", "format": "turbo //#prettier:fix prettier:fix lint:fix", "lint": "turbo lint", @@ -27,6 +26,7 @@ "prepare": "husky", "prettier": "prettier \"**/*.{js,mjs,ts,tsx,md,mdx,json,yml,css}\" --check --cache --cache-strategy=content --cache-location=.prettiercache", "prettier:fix": "node --run prettier -- --write", + "release": "pnpm --filter './packages/*' --if-present run build && pnpm publish -r --access=public", "scripts:release-post": "turbo scripts:release-post", "start": "turbo start", "storybook": "turbo storybook", @@ -36,24 +36,26 @@ }, "dependencies": { "husky": "9.1.7", - "lint-staged": "16.4.0", - "turbo": "2.9.6" + "lint-staged": "17.2.0", + "turbo": "2.10.6" }, "devDependencies": { + "@changesets/changelog-github": "^0.7.0", + "@changesets/cli": "^2.31.1", "@eslint/js": "~10.0.1", - "@reporters/github": "^1.12.0", + "@reporters/github": "^2.0.2", "@testing-library/react": "~16.3.2", "cross-env": "^10.1.0", - "eslint": "~10.1.0", - "eslint-import-resolver-typescript": "~4.4.4", - "eslint-plugin-import-x": "~4.16.2", - "globals": "^17.4.0", - "prettier": "3.8.1", - "prettier-plugin-tailwindcss": "0.7.2", + "eslint": "~10.8.0", + "eslint-import-resolver-typescript": "~4.4.5", + "eslint-plugin-import-x": "~4.17.1", + "globals": "^17.7.0", + "prettier": "3.9.6", + "prettier-plugin-tailwindcss": "0.8.1", "typescript": "catalog:", - "typescript-eslint": "~8.57.2" + "typescript-eslint": "~8.65.0" }, - "packageManager": "pnpm@10.33.0", + "packageManager": "pnpm@11.18.0", "devEngines": { "runtime": { "name": "node", @@ -62,7 +64,7 @@ }, "packageManager": { "name": "pnpm", - "version": "10.33.0", + "version": "11.18.0", "onFail": "error" } } diff --git a/packages/i18n/CHANGELOG.md b/packages/i18n/CHANGELOG.md new file mode 100644 index 0000000000000..adebf4bc6538c --- /dev/null +++ b/packages/i18n/CHANGELOG.md @@ -0,0 +1,13 @@ +# @node-core/website-i18n + +## 1.1.34 + +### Patch Changes + +- [#9053](https://github.com/nodejs/nodejs.org/pull/9053) [`5bbc791`](https://github.com/nodejs/nodejs.org/commit/5bbc791b4ee5783e629b3c2da1a91d516f8b5871) Thanks [@nodejs-crowdin](https://github.com/nodejs-crowdin)! - Update translations from Crowdin. + +## 1.1.33 + +### Patch Changes + +- [#9020](https://github.com/nodejs/nodejs.org/pull/9020) [`0492dea`](https://github.com/nodejs/nodejs.org/commit/0492dea85e2a7096cf89e26c1e24d1c41b7d595f) Thanks [@avivkeller](https://github.com/avivkeller)! - Migrate package versioning and publishing to Changesets. diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 2ab4740301818..48ca01f1ebc39 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@node-core/website-i18n", - "version": "1.1.28", + "version": "1.1.34", "type": "module", "exports": { "./*": [ diff --git a/packages/i18n/src/locales/ar.json b/packages/i18n/src/locales/ar.json new file mode 100644 index 0000000000000..0ce0acd299dd0 --- /dev/null +++ b/packages/i18n/src/locales/ar.json @@ -0,0 +1,310 @@ +{ + "components": { + "header": { + "buttons": { + "theme": "اختر السمة", + "themeSystem": "النظام", + "themeLightMode": "فاتح", + "themeDarkMode": "داكن" + } + }, + "containers": { + "footer": { + "legal": "حقوق النشر محفوظة لـ OpenJS Foundation ومساهمي Node.js. جميع الحقوق محفوظة. لدى OpenJS Foundation علامات تجارية مسجلة وتستخدم علامات تجارية. للاطلاع على قائمة العلامات التجارية الخاصة بـ OpenJS Foundation، راجع سياسة العلامات التجارية وقائمة العلامات التجارية. العلامات التجارية والشعارات غير المذكورة في قائمة العلامات التجارية الخاصة بـ OpenJS Foundation هي علامات تجارية™ أو علامات تجارية مسجلة® لمالكيها. لا يعني استخدامها وجود أي ارتباط بها أو تأييد منها.", + "links": { + "foundationName": "OpenJS Foundation", + "aiCodingAssistantsPolicy": "سياسة مساعدي البرمجة بالذكاء الاصطناعي (AI Coding Assistants)", + "termsOfUse": "شروط الاستخدام", + "privacyPolicy": "سياسة الخصوصية", + "bylaws": "اللوائح الداخلية", + "codeOfConduct": "مدونة قواعد السلوك", + "trademarkPolicy": "سياسة العلامات التجارية", + "trademarkList": "قائمة العلامات التجارية", + "cookiePolicy": "سياسة ملفات تعريف الارتباط", + "security": "سياسة الأمان" + }, + "releasePills": { + "latestLTS": "أحدث LTS", + "latestRelease": "أحدث إصدار" + } + }, + "navBar": { + "controls": { + "toggle": "تبديل قائمة التنقل" + }, + "links": { + "about": "حول", + "download": "تنزيل", + "docs": "التوثيق", + "guides": "الأدلة", + "learn": "تعلّم", + "security": "الأمان", + "certification": "الدورات", + "blog": "المدونة", + "contribute": "المساهمة" + } + } + }, + "navigation": { + "about": { + "links": { + "about": "حول Node.js", + "aboutSide": "حول Node.js®", + "branding": "هوية Node.js", + "governance": "حوكمة المشروع", + "releases": "إصدارات Node.js", + "security": "الإبلاغ الأمني", + "partners": "الشركاء والداعمون", + "eol": "نهاية الدعم (EOL)" + } + }, + "getInvolved": { + "links": { + "getInvolved": "شارك معنا", + "collabSummit": "القمة التعاونية", + "upcomingEvents": "الأحداث القادمة", + "contribute": "المساهمة في Node.js", + "codeOfConduct": "مدونة قواعد السلوك" + } + } + }, + "downloadReleasesTable": { + "version": "Node.js", + "nApiVersion": "N-API", + "codename": "الاسم الرمزي", + "releaseDate": "تاريخ الإصدار", + "firstReleased": "أول إصدار", + "lastUpdated": "آخر تحديث", + "status": "الحالة", + "details": "التفاصيل" + }, + "downloadsTable": { + "fileName": "اسم الملف", + "operatingSystem": "نظام التشغيل", + "architecture": "المعمارية" + }, + "releaseModal": { + "title": "Node.js v{version} ({codename})", + "titleWithoutCodename": "Node.js v{version}", + "overview": "نظرة عامة", + "minorVersions": "الإصدارات الفرعية", + "releaseAnnouncement": "إعلان الإصدار", + "unsupportedVersionWarning": "هذا الإصدار خارج مرحلة الصيانة. يرجى استخدام إصدار مدعوم. تعرّف على دعم EOL.", + "ltsVersionFeaturesNotice": "هل تريد الميزات الجديدة في وقت أقرب؟ استخدم أحدث إصدار من Node.js بدلًا من ذلك وجرب أحدث التحسينات!" + }, + "eolAlert": { + "message": "يتوفر الدعم التجاري للإصدارات التي تجاوزت مرحلة الصيانة LTS عبر شركائنا في OpenJS Ecosystem Sustainability Program" + }, + "eolChip": { + "severity": { + "unknown": "غير معروف", + "low": "منخفض", + "medium": "متوسط", + "high": "عالٍ", + "critical": "حرج" + } + }, + "eolModal": { + "title": "وصل Node.js v{version} ({codename}) إلى EOL", + "titleWithoutCodename": "وصل Node.js v{version} إلى EOL", + "vulnerabilitiesMessage": "توجد {count}+ مشكلة أمان معروفة (CVEs) مرتبطة بهذا الإصدار من Node.js. تُستخدم CVEs (Common Vulnerabilities and Exposures) كمعرّفات للثغرات الأمنية المبلغ عنها علنًا. عند الضغط على رابط CVE ستنتقل إلى تفاصيل تقنية أكثر، مثل طريقة عمل الثغرة.", + "noVulnerabilitiesMessage": "لا توجد حاليًا CVEs (Common Vulnerabilities and Exposures) معروفة مرتبطة بهذا الإصدار من Node.js. لكن هذا لا يعني أنه آمن بالكامل، فقد توجد ثغرات لم تُكتشف بعد أو لم يُفصح عنها علنًا. إذا كان هذا الإصدار قديمًا أو غير مدعوم، فمن الأفضل التفكير في الترقية للاستفادة من أحدث الإصلاحات والتحسينات الأمنية.", + "blogLinkText": "المدونة", + "showUnknownSeverities": "إظهار الثغرات ذات الخطورة غير المعروفة", + "table": { + "cves": "CVE(s)", + "severity": "الخطورة", + "overview": "نظرة عامة", + "details": "التفاصيل" + } + }, + "eolTable": { + "version": "الإصدار", + "codename": "الاسم الرمزي", + "releaseDate": "تاريخ الإصدار", + "lastUpdated": "آخر تحديث", + "vulnerabilities": "الثغرات", + "details": "التفاصيل", + "hideNonLts": "إخفاء الإصدارات غير LTS" + }, + "minorReleasesTable": { + "version": "الإصدار", + "links": "الروابط", + "nApiVersion": "إصدار N-API", + "npmVersion": "إصدار npm", + "v8Version": "إصدار V8", + "actions": { + "release": "الإصدار", + "changelog": "سجل التغييرات", + "docs": "التوثيق" + } + }, + "releaseOverview": { + "firstReleased": "أول إصدار", + "lastUpdated": "آخر تحديث", + "minorVersions": "الإصدارات الفرعية", + "nApiVersion": "إصدار N-API", + "npmVersion": "إصدار npm", + "v8Version": "إصدار V8" + }, + "common": { + "alertBox": { + "info": "معلومة", + "warning": "تحذير", + "danger": "خطر" + }, + "breadcrumbs": { + "navigateToHome": "الانتقال إلى الصفحة الرئيسية" + }, + "codebox": { + "copy": "نسخ إلى الحافظة", + "copied": "تم النسخ إلى الحافظة!" + }, + "pagination": { + "previous": "السابق", + "previousAriaLabel": "الصفحة السابقة", + "next": "التالي", + "nextAriaLabel": "الصفحة التالية", + "defaultLabel": "ترقيم الصفحات", + "pageLabel": "الانتقال إلى الصفحة {pageNumber}" + }, + "sidebar": { + "title": "تغيير الصفحة" + }, + "languageDropdown": { + "label": "اختر اللغة" + }, + "skipToContent": "تخطي إلى المحتوى" + }, + "metabar": { + "lastUpdated": "آخر تحديث", + "readingTime": "مدة القراءة", + "addedIn": "أضيف في", + "author": "المؤلف", + "authors": "المؤلفون", + "contribute": "المساهمة", + "contributeText": "عدّل هذه الصفحة", + "viewAs": "عرض كـ", + "tableOfContents": "جدول المحتويات", + "metadata": "بيانات المقال" + }, + "banner": { + "default": "إعلان", + "warning": "إشعار تحذيري", + "error": "إشعار خطأ", + "close": "إغلاق" + }, + "search": { + "searchPlaceholder": "ابدأ الكتابة...", + "chatPlaceholder": "اسألني عن أي شيء", + "noResultsFoundFor": "لم يتم العثور على نتائج لـ", + "suggestions": "اقتراحات", + "seeAll": "عرض الكل", + "addMore": "إضافة المزيد", + "clearChat": "مسح المحادثة", + "errorMessage": "حدث خطأ أثناء محاولة البحث. يرجى المحاولة مرة أخرى.", + "disclaimer": "قد تخطئ ملخصات AI. يرجى التحقق من المعلومات.", + "startYourSearch": "ابدأ البحث", + "initErrorSearch": "تعذّر تهيئة خدمة البحث", + "initErrorChat": "تعذّر تهيئة خدمة الدردشة", + "chatButtonLabel": "الحصول على ملخص AI", + "searchButtonLabel": "بحث", + "poweredBy": "مدعوم من", + "suggestionOne": "كيف أثبّت Node.js؟", + "suggestionTwo": "كيف أنشئ خادم HTTP؟", + "suggestionThree": "ترقية إصدار Node.js", + "scrollToBottom": "التمرير إلى الأسفل", + "closeChat": "إغلاق الدردشة", + "keyboardShortcuts": { + "select": "للاختيار", + "navigate": "للتنقل", + "close": "للإغلاق" + } + }, + "blog": { + "blogHeader": { + "subtitle": "آخر أخبار Node.js وأدلة الترحيل وملخصات الأحداث", + "rssLink": "تغذية RSS" + } + } + }, + "layouts": { + "blogPost": { + "author": { + "byLine": "{author, select, null {} other {بواسطة {author}، }}" + } + }, + "blogIndex": { + "categoryName": "{category, select, all {المدونة} other {منشورات مدونة {category}}}" + }, + "blog": { + "title": "المدونة", + "selectCategory": "التصنيفات", + "categories": { + "all": "الكل", + "announcements": "الإعلانات", + "release": "الإصدارات", + "vulnerability": "الثغرات", + "advisory-board": "المجلس الاستشاري", + "community": "المجتمع", + "feature": "ميزة", + "module": "الوحدات", + "npm": "npm", + "uncategorized": "غير مصنف", + "video": "فيديو", + "weekly": "التحديثات الأسبوعية", + "wg": "مجموعات العمل", + "migrations": "أدلة الترحيل", + "events": "الأحداث" + } + }, + "error": { + "notFound": { + "title": "تعذّر العثور على الصفحة", + "description": "عذرًا، لم نتمكن من العثور على الصفحة التي تبحث عنها. حاول البدء من الصفحة الرئيسية." + }, + "internalServerError": { + "title": "خطأ داخلي في الخادم", + "description": "حدث في هذه الصفحة خطأ لا يمكن التعافي منه." + }, + "backToHome": "العودة إلى الصفحة الرئيسية" + }, + "download": { + "buttons": { + "installer": "مثبّت {os} (.{extension})", + "binary": "ملف ثنائي مستقل (.{extension})" + }, + "dropdown": { + "platform": "المنصة", + "os": "نظام التشغيل", + "version": "الإصدار", + "installMethod": "طريقة التثبيت", + "packageManager": "مدير الحزم", + "unknown": "غير معروف", + "platformGroups": { + "official": "موصى به (رسمي)", + "unofficial": "المجتمع (غير رسمي)" + } + }, + "codeBox": { + "unsupportedVersionWarning": "هذا الإصدار خارج مرحلة الصيانة. يرجى استخدام إصدار مدعوم حاليًا. تعرّف على دعم EOL.", + "ltsVersionFeaturesNotice": "هل تريد الميزات الجديدة في وقت أقرب؟ استخدم أحدث إصدار من Node.js بدلًا من ذلك وجرب أحدث التحسينات!", + "communityPlatformInfo": "طرق التثبيت التي تعتمد على برمجيات المجتمع تدعمها الفرق المسؤولة عن صيانة تلك البرمجيات.", + "externalSupportInfo": "إذا واجهت أي مشاكل، يرجى زيارة موقع {platform}", + "noScriptDetected": "تتطلب هذه الصفحة JavaScript. يمكنك تنزيل Node.js بدون JavaScript عبر زيارة صفحة أرشيف التنزيلات مباشرة.", + "platformInfo": { + "default": "لا تتم صيانة {platform} وسكريبتات التثبيت الخاصة به من قبل مشروع Node.js.", + "nvm": "\"nvm\" هو مدير إصدارات Node.js متعدد المنصات.", + "fnm": "\"fnm\" هو مدير إصدارات Node.js متعدد المنصات.", + "brew": "Homebrew هو مدير حزم لـ macOS وLinux.", + "choco": "Chocolatey هو مدير حزم لـ Windows.", + "docker": "Docker هو منصة للحاويات.", + "n": "\"n\" هو مدير إصدارات Node.js متعدد المنصات.", + "asdf": "\"asdf\" هو مدير إصدارات يعمل على عدة أنظمة تشغيل ويدعم عدة لغات برمجية.", + "volta": "\"Volta\" هو مدير إصدارات Node.js متعدد المنصات." + } + } + }, + "logo": "شعار Node.js" + } +} diff --git a/packages/i18n/src/locales/en.json b/packages/i18n/src/locales/en.json index d177c91c3a4ef..72694ac172947 100644 --- a/packages/i18n/src/locales/en.json +++ b/packages/i18n/src/locales/en.json @@ -36,6 +36,7 @@ "about": "About", "download": "Download", "docs": "Docs", + "betaDocs": "Beta Docs", "guides": "Guides", "learn": "Learn", "security": "Security", @@ -191,7 +192,8 @@ "banner": { "default": "Announcement", "warning": "Warning notification", - "error": "Error notification" + "error": "Error notification", + "close": "Close banner" }, "search": { "searchPlaceholder": "Start typing...", @@ -299,6 +301,7 @@ "choco": "Chocolatey is a package manager for Windows.", "docker": "Docker is a containerization platform.", "n": "\"n\" is a cross-platform Node.js version manager.", + "asdf": "\"asdf\" is a cross-platform version manager that supports multiple languages.", "volta": "\"Volta\" is a cross-platform Node.js version manager." } } diff --git a/packages/i18n/src/locales/fr.json b/packages/i18n/src/locales/fr.json index e6b09e4acbf3a..f26be8dd53f94 100644 --- a/packages/i18n/src/locales/fr.json +++ b/packages/i18n/src/locales/fr.json @@ -299,6 +299,7 @@ "choco": "Chocolatey est un gestionnaire de paquets pour Windows.", "docker": "Docker est une plateforme de conteneurisation.", "n": "\"n\" est un gestionnaire de versions Node.js multiplateforme.", + "asdf": "\"asdf\" est un gestionnaire de versions multiplateforme qui prend en charge plusieurs langages.", "volta": "\"Volta\" est un gestionnaire de versions Node.js multiplateforme." } } diff --git a/packages/i18n/src/locales/ja.json b/packages/i18n/src/locales/ja.json index 631b7f673ecd3..a55e9579379e3 100644 --- a/packages/i18n/src/locales/ja.json +++ b/packages/i18n/src/locales/ja.json @@ -36,6 +36,7 @@ "about": "はじめに", "download": "ダウンロード", "docs": "ドキュメント", + "betaDocs": "ベータ版ドキュメント", "guides": "ガイド", "learn": "学ぶ", "security": "セキュリティー", @@ -191,7 +192,8 @@ "banner": { "default": "お知らせ", "warning": "警告通知", - "error": "エラー通知" + "error": "エラー通知", + "close": "バナーを閉じる" }, "search": { "searchPlaceholder": "入力を開始...", @@ -299,6 +301,7 @@ "choco": "ChocolateyはWIndows用のパッケージマネージャーです。", "docker": "Dockerはコンテナー化に関するプラットフォームです。", "n": "\"n\"はクロスプラットフォームに対応したNode.jsのバージョンマネージャーです。", + "asdf": "「asdf」は数多くのプログラミング言語に対応したクロスプラットフォームのバージョン管理ツールです。", "volta": "\"Volta\"はクロスプラットフォームに対応したNode.jsのバージョンマネージャーです。" } } diff --git a/packages/i18n/src/locales/uk.json b/packages/i18n/src/locales/uk.json index 374fb55518c18..cb7f8dde142f7 100644 --- a/packages/i18n/src/locales/uk.json +++ b/packages/i18n/src/locales/uk.json @@ -13,6 +13,7 @@ "legal": "Авторські права OpenJS Foundation та учасників спільноти Node.js. Усі права захищено. OpenJS Foundation має зареєстровані торговельні марки та використовує торговельні марки. Аби побачити список торговельних марок OpenJS Foundation, перегляньте нашу політику торговельних марок та список торговельних марок. Торговельні марки та логотипи, не зазначені в списку торговельних марок OpenJS Foundation, є торговельними марками™ або зареєстрованими® торговельними марками їхніх відповідних власників. Їхнє використання не означає певну приналежність чи схвалення ними.", "links": { "foundationName": "OpenJS Foundation", + "aiCodingAssistantsPolicy": "Політика щодо ШІ-асистентів з кодування", "termsOfUse": "Умови використання", "privacyPolicy": "Політика конфіденційності", "bylaws": "Статути", @@ -35,6 +36,7 @@ "about": "Про Node.js", "download": "Завантажити", "docs": "Документація", + "betaDocs": "Бета-документація", "guides": "Посібники", "learn": "Навчання", "security": "Безпека", @@ -190,7 +192,8 @@ "banner": { "default": "Оголошення", "warning": "Попередження", - "error": "Помилка" + "error": "Помилка", + "close": "Закрити банер" }, "search": { "searchPlaceholder": "Уведіть щось...", @@ -298,6 +301,7 @@ "choco": "Chocolatey — це менеджер пакетів для Windows.", "docker": "Docker — це платформа контейнеризації.", "n": "«n» — це кросплатформний менеджер версій Node.js.", + "asdf": "«asdf» — це кросплатформний менеджер версій, який підтримує багато мов.", "volta": "«Volta» — це кросплатформний менеджер версій Node.js." } } diff --git a/packages/rehype-shiki/CHANGELOG.md b/packages/rehype-shiki/CHANGELOG.md new file mode 100644 index 0000000000000..b7ed9d91622ee --- /dev/null +++ b/packages/rehype-shiki/CHANGELOG.md @@ -0,0 +1,9 @@ +# @node-core/rehype-shiki + +## 1.4.3 + +### Patch Changes + +- [#9020](https://github.com/nodejs/nodejs.org/pull/9020) [`0492dea`](https://github.com/nodejs/nodejs.org/commit/0492dea85e2a7096cf89e26c1e24d1c41b7d595f) Thanks [@avivkeller](https://github.com/avivkeller)! - Migrate package versioning and publishing to Changesets. + +- [#9031](https://github.com/nodejs/nodejs.org/pull/9031) [`07a6844`](https://github.com/nodejs/nodejs.org/commit/07a684424a5895793899c20366bd9b4a0426be6b) Thanks [@avivkeller](https://github.com/avivkeller)! - Add light mode theming to code elements diff --git a/packages/rehype-shiki/package.json b/packages/rehype-shiki/package.json index 9781a3386b634..eff1ae6e5abf6 100644 --- a/packages/rehype-shiki/package.json +++ b/packages/rehype-shiki/package.json @@ -1,6 +1,6 @@ { "name": "@node-core/rehype-shiki", - "version": "1.4.1", + "version": "1.4.3", "type": "module", "types": "./dist/index.d.mts", "exports": { @@ -20,9 +20,8 @@ "directory": "packages/rehype-shiki" }, "scripts": { - "compile:ts": "tsc", - "compile": "node --run compile:ts", - "release": "node --run compile", + "build:ts": "tsc", + "build": "node --run build:ts", "lint": "node --run lint:js", "lint:fix": "node --run lint:js:fix", "lint:js": "eslint \"**/*.mjs\"", @@ -32,13 +31,13 @@ "test:unit": "cross-env NODE_NO_WARNINGS=1 node --experimental-test-coverage --experimental-test-module-mocks --test \"**/*.test.mjs\"" }, "dependencies": { - "@shikijs/core": "^4.0.2", - "@shikijs/engine-javascript": "^4.0.2", - "@shikijs/engine-oniguruma": "^4.0.2", - "@shikijs/twoslash": "^4.0.2", + "@shikijs/core": "^4.3.1", + "@shikijs/engine-javascript": "^4.3.1", + "@shikijs/engine-oniguruma": "^4.3.1", + "@shikijs/twoslash": "^4.3.1", "classnames": "catalog:", "hast-util-to-string": "^3.0.1", - "shiki": "~4.0.2", + "shiki": "~4.3.1", "typescript": "catalog:", "unist-util-visit": "^5.1.0" }, diff --git a/packages/rehype-shiki/src/__tests__/highlighter.test.mjs b/packages/rehype-shiki/src/__tests__/highlighter.test.mjs index b128b7c68a498..49479bedc367f 100644 --- a/packages/rehype-shiki/src/__tests__/highlighter.test.mjs +++ b/packages/rehype-shiki/src/__tests__/highlighter.test.mjs @@ -5,10 +5,16 @@ import { describe, it, mock } from 'node:test'; const mockShiki = { codeToHtml: mock.fn(() => '
highlighted code
'), codeToHast: mock.fn(() => ({ type: 'element', tagName: 'pre' })), + getLoadedLanguages: mock.fn(() => ['javascript', 'js']), }; +const SPECIAL_LANGS = ['text', 'plaintext', 'txt', 'ansi']; + mock.module('@shikijs/core', { - namedExports: { createHighlighterCoreSync: () => mockShiki }, + namedExports: { + createHighlighterCoreSync: () => mockShiki, + isSpecialLang: lang => SPECIAL_LANGS.includes(lang), + }, }); mock.module('@shikijs/engine-javascript', { @@ -19,9 +25,39 @@ mock.module('shiki/themes/nord.mjs', { defaultExport: { name: 'nord', colors: { 'editor.background': '#2e3440' } }, }); +mock.module('shiki/themes/github-light-default.mjs', { + defaultExport: { + name: 'github-light-default', + colors: { 'editor.background': '#fff' }, + }, +}); + describe('createHighlighter', async () => { const { default: createHighlighter } = await import('../highlighter.mjs'); + describe('resolveLanguage', () => { + it('returns the language when it is loaded', () => { + const highlighter = createHighlighter({}); + + assert.strictEqual( + highlighter.resolveLanguage('javascript'), + 'javascript' + ); + }); + + it('returns the language when it is a special language', () => { + const highlighter = createHighlighter({}); + + assert.strictEqual(highlighter.resolveLanguage('plaintext'), 'plaintext'); + }); + + it('falls back to text for unknown languages', () => { + const highlighter = createHighlighter({}); + + assert.strictEqual(highlighter.resolveLanguage('unknown'), 'text'); + }); + }); + describe('highlightToHtml', () => { it('extracts inner HTML from code tag', () => { mockShiki.codeToHtml.mock.mockImplementationOnce( @@ -33,6 +69,23 @@ describe('createHighlighter', async () => { assert.strictEqual(result, 'const x = 1;'); }); + + it('falls back to text for unknown languages', () => { + const highlighter = createHighlighter({}); + highlighter.highlightToHtml('code', 'not-a-language'); + + const [, options] = mockShiki.codeToHtml.mock.calls.at(-1).arguments; + assert.strictEqual(options.lang, 'text'); + }); + + it('emits light and dark syntax colors with light as the default', () => { + const highlighter = createHighlighter({}); + highlighter.highlightToHtml('const x = 1;', 'javascript'); + + const [, options] = mockShiki.codeToHtml.mock.calls.at(-1).arguments; + assert.deepStrictEqual(Object.keys(options.themes), ['light', 'dark']); + assert.strictEqual(options.defaultColor, 'light'); + }); }); describe('highlightToHast', () => { @@ -45,5 +98,13 @@ describe('createHighlighter', async () => { assert.deepStrictEqual(result, expectedHast); }); + + it('falls back to text for unknown languages', () => { + const highlighter = createHighlighter({}); + highlighter.highlightToHast('code', 'not-a-language'); + + const [, options] = mockShiki.codeToHast.mock.calls.at(-1).arguments; + assert.strictEqual(options.lang, 'text'); + }); }); }); diff --git a/packages/rehype-shiki/src/highlighter.mjs b/packages/rehype-shiki/src/highlighter.mjs index 56c5d5e11eb29..52920e33cbba0 100644 --- a/packages/rehype-shiki/src/highlighter.mjs +++ b/packages/rehype-shiki/src/highlighter.mjs @@ -1,14 +1,25 @@ -import { createHighlighterCoreSync } from '@shikijs/core'; +import { createHighlighterCoreSync, isSpecialLang } from '@shikijs/core'; +import shikiGitHubLightTheme from 'shiki/themes/github-light-default.mjs'; import shikiNordTheme from 'shiki/themes/nord.mjs'; -const DEFAULT_THEME = { +const DEFAULT_DARK_THEME = { // We are updating this color because the background color and comment text color // in the Codebox component do not comply with accessibility standards. // See: https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html - colorReplacements: { '#616e88': '#707e99' }, ...shikiNordTheme, + colorReplacements: { + ...shikiNordTheme.colorReplacements, + '#616e88': '#707e99', + }, }; +const DEFAULT_THEMES = { + light: shikiGitHubLightTheme, + dark: DEFAULT_DARK_THEME, +}; + +const FALLBACK_LANGUAGE = 'text'; + /** * @template {{ name: string; aliases?: string[] }} T * @param {string} language @@ -17,7 +28,6 @@ const DEFAULT_THEME = { */ export const getLanguageByName = (language, langs) => { const normalized = language.toLowerCase(); - return langs.find( ({ name, aliases }) => name.toLowerCase() === normalized || aliases?.includes(normalized) @@ -27,6 +37,7 @@ export const getLanguageByName = (language, langs) => { /** * @typedef {Object} SyntaxHighlighter * @property {import('@shikijs/core').HighlighterCore} shiki - The underlying shiki core instance. + * @property {(languageId?: string) => string} resolveLanguage - Resolves a language id to a loaded language, falling back to plain text. * @property {(code: string, lang: string, meta?: Record) => string} highlightToHtml - Highlights code and returns inner HTML of the tag. * @property {(code: string, lang: string, meta?: Record) => any} highlightToHast - Highlights code and returns a HAST tree. */ @@ -40,14 +51,43 @@ export const getLanguageByName = (language, langs) => { * @returns {SyntaxHighlighter} */ const createHighlighter = ({ coreOptions = {}, highlighterOptions = {} }) => { + const usesCustomTheme = + 'theme' in highlighterOptions || 'themes' in highlighterOptions; const options = { - themes: [DEFAULT_THEME], + themes: Object.values(DEFAULT_THEMES), ...coreOptions, }; - const shiki = createHighlighterCoreSync(options); + const themeOptions = usesCustomTheme + ? {} + : coreOptions.themes + ? { theme: options.themes[0] } + : { themes: DEFAULT_THEMES, defaultColor: 'light' }; + + const loadedLanguages = new Set( + shiki.getLoadedLanguages().map(lang => lang.toLowerCase()) + ); - const theme = options.themes[0]; + /** + * Resolves a language id to one this highlighter can handle. + * Falls back to plain text for unknown/unloaded languages so + * highlighting never throws on unrecognized code fences. + * + * @param {string} [languageId] + * @returns {string} + */ + const resolveLanguage = languageId => { + const normalized = languageId?.toLowerCase(); + + if ( + normalized && + (isSpecialLang(normalized) || loadedLanguages.has(normalized)) + ) { + return languageId; + } + + return FALLBACK_LANGUAGE; + }; /** * Highlights code and returns the inner HTML inside the tag @@ -59,7 +99,12 @@ const createHighlighter = ({ coreOptions = {}, highlighterOptions = {} }) => { */ const highlightToHtml = (code, lang, meta = {}) => shiki - .codeToHtml(code, { lang, theme, meta, ...highlighterOptions }) + .codeToHtml(code, { + lang: resolveLanguage(lang), + ...themeOptions, + meta, + ...highlighterOptions, + }) // Shiki will always return the Highlighted code encapsulated in a
 and  tag
       // since our own CodeBox component handles the  tag, we just want to extract
       // the inner highlighted code to the CodeBox
@@ -73,10 +118,16 @@ const createHighlighter = ({ coreOptions = {}, highlighterOptions = {} }) => {
    * @param {Record} meta - Metadata
    */
   const highlightToHast = (code, lang, meta = {}) =>
-    shiki.codeToHast(code, { lang, theme, meta, ...highlighterOptions });
+    shiki.codeToHast(code, {
+      lang: resolveLanguage(lang),
+      ...themeOptions,
+      meta,
+      ...highlighterOptions,
+    });
 
   return {
     shiki,
+    resolveLanguage,
     highlightToHtml,
     highlightToHast,
   };
diff --git a/packages/rehype-shiki/src/index.css b/packages/rehype-shiki/src/index.css
index 61a75fb5ed25f..92ddfcfcce8ba 100644
--- a/packages/rehype-shiki/src/index.css
+++ b/packages/rehype-shiki/src/index.css
@@ -1 +1,14 @@
 @import './transformers/twoslash/index.css';
+
+/*
+ * Shiki renders the light palette as the inline default and stores the dark
+ * palette in custom properties. This selector also covers snippets whose
+ * highlighted HTML is inserted without the outer `.shiki` element.
+ */
+[data-theme='dark'] span[style*='--shiki-dark'] {
+  color: var(--shiki-dark) !important;
+  background-color: var(--shiki-dark-bg) !important;
+  font-style: var(--shiki-dark-font-style) !important;
+  font-weight: var(--shiki-dark-font-weight) !important;
+  text-decoration: var(--shiki-dark-text-decoration) !important;
+}
diff --git a/packages/rehype-shiki/src/index.mjs b/packages/rehype-shiki/src/index.mjs
index 229f079bc084f..dffbde84d8b69 100644
--- a/packages/rehype-shiki/src/index.mjs
+++ b/packages/rehype-shiki/src/index.mjs
@@ -1,18 +1,4 @@
-// Keep all imports at the top
-import cLanguage from 'shiki/langs/c.mjs';
-import coffeeScriptLanguage from 'shiki/langs/coffeescript.mjs';
-import cPlusPlusLanguage from 'shiki/langs/cpp.mjs';
-import diffLanguage from 'shiki/langs/diff.mjs';
-import dockerLanguage from 'shiki/langs/docker.mjs';
-import httpLanguage from 'shiki/langs/http.mjs';
-import iniLanguage from 'shiki/langs/ini.mjs';
-import javaScriptLanguage from 'shiki/langs/javascript.mjs';
-import jsonLanguage from 'shiki/langs/json.mjs';
-import powershellLanguage from 'shiki/langs/powershell.mjs';
-import shellScriptLanguage from 'shiki/langs/shellscript.mjs';
-import shellSessionLanguage from 'shiki/langs/shellsession.mjs';
-import typeScriptLanguage from 'shiki/langs/typescript.mjs';
-import yamlLanguage from 'shiki/langs/yaml.mjs';
+import { bundledLanguagesInfo } from 'shiki/langs';
 
 import createHighlighter, { getLanguageByName } from '#rs/highlighter.mjs';
 
@@ -37,7 +23,10 @@ async function getEngine({ wasm = false }) {
 
   const { createJavaScriptRegexEngine } =
     await import('@shikijs/engine-javascript');
-  return createJavaScriptRegexEngine();
+
+  // Not every bundled grammar compiles under the JavaScript engine,
+  // so skip the patterns it cannot handle instead of throwing.
+  return createJavaScriptRegexEngine({ forgiving: true });
 }
 
 /**
@@ -55,24 +44,13 @@ async function getTransformers({ twoslash = false, twoslashOptions }) {
   return transformers;
 }
 
+// Every language, since this is meant for SSR-ed highlighting :-)
 export const LANGS = [
-  ...cLanguage,
-  ...coffeeScriptLanguage,
-  ...cPlusPlusLanguage,
-  ...diffLanguage,
-  ...dockerLanguage,
-  ...httpLanguage,
-  ...iniLanguage,
-  {
-    ...javaScriptLanguage[0],
-    aliases: javaScriptLanguage[0].aliases.concat('cjs', 'mjs'),
-  },
-  ...jsonLanguage,
-  ...powershellLanguage,
-  ...shellScriptLanguage,
-  ...shellSessionLanguage,
-  ...typeScriptLanguage,
-  ...yamlLanguage,
+  ...new Set(
+    (
+      await Promise.all(bundledLanguagesInfo.map(language => language.import()))
+    ).flatMap(language => language.default)
+  ),
 ];
 
 export const getLanguageDisplayName = language =>
diff --git a/packages/rehype-shiki/src/transformers/twoslash/index.css b/packages/rehype-shiki/src/transformers/twoslash/index.css
index 4ceaa24315153..37073ff585027 100644
--- a/packages/rehype-shiki/src/transformers/twoslash/index.css
+++ b/packages/rehype-shiki/src/transformers/twoslash/index.css
@@ -17,13 +17,19 @@
   min-width: 100%;
   padding: 6px 12px;
   white-space: pre-wrap;
-  background-color: var(--color-neutral-950);
+  color: var(--color-neutral-900);
+  background-color: var(--color-neutral-100);
 
   span {
     display: inline-block;
   }
 }
 
+[data-theme='dark'] .twoslash-popup-code:not(:has(div)) {
+  color: var(--color-white);
+  background-color: var(--color-neutral-950);
+}
+
 .twoslash-completion-list {
   display: block;
   width: auto;
diff --git a/packages/rehype-shiki/src/transformers/twoslash/index.mjs b/packages/rehype-shiki/src/transformers/twoslash/index.mjs
index 2a6f347a04d0e..0d3be5b76a1c1 100644
--- a/packages/rehype-shiki/src/transformers/twoslash/index.mjs
+++ b/packages/rehype-shiki/src/transformers/twoslash/index.mjs
@@ -1,4 +1,8 @@
-import { transformerTwoslash } from '@shikijs/twoslash';
+import {
+  createTransformerFactory,
+  rendererRich,
+  transformerTwoslash,
+} from '@shikijs/twoslash';
 
 const compose = ({ token, cursor, popup }) => [
   {
@@ -10,39 +14,60 @@ const compose = ({ token, cursor, popup }) => [
   popup,
 ];
 
-export const twoslash = (options = {}) =>
-  transformerTwoslash({
-    langs: ['ts', 'js', 'cjs', 'mjs'],
-    rendererRich: {
-      jsdoc: false,
-      hast: {
-        hoverToken: { tagName: 'MDXTooltip' },
-        hoverPopup: { tagName: 'MDXTooltipContent' },
-        hoverCompose: compose,
-
-        queryToken: { tagName: 'MDXTooltip' },
-        queryPopup: { tagName: 'MDXTooltipContent' },
-        queryCompose: compose,
-
-        errorToken: { tagName: 'MDXTooltip' },
-        errorPopup: { tagName: 'MDXTooltipContent' },
-        errorCompose: compose,
-
-        completionToken: {
-          tagName: 'MDXTooltip',
-          properties: {
-            open: true,
-          },
-        },
-        completionPopup: {
-          tagName: 'MDXTooltipContent',
-          properties: {
-            align: 'start',
-          },
-        },
-        completionCompose: compose,
+const rendererOptions = {
+  jsdoc: false,
+  hast: {
+    hoverToken: { tagName: 'MDXTooltip' },
+    hoverPopup: { tagName: 'MDXTooltipContent' },
+    hoverCompose: compose,
+
+    queryToken: { tagName: 'MDXTooltip' },
+    queryPopup: { tagName: 'MDXTooltipContent' },
+    queryCompose: compose,
+
+    errorToken: { tagName: 'MDXTooltip' },
+    errorPopup: { tagName: 'MDXTooltipContent' },
+    errorCompose: compose,
+
+    completionToken: {
+      tagName: 'MDXTooltip',
+      properties: {
+        open: true,
+      },
+    },
+    completionPopup: {
+      tagName: 'MDXTooltipContent',
+      properties: {
+        align: 'start',
       },
     },
-    throws: false,
-    ...options,
-  });
+    completionCompose: compose,
+  },
+};
+
+const transformerOptions = {
+  langs: ['ts', 'js', 'cjs', 'mjs'],
+  rendererRich: rendererOptions,
+  throws: false,
+};
+
+/**
+ * Creates the Twoslash Shiki transformer.
+ *
+ * When `options.twoslasher` is provided, uses `createTransformerFactory`
+ * directly to avoid importing the default Node.js-dependent twoslasher from
+ * `twoslash`. This is needed for environments like Cloudflare Workers where
+ * the filesystem-backed default twoslasher cannot be used.
+ *
+ * @param {import('@shikijs/twoslash').TransformerTwoslashIndexOptions} [options]
+ */
+export const twoslash = (options = {}) => {
+  if (options.twoslasher) {
+    return createTransformerFactory(
+      options.twoslasher,
+      rendererRich(rendererOptions)
+    )({ ...transformerOptions, ...options });
+  }
+
+  return transformerTwoslash({ ...transformerOptions, ...options });
+};
diff --git a/packages/remark-lint/CHANGELOG.md b/packages/remark-lint/CHANGELOG.md
new file mode 100644
index 0000000000000..5a4a9b87160c7
--- /dev/null
+++ b/packages/remark-lint/CHANGELOG.md
@@ -0,0 +1,9 @@
+# @node-core/remark-lint
+
+## 1.4.1
+
+### Patch Changes
+
+- [#9020](https://github.com/nodejs/nodejs.org/pull/9020) [`0492dea`](https://github.com/nodejs/nodejs.org/commit/0492dea85e2a7096cf89e26c1e24d1c41b7d595f) Thanks [@avivkeller](https://github.com/avivkeller)! - Migrate package versioning and publishing to Changesets.
+
+- [#9016](https://github.com/nodejs/nodejs.org/pull/9016) [`b42f4e0`](https://github.com/nodejs/nodejs.org/commit/b42f4e07fa7488599dba3d33a0183216b6354dcb) Thanks [@avivkeller](https://github.com/avivkeller)! - Add link validation and fix issues in type validation
diff --git a/packages/remark-lint/README.md b/packages/remark-lint/README.md
index dd58bb3658a44..649812956873a 100644
--- a/packages/remark-lint/README.md
+++ b/packages/remark-lint/README.md
@@ -109,6 +109,16 @@ Enforces alphabetical sorting of reference-style link definitions.
 [info]: https://example.com/info
 ```
 
+### `node-core:validate-links`
+
+Checks that local links point to existing files and that fragment-only links
+point to headings in the current document. Root-relative website routes are
+mapped to the configured content and public directories, and route targets do
+not need to include a file extension. Directory routes resolve to `index.*`.
+
+For example, `/about/previous-releases` resolves to
+`pages/en/about/previous-releases.mdx`.
+
 ### `node-core:required-metadata`
 
 Requires essential metadata for documentation:
diff --git a/packages/remark-lint/package.json b/packages/remark-lint/package.json
index c84ec7b53ddd1..348fed45129aa 100644
--- a/packages/remark-lint/package.json
+++ b/packages/remark-lint/package.json
@@ -1,10 +1,11 @@
 {
   "name": "@node-core/remark-lint",
   "type": "module",
-  "version": "1.3.0",
+  "version": "1.4.1",
   "exports": {
     ".": "./src/index.mjs",
-    "./api": "./src/api.mjs"
+    "./api": "./src/api.mjs",
+    "./rules/*": "./src/rules/*.mjs"
   },
   "repository": {
     "type": "git",
@@ -21,6 +22,7 @@
   },
   "dependencies": {
     "@node-core/doc-kit": "^1.2.0",
+    "mdast-util-to-string": "^4.0.0",
     "remark-gfm": "^4.0.1",
     "remark-lint-blockquote-indentation": "^4.0.1",
     "remark-lint-checkbox-character-style": "^5.0.1",
@@ -41,19 +43,19 @@
     "remark-lint-no-shell-dollars": "^4.0.1",
     "remark-lint-no-table-indentation": "^5.0.1",
     "remark-lint-no-tabs": "^4.0.1",
-    "remark-lint-no-trailing-spaces": "^4.0.3",
+    "remark-lint-no-trailing-spaces": "^5.0.0",
     "remark-lint-no-unused-definitions": "^4.0.2",
-    "remark-lint-prohibited-strings": "^4.0.0",
+    "remark-lint-prohibited-strings": "^5.0.0",
     "remark-lint-rule-style": "^4.0.1",
     "remark-lint-strong-marker": "^4.0.1",
     "remark-lint-table-cell-padding": "^5.1.1",
     "remark-lint-table-pipes": "^5.0.1",
     "remark-lint-unordered-list-marker-style": "^4.0.1",
     "remark-preset-lint-recommended": "^7.0.1",
-    "semver": "^7.7.4",
+    "semver": "^7.8.1",
     "unified-lint-rule": "^3.0.1",
     "unist-util-visit": "^5.1.0",
-    "yaml": "^2.8.3"
+    "yaml": "^2.8.4"
   },
   "devDependencies": {
     "cross-env": "catalog:",
diff --git a/packages/remark-lint/src/index.mjs b/packages/remark-lint/src/index.mjs
index bad1d1d8cc956..14c1d52f926e2 100644
--- a/packages/remark-lint/src/index.mjs
+++ b/packages/remark-lint/src/index.mjs
@@ -24,6 +24,8 @@ import remarkLintTableCellPadding from 'remark-lint-table-cell-padding';
 import remarkLintTablePipes from 'remark-lint-table-pipes';
 import remarkPresetLintRecommended from 'remark-preset-lint-recommended';
 
+import validateLinks from './rules/validate-links.mjs';
+
 export default {
   settings: {
     tightDefinitions: true,
@@ -54,6 +56,7 @@ export default {
     remarkLintNofileNameOuterDashes,
 
     // Heading and link rules
+    [validateLinks, { basePaths: ['pages/en', 'pages', 'public', '.'] }],
     remarkLintFinalDefinition,
     [remarkLintNoUnusedDefinitions, false],
     [remarkLintNoLiteralURLs, false],
diff --git a/packages/remark-lint/src/rules/__tests__/invalid-type-reference.test.mjs b/packages/remark-lint/src/rules/__tests__/invalid-type-reference.test.mjs
index 21f1cc54053f4..c5a11565711a2 100644
--- a/packages/remark-lint/src/rules/__tests__/invalid-type-reference.test.mjs
+++ b/packages/remark-lint/src/rules/__tests__/invalid-type-reference.test.mjs
@@ -1,3 +1,4 @@
+import assert from 'node:assert/strict';
 import { describe, it } from 'node:test';
 
 import { testRule } from './utils.mjs';
@@ -24,6 +25,19 @@ const testCases = [
     input: '[]()',
     expected: ['Type reference must be wrapped in "{}"; saw ""'],
   },
+  {
+    name: 'ignores references in non-prose nodes',
+    input: `
+
+\`{invalid}\`
+
+\`\`\`js
+const value = {invalid};
+\`\`\`
+
+
{invalid}
`, + expected: [], + }, { name: 'multiple references', input: 'Psst, are you a {string | boolean}', @@ -54,9 +68,13 @@ const testCases = [ ]; describe('invalid-type-reference', () => { - for (const { name, input, expected, options } of testCases) { - it(name, () => - testRule(invalidTypeReference, input, expected, {}, options) - ); + for (const { name, input, expected, options, replacement } of testCases) { + it(name, () => { + const tree = testRule(invalidTypeReference, input, expected, {}, options); + + if (replacement) { + assert.equal(tree.children[0].children[0].value, replacement); + } + }); } }); diff --git a/packages/remark-lint/src/rules/__tests__/utils.mjs b/packages/remark-lint/src/rules/__tests__/utils.mjs index e3245368b5173..df98694f0b687 100644 --- a/packages/remark-lint/src/rules/__tests__/utils.mjs +++ b/packages/remark-lint/src/rules/__tests__/utils.mjs @@ -32,6 +32,8 @@ export const testRule = ( vfile.message.mock.calls.map(call => call.arguments[0]), expected ); + + return tree; }; /** diff --git a/packages/remark-lint/src/rules/__tests__/validate-links.test.mjs b/packages/remark-lint/src/rules/__tests__/validate-links.test.mjs new file mode 100644 index 0000000000000..5c1d1f51d5f0c --- /dev/null +++ b/packages/remark-lint/src/rules/__tests__/validate-links.test.mjs @@ -0,0 +1,185 @@ +import assert from 'node:assert/strict'; +import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { after, before, describe, it } from 'node:test'; + +import remarkParse from 'remark-parse'; +import { unified } from 'unified'; + +import basePreset from '../../index.mjs'; +import validateLinks from '../validate-links.mjs'; + +const processMarkdown = async ( + cwd, + markdown, + options = {}, + filePath = 'pages/en/example.mdx' +) => { + const processor = unified() + .use(remarkParse) + .use(validateLinks, { + basePaths: ['pages/en', 'pages', 'public', '.'], + ...options, + }); + const tree = processor.parse(markdown); + + return new Promise((resolve, reject) => { + processor.run(tree, { cwd, path: filePath }, (error, _, file) => { + if (error) { + reject(error); + } else { + resolve(file); + } + }); + }); +}; + +describe('validate-links', () => { + let cwd; + + before(async () => { + cwd = await mkdtemp(path.join(tmpdir(), 'node-core-validate-links-')); + + await Promise.all([ + mkdir(path.join(cwd, 'pages/en/about'), { recursive: true }), + mkdir(path.join(cwd, 'pages/en/blog/release'), { recursive: true }), + mkdir(path.join(cwd, 'pages/en/guide'), { recursive: true }), + mkdir(path.join(cwd, 'pages/ja/about'), { recursive: true }), + mkdir(path.join(cwd, 'public/static'), { recursive: true }), + ]); + + await Promise.all([ + writeFile(path.join(cwd, 'pages/en/about/previous-releases.mdx'), ''), + writeFile(path.join(cwd, 'pages/en/blog/release/v20.0.0.md'), ''), + writeFile(path.join(cwd, 'pages/en/guide/index.markdown'), ''), + writeFile(path.join(cwd, 'pages/en/related.mdx'), ''), + writeFile(path.join(cwd, 'pages/ja/about/releases.md'), ''), + writeFile(path.join(cwd, 'public/static/logo.svg'), ''), + ]); + }); + + after(() => rm(cwd, { force: true, recursive: true })); + + it('is enabled by the base preset with route mappings', () => { + assert.ok( + basePreset.plugins.some( + plugin => Array.isArray(plugin) && plugin[0] === validateLinks + ) + ); + }); + + it('maps root-relative routes to files regardless of extension', async () => { + const file = await processMarkdown( + cwd, + [ + '[Releases](/about/previous-releases?from=test)', + '[Dotted route](/blog/release/v20.0.0)', + '[Japanese](/ja/about/releases)', + '[Relative](./related)', + ].join('\n\n') + ); + + assert.deepEqual(file.messages, []); + }); + + it('maps directory routes and public assets', async () => { + const file = await processMarkdown( + cwd, + '[Guide](/guide)\n\n![Node.js logo](/static/logo.svg)' + ); + + assert.deepEqual(file.messages, []); + }); + + it('reports missing route targets', async () => { + const file = await processMarkdown(cwd, '[Missing](/about/missing)'); + + assert.equal(file.messages.length, 1); + assert.equal( + file.messages[0].message, + 'Cannot find file for link `/about/missing`' + ); + assert.equal(file.messages[0].source, 'node-core'); + assert.equal(file.messages[0].ruleId, 'validate-links'); + }); + + it('validates headings in the current document', async () => { + const valid = await processMarkdown( + cwd, + [ + '# Existing heading', + '[Existing](#existing%2Dheading)', + '# child_process', + '[Node.js-style slug](#child-process)', + ].join('\n\n') + ); + const invalid = await processMarkdown( + cwd, + '# Existing heading\n\n[Missing](#missing)' + ); + + assert.deepEqual(valid.messages, []); + assert.deepEqual( + invalid.messages.map(message => message.message), + ['Cannot find heading for `#missing`'] + ); + }); + + it('recognizes custom HTML anchors', async () => { + const file = await processMarkdown( + cwd, + '\n\n[Security advisory](#CVE-2016-6304)' + ); + + assert.deepEqual(file.messages, []); + }); + + it('ignores link paths matching configured globs', async () => { + const file = await processMarkdown( + cwd, + [ + '[Learn article](/learn/getting-started?from=test)', + '[Feed](/feed/blog.xml)', + '[Different missing route](/learning)', + ].join('\n\n'), + { ignoreLinks: ['/learn/*', '/feed/*'] } + ); + + assert.deepEqual( + file.messages.map(message => message.message), + ['Cannot find file for link `/learning`'] + ); + }); + + it('skips files matching configured globs', async () => { + const markdown = '[Missing](/about/missing)'; + const options = { ignoreFiles: ['pages/!(en)/**/*.{md,mdx}'] }; + const translation = await processMarkdown( + cwd, + markdown, + options, + 'pages/fr/example.mdx' + ); + const english = await processMarkdown(cwd, markdown, options); + + assert.deepEqual(translation.messages, []); + assert.deepEqual( + english.messages.map(message => message.message), + ['Cannot find file for link `/about/missing`'] + ); + }); + + it('ignores external links', async () => { + const file = await processMarkdown( + cwd, + [ + '[Website](https://nodejs.org)', + '[Protocol-relative](//nodejs.org)', + '[Email](mailto:test@example.com)', + ].join('\n\n') + ); + + assert.deepEqual(file.messages, []); + }); +}); diff --git a/packages/remark-lint/src/rules/invalid-type-reference.mjs b/packages/remark-lint/src/rules/invalid-type-reference.mjs index c833847bc2027..4fa4d2c6dcd14 100644 --- a/packages/remark-lint/src/rules/invalid-type-reference.mjs +++ b/packages/remark-lint/src/rules/invalid-type-reference.mjs @@ -6,47 +6,55 @@ import { visit } from 'unist-util-visit'; const MATCH_RE = /\s\||\| /g; const REPLACE_RE = /\s*\| */g; +const isTypeNode = node => { + if (node.type === 'text') { + return QUERIES.normalizeTypes.test(node.value); + } + + if (node.type === 'html') { + return node.value.match(QUERIES.normalizeTypes)?.includes(node.value); + } + + return false; +}; + /** * Ensures that all type references are valid * @type {import('unified-lint-rule').Rule<, import('../api.mjs').Options>} */ const invalidTypeReference = (tree, vfile, { typeMap = {} }) => { - visit( - tree, - ({ value }) => QUERIES.normalizeTypes.test(value), - node => { - const types = node.value.match(QUERIES.normalizeTypes); - - types.forEach(type => { - // Ensure wrapped in {} - if (type[0] !== '{' || type[type.length - 1] !== '}') { - vfile.message( - `Type reference must be wrapped in "{}"; saw "${type}"`, - node - ); - - const newType = `{${type.slice(1, -1)}}`; - node.value = node.value.replace(type, newType); - type = newType; - } - - // Fix spaces around | - if (MATCH_RE.test(type)) { - vfile.message( - `Type reference should be separated by "|", without spaces; saw "${type}"`, - node - ); - - const normalized = type.replace(REPLACE_RE, '|'); - node.value = node.value.replace(type, normalized); - } - - if (transformTypeToReferenceLink(type, typeMap) === type) { - vfile.message(`Invalid type reference: ${type}`, node); - } - }); - } - ); + visit(tree, isTypeNode, node => { + const types = node.value.match(QUERIES.normalizeTypes); + + types.forEach(type => { + // Ensure wrapped in {} + if (type[0] !== '{' || type[type.length - 1] !== '}') { + vfile.message( + `Type reference must be wrapped in "{}"; saw "${type}"`, + node + ); + + const newType = `{${type.slice(1, -1)}}`; + node.value = node.value.replace(type, newType); + type = newType; + } + + // Fix spaces around | + if (MATCH_RE.test(type)) { + vfile.message( + `Type reference should be separated by "|", without spaces; saw "${type}"`, + node + ); + + const normalized = type.replace(REPLACE_RE, '|'); + node.value = node.value.replace(type, normalized); + } + + if (transformTypeToReferenceLink(type, typeMap) === type) { + vfile.message(`Invalid type reference: ${type}`, node); + } + }); + }); }; export default lintRule( diff --git a/packages/remark-lint/src/rules/validate-links.mjs b/packages/remark-lint/src/rules/validate-links.mjs new file mode 100644 index 0000000000000..5cfade0ecc93c --- /dev/null +++ b/packages/remark-lint/src/rules/validate-links.mjs @@ -0,0 +1,197 @@ +import { readdir } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +import createNodeSlugger from '@node-core/doc-kit/src/generators/metadata/utils/slugger.mjs'; +import { toString } from 'mdast-util-to-string'; +import { lintRule } from 'unified-lint-rule'; +import { visit } from 'unist-util-visit'; + +const directoryCache = new Map(); +const urlBase = new URL('https://nodejs.org'); +const htmlTag = /<[a-z][^>]*>/giu; +const htmlIdentifier = + /\s(?:id|name)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/iu; + +const decode = value => { + try { + return decodeURIComponent(value); + } catch { + return value; + } +}; + +const getUrlParts = (value, basePath) => { + try { + const url = new URL(value, pathToFileURL(basePath)); + + if (url.protocol !== 'file:' || url.host) { + return; + } + + return { + hash: decode(url.hash.slice(1)).toLowerCase(), + // Don't use the basePath as the base here, since `file:///repo/path/to/file` -> `/target` + // will not prefix `/repo`. + pathname: decode(new URL(value, urlBase).pathname), + rootRelative: value.startsWith('/'), + targetPath: fileURLToPath(url), + }; + } catch { + return; + } +}; + +const readDirectory = directory => { + let entries = directoryCache.get(directory); + + if (!entries) { + entries = readdir(directory, { withFileTypes: true }).then( + items => { + const files = items + .filter(item => item.isFile()) + .map(item => item.name); + const result = new Map(files.map(name => [name, name])); + + for (const name of files) { + const extension = path.extname(name); + const stem = extension && name.slice(0, -extension.length); + + if (stem && !result.has(stem)) { + result.set(stem, name); + } + } + + return result; + }, + () => new Map() + ); + + directoryCache.set(directory, entries); + } + + return entries; +}; + +const resolveFile = async target => { + const directory = path.dirname(target); + const file = (await readDirectory(directory)).get(path.basename(target)); + + if (file) { + return path.join(directory, file); + } + + const index = (await readDirectory(target)).get('index'); + + return index && path.join(target, index); +}; + +const getHeadings = tree => { + const headings = new Set(); + const slugger = createNodeSlugger(); + const addIdentifier = identifier => { + if (identifier) { + headings.add(String(identifier).toLowerCase()); + } + }; + + visit(tree, node => { + const properties = node.data?.hProperties; + const identifier = + properties?.id || + properties?.name || + node.data?.id || + node.attributes?.find( + item => + (item.name === 'id' || item.name === 'name') && + typeof item.value === 'string' + )?.value; + + if (identifier) { + addIdentifier(identifier); + } else if (node.type === 'heading') { + headings.add(slugger.slug(toString(node))); + } + + if (node.type === 'html') { + for (const [tag] of node.value.matchAll(htmlTag)) { + const match = htmlIdentifier.exec(tag); + + addIdentifier(match?.[1] || match?.[2] || match?.[3]); + } + } + }); + + return headings; +}; + +/** + * @type {import('unified-lint-rule').Rule} + */ +const validateLinks = async (tree, file, options = {}) => { + const basePaths = [options.basePaths || '.'].flat(); + const ignoredFiles = [options.ignoreFiles || []].flat(); + const ignoredLinks = [options.ignoreLinks || []].flat(); + const cwd = path.resolve(file.cwd || process.cwd()); + const currentPath = file.path && path.resolve(cwd, file.path); + + if ( + currentPath && + ignoredFiles.some(pattern => + path.matchesGlob(path.relative(cwd, currentPath), pattern) + ) + ) { + return; + } + + const basePath = currentPath || path.join(cwd, 'index'); + const links = []; + let headings; + + visit(tree, node => { + if ( + (node.type === 'link' || + node.type === 'image' || + node.type === 'definition') && + node.url + ) { + const parts = getUrlParts(node.url, basePath); + + if ( + parts && + !ignoredLinks.some(pattern => path.matchesGlob(parts.pathname, pattern)) + ) { + links.push([node, parts]); + } + } + }); + + for (const [node, parts] of links) { + const candidates = parts.rootRelative + ? basePaths.map(base => path.join(cwd, base, parts.pathname)) + : [parts.targetPath]; + + let match; + + for (const candidate of candidates) { + match = + candidate === currentPath ? currentPath : await resolveFile(candidate); + + if (match) { + break; + } + } + + if (!match) { + file.message(`Cannot find file for link \`${node.url}\``, node); + } else if (node.type !== 'image' && parts.hash && match === currentPath) { + headings ??= getHeadings(tree); + + if (!headings.has(parts.hash)) { + file.message(`Cannot find heading for \`#${parts.hash}\``, node); + } + } + } +}; + +export default lintRule('node-core:validate-links', validateLinks); diff --git a/packages/ui-components/.storybook/main.ts b/packages/ui-components/.storybook/main.ts index 5580f40838c5b..87d435aae39eb 100644 --- a/packages/ui-components/.storybook/main.ts +++ b/packages/ui-components/.storybook/main.ts @@ -7,6 +7,14 @@ const config: StorybookConfig = { core: { disableTelemetry: true, disableWhatsNewNotifications: true }, framework: '@storybook/react-webpack5', swc: () => ({ jsc: { transform: { react: { runtime: 'automatic' } } } }), + // Storybook renders the components straight from `src`, so `#ui/*` must + // resolve to the uncompiled sources rather than to the published `dist` output + webpackFinal: async config => { + config.resolve ??= {}; + config.resolve.conditionNames = ['source', '...']; + + return config; + }, addons: [ '@storybook/addon-webpack5-compiler-swc', '@storybook/addon-themes', diff --git a/packages/ui-components/.stylelintrc.mjs b/packages/ui-components/.stylelintrc.mjs index eed4b8c01f612..e371180e8fe7c 100644 --- a/packages/ui-components/.stylelintrc.mjs +++ b/packages/ui-components/.stylelintrc.mjs @@ -26,7 +26,9 @@ export default { plugins: [ 'stylelint-order', 'stylelint-selector-bem-pattern', - '#ui/stylelint/one-utility-class-per-line.mjs', + // Stylelint resolves plugins with its own resolver, which cannot be told to + // prefer the `source` condition, so we point at the uncompiled source directly + './src/stylelint/one-utility-class-per-line.mjs', ], rules: { // Enforces Element Class Names to be camelCase diff --git a/packages/ui-components/CHANGELOG.md b/packages/ui-components/CHANGELOG.md new file mode 100644 index 0000000000000..013a2f3c67eb4 --- /dev/null +++ b/packages/ui-components/CHANGELOG.md @@ -0,0 +1,24 @@ +# @node-core/ui-components + +## 1.7.5 + +### Patch Changes + +- [#9093](https://github.com/nodejs/nodejs.org/pull/9093) [`96e49e7`](https://github.com/nodejs/nodejs.org/commit/96e49e7265d2bf73b9a0a632c322d6c146aa498e) Thanks [@avivkeller](https://github.com/avivkeller)! - Quality-of-life style improvements + +- [#9062](https://github.com/nodejs/nodejs.org/pull/9062) [`58e004e`](https://github.com/nodejs/nodejs.org/commit/58e004e8e1177cf1a13162008444d982433b79c2) Thanks [@avivkeller](https://github.com/avivkeller)! - Widen the article layout to 120rem on 3xl screens and give the extra space to the + metabar, so table of contents entries no longer wrap. + +## 1.7.4 + +### Patch Changes + +- [#9047](https://github.com/nodejs/nodejs.org/pull/9047) [`94e27d2`](https://github.com/nodejs/nodejs.org/commit/94e27d221d09665d7464498e3e9de37ac45168a1) Thanks [@avivkeller](https://github.com/avivkeller)! - Always mount CodeTabs, even when inactive + +- [#9020](https://github.com/nodejs/nodejs.org/pull/9020) [`0492dea`](https://github.com/nodejs/nodejs.org/commit/0492dea85e2a7096cf89e26c1e24d1c41b7d595f) Thanks [@avivkeller](https://github.com/avivkeller)! - Migrate package versioning and publishing to Changesets. + +- [#9024](https://github.com/nodejs/nodejs.org/pull/9024) [`ec199d0`](https://github.com/nodejs/nodejs.org/commit/ec199d069317b296cf88ef8c6cba0226a64ef9fd) Thanks [@avivkeller](https://github.com/avivkeller)! - Add a default `aria-label` to `ThemeToggle`. + +- [#9028](https://github.com/nodejs/nodejs.org/pull/9028) [`ca47d6f`](https://github.com/nodejs/nodejs.org/commit/ca47d6fa908901335705782ee71216f711fa87c8) Thanks [@avivkeller](https://github.com/avivkeller)! - Account for a possible wrapper between a code boxes `pre` and `code`. + +- [#9031](https://github.com/nodejs/nodejs.org/pull/9031) [`07a6844`](https://github.com/nodejs/nodejs.org/commit/07a684424a5895793899c20366bd9b4a0426be6b) Thanks [@avivkeller](https://github.com/avivkeller)! - Add light mode theming to code elements diff --git a/packages/ui-components/README.md b/packages/ui-components/README.md index b1f590f2dc541..061652eea6240 100644 --- a/packages/ui-components/README.md +++ b/packages/ui-components/README.md @@ -17,7 +17,7 @@ compiled outputs and keep them updated while you work. ```bash # From nodejs.org/packages/ui-components pnpm install -node --run compile:watch +node --run build:watch # In another terminal, still in nodejs.org/packages/ui-components npm link @@ -26,5 +26,5 @@ npm link npm link @node-core/ui-components ``` -The `compile:watch` script keeps `dist/` up to date so consumers resolve compiled +The `build:watch` script keeps `dist/` up to date so consumers resolve compiled CSS and JavaScript instead of the raw Tailwind source. diff --git a/packages/ui-components/__design__/colors.stories.tsx b/packages/ui-components/__design__/colors.stories.tsx index ab07ebb262ade..fc5ca0a7e2cf3 100644 --- a/packages/ui-components/__design__/colors.stories.tsx +++ b/packages/ui-components/__design__/colors.stories.tsx @@ -5,14 +5,14 @@ export const Colors: StoryObj = {
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 71af2eaff5289..d30ffc949d6b7 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -1,22 +1,29 @@ { "name": "@node-core/ui-components", - "version": "1.7.0", + "version": "1.7.5", "type": "module", "exports": { "./*": { - "rolldown": [ - "./dist/*", - "./dist/*.js", - "./dist/*/index.js" - ], - "default": [ + "types": [ "./src/*", "./src/*.tsx", "./src/*/index.tsx", "./src/*.ts", "./src/*/index.ts" + ], + "default": [ + "./dist/*", + "./dist/*.js", + "./dist/*/index.js" ] - } + }, + "./src/*": [ + "./src/*", + "./src/*.tsx", + "./src/*/index.tsx", + "./src/*.ts", + "./src/*/index.ts" + ] }, "repository": { "type": "git", @@ -24,11 +31,10 @@ "directory": "packages/ui-components" }, "scripts": { - "compile:ts": "tsc", - "compile:css": "postcss --dir dist --base src \"src/**/*.module.css\" src/styles/index.css", - "compile": "node --run compile:ts && node --run compile:css", - "compile:watch": "concurrently -k \"node --run compile:ts -- --watch\" \"node --run compile:css -- --watch\"", - "release": "node --run compile", + "build": "node --run build:ts && node --run build:css", + "build:css": "postcss --dir dist --base src \"src/**/*.module.css\" src/styles/index.css", + "build:ts": "tsc", + "build:watch": "concurrently -k \"node --run build:ts -- --watch\" \"node --run build:css -- --watch\"", "lint": "node --run lint:js && node --run lint:css", "lint:css": "stylelint \"**/*.css\" --allow-empty-input --cache --cache-strategy=content --cache-location=.stylelintcache", "lint:css:fix": "node --run lint:css -- --fix", @@ -39,69 +45,76 @@ "storybook": "cross-env NODE_NO_WARNINGS=1 storybook dev -p 6006 --quiet", "storybook:build": "cross-env NODE_NO_WARNINGS=1 storybook build --quiet --webpack-stats-json", "test": "node --run test:unit", - "test:unit": "cross-env NODE_NO_WARNINGS=1 node --experimental-test-coverage --test-coverage-exclude=**/*.test.* --enable-source-maps --import=global-jsdom/register --import=tsx --import=../../tests/setup.mjs --test **/*.test.*", + "test:unit": "cross-env NODE_NO_WARNINGS=1 node --conditions=source --experimental-test-coverage --test-coverage-exclude=**/*.test.* --enable-source-maps --import=global-jsdom/register --import=tsx --import=../../tests/setup.mjs --test **/*.test.*", "test:unit:watch": "node --run test:unit -- --watch" }, "dependencies": { "@heroicons/react": "^2.2.0", "@orama/orama": "^3.1.18", "@orama/ui": "^1.5.4", - "@radix-ui/react-avatar": "^1.1.11", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-dropdown-menu": "^2.1.16", - "@radix-ui/react-label": "^2.1.8", - "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-separator": "^1.1.8", - "@radix-ui/react-tabs": "^1.1.13", - "@radix-ui/react-tooltip": "^1.2.8", - "@tailwindcss/postcss": "~4.2.2", - "@types/react": "catalog:", + "@radix-ui/react-avatar": "^1.2.3", + "@radix-ui/react-dialog": "^1.1.20", + "@radix-ui/react-dropdown-menu": "^2.1.21", + "@radix-ui/react-label": "^2.1.12", + "@radix-ui/react-select": "^2.3.4", + "@radix-ui/react-separator": "^1.1.12", + "@radix-ui/react-tabs": "^1.1.18", + "@radix-ui/react-tooltip": "^1.2.13", "@vcarl/remark-headings": "~0.1.0", "classnames": "catalog:", - "postcss-calc": "10.1.1", - "postcss-cli": "11.0.1", - "react": "catalog:", - "tailwindcss": "catalog:", - "typescript": "catalog:" + "react": "catalog:" }, "devDependencies": { - "@eslint-react/eslint-plugin": "~3.0.0", + "@eslint-react/eslint-plugin": "~5.8.6", "@orama/core": "^1.2.19", - "@storybook/addon-styling-webpack": "~3.0.1", - "@storybook/addon-themes": "~10.3.3", + "@storybook/addon-styling-webpack": "~3.0.2", + "@storybook/addon-themes": "~10.5.4", "@storybook/addon-webpack5-compiler-swc": "~4.0.3", - "@storybook/react-webpack5": "~10.3.3", + "@storybook/react-webpack5": "~10.5.4", + "@tailwindcss/postcss": "~4.3.3", "@testing-library/user-event": "~14.6.1", "@types/node": "catalog:", + "@types/react": "catalog:", "concurrently": "9.2.1", "cross-env": "catalog:", "css-loader": "7.1.4", "eslint-plugin-react": "7.37.5", - "eslint-plugin-react-hooks": "7.0.1", + "eslint-plugin-react-hooks": "7.1.1", "eslint-plugin-storybook": "10.3.3", "global-jsdom": "29.0.0", + "postcss-calc": "~10.1.1", + "postcss-cli": "^11.0.1", "postcss-loader": "8.2.1", - "storybook": "~10.3.3", + "storybook": "~10.5.4", "style-loader": "4.0.0", - "stylelint": "17.5.0", + "stylelint": "17.14.1", "stylelint-config-standard": "40.0.0", "stylelint-order": "8.1.1", - "stylelint-selector-bem-pattern": "4.0.1", - "tsx": "4.21.0" + "stylelint-selector-bem-pattern": "5.0.0", + "tailwindcss": "catalog:", + "tsx": "4.23.1", + "typescript": "catalog:" }, "imports": { "#ui/*": { - "rolldown": [ - "./dist/*", - "./dist/*.js", - "./dist/*/index.js" + "types": [ + "./src/*", + "./src/*.tsx", + "./src/*/index.tsx", + "./src/*.ts", + "./src/*/index.ts" ], - "default": [ + "source": [ "./src/*", "./src/*.tsx", "./src/*/index.tsx", "./src/*.ts", "./src/*/index.ts" + ], + "default": [ + "./dist/*", + "./dist/*.js", + "./dist/*/index.js" ] } }, diff --git a/packages/ui-components/src/Common/AlertBox/index.module.css b/packages/ui-components/src/Common/AlertBox/index.module.css index 685f20a75e64a..0c5e6e097f4ae 100644 --- a/packages/ui-components/src/Common/AlertBox/index.module.css +++ b/packages/ui-components/src/Common/AlertBox/index.module.css @@ -63,10 +63,10 @@ } &.success { - @apply bg-green-600; + @apply bg-brand-600; .title { - @apply bg-green-700; + @apply bg-brand-700; } } diff --git a/packages/ui-components/src/Common/Badge/index.module.css b/packages/ui-components/src/Common/Badge/index.module.css index 008f025cf3d95..5c757e89e2acd 100644 --- a/packages/ui-components/src/Common/Badge/index.module.css +++ b/packages/ui-components/src/Common/Badge/index.module.css @@ -19,7 +19,7 @@ } &.default { - @apply bg-green-600; + @apply bg-brand-600; } &.error { diff --git a/packages/ui-components/src/Common/BadgeGroup/index.module.css b/packages/ui-components/src/Common/BadgeGroup/index.module.css index 2304c408078a0..f91628a893b88 100644 --- a/packages/ui-components/src/Common/BadgeGroup/index.module.css +++ b/packages/ui-components/src/Common/BadgeGroup/index.module.css @@ -25,20 +25,20 @@ } &.default { - @apply border-green-200 - bg-green-100 - dark:border-green-700 + @apply border-brand-200 + bg-brand-100 + dark:border-brand-700 dark:bg-neutral-900; .icon { - @apply text-green-500 - dark:text-green-300; + @apply text-brand-500 + dark:text-brand-300; } .message, .message a:not(:hover) { - @apply text-green-700 - dark:text-green-300; + @apply text-brand-700 + dark:text-brand-300; } } diff --git a/packages/ui-components/src/Common/Banner/index.module.css b/packages/ui-components/src/Common/Banner/index.module.css index 282a93668d2ad..d9bf2cc94b981 100644 --- a/packages/ui-components/src/Common/Banner/index.module.css +++ b/packages/ui-components/src/Common/Banner/index.module.css @@ -1,12 +1,11 @@ @reference "../../styles/index.css"; .banner { - @apply flex + @apply animate-fade-and-slide-down + relative + grid w-full - flex-row - items-center - justify-center - gap-2 + grid-rows-1 px-8 py-3 text-sm; @@ -29,8 +28,17 @@ } } +.bannerContent { + @apply flex + min-h-0 + flex-row + items-center + justify-center + gap-2; +} + .default { - @apply bg-green-600; + @apply bg-brand-600; } .error { @@ -40,3 +48,15 @@ .warning { @apply bg-warning-600; } + +.closeButton { + @apply absolute + right-4 + text-lg; + + span { + @apply text-white/60 + transition-colors + hover:text-white; + } +} diff --git a/packages/ui-components/src/Common/Banner/index.stories.tsx b/packages/ui-components/src/Common/Banner/index.stories.tsx index 60fc4c4d2e336..1ca61a6312971 100644 --- a/packages/ui-components/src/Common/Banner/index.stories.tsx +++ b/packages/ui-components/src/Common/Banner/index.stories.tsx @@ -26,4 +26,12 @@ export const Warning: Story = { }, }; +export const Dismissible: Story = { + args: { + children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + type: 'default', + onClose: () => {}, + }, +}; + export default { component: Banner } as Meta; diff --git a/packages/ui-components/src/Common/Banner/index.tsx b/packages/ui-components/src/Common/Banner/index.tsx index b28ceade24816..b3cba18be84ec 100644 --- a/packages/ui-components/src/Common/Banner/index.tsx +++ b/packages/ui-components/src/Common/Banner/index.tsx @@ -1,21 +1,44 @@ -import type { FC, HTMLAttributes, PropsWithChildren } from 'react'; +import classNames from 'classnames'; + +import type { + FC, + HTMLAttributes, + MouseEventHandler, + PropsWithChildren, +} from 'react'; import styles from './index.module.css'; export type BannerProps = { type?: 'default' | 'warning' | 'error'; + closeButtonAriaLabel?: string; + onClose?: MouseEventHandler; } & HTMLAttributes; const Banner: FC> = ({ type = 'default', + closeButtonAriaLabel = 'Close banner', + onClose, children, ...props }) => (
- {children} +
+ {children} + {onClose && ( + + )} +
); diff --git a/packages/ui-components/src/Common/BaseActiveLink/__tests__/index.test.jsx b/packages/ui-components/src/Common/BaseActiveLink/__tests__/index.test.jsx index 435e3a1c9c0b4..8fe674313512b 100644 --- a/packages/ui-components/src/Common/BaseActiveLink/__tests__/index.test.jsx +++ b/packages/ui-components/src/Common/BaseActiveLink/__tests__/index.test.jsx @@ -49,4 +49,42 @@ describe('ActiveLink', () => { 'link active' ); }); + + it('forces active class when active override is true regardless of pathname', async () => { + render( + + Link + + ); + + assert.equal( + (await screen.findByText('Link')).getAttribute('class'), + 'link active' + ); + }); + + it('forces inactive when active override is false even when pathname matches', async () => { + render( + + Link + + ); + + assert.equal( + (await screen.findByText('Link')).getAttribute('class'), + 'link' + ); + }); }); diff --git a/packages/ui-components/src/Common/BaseActiveLink/index.tsx b/packages/ui-components/src/Common/BaseActiveLink/index.tsx index d0c2051de16e2..4eee4a8f67183 100644 --- a/packages/ui-components/src/Common/BaseActiveLink/index.tsx +++ b/packages/ui-components/src/Common/BaseActiveLink/index.tsx @@ -7,6 +7,7 @@ export type ActiveLocalizedLinkProps = ComponentProps & { activeClassName?: string; allowSubPath?: boolean; pathname?: string; + active?: boolean; as?: LinkLike; }; @@ -16,17 +17,21 @@ const BaseActiveLink: FC = ({ className, href = '', pathname = '/', + active, as: Component = 'a', ...props }) => { - const finalClassName = classNames(className, { - [activeClassName]: allowSubPath + // NavBar sets `active` explicitly; if it's not passed we fall back to the path check. + const isActive = + active ?? + (allowSubPath ? // When using allowSubPath we want only to check if // the current pathname starts with the utmost upper level // of an href (e.g. /docs/...) pathname.startsWith(`/${href.toString().split('/')[1]}`) - : href.toString() === pathname, - }); + : href.toString() === pathname); + + const finalClassName = classNames(className, { [activeClassName]: isActive }); return ; }; diff --git a/packages/ui-components/src/Common/BaseButton/index.module.css b/packages/ui-components/src/Common/BaseButton/index.module.css index 4eb0e97fd2500..6d839ac2bce7f 100644 --- a/packages/ui-components/src/Common/BaseButton/index.module.css +++ b/packages/ui-components/src/Common/BaseButton/index.module.css @@ -27,45 +27,49 @@ &.neutral { @apply rounded-sm - bg-neutral-900 - text-white + bg-neutral-100 + text-neutral-900 + dark:bg-neutral-900 dark:text-neutral-200; &:hover:not([aria-disabled='true']) { - @apply bg-neutral-800; + @apply bg-neutral-200 + dark:bg-neutral-800; } &[aria-disabled='true'] { - @apply bg-neutral-900 - opacity-50; + @apply bg-neutral-100 + opacity-50 + dark:bg-neutral-900; } &:focus { - @apply bg-neutral-800; + @apply bg-neutral-200 + dark:bg-neutral-800; } } &.primary { - @apply rounded-sm + @apply border-brand-600 + bg-brand-600 + rounded-sm border - border-green-600 - bg-green-600 text-white shadow-xs; &:hover:not([aria-disabled='true']) { - @apply border-green-700 - bg-green-700 + @apply border-brand-700 + bg-brand-700 text-white; } &:focus { - @apply border-green-700 - bg-green-700; + @apply border-brand-700 + bg-brand-700; } &[aria-disabled='true'] { - @apply bg-green-600 + @apply bg-brand-600 opacity-50; } } @@ -100,10 +104,13 @@ &.special { @apply before:bg-gradient-glow-backdrop + border-brand-600/30 + bg-brand-600/10 + after:from-brand-600/0 + after:via-brand-600 + after:to-brand-600/0 rounded-lg border - border-green-600/30 - bg-green-600/10 text-white shadow-xs before:absolute @@ -124,9 +131,6 @@ after:h-px after:w-2/5 after:bg-linear-to-r - after:from-green-600/0 - after:via-green-600 - after:to-green-600/0 after:content-['']; &[aria-disabled='true'] { @@ -134,11 +138,11 @@ } &:hover:not([aria-disabled='true']) { - @apply bg-green-600/20; + @apply bg-brand-600/20; } &:focus { - @apply bg-green-600/20; + @apply bg-brand-600/20; } } diff --git a/packages/ui-components/src/Common/BaseCodeBox/index.module.css b/packages/ui-components/src/Common/BaseCodeBox/index.module.css index b74075c89c921..da0f2e11de2a0 100644 --- a/packages/ui-components/src/Common/BaseCodeBox/index.module.css +++ b/packages/ui-components/src/Common/BaseCodeBox/index.module.css @@ -6,15 +6,17 @@ overflow-x-hidden rounded-sm border - border-neutral-900 - bg-neutral-950; + border-neutral-200 + bg-white + dark:border-neutral-900 + dark:bg-neutral-950; .content { @apply m-0 min-w-0 p-4; - & > code { + code { @apply font-ibm-plex-mono font-regular scrollbar-thin @@ -26,8 +28,9 @@ p-0 text-sm leading-snug - text-neutral-400 - [counter-reset:line]; + text-neutral-800 + [counter-reset:line] + dark:text-neutral-400; & > [class='line'] { @apply relative @@ -48,9 +51,10 @@ mr-4 w-4.5 text-right - text-neutral-600 + text-neutral-700 [content:counter(line)] - [counter-increment:line]; + [counter-increment:line] + dark:text-neutral-600; } } @@ -59,7 +63,7 @@ } } - &[class*='no-line-numbers'] > code > [class='line'] { + &[class*='no-line-numbers'] code > [class='line'] { @apply pl-0; &:not(:empty:last-child)::after { @@ -74,14 +78,16 @@ items-center justify-between border-t - border-t-neutral-900 + border-t-neutral-200 px-3 py-2 text-sm - font-medium; + font-medium + dark:border-t-neutral-900; & > .language { - @apply text-neutral-400; + @apply text-neutral-800 + dark:text-neutral-400; } & > .action { diff --git a/packages/ui-components/src/Common/BaseLinkTabs/index.module.css b/packages/ui-components/src/Common/BaseLinkTabs/index.module.css index ca9292fdb8bf4..947473ce50133 100644 --- a/packages/ui-components/src/Common/BaseLinkTabs/index.module.css +++ b/packages/ui-components/src/Common/BaseLinkTabs/index.module.css @@ -29,10 +29,10 @@ } &[data-state='active'] { - @apply border-b-green-600 - text-green-600 - dark:border-b-green-400 - dark:text-green-400; + @apply border-b-brand-600 + text-brand-600 + dark:border-b-brand-400 + dark:text-brand-400; } } } diff --git a/packages/ui-components/src/Common/BasePagination/PaginationListItem/index.module.css b/packages/ui-components/src/Common/BasePagination/PaginationListItem/index.module.css index 44fe08cd5fb70..d512397c2edb2 100644 --- a/packages/ui-components/src/Common/BasePagination/PaginationListItem/index.module.css +++ b/packages/ui-components/src/Common/BasePagination/PaginationListItem/index.module.css @@ -7,7 +7,8 @@ .listItem, .listItem:link, .listItem:active { - @apply flex + @apply aria-current:bg-brand-600 + flex size-10 cursor-pointer items-center @@ -17,7 +18,6 @@ py-2.5 text-neutral-800 aria-current:cursor-default - aria-current:bg-green-600 aria-current:text-white motion-safe:transition-colors dark:text-neutral-200; diff --git a/packages/ui-components/src/Common/Blockquote/index.module.css b/packages/ui-components/src/Common/Blockquote/index.module.css index cdb4642e9f40e..53d8d3e6e8ecc 100644 --- a/packages/ui-components/src/Common/Blockquote/index.module.css +++ b/packages/ui-components/src/Common/Blockquote/index.module.css @@ -1,20 +1,20 @@ @reference "../../styles/index.css"; .wrapper { - @apply flex + @apply border-brand-600 + dark:border-brand-400 + flex max-w-2xl flex-col items-start gap-4 self-stretch border-l-2 - border-green-600 py-2 pl-5 text-lg font-semibold text-neutral-900 - dark:border-green-400 dark:text-white; & cite { diff --git a/packages/ui-components/src/Common/Breadcrumbs/BreadcrumbLink/index.module.css b/packages/ui-components/src/Common/Breadcrumbs/BreadcrumbLink/index.module.css index e708032a4bfbb..9f1e5572631c1 100644 --- a/packages/ui-components/src/Common/Breadcrumbs/BreadcrumbLink/index.module.css +++ b/packages/ui-components/src/Common/Breadcrumbs/BreadcrumbLink/index.module.css @@ -5,8 +5,8 @@ truncate; &.active { - @apply rounded - bg-green-800 + @apply bg-brand-800 + rounded px-2 py-1 font-semibold @@ -15,7 +15,7 @@ dark:text-white; &:hover { - @apply bg-green-700 + @apply bg-brand-700 text-white; } } diff --git a/packages/ui-components/src/Common/ChangeHistory/index.module.css b/packages/ui-components/src/Common/ChangeHistory/index.module.css index 3f6225b555c48..0fbfc01add394 100644 --- a/packages/ui-components/src/Common/ChangeHistory/index.module.css +++ b/packages/ui-components/src/Common/ChangeHistory/index.module.css @@ -32,7 +32,8 @@ z-50 mt-1 max-h-80 - w-52 + w-[26rem] + max-w-[calc(100vw-2rem)] overflow-hidden rounded-sm border @@ -45,7 +46,7 @@ .dropdownContentInner { @apply max-h-80 - w-52 + w-full overflow-y-auto; } @@ -63,8 +64,8 @@ &:hover, &:focus-visible { - @apply bg-green-600 - text-white; + @apply bg-brand-600 + text-white!; } } diff --git a/packages/ui-components/src/Common/CodeTabs/index.module.css b/packages/ui-components/src/Common/CodeTabs/index.module.css index 879ae26ace6b5..0c15b4775d1f0 100644 --- a/packages/ui-components/src/Common/CodeTabs/index.module.css +++ b/packages/ui-components/src/Common/CodeTabs/index.module.css @@ -1,6 +1,11 @@ @reference "../../styles/index.css"; .root { + /* `forceMount` keeps every panel in the DOM, so hide the inactive ones here */ + > [role='tabpanel'][data-state='inactive'] { + @apply hidden; + } + > [role='tabpanel'] > :first-child { @apply rounded-t-none; } @@ -10,21 +15,26 @@ rounded-t border-x border-t - border-neutral-900 - bg-neutral-950 + border-neutral-200 + bg-neutral-100 px-2 pt-2 - md:px-4; + md:px-4 + dark:border-neutral-900 + dark:bg-neutral-950; .trigger { @apply border-b border-b-transparent px-1 - text-neutral-200; + text-neutral-800 + dark:text-neutral-200; &[data-state='active'] { - @apply border-b-green-400 - text-green-400; + @apply border-b-brand-600 + text-brand-700 + dark:border-b-brand-400 + dark:text-brand-400; } } @@ -33,21 +43,25 @@ items-center gap-2 text-center - text-neutral-200 + text-neutral-800 motion-safe:transition-colors - lg:flex; + lg:flex + dark:text-neutral-200; & > .icon { @apply size-4 - text-neutral-300; + text-neutral-700 + dark:text-neutral-300; } &:is(:link, :visited) { &:hover { - @apply text-neutral-400; + @apply text-neutral-900 + dark:text-neutral-400; & > .icon { - @apply text-neutral-600; + @apply text-neutral-800 + dark:text-neutral-600; } } } diff --git a/packages/ui-components/src/Common/DataTag/index.module.css b/packages/ui-components/src/Common/DataTag/index.module.css index ef244b3765153..77d2db5c398fe 100644 --- a/packages/ui-components/src/Common/DataTag/index.module.css +++ b/packages/ui-components/src/Common/DataTag/index.module.css @@ -32,7 +32,7 @@ } &.property { - @apply bg-green-600; + @apply bg-brand-600; } &.class { diff --git a/packages/ui-components/src/Common/GlowingBackdrop/index.module.css b/packages/ui-components/src/Common/GlowingBackdrop/index.module.css index 75f47615984c0..427232f2210a7 100644 --- a/packages/ui-components/src/Common/GlowingBackdrop/index.module.css +++ b/packages/ui-components/src/Common/GlowingBackdrop/index.module.css @@ -1,7 +1,9 @@ @reference "../../styles/index.css"; .glowingBackdrop { - @apply absolute + @apply after:bg-brand-300 + dark:after:bg-brand-700 + absolute top-0 left-0 -z-10 @@ -13,11 +15,9 @@ after:aspect-square after:w-[300px] after:rounded-full - after:bg-green-300 after:blur-[120px] after:content-[''] - md:opacity-100 - dark:after:bg-green-700; + md:opacity-100; svg { @apply absolute diff --git a/packages/ui-components/src/Common/LanguageDropDown/index.module.css b/packages/ui-components/src/Common/LanguageDropDown/index.module.css index 8cbcf1b574796..c482a2963e0a3 100644 --- a/packages/ui-components/src/Common/LanguageDropDown/index.module.css +++ b/packages/ui-components/src/Common/LanguageDropDown/index.module.css @@ -35,19 +35,19 @@ } .dropDownItem { - @apply cursor-pointer + @apply data-[highlighted]:bg-brand-600 + cursor-pointer px-2.5 py-1.5 text-sm font-medium text-neutral-800 outline-hidden - data-[highlighted]:bg-green-600 data-[highlighted]:text-white dark:text-white; } .currentDropDown { - @apply bg-green-600 + @apply bg-brand-600 text-white; } diff --git a/packages/ui-components/src/Common/Preview/index.module.css b/packages/ui-components/src/Common/Preview/index.module.css index e70cfb4ca3bb5..808df784a6485 100644 --- a/packages/ui-components/src/Common/Preview/index.module.css +++ b/packages/ui-components/src/Common/Preview/index.module.css @@ -21,7 +21,7 @@ after:content-['']; &.announcements { - @apply after:from-green-700/90; + @apply after:from-brand-700/90; } &.release { diff --git a/packages/ui-components/src/Common/Search/Results/Hit/index.module.css b/packages/ui-components/src/Common/Search/Results/Hit/index.module.css index edb4bf24de14e..d1bc33ab69e02 100644 --- a/packages/ui-components/src/Common/Search/Results/Hit/index.module.css +++ b/packages/ui-components/src/Common/Search/Results/Hit/index.module.css @@ -1,7 +1,8 @@ @reference "../../../../styles/index.css"; .link { - @apply flex + @apply focus-visible:border-brand-600 + flex items-center gap-4 rounded-xl @@ -15,7 +16,6 @@ outline-none hover:bg-neutral-200 focus:bg-neutral-200 - focus-visible:border-green-600 focus-visible:bg-transparent motion-safe:transition-colors dark:bg-zinc-950 diff --git a/packages/ui-components/src/Common/Search/Results/Tabs/index.module.css b/packages/ui-components/src/Common/Search/Results/Tabs/index.module.css index 223374299130b..2cdf6ae0fcfda 100644 --- a/packages/ui-components/src/Common/Search/Results/Tabs/index.module.css +++ b/packages/ui-components/src/Common/Search/Results/Tabs/index.module.css @@ -29,9 +29,9 @@ } .facetTabItemSelected { - @apply border-2 - border-green-600 - dark:border-green-400; + @apply border-brand-600 + dark:border-brand-400 + border-2; } .facetTabsList { diff --git a/packages/ui-components/src/Common/Select/index.module.css b/packages/ui-components/src/Common/Select/index.module.css index 98f528ae283c9..fcb1e0bc68c50 100644 --- a/packages/ui-components/src/Common/Select/index.module.css +++ b/packages/ui-components/src/Common/Select/index.module.css @@ -84,12 +84,12 @@ } .text { - @apply text-neutral-800 - data-highlighted:bg-green-500 + @apply data-highlighted:bg-brand-500 + dark:data-highlighted:bg-brand-600 + text-neutral-800 data-highlighted:text-white data-highlighted:outline-hidden dark:text-neutral-200 - dark:data-highlighted:bg-green-600 dark:data-highlighted:text-white; } @@ -191,15 +191,15 @@ } .text { - @apply block + @apply hover:bg-brand-500 + dark:hover:bg-brand-600 + block pl-4 whitespace-normal text-neutral-800 - hover:bg-green-500 hover:text-white hover:outline-hidden dark:text-neutral-200 - dark:hover:bg-green-600 dark:hover:text-white; span { diff --git a/packages/ui-components/src/Common/Signature/SignatureHeader/index.module.css b/packages/ui-components/src/Common/Signature/SignatureHeader/index.module.css index da2da6867d349..67c9aec43272f 100644 --- a/packages/ui-components/src/Common/Signature/SignatureHeader/index.module.css +++ b/packages/ui-components/src/Common/Signature/SignatureHeader/index.module.css @@ -39,7 +39,7 @@ break-all; a { - @apply text-green-700 - dark:text-green-400; + @apply text-brand-700 + dark:text-brand-400; } } diff --git a/packages/ui-components/src/Common/Signature/SignatureItem/index.module.css b/packages/ui-components/src/Common/Signature/SignatureItem/index.module.css index d1554950a9370..f82326fb8d069 100644 --- a/packages/ui-components/src/Common/Signature/SignatureItem/index.module.css +++ b/packages/ui-components/src/Common/Signature/SignatureItem/index.module.css @@ -7,13 +7,27 @@ } .return { - @apply rounded-sm - bg-green-100 + @apply bg-brand-100 + rounded-sm px-4 py-3 dark:bg-neutral-900/40; } +/* + * A top-level return (not nested inside a `.children` group) attaches to the + * items above it as a full-bleed footer: it bleeds past the container padding + * and is separated by a divider rather than floating on its own. + */ +.return:not(:first-child, .children .return) { + @apply -mx-4 + -mb-3 + rounded-t-none + border-t + border-neutral-200 + dark:border-neutral-900; +} + .children { @apply relative flex diff --git a/packages/ui-components/src/Common/Signature/SignatureItem/index.tsx b/packages/ui-components/src/Common/Signature/SignatureItem/index.tsx index df079276d70dd..ef0747380b195 100644 --- a/packages/ui-components/src/Common/Signature/SignatureItem/index.tsx +++ b/packages/ui-components/src/Common/Signature/SignatureItem/index.tsx @@ -18,6 +18,7 @@ const SignatureItem: FC> = ({ children, }) => (
[data-kind='return']:only-child) { + @apply border-0 + px-0 + py-0; + } } diff --git a/packages/ui-components/src/Common/Signature/SignatureRoot/index.tsx b/packages/ui-components/src/Common/Signature/SignatureRoot/index.tsx index 79e3f145b0d84..d8762d8d57c6e 100644 --- a/packages/ui-components/src/Common/Signature/SignatureRoot/index.tsx +++ b/packages/ui-components/src/Common/Signature/SignatureRoot/index.tsx @@ -14,10 +14,15 @@ const SignatureRoot: FC> = ({ const titleId = useId(); return ( -
-
- {title} -
+
+ {title && ( +
+ {title} +
+ )}
{children}
); diff --git a/packages/ui-components/src/Common/Signature/index.tsx b/packages/ui-components/src/Common/Signature/index.tsx index e24ef4fca18cc..fb22b332279bf 100644 --- a/packages/ui-components/src/Common/Signature/index.tsx +++ b/packages/ui-components/src/Common/Signature/index.tsx @@ -21,7 +21,9 @@ const Signature: FC> = ({ title, children, }) => { - if (title) { + // A Signature without its own name or type is the grouping container, with + // an optional title. Everything else renders as an individual item. + if (!name && !type) { return {children}; } diff --git a/packages/ui-components/src/Common/SkipToContentButton/index.module.css b/packages/ui-components/src/Common/SkipToContentButton/index.module.css index cd8457908d2d3..b0359f43d7912 100644 --- a/packages/ui-components/src/Common/SkipToContentButton/index.module.css +++ b/packages/ui-components/src/Common/SkipToContentButton/index.module.css @@ -1,19 +1,19 @@ @reference "../../styles/index.css"; .skipToContent { - @apply sr-only + @apply focus:bg-brand-600 + focus:ring-brand-500 + dark:focus:ring-brand-400 + sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:rounded - focus:bg-green-600 focus:px-4 focus:py-2 focus:text-white focus:ring-2 - focus:ring-green-500 - focus:outline-none - dark:focus:ring-green-400; + focus:outline-none; } diff --git a/packages/ui-components/src/Common/Switch/index.module.css b/packages/ui-components/src/Common/Switch/index.module.css index 7d700b862b0d1..8dd9ee20085aa 100644 --- a/packages/ui-components/src/Common/Switch/index.module.css +++ b/packages/ui-components/src/Common/Switch/index.module.css @@ -34,16 +34,16 @@ } .input:focus-visible + .root { - @apply ring-2 - ring-green-500 + @apply ring-brand-500 + dark:ring-brand-400 + ring-2 ring-offset-2 ring-offset-neutral-100 - dark:ring-green-400 dark:ring-offset-neutral-900; } .input:checked + .root { - @apply bg-green-600; + @apply bg-brand-600; } .thumb { diff --git a/packages/ui-components/src/Common/TableOfContents/index.module.css b/packages/ui-components/src/Common/TableOfContents/index.module.css index eac5bd455be23..5416aaff3f998 100644 --- a/packages/ui-components/src/Common/TableOfContents/index.module.css +++ b/packages/ui-components/src/Common/TableOfContents/index.module.css @@ -41,7 +41,6 @@ .link { @apply inline-block text-sm - font-semibold text-neutral-900 underline hover:text-neutral-700 @@ -50,8 +49,7 @@ } .codeLink { - @apply font-ibm-plex-mono - font-medium; + @apply font-ibm-plex-mono; } .depthThree { diff --git a/packages/ui-components/src/Common/Tabs/index.module.css b/packages/ui-components/src/Common/Tabs/index.module.css index f700b7b1321aa..c7f3d6d32a3e3 100644 --- a/packages/ui-components/src/Common/Tabs/index.module.css +++ b/packages/ui-components/src/Common/Tabs/index.module.css @@ -42,19 +42,19 @@ } &[data-state='active'] { - @apply border-b-green-600 - text-green-600 - dark:border-b-green-400 - dark:text-green-400; + @apply border-b-brand-600 + text-brand-600 + dark:border-b-brand-400 + dark:text-brand-400; .tabExtension { - @apply border-green-400 - text-green-400; + @apply border-brand-400 + text-brand-400; } .tabSecondaryLabel { - @apply text-green-800 - dark:text-green-600; + @apply text-brand-800 + dark:text-brand-600; } } } diff --git a/packages/ui-components/src/Common/ThemeToggle/index.module.css b/packages/ui-components/src/Common/ThemeToggle/index.module.css index d1480cf221026..a9e28f2325466 100644 --- a/packages/ui-components/src/Common/ThemeToggle/index.module.css +++ b/packages/ui-components/src/Common/ThemeToggle/index.module.css @@ -28,7 +28,8 @@ } .dropDownItem { - @apply flex + @apply data-highlighted:bg-brand-600 + flex cursor-pointer items-center gap-2 @@ -38,12 +39,11 @@ font-medium text-neutral-800 outline-hidden - data-highlighted:bg-green-600 data-highlighted:text-white dark:text-white; } .activeItem { - @apply bg-green-600 + @apply bg-brand-600 text-white; } diff --git a/packages/ui-components/src/Common/ThemeToggle/index.tsx b/packages/ui-components/src/Common/ThemeToggle/index.tsx index 923aba5f90d16..401f1472e4bad 100644 --- a/packages/ui-components/src/Common/ThemeToggle/index.tsx +++ b/packages/ui-components/src/Common/ThemeToggle/index.tsx @@ -30,7 +30,7 @@ const themes: Array = ['system', 'light', 'dark']; const ThemeToggle: FC = ({ onChange = () => {}, currentTheme = 'system', - ariaLabel, + ariaLabel = 'Select theme', themeLabels = { system: 'System', light: 'Light', dark: 'Dark' }, }) => { const TriggerIcon = themeIcons[currentTheme]; diff --git a/packages/ui-components/src/Containers/Article/index.module.css b/packages/ui-components/src/Containers/Article/index.module.css index 8d3dccdfd9a8f..0dd9ce4655dbe 100644 --- a/packages/ui-components/src/Containers/Article/index.module.css +++ b/packages/ui-components/src/Containers/Article/index.module.css @@ -7,7 +7,8 @@ ml:grid-rows-[1fr] ml:overflow-visible ml:[grid-template-areas:'sidebar_main''sidebar_footer'] - 3xl:grid-cols-[--spacing(80)_1fr_--spacing(80)] + 3xl:grid-cols-[--spacing(80)_1fr_--spacing(144)] + 3xl:max-w-12xl mx-auto block w-full diff --git a/packages/ui-components/src/Containers/Footer/index.module.css b/packages/ui-components/src/Containers/Footer/index.module.css index b4f6a0f44f9b9..c285c5571f862 100644 --- a/packages/ui-components/src/Containers/Footer/index.module.css +++ b/packages/ui-components/src/Containers/Footer/index.module.css @@ -13,6 +13,7 @@ .innerContainer { @apply max-w-10xl + 3xl:max-w-12xl mx-auto flex flex-col @@ -76,13 +77,13 @@ a { @apply max-ml:font-semibold - text-green-600 - dark:text-green-400; + text-brand-600 + dark:text-brand-400; &:hover { - @apply cursor-pointer - text-green-900 - dark:text-green-200; + @apply text-brand-900 + dark:text-brand-200 + cursor-pointer; } } } diff --git a/packages/ui-components/src/Containers/FunctionSignature/index.tsx b/packages/ui-components/src/Containers/FunctionSignature/index.tsx index 0f661394643d8..ab84c0c0f5f2e 100644 --- a/packages/ui-components/src/Containers/FunctionSignature/index.tsx +++ b/packages/ui-components/src/Containers/FunctionSignature/index.tsx @@ -27,32 +27,10 @@ const renderSignature = (param: SignatureDefinition, index: number) => ( ); -const FunctionSignature: FC = ({ title, items }) => { - if (title) { - const attributes: Array = []; - const returnTypes: Array = []; - - for (const item of items) { - const target = item.kind === 'return' ? returnTypes : attributes; - - target.push(item); - } - - return ( - <> - - {attributes.map((param, i) => renderSignature(param, i))} - - - {returnTypes.length > 0 && - returnTypes.map((param, i) => - renderSignature(param, attributes.length + i) - )} - - ); - } - - return items.map((param, i) => renderSignature(param, i)); -}; +const FunctionSignature: FC = ({ title, items }) => ( + + {items.map((param, i) => renderSignature(param, i))} + +); export default FunctionSignature; diff --git a/packages/ui-components/src/Containers/MetaBar/index.module.css b/packages/ui-components/src/Containers/MetaBar/index.module.css index f06fc547e2668..e04db496a7c64 100644 --- a/packages/ui-components/src/Containers/MetaBar/index.module.css +++ b/packages/ui-components/src/Containers/MetaBar/index.module.css @@ -48,7 +48,6 @@ a { @apply max-ml:inline-block max-ml:py-1 - font-semibold text-neutral-900 underline dark:text-white; @@ -60,8 +59,7 @@ } a.codeLink { - @apply font-ibm-plex-mono - font-medium; + @apply font-ibm-plex-mono; } ol { diff --git a/packages/ui-components/src/Containers/NavBar/NavItem/index.module.css b/packages/ui-components/src/Containers/NavBar/NavItem/index.module.css index be3520a41e57f..8c1d47bb58f5c 100644 --- a/packages/ui-components/src/Containers/NavBar/NavItem/index.module.css +++ b/packages/ui-components/src/Containers/NavBar/NavItem/index.module.css @@ -6,7 +6,8 @@ gap-2 rounded-sm px-3 - py-2; + py-2 + motion-safe:transition-colors; .label { @apply text-base @@ -32,7 +33,7 @@ } &.active { - @apply bg-green-600; + @apply bg-brand-600; .label { @apply text-white; @@ -45,6 +46,19 @@ } } + &.accent { + @apply bg-transparent + ring-1 + ring-neutral-200 + ring-inset + dark:ring-neutral-900; + + &:hover { + @apply bg-neutral-200 + dark:bg-neutral-900; + } + } + &.footer { .label { @apply text-neutral-800 diff --git a/packages/ui-components/src/Containers/NavBar/NavItem/index.tsx b/packages/ui-components/src/Containers/NavBar/NavItem/index.tsx index 69faa243a455c..848e6d98c45d1 100644 --- a/packages/ui-components/src/Containers/NavBar/NavItem/index.tsx +++ b/packages/ui-components/src/Containers/NavBar/NavItem/index.tsx @@ -15,8 +15,10 @@ type NavItemProps = { type?: NavItemType; className?: string; target?: HTMLAttributeAnchorTarget | undefined; + accent?: boolean; pathname: string; + active?: boolean; as: LinkLike; }; @@ -26,12 +28,18 @@ const NavItem: FC> = ({ children, className, target, + accent, ...props }) => ( { + it('picks the most specific sibling on its own pages', () => { + assert.equal( + getActiveNavLink(navItems, '/about/sponsors'), + '/about/sponsors' + ); + }); + + it('keeps the parent active on its other sub-pages', () => { + assert.equal( + getActiveNavLink(navItems, '/about/governance/charter'), + '/about' + ); + }); + + it('matches the parent on its own index page', () => { + assert.equal(getActiveNavLink(navItems, '/about'), '/about'); + }); + + it('does not match across word boundaries', () => { + assert.equal(getActiveNavLink(navItems, '/about-us'), ''); + }); + + it('never returns an external (non-internal) link', () => { + assert.equal(getActiveNavLink(navItems, 'https://nodejs.org/learn'), ''); + }); + + it('returns empty string when nothing matches', () => { + assert.equal(getActiveNavLink(navItems, '/unknown'), ''); + }); +}); diff --git a/packages/ui-components/src/Containers/NavBar/index.module.css b/packages/ui-components/src/Containers/NavBar/index.module.css index 99775c5f02fe4..e445929b15c7e 100644 --- a/packages/ui-components/src/Containers/NavBar/index.module.css +++ b/packages/ui-components/src/Containers/NavBar/index.module.css @@ -11,6 +11,7 @@ .innerContainer { @apply max-w-10xl + 3xl:max-w-12xl xl:mx-auto xl:flex xl:h-16 @@ -39,16 +40,17 @@ } .sidebarItemTogglerLabel { - @apply block + @apply ml-auto + block cursor-pointer - xl:hidden; - - .navInteractionIcon { - @apply size-6; - } + xl:hidden; /* Push toggler to the right if Logo exists */ } } + .navInteractionIcon { + @apply size-6; + } + .sidebarItemToggler { @apply absolute right-4 @@ -62,11 +64,11 @@ .main { @apply flex-1 flex-col - justify-between gap-4 xl:flex xl:flex-row - xl:items-center; + xl:items-center + xl:justify-between; } .navItems { @@ -86,6 +88,7 @@ .actionsWrapper { @apply flex + w-full flex-row flex-wrap items-center @@ -95,6 +98,7 @@ border-neutral-200 p-4 sm:flex-nowrap + xl:ml-auto xl:basis-96 xl:border-0 xl:p-0 diff --git a/packages/ui-components/src/Containers/NavBar/index.stories.tsx b/packages/ui-components/src/Containers/NavBar/index.stories.tsx index 2c055e4a43e79..446f467d4e9a0 100644 --- a/packages/ui-components/src/Containers/NavBar/index.stories.tsx +++ b/packages/ui-components/src/Containers/NavBar/index.stories.tsx @@ -1,3 +1,5 @@ +import Logo from '#ui/Common/NodejsLogo'; +import SearchModal from '#ui/Common/Search/Modal'; import NavBar from '#ui/Containers/NavBar'; import type { Meta as MetaObj, StoryObj } from '@storybook/react-webpack5'; @@ -5,17 +7,61 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react-webpack5'; type Story = StoryObj; type Meta = MetaObj; -const common = { - as: 'a', - Logo: 'a', - pathname: '/', +export const Default: Story = { + args: { + as: 'a', + Logo, + pathname: '/', + children: , + navItems: [ + { + text: 'Learn', + link: '/', + }, + { + text: 'About', + link: '/about', + }, + { + text: 'Docs', + link: '/docs', + }, + { + text: 'Beta Docs', + link: 'https://beta.docs.nodejs.org/', + target: '_blank', + accent: true, + }, + { + text: 'Download', + link: '/download', + }, + { + text: 'Blog', + link: '/blog', + }, + { + text: 'Certification', + link: 'https://openjsf.org/certification', + }, + ], + }, +}; - children: Some other child, -} as const; +export const NoNavItems: Story = { + args: { + as: 'a', + Logo, + pathname: '/', + children: , + }, +}; -export const Default: Story = { +export const NoLogo: Story = { args: { - ...common, + as: 'a', + pathname: '/', + children: , navItems: [ { text: 'Learn', @@ -45,6 +91,12 @@ export const Default: Story = { }, }; -export const NoNavItems: Story = { args: common }; +export const NoLogoNoNavItems: Story = { + args: { + as: 'a', + pathname: '/', + children: , + }, +}; export default { component: NavBar } as Meta; diff --git a/packages/ui-components/src/Containers/NavBar/index.tsx b/packages/ui-components/src/Containers/NavBar/index.tsx index 0d9404b27c521..b2eac53f34ffd 100644 --- a/packages/ui-components/src/Containers/NavBar/index.tsx +++ b/packages/ui-components/src/Containers/NavBar/index.tsx @@ -21,13 +21,26 @@ const navInteractionIcons = { close: , }; +// Picks the longest nav link that prefixes the current path, so `/about` and `/about/sponsors` never highlight together. +export const getActiveNavLink = ( + navItems: Array<{ link: string }>, + pathname: string +): string => + navItems.reduce((longest, { link }) => { + const isMatch = + link.startsWith('/') && + (pathname === link || pathname.startsWith(`${link}/`)); + return isMatch && link.length > longest.length ? link : longest; + }, ''); + type NavbarProps = { navItems?: Array<{ text: FormattedMessage; link: string; target?: HTMLAttributeAnchorTarget | undefined; + accent?: boolean; }>; - Logo: ElementType; + Logo?: ElementType; as: LinkLike; pathname: string; sidebarItemTogglerAriaLabel: string; @@ -43,17 +56,21 @@ const NavBar: FC> = ({ }) => { const [isMenuOpen, setIsMenuOpen] = useState(false); + const activeLink = getActiveNavLink(navItems ?? [], pathname); + return (