All files / router/src redirect.ts

100% Statements 27/27
100% Branches 5/5
100% Functions 4/4
100% Lines 27/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 1421x     1x                 1x                                       1x 44x 44x   1x                                         1x           1x               1x 91x 91x 91x 91x 1x                                                         1x             26x 26x 26x 26x 26x 26x 26x 26x                                     1x 36x 36x  
import { microtask } from '@anchorlib/core';
import type { Route } from './route.js';
import type { ExtractParams, ExtractQueryParams, RouteOptions, RoutePath, UnknownRedirect } from './types.js';
import { createUrl } from './url.js';
 
/**
 * Internal handler for processing redirects.
 *
 * Set via {@link setRedirectHandler} to customize redirect behavior.
 *
 * @internal
 */
let redirectHandler: (redirect: UnknownRedirect) => void;
 
/**
 * Sets the handler for processing redirects.
 *
 * This allows customizing how redirects are handled, such as
 * integrating with a specific routing library or browser history API.
 *
 * @param handler - A function that receives a redirect object
 *
 * @example
 * ```ts
 * import { setRedirectHandler } from '@anchorlib/router';
 *
 * setRedirectHandler((redirect) => {
 *   const url = redirectUrl(redirect);
 *   window.location.href = url;
 * });
 * ```
 */
export function setRedirectHandler(handler: (redirect: UnknownRedirect) => void) {
  redirectHandler = handler;
}
 
const [schedule] = microtask(0);
 
/**
 * Represents a redirect to a different route.
 *
 * Redirects can be thrown from guards to trigger navigation to another route.
 *
 * @template TPath - The route path type
 * @template TParams - The route parameters type
 * @template TQueryParams - The query parameters type
 * @template TOptions - The route options type
 * @template TData - The route data type
 *
 * @example
 * ```ts
 * import { Redirect } from '@anchorlib/router';
 *
 * const redirect = new Redirect(loginRoute, { returnTo: '/dashboard' });
 * throw redirect;
 * ```
 */
export class Redirect<
  TPath extends RoutePath,
  TParams extends ExtractParams<TPath>,
  TQueryParams extends ExtractQueryParams<TPath>,
  TOptions extends RouteOptions,
  TData = unknown,
> {
  /**
   * Creates a new Redirect instance.
   *
   * @param route - The target route to redirect to
   * @param params - Optional route parameters
   * @param query - Optional query parameters
   */
  constructor(
    public route: Route<TPath, TParams, TQueryParams, TOptions, TData>,
    public params?: TParams,
    public query?: TQueryParams
  ) {}
}
 
/**
 * Creates a redirect to a different route.
 *
 * This function creates a Redirect object and schedules it to be processed
 * by the redirect handler. Can be thrown from guards to trigger navigation.
 *
 * @template TPath - The route path type
 * @template TParams - The route parameters type
 * @template TQueryParams - The query parameters type
 * @template TOptions - The route options type
 * @template TData - The route data type
 * @param route - The target route to redirect to
 * @param params - Optional route parameters
 * @param query - Optional query parameters
 * @returns A Redirect object
 *
 * @example
 * ```ts
 * import { redirect } from '@anchorlib/router';
 *
 * route.guard(async ({ params }) => {
 *   if (!await isAuthenticated()) {
 *     throw redirect(loginRoute, { returnTo: '/dashboard' });
 *   }
 * });
 * ```
 */
export function redirect<
  TPath extends RoutePath,
  TParams extends ExtractParams<TPath>,
  TQueryParams extends ExtractQueryParams<TPath>,
  TOptions extends RouteOptions,
  TData,
>(
  route: Route<TPath, TParams, TQueryParams, TOptions, TData>,
  params?: TParams,
  query?: TQueryParams
): Redirect<TPath, TParams, TQueryParams, TOptions, TData> {
  const redirect = new Redirect(route, params, query);
  schedule(() => redirectHandler?.(redirect as UnknownRedirect));
  return redirect;
}
 
/**
 * Converts a Redirect object to a URL string.
 *
 * Replaces route parameters with their values and appends query parameters.
 *
 * @param redirect - The redirect object to convert
 * @returns The full URL string for the redirect
 *
 * @example
 * ```ts
 * import { redirect, redirectUrl } from '@anchorlib/router';
 *
 * const r = redirect(userRoute, { id: '123' }, { tab: 'profile' });
 * const url = redirectUrl(r);
 * // Returns: '/users/123?tab=profile'
 * ```
 */
export function redirectUrl(redirect: UnknownRedirect): string {
  return createUrl(redirect.route.path, redirect.params, redirect.query);
}