608 lines
16 KiB
TypeScript
608 lines
16 KiB
TypeScript
import type { CollectionEntry } from "astro:content";
|
|
|
|
export type GlossaryEntry = CollectionEntry<"glossaire">;
|
|
|
|
export type GlossaryPortalLink = {
|
|
href: string;
|
|
label: string;
|
|
};
|
|
|
|
export type GlossaryRelationSection = {
|
|
title: string;
|
|
items: GlossaryEntry[];
|
|
};
|
|
|
|
export type GlossaryRelationBlock = GlossaryRelationSection & {
|
|
className: string;
|
|
};
|
|
|
|
export type GlossaryHomeStats = {
|
|
totalEntries: number;
|
|
paradigmesCount: number;
|
|
doctrinesCount: number;
|
|
metaRegimesCount: number;
|
|
};
|
|
|
|
export type GlossaryEntryAsideData = {
|
|
displayFamily: string;
|
|
displayDomain: string;
|
|
displayLevel: string;
|
|
showNoyau: boolean;
|
|
showSameFamily: boolean;
|
|
fondamentaux: GlossaryEntry[];
|
|
sameFamilyTitle: string;
|
|
sameFamilyEntries: GlossaryEntry[];
|
|
relationSections: GlossaryRelationSection[];
|
|
contextualTheory: GlossaryEntry[];
|
|
};
|
|
|
|
export type GlossaryHomeData = {
|
|
fondamentaux: GlossaryEntry[];
|
|
scenes: GlossaryEntry[];
|
|
dynamiques: GlossaryEntry[];
|
|
metaRegimes: GlossaryEntry[];
|
|
metaRegimesPreview: GlossaryEntry[];
|
|
arcalite?: GlossaryEntry;
|
|
cratialite?: GlossaryEntry;
|
|
tension?: GlossaryEntry;
|
|
sceneDepreuve?: GlossaryEntry;
|
|
archicration?: GlossaryEntry;
|
|
};
|
|
|
|
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",
|
|
};
|
|
|
|
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 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 slugsOfGlossaryEntries(
|
|
entries: GlossaryEntry[] = [],
|
|
): Set<string> {
|
|
const slugs = new Set<string>();
|
|
|
|
for (const entry of entries) {
|
|
const slug = slugOfGlossaryEntry(entry);
|
|
if (!slug) continue;
|
|
slugs.add(slug);
|
|
}
|
|
|
|
return slugs;
|
|
}
|
|
|
|
export function excludeGlossaryEntries(
|
|
entries: GlossaryEntry[] = [],
|
|
excluded: Iterable<string> = [],
|
|
): GlossaryEntry[] {
|
|
const excludedSlugs = new Set(
|
|
Array.from(excluded)
|
|
.map((value) => normalizeGlossarySlug(value))
|
|
.filter(Boolean),
|
|
);
|
|
|
|
return entries.filter((entry) => {
|
|
const slug = slugOfGlossaryEntry(entry);
|
|
return Boolean(slug) && !excludedSlugs.has(slug);
|
|
});
|
|
}
|
|
|
|
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 getGlossaryPortalLinks(): GlossaryPortalLink[] {
|
|
return [
|
|
{ href: "/glossaire/", label: "Accueil du glossaire" },
|
|
{ href: "/glossaire/concepts-fondamentaux/", label: "Concepts fondamentaux" },
|
|
{ href: "/glossaire/index-complet/", label: "Index complet" },
|
|
{
|
|
href: "/glossaire/paradigme-archicratique/",
|
|
label: "Paradigme archicratique",
|
|
},
|
|
{
|
|
href: "/glossaire/scenes-archicratiques/",
|
|
label: "Scènes archicratiques",
|
|
},
|
|
{
|
|
href: "/glossaire/dynamiques-archicratiques/",
|
|
label: "Dynamiques archicratiques",
|
|
},
|
|
{
|
|
href: "/glossaire/tensions-irreductibles/",
|
|
label: "Tensions irréductibles",
|
|
},
|
|
{
|
|
href: "/glossaire/archicrations/",
|
|
label: "Méta-régimes archicratiques",
|
|
},
|
|
{ href: "/glossaire/paradigmes/", label: "Paradigmes et doctrines" },
|
|
{ href: "/glossaire/verbes-de-la-scene/", label: "Verbes de la scène" },
|
|
];
|
|
}
|
|
|
|
export function getGlossaryHomeStats(
|
|
allEntries: GlossaryEntry[] = [],
|
|
): GlossaryHomeStats {
|
|
return {
|
|
totalEntries: allEntries.length,
|
|
paradigmesCount: countGlossaryEntriesByKind(allEntries, "paradigme"),
|
|
doctrinesCount: countGlossaryEntriesByKind(allEntries, "doctrine"),
|
|
metaRegimesCount: countGlossaryEntriesByFamily(allEntries, "meta-regime"),
|
|
};
|
|
}
|
|
|
|
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 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[] = [],
|
|
): GlossaryRelationSection[] {
|
|
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"
|
|
);
|
|
}
|
|
|
|
export function getContextualTheory(
|
|
entry: GlossaryEntry,
|
|
allEntries: GlossaryEntry[] = [],
|
|
): GlossaryEntry[] {
|
|
const currentSlug = slugOfGlossaryEntry(entry);
|
|
const bySlug = buildGlossaryBySlug(allEntries);
|
|
|
|
const fromRelations = uniqueGlossaryEntries([
|
|
...resolveGlossaryEntriesInSourceOrder(entry.data.related ?? [], allEntries),
|
|
...resolveGlossaryEntriesInSourceOrder(entry.data.seeAlso ?? [], allEntries),
|
|
...resolveGlossaryEntriesInSourceOrder(entry.data.opposedTo ?? [], allEntries),
|
|
])
|
|
.filter((item) => slugOfGlossaryEntry(item) !== currentSlug)
|
|
.filter((item) => isTheoryEntry(item));
|
|
|
|
if (fromRelations.length > 0) {
|
|
return 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 [];
|
|
}
|
|
|
|
export function getGlossaryEntryAsideData(
|
|
currentEntry: GlossaryEntry,
|
|
allEntries: GlossaryEntry[] = [],
|
|
): GlossaryEntryAsideData {
|
|
const currentFamily = familyOf(currentEntry);
|
|
const currentSlug = slugOfGlossaryEntry(currentEntry);
|
|
|
|
const fondamentaux = getFondamentaux(allEntries);
|
|
const sameFamilyTitle = getSameFamilyTitle(currentEntry);
|
|
const relationSections = getRelationSections(currentEntry, allEntries);
|
|
|
|
const relationEntries = uniqueGlossaryEntries(
|
|
relationSections.flatMap((section) => section.items),
|
|
);
|
|
const relationSlugs = slugsOfGlossaryEntries(relationEntries);
|
|
|
|
const contextualTheory = excludeGlossaryEntries(
|
|
getContextualTheory(currentEntry, allEntries),
|
|
new Set([currentSlug, ...relationSlugs]),
|
|
).slice(0, 6);
|
|
const contextualTheorySlugs = slugsOfGlossaryEntries(contextualTheory);
|
|
|
|
const sameFamilyEntries = excludeGlossaryEntries(
|
|
getEntriesOfSameFamily(currentEntry, allEntries),
|
|
new Set([currentSlug, ...relationSlugs, ...contextualTheorySlugs]),
|
|
).slice(0, 8);
|
|
|
|
const showNoyau =
|
|
currentFamily !== "concept-fondamental" &&
|
|
fondamentaux.length > 0;
|
|
|
|
const showSameFamily =
|
|
currentFamily !== "concept-fondamental" &&
|
|
sameFamilyEntries.length > 0;
|
|
|
|
return {
|
|
displayFamily: getDisplayFamily(currentEntry),
|
|
displayDomain: getDisplayDomain(currentEntry),
|
|
displayLevel: getDisplayLevel(currentEntry),
|
|
showNoyau,
|
|
showSameFamily,
|
|
fondamentaux,
|
|
sameFamilyTitle,
|
|
sameFamilyEntries: showSameFamily ? sameFamilyEntries : [],
|
|
relationSections,
|
|
contextualTheory,
|
|
};
|
|
}
|
|
|
|
export function getGlossaryHomeData(
|
|
entries: GlossaryEntry[] = [],
|
|
): GlossaryHomeData {
|
|
const bySlug = buildGlossaryBySlug(entries);
|
|
|
|
const fondamentaux = getGlossaryEntriesByFamily(entries, "concept-fondamental");
|
|
const scenes = getGlossaryEntriesByFamily(entries, "scene");
|
|
const dynamiques = sortGlossaryEntries(
|
|
entries.filter((entry) =>
|
|
["dynamique", "pathologie"].includes(familyOf(entry)),
|
|
),
|
|
);
|
|
const metaRegimes = getGlossaryEntriesByFamily(entries, "meta-regime");
|
|
|
|
return {
|
|
fondamentaux,
|
|
scenes,
|
|
dynamiques,
|
|
metaRegimes,
|
|
metaRegimesPreview: metaRegimes.slice(0, 6),
|
|
arcalite: bySlug.get("arcalite"),
|
|
cratialite: bySlug.get("cratialite"),
|
|
tension: bySlug.get("tension"),
|
|
sceneDepreuve: bySlug.get("scene-depreuve"),
|
|
archicration: bySlug.get("archicration"),
|
|
};
|
|
}
|