SPEC — Système de Visibilité Starlight
SPEC — Système de Visibilité Starlight
Spécification technique détaillée
Cycle : DOC → SPEC → CODE → TEST → INTÉG
Composant : Système de Visibilité Starlight
Fichier :docs/L2_Presentation/astro/starlight/SPEC-Starlight-Visibility.md
Statut : SPEC
1. Périmètre et Objectifs
1.1. Objectifs
- Spécifier le schéma Zod pour le champ
visibility - Définir la configuration content.config.ts
- Spécifier les modifications du middleware
- Définir la stratégie de migration des documents existants
- Spécifier les tests de validation
1.2. Contraintes
- Secure by default : tous les documents privés par défaut
- Utiliser Zod pour validation et typage TypeScript
- Intégration transparente avec Starlight
- Aucun impact sur les documents sans champ
visibility
2. Spécification Schéma Zod
2.1. Extension Schema Starlight
Configuration content.config.ts :
import { defineCollection } from 'astro:content';import { docsLoader, i18nLoader } from '@astrojs/starlight/loaders';import { docsSchema, i18nSchema } from '@astrojs/starlight/schema';import { z } from 'astro/zod';
const docs = defineCollection({ loader: docsLoader(), schema: docsSchema({ extend: z.object({ // Champ de visibilité : public ou private (par défaut) visibility: z.enum(['public', 'private']).default('private'), }), }),});
export const collections = { docs, i18n: defineCollection({ loader: i18nLoader(), schema: i18nSchema() }),};2.2. Interface TypeScript Générée
Interface générée automatiquement par Astro :
interface DocsEntry { id: string; slug: string; body: string; collection: 'docs'; data: { title: string; description: string; visibility: 'public' | 'private'; // Nouveau champ // ... autres champs Starlight };}2.3. Validation Zod
Règles de validation :
visibilitydoit être'public'ou'private'- Valeur par défaut :
'private' - Si champ absent :
'private'(secure by default) - Si valeur invalide : erreur de validation Zod
3. Spécification Frontmatter
3.1. Format Frontmatter1/3
Document public :
---title: "Guide de Démarrage"description: "Guide pour les nouveaux développeurs"visibility: "public"---
Contenu du guide...3.1. Format Frontmatter2/3
Document privé (valeur par défaut) :
---title: "Architecture Interne"description: "Documentation technique sensible"visibility: "private"---
Contenu technique sensible...3.1. Format Frontmatter3/3
Document sans champ (privé par défaut) :
---title: "Document Privé"description: "Ce document est privé par défaut"---
Contenu...3.2. Stratégie de Visibilité
Règle 1 : Par défaut, tous les documents sont privés
Règle 2 : Explicitement marquer les documents publics
Règle 3 : overview est public via override middleware
Règle 4 : Audit régulier des documents publics
4. Spécification Middleware
4.1. Logique Middleware Actuelle
Fichier : apps/web/src/middleware.ts
Logique existante :
let isPrivate = true;
try { const entry = await getEntry('docs', docId); if (entry && entry.data) { const data = entry.data as { visibility?: 'public' | 'private' }; if (data.visibility === 'public') { isPrivate = false; } }} catch (_e) { isPrivate = true;}
// Override pour overviewif (docId === 'overview') { isPrivate = false;}4.2. Modification Requise1/2
Amélioration de la logique :
4.2. Modification Requise2/2
let isPrivate = true;
try { const entry = await getEntry('docs', docId); if (entry && entry.data) { const data = entry.data as { visibility?: 'public' | 'private' }; // Si visibility est explicitement 'public', document public if (data.visibility === 'public') { isPrivate = false; } }} catch (_e) { // En cas d'erreur de lecture, considérer comme privé (secure by default) isPrivate = true;}
// Override explicite pour overview (public par défaut)if (docId === 'overview') { isPrivate = false;}
// Pour les documents privés, vérifier l'authentificationif (isPrivate) { const sessionToken = context.cookies.get('auth_session')?.value; if (!sessionToken) { return context.redirect(`/login?redirectTo=${encodeURIComponent(pathname)}`); }
const isAuthValid = await verifyAuthSession(sessionToken); if (!isAuthValid.valid) { return context.redirect(`/login?redirectTo=${encodeURIComponent(pathname)}`); }
context.locals.user = isAuthValid.user;}4.3. Interface TypeScript
Déclaration types :
declare namespace App { interface Locals { user?: { id: string; email: string; role: string; } | null; isDocPrivate?: boolean; }}5. Spécification Configuration Starlight
5.1. Configuration Sidebar
Fichier : apps/web/astro.config.mjs
Configuration actuelle (déjà correcte) :
sidebar: [ { label: 'Getting Started', items: [{ autogenerate: { directory: 'gettingStarted' } }], }, { label: 'Documentation Technique', items: [{ autogenerate: { directory: 'docs' } }], },],Note : La configuration existante est déjà correcte avec les répertoires gettingStarted et docs.
5.2. Comportement Sidebar1/2
Mise à jour (Octobre 2026) : Voir la spécification dédiée SPEC-Starlight-Dual-Sidebar-Algolia.md et l’ADR ADR-Starlight-Dual-Sidebar-Algolia.md.
Règles initiales v1.0 :
- Sidebar affiche TOUS les documents (publics et privés)
- Navigation fonctionne pour tous les documents
- Accès aux documents privés redirige vers login
5.2. Comportement Sidebar2/2
Nouvelles règles bimodales v2.0 (SPEC-Starlight-Dual-Sidebar-Algolia) :
- Visiteur public (non connecté) : Sidebar filtrée dynamiquement via
routeMiddlewarepour afficher uniquement les documents publics (visibility: 'public'). Élagage des groupes vides et de la pagination. - Membre authentifié (connecté) : Sidebar complète affichant documents publics et privés, avec ajout de badges visuels « Privé ».
6. Spécification Migration Documents
6.1. Stratégie de Migration
Approche progressive :
- Aucune modification requise pour les documents privés (défaut)
- Ajouter
visibility: "public"aux documents devant être publics overviewreste public via override middleware
6.2. Documents Candidats Publics1/2
Critères pour document public :
- Documentation Getting Started générale
- Guides de démarrage non sensibles
- Documentation API publique
- Overview et pages d’accueil
6.2. Documents Candidats Publics2/2
Critères pour document privé :
- Architecture interne
- Spécifications techniques sensibles
- Configuration et secrets
- Documentation de développement interne
6.3. Script de Migration (Optionnel)
Script pour marquer des documents comme publics :
# Marquer tous les docs d'un répertoire comme publicsfind src/content/docs/gettingStarted -name "*.md" -exec sed -i '2a visibility: "public"' {} \;Note : Utiliser avec précaution, vérifier manuellement après exécution.
7. Spécification Tests
7.1. Tests Schéma1/2
Test validation Zod :
7.1. Tests Schéma2/2
describe('Visibility Schema Validation', () => { it('should accept "public" value', () => { const result = docsSchema.parse({ title: 'Test', description: 'Test', visibility: 'public' }); expect(result.visibility).toBe('public'); });
it('should accept "private" value', () => { const result = docsSchema.parse({ title: 'Test', description: 'Test', visibility: 'private' }); expect(result.visibility).toBe('private'); });
it('should default to "private" when omitted', () => { const result = docsSchema.parse({ title: 'Test', description: 'Test' }); expect(result.visibility).toBe('private'); });
it('should reject invalid values', () => { expect(() => { docsSchema.parse({ title: 'Test', description: 'Test', visibility: 'invalid' }); }).toThrow(); });});7.2. Tests Middleware
Test protection documents privés :
describe('Middleware Visibility', () => { it('should allow access to public documents without auth', async () => { const response = await fetch('/docs/public-doc'); expect(response.status).toBe(200); });
it('should redirect to login for private documents without auth', async () => { const response = await fetch('/docs/private-doc'); expect(response.status).toBe(302); expect(response.headers.get('location')).toContain('/login'); });
it('should allow access to private documents with valid auth', async () => { const response = await fetch('/docs/private-doc', { headers: { Cookie: 'auth_session=valid-token' } }); expect(response.status).toBe(200); });
it('should treat overview as public', async () => { const response = await fetch('/overview'); expect(response.status).toBe(200); });});7.3. Tests Intégration Starlight
Test sidebar et navigation :
describe('Starlight Integration', () => { it('should display all documents in sidebar', async () => { const response = await fetch('/docs'); const html = await response.text(); expect(html).toContain('Getting Started'); expect(html).toContain('Documentation Technique'); });
it('should generate correct TypeScript types', async () => { // Vérifier que le typecheck passe const result = await exec('pnpm typecheck'); expect(result.exitCode).toBe(0); });});8. Spécification Performance
8.1. Impact Performance
Impact minimal :
- Lecture frontmatter : négligeable (déjà fait par Starlight)
- Middleware : vérification simple du champ
visibility - Aucun impact sur le build statique
8.2. Optimisation
Cache middleware :
- Les résultats
getEntrysont cachés par Astro - Aucune optimisation supplémentaire requise
9. Spécification Sécurité
9.1. Secure by Default
Principe :
- Valeur par défaut :
'private' - En cas d’erreur : considérer comme privé
- Override explicite uniquement pour
overview
9.2. Validation
Validation Zod :
- Empêche les valeurs invalides
- Typage TypeScript automatique
- Erreurs de build si valeurs invalides
10. Critères de Validation
10.1. Validation Technique
- Schéma Zod configuré dans content.config.ts
- Middleware modifié pour lire
visibility - TypeScript types générés correctement
- Build réussi sans erreurs
10.2. Validation Fonctionnelle
- Documents publics accessibles sans auth
- Documents privés redirigent vers login
- Overview accessible sans auth
- Sidebar affiche tous les documents
- Navigation fonctionne correctement
10.3. Validation Sécurité
- Par défaut privé (secure by default)
- Erreur de lecture → document privé
- Validation Zod empêche valeurs invalides
- Aucun document sensible public par accident
11. Documentation Utilisateur
11.1. Guide pour Éditeurs1/2
Comment marquer un document public :
---title: "Mon Document Public"description: "Description"visibility: "public"---
Contenu...11.1. Guide pour Éditeurs2/2
Comment garder un document privé :
---title: "Mon Document Privé"description: "Description"# visibility: "private" # Optionnel, c'est le défaut---
Contenu...11.2. Checklist Publication
Avant de publier un document :
- Le contenu est-il sensible ?
- Si oui, ne PAS ajouter
visibility: "public" - Si non, ajouter
visibility: "public" - Vérifier que le document s’affiche correctement
12. Historique des Modifications
| Date | Version | Description | Auteur |
|---|---|---|---|
| 2026-09-27 | 1.0 | Création initiale SPEC Système Visibilité | Team (Agent) |