feat(deeplink): confirm routes for invite, plugin-install and snippet-install - #150
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the Part 3 remainder of #144.
DeepLinkJoinModalbecomesDeepLinkConfirmModal: one sheet driven by a per-route spec (icon, optional asyncload,details, optionalextra, optionalcanAccept,accept). Thejoinroute moves onto it with no behaviour change — every assertion in its test file is byte-identical; only the import line and the JSX tags follow the rename.loadbeing optional is deliberate:join's copy is complete from the intent alone, so its sheet paints complete in the first frame instead of flashing a spinner over text it already has.Three confirm routes on top of it:
invite?h=@handle— resolves the handle by exact match and knocks that user into the terminal this device is currently sharing. No new server surface: the only knock that exists is session-scoped. A fuzzy match would let@kevland on@kevin-p, which is the impersonation shape the unified invite work set out to close. With no shareable session the sheet still names the handle and accept stays disabled with a reason.plugin-install?id=…&src=…—srcis a catalogue id already configured and enabled on this device, never a URL, so a link cannot introduce a code source; a disabled source fails exactly like an absent one. The plugin id is validated byisValidPluginIdin the codec, before any path is built from it. The sheet names the plugin, version, author, catalogue and permissions before accept does anything, and the manifest text disclosed is the manifest text installed —loadreads it once andacceptpasses that same string.snippet-install?id=…— names the entry, its author, how many snippets it carries, and which vault they land in. A link the user did not author should not quietly write into a vault they were not thinking about.Both installs are code execution driven by unauthenticated input, so both are
confirmby construction: a sheet whoseloadfailed can never be accepted.Two DRY extractions fall out, each done in the task that needed it:
import-export/storeAccess.ts(so a link-driven snippet install runs the same path as the community tab) andPluginPermissionList(so a link-driven plugin install cannot disclose less than a click-driven one).Testing
Full
CI=true pnpm vitest run: 3705 passed. The 3 remaining failures —pluginBundleBuild,TitleBar.syncState(×2) — are 5000ms timeouts that reproduce identically at the branch pointb1d29ed4with none of these changes present, and touch no file in this PR.tsc --noEmitclean.Not covered: no live click-through. These sheets have not been opened in a running app, only under vitest. The
inviteroute in particular touches the session-key wrap path, which is exactly what unit tests structurally cannot prove.Pairs with VoltiusApp/web#14.