export type TechLogServingContract = Readonly<{ schemaVersion: 1; publicSpaPaths: readonly string[]; studioPathPrefix: "/studio"; studioSpaPathPatterns: readonly string[]; notFound: Readonly<{ status: 404; contentType: "text/plain;charset=UTF-8"; body: "Not Found"; }>; }>; type ServingContractInput = Readonly<{ publicRecords: readonly Readonly<{ path: string; topicSlug: string; }>[]; projects: readonly Readonly<{ slug: string }>[]; releases: readonly Readonly<{ path: string }>[]; }>; const staticPublicPaths = Object.freeze([ "/", "/explore", "/explore/cases", "/explore/questions", "/explore/references", "/profile", "/projects", "/releases", "/search", ]); const studioSpaPathPatterns = Object.freeze([ "^/studio$", // The Asset Library is a first-class Studio route (TECH_LOG_STUDIO_ASSETS in // the route contract) but was never listed here, so a hard navigation or a // reload of /studio/assets was served the in-shell Studio 404 -- the screen // was only reachable by client-side navigation from another Studio page. "^/studio/assets$", "^/studio/documents$", "^/studio/documents/new$", "^/studio/documents/[^/]+/(edit|validation|preview|publish)$", "^/studio/publications$", "^/studio/publications/[^/]+/preview$", ]); function asciiCompare(left: string, right: string): number { return left < right ? -1 : left > right ? 1 : 0; } export function createTechLogServingContract({ publicRecords, projects, releases, }: ServingContractInput): TechLogServingContract { const publicSpaPaths = new Set(staticPublicPaths); for (const record of publicRecords) { publicSpaPaths.add(record.path); publicSpaPaths.add(`/topics/${record.topicSlug}`); } for (const project of projects) { const projectPath = `/projects/${project.slug}`; publicSpaPaths.add(projectPath); publicSpaPaths.add(`${projectPath}/activity`); publicSpaPaths.add(`${projectPath}/decisions`); publicSpaPaths.add(`${projectPath}/records`); } for (const release of releases) publicSpaPaths.add(release.path); return Object.freeze({ schemaVersion: 1, publicSpaPaths: Object.freeze([...publicSpaPaths].sort(asciiCompare)), studioPathPrefix: "/studio", studioSpaPathPatterns, notFound: Object.freeze({ status: 404, contentType: "text/plain;charset=UTF-8", body: "Not Found", }), }); }