feat(persistence): add workspace-scoped storage keys and types#8517
Conversation
📝 WalkthroughWalkthroughA new V2 draft persistence system is introduced with type definitions for draft metadata and storage structures, FNV-1a hash utilities for path-based key generation, and workspace-scoped storage key management functions. Includes comprehensive test coverage for all new utilities. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 02/18/2026, 11:26:37 PM UTC 🔗 Links🎉 Your Storybook is ready for review! |
|
Playwright: ✅ 524 passed, 0 failed · 2 flaky 📊 Browser Reports
|
Bundle Size ReportSummary
Category Glance Per-category breakdownApp Entry Points — 21.7 kB (baseline 21.7 kB) • ⚪ 0 BMain entry bundles and manifests
Graph Workspace — 905 kB (baseline 905 kB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Views & Navigation — 69 kB (baseline 69 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Panels & Settings — 427 kB (baseline 427 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
User & Accounts — 16.1 kB (baseline 16.1 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Editors & Dialogs — 785 B (baseline 785 B) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
UI Components — 36.6 kB (baseline 36.6 kB) • ⚪ 0 BReusable component library chunks
Data & Services — 2.16 MB (baseline 2.16 MB) • ⚪ 0 BStores, services, APIs, and repositories
Utilities & Hooks — 237 kB (baseline 237 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Vendor & Third-Party — 8.69 MB (baseline 8.69 MB) • ⚪ 0 BExternal libraries and shared vendor chunks
Other — 7.42 MB (baseline 7.42 MB) • ⚪ 0 BBundles that do not match a named category
|
86cf743 to
8212e12
Compare
8212e12 to
30b3c47
Compare
30b3c47 to
186a3b9
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/platform/workflow/persistence/base/draftTypes.ts`:
- Around line 81-85: Remove the unused exported constants MAX_DRAFTS and
PERSIST_DEBOUNCE_MS from draftTypes.ts: delete their declarations and any export
so they are no longer part of the module surface; keep using the canonical
MAX_DRAFTS from draftCache (which other modules already import) and verify no
code references PERSIST_DEBOUNCE_MS (if any exist, replace with the appropriate
debounce constant or inline value), ensuring only the single authoritative
MAX_DRAFTS symbol (from draftCache) remains exported in the codebase.
In `@src/platform/workflow/persistence/base/storageKeys.ts`:
- Around line 9-22: The getCurrentWorkspaceId function reads sessionStorage and
parses JSON without validating the parsed shape; update getCurrentWorkspaceId to
validate that the parsed workspace is a non-null object and that workspace.id is
a non-empty string and workspace.type is a string that can be compared to
'personal' (i.e., use explicit type guards instead of truthy checks), return
'personal' if validation fails, and avoid relying on any/implicit types so the
result conforms to WorkspaceWithRoleSchema expectations.
| /** Maximum number of drafts to keep per workspace */ | ||
| export const MAX_DRAFTS = 32 | ||
|
|
||
| /** Debounce delay for persisting graph changes (ms) */ | ||
| export const PERSIST_DEBOUNCE_MS = 512 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -n 'MAX_DRAFTS|PERSIST_DEBOUNCE_MS' srcRepository: Comfy-Org/ComfyUI_frontend
Length of output: 1209
🏁 Script executed:
head -20 src/platform/workflow/persistence/stores/workflowDraftStore.tsRepository: Comfy-Org/ComfyUI_frontend
Length of output: 600
🏁 Script executed:
head -10 src/platform/workflow/persistence/base/draftCache.test.tsRepository: Comfy-Org/ComfyUI_frontend
Length of output: 243
Remove these unused exported constants from draftTypes.ts.
PERSIST_DEBOUNCE_MS is never referenced anywhere in the codebase. MAX_DRAFTS is a duplicate—the active definition is in draftCache.ts and is imported from there. Neither should be exported from draftTypes.ts.
🔧 Change required
-/** Maximum number of drafts to keep per workspace */
-export const MAX_DRAFTS = 32
-
-/** Debounce delay for persisting graph changes (ms) */
-export const PERSIST_DEBOUNCE_MS = 512📝 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.
| /** Maximum number of drafts to keep per workspace */ | |
| export const MAX_DRAFTS = 32 | |
| /** Debounce delay for persisting graph changes (ms) */ | |
| export const PERSIST_DEBOUNCE_MS = 512 |
🤖 Prompt for AI Agents
In `@src/platform/workflow/persistence/base/draftTypes.ts` around lines 81 - 85,
Remove the unused exported constants MAX_DRAFTS and PERSIST_DEBOUNCE_MS from
draftTypes.ts: delete their declarations and any export so they are no longer
part of the module surface; keep using the canonical MAX_DRAFTS from draftCache
(which other modules already import) and verify no code references
PERSIST_DEBOUNCE_MS (if any exist, replace with the appropriate debounce
constant or inline value), ensuring only the single authoritative MAX_DRAFTS
symbol (from draftCache) remains exported in the codebase.
| function getCurrentWorkspaceId(): string { | ||
| try { | ||
| const json = sessionStorage.getItem( | ||
| WORKSPACE_STORAGE_KEYS.CURRENT_WORKSPACE | ||
| ) | ||
| if (!json) return 'personal' | ||
|
|
||
| const workspace = JSON.parse(json) | ||
| if (workspace.type === 'personal' || !workspace.id) return 'personal' | ||
| return workspace.id | ||
| } catch { | ||
| return 'personal' | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -C3 'CURRENT_WORKSPACE' srcRepository: Comfy-Org/ComfyUI_frontend
Length of output: 12354
🏁 Script executed:
#!/bin/bash
# Find workspace type definition
rg -n 'interface.*[Ww]orkspace|type.*[Ww]orkspace' src --type ts --type tsx -A 5 | head -80
# Also check workspaceAuthStore to see what structure is being written
sed -n '85,95p' src/stores/workspaceAuthStore.tsRepository: Comfy-Org/ComfyUI_frontend
Length of output: 428
🏁 Script executed:
#!/bin/bash
# Find WorkspaceWithRole type definition
rg -n 'WorkspaceWithRole' src --type ts -A 10 | head -100Repository: Comfy-Org/ComfyUI_frontend
Length of output: 6859
Validate the workspace object shape before accessing id.
getCurrentWorkspaceId() reads from sessionStorage at module initialization time without validating the parsed value's structure. The check !workspace.id doesn't ensure id is a string—it could be any falsy/truthy value. Add explicit type guards to match the WorkspaceWithRoleSchema requirements (id must be a non-empty string).
🔧 Suggested fix
function getCurrentWorkspaceId(): string {
try {
const json = sessionStorage.getItem(
WORKSPACE_STORAGE_KEYS.CURRENT_WORKSPACE
)
if (!json) return 'personal'
- const workspace = JSON.parse(json)
- if (workspace.type === 'personal' || !workspace.id) return 'personal'
- return workspace.id
+ const raw: unknown = JSON.parse(json)
+ if (!raw || typeof raw !== 'object') return 'personal'
+ const workspace = raw as Record<string, unknown>
+ if (workspace.type === 'personal') return 'personal'
+ if (typeof workspace.id !== 'string' || workspace.id.length === 0)
+ return 'personal'
+ return workspace.id
} catch {
return 'personal'
}
}Aligns with "Validate trusted sources before processing" and "Never use any type; use proper TypeScript types."
🤖 Prompt for AI Agents
In `@src/platform/workflow/persistence/base/storageKeys.ts` around lines 9 - 22,
The getCurrentWorkspaceId function reads sessionStorage and parses JSON without
validating the parsed shape; update getCurrentWorkspaceId to validate that the
parsed workspace is a non-null object and that workspace.id is a non-empty
string and workspace.type is a string that can be compared to 'personal' (i.e.,
use explicit type guards instead of truthy checks), return 'personal' if
validation fails, and avoid relying on any/implicit types so the result conforms
to WorkspaceWithRoleSchema expectations.
Add foundational utilities for the V2 workflow persistence system: - hashUtil: FNV-1a hash function for creating 8-char hex keys from paths - storageKeys: Workspace-scoped key generators for localStorage/sessionStorage - draftTypes: TypeScript interfaces for drafts, index, and pointers Part of the V2 persistence rewrite to fix: - QuotaExceededError on mobile (sessionStorage now stores pointers only) - Draft leakage between workspaces (keys scoped by workspaceId) - No multi-tab support (pointers scoped by clientId) Amp-Thread-ID: https://ampcode.com/threads/T-019c16f4-05a2-779d-aa0e-a0e098308a95 Co-authored-by: Amp <amp@ampcode.com>
ba3ec21 to
64fd51d
Compare
There was a problem hiding this comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/platform/workflow/persistence/base/storageKeys.ts`:
- Around line 9-22: The getCurrentWorkspaceId function parses sessionStorage but
assumes workspace.id is a valid string; change it to validate the parsed object
shape before returning id: after JSON.parse, check that workspace is an object,
workspace.type is a string, and that workspace.id is typeof 'string' and not
empty/whitespace (e.g., workspace.id.trim().length > 0); only then return
workspace.id, otherwise return 'personal'. Keep the try/catch and continue to
treat missing/invalid data as 'personal'; reference
WORKSPACE_STORAGE_KEYS.CURRENT_WORKSPACE and function getCurrentWorkspaceId when
making the change.
## Summary Adds the foundational types and key generation utilities for workspace-scoped workflow draft persistence. This enables storing drafts per-workspace to prevent data leakage between different ComfyUI instances. [Screencast from 2026-02-08 18-17-45.webm](https://github.com/user-attachments/assets/f16226e9-c1db-469d-a0b7-aa6af725db53) ## Changes - **What**: Type definitions for draft storage (`DraftIndexV2`, `DraftPayloadV2`, session pointers) and key generation utilities with workspace/client scoping - **Why**: The current persistence system stores all drafts globally, causing cross-workspace data leakage when users work with multiple ComfyUI instances --------- Co-authored-by: Amp <amp@ampcode.com>
|
@christian-byrne Successfully backported to #8982 |
## Summary - Remove `src/platform/workflow/persistence/base/draftTypes.ts` which is not imported anywhere - Fixes `knip` reporting it as an unused file The file was added in #8517 but nothing consumes its exports yet. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8993-chore-remove-unused-draftTypes-ts-to-fix-knip-30d6d73d36508151a9e1e936864fd311) by [Unito](https://www.unito.io)
## Summary - Remove `src/platform/workflow/persistence/base/draftTypes.ts` which is not imported anywhere - Fixes `knip` reporting it as an unused file The file was added in #8517 but nothing consumes its exports yet. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8993-chore-remove-unused-draftTypes-ts-to-fix-knip-30d6d73d36508151a9e1e936864fd311) by [Unito](https://www.unito.io)
## Summary - Remove `src/platform/workflow/persistence/base/draftTypes.ts` which is not imported anywhere - Fixes `knip` reporting it as an unused file The file was added in #8517 but nothing consumes its exports yet. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8993-chore-remove-unused-draftTypes-ts-to-fix-knip-30d6d73d36508151a9e1e936864fd311) by [Unito](https://www.unito.io)

Summary
Adds the foundational types and key generation utilities for workspace-scoped workflow draft persistence. This enables storing drafts per-workspace to prevent data leakage between different ComfyUI instances.
Screencast.from.2026-02-08.18-17-45.webm
Changes
DraftIndexV2,DraftPayloadV2, session pointers) and key generation utilities with workspace/client scoping