[backport core/1.49] fix(billing): resume a pending top-up as a top-up, not a subscription - #14724
Conversation
…#14715) ## Description Billing status can return an in-flight billing operation (`pending_billing_op_id` + `action_url`) so a client that lost its local reference can resume the payment — a reload, a cleared browser, a different device. The recovery path in `useWorkspaceBilling` assumed that operation was always a subscription and passed a hardcoded `'subscription'`. That assumption is a latent bug regardless of what the server sends today: any non-subscription operation surfaced there takes the wrong branch throughout. - `isSettingUp` turns true, so `SubscriptionPanelContentWorkspace` replaces the whole panel with a "setting up your subscription" spinner — hiding the user's actual plan for as long as the operation is pending - `topupActionOperation` requires `type === 'topup'`, so the top-up dialog's own recovery prompt never matches - `handleSuccess` runs `reconcileSubscriptionSuccess()` instead of `fetchStatus()` + `fetchBalance()`, so credits just purchased never appear - the toast and telemetry describe a subscription checkout Uses the operation type when the API reports one, falling back to `'subscription'` when it does not — so this is inert against a server that never sends the field, and correct the moment one does. **Status:** the server side does not populate a top-up here yet. This is the client half, landing first because it is safe in isolation; without it, a server that starts reporting top-ups would drive the wrong UI. Treat it as removing a hardcoded assumption, not as shipping a user-visible fix on its own. ## Checklist - [x] Test added — `recovers a pending top-up as a top-up, not a subscription`, confirmed to fail without the fix - [x] `pnpm test:unit` (72 passed in the touched suite), `pnpm typecheck`, `pnpm lint`, `pnpm format` ## Follow-up (not in this PR) `billingOperationStore` gives a top-up 120s to discover an authentication URL (`TIMEOUT_MS`) while a subscription gets 5min (`SUBSCRIPTION_ACTION_DISCOVERY_TIMEOUT_MS`). Only bites when the payment page is slow to appear, so it is kept separate.
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1788 passed, 0 failed · 1 flaky📊 Browser Reports
📦 Bundle Size
⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-04T23:49:51.208Z",
"gitSha": "c6b28b8923ff5ae4e904cde8e70036c4537a50d0",
"branch": "backport-14715-to-core-1.49",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2114.682000000016,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.302,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 542.473,
"heapDeltaBytes": 5105228,
"heapUsedBytes": 73227912,
"domNodes": 18,
"jsHeapTotalBytes": 20303872,
"scriptDurationMs": 22.062,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 2054.726999999957,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.091,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 469.001,
"heapDeltaBytes": 5078848,
"heapUsedBytes": 73261616,
"domNodes": 18,
"jsHeapTotalBytes": 20041728,
"scriptDurationMs": 18.234,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1986.586999999986,
"styleRecalcs": 78,
"styleRecalcDurationMs": 43.52,
"layouts": 12,
"layoutDurationMs": 3.623,
"taskDurationMs": 941.538,
"heapDeltaBytes": -252556,
"heapUsedBytes": 67766380,
"domNodes": 63,
"jsHeapTotalBytes": 21352448,
"scriptDurationMs": 136.86599999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1811.286999999993,
"styleRecalcs": 73,
"styleRecalcDurationMs": 35.78000000000001,
"layouts": 12,
"layoutDurationMs": 3.164,
"taskDurationMs": 847.6570000000002,
"heapDeltaBytes": -581460,
"heapUsedBytes": 67494648,
"domNodes": 57,
"jsHeapTotalBytes": 21352448,
"scriptDurationMs": 125.21200000000002,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1743.0850000000078,
"styleRecalcs": 30,
"styleRecalcDurationMs": 17.598,
"layouts": 6,
"layoutDurationMs": 0.7770000000000001,
"taskDurationMs": 393.475,
"heapDeltaBytes": 7864896,
"heapUsedBytes": 75933844,
"domNodes": 76,
"jsHeapTotalBytes": 20303872,
"scriptDurationMs": 19.499,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1739.5660000000817,
"styleRecalcs": 31,
"styleRecalcDurationMs": 17.86,
"layouts": 6,
"layoutDurationMs": 0.624,
"taskDurationMs": 377.8419999999999,
"heapDeltaBytes": 7973412,
"heapUsedBytes": 76391496,
"domNodes": 77,
"jsHeapTotalBytes": 20041728,
"scriptDurationMs": 19.394000000000002,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 593.3440000000019,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.261000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 400.068,
"heapDeltaBytes": -12273836,
"heapUsedBytes": 55937432,
"domNodes": 18,
"jsHeapTotalBytes": 21876736,
"scriptDurationMs": 61.483000000000004,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 577.6479999999538,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.76,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 380.42600000000004,
"heapDeltaBytes": -12227028,
"heapUsedBytes": 55909328,
"domNodes": 18,
"jsHeapTotalBytes": 21614592,
"scriptDurationMs": 60.503,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2033.2149999999842,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.549999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 685.7600000000001,
"heapDeltaBytes": 8124040,
"heapUsedBytes": 67713900,
"domNodes": -283,
"jsHeapTotalBytes": 5300224,
"scriptDurationMs": 108.88499999999999,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2026.5249999999924,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.055,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 695.855,
"heapDeltaBytes": 7705524,
"heapUsedBytes": 66691776,
"domNodes": -282,
"jsHeapTotalBytes": 4513792,
"scriptDurationMs": 109.926,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2208.712000000048,
"styleRecalcs": 68,
"styleRecalcDurationMs": 12.956,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1287.829,
"heapDeltaBytes": 5569020,
"heapUsedBytes": 65720584,
"domNodes": -286,
"jsHeapTotalBytes": 4739072,
"scriptDurationMs": 431.566,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2190.59900000002,
"styleRecalcs": 68,
"styleRecalcDurationMs": 13.724999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1285.714,
"heapDeltaBytes": -4176452,
"heapUsedBytes": 56158776,
"domNodes": -287,
"jsHeapTotalBytes": 4775936,
"scriptDurationMs": 426.182,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3182.512000000031,
"styleRecalcs": 65,
"styleRecalcDurationMs": 14.879,
"layouts": 60,
"layoutDurationMs": 7.1979999999999995,
"taskDurationMs": 1482.0919999999999,
"heapDeltaBytes": 23898540,
"heapUsedBytes": 86081152,
"domNodes": 12,
"jsHeapTotalBytes": 8126464,
"scriptDurationMs": 523.845,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3231.665000000021,
"styleRecalcs": 64,
"styleRecalcDurationMs": 14.211000000000002,
"layouts": 60,
"layoutDurationMs": 7.213000000000001,
"taskDurationMs": 1510.8300000000002,
"heapDeltaBytes": 23376560,
"heapUsedBytes": 85297684,
"domNodes": 10,
"jsHeapTotalBytes": 8650752,
"scriptDurationMs": 548.985,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2030.5000000000177,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.397000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 677.1850000000001,
"heapDeltaBytes": 8187608,
"heapUsedBytes": 69644332,
"domNodes": -284,
"jsHeapTotalBytes": 4251648,
"scriptDurationMs": 108.198,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2068.538999999987,
"styleRecalcs": 9,
"styleRecalcDurationMs": 10.638000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 696.202,
"heapDeltaBytes": 5978252,
"heapUsedBytes": 66885324,
"domNodes": -282,
"jsHeapTotalBytes": 4775936,
"scriptDurationMs": 109.671,
"eventListeners": -177,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 601.6700000000128,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.543999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 404.713,
"heapDeltaBytes": -12177200,
"heapUsedBytes": 56221568,
"domNodes": 20,
"jsHeapTotalBytes": 22138880,
"scriptDurationMs": 119.557,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 583.4150000000591,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.447,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 396.004,
"heapDeltaBytes": -11969520,
"heapUsedBytes": 56368044,
"domNodes": 20,
"jsHeapTotalBytes": 22401024,
"scriptDurationMs": 114.823,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2031.517000000008,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.311000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 488.31499999999994,
"heapDeltaBytes": -22065876,
"heapUsedBytes": 46173256,
"domNodes": 20,
"jsHeapTotalBytes": 19906560,
"scriptDurationMs": 14.279999999999998,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2016.7599999999766,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.378000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 449.74399999999997,
"heapDeltaBytes": 5046852,
"heapUsedBytes": 73178732,
"domNodes": 18,
"jsHeapTotalBytes": 20303872,
"scriptDurationMs": 14.158999999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1697.0509999999877,
"styleRecalcs": 76,
"styleRecalcDurationMs": 38.481,
"layouts": 16,
"layoutDurationMs": 5.009,
"taskDurationMs": 781.6070000000001,
"heapDeltaBytes": -4538520,
"heapUsedBytes": 63938120,
"domNodes": 62,
"jsHeapTotalBytes": 20566016,
"scriptDurationMs": 92.215,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1682.9020000000128,
"styleRecalcs": 76,
"styleRecalcDurationMs": 36.862,
"layouts": 16,
"layoutDurationMs": 3.9510000000000005,
"taskDurationMs": 802.044,
"heapDeltaBytes": -4141128,
"heapUsedBytes": 64291472,
"domNodes": 64,
"jsHeapTotalBytes": 20566016,
"scriptDurationMs": 91.76200000000001,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 1430.3750000000264,
"styleRecalcs": 17,
"styleRecalcDurationMs": 30.148000000000007,
"layouts": 13,
"layoutDurationMs": 12.277,
"taskDurationMs": 961.8240000000002,
"heapDeltaBytes": 31094864,
"heapUsedBytes": 99687476,
"domNodes": 13673,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 37.04099999999999,
"eventListeners": 2375,
"totalBlockingTimeMs": 135,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.80000000000109
},
{
"name": "viewport-pan-sweep",
"durationMs": 8354.217000000006,
"styleRecalcs": 249,
"styleRecalcDurationMs": 35.62,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4565.599,
"heapDeltaBytes": 9881156,
"heapUsedBytes": 69197996,
"domNodes": -284,
"jsHeapTotalBytes": 6541312,
"scriptDurationMs": 1413.25,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8493.360999999935,
"styleRecalcs": 250,
"styleRecalcDurationMs": 39.337,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4777.892,
"heapDeltaBytes": 15308968,
"heapUsedBytes": 74347252,
"domNodes": -283,
"jsHeapTotalBytes": 7065600,
"scriptDurationMs": 1447.0400000000002,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 17257.46700000002,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17212.867,
"heapDeltaBytes": -43991116,
"heapUsedBytes": 167075668,
"domNodes": -8312,
"jsHeapTotalBytes": -9379840,
"scriptDurationMs": 604.192,
"eventListeners": -16389,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666763,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 17314.28500000004,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17271.802,
"heapDeltaBytes": -58648056,
"heapUsedBytes": 167641572,
"domNodes": -8315,
"jsHeapTotalBytes": -8855552,
"scriptDurationMs": 613.6129999999999,
"eventListeners": -16389,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666763,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 20263.374,
"styleRecalcs": 138,
"styleRecalcDurationMs": 16.41900000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20216.313,
"heapDeltaBytes": -30664696,
"heapUsedBytes": 178173260,
"domNodes": -8312,
"jsHeapTotalBytes": -11022336,
"scriptDurationMs": 887.205,
"eventListeners": -16381,
"totalBlockingTimeMs": 169,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 20868.077000000085,
"styleRecalcs": 141,
"styleRecalcDurationMs": 16.569999999999972,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20801.392,
"heapDeltaBytes": -55575440,
"heapUsedBytes": 154249892,
"domNodes": -8312,
"jsHeapTotalBytes": -10690560,
"scriptDurationMs": 887.3350000000002,
"eventListeners": -16383,
"totalBlockingTimeMs": 164,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 481.1700000000201,
"styleRecalcs": 15,
"styleRecalcDurationMs": 19.355999999999998,
"layouts": 2,
"layoutDurationMs": 0.312,
"taskDurationMs": 131.537,
"heapDeltaBytes": -15591728,
"heapUsedBytes": 51670340,
"domNodes": 136,
"jsHeapTotalBytes": 7720960,
"scriptDurationMs": 10.072999999999999,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "workflow-execution",
"durationMs": 450.26800000005096,
"styleRecalcs": 14,
"styleRecalcDurationMs": 19.403999999999996,
"layouts": 3,
"layoutDurationMs": 0.611,
"taskDurationMs": 120.74399999999999,
"heapDeltaBytes": -15763152,
"heapUsedBytes": 51588356,
"domNodes": 123,
"jsHeapTotalBytes": 7720960,
"scriptDurationMs": 9.001,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
}
]
} |
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## core/1.49 #14724 +/- ##
===========================================
Coverage 78.60% 78.60%
===========================================
Files 1789 1789
Lines 104423 104424 +1
Branches 32882 32025 -857
===========================================
+ Hits 82078 82079 +1
Misses 21915 21915
Partials 430 430
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Backport of #14715 to
core/1.49Automatically created by backport workflow.