sorted, Timsort), mais comprendre ces algorithmes permet de raisonner sur les coûts et de réussir les épreuves.', exemple: 'def dichotomie(t, x):\n g, d = 0, len(t) - 1\n while g <= d:\n m = (g + d) // 2\n if t[m] == x:\n return m\n if t[m] < x:\n g = m + 1\n else:\n d = m - 1\n return -1' },
{ t: 'Récursivité', d: '', html: 'Une fonction récursive s’appelle elle-même sur un problème plus petit, jusqu’à un cas de base qui s’arrête. Elle exprime naturellement les problèmes à structure récursive : parcours d’arborescence de fichiers, tri fusion, calculs sur des arbres. Chaque appel occupe une place sur la pile d’appels : sans cas de base atteint, on obtient un dépassement (RecursionError en Python, limite par défaut de 1 000 appels). Certaines récursions recalculent plusieurs fois les mêmes valeurs (Fibonacci naïf en O(2ⁿ)) : la mémoïsation (functools.cache) ou une version itérative corrige le problème.' },
{ t: 'Structures de données linéaires', d: '', html: 'Une pile (LIFO, dernier entré premier sorti) sert à l’historique d’annulation, à la pile d’appels, à la vérification des parenthèses. Une file (FIFO) sert aux files d’attente : impressions, requêtes, mains levées dans l’ordre d’arrivée. En Python, une liste fait une bonne pile (append, pop), mais une mauvaise file (pop(0) en O(n)) : on utilise collections.deque. Une liste chaînée relie des maillons par des références : insertion en tête en O(1), accès au k-ième élément en O(k). Le dictionnaire (table de hachage) donne un accès moyen en O(1) par clé : c’est souvent la structure qui transforme un O(n²) en O(n).' },
{ t: 'Graphes et parcours', d: '', html: 'Un graphe est un ensemble de sommets reliés par des arêtes, éventuellement orientées et pondérées : routeurs et liens, pages web, dépendances entre tâches. On le représente par une matrice d’adjacence ou, plus économiquement, par des listes d’adjacence (un dictionnaire de voisins). Le parcours en largeur (BFS, avec une file) visite par distance croissante et trouve le plus court chemin en nombre d’arêtes ; le parcours en profondeur (DFS, avec une pile ou la récursivité) sert à détecter des cycles ou des composantes connexes.' },
{ t: 'Plus courts chemins : Dijkstra', d: '', html: 'L’algorithme de Dijkstra calcule les plus courts chemins depuis un sommet dans un graphe à poids positifs : il sélectionne à chaque étape le sommet non traité le plus proche et met à jour les distances de ses voisins. Avec une file de priorité (heapq), il s’exécute en O((V + E) log V). C’est exactement ce que fait chaque routeur OSPF pour construire sa table de routage à partir de la carte du réseau, avec un coût par lien inversement proportionnel au débit.', exemple: 'import heapq\ndef dijkstra(g, s):\n dist = {s: 0}\n file = [(0, s)]\n while file:\n d, u = heapq.heappop(file)\n if d > dist[u]:\n continue\n for v, w in g[u].items():\n if d + w < dist.get(v, float("inf")):\n dist[v] = d + w\n heapq.heappush(file, (d + w, v))\n return dist' }
],
synthese: ['Complexité O : O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ)', 'Dichotomie sur tableau trié en O(log n) ; tri fusion en O(n log n)', 'Récursivité : cas de base, pile d’appels, mémoïsation', 'Pile LIFO, file FIFO avec deque, dictionnaire en O(1) moyen', 'Graphes : listes d’adjacence ; BFS et DFS', 'Dijkstra et OSPF'],
qcm: [
['Complexité de la recherche dichotomique ?', ['O(1)', 'O(log n)', 'O(n)', 'O(n²)'], 1, 'On divise par deux à chaque étape.'],
['Condition pour utiliser la dichotomie ?', ['Tableau trié', 'Tableau court', 'Nombres entiers', 'Aucune'], 0, 'Sinon on ne sait pas quelle moitié choisir.'],
['Quelle structure pour une file d’attente en Python ?', ['list avec pop(0)', 'collections.deque', 'set', 'tuple'], 1, 'popleft en O(1).'],
['Que manque-t-il à une récursion qui provoque RecursionError ?', ['Un return', 'Un cas de base atteint', 'Une boucle', 'Un print'], 1, 'Elle ne s’arrête jamais.'],
['Quel parcours trouve le plus court chemin en nombre d’arêtes ?', ['DFS', 'BFS', 'Tri fusion', 'Dichotomie'], 1, 'Par distance croissante.'],
['Quel protocole de routage utilise Dijkstra ?', ['RIP', 'OSPF', 'DHCP', 'ARP'], 1, 'État de liens.'],
['Deux boucles imbriquées sur n éléments :', ['O(n)', 'O(n²)', 'O(log n)', 'O(2n)'], 1, 'n × n.']
],
td: [
{ n: 'Échauffement', c: 'Donner la complexité de : parcourir une liste ; vérifier si deux listes ont un élément commun avec deux boucles ; même chose en transformant la seconde en ensemble.', r: 'O(n) · O(n × m) · O(n + m)' },
{ n: 'Application', c: 'Écrire un tri par insertion, puis compter le nombre de comparaisons sur une liste déjà triée et sur une liste triée à l’envers de 1 000 éléments.', r: 'Déjà triée : environ n comparaisons (O(n)) · à l’envers : environ n²/2 = 500 000 (O(n²)).' },
{ n: 'Application', c: 'Vérifier le bon parenthésage d’une expression avec une pile.', r: 'Empiler chaque ouvrante ; pour chaque fermante, la pile ne doit pas être vide et son sommet doit correspondre ; à la fin, la pile doit être vide.' },
{ n: 'Approfondissement', c: 'Appliquer Dijkstra à la main sur un réseau de cinq routeurs fourni, puis en déduire la table de routage du routeur A.', r: 'Tableau des distances étape par étape ; la table associe à chaque destination le premier saut du plus court chemin.' }
],
tp: { type: 'IDE Python', url: 'apercu-ide.html', desc: 'Exercices avec tests et mesure du temps d’exécution.', etapes: ['Dichotomie et tests', 'Tri fusion', 'Comparaison des temps selon n', 'File des mains levées avec deque', 'BFS sur un plan de salles', 'Dijkstra sur un réseau de routeurs'] },
plusLoin: [['France-IOI · parcours algorithmique', 'https://www.france-ioi.org/'], ['HackerRank · Data Structures et Algorithms', 'https://www.hackerrank.com/domains/algorithms'], ['VisuAlgo (visualisation)', 'https://visualgo.net/fr']] },
plus: { cas: { titre: '', texte: '' },
resolu: { enonce: 'Le calcul des doublons dans l’export des 12 000 élèves de l’académie prend 40 minutes. Rendre le traitement quasi instantané.', etapes: [
['Lire le code', 'for a in eleves:\n for b in eleves:\n if a is not b and a["ine"] == b["ine"]: doublons.append(a)\n→ Deux boucles imbriquées : 144 millions de comparaisons, O(n²).'],
['Choisir la structure', '→ Un dictionnaire (ou un Counter) compte les occurrences de chaque identifiant en un seul parcours.'],
['Réécrire', 'from collections import Counter\nc = Counter(e["ine"] for e in eleves)\ndoublons = [e for e in eleves if c[e["ine"]] > 1]\n→ Deux parcours : O(n).'],
['Mesurer', 'Temps : 40 min → 0,02 s\n→ Même résultat vérifié par un test sur un petit jeu de données connu.'],
['Retenir', '→ Devant une double boucle de recherche, se demander quelle clé permettrait un accès direct par dictionnaire ou ensemble.']
] },
erreurs: ['Dichotomie sur une liste non triée', 'Oublier le cas de base', 'list.pop(0) pour une file', 'Double boucle là où un dictionnaire suffit', 'Recalculer sans mémoïsation', 'Dijkstra avec des poids négatifs'],
memo: [['Outil', 'Usage'], ['len, in sur set/dict', 'O(1) en moyenne'], ['sorted(t, key=…)', 'tri O(n log n)'], ['bisect', 'dichotomie de la bibliothèque'], ['collections.deque', 'file'], ['collections.Counter', 'compter'], ['heapq', 'file de priorité'], ['functools.cache', 'mémoïsation'], ['timeit', 'mesurer le temps'], ['assert f(x) == y', 'tests simples'], ['sys.setrecursionlimit', 'limite de récursion (avec prudence)']],
examen: { titre: 'Sujet type · 20 points', q: ['Déterminer la complexité de trois fonctions. (4 pts)', 'Écrire la recherche dichotomique et ses tests. (4 pts)', 'Écrire une fonction récursive et justifier sa terminaison. (4 pts)', 'Implémenter une file avec deque. (3 pts)', 'Appliquer Dijkstra à la main. (5 pts)'], r: ['O(n), O(n²), O(log n)', 'voir cours', 'cas de base, taille décroissante', 'append / popleft', 'tableau des distances'] },
prof: { deroule: [['Ouverture', 'Chronométrer une double boucle puis un dictionnaire'], ['Cours', 'Complexité'], ['Pratique', 'Tris et dichotomie'], ['Cours', 'Récursivité et structures'], ['Pratique', 'Graphes et Dijkstra'], ['Clôture', 'Quiz flash']], diff: 'Élèves en difficulté : visualisations VisuAlgo. Élèves avancés : HackerRank Medium, A*.', eval: 'Tests IDE, sujet type.' } },
exemples: [
['Comparer deux complexités', 'Justifier un choix.', 'python3 -m timeit -s "l=list(range(10**5)); s=set(l)" "99999 in l"\npython3 -m timeit -s "l=list(range(10**5)); s=set(l)" "99999 in s"', '500 loops, best of 5: 612 usec per loop\n5000000 loops, best of 5: 42.1 nsec per loop', 'La recherche dans un ensemble est environ 15 000 fois plus rapide que dans une liste.'],
['Dichotomie de la bibliothèque', 'Retrouver un seuil de note.', 'import bisect; bisect.bisect_left([8, 10, 12, 14, 16], 13)', '3', 'Position d’insertion de 13 dans la liste triée, en O(log n).'],
['Limite de récursion', 'Fonction récursive sur une longue liste.', 'def somme(l): return 0 if not l else l[0] + somme(l[1:])\nsomme(list(range(5000)))', 'RecursionError: maximum recursion depth exceeded', 'Au-delà de 1 000 appels : on écrit une version itérative, plus adaptée ici.'],
['Mémoïsation', 'Fibonacci lent.', 'from functools import cache\n@cache\ndef fib(n): return n if n < 2 else fib(n-1) + fib(n-2)\nfib(200)', '280571172992510140037611932413038677189525', 'Instantané grâce au cache ; la version naïve ne terminerait pas.'],
['File des mains levées', 'Fonction Lever la main.', 'from collections import deque\nq = deque(["Nora", "Yanis"]); q.append("Léa"); q.popleft(), list(q)', "('Nora', ['Yanis', 'Léa'])", 'Premier arrivé, premier servi, en O(1).'],
['Plus courts chemins', 'Réseau de quatre routeurs.', 'g = {"A":{"B":10,"C":1},"B":{"D":1},"C":{"B":1,"D":20},"D":{}}\ndijkstra(g, "A")', "{'A': 0, 'C': 1, 'B': 2, 'D': 3}", 'Le chemin vers D passe par C puis B (coût 3) plutôt que par le lien direct C–D (21).']
],
termes: ['complexité', 'O(n)', 'O(log n)', 'O(n²)', 'O(n log n)', 'recherche dichotomique', 'tri par insertion', 'tri fusion', 'récursivité', 'cas de base', 'pile d’appels', 'mémoïsation', 'pile', 'file', 'deque', 'liste chaînée', 'dictionnaire', 'graphe', 'BFS', 'DFS', 'Dijkstra', 'heapq']
});
reg({
cours: { id: 'backend', match: /Laravel|MVC|API REST|OpenAPI|Authentification : sessions|OAuth|PHP : bases|Framework back|Déployer une application web|Sécurité web|CSP|CORS|Tests : unitaires, intégration/i,
titre: 'Développement web back-end : MVC avec Laravel, API REST, authentification et sécurité', duree: '',
niveau: 'BTS SIO SLAM · BUT Informatique · licence pro · Master',
prerequis: ['Le Web : HTTP et HTML', 'SQL', 'Git'],
competences: ['BTS SIO SLAM · Bloc 2 · Concevoir et développer une solution applicative', 'BTS SIO · Bloc 3 · Cybersécurité des services', 'BUT Info · Réaliser et déployer'],
intro: 'Une application web professionnelle se compose d’une interface, d’une logique métier, d’une base de données et d’une API, le tout sécurisé et testé. Les frameworks comme Laravel, sur lequel est construit Syllabus, apportent une organisation éprouvée et des protections intégrées. Ce module présente l’architecture MVC, le routage, l’ORM, la conception d’une API REST, l’authentification, les protections contre les failles courantes et le déploiement.',
cm: [
{ t: 'Architecture MVC', d: '', html: 'Le motif Modèle-Vue-Contrôleur sépare les responsabilités. Le modèle représente les données et les règles métier (un élève, une classe, un devoir). La vue présente les données (templates Blade). Le contrôleur reçoit la requête, appelle le modèle et choisit la vue ou la réponse. Le routeur associe chaque URL et méthode HTTP à une action. Cette séparation rend le code testable et permet à plusieurs développeurs de travailler en parallèle.', exemple: '// routes/web.php\nRoute::middleware("auth")->group(function () {\n Route::get("/classes/{classe}", [ClasseController::class, "show"])->name("classes.show");\n});\n\n// app/Http/Controllers/ClasseController.php\npublic function show(Classe $classe) {\n $this->authorize("view", $classe);\n return view("classes.show", [\n "classe" => $classe->load("eleves"),\n ]);\n}' },
{ t: 'Modèles, ORM et migrations', d: '', html: 'L’ORM Eloquent associe chaque table à une classe : Eleve::where("classe_id", 3)->orderBy("nom")->get() produit une requête SQL préparée. Les relations (hasMany, belongsTo, belongsToMany) décrivent les clés étrangères. Les migrations versionnent le schéma de la base avec le code : chaque modification est un fichier, appliqué dans le même ordre partout. Piège de performance classique : le problème N+1, une requête par élément d’une liste, évité par le chargement anticipé (with("eleves")).' },
{ t: 'Concevoir une API REST', d: '', html: 'Une API REST expose des ressources identifiées par des URL et manipulées par les méthodes HTTP : GET /api/devoirs liste, POST /api/devoirs crée, GET /api/devoirs/12 lit, PUT ou PATCH modifie, DELETE supprime. Les réponses sont en JSON avec des codes cohérents : 200, 201 Created, 204 No Content, 400 et 422 pour une requête invalide, 401 non authentifié, 403 interdit, 404 introuvable. On versionne l’API (/api/v1), on pagine les listes, et on la documente avec OpenAPI, qui génère une documentation interactive et des clients.' },
{ t: 'Authentification et autorisation', d: '', html: 'L’authentification établit qui est l’utilisateur ; l’autorisation décide de ce qu’il peut faire. Pour une application web, une session côté serveur, identifiée par un cookie HttpOnly, Secure et SameSite, reste la solution la plus sûre. Pour une API consommée par d’autres applications, on utilise des jetons (tokens d’API, Laravel Sanctum) ou OAuth 2 / OpenID Connect pour déléguer l’authentification à un fournisseur d’identité (ENT, EduConnect). Les mots de passe sont hachés avec bcrypt ou Argon2. L’autorisation se centralise dans des politiques (policies) : un professeur ne voit que ses classes.' },
{ t: 'Sécurité : les failles courantes', d: '', html: 'L’OWASP Top 10 recense les failles les plus fréquentes. Contrôle d’accès défaillant : changer un identifiant dans l’URL donne accès aux données d’un autre (IDOR) ; on vérifie l’autorisation à chaque action. Injection : requêtes préparées systématiques. XSS : Blade échappe automatiquement {{ }} ; {!! !!} ne s’utilise que sur du contenu maîtrisé. CSRF : un jeton dans chaque formulaire (@csrf). Assignation de masse : déclarer les champs modifiables ($fillable). À quoi s’ajoutent les en-têtes : Content-Security-Policy contre l’injection de scripts, CORS limité aux origines autorisées, HSTS.', attention: 'Le mode débogage (APP_DEBUG=true) en production affiche les traces d’erreur, les variables d’environnement et parfois les mots de passe de la base. Il doit être désactivé, et le fichier .env ne doit jamais être accessible par le web ni versionné.' },
{ t: 'Tester et déployer', d: '', html: 'Les tests unitaires vérifient une classe isolée, les tests de fonctionnalité envoient de vraies requêtes à l’application ($this->actingAs($prof)->get("/classes/3")->assertOk()), les tests de bout en bout pilotent un navigateur. Le déploiement suit toujours les mêmes étapes, automatisées par la CI : récupération du code, composer install --no-dev, migrations, mise en cache de la configuration et des routes, redémarrage des workers de file d’attente, derrière Nginx et PHP-FPM, avec un retour arrière possible.' }
],
synthese: ['MVC : modèle, vue, contrôleur, routeur', 'ORM et migrations ; éviter le N+1 avec with()', 'REST : ressources, méthodes, codes HTTP, JSON, versionnement, OpenAPI', 'Session sécurisée ou jetons ; OAuth 2 / OIDC ; policies pour l’autorisation', 'OWASP : contrôle d’accès, injection, XSS, CSRF, assignation de masse ; en-têtes', 'APP_DEBUG=false, .env protégé ; tests et déploiement automatisés'],
qcm: [
['Dans MVC, qui reçoit la requête et choisit la réponse ?', ['Le modèle', 'Le contrôleur', 'La vue', 'La base'], 1, 'Il orchestre.'],
['Quelle méthode pour créer une ressource en REST ?', ['GET', 'POST', 'DELETE', 'HEAD'], 1, 'Réponse 201.'],
['Code HTTP pour « authentifié mais pas autorisé » ?', ['401', '403', '404', '500'], 1, '401 = non authentifié.'],
['Qu’est-ce que le problème N+1 ?', ['Une faille', 'Une requête SQL par élément d’une liste', 'Un code HTTP', 'Une erreur de migration'], 1, 'Chargement anticipé.'],
['Protection contre le CSRF ?', ['Un jeton dans chaque formulaire', 'HTTPS seul', 'Un captcha', 'Le hachage'], 0, '@csrf.'],
['Pourquoi désactiver APP_DEBUG en production ?', ['Performance', 'Il expose des informations sensibles', 'Obligation légale', 'Pour le cache'], 1, 'Traces, variables, secrets.']
],
td: [
{ n: 'Application', c: 'Concevoir les routes REST de la ressource « devoirs » d’une classe, avec les codes de réponse attendus.', r: 'GET /api/v1/classes/{c}/devoirs 200 · POST 201 · GET /devoirs/{d} 200/404 · PATCH 200/422 · DELETE 204 · 401 sans jeton, 403 si pas professeur de la classe.' },
{ n: 'Application', c: 'Corriger la faille : « public function show($id) { return Eleve::find($id); } ».', r: 'Liaison de modèle + autorisation : show(Eleve $eleve) { $this->authorize("view", $eleve); return $eleve; } avec une policy vérifiant que l’élève appartient à une classe du professeur.' },
{ n: 'Approfondissement', c: 'Écrire un test de fonctionnalité qui vérifie qu’un professeur ne peut pas voir la fiche d’un élève d’une autre classe.', r: '$prof = User::factory()->prof()->create(); $eleve = Eleve::factory()->create(); $this->actingAs($prof)->get(route("eleves.show", $eleve))->assertForbidden();' }
],
tp: { type: 'IDE', url: 'apercu-ide.html', desc: 'Mini-application Laravel de gestion de devoirs.', etapes: ['Migration et modèle', 'Contrôleur et vues', 'API REST', 'Policy d’autorisation', 'Tests de fonctionnalité', 'Déploiement par CI'] },
plusLoin: [['Documentation Laravel', 'https://laravel.com/docs'], ['OWASP Top 10', 'https://owasp.org/Top10/fr/'], ['OpenAPI Specification', 'https://spec.openapis.org/oas/latest.html'], ['ANSSI · Recommandations pour la mise en œuvre d’un site web', 'https://cyber.gouv.fr/']] },
plus: { cas: { titre: '', texte: '' },
resolu: { enonce: 'Un élève signale qu’en modifiant le numéro dans l’adresse de sa page de résultats, il voit les notes d’un camarade.', etapes: [
['Reproduire', 'Connecté comme élève 1204 : GET /resultats/1205 → 200 OK, notes de l’élève 1205.\n→ Faille de contrôle d’accès (IDOR).'],
['Lire le code', 'public function show($id) { return view("resultats", ["eleve" => Eleve::findOrFail($id)]); }\n→ Aucune vérification que l’élève connecté est celui demandé.'],
['Corriger', 'Policy EleveResultatsPolicy : un élève voit ses résultats, un professeur ceux de ses classes, la vie scolaire tout l’établissement.\npublic function show(Eleve $eleve) { $this->authorize("voirResultats", $eleve); … }\n→ Réponse 403 pour un autre élève.'],
['Tester et vérifier les autres routes', '→ Test automatisé ajouté ; revue de toutes les routes prenant un identifiant : deux autres corrigées (exports, devoirs rendus).'],
['Traiter les conséquences', '→ Analyse des journaux d’accès : consultations anormales par deux comptes. Information du DPO, qui évalue la notification à la CNIL et l’information des familles concernées.']
] },
erreurs: ['Contrôler l’accès seulement dans l’interface', 'Requêtes SQL construites par concaténation', 'Utiliser {!! !!} sur une saisie utilisateur', 'APP_DEBUG=true en production', 'Pas de tests d’autorisation', 'Codes HTTP incohérents dans l’API'],
memo: [['Commande / élément', 'Usage'], ['php artisan route:list', 'lister les routes'], ['php artisan make:model -mcr Devoir', 'modèle, migration, contrôleur'], ['php artisan migrate', 'appliquer les migrations'], ['php artisan make:policy', 'politique d’autorisation'], ['$this->authorize()', 'vérifier un droit'], ['with("relation")', 'éviter le N+1'], ['@csrf', 'jeton de formulaire'], ['php artisan test', 'lancer les tests'], ['php artisan config:cache route:cache', 'optimiser en production'], ['curl -H "Authorization: Bearer …" /api/v1/…', 'tester une API']],
examen: { titre: 'Sujet type · 20 points', q: ['Expliquer MVC sur un exemple. (3 pts)', 'Concevoir une API REST. (5 pts)', 'Écrire un contrôleur avec autorisation. (5 pts)', 'Identifier et corriger trois failles dans un code fourni. (5 pts)', 'Décrire un déploiement. (2 pts)'], r: ['voir cours', 'ressources et codes', 'authorize + policy', 'IDOR, XSS, injection', 'composer, migrate, cache'] },
prof: { deroule: [['Ouverture', 'Démonstration d’une faille IDOR sur une application de lab'], ['Cours', 'MVC et routage'], ['Pratique', 'Modèle et contrôleur'], ['Cours', 'API et sécurité'], ['Pratique', 'Policies et tests'], ['Clôture', 'Quiz flash']], diff: 'Élèves en difficulté : squelette fourni. Élèves avancés : OAuth 2, files d’attente, OpenAPI.', eval: 'Application testée, sujet type.' } },
exemples: [
['Lister les routes', 'Audit d’une application.', 'php artisan route:list --path=api', 'GET|HEAD api/v1/devoirs ...... devoirs.index › DevoirController@index\nPOST api/v1/devoirs ...... devoirs.store › DevoirController@store\nDELETE api/v1/devoirs/{devoir} devoirs.destroy', 'On vérifie que chaque route sensible a un middleware d’authentification.'],
['Détecter un N+1', 'Page lente.', 'DB::enableQueryLog(); $c = Classe::all(); foreach ($c as $x) $x->eleves->count(); count(DB::getQueryLog())', '31', '31 requêtes pour 30 classes. Avec Classe::withCount("eleves")->get(), une seule.'],
['Tester une API', 'Création d’un devoir.', 'curl -s -X POST https://syllabus.local/api/v1/devoirs -H "Authorization: Bearer $T" -H "Content-Type: application/json" -d \'{"titre":"DM 3"}\'', '{"message":"The date limite field is required.","errors":{"date_limite":["…"]}} (HTTP 422)', 'La validation renvoie 422 avec le détail des champs manquants.'],
['Test d’autorisation', 'Non-régression.', 'php artisan test --filter=ResultatsTest', 'PASS Tests\\Feature\\ResultatsTest\n✓ un élève voit ses résultats\n✓ un élève ne voit pas ceux d’un camarade', 'Le test échouerait si la faille réapparaissait.'],
['Vérifier les en-têtes', 'Audit de sécurité.', 'curl -sI https://syllabus.local | grep -iE "content-security|set-cookie"', 'content-security-policy: default-src \'self\'; frame-ancestors \'none\'\nset-cookie: syllabus_session=…; secure; httponly; samesite=lax', 'CSP restrictive et cookie de session protégé.'],
['Déploiement', 'Mise en production.', 'composer install --no-dev -o && php artisan migrate --force && php artisan optimize && php artisan queue:restart', 'INFO Running migrations. 2026_10_01_create_rendus_table ....... DONE\nINFO Caching framework bootstrap, configuration, and metadata.', 'Chaque étape est dans le pipeline CI, avec retour arrière en cas d’échec.']
],
termes: ['MVC', 'modèle', 'vue', 'contrôleur', 'routeur', 'Laravel', 'Blade', 'Eloquent', 'ORM', 'migration', 'N+1', 'API REST', 'JSON', 'OpenAPI', 'session', 'jeton', 'OAuth 2', 'OpenID Connect', 'policy', 'OWASP', 'IDOR', 'XSS', 'CSRF', 'CSP', 'CORS', 'APP_DEBUG']
});
reg({
cours: { id: 'js', match: /JavaScript|TypeScript|DOM|fetch|Accessibilité web|RGAA|WCAG|Performance web/i,
titre: 'JavaScript et TypeScript côté navigateur : DOM, asynchrone, fetch, accessibilité', duree: '',
niveau: 'NSI Première · BTS SIO SLAM · BUT Informatique · Titre pro DWWM',
prerequis: ['Le Web : HTML et CSS', 'Python : bases de la programmation'],
competences: ['NSI · Interactions dans le navigateur', 'BTS SIO SLAM · Développer une interface', 'DWWM · Développer la partie front-end'],
intro: 'Tout ce qui réagit dans une page web, du bouton « Lever la main » au terminal xterm.js des labs, est écrit en JavaScript. Le langage s’exécute dans le navigateur, manipule la page, dialogue avec le serveur sans la recharger et doit rester utilisable par tous, y compris au clavier et avec un lecteur d’écran. Ce module présente le langage moderne, le DOM, la programmation asynchrone avec fetch, TypeScript et les exigences d’accessibilité et de performance.',
cm: [
{ t: 'Le langage moderne', d: '', html: 'On déclare avec const (par défaut) et let ; var est à éviter. Les types de base : nombres, chaînes, booléens, null, undefined, objets et tableaux. Les fonctions fléchées (x => x * 2), la déstructuration (const { nom, classe } = eleve), les gabarits de chaînes (`Bonjour ${nom}`) et les méthodes de tableaux (map, filter, reduce, find) forment le socle quotidien. On compare avec ===, qui ne convertit pas les types ("1" == 1 est vrai, "1" === 1 est faux). Les modules (import / export) organisent le code.' },
{ t: 'Le DOM et les événements', d: '', html: 'Le DOM est la représentation de la page sous forme d’arbre d’objets. On sélectionne un élément (document.querySelector("#liste")), on modifie son contenu (textContent), ses classes (classList.add) ou ses attributs, on en crée (createElement). On réagit aux actions de l’utilisateur avec addEventListener("click", …). La délégation d’événements place un seul écouteur sur un parent pour gérer de nombreux enfants, y compris ceux ajoutés plus tard.', attention: 'innerHTML avec une donnée saisie par un utilisateur ouvre une faille XSS : le texte peut contenir un script. On utilise textContent pour du texte, ou on échappe explicitement.' },
{ t: 'Asynchrone : promesses et async/await', d: '', html: 'Le navigateur n’exécute qu’un seul fil de JavaScript ; une opération longue (requête réseau, minuterie) ne doit pas le bloquer. Elle renvoie une promesse, résolue plus tard. La syntaxe async / await permet d’écrire ce code de façon lisible, et try / catch gère les erreurs. Promise.all lance plusieurs requêtes en parallèle. La boucle d’événements exécute les rappels quand le fil principal est libre.' },
{ t: 'Dialoguer avec le serveur : fetch', d: '', html: 'fetch envoie une requête HTTP et renvoie une promesse de réponse. Il ne rejette la promesse qu’en cas d’erreur réseau : une réponse 404 ou 500 est une réponse « réussie », il faut tester response.ok. On envoie du JSON avec l’en-tête Content-Type et on joint le jeton CSRF ou d’authentification. Pour des mises à jour en temps réel (mains levées, présences du quiz), on utilise les WebSockets ou les Server-Sent Events plutôt qu’interroger le serveur toutes les secondes.', exemple: 'async function leverLaMain(labId) {\n const r = await fetch(`/api/v1/labs/${labId}/mains`, {\n method: "POST",\n headers: { "Content-Type": "application/json", "X-CSRF-TOKEN": jeton },\n body: JSON.stringify({ etape: 4 }),\n });\n if (!r.ok) throw new Error(`Erreur ${r.status}`);\n const { position } = await r.json();\n document.querySelector("#position").textContent = `Vous êtes ${position}e dans la file.`;\n}' },
{ t: 'TypeScript', d: '', html: 'TypeScript ajoute un typage statique à JavaScript : on déclare la forme des données (interface Eleve { id: number; nom: string; classe?: string }) et le compilateur signale les erreurs avant l’exécution : propriété mal orthographiée, valeur possiblement undefined, mauvais type d’argument. Le code est compilé en JavaScript standard. Sur un projet de plusieurs milliers de lignes, il réduit fortement les régressions et rend l’éditeur beaucoup plus utile (complétion, renommage sûr).' },
{ t: 'Accessibilité et performance', d: '', html: 'Un site public en France doit respecter le RGAA, fondé sur les normes WCAG. Les règles de base : utiliser les bons éléments HTML (un <button> plutôt qu’un <div> cliquable, qui ne se commande pas au clavier), des étiquettes de formulaire liées, des textes alternatifs, un contraste suffisant (4,5:1 pour le texte), un focus visible, et aria-live pour annoncer les mises à jour dynamiques. Côté performance : réduire le JavaScript chargé, différer ce qui n’est pas indispensable (defer, chargement à la demande), optimiser les images, mettre en cache. Les outils de développement du navigateur et Lighthouse mesurent les deux.' }
],
synthese: ['const/let, fonctions fléchées, déstructuration, ===, modules', 'DOM : querySelector, textContent, addEventListener, délégation', 'Jamais innerHTML avec une saisie utilisateur', 'Promesses, async/await, try/catch, Promise.all', 'fetch : tester response.ok ; WebSockets pour le temps réel', 'TypeScript : interfaces et vérification avant exécution', 'RGAA/WCAG : éléments natifs, clavier, contraste, aria-live ; performance'],
qcm: [
['Que vaut "1" === 1 ?', ['true', 'false', 'undefined', 'erreur'], 1, 'Pas de conversion de type.'],
['Quelle propriété insérer pour afficher un texte saisi par l’utilisateur ?', ['innerHTML', 'textContent', 'outerHTML', 'eval'], 1, 'Pas d’interprétation HTML.'],
['Quand fetch rejette-t-il sa promesse ?', ['Sur un 404', 'Sur un 500', 'En cas d’erreur réseau', 'Jamais'], 2, 'Tester response.ok.'],
['À quoi sert await ?', ['Bloquer le navigateur', 'Attendre le résultat d’une promesse dans une fonction async', 'Créer une boucle', 'Déclarer une variable'], 1, 'Sans bloquer le fil principal.'],
['Pourquoi préférer