import { ApiGetResult } from "./api"; import { DataProviderKey } from "./dataProviderKey"; /** * Représente un chemin d’endpoint HTTP (ou path accepté par `API.get`), * typé par la forme de la réponse attendue `T`. * * **`U` (optionnel, défaut `any`)** : propriétés minimales sur le composant pour résoudre les * segments dynamiques du path (`${…}` / `{$…}`), comme pour `DataProviderKey`. Utilisé par `@get`. * * Contrairement à `DataProviderKey`, il n’y a **pas** de navigation par propriétés * (pas de dot-syntax) : le path est une seule chaîne. * * @example * new Endpoint("users/${userId}"); */ export class Endpoint { declare readonly _phantom?: T; declare readonly _phantomHost?: U; readonly path: string; constructor(path: string) { this.path = Endpoint.normalizePath(path); } /** Même path qu’`Endpoint` ; le 2ᵉ générique `U` est propagé sur la clé publisher. */ getDataProviderKey(): DataProviderKey, U> { return new DataProviderKey, U>(this.path); } /** * Trim, refuse le path vide, enlève les `/` en tête (path relatif au `serviceURL`), * et fusionne les `/` successifs en un seul. * Pour une URL absolue (`http://` / `https://`), normalise surtout le pathname (pas de `//` redondants). */ static normalizePath(path: string): string { const t = String(path).trim(); if (!t) { throw new RangeError("Endpoint: path cannot be empty"); } if (/^https?:\/\//i.test(t)) { let u: URL; try { u = new URL(t); } catch { throw new RangeError("Endpoint: invalid absolute URL"); } u.pathname = u.pathname.replace(/\/+/g, "/"); return u.href; } const q = t.indexOf("?"); const hash = t.indexOf("#"); let end = t.length; if (q >= 0) end = Math.min(end, q); if (hash >= 0) end = Math.min(end, hash); let base = t.slice(0, end); const tail = t.slice(end); base = base.replace(/^\/+/, ""); base = base.replace(/\/+/g, "/"); if (!base && tail) { throw new RangeError("Endpoint: path cannot be empty"); } return base + tail; } /** Utile avant construction dynamique. */ static isNonEmpty(path: string): boolean { return String(path).trim().length > 0; } /** * Indique un path local du type `dataProvider(id)…` reconnu par `API.get`. */ static looksLikeDataProviderPath(path: string): boolean { return /^\s*dataProvider\s*\(/i.test(String(path)); } toString(): string { return this.path; } }