Mirrors the GitLab pipeline — typecheck, tests, feed sanity — and adds a Pages deploy. Pages serves from /<repo>/, and the app used absolute asset paths, so that job would have shipped a site that 404s on everything. Assets now resolve against a <base href> the build substitutes, the feed URL resolves against document.baseURI so deep links work too, and the service worker derives its paths from its own registration scope. Root-hosted builds are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
38 lines
1.3 KiB
TypeScript
38 lines
1.3 KiB
TypeScript
import { EventFeed, SCHEMA_VERSION } from "../shared/feed.ts";
|
|
|
|
export type FeedState =
|
|
| { status: "loading" }
|
|
| { status: "ready"; feed: EventFeed }
|
|
| { status: "error"; message: string };
|
|
|
|
/**
|
|
* Fetch the published feed.
|
|
*
|
|
* A `schemaVersion` we do not recognise is refused rather than rendered — the
|
|
* client would be guessing at unfamiliar fields, and a calendar that guesses is
|
|
* worse than one that asks you to reload.
|
|
*/
|
|
export async function fetchFeed(signal?: AbortSignal): Promise<EventFeed> {
|
|
// Resolved against <base>, so this is correct at a domain root, under a
|
|
// GitHub Pages subpath, and on a deep link alike.
|
|
const feedUrl = new URL("data/events.v1.json", document.baseURI);
|
|
const res = await fetch(feedUrl, { signal: signal ?? null });
|
|
if (!res.ok) {
|
|
throw new Error(`Feed request failed (${res.status}).`);
|
|
}
|
|
|
|
const json: unknown = await res.json();
|
|
const version = (json as { schemaVersion?: number }).schemaVersion;
|
|
if (version !== SCHEMA_VERSION) {
|
|
throw new Error(
|
|
`This page expects feed v${SCHEMA_VERSION} but the server sent v${String(version)}. Reload to get the current app.`,
|
|
);
|
|
}
|
|
|
|
const parsed = EventFeed.safeParse(json);
|
|
if (!parsed.success) {
|
|
throw new Error("The feed did not match the expected shape.");
|
|
}
|
|
return parsed.data;
|
|
}
|