Skip to content

Sync dev into main: async data safeguards, v0.7.0, npm Trusted Publishing - #19

Merged
cmm-cmm merged 6 commits into
mainfrom
dev
Aug 2, 2026
Merged

Sync dev into main: async data safeguards, v0.7.0, npm Trusted Publishing#19
cmm-cmm merged 6 commits into
mainfrom
dev

Conversation

@cmm-cmm

@cmm-cmm cmm-cmm commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Summary

main was 3 commits behind dev. Syncing per request — bringing dev's latest work into main:

  • 8f28a23 — Add async data safeguards and cut 0.7.0: substantial feature work (tree-select children, dropdown portaling, sortable tags, required validation, beforeSelect/beforeUnselect/beforeCreate guards, createOption, sanitizeTemplate, custom search scoring/highlighting, AJAX pagination/retry/prefetch/caching safeguards, RTL-safe CSS logical properties, new e2e coverage) plus the 0.7.0 version bump, CHANGELOG entry, and updated docs.
  • 40a35b2 — Use npm trusted publishing for releases: migrates release.yml from a long-lived NPM_TOKEN secret to npm's OIDC-based Trusted Publishing (id-token: write, npm CLI ≥11.15.0).
  • e94bfa7 — Fix OIDC npm publish invocation: follow-up fix restoring --provenance --access public on the npm publish calls under the new trusted-publishing flow.

Test plan

  • All three commits already have green CI on dev (typecheck, lint, format, coverage, workspace build/test, site build + validation, npm pack validation, npm audit, Playwright across Chromium/Firefox/WebKit, benchmark budgets).
  • Re-ran npm run typecheck and npm run lint locally against dev's tip — clean.
  • ⚠️ Needs verification after merge: npm Trusted Publishing must be configured on the npmjs.com side (registry → package settings → Trusted Publisher, pointing at this repo's release.yml workflow) for the next release to actually publish — this is an out-of-band step only the account owner can complete, not verifiable from CI.

Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added cursor-based pagination for remote data.
    • Added selection and creation guards, asynchronous option creation, and configurable missing or duplicate selection handling.
    • Added template sanitization and improved mobile, RTL, and IME input behavior.
    • Improved virtual scrolling, search performance, and lifecycle cleanup.
  • Documentation

    • Expanded API and usage guidance for new options and pagination.
  • Bug Fixes

    • Improved mobile header navigation and dropdown positioning across screen sizes.
  • Performance

    • Added benchmark checks for startup, search, rendering, bundle size, and cleanup budgets.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 2, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
forge-select 7b2a9f7 Commit Preview URL

Branch Preview URL
Aug 02 2026, 05:48 AM

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@cmm-cmm, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 69ef13e3-1be3-49d7-b82b-e9c2e2e8f398

📥 Commits

Reviewing files that changed from the base of the PR and between 4b40c95 and 7b2a9f7.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • .github/workflows/release.yml
  • packages/react/CHANGELOG.md
  • packages/vue/CHANGELOG.md
📝 Walkthrough

Walkthrough

ForgeSelect 0.7.0 adds guarded selection and asynchronous option creation, template sanitization, cursor-based remote pagination, indexed and virtualized rendering, lifecycle benchmark budgets, mobile layout fixes, and npm Trusted Publishing configuration.

Changes

ForgeSelect 0.7.0

Layer / File(s) Summary
Selection, creation, and rendering contracts
src/types.ts, src/ForgeSelect.ts, src/option-renderer.ts, src/index.ts, tests/forge-select.test.ts, docs/api-reference.md, docs/examples.md
Adds public types and options for sanitization, selection guards, asynchronous creation, missing selections, and duplicate values. Updates selection, creation, rendering, indexing, IME handling, tests, and documentation.
Cursor-based remote loading
src/remote.ts, src/ForgeSelect.ts, src/types.ts, tests/forge-select.test.ts, docs/api-reference.md, docs/examples.md
Adds cursor-aware request callbacks, result normalization, caching, request deduplication, prefetch cancellation, and accumulated pagination results.
Performance and lifecycle controls
src/ForgeSelect.ts, scripts/benchmark.mjs, docs/benchmarks.md, package.json, .github/workflows/ci.yml
Adds memoized tree search, binary-search virtualization, row recycling, cleanup tracking, benchmark budgets, and CI budget checks.
Mobile positioning and navigation
src/ForgeSelect.ts, styles/forge-select.css, site/assets/site.css, tests/e2e/forge-select.spec.ts, tests/e2e/site-navigation.spec.ts
Updates visual viewport positioning, logical directional styles, responsive header behavior, and mobile RTL layout tests.
Release and publishing updates
.github/workflows/release.yml, package.json, CHANGELOG.md, packages/react/*, packages/vue/*
Sets package versions, updates development and package dependencies, documents releases, and configures npm Trusted Publishing with Node.js 24 and public package access.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ForgeSelect
  participant remote_ts as remote.ts
  participant AjaxRequest as AjaxConfig.request
  User->>ForgeSelect: request more remote options
  ForgeSelect->>remote_ts: load current cursor
  remote_ts->>AjaxRequest: pass query, page, signal, and cursor
  AjaxRequest-->>remote_ts: return options and nextCursor
  remote_ts-->>ForgeSelect: return normalized result
  ForgeSelect->>ForgeSelect: cache cursor and append options
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the branch synchronization, async safeguards, 0.7.0 release, and npm Trusted Publishing changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/ForgeSelect.ts (1)

1175-1237: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

beforeSelect guard is bypassed when a created tag matches an existing option by label or value.

activateNavItem invokes this.opts.beforeSelect?.(item.option) before selecting an option from the list. createTag's "existing option" branch (matched by label) and addCreatedOption's "duplicate" branch (matched by value) both call this.selectValue(...) directly, without checking beforeSelect.

This means the beforeSelect guard is not enforced when a user pastes text, or presses Enter with allowCreate, that exactly matches an existing option's label/value. The new docs/examples.md guard example (beforeSelect: (option) => !option.meta?.archived) is specifically intended to block selecting an archived option — but a user who types that archived option's exact label and hits "Enter" (or pastes it) bypasses the guard entirely, since createTag/addCreatedOption select it directly. This affects both the paste-multi-create handler (lines 843-861) and createFromQuery (lines 1239-1266), both of which route through createTag.

Invoke beforeSelect in both "existing" branches, consistent with activateNavItem.

🐛 Proposed fix
   private createTag(label: string): TagCreation | undefined | Promise<TagCreation | undefined> {
     const trimmed = label.trim();
     if (!trimmed) return undefined;
     const existing = this.findOptionByLabel(trimmed);
     if (existing) {
       if (this.selected.includes(existing.value)) return undefined;
       if (this.opts.multiple && !this.canSelectOption(existing)) {
         this.announceMaximum(existing);
         return undefined;
       }
+      if (this.opts.beforeSelect?.(existing) === false) return undefined;
       this.selectValue(existing.value, false);
       return { option: existing, created: false };
     }
     ...
   private addCreatedOption(option: Option): TagCreation | undefined {
     if (this.opts.multiple && !this.canSelectOption(option)) {
       this.announceMaximum(option);
       return undefined;
     }
     const duplicate = this.findOption(option.value);
     if (duplicate) {
       if (this.selected.includes(duplicate.value)) return undefined;
+      if (this.opts.beforeSelect?.(duplicate) === false) return undefined;
       this.selectValue(duplicate.value, false);
       return { option: duplicate, created: false };
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ForgeSelect.ts` around lines 1175 - 1237, Enforce the beforeSelect guard
in both existing-option paths: update createTag’s label-matched branch and
addCreatedOption’s value-duplicate branch to invoke
this.opts.beforeSelect?.(option) before selecting, and abort when it rejects.
Preserve the existing duplicate, maximum-selection, and return-value behavior
for allowed selections.
🧹 Nitpick comments (4)
.github/workflows/release.yml (1)

100-102: 🔒 Security & Privacy | 🔵 Trivial

Verify npm Trusted Publishing for every package target.

These commands now rely on OIDC instead of NPM_TOKEN. Configure and test the npm Trusted Publisher relationship separately for forge-select, forge-select-react, and forge-select-vue, using repository cmm-cmm/ForgeSelect and workflow .github/workflows/release.yml. id-token: write enables the token request but does not create the npm-side relationship. npm also requires the configured workflow identity to match exactly. (docs.npmjs.com)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 100 - 102, Verify and configure
npm Trusted Publisher relationships for each release target—forge-select,
forge-select-react, and forge-select-vue—using repository cmm-cmm/ForgeSelect
and workflow .github/workflows/release.yml, then test each corresponding npm
publish path. Ensure the npm-side workflow identity matches exactly; retain the
existing publish commands and id-token permission.
src/types.ts (1)

29-37: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

AjaxConfig.url function form can't receive the new pagination cursor. request and params both gained an optional cursor parameter for cursor-based pagination, but url's callback signature was left at (query: string, page: number) => string, so the buildUrl consumer has no cursor to forward when url is a function.

  • src/types.ts#L29-L37: extend url?: string | ((query: string, page: number, cursor?: string) => string); to match request/params.
  • src/remote.ts#L3-L11: once the type is extended, pass cursor through in return ajax.url(query, page, cursor);.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/types.ts` around lines 29 - 37, Extend the AjaxConfig.url callback
signature in src/types.ts (lines 29-37) to accept an optional cursor, and update
buildUrl in src/remote.ts (lines 3-11) to forward cursor when invoking ajax.url;
keep string URL handling unchanged.
src/ForgeSelect.ts (1)

1682-1790: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reduce renderRow's cognitive complexity.

Static analysis reports cognitive complexity of 25 against a limit of 15 for renderRow. The switch handles nine row kinds inline with repeated attribute setup. Extract each case body (or at least the "option" case, which is the largest) into a dedicated private method (for example renderOptionRow(li, row)) to bring the function under the limit and make each row kind independently testable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ForgeSelect.ts` around lines 1682 - 1790, Reduce cognitive complexity in
renderRow by extracting the inline row-kind rendering logic, especially the
large "option" branch, into dedicated private helpers such as renderOptionRow
and corresponding helpers for other cases as needed. Keep renderRow responsible
for element reset, dispatching by row.kind, and returning the element, while
preserving all existing attributes, classes, selection, highlighting, and
expansion behavior.

Source: Linters/SAST tools

tests/forge-select.test.ts (1)

192-207: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add regression coverage for beforeSelect during tag creation.

This test exercises beforeSelect only through a direct list click (optionEls()[0].click()). It doesn't cover the paste or Enter-to-create path where the typed/pasted label matches an existing option — which is exactly the scenario flagged in src/ForgeSelect.ts (createTag/addCreatedOption, lines 1175-1237), where beforeSelect is currently not invoked. Once that fix lands, add a case where select.setSearchQuery(existingBlockedLabel) followed by "Enter" (or a paste of that label) is expected to be rejected by beforeSelect.

Would you like me to draft this test?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/forge-select.test.ts` around lines 192 - 207, Extend the “supports
selection guards and async tag creation” test to cover creation via Enter or
paste when the query matches the existing blocked option. Set the search query
to the blocked label, trigger the create flow, and assert beforeSelect rejects
it by keeping getValue() null; retain the existing successful async creation
assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Line 43: Update the npm installation command in the release workflow to use an
exact reviewed npm version instead of the ^11.15.0 semver range, and add
--ignore-scripts to prevent lifecycle scripts from running during this
privileged publish path.

In `@CHANGELOG.md`:
- Line 10: Complete the changelog reference definitions for the 0.7.0 release:
add a matching [0.7.0] link targeting the v0.6.0...v0.7.0 comparison, and update
the [Unreleased] reference to start at v0.7.0 so it excludes the released
changes.

In `@scripts/benchmark.mjs`:
- Around line 40-48: Update the initialization sampling loop around ForgeSelect
and initSamples to verify DOM cleanup after every mount/destroy pair. Capture
the relevant selector count before creating each instance and after
instance.destroy() and mount.remove(), then assert or record that the count
returns to its prior value; ensure the baseline used by
residualNodesAfterDestroy cannot include leaks from earlier samples.

In `@src/ForgeSelect.ts`:
- Around line 1682-1697: Update the attribute-reset list in renderRow to include
data-selection-state, ensuring recycled rows remove stale indeterminate state
before new row attributes are applied.

In `@styles/forge-select.css`:
- Line 269: Keep the logical margin in styles/forge-select.css unchanged, and
update the forge-select DOM setup to copy the mounted component’s direction to
the .forge-select--portal-host when dropdownParent is document.body. In
tests/e2e/forge-select.spec.ts lines 72-94, add an assertion that the portal
host’s computed direction is rtl; no direct CSS change is needed.

---

Outside diff comments:
In `@src/ForgeSelect.ts`:
- Around line 1175-1237: Enforce the beforeSelect guard in both existing-option
paths: update createTag’s label-matched branch and addCreatedOption’s
value-duplicate branch to invoke this.opts.beforeSelect?.(option) before
selecting, and abort when it rejects. Preserve the existing duplicate,
maximum-selection, and return-value behavior for allowed selections.

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 100-102: Verify and configure npm Trusted Publisher relationships
for each release target—forge-select, forge-select-react, and
forge-select-vue—using repository cmm-cmm/ForgeSelect and workflow
.github/workflows/release.yml, then test each corresponding npm publish path.
Ensure the npm-side workflow identity matches exactly; retain the existing
publish commands and id-token permission.

In `@src/ForgeSelect.ts`:
- Around line 1682-1790: Reduce cognitive complexity in renderRow by extracting
the inline row-kind rendering logic, especially the large "option" branch, into
dedicated private helpers such as renderOptionRow and corresponding helpers for
other cases as needed. Keep renderRow responsible for element reset, dispatching
by row.kind, and returning the element, while preserving all existing
attributes, classes, selection, highlighting, and expansion behavior.

In `@src/types.ts`:
- Around line 29-37: Extend the AjaxConfig.url callback signature in
src/types.ts (lines 29-37) to accept an optional cursor, and update buildUrl in
src/remote.ts (lines 3-11) to forward cursor when invoking ajax.url; keep string
URL handling unchanged.

In `@tests/forge-select.test.ts`:
- Around line 192-207: Extend the “supports selection guards and async tag
creation” test to cover creation via Enter or paste when the query matches the
existing blocked option. Set the search query to the blocked label, trigger the
create flow, and assert beforeSelect rejects it by keeping getValue() null;
retain the existing successful async creation assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8928e7ee-5fd4-4319-967f-21f4db01ad98

📥 Commits

Reviewing files that changed from the base of the PR and between 9e2ba81 and e94bfa7.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (20)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • CHANGELOG.md
  • docs/api-reference.md
  • docs/benchmarks.md
  • docs/examples.md
  • package.json
  • packages/react/CHANGELOG.md
  • packages/vue/CHANGELOG.md
  • scripts/benchmark.mjs
  • site/assets/site.css
  • src/ForgeSelect.ts
  • src/index.ts
  • src/option-renderer.ts
  • src/remote.ts
  • src/types.ts
  • styles/forge-select.css
  • tests/e2e/forge-select.spec.ts
  • tests/e2e/site-navigation.spec.ts
  • tests/forge-select.test.ts

Comment thread .github/workflows/release.yml Outdated
Comment thread CHANGELOG.md
Comment thread scripts/benchmark.mjs
Comment thread src/ForgeSelect.ts
Comment thread styles/forge-select.css
.forge-select__option--indeterminate::after {
content: "−";
margin-left: auto;
margin-inline-start: auto;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Propagate the source direction to the portal host.

When dropdownParent is document.body, the portal host is outside the <main dir="rtl"> subtree. It inherits LTR direction. Line 269 then resolves margin-inline-start as LTR, and the test passes without testing RTL portal content.

  • styles/forge-select.css#L269-L269: Keep the logical margin, but copy the mounted component direction to the portal host during DOM setup.
  • tests/e2e/forge-select.spec.ts#L72-L94: Assert that .forge-select--portal-host has computed direction: rtl.
Proposed fix
 if (portalParent) {
   this.portalHost = document.createElement("div");
   this.portalHost.className = "forge-select forge-select--portal-host";
+  this.portalHost.style.direction = getComputedStyle(this.root).direction;
   this.portalHost.dataset.theme = this.opts.theme;
   const box = await page.locator(".forge-select--portal-host").boundingBox();
   if (!box) throw new Error("Portal geometry was unavailable");
+  await expect(page.locator(".forge-select--portal-host")).toHaveCSS("direction", "rtl");
   expect(box.x).toBeGreaterThanOrEqual(0);
📍 Affects 2 files
  • styles/forge-select.css#L269-L269 (this comment)
  • tests/e2e/forge-select.spec.ts#L72-L94
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@styles/forge-select.css` at line 269, Keep the logical margin in
styles/forge-select.css unchanged, and update the forge-select DOM setup to copy
the mounted component’s direction to the .forge-select--portal-host when
dropdownParent is document.body. In tests/e2e/forge-select.spec.ts lines 72-94,
add an assertion that the portal host’s computed direction is rtl; no direct CSS
change is needed.

cmm-cmm added 2 commits August 2, 2026 01:55
- Enforce beforeSelect when createTag/addCreatedOption match an existing
  option by label or value (paste-multi-create bypassed the guard).
- Clear stale data-selection-state on recycled dropdown rows.
- Extract renderRow's option branch into renderOptionRow to cut cognitive
  complexity.
- Pin release.yml's npm CLI to an exact version and add --ignore-scripts.
- Propagate the mounted component's direction to the portalled dropdown
  host so RTL layouts render correctly outside the source subtree.
- Forward the pagination cursor to AjaxConfig.url's function form.
- Check DOM cleanup after every init sample in the benchmark script so a
  destroy leak tied to large datasets can't hide in the baseline.
- Complete the CHANGELOG 0.7.0 reference links.
Bump the forge-select dependency to ^0.7.0 so the new selection-guard,
async-creation, sanitization, and cursor-pagination options are exposed
through the existing typed props/options surface.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.github/workflows/release.yml (2)

98-102: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Complete npm Trusted Publisher setup before release.

Configure an npm Trusted Publisher for core, packages/react, and packages/vue to trust this repository and workflow. This publish step relies only on id-token: write and npm publish --provenance; without the registry-side Trusted Publishing allowlist, release publishes will fail.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 98 - 102, Complete npm Trusted
Publisher configuration for the core, packages/react, and packages/vue packages,
allowing this repository and release workflow to publish each package. Ensure
the registry-side trust allowlist matches the workflow identity used by the npm
publish commands and existing id-token: write/provenance setup.

31-35: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pin release actions to immutable commit SHAs.

actions/checkout@v6 and actions/setup-node@v6 are mutable references, and this workflow has id-token: write to publish to npm. Pin both actions to full commit SHAs, plus the npm CLI install to a pinned URL/checksum, then keep the current tag as a reviewed dependency comment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 31 - 35, Update the release
workflow’s actions/checkout and actions/setup-node steps to use reviewed full
commit SHAs instead of version tags, retaining each current tag in a dependency
comment. Replace the npm CLI installation with a pinned URL and checksum,
preserving the existing release behavior and id-token permissions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 43-44: Update the global npm installation step to use the
checked-in dist.integrity checksum for npm@11.15.0, ensuring the package is
verified rather than only version-pinned. Keep the subsequent npm --version
check and make it fail unless the installed version is exactly 11.15.0;
alternatively, use a repository-controlled artifact that preserves the required
Trusted Publishing flow.

In `@packages/react/CHANGELOG.md`:
- Line 14: Update the changelog dependency wording in
packages/react/CHANGELOG.md:14-14 and packages/vue/CHANGELOG.md:14-14 to state
that forge-select versions in the 0.7.x range are required, matching the ^0.7.0
dependency constraint; make the same wording change at both affected sites.

---

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 98-102: Complete npm Trusted Publisher configuration for the core,
packages/react, and packages/vue packages, allowing this repository and release
workflow to publish each package. Ensure the registry-side trust allowlist
matches the workflow identity used by the npm publish commands and existing
id-token: write/provenance setup.
- Around line 31-35: Update the release workflow’s actions/checkout and
actions/setup-node steps to use reviewed full commit SHAs instead of version
tags, retaining each current tag in a dependency comment. Replace the npm CLI
installation with a pinned URL and checksum, preserving the existing release
behavior and id-token permissions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0738e0b7-7af6-40c6-9ac7-9c67214cde37

📥 Commits

Reviewing files that changed from the base of the PR and between e94bfa7 and 4b40c95.

📒 Files selected for processing (12)
  • .github/workflows/release.yml
  • CHANGELOG.md
  • packages/react/CHANGELOG.md
  • packages/react/package.json
  • packages/vue/CHANGELOG.md
  • packages/vue/package.json
  • scripts/benchmark.mjs
  • src/ForgeSelect.ts
  • src/remote.ts
  • src/types.ts
  • tests/e2e/forge-select.spec.ts
  • tests/forge-select.test.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • tests/e2e/forge-select.spec.ts
  • CHANGELOG.md
  • tests/forge-select.test.ts
  • scripts/benchmark.mjs
  • src/remote.ts
  • src/types.ts
  • src/ForgeSelect.ts

Comment on lines +43 to +44
npm install --global --ignore-scripts npm@11.15.0
npm --version

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

expected='11.15.0'
test "$(npm view "npm@${expected}" version)" = "$expected"
npm view "npm@${expected}" engines dist.integrity --json

Repository: cmm-cmm/ForgeSelect

Length of output: 958


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== release workflow relevant actions =="
sed -n '1,90p' .github/workflows/release.yml || true

echo
echo "== package metadata =="
for f in package.json package-lock.json; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    sed -n '1,80p' "$f"
  else
    echo "--- $f: not found ---"
  fi
done

Repository: cmm-cmm/ForgeSelect

Length of output: 8032


Pin the global npm install to a checked-in integrity checksum.

npm@11.15.0 is exact, but npm install --global still resolves the CLI executable from the public registry. Install it with the checked-in dist.integrity digest, fail if npm --version is not 11.15.0, or switch to a repository-controlled artifact that supports the required Trusted Publishing flow.

🧰 Tools
🪛 zizmor (1.28.0)

[warning] 43-43: ad-hoc installation of packages (adhoc-packages): installs a package outside of a lockfile

(adhoc-packages)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 43 - 44, Update the global npm
installation step to use the checked-in dist.integrity checksum for npm@11.15.0,
ensuring the package is verified rather than only version-pinned. Keep the
subsequent npm --version check and make it fail unless the installed version is
exactly 11.15.0; alternatively, use a repository-controlled artifact that
preserves the required Trusted Publishing flow.

Source: Linters/SAST tools

Comment thread packages/react/CHANGELOG.md Outdated
- package-lock.json was out of sync after bumping the wrapper packages'
  forge-select dependency to ^0.7.0, breaking npm ci in CI.
- Pin actions/checkout and actions/setup-node in release.yml to reviewed
  commit SHAs instead of a floating major tag, since this workflow holds
  id-token: write for npm publishing.
- Correct the react/vue changelog wording: ^0.7.0 pins the 0.7.x range,
  not "0.7.0 or newer".
@sonarqubecloud

sonarqubecloud Bot commented Aug 2, 2026

Copy link
Copy Markdown

@cmm-cmm
cmm-cmm merged commit 8913202 into main Aug 2, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant