[backport cloud/1.49] fix: bound auth wait in startStoreBootstrap + catch-all for unknown routes (IR-105) - #15179
Conversation
…outes (IR-105) (#15063) ## Summary Two durable fixes for the IR-105 splash-screen hang incident. ### Fix 1 — Bootstrap auth wait timeout (`bootstrapStore.ts`) **Root cause:** `until(isInitialized).toBe(true)` and `until(isAuthenticated).toBe(true)` in `startStoreBootstrap` have no timeout. A stale Firebase session or broken auth response hangs bootstrap silently and indefinitely — no error, no log, just a forever splash screen. **Fix:** Extract `waitForCloudAuth()` with: - 16 s timeout (matching `router.ts:176` and `WorkspaceAuthGate.vue:78` — existing convention) - One retry after 3 s backoff - On second timeout: `captureException` to Sentry + **continue bootstrap** rather than blocking This means the worst case is now 35 s to a usable (logged-out) state instead of infinity. ### Fix 2 — Catch-all route for unknown paths (`router.ts`) **Root cause:** A request to e.g. `cloud.comfy.org/woiadawd` gets a 200 + app shell from the static host, Vue boots, finds **no matching route**, and the global auth guard's `until(isInitialized)` then hangs (see Fix 1). Even with Fix 1, a no-match route leaves the app on the splash screen. **Fix:** Add `{ path: '/:pathMatch(.*)*', redirect: '/' }` at the end of the route list. Unknown paths redirect to root; the global auth guard then sends unauthenticated users to `/cloud/login` as normal. ## Red-Green Verification | Commit | Purpose | |--------|---------| | `test: add failing tests for unbounded auth wait in startStoreBootstrap` | 🔴 Proves test catches the bug — the "gives up" test times out in 5 s against the unpatched code | | `fix: bound auth wait in startStoreBootstrap and add catch-all route` | 🟢 Both new tests pass, all 18 tests in the two affected files pass | ## Relation to open PRs - Supersedes / closes the scope of #15032 (`fix/bootstrap-auth-wait-timeout`) — same fix, same tests. If reviewers prefer to land #15032 first this PR can drop Fix 1. - Companion to #15022 (marketing-site redirect) and #15026 (`/login` → `cloud-login` route, already on main). ## Test Plan - [x] `pnpm vitest run src/stores/bootstrapStore.test.ts` — 7/7 pass (including 2 new timeout tests) - [x] `pnpm vitest run src/platform/cloud/onboarding/onboardingCloudRoutes.test.ts` — 11/11 pass - [x] `pnpm typecheck` — clean - [x] `pnpm exec eslint src/stores/bootstrapStore.ts src/router.ts` — clean - [ ] CI green (Playwright, Storybook, Codecov) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: GitHub Action <action@github.com>
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1794 passed, 0 failed📊 Browser Reports
📦 Bundle Size
⚡ Performance Report
Absolute values
Raw data{
"timestamp": "2026-08-13T05:03:34.625Z",
"gitSha": "a2312d7ba8f73c14a726ea475b9ece0ea03deca2",
"branch": "backport-15063-to-cloud-1.49",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2071.5430000000197,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.269,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 493.156,
"heapDeltaBytes": 5532336,
"heapUsedBytes": 74116564,
"domNodes": 20,
"jsHeapTotalBytes": 20037632,
"scriptDurationMs": 20.173999999999996,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-idle",
"durationMs": 2033.551999999986,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.337000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 486.584,
"heapDeltaBytes": 5266184,
"heapUsedBytes": 74025300,
"domNodes": 16,
"jsHeapTotalBytes": 20299776,
"scriptDurationMs": 20.120999999999995,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2065.1169999999865,
"styleRecalcs": 79,
"styleRecalcDurationMs": 46.855999999999995,
"layouts": 12,
"layoutDurationMs": 4.103,
"taskDurationMs": 1010.5470000000001,
"heapDeltaBytes": 175652,
"heapUsedBytes": 69125800,
"domNodes": 62,
"jsHeapTotalBytes": 21348352,
"scriptDurationMs": 136.048,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1874.4930000000295,
"styleRecalcs": 77,
"styleRecalcDurationMs": 40.408,
"layouts": 12,
"layoutDurationMs": 3.654,
"taskDurationMs": 902.205,
"heapDeltaBytes": -20567992,
"heapUsedBytes": 48332976,
"domNodes": -280,
"jsHeapTotalBytes": 20164608,
"scriptDurationMs": 126.44800000000001,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1748.8109999999892,
"styleRecalcs": 30,
"styleRecalcDurationMs": 18.545,
"layouts": 6,
"layoutDurationMs": 0.549,
"taskDurationMs": 430.628,
"heapDeltaBytes": 8330752,
"heapUsedBytes": 77102272,
"domNodes": 77,
"jsHeapTotalBytes": 20037632,
"scriptDurationMs": 26.151999999999997,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1741.7549999998982,
"styleRecalcs": 31,
"styleRecalcDurationMs": 18.038999999999998,
"layouts": 6,
"layoutDurationMs": 0.7599999999999998,
"taskDurationMs": 396.972,
"heapDeltaBytes": 8388968,
"heapUsedBytes": 77246248,
"domNodes": 77,
"jsHeapTotalBytes": 20299776,
"scriptDurationMs": 21.909,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 694.1630000000032,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.8039999999999985,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 431.673,
"heapDeltaBytes": -12310520,
"heapUsedBytes": 56475988,
"domNodes": 18,
"jsHeapTotalBytes": 21872640,
"scriptDurationMs": 68.64099999999999,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "dom-widget-clipping",
"durationMs": 623.5960000000205,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.658,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 405.179,
"heapDeltaBytes": -11447524,
"heapUsedBytes": 57376876,
"domNodes": 18,
"jsHeapTotalBytes": 21086208,
"scriptDurationMs": 61.126,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-idle",
"durationMs": 2059.042999999974,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.7330000000000005,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 741.99,
"heapDeltaBytes": 5505784,
"heapUsedBytes": 65629016,
"domNodes": -284,
"jsHeapTotalBytes": 4513792,
"scriptDurationMs": 122.787,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2029.8440000000255,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.196000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 687.8410000000001,
"heapDeltaBytes": 8505756,
"heapUsedBytes": 68433616,
"domNodes": -284,
"jsHeapTotalBytes": 5038080,
"scriptDurationMs": 105.70299999999999,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2384.08400000003,
"styleRecalcs": 70,
"styleRecalcDurationMs": 15.66,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1422.071,
"heapDeltaBytes": 7877080,
"heapUsedBytes": 68979620,
"domNodes": -281,
"jsHeapTotalBytes": 4476928,
"scriptDurationMs": 472.54300000000006,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2172.209000000066,
"styleRecalcs": 68,
"styleRecalcDurationMs": 13.316,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1282.726,
"heapDeltaBytes": -4408788,
"heapUsedBytes": 56793596,
"domNodes": -287,
"jsHeapTotalBytes": 5300224,
"scriptDurationMs": 441.12999999999994,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3328.9970000000153,
"styleRecalcs": 65,
"styleRecalcDurationMs": 16.302,
"layouts": 60,
"layoutDurationMs": 7.537,
"taskDurationMs": 1649.3430000000003,
"heapDeltaBytes": 4215500,
"heapUsedBytes": 67273824,
"domNodes": -287,
"jsHeapTotalBytes": 6610944,
"scriptDurationMs": 574.6510000000001,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3228.566999999998,
"styleRecalcs": 65,
"styleRecalcDurationMs": 14.896000000000003,
"layouts": 60,
"layoutDurationMs": 7.323000000000001,
"taskDurationMs": 1552.7649999999999,
"heapDeltaBytes": -495844,
"heapUsedBytes": 62188588,
"domNodes": -288,
"jsHeapTotalBytes": 8708096,
"scriptDurationMs": 535.177,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2035.7290000000035,
"styleRecalcs": 8,
"styleRecalcDurationMs": 9.023,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 804.4520000000001,
"heapDeltaBytes": 7403120,
"heapUsedBytes": 69673604,
"domNodes": -283,
"jsHeapTotalBytes": 4775936,
"scriptDurationMs": 126.506,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "minimap-idle",
"durationMs": 2041.6779999999335,
"styleRecalcs": 7,
"styleRecalcDurationMs": 6.976,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 709.0520000000001,
"heapDeltaBytes": 9713016,
"heapUsedBytes": 71652124,
"domNodes": -284,
"jsHeapTotalBytes": 5562368,
"scriptDurationMs": 118.43299999999999,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 627.9570000000376,
"styleRecalcs": 46,
"styleRecalcDurationMs": 10.223999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 428.471,
"heapDeltaBytes": -11207688,
"heapUsedBytes": 57815996,
"domNodes": 18,
"jsHeapTotalBytes": 21348352,
"scriptDurationMs": 125.31200000000001,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 594.8130000000447,
"styleRecalcs": 47,
"styleRecalcDurationMs": 11.36,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 407.671,
"heapDeltaBytes": -11241724,
"heapUsedBytes": 57632200,
"domNodes": 20,
"jsHeapTotalBytes": 21872640,
"scriptDurationMs": 121.756,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2035.2159999999913,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.563,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 502.11899999999997,
"heapDeltaBytes": 5367192,
"heapUsedBytes": 74325092,
"domNodes": 20,
"jsHeapTotalBytes": 20824064,
"scriptDurationMs": 19.159000000000002,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 1992.3209999999472,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.369999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 469.81899999999996,
"heapDeltaBytes": 5452196,
"heapUsedBytes": 74354388,
"domNodes": 22,
"jsHeapTotalBytes": 20037632,
"scriptDurationMs": 15.207,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1740.883999999994,
"styleRecalcs": 77,
"styleRecalcDurationMs": 38.336,
"layouts": 16,
"layoutDurationMs": 4.774000000000001,
"taskDurationMs": 835.897,
"heapDeltaBytes": -3782364,
"heapUsedBytes": 65470756,
"domNodes": 63,
"jsHeapTotalBytes": 19513344,
"scriptDurationMs": 97.996,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1702.7560000000221,
"styleRecalcs": 75,
"styleRecalcDurationMs": 37.382,
"layouts": 16,
"layoutDurationMs": 4.669,
"taskDurationMs": 773.468,
"heapDeltaBytes": -3952196,
"heapUsedBytes": 65014328,
"domNodes": 60,
"jsHeapTotalBytes": 20299776,
"scriptDurationMs": 94.076,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 1431.8519999999921,
"styleRecalcs": 19,
"styleRecalcDurationMs": 31.167,
"layouts": 15,
"layoutDurationMs": 13.094999999999999,
"taskDurationMs": 998.7340000000003,
"heapDeltaBytes": 1477992,
"heapUsedBytes": 73576164,
"domNodes": 13673,
"jsHeapTotalBytes": 16252928,
"scriptDurationMs": 36.93700000000001,
"eventListeners": 2375,
"totalBlockingTimeMs": 143,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8740.384000000005,
"styleRecalcs": 249,
"styleRecalcDurationMs": 36.609,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4887.709,
"heapDeltaBytes": 8803868,
"heapUsedBytes": 68922484,
"domNodes": -282,
"jsHeapTotalBytes": 6836224,
"scriptDurationMs": 1486.19,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.80000000000109
},
{
"name": "viewport-pan-sweep",
"durationMs": 8400.237999999945,
"styleRecalcs": 249,
"styleRecalcDurationMs": 36.69700000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4723.559,
"heapDeltaBytes": 12302032,
"heapUsedBytes": 72023508,
"domNodes": -283,
"jsHeapTotalBytes": 6574080,
"scriptDurationMs": 1418.128,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 17477.517999999975,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17456.430999999997,
"heapDeltaBytes": -58928568,
"heapUsedBytes": 168474640,
"domNodes": -8312,
"jsHeapTotalBytes": -7806976,
"scriptDurationMs": 562.6850000000001,
"eventListeners": -16391,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 17329.031999999985,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17311.442999999996,
"heapDeltaBytes": -60006156,
"heapUsedBytes": 167751492,
"domNodes": -8312,
"jsHeapTotalBytes": -9641984,
"scriptDurationMs": 617.004,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 21071.02100000003,
"styleRecalcs": 147,
"styleRecalcDurationMs": 19.70900000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 21043.665,
"heapDeltaBytes": -38871676,
"heapUsedBytes": 184653116,
"domNodes": -8312,
"jsHeapTotalBytes": -11247616,
"scriptDurationMs": 879.2059999999999,
"eventListeners": -16387,
"totalBlockingTimeMs": 235,
"frameDurationMs": 18.333333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 20746.531000000003,
"styleRecalcs": 139,
"styleRecalcDurationMs": 16.38400000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20677.678,
"heapDeltaBytes": -32619808,
"heapUsedBytes": 177769820,
"domNodes": -8312,
"jsHeapTotalBytes": -10498048,
"scriptDurationMs": 884.358,
"eventListeners": -16383,
"totalBlockingTimeMs": 189,
"frameDurationMs": 17.776666666666763,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 450.16200000003437,
"styleRecalcs": 14,
"styleRecalcDurationMs": 18.992,
"layouts": 2,
"layoutDurationMs": 0.30499999999999994,
"taskDurationMs": 126.227,
"heapDeltaBytes": -16012640,
"heapUsedBytes": 52212852,
"domNodes": 134,
"jsHeapTotalBytes": 7716864,
"scriptDurationMs": 10.477,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 459.6129999999903,
"styleRecalcs": 14,
"styleRecalcDurationMs": 18.750000000000004,
"layouts": 3,
"layoutDurationMs": 0.5289999999999999,
"taskDurationMs": 118.82000000000001,
"heapDeltaBytes": -15998160,
"heapUsedBytes": 52016992,
"domNodes": 123,
"jsHeapTotalBytes": 6930432,
"scriptDurationMs": 9.469999999999999,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## cloud/1.49 #15179 +/- ##
=============================================
Coverage ? 71.92%
=============================================
Files ? 1767
Lines ? 92476
Branches ? 24894
=============================================
Hits ? 66514
Misses ? 25843
Partials ? 119
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Backport of #15063 to
cloud/1.49Automatically created by backport workflow.