First cut

This commit is contained in:
kadil
2026-04-17 16:08:31 -05:00
parent d10105ac00
commit 4ee4cb8e7c
58 changed files with 3243 additions and 1 deletions

35
src/lib/site-bundle.ts Normal file
View File

@@ -0,0 +1,35 @@
import {
siteContextSchema,
eventsFileSchema,
sectionFileSchema,
type SiteContext,
type SectionFile,
type EventsFile,
} from '@dynamic-sites/shared';
export interface SiteBundle {
siteContext: SiteContext;
sections: SectionFile[];
events: EventsFile;
}
export function parseSiteBundle(
siteContextRaw: unknown,
eventsRaw: unknown,
sectionRaws: unknown[]
): SiteBundle {
const siteContext = siteContextSchema.parse(siteContextRaw);
const events = eventsFileSchema.parse(eventsRaw);
const sections: SectionFile[] = [];
for (const raw of sectionRaws) {
const result = sectionFileSchema.safeParse(raw);
if (result.success && result.data.visible) {
sections.push(result.data);
}
}
// Sort by order, then id as tiebreaker
sections.sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
return { siteContext, sections, events };
}

39
src/lib/site-data.ts Normal file
View File

@@ -0,0 +1,39 @@
import fs from 'node:fs';
import path from 'node:path';
import { parseSiteBundle, type SiteBundle } from './site-bundle.ts';
const REPO_ROOT = process.env.REPO_ROOT || '.';
const TTL = parseInt(process.env.SITE_DATA_TTL_MS || '2000', 10);
let cached: { data: SiteBundle; loadedAt: number } | null = null;
export function loadSiteData(): SiteBundle {
const now = Date.now();
if (cached && now - cached.loadedAt < TTL) {
return cached.data;
}
const siteContextRaw = JSON.parse(
fs.readFileSync(path.join(REPO_ROOT, 'site-context.json'), 'utf-8')
);
const eventsRaw = JSON.parse(
fs.readFileSync(path.join(REPO_ROOT, 'content/events.json'), 'utf-8')
);
const sectionsDir = path.join(REPO_ROOT, 'content/sections');
const sectionRaws: unknown[] = [];
if (fs.existsSync(sectionsDir)) {
for (const file of fs.readdirSync(sectionsDir).filter(f => f.endsWith('.json'))) {
try {
sectionRaws.push(JSON.parse(fs.readFileSync(path.join(sectionsDir, file), 'utf-8')));
} catch {
// skip invalid files
}
}
}
const data = parseSiteBundle(siteContextRaw, eventsRaw, sectionRaws);
cached = { data, loadedAt: now };
return data;
}