···1414 */
15151616import type * as PubLeafletDocument from "../api/types/pub/leaflet/document";
1717-import type * as PubLeafletPublication from "../api/types/pub/leaflet/publication";
1717+import * as PubLeafletPublication from "../api/types/pub/leaflet/publication";
1818import type * as PubLeafletContent from "../api/types/pub/leaflet/content";
1919import type * as SiteStandardDocument from "../api/types/site/standard/document";
2020import type * as SiteStandardPublication from "../api/types/site/standard/publication";
···3131};
32323333// Normalized publication type - uses the generated site.standard.publication type
3434-export type NormalizedPublication = SiteStandardPublication.Record;
3434+// with the theme narrowed to only the valid pub.leaflet.publication#theme type
3535+// (isTheme validates that $type is present, so we use $Typed)
3636+// Note: We explicitly list fields rather than using Omit because the generated Record type
3737+// has an index signature [k: string]: unknown that interferes with property typing
3838+export type NormalizedPublication = {
3939+ $type: "site.standard.publication";
4040+ name: string;
4141+ url: string;
4242+ description?: string;
4343+ icon?: SiteStandardPublication.Record["icon"];
4444+ basicTheme?: SiteStandardThemeBasic.Main;
4545+ theme?: $Typed<PubLeafletPublication.Theme>;
4646+ preferences?: SiteStandardPublication.Preferences;
4747+};
35483649/**
3750 * Checks if the record is a pub.leaflet.document
···210223): NormalizedPublication | null {
211224 if (!record || typeof record !== "object") return null;
212225213213- // Pass through site.standard records directly
226226+ // Pass through site.standard records directly, but validate the theme
214227 if (isStandardPublication(record)) {
215215- return record;
228228+ // Validate theme - only keep if it's a valid pub.leaflet.publication#theme
229229+ const theme = PubLeafletPublication.isTheme(record.theme)
230230+ ? (record.theme as $Typed<PubLeafletPublication.Theme>)
231231+ : undefined;
232232+ return {
233233+ ...record,
234234+ theme,
235235+ };
216236 }
217237218238 if (isLeafletPublication(record)) {
···225245226246 const basicTheme = leafletThemeToBasicTheme(record.theme);
227247248248+ // Validate theme - only keep if it's a valid pub.leaflet.publication#theme with $type set
249249+ // For legacy records without $type, add it during normalization
250250+ let theme: $Typed<PubLeafletPublication.Theme> | undefined;
251251+ if (record.theme) {
252252+ if (PubLeafletPublication.isTheme(record.theme)) {
253253+ theme = record.theme as $Typed<PubLeafletPublication.Theme>;
254254+ } else {
255255+ // Legacy theme without $type - add it
256256+ theme = {
257257+ ...record.theme,
258258+ $type: "pub.leaflet.publication#theme",
259259+ };
260260+ }
261261+ }
262262+228263 // Convert preferences to site.standard format (strip/replace $type)
229264 const preferences: SiteStandardPublication.Preferences | undefined =
230265 record.preferences
···243278 description: record.description,
244279 icon: record.icon,
245280 basicTheme,
246246- theme: record.theme,
281281+ theme,
247282 preferences,
248283 };
249284 }