LCOV - code coverage report
Current view: top level - dashql-app/src/config - flavor_resolution.ts Coverage Total Hit
Test: DashQL 915828b Lines: 100.0 % 52 52
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import { AppFlavor, DASHQL_FLAVOR, FLAVORS } from './flavors.js';
       2              : 
       3              : /// The query parameter selecting a flavor, e.g. `?flavor=sidb`
       4            1 : export const FLAVOR_QUERY_PARAMETER = 'flavor';
       5              : 
       6              : /// The result of resolving the flavor
       7              : export interface FlavorResolution {
       8              :     /// The resolved flavor
       9              :     flavor: AppFlavor;
      10              :     /// A requested flavor id that is not known
      11              :     unknownId?: string;
      12              : }
      13              : 
      14              : /// Resolve the flavor from the query parameter, DashQL is the default
      15            1 : export function resolveFlavor(requested: string | null): FlavorResolution {
      16            1 :     if (requested == null) {
      17            1 :         return { flavor: DASHQL_FLAVOR };
      18              :     }
      19            1 :     const flavor = FLAVORS.get(requested);
      20            1 :     if (flavor == null) {
      21            1 :         return { flavor: DASHQL_FLAVOR, unknownId: requested };
      22              :     }
      23            1 :     return { flavor };
      24            1 : }
      25              : 
      26              : /// Get the flavor requested by the query parameter of this page, null if it requests none.
      27              : /// An unknown flavor requests the default one.
      28            1 : export function getRequestedFlavor(): AppFlavor | null {
      29            1 :     const search = new URLSearchParams(globalThis.location?.search ?? '');
      30            1 :     const requested = search.get(FLAVOR_QUERY_PARAMETER);
      31            1 :     if (requested == null) {
      32            1 :         return null;
      33              :     }
      34            1 :     const resolution = resolveFlavor(requested);
      35            1 :     if (resolution.unknownId !== undefined) {
      36            1 :         console.warn(`unknown app flavor: ${resolution.unknownId}`);
      37              :     }
      38            1 :     return resolution.flavor;
      39            1 : }
      40              : 
      41              : /// Select the flavor of the app.
      42              : /// The flavor requested by the url wins over the flavor of the app settings, DashQL is the default.
      43            1 : export function selectFlavor(requested: AppFlavor | null, settingId: string | undefined): AppFlavor {
      44            1 :     return requested ?? (settingId == null ? null : FLAVORS.get(settingId)) ?? DASHQL_FLAVOR;
      45            1 : }
      46              : 
      47              : /// The url with the query parameter of a flavor, null if it already carries it.
      48              : /// The default flavor needs no query parameter.
      49            1 : export function withFlavorParameter(url: URL, flavor: AppFlavor): URL | null {
      50            1 :     if (flavor.id === DASHQL_FLAVOR.id || url.searchParams.get(FLAVOR_QUERY_PARAMETER) === flavor.id) {
      51            1 :         return null;
      52              :     }
      53            1 :     const next = new URL(url);
      54            1 :     next.searchParams.set(FLAVOR_QUERY_PARAMETER, flavor.id);
      55            1 :     return next;
      56            1 : }
      57              : 
      58              : /// The static favicons of the entry pages that a branded flavor replaced
      59            1 : const STATIC_FAVICONS = new WeakMap<Document, Element[]>();
      60              : 
      61              : /// Brand the document of an html entry page with a flavor.
      62              : /// The static entry pages carry the DashQL favicons, they are only replaced for flavors with their own
      63              : /// and restored when switching back to a flavor without.
      64            1 : export function applyFlavorToDocument(flavor: AppFlavor, doc: Document = document) {
      65            1 :     doc.documentElement.dataset.flavor = flavor.id;
      66            1 :     doc.title = flavor.displayName;
      67            1 :     doc.querySelector('meta[name="apple-mobile-web-app-title"]')?.setAttribute('content', flavor.displayName);
      68            1 :     doc.head.querySelector('link[data-flavor-favicon]')?.remove();
      69            1 :     if (flavor.favicon != null) {
      70            1 :         const statics = [...doc.head.querySelectorAll('link[rel~="icon"], link[rel="apple-touch-icon"], link[rel="mask-icon"]')];
      71            1 :         if (statics.length > 0) {
      72            1 :             STATIC_FAVICONS.set(doc, statics);
      73              :         }
      74            1 :         for (const link of statics) {
      75            1 :             link.remove();
      76              :         }
      77            1 :         const icon = doc.createElement('link');
      78            1 :         icon.rel = 'icon';
      79            1 :         icon.href = flavor.favicon.href;
      80            1 :         icon.dataset.flavorFavicon = flavor.id;
      81            1 :         doc.head.appendChild(icon);
      82            1 :     } else {
      83            1 :         for (const link of STATIC_FAVICONS.get(doc) ?? []) {
      84            1 :             doc.head.appendChild(link);
      85              :         }
      86            1 :         STATIC_FAVICONS.delete(doc);
      87            1 :     }
      88            1 : }
        

Generated by: LCOV version 2.0-1