util.ts•1.97 kB
import type { ExtractParams } from './types.js';
/**
 * Asserts that a URI is valid.
 */
export function assertValidUri(uri: string) {
  try {
    new URL(uri);
    return uri;
  } catch {
    throw new Error(`invalid uri: ${uri}`);
  }
}
/**
 * Compares two URIs.
 */
export function compareUris(uriA: string, uriB: string): boolean {
  const urlA = new URL(uriA);
  const urlB = new URL(uriB);
  return urlA.href === urlB.href;
}
/**
 * Matches a URI to a RFC 6570 URI Template (resourceUris) and extracts
 * the parameters.
 *
 * Currently only supports simple string parameters.
 */
export function matchUriTemplate<Templates extends string[]>(
  uri: string,
  uriTemplates: Templates
):
  | {
      uri: Templates[number];
      params: { [Param in ExtractParams<Templates[number]>]: string };
    }
  | undefined {
  const url = new URL(uri);
  const segments = url.pathname.split('/').slice(1);
  for (const resourceUri of uriTemplates) {
    const resourceUrl = new URL(resourceUri);
    const resourceSegments = decodeURIComponent(resourceUrl.pathname)
      .split('/')
      .slice(1);
    if (segments.length !== resourceSegments.length) {
      continue;
    }
    const params: Record<string, string> = {};
    let isMatch = true;
    for (let i = 0; i < segments.length; i++) {
      const resourceSegment = resourceSegments[i];
      const segment = segments[i];
      if (!resourceSegment || !segment) {
        break;
      }
      if (resourceSegment.startsWith('{') && resourceSegment.endsWith('}')) {
        const paramKey = resourceSegment.slice(1, -1);
        if (!paramKey) {
          break;
        }
        params[paramKey] = segment;
      } else if (segments[i] !== resourceSegments[i]) {
        isMatch = false;
        break;
      }
    }
    if (isMatch) {
      return {
        uri: resourceUri,
        params: params as {
          [Param in ExtractParams<Templates[number]>]: string;
        },
      };
    }
  }
}