All files / src/generators/shared types.ts

0% Statements 0/0
0% Branches 0/0
0% Functions 0/0
0% Lines 0/0

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                                                                                                                                                                                                                                   
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
// SPDX-License-Identifier: Apache-2.0
 
/**
 * @module Generators/Shared/Types
 * @description Branded types and shared interfaces for the HTML/XML/RSS
 * generation bounded contexts. Branded types prevent accidental mixing of
 * raw strings with already-escaped or validated output.
 *
 * These types are compile-time-only (erased by TypeScript) so they impose
 * zero runtime cost while catching misuse like passing unsanitized input
 * directly into an HTML template.
 */
 
import type { LanguageCode } from '../../types/index.js';
 
// ─── Branded-type infrastructure ────────────────────────────────────────────
 
/**
 * Phantom brand tag. The unique symbol ensures no two branded types are
 * assignable to each other even if they share the same base type.
 */
declare const __brand: unique symbol;
 
/**
 * A branded type wraps a base primitive with a compile-time-only tag so
 * TypeScript prevents accidental interchange. Runtime representation is
 * identical to `Base`; the brand exists only in the type system.
 */
type Brand<Base, Tag extends string> = Base & { readonly [__brand]: Tag };
 
// ─── HTML-safe branded types ────────────────────────────────────────────────
 
/**
 * A string that has been HTML-entity-escaped and is safe for interpolation
 * into an HTML document. Created via {@link toSafeHtml}.
 */
export type SafeHtmlString = Brand<string, 'SafeHtml'>;
 
/**
 * A string that has been XML-entity-escaped and is safe for interpolation
 * into an XML document (sitemap.xml, rss.xml). Created via {@link toSafeXml}.
 */
export type SafeXmlString = Brand<string, 'SafeXml'>;
 
/**
 * An absolute URL validated to start with `https://`. Prevents accidental
 * injection of `javascript:` or relative paths into `href` attributes.
 */
export type AbsoluteUrl = Brand<string, 'AbsoluteUrl'>;
 
/**
 * A POSIX-normalized relative file path (no leading slash, forward-slash
 * separators). Used for article output paths and sitemap entries.
 */
export type RelativeFilePath = Brand<string, 'RelativeFilePath'>;
 
// ─── Shared generation interfaces ───────────────────────────────────────────
 
/**
 * Cache-busting configuration injected into every HTML template.
 * Ensures browser and CDN caches are invalidated on each deploy.
 */
export interface CacheBustConfig {
  /** Short build hash appended as `?v=<hash>` to asset URLs */
  readonly buildShort: string;
  /** Full semantic version string (e.g. `0.8.59`) */
  readonly appVersion: string;
}
 
/**
 * Common page metadata shared by every generated HTML page (article,
 * sitemap, political-intelligence, news-index).
 */
export interface PageMeta {
  /** Target ISO 639-1 language code */
  readonly lang: LanguageCode;
  /** `<title>` element content */
  readonly title: string;
  /** `<meta name="description">` content */
  readonly description: string;
  /** Canonical URL of the page */
  readonly canonicalUrl: string;
  /** Text direction (`ltr` or `rtl`) */
  readonly dir: 'ltr' | 'rtl';
}
 
/**
 * Structured data (JSON-LD) payload shape shared across page types.
 * Each generator builds its own specialization but the base fields
 * are common.
 */
export interface BaseJsonLd {
  readonly '@context': 'https://schema.org';
  readonly '@type': string;
  readonly name: string;
  readonly url: string;
  readonly inLanguage: string;
}
 
/**
 * Options bag for any generator that produces multi-language output.
 * Every bounded context that emits per-language files accepts at least
 * these fields.
 */
export interface MultiLanguageGeneratorOptions {
  /** Subset of languages to generate (defaults to ALL_LANGUAGES) */
  readonly languages?: readonly LanguageCode[];
  /** Cache-busting parameters */
  readonly cacheBust: CacheBustConfig;
  /** Whether to include structured data (JSON-LD) in output */
  readonly includeStructuredData?: boolean;
}