feat(telemetry): capture Rewardful referral on checkout attribution#12311
feat(telemetry): capture Rewardful referral on checkout attribution#12311nav-tej wants to merge 1 commit into
Conversation
Mirrors the existing Impact wiring for the new Rewardful affiliate network: client reads window.Rewardful.referral when getCheckoutAttribution runs, and emits it as a new optional rewardful_referral field on CheckoutAttributionMetadata. The Go backend (comfy-api) consumes this field separately and passes it to Stripe as ClientReferenceID on the CheckoutSession create call — that wiring lives in a sibling PR on Comfy-Org/comfy-api and is the path that actually credits affiliate commissions for Stripe subscriptions (per Rewardful docs, GTM-loaded JS alone cannot attribute Checkout Sessions; the merchant must pass the referral UUID server-side). Why this is the simplest possible client-side change: - Rewardful's JS (loaded via GTM) owns its own cookie persistence, so unlike Impact (where we capture im_ref from URL params and persist to localStorage ourselves) we just read window.Rewardful.referral at checkout time. No URL fallback, no localStorage handling. If Rewardful's script hasn't loaded or the user didn't come from an affiliate link, the field is omitted from the payload entirely. - Adds a narrow RewardfulGlobal interface to global.d.ts (referral plus optional affiliate/campaign metadata Rewardful exposes) so window.Rewardful is typed everywhere. - Adds 4 unit tests covering: present, absent, empty-string, and alongside Impact attribution. The existing 10 Impact/UTM tests are untouched. Verified (locally on the workspace clone): - pnpm typecheck — clean - pnpm test:unit src/platform/telemetry/utils — 14/14 (10 prior + 4 new) - pnpm test:unit (full repo) — passing - pnpm lint — 3 warnings, 0 errors (warnings pre-existing on main) - pnpm format:check — clean - pnpm knip — clean (1 pre-existing warning unrelated) - pnpm exec vite build — successful (7.85s) Cross-PR dependency: needs the sibling comfy-api PR to actually credit referrals. This PR is safe to ship independently — the field is just ignored by the existing comfy-api endpoint until that PR lands.
📝 WalkthroughWalkthroughAdds Rewardful referral attribution tracking to checkout telemetry. Defines global types for Rewardful configuration, extends attribution metadata to include rewardful_referral, implements extraction logic, and validates behavior with tests. ChangesRewardful Referral Attribution
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 6 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (6 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
🎨 Storybook: ✅ Built — View Storybook |
🎭 Playwright: ✅ 1602 passed, 0 failed · 3 flaky📊 Browser Reports
|
|
Acknowledged — that's the "review in progress" placeholder from @coderabbitai with no findings yet. Will address any actionable comments once the real review lands. For context, the PR has already had:
Cross-PR dependency to ship referral attribution end-to-end: Comfy-Org/cloud#3724. |
|
Thanks for the approval, @coderabbitai — and noted that zero inline findings were raised. 🎉 Pushing back on the Docstring Coverage warningI'm intentionally not adding docstrings to address the 0% docstring-coverage warning. Reasoning: 1. Matches the file's established convention. The existing functions in // existing pattern in the same file:
async function getGeneratedClickId(): Promise<string | undefined> {
// ...
}
function getGaIdentity(): Promise<GaIdentity | undefined> {
// ...
}vs. what I added (intentionally matching): function getRewardfulReferral(): string | undefined {
if (typeof window === 'undefined') return undefined
return asNonEmptyString(window.Rewardful?.referral)
}The function is 3 lines, the name and return type fully describe it, and prose docstrings would just restate 2. The repo's project-level AGENTS.md does not require docstrings. Frontend rules emphasize TypeScript safety, conventional commits, and tests — not prose annotation. The 80% threshold is a generic CodeRabbit preset, not a repo-specific bar. 3. The reasoning that actually matters lives where it's needed. All non-obvious decisions in this PR are documented in:
If the codebase later adopts a docstring-coverage rule at the repo level (eslint rule, TSDoc plugin, or CI gate), I'm happy to come back and address it consistently across The other 6 pre-merge checks (title, description, linked issues, scope, e2e coverage for fixes, ADR compliance) all passed. Holding here. |
📦 Bundle: 5.36 MB gzip 🟢 -122 BDetailsSummary
Category Glance App Entry Points — 26.1 kB (baseline 26.1 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.24 MB (baseline 1.24 MB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 82.9 kB (baseline 82.9 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 9 added / 9 removed / 2 unchanged Panels & Settings — 527 kB (baseline 527 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 10 added / 10 removed / 14 unchanged User & Accounts — 17.8 kB (baseline 17.8 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 5 added / 5 removed / 2 unchanged Editors & Dialogs — 112 kB (baseline 112 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 4 added / 4 removed UI Components — 58 kB (baseline 58 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed / 8 unchanged Data & Services — 3.16 MB (baseline 3.16 MB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 13 added / 13 removed / 4 unchanged Utilities & Hooks — 366 kB (baseline 366 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 13 added / 13 removed / 18 unchanged Vendor & Third-Party — 9.94 MB (baseline 9.94 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 9.16 MB (baseline 9.16 MB) • ⚪ 0 BBundles that do not match a named category
Status: 57 added / 57 removed / 86 unchanged ⚡ Performance Report
No regressions detected. All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-05-16T09:34:41.299Z",
"gitSha": "5b4c9e65a6b4fc4e863746751c5f632bc08557c2",
"branch": "glary/rewardful-affiliate-tracking",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2073.2600000001185,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.536,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 350.583,
"heapDeltaBytes": 19587528,
"heapUsedBytes": 67189988,
"domNodes": 22,
"jsHeapTotalBytes": 18350080,
"scriptDurationMs": 22.557000000000002,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2049.015999999938,
"styleRecalcs": 10,
"styleRecalcDurationMs": 12.418999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 394.36499999999995,
"heapDeltaBytes": 11811376,
"heapUsedBytes": 60974416,
"domNodes": -242,
"jsHeapTotalBytes": 13328384,
"scriptDurationMs": 22.729000000000003,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1903.3699999999953,
"styleRecalcs": 75,
"styleRecalcDurationMs": 38.908,
"layouts": 12,
"layoutDurationMs": 3.9179999999999997,
"taskDurationMs": 859.908,
"heapDeltaBytes": 2001440,
"heapUsedBytes": 50371344,
"domNodes": -263,
"jsHeapTotalBytes": 16117760,
"scriptDurationMs": 136.02599999999998,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1978.1980000000203,
"styleRecalcs": 80,
"styleRecalcDurationMs": 42.604,
"layouts": 12,
"layoutDurationMs": 3.174,
"taskDurationMs": 916.2170000000001,
"heapDeltaBytes": 19431528,
"heapUsedBytes": 68656956,
"domNodes": 65,
"jsHeapTotalBytes": 15990784,
"scriptDurationMs": 129.472,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1713.667000000214,
"styleRecalcs": 30,
"styleRecalcDurationMs": 16.281999999999996,
"layouts": 6,
"layoutDurationMs": 0.5680000000000002,
"taskDurationMs": 344.567,
"heapDeltaBytes": -3829264,
"heapUsedBytes": 44875604,
"domNodes": -214,
"jsHeapTotalBytes": 19787776,
"scriptDurationMs": 17.725,
"eventListeners": -118,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1735.9959999998864,
"styleRecalcs": 31,
"styleRecalcDurationMs": 17.249,
"layouts": 6,
"layoutDurationMs": 0.5720000000000001,
"taskDurationMs": 284.539,
"heapDeltaBytes": 525340,
"heapUsedBytes": 50051876,
"domNodes": 77,
"jsHeapTotalBytes": 14417920,
"scriptDurationMs": 17.361,
"eventListeners": 21,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "dom-widget-clipping",
"durationMs": 625.7949999999255,
"styleRecalcs": 12,
"styleRecalcDurationMs": 7.834999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 383.23400000000004,
"heapDeltaBytes": 11109636,
"heapUsedBytes": 63777348,
"domNodes": 20,
"jsHeapTotalBytes": 17301504,
"scriptDurationMs": 66.79499999999999,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "dom-widget-clipping",
"durationMs": 563.7110000000121,
"styleRecalcs": 14,
"styleRecalcDurationMs": 9.09,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 343.23199999999997,
"heapDeltaBytes": 11073228,
"heapUsedBytes": 63763368,
"domNodes": 24,
"jsHeapTotalBytes": 18350080,
"scriptDurationMs": 63.25,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "large-graph-idle",
"durationMs": 2013.4729999999763,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.627999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 500.749,
"heapDeltaBytes": -11252128,
"heapUsedBytes": 53099960,
"domNodes": 22,
"jsHeapTotalBytes": 14036992,
"scriptDurationMs": 94.14999999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2021.1930000000393,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.000000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 519.2460000000001,
"heapDeltaBytes": 14941472,
"heapUsedBytes": 71713504,
"domNodes": -259,
"jsHeapTotalBytes": 28672,
"scriptDurationMs": 94.27700000000002,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2135.8829999999216,
"styleRecalcs": 69,
"styleRecalcDurationMs": 17.999000000000006,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1108.1080000000002,
"heapDeltaBytes": 28440408,
"heapUsedBytes": 94222064,
"domNodes": -261,
"jsHeapTotalBytes": 33116160,
"scriptDurationMs": 383.43,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2111.902999999984,
"styleRecalcs": 68,
"styleRecalcDurationMs": 16.814,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1039.565,
"heapDeltaBytes": -1799488,
"heapUsedBytes": 57853940,
"domNodes": -263,
"jsHeapTotalBytes": 5738496,
"scriptDurationMs": 385.484,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3163.4570000001077,
"styleRecalcs": 64,
"styleRecalcDurationMs": 15.706999999999999,
"layouts": 60,
"layoutDurationMs": 6.841,
"taskDurationMs": 1314.743,
"heapDeltaBytes": 40353196,
"heapUsedBytes": 100930028,
"domNodes": -268,
"jsHeapTotalBytes": 41971712,
"scriptDurationMs": 463.95500000000004,
"eventListeners": -157,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-zoom",
"durationMs": 3197.424000000183,
"styleRecalcs": 66,
"styleRecalcDurationMs": 18.435000000000002,
"layouts": 60,
"layoutDurationMs": 6.939000000000001,
"taskDurationMs": 1349.161,
"heapDeltaBytes": 41208072,
"heapUsedBytes": 103965456,
"domNodes": -264,
"jsHeapTotalBytes": 33116160,
"scriptDurationMs": 477.95,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "minimap-idle",
"durationMs": 2012.098000000151,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.344999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 552.288,
"heapDeltaBytes": -11844376,
"heapUsedBytes": 61077588,
"domNodes": -265,
"jsHeapTotalBytes": 5472256,
"scriptDurationMs": 92.19299999999998,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2052.989000000025,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.493999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 525.784,
"heapDeltaBytes": 4059024,
"heapUsedBytes": 62464576,
"domNodes": -264,
"jsHeapTotalBytes": 5009408,
"scriptDurationMs": 93.256,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 575.7679999999255,
"styleRecalcs": 47,
"styleRecalcDurationMs": 11.136,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 367.32199999999995,
"heapDeltaBytes": 8548916,
"heapUsedBytes": 61435392,
"domNodes": 20,
"jsHeapTotalBytes": 16777216,
"scriptDurationMs": 131.096,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 527.8220000000147,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.99,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 350.8300000000001,
"heapDeltaBytes": 15030236,
"heapUsedBytes": 63923004,
"domNodes": 19,
"jsHeapTotalBytes": 17563648,
"scriptDurationMs": 119.49799999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-idle",
"durationMs": 2015.6040000001667,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.264999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 342.294,
"heapDeltaBytes": 23835148,
"heapUsedBytes": 73257656,
"domNodes": 21,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 18.921,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2014.4020000000182,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.674999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 376.29999999999995,
"heapDeltaBytes": -5537900,
"heapUsedBytes": 43455872,
"domNodes": -260,
"jsHeapTotalBytes": 15069184,
"scriptDurationMs": 14.208,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1733.36600000016,
"styleRecalcs": 77,
"styleRecalcDurationMs": 33.809,
"layouts": 16,
"layoutDurationMs": 3.8739999999999997,
"taskDurationMs": 709.9380000000001,
"heapDeltaBytes": 10450340,
"heapUsedBytes": 60304676,
"domNodes": -258,
"jsHeapTotalBytes": 22503424,
"scriptDurationMs": 92.177,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1692.2700000000077,
"styleRecalcs": 75,
"styleRecalcDurationMs": 35.456,
"layouts": 16,
"layoutDurationMs": 4.369000000000001,
"taskDurationMs": 674.165,
"heapDeltaBytes": 56544,
"heapUsedBytes": 49049740,
"domNodes": -260,
"jsHeapTotalBytes": 15331328,
"scriptDurationMs": 90.09,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 933.238000000074,
"styleRecalcs": 14,
"styleRecalcDurationMs": 27.049999999999997,
"layouts": 4,
"layoutDurationMs": 12.882000000000001,
"taskDurationMs": 734.2969999999998,
"heapDeltaBytes": 30209160,
"heapUsedBytes": 95059192,
"domNodes": 13513,
"jsHeapTotalBytes": 16777216,
"scriptDurationMs": 26.750999999999998,
"eventListeners": 2527,
"totalBlockingTimeMs": 148,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8130.396999999903,
"styleRecalcs": 248,
"styleRecalcDurationMs": 48.274,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3539.0519999999997,
"heapDeltaBytes": 12579044,
"heapUsedBytes": 70952240,
"domNodes": -263,
"jsHeapTotalBytes": 2592768,
"scriptDurationMs": 1210.5439999999999,
"eventListeners": -113,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "viewport-pan-sweep",
"durationMs": 8156.992000000173,
"styleRecalcs": 249,
"styleRecalcDurationMs": 49.56000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3523.7889999999998,
"heapDeltaBytes": 14146304,
"heapUsedBytes": 73955044,
"domNodes": -260,
"jsHeapTotalBytes": 4980736,
"scriptDurationMs": 1209.349,
"eventListeners": -113,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 11868.138000000044,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 11858.789999999999,
"heapDeltaBytes": -26551116,
"heapUsedBytes": 192225464,
"domNodes": -8332,
"jsHeapTotalBytes": 24440832,
"scriptDurationMs": 560.848,
"eventListeners": -16464,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.333333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 11981.88099999993,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 11973.146,
"heapDeltaBytes": -31087976,
"heapUsedBytes": 171909444,
"domNodes": -8331,
"jsHeapTotalBytes": 24440832,
"scriptDurationMs": 573.427,
"eventListeners": -16466,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 16883.917999999994,
"styleRecalcs": 100,
"styleRecalcDurationMs": 17.928,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16864.335,
"heapDeltaBytes": -447708,
"heapUsedBytes": 269275976,
"domNodes": -8331,
"jsHeapTotalBytes": 14655488,
"scriptDurationMs": 1074.178,
"eventListeners": -16488,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333237,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 13976.159999999936,
"styleRecalcs": 64,
"styleRecalcDurationMs": 15.849000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13960.778000000002,
"heapDeltaBytes": -12811056,
"heapUsedBytes": 191186632,
"domNodes": -8331,
"jsHeapTotalBytes": 23568384,
"scriptDurationMs": 817.091,
"eventListeners": -16462,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.220000000000073,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 450.31699999981356,
"styleRecalcs": 20,
"styleRecalcDurationMs": 25.514,
"layouts": 5,
"layoutDurationMs": 1.3660000000000003,
"taskDurationMs": 130.978,
"heapDeltaBytes": 5408392,
"heapUsedBytes": 59277356,
"domNodes": 170,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 29.26000000000001,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "workflow-execution",
"durationMs": 461.86099999999897,
"styleRecalcs": 20,
"styleRecalcDurationMs": 25.807000000000002,
"layouts": 6,
"layoutDurationMs": 1.3750000000000004,
"taskDurationMs": 128.71399999999997,
"heapDeltaBytes": 5269928,
"heapUsedBytes": 56884652,
"domNodes": 159,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 29.674,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
}
]
} |
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #12311 +/- ##
===========================================
- Coverage 74.02% 59.56% -14.47%
===========================================
Files 1522 1412 -110
Lines 85497 71882 -13615
Branches 22618 19026 -3592
===========================================
- Hits 63293 42815 -20478
- Misses 21392 28594 +7202
+ Partials 812 473 -339
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 1007 files with indirect coverage changes 🚀 New features to boost your workflow:
|
PR Created by the Glary-Bot Agent
Summary
Mirrors the existing Impact affiliate wiring for the new Rewardful affiliate network. The client reads
window.Rewardful.referralwhengetCheckoutAttribution()runs at checkout time and emits it as a new optionalrewardful_referralfield onCheckoutAttributionMetadata. The Go backend consumes this field separately and passes it to Stripe asClientReferenceIDon the Checkout Session create call — that wiring lives in a sibling PR onComfy-Org/cloud(services/comfy-api) and is the path that actually credits affiliate commissions for Stripe subscriptions.Per Rewardful's docs for server-side Stripe Checkout Sessions, the GTM-loaded Rewardful JS handles cookie persistence on the client but cannot attribute Checkout Sessions on its own — the merchant must explicitly pass the referral UUID server-side as
client_reference_id.Why this is the simplest possible client-side change
im_reffrom URL params and persist to localStorage ourselves), we just readwindow.Rewardful.referralat checkout time. No URL fallback, no localStorage handling.RewardfulGlobalinterface toglobal.d.ts(referralplus optionalaffiliate/campaignmetadata Rewardful exposes) sowindow.Rewardfulis typed everywhere it's accessed.Files touched
global.d.tsRewardfulGlobalinterface +Rewardful?:onWindowsrc/platform/telemetry/types.tsrewardful_referral?: stringtoCheckoutAttributionMetadatasrc/platform/telemetry/utils/checkoutAttribution.tswindow.Rewardful?.referralingetCheckoutAttribution()src/platform/telemetry/utils/__tests__/checkoutAttribution.test.tswindow.Rewardful = undefinedreset inbeforeEachCross-PR dependency
Needs the sibling
Comfy-Org/cloudPR (branchglary/rewardful-affiliate-tracking) to actually credit referrals. This PR is safe to ship independently — the field is just ignored by the existing comfy-api endpoint until that PR lands.Verification
pnpm typecheck— cleanpnpm test:unit src/platform/telemetry/utils— 14/14 passing (10 prior + 4 new)pnpm test:unit(full repo) — passingpnpm lint— 3 warnings, 0 errors (warnings pre-existing onmain)pnpm format:check— cleanpnpm knip— clean (1 pre-existing unrelated warning)pnpm exec vite build— successful (7.85s)Related: FE-704 (Finish affiliate pages PRs) for context on the broader affiliate launch.
┆Issue is synchronized with this Notion page by Unito