feat(navigation): add opt-in strip-on-capture to preserved-query tracker#13465
Conversation
🎭 Playwright: ✅ 1691 passed, 0 failed · 2 flaky📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle: 7.77 MB gzip 🔴 +394 BDetailsSummary
Category Glance App Entry Points — 47.8 kB (baseline 47.4 kB) • 🔴 +369 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.26 MB (baseline 1.26 MB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 98 kB (baseline 98 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 9 added / 9 removed / 3 unchanged Panels & Settings — 546 kB (baseline 546 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 11 added / 11 removed / 16 unchanged User & Accounts — 26.9 kB (baseline 26.9 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 7 added / 7 removed / 3 unchanged Editors & Dialogs — 117 kB (baseline 117 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 4 added / 4 removed / 1 unchanged UI Components — 57.5 kB (baseline 57.5 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed / 8 unchanged Data & Services — 270 kB (baseline 270 kB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 13 added / 13 removed / 3 unchanged Utilities & Hooks — 3.38 MB (baseline 3.37 MB) • 🔴 +1.04 kBHelpers, composables, and utility bundles
Status: 16 added / 16 removed / 17 unchanged Vendor & Third-Party — 15.3 MB (baseline 15.3 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 11.7 MB (baseline 11.7 MB) • ⚪ 0 BBundles that do not match a named category
Status: 67 added / 67 removed / 98 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-07-06T20:32:15.593Z",
"gitSha": "146666a2836869ad52cb267e848f9a6f0d4feba4",
"branch": "benceruleanlu/preserved-query-strip-on-capture",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2101.293999999996,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.133000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 480.73099999999994,
"heapDeltaBytes": -7213900,
"heapUsedBytes": 62158068,
"domNodes": -285,
"jsHeapTotalBytes": 19288064,
"scriptDurationMs": 14.205000000000002,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2032.8640000000178,
"styleRecalcs": 8,
"styleRecalcDurationMs": 6.298,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 377.65700000000004,
"heapDeltaBytes": 324136,
"heapUsedBytes": 51869224,
"domNodes": -293,
"jsHeapTotalBytes": 17428480,
"scriptDurationMs": 14.447,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1858.7689999999952,
"styleRecalcs": 76,
"styleRecalcDurationMs": 36.712,
"layouts": 12,
"layoutDurationMs": 3.2509999999999994,
"taskDurationMs": 785.954,
"heapDeltaBytes": -13059108,
"heapUsedBytes": 56415096,
"domNodes": -240,
"jsHeapTotalBytes": 19812352,
"scriptDurationMs": 120.17800000000001,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1809.1810000000237,
"styleRecalcs": 74,
"styleRecalcDurationMs": 35.346,
"layouts": 12,
"layoutDurationMs": 3.4749999999999996,
"taskDurationMs": 764.82,
"heapDeltaBytes": -13175124,
"heapUsedBytes": 56110500,
"domNodes": -242,
"jsHeapTotalBytes": 19550208,
"scriptDurationMs": 114,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1738.4570000000394,
"styleRecalcs": 33,
"styleRecalcDurationMs": 15.634000000000002,
"layouts": 6,
"layoutDurationMs": 0.5910000000000001,
"taskDurationMs": 286.90500000000003,
"heapDeltaBytes": 1952664,
"heapUsedBytes": 60737356,
"domNodes": 80,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 17.460000000000004,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1723.2890000000225,
"styleRecalcs": 31,
"styleRecalcDurationMs": 18.558,
"layouts": 6,
"layoutDurationMs": 0.48299999999999993,
"taskDurationMs": 335.858,
"heapDeltaBytes": -3963376,
"heapUsedBytes": 65251920,
"domNodes": -224,
"jsHeapTotalBytes": 11948032,
"scriptDurationMs": 21.386,
"eventListeners": -184,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 571.4930000000038,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.636000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 363.82000000000005,
"heapDeltaBytes": -3565484,
"heapUsedBytes": 48314160,
"domNodes": -275,
"jsHeapTotalBytes": 651264,
"scriptDurationMs": 54.986999999999995,
"eventListeners": -203,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "dom-widget-clipping",
"durationMs": 589.6799999999871,
"styleRecalcs": 12,
"styleRecalcDurationMs": 7.958,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 341.58900000000006,
"heapDeltaBytes": 7402920,
"heapUsedBytes": 66455660,
"domNodes": 20,
"jsHeapTotalBytes": 19136512,
"scriptDurationMs": 63.129000000000005,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2009.2970000000037,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.597999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 474.482,
"heapDeltaBytes": -12372560,
"heapUsedBytes": 60487260,
"domNodes": 20,
"jsHeapTotalBytes": 12726272,
"scriptDurationMs": 85.90500000000002,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2034.9199999999996,
"styleRecalcs": 10,
"styleRecalcDurationMs": 11.773,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 515.803,
"heapDeltaBytes": -1445976,
"heapUsedBytes": 58369888,
"domNodes": -270,
"jsHeapTotalBytes": -1089536,
"scriptDurationMs": 92.53900000000002,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2113.7279999999805,
"styleRecalcs": 69,
"styleRecalcDurationMs": 16.859,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1091.6080000000002,
"heapDeltaBytes": 12322164,
"heapUsedBytes": 80468444,
"domNodes": -274,
"jsHeapTotalBytes": -65536,
"scriptDurationMs": 423.636,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2107.0930000000203,
"styleRecalcs": 69,
"styleRecalcDurationMs": 16.666000000000004,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1077.723,
"heapDeltaBytes": 12746292,
"heapUsedBytes": 80998404,
"domNodes": -274,
"jsHeapTotalBytes": -327680,
"scriptDurationMs": 436.529,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3064.194000000043,
"styleRecalcs": 63,
"styleRecalcDurationMs": 14.973,
"layouts": 60,
"layoutDurationMs": 6.852,
"taskDurationMs": 1185.659,
"heapDeltaBytes": 12471648,
"heapUsedBytes": 66281344,
"domNodes": -280,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 456.612,
"eventListeners": -195,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3060.609999999997,
"styleRecalcs": 66,
"styleRecalcDurationMs": 18.929,
"layouts": 60,
"layoutDurationMs": 7.375999999999999,
"taskDurationMs": 1210.127,
"heapDeltaBytes": 14540852,
"heapUsedBytes": 80763128,
"domNodes": 16,
"jsHeapTotalBytes": 8183808,
"scriptDurationMs": 461.62499999999994,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2010.89300000001,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.8050000000000015,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 463.82199999999995,
"heapDeltaBytes": 7132732,
"heapUsedBytes": 60483504,
"domNodes": -261,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 85.06,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2031.8809999999985,
"styleRecalcs": 7,
"styleRecalcDurationMs": 5.951000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 493.488,
"heapDeltaBytes": -2248120,
"heapUsedBytes": 59475000,
"domNodes": -275,
"jsHeapTotalBytes": -1351680,
"scriptDurationMs": 90.468,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 577.8689999999642,
"styleRecalcs": 47,
"styleRecalcDurationMs": 12.298000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 397.86499999999995,
"heapDeltaBytes": -23213908,
"heapUsedBytes": 46360540,
"domNodes": -277,
"jsHeapTotalBytes": 5918720,
"scriptDurationMs": 118.48800000000001,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 595.623000000046,
"styleRecalcs": 46,
"styleRecalcDurationMs": 10.953999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 403.561,
"heapDeltaBytes": -22256572,
"heapUsedBytes": 47094168,
"domNodes": -279,
"jsHeapTotalBytes": 5918720,
"scriptDurationMs": 116.16000000000001,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "subgraph-idle",
"durationMs": 2029.0399999999806,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.183,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 338.865,
"heapDeltaBytes": 2495516,
"heapUsedBytes": 68299392,
"domNodes": 20,
"jsHeapTotalBytes": 20209664,
"scriptDurationMs": 13.734,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2024.7529999999756,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.782,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 390.843,
"heapDeltaBytes": -8275480,
"heapUsedBytes": 61131024,
"domNodes": -277,
"jsHeapTotalBytes": 20336640,
"scriptDurationMs": 12.985000000000001,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1761.3660000000095,
"styleRecalcs": 77,
"styleRecalcDurationMs": 35.711999999999996,
"layouts": 16,
"layoutDurationMs": 3.835,
"taskDurationMs": 656.5260000000001,
"heapDeltaBytes": 3680036,
"heapUsedBytes": 55872944,
"domNodes": -221,
"jsHeapTotalBytes": 16117760,
"scriptDurationMs": 84.53999999999999,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1742.0629999999733,
"styleRecalcs": 76,
"styleRecalcDurationMs": 34.842999999999996,
"layouts": 16,
"layoutDurationMs": 4.189,
"taskDurationMs": 671.1270000000001,
"heapDeltaBytes": -5694320,
"heapUsedBytes": 46417588,
"domNodes": -258,
"jsHeapTotalBytes": 16642048,
"scriptDurationMs": 85.08099999999999,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 965.754000000004,
"styleRecalcs": 18,
"styleRecalcDurationMs": 31.242000000000004,
"layouts": 14,
"layoutDurationMs": 16.072999999999997,
"taskDurationMs": 755.2350000000001,
"heapDeltaBytes": 28003000,
"heapUsedBytes": 97987196,
"domNodes": 13673,
"jsHeapTotalBytes": 16252928,
"scriptDurationMs": 35.007999999999996,
"eventListeners": 2533,
"totalBlockingTimeMs": 155,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8145.603999999992,
"styleRecalcs": 250,
"styleRecalcDurationMs": 51.634,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3471.624,
"heapDeltaBytes": 5826100,
"heapUsedBytes": 65399096,
"domNodes": -274,
"jsHeapTotalBytes": 950272,
"scriptDurationMs": 1186.482,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8165.245000000027,
"styleRecalcs": 251,
"styleRecalcDurationMs": 54.51299999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3496.109,
"heapDeltaBytes": 8542456,
"heapUsedBytes": 68019556,
"domNodes": -272,
"jsHeapTotalBytes": 688128,
"scriptDurationMs": 1219.932,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 11161.066000000006,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 11142.909,
"heapDeltaBytes": -28019916,
"heapUsedBytes": 165936140,
"domNodes": -3300,
"jsHeapTotalBytes": -3436544,
"scriptDurationMs": 491.974,
"eventListeners": -16378,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333338,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 10813.331999999946,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 10796.15,
"heapDeltaBytes": -42250160,
"heapUsedBytes": 166200548,
"domNodes": -3300,
"jsHeapTotalBytes": -4485120,
"scriptDurationMs": 482.926,
"eventListeners": -16382,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 14937.207,
"styleRecalcs": 66,
"styleRecalcDurationMs": 15.907999999999978,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14904.728,
"heapDeltaBytes": -47577820,
"heapUsedBytes": 166329132,
"domNodes": -3300,
"jsHeapTotalBytes": 4427776,
"scriptDurationMs": 760.674,
"eventListeners": -16372,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.219999999999953,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 14752.390999999989,
"styleRecalcs": 67,
"styleRecalcDurationMs": 16.099000000000004,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14729.394999999999,
"heapDeltaBytes": -48914692,
"heapUsedBytes": 176430192,
"domNodes": -3300,
"jsHeapTotalBytes": -3522560,
"scriptDurationMs": 786.856,
"eventListeners": -16372,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 475.85400000002664,
"styleRecalcs": 17,
"styleRecalcDurationMs": 27.655,
"layouts": 4,
"layoutDurationMs": 1.197,
"taskDurationMs": 127.99300000000002,
"heapDeltaBytes": -14835600,
"heapUsedBytes": 51075036,
"domNodes": 168,
"jsHeapTotalBytes": 7888896,
"scriptDurationMs": 16.723000000000003,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666682,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "workflow-execution",
"durationMs": 472.3950000000059,
"styleRecalcs": 16,
"styleRecalcDurationMs": 21.029999999999998,
"layouts": 4,
"layoutDurationMs": 0.9710000000000001,
"taskDurationMs": 209.55800000000002,
"heapDeltaBytes": -22202308,
"heapUsedBytes": 48455796,
"domNodes": -178,
"jsHeapTotalBytes": 5394432,
"scriptDurationMs": 16.33,
"eventListeners": -134,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughModifies preserved query capture to handle array-valued query parameters and incremental namespace updates. Adds optional ChangesPreserved query updates
Estimated code review effort: 3 (Moderate) | ~25 minutes Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (1 error, 1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d8f63d7783
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/platform/navigation/preservedQueryTracker.test.ts`:
- Around line 137-149: Add a companion history test in
preservedQueryTracker.test that covers router.replace with a strip-marked query
key, since the current check only exercises router.push. Use createTestRouter
and installPreservedQueryTracker like the existing test, then call
router.replace with the preserved-query URL and assert the redirect leaves the
history stack unchanged with only one entry and no extra back navigation target.
This should validate the replace behavior alongside the existing push-based test
in preservedQueryTracker.
In `@src/platform/navigation/preservedQueryTracker.ts`:
- Around line 11-17: The stripAfterCapture behavior in preservedQueryTracker
only removes sensitive query keys on the client after navigation, so it still
leaves the original URL visible to server/CDN/telemetry logs. Update the
navigation flow around preservedQueryTracker (and any caller that sets
stripAfterCapture) so the same query keys are also redacted or handled
server-side before logging, ensuring the secret never appears in access logs,
redirects, or analytics. Reference preservedQueryTracker and stripAfterCapture
when wiring the server/log-pipeline redaction path.
- Around line 38-45: The redirect in preservedQueryTracker’s navigation cleanup
path is dropping the replace flag, so update the logic in preservedQueryTracker
to pass replace: true when calling next with the cleaned query. Make sure the
behavior is applied in the branch that strips keys from to.query, and add a test
that drives navigation through router.replace() to verify it does not create a
new history entry.
- Around line 24-36: The preserved-query capture logic in
preservedQueryTracker’s beforeEach hook is overwriting namespace state instead
of merging it. Update the flow around hydratePreservedQuery and
capturePreservedQuery so that when a navigation matches only some keys for a
namespace, the existing preserved payload is read first and the new query values
are merged into it before saving back to memory and sessionStorage. Use the
namespace handling in capturePreservedQuery and the definitions loop to keep
previously captured keys intact for partial navigations.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 0ae4409b-bd2c-4cfc-905f-906778a07b54
📒 Files selected for processing (4)
src/platform/navigation/preservedQueryManager.test.tssrc/platform/navigation/preservedQueryManager.tssrc/platform/navigation/preservedQueryTracker.test.tssrc/platform/navigation/preservedQueryTracker.ts
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #13465 +/- ##
==========================================
- Coverage 77.71% 77.69% -0.03%
==========================================
Files 1648 1648
Lines 93650 92528 -1122
Branches 32006 31669 -337
==========================================
- Hits 72783 71892 -891
+ Misses 20178 19979 -199
+ Partials 689 657 -32
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 38 files with indirect coverage changes 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/platform/navigation/preservedQueryTracker.ts (1)
8-18: 🧹 Nitpick | 🔵 TrivialClient-side-only stripping is still a partial mitigation — still valid architectural note.
Reiterating a still-open concern from a prior review: stripping the key from the client-side route only prevents it from persisting in browser history/state. The initial request carrying the raw secret in the query string will already have hit the server, any edge/CDN, and access/analytics logs before this guard runs. If the goal is to fully keep the value out of "redirects, or telemetry" (per PR objectives), server-side/log-pipeline redaction for the same keys is still needed as a complementary measure; this is out of scope for a client-side router file but worth tracking.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/navigation/preservedQueryTracker.ts` around lines 8 - 18, Client-side stripping in PreservedQueryDefinition.stripAfterCapture only removes values from browser state, so the secret can still reach the server and logs on the initial request. Keep this as a client-side behavior, but add a clear architectural note or TODO alongside preservedQueryTracker/PreservedQueryDefinition that server-side and telemetry/log-pipeline redaction for the same keys is still required, and track that complementary work separately so the scope is explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@src/platform/navigation/preservedQueryTracker.ts`:
- Around line 8-18: Client-side stripping in
PreservedQueryDefinition.stripAfterCapture only removes values from browser
state, so the secret can still reach the server and logs on the initial request.
Keep this as a client-side behavior, but add a clear architectural note or TODO
alongside preservedQueryTracker/PreservedQueryDefinition that server-side and
telemetry/log-pipeline redaction for the same keys is still required, and track
that complementary work separately so the scope is explicit.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: e3fc6f84-2dfb-420b-8a6c-b81a6ef43bb6
📒 Files selected for processing (4)
src/platform/navigation/preservedQueryManager.test.tssrc/platform/navigation/preservedQueryManager.tssrc/platform/navigation/preservedQueryTracker.test.tssrc/platform/navigation/preservedQueryTracker.ts
| if (!merge) { | ||
| const payload: Record<string, string> = {} | ||
| keys.forEach((key) => { | ||
| const value = readQueryParam(query[key]) | ||
| if (value) { | ||
| payload[key] = value | ||
| } | ||
| }) | ||
| if (Object.keys(payload).length === 0) { | ||
| return | ||
| } | ||
| preservedQueries.set(namespace, payload) | ||
| writeToStorage(namespace, payload) | ||
| return | ||
| } | ||
|
|
||
| hydratePreservedQuery(namespace) | ||
| const payload: Record<string, string> = { | ||
| ...(preservedQueries.get(namespace) ?? {}) | ||
| } | ||
| let changed = false | ||
|
|
||
| keys.forEach((key) => { | ||
| if (!Object.hasOwn(query, key)) return | ||
|
|
||
| const value = readQueryParam(query[key]) | ||
| if (value) { | ||
| payload[key] = value | ||
| changed = true | ||
| return | ||
| } | ||
|
|
||
| if (key in payload) { | ||
| delete payload[key] | ||
| changed = true | ||
| } | ||
| }) | ||
|
|
||
| if (Object.keys(payload).length === 0) { | ||
| if (!changed) { | ||
| return | ||
| } | ||
|
|
||
| preservedQueries.set(namespace, payload) | ||
| if (Object.keys(payload).length === 0) { | ||
| preservedQueries.delete(namespace) | ||
| } else { | ||
| preservedQueries.set(namespace, payload) | ||
| } | ||
| writeToStorage(namespace, payload) |
There was a problem hiding this comment.
The duplication in this function doesn't feel great. I probably wasted far more time than I should have trying to optimize this, but I think this works?
if (merge) hydratePreservedQuery(namespace)
const prior = merge ? { ...preservedQueries.get(namespace) } : {}
let changed = false
const payload = Object.fromEntries(keys.flatMap((key) => {
const value = readQueryParam(query[key])
const queryValue = readQueryParam(query[key])
changed ||= queryValue !== prior[key]
const newValue = !merge || key in query ? queryValue : prior[key]
return newValue ? [[key, newValue]] : []
}))
if (merge ? !changed : Object.keys(payload).length === 0) return
if (Object.keys(payload).length === 0) {
preservedQueries.delete(namespace)
} else {
preservedQueries.set(namespace, payload)
}
writeToStorage(namespace, payload)Of note, the original code causes a merge to the same value to still cause an update (!!queryValue || !!key[prior])
There was a problem hiding this comment.
Optimizations are always fun, tragically because of SOC II I will have to add this in a followup
This isn't a fix, just a new feature
What
installPreservedQueryTrackerdefinitions accept an opt-instripAfterCaptureflag: the marked keys are captured into the sessionStorage stash and removed from the URL before the navigation completes (single guard redirect at the decoded query-object level; push/replace semantics are inherited from the original navigation, and vue-router force-replaces the initial one).preservedQueryManagernow captures the first non-empty string element of repeated (array-valued) params instead of silently dropping them.Why
One-time secrets in query params (first consumer: desktop login codes, GTM-93) must not linger in the visible URL, browser history,
previousFullPathredirects, or telemetry. Stripping after navigation — what each loader does ad hoc today — leaves a window and forces per-feature URL scrubbing; #13418 originally needed a hand-rolled encoding-aware string parser in three places. Stripping at capture time, at the decoded query-object level, makes the stash the only carrier and lets vue-router round-trip the surviving params' encoding itself.Capability only — no existing namespace opts in; behavior is unchanged for all current definitions.
stripAfterCapture's contract is documented on the option: strip-marked keys must never be read fromroute.queryby later guards or views; the stash is the only post-capture source.Landing order
Independent of everything else; #13418 stacks on this branch.