Repository navigation
Conversation
A link shared as `/abc123?slug=titanic` stored `slug` on the click row for deferred installs, but the Universal Link / App Link / web fallback redirect dropped it, so an app that was already installed opened without it. decorateWebDestination() now merges extractLinkParams(request.query) into the destination, fill-only: keys already on the destination URL or set by the link's deep_link_parameters always win, and reserved utm_*/fp_*/lf_click are excluded as before. Closes LinkForty#66 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
|
Hi @hdeyana! Thank you for the contribution and such a detailed PR description! The PR is currently going through our review process, and I will follow up soon. 🚀 As we review, there's a piece of the picture that isn't visible from this repo, though, and it might be the answer to your case. Parameter passthrough is implemented in the SDKs.The server deliberately does not forward these because on the supported path it doesn't need to. The app receives the tapped URL itself, query string intact, and the SDK parses it. From
So This is the configuration to reach for. Whichever host serves your short links also needs to serve the association file, so the OS hands your app One question so I point you at the right specifics: are you self-hosting @linkforty/core, or running into this on the hosted platform? The setup differs between the two, and it changes what I'd suggest checking first. For self-hosting, Core serves both files from environment config via the /.well-known/* routes:
With any of those unset the endpoint returns a 404 naming the ones it's missing, so that's worth curling first - it's the quickest way to tell whether the association is actually in place. One other thing to check: direct-open passthrough has an SDK version floor.
Below those, a direct open falls back to the link's stored configuration and customParameters won't carry what was appended to the URL. That alone would produce the symptom you describe. Your case is real, thoughIf the association file sits on the short link's host, none of this comes up. Where it does is when your ios_universal_link points at a different domain than the short link, so the chain is So there's a genuine hole, but I think it might be narrower than the issue frames it. Specifically, the hop where the destination is a Universal Link on a different domain than the short link. On the change itselfWe'll need to consider whether we want a per-link flag for a second-order path, so I'm not going to merge this as it stands. However, I don't want to leave you guessing about why. Forwarding unconditionally isn't something we can do. Today a short link's destination is a pure function of its stored configuration. With this change, anyone who can hand someone a link can append arbitrary parameters to the destination. Fill-only protects keys the destination already carries - but a bare Universal Link like Which leaves the question worth sitting with: whether a new per-link flag and migration is the right amount of permanent surface for a path that serving the association file on the short link's host already handles. I'd rather weigh that properly than take it on reflex, so I'll come back to you with where we land. I'll leave this open rather than close it - the gap you found is genuine even if the fix isn't settled yet. Thanks again for the submission. The test file in particular is written the way we'd write it, and the fill-only instinct was the right call! |
Description
Forwards the visitor's own query parameters to the http(s) redirect destination, so a link shared as
/<code>?v=titanic-ep1opens an already-installed app (via the Universal Link / App Link) withv=titanic-ep1, the same value a deferred install already gets from the click row.Type of Change
Related Issues
Fixes #66
Changes Made
decorateWebDestination()insrc/routes/redirect.tsmergesextractLinkParams(request.query)into the destination URL. This covers the Universal Link / App Link 302, the in-app-browserweb_fallback_urlhop, the desktop destination and the launchpad's "Continue on the web" link.deep_link_parameters, is never overridden. A public URL can't rewrite e.g. theidof a Play Store link.extractLinkParams, so reservedutm_*/fp_*/lf_clickare excluded and the count/length caps apply, as for the click row.app_scheme) URLs are left as they were; happy to extend it there too if you want it.Testing
npm test): 410 passed, 2 skippedsrc/routes/redirect.query-forwarding.test.ts(8 cases). 7 of them fail without the change; the 8th guards that a request with no parameters is unchanged.npm run typecheck)Breaking Changes
None for links whose visitors arrive without a query string, and destinations never lose or change a parameter they already had. The one behavioural difference: extra parameters on an inbound URL now reach the destination instead of being dropped. If you'd rather make this opt-in per link (like
append_click_id), I can add the flag and migration.Checklist
feat:,fix:)🤖 Generated with Claude Code