# Link Shortener

## Objective

Turn long URLs into short branded links and see how many people actually clicked.

A short-link service with custom or generated slugs, a redirect endpoint, and per-link click counts.

## Before You Begin

This feature is being added to an application that already exists and already
works. Do not scaffold a new project, and do not assume a blank slate.

Inspect the codebase first and establish:

- The existing application structure and where code of this kind already lives.
- The framework and version in use.
- The existing design system — colours, spacing, typography, and component conventions.
- Existing UI components you can reuse instead of writing new ones.
- The existing database structure, if this feature needs to persist anything.
- The existing authentication and authorization system, if this feature is user-scoped.
- Dependencies already installed, so you don't add a library that duplicates one.
- The existing test setup and conventions.

Only start writing code once you understand the above. If the application
already implements part of this feature, extend it rather than replacing it.

## Implementation Instructions

1. The app already has Shareable Deep Links and Signed Share Links. Extend that machinery with a short-slug alias rather than standing up a second, parallel link table that will drift out of sync with it.
2. Let a user accept a generated slug or type their own. Validate a custom slug on the server against reserved application routes, existing slugs, and profanity, and say which rule failed rather than silently generating a different one.
3. Record a click on the redirect itself: timestamp, referrer, coarse device type, and coarse location. Feed those into the app's existing Event Tracking so the numbers appear beside the rest of the analytics instead of in an isolated counter.
4. Constrain destinations. Either restrict them to hosts the account controls, or run every destination through the same allowlist and safe-browsing check the app already applies to user-submitted URLs before the redirect is ever served.
5. Do not count the click inside the request that performs the redirect if that write can block or fail. The redirect must still happen when the counter is unavailable; record the click asynchronously and accept that a small number are lost.

## UI and UX Requirements

Match the application's existing design system exactly. Reuse its components,
spacing, and typography. This feature should look like it was always there.

## Responsive Requirements

Works on mobile, tablet, and desktop. Touch targets are large enough to hit on a
phone, and nothing overflows horizontally at 320px.

## Accessibility Requirements

- Fully keyboard navigable.
- Correct semantic elements and ARIA roles.
- Visible focus states.
- Meets WCAG AA contrast.
- Dynamic changes are announced to screen readers.
- Respects prefers-reduced-motion.

## Edge Cases

- A slug that collides with a real application route hijacks part of the app. Reserve every top-level path the router already claims, plus the ones you are likely to add later such as admin, api, login, and settings, and reject those slugs at creation time.
- An unrestricted shortener becomes an open redirect that lends your domain's reputation to phishing. Refuse destinations that fail the allowlist, and re-check a destination periodically because a host that was safe when the link was created may not stay that way.
- Link previewers, mail scanners, and browser prefetch all fetch the redirect without a human involved, which inflates counts badly on links shared by email. Identify and exclude them, and deduplicate repeat hits from the same client within a short window.
- Decide explicitly what a deleted or expired link does. Silently 404 is confusing to someone who was handed the link in good faith; show a short page saying the link is no longer active and offer a route back to the app.
- A shortened link that drops the query string or fragment breaks tracking parameters and in-page anchors. Append the incoming query and fragment to the destination, merging rather than overwriting parameters the destination already carries.
- Slugs are read aloud and typed by hand. Treat them case-insensitively and avoid characters that are easily confused, or a link that works when pasted will fail when someone types it off a slide.
- Reusing a deleted slug for a new destination sends people who kept the old link somewhere unexpected. Retire slugs rather than returning them to the pool.
- Sequential slugs let anyone enumerate every link in the system. Generate unpredictable slugs, and apply the app's existing Rate Limiting to the redirect endpoint so guessing is not cheap.

## Testing

Exercise the feature end to end in the running application. Cover every edge case
above, then run the existing test suite and confirm nothing regressed.

## Acceptance Criteria

- [ ] A user can create a short link with a generated or custom slug and is told clearly when a custom slug is rejected.
- [ ] Slugs cannot collide with application routes, and retired slugs are never reissued.
- [ ] Redirects preserve incoming query parameters and fragments.
- [ ] Destinations are validated against the app's existing URL safety checks before a redirect is served.
- [ ] Click counts exclude prefetch and bot traffic and appear in the app's existing analytics.
- [ ] A redirect still succeeds when click recording is unavailable.
- [ ] Deleted and expired links show an explanatory page rather than a bare error.
- [ ] The feature matches the existing design system.
- [ ] No existing functionality is broken.

## Adaptation Rules

- Match the existing design system. Do not introduce a new colour palette,
  spacing scale, or component library.
- Reuse existing components and utilities wherever they fit.
- Follow the naming, file layout, and code style already present.
- Do not upgrade, replace, or remove existing dependencies to make this
  feature fit. Adapt the feature to the app, not the app to the feature.
- Do not break existing functionality. If a change is genuinely required in
  existing code, make the smallest one that works and say so.
- If something in these instructions conflicts with how the application is
  built, follow the application and explain the deviation.

## Final Verification

Before you report the work as done:

1. Re-read the acceptance criteria above and check each one against what you
   actually built.
2. Run the application and exercise the feature end to end.
3. Run the existing test suite and confirm you have broken nothing.
4. Check the feature on mobile, tablet, and desktop widths.
5. Check keyboard navigation and focus handling.
6. Summarize what changed: files added, files modified, and anything you
   deliberately did differently because of how this application is built.

If any acceptance criterion is unmet, fix it before reporting completion.
