deeplink

package
v0.18.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package deeplink parses and builds the gadak:// URLs that hand a piece of gadak to someone — a link in chat or on a web page instead of a shell, a running serve, and a command with side effects.

The grammar

gadak://<action>[/w/<profile>][/<subject>][?<params>]

gadak://view?pj=GDK&sc=inprogress        a filtered list, primary mirror
gadak://view/w/oss?issue=GDK-119         the same, on the "oss" mirror

The action is the host. The optional `/w/<profile>` segment is the same one the web UI uses for a workspace mount, in the same position, so a gadak:// link and the http:// link `gadak views open` prints describe the same thing the same way. The optional subject that may follow is what the action acts on — an issue key, a page key — and the query is passed through verbatim.

Why the whole grammar exists for one action

Only `view` is implemented today, and this package deliberately does not know that: it validates the *shape* and returns whichever action it read. Deciding which actions exist belongs to the app that handles them (desktop/deeplink.go), not to the parser.

The split matters because these two halves ship separately. A link lives in a chat log forever and is opened by whatever version happens to be installed, so the grammar is the part that must not change — while the set of actions grows every time a surface becomes addressable. Keeping the action list out of the parser means growing it is a table entry in the handler, and an older app meeting a newer link produces "this link needs a newer Gadak" instead of a parse error.

What `view` can say is decided on the web side: the hash carries the view params (filters, display) and the place params (which panel and screen — issue, doc, person, feed, settings tab…). The registry of place params is web/src/lib/url-state.ts; a param registered there is linkable from here the same moment, with no change in this package. A second action is for what a hash cannot express — a place with no URL, or a different kind of address entirely.

Security posture

Any web page can embed a gadak:// link, so the worst one may achieve is that the user briefly looks at the wrong thing. The grammar carries no verb and no payload beyond an address: a link says *where to go*, never what to do. Handlers must keep that true — an action that writes, or that submits anything, does not belong in this scheme however convenient it would be.

Validation here is shape and size only:

  • a well-formed action, an optional profile that is a safe directory name, an optional subject that cannot escape a path,
  • length and arity limits that bound what a handler is asked to parse.

There is deliberately no allowlist of view parameter keys. The keys the UI understands are owned by VIEW_PARAM_KEYS in web/src/lib/view-config; a second copy here would drift the moment a new axis is added there, and the drift fails silently in the direction that matters — a link would stop working with no error anywhere. Unknown keys pass through and are ignored by the UI, which is the "wrong thing" worst case already accepted above.

Index

Constants

View Source
const ActionView = "view"

ActionView shows a list of issues, filtered by the link's hash. The only action implemented today; see the package comment for why the parser does not enforce that.

View Source
const Scheme = "gadak"

Scheme is the URL scheme, registered by the macOS bundle in desktop/build-app.sh. One owner, because the two are in a .go and a .sh file that nothing else connects and a disagreement is silent.

Variables

View Source
var (
	// ErrNotGadak reports a URL that is not a gadak:// link at all.
	ErrNotGadak = errors.New("deeplink: not a gadak:// URL")

	// ErrMalformed reports a gadak:// link this package refuses to parse.
	// Wrapping carries the specific rule that fired.
	ErrMalformed = errors.New("deeplink: malformed gadak:// URL")
)

Callers must be able to tell these apart: a handler stays silent when the link simply is not ours (another app's scheme reaching a shared handler) and logs when it is ours but refused.

Functions

func Compose

func Compose(action, prefix, hash string) string

Compose builds a gadak:// link: the inverse of Parse, in the package that owns the grammar, so the two cannot drift into disagreeing about the shape.

prefix is the workspace segment — "" for the primary mirror, "/w/<name>" otherwise — and is taken as a parameter rather than derived here on purpose. The rule mapping a profile onto that segment belongs to internal/workspace, which pulls in the server and the sync engine; a parser any process might run should not drag those along to answer a question about a URL. Callers pass workspace.Prefix(profile, "").

An empty hash yields "" for ActionView: there is no view to link to, and a link to nothing is worse than no link, because it looks like one that works.

Types

type Link struct {
	// Action is what the link asks for, lowercased. Compare against the
	// Action* constants; an unrecognised one is a link from a newer gadak,
	// not an error.
	Action string

	// Profile is the mirror the link addresses. "" means the primary one;
	// "default" is accepted and means the same, because config does.
	Profile string

	// Subject is what the action acts on, or "" when it needs none. No action
	// uses it yet; the slot is reserved so adding one (gadak://issue/GDK-119)
	// does not have to change the grammar.
	Subject string

	// Hash is the view hash: the raw query string exactly as the UI's hash
	// router and uifocus want it, with no leading "#" or "?" and
	// percent-encoding untouched. Returned verbatim — never decoded,
	// re-encoded, or re-ordered — because the CLI's composeServeURL treats it
	// as opaque too, and any rewrite here would make the two paths disagree.
	// Empty when the link carries no query; whether that is acceptable is the
	// action's rule, not the grammar's.
	Hash string
}

A Link is a parsed gadak:// URL.

func Parse

func Parse(raw string) (Link, error)

Parse turns a gadak:// URL into a Link.

The host is compared case-insensitively and returned lowercased; the path is not, because profile and subject are identifiers. One trailing slash before the query is tolerated, because a human retyping a link produces one.

err is ErrNotGadak when the URL is not a gadak:// link at all (the caller should stay silent) and wraps ErrMalformed when it is one this package rejects (the caller should say so).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL