payloads.ts (1939B)
1 import { getCollection, type CollectionEntry } from 'astro:content'; 2 3 export type Payload = CollectionEntry<'payloads'>; 4 5 export async function allPayloads(): Promise<Payload[]> { 6 return await getCollection('payloads'); 7 } 8 9 /** Route (relative to base) for a payload entry. README -> topic root. */ 10 export function payloadHref(entry: Payload): string { 11 const id = entry.id.replace(/\/index$/, ''); 12 return `payloads/${id}`; 13 } 14 15 export interface TopicGroup { 16 slug: string; 17 title: string; 18 readme?: Payload; 19 items: Payload[]; // sub-pages (excludes readme), alpha 20 count: number; // total pages incl. readme 21 } 22 23 /** Payloads grouped by topic, topics alpha, readme first inside a topic. */ 24 export async function payloadsByTopic(): Promise<TopicGroup[]> { 25 const all = await allPayloads(); 26 const map = new Map<string, Payload[]>(); 27 for (const p of all) { 28 const arr = map.get(p.data.topicSlug) ?? []; 29 arr.push(p); 30 map.set(p.data.topicSlug, arr); 31 } 32 const groups: TopicGroup[] = []; 33 for (const [slug, entries] of map) { 34 const readme = entries.find((e) => e.data.isReadme); 35 const items = entries 36 .filter((e) => !e.data.isReadme) 37 .sort((a, b) => a.data.title.localeCompare(b.data.title)); 38 groups.push({ 39 slug, 40 title: readme?.data.topic ?? entries[0].data.topic, 41 readme, 42 items, 43 count: entries.length, 44 }); 45 } 46 return groups.sort((a, b) => a.title.localeCompare(b.title)); 47 } 48 49 /** Ordered pages within a topic (readme first) for prev/next. */ 50 export function topicOrder(group: TopicGroup): Payload[] { 51 return [...(group.readme ? [group.readme] : []), ...group.items]; 52 } 53 54 export function prevNext(order: Payload[], current: Payload): { prev?: Payload; next?: Payload } { 55 const i = order.findIndex((p) => p.id === current.id); 56 return { 57 prev: i > 0 ? order[i - 1] : undefined, 58 next: i >= 0 && i < order.length - 1 ? order[i + 1] : undefined, 59 }; 60 }