Skip to content

fix: add LRU eviction for session storage to prevent QuotaExceededError#8101

Closed
christian-byrne wants to merge 7 commits intomainfrom
fix/session-storage-lru-eviction
Closed

fix: add LRU eviction for session storage to prevent QuotaExceededError#8101
christian-byrne wants to merge 7 commits intomainfrom
fix/session-storage-lru-eviction

Conversation

@christian-byrne
Copy link
Contributor

@christian-byrne christian-byrne commented Jan 16, 2026

Summary

Implement LRU-based session storage service for workflow data to prevent QuotaExceededError that was breaking workspace/teams features, particularly on mobile devices.

Problem

  • Sentry error: https://comfy-org.sentry.io/issues/6955016837/
  • QuotaExceededError: Failed to execute 'setItem' on 'Storage' when storing workflow data
  • Mobile devices have ~5MB session storage limits; complex workflows easily exceed this
  • Error was breaking workspace and teams features

Solution

Add an LRU eviction system that:

  1. Wraps workflow data with { accessedAt, data } metadata for tracking
  2. On QuotaExceededError: scans for evictable entries, sorts by age, evicts oldest
  3. Retries the write (up to 3 attempts)
  4. Gracefully degrades if storage still fails (logs warning, doesn't throw)

Backward Compatibility

Legacy entries (raw workflow JSON without wrapper) are detected and treated as accessedAt: 0, prioritizing them for eviction. This provides natural migration.

Follow-up

A tracking issue should be created to remove legacy format support after 2026-07-15 (6 months), once all active users have naturally migrated. Code is marked with @deprecated Remove after 2026-07-15 comments.

┆Issue is synchronized with this Notion page by Unito

Implement LRU-based session storage service for workflow data to prevent
QuotaExceededError that was breaking workspace/teams features on mobile.

Changes:
- Add sessionStorageLruService with try-evict-retry logic
- Store workflow data with embedded accessedAt timestamps for LRU tracking
- Evict oldest entries when quota is exceeded (legacy entries first)
- Graceful degradation: log warnings instead of throwing on persistent failures
- Backward compatibility: legacy unwrapped data treated as oldest

Fixes: https://comfy-org.sentry.io/issues/6955016837/
Amp-Thread-ID: https://ampcode.com/threads/T-019bc566-eb91-7545-a462-658c2b33083b
Co-authored-by: Amp <amp@ampcode.com>
@christian-byrne christian-byrne requested a review from a team as a code owner January 16, 2026 06:50
@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Jan 16, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 16, 2026

Important

Review skipped

Auto reviews are limited based on label configuration.

🚫 Review skipped — only excluded labels are configured. (1)
  • backport

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review
📝 Walkthrough

Walkthrough

Refactors workflow persistence to store JSON-structured workflow payloads and adds a session-storage service with eviction and access-tracking. The composable now uses eviction-backed session storage when api.clientId exists and falls back to parsed localStorage data.

Changes

Cohort / File(s) Summary
Workflow Session Storage Service
src/platform/workflow/persistence/services/workflowSessionStorageService.ts
New session-storage service implementing wrapped storage { accessedAt, data }, LRU-style eviction (excluding protected keys), setWithEviction, getWithAccessTracking, removeFromStorage, legacy-format support, and logging/parse error handling.
Workflow Session Storage Tests
src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
Tests for eviction behavior under quota errors, eviction ordering (legacy-first), protected-key preservation, access-time updates, legacy/unwrapped formats, invalid-JSON handling, and removal. Uses sessionStorage mocks and simulated QuotaExceededErrors.
Workflow Persistence Composable
src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
Refactors persistence to use ComfyWorkflowJSON payloads, introduces WORKFLOW_KEY_PATTERN, replaces direct sessionStorage writes with setWithEviction, reads via getWithAccessTracking (session-first) and falls back to parsing localStorage JSON with safe-parse and warnings; adds loadWorkflowFromData path.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Composable as useWorkflowPersistence
    participant SessionSvc as workflowSessionStorageService
    participant SessionStorage as Browser SessionStorage
    participant LocalStorage as Browser LocalStorage

    User->>Composable: Save workflow (ComfyWorkflowJSON)
    Composable->>SessionSvc: setWithEviction(key, workflowJSON)
    SessionSvc->>SessionStorage: store wrapped {accessedAt, data}

    alt QuotaExceededError
        SessionStorage-->>SessionSvc: QuotaExceededError
        SessionSvc->>SessionSvc: getEvictableKeys(evictionPattern)
        SessionSvc->>SessionSvc: evictOldestEntries(...)
        SessionSvc->>SessionStorage: remove(oldest)
        SessionSvc->>SessionStorage: retry store
        SessionStorage-->>SessionSvc: success / error
    end

    SessionSvc-->>Composable: success/failure

    User->>Composable: Load workflow
    Composable->>SessionSvc: getWithAccessTracking(key, updateAccessTime=true)
    SessionSvc->>SessionStorage: retrieve wrapped
    alt Found in session
        SessionStorage-->>SessionSvc: {accessedAt, data}
        SessionSvc->>SessionStorage: update accessedAt wrapper
        SessionSvc-->>Composable: ComfyWorkflowJSON
    else Not in session
        Composable->>LocalStorage: getItem(key)
        LocalStorage-->>Composable: raw JSON string/null
        Composable->>Composable: safe JSON.parse -> ComfyWorkflowJSON / warn
        Composable-->>User: workflow or failure
    end
Loading

Suggested reviewers

  • DrJKL
  • shinshin86
  • KarryCharon
  • Myestery

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 and usage tips.

@github-actions
Copy link

github-actions bot commented Jan 16, 2026

🎭 Playwright Tests: ❌ Failed

Results: 501 passed, 3 failed, 0 flaky, 8 skipped (Total: 512)

❌ Failed Tests

📊 Browser Reports
  • chromium: View Report (✅ 489 / ❌ 3 / ⚠️ 0 / ⏭️ 8)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 9 / ❌ 0 / ⚠️ 0 / ⏭️ 0)

@github-actions
Copy link

github-actions bot commented Jan 16, 2026

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 01/28/2026, 05:03:37 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Jan 16, 2026

Bundle Size Report

Summary

  • Raw size: 22.1 MB baseline 22.1 MB — 🟢 -692 B
  • Gzip: 4.59 MB baseline 4.59 MB — 🟢 -242 B
  • Brotli: 3.41 MB baseline 3.41 MB — 🟢 -257 B
  • Bundles: 172 current • 172 baseline • 81 added / 81 removed

Category Glance
Graph Workspace 🟢 -613 B (959 kB) · Other 🟢 -195 B (7.04 MB) · Data & Services 🔴 +124 B (2.71 MB) · Panels & Settings 🟢 -8 B (471 kB) · Vendor & Third-Party ⚪ 0 B (10.7 MB) · Views & Navigation ⚪ 0 B (80.7 kB) · + 5 more

Per-category breakdown
App Entry Points — 25.9 kB (baseline 25.9 kB) • ⚪ 0 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-CEONfreg.js (removed) 25.9 kB 🟢 -25.9 kB 🟢 -7.47 kB 🟢 -6.58 kB
assets/index-CZd37ZE9.js (new) 25.9 kB 🔴 +25.9 kB 🔴 +7.46 kB 🔴 +6.55 kB

Status: 1 added / 1 removed

Graph Workspace — 959 kB (baseline 960 kB) • 🟢 -613 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-ITWXKUyU.js (removed) 960 kB 🟢 -960 kB 🟢 -194 kB 🟢 -147 kB
assets/GraphView-xhpnySpX.js (new) 959 kB 🔴 +959 kB 🔴 +194 kB 🔴 +147 kB

Status: 1 added / 1 removed

Views & Navigation — 80.7 kB (baseline 80.7 kB) • ⚪ 0 B

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-CEQ3R85s.js (new) 17.1 kB 🔴 +17.1 kB 🔴 +3.6 kB 🔴 +3.05 kB
assets/CloudSurveyView-D6zOl5u8.js (removed) 17.1 kB 🟢 -17.1 kB 🟢 -3.61 kB 🟢 -3.06 kB
assets/CloudLoginView-C6DXgsa_.js (new) 11.8 kB 🔴 +11.8 kB 🔴 +3.09 kB 🔴 +2.71 kB
assets/CloudLoginView-Dwf4tgtB.js (removed) 11.8 kB 🟢 -11.8 kB 🟢 -3.09 kB 🟢 -2.71 kB
assets/UserCheckView-BG1ZrYY1.js (new) 10.5 kB 🔴 +10.5 kB 🔴 +2.45 kB 🔴 +2.13 kB
assets/UserCheckView-C6oRpp4k.js (removed) 10.5 kB 🟢 -10.5 kB 🟢 -2.45 kB 🟢 -2.13 kB
assets/CloudLayoutView-BlY64SzF.js (removed) 8.54 kB 🟢 -8.54 kB 🟢 -2.25 kB 🟢 -1.96 kB
assets/CloudLayoutView-DpkLWUKW.js (new) 8.54 kB 🔴 +8.54 kB 🔴 +2.25 kB 🔴 +1.96 kB
assets/CloudSignupView-Cw28Vi9_.js (new) 8.18 kB 🔴 +8.18 kB 🔴 +2.33 kB 🔴 +2.02 kB
assets/CloudSignupView-GcTJXSST.js (removed) 8.18 kB 🟢 -8.18 kB 🟢 -2.33 kB 🟢 -2.02 kB
assets/CloudForgotPasswordView-BmGd3qBy.js (new) 6.26 kB 🔴 +6.26 kB 🔴 +1.93 kB 🔴 +1.69 kB
assets/CloudForgotPasswordView-DJlVuJVQ.js (removed) 6.26 kB 🟢 -6.26 kB 🟢 -1.93 kB 🟢 -1.69 kB
assets/UserSelectView-Bq28s9QA.js (new) 5.28 kB 🔴 +5.28 kB 🔴 +1.76 kB 🔴 +1.58 kB
assets/UserSelectView-DqHlPVhL.js (removed) 5.28 kB 🟢 -5.28 kB 🟢 -1.76 kB 🟢 -1.58 kB
assets/CloudSubscriptionRedirectView-CXKSIVEM.js (removed) 5.27 kB 🟢 -5.27 kB 🟢 -1.73 kB 🟢 -1.54 kB
assets/CloudSubscriptionRedirectView-DwYS7mz3.js (new) 5.27 kB 🔴 +5.27 kB 🔴 +1.73 kB 🔴 +1.54 kB
assets/CloudAuthTimeoutView-b-Jd3vwm.js (removed) 5.24 kB 🟢 -5.24 kB 🟢 -1.71 kB 🟢 -1.48 kB
assets/CloudAuthTimeoutView-B5_OL08c.js (new) 5.24 kB 🔴 +5.24 kB 🔴 +1.7 kB 🔴 +1.48 kB
assets/CloudSorryContactSupportView-BBzzzDkN.js 1.97 kB 1.97 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/layout-Rbze1-VP.js 500 B 500 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 9 added / 9 removed

Panels & Settings — 471 kB (baseline 471 kB) • 🟢 -8 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/WorkspacePanel-CwnOHdIE.js (removed) 29.8 kB 🟢 -29.8 kB 🟢 -5.89 kB 🟢 -5.15 kB
assets/WorkspacePanel-La--48so.js (new) 29.8 kB 🔴 +29.8 kB 🔴 +5.89 kB 🔴 +5.15 kB
assets/LegacyCreditsPanel-Cf-JXKMn.js (new) 23.8 kB 🔴 +23.8 kB 🔴 +5.95 kB 🔴 +5.23 kB
assets/LegacyCreditsPanel-DhNmeYXV.js (removed) 23.8 kB 🟢 -23.8 kB 🟢 -5.95 kB 🟢 -5.23 kB
assets/SubscriptionPanel-fc6q8J2d.js (removed) 21 kB 🟢 -21 kB 🟢 -5.02 kB 🟢 -4.43 kB
assets/SubscriptionPanel-umXyGTB0.js (new) 21 kB 🔴 +21 kB 🔴 +5.02 kB 🔴 +4.43 kB
assets/KeybindingPanel-COHzjQkR.js (removed) 14.2 kB 🟢 -14.2 kB 🟢 -3.74 kB 🟢 -3.3 kB
assets/KeybindingPanel-CR3KeiVv.js (new) 14.2 kB 🔴 +14.2 kB 🔴 +3.74 kB 🔴 +3.3 kB
assets/AboutPanel-BczaxJlc.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.68 kB 🔴 +2.43 kB
assets/AboutPanel-BINtxkCr.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.68 kB 🟢 -2.43 kB
assets/ExtensionPanel-BGDdbpB_.js (new) 10.2 kB 🔴 +10.2 kB 🔴 +2.71 kB 🔴 +2.41 kB
assets/ExtensionPanel-DQeDL7Fc.js (removed) 10.2 kB 🟢 -10.2 kB 🟢 -2.71 kB 🟢 -2.4 kB
assets/ServerConfigPanel-DteiZTvQ.js (removed) 7.23 kB 🟢 -7.23 kB 🟢 -2.17 kB 🟢 -1.94 kB
assets/ServerConfigPanel-KtHjlqO8.js (new) 7.23 kB 🔴 +7.23 kB 🔴 +2.17 kB 🔴 +1.94 kB
assets/UserPanel-D8FXxh3L.js (removed) 6.58 kB 🟢 -6.58 kB 🟢 -1.9 kB 🟢 -1.67 kB
assets/UserPanel-YI1tG6hq.js (new) 6.58 kB 🔴 +6.58 kB 🔴 +1.9 kB 🔴 +1.68 kB
assets/refreshRemoteConfig-Bx-Lfuqg.js (removed) 1.31 kB 🟢 -1.31 kB 🟢 -573 B 🟢 -496 B
assets/refreshRemoteConfig-BYHLoWKf.js (new) 1.31 kB 🔴 +1.31 kB 🔴 +574 B 🔴 +497 B
assets/config-BNFNqHfS.js (removed) 1.16 kB 🟢 -1.16 kB 🟢 -611 B 🟢 -536 B
assets/config-CaD3Ldai.js (new) 1.15 kB 🔴 +1.15 kB 🔴 +605 B 🔴 +529 B
assets/cloudRemoteConfig-CpU1xxNJ.js (new) 1.08 kB 🔴 +1.08 kB 🔴 +496 B 🔴 +429 B
assets/cloudRemoteConfig-D9ESzBaF.js (removed) 1.08 kB 🟢 -1.08 kB 🟢 -496 B 🟢 -428 B
assets/refreshRemoteConfig-BhVRLjPE.js (new) 169 B 🔴 +169 B 🔴 +108 B 🔴 +97 B
assets/refreshRemoteConfig-D8-kzAhj.js (removed) 169 B 🟢 -169 B 🟢 -108 B 🟢 -105 B
assets/remoteConfig-BrsVXI_d.js 788 B 788 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-B9wKvI1J.js 32 kB 32 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BmGbe2N6.js 35.2 kB 35.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Bvo_gE5u.js 29.6 kB 29.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C352ZOFL.js 26.4 kB 26.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CfTcklGM.js 30.2 kB 30.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DcSPTvwN.js 29.4 kB 29.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DjnBsCcX.js 39.4 kB 39.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DzHQYae7.js 32.9 kB 32.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-kAwy8UoQ.js 25.8 kB 25.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-KHOuN10S.js 30.4 kB 30.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Y1z1vZwm.js 31.2 kB 31.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 12 added / 12 removed

User & Accounts — 3.94 kB (baseline 3.94 kB) • ⚪ 0 B

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/auth-BVrPy_aM.js (new) 3.54 kB 🔴 +3.54 kB 🔴 +1.24 kB 🔴 +1.05 kB
assets/auth-C9NI6ms6.js (removed) 3.54 kB 🟢 -3.54 kB 🟢 -1.24 kB 🟢 -1.06 kB
assets/firebaseAuthStore-BiA8UW0Y.js (removed) 217 B 🟢 -217 B 🟢 -136 B 🟢 -120 B
assets/firebaseAuthStore-ZZ62aLbU.js (new) 217 B 🔴 +217 B 🔴 +136 B 🔴 +126 B
assets/auth-CGERz9Nj.js (new) 178 B 🔴 +178 B 🔴 +142 B 🔴 +131 B
assets/auth-w2xwuohN.js (removed) 178 B 🟢 -178 B 🟢 -142 B 🟢 -133 B

Status: 3 added / 3 removed

Editors & Dialogs — 2.86 kB (baseline 2.86 kB) • ⚪ 0 B

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useSubscriptionDialog-CRWWs2T7.js (new) 2.68 kB 🔴 +2.68 kB 🔴 +1.26 kB 🔴 +1.12 kB
assets/useSubscriptionDialog-DWw9SF4v.js (removed) 2.68 kB 🟢 -2.68 kB 🟢 -1.27 kB 🟢 -1.12 kB
assets/useSubscriptionDialog-CDXgtapK.js (removed) 179 B 🟢 -179 B 🟢 -110 B 🟢 -95 B
assets/useSubscriptionDialog-COWVeW4j.js (new) 179 B 🔴 +179 B 🔴 +110 B 🔴 +99 B

Status: 2 added / 2 removed

UI Components — 33.7 kB (baseline 33.7 kB) • ⚪ 0 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-C1bnKZN2.js (new) 9.52 kB 🔴 +9.52 kB 🔴 +2.69 kB 🔴 +2.41 kB
assets/ComfyQueueButton-D5FrZ3PL.js (removed) 9.52 kB 🟢 -9.52 kB 🟢 -2.68 kB 🟢 -2.41 kB
assets/SubscribeButton-C7CxOkbn.js (new) 4.63 kB 🔴 +4.63 kB 🔴 +1.57 kB 🔴 +1.39 kB
assets/SubscribeButton-D1j1YfXw.js (removed) 4.63 kB 🟢 -4.63 kB 🟢 -1.57 kB 🟢 -1.39 kB
assets/cloudFeedbackTopbarButton-BF1qr9B4.js (new) 1.24 kB 🔴 +1.24 kB 🔴 +675 B 🔴 +573 B
assets/cloudFeedbackTopbarButton-BHO0j8JQ.js (removed) 1.24 kB 🟢 -1.24 kB 🟢 -678 B 🟢 -575 B
assets/ComfyQueueButton-Cg1XwvbB.js (removed) 181 B 🟢 -181 B 🟢 -118 B 🟢 -112 B
assets/ComfyQueueButton-DgAeoKJ_.js (new) 181 B 🔴 +181 B 🔴 +118 B 🔴 +112 B
assets/Button-hzNlXzDK.js 3.82 kB 3.82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudBadge-DHwvysLJ.js 1.85 kB 1.85 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/TopbarBadge-CGy2FpT1.js 8.36 kB 8.36 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserAvatar-CWRrnxl9.js 1.73 kB 1.73 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-DUixEvZU.js 2.41 kB 2.41 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 4 added / 4 removed

Data & Services — 2.71 MB (baseline 2.71 MB) • 🔴 +124 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-R20r5guM.js (new) 2.01 MB 🔴 +2.01 MB 🔴 +424 kB 🔴 +324 kB
assets/dialogService-BCk0crVT.js (removed) 2.01 MB 🟢 -2.01 MB 🟢 -424 kB 🟢 -324 kB
assets/api-HFFg61xJ.js (new) 672 kB 🔴 +672 kB 🔴 +148 kB 🔴 +118 kB
assets/api-DSWNzJbX.js (removed) 672 kB 🟢 -672 kB 🟢 -148 kB 🟢 -118 kB
assets/releaseStore-BETf4IyJ.js (removed) 8.91 kB 🟢 -8.91 kB 🟢 -2.4 kB 🟢 -2.12 kB
assets/releaseStore-D8xWFLHR.js (new) 8.91 kB 🔴 +8.91 kB 🔴 +2.4 kB 🔴 +2.12 kB
assets/keybindingService-BbSW51s5.js (new) 6.78 kB 🔴 +6.78 kB 🔴 +1.74 kB 🔴 +1.52 kB
assets/keybindingService-jqctbGNY.js (removed) 6.78 kB 🟢 -6.78 kB 🟢 -1.74 kB 🟢 -1.51 kB
assets/bootstrapStore-aXZo9BK9.js (new) 2.69 kB 🔴 +2.69 kB 🔴 +1.03 kB 🔴 +952 B
assets/bootstrapStore-CNL7o6pZ.js (removed) 2.69 kB 🟢 -2.69 kB 🟢 -1.03 kB 🟢 -955 B
assets/userStore-Bdoy0Bcm.js (removed) 2.16 kB 🟢 -2.16 kB 🟢 -810 B 🟢 -723 B
assets/userStore-D4pC_OBx.js (new) 2.16 kB 🔴 +2.16 kB 🔴 +812 B 🔴 +727 B
assets/audioService-BKxqe3Fa.js (new) 2.03 kB 🔴 +2.03 kB 🔴 +932 B 🔴 +812 B
assets/audioService-CIGWKVRQ.js (removed) 2.03 kB 🟢 -2.03 kB 🟢 -934 B 🟢 -813 B
assets/releaseStore-CcoEzw6o.js (removed) 140 B 🟢 -140 B 🟢 -106 B 🟢 -104 B
assets/releaseStore-D5i9SXVA.js (new) 140 B 🔴 +140 B 🔴 +106 B 🔴 +108 B
assets/serverConfigStore-V1hr6NOk.js 2.64 kB 2.64 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 8 added / 8 removed

Utilities & Hooks — 25.3 kB (baseline 25.3 kB) • ⚪ 0 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useErrorHandling-CKVqSMqY.js (removed) 5.21 kB 🟢 -5.21 kB 🟢 -1.53 kB 🟢 -1.34 kB
assets/useErrorHandling-MkoGxhPD.js (new) 5.21 kB 🔴 +5.21 kB 🔴 +1.53 kB 🔴 +1.35 kB
assets/useWorkspaceUI-9M4zcVSX.js (new) 3.42 kB 🔴 +3.42 kB 🔴 +972 B 🔴 +843 B
assets/useWorkspaceUI-Cak7HPg3.js (removed) 3.42 kB 🟢 -3.42 kB 🟢 -974 B 🟢 -840 B
assets/useSubscriptionActions-Ddnb21VU.js (new) 2.22 kB 🔴 +2.22 kB 🔴 +869 B 🔴 +763 B
assets/useSubscriptionActions-DOuVHahD.js (removed) 2.22 kB 🟢 -2.22 kB 🟢 -868 B 🟢 -759 B
assets/subscriptionCheckoutUtil-BrTdYNMs.js (new) 2.03 kB 🔴 +2.03 kB 🔴 +875 B 🔴 +767 B
assets/subscriptionCheckoutUtil-DadM0LnJ.js (removed) 2.03 kB 🟢 -2.03 kB 🟢 -875 B 🟢 -771 B
assets/useSubscriptionCredits-BMWHkhID.js (new) 1.39 kB 🔴 +1.39 kB 🔴 +599 B 🔴 +529 B
assets/useSubscriptionCredits-CfStLtuU.js (removed) 1.39 kB 🟢 -1.39 kB 🟢 -597 B 🟢 -530 B
assets/audioUtils-Cz8eFVtB.js (removed) 970 B 🟢 -970 B 🟢 -547 B 🟢 -454 B
assets/audioUtils-DXbeSyKa.js (new) 970 B 🔴 +970 B 🔴 +547 B 🔴 +459 B
assets/useCurrentUser-BzgG70hR.js (new) 145 B 🔴 +145 B 🔴 +114 B 🔴 +101 B
assets/useCurrentUser-CZ5hZL0c.js (removed) 145 B 🟢 -145 B 🟢 -114 B 🟢 -106 B
assets/_plugin-vue_export-helper-DuK_Fly3.js 467 B 467 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/colorUtil-DLyjcs70.js 7.2 kB 7.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/markdownRendererUtil-Bi2cfnIy.js 1.78 kB 1.78 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/tailwindUtil-Dm8WXWty.js 488 B 488 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 7 added / 7 removed

Vendor & Third-Party — 10.7 MB (baseline 10.7 MB) • ⚪ 0 B

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-chart-dHjzWL-2.js 408 kB 408 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-C6rCU2OI.js 4.1 MB 4.1 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-VBlLkufO.js 3.04 MB 3.04 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-reka-ui-CR4RbTw1.js 256 kB 256 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-BeIVXtul.js 1.83 MB 1.83 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-DdP0ccEc.js 650 kB 650 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-VB6jWz3M.js 13.6 kB 13.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-Ce2gRtuj.js 398 kB 398 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 7.04 MB (baseline 7.04 MB) • 🟢 -195 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-ylXSWYy4.js (removed) 180 kB 🟢 -180 kB 🟢 -43.3 kB 🟢 -36.2 kB
assets/core-DAZ_dsM_.js (new) 180 kB 🔴 +180 kB 🔴 +43.3 kB 🔴 +36.2 kB
assets/WidgetSelect-C1tEjwHw.js (new) 51 kB 🔴 +51 kB 🔴 +11.2 kB 🔴 +9.81 kB
assets/WidgetSelect-CjqXM-Dd.js (removed) 51 kB 🟢 -51 kB 🟢 -11.2 kB 🟢 -9.81 kB
assets/Load3DControls-Buc0XWcJ.js (removed) 35.9 kB 🟢 -35.9 kB 🟢 -5.87 kB 🟢 -5.08 kB
assets/Load3DControls-ZLqA3oUQ.js (new) 35.9 kB 🔴 +35.9 kB 🔴 +5.87 kB 🔴 +5.08 kB
assets/SubscriptionRequiredDialogContent-93mxULD8.js (new) 28.7 kB 🔴 +28.7 kB 🔴 +6.79 kB 🔴 +5.91 kB
assets/SubscriptionRequiredDialogContent-SElF2iun.js (removed) 28.7 kB 🟢 -28.7 kB 🟢 -6.79 kB 🟢 -5.92 kB
assets/CurrentUserPopoverWorkspace-C-PTVjTD.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -4.99 kB 🟢 -4.43 kB
assets/CurrentUserPopoverWorkspace-OhwFXTJE.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +5 kB 🔴 +4.43 kB
assets/Load3D-CzwR7tEp.js (new) 19.2 kB 🔴 +19.2 kB 🔴 +4.37 kB 🔴 +3.85 kB
assets/Load3D-DPG4JV-g.js (removed) 19.2 kB 🟢 -19.2 kB 🟢 -4.37 kB 🟢 -3.85 kB
assets/WidgetRecordAudio-BMxJnkqV.js (removed) 18.3 kB 🟢 -18.3 kB 🟢 -4.97 kB 🟢 -4.45 kB
assets/WidgetRecordAudio-WGZU0Iz2.js (new) 18.3 kB 🔴 +18.3 kB 🔴 +4.97 kB 🔴 +4.44 kB
assets/WidgetInputNumber-C3yvyGuk.js (new) 18.3 kB 🔴 +18.3 kB 🔴 +4.51 kB 🔴 +4.01 kB
assets/WidgetInputNumber-CMNX6qIS.js (removed) 18.3 kB 🟢 -18.3 kB 🟢 -4.51 kB 🟢 -4.02 kB
assets/SubscriptionPanelContentWorkspace-BjfFKzbB.js (removed) 18.2 kB 🟢 -18.2 kB 🟢 -4.47 kB 🟢 -3.9 kB
assets/SubscriptionPanelContentWorkspace-DHM6ROr-.js (new) 18.2 kB 🔴 +18.2 kB 🔴 +4.47 kB 🔴 +3.9 kB
assets/WidgetImageCrop-B4XeyItn.js (new) 17.1 kB 🔴 +17.1 kB 🔴 +4.13 kB 🔴 +3.63 kB
assets/WidgetImageCrop-BFHFenVo.js (removed) 17.1 kB 🟢 -17.1 kB 🟢 -4.14 kB 🟢 -3.63 kB
assets/PanelTemplate-BXUpxOOE.js (removed) 16.2 kB 🟢 -16.2 kB 🟢 -5.46 kB 🟢 -4.8 kB
assets/PanelTemplate-DVPLdGzq.js (new) 16.2 kB 🔴 +16.2 kB 🔴 +5.46 kB 🔴 +4.8 kB
assets/AudioPreviewPlayer-B3fdh0JU.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.97 kB 🔴 +2.65 kB
assets/AudioPreviewPlayer-Dh2VZiA-.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.97 kB 🟢 -2.65 kB
assets/InviteMemberDialogContent-BxOtMSZE.js (new) 8.36 kB 🔴 +8.36 kB 🔴 +2.5 kB 🔴 +2.16 kB
assets/InviteMemberDialogContent-D9rn81kp.js (removed) 8.36 kB 🟢 -8.36 kB 🟢 -2.5 kB 🟢 -2.17 kB
assets/WidgetWithControl-DARgf8kJ.js (new) 8.04 kB 🔴 +8.04 kB 🔴 +2.66 kB 🔴 +2.4 kB
assets/WidgetWithControl-ZBVHOguP.js (removed) 8.04 kB 🟢 -8.04 kB 🟢 -2.66 kB 🟢 -2.39 kB
assets/CreateWorkspaceDialogContent-Ok9opT0O.js (new) 5.93 kB 🔴 +5.93 kB 🔴 +1.92 kB 🔴 +1.68 kB
assets/CreateWorkspaceDialogContent-yFhqEgGZ.js (removed) 5.93 kB 🟢 -5.93 kB 🟢 -1.93 kB 🟢 -1.68 kB
assets/EditWorkspaceDialogContent-BGjOZjtP.js (new) 5.7 kB 🔴 +5.7 kB 🔴 +1.88 kB 🔴 +1.64 kB
assets/EditWorkspaceDialogContent-PV1wa3q5.js (removed) 5.7 kB 🟢 -5.7 kB 🟢 -1.88 kB 🟢 -1.64 kB
assets/ValueControlPopover-BWlQe5e8.js (removed) 5.17 kB 🟢 -5.17 kB 🟢 -1.69 kB 🟢 -1.5 kB
assets/ValueControlPopover-DOq05xvP.js (new) 5.17 kB 🔴 +5.17 kB 🔴 +1.69 kB 🔴 +1.5 kB
assets/DeleteWorkspaceDialogContent-Bl7MzNVG.js (removed) 4.59 kB 🟢 -4.59 kB 🟢 -1.56 kB 🟢 -1.35 kB
assets/DeleteWorkspaceDialogContent-BotAzp-6.js (new) 4.59 kB 🔴 +4.59 kB 🔴 +1.56 kB 🔴 +1.35 kB
assets/LeaveWorkspaceDialogContent-Cmc4DcfH.js (new) 4.41 kB 🔴 +4.41 kB 🔴 +1.5 kB 🔴 +1.3 kB
assets/LeaveWorkspaceDialogContent-HaIx8z9s.js (removed) 4.41 kB 🟢 -4.41 kB 🟢 -1.5 kB 🟢 -1.31 kB
assets/RemoveMemberDialogContent-CCfomoSZ.js (removed) 4.38 kB 🟢 -4.38 kB 🟢 -1.45 kB 🟢 -1.27 kB
assets/RemoveMemberDialogContent-VhPs2nn4.js (new) 4.38 kB 🔴 +4.38 kB 🔴 +1.45 kB 🔴 +1.27 kB
assets/RevokeInviteDialogContent-C3QRm4Og.js (new) 4.29 kB 🔴 +4.29 kB 🔴 +1.47 kB 🔴 +1.29 kB
assets/RevokeInviteDialogContent-C3SX537q.js (removed) 4.29 kB 🟢 -4.29 kB 🟢 -1.47 kB 🟢 -1.29 kB
assets/GlobalToast-8vbyICLE.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.1 kB 🔴 +941 B
assets/GlobalToast-BKkzNoSs.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.1 kB 🟢 -939 B
assets/SubscribeToRun-CsjzP46r.js (removed) 2.96 kB 🟢 -2.96 kB 🟢 -1.15 kB 🟢 -1.01 kB
assets/SubscribeToRun-CzsEqQUU.js (new) 2.96 kB 🔴 +2.96 kB 🔴 +1.16 kB 🔴 +1.01 kB
assets/cloudSessionCookie-9tb8eJJi.js (new) 2.94 kB 🔴 +2.94 kB 🔴 +929 B 🔴 +800 B
assets/cloudSessionCookie-CV-gFnrP.js (removed) 2.94 kB 🟢 -2.94 kB 🟢 -929 B 🟢 -807 B
assets/BaseViewTemplate-DuIGzeyw.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +1.04 kB 🔴 +942 B
assets/BaseViewTemplate-tLSchlN1.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -1.04 kB 🟢 -937 B
assets/CloudRunButtonWrapper-Bg-RUYLY.js (removed) 1.79 kB 🟢 -1.79 kB 🟢 -643 B 🟢 -567 B
assets/CloudRunButtonWrapper-BHyJFBkP.js (new) 1.79 kB 🔴 +1.79 kB 🔴 +645 B 🔴 +564 B
assets/cloudBadges-CjKdAjPw.js (new) 1.08 kB 🔴 +1.08 kB 🔴 +537 B 🔴 +476 B
assets/cloudBadges-D0szGafM.js (removed) 1.08 kB 🟢 -1.08 kB 🟢 -537 B 🟢 -489 B
assets/graphHasMissingNodes-C4Qnvcoo.js (removed) 1.06 kB 🟢 -1.06 kB 🟢 -461 B 🟢 -413 B
assets/graphHasMissingNodes-DTTIt_e3.js (new) 1.06 kB 🔴 +1.06 kB 🔴 +462 B 🔴 +410 B
assets/cloudSubscription-B8Za_qWi.js (new) 976 B 🔴 +976 B 🔴 +464 B 🔴 +397 B
assets/cloudSubscription-ivMRLjHz.js (removed) 976 B 🟢 -976 B 🟢 -461 B 🟢 -397 B
assets/nightlyBadges--zR6Utuj.js (new) 595 B 🔴 +595 B 🔴 +357 B 🔴 +308 B
assets/nightlyBadges-2Jgn4DJk.js (removed) 595 B 🟢 -595 B 🟢 -357 B 🟢 -309 B
assets/SubscriptionPanelContentWorkspace-D15rnYDB.js (new) 266 B 🔴 +266 B 🔴 +136 B 🔴 +118 B
assets/SubscriptionPanelContentWorkspace-xcgJSttX.js (removed) 266 B 🟢 -266 B 🟢 -136 B 🟢 -112 B
assets/WidgetInputNumber-B1_FE8Up.js (removed) 186 B 🟢 -186 B 🟢 -119 B 🟢 -112 B
assets/WidgetInputNumber-DXR6Ujdd.js (new) 186 B 🔴 +186 B 🔴 +119 B 🔴 +121 B
assets/WidgetLegacy-Bz9wvqH6.js (new) 164 B 🔴 +164 B 🔴 +125 B 🔴 +113 B
assets/WidgetLegacy-DPDoTZhq.js (removed) 164 B 🟢 -164 B 🟢 -125 B 🟢 -112 B
assets/Load3D-BmBkQlPP.js (removed) 131 B 🟢 -131 B 🟢 -107 B 🟢 -119 B
assets/Load3D-CbiLKXTj.js (new) 131 B 🔴 +131 B 🔴 +107 B 🔴 +106 B
assets/auto-IxU1eXFy.js 1.73 kB 1.73 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-B5unjTgE.js 18 kB 18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BsP-x5ri.js 17.9 kB 17.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BtcJfAd5.js 17 kB 17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CEbEylFJ.js 18.5 kB 18.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CSE42kAA.js 20.6 kB 20.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CX9qSh0Y.js 19.3 kB 19.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DgvoXt0v.js 18.8 kB 18.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-Dk-PWJsO.js 17.2 kB 17.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DKwvlJiD.js 19.3 kB 19.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DQuDUlM5.js 17.8 kB 17.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-KhEZ5Fo9.js 18 kB 18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/i18n-Ckejo70h.js 188 B 188 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/i18n-DyLIWZ1Y.js 496 kB 496 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/LazyImage-D5HGD2sm.js 14.1 kB 14.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BAsVw_VR.js 128 kB 128 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BdcuOtW_.js 172 kB 172 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BjdhOMKP.js 112 kB 112 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-C1_ALuzX.js 154 kB 154 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CawlsjrP.js 111 kB 111 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CueKFh2S.js 125 kB 125 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CuMAi0Yg.js 132 kB 132 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Dgyoixz9.js 143 kB 143 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DrWtIub4.js 150 kB 150 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DZ27z6-O.js 125 kB 125 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-nzg6hhK8.js 129 kB 129 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Media3DTop-B5p5daPP.js 2.38 kB 2.38 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-LOmU6GXe.js 2 kB 2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-C7M85A9X.js 2.34 kB 2.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-ChOx-pbs.js 2.82 kB 2.82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/mixpanel.module-DaC789SX.js 143 B 143 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-B8DCf1mS.js 413 kB 413 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BAFHrj6M.js 366 kB 366 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BlUt4zjH.js 412 kB 412 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BuHiqTYg.js 342 kB 342 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-cE9XzsAm.js 363 kB 363 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DiynUDVW.js 383 kB 383 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Dk1oqXg6.js 373 kB 373 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DpRc9McY.js 339 kB 339 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-NvS3ne6s.js 369 kB 369 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-SBfEQMMs.js 369 kB 369 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-W4n6VSIV.js 448 kB 448 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/previousFullPath-0kMFfOzo.js 838 B 838 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/rolldown-runtime-cVp-94Rc.js 1.96 kB 1.96 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Slider-C5sanYDO.js 4.21 kB 4.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widget-BJiJuR5i.js 518 B 518 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-CdPxGIkA.js 4.71 kB 4.71 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-kYf5x876.js 186 B 186 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-BflopHUr.js 2.79 kB 2.79 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetColorPicker-BgT2fava.js 3.71 kB 3.71 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetGalleria-CYuetUyx.js 4.57 kB 4.57 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-DG_IjzLF.js 3.79 kB 3.79 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetInputText-BLB-l7ow.js 2.58 kB 2.58 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetLayoutField-B6sR-d_3.js 2.61 kB 2.61 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetMarkdown-CeUTWczT.js 3.22 kB 3.22 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BtBp3h_n.js 1.31 kB 1.31 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetTextarea-FfGbecfC.js 3.52 kB 3.52 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetToggleSwitch-Cu4DaH99.js 3.08 kB 3.08 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 34 added / 34 removed

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

🤖 Fix all issues with AI agents
In `@src/platform/workflow/persistence/services/sessionStorageLruService.ts`:
- Around line 28-30: The function isProtectedKey currently checks
PROTECTED_KEY_PREFIXES.some(prefix => key.includes(prefix)), which matches the
prefix anywhere in the key; if the intent is true prefix matching change the
check to use startsWith instead of includes (i.e., in isProtectedKey replace the
includes-based predicate with prefix => key.startsWith(prefix)); if the broader
"contains" behavior is intended, add a clarifying comment above
PROTECTED_KEY_PREFIXES explaining that entries may appear anywhere in the key.
- Around line 205-211: The comparison entry.accessedAt !== Date.now() is
pointless because Date.now() changes each call; in sessionStorageLruService.ts
inside the block guarded by updateAccessTime, remove the Date.now() equality
check and always update entry.accessedAt = Date.now() when updateAccessTime is
true, then write back via sessionStorage.setItem(key,
wrapForStorage(entry.data)) inside the existing try/catch to avoid quota errors;
reference symbols: updateAccessTime, entry.accessedAt, Date.now(),
sessionStorage.setItem, wrapForStorage, key.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6714b95 and 78e2d70.

📒 Files selected for processing (3)
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
🧰 Additional context used
📓 Path-based instructions (11)
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Use es-toolkit for utility functions
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional
Minimize nesting (if statements, for loops, etc.)
Use function declarations instead of function expressions when possible

Files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
+(tests-ui|src)/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src)/**/*.test.ts: Unit and component tests belong in tests-ui/ or src/**/*.test.ts using Vitest
Write tests for all changes, especially bug fixes to catch future regressions
Do not write tests dependent on non-behavioral features like utility classes or styles
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
Leverage Vitest's utilities for mocking where possible
Keep module mocks contained - do not use global mutable state within test files; use vi.hoisted() if necessary
Use Vue Test Utils for Component testing and follow best practices for making components easy to test
Aim for behavioral coverage of critical and new features in unit tests

Files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts)

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts): Do not write change detector tests - avoid tests that only assert default values
Be parsimonious in testing - do not write redundant tests
Don't Mock What You Don't Own

Files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
🧠 Learnings (21)
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/stores/**/*.{ts,tsx} : Maintain clear public interfaces and restrict extension access in stores

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2026-01-12T17:39:27.738Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.ts
  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Leverage Vitest's utilities for mocking where possible

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Aim for behavioral coverage of critical and new features in unit tests

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files; use `vi.hoisted()` if necessary

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Unit and component tests belong in `tests-ui/` or `src/**/*.test.ts` using Vitest

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts) : Be parsimonious in testing - do not write redundant tests

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Test user workflows in browser tests

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Use `vitest` for unit testing in this project

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{js,ts,jsx,tsx} : When adding features, always write vitest unit tests using cursor rules in @.cursor

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Do not write tests dependent on non-behavioral features like utility classes or styles

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2025-12-10T03:09:13.807Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7303
File: src/components/topbar/CurrentUserPopover.test.ts:199-205
Timestamp: 2025-12-10T03:09:13.807Z
Learning: In test files, prefer selecting or asserting on accessible properties (text content, aria-label, role, accessible name) over data-testid attributes. This ensures tests validate actual user-facing behavior and accessibility, reducing reliance on implementation details like test IDs.

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2025-12-30T01:31:04.927Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7797
File: tests-ui/tests/lib/litegraph/src/widgets/ComboWidget.test.ts:648-648
Timestamp: 2025-12-30T01:31:04.927Z
Learning: In Vitest v4, when mocking functions that may be called as constructors (using new), the mock implementation must use function() or class syntax rather than an arrow function. Arrow mocks can cause '<anonymous> is not a constructor' errors. This is a breaking change from Vitest v3 where mocks could use an arrow function. Apply this guideline to test files that mock constructor-like calls (e.g., in tests under tests-ui, such as ComboWidget.test.ts) and ensure mock implementations are defined with function() { ... } or class { ... } to preserve constructor behavior.

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2026-01-08T02:40:15.482Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7894
File: src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts:11-14
Timestamp: 2026-01-08T02:40:15.482Z
Learning: In TypeScript test files (e.g., any test under src), avoid duplicating interface/type definitions. Import real type definitions from the component modules under test and reference them directly, so there is a single source of truth and to prevent type drift. This improves maintainability and consistency across tests.

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
📚 Learning: 2026-01-09T02:07:54.558Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7898
File: src/composables/usePaste.test.ts:248-248
Timestamp: 2026-01-09T02:07:54.558Z
Learning: In test files (e.g., any .test.ts or .test.tsx under src/...), when you create mock objects that partially implement an interface (such as LGraphNode), prefer casting with as Partial<InterfaceType> as InterfaceType rather than as any or as unknown as InterfaceType. This makes the incomplete implementation explicit while preserving type safety, improving readability and maintainability of tests.

Applied to files:

  • src/platform/workflow/persistence/services/sessionStorageLruService.test.ts
🧬 Code graph analysis (2)
src/platform/workflow/persistence/services/sessionStorageLruService.test.ts (1)
src/platform/workflow/persistence/services/sessionStorageLruService.ts (4)
  • setWithLruEviction (146-186)
  • getWithLruTracking (195-218)
  • removeFromStorage (223-225)
  • getStorageStats (230-280)
src/platform/workflow/persistence/composables/useWorkflowPersistence.ts (4)
src/scripts/api.ts (1)
  • api (1304-1304)
src/platform/workflow/persistence/services/sessionStorageLruService.ts (3)
  • setWithLruEviction (146-186)
  • getStorageStats (230-280)
  • getWithLruTracking (195-218)
src/platform/workflow/validation/schemas/workflowSchema.ts (1)
  • ComfyWorkflowJSON (459-461)
src/scripts/utils.ts (1)
  • getStorageValue (96-102)
⏰ 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: test
  • GitHub Check: lint-and-format
  • GitHub Check: setup
  • GitHub Check: collect
🔇 Additional comments (17)
src/platform/workflow/persistence/services/sessionStorageLruService.test.ts (5)

1-18: LGTM! Test setup follows Vitest best practices.

The setup uses vi.clearAllMocks() in beforeEach and vi.restoreAllMocks() in afterEach, ensuring clean isolation between tests. Based on learnings, this leverages Vitest's utilities appropriately.


42-69: Well-structured eviction test with proper mock isolation.

The mock correctly simulates QuotaExceededError on the first attempt, then allows subsequent writes via the original setItem. The assertions verify both the eviction (oldest entry removed) and successful storage (newer entry preserved). This tests the actual LRU eviction behavior.


97-103: Good coverage for legacy format backward compatibility.

This test ensures legacy entries (raw workflow JSON without LRU wrapper) are handled correctly, which is critical for the migration period mentioned in the PR objectives.


166-188: Critical tests for LRU ordering semantics.

These tests verify that entries are sorted by accessedAt (oldest first) and that legacy entries receive accessedAt: 0, ensuring they're prioritized for eviction. This is essential for the migration strategy.


191-237: Comprehensive LRU eviction order verification.

These tests confirm the eviction priority: legacy entries (with accessedAt: 0) are evicted before newer entries, and among new-format entries, oldest are evicted first. This validates the core LRU behavior.

src/platform/workflow/persistence/services/sessionStorageLruService.ts (8)

1-26: LGTM! Well-documented module with clear deprecation timeline.

The JSDoc header clearly explains the module's purpose and the deprecation notice for legacy format support provides a clear migration path. The StorageEntry<T> interface correctly uses generics for type safety.


39-43: LGTM! Legacy format detection is straightforward.

The check for both accessedAt and data properties correctly distinguishes between legacy raw workflow JSON and the new wrapped format.


62-73: LGTM! Legacy handling with eviction priority.

Assigning accessedAt: 0 to legacy entries ensures they're evicted first during the migration period, which is a smart approach for backward compatibility.


88-101: Good defensive error handling for malformed entries.

Entries that fail to parse are still included with accessedAt: 0, ensuring they can be evicted. This prevents corrupt entries from blocking storage operations.


111-136: LGTM! Core LRU eviction logic is correct.

The entries are sorted by accessedAt ascending (oldest first), filtered to exclude the current key, and evicted in batches. The freed bytes calculation enables retry decisions.


146-186: LGTM! Robust retry logic with escalating eviction.

The implementation correctly:

  1. Attempts storage first before any eviction
  2. Escalates eviction count (1 → 2 → 3 entries) on successive failures
  3. Bails out early if no entries are available for eviction
  4. Distinguishes QuotaExceededError from other errors

222-225: LGTM! Simple and correct.


229-279: LGTM! Comprehensive storage statistics.

The size estimation using raw.length * 2 correctly accounts for JavaScript's UTF-16 string encoding. The function provides useful debugging information while maintaining consistency with the eviction logic (unparseable entries get accessedAt: 0).

src/platform/workflow/persistence/composables/useWorkflowPersistence.ts (4)

14-25: LGTM! Imports follow coding guidelines.

The type import ComfyWorkflowJSON is correctly on a separate line from non-type imports, adhering to the repository's lint rules. Based on learnings, this pattern is required.


53-86: LGTM! Dual storage strategy with LRU for session-specific persistence.

The implementation correctly:

  1. Always persists to localStorage as a fallback (line 59)
  2. Uses LRU-backed sessionStorage when api.clientId exists (lines 67-85)
  3. Logs comprehensive debug info on failure including workflow size and storage stats

88-95: LGTM! Clean refactor with proper typing.

The function now accepts ComfyWorkflowJSON | null directly, eliminating the need for internal JSON parsing. The null guard ensures safe usage.


97-125: LGTM! Robust loading with session-first, localStorage-fallback strategy.

The implementation:

  1. Tries LRU-tracked sessionStorage first (client-specific)
  2. Falls back to localStorage (shared across sessions)
  3. Handles JSON parse failures gracefully with a warning

The type assertion as ComfyWorkflowJSON at line 115 is acceptable since JSON.parse returns unknown and validation would add overhead for trusted local data.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +28 to +30
function isProtectedKey(key: string): boolean {
return PROTECTED_KEY_PREFIXES.some((prefix) => key.includes(prefix))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Clarify: includes vs startsWith for protected key matching.

The constant is named PROTECTED_KEY_PREFIXES, but includes() matches anywhere in the string, not just at the start. If keys like foo.workspace.bar should also be protected, this is correct. Otherwise, consider using startsWith() for true prefix matching.

♻️ If prefix-only matching is intended
 function isProtectedKey(key: string): boolean {
-  return PROTECTED_KEY_PREFIXES.some((prefix) => key.includes(prefix))
+  return PROTECTED_KEY_PREFIXES.some((prefix) => key.startsWith(prefix))
 }
📝 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
function isProtectedKey(key: string): boolean {
return PROTECTED_KEY_PREFIXES.some((prefix) => key.includes(prefix))
}
function isProtectedKey(key: string): boolean {
return PROTECTED_KEY_PREFIXES.some((prefix) => key.startsWith(prefix))
}
🤖 Prompt for AI Agents
In `@src/platform/workflow/persistence/services/sessionStorageLruService.ts`
around lines 28 - 30, The function isProtectedKey currently checks
PROTECTED_KEY_PREFIXES.some(prefix => key.includes(prefix)), which matches the
prefix anywhere in the key; if the intent is true prefix matching change the
check to use startsWith instead of includes (i.e., in isProtectedKey replace the
includes-based predicate with prefix => key.startsWith(prefix)); if the broader
"contains" behavior is intended, add a clarifying comment above
PROTECTED_KEY_PREFIXES explaining that entries may appear anywhere in the key.

Comment on lines +205 to +211
if (updateAccessTime && entry.accessedAt !== Date.now()) {
try {
sessionStorage.setItem(key, wrapForStorage(entry.data))
} catch {
// Ignore quota errors when updating access time
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

The Date.now() comparison is effectively always true.

The condition entry.accessedAt !== Date.now() will almost always evaluate to true since Date.now() returns the current time at evaluation, not the time when the entry was stored. This comparison seems intended to skip unnecessary writes, but it provides minimal benefit and adds complexity.

Consider simplifying to just check updateAccessTime:

♻️ Simplified access time update
-    if (updateAccessTime && entry.accessedAt !== Date.now()) {
+    if (updateAccessTime) {
       try {
         sessionStorage.setItem(key, wrapForStorage(entry.data))
       } catch {
         // Ignore quota errors when updating access time
       }
     }
📝 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
if (updateAccessTime && entry.accessedAt !== Date.now()) {
try {
sessionStorage.setItem(key, wrapForStorage(entry.data))
} catch {
// Ignore quota errors when updating access time
}
}
if (updateAccessTime) {
try {
sessionStorage.setItem(key, wrapForStorage(entry.data))
} catch {
// Ignore quota errors when updating access time
}
}
🤖 Prompt for AI Agents
In `@src/platform/workflow/persistence/services/sessionStorageLruService.ts`
around lines 205 - 211, The comparison entry.accessedAt !== Date.now() is
pointless because Date.now() changes each call; in sessionStorageLruService.ts
inside the block guarded by updateAccessTime, remove the Date.now() equality
check and always update entry.accessedAt = Date.now() when updateAccessTime is
true, then write back via sessionStorage.setItem(key,
wrapForStorage(entry.data)) inside the existing try/catch to avoid quota errors;
reference symbols: updateAccessTime, entry.accessedAt, Date.now(),
sessionStorage.setItem, wrapForStorage, key.

@christian-byrne
Copy link
Contributor Author

Keeping out of draft so I can get CR review. But can be considered draft.

- Rename to workflowSessionStorageService with semantic names
- Remove verbose JSDoc and byte tracking
- Streamline logging to specific error cases
- Simplify eviction flow (280 → 115 lines)

Amp-Thread-ID: https://ampcode.com/threads/T-019bc597-fbea-778a-ae15-92061b3d0812
Co-authored-by: Amp <amp@ampcode.com>
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. labels Jan 16, 2026
@coderabbitai coderabbitai bot requested a review from pythongosssss January 16, 2026 07:05
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

🤖 Fix all issues with AI agents
In `@src/platform/workflow/persistence/composables/useWorkflowPersistence.ts`:
- Around line 95-106: Replace the unchecked cast of parsed localStorage JSON
with Zod validation: parse localStorage.getItem('workflow'), pass the parsed
value into validateComfyWorkflow(), and if validation fails log
'[WorkflowPersistence] Invalid localStorage workflow' and return false; if
validation succeeds call loadWorkflowFromData(validated, workflowName). Update
the try/catch around JSON.parse to use validateComfyWorkflow() instead of "as
ComfyWorkflowJSON" and preserve the existing catch that logs parse failures.

In
`@src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts`:
- Around line 61-70: Remove the redundant explicit mock restoration calls in the
tests: delete the spy.mockRestore() invocations in the test cases that mock
sessionStorage.setItem (the tests calling setWithEviction('key', ...) and the
other similar test), since the test suite already uses vi.restoreAllMocks() in
afterEach; leave the spy creation (vi.spyOn(...).mockImplementation(...)) and
assertions intact and rely on vi.restoreAllMocks() to restore the mocks
automatically.

In `@src/platform/workflow/persistence/services/workflowSessionStorageService.ts`:
- Around line 12-16: isProtectedKey currently uses PROTECTED_KEY_PREFIXES with
Array.prototype.includes which checks anywhere in the string; change the match
to prefix-only by using String.prototype.startsWith instead (i.e., in function
isProtectedKey replace key.includes(prefix) with key.startsWith(prefix)) so keys
are only considered protected when they begin with the listed prefixes (keep the
existing PROTECTED_KEY_PREFIXES values like 'workspace.' and 'Workspace.').
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78e2d70 and add7a4f.

📒 Files selected for processing (3)
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
🧰 Additional context used
📓 Path-based instructions (11)
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Use es-toolkit for utility functions
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
+(tests-ui|src)/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src)/**/*.test.ts: Unit and component tests belong in tests-ui/ or src/**/*.test.ts using Vitest
Write tests for all changes, especially bug fixes to catch future regressions
Do not write tests dependent on non-behavioral features like utility classes or styles
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
Leverage Vitest's utilities for mocking where possible
Keep module mocks contained - do not use global mutable state within test files; use vi.hoisted() if necessary
Use Vue Test Utils for Component testing and follow best practices for making components easy to test
Aim for behavioral coverage of critical and new features in unit tests

Files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional
Minimize nesting (if statements, for loops, etc.)
Use function declarations instead of function expressions when possible

Files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts)

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts): Do not write change detector tests - avoid tests that only assert default values
Be parsimonious in testing - do not write redundant tests
Don't Mock What You Don't Own

Files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
🧠 Learnings (16)
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Test user workflows in browser tests

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Aim for behavioral coverage of critical and new features in unit tests

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files; use `vi.hoisted()` if necessary

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Leverage Vitest's utilities for mocking where possible

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Use `vitest` for unit testing in this project

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
📚 Learning: 2025-12-10T03:09:13.807Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7303
File: src/components/topbar/CurrentUserPopover.test.ts:199-205
Timestamp: 2025-12-10T03:09:13.807Z
Learning: In test files, prefer selecting or asserting on accessible properties (text content, aria-label, role, accessible name) over data-testid attributes. This ensures tests validate actual user-facing behavior and accessibility, reducing reliance on implementation details like test IDs.

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
📚 Learning: 2025-12-30T01:31:04.927Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7797
File: tests-ui/tests/lib/litegraph/src/widgets/ComboWidget.test.ts:648-648
Timestamp: 2025-12-30T01:31:04.927Z
Learning: In Vitest v4, when mocking functions that may be called as constructors (using new), the mock implementation must use function() or class syntax rather than an arrow function. Arrow mocks can cause '<anonymous> is not a constructor' errors. This is a breaking change from Vitest v3 where mocks could use an arrow function. Apply this guideline to test files that mock constructor-like calls (e.g., in tests under tests-ui, such as ComboWidget.test.ts) and ensure mock implementations are defined with function() { ... } or class { ... } to preserve constructor behavior.

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
📚 Learning: 2026-01-08T02:40:15.482Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7894
File: src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts:11-14
Timestamp: 2026-01-08T02:40:15.482Z
Learning: In TypeScript test files (e.g., any test under src), avoid duplicating interface/type definitions. Import real type definitions from the component modules under test and reference them directly, so there is a single source of truth and to prevent type drift. This improves maintainability and consistency across tests.

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
📚 Learning: 2026-01-09T02:07:54.558Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7898
File: src/composables/usePaste.test.ts:248-248
Timestamp: 2026-01-09T02:07:54.558Z
Learning: In test files (e.g., any .test.ts or .test.tsx under src/...), when you create mock objects that partially implement an interface (such as LGraphNode), prefer casting with as Partial<InterfaceType> as InterfaceType rather than as any or as unknown as InterfaceType. This makes the incomplete implementation explicit while preserving type safety, improving readability and maintainability of tests.

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
📚 Learning: 2026-01-12T17:39:27.738Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Applied to files:

  • src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/services/workflowSessionStorageService.ts
🧬 Code graph analysis (2)
src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts (1)
src/platform/workflow/persistence/services/workflowSessionStorageService.ts (3)
  • setWithEviction (89-125)
  • getWithAccessTracking (131-154)
  • removeFromStorage (156-158)
src/platform/workflow/persistence/composables/useWorkflowPersistence.ts (4)
src/scripts/api.ts (1)
  • api (1304-1304)
src/platform/workflow/management/stores/workflowStore.ts (1)
  • key (55-57)
src/platform/workflow/persistence/services/workflowSessionStorageService.ts (2)
  • setWithEviction (89-125)
  • getWithAccessTracking (131-154)
src/platform/workflow/validation/schemas/workflowSchema.ts (1)
  • ComfyWorkflowJSON (459-461)
⏰ 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: setup
  • GitHub Check: test
  • GitHub Check: collect
  • GitHub Check: lint-and-format
🔇 Additional comments (14)
src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts (3)

84-133: LGTM!

Comprehensive test coverage for getWithAccessTracking including both wrapped and legacy format handling, access time updates, and error scenarios.


135-143: LGTM!

Simple and appropriate test for the removeFromStorage function.


145-167: LGTM!

Good tests verifying eviction order prioritizes legacy entries and respects protected keys. The mock pattern correctly simulates quota exceeded on first attempt, allowing the eviction logic to be exercised.

src/platform/workflow/persistence/composables/useWorkflowPersistence.ts (3)

18-25: LGTM!

Imports are correctly separated with type imports on their own line. The WORKFLOW_KEY_PATTERN constant appropriately matches the pattern used for eviction in the service.


52-70: LGTM!

The dual storage approach (localStorage as fallback, session storage with eviction as primary) provides good resilience. The ignored return value from setWithEviction aligns with the graceful degradation design documented in the PR objectives.


72-79: LGTM!

Clean refactor with proper TypeScript typing. The null check with early return allows the caller to handle fallback logic cleanly.

src/platform/workflow/persistence/services/workflowSessionStorageService.ts (8)

18-26: LGTM!

Legacy format detection is correctly implemented with appropriate deprecation notice. The logic properly identifies entries that lack the { accessedAt, data } wrapper structure.


28-30: LGTM!

Clean wrapper function that adds the access timestamp for LRU tracking.


32-41: LGTM!

Correctly handles both wrapped and legacy formats. Assigning accessedAt: 0 to legacy entries ensures they're prioritized for eviction.


43-68: LGTM!

Well-implemented eviction candidate selection. The approach of treating parse errors as legacy (oldest) is sensible for cleanup. Sorting by accessedAt ascending ensures proper LRU ordering.


70-83: LGTM!

Clean eviction logic that removes the oldest entries first and returns the count for retry logic.


89-125: LGTM!

Solid retry-with-eviction implementation. The progressive eviction strategy (1, 2, 3 entries per retry) balances between recovering from quota issues and not over-evicting. Error handling properly distinguishes quota errors from unexpected failures.


131-154: LGTM!

Well-designed read API with proper access tracking. The nested try-catch correctly handles quota errors during timestamp updates without failing the read operation.


156-158: LGTM!

Simple and appropriate wrapper providing a consistent API surface for the storage service.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +95 to +106
// Fall back to local storage (raw JSON, no LRU wrapper)
const localWorkflowJson = localStorage.getItem('workflow')
if (localWorkflowJson) {
try {
const localWorkflow = JSON.parse(localWorkflowJson) as ComfyWorkflowJSON
return await loadWorkflowFromData(localWorkflow, workflowName)
} catch {
console.warn(
'[WorkflowPersistence] Failed to parse localStorage workflow'
)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

# Find and read the target file
find . -name "useWorkflowPersistence.ts" -type f | head -5

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 143


🏁 Script executed:

# Look at the structure around the lines in question
find . -path "*/workflow/persistence/composables/*" -type f

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 290


🏁 Script executed:

# Search for ComfyWorkflowJSON type definition and any Zod schemas
rg "ComfyWorkflowJSON" --type ts -A 3 -B 3

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 48392


🏁 Script executed:

# Search for loadGraphData function to check its validation
rg "loadGraphData" --type ts -A 15 | head -100

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 8193


🏁 Script executed:

# Check for Zod schema validation patterns in the codebase
rg "safeParse|schema\.parse" --type ts | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2216


🏁 Script executed:

# Read the file around the problematic lines
cat -n src/platform/workflow/persistence/composables/useWorkflowPersistence.ts | sed -n '80,115p'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1538


🏁 Script executed:

# Check if validateComfyWorkflow is imported or used in this file
rg "validateComfyWorkflow" src/platform/workflow/persistence/composables/useWorkflowPersistence.ts -A 5 -B 5

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

# Check the loadWorkflowFromData function to see if it does validation
rg "loadWorkflowFromData" src/platform/workflow/persistence/composables/useWorkflowPersistence.ts -A 10

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1029


🏁 Script executed:

# Check loadGraphData to see if it validates the input
rg "async loadGraphData" src/scripts/app.ts -A 30 | head -60

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 961


🏁 Script executed:

# Check if loadGraphData has any validation logic
rg "loadGraphData" src/scripts/app.ts -A 80 | grep -A 80 "async loadGraphData"

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2577


🏁 Script executed:

# Check if there's any validation in the workflow validation composable that might be called
rg "useWorkflowValidation" src/platform/workflow/persistence/composables/useWorkflowPersistence.ts -B 3 -A 3

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

# Look at the validateComfyWorkflow function to understand the pattern
cat -n src/platform/workflow/validation/schemas/workflowSchema.ts | sed -n '350,400p'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1903


🏁 Script executed:

# Find validateComfyWorkflow signature and pattern
cat -n src/platform/workflow/validation/schemas/workflowSchema.ts | sed -n '490,520p'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1165


Consider validating parsed localStorage workflow data with Zod schema.

The as ComfyWorkflowJSON cast on line 99 accepts any parsed JSON without validation. While loadGraphData includes optional schema validation (if Comfy.Validation.Workflows is enabled), corrupted or invalid data could slip through if validation is disabled. Following the codebase pattern, use validateComfyWorkflow() instead:

try {
  const validated = await validateComfyWorkflow(JSON.parse(localWorkflowJson))
  if (!validated) {
    console.warn('[WorkflowPersistence] Invalid localStorage workflow')
    return false
  }
  return await loadWorkflowFromData(validated, workflowName)
} catch {
  console.warn('[WorkflowPersistence] Failed to parse localStorage workflow')
}

This validates data at the entry point and aligns with existing patterns in the codebase (e.g., src/platform/remote/comfyui/jobs/fetchJobs.ts).

🤖 Prompt for AI Agents
In `@src/platform/workflow/persistence/composables/useWorkflowPersistence.ts`
around lines 95 - 106, Replace the unchecked cast of parsed localStorage JSON
with Zod validation: parse localStorage.getItem('workflow'), pass the parsed
value into validateComfyWorkflow(), and if validation fails log
'[WorkflowPersistence] Invalid localStorage workflow' and return false; if
validation succeeds call loadWorkflowFromData(validated, workflowName). Update
the try/catch around JSON.parse to use validateComfyWorkflow() instead of "as
ComfyWorkflowJSON" and preserve the existing catch that logs parse failures.

vi.restoreAllMocks() in afterEach already handles cleanup
Prevents false positives from substring matches
@coderabbitai coderabbitai bot requested a review from Myestery January 16, 2026 20:19
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

🤖 Fix all issues with AI agents
In
`@src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts`:
- Around line 165-189: The test currently uses pattern /^workflow:/ which
doesn't match 'workspace.settings' so isProtectedKey is never exercised; update
the test to use a pattern that matches both keys (for example a regex matching
either workspace.* or a catch-all) so calling
setWithEviction('workflow:current', ...) will trigger eviction logic while still
allowing isProtectedKey to protect 'workspace.settings'; ensure you keep
references to sessionStorage.setItem spy and the existing keys
'workspace.settings' and 'workflow:old' and the function under test
setWithEviction so the protected-key behavior is actually asserted.

Comment on lines +165 to +189
it('does not evict protected keys', () => {
const workspaceEntry = { accessedAt: 0, data: { workspace: true } }
const workflowEntry = { accessedAt: 1000, data: { workflow: true } }

sessionStorage.setItem(
'workspace.settings',
JSON.stringify(workspaceEntry)
)
sessionStorage.setItem('workflow:old', JSON.stringify(workflowEntry))

let callCount = 0
const originalSetItem = sessionStorage.setItem.bind(sessionStorage)
vi.spyOn(sessionStorage, 'setItem').mockImplementation((key, value) => {
callCount++
if (key === 'workflow:current' && callCount === 1) {
throw new DOMException('Quota exceeded', 'QuotaExceededError')
}
return originalSetItem(key, value)
})

setWithEviction('workflow:current', { current: true }, /^workflow:/)

expect(sessionStorage.getItem('workspace.settings')).toBeTruthy()
expect(sessionStorage.getItem('workflow:old')).toBeNull()
})
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

Test doesn't exercise the isProtectedKey logic.

The pattern /^workflow:/ (line 185) doesn't match workspace.settings, so the key is excluded by pattern mismatch before isProtectedKey is ever checked. This test passes for the wrong reason.

To actually verify protected key behavior, use a pattern that matches both keys:

🔧 Suggested fix
  it('does not evict protected keys', () => {
    const workspaceEntry = { accessedAt: 0, data: { workspace: true } }
    const workflowEntry = { accessedAt: 1000, data: { workflow: true } }

    sessionStorage.setItem(
      'workspace.settings',
      JSON.stringify(workspaceEntry)
    )
    sessionStorage.setItem('workflow:old', JSON.stringify(workflowEntry))

    let callCount = 0
    const originalSetItem = sessionStorage.setItem.bind(sessionStorage)
    vi.spyOn(sessionStorage, 'setItem').mockImplementation((key, value) => {
      callCount++
      if (key === 'workflow:current' && callCount === 1) {
        throw new DOMException('Quota exceeded', 'QuotaExceededError')
      }
      return originalSetItem(key, value)
    })

-    setWithEviction('workflow:current', { current: true }, /^workflow:/)
+    setWithEviction('workflow:current', { current: true }, /^work/)

    expect(sessionStorage.getItem('workspace.settings')).toBeTruthy()
    expect(sessionStorage.getItem('workflow:old')).toBeNull()
  })
📝 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
it('does not evict protected keys', () => {
const workspaceEntry = { accessedAt: 0, data: { workspace: true } }
const workflowEntry = { accessedAt: 1000, data: { workflow: true } }
sessionStorage.setItem(
'workspace.settings',
JSON.stringify(workspaceEntry)
)
sessionStorage.setItem('workflow:old', JSON.stringify(workflowEntry))
let callCount = 0
const originalSetItem = sessionStorage.setItem.bind(sessionStorage)
vi.spyOn(sessionStorage, 'setItem').mockImplementation((key, value) => {
callCount++
if (key === 'workflow:current' && callCount === 1) {
throw new DOMException('Quota exceeded', 'QuotaExceededError')
}
return originalSetItem(key, value)
})
setWithEviction('workflow:current', { current: true }, /^workflow:/)
expect(sessionStorage.getItem('workspace.settings')).toBeTruthy()
expect(sessionStorage.getItem('workflow:old')).toBeNull()
})
it('does not evict protected keys', () => {
const workspaceEntry = { accessedAt: 0, data: { workspace: true } }
const workflowEntry = { accessedAt: 1000, data: { workflow: true } }
sessionStorage.setItem(
'workspace.settings',
JSON.stringify(workspaceEntry)
)
sessionStorage.setItem('workflow:old', JSON.stringify(workflowEntry))
let callCount = 0
const originalSetItem = sessionStorage.setItem.bind(sessionStorage)
vi.spyOn(sessionStorage, 'setItem').mockImplementation((key, value) => {
callCount++
if (key === 'workflow:current' && callCount === 1) {
throw new DOMException('Quota exceeded', 'QuotaExceededError')
}
return originalSetItem(key, value)
})
setWithEviction('workflow:current', { current: true }, /^work/)
expect(sessionStorage.getItem('workspace.settings')).toBeTruthy()
expect(sessionStorage.getItem('workflow:old')).toBeNull()
})
🤖 Prompt for AI Agents
In
`@src/platform/workflow/persistence/services/workflowSessionStorageService.test.ts`
around lines 165 - 189, The test currently uses pattern /^workflow:/ which
doesn't match 'workspace.settings' so isProtectedKey is never exercised; update
the test to use a pattern that matches both keys (for example a regex matching
either workspace.* or a catch-all) so calling
setWithEviction('workflow:current', ...) will trigger eviction logic while still
allowing isProtectedKey to protect 'workspace.settings'; ensure you keep
references to sessionStorage.setItem spy and the existing keys
'workspace.settings' and 'workflow:old' and the function under test
setWithEviction so the protected-key behavior is actually asserted.

christian-byrne and others added 2 commits January 20, 2026 14:03
mockRestore() calls are needed for these specific mocks
Main introduced workflowDraftStore with built-in QuotaExceededError
handling, making the session storage LRU service redundant.

Amp-Thread-ID: https://ampcode.com/threads/T-019bc597-fbea-778a-ae15-92061b3d0812
Co-authored-by: Amp <amp@ampcode.com>
@dosubot dosubot bot added size:XS This PR changes 0-9 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Jan 28, 2026
…aExceededError

Instead of storing full workflow JSON in sessionStorage (which can hit
quota on mobile), store only a small pointer to the draft path. The
actual workflow data lives in the draft store which has LRU eviction.

Loading priority:
1. SessionStorage pointer -> draft store
2. Preferred path -> draft store
3. Most recent draft (fallback)
4. Legacy sessionStorage payload (backward compat, remove after 2026-07-15)
5. Legacy localStorage payload (backward compat, remove after 2026-07-15)

This maintains duplicate-tab support while eliminating the quota issue.

Amp-Thread-ID: https://ampcode.com/threads/T-019bc597-fbea-778a-ae15-92061b3d0812
Co-authored-by: Amp <amp@ampcode.com>
@dosubot dosubot bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels Jan 28, 2026
@christian-byrne
Copy link
Contributor Author

Replaced by #8520

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:workflows size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant