Files
archicratie-edition/src/lib/glossary.ts

416 lines
11 KiB
TypeScript

import type { CollectionEntry } from "astro:content";
export type GlossaryEntry = CollectionEntry<"glossaire">;
export const GLOSSARY_COLLATOR = new Intl.Collator("fr", {
sensitivity: "base",
numeric: true,
});
export const FAMILY_LABELS: Record<string, string> = {
"concept-fondamental": "Concept fondamental",
scene: "Scène",
dynamique: "Dynamique",
pathologie: "Pathologie",
topologie: "Topologie",
"meta-regime": "Méta-régime",
paradigme: "Paradigme",
doctrine: "Doctrine",
verbe: "Verbe",
"dispositif-ia": "Dispositif IA",
"tension-irreductible": "Tension irréductible",
figure: "Figure",
qualification: "Qualification",
epistemologie: "Épistémologie",
};
export const KIND_LABELS: Record<string, string> = {
concept: "Concept",
diagnostic: "Diagnostic",
topologie: "Topologie",
verbe: "Verbe",
paradigme: "Paradigme",
doctrine: "Doctrine",
dispositif: "Dispositif",
figure: "Figure",
qualification: "Qualification",
epistemologie: "Épistémologie",
};
export const DOMAIN_LABELS: Record<string, string> = {
transversal: "Transversal",
theorie: "Théorie",
"cas-ia": "Cas IA",
};
export const LEVEL_LABELS: Record<string, string> = {
fondamental: "Fondamental",
intermediaire: "Intermédiaire",
avance: "Avancé",
};
export const FONDAMENTAUX_WANTED = [
"archicratie",
"arcalite",
"cratialite",
"archicration",
"co-viabilite",
"tension",
] as const;
export const FAMILY_SECTION_TITLES: Record<string, string> = {
"concept-fondamental": "Noyau archicratique",
scene: "Scènes archicratiques",
dynamique: "Dynamiques archicratiques",
pathologie: "Pathologies archicratiques",
topologie: "Topologies voisines",
"meta-regime": "Méta-régimes archicratiques",
paradigme: "Paradigmes voisins",
doctrine: "Doctrines fondatrices",
verbe: "Verbes de la scène",
"dispositif-ia": "Dispositifs IA",
"tension-irreductible": "Tensions irréductibles",
figure: "Figures archicratiques",
qualification: "Qualifications archicratiques",
epistemologie: "Outillage épistémologique",
};
export function normalizeGlossarySlug(value: unknown): string {
return String(value ?? "")
.trim()
.replace(/^\/+|\/+$/g, "")
.replace(/\.(md|mdx)$/i, "")
.toLowerCase();
}
export function slugOfGlossaryEntry(
entry: Pick<GlossaryEntry, "id"> | null | undefined,
): string {
return normalizeGlossarySlug(entry?.id ?? "");
}
export function hrefOfGlossaryEntry(
entry: Pick<GlossaryEntry, "id"> | null | undefined,
): string {
const slug = slugOfGlossaryEntry(entry);
return slug ? `/glossaire/${slug}/` : "/glossaire/";
}
export function buildGlossaryBySlug(
entries: GlossaryEntry[] = [],
): Map<string, GlossaryEntry> {
return new Map(
entries.map((entry) => [slugOfGlossaryEntry(entry), entry]),
);
}
export function sortGlossaryEntries(
entries: GlossaryEntry[] = [],
): GlossaryEntry[] {
return [...entries].sort((a, b) =>
GLOSSARY_COLLATOR.compare(a.data.term, b.data.term),
);
}
export function uniqueGlossaryEntries(
entries: GlossaryEntry[] = [],
): GlossaryEntry[] {
const seen = new Set<string>();
const out: GlossaryEntry[] = [];
for (const entry of entries) {
const slug = slugOfGlossaryEntry(entry);
if (!slug || seen.has(slug)) continue;
seen.add(slug);
out.push(entry);
}
return out;
}
export function resolveGlossaryEntriesInSourceOrder(
slugs: string[] = [],
allEntries: GlossaryEntry[] = [],
): GlossaryEntry[] {
const bySlug = buildGlossaryBySlug(allEntries);
const seen = new Set<string>();
const resolved: GlossaryEntry[] = [];
for (const rawSlug of slugs) {
const slug = normalizeGlossarySlug(rawSlug);
if (!slug || seen.has(slug)) continue;
const entry = bySlug.get(slug);
if (!entry) continue;
seen.add(slug);
resolved.push(entry);
}
return resolved;
}
export function resolveGlossaryEntries(
slugs: string[] = [],
allEntries: GlossaryEntry[] = [],
): GlossaryEntry[] {
return sortGlossaryEntries(
resolveGlossaryEntriesInSourceOrder(slugs, allEntries),
);
}
export function rawFamilyOf(
entry: GlossaryEntry | null | undefined,
): string {
return String(entry?.data?.family ?? "");
}
export function kindOf(
entry: GlossaryEntry | null | undefined,
): string {
return String(entry?.data?.kind ?? "");
}
export function familyOf(
entry: GlossaryEntry | null | undefined,
): string {
const explicit = rawFamilyOf(entry);
if (explicit) return explicit;
const slug = slugOfGlossaryEntry(entry);
const kind = kindOf(entry);
if (kind === "paradigme") return "paradigme";
if (kind === "doctrine") return "doctrine";
if (kind === "verbe") return "verbe";
if (slug === "scene-depreuve") return "scene";
if (slug === "autarchicratie") return "pathologie";
if (slug === "obliteration-archicratique") return "dynamique";
if (FONDAMENTAUX_WANTED.includes(slug as (typeof FONDAMENTAUX_WANTED)[number])) {
return "concept-fondamental";
}
if (slug === "archicrations-differentielles-et-formes-hybrides") {
return "topologie";
}
if (kind === "topologie" && slug.startsWith("archicrations-")) {
return "meta-regime";
}
return "";
}
export function getDisplayFamily(
entry: GlossaryEntry | null | undefined,
): string {
const familyKey = rawFamilyOf(entry) || familyOf(entry);
return FAMILY_LABELS[familyKey] ?? KIND_LABELS[kindOf(entry)] ?? "Fiche";
}
export function getDisplayDomain(
entry: GlossaryEntry | null | undefined,
): string {
const key = String(entry?.data?.domain ?? "");
return key ? (DOMAIN_LABELS[key] ?? key) : "";
}
export function getDisplayLevel(
entry: GlossaryEntry | null | undefined,
): string {
const key = String(entry?.data?.level ?? "");
return key ? (LEVEL_LABELS[key] ?? key) : "";
}
export function getFondamentaux(
entries: GlossaryEntry[] = [],
): GlossaryEntry[] {
const bySlug = buildGlossaryBySlug(entries);
return sortGlossaryEntries(
FONDAMENTAUX_WANTED
.map((slug) => bySlug.get(slug))
.filter(Boolean) as GlossaryEntry[],
);
}
export function getGlossaryEntriesByFamily(
entries: GlossaryEntry[] = [],
familyKey: string,
): GlossaryEntry[] {
return sortGlossaryEntries(
entries.filter((entry) => familyOf(entry) === familyKey),
);
}
export function countGlossaryEntriesByKind(
entries: GlossaryEntry[] = [],
kindKey: string,
): number {
return entries.filter((entry) => kindOf(entry) === kindKey).length;
}
export function countGlossaryEntriesByFamily(
entries: GlossaryEntry[] = [],
familyKey: string,
): number {
return entries.filter((entry) => familyOf(entry) === familyKey).length;
}
export function getEntriesOfSameFamily(
entry: GlossaryEntry,
allEntries: GlossaryEntry[] = [],
): GlossaryEntry[] {
const familyKey = familyOf(entry);
if (!familyKey) return [];
if (familyKey === "concept-fondamental") {
return getFondamentaux(allEntries);
}
return getGlossaryEntriesByFamily(allEntries, familyKey);
}
export function getSameFamilyTitle(
entry: GlossaryEntry,
): string {
return FAMILY_SECTION_TITLES[familyOf(entry)] ?? "Même famille";
}
export type GlossaryRelationBlock = {
title: string;
items: GlossaryEntry[];
className: string;
};
export function getRelationBlocks(
entry: GlossaryEntry,
allEntries: GlossaryEntry[] = [],
): GlossaryRelationBlock[] {
const currentSlug = slugOfGlossaryEntry(entry);
const relatedEntries = resolveGlossaryEntriesInSourceOrder(
entry.data.related ?? [],
allEntries,
).filter((item) => slugOfGlossaryEntry(item) !== currentSlug);
const opposedEntries = resolveGlossaryEntriesInSourceOrder(
entry.data.opposedTo ?? [],
allEntries,
).filter((item) => slugOfGlossaryEntry(item) !== currentSlug);
const seeAlsoEntries = resolveGlossaryEntriesInSourceOrder(
entry.data.seeAlso ?? [],
allEntries,
).filter((item) => slugOfGlossaryEntry(item) !== currentSlug);
return [
{
title: "Concepts liés",
items: relatedEntries,
className: "is-related",
},
{
title: "En tension avec",
items: opposedEntries,
className: "is-opposed",
},
{
title: "Voir aussi",
items: seeAlsoEntries,
className: "is-see-also",
},
].filter((block) => block.items.length > 0);
}
export function getRelationSections(
entry: GlossaryEntry,
allEntries: GlossaryEntry[] = [],
): Array<{ title: string; items: GlossaryEntry[] }> {
return getRelationBlocks(entry, allEntries).map(({ title, items }) => ({
title,
items,
}));
}
function isTheoryEntry(entry: GlossaryEntry): boolean {
const familyKey = familyOf(entry);
const kindKey = kindOf(entry);
return (
familyKey === "paradigme" ||
familyKey === "doctrine" ||
kindKey === "paradigme" ||
kindKey === "doctrine"
);
}
const PREFERRED_PARADIGME_SLUGS = [
"gouvernementalite",
"gouvernementalite-algorithmique",
"cybernetique",
"biopolitique",
"domination-legale-rationnelle",
"democratie-deliberative",
"gouvernance-des-communs",
"agencement-machinique",
"pharmacologie-technique",
"preemption-algorithmique",
"dissensus-politique",
"lieu-vide-du-pouvoir",
"habitus-et-violence-symbolique",
"theorie-de-la-resonance",
"conatus-et-multitude",
"configuration-et-interdependance",
"technodiversite-et-cosmotechnie",
"grammatisation-et-proletarisation-cognitive",
] as const;
const PREFERRED_DOCTRINE_SLUGS = [
"contractualisme-hobbesien",
"droit-naturel-et-propriete",
"volonte-generale",
"decisionnisme-souverain",
] as const;
export function getContextualTheory(
entry: GlossaryEntry,
allEntries: GlossaryEntry[] = [],
): GlossaryEntry[] {
const currentSlug = slugOfGlossaryEntry(entry);
const bySlug = buildGlossaryBySlug(allEntries);
const fromRelations = uniqueGlossaryEntries([
...resolveGlossaryEntries(entry.data.related ?? [], allEntries),
...resolveGlossaryEntries(entry.data.seeAlso ?? [], allEntries),
...resolveGlossaryEntries(entry.data.opposedTo ?? [], allEntries),
])
.filter((item) => slugOfGlossaryEntry(item) !== currentSlug)
.filter((item) => isTheoryEntry(item));
if (fromRelations.length > 0) {
return sortGlossaryEntries(fromRelations).slice(0, 6);
}
if (familyOf(entry) === "paradigme") {
return uniqueGlossaryEntries(
PREFERRED_PARADIGME_SLUGS
.filter((slug) => slug !== currentSlug)
.map((slug) => bySlug.get(slug))
.filter(Boolean) as GlossaryEntry[],
).slice(0, 8);
}
if (familyOf(entry) === "doctrine") {
return uniqueGlossaryEntries(
PREFERRED_DOCTRINE_SLUGS
.filter((slug) => slug !== currentSlug)
.map((slug) => bySlug.get(slug))
.filter(Boolean) as GlossaryEntry[],
).slice(0, 6);
}
return [];
}