Compare commits

...

7 Commits

Author SHA1 Message Date
bymyself
aec1a55667 fix: use pointer instead of payload in sessionStorage to prevent QuotaExceededError
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>
2026-01-27 21:02:08 -08:00
bymyself
3d884976c4 merge: resolve conflict with main - adopt draft store system
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>
2026-01-27 20:52:42 -08:00
bymyself
f44c06f16b fix: restore mock cleanup in tests
mockRestore() calls are needed for these specific mocks
2026-01-20 14:03:57 -08:00
bymyself
5b6cc8777e fix: use startsWith for prefix matching in protected keys
Prevents false positives from substring matches
2026-01-15 23:20:48 -08:00
bymyself
fdba5d270f test: remove redundant mock restoration calls
vi.restoreAllMocks() in afterEach already handles cleanup
2026-01-15 23:20:28 -08:00
bymyself
add7a4fbd4 refactor: simplify session storage service
- 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>
2026-01-15 23:04:51 -08:00
bymyself
78e2d702de fix: add LRU eviction for session storage to prevent QuotaExceededError
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>
2026-01-15 22:49:37 -08:00
2 changed files with 16 additions and 21 deletions

View File

@@ -81,27 +81,12 @@ export function useWorkflowPersistence() {
return
}
// Store pointer in sessionStorage for duplicate-tab support (small, won't hit quota)
// The actual workflow data is stored in the draft store which has eviction
try {
localStorage.setItem('workflow', workflowJson)
if (api.clientId) {
sessionStorage.setItem(`workflow:${api.clientId}`, workflowJson)
}
} catch (error) {
// Only log our own keys and aggregate stats
const ourKeys = Object.keys(sessionStorage).filter(
(key) => key.startsWith('workflow:') || key === 'workflow'
)
console.error('QuotaExceededError details:', {
workflowSizeKB: Math.round(workflowJson.length / 1024),
totalStorageItems: Object.keys(sessionStorage).length,
ourWorkflowKeys: ourKeys.length,
ourWorkflowSizes: ourKeys.map((key) => ({
key,
sizeKB: Math.round(sessionStorage[key].length / 1024)
})),
error: error instanceof Error ? error.message : String(error)
})
throw error
sessionStorage.setItem('Comfy.Workflow.ActivePath', workflowPath)
} catch {
// Ignore - pointer is best-effort
}
lastSavedJsonByPath.value[workflowPath] = workflowJson

View File

@@ -124,17 +124,26 @@ export const useWorkflowDraftStore = defineStore('workflowDraft', () => {
fallbackToLatestDraft = false
} = options
// 1. Try sessionStorage pointer (for duplicate-tab support)
const sessionPath = sessionStorage.getItem('Comfy.Workflow.ActivePath')
if (sessionPath && (await loadDraft(sessionPath))) {
return true
}
// 2. Try preferred path from caller
if (preferredPath && (await loadDraft(preferredPath))) {
return true
}
if (!preferredPath && fallbackToLatestDraft) {
// 3. Fall back to most recent draft
if (fallbackToLatestDraft) {
const fallbackPath = mostRecentDraft.value
if (fallbackPath && (await loadDraft(fallbackPath))) {
return true
}
}
// 4. Legacy fallback: sessionStorage payload (remove after 2026-07-15)
const clientId = api.initialClientId ?? api.clientId
if (clientId) {
const sessionPayload = sessionStorage.getItem(`workflow:${clientId}`)
@@ -143,6 +152,7 @@ export const useWorkflowDraftStore = defineStore('workflowDraft', () => {
}
}
// 5. Legacy fallback: localStorage payload (remove after 2026-07-15)
const localPayload = localStorage.getItem('workflow')
return await tryLoadGraph(localPayload, workflowName)
}