summaryrefslogtreecommitdiff
path: root/workers/apex/src/manifest.ts
blob: 396f29ede1599b7b46ec87f774337c5d1827fe8a (plain)
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
import raw from "../../versions.json";

export interface VersionEntry {
  slug: string; label: string;
  status: "dev" | "lts" | "eol";
  binding: string; aliases: string[];
  pdf: string | null;
  // R2 object key for the apex PDF fallback (spec §5) — set only on versions whose PDF
  // exceeds the 25 MiB static-asset cap and is therefore absent from the content
  // Worker's own asset tree (currently just 1.3). Optional; most versions omit it.
  pdf_r2_key?: string;
}
export interface Manifest {
  schema_version: number;
  default_lang: string; default_version: string;
  languages: { code: string; label: string }[];
  versions: VersionEntry[];
}

// Split out from loadManifest() so tests can validate synthetic manifests without
// touching the real ../../versions.json import (which loadManifest() is hardwired to).
export function validateManifest(m: Manifest): Manifest {
  if (m.schema_version !== 2) throw new Error(`versions.json schema_version ${m.schema_version} != 2`);
  const slugs = new Set<string>();
  for (const v of m.versions) {
    if (!/^DOCS_[A-Z0-9_]+$/.test(v.binding)) throw new Error(`bad binding for ${v.slug}`);
    if (!["dev", "lts", "eol"].includes(v.status)) throw new Error(`bad status for ${v.slug}`);
    if (slugs.has(v.slug)) throw new Error(`duplicate slug: ${v.slug}`);
    // pdf_r2_key names an R2 fallback for the exact `pdf` URL — a null pdf has no URL
    // for the fallback to ever be reached at, so the pairing is nonsensical.
    if (v.pdf_r2_key && v.pdf === null) throw new Error(`pdf_r2_key set but pdf is null for ${v.slug}`);
    slugs.add(v.slug);
  }
  // Separate pass: an alias must not collide with ANY canonical slug (not just an
  // earlier one), so `slugs` must be fully populated before this check runs.
  const aliases = new Set<string>();
  for (const v of m.versions) {
    for (const alias of v.aliases) {
      if (slugs.has(alias)) throw new Error(`alias ${alias} (on ${v.slug}) collides with a canonical slug`);
      if (aliases.has(alias)) throw new Error(`duplicate alias: ${alias}`);
      aliases.add(alias);
    }
  }
  if (!m.versions.some((v) => v.slug === m.default_version))
    throw new Error(`default_version ${m.default_version} not in versions[]`);
  return m;
}

export function loadManifest(): Manifest {
  return validateManifest(raw as Manifest);
}

export function buildDispatch(m: Manifest): Map<string, string> {
  return new Map(m.versions.map((v) => [v.slug, v.binding]));
}