jevstrudel.git / website / src / jev / releases.mjs

Release notes: RELEASES.md at the repo root, newest first, one ## heading per deploy:

2026-09-24.2 — Jev arranges the whole song

  • a line a listener would care about

The heading's first word is the release's version. The site embeds its own version (ReplPage.astro, meta jev-release) and publishes every note (/release-notes.json). The release notes modal lists every release up to the one it is about, each a heading that expands: when a newer deploy is waiting, the ones newer than the page's own are expanded; after a return visit, the ones not seen yet; opened from the header's version, the newest. The deploy refuses to publish a site whose newest note is already live (tools/deploy/).

14export function parseReleases(text) {
15  const releases = [];
16  for (const block of text.split(/^## /m).slice(1)) {
17    const [heading, ...lines] = block.split('\n');
18    const [, version, title] = heading.match(/^(\S+)\s*(?:[—-]\s*(.*))?$/) ?? [];
19    if (!version) continue;
20    const notes = lines
21      .filter((l) => /^\s*- /.test(l))
22      .map((l) => l.replace(/^\s*- /, '').trim())
23      .filter(Boolean);
24    releases.push({ version, title: title?.trim() ?? '', notes });
25  }
26  return releases;
27}

The releases a page on version has not seen, newest first; all but the newest are dropped when the page's version is unknown.

31export function newerThan(releases, version) {
32  const i = releases.findIndex((r) => r.version === version);
33  return i === -1 ? releases.slice(0, 1) : releases.slice(0, i);
34}

What the release notes modal lists: every release up to and including upTo (all of them when it is unknown), newest first, each with open when it is one of open's (or, if none of those is listed, the newest).

39export function releaseList(releases, upTo, open) {
40  const from = releases.findIndex((r) => r.version === upTo);
41  const listed = from === -1 ? releases : releases.slice(from);
42  const opened = new Set(open.map((r) => r.version));
43  if (!listed.some((r) => opened.has(r.version)) && listed[0]) opened.add(listed[0].version);
44  return listed.map((r) => ({ ...r, open: opened.has(r.version) }));
45}