Skip to content

Conversation

@posva
Copy link
Owner

@posva posva commented Dec 15, 2025

Summary by CodeRabbit

  • New Features
    • Full Mutations inspector: searchable/grouped list, per-mutation details view, resizable panes, GC progress, simulate loading/error, replay/remove, and a Mutations Test page with multiple mutation scenarios (sequential/parallel/batch).
  • Chores
    • Runtime mutation tracking added with corresponding UI entries, events and payloads; mutation cache exposed for debugging; unified key formatting for display; new status/color mappings and hooks for mutation entries.
  • Documentation
    • Clarified mutation untracking, GC timing and devtools payload/metadata.

✏️ Tip: You can customize this high-level summary in your review settings.

@netlify
Copy link

netlify bot commented Dec 15, 2025

Deploy Preview for funny-banoffee-0afb46 ready!

Name Link
🔨 Latest commit 0f3a56c
🔍 Latest deploy log https://app.netlify.com/projects/funny-banoffee-0afb46/deploys/6941c9dcec553d000801bf45
😎 Deploy Preview https://deploy-preview-439--funny-banoffee-0afb46.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Walkthrough

Adds mutation support across DevTools: types/serializers for mutation entries, plugin wiring to track/serialize mutations, RPC emits for mutation events, mutationCache initialization/exposure, panel injection and UI (list/detail), demo page/route, route typings, and related components/composables. (28 words)

Changes

Cohort / File(s) Summary
Global typings & shared exports
devtools/components.d.ts, devtools/src/shared/index.ts
Add global component typings for new icons/components (ILucideCheckLine, ILucideCircle, ILucideFileQuestion, ILucideInbox, ILucideRepeat2, ILucideTrash2, EntryListKey, ListMutationEntry) and re-export mutation-serialized.
Serialized mutation payloads & devtools-info types
devtools/src/shared/mutation-serialized.ts, devtools/src/shared/plugins/devtools-info.ts
New UseMutationEntryPayload and options; add EntryDevtoolsInfo, UseMutationDevtoolsInfo, extend query history typing; augment @pinia/colada types to attach devtools info to UseMutationEntry.
RPC / bridge typings & emits
devtools/src/shared/rpc/index.ts
Add mutation-related RPC emits/types and typed payloads (mutations:all, mutations:update, mutations:delete, mutations:clear, mutations:remove, simulate/loading/error events, mutations:replay).
Devtools core plugin & wiring
devtools/src/pc-devtools-info-plugin.ts, devtools/src/PiniaColadaDevtools.vue, devtools/src/PCDevtoolsWrapper.vue
Add createMutationEntryPayload and addDevtoolsInfoForMutations; accept MutationCache, track mutation lifecycle via $onAction, serialize mutation entries, emit/listen mutation events, and initialize mutationCache alongside queryCache.
Panel plumbing & injection
devtools/src/panel/composables/duplex-channel.ts, devtools/src/panel/DevtoolsPanel.ce.vue
Introduce MUTATIONS_KEY injection key and useMutationEntries(); provide mutations ref in panel and handle duplex-channel mutation events (mutations:all, mutations:update, mutations:delete).
List entry UI, utils & colors
devtools/src/panel/components/ListMutationEntry.vue, devtools/src/panel/utils/mutation-state.ts
New ListMutationEntry component; implement getMutationStatus, STATUS_COLOR_CLASSES, status rendering, anonymous/key formatting, GC progress indicator, and router navigation behaviors.
Mutations pages & routing
devtools/src/panel/pages/mutations.vue, devtools/src/panel/pages/mutations/(unselected).vue, devtools/src/panel/pages/mutations/[mutationId].vue, devtools/typed-router.d.ts
Add searchable/grouped mutations list, unselected placeholder, detailed mutation inspector with simulate/replay/remove controls, and typed route entries for mutation routes.
App integration & demo
devtools/src/app/main.ts, devtools/src/app/app.vue, devtools/src/app/pages/mutations-test.vue, devtools/src/app/pages/contacts/[id].vue, package.json
Initialize/expose mutationCache, add /mutations-test route and nav link and demo page, add mutation key resolver for contacts page.
Entries formatting & ListQuery adjustments
devtools/src/panel/composables/entries.ts, devtools/src/panel/components/ListQueryEntry.vue
Add useFormattedKey composable; replace inline key formatting in ListQueryEntry and adjust layout/wrapping classes.
Library mutation API & types
src/mutation-store.ts, src/use-mutation.ts, src/mutation-options.ts, src/index.ts, src/mutation-store.spec.ts, src/mutation-store.spec.ts
Introduce UseMutationOptionsWithDefaults and USE_MUTATION_DEFAULTS usage; change UseMutationEntry.options and creation/reset flows to use merged defaults; update exports and tests to the defaults-aware types.
Shared helpers & minor UI tweaks
devtools/src/shared/rpc/index.ts, devtools/src/panel/pages/queries/[queryId].vue, devtools/src/panel/pages/queries.vue, devtools/src/panel/components/ListQueryEntry.vue
Re-export mutation serialized types; adjust TIME_AGO_OPTIONS, add no-results UI, switch to immutable sorting, and minor layout/class tweaks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Extra attention:
    • Consistency between createMutationEntryPayload output and UseMutationEntryPayload typings.
    • Symmetry of RPC event names/payloads vs. panel listeners and duplex-channel handling.
    • Provisioning and consumption of MUTATIONS_KEY / useMutationEntries().
    • Replay/simulate handlers and GC timing/progress logic in list/detail components.
    • MutationCache initialization/exposure and interplay with existing queryCache wiring.

Possibly related PRs

  • Mutations Store #238 — Touches mutation-store and use-mutation APIs; closely related to mutation entry shapes and options handling.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(devtools): mutations' clearly and concisely describes the main change: adding mutations feature to devtools. It directly maps to the primary objective evident across the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/devtools-mutations

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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 15, 2025

Open in StackBlitz

@pinia/colada

npm i https://pkg.pr.new/@pinia/colada@439

@pinia/colada-devtools

npm i https://pkg.pr.new/@pinia/colada-devtools@439

@pinia/colada-nuxt

npm i https://pkg.pr.new/@pinia/colada-nuxt@439

@pinia/colada-plugin-auto-refetch

npm i https://pkg.pr.new/@pinia/colada-plugin-auto-refetch@439

@pinia/colada-plugin-cache-persister

npm i https://pkg.pr.new/@pinia/colada-plugin-cache-persister@439

@pinia/colada-plugin-debug

npm i https://pkg.pr.new/@pinia/colada-plugin-debug@439

@pinia/colada-plugin-delay

npm i https://pkg.pr.new/@pinia/colada-plugin-delay@439

@pinia/colada-plugin-retry

npm i https://pkg.pr.new/@pinia/colada-plugin-retry@439

commit: 0f3a56c

@codecov
Copy link

codecov bot commented Dec 15, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.18%. Comparing base (4879189) to head (2a85cb7).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #439   +/-   ##
=======================================
  Coverage   86.18%   86.18%           
=======================================
  Files          17       17           
  Lines         579      579           
  Branches      151      151           
=======================================
  Hits          499      499           
  Misses         47       47           
  Partials       33       33           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
devtools/src/shared/plugins/devtools-info.ts (1)

56-60: Mutation devtools typing matches existing query pattern.

UseMutationDevtoolsInfo plus the UseMutationEntry augmentation via DEVTOOLS_INFO_KEY look correct and consistent; you might optionally extract the 'error' | 'loading' | null union into a shared alias to avoid duplication.

Also applies to: 71-74

devtools/src/panel/pages/mutations.vue (2)

2-3: Unused imports.

ComponentPublicInstance (line 3) and useRoute (line 11) are imported but not used.

-import { computed, ref, useTemplateRef } from 'vue'
-import type { ComponentPublicInstance } from 'vue'
+import { computed, ref, useTemplateRef } from 'vue'

108-112: Consider adding an empty state.

When filteredItems is empty (no mutations or no search matches), the list renders nothing. A placeholder message would improve UX.

devtools/src/app/pages/mutations-test.vue (1)

211-212: Margin hack for devtools space.

The mb-[700px] works but consider using min-h-screen or a dedicated spacer component for cleaner intent.

devtools/src/pc-devtools-info-plugin.ts (1)

185-217: Consider updating timestamp after mutation completes.

Line 209 updates updatedAt at the start of mutate. For consistency with setEntryState (which uses after()), consider whether you also want to update updatedAt after the mutation resolves/rejects.

     } else if (name === 'mutate') {
       const [entry] = args
       entry[DEVTOOLS_INFO_KEY].updatedAt = now()
+      after(() => {
+        entry[DEVTOOLS_INFO_KEY].updatedAt = now()
+      })
     } else if (name === 'setEntryState') {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 29d8817 and 591908b.

📒 Files selected for processing (18)
  • devtools/components.d.ts (4 hunks)
  • devtools/src/PiniaColadaDevtools.vue (5 hunks)
  • devtools/src/app/app.vue (1 hunks)
  • devtools/src/app/main.ts (3 hunks)
  • devtools/src/app/pages/contacts/[id].vue (2 hunks)
  • devtools/src/app/pages/mutations-test.vue (1 hunks)
  • devtools/src/panel/DevtoolsPanel.ce.vue (2 hunks)
  • devtools/src/panel/components/ListMutationEntry.vue (1 hunks)
  • devtools/src/panel/composables/duplex-channel.ts (2 hunks)
  • devtools/src/panel/pages/mutations.vue (1 hunks)
  • devtools/src/panel/pages/mutations/(unselected).vue (1 hunks)
  • devtools/src/panel/pages/mutations/[mutationId].vue (1 hunks)
  • devtools/src/panel/utils/mutation-state.ts (1 hunks)
  • devtools/src/pc-devtools-info-plugin.ts (2 hunks)
  • devtools/src/shared/mutation-serialized.ts (1 hunks)
  • devtools/src/shared/plugins/devtools-info.ts (2 hunks)
  • devtools/src/shared/rpc/index.ts (3 hunks)
  • devtools/typed-router.d.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
devtools/src/shared/mutation-serialized.ts (1)
devtools/src/shared/plugins/devtools-info.ts (1)
  • DEVTOOLS_INFO_KEY (7-7)
devtools/src/shared/rpc/index.ts (3)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-54)
src/mutation-store.ts (1)
  • UseMutationEntryFilter (92-92)
src/entry-keys.ts (1)
  • EntryKey (88-88)
devtools/src/panel/utils/mutation-state.ts (1)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-54)
devtools/src/pc-devtools-info-plugin.ts (2)
devtools/src/shared/plugins/devtools-info.ts (1)
  • DEVTOOLS_INFO_KEY (7-7)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-54)
devtools/src/panel/composables/duplex-channel.ts (1)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-54)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build
  • GitHub Check: autofix
  • GitHub Check: build
🔇 Additional comments (24)
devtools/src/panel/pages/mutations/(unselected).vue (1)

1-10: Unselected mutations placeholder is fine.

Purely presentational SFC; no logic or state to validate.

devtools/src/panel/utils/mutation-state.ts (1)

1-58: Mutation status helpers and color map look coherent.

Status derivation covers all variants with clear precedence, and STATUS_COLOR_CLASSES defines entries for each status; no functional issues spotted.

devtools/src/app/app.vue (1)

11-12: New “Mutations Test” nav link is consistent.

Path /mutations-test matches the router config; no issues.

devtools/src/app/pages/contacts/[id].vue (1)

32-32: Mutation key now matches query key; template change is cosmetic.

Key (contact) => ['contacts', contact.id] aligns with the contact query key shape, improving cache/devtools consistency; the grow class swap is a harmless styling tweak.

Also applies to: 56-56

devtools/src/app/main.ts (1)

6-6: Mutation cache wiring and test route are aligned with existing patterns.

Importing useMutationCache, exposing it on window for debugging, and adding the /mutations-test route all mirror the existing query cache and routing setup without introducing new risks.

Also applies to: 24-27, 41-44

devtools/src/panel/DevtoolsPanel.ce.vue (1)

3-8: Mutation list state and channel handlers mirror the queries flow correctly.

mutations is provided via MUTATIONS_KEY and kept in sync with mutations:all/update/delete events using id-based upserts, matching the existing queries pattern and giving consumers a consistent API.

Also applies to: 10-10, 63-81

devtools/src/panel/composables/duplex-channel.ts (1)

6-6: Mutation entries injection hook is consistent with query entries.

MUTATIONS_KEY and useMutationEntries() follow the same pattern as the query helper and provide a clear error when used outside the provider context.

Also applies to: 36-46

devtools/src/panel/pages/mutations.vue (1)

43-43: Verify Object.groupBy browser support.

Object.groupBy is ES2024. Ensure your build target/polyfills cover this.

devtools/src/app/pages/mutations-test.vue (1)

1-119: LGTM!

Well-structured test page covering various mutation scenarios: success, failure, long-running, complex variables, anonymous, and batch operations.

devtools/src/panel/pages/mutations/[mutationId].vue (2)

129-146: Same null key concern for error simulation.

Apply the same guard as suggested above.


160-204: LGTM!

Collapsible sections for Variables, Data, Error, and Options are well implemented with appropriate fallback messages.

devtools/src/shared/mutation-serialized.ts (1)

4-54: LGTM!

Well-documented payload interface with clear field descriptions. Types properly reference source types from @pinia/colada.

devtools/src/PiniaColadaDevtools.vue (5)

20-22: Module-level side effect.

addDevtoolsInfoForMutations(mutationCache) runs at component parse time, before the component is mounted. This is consistent with how addDevtoolsInfo likely works for queries (implicit in the codebase), but worth confirming this is intentional.


83-113: LGTM!

Mutation cache action handler properly mirrors the query cache pattern. Covers remove, mutate, setEntryState, and ensure actions with appropriate update emissions.


213-256: LGTM!

Mutation simulate handlers follow the same pattern as query handlers. State management for loading/error simulation is correct.


373-374: LGTM!

Initial state emission for both queries and mutations on devtools ready.


206-211: mutationCache.get(key) API is valid.

The API exists and is correctly typed to accept EntryKey. Tests confirm it returns the mutation entry or undefined. The pattern is consistent across all mutation event handlers in the file.

devtools/src/panel/components/ListMutationEntry.vue (2)

1-42: LGTM!

Script logic is clean. The formattedKey computed handles JSON parsing gracefully with try/catch, and isAnonymous correctly identifies mutations without meaningful keys.


44-107: LGTM!

Template is well-structured with proper accessibility attributes. The status icons, anonymous detection display, and key formatting are correctly implemented.

devtools/src/shared/rpc/index.ts (2)

1-12: LGTM!

Import additions for mutation types are correct and consistent with the query patterns.


86-115: LGTM!

Mutation events in AppEmits and DevtoolsEmits follow the established query event patterns. The type signatures are consistent.

devtools/src/pc-devtools-info-plugin.ts (1)

219-234: LGTM!

createMutationEntryPayload correctly serializes all required fields matching the UseMutationEntryPayload interface.

devtools/typed-router.d.ts (1)

38-54: LGTM!

Auto-generated route types correctly mirror the queries pattern for mutations. Route parameters are properly typed.

Also applies to: 106-122

devtools/components.d.ts (1)

18-36: LGTM!

Auto-generated component declarations correctly add the new icons and ListMutationEntry component types.

Also applies to: 54-72

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
devtools/src/panel/pages/mutations/[mutationId].vue (1)

147-166: Simulate actions may emit null key for anonymous mutations.

Lines 152, 162, 173, and 182 emit selectedMutation.key which can be null for anonymous mutations. Unlike the Remove button (line 199), these buttons lack the v-if="selectedMutation.key" guard.

Add the same guard as the Remove button:

 <UButton
-  v-if="selectedMutation.devtools.simulate !== 'loading'"
+  v-if="selectedMutation.key && selectedMutation.devtools.simulate !== 'loading'"

Apply similar guards to all four simulate buttons (lines 147, 157, 168, 177).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 591908b and efeb21a.

📒 Files selected for processing (8)
  • devtools/components.d.ts (2 hunks)
  • devtools/src/PiniaColadaDevtools.vue (5 hunks)
  • devtools/src/app/pages/mutations-test.vue (1 hunks)
  • devtools/src/panel/components/ListMutationEntry.vue (1 hunks)
  • devtools/src/panel/components/ListQueryEntry.vue (1 hunks)
  • devtools/src/panel/pages/mutations/[mutationId].vue (1 hunks)
  • devtools/src/shared/index.ts (1 hunks)
  • devtools/src/shared/rpc/index.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • devtools/src/panel/components/ListMutationEntry.vue
  • devtools/components.d.ts
  • devtools/src/app/pages/mutations-test.vue
🧰 Additional context used
🧬 Code graph analysis (1)
devtools/src/shared/rpc/index.ts (4)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-54)
src/index.ts (2)
  • UseMutationEntryFilter (60-60)
  • EntryKey (15-15)
src/mutation-store.ts (1)
  • UseMutationEntryFilter (92-92)
src/entry-keys.ts (1)
  • EntryKey (88-88)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (11)
devtools/src/shared/index.ts (1)

2-2: LGTM!

The new export follows the existing pattern and properly exposes mutation types.

devtools/src/shared/rpc/index.ts (3)

1-8: LGTM!

The new imports for mutation types follow the established pattern for queries and are correctly sourced.


90-92: LGTM!

The mutation events in AppEmits mirror the query pattern and use proper payload types.


108-116: LGTM!

The mutation-related events in DevtoolsEmits are consistent with the query events pattern and properly typed.

devtools/src/panel/components/ListQueryEntry.vue (1)

84-84: Project is using Tailwind v4.1.17; the classes grow and wrap-break-word are correct for this version.

No issues.

devtools/src/PiniaColadaDevtools.vue (6)

83-113: LGTM - mutation cache action listener mirrors query pattern.

The implementation correctly handles mutation lifecycle events and mirrors the query cache pattern for consistency.


213-256: LGTM - simulation handlers mirror query implementation.

The simulation handlers correctly mirror the query cache pattern for consistency.


397-397: LGTM - mutations:all emission mirrors queries pattern.

Correctly emits all mutation entries on devtools ready.


201-211: Code is correct. Both mutationCache.getEntries(filters) and mutationCache.get(key) are valid API methods with the expected signatures.


3-3: The original review comment is based on incorrect assumptions about the import sources. Only useMutationCache is from @pinia/colada (confirmed available). The functions addDevtoolsInfoForMutations and createMutationEntryPayload are imported from the local plugin file ./pc-devtools-info-plugin, not from @pinia/colada, so there is no version concern for those two methods.

Likely an incorrect or invalid review comment.


258-279: Check if vars can be null; add guard if needed.

The check originalEntry.vars === undefined does not guard against null. If vars can be null, it would pass the check and be passed to mutation callbacks as null rather than being rejected. Consider: if (originalEntry.vars == null) instead.

Method signatures are correct: ensure(entry, vars) assigns vars to the entry, and mutate(entry) triggers the mutation using the stored vars.

No guard prevents the original entry from being removed or mutated again between the get() call and the subsequent ensure() and mutate() operations.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (2)
devtools/src/panel/pages/mutations/[mutationId].vue (2)

176-176: Simulate action buttons still emit potentially null keys.

Lines 176, 186, 197, and 206 emit selectedMutation.key without verifying it's non-null, unlike the Remove button (line 223) which guards with v-if="selectedMutation.key".

Also applies to: 186-186, 197-197, 206-206


70-72: ID parsing still assumes $N format.

Line 71 uses slice(1) assuming all IDs start with $. Non-conforming IDs will produce NaN or incorrect sorts.

🧹 Nitpick comments (1)
devtools/src/panel/pages/mutations.vue (1)

11-11: Unused import.

useRoute is imported but not used.

-import { useRoute } from 'vue-router'
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between efeb21a and e633a1c.

📒 Files selected for processing (10)
  • devtools/src/app/pages/mutations-test.vue (1 hunks)
  • devtools/src/panel/components/ListMutationEntry.vue (1 hunks)
  • devtools/src/panel/pages/mutations.vue (1 hunks)
  • devtools/src/panel/pages/mutations/[mutationId].vue (1 hunks)
  • devtools/src/panel/utils/mutation-state.ts (1 hunks)
  • devtools/src/pc-devtools-info-plugin.ts (2 hunks)
  • devtools/src/shared/mutation-serialized.ts (1 hunks)
  • devtools/src/shared/plugins/devtools-info.ts (2 hunks)
  • src/mutation-store.ts (1 hunks)
  • src/use-mutation.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/mutation-store.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • devtools/src/panel/utils/mutation-state.ts
  • devtools/src/panel/components/ListMutationEntry.vue
🧰 Additional context used
🧬 Code graph analysis (2)
devtools/src/shared/mutation-serialized.ts (1)
devtools/src/shared/plugins/devtools-info.ts (1)
  • DEVTOOLS_INFO_KEY (7-7)
devtools/src/pc-devtools-info-plugin.ts (2)
devtools/src/shared/plugins/devtools-info.ts (1)
  • DEVTOOLS_INFO_KEY (7-7)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (7)
src/use-mutation.ts (1)

156-156: Comment clarification approved.

Accurately describes the cleanup behavior.

devtools/src/shared/plugins/devtools-info.ts (1)

56-79: LGTM!

Interface structure and module augmentation are consistent with the existing UseQueryDevtoolsInfo pattern.

devtools/src/shared/mutation-serialized.ts (1)

1-63: LGTM!

Well-documented payload interface with appropriate type references.

devtools/src/pc-devtools-info-plugin.ts (1)

185-198: LGTM!

Initialization and idempotency check follow the query pattern correctly.

devtools/src/app/pages/mutations-test.vue (1)

1-124: LGTM!

Demo page covers good variety of mutation scenarios for devtools testing.

devtools/src/panel/pages/mutations.vue (1)

101-120: LGTM!

Splitpanes layout with responsive behavior and size persistence is well implemented.

devtools/src/panel/pages/mutations/[mutationId].vue (1)

1-290: Overall implementation is solid.

The component structure, type safety, Vue 3 patterns, and Tailwind v4 syntax are all correct. Replay and Remove buttons properly guard against null keys.

@posva posva force-pushed the feat/devtools-mutations branch from f4191a8 to 1140df1 Compare December 16, 2025 09:28
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (5)
devtools/src/panel/pages/mutations.vue (1)

17-32: In-place sort mutates original array when search is empty.

When searchQuery is empty, items references mutations.value directly, and sort() mutates it. This was flagged in a previous review.

-  return items.sort((a, b) => {
+  return [...items].sort((a, b) => {
devtools/src/panel/pages/mutations/[mutationId].vue (2)

70-72: ID parsing assumes $N format.

Already flagged in previous review. Guard or document the expected ID format.


171-190: Simulate buttons may emit null key for anonymous mutations.

Already flagged in previous review. Add the same guard as Remove/Replay buttons.

devtools/src/pc-devtools-info-plugin.ts (2)

217-221: Missing !entry.active guard in untrack handler.

The query version (line 131) checks if (!entry.active) before setting inactiveAt. This handler sets it unconditionally.

     } else if (name === 'untrack') {
       const [entry] = args
       after(() => {
-        entry[DEVTOOLS_INFO_KEY].inactiveAt = now()
+        if (!entry.active) {
+          entry[DEVTOOLS_INFO_KEY].inactiveAt = now()
+        }
       })
     }

239-239: Use entry.active directly.

Deriving active from inactiveAt === 0 is incorrect for reactivated entries. Query payload (line 155) uses entry.active.

-    active: entry[DEVTOOLS_INFO_KEY].inactiveAt === 0,
+    active: entry.active,
🧹 Nitpick comments (2)
devtools/src/panel/pages/mutations/[mutationId].vue (1)

89-107: wasDataOpen persists across mutation selection changes.

wasDataOpen and lastStatus are module-level variables that don't reset when navigating to a different mutation. This could cause the data section to unexpectedly open/close based on the previous mutation's state.

Consider resetting these when selectedMutation changes:

+watch(
+  () => selectedMutation.value?.id,
+  () => {
+    lastStatus = null
+    wasDataOpen = isDataOpen.value
+  },
+)
devtools/src/panel/components/ListMutationEntry.vue (1)

116-132: Consider extracting the GC progress threshold.

The 30_000 magic number could be a named constant for clarity.

+const GC_PROGRESS_MAX_DISPLAY_TIME = 30_000
+
 <div
   v-if="
     !entry.active &&
     entry.gcTimeout &&
     entry.devtools.inactiveAt &&
     typeof entry.options?.gcTime === 'number' &&
     Number.isFinite(entry.options.gcTime) &&
-    entry.options.gcTime <= 30_000
+    entry.options.gcTime <= GC_PROGRESS_MAX_DISPLAY_TIME
   "
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4191a8 and 1140df1.

📒 Files selected for processing (22)
  • devtools/components.d.ts (2 hunks)
  • devtools/src/PiniaColadaDevtools.vue (5 hunks)
  • devtools/src/app/app.vue (1 hunks)
  • devtools/src/app/main.ts (3 hunks)
  • devtools/src/app/pages/contacts/[id].vue (2 hunks)
  • devtools/src/app/pages/mutations-test.vue (1 hunks)
  • devtools/src/panel/DevtoolsPanel.ce.vue (2 hunks)
  • devtools/src/panel/components/ListMutationEntry.vue (1 hunks)
  • devtools/src/panel/components/ListQueryEntry.vue (1 hunks)
  • devtools/src/panel/composables/duplex-channel.ts (2 hunks)
  • devtools/src/panel/pages/mutations.vue (1 hunks)
  • devtools/src/panel/pages/mutations/(unselected).vue (1 hunks)
  • devtools/src/panel/pages/mutations/[mutationId].vue (1 hunks)
  • devtools/src/panel/utils/mutation-state.ts (1 hunks)
  • devtools/src/pc-devtools-info-plugin.ts (2 hunks)
  • devtools/src/shared/index.ts (1 hunks)
  • devtools/src/shared/mutation-serialized.ts (1 hunks)
  • devtools/src/shared/plugins/devtools-info.ts (2 hunks)
  • devtools/src/shared/rpc/index.ts (3 hunks)
  • devtools/typed-router.d.ts (2 hunks)
  • src/mutation-store.ts (1 hunks)
  • src/use-mutation.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • devtools/src/app/app.vue
  • src/use-mutation.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • devtools/src/app/main.ts
  • devtools/typed-router.d.ts
  • devtools/src/shared/mutation-serialized.ts
  • devtools/src/panel/DevtoolsPanel.ce.vue
  • devtools/src/app/pages/contacts/[id].vue
  • devtools/src/shared/index.ts
  • devtools/src/panel/components/ListQueryEntry.vue
🧰 Additional context used
🧬 Code graph analysis (4)
devtools/src/shared/rpc/index.ts (3)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-59)
src/mutation-store.ts (1)
  • UseMutationEntryFilter (87-87)
src/entry-keys.ts (1)
  • EntryKey (88-88)
devtools/src/panel/composables/duplex-channel.ts (1)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-59)
devtools/src/panel/utils/mutation-state.ts (1)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-59)
devtools/src/pc-devtools-info-plugin.ts (2)
devtools/src/shared/plugins/devtools-info.ts (1)
  • DEVTOOLS_INFO_KEY (7-7)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (24)
src/mutation-store.ts (1)

325-327: LGTM!

The documentation updates accurately describe the function's behavior and parameter.

devtools/src/app/pages/mutations-test.vue (1)

1-234: Test page implementation looks good.

The mutation patterns are correctly demonstrated, covering success, failure, long-running, complex variables, anonymous, and batch scenarios. Implementation is appropriate for a DevTools test/demo page.

devtools/src/panel/utils/mutation-state.ts (2)

11-29: LGTM!

Status derivation logic is clean. The 'inactive' status being absent from getMutationStatus is correct—it's determined by the active property separately in the UI layer.


31-69: LGTM!

Color mappings are complete and consistent.

devtools/src/panel/pages/mutations/(unselected).vue (1)

1-10: LGTM!

Simple placeholder component. No issues.

devtools/src/panel/composables/duplex-channel.ts (1)

35-46: LGTM!

Follows the established pattern from useQueryEntries. Consistent and properly typed.

devtools/src/shared/plugins/devtools-info.ts (1)

56-79: LGTM!

Interface mirrors the query version appropriately. Module augmentation is correct.

devtools/src/panel/pages/mutations.vue (2)

34-46: LGTM!

Grouping logic correctly handles the 'inactive' status separately since getMutationStatus doesn't return it.


101-121: LGTM!

Splitpanes layout with persistence is well implemented.

devtools/src/PiniaColadaDevtools.vue (4)

19-23: LGTM!

Mutation cache initialization mirrors the query cache pattern.


83-113: LGTM!

Action handler follows the established query cache pattern with appropriate mutations-specific events.


258-279: LGTM!

Replay implementation correctly creates a new entry to avoid reuse issues. The empty catch is fine since errors propagate via the $onAction listener.


396-398: LGTM!

Initial state synchronization for mutations.

devtools/src/panel/pages/mutations/[mutationId].vue (2)

21-34: LGTM!

Anonymous detection logic is correct.


234-278: LGTM!

Collapsible sections are well structured with appropriate conditional rendering.

devtools/src/shared/rpc/index.ts (3)

1-8: LGTM!

Import additions are consistent with the new mutation types from @pinia/colada and the shared serialized payload.


90-92: LGTM!

Mutation events in AppEmits mirror the query event structure correctly.


107-116: LGTM!

DevtoolsEmits mutation events are well-structured and parallel the query simulation/control events.

devtools/src/pc-devtools-info-plugin.ts (3)

5-12: LGTM!

Import additions for mutation types are correct.


185-198: LGTM!

Initialization logic correctly mirrors the query version with appropriate mutation-specific metadata.


244-245: LGTM!

Separate installation map for mutations is appropriate.

devtools/src/panel/components/ListMutationEntry.vue (2)

1-44: LGTM!

Script setup is well-structured with appropriate computed properties and error handling.


46-114: LGTM!

Template structure with status icons and accessible labels is well implemented.

devtools/components.d.ts (1)

1-84: LGTM!

Auto-generated type declarations for new icons and ListMutationEntry component.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
devtools/src/panel/pages/mutations.vue (1)

15-30: Avoid mutating the filtered array.

Line 27 uses items.sort() which mutates in place, potentially affecting the original reactive array.

Apply this diff:

   // Sort by most recent first
-  return items.sort((a, b) => {
+  return [...items].sort((a, b) => {
     return b.devtools.updatedAt - a.devtools.updatedAt
   })
devtools/src/pc-devtools-info-plugin.ts (2)

217-221: Missing !entry.active check in untrack handler.

Line 220 sets inactiveAt unconditionally. The query version (line 131) checks if (!entry.active) to avoid incorrect updates when multiple components track the same entry.

Apply this diff:

     } else if (name === 'untrack') {
       const [entry] = args
       after(() => {
-        entry[DEVTOOLS_INFO_KEY].inactiveAt = now()
+        if (!entry.active) {
+          entry[DEVTOOLS_INFO_KEY].inactiveAt = now()
+        }
       })
     }

226-241: Verify active property availability on UseMutationEntry.

Line 238 derives active from inactiveAt === 0, which is incorrect for reactivated entries. The query payload (line 155) uses entry.active directly. Ensure UseMutationEntry exposes an active property consistent with UseQueryEntry.

If the property exists, apply this diff:

-    active: entry[DEVTOOLS_INFO_KEY].inactiveAt === 0,
+    active: entry.active,

Otherwise, verify whether UseMutationEntry should be extended to include readonly active: boolean to match UseQueryEntry.

Run this script to check:

#!/bin/bash
# Check if UseMutationEntry has active property
rg -n "interface UseMutationEntry" --type=ts -A 20 | grep -i "active"

# Compare with UseQueryEntry
rg -n "interface UseQueryEntry" --type=ts -A 20 | grep -i "active"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1140df1 and 8af92ac.

📒 Files selected for processing (12)
  • devtools/components.d.ts (2 hunks)
  • devtools/src/PiniaColadaDevtools.vue (5 hunks)
  • devtools/src/panel/components/ListMutationEntry.vue (1 hunks)
  • devtools/src/panel/components/ListQueryEntry.vue (3 hunks)
  • devtools/src/panel/composables/entries.ts (1 hunks)
  • devtools/src/panel/pages/mutations.vue (1 hunks)
  • devtools/src/panel/pages/mutations/[mutationId].vue (1 hunks)
  • devtools/src/panel/pages/queries/[queryId].vue (1 hunks)
  • devtools/src/pc-devtools-info-plugin.ts (2 hunks)
  • devtools/src/shared/mutation-serialized.ts (1 hunks)
  • devtools/src/shared/plugins/devtools-info.ts (2 hunks)
  • devtools/src/shared/rpc/index.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • devtools/src/shared/mutation-serialized.ts
  • devtools/src/panel/components/ListQueryEntry.vue
  • devtools/components.d.ts
🧰 Additional context used
🧬 Code graph analysis (3)
devtools/src/panel/composables/entries.ts (1)
devtools/src/shared/query-serialized.ts (1)
  • miniJsonParse (48-75)
devtools/src/shared/rpc/index.ts (3)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-54)
src/mutation-store.ts (1)
  • UseMutationEntryFilter (87-87)
src/index.ts (1)
  • UseMutationEntryFilter (60-60)
devtools/src/pc-devtools-info-plugin.ts (2)
devtools/src/shared/plugins/devtools-info.ts (1)
  • DEVTOOLS_INFO_KEY (7-7)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-54)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (6)
devtools/src/panel/pages/queries/[queryId].vue (1)

336-338: Good addition for user feedback.

The message provides helpful context by displaying the specific query key that wasn't found, improving the user experience when a query is missing from the cache.

devtools/src/panel/composables/entries.ts (1)

5-24: LGTM!

The implementation safely handles JSON parsing with proper fallbacks and correctly applies miniJsonParse for objects.

devtools/src/PiniaColadaDevtools.vue (2)

83-113: LGTM!

The mutation cache action handlers correctly mirror the query handling patterns and emit appropriate events.


201-279: LGTM!

Mutation event handlers are well-structured and consistent with the existing query handling. The replay logic appropriately creates new entries and handles errors.

devtools/src/shared/rpc/index.ts (1)

1-117: LGTM!

The RPC type extensions for mutations correctly mirror the query event structure and provide proper type safety.

devtools/src/shared/plugins/devtools-info.ts (1)

43-101: LGTM!

The refactoring to extract a common EntryDevtoolsInfo base interface is a good design choice that reduces duplication while maintaining consistency.

Comment on lines 22 to 28
const isAnonymous = computed(() => {
// No key means anonymous
if (!entry.key) return true
// If the key only contains the ID (e.g., ["$0"]), it's effectively anonymous
return entry.key.length === 1 && entry.key[0] === entry.id
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Type mismatch in anonymous detection.

Line 27 compares entry.key[0] (potentially a string) with entry.id (a number). This will fail if the key contains string representations.

Consider using string comparison:

-  return entry.key.length === 1 && entry.key[0] === entry.id
+  return entry.key.length === 1 && String(entry.key[0]) === String(entry.id)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isAnonymous = computed(() => {
// No key means anonymous
if (!entry.key) return true
// If the key only contains the ID (e.g., ["$0"]), it's effectively anonymous
return entry.key.length === 1 && entry.key[0] === entry.id
})
const isAnonymous = computed(() => {
// No key means anonymous
if (!entry.key) return true
// If the key only contains the ID (e.g., ["$0"]), it's effectively anonymous
return entry.key.length === 1 && String(entry.key[0]) === String(entry.id)
})
🤖 Prompt for AI Agents
In devtools/src/panel/components/ListMutationEntry.vue around lines 22 to 28,
the anonymous detection compares entry.key[0] (which may be a string) to
entry.id (a number), causing mismatches; coerce both sides to the same type
before comparing (for example, compare String(entry.key[0]) ===
String(entry.id)) or otherwise normalize entry.key elements to numbers/strings
consistently so the equality check reliably detects anonymous keys.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
devtools/src/panel/pages/mutations/[mutationId].vue (2)

26-35: Type mismatch in anonymous detection.

Line 34 compares mutation.key[0] (likely a string) with mutation.id (a number). The equality check will fail if the key contains string representations.

Consider using type-safe comparison:

-  return mutation.key.length === 1 && mutation.key[0] === mutation.id
+  return mutation.key.length === 1 && String(mutation.key[0]) === String(mutation.id)

166-228: Inconsistent key guards for mutation actions.

Replay (line 209) and Remove (line 220) both check selectedMutation.key before rendering, but the simulate loading/error buttons (lines 168-206) don't. If simulate operations shouldn't work on anonymous mutations, add the same guard.

Consider adding guards to simulate buttons if needed:

 <UButton
-  v-if="selectedMutation.devtools.simulate !== 'loading'"
+  v-if="selectedMutation.key && selectedMutation.devtools.simulate !== 'loading'"

And similarly for the other simulate buttons.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8af92ac and b8aadb6.

📒 Files selected for processing (3)
  • devtools/src/panel/components/ListMutationEntry.vue (1 hunks)
  • devtools/src/panel/pages/mutations/[mutationId].vue (1 hunks)
  • devtools/src/panel/pages/queries/[queryId].vue (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • devtools/src/panel/pages/queries/[queryId].vue
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08dda05 and dfdb9aa.

📒 Files selected for processing (2)
  • devtools/src/panel/pages/mutations.vue (1 hunks)
  • devtools/src/panel/pages/queries.vue (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
devtools/src/panel/pages/queries.vue (1)

27-29: Use toSorted for immutable sorting.

The switch from sort to toSorted provides the copying version of the sort() method, which is better practice. Since devtools panels run in modern browsers, ES2023 is supported in all modern browsers since July 2023.

devtools/src/panel/pages/mutations.vue (2)

78-78: No action needed. STATUS_COLOR_CLASSES is typed as Record<UseMutationEntryPayloadStatus, {...}>, which requires all six status values ('loading', 'success', 'error', 'pending', 'idle', 'inactive') to be present. TypeScript enforces this completeness at compile time, making the access at line 78 safe.

Likely an incorrect or invalid review comment.


27-29: Approved. The devtools build target is configured to esnext (ES2023+), which preserves toSorted() as-is. Since July 2023, this feature works across the latest devices and browser versions, including Chrome, Edge, Safari, and Firefox. As a browser extension for modern browsers, no polyfill is needed.

Comment on lines +32 to +44
const mutationsGrouped = computed<Record<UseMutationEntryPayloadStatus, UseMutationEntryPayload[]>>(
() => {
return {
loading: [],
success: [],
error: [],
pending: [],
idle: [],
inactive: filteredItems.value.filter((item) => !item.active),
...Object.groupBy(filteredItems.value, (item) => getMutationStatus(item)),
}
},
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "mutations.vue" | head -1

Repository: posva/pinia-colada

Length of output: 102


🏁 Script executed:

find . -type f \( -name "vite.config*" -o -name "tsconfig*" -o -name "babel.config*" -o -name ".browserslistrc" \) | xargs ls -la 2>/dev/null

Repository: posva/pinia-colada

Length of output: 1060


🏁 Script executed:

cd devtools/src/panel/pages && cat -n mutations.vue | sed -n '32,50p'

Repository: posva/pinia-colada

Length of output: 816


🏁 Script executed:

grep -r "Object.groupBy" --include="*.vue" --include="*.ts" --include="*.js" .

Repository: posva/pinia-colada

Length of output: 298


🏁 Script executed:

cd devtools/src/panel/pages && cat -n mutations.vue | sed -n '1,80p'

Repository: posva/pinia-colada

Length of output: 3531


🏁 Script executed:

cd devtools && cat -n tsconfig.json

Repository: posva/pinia-colada

Length of output: 1193


🏁 Script executed:

cat devtools/vite.config.ts

Repository: posva/pinia-colada

Length of output: 3491


🏁 Script executed:

grep -r "getMutationStatus" devtools/src/panel --include="*.vue" --include="*.ts" -A 3

Repository: posva/pinia-colada

Length of output: 1873


🏁 Script executed:

cat -n devtools/src/panel/utils/mutation-state.ts

Repository: posva/pinia-colada

Length of output: 1893


🏁 Script executed:

grep -r "inactive\|active" devtools/src/panel/utils/mutation-state.ts -B 2 -A 2

Repository: posva/pinia-colada

Length of output: 310


🏁 Script executed:

grep -r "package.json" devtools | grep "^devtools/package.json"

Repository: posva/pinia-colada

Length of output: 44


🏁 Script executed:

cat devtools/package.json | head -30

Repository: posva/pinia-colada

Length of output: 739


🏁 Script executed:

cat devtools/package.json | grep -i "group\|polyfill"

Repository: posva/pinia-colada

Length of output: 44


🏁 Script executed:

cat -n devtools/src/panel/pages/mutations.vue | sed -n '70,85p'

Repository: posva/pinia-colada

Length of output: 1047


Inactive items appear in multiple groups, inflating counts.

Items marked as inactive (where active is false) are placed in the inactive group on line 40, but then all items—including inactive ones—are grouped by status on line 41 using Object.groupBy. An inactive item with an async status will appear in both groups, skewing the count displayed in the status badges (line 76).

Additionally, Object.groupBy is supported in Chrome 117+, Firefox 119+, Safari 17+, but this project has no polyfill. For broader compatibility, add a polyfill or filter out inactive items from the groupBy operation.

Apply this diff to fix the duplicate:

       inactive: filteredItems.value.filter((item) => !item.active),
-      ...Object.groupBy(filteredItems.value, (item) => getMutationStatus(item)),
+      ...Object.groupBy(filteredItems.value.filter(item => item.active), (item) => getMutationStatus(item)),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const mutationsGrouped = computed<Record<UseMutationEntryPayloadStatus, UseMutationEntryPayload[]>>(
() => {
return {
loading: [],
success: [],
error: [],
pending: [],
idle: [],
inactive: filteredItems.value.filter((item) => !item.active),
...Object.groupBy(filteredItems.value, (item) => getMutationStatus(item)),
}
},
)
const mutationsGrouped = computed<Record<UseMutationEntryPayloadStatus, UseMutationEntryPayload[]>>(
() => {
return {
loading: [],
success: [],
error: [],
pending: [],
idle: [],
inactive: filteredItems.value.filter((item) => !item.active),
...Object.groupBy(filteredItems.value.filter(item => item.active), (item) => getMutationStatus(item)),
}
},
)
🤖 Prompt for AI Agents
In devtools/src/panel/pages/mutations.vue around lines 32 to 44, inactive items
are duplicated because you assign inactive items to the inactive group then
still include them in Object.groupBy, and Object.groupBy also lacks a polyfill;
fix by excluding inactive items from the grouping call (i.e., pass
filteredItems.value.filter(item => item.active) into the groupBy) so inactive
entries only appear in the inactive array, and either add a runtime polyfill for
Object.groupBy or replace it with a small utility implementation to preserve
cross‑browser compatibility.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
devtools/src/pc-devtools-info-plugin.ts (2)

219-223: Missing !entry.active guard in untrack handler.

The query handler (line 138) checks if (!entry.active) before setting inactiveAt. This mutation handler sets it unconditionally.

     } else if (name === 'untrack') {
       const [entry] = args
       after(() => {
-        entry[DEVTOOLS_INFO_KEY].inactiveAt = now()
+        if (!entry.active) {
+          entry[DEVTOOLS_INFO_KEY].inactiveAt = now()
+        }
       })
     }
Does UseMutationEntry in pinia-colada have an active property?

240-240: Derive active from entry.active if available.

Using inactiveAt === 0 is fragile for reactivated entries. Query payload uses entry.active directly (line 162).

-    active: entry[DEVTOOLS_INFO_KEY].inactiveAt === 0,
+    active: entry.active,

If UseMutationEntry lacks an active property, consider adding it for consistency with queries.

🧹 Nitpick comments (3)
src/use-mutation.ts (1)

17-17: Unused import: useMutationOptions

Imported on line 17 but only referenced in commented-out code on line 152. Either remove the import until the global options feature is implemented, or complete the TODO now.

-import { useMutationOptions, type UseMutationOptions } from './mutation-options'
+import type { UseMutationOptions } from './mutation-options'

Also applies to: 151-153

devtools/src/app/pages/mutations-test.vue (2)

52-58: Missing status tracking for anonymous mutation.

The anonymous mutation doesn't destructure asyncStatus, so there's no way to display its execution state in the UI (unlike the other mutations). Consider adding asyncStatus: anonymousStatus to the destructuring for consistency and better demonstration.

Apply this diff:

-const { mutate: anonymousMutation } = useMutation({
+const { mutate: anonymousMutation, asyncStatus: anonymousStatus } = useMutation({
   mutation: async (message: string) => {
     await delay(500)
     return { message, processed: true }
   },
 })

Then update the template to display the status:

       <button style="background: #ec4899" @click="runAnonymousMutation">
         Run Anonymous Mutation
       </button>
+      <span>Status: {{ anonymousStatus }}</span>

112-117: Negligible delays in sequential execution.

The delays between mutations (0ms, 20ms, 40ms) are much smaller than the mutation's own 1000ms execution time. These tiny inter-mutation delays won't meaningfully demonstrate sequential behavior in the devtools.

Consider removing the delay or making it more substantial:

 async function runMultipleSequential() {
   for (let i = 0; i < 3; i++) {
-    await delay(20 * i)
     await runSimpleMutation()
   }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dfdb9aa and c7e79dc.

📒 Files selected for processing (5)
  • devtools/src/PCDevtoolsWrapper.vue (2 hunks)
  • devtools/src/PiniaColadaDevtools.vue (5 hunks)
  • devtools/src/app/pages/mutations-test.vue (1 hunks)
  • devtools/src/pc-devtools-info-plugin.ts (2 hunks)
  • src/use-mutation.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
devtools/src/pc-devtools-info-plugin.ts (2)
devtools/src/shared/plugins/devtools-info.ts (1)
  • DEVTOOLS_INFO_KEY (7-7)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-54)
src/use-mutation.ts (2)
src/mutation-store.ts (1)
  • UseMutationEntry (31-82)
src/index.ts (1)
  • UseMutationEntry (58-58)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Redirect rules
  • GitHub Check: Header rules
  • GitHub Check: Pages changed
  • GitHub Check: build
🔇 Additional comments (11)
src/use-mutation.ts (1)

159-169: LGTM!

Comment clarification and cleanup logic are correct.

devtools/src/pc-devtools-info-plugin.ts (3)

16-26: LGTM!

Signature update and dual-cache initialization logic is clean. Using WeakMap<object, boolean> keeps the installation tracking simple.


192-200: LGTM!

Initialization mirrors query logic correctly.


228-243: Payload structure looks correct.

Serialization captures expected fields. The active derivation issue noted above is the only concern.

devtools/src/PCDevtoolsWrapper.vue (1)

9-9: LGTM!

Mutation cache integration follows the established pattern.

Also applies to: 25-26

devtools/src/PiniaColadaDevtools.vue (3)

3-6: LGTM!

Imports and cache initialization are correct.

Also applies to: 15-16


194-278: Mutation transmitter handlers are well-structured.

Handlers follow query patterns. The replay logic (lines 257-278) correctly creates a new entry and handles errors through cache listeners.


395-396: LGTM!

Initial emission includes both queries and mutations.

devtools/src/app/pages/mutations-test.vue (3)

1-5: LGTM!

Imports and utility function are clean and appropriate for the test page.


126-219: LGTM!

Template structure is clear and well-organized. Each mutation section provides good context for testing. The HR spacer (line 217) is a pragmatic solution for devtools display.


221-234: LGTM!

Scoped styles are clean and provide good visual separation for the mutation test sections.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
devtools/src/pc-devtools-info-plugin.ts (2)

219-223: Missing !entry.active check in untrack handler.

The query handler (line 138) checks if (!entry.active) before setting inactiveAt. This mutation handler sets inactiveAt unconditionally, which is incorrect if multiple components track the same mutation entry.

Apply this diff:

     } else if (name === 'untrack') {
       const [entry] = args
       after(() => {
-        entry[DEVTOOLS_INFO_KEY].inactiveAt = now()
+        if (!entry.active) {
+          entry[DEVTOOLS_INFO_KEY].inactiveAt = now()
+        }
       })
     }

240-240: Use entry.active directly.

Deriving active from inactiveAt === 0 is incorrect if an entry was previously inactive then reactivated. The query payload uses entry.active directly (line 162).

Apply this diff if UseMutationEntry exposes an active property:

-    active: entry[DEVTOOLS_INFO_KEY].inactiveAt === 0,
+    active: entry.active,

Otherwise, verify whether UseMutationEntry needs an active property added (similar to UseQueryEntry):

#!/bin/bash
# Check if UseMutationEntry has an active property
rg -n "interface UseMutationEntry" --type=ts -A 30
rg -n "class UseMutationEntry" --type=ts -A 30
🧹 Nitpick comments (2)
src/use-query.ts (1)

164-169: Remove commented-out parameter declarations.

The commented-out type annotations duplicate the spread parameter pattern on lines 155-163 and serve no documentation purpose. Version control preserves the history if needed.

Apply this diff to remove the dead code:

-) // _options:
-//   | UseQueryOptions<TData, TError, TDataInitial>
-//   | (() => DefineQueryOptions<TData, TError, TDataInitial>)
-//   | ((params: unknown) => DefineQueryOptions<TData, TError, TDataInitial>),
-// paramsGetter?: MaybeRefOrGetter<unknown>,
-: UseQueryReturn<TData, TError, TDataInitial> {
+): UseQueryReturn<TData, TError, TDataInitial> {
devtools/src/panel/utils/mutation-state.ts (1)

3-9: The 'inactive' status is unused by getMutationStatus.

The union includes 'inactive', but getMutationStatus never returns it. Verify this status is needed or remove it.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a85cb7 and 0f3a56c.

📒 Files selected for processing (9)
  • devtools/src/panel/utils/mutation-state.ts (1 hunks)
  • devtools/src/pc-devtools-info-plugin.ts (2 hunks)
  • devtools/src/shared/mutation-serialized.ts (1 hunks)
  • src/index.ts (1 hunks)
  • src/mutation-options.ts (2 hunks)
  • src/mutation-store.spec.ts (14 hunks)
  • src/mutation-store.ts (4 hunks)
  • src/use-mutation.ts (3 hunks)
  • src/use-query.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/use-mutation.ts
  • devtools/src/shared/mutation-serialized.ts
🧰 Additional context used
🧬 Code graph analysis (6)
src/mutation-options.ts (2)
src/index.ts (4)
  • UseMutationOptionsWithDefaults (52-52)
  • _EmptyObject (102-102)
  • UseMutationOptions (49-49)
  • UseMutationOptionsGlobalDefaults (51-51)
src/types-extension.ts (1)
  • ErrorDefault (27-27)
devtools/src/pc-devtools-info-plugin.ts (3)
src/mutation-store.ts (1)
  • UseMutationEntry (31-82)
devtools/src/shared/plugins/devtools-info.ts (1)
  • DEVTOOLS_INFO_KEY (7-7)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-54)
src/mutation-store.spec.ts (2)
src/mutation-options.ts (2)
  • USE_MUTATION_DEFAULTS (104-106)
  • UseMutationOptionsWithDefaults (108-113)
src/index.ts (1)
  • UseMutationOptionsWithDefaults (52-52)
src/use-query.ts (1)
src/index.ts (1)
  • UseQueryReturn (27-27)
src/mutation-store.ts (2)
src/mutation-options.ts (1)
  • UseMutationOptionsWithDefaults (108-113)
src/index.ts (1)
  • UseMutationOptionsWithDefaults (52-52)
devtools/src/panel/utils/mutation-state.ts (1)
devtools/src/shared/mutation-serialized.ts (1)
  • UseMutationEntryPayload (4-54)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Redirect rules
  • GitHub Check: Header rules
  • GitHub Check: Pages changed
🔇 Additional comments (22)
src/index.ts (1)

52-52: LGTM!

Public export of the new defaults-aware type is correctly placed and consistent with the existing export structure.

src/mutation-options.ts (2)

108-113: LGTM!

Type definition correctly intersects base options with defaults, enabling proper typing for mutation entries with guaranteed default values.


253-254: LGTM!

Return type correctly reflects that defaults are always included in the injected options.

src/mutation-store.ts (5)

5-5: LGTM!

Removal of unused toCacheKey import is appropriate.


11-11: LGTM!

Import addition aligns with the new defaults-aware typing used in entry and create signatures.


71-71: Type change ensures options always include defaults.

The change from UseMutationOptions to UseMutationOptionsWithDefaults guarantees that entry.options contains default values like gcTime. This is consistent with the create function signature change.


157-157: LGTM!

Parameter type change ensures created entries have defaults applied, maintaining consistency with the UseMutationEntry.options type.


325-327: LGTM!

JSDoc simplification improves clarity without changing meaning.

src/mutation-store.spec.ts (7)

5-11: LGTM!

Import updates correctly add the defaults constant and new type needed for test assertions.


28-31: LGTM!

Spreading defaults ensures the created options conform to UseMutationOptionsWithDefaults, maintaining type safety in tests.


124-126: LGTM!

Test correctly applies defaults to match the new options typing.


142-145: LGTM!

All test mutations properly spread defaults, ensuring type consistency with UseMutationOptionsWithDefaults.

Also applies to: 152-155, 162-165, 180-183


221-224: LGTM!

Extensibility tests correctly apply defaults when creating entries.

Also applies to: 239-242, 259-262, 265-268, 287-290


309-311: Test intentionally uses base type.

This plugin test uses UseMutationOptions without defaults, likely verifying that useMutation() applies defaults internally. This is correct behavior for testing the full mutation creation path.


328-331: LGTM!

Tests for entry retrieval and unkeyed mutations correctly apply defaults.

Also applies to: 342-345, 372-375, 379-382

devtools/src/panel/utils/mutation-state.ts (2)

11-26: LGTM!

The status resolution logic is correct and follows a clear precedence.


28-66: LGTM!

The color class mappings are consistent and appropriate for UI rendering.

devtools/src/pc-devtools-info-plugin.ts (5)

18-26: LGTM!

The refactoring cleanly separates query and mutation initialization while maintaining the guard logic.


28-28: LGTM!

Extracting query initialization into a separate function improves modularity.


192-218: LGTM!

The initialization and lifecycle handlers for 'create', 'mutate', and 'setEntryState' are correctly implemented.


228-239: LGTM!

The payload serialization correctly maps mutation entry fields to the payload structure.


16-16: The current keying is correct for Pinia Colada's architecture.

Pinia Colada maintains one global queryCache and one global mutationCache per Pinia instance, obtained via useQueryCache() and useMutationCache(). The devtools component calls addDevtoolsInfo once at setup time with the matching singleton caches. Keying by only queryCache prevents redundant initialization and is the appropriate design for this usage pattern.

Likely an incorrect or invalid review comment.

@posva posva merged commit 0f3a56c into main Dec 16, 2025
8 of 9 checks passed
@github-project-automation github-project-automation bot moved this from 🆕 Triaging to Done in Pinia Colada Roadmap Dec 16, 2025
@posva posva deleted the feat/devtools-mutations branch December 16, 2025 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants